Compare commits

...
Author SHA1 Message Date
aleka fc5d8538fe bump abacus for new env config path usage (#314)
* bump abacus for new env config path usage

* bump abacus + localization for good til date
2024-02-20 22:42:18 -05:00
yujin512 fdc262f59f Update otherMarketExchangeConfig.json (#315)
* Update otherMarketExchangeConfig.json

add strk

* Update otherMarketExchangeConfig.json

remove huobi, add kraken

* Update otherMarketParameters.json

add strk to parameters

* add strk icon and metadata
2024-02-20 16:08:11 -05:00
Bill 7f947804bd Enable CCTP onboarding for avalanche, optimism (#310)
* Enable CCTP for arbitrum avalanche optimism

* Enable CCTP onboarding for arbitrum, avalanche, optimism

* remove arb
2024-02-19 20:18:57 -08:00
aleka 6620a70a5d fix parsing error and surface tx broadcast error (#313)
* fix parsing error and surface tx broadcast error

* fix network as chain id
2024-02-19 22:06:13 -05:00
Bill bff779631b Cex withdraw (#301)
* Coinbase withdrawal and deposit UI

* analytics

* packages bumps

* address comments

* address comments

* add test flag
2024-02-16 17:56:06 -08:00
yujin512 b115bf33a0 Update markets.json (#311)
update ORDI asset name
2024-02-16 10:54:13 -08:00
aleka b544b8202b fetch chaos labs latest season (#309)
* fetch chaos labs latest season

* safer access
2024-02-16 11:09:48 -05:00
yujin512 ad6f99ffdf add icons and metadata (#305)
* add icons

* Update otherMarketParameters.json

update numOracles for markets with robust DEX oracles (not currently queryable)

* Update otherMarketParameters.json

update numOracles for markets with robust DEX oracles

* update asset name to match cmc

* Update otherMarketParameters.json

* add icons for other markets

* fix url and market name
2024-02-16 09:44:08 -05:00
Bill a7bd6a3ee8 host amplitude script ourselves (#307)
* host amplitude script ourselves

* update sha
2024-02-15 15:45:44 -08:00
aleka c92f218493 bump abacus + use new env config format (#298)
* bump abacus + use new env config format

* use new config path

* bump abacus to 1.4.5

* util function for getting mintscan link
2024-02-15 13:38:05 -05:00
moo-onthelawn b09fe9ebb1 fix bug (#304) 2024-02-14 15:19:37 -08:00
Bill 1c28505ec0 Set minimum for CCTP withdraws (#306) 2024-02-14 14:59:04 -08:00
moo-onthelawn 5531e21c76 change text color (#302) 2024-02-14 14:29:38 -05:00
John Huang 4ebeb21841 MOB-231 Refactor env.json to env2.json (#303)
* MOB-231 Refactor env.json to env2.json

* Move to v1/env.json

* dydx-testnet-4
2024-02-14 13:37:51 -05:00
moo-onthelawn b638ec1ad6 CT-463 Add Position (entry) line to TradingView (#292)
* wip

* fix deleted line issue

* compress tgz changes

* fix dependency hook

* simplify

* typo

* add trailing percent

* fix bug

* fix order

* merge order types

* wip

* rename variable

* review comments

* remove file

* rebase on new changes

* remove log

* clean up chart line type

* clean up typings a bit more

* fix var rename

* review comments

* clean up logic

* typing
2024-02-14 09:41:04 -05:00
Jared Vu f7a1cfc67e Updated Keplr language (#300) 2024-02-13 17:07:22 -08:00
aleka d66a776ad2 show trading rewards in testnet (#299) 2024-02-13 16:02:10 -05:00
moo-onthelawn e3e00f4f1e CT-145 Add warning icon + tooltip to trade order button CTA (#297)
* update

* rename to config
2024-02-13 16:00:46 -05:00
aleka 9ea578b8e4 avoid logging expected switch chain error (#291)
* avoid logging expected switch chain error

* skip logging more expected errors

* update switch chain error requirements check
2024-02-13 13:51:47 -05:00
moo-onthelawn f47fda45bf CT-428 Update browser tab title to be mid market price (#294)
* calculation

* make new selectors
2024-02-13 13:49:24 -05:00
moo-onthelawn a7addda930 CT-425 + TRCL-2154 Add orderlines to TradingView + fix colors (#287)
* wip

* fix deleted line issue

* compress tgz changes

* fix dependency hook

* simplify

* typo

* add trailing percent

* fix bug

* fix order

* merge order types
2024-02-12 10:31:15 -05:00
yujin512andjaredvu b839cf695e upload new jsons and update file names (#295)
* upload new jsons and update file names

* add disclaimer and format json

* change filename

* remove (ENS) from assetName

---------

Co-authored-by: jaredvu <jaredvu@gmail.com>
2024-02-09 21:03:00 -08:00
yujin512andJared Vu c9b8f1d59b Yujin/update assets (#293)
* add links for DYM, BNB, ZETA

* add icons

* update asseticon.tsc

* readd JUP

* bump v4-localization

* Added TODO

* Update public/configs/markets.json

Co-authored-by: Jared Vu <jaredvu@gmail.com>

* address comments

* higher resolution icons

---------

Co-authored-by: jaredvu <jaredvu@gmail.com>
2024-02-09 14:32:52 -08:00
moo-onthelawn 91c04956d6 adjust calculation (#290) 2024-02-08 16:12:42 -08:00
aleka 3a3f817501 wrap details item + fix cancel reason overflow/overlay (#284)
* wrap details item

* more details nits
2024-02-08 13:10:42 -05:00
108 changed files with 3030 additions and 4634 deletions
+1
View File
@@ -1 +1,2 @@
node_modules/ node_modules/
public/
+2 -2
View File
@@ -40,9 +40,9 @@
"@cosmjs/proto-signing": "^0.32.1", "@cosmjs/proto-signing": "^0.32.1",
"@cosmjs/stargate": "^0.32.1", "@cosmjs/stargate": "^0.32.1",
"@cosmjs/tendermint-rpc": "^0.32.1", "@cosmjs/tendermint-rpc": "^0.32.1",
"@dydxprotocol/v4-abacus": "^1.4.2", "@dydxprotocol/v4-abacus": "^1.4.9",
"@dydxprotocol/v4-client-js": "^1.0.20", "@dydxprotocol/v4-client-js": "^1.0.20",
"@dydxprotocol/v4-localization": "^1.1.26", "@dydxprotocol/v4-localization": "^1.1.34",
"@ethersproject/providers": "^5.7.2", "@ethersproject/providers": "^5.7.2",
"@js-joda/core": "^5.5.3", "@js-joda/core": "^5.5.3",
"@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-accordion": "^1.1.2",
+11 -15
View File
@@ -30,14 +30,14 @@ dependencies:
specifier: ^0.32.1 specifier: ^0.32.1
version: 0.32.2 version: 0.32.2
'@dydxprotocol/v4-abacus': '@dydxprotocol/v4-abacus':
specifier: ^1.4.2 specifier: ^1.4.9
version: 1.4.2 version: 1.4.9
'@dydxprotocol/v4-client-js': '@dydxprotocol/v4-client-js':
specifier: ^1.0.20 specifier: ^1.0.20
version: 1.0.20 version: 1.0.20
'@dydxprotocol/v4-localization': '@dydxprotocol/v4-localization':
specifier: ^1.1.26 specifier: ^1.1.34
version: 1.1.26 version: 1.1.34
'@ethersproject/providers': '@ethersproject/providers':
specifier: ^5.7.2 specifier: ^5.7.2
version: 5.7.2 version: 5.7.2
@@ -1290,8 +1290,8 @@ packages:
resolution: {integrity: sha512-Gg5t+eR7vPJMAmhkFt6CZrzPd0EKpAslWwk5rFVYZpJsM8JG5KT9XQ99hgNM3Ov6ScNoIWbXkpX27F6A9cXR4Q==} resolution: {integrity: sha512-Gg5t+eR7vPJMAmhkFt6CZrzPd0EKpAslWwk5rFVYZpJsM8JG5KT9XQ99hgNM3Ov6ScNoIWbXkpX27F6A9cXR4Q==}
dev: false dev: false
/@dydxprotocol/v4-abacus@1.4.2: /@dydxprotocol/v4-abacus@1.4.9:
resolution: {integrity: sha512-+hugk0RulMwMthR2xCMYXohcC3sEYqVW/lmiq0RUuHZ9yrjmgy48xl0aZUmXGUYXyoiHXPS4AULhRKHQ4OOLwg==} resolution: {integrity: sha512-OnrD5Db7AGSxyQE+gWRmkBuXbkZMfSEDB+v2s+ft0zgd4D5j/WTZkk1RG0fn8ISV4AffoN5T24jBWxLfqVlmZw==}
dev: false dev: false
/@dydxprotocol/v4-client-js@1.0.20: /@dydxprotocol/v4-client-js@1.0.20:
@@ -1323,8 +1323,8 @@ packages:
- utf-8-validate - utf-8-validate
dev: false dev: false
/@dydxprotocol/v4-localization@1.1.26: /@dydxprotocol/v4-localization@1.1.34:
resolution: {integrity: sha512-u+J1J5Up8McwZOcDaG9GrH/A8p4vD2NU2CT2hPdYyKgEd28XeAYvv7bNAQRKdiBaqTePbbF5uB6TMAQnEGnb1A==} resolution: {integrity: sha512-I7zivjv8gS+6b9n7/wh7PY9QEUDIIyLx9ugZ5K6ybar0xT/06yupOVdVE6iLivQ/IAsVi/RFHkD8bQd0DWCetg==}
dev: false dev: false
/@dydxprotocol/v4-proto@4.0.0-dev.0: /@dydxprotocol/v4-proto@4.0.0-dev.0:
@@ -12888,10 +12888,6 @@ packages:
engines: {node: '>=10.5.0'} engines: {node: '>=10.5.0'}
dev: true dev: true
/node-fetch-native@1.6.1:
resolution: {integrity: sha512-bW9T/uJDPAJB2YNYEpWzE54U5O3MQidXsOyTfnbKYtTtFexRvGzb1waphBN4ZwP6EcIvYYEOwW0b72BpAqydTw==}
dev: false
/node-fetch-native@1.6.2: /node-fetch-native@1.6.2:
resolution: {integrity: sha512-69mtXOFZ6hSkYiXAVB5SqaRvrbITC/NPyqv7yuu/qw0nmgPyYbIMYYNIDhNtwPrzk0ptrimrLz/hhjvm4w5Z+w==} resolution: {integrity: sha512-69mtXOFZ6hSkYiXAVB5SqaRvrbITC/NPyqv7yuu/qw0nmgPyYbIMYYNIDhNtwPrzk0ptrimrLz/hhjvm4w5Z+w==}
dev: false dev: false
@@ -13049,7 +13045,7 @@ packages:
resolution: {integrity: sha512-s1ZCMmQWXy4b5K/TW9i/DtiN8Ku+xCiHcjQ6/J/nDdssirrQNOoB165Zu8EqLMA2lln1JUth9a0aW9Ap2ctrUg==} resolution: {integrity: sha512-s1ZCMmQWXy4b5K/TW9i/DtiN8Ku+xCiHcjQ6/J/nDdssirrQNOoB165Zu8EqLMA2lln1JUth9a0aW9Ap2ctrUg==}
dependencies: dependencies:
destr: 2.0.2 destr: 2.0.2
node-fetch-native: 1.6.1 node-fetch-native: 1.6.2
ufo: 1.3.2 ufo: 1.3.2
dev: false dev: false
@@ -15029,7 +15025,7 @@ packages:
consola: 3.2.3 consola: 3.2.3
defu: 6.1.3 defu: 6.1.3
mime: 3.0.0 mime: 3.0.0
node-fetch-native: 1.6.1 node-fetch-native: 1.6.2
pathe: 1.1.1 pathe: 1.1.1
dev: false dev: false
@@ -15175,7 +15171,7 @@ packages:
listhen: 1.5.5 listhen: 1.5.5
lru-cache: 10.1.0 lru-cache: 10.1.0
mri: 1.2.0 mri: 1.2.0
node-fetch-native: 1.6.1 node-fetch-native: 1.6.2
ofetch: 1.3.3 ofetch: 1.3.3
ufo: 1.3.2 ufo: 1.3.2
transitivePeerDependencies: transitivePeerDependencies:
+21 -11
View File
@@ -1,12 +1,22 @@
[ [
{ {
"chainId": "1", "chainId": "1",
"tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"name": "Ethereum" "name": "Ethereum"
}, },
{ {
"chainId": "5", "chainId": "5",
"tokenAddress": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F", "tokenAddress": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F",
"name": "Ethereum Goerli" "name": "Ethereum Goerli"
} },
] {
"chainId": "43114",
"tokenAddress": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"name": "Avalanche"
},
{
"chainId": "10",
"tokenAddress": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
"name": "optimism"
}
]
+1 -1
View File
@@ -1329,4 +1329,4 @@
} }
} }
} }
} }
+184 -1
View File
@@ -20,6 +20,13 @@
"whitepaperLink": "https://why.cardano.org/en/introduction/motivation/", "whitepaperLink": "https://why.cardano.org/en/introduction/motivation/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/cardano/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/cardano/"
}, },
"AGIX-USD": {
"name": "SingularityNET",
"tags": ["AI"],
"websiteLink": "https://public.singularitynet.io/whitepaper.pdf",
"whitepaperLink": "https://public.singularitynet.io/whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/singularitynet/"
},
"ALGO-USD": { "ALGO-USD": {
"name": "Algorand", "name": "Algorand",
"tags": ["Layer 1"], "tags": ["Layer 1"],
@@ -78,11 +85,26 @@
}, },
"BLUR-USD": { "BLUR-USD": {
"name": "Blur", "name": "Blur",
"tags": [], "tags": ["NFT"],
"websiteLink": "https://blur.io/", "websiteLink": "https://blur.io/",
"whitepaperLink": "https://docs.blur.foundation/", "whitepaperLink": "https://docs.blur.foundation/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/blur-token/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/blur-token/"
}, },
"BNB-USD":{
"name": "BNB",
"tags": ["Layer 1"],
"websiteLink": "https://www.bnbchain.org/en",
"whitepaperLink": "https://www.exodus.com/assets/docs/binance-coin-whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/bnb/"
},
"CHZ-USD": {
"name": "Chiliz",
"tags": ["Layer 1"],
"websiteLink": "https://www.chiliz.com/",
"whitepaperLink": "https://www.chiliz.com/docs/litepaper-v1.1-20230703.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/chiliz/"
},
"CELO-USD": { "CELO-USD": {
"name": "Celo", "name": "Celo",
"tags": [], "tags": [],
@@ -125,6 +147,13 @@
"whitepaperLink": "https://polkadot.network/PolkaDotPaper.pdf", "whitepaperLink": "https://polkadot.network/PolkaDotPaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/polkadot-new/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/polkadot-new/"
}, },
"DYM-USD": {
"name": "Dymension",
"tags": [],
"websiteLink": "https://dymension.xyz/",
"whitepaperLink": "https://docs.dymension.xyz/dymension-litepaper/dymension-litepaper-index",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/dymension/"
},
"ENJ-USD": { "ENJ-USD": {
"name": "Enjin", "name": "Enjin",
"tags": [], "tags": [],
@@ -132,6 +161,13 @@
"whitepaperLink": "https://cdn.enjin.io/downloads/whitepapers/enjin-coin/en.pdf/", "whitepaperLink": "https://cdn.enjin.io/downloads/whitepapers/enjin-coin/en.pdf/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/enjin-coin/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/enjin-coin/"
}, },
"ENS-USD": {
"name": "Ethereum Name Service",
"tags": [],
"websiteLink": "https://coinmarketcap.com/currencies/ethereum-name-service/",
"whitepaperLink": "https://docs.ens.domains/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/ethereum-name-service/"
},
"EOS-USD": { "EOS-USD": {
"name": "EOS", "name": "EOS",
"tags": ["Layer 1"], "tags": ["Layer 1"],
@@ -155,6 +191,13 @@
"displayStepSize": "0.001", "displayStepSize": "0.001",
"displayTickSize": "0.1" "displayTickSize": "0.1"
}, },
"FET-USD": {
"name": "Fetch.ai",
"tags": ["AI"],
"websiteLink": "https://fetch.ai/",
"whitepaperLink": "https://www.dropbox.com/s/gxptsecwdl3jjtn/David%20Minarsch%20-%202021-04-26%2010.34.17%20-%20paper_21_finalversion.pdf?e=1&dl=0",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/fetch/"
},
"FIL-USD": { "FIL-USD": {
"name": "Filecoin", "name": "Filecoin",
"tags": ["Layer 1"], "tags": ["Layer 1"],
@@ -162,6 +205,34 @@
"whitepaperLink": "https://filecoin.io/filecoin.pdf", "whitepaperLink": "https://filecoin.io/filecoin.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/filecoin/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/filecoin/"
}, },
"FTM-USD": {
"name": "Fantom",
"tags": [],
"websiteLink": "https://fantom.foundation/",
"whitepaperLink": "https://fantom.foundation/_next/static/media/wp_fantom_v1.6.39329cdc5d0ee59684cbc6f228516383.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/fantom/"
},
"GALA-USD": {
"name": "Gala",
"tags": ["Gaming", "Layer 1"],
"websiteLink": "https://gala.com/",
"whitepaperLink": "https://galahackathon.com/v1.0.0/pdf/sdk-documentation.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/gala/"
},
"GMT-USD": {
"name": "GMT",
"tags": ["Gaming"],
"websiteLink": "https://stepn.com/",
"whitepaperLink/": "https://whitepaper.stepn.com/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/green-metaverse-token/"
},
"GRT-USD": {
"name": "The Graph",
"tags": [],
"websiteLink": "https://thegraph.com/",
"whitepaperLink/": "https://github.com/graphprotocol/research/blob/master/papers/whitepaper/the-graph-whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/the-graph/"
},
"HNT-USD": { "HNT-USD": {
"name": "Helium", "name": "Helium",
"tags": ["Layer 1"], "tags": ["Layer 1"],
@@ -169,6 +240,13 @@
"whitepaperLink": "http://whitepaper.helium.com", "whitepaperLink": "http://whitepaper.helium.com",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/helium/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/helium/"
}, },
"HBAR-USD": {
"name": "Hedera",
"tags": [],
"websiteLink": "https://hedera.com/",
"whitepaperLink/": "https://files.hedera.com/hh_whitepaper_v2.2-20230918.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/hedera/"
},
"ICP-USD": { "ICP-USD": {
"name": "Internet Computer", "name": "Internet Computer",
"tags": ["Layer 1"], "tags": ["Layer 1"],
@@ -176,6 +254,27 @@
"whitepaperLink": "https://dfinity.org/whitepaper.pdf", "whitepaperLink": "https://dfinity.org/whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/internet-computer/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/internet-computer/"
}, },
"IMX-USD": {
"name": "Immutable X",
"tags": ["Gaming", "Layer 2", "NFT"],
"websiteLink": "https://www.immutable.com/",
"whitepaperLink": "https://assets.website-files.com/646557ee455c3e16e4a9bcb3/6499367de527dd82ab7475a3_Immutable%20Whitepaper%20Update%202023%20(3).pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/immutable-x/"
},
"INJ-USD": {
"name": "Injective",
"tags": ["Layer 1", "Defi"],
"websiteLink": "https://injective.com/",
"whitepaperLink": "https://docs.injective.network/intro/01_overview.html",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/injective/"
},
"JTO-USD": {
"name": "Jito",
"tags": ["Defi"],
"websiteLink": "https://www.jito.network/",
"whitepaperLink": "https://github.com/jito-foundation",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/jito/"
},
"JUP-USD": { "JUP-USD": {
"name": "Jupiter", "name": "Jupiter",
"tags": ["Defi"], "tags": ["Defi"],
@@ -183,6 +282,13 @@
"whitepaperLink": "https://station.jup.ag/blog/green-paper", "whitepaperLink": "https://station.jup.ag/blog/green-paper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/jupiter-ag/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/jupiter-ag/"
}, },
"KAVA-USD": {
"name": "Kava",
"tags": ["Layer 1"],
"websiteLink": "https://www.kava.io/",
"whitepaperLink": "https://docsend.com/view/gwbwpc3",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/kava/"
},
"LDO-USD": { "LDO-USD": {
"name": "Lido DAO", "name": "Lido DAO",
"tags": ["Defi"], "tags": ["Defi"],
@@ -204,6 +310,20 @@
"whitepaperLink": "https://litecoin.info/index.php/Main_Page", "whitepaperLink": "https://litecoin.info/index.php/Main_Page",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/litecoin/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/litecoin/"
}, },
"MANA-USD": {
"name": "Decentraland",
"tags": ["AR/VR"],
"websiteLink": "https://decentraland.org/",
"whitepaperLink": "https://decentraland.org/whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/decentraland/"
},
"MASK-USD": {
"name": "Mask Network",
"tags": [],
"websiteLink": "https://mask.io/",
"whitepaperLink": "https://masknetwork.medium.com/introducing-mask-network-maskbook-the-future-of-the-internet-5a973d874edd",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/mask-network/"
},
"MATIC-USD": { "MATIC-USD": {
"name": "Polygon", "name": "Polygon",
"tags": ["Layer 2"], "tags": ["Layer 2"],
@@ -211,6 +331,13 @@
"whitepaperLink": "https://polygon.technology/lightpaper-polygon.pdf", "whitepaperLink": "https://polygon.technology/lightpaper-polygon.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/polygon/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/polygon/"
}, },
"MINA-USD": {
"name": "Mina",
"tags": ["Layer 1"],
"websiteLink": "https://minaprotocol.com/",
"whitepaperLink": "https://docs.minaprotocol.com/assets/economicsWhitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/mina/"
},
"MKR-USD": { "MKR-USD": {
"name": "Maker", "name": "Maker",
"tags": ["Governance"], "tags": ["Governance"],
@@ -225,6 +352,13 @@
"whitepaperLink": "https://near.org/papers/the-official-near-white-paper/", "whitepaperLink": "https://near.org/papers/the-official-near-white-paper/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/near-protocol/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/near-protocol/"
}, },
"ORDI-USD": {
"name": "Ordinals",
"tags": ["NFT"],
"websiteLink": "https://ordinals.com/",
"whitepaperLink": "https://rodarmor.com/blog/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/ordi/"
},
"OP-USD": { "OP-USD": {
"name": "Optimism", "name": "Optimism",
"tags": [], "tags": [],
@@ -238,6 +372,20 @@
"websiteLink": "https://www.pepe.vip/", "websiteLink": "https://www.pepe.vip/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/pepe/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/pepe/"
}, },
"PYTH-USD": {
"name": "Pyth Network",
"tags": [],
"websiteLink": "https://pyth.network/",
"whitepaperLink": "https://pyth.network/whitepaper_v2.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/pyth-network/"
},
"RNDR-USD": {
"name": "Render Token",
"tags": ["AI"],
"websiteLink": "https://rendernetwork.com/",
"whitepaperLink": "https://renderfoundation.com/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/render/"
},
"RUNE-USD": { "RUNE-USD": {
"name": "THORChain", "name": "THORChain",
"tags": ["Layer 1"], "tags": ["Layer 1"],
@@ -245,6 +393,13 @@
"whitepaperLink": "https://whitepaper.io/document/709/thorchain-whitepaper", "whitepaperLink": "https://whitepaper.io/document/709/thorchain-whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/thorchain/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/thorchain/"
}, },
"SAND-USD": {
"name": "The Sandbox",
"tags": ["Gaming"],
"websiteLink": "https://www.sandbox.game/en/",
"whitepaperLink": "https://installers.sandbox.game/The_Sandbox_Whitepaper_2020.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/the-sandbox/"
},
"SEI-USD": { "SEI-USD": {
"name": "Sei", "name": "Sei",
"tags": ["Layer 1", "Defi"], "tags": ["Layer 1", "Defi"],
@@ -273,6 +428,20 @@
"whitepaperLink": "https://solana.com/solana-whitepaper.pdf", "whitepaperLink": "https://solana.com/solana-whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/solana/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/solana/"
}, },
"STRK-USD": {
"name": "Starknet",
"tags": ["Layer 2"],
"websiteLink": "https://www.starknet.io/en",
"whitepaperLink": "https://docs.starknet.io/documentation/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/starknet-token/"
},
"STX-USD": {
"name": "Stacks",
"tags": ["Layer 2"],
"websiteLink": "https://www.stacks.co/",
"whitepaperLink": "https://gaia.blockstack.org/hub/1AxyPunHHAHiEffXWESKfbvmBpGQv138Fp/stacks.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/stacks/"
},
"SUI-USD": { "SUI-USD": {
"name": "Sui", "name": "Sui",
"tags": ["Layer 1"], "tags": ["Layer 1"],
@@ -322,6 +491,13 @@
"whitepaperLink": "https://whitepaper.worldcoin.org/", "whitepaperLink": "https://whitepaper.worldcoin.org/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/worldcoin-org/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/worldcoin-org/"
}, },
"WOO-USD": {
"name": "WOO Network",
"tags": ["Defi"],
"websiteLink": "https://woo.org/",
"whitepaperLink": "https://woo.org/Litepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/wootrade/"
},
"XLM-USD": { "XLM-USD": {
"name": "Stellar", "name": "Stellar",
"tags": ["Layer 1"], "tags": ["Layer 1"],
@@ -364,6 +540,13 @@
"whitepaperLink": "https://z.cash/technology/", "whitepaperLink": "https://z.cash/technology/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/zcash/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/zcash/"
}, },
"ZETA-USD": {
"name": "ZetaChain",
"tags": ["Layer 1"],
"websiteLink": "https://www.zetachain.com/",
"whitepaperLink": "https://www.zetachain.com/whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/zetachain/"
},
"ZRX-USD": { "ZRX-USD": {
"name": "0x", "name": "0x",
"tags": ["Defi"], "tags": ["Defi"],
+1
View File
@@ -0,0 +1 @@
This file identifies parameters for the optimal performance of various assets with the dYdX v4 open source software ("dYdX Chain"). For information on which assets are likely to be best compatible with dYdX Chain and how likely software compatibility and optimal parameters are assessed, please review the documentation [here](https://docs.dydx.trade/governance/proposing_a_new_market#example-proposal-json). Users considering using the permissionless markets function of the dYdX Chain are encouraged to consult qualified legal counsel to ensure compliance with the laws of their jurisdiction. The information herein does not constitute and should not be relied on as an endorsement or recommendation for any specific market, or investment, legal, or any other form of professional advice. Use of the v4 software is prohibited in the United States, Canada, and sanctioned jurisdictions as described in the [v4 Terms of Use](https://dydx.exchange/v4-terms).
@@ -0,0 +1,616 @@
{
"1INCH": [
{ "exchangeName": "Binance", "ticker": "1INCHUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "1INCH-USD" },
{ "exchangeName": "Gate", "ticker": "1INCH_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kucoin", "ticker": "1INCH-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "1INCH-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "1INCH_USDT", "adjustByMarket": "USDT-USD" }
],
"AAVE": [
{ "exchangeName": "Binance", "ticker": "AAVEUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "AAVE-USD" },
{ "exchangeName": "Huobi", "ticker": "aaveusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "AAVEUSD" },
{ "exchangeName": "Kucoin", "ticker": "AAVE-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "AAVE-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "AAVE_USDT", "adjustByMarket": "USDT-USD" }
],
"ADA": [
{ "exchangeName": "Binance", "ticker": "ADAUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bitstamp", "ticker": "ADA/USD" },
{ "exchangeName": "Bybit", "ticker": "ADAUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "ADA-USD" },
{ "exchangeName": "Huobi", "ticker": "adausdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "ADAUSD" },
{ "exchangeName": "Kucoin", "ticker": "ADA-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "ADA-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "ADA_USDT", "adjustByMarket": "USDT-USD" }
],
"AGIX": [
{ "exchangeName": "Binance", "ticker": "AGIXUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "AGIXUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Gate", "ticker": "AGIX_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kucoin", "ticker": "AGIX-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "AGIX-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "AGIX_USDT", "adjustByMarket": "USDT-USD" }
],
"ALGO": [
{ "exchangeName": "Binance", "ticker": "ALGOUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "ALGO-USD" },
{ "exchangeName": "Kraken", "ticker": "ALGOUSD" },
{ "exchangeName": "Kucoin", "ticker": "ALGO-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "ALGO-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "ALGO_USDT", "adjustByMarket": "USDT-USD" }
],
"APE": [
{ "exchangeName": "Binance", "ticker": "APEUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "APE-USD" },
{ "exchangeName": "Gate", "ticker": "APE_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "APEUSD" },
{ "exchangeName": "Kucoin", "ticker": "APE-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "APE-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "APE_USDT", "adjustByMarket": "USDT-USD" }
],
"APT": [
{ "exchangeName": "Binance", "ticker": "APTUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "APTUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "APT-USD" },
{ "exchangeName": "Gate", "ticker": "APT_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Huobi", "ticker": "aptusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "APTUSD" },
{ "exchangeName": "Kucoin", "ticker": "APT-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "APT-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "APT_USDT", "adjustByMarket": "USDT-USD" }
],
"ARB": [
{ "exchangeName": "Binance", "ticker": "ARBUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "ARBUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "ARB-USD" },
{ "exchangeName": "Huobi", "ticker": "arbusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "ARBUSD" },
{ "exchangeName": "Kucoin", "ticker": "ARB-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "ARB-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "ARB_USDT", "adjustByMarket": "USDT-USD" }
],
"ATOM": [
{ "exchangeName": "Binance", "ticker": "ATOMUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "ATOMUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "ATOM-USD" },
{ "exchangeName": "Gate", "ticker": "ATOM_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "ATOMUSD" },
{ "exchangeName": "Kucoin", "ticker": "ATOM-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "ATOM-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "ATOM_USDT", "adjustByMarket": "USDT-USD" }
],
"AVAX": [
{ "exchangeName": "Binance", "ticker": "AVAXUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bitstamp", "ticker": "AVAX/USD" },
{ "exchangeName": "Bybit", "ticker": "AVAXUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "AVAX-USD" },
{ "exchangeName": "Huobi", "ticker": "avaxusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "AVAXUSD" },
{ "exchangeName": "Kucoin", "ticker": "AVAX-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "AVAX-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "AVAX_USDT", "adjustByMarket": "USDT-USD" }
],
"BCH": [
{ "exchangeName": "Binance", "ticker": "BCHUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bitstamp", "ticker": "BCH/USD" },
{ "exchangeName": "Bybit", "ticker": "BCHUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "BCH-USD" },
{ "exchangeName": "Huobi", "ticker": "bchusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "BCHUSD" },
{ "exchangeName": "Kucoin", "ticker": "BCH-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "BCH-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "BCH_USDT", "adjustByMarket": "USDT-USD" }
],
"BLUR": [
{ "exchangeName": "Binance", "ticker": "BLURUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "BLURUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "BLUR-USD" },
{ "exchangeName": "Kraken", "ticker": "BLURUSD" },
{ "exchangeName": "Kucoin", "ticker": "BLUR-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "BLUR-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "BLUR_USDT", "adjustByMarket": "USDT-USD" }
],
"BNB": [
{ "exchangeName": "Binance", "ticker": "BNBUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "BNBUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Gate", "ticker": "BNB_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kucoin", "ticker": "BNB-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "BNB-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "BNB_USDT", "adjustByMarket": "USDT-USD" }
],
"BONK": [
{ "exchangeName": "Binance", "ticker": "BONKUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "BONKUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "BONK-USD" },
{ "exchangeName": "Kucoin", "ticker": "BONK-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "BONK-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "BONK_USDT", "adjustByMarket": "USDT-USD" }
],
"BTC": [
{ "exchangeName": "Binance", "ticker": "BTCUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bitstamp", "ticker": "BTC/USD" },
{ "exchangeName": "Bybit", "ticker": "BTCUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "BTC-USD" },
{ "exchangeName": "Huobi", "ticker": "btcusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "BTCUSD" },
{ "exchangeName": "Kucoin", "ticker": "BTC-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "BTC-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "BTC_USDT", "adjustByMarket": "USDT-USD" }
],
"CHZ": [
{ "exchangeName": "Binance", "ticker": "CHZUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "CHZ-USD" },
{ "exchangeName": "Kraken", "ticker": "CHZUSD" },
{ "exchangeName": "Kucoin", "ticker": "CHZ-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "CHZ-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "CHZ_USDT", "adjustByMarket": "USDT-USD" }
],
"CRV": [
{ "exchangeName": "Binance", "ticker": "CRVUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "CRV-USD" },
{ "exchangeName": "Kraken", "ticker": "CRVUSD" },
{ "exchangeName": "Kucoin", "ticker": "CRV-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "CRV-USDT", "adjustByMarket": "USDT-USD" }
],
"DOGE": [
{ "exchangeName": "Binance", "ticker": "DOGEUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "DOGEUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "DOGE-USD" },
{ "exchangeName": "Huobi", "ticker": "dogeusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "DOGEUSD" },
{ "exchangeName": "Kucoin", "ticker": "DOGE-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "DOGE-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "DOGE_USDT", "adjustByMarket": "USDT-USD" }
],
"DOT": [
{ "exchangeName": "Binance", "ticker": "DOTUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "DOTUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "DOT-USD" },
{ "exchangeName": "Huobi", "ticker": "dotusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "DOTUSD" },
{ "exchangeName": "Kucoin", "ticker": "DOT-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "DOT-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "DOT_USDT", "adjustByMarket": "USDT-USD" }
],
"DYM": [
{ "exchangeName": "Binance", "ticker": "DYMUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "DYMUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Gate", "ticker": "DYM_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kucoin", "ticker": "DYM-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "DYM_USDT", "adjustByMarket": "USDT-USD" }
],
"ENS": [
{ "exchangeName": "Binance", "ticker": "ENSUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "ENS-USD" },
{ "exchangeName": "Gate", "ticker": "ENS_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kucoin", "ticker": "ENS-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "ENS-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "ENS_USDT", "adjustByMarket": "USDT-USD" }
],
"EOS": [
{ "exchangeName": "Binance", "ticker": "EOSUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "EOSUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "EOS-USD" },
{ "exchangeName": "Gate", "ticker": "EOS_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "EOSUSD" },
{ "exchangeName": "Kucoin", "ticker": "EOS-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "EOS-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "EOS_USDT", "adjustByMarket": "USDT-USD" }
],
"ETC": [
{ "exchangeName": "Binance", "ticker": "ETCUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "ETCUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "ETC-USD" },
{ "exchangeName": "Huobi", "ticker": "etcusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "ETCUSD" },
{ "exchangeName": "Kucoin", "ticker": "ETC-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "ETC-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "ETC_USDT", "adjustByMarket": "USDT-USD" }
],
"ETH": [
{ "exchangeName": "Binance", "ticker": "ETHUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bitstamp", "ticker": "ETH/USD" },
{ "exchangeName": "Bybit", "ticker": "ETHUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "ETH-USD" },
{ "exchangeName": "Huobi", "ticker": "ethusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "ETHUSD" },
{ "exchangeName": "Kucoin", "ticker": "ETH-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "ETH-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "ETH_USDT", "adjustByMarket": "USDT-USD" }
],
"FET": [
{ "exchangeName": "Binance", "ticker": "FETUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "FET-USD" },
{ "exchangeName": "Kraken", "ticker": "FETUSD" },
{ "exchangeName": "Kucoin", "ticker": "FET-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "FET-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "FET_USDT", "adjustByMarket": "USDT-USD" }
],
"FIL": [
{ "exchangeName": "Binance", "ticker": "FILUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "FILUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "FIL-USD" },
{ "exchangeName": "Huobi", "ticker": "filusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "FILUSD" },
{ "exchangeName": "Kucoin", "ticker": "FIL-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "FIL-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "FIL_USDT", "adjustByMarket": "USDT-USD" }
],
"FTM": [
{ "exchangeName": "Binance", "ticker": "FTMUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "FTMUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "FTMUSD" },
{ "exchangeName": "Kucoin", "ticker": "FTM-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "FTM-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "FTM_USDT", "adjustByMarket": "USDT-USD" }
],
"GALA": [
{ "exchangeName": "Binance", "ticker": "GALAUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "GALAUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Gate", "ticker": "GALA_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "GALAUSD" },
{ "exchangeName": "Okx", "ticker": "GALA-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "GALA_USDT", "adjustByMarket": "USDT-USD" }
],
"GMT": [
{ "exchangeName": "Binance", "ticker": "GMTUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "GMTUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "GMT-USD" },
{ "exchangeName": "Gate", "ticker": "GMT_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kucoin", "ticker": "GMT-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "GMT-USDT", "adjustByMarket": "USDT-USD" }
],
"GRT": [
{ "exchangeName": "Binance", "ticker": "GRTUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "GRT-USD" },
{ "exchangeName": "Gate", "ticker": "GRT_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "GRTUSD" },
{ "exchangeName": "Kucoin", "ticker": "GRT-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "GRT-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "GRT_USDT", "adjustByMarket": "USDT-USD" }
],
"HBAR": [
{ "exchangeName": "Binance", "ticker": "HBARUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "HBARUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "HBAR-USD" },
{ "exchangeName": "Kucoin", "ticker": "HBAR-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "HBAR-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "HBAR_USDT", "adjustByMarket": "USDT-USD" }
],
"ICP": [
{ "exchangeName": "Binance", "ticker": "ICPUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "ICPUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "ICP-USD" },
{ "exchangeName": "Kraken", "ticker": "ICPUSD" },
{ "exchangeName": "Kucoin", "ticker": "ICP-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "ICP-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "ICP_USDT", "adjustByMarket": "USDT-USD" }
],
"IMX": [
{ "exchangeName": "Binance", "ticker": "IMXUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "IMX-USD" },
{ "exchangeName": "Kraken", "ticker": "IMXUSD" },
{ "exchangeName": "Kucoin", "ticker": "IMX-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "IMX-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "IMX_USDT", "adjustByMarket": "USDT-USD" }
],
"INJ": [
{ "exchangeName": "Binance", "ticker": "INJUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "INJUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "INJ-USD" },
{ "exchangeName": "Kraken", "ticker": "INJUSD" },
{ "exchangeName": "Kucoin", "ticker": "INJ-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "INJ-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "INJ_USDT", "adjustByMarket": "USDT-USD" }
],
"JTO": [
{ "exchangeName": "Binance", "ticker": "JTOUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "JTOUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "JTO-USD" },
{ "exchangeName": "Kucoin", "ticker": "JTO-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "JTO-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "JTO_USDT", "adjustByMarket": "USDT-USD" }
],
"JUP": [
{ "exchangeName": "Binance", "ticker": "JUPUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "JUPUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Gate", "ticker": "JUP_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "JUP-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "JUP_USDT", "adjustByMarket": "USDT-USD" }
],
"KAVA": [
{ "exchangeName": "Binance", "ticker": "KAVAUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "KAVAUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "KAVA-USD" },
{ "exchangeName": "Gate", "ticker": "KAVA_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "KAVAUSD" },
{ "exchangeName": "Kucoin", "ticker": "KAVA-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "KAVA_USDT", "adjustByMarket": "USDT-USD" }
],
"LDO": [
{ "exchangeName": "Binance", "ticker": "LDOUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "LDOUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "LDO-USD" },
{ "exchangeName": "Kraken", "ticker": "LDOUSD" },
{ "exchangeName": "Kucoin", "ticker": "LDO-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "LDO-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "LDO_USDT", "adjustByMarket": "USDT-USD" }
],
"LINK": [
{ "exchangeName": "Binance", "ticker": "LINKUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bitstamp", "ticker": "LINK/USD" },
{ "exchangeName": "Bybit", "ticker": "LINKUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "LINK-USD" },
{ "exchangeName": "Kraken", "ticker": "LINKUSD" },
{ "exchangeName": "Kucoin", "ticker": "LINK-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "LINK-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "LINK_USDT", "adjustByMarket": "USDT-USD" }
],
"LTC": [
{ "exchangeName": "Binance", "ticker": "LTCUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bitstamp", "ticker": "LTC/USD" },
{ "exchangeName": "Bybit", "ticker": "LTCUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "LTC-USD" },
{ "exchangeName": "Huobi", "ticker": "ltcusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "LTCUSD" },
{ "exchangeName": "Kucoin", "ticker": "LTC-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "LTC-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "LTC_USDT", "adjustByMarket": "USDT-USD" }
],
"MANA": [
{ "exchangeName": "Binance", "ticker": "MANAUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "MANA-USD" },
{ "exchangeName": "Gate", "ticker": "MANA_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "MANAUSD" },
{ "exchangeName": "Kucoin", "ticker": "MANA-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "MANA-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "MANA_USDT", "adjustByMarket": "USDT-USD" }
],
"MASK": [
{ "exchangeName": "Binance", "ticker": "MASKUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "MASKUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "MASK-USD" },
{ "exchangeName": "Gate", "ticker": "MASK_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Huobi", "ticker": "maskusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kucoin", "ticker": "MASK-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "MASK-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "MASK_USDT", "adjustByMarket": "USDT-USD" }
],
"MATIC": [
{ "exchangeName": "Binance", "ticker": "MATICUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bitstamp", "ticker": "MATIC/USD" },
{ "exchangeName": "Bybit", "ticker": "MATICUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "MATIC-USD" },
{ "exchangeName": "Huobi", "ticker": "maticusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "MATICUSD" },
{ "exchangeName": "Kucoin", "ticker": "MATIC-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "MATIC-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "MATIC_USDT", "adjustByMarket": "USDT-USD" }
],
"MINA": [
{ "exchangeName": "Binance", "ticker": "MINAUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "MINA-USD" },
{ "exchangeName": "Gate", "ticker": "MINA_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "MINAUSD" },
{ "exchangeName": "Okx", "ticker": "MINA-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "MINA_USDT", "adjustByMarket": "USDT-USD" }
],
"MKR": [
{ "exchangeName": "Binance", "ticker": "MKRUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "MKR-USD" },
{ "exchangeName": "Kraken", "ticker": "MKRUSD" },
{ "exchangeName": "Kucoin", "ticker": "MKR-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "MKR-USDT", "adjustByMarket": "USDT-USD" }
],
"NEAR": [
{ "exchangeName": "Binance", "ticker": "NEARUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "NEARUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "NEAR-USD" },
{ "exchangeName": "Huobi", "ticker": "nearusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "NEARUSD" },
{ "exchangeName": "Kucoin", "ticker": "NEAR-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "NEAR-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "NEAR_USDT", "adjustByMarket": "USDT-USD" }
],
"OP": [
{ "exchangeName": "Binance", "ticker": "OPUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "OPUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "OP-USD" },
{ "exchangeName": "Gate", "ticker": "OP_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "OPUSD" },
{ "exchangeName": "Kucoin", "ticker": "OP-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "OP-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "OP_USDT", "adjustByMarket": "USDT-USD" }
],
"ORDI": [
{ "exchangeName": "Binance", "ticker": "ORDIUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "ORDIUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Gate", "ticker": "ORDI_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Huobi", "ticker": "ordiusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kucoin", "ticker": "ORDI-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "ORDI-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "ORDI_USDT", "adjustByMarket": "USDT-USD" }
],
"PEPE": [
{ "exchangeName": "Binance", "ticker": "PEPEUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "PEPEUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "PEPEUSD" },
{ "exchangeName": "Kucoin", "ticker": "PEPE-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "PEPE-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "PEPE_USDT", "adjustByMarket": "USDT-USD" }
],
"PYTH": [
{ "exchangeName": "Binance", "ticker": "PYTHUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "PYTHUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Gate", "ticker": "PYTH_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kucoin", "ticker": "PYTH-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "PYTH-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "PYTH_USDT", "adjustByMarket": "USDT-USD" }
],
"RNDR": [
{ "exchangeName": "Binance", "ticker": "RNDRUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "RNDR-USD" },
{ "exchangeName": "Kraken", "ticker": "RNDRUSD" },
{ "exchangeName": "Kucoin", "ticker": "RNDR-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "RNDR-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "RNDR_USDT", "adjustByMarket": "USDT-USD" }
],
"RUNE": [
{ "exchangeName": "Binance", "ticker": "RUNEUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Gate", "ticker": "RUNE_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "RUNEUSD" },
{ "exchangeName": "Kucoin", "ticker": "RUNE-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "RUNE_USDT", "adjustByMarket": "USDT-USD" }
],
"SAND": [
{ "exchangeName": "Binance", "ticker": "SANDUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "SAND-USD" },
{ "exchangeName": "Gate", "ticker": "SAND_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kucoin", "ticker": "SAND-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "SAND-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "SAND_USDT", "adjustByMarket": "USDT-USD" }
],
"SEI": [
{ "exchangeName": "Binance", "ticker": "SEIUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "SEIUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "SEI-USD" },
{ "exchangeName": "Huobi", "ticker": "seiusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "SEIUSD" },
{ "exchangeName": "Kucoin", "ticker": "SEI-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "SEI_USDT", "adjustByMarket": "USDT-USD" }
],
"SHIB": [
{ "exchangeName": "Binance", "ticker": "SHIBUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "SHIBUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "SHIB-USD" },
{ "exchangeName": "Huobi", "ticker": "shibusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "SHIBUSD" },
{ "exchangeName": "Kucoin", "ticker": "SHIB-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "SHIB-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "SHIB_USDT", "adjustByMarket": "USDT-USD" }
],
"SNX": [
{ "exchangeName": "Binance", "ticker": "SNXUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "SNXUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "SNX-USD" },
{ "exchangeName": "Kraken", "ticker": "SNXUSD" },
{ "exchangeName": "Kucoin", "ticker": "SNX-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "SNX-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "SNX_USDT", "adjustByMarket": "USDT-USD" }
],
"SOL": [
{ "exchangeName": "Binance", "ticker": "SOLUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bitstamp", "ticker": "SOL/USD" },
{ "exchangeName": "Bybit", "ticker": "SOLUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "SOL-USD" },
{ "exchangeName": "Huobi", "ticker": "solusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "SOLUSD" },
{ "exchangeName": "Kucoin", "ticker": "SOL-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "SOL-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "SOL_USDT", "adjustByMarket": "USDT-USD" }
],
"STRK": [
{ "exchangeName": "Binance", "ticker": "STRKUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "STRKUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "STRKUSD" },
{ "exchangeName": "Kucoin", "ticker": "STRK-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "STRK-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Gate", "ticker": "STRK_USDT", "adjustByMarket": "USDT-USD" }
],
"STX": [
{ "exchangeName": "Binance", "ticker": "STXUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "STXUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "STX-USD" },
{ "exchangeName": "Gate", "ticker": "STX_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "STXUSD" },
{ "exchangeName": "Kucoin", "ticker": "STX-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "STX-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "STX_USDT", "adjustByMarket": "USDT-USD" }
],
"SUI": [
{ "exchangeName": "Binance", "ticker": "SUIUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "SUIUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "SUI-USD" },
{ "exchangeName": "Huobi", "ticker": "suiusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "SUIUSD" },
{ "exchangeName": "Kucoin", "ticker": "SUI-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "SUI-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "SUI_USDT", "adjustByMarket": "USDT-USD" }
],
"TIA": [
{ "exchangeName": "Binance", "ticker": "TIAUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "TIAUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "TIA-USD" },
{ "exchangeName": "Kraken", "ticker": "TIAUSD" },
{ "exchangeName": "Kucoin", "ticker": "TIA-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "TIA-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "TIA_USDT", "adjustByMarket": "USDT-USD" }
],
"TRX": [
{ "exchangeName": "Binance", "ticker": "TRXUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "TRXUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Gate", "ticker": "TRX_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Huobi", "ticker": "trxusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "TRXUSD" },
{ "exchangeName": "Kucoin", "ticker": "TRX-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "TRX-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "TRX_USDT", "adjustByMarket": "USDT-USD" }
],
"UNI": [
{ "exchangeName": "Binance", "ticker": "UNIUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "UNIUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "UNI-USD" },
{ "exchangeName": "Kraken", "ticker": "UNIUSD" },
{ "exchangeName": "Kucoin", "ticker": "UNI-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "UNI-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "UNI_USDT", "adjustByMarket": "USDT-USD" }
],
"WLD": [
{ "exchangeName": "Binance", "ticker": "WLDUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "WLDUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Gate", "ticker": "WLD_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kucoin", "ticker": "WLD-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "WLD-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "WLD_USDT", "adjustByMarket": "USDT-USD" }
],
"WOO": [
{ "exchangeName": "Binance", "ticker": "WOOUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Gate", "ticker": "WOO_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kucoin", "ticker": "WOO-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "WOO-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "WOO_USDT", "adjustByMarket": "USDT-USD" }
],
"XLM": [
{ "exchangeName": "Binance", "ticker": "XLMUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bitstamp", "ticker": "XLM/USD" },
{ "exchangeName": "Bybit", "ticker": "XLMUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "XLM-USD" },
{ "exchangeName": "Kraken", "ticker": "XLMUSD" },
{ "exchangeName": "Kucoin", "ticker": "XLM-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "XLM-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "XLM_USDT", "adjustByMarket": "USDT-USD" }
],
"XRP": [
{ "exchangeName": "Binance", "ticker": "XRPUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bitstamp", "ticker": "XRP/USD" },
{ "exchangeName": "Bybit", "ticker": "XRPUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "XRP-USD" },
{ "exchangeName": "Huobi", "ticker": "xrpusdt", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "XRPUSD" },
{ "exchangeName": "Kucoin", "ticker": "XRP-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "XRP-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "XRP_USDT", "adjustByMarket": "USDT-USD" }
],
"ZETA": [
{ "exchangeName": "Bybit", "ticker": "ZETAUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "CoinbasePro", "ticker": "ZETA-USD" },
{ "exchangeName": "Gate", "ticker": "ZETA_USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kucoin", "ticker": "ZETA-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "ZETA-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "ZETA_USDT", "adjustByMarket": "USDT-USD" }
]
}
File diff suppressed because it is too large Load Diff
+638
View File
@@ -0,0 +1,638 @@
{
"apps": {
"ios": {
"scheme": "dydx-t-v4"
}
},
"tokens": {
"dydxprotocol-testnet": {
"chain": {
"name": "Dv4TNT",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"decimals": 6,
"image": "/currencies/usdc.png"
}
},
"dydx-testnet-4": {
"chain": {
"name": "Dv4TNT",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"decimals": 6,
"image": "/currencies/usdc.png"
}
},
"[mainnet chain id]": {
"comment": "Change according to mainnet release",
"chain": {
"name": "TokenName",
"denom": "tokenDenom",
"decimals": 18,
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"decimals": 6,
"image": "/currencies/usdc.png"
}
}
},
"links": {
"dydxprotocol-testnet": {
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"statusPage": "https://status.v4testnet.dydx.exchange/",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"blogs": "https://www.dydx.foundation/blog",
"foundation": "https://www.dydx.foundation",
"help": "https://help.dydx.exchange/",
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
"documentation": "https://docs.dydx.exchange/",
"community": "https://discord.com/invite/dydx",
"governanceLearnMore": "https://help.dydx.exchange",
"newMarketProposalLearnMore": "https://dydx.exchange/blog/new-market-proposals",
"stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
},
"dydx-testnet-4": {
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"statusPage": "https://status.v4testnet.dydx.exchange/",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"documentation": "https://docs.dydx.exchange/",
"community": "https://discord.com/invite/dydx",
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
"blogs": "https://www.dydx.foundation/blog",
"foundation": "https://www.dydx.foundation",
"help": "https://help.dydx.exchange/",
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
"governanceLearnMore": "https://help.dydx.exchange",
"newMarketProposalLearnMore": "https://dydx.exchange/blog/new-market-proposals",
"stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
},
"[mainnet chain id]": {
"tos": "[HTTP link to TOS]",
"privacy": "[HTTP link to Privacy Policy]",
"statusPage": "[HTTP link to status page]",
"mintscan": "[HTTP link to Mintscan, with {tx_hash} placeholder]",
"mintscanBase": "[HTTP link to TOS mintscan base url]",
"feedback": "[HTTP link to feedback form, can be null]",
"blogs": "[HTTP link to blogs, can be null]",
"foundation": "[HTTP link to foundation, can be null]",
"reduceOnlyLearnMore": "[HTTP link to reduce-only learn more, can be null]",
"documentation": "[HTTP link to documentation, can be null]",
"community": "[HTTP link to community, can be null]",
"help": "[HTTP link to help page, can be null]",
"governanceLearnMore": "[HTTP link to governance learn more, can be null]",
"newMarketProposalLearnMore": "[HTTP link to new market proposal learn more, can be null]",
"stakingLearnMore": "[HTTP link to staking learn more, can be null]",
"keplrDashboard": "[HTTP link to keplr dashboard, can be null]",
"strideZoneApp": "[HTTP link to stride zone app, can be null]",
"accountExportLearnMore": "[HTTP link to account export learn more, can be null]",
"walletLearnMore": "[HTTP link to wallet learn more, can be null]"
}
},
"wallets": {
"dydxprotocol-testnet": {
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX Chain Onboarding",
"signTypedDataDomainName": "dYdX Chain"
},
"dydx-testnet-4": {
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX Chain Onboarding",
"signTypedDataDomainName": "dYdX Chain"
},
"[mainnet chain id]": {
"walletconnect": {
"client": {
"name": "[Name of the app]",
"description": "[Description of the app]",
"iconUrl": "[Relative URL of the icon URL]"
},
"v2": {
"projectId": "[Project ID]"
}
},
"walletSegue": {
"callbackUrl": "[Relative callback URL for WalletSegue, should match apple-app-site-association]"
},
"images": "[Relative URL for wallet images]",
"signTypedDataAction": "dYdX Chain Onboarding",
"signTypedDataDomainName": "dYdX Chain"
}
},
"governance": {
"dydxprotocol-testnet": {
"newMarketProposal": {
"initialDepositAmount": 10000000,
"delayBlocks": 900,
"newMarketsMethodology": "https://docs.google.com/spreadsheets/d/1zjkV9R7R_7KMItuzqzvKGwefSBRfE-ZNAx1LH55OcqY/edit?usp=sharing"
}
},
"dydx-testnet-4": {
"newMarketProposal": {
"initialDepositAmount": 10000000,
"delayBlocks": 900,
"newMarketsMethodology": "https://docs.google.com/spreadsheets/d/1zjkV9R7R_7KMItuzqzvKGwefSBRfE-ZNAx1LH55OcqY/edit?usp=sharing"
}
},
"[mainnet chain id]": {
"newMarketProposal": {
"initialDepositAmount": 0,
"delayBlocks": 0,
"newMarketsMethodology": "[URL to spreadsheet or document that explains methodology]"
}
}
},
"deployments": {
"MAINNET": {
"environments": [
"dydxprotocol-mainnet"
],
"default": "dydxprotocol-mainnet"
},
"TESTFLIGHT": {
"environments": [
"dydxprotocol-mainnet",
"dydxprotocol-testnet"
],
"default": "dydxprotocol-mainnet"
},
"TESTNET": {
"environments": [
"dydxprotocol-testnet"
],
"default": "dydxprotocol-testnet"
},
"DEV": {
"environments": [
"dydxprotocol-dev",
"dydxprotocol-dev-2",
"dydxprotocol-dev-4",
"dydxprotocol-dev-5",
"dydxprotocol-staging",
"dydxprotocol-staging-west",
"dydxprotocol-testnet",
"dydxprotocol-testnet-dydx",
"dydxprotocol-testnet-nodefleet",
"dydxprotocol-testnet-kingnodes",
"dydxprotocol-testnet-liquify",
"dydxprotocol-testnet-polkachu",
"dydxprotocol-testnet-bware"
],
"default": "dydxprotocol-testnet"
}
},
"environments": {
"dydxprotocol-dev": {
"name": "v4 Dev",
"ethereumChainId": "11155111",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4dev.dydx.exchange",
"socket": "wss://indexer.v4dev.dydx.exchange"
}
],
"validators": [
"https://validator.v4dev.dydx.exchange"
],
"0xsquid": "https://testnet.api.0xsquid.com",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4dev.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": true
}
},
"dydxprotocol-dev-2": {
"name": "v4 Dev 2",
"ethereumChainId": "11155111",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"endpoints": {
"indexers": [
{
"api": "http://dev2-indexer-apne1-lb-public-2076363889.ap-northeast-1.elb.amazonaws.com",
"socket": "ws://dev2-indexer-apne1-lb-public-2076363889.ap-northeast-1.elb.amazonaws.com"
}
],
"validators": [
"http://54.92.118.111"
],
"0xsquid": "https://testnet.api.0xsquid.com",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"featureFlags": {
"reduceOnlySupported": true
}
},
"dydxprotocol-dev-4": {
"name": "v4 Dev 4",
"ethereumChainId": "11155111",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4dev4.dydx.exchange",
"socket": "wss://indexer.v4dev4.dydx.exchange"
}
],
"validators": [
"https://validator.v4dev4.dydx.exchange"
],
"0xsquid": "https://testnet.api.0xsquid.com",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4dev4.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": true
}
},
"dydxprotocol-dev-5": {
"name": "v4 Dev 5",
"ethereumChainId": "11155111",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"endpoints": {
"indexers": [
{
"api": "http://dev5-indexer-apne1-lb-public-1721328151.ap-northeast-1.elb.amazonaws.com",
"socket": "ws://dev5-indexer-apne1-lb-public-1721328151.ap-northeast-1.elb.amazonaws.com"
}
],
"validators": [
"http://18.223.78.50"
],
"0xsquid": "https://testnet.api.0xsquid.com",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"featureFlags": {
"reduceOnlySupported": true
}
},
"dydxprotocol-staging": {
"name": "v4 Staging",
"ethereumChainId": "11155111",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4staging.dydx.exchange",
"socket": "wss://indexer.v4staging.dydx.exchange"
}
],
"faucet": "https://faucet.v4staging.dydx.exchange",
"validators": [
"https://validator.v4staging.dydx.exchange"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"featureFlags": {
"reduceOnlySupported": true
}
},
"dydxprotocol-staging-forced-update": {
"name": "v4 Staging Forced Update",
"ethereumChainId": "11155111",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4staging.dydx.exchange",
"socket": "wss://indexer.v4staging.dydx.exchange"
}
],
"faucet": "https://faucet.v4staging.dydx.exchange",
"validators": [
"https://validator.v4staging.dydx.exchange"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"apps": {
"ios": {
"minimalVersion": "1.0",
"build": 40000,
"url": "https://apps.apple.com/app/dydx/id1564787350"
}
},
"featureFlags": {
"reduceOnlySupported": true
}
},
"dydxprotocol-staging-west": {
"name": "v4 Staging West",
"ethereumChainId": "11155111",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4staging.dydx.exchange",
"socket": "wss://indexer.v4staging.dydx.exchange"
}
],
"faucet": "https://faucet.v4staging.dydx.exchange",
"validators": [
"https://validator-uswest1.v4staging.dydx.exchange"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"featureFlags": {
"reduceOnlySupported": true
}
},
"dydxprotocol-testnet": {
"name": "v4 Public Testnet",
"ethereumChainId": "11155111",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4testnet.dydx.exchange",
"socket": "wss://indexer.v4testnet.dydx.exchange"
}
],
"validators": [
"https://dydx-testnet-full-rpc.public.blastapi.io/",
"https://dydx-testnet-rpc.polkachu.com/",
"https://dydx-testnet.nodefleet.org",
"https://test-dydx.kingnodes.com",
"https://dydx-rpc.liquify.com/api=8878132/dydx"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": false
}
},
"dydxprotocol-testnet-dydx": {
"name": "v4 Public Testnet/dYdX",
"ethereumChainId": "11155111",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4testnet.dydx.exchange",
"socket": "wss://indexer.v4testnet.dydx.exchange"
}
],
"validators": [
"https://validator.v4testnet.dydx.exchange"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": false
}
},
"dydxprotocol-testnet-nodefleet": {
"name": "v4 Public Testnet/nodefleet",
"ethereumChainId": "11155111",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4testnet.dydx.exchange",
"socket": "wss://indexer.v4testnet.dydx.exchange"
}
],
"validators": [
"https://dydx-testnet.nodefleet.org"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": false
}
},
"dydxprotocol-testnet-kingnodes": {
"name": "v4 Public Testnet/KingNodes",
"ethereumChainId": "11155111",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4testnet.dydx.exchange",
"socket": "wss://indexer.v4testnet.dydx.exchange"
}
],
"validators": [
"https://test-dydx.kingnodes.com"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": false
}
},
"dydxprotocol-testnet-liquify": {
"name": "v4 Public Testnet/Liquify",
"ethereumChainId": "11155111",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4testnet.dydx.exchange",
"socket": "wss://indexer.v4testnet.dydx.exchange"
}
],
"validators": [
"https://dydx-rpc.liquify.com/api=8878132/dydx"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": false
}
},
"dydxprotocol-testnet-polkachu": {
"name": "v4 Public Testnet/Polkahcu",
"ethereumChainId": "11155111",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4testnet.dydx.exchange",
"socket": "wss://indexer.v4testnet.dydx.exchange"
}
],
"validators": [
"https://dydx-testnet-rpc.polkachu.com/"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": false
}
},
"dydxprotocol-testnet-bware": {
"name": "v4 Public Testnet/BWare",
"ethereumChainId": "11155111",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4testnet.dydx.exchange",
"socket": "wss://indexer.v4testnet.dydx.exchange"
}
],
"validators": [
"https://dydx-testnet-full-rpc.public.blastapi.io/"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": false
}
},
"dydxprotocol-mainnet": {
"name": "v4",
"ethereumChainId": "1",
"dydxChainId": "[mainnet chain id]",
"chainName": "dYdX Chain",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "[mainnet squid integrator id]",
"isMainNet": true,
"endpoints": {
"indexers": [
{
"api": "[REST endpoint]",
"socket": "[Websocket endpoint]"
}
],
"validators": [
"[Validator endpoint 1",
"[Validator endpoint n]"
],
"0xsquid": "[0xSquid endpoint for mainnet]",
"nobleValidator": "[noble validator endpoint for mainnet]"
},
"featureFlags": {
"reduceOnlySupported": false
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -14,7 +14,7 @@ if (AMPLITUDE_API_KEY) {
const html = await fs.readFile(htmlFilePath, 'utf-8'); const html = await fs.readFile(htmlFilePath, 'utf-8');
const amplitudeCdnScript = `<script type="text/javascript"> const amplitudeCdnScript = `<script type="text/javascript">
!function(){"use strict";!function(e,t){var n=e.amplitude||{_q:[],_iq:{}};if(n.invoked)e.console&&console.error&&console.error("Amplitude snippet has been loaded.");else{var r=function(e,t){e.prototype[t]=function(){return this._q.push({name:t,args:Array.prototype.slice.call(arguments,0)}),this}},s=function(e,t,n){return function(r){e._q.push({name:t,args:Array.prototype.slice.call(n,0),resolve:r})}},o=function(e,t,n){e[t]=function(){if(n)return{promise:new Promise(s(e,t,Array.prototype.slice.call(arguments)))}}},i=function(e){for(var t=0;t<m.length;t++)o(e,m[t],!1);for(var n=0;n<g.length;n++)o(e,g[n],!0)};n.invoked=!0;var u=t.createElement("script");u.type="text/javascript",u.integrity="sha384-x0ik2D45ZDEEEpYpEuDpmj05fY91P7EOZkgdKmq4dKL/ZAVcufJ+nULFtGn0HIZE",u.crossOrigin="anonymous",u.async=!0,u.src="https://cdn.amplitude.com/libs/analytics-browser-2.0.0-min.js.gz",u.onload=function(){e.amplitude.runQueuedFunctions||console.log("[Amplitude] Error: could not load SDK")};var a=t.getElementsByTagName("script")[0];a.parentNode.insertBefore(u,a);for(var c=function(){return this._q=[],this},p=["add","append","clearAll","prepend","set","setOnce","unset","preInsert","postInsert","remove","getUserProperties"],l=0;l<p.length;l++)r(c,p[l]);n.Identify=c;for(var d=function(){return this._q=[],this},f=["getEventProperties","setProductId","setQuantity","setPrice","setRevenue","setRevenueType","setEventProperties"],v=0;v<f.length;v++)r(d,f[v]);n.Revenue=d;var m=["getDeviceId","setDeviceId","getSessionId","setSessionId","getUserId","setUserId","setOptOut","setTransport","reset","extendSession"],g=["init","add","remove","track","logEvent","identify","groupIdentify","setGroup","revenue","flush"];i(n),n.createInstance=function(e){return n._iq[e]={_q:[]},i(n._iq[e]),n._iq[e]},e.amplitude=n}}(window,document)}(); !function(){"use strict";!function(e,t){var n=e.amplitude||{_q:[],_iq:{}};if(n.invoked)e.console&&console.error&&console.error("Amplitude snippet has been loaded.");else{var r=function(e,t){e.prototype[t]=function(){return this._q.push({name:t,args:Array.prototype.slice.call(arguments,0)}),this}},s=function(e,t,n){return function(r){e._q.push({name:t,args:Array.prototype.slice.call(n,0),resolve:r})}},o=function(e,t,n){e[t]=function(){if(n)return{promise:new Promise(s(e,t,Array.prototype.slice.call(arguments)))}}},i=function(e){for(var t=0;t<m.length;t++)o(e,m[t],!1);for(var n=0;n<g.length;n++)o(e,g[n],!0)};n.invoked=!0;var u=t.createElement("script");u.type="text/javascript",u.integrity="sha384-BVo5ZjsjH373rWbcjz9Qjb2L6BgLwLADcZtZZPu3nMl8+7LPDhi1NcUEf0Ate41Y",u.crossOrigin="anonymous",u.async=!0,u.src="/libs/amplitude-analytics-browser-2.0.0-min.js",u.onload=function(){e.amplitude.runQueuedFunctions||console.log("[Amplitude] Error: could not load SDK")};var a=t.getElementsByTagName("script")[0];a.parentNode.insertBefore(u,a);for(var c=function(){return this._q=[],this},p=["add","append","clearAll","prepend","set","setOnce","unset","preInsert","postInsert","remove","getUserProperties"],l=0;l<p.length;l++)r(c,p[l]);n.Identify=c;for(var d=function(){return this._q=[],this},f=["getEventProperties","setProductId","setQuantity","setPrice","setRevenue","setRevenueType","setEventProperties"],v=0;v<f.length;v++)r(d,f[v]);n.Revenue=d;var m=["getDeviceId","setDeviceId","getSessionId","setSessionId","getUserId","setUserId","setOptOut","setTransport","reset","extendSession"],g=["init","add","remove","track","logEvent","identify","groupIdentify","setGroup","revenue","flush"];i(n),n.createInstance=function(e){return n._iq[e]={_q:[]},i(n._iq[e]),n._iq[e]},e.amplitude=n}}(window,document)}();
</script> </script>
`; `;
+25
View File
@@ -8,6 +8,7 @@ const assetIcons = {
'1INCH': '/currencies/1inch.png', '1INCH': '/currencies/1inch.png',
AAVE: '/currencies/aave.png', AAVE: '/currencies/aave.png',
ADA: '/currencies/ada.png', ADA: '/currencies/ada.png',
AGIX: '/currencies/agix.png',
ALGO: '/currencies/algo.png', ALGO: '/currencies/algo.png',
APE: '/currencies/ape.png', APE: '/currencies/ape.png',
APT: '/currencies/apt.png', APT: '/currencies/apt.png',
@@ -16,35 +17,57 @@ const assetIcons = {
AVAX: '/currencies/avax.png', AVAX: '/currencies/avax.png',
BCH: '/currencies/bch.png', BCH: '/currencies/bch.png',
BLUR: '/currencies/blur.png', BLUR: '/currencies/blur.png',
BNB: '/currencies/bnb.png',
BONK: '/currencies/bonk.png', BONK: '/currencies/bonk.png',
BTC: '/currencies/btc.png', BTC: '/currencies/btc.png',
CELO: '/currencies/celo.png', CELO: '/currencies/celo.png',
CHZ: '/currencies/chz.png',
COMP: '/currencies/comp.png', COMP: '/currencies/comp.png',
CRV: '/currencies/crv.png', CRV: '/currencies/crv.png',
DAI: '/currencies/dai.png', DAI: '/currencies/dai.png',
DOGE: '/currencies/doge.png', DOGE: '/currencies/doge.png',
DOT: '/currencies/dot.png', DOT: '/currencies/dot.png',
DYDX: '/currencies/dydx.png', DYDX: '/currencies/dydx.png',
DYM: '/currencies/dym.png',
ENJ: '/currencies/enj.png', ENJ: '/currencies/enj.png',
ENS: '/currencies/ens.png',
EOS: '/currencies/eos.png', EOS: '/currencies/eos.png',
ETC: '/currencies/etc.png', ETC: '/currencies/etc.png',
ETH: '/currencies/eth.png', ETH: '/currencies/eth.png',
FET: '/currencies/fet.png',
FIL: '/currencies/fil.png', FIL: '/currencies/fil.png',
FTM: '/currencies/ftm.png',
GALA: "/currencies/gala.png",
GMT: "/currencies/gmt.png",
GRT: "/currencies/grt.png",
HBAR: "/currencies/hbar.png",
ICP: '/currencies/icp.png', ICP: '/currencies/icp.png',
IMX: '/currencies/imx.png',
INJ: '/currencies/inj.png',
JTO: '/currencies/jto.png',
JUP: '/currencies/jup.png', JUP: '/currencies/jup.png',
KAVA: '/currencies/kava.png',
LDO: '/currencies/ldo.png', LDO: '/currencies/ldo.png',
LINK: '/currencies/link.png', LINK: '/currencies/link.png',
LTC: '/currencies/ltc.png', LTC: '/currencies/ltc.png',
MANA: '/currencies/mana.png',
MATIC: '/currencies/matic.png', MATIC: '/currencies/matic.png',
MASK: '/currencies/mask.png',
MINA: '/currencies/mina.png',
MKR: '/currencies/mkr.png', MKR: '/currencies/mkr.png',
NEAR: '/currencies/near.png', NEAR: '/currencies/near.png',
ORDI: "/currencies/ordi.png",
OP: '/currencies/op.png', OP: '/currencies/op.png',
PEPE: '/currencies/pepe.png', PEPE: '/currencies/pepe.png',
PYTH: '/currencies/pyth.png',
RNDR: '/currencies/rndr.png',
RUNE: '/currencies/rune.png', RUNE: '/currencies/rune.png',
SAND: '/currencies/sand.png',
SEI: '/currencies/sei.png', SEI: '/currencies/sei.png',
SHIB: '/currencies/shib.png', SHIB: '/currencies/shib.png',
SNX: '/currencies/snx.png', SNX: '/currencies/snx.png',
SOL: '/currencies/sol.png', SOL: '/currencies/sol.png',
STX: '/currencies/stx.png',
SUI: '/currencies/sui.png', SUI: '/currencies/sui.png',
SUSHI: '/currencies/sushi.png', SUSHI: '/currencies/sushi.png',
TIA: '/currencies/tia.png', TIA: '/currencies/tia.png',
@@ -55,6 +78,7 @@ const assetIcons = {
USDT: '/currencies/usdt.png', USDT: '/currencies/usdt.png',
WBTC: '/currencies/wbtc.png', WBTC: '/currencies/wbtc.png',
WETH: '/currencies/weth.png', WETH: '/currencies/weth.png',
WOO: '/currencies/woo.png',
WLD: '/currencies/wld.png', WLD: '/currencies/wld.png',
XLM: '/currencies/xlm.png', XLM: '/currencies/xlm.png',
XMR: '/currencies/xmr.png', XMR: '/currencies/xmr.png',
@@ -62,6 +86,7 @@ const assetIcons = {
XTZ: '/currencies/xtz.png', XTZ: '/currencies/xtz.png',
YFI: '/currencies/yfi.png', YFI: '/currencies/yfi.png',
ZEC: '/currencies/zec.png', ZEC: '/currencies/zec.png',
ZETA: '/currencies/zeta.png',
ZRX: '/currencies/zrx.png', ZRX: '/currencies/zrx.png',
} as const; } as const;
+9 -5
View File
@@ -133,6 +133,7 @@ const detailsLayoutVariants = {
row: css` row: css`
${layoutMixins.row} ${layoutMixins.row}
align-self: stretch; align-self: stretch;
white-space: nowrap;
`, `,
rowColumns: css` rowColumns: css`
@@ -159,12 +160,16 @@ const itemLayoutVariants: Record<string, FlattenInterpolation<ThemeProps<any>>>
${layoutMixins.spacedRow} ${layoutMixins.spacedRow}
gap: 0.5rem; gap: 0.5rem;
align-items: start;
padding: 0.5rem 0;
min-height: var(--details-item-height); min-height: var(--details-item-height);
> :last-child { > :first-child > abbr {
align-self: stretch; min-width: auto;
}
> :last-child {
${layoutMixins.row} ${layoutMixins.row}
${layoutMixins.stickyRight} ${layoutMixins.stickyRight}
@@ -217,8 +222,6 @@ Styled.Details = styled.dl<{
--details-grid-numColumns: 2; --details-grid-numColumns: 2;
${({ layout }) => layout && detailsLayoutVariants[layout]} ${({ layout }) => layout && detailsLayoutVariants[layout]}
white-space: nowrap;
`; `;
Styled.Item = styled.div<{ Styled.Item = styled.div<{
@@ -231,7 +234,7 @@ Styled.Item = styled.div<{
${({ justifyItems }) => ${({ justifyItems }) =>
justifyItems === 'end' && justifyItems === 'end' &&
css` css`
&:nth-child(even) { > :nth-child(even) {
justify-items: end; justify-items: end;
text-align: end; text-align: end;
} }
@@ -243,6 +246,7 @@ Styled.Item = styled.div<{
{ {
column: css` column: css`
&:not(:hover) > :first-child { &:not(:hover) > :first-child {
white-space: nowrap;
overflow-x: hidden; overflow-x: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
+1 -1
View File
@@ -26,7 +26,7 @@ export const DetailsDialog = ({ slotIcon, title, items, slotFooter, setIsOpen }:
placement={isTablet ? DialogPlacement.Default : DialogPlacement.Sidebar} placement={isTablet ? DialogPlacement.Default : DialogPlacement.Sidebar}
> >
<Styled.Content> <Styled.Content>
<Styled.Details withSeparators items={items} /> <Styled.Details withSeparators justifyItems="end" items={items} />
<Styled.Footer>{slotFooter}</Styled.Footer> <Styled.Footer>{slotFooter}</Styled.Footer>
</Styled.Content> </Styled.Content>
+5 -3
View File
@@ -39,7 +39,7 @@ export const SearchSelectMenu = ({
disabled, disabled,
label, label,
items, items,
withSearch, withSearch = true,
withReceiptItems, withReceiptItems,
}: SearchSelectMenuProps) => { }: SearchSelectMenuProps) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -77,6 +77,7 @@ export const SearchSelectMenu = ({
withSearch={withSearch} withSearch={withSearch}
onItemSelected={() => setOpen(false)} onItemSelected={() => setOpen(false)}
withStickyLayout withStickyLayout
$withSearch={withSearch}
/> />
</Styled.Popover> </Styled.Popover>
</Styled.WithDetailsReceipt> </Styled.WithDetailsReceipt>
@@ -127,7 +128,7 @@ Styled.Popover = styled(Popover)`
box-shadow: none; box-shadow: none;
`; `;
Styled.ComboboxMenu = styled(ComboboxMenu)` Styled.ComboboxMenu = styled(ComboboxMenu)<{ $withSearch?: boolean }>`
${layoutMixins.withInnerHorizontalBorders} ${layoutMixins.withInnerHorizontalBorders}
--comboboxMenu-backgroundColor: var(--color-layer-4); --comboboxMenu-backgroundColor: var(--color-layer-4);
@@ -140,7 +141,8 @@ Styled.ComboboxMenu = styled(ComboboxMenu)`
--comboboxMenu-item-checked-textColor: var(--color-text-2); --comboboxMenu-item-checked-textColor: var(--color-text-2);
--comboboxMenu-item-highlighted-textColor: var(--color-text-2); --comboboxMenu-item-highlighted-textColor: var(--color-text-2);
--stickyArea1-topHeight: var(--form-input-height); --stickyArea1-topHeight: ${({ $withSearch }) =>
!$withSearch ? '0' : 'var(--form-input-height)'};
input:focus-visible { input:focus-visible {
outline: none; outline: none;
+5
View File
@@ -33,4 +33,9 @@ TagStory.argTypes = {
control: { type: 'select' }, control: { type: 'select' },
defaultValue: undefined, defaultValue: undefined,
}, },
isHighlighted: {
options: [true, false],
control: { type: 'select' },
defaultValue: false,
},
}; };
+1
View File
@@ -76,5 +76,6 @@ export const Tag = styled.span<StyleProps>`
isHighlighted && isHighlighted &&
css` css`
background-color: var(--color-accent); background-color: var(--color-accent);
color: var(--color-text-button);
`} `}
`; `;
+8 -1
View File
@@ -1,4 +1,4 @@
import { type ReactNode, useState } from 'react'; import { type ReactNode, useState, useEffect } from 'react';
import { ButtonAction, ButtonState } from '@/constants/buttons'; import { ButtonAction, ButtonState } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
@@ -8,6 +8,7 @@ import { Button, type ButtonStateConfig, type ButtonProps } from '@/components/B
type ElementProps = { type ElementProps = {
timeoutInSeconds: number; timeoutInSeconds: number;
onTimeOut?: () => void;
slotFinal?: ReactNode; slotFinal?: ReactNode;
} & ButtonProps; } & ButtonProps;
@@ -16,6 +17,7 @@ export type TimeoutButtonProps = ElementProps;
export const TimeoutButton = ({ export const TimeoutButton = ({
children, children,
timeoutInSeconds, timeoutInSeconds,
onTimeOut,
slotFinal, slotFinal,
...otherProps ...otherProps
}: TimeoutButtonProps) => { }: TimeoutButtonProps) => {
@@ -25,6 +27,11 @@ export const TimeoutButton = ({
const secondsLeft = Math.max(0, (timeoutDeadline - now) / 1000); const secondsLeft = Math.max(0, (timeoutDeadline - now) / 1000);
useEffect(() => {
if (secondsLeft > 0) return;
onTimeOut?.();
}, [secondsLeft]);
if (slotFinal && secondsLeft <= 0) return slotFinal; if (slotFinal && secondsLeft <= 0) return slotFinal;
return ( return (
+10 -2
View File
@@ -144,9 +144,17 @@ export type AnalyticsEventData<T extends AnalyticsEvent> =
validatorUrl: string; validatorUrl: string;
} }
: T extends AnalyticsEvent.TransferDeposit : T extends AnalyticsEvent.TransferDeposit
? {} ? {
chainId?: string;
tokenAddress?: string;
tokenSymbol?: string;
}
: T extends AnalyticsEvent.TransferWithdraw : T extends AnalyticsEvent.TransferWithdraw
? {} ? {
chainId?: string;
tokenAddress?: string;
tokenSymbol?: string;
}
: // Trading : // Trading
T extends AnalyticsEvent.TradeOrderTypeSelected T extends AnalyticsEvent.TradeOrderTypeSelected
? { ? {
+6 -1
View File
@@ -1,4 +1,4 @@
import environments from '../../public/configs/env.json'; import environments from '../../public/configs/v1/env.json';
const CURRENT_MODE = ({ const CURRENT_MODE = ({
production: 'MAINNET', production: 'MAINNET',
@@ -14,5 +14,10 @@ export const isDev = CURRENT_MODE === 'DEV';
export const AVAILABLE_ENVIRONMENTS = environments.deployments[CURRENT_MODE]; export const AVAILABLE_ENVIRONMENTS = environments.deployments[CURRENT_MODE];
export const CURRENT_ABACUS_DEPLOYMENT = CURRENT_MODE; export const CURRENT_ABACUS_DEPLOYMENT = CURRENT_MODE;
export const ENVIRONMENT_CONFIG_MAP = environments.environments; export const ENVIRONMENT_CONFIG_MAP = environments.environments;
export const TOKEN_CONFIG_MAP = environments.tokens;
export const LINKS_CONFIG_MAP = environments.links;
export const WALLETS_CONFIG_MAP = environments.wallets;
export const GOVERNANCE_CONFIG_MAP = environments.governance;
export type DydxNetwork = keyof typeof ENVIRONMENT_CONFIG_MAP; export type DydxNetwork = keyof typeof ENVIRONMENT_CONFIG_MAP;
export type DydxChainId = keyof typeof TOKEN_CONFIG_MAP;
export const DEFAULT_APP_ENVIRONMENT = AVAILABLE_ENVIRONMENTS.default as DydxNetwork; export const DEFAULT_APP_ENVIRONMENT = AVAILABLE_ENVIRONMENTS.default as DydxNetwork;
+1
View File
@@ -140,6 +140,7 @@ export type TransferNotifcation = {
isCctp?: boolean; isCctp?: boolean;
errorCount?: number; errorCount?: number;
status?: StatusResponse; status?: StatusResponse;
isExchange?: boolean;
}; };
/** /**
+1
View File
@@ -22,4 +22,5 @@ export enum NumberSign {
// Deposit/Withdraw // Deposit/Withdraw
export const MAX_CCTP_TRANSFER_AMOUNT = 1_000_000; export const MAX_CCTP_TRANSFER_AMOUNT = 1_000_000;
export const MIN_CCTP_TRANSFER_AMOUNT = 10;
export const MAX_PRICE_IMPACT = 0.02; // 2% export const MAX_PRICE_IMPACT = 0.02; // 2%
+9 -1
View File
@@ -1,4 +1,12 @@
import { AppColorMode } from '@/state/configs'; import type { ThemeName } from 'public/tradingview/charting_library';
import { AppColorMode, AppTheme } from '@/state/configs';
export const THEME_NAMES: Record<AppTheme, ThemeName> = {
[AppTheme.Classic]: 'Classic',
[AppTheme.Dark]: 'Dark',
[AppTheme.Light]: 'Light',
};
export type Theme = { export type Theme = {
[AppColorMode.GreenUp]: ThemeColorBase; [AppColorMode.GreenUp]: ThemeColorBase;
+54 -20
View File
@@ -4,7 +4,6 @@ import { AlertType } from '@/constants/alerts';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { TimeUnitShort } from '@/constants/time'; import { TimeUnitShort } from '@/constants/time';
// TODO: rename to OrderType
export enum TradeTypes { export enum TradeTypes {
MARKET = 'MARKET', MARKET = 'MARKET',
LIMIT = 'LIMIT', LIMIT = 'LIMIT',
@@ -15,6 +14,16 @@ export enum TradeTypes {
TRAILING_STOP = 'TRAILING_STOP', TRAILING_STOP = 'TRAILING_STOP',
} }
enum ClosingTradeTypes {
LIQUIDATED = 'LIQUIDATED',
LIQUIDATION = 'LIQUIDATION',
OFFSETTING = 'OFFSETTING',
DELEVERAGED = 'DELEVERAGED',
FINAL_SETTLEMENT = 'FINAL_SETTLEMENT',
}
export type OrderType = TradeTypes | ClosingTradeTypes;
export enum TimeInForceOptions { export enum TimeInForceOptions {
GTT = 'GTT', GTT = 'GTT',
FOK = 'FOK', FOK = 'FOK',
@@ -40,49 +49,74 @@ export const POSITION_SIDE_STRINGS: Record<PositionSide, string> = {
[PositionSide.Short]: STRING_KEYS.SHORT_POSITION_SHORT, [PositionSide.Short]: STRING_KEYS.SHORT_POSITION_SHORT,
}; };
export const TRADE_TYPE_STRINGS: Record< export const ORDER_TYPE_STRINGS: Record<
TradeTypes, OrderType,
{ {
tradeTypeKeyShort: string; orderTypeKeyShort: string;
tradeTypeKey: string; orderTypeKey: string;
descriptionKey: string; descriptionKey: string | null;
} }
> = { > = {
[TradeTypes.LIMIT]: { [TradeTypes.LIMIT]: {
tradeTypeKeyShort: STRING_KEYS.LIMIT_ORDER_SHORT, orderTypeKeyShort: STRING_KEYS.LIMIT_ORDER_SHORT,
tradeTypeKey: STRING_KEYS.LIMIT_ORDER, orderTypeKey: STRING_KEYS.LIMIT_ORDER,
descriptionKey: STRING_KEYS.LIMIT_ORDER_DESCRIPTION, descriptionKey: STRING_KEYS.LIMIT_ORDER_DESCRIPTION,
}, },
[TradeTypes.MARKET]: { [TradeTypes.MARKET]: {
tradeTypeKeyShort: STRING_KEYS.MARKET_ORDER_SHORT, orderTypeKeyShort: STRING_KEYS.MARKET_ORDER_SHORT,
tradeTypeKey: STRING_KEYS.MARKET_ORDER, orderTypeKey: STRING_KEYS.MARKET_ORDER,
descriptionKey: STRING_KEYS.MARKET_ORDER_DESCRIPTION, descriptionKey: STRING_KEYS.MARKET_ORDER_DESCRIPTION,
}, },
[TradeTypes.STOP_LIMIT]: { [TradeTypes.STOP_LIMIT]: {
tradeTypeKeyShort: STRING_KEYS.STOP_LIMIT, orderTypeKeyShort: STRING_KEYS.STOP_LIMIT,
tradeTypeKey: STRING_KEYS.STOP_LIMIT, orderTypeKey: STRING_KEYS.STOP_LIMIT,
descriptionKey: STRING_KEYS.STOP_LIMIT_DESCRIPTION, descriptionKey: STRING_KEYS.STOP_LIMIT_DESCRIPTION,
}, },
[TradeTypes.STOP_MARKET]: { [TradeTypes.STOP_MARKET]: {
tradeTypeKeyShort: STRING_KEYS.STOP_MARKET, orderTypeKeyShort: STRING_KEYS.STOP_MARKET,
tradeTypeKey: STRING_KEYS.STOP_MARKET, orderTypeKey: STRING_KEYS.STOP_MARKET,
descriptionKey: STRING_KEYS.STOP_MARKET_DESCRIPTION, descriptionKey: STRING_KEYS.STOP_MARKET_DESCRIPTION,
}, },
[TradeTypes.TAKE_PROFIT]: { [TradeTypes.TAKE_PROFIT]: {
tradeTypeKeyShort: STRING_KEYS.TAKE_PROFIT_LIMIT, orderTypeKeyShort: STRING_KEYS.TAKE_PROFIT_LIMIT,
tradeTypeKey: STRING_KEYS.TAKE_PROFIT_LIMIT, orderTypeKey: STRING_KEYS.TAKE_PROFIT_LIMIT,
descriptionKey: STRING_KEYS.TAKE_PROFIT_LIMIT_DESCRIPTION, descriptionKey: STRING_KEYS.TAKE_PROFIT_LIMIT_DESCRIPTION,
}, },
[TradeTypes.TAKE_PROFIT_MARKET]: { [TradeTypes.TAKE_PROFIT_MARKET]: {
tradeTypeKeyShort: STRING_KEYS.TAKE_PROFIT_MARKET, orderTypeKeyShort: STRING_KEYS.TAKE_PROFIT_MARKET,
tradeTypeKey: STRING_KEYS.TAKE_PROFIT_MARKET, orderTypeKey: STRING_KEYS.TAKE_PROFIT_MARKET,
descriptionKey: STRING_KEYS.TAKE_PROFIT_MARKET_DESCRIPTION, descriptionKey: STRING_KEYS.TAKE_PROFIT_MARKET_DESCRIPTION,
}, },
[TradeTypes.TRAILING_STOP]: { [TradeTypes.TRAILING_STOP]: {
tradeTypeKeyShort: STRING_KEYS.TRAILING_STOP, orderTypeKeyShort: STRING_KEYS.TRAILING_STOP,
tradeTypeKey: STRING_KEYS.TRAILING_STOP, orderTypeKey: STRING_KEYS.TRAILING_STOP,
descriptionKey: STRING_KEYS.TRAILING_STOP_DESCRIPTION, descriptionKey: STRING_KEYS.TRAILING_STOP_DESCRIPTION,
}, },
[ClosingTradeTypes.LIQUIDATED]: {
orderTypeKeyShort: STRING_KEYS.LIQUIDATED,
orderTypeKey: STRING_KEYS.LIQUIDATED,
descriptionKey: null,
},
[ClosingTradeTypes.LIQUIDATION]: {
orderTypeKeyShort: STRING_KEYS.LIQUIDATION,
orderTypeKey: STRING_KEYS.LIQUIDATION,
descriptionKey: null,
},
[ClosingTradeTypes.OFFSETTING]: {
orderTypeKeyShort: STRING_KEYS.OFFSETTING,
orderTypeKey: STRING_KEYS.OFFSETTING,
descriptionKey: null,
},
[ClosingTradeTypes.DELEVERAGED]: {
orderTypeKeyShort: STRING_KEYS.DELEVERAGED,
orderTypeKey: STRING_KEYS.DELEVERAGED,
descriptionKey: null,
},
[ClosingTradeTypes.FINAL_SETTLEMENT]: {
orderTypeKeyShort: STRING_KEYS.FINAL_SETTLEMENT,
orderTypeKey: STRING_KEYS.FINAL_SETTLEMENT,
descriptionKey: null,
},
}; };
export const GOOD_TIL_TIME_TIMESCALE_STRINGS: Record<TimeUnitShort, string> = { export const GOOD_TIL_TIME_TIMESCALE_STRINGS: Record<TimeUnitShort, string> = {
+16
View File
@@ -0,0 +1,16 @@
import { OrderSide } from '@dydxprotocol/v4-client-js';
import type {
IChartingLibraryWidget,
IOrderLineAdapter,
IPositionLineAdapter,
} from 'public/tradingview/charting_library';
export type TvWidget = IChartingLibraryWidget & { _id?: string; _ready?: boolean };
export type ChartLineType = OrderSide | 'position';
export type ChartLine = {
line: IOrderLineAdapter | IPositionLineAdapter;
chartLineType: ChartLineType;
};
+5 -4
View File
@@ -24,7 +24,7 @@ import {
import { isMetaMask } from '@/lib/wallet/providers'; import { isMetaMask } from '@/lib/wallet/providers';
import { DydxNetwork, ENVIRONMENT_CONFIG_MAP } from './networks'; import { DydxChainId, WALLETS_CONFIG_MAP } from './networks';
// Wallet connection types // Wallet connection types
@@ -40,6 +40,7 @@ export enum WalletErrorType {
// General // General
ChainMismatch, ChainMismatch,
UserCanceled, UserCanceled,
SwitchChainMethodMissing,
// Non-Deterministic // Non-Deterministic
NonDeterministicWallet, NonDeterministicWallet,
@@ -290,17 +291,17 @@ export const COSMOS_DERIVATION_PATH = "m/44'/118'/0'/0/0";
/** /**
* @description typed data to sign for dYdX Chain onboarding * @description typed data to sign for dYdX Chain onboarding
*/ */
export const getSignTypedData = (selectedNetwork: DydxNetwork) => export const getSignTypedData = (selectedDydxChainId: DydxChainId) =>
({ ({
primaryType: 'dYdX', primaryType: 'dYdX',
domain: { domain: {
name: ENVIRONMENT_CONFIG_MAP[selectedNetwork].wallets.signTypedDataDomainName, name: WALLETS_CONFIG_MAP[selectedDydxChainId].signTypedDataDomainName,
}, },
types: { types: {
dYdX: [{ name: 'action', type: 'string' }], dYdX: [{ name: 'action', type: 'string' }],
}, },
message: { message: {
action: ENVIRONMENT_CONFIG_MAP[selectedNetwork].wallets.signTypedDataAction, action: WALLETS_CONFIG_MAP[selectedDydxChainId].signTypedDataAction,
}, },
} as const); } as const);
+2
View File
@@ -1,2 +1,4 @@
export { useChartLines } from './useChartLines';
export { useChartMarketAndResolution } from './useChartMarketAndResolution';
export { useTradingView } from './useTradingView'; export { useTradingView } from './useTradingView';
export { useTradingViewTheme } from './useTradingViewTheme'; export { useTradingViewTheme } from './useTradingViewTheme';
+209
View File
@@ -0,0 +1,209 @@
import { useEffect, useState } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import { AbacusOrderStatus, ORDER_SIDES, SubaccountOrder } from '@/constants/abacus';
import { STRING_KEYS } from '@/constants/localization';
import { type OrderType, ORDER_TYPE_STRINGS } from '@/constants/trade';
import type { ChartLine, TvWidget } from '@/constants/tvchart';
import { useStringGetter } from '@/hooks';
import { getCurrentMarketOrders, getCurrentMarketPositionData } from '@/state/accountSelectors';
import { getAppTheme, getAppColorMode } from '@/state/configsSelectors';
import { MustBigNumber } from '@/lib/numbers';
import { getChartLineColors } from '@/lib/tradingView/utils';
let chartLines: Record<string, ChartLine> = {};
/**
* @description Hook to handle drawing chart lines
*/
export const useChartLines = ({
tvWidget,
displayButton,
isChartReady,
}: {
tvWidget: TvWidget | null;
displayButton: HTMLElement | null;
isChartReady?: boolean;
}) => {
const [showOrderLines, setShowOrderLines] = useState(false);
const stringGetter = useStringGetter();
const appTheme = useSelector(getAppTheme);
const appColorMode = useSelector(getAppColorMode);
const currentMarketPositionData = useSelector(getCurrentMarketPositionData, shallowEqual);
const currentMarketOrders: SubaccountOrder[] = useSelector(getCurrentMarketOrders, shallowEqual);
useEffect(() => {
if (isChartReady && displayButton) {
displayButton.onclick = () => {
const newShowOrderLinesState = !showOrderLines;
if (newShowOrderLinesState) {
displayButton?.classList?.add('order-lines-active');
} else {
displayButton?.classList?.remove('order-lines-active');
}
setShowOrderLines(newShowOrderLinesState);
};
}
}, [isChartReady, showOrderLines]);
useEffect(() => {
if (tvWidget && isChartReady) {
tvWidget.onChartReady(() => {
tvWidget.chart().dataReady(() => {
if (showOrderLines) {
drawOrderLines();
drawPositionLine();
} else {
deleteChartLines();
}
});
});
}
}, [isChartReady, showOrderLines, currentMarketPositionData, currentMarketOrders]);
const drawPositionLine = () => {
if (!currentMarketPositionData) return;
const entryPrice = currentMarketPositionData.entryPrice?.current;
const size = currentMarketPositionData.size?.current;
const key = currentMarketPositionData.id;
const price = MustBigNumber(entryPrice).toNumber();
const maybePositionLine = chartLines[key]?.line;
const shouldShow = size && size !== 0;
if (!shouldShow) {
if (maybePositionLine) {
maybePositionLine.remove();
delete chartLines[key];
return;
}
} else {
const quantity = size.toString();
if (maybePositionLine) {
if (maybePositionLine.getQuantity() !== quantity) {
maybePositionLine.setQuantity(quantity);
}
if (maybePositionLine.getPrice() !== price) {
maybePositionLine.setPrice(price);
}
} else {
const positionLine = tvWidget
?.chart()
.createPositionLine({ disableUndo: false })
.setText(stringGetter({ key: STRING_KEYS.ENTRY_PRICE_SHORT }))
.setPrice(price)
.setQuantity(quantity);
if (positionLine) {
const chartLine = { line: positionLine, chartLineType: 'position' };
setLineColors({ chartLine: chartLine });
chartLines[key] = chartLine;
}
}
}
};
const drawOrderLines = () => {
if (!currentMarketOrders) return;
currentMarketOrders.forEach(
({
id,
type,
status,
side,
cancelReason,
remainingSize,
size,
triggerPrice,
price,
trailingPercent,
}) => {
const key = id;
const quantity = (remainingSize ?? size).toString();
const orderType = type.rawValue as OrderType;
const orderLabel = stringGetter({
key: ORDER_TYPE_STRINGS[orderType].orderTypeKey,
});
const orderString = trailingPercent ? `${orderLabel} ${trailingPercent}%` : orderLabel;
const shouldShow =
!cancelReason &&
(status === AbacusOrderStatus.open || status === AbacusOrderStatus.untriggered);
const maybeOrderLine = chartLines[key]?.line;
if (!shouldShow) {
if (maybeOrderLine) {
maybeOrderLine.remove();
delete chartLines[key];
return;
}
} else {
if (maybeOrderLine) {
if (maybeOrderLine.getQuantity() !== quantity) {
maybeOrderLine.setQuantity(quantity);
}
} else {
const orderLine = tvWidget
?.chart()
.createOrderLine({ disableUndo: false })
.setPrice(MustBigNumber(triggerPrice ?? price).toNumber())
.setQuantity(quantity)
.setText(orderString);
if (orderLine) {
const chartLine: ChartLine = {
line: orderLine,
chartLineType: ORDER_SIDES[side.name],
};
setLineColors({ chartLine: chartLine });
chartLines[key] = chartLine;
}
}
}
}
);
};
const deleteChartLines = () => {
Object.values(chartLines).forEach(({ line }) => {
line.remove();
});
chartLines = {};
};
const setLineColors = ({ chartLine }: { chartLine: ChartLine }) => {
const { line, chartLineType } = chartLine;
const { maybeQuantityColor, borderColor, backgroundColor, textColor, textButtonColor } =
getChartLineColors({
appTheme,
appColorMode,
chartLineType,
});
line
.setQuantityBorderColor(borderColor)
.setBodyBackgroundColor(backgroundColor)
.setBodyBorderColor(borderColor)
.setBodyTextColor(textColor)
.setQuantityTextColor(textButtonColor);
maybeQuantityColor &&
line.setLineColor(maybeQuantityColor).setQuantityBackgroundColor(maybeQuantityColor);
};
return { chartLines };
};
@@ -0,0 +1,72 @@
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import type { ResolutionString } from 'public/tradingview/charting_library';
import { DEFAULT_RESOLUTION, RESOLUTION_CHART_CONFIGS } from '@/constants/candles';
import { DEFAULT_MARKETID } from '@/constants/markets';
import type { TvWidget } from '@/constants/tvchart';
import { setTvChartResolution } from '@/state/perpetuals';
import { getCurrentMarketId, getSelectedResolutionForMarket } from '@/state/perpetualsSelectors';
/**
* @description Hook to handle changing markets and setting chart resolution
*/
export const useChartMarketAndResolution = ({
tvWidget,
isWidgetReady,
savedResolution,
}: {
tvWidget: TvWidget | null;
isWidgetReady?: boolean;
savedResolution?: ResolutionString;
}) => {
const dispatch = useDispatch();
const currentMarketId: string = useSelector(getCurrentMarketId) || DEFAULT_MARKETID;
const selectedResolution: string =
useSelector(getSelectedResolutionForMarket(currentMarketId)) || DEFAULT_RESOLUTION;
const chart = isWidgetReady ? tvWidget?.chart() : undefined;
const chartResolution = chart?.resolution?.();
/**
* @description Hook to handle changing markets - intentionally should avoid triggering on change of resolutions.
*/
useEffect(() => {
if (currentMarketId && isWidgetReady) {
const resolution = savedResolution || selectedResolution;
tvWidget?.setSymbol(currentMarketId, resolution as ResolutionString, () => {});
}
}, [currentMarketId, isWidgetReady]);
/**
* @description Hook to handle changing chart resolution
*/
useEffect(() => {
if (chartResolution) {
if (chartResolution !== selectedResolution) {
dispatch(setTvChartResolution({ marketId: currentMarketId, resolution: chartResolution }));
}
setVisibleRangeForResolution({ resolution: chartResolution });
}
}, [currentMarketId, chartResolution, selectedResolution]);
const setVisibleRangeForResolution = ({ resolution }: { resolution: ResolutionString }) => {
// Different resolutions have different timeframes to display data efficiently.
const { defaultRange } = RESOLUTION_CHART_CONFIGS[resolution];
// from/to values converted to epoch seconds
const newRange = {
from: (Date.now() - defaultRange) / 1000,
to: Date.now() / 1000,
};
tvWidget?.activeChart().setVisibleRange(newRange, { percentRightMargin: 10 });
};
};
+30 -7
View File
@@ -1,15 +1,19 @@
import { useEffect } from 'react'; import React, { useEffect } from 'react';
import { shallowEqual, useSelector } from 'react-redux'; import { shallowEqual, useSelector } from 'react-redux';
import isEmpty from 'lodash/isEmpty'; import isEmpty from 'lodash/isEmpty';
import { LanguageCode, ResolutionString, widget } from 'public/tradingview/charting_library'; import { LanguageCode, ResolutionString, widget } from 'public/tradingview/charting_library';
import { DEFAULT_RESOLUTION } from '@/constants/candles'; import { DEFAULT_RESOLUTION } from '@/constants/candles';
import { SUPPORTED_LOCALE_BASE_TAGS } from '@/constants/localization'; import { SUPPORTED_LOCALE_BASE_TAGS, STRING_KEYS } from '@/constants/localization';
import { LocalStorageKey } from '@/constants/localStorage'; import { LocalStorageKey } from '@/constants/localStorage';
import { useDydxClient, useLocalStorage } from '@/hooks'; import type { TvWidget } from '@/constants/tvchart';
import { store } from '@/state/_store';
import { useDydxClient, useLocalStorage, useStringGetter } from '@/hooks';
import { store } from '@/state/_store';
import { getSelectedNetwork } from '@/state/appSelectors'; import { getSelectedNetwork } from '@/state/appSelectors';
import { getAppTheme, getAppColorMode } from '@/state/configsSelectors'; import { getAppTheme, getAppColorMode } from '@/state/configsSelectors';
import { getSelectedLocale } from '@/state/localizationSelectors'; import { getSelectedLocale } from '@/state/localizationSelectors';
@@ -23,14 +27,19 @@ import { getSavedResolution, getWidgetOptions, getWidgetOverrides } from '@/lib/
*/ */
export const useTradingView = ({ export const useTradingView = ({
tvWidgetRef, tvWidgetRef,
displayButtonRef,
setIsChartReady, setIsChartReady,
}: { }: {
tvWidgetRef: React.MutableRefObject<any>; tvWidgetRef: React.MutableRefObject<TvWidget | null>;
displayButtonRef: React.MutableRefObject<HTMLElement | null>;
setIsChartReady: React.Dispatch<React.SetStateAction<boolean>>; setIsChartReady: React.Dispatch<React.SetStateAction<boolean>>;
}) => { }) => {
const marketId = useSelector(getCurrentMarketId); const stringGetter = useStringGetter();
const appTheme = useSelector(getAppTheme); const appTheme = useSelector(getAppTheme);
const appColorMode = useSelector(getAppColorMode); const appColorMode = useSelector(getAppColorMode);
const marketId = useSelector(getCurrentMarketId);
const marketIds = useSelector(getMarketIds, shallowEqual); const marketIds = useSelector(getMarketIds, shallowEqual);
const selectedLocale = useSelector(getSelectedLocale); const selectedLocale = useSelector(getSelectedLocale);
const selectedNetwork = useSelector(getSelectedNetwork); const selectedNetwork = useSelector(getSelectedNetwork);
@@ -49,7 +58,6 @@ export const useTradingView = ({
const widgetOptions = getWidgetOptions(); const widgetOptions = getWidgetOptions();
const widgetOverrides = getWidgetOverrides({ appTheme, appColorMode }); const widgetOverrides = getWidgetOverrides({ appTheme, appColorMode });
const options = { const options = {
// debug: true,
...widgetOptions, ...widgetOptions,
...widgetOverrides, ...widgetOverrides,
datafeed: getDydxDatafeed(store, getCandlesForDatafeed), datafeed: getDydxDatafeed(store, getCandlesForDatafeed),
@@ -63,6 +71,19 @@ export const useTradingView = ({
tvWidgetRef.current = tvChartWidget; tvWidgetRef.current = tvChartWidget;
tvWidgetRef.current.onChartReady(() => { tvWidgetRef.current.onChartReady(() => {
tvWidgetRef.current?.headerReady().then(() => {
if (displayButtonRef && tvWidgetRef.current) {
displayButtonRef.current = tvWidgetRef.current.createButton();
displayButtonRef.current.innerHTML = `<span>${stringGetter({
key: STRING_KEYS.ORDER_LINES,
})}</span> <div class="displayOrdersButton-toggle"></div>`;
displayButtonRef.current.setAttribute(
'title',
stringGetter({ key: STRING_KEYS.ORDER_LINES_TOOLTIP })
);
}
});
tvWidgetRef?.current?.subscribe('onAutoSaveNeeded', () => tvWidgetRef?.current?.subscribe('onAutoSaveNeeded', () =>
tvWidgetRef?.current?.save((chartConfig: object) => setTvChartConfig(chartConfig)) tvWidgetRef?.current?.save((chartConfig: object) => setTvChartConfig(chartConfig))
); );
@@ -72,6 +93,8 @@ export const useTradingView = ({
} }
return () => { return () => {
displayButtonRef.current?.remove();
displayButtonRef.current = null;
tvWidgetRef.current?.remove(); tvWidgetRef.current?.remove();
tvWidgetRef.current = null; tvWidgetRef.current = null;
setIsChartReady(false); setIsChartReady(false);
+56 -35
View File
@@ -1,12 +1,14 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import type { IChartingLibraryWidget, ThemeName } from 'public/tradingview/charting_library'; import { THEME_NAMES } from '@/constants/styles/colors';
import type { ChartLine, TvWidget } from '@/constants/tvchart';
import { AppColorMode, AppTheme } from '@/state/configs'; import { AppColorMode, AppTheme } from '@/state/configs';
import { getAppTheme, getAppColorMode } from '@/state/configsSelectors'; import { getAppTheme, getAppColorMode } from '@/state/configsSelectors';
import { getWidgetOverrides } from '@/lib/tradingView/utils'; import { getWidgetOverrides, getChartLineColors } from '@/lib/tradingView/utils';
/** /**
* @description Method to define a type guard and check that an element is an IFRAME * @description Method to define a type guard and check that an element is an IFRAME
@@ -22,10 +24,12 @@ const isIFrame = (element: HTMLElement | null): element is HTMLIFrameElement =>
* In order to support our Classic along with Dark/Light, we are directly accessing the <html> within the iFrame. * In order to support our Classic along with Dark/Light, we are directly accessing the <html> within the iFrame.
*/ */
export const useTradingViewTheme = ({ export const useTradingViewTheme = ({
chartLines,
tvWidget, tvWidget,
isWidgetReady, isWidgetReady,
}: { }: {
tvWidget: (IChartingLibraryWidget & { _id?: string; _ready?: boolean }) | null; chartLines: Record<string, ChartLine>;
tvWidget: TvWidget | null;
isWidgetReady?: boolean; isWidgetReady?: boolean;
}) => { }) => {
const appTheme: AppTheme = useSelector(getAppTheme); const appTheme: AppTheme = useSelector(getAppTheme);
@@ -33,47 +37,64 @@ export const useTradingViewTheme = ({
useEffect(() => { useEffect(() => {
if (tvWidget && isWidgetReady) { if (tvWidget && isWidgetReady) {
tvWidget tvWidget.changeTheme?.(THEME_NAMES[appTheme]).then(() => {
.changeTheme?.( const tvChartId = tvWidget?._id;
{
[AppTheme.Classic]: '',
[AppTheme.Dark]: 'Dark',
[AppTheme.Light]: 'Light',
}[appTheme] as ThemeName
)
.then(() => {
const tvChartId = tvWidget?._id;
if (tvChartId) { if (tvChartId) {
const frame = document?.getElementById(tvChartId); const frame = document?.getElementById(tvChartId);
if (isIFrame(frame) && frame.contentWindow) { if (isIFrame(frame) && frame.contentWindow) {
const innerHtml = frame.contentWindow.document.documentElement; const innerHtml = frame.contentWindow.document.documentElement;
switch (appTheme) {
if (appTheme === AppTheme.Classic) { case AppTheme.Classic:
innerHtml?.classList.remove('theme-dark', 'theme-light'); innerHtml?.classList.remove('theme-dark', 'theme-light');
} break;
case AppTheme.Dark:
innerHtml?.classList.remove('theme-light');
innerHtml?.classList.add('theme-dark');
break;
case AppTheme.Light:
innerHtml?.classList.remove('theme-dark');
innerHtml?.classList.add('theme-light');
} }
} }
}
const { overrides, studies_overrides } = getWidgetOverrides({ appTheme, appColorMode }); const { overrides, studies_overrides } = getWidgetOverrides({ appTheme, appColorMode });
tvWidget?.applyOverrides(overrides); tvWidget?.applyOverrides(overrides);
tvWidget?.applyStudiesOverrides(studies_overrides); tvWidget?.applyStudiesOverrides(studies_overrides);
// Necessary to update existing indicators // Necessary to update existing indicators
const volumeStudyId = tvWidget const volumeStudyId = tvWidget
?.activeChart() ?.activeChart()
?.getAllStudies() ?.getAllStudies()
?.find((x) => x.name === 'Volume')?.id; ?.find((x) => x.name === 'Volume')?.id;
if (volumeStudyId) { if (volumeStudyId) {
const volume = tvWidget?.activeChart()?.getStudyById(volumeStudyId); const volume = tvWidget?.activeChart()?.getStudyById(volumeStudyId);
volume.applyOverrides({ volume.applyOverrides({
'volume.color.0': studies_overrides['volume.volume.color.0'], 'volume.color.0': studies_overrides['volume.volume.color.0'],
'volume.color.1': studies_overrides['volume.volume.color.1'], 'volume.color.1': studies_overrides['volume.volume.color.1'],
}); });
}
// Necessary to update existing chart lines
Object.values(chartLines).forEach(({ chartLineType, line }) => {
const { maybeQuantityColor, borderColor, backgroundColor, textColor, textButtonColor } =
getChartLineColors({ chartLineType: chartLineType, appTheme, appColorMode });
if (maybeQuantityColor) {
line.setLineColor(maybeQuantityColor).setQuantityBackgroundColor(maybeQuantityColor);
} }
line
.setQuantityBorderColor(borderColor)
.setBodyBackgroundColor(backgroundColor)
.setBodyBorderColor(borderColor)
.setBodyTextColor(textColor)
.setQuantityTextColor(textButtonColor);
}); });
});
} }
}, [appTheme, appColorMode]); }, [appTheme, appColorMode, isWidgetReady]);
}; };
+8 -8
View File
@@ -18,14 +18,13 @@ import type { ResolutionString } from 'public/tradingview/charting_library';
import type { ConnectNetworkEvent, NetworkConfig } from '@/constants/abacus'; import type { ConnectNetworkEvent, NetworkConfig } from '@/constants/abacus';
import { DEFAULT_TRANSACTION_MEMO } from '@/constants/analytics'; import { DEFAULT_TRANSACTION_MEMO } from '@/constants/analytics';
import { type Candle, RESOLUTION_MAP } from '@/constants/candles'; import { type Candle, RESOLUTION_MAP } from '@/constants/candles';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { DydxChainAsset } from '@/constants/wallets';
import { getSelectedNetwork } from '@/state/appSelectors'; import { getSelectedNetwork } from '@/state/appSelectors';
import { log } from '@/lib/telemetry'; import { log } from '@/lib/telemetry';
import { useRestrictions } from './useRestrictions'; import { useRestrictions } from './useRestrictions';
import { useTokenConfigs } from './useTokenConfigs';
type DydxContextType = ReturnType<typeof useDydxClientContext>; type DydxContextType = ReturnType<typeof useDydxClientContext>;
const DydxContext = createContext<DydxContextType>({} as DydxContextType); const DydxContext = createContext<DydxContextType>({} as DydxContextType);
@@ -41,7 +40,8 @@ const useDydxClientContext = () => {
// ------ Network ------ // // ------ Network ------ //
const selectedNetwork = useSelector(getSelectedNetwork); const selectedNetwork = useSelector(getSelectedNetwork);
const tokensConfigs = ENVIRONMENT_CONFIG_MAP[selectedNetwork].tokens; const { usdcDenom, usdcDecimals, usdcGasDenom, chainTokenDenom, chainTokenDecimals } =
useTokenConfigs();
const [networkConfig, setNetworkConfig] = useState<NetworkConfig>(); const [networkConfig, setNetworkConfig] = useState<NetworkConfig>();
@@ -75,11 +75,11 @@ const useDydxClientContext = () => {
networkConfig.validatorUrl, networkConfig.validatorUrl,
networkConfig.chainId, networkConfig.chainId,
{ {
USDC_DENOM: tokensConfigs[DydxChainAsset.USDC].denom, USDC_DENOM: usdcDenom,
USDC_DECIMALS: tokensConfigs[DydxChainAsset.USDC].decimals, USDC_DECIMALS: usdcDecimals,
USDC_GAS_DENOM: tokensConfigs[DydxChainAsset.USDC].gasDenom, USDC_GAS_DENOM: usdcGasDenom,
CHAINTOKEN_DENOM: tokensConfigs[DydxChainAsset.CHAINTOKEN].denom, CHAINTOKEN_DENOM: chainTokenDenom,
CHAINTOKEN_DECIMALS: tokensConfigs[DydxChainAsset.CHAINTOKEN].decimals, CHAINTOKEN_DECIMALS: chainTokenDecimals,
}, },
{ {
broadcastPollIntervalMs: 3_000, broadcastPollIntervalMs: 3_000,
+7 -4
View File
@@ -1,5 +1,8 @@
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { useSelector } from 'react-redux';
import { useSelectedNetwork } from '@/hooks';
import { GOVERNANCE_CONFIG_MAP } from '@/constants/networks';
import { getSelectedDydxChainId } from '@/state/appSelectors';
export interface GovernanceVariables { export interface GovernanceVariables {
newMarketProposal: { newMarketProposal: {
@@ -10,7 +13,7 @@ export interface GovernanceVariables {
} }
export const useGovernanceVariables = (): GovernanceVariables => { export const useGovernanceVariables = (): GovernanceVariables => {
const { selectedNetwork } = useSelectedNetwork(); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const governanceVars = ENVIRONMENT_CONFIG_MAP[selectedNetwork].governance as GovernanceVariables; const governanceVars = GOVERNANCE_CONFIG_MAP[selectedDydxChainId] as GovernanceVariables;
return governanceVars; return governanceVars;
}; };
+15 -9
View File
@@ -82,24 +82,30 @@ const useLocalNotificationsContext = () => {
isCctp, isCctp,
errorCount, errorCount,
status: currentStatus, status: currentStatus,
isExchange,
} = transferNotification; } = transferNotification;
// @ts-ignore status.errors is not in the type definition but can be returned const hasErrors =
// also error can some time come back as an empty object so we need to ignore for that // @ts-ignore status.errors is not in the type definition but can be returned
const hasErrors = !!currentStatus?.errors || // also error can some time come back as an empty object so we need to ignore for that
(currentStatus?.error && Object.keys(currentStatus.error).length !== 0); !!currentStatus?.errors ||
(currentStatus?.error && Object.keys(currentStatus.error).length !== 0);
if ( if (
!isExchange &&
!hasErrors && !hasErrors &&
(!currentStatus?.squidTransactionStatus || (!currentStatus?.squidTransactionStatus ||
currentStatus?.squidTransactionStatus === 'ongoing') currentStatus?.squidTransactionStatus === 'ongoing')
) { ) {
try { try {
const status = await fetchSquidStatus({ const status = await fetchSquidStatus(
transactionId: txHash, {
toChainId, transactionId: txHash,
fromChainId, toChainId,
}, isCctp); fromChainId,
},
isCctp
);
if (status) { if (status) {
transferNotification.status = status; transferNotification.status = status;
+7 -6
View File
@@ -5,7 +5,6 @@ import { isEqual, groupBy } from 'lodash';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { DialogTypes } from '@/constants/dialogs'; import { DialogTypes } from '@/constants/dialogs';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { AppRoute } from '@/constants/routes'; import { AppRoute } from '@/constants/routes';
import { DydxChainAsset } from '@/constants/wallets'; import { DydxChainAsset } from '@/constants/wallets';
@@ -23,7 +22,7 @@ import {
TransferNotificationTypes, TransferNotificationTypes,
} from '@/constants/notifications'; } from '@/constants/notifications';
import { useSelectedNetwork, useStringGetter } from '@/hooks'; import { useStringGetter } from '@/hooks';
import { useLocalNotifications } from '@/hooks/useLocalNotifications'; import { useLocalNotifications } from '@/hooks/useLocalNotifications';
import { AssetIcon } from '@/components/AssetIcon'; import { AssetIcon } from '@/components/AssetIcon';
@@ -36,6 +35,7 @@ import { getSubaccountFills, getSubaccountOrders } from '@/state/accountSelector
import { openDialog } from '@/state/dialogs'; import { openDialog } from '@/state/dialogs';
import { getAbacusNotifications } from '@/state/notificationsSelectors'; import { getAbacusNotifications } from '@/state/notificationsSelectors';
import { getMarketIds } from '@/state/perpetualsSelectors'; import { getMarketIds } from '@/state/perpetualsSelectors';
import { getSelectedDydxChainId } from '@/state/appSelectors';
import { formatSeconds } from '@/lib/timeUtils'; import { formatSeconds } from '@/lib/timeUtils';
@@ -175,16 +175,17 @@ export const notificationTypes: NotificationTypeConfig[] = [
useTrigger: ({ trigger }) => { useTrigger: ({ trigger }) => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { transferNotifications } = useLocalNotifications(); const { transferNotifications } = useLocalNotifications();
const { selectedNetwork } = useSelectedNetwork(); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
useEffect(() => { useEffect(() => {
for (const transfer of transferNotifications) { for (const transfer of transferNotifications) {
const { fromChainId, status, txHash, toAmount, type } = transfer; const { fromChainId, status, txHash, toAmount, type, isExchange } = transfer;
const isFinished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing'; const isFinished =
(Boolean(status) && status?.squidTransactionStatus !== 'ongoing') || isExchange;
const icon = <Icon iconName={isFinished ? IconName.Transfer : IconName.Clock} />; const icon = <Icon iconName={isFinished ? IconName.Transfer : IconName.Clock} />;
const transferType = const transferType =
type ?? fromChainId === ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId type ?? fromChainId === selectedDydxChainId
? TransferNotificationTypes.Withdrawal ? TransferNotificationTypes.Withdrawal
: TransferNotificationTypes.Deposit; : TransferNotificationTypes.Deposit;
+12 -6
View File
@@ -4,8 +4,11 @@ import { useSelector } from 'react-redux';
import { DEFAULT_DOCUMENT_TITLE } from '@/constants/routes'; import { DEFAULT_DOCUMENT_TITLE } from '@/constants/routes';
import { getSelectedLocale } from '@/state/localizationSelectors'; import { getSelectedLocale } from '@/state/localizationSelectors';
import { getCurrentMarketData, getCurrentMarketId } from '@/state/perpetualsSelectors'; import {
import type { RootState } from '@/state/_store'; getCurrentMarketId,
getCurrentMarketMidMarketPrice,
getCurrentMarketOraclePrice,
} from '@/state/perpetualsSelectors';
import { useBreakpoints } from './useBreakpoints'; import { useBreakpoints } from './useBreakpoints';
@@ -13,11 +16,14 @@ export const usePageTitlePriceUpdates = () => {
const selectedLocale = useSelector(getSelectedLocale); const selectedLocale = useSelector(getSelectedLocale);
const { isNotTablet } = useBreakpoints(); const { isNotTablet } = useBreakpoints();
const id = useSelector(getCurrentMarketId); const id = useSelector(getCurrentMarketId);
const oraclePrice = useSelector((state: RootState) => getCurrentMarketData(state)?.oraclePrice); const oraclePrice = useSelector(getCurrentMarketOraclePrice);
const orderbookMidMarketPrice = useSelector(getCurrentMarketMidMarketPrice);
const price = orderbookMidMarketPrice ?? oraclePrice;
useEffect(() => { useEffect(() => {
if (id && oraclePrice && isNotTablet) { if (id && price && isNotTablet) {
const priceString = oraclePrice.toLocaleString(selectedLocale); const priceString = price.toLocaleString(selectedLocale);
document.title = `$${priceString} ${id} · ${DEFAULT_DOCUMENT_TITLE}`; document.title = `$${priceString} ${id} · ${DEFAULT_DOCUMENT_TITLE}`;
} else { } else {
document.title = DEFAULT_DOCUMENT_TITLE; document.title = DEFAULT_DOCUMENT_TITLE;
@@ -26,5 +32,5 @@ export const usePageTitlePriceUpdates = () => {
return () => { return () => {
document.title = DEFAULT_DOCUMENT_TITLE; document.title = DEFAULT_DOCUMENT_TITLE;
}; };
}, [oraclePrice]); }, [price]);
}; };
+3 -2
View File
@@ -47,8 +47,9 @@ export const PotentialMarketsProvider = ({ ...props }) => (
export const usePotentialMarkets = () => useContext(PotentialMarketsContext); export const usePotentialMarkets = () => useContext(PotentialMarketsContext);
const EXCHANGE_CONFIG_FILE_PATH = '/configs/potentialMarketExchangeConfig.json';
const POTENTIAL_MARKETS_FILE_PATH = '/configs/potentialMarketParameters.json'; const EXCHANGE_CONFIG_FILE_PATH = '/configs/otherMarketExchangeConfig.json';
const POTENTIAL_MARKETS_FILE_PATH = '/configs/otherMarketParameters.json';
export const usePotentialMarketsContext = () => { export const usePotentialMarketsContext = () => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
+1 -1
View File
@@ -2,7 +2,7 @@ import { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { LocalStorageKey } from '@/constants/localStorage'; import { LocalStorageKey } from '@/constants/localStorage';
import { DEFAULT_APP_ENVIRONMENT, DydxNetwork, ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { DEFAULT_APP_ENVIRONMENT, DydxNetwork } from '@/constants/networks';
import { useAccounts, useLocalStorage } from '@/hooks'; import { useAccounts, useLocalStorage } from '@/hooks';
+1 -1
View File
@@ -28,7 +28,7 @@ import { setSubaccount, setHistoricalPnl, removeUncommittedOrderClientId } from
import { getBalances } from '@/state/accountSelectors'; import { getBalances } from '@/state/accountSelectors';
import abacusStateManager from '@/lib/abacus'; import abacusStateManager from '@/lib/abacus';
import { hashFromTx } from '@/lib/hashfromTx'; import { hashFromTx } from '@/lib/txUtils';
import { log } from '@/lib/telemetry'; import { log } from '@/lib/telemetry';
import { useAccounts } from './useAccounts'; import { useAccounts } from './useAccounts';
+14 -10
View File
@@ -1,7 +1,9 @@
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { useSelector } from 'react-redux';
import { TOKEN_CONFIG_MAP } from '@/constants/networks';
import { DydxChainAsset } from '@/constants/wallets'; import { DydxChainAsset } from '@/constants/wallets';
import { useSelectedNetwork } from '@/hooks'; import { getSelectedDydxChainId } from '@/state/appSelectors';
export const useTokenConfigs = (): { export const useTokenConfigs = (): {
tokensConfigs: { tokensConfigs: {
@@ -10,31 +12,33 @@ export const useTokenConfigs = (): {
name: string; name: string;
decimals: number; decimals: number;
gasDenom?: string; gasDenom?: string;
}, };
[DydxChainAsset.CHAINTOKEN]: { [DydxChainAsset.CHAINTOKEN]: {
denom: string; denom: string;
name: string; name: string;
decimals: number; decimals: number;
gasDenom?: string; gasDenom?: string;
}, };
}; };
usdcDenom: string; usdcDenom: string;
usdcDecimals: number; usdcDecimals: number;
usdcGasDenom: string;
usdcLabel: string; usdcLabel: string;
chainTokenDenom: string; chainTokenDenom: string;
chainTokenDecimals: number; chainTokenDecimals: number;
chainTokenLabel: string; chainTokenLabel: string;
} => { } => {
const { selectedNetwork } = useSelectedNetwork(); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const tokensConfigs = ENVIRONMENT_CONFIG_MAP[selectedNetwork].tokens; const tokensConfigs = TOKEN_CONFIG_MAP[selectedDydxChainId];
return { return {
tokensConfigs, tokensConfigs,
usdcDenom: tokensConfigs[DydxChainAsset.USDC].denom, usdcDenom: tokensConfigs[DydxChainAsset.USDC].denom,
usdcDecimals: tokensConfigs[DydxChainAsset.USDC].decimals, usdcDecimals: tokensConfigs[DydxChainAsset.USDC].decimals,
usdcGasDenom: tokensConfigs[DydxChainAsset.USDC].gasDenom,
usdcLabel: tokensConfigs[DydxChainAsset.USDC].name, usdcLabel: tokensConfigs[DydxChainAsset.USDC].name,
chainTokenDenom: tokensConfigs[DydxChainAsset.CHAINTOKEN].denom, chainTokenDenom: tokensConfigs[DydxChainAsset.CHAINTOKEN].denom,
chainTokenDecimals: tokensConfigs[DydxChainAsset.CHAINTOKEN].decimals, chainTokenDecimals: tokensConfigs[DydxChainAsset.CHAINTOKEN].decimals,
chainTokenLabel: tokensConfigs[DydxChainAsset.CHAINTOKEN].name, chainTokenLabel: tokensConfigs[DydxChainAsset.CHAINTOKEN].name,
}; };
}; };
+8 -4
View File
@@ -1,12 +1,15 @@
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { useSelector } from 'react-redux';
import { useSelectedNetwork } from '@/hooks'; import { LINKS_CONFIG_MAP } from '@/constants/networks';
import { getSelectedDydxChainId } from '@/state/appSelectors';
const FALLBACK_URL = 'https://help.dydx.exchange/'; const FALLBACK_URL = 'https://help.dydx.exchange/';
export interface LinksConfigs { export interface LinksConfigs {
tos: string; tos: string;
privacy: string; privacy: string;
statusPage: string;
mintscan: string; mintscan: string;
mintscanBase: string; mintscanBase: string;
feedback?: string; feedback?: string;
@@ -27,12 +30,13 @@ export interface LinksConfigs {
} }
export const useURLConfigs = (): LinksConfigs => { export const useURLConfigs = (): LinksConfigs => {
const { selectedNetwork } = useSelectedNetwork(); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const linksConfigs = ENVIRONMENT_CONFIG_MAP[selectedNetwork].links as LinksConfigs; const linksConfigs = LINKS_CONFIG_MAP[selectedDydxChainId] as LinksConfigs;
return { return {
tos: linksConfigs.tos, tos: linksConfigs.tos,
privacy: linksConfigs.privacy, privacy: linksConfigs.privacy,
statusPage: linksConfigs.statusPage,
mintscan: linksConfigs.mintscan, mintscan: linksConfigs.mintscan,
mintscanBase: linksConfigs.mintscanBase, mintscanBase: linksConfigs.mintscanBase,
feedback: linksConfigs.feedback || FALLBACK_URL, feedback: linksConfigs.feedback || FALLBACK_URL,
+14 -11
View File
@@ -4,7 +4,7 @@ import { useSelector } from 'react-redux';
import { EvmDerivedAddresses } from '@/constants/account'; import { EvmDerivedAddresses } from '@/constants/account';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { LocalStorageKey } from '@/constants/localStorage'; import { LocalStorageKey } from '@/constants/localStorage';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { ENVIRONMENT_CONFIG_MAP, WALLETS_CONFIG_MAP } from '@/constants/networks';
import { import {
type DydxAddress, type DydxAddress,
@@ -32,7 +32,7 @@ import {
WalletType as CosmosWalletType, WalletType as CosmosWalletType,
} from 'graz'; } from 'graz';
import { getSelectedNetwork } from '@/state/appSelectors'; import { getSelectedDydxChainId } from '@/state/appSelectors';
import { resolveWagmiConnector } from '@/lib/wagmi'; import { resolveWagmiConnector } from '@/lib/wagmi';
import { getWalletConnection, parseWalletError } from '@/lib/wallet'; import { getWalletConnection, parseWalletError } from '@/lib/wallet';
@@ -91,8 +91,8 @@ export const useWalletConnection = () => {
// Wallet connection // Wallet connection
const selectedNetwork = useSelector(getSelectedNetwork); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const walletConnectConfig = ENVIRONMENT_CONFIG_MAP[selectedNetwork].wallets.walletconnect; const walletConnectConfig = WALLETS_CONFIG_MAP[selectedDydxChainId].walletconnect;
const wagmiConnector = useMemo( const wagmiConnector = useMemo(
() => () =>
walletType && walletConnectionType walletType && walletConnectionType
@@ -163,17 +163,20 @@ export const useWalletConnection = () => {
} }
} }
} catch (error) { } catch (error) {
throw Object.assign( const { isErrorExpected } = parseWalletError({ error, stringGetter });
new Error([error.message, error.cause?.message].filter(Boolean).join('\n')), if (!isErrorExpected) {
{ throw Object.assign(
walletConnectionType: walletConnection?.type, new Error([error.message, error.cause?.message].filter(Boolean).join('\n')),
} {
); walletConnectionType: walletConnection?.type,
}
);
}
} }
return { return {
walletType, walletType,
walletConnectionType: walletConnection.type, walletConnectionType: walletConnection?.type,
}; };
}, },
[isConnectedGraz, signerGraz, isConnectedWagmi, signerWagmi] [isConnectedGraz, signerGraz, isConnectedWagmi, signerWagmi]
+3 -4
View File
@@ -3,9 +3,9 @@ import styled, { type AnyStyledComponent, css } from 'styled-components';
import { AbacusApiStatus } from '@/constants/abacus'; import { AbacusApiStatus } from '@/constants/abacus';
import { ButtonSize, ButtonType } from '@/constants/buttons'; import { ButtonSize, ButtonType } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP, isDev } from '@/constants/networks'; import { isDev } from '@/constants/networks';
import { useApiState, useSelectedNetwork, useStringGetter } from '@/hooks'; import { useApiState, useStringGetter, useURLConfigs } from '@/hooks';
import { ChatIcon, LinkOutIcon } from '@/icons'; import { ChatIcon, LinkOutIcon } from '@/icons';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
@@ -28,8 +28,7 @@ enum ExchangeStatus {
export const FooterDesktop = () => { export const FooterDesktop = () => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { height, indexerHeight, status, statusErrorMessage } = useApiState(); const { height, indexerHeight, status, statusErrorMessage } = useApiState();
const { selectedNetwork } = useSelectedNetwork(); const { statusPage } = useURLConfigs();
const { statusPage } = ENVIRONMENT_CONFIG_MAP[selectedNetwork].links;
const { exchangeStatus, label } = const { exchangeStatus, label } =
!status || status === AbacusApiStatus.NORMAL !status || status === AbacusApiStatus.NORMAL
+7 -6
View File
@@ -34,7 +34,7 @@ import {
import { DEFAULT_TRANSACTION_MEMO } from '@/constants/analytics'; import { DEFAULT_TRANSACTION_MEMO } from '@/constants/analytics';
import { DialogTypes } from '@/constants/dialogs'; import { DialogTypes } from '@/constants/dialogs';
import { UNCOMMITTED_ORDER_TIMEOUT_MS } from '@/constants/trade'; import { UNCOMMITTED_ORDER_TIMEOUT_MS } from '@/constants/trade';
import { ENVIRONMENT_CONFIG_MAP, DydxNetwork, isTestnet } from '@/constants/networks'; import { DydxChainId, isTestnet } from '@/constants/networks';
import { RootStore } from '@/state/_store'; import { RootStore } from '@/state/_store';
import { addUncommittedOrderClientId, removeUncommittedOrderClientId } from '@/state/account'; import { addUncommittedOrderClientId, removeUncommittedOrderClientId } from '@/state/account';
@@ -43,7 +43,11 @@ import { openDialog } from '@/state/dialogs';
import { StatefulOrderError } from '../errors'; import { StatefulOrderError } from '../errors';
import { bytesToBigInt } from '../numbers'; import { bytesToBigInt } from '../numbers';
import { log } from '../telemetry'; import { log } from '../telemetry';
import { hashFromTx } from '../hashfromTx'; import { hashFromTx, getMintscanTxLink } from '../txUtils';
(BigInt.prototype as any).toJSON = function () {
return this.toString();
};
class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol { class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
private compositeClient: CompositeClient | undefined; private compositeClient: CompositeClient | undefined;
@@ -238,9 +242,7 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
if (isTestnet) { if (isTestnet) {
console.log( console.log(
`${ENVIRONMENT_CONFIG_MAP[ getMintscanTxLink(this.compositeClient.network.getString() as DydxChainId, hash)
this.compositeClient.network.getString() as DydxNetwork
]?.links?.mintscan?.replace('{tx_hash}', hash.toString())}`
); );
} else console.log(`txHash: ${hash}`); } else console.log(`txHash: ${hash}`);
@@ -536,7 +538,6 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
const result = await this.cctpWithdraw(params); const result = await this.cctpWithdraw(params);
callback(result); callback(result);
break; break;
break;
} }
default: { default: {
break; break;
+19
View File
@@ -25,3 +25,22 @@ export function convertBech32Address({
}): string { }): string {
return toBech32(bech32Prefix, fromHex(toHex(fromBech32(address).data))); return toBech32(bech32Prefix, fromHex(toHex(fromBech32(address).data)));
} }
/**
* Validates a Cosmos address with a specific prefix.
* @param {string} address The Cosmos address to validate.
* @param {string} prefix The expected prefix for the address.
* @returns {boolean} True if the address is valid and matches the prefix, false otherwise.
*/
export function validateCosmosAddress(address: string, prefix: string) {
try {
// Decode the address to verify its structure and prefix
const { prefix: decodedPrefix } = fromBech32(address);
// Check if the decoded address has the expected prefix
return decodedPrefix === prefix;
} catch (error) {
// If decoding fails, the address is not valid
return false;
}
}
-3
View File
@@ -1,3 +0,0 @@
export const hashFromTx = (
txHash: string | Uint8Array
): string => `0x${Buffer.from(txHash).toString('hex')}`;
+2 -2
View File
@@ -1,4 +1,4 @@
import { type DydxNetwork, ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { type DydxNetwork, ENVIRONMENT_CONFIG_MAP, type DydxChainId } from '@/constants/networks';
export const validateAgainstAvailableEnvironments = (value: DydxNetwork) => export const validateAgainstAvailableEnvironments = (value: DydxNetwork) =>
Object.keys(ENVIRONMENT_CONFIG_MAP).includes(value); Object.keys(ENVIRONMENT_CONFIG_MAP).includes(value);
+3 -3
View File
@@ -31,13 +31,13 @@ export const fetchSquidStatus = async (
const response = await fetch(url, { const response = await fetch(url, {
headers: { headers: {
"x-integrator-id": integratorId || 'dYdX-api' 'x-integrator-id': integratorId || 'dYdX-api',
}, },
}); });
if (!response.ok) { if (!response.ok) {
const error = await response.json(); const error = await response.json();
throw new Error(error); throw error;
} }
return response.json(); return response.json();
@@ -45,4 +45,4 @@ export const fetchSquidStatus = async (
export const getNobleChainId = () => { export const getNobleChainId = () => {
return isMainnet ? 'noble-1' : 'grand-1'; return isMainnet ? 'noble-1' : 'grand-1';
} };
+6 -2
View File
@@ -19,13 +19,17 @@ class TestFlags {
return !!this.queryParams.displayinitializingmarkets; return !!this.queryParams.displayinitializingmarkets;
} }
get addressOverride():string { get addressOverride(): string {
return this.queryParams.address; return this.queryParams.address;
} }
get showTradingRewards() { get showTradingRewards() {
return !!this.queryParams.tradingrewards; return !!this.queryParams.tradingrewards;
} }
get showCexWithdrawal() {
return !!this.queryParams.cexwithdrawal;
}
} }
export const testFlags = new TestFlags(); export const testFlags = new TestFlags();
+32 -2
View File
@@ -1,6 +1,10 @@
import { Candle, TradingViewBar, TradingViewSymbol } from '@/constants/candles'; import { OrderSide } from '@dydxprotocol/v4-client-js';
import type { AppTheme, AppColorMode } from '@/state/configs'; import { Candle, TradingViewBar, TradingViewSymbol } from '@/constants/candles';
import { THEME_NAMES } from '@/constants/styles/colors';
import type { ChartLineType } from '@/constants/tvchart';
import { type AppColorMode, AppTheme } from '@/state/configs';
import { Themes } from '@/styles/themes'; import { Themes } from '@/styles/themes';
@@ -47,6 +51,31 @@ export const getHistorySlice = ({
return bars.filter(({ time }) => time >= fromMs); return bars.filter(({ time }) => time >= fromMs);
}; };
export const getChartLineColors = ({
appTheme,
appColorMode,
chartLineType,
}: {
appTheme: AppTheme;
appColorMode: AppColorMode;
chartLineType: ChartLineType;
}) => {
const theme = Themes[appTheme][appColorMode];
const orderColors = {
[OrderSide.BUY]: theme.positive,
[OrderSide.SELL]: theme.negative,
['position']: null,
};
return {
maybeQuantityColor: orderColors[chartLineType],
borderColor: theme.borderDefault,
backgroundColor: theme.layer1,
textColor: theme.textTertiary,
textButtonColor: theme.textButton,
};
};
export const getWidgetOverrides = ({ export const getWidgetOverrides = ({
appTheme, appTheme,
appColorMode, appColorMode,
@@ -57,6 +86,7 @@ export const getWidgetOverrides = ({
const theme = Themes[appTheme][appColorMode]; const theme = Themes[appTheme][appColorMode];
return { return {
theme: THEME_NAMES[appTheme],
overrides: { overrides: {
'paneProperties.background': theme.layer2, 'paneProperties.background': theme.layer2,
'paneProperties.horzGridProperties.color': theme.layer3, 'paneProperties.horzGridProperties.color': theme.layer3,
+7
View File
@@ -0,0 +1,7 @@
import { DydxChainId, LINKS_CONFIG_MAP } from '@/constants/networks';
export const hashFromTx = (txHash: string | Uint8Array): string =>
`0x${Buffer.from(txHash).toString('hex')}`;
export const getMintscanTxLink = (dydxChainId: DydxChainId, txHash: string): string =>
`${LINKS_CONFIG_MAP[dydxChainId]?.mintscan?.replace('{tx_hash}', txHash.toString())}`;
+11 -1
View File
@@ -92,6 +92,10 @@ export const getWalletErrorType = ({ error }: { error: Error }) => {
return WalletErrorType.ChainMismatch; return WalletErrorType.ChainMismatch;
} }
if (messageLower.includes('Missing or invalid. request() method: wallet_switchEthereumChain')) {
return WalletErrorType.SwitchChainMethodMissing;
}
// ImToken - User canceled // ImToken - User canceled
if (messageLower.includes('用户取消了操作')) { if (messageLower.includes('用户取消了操作')) {
return WalletErrorType.UserCanceled; return WalletErrorType.UserCanceled;
@@ -113,13 +117,18 @@ export const parseWalletError = ({
}) => { }) => {
const walletErrorType = getWalletErrorType({ error }); const walletErrorType = getWalletErrorType({ error });
let message; let message;
let isErrorExpected;
switch (walletErrorType) { switch (walletErrorType) {
case WalletErrorType.ChainMismatch: case WalletErrorType.ChainMismatch:
case WalletErrorType.UserCanceled: { case WalletErrorType.UserCanceled:
case WalletErrorType.SwitchChainMethodMissing: {
isErrorExpected = true;
message = error.message;
break; break;
} }
default: { default: {
isErrorExpected = false;
message = stringGetter({ message = stringGetter({
key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE, key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE,
params: { params: {
@@ -132,5 +141,6 @@ export const parseWalletError = ({
return { return {
walletErrorType, walletErrorType,
message, message,
isErrorExpected,
}; };
}; };
@@ -26,8 +26,6 @@ import { openDialog } from '@/state/dialogs';
import { log } from '@/lib/telemetry'; import { log } from '@/lib/telemetry';
const SEASON_NUMBER = 2;
export const LaunchIncentivesPanel = ({ className }: { className?: string }) => { export const LaunchIncentivesPanel = ({ className }: { className?: string }) => {
const { isNotTablet } = useBreakpoints(); const { isNotTablet } = useBreakpoints();
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -74,14 +72,38 @@ const EstimatedRewards = () => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { dydxAddress } = useAccounts(); const { dydxAddress } = useAccounts();
const { data: seasonNumber } = useQuery({
queryKey: 'chaos_labs_season_number',
queryFn: async () => {
const resp = await fetch('https://cloud.chaoslabs.co/query/ccar-perpetuals', {
method: 'POST',
headers: {
'apollographql-client-name': 'dydx-v4',
'content-type': 'application/json',
protocol: 'dydx-v4',
},
body: JSON.stringify({
operationName: 'TradingSeasons',
variables: {},
query: `query TradingSeasons {
tradingSeasons {
label
}
}`,
}),
});
const seasons = (await resp.json())?.data?.tradingSeasons;
return seasons && seasons.length > 0 ? seasons[seasons.length - 1].label : undefined;
},
onError: (error: Error) => log('LaunchIncentives/fetchSeasonNumber', error),
});
const { data, isLoading } = useQuery({ const { data, isLoading } = useQuery({
enabled: !!dydxAddress, enabled: !!dydxAddress,
queryKey: `launch_incentives_rewards_${dydxAddress ?? ''}_${SEASON_NUMBER}`, queryKey: `launch_incentives_rewards_${dydxAddress ?? ''}`,
queryFn: async () => { queryFn: async () => {
if (!dydxAddress) return undefined; if (!dydxAddress) return undefined;
const resp = await fetch( const resp = await fetch(`https://cloud.chaoslabs.co/query/api/dydx/points/${dydxAddress}`);
`https://cloud.chaoslabs.co/query/api/dydx/points/${dydxAddress}?n=${SEASON_NUMBER}`
);
return (await resp.json())?.incentivePoints; return (await resp.json())?.incentivePoints;
}, },
onError: (error: Error) => log('LaunchIncentives/fetchPoints', error), onError: (error: Error) => log('LaunchIncentives/fetchPoints', error),
@@ -92,12 +114,14 @@ const EstimatedRewards = () => {
<Styled.EstimatedRewardsCardContent> <Styled.EstimatedRewardsCardContent>
<div> <div>
<span>{stringGetter({ key: STRING_KEYS.ESTIMATED_REWARDS })}</span> <span>{stringGetter({ key: STRING_KEYS.ESTIMATED_REWARDS })}</span>
<Styled.Season> {seasonNumber !== undefined && (
{stringGetter({ <Styled.Season>
key: STRING_KEYS.LAUNCH_INCENTIVES_SEASON_NUM, {stringGetter({
params: { SEASON_NUMBER }, key: STRING_KEYS.LAUNCH_INCENTIVES_SEASON_NUM,
})} params: { SEASON_NUMBER: seasonNumber },
</Styled.Season> })}
</Styled.Season>
)}
</div> </div>
<Styled.Points> <Styled.Points>
+24 -1
View File
@@ -9,10 +9,13 @@ import {
HistoricalTradingRewardsPeriods, HistoricalTradingRewardsPeriods,
} from '@/constants/abacus'; } from '@/constants/abacus';
import { isMainnet } from '@/constants/networks';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import breakpoints from '@/styles/breakpoints'; import breakpoints from '@/styles/breakpoints';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
import { Output, OutputType } from '@/components/Output';
import { Panel } from '@/components/Panel'; import { Panel } from '@/components/Panel';
import { ToggleGroup } from '@/components/ToggleGroup'; import { ToggleGroup } from '@/components/ToggleGroup';
import { WithTooltip } from '@/components/WithTooltip'; import { WithTooltip } from '@/components/WithTooltip';
@@ -20,6 +23,9 @@ import { TradingRewardHistoryTable } from '@/views/tables/TradingRewardHistoryTa
import abacusStateManager from '@/lib/abacus'; import abacusStateManager from '@/lib/abacus';
// TODO: set in env featureFlag config
const REWARDS_HISTORY_START_DATE_MS = isMainnet ? 1706486400000 : 1704844800000;
export const RewardHistoryPanel = () => { export const RewardHistoryPanel = () => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
@@ -47,7 +53,20 @@ export const RewardHistoryPanel = () => {
<WithTooltip tooltip="reward-history"> <WithTooltip tooltip="reward-history">
<h3>{stringGetter({ key: STRING_KEYS.REWARD_HISTORY })}</h3> <h3>{stringGetter({ key: STRING_KEYS.REWARD_HISTORY })}</h3>
</WithTooltip> </WithTooltip>
<span>{stringGetter({ key: STRING_KEYS.REWARD_HISTORY_DESCRIPTION })}</span> <span>
{stringGetter({
key: STRING_KEYS.REWARD_HISTORY_DESCRIPTION,
params: {
REWARDS_HISTORY_START_DATE: (
<Styled.Output
type={OutputType.Date}
value={REWARDS_HISTORY_START_DATE_MS}
timeOptions={{ useUTC: true }}
/>
),
},
})}
</span>
</Styled.Title> </Styled.Title>
<ToggleGroup <ToggleGroup
items={[ items={[
@@ -103,3 +122,7 @@ Styled.Content = styled.div`
${layoutMixins.flexColumn} ${layoutMixins.flexColumn}
gap: 0.75rem; gap: 0.75rem;
`; `;
Styled.Output = styled(Output)`
display: inline;
`;
+6 -3
View File
@@ -1,6 +1,7 @@
import styled, { AnyStyledComponent, css } from 'styled-components'; import styled, { AnyStyledComponent, css } from 'styled-components';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { isMainnet } from '@/constants/networks';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { AppRoute } from '@/constants/routes'; import { AppRoute } from '@/constants/routes';
@@ -27,6 +28,8 @@ const RewardsPage = () => {
const { isTablet, isNotTablet } = useBreakpoints(); const { isTablet, isNotTablet } = useBreakpoints();
const navigate = useNavigate(); const navigate = useNavigate();
const showTradingRewards = testFlags.showTradingRewards || !isMainnet;
return ( return (
<div> <div>
{isTablet && ( {isTablet && (
@@ -37,7 +40,7 @@ const RewardsPage = () => {
)} )}
<DetachedSection> <DetachedSection>
<Styled.GridLayout <Styled.GridLayout
showTradingRewards={testFlags.showTradingRewards} showTradingRewards={showTradingRewards}
showMigratePanel={import.meta.env.VITE_V3_TOKEN_ADDRESS && isNotTablet} showMigratePanel={import.meta.env.VITE_V3_TOKEN_ADDRESS && isNotTablet}
> >
{import.meta.env.VITE_V3_TOKEN_ADDRESS && isNotTablet && <Styled.MigratePanel />} {import.meta.env.VITE_V3_TOKEN_ADDRESS && isNotTablet && <Styled.MigratePanel />}
@@ -51,7 +54,7 @@ const RewardsPage = () => {
</> </>
)} )}
{testFlags.showTradingRewards && ( {showTradingRewards && (
<Styled.TradingRewardsColumn> <Styled.TradingRewardsColumn>
<TradingRewardsSummaryPanel /> <TradingRewardsSummaryPanel />
{isTablet && <RewardsHelpPanel />} {isTablet && <RewardsHelpPanel />}
@@ -60,7 +63,7 @@ const RewardsPage = () => {
)} )}
{isNotTablet && ( {isNotTablet && (
<Styled.OtherColumn showTradingRewards={testFlags.showTradingRewards}> <Styled.OtherColumn showTradingRewards={showTradingRewards}>
<RewardsHelpPanel /> <RewardsHelpPanel />
</Styled.OtherColumn> </Styled.OtherColumn>
)} )}
+2 -2
View File
@@ -3,7 +3,7 @@ import { useSelector, shallowEqual } from 'react-redux';
import styled, { AnyStyledComponent } from 'styled-components'; import styled, { AnyStyledComponent } from 'styled-components';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { TRADE_TYPE_STRINGS } from '@/constants/trade'; import { ORDER_TYPE_STRINGS } from '@/constants/trade';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
@@ -43,7 +43,7 @@ export const TradeDialogTrigger = () => {
<Styled.TradeSummary> <Styled.TradeSummary>
<Styled.TradeType> <Styled.TradeType>
<span> <span>
{stringGetter({ key: TRADE_TYPE_STRINGS[selectedTradeType].tradeTypeKey })} {stringGetter({ key: ORDER_TYPE_STRINGS[selectedTradeType].orderTypeKey })}
</span> </span>
<OrderSideTag size={TagSize.Medium} orderSide={selectedOrderSide} /> <OrderSideTag size={TagSize.Medium} orderSide={selectedOrderSide} />
</Styled.TradeType> </Styled.TradeType>
+4
View File
@@ -1,4 +1,8 @@
import { DydxChainId, ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import type { RootState } from './_store'; import type { RootState } from './_store';
export const getApiState = (state: RootState) => state.app.apiState; export const getApiState = (state: RootState) => state.app.apiState;
export const getSelectedNetwork = (state: RootState) => state.app.selectedNetwork; export const getSelectedNetwork = (state: RootState) => state.app.selectedNetwork;
export const getSelectedDydxChainId = (state: RootState) =>
ENVIRONMENT_CONFIG_MAP[state.app.selectedNetwork].dydxChainId as DydxChainId;
+6
View File
@@ -107,6 +107,12 @@ export const getCurrentMarketHistoricalFundings = createSelector(
currentMarketId ? historicalFundings?.[currentMarketId] ?? [] : [] currentMarketId ? historicalFundings?.[currentMarketId] ?? [] : []
); );
/**
* @returns oracle price of the market the user is currently viewing
*/
export const getCurrentMarketOraclePrice = (state: RootState) =>
getCurrentMarketData(state)?.oraclePrice;
/** /**
* @returns Mid market price for the market the user is currently viewing * @returns Mid market price for the market the user is currently viewing
*/ */
+5 -5
View File
@@ -48,12 +48,12 @@ export const DepthChartTooltipContent = ({
const depthChartSeries = nearestDatum.key as DepthChartSeries; const depthChartSeries = nearestDatum.key as DepthChartSeries;
return { return {
[DepthChartSeries.Bids]: MustBigNumber(nearestDatum?.datum.price) [DepthChartSeries.Bids]: MustBigNumber(midMarketPrice)
.minus(chartPointAtPointer.price)
.div(nearestDatum?.datum.price),
[DepthChartSeries.Asks]: MustBigNumber(chartPointAtPointer.price)
.minus(nearestDatum?.datum.price) .minus(nearestDatum?.datum.price)
.div(chartPointAtPointer.price), .div(midMarketPrice),
[DepthChartSeries.Asks]: MustBigNumber(nearestDatum?.datum.price)
.minus(midMarketPrice)
.div(midMarketPrice),
[DepthChartSeries.MidMarket]: undefined, [DepthChartSeries.MidMarket]: undefined,
}[depthChartSeries]; }[depthChartSeries];
} }
+21 -54
View File
@@ -1,73 +1,40 @@
import { useEffect, useRef, useState } from 'react'; import { useRef, useState } from 'react';
import styled, { type AnyStyledComponent, css } from 'styled-components'; import styled, { type AnyStyledComponent, css } from 'styled-components';
import { useDispatch, useSelector } from 'react-redux';
import type { IChartingLibraryWidget, ResolutionString } from 'public/tradingview/charting_library'; import type { ResolutionString } from 'public/tradingview/charting_library';
import { DEFAULT_MARKETID } from '@/constants/markets'; import type { TvWidget } from '@/constants/tvchart';
import { DEFAULT_RESOLUTION, RESOLUTION_CHART_CONFIGS } from '@/constants/candles';
import { useTradingView, useTradingViewTheme } from '@/hooks/tradingView'; import {
useChartLines,
useChartMarketAndResolution,
useTradingView,
useTradingViewTheme,
} from '@/hooks/tradingView';
import { LoadingSpace } from '@/components/Loading/LoadingSpinner'; import { LoadingSpace } from '@/components/Loading/LoadingSpinner';
import { setTvChartResolution } from '@/state/perpetuals';
import { getCurrentMarketId, getSelectedResolutionForMarket } from '@/state/perpetualsSelectors';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
type TvWidget = IChartingLibraryWidget & { _id?: string; _ready?: boolean };
export const TvChart = () => { export const TvChart = () => {
const [isChartReady, setIsChartReady] = useState(false); const [isChartReady, setIsChartReady] = useState(false);
const dispatch = useDispatch();
const currentMarketId: string = useSelector(getCurrentMarketId) || DEFAULT_MARKETID;
const selectedResolution: string =
useSelector(getSelectedResolutionForMarket(currentMarketId)) || DEFAULT_RESOLUTION;
const tvWidgetRef = useRef<TvWidget | null>(null); const tvWidgetRef = useRef<TvWidget | null>(null);
const tvWidget = tvWidgetRef.current; const tvWidget = tvWidgetRef.current;
const isWidgetReady = tvWidget?._ready; const isWidgetReady = tvWidget?._ready;
const chart = isWidgetReady ? tvWidget?.chart() : undefined;
const chartResolution = chart?.resolution?.();
const { savedResolution } = useTradingView({ tvWidgetRef, setIsChartReady }); const displayButtonRef = useRef<HTMLElement | null>(null);
useTradingViewTheme({ tvWidget, isWidgetReady }); const displayButton = displayButtonRef.current;
const setVisibleRangeForResolution = ({ resolution }: { resolution: ResolutionString }) => { const { savedResolution } = useTradingView({ tvWidgetRef, displayButtonRef, setIsChartReady });
// Different resolutions have different timeframes to display data efficiently. useChartMarketAndResolution({
const { defaultRange } = RESOLUTION_CHART_CONFIGS[resolution]; tvWidget,
isWidgetReady,
// from/to values converted to epoch seconds savedResolution: savedResolution as ResolutionString | undefined,
const newRange = { });
from: (Date.now() - defaultRange) / 1000, const { chartLines } = useChartLines({ tvWidget, displayButton, isChartReady });
to: Date.now() / 1000, useTradingViewTheme({ tvWidget, isWidgetReady, chartLines });
};
tvWidget?.activeChart().setVisibleRange(newRange, { percentRightMargin: 10 });
};
useEffect(() => {
if (chartResolution) {
if (chartResolution !== selectedResolution) {
dispatch(setTvChartResolution({ marketId: currentMarketId, resolution: chartResolution }));
}
setVisibleRangeForResolution({ resolution: chartResolution });
}
}, [chartResolution]);
/**
* @description Hook to handle changing markets
*/
useEffect(() => {
if (currentMarketId && isWidgetReady) {
const resolution = savedResolution || selectedResolution;
tvWidget?.setSymbol(currentMarketId, resolution as ResolutionString, () => {});
}
}, [currentMarketId, isWidgetReady]);
return ( return (
<Styled.PriceChart isChartReady={isChartReady}> <Styled.PriceChart isChartReady={isChartReady}>
+1 -3
View File
@@ -13,8 +13,6 @@ export const DepositDialog = ({ setIsOpen }: ElementProps) => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { isMobile } = useBreakpoints(); const { isMobile } = useBreakpoints();
const closeDialog = () => setIsOpen?.(false);
return ( return (
<Dialog <Dialog
isOpen isOpen
@@ -22,7 +20,7 @@ export const DepositDialog = ({ setIsOpen }: ElementProps) => {
title={stringGetter({ key: STRING_KEYS.DEPOSIT })} title={stringGetter({ key: STRING_KEYS.DEPOSIT })}
placement={isMobile ? DialogPlacement.FullScreen : DialogPlacement.Default} placement={isMobile ? DialogPlacement.FullScreen : DialogPlacement.Default}
> >
<DepositDialogContent onDeposit={closeDialog} /> <DepositDialogContent />
</Dialog> </Dialog>
); );
}; };
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
import styled, { type AnyStyledComponent } from 'styled-components'; import styled, { type AnyStyledComponent } from 'styled-components';
import { TransferInputField, TransferType } from '@/constants/abacus'; import { TransferInputField, TransferType } from '@/constants/abacus';
import { AnalyticsEvent } from '@/constants/analytics';
import { isMainnet } from '@/constants/networks'; import { isMainnet } from '@/constants/networks';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
@@ -9,6 +10,7 @@ import { DepositForm } from '@/views/forms/AccountManagementForms/DepositForm';
import { TestnetDepositForm } from '@/views/forms/AccountManagementForms/TestnetDepositForm'; import { TestnetDepositForm } from '@/views/forms/AccountManagementForms/TestnetDepositForm';
import abacusStateManager from '@/lib/abacus'; import abacusStateManager from '@/lib/abacus';
import { track } from '@/lib/analytics';
type ElementProps = { type ElementProps = {
onDeposit?: () => void; onDeposit?: () => void;
@@ -34,9 +36,19 @@ export const DepositDialogContent = ({ onDeposit }: ElementProps) => {
return ( return (
<Styled.Content> <Styled.Content>
{isMainnet || !showFaucet ? ( {isMainnet || !showFaucet ? (
<DepositForm onDeposit={onDeposit} /> <DepositForm
onDeposit={(event) => {
track(AnalyticsEvent.TransferDeposit, event);
onDeposit?.();
}}
/>
) : ( ) : (
<TestnetDepositForm onDeposit={onDeposit} /> <TestnetDepositForm
onDeposit={() => {
track(AnalyticsEvent.TransferFaucet);
onDeposit?.();
}}
/>
)} )}
{!isMainnet && ( {!isMainnet && (
<Styled.TextToggle onClick={() => setShowFaucet(!showFaucet)}> <Styled.TextToggle onClick={() => setShowFaucet(!showFaucet)}>
@@ -32,7 +32,15 @@ export const ExternalNavKeplrDialog = ({ setIsOpen }: ElementProps) => {
openDialog({ openDialog({
type: DialogTypes.ExternalLink, type: DialogTypes.ExternalLink,
dialogProps: { dialogProps: {
buttonText: stringGetter({ key: STRING_KEYS.CONTINUE }),
link: keplrDashboard, link: keplrDashboard,
title: stringGetter({ key: STRING_KEYS.LEAVING_WEBSITE_STAKING_GOVERNANCE }),
slotContent: stringGetter({
key: STRING_KEYS.STAKE_WITH_KEPLR_AND_LEAVING_DESCRIPTION,
params: {
CTA: stringGetter({ key: STRING_KEYS.CONTINUE }),
},
}),
}, },
}) })
); );
+2 -2
View File
@@ -105,8 +105,8 @@ export const OnboardingDialog = ({ setIsOpen }: ElementProps) => {
<Styled.Content> <Styled.Content>
{isMainnet ? ( {isMainnet ? (
<DepositForm <DepositForm
onDeposit={() => { onDeposit={(event) => {
track(AnalyticsEvent.TransferDeposit); track(AnalyticsEvent.TransferDeposit, event);
}} }}
/> />
) : ( ) : (
@@ -25,7 +25,7 @@ import { Switch } from '@/components/Switch';
import { WithReceipt } from '@/components/WithReceipt'; import { WithReceipt } from '@/components/WithReceipt';
import { WithTooltip } from '@/components/WithTooltip'; import { WithTooltip } from '@/components/WithTooltip';
import { getSelectedNetwork } from '@/state/appSelectors'; import { getSelectedNetwork, getSelectedDydxChainId } from '@/state/appSelectors';
import { track } from '@/lib/analytics'; import { track } from '@/lib/analytics';
import { isTruthy } from '@/lib/isTruthy'; import { isTruthy } from '@/lib/isTruthy';
@@ -44,7 +44,6 @@ export const GenerateKeys = ({
onKeysDerived = () => {}, onKeysDerived = () => {},
}: ElementProps) => { }: ElementProps) => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { isMobile } = useBreakpoints();
const [shouldRememberMe, setShouldRememberMe] = useState(false); const [shouldRememberMe, setShouldRememberMe] = useState(false);
@@ -66,17 +65,30 @@ export const GenerateKeys = ({
try { try {
await matchNetwork?.(); await matchNetwork?.();
return true;
} catch (error) { } catch (error) {
const { message, walletErrorType } = parseWalletError({ error, stringGetter }); const { message, walletErrorType, isErrorExpected } = parseWalletError({
error,
stringGetter,
});
if (!isErrorExpected) {
log('GenerateKeys/switchNetwork', error, { walletErrorType });
}
if (message) { if (message) {
log('GenerateKeys/switchNetwork', error, { walletErrorType });
setError(message); setError(message);
throw error;
} }
return false;
} }
}; };
const switchNetworkAndDeriveKeys = async () => {
const networkSwitched = await switchNetwork();
if (networkSwitched) await deriveKeys();
};
// 2. Derive keys from EVM account // 2. Derive keys from EVM account
const { getWalletFromEvmSignature } = useDydxClient(); const { getWalletFromEvmSignature } = useDydxClient();
const { getSubaccounts } = useAccounts(); const { getSubaccounts } = useAccounts();
@@ -86,7 +98,8 @@ export const GenerateKeys = ({
EvmDerivedAccountStatus.Derived, EvmDerivedAccountStatus.Derived,
].includes(status); ].includes(status);
const signTypedData = getSignTypedData(selectedNetwork); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const signTypedData = getSignTypedData(selectedDydxChainId);
const { signTypedDataAsync } = useSignTypedData({ const { signTypedDataAsync } = useSignTypedData({
...signTypedData, ...signTypedData,
domain: { domain: {
@@ -154,11 +167,16 @@ export const GenerateKeys = ({
setStatus(EvmDerivedAccountStatus.Derived); setStatus(EvmDerivedAccountStatus.Derived);
} catch (error) { } catch (error) {
setStatus(EvmDerivedAccountStatus.NotDerived); setStatus(EvmDerivedAccountStatus.NotDerived);
const { message, walletErrorType } = parseWalletError({ error, stringGetter }); const { message, walletErrorType, isErrorExpected } = parseWalletError({
error,
stringGetter,
});
if (message) { if (message) {
setError(message); setError(message);
log('GenerateKeys/deriveKeys', error, { walletErrorType }); if (!isErrorExpected) {
log('GenerateKeys/deriveKeys', error, { walletErrorType });
}
} }
} }
}; };
@@ -231,7 +249,7 @@ export const GenerateKeys = ({
{!isMatchingNetwork ? ( {!isMatchingNetwork ? (
<Button <Button
action={ButtonAction.Primary} action={ButtonAction.Primary}
onClick={() => switchNetwork().then(deriveKeys).then(onKeysDerived)} onClick={() => switchNetworkAndDeriveKeys().then(onKeysDerived)}
state={{ isLoading: isSwitchingNetwork }} state={{ isLoading: isSwitchingNetwork }}
> >
{stringGetter({ key: STRING_KEYS.SWITCH_NETWORK })} {stringGetter({ key: STRING_KEYS.SWITCH_NETWORK })}
+1 -1
View File
@@ -4,7 +4,7 @@ import styled, { AnyStyledComponent, css } from 'styled-components';
import { TradeInputField } from '@/constants/abacus'; import { TradeInputField } from '@/constants/abacus';
import { STRING_KEYS, StringKey } from '@/constants/localization'; import { STRING_KEYS, StringKey } from '@/constants/localization';
import { TradeTypes, TRADE_TYPE_STRINGS, MobilePlaceOrderSteps } from '@/constants/trade'; import { TradeTypes, ORDER_TYPE_STRINGS, MobilePlaceOrderSteps } from '@/constants/trade';
import { useBreakpoints, useStringGetter } from '@/hooks'; import { useBreakpoints, useStringGetter } from '@/hooks';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
@@ -7,10 +7,11 @@ import { Abi, parseUnits } from 'viem';
import erc20 from '@/abi/erc20.json'; import erc20 from '@/abi/erc20.json';
import erc20_usdt from '@/abi/erc20_usdt.json'; import erc20_usdt from '@/abi/erc20_usdt.json';
import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus'; import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus';
import { AnalyticsEvent, AnalyticsEventData } from '@/constants/analytics';
import { AlertType } from '@/constants/alerts'; import { AlertType } from '@/constants/alerts';
import { ButtonSize } from '@/constants/buttons'; import { ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP, isMainnet } from '@/constants/networks'; import { isMainnet } from '@/constants/networks';
import { MAX_CCTP_TRANSFER_AMOUNT, MAX_PRICE_IMPACT, NumberSign } from '@/constants/numbers'; import { MAX_CCTP_TRANSFER_AMOUNT, MAX_PRICE_IMPACT, NumberSign } from '@/constants/numbers';
import type { EvmAddress } from '@/constants/wallets'; import type { EvmAddress } from '@/constants/wallets';
@@ -32,6 +33,7 @@ import { OutputType } from '@/components/Output';
import { Tag } from '@/components/Tag'; import { Tag } from '@/components/Tag';
import { WithDetailsReceipt } from '@/components/WithDetailsReceipt'; import { WithDetailsReceipt } from '@/components/WithDetailsReceipt';
import { getSelectedDydxChainId } from '@/state/appSelectors';
import { getTransferInputs } from '@/state/inputsSelectors'; import { getTransferInputs } from '@/state/inputsSelectors';
import abacusStateManager from '@/lib/abacus'; import abacusStateManager from '@/lib/abacus';
@@ -47,7 +49,7 @@ import { DepositButtonAndReceipt } from './DepositForm/DepositButtonAndReceipt';
import { NobleDeposit } from '../NobleDeposit'; import { NobleDeposit } from '../NobleDeposit';
type DepositFormProps = { type DepositFormProps = {
onDeposit?: () => void; onDeposit?: (event?: AnalyticsEventData<AnalyticsEvent.TransferDeposit>) => void;
onError?: () => void; onError?: () => void;
}; };
@@ -56,7 +58,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
const [error, setError] = useState<Error | null>(null); const [error, setError] = useState<Error | null>(null);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [requireUserActionInWallet, setRequireUserActionInWallet] = useState(false); const [requireUserActionInWallet, setRequireUserActionInWallet] = useState(false);
const { selectedNetwork } = useSelectedNetwork(); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const { evmAddress, signerWagmi, publicClientWagmi, nobleAddress } = useAccounts(); const { evmAddress, signerWagmi, publicClientWagmi, nobleAddress } = useAccounts();
@@ -131,7 +133,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
if (error) onError?.(); if (error) onError?.();
}, [error]); }, [error]);
const onSelectChain = useCallback((name: string, type: 'chain' | 'exchange') => { const onSelectNetwork = useCallback((name: string, type: 'chain' | 'exchange') => {
if (name) { if (name) {
abacusStateManager.clearTransferInputValues(); abacusStateManager.clearTransferInputValues();
setFromAmount(''); setFromAmount('');
@@ -257,14 +259,10 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
}; };
const txHash = await signerWagmi.sendTransaction(tx); const txHash = await signerWagmi.sendTransaction(tx);
onDeposit?.();
if (txHash) { if (txHash) {
addTransferNotification({ addTransferNotification({
txHash: txHash, txHash: txHash,
toChainId: !isCctp toChainId: !isCctp ? selectedDydxChainId : getNobleChainId(),
? ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId
: getNobleChainId(),
fromChainId: chainIdStr || undefined, fromChainId: chainIdStr || undefined,
toAmount: summary?.usdcSize || undefined, toAmount: summary?.usdcSize || undefined,
triggeredAt: Date.now(), triggeredAt: Date.now(),
@@ -272,6 +270,12 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
}); });
abacusStateManager.clearTransferInputValues(); abacusStateManager.clearTransferInputValues();
setFromAmount(''); setFromAmount('');
onDeposit?.({
chainId: chainIdStr || undefined,
tokenAddress: sourceToken?.address || undefined,
tokenSymbol: sourceToken?.symbol || undefined,
});
} }
} catch (error) { } catch (error) {
log('DepositForm/onSubmit', error); log('DepositForm/onSubmit', error);
@@ -280,7 +284,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
setIsLoading(false); setIsLoading(false);
} }
}, },
[requestPayload, signerWagmi, chainId] [requestPayload, signerWagmi, chainId, sourceToken, sourceChain]
); );
const amountInputReceipt = [ const amountInputReceipt = [
@@ -379,7 +383,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
<SourceSelectMenu <SourceSelectMenu
selectedChain={chainIdStr || undefined} selectedChain={chainIdStr || undefined}
selectedExchange={exchange || undefined} selectedExchange={exchange || undefined}
onSelect={onSelectChain} onSelect={onSelectNetwork}
/> />
{exchange && nobleAddress ? ( {exchange && nobleAddress ? (
<NobleDeposit /> <NobleDeposit />
@@ -14,6 +14,7 @@ import { popoverMixins } from '@/styles/popoverMixins';
import { getTransferInputs } from '@/state/inputsSelectors'; import { getTransferInputs } from '@/state/inputsSelectors';
import { isTruthy } from '@/lib/isTruthy'; import { isTruthy } from '@/lib/isTruthy';
import { testFlags } from '@/lib/testFlags';
type ElementProps = { type ElementProps = {
label?: string; label?: string;
@@ -62,11 +63,12 @@ export const SourceSelectMenu = ({
return ( return (
<SearchSelectMenu <SearchSelectMenu
items={[ items={[
exchangeItems.length > 0 && { exchangeItems.length > 0 &&
group: 'exchanges', (testFlags.showCexWithdrawal || type === TransferType.deposit) && {
groupLabel: stringGetter({ key: STRING_KEYS.EXCHANGES }), group: 'exchanges',
items: exchangeItems, groupLabel: stringGetter({ key: STRING_KEYS.EXCHANGES }),
}, items: exchangeItems,
},
chainItems.length > 0 && { chainItems.length > 0 && {
group: 'chains', group: 'chains',
groupLabel: stringGetter({ key: STRING_KEYS.CHAINS }), groupLabel: stringGetter({ key: STRING_KEYS.CHAINS }),
@@ -17,9 +17,10 @@ import { getTransferInputs } from '@/state/inputsSelectors';
type ElementProps = { type ElementProps = {
selectedToken?: TransferInputTokenResource; selectedToken?: TransferInputTokenResource;
onSelectToken: (token: TransferInputTokenResource) => void; onSelectToken: (token: TransferInputTokenResource) => void;
isExchange?: boolean;
}; };
export const TokenSelectMenu = ({ selectedToken, onSelectToken }: ElementProps) => { export const TokenSelectMenu = ({ selectedToken, onSelectToken, isExchange }: ElementProps) => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { type, depositOptions, withdrawalOptions, resources } = const { type, depositOptions, withdrawalOptions, resources } =
useSelector(getTransferInputs, shallowEqual) || {}; useSelector(getTransferInputs, shallowEqual) || {};
@@ -47,19 +48,24 @@ export const TokenSelectMenu = ({ selectedToken, onSelectToken }: ElementProps)
}, },
]} ]}
label={stringGetter({ key: STRING_KEYS.ASSET })} label={stringGetter({ key: STRING_KEYS.ASSET })}
withReceiptItems={[ withSearch={!isExchange}
{ withReceiptItems={
key: 'swap', !isExchange
label: stringGetter({ key: STRING_KEYS.SWAP }), ? [
value: selectedToken && ( {
<> key: 'swap',
<Tag>{type === TransferType.deposit ? selectedToken?.symbol : 'USDC'}</Tag> label: stringGetter({ key: STRING_KEYS.SWAP }),
<DiffArrow /> value: selectedToken && (
<Tag>{type === TransferType.deposit ? 'USDC' : selectedToken?.symbol}</Tag> <>
</> <Tag>{type === TransferType.deposit ? selectedToken?.symbol : 'USDC'}</Tag>
), <DiffArrow />
}, <Tag>{type === TransferType.deposit ? 'USDC' : selectedToken?.symbol}</Tag>
]} </>
),
},
]
: undefined
}
> >
<Styled.AssetRow> <Styled.AssetRow>
{selectedToken ? ( {selectedToken ? (
@@ -7,11 +7,17 @@ import { isAddress } from 'viem';
import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus'; import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus';
import { AlertType } from '@/constants/alerts'; import { AlertType } from '@/constants/alerts';
import { AnalyticsEvent } from '@/constants/analytics';
import { ButtonSize } from '@/constants/buttons'; import { ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP, isMainnet } from '@/constants/networks'; import { isMainnet } from '@/constants/networks';
import { TransferNotificationTypes } from '@/constants/notifications'; import { TransferNotificationTypes } from '@/constants/notifications';
import { MAX_CCTP_TRANSFER_AMOUNT, MAX_PRICE_IMPACT, NumberSign } from '@/constants/numbers'; import {
MAX_CCTP_TRANSFER_AMOUNT,
MAX_PRICE_IMPACT,
MIN_CCTP_TRANSFER_AMOUNT,
NumberSign,
} from '@/constants/numbers';
import { import {
useAccounts, useAccounts,
@@ -40,6 +46,7 @@ import { Icon, IconName } from '@/components/Icon';
import { SourceSelectMenu } from '@/views/forms/AccountManagementForms/SourceSelectMenu'; import { SourceSelectMenu } from '@/views/forms/AccountManagementForms/SourceSelectMenu';
import { getSelectedDydxChainId } from '@/state/appSelectors';
import { getSubaccount } from '@/state/accountSelectors'; import { getSubaccount } from '@/state/accountSelectors';
import { getTransferInputs } from '@/state/inputsSelectors'; import { getTransferInputs } from '@/state/inputsSelectors';
@@ -49,12 +56,14 @@ import { getNobleChainId } from '@/lib/squid';
import { TokenSelectMenu } from './TokenSelectMenu'; import { TokenSelectMenu } from './TokenSelectMenu';
import { WithdrawButtonAndReceipt } from './WithdrawForm/WithdrawButtonAndReceipt'; import { WithdrawButtonAndReceipt } from './WithdrawForm/WithdrawButtonAndReceipt';
import { validateCosmosAddress } from '@/lib/addressUtils';
import { track } from '@/lib/analytics';
export const WithdrawForm = () => { export const WithdrawForm = () => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const [error, setError] = useState<string>(); const [error, setError] = useState<string>();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { selectedNetwork } = useSelectedNetwork(); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const { sendSquidWithdraw } = useSubaccount(); const { sendSquidWithdraw } = useSubaccount();
const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {}; const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {};
@@ -62,6 +71,7 @@ export const WithdrawForm = () => {
const { const {
requestPayload, requestPayload,
token, token,
exchange,
chain: chainIdStr, chain: chainIdStr,
address: toAddress, address: toAddress,
resources, resources,
@@ -173,20 +183,27 @@ export const WithdrawForm = () => {
requestPayload.data, requestPayload.data,
isCctp isCctp
); );
if (txHash) { const nobleChainId = getNobleChainId();
const toChainId = Boolean(exchange) ? nobleChainId : chainIdStr || undefined;
if (txHash && toChainId) {
addTransferNotification({ addTransferNotification({
txHash: txHash, txHash: txHash,
type: TransferNotificationTypes.Withdrawal, type: TransferNotificationTypes.Withdrawal,
fromChainId: !isCctp fromChainId: !isCctp ? selectedDydxChainId : nobleChainId,
? ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId toChainId,
: getNobleChainId(),
toChainId: chainIdStr || undefined,
toAmount: debouncedAmountBN.toNumber(), toAmount: debouncedAmountBN.toNumber(),
triggeredAt: Date.now(), triggeredAt: Date.now(),
isCctp, isCctp,
isExchange: Boolean(exchange),
}); });
abacusStateManager.clearTransferInputValues(); abacusStateManager.clearTransferInputValues();
setWithdrawAmount(''); setWithdrawAmount('');
track(AnalyticsEvent.TransferWithdraw, {
chainId: toChainId,
tokenAddress: toToken?.address || undefined,
tokenSymbol: toToken?.symbol || undefined,
});
} }
} }
} catch (error) { } catch (error) {
@@ -209,7 +226,17 @@ export const WithdrawForm = () => {
setIsLoading(false); setIsLoading(false);
} }
}, },
[requestPayload, debouncedAmountBN, chainIdStr, toAddress, screenAddresses, stringGetter] [
requestPayload,
debouncedAmountBN,
chainIdStr,
toAddress,
selectedDydxChainId,
exchange,
toToken,
screenAddresses,
stringGetter,
]
); );
const onChangeAddress = useCallback((e: ChangeEvent<HTMLInputElement>) => { const onChangeAddress = useCallback((e: ChangeEvent<HTMLInputElement>) => {
@@ -242,13 +269,20 @@ export const WithdrawForm = () => {
setWithdrawAmount(freeCollateralBN.toString()); setWithdrawAmount(freeCollateralBN.toString());
}, [freeCollateralBN, setWithdrawAmount]); }, [freeCollateralBN, setWithdrawAmount]);
const onSelectChain = useCallback((chain: string) => { const onSelectNetwork = useCallback((name: string, type: 'chain' | 'exchange') => {
if (chain) { if (name) {
abacusStateManager.setTransferValue({
field: TransferInputField.chain,
value: chain,
});
setWithdrawAmount(''); setWithdrawAmount('');
if (type === 'chain') {
abacusStateManager.setTransferValue({
field: TransferInputField.chain,
value: name,
});
} else {
abacusStateManager.setTransferValue({
field: TransferInputField.exchange,
value: name,
});
}
} }
}, []); }, []);
@@ -309,7 +343,7 @@ export const WithdrawForm = () => {
}); });
if (debouncedAmountBN) { if (debouncedAmountBN) {
if (!chainIdStr) { if (!chainIdStr && !exchange) {
return stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_CHAIN }); return stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_CHAIN });
} else if (!toToken) { } else if (!toToken) {
return stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_ASSET }); return stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_ASSET });
@@ -329,6 +363,12 @@ export const WithdrawForm = () => {
}, },
}); });
} }
if (
!debouncedAmountBN.isZero() &&
MustBigNumber(debouncedAmountBN).lte(MIN_CCTP_TRANSFER_AMOUNT)
) {
return 'Amount must be greater than 10 USDC';
}
} }
if (isMainnet && MustBigNumber(summary?.aggregatePriceImpact).gte(MAX_PRICE_IMPACT)) { if (isMainnet && MustBigNumber(summary?.aggregatePriceImpact).gte(MAX_PRICE_IMPACT)) {
@@ -350,14 +390,19 @@ export const WithdrawForm = () => {
summary, summary,
]); ]);
const isInvalidNobleAddress = Boolean(
exchange && toAddress && !validateCosmosAddress(toAddress, 'noble')
);
const isDisabled = const isDisabled =
!!errorMessage || !!errorMessage ||
!toToken || !toToken ||
!chainIdStr || (!chainIdStr && !exchange) ||
!toAddress || !toAddress ||
debouncedAmountBN.isNaN() || debouncedAmountBN.isNaN() ||
debouncedAmountBN.isZero() || debouncedAmountBN.isZero() ||
isLoading; isLoading ||
isInvalidNobleAddress;
return ( return (
<Styled.Form onSubmit={onSubmit}> <Styled.Form onSubmit={onSubmit}>
@@ -375,12 +420,21 @@ export const WithdrawForm = () => {
} }
/> />
<SourceSelectMenu <SourceSelectMenu
label={stringGetter({ key: STRING_KEYS.NETWORK })} selectedExchange={exchange || undefined}
selectedChain={chainIdStr || undefined} selectedChain={chainIdStr || undefined}
onSelect={onSelectChain} onSelect={onSelectNetwork}
/> />
</Styled.DestinationRow> </Styled.DestinationRow>
<TokenSelectMenu selectedToken={toToken || undefined} onSelectToken={onSelectToken} /> {isInvalidNobleAddress && (
<AlertMessage type={AlertType.Error}>
{stringGetter({ key: STRING_KEYS.NOBLE_ADDRESS_VALIDATION })}
</AlertMessage>
)}
<TokenSelectMenu
selectedToken={toToken || undefined}
onSelectToken={onSelectToken}
isExchange={Boolean(exchange)}
/>
<Styled.WithDetailsReceipt side="bottom" detailItems={amountInputReceipt}> <Styled.WithDetailsReceipt side="bottom" detailItems={amountInputReceipt}>
<FormInput <FormInput
type={InputType.Number} type={InputType.Number}
@@ -27,6 +27,8 @@ import { calculateCanAccountTrade } from '@/state/accountCalculators';
import { getSubaccount } from '@/state/accountSelectors'; import { getSubaccount } from '@/state/accountSelectors';
import { getTransferInputs } from '@/state/inputsSelectors'; import { getTransferInputs } from '@/state/inputsSelectors';
import { isTruthy } from '@/lib/isTruthy';
import { SlippageEditor } from '../SlippageEditor'; import { SlippageEditor } from '../SlippageEditor';
type ElementProps = { type ElementProps = {
@@ -55,7 +57,7 @@ export const WithdrawButtonAndReceipt = ({
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { leverage } = useSelector(getSubaccount, shallowEqual) || {}; const { leverage } = useSelector(getSubaccount, shallowEqual) || {};
const { summary, requestPayload } = useSelector(getTransferInputs, shallowEqual) || {}; const { summary, requestPayload, exchange } = useSelector(getTransferInputs, shallowEqual) || {};
const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual); const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual);
const { usdcLabel } = useTokenConfigs(); const { usdcLabel } = useTokenConfigs();
@@ -92,7 +94,7 @@ export const WithdrawButtonAndReceipt = ({
value: <Output type={OutputType.Fiat} value={totalFees} />, value: <Output type={OutputType.Fiat} value={totalFees} />,
subitems: feeSubitems, subitems: feeSubitems,
}, },
{ !exchange && {
key: 'exchange-rate', key: 'exchange-rate',
label: <span>{stringGetter({ key: STRING_KEYS.EXCHANGE_RATE })}</span>, label: <span>{stringGetter({ key: STRING_KEYS.EXCHANGE_RATE })}</span>,
value: withdrawToken && typeof summary?.exchangeRate === 'number' && ( value: withdrawToken && typeof summary?.exchangeRate === 'number' && (
@@ -133,7 +135,9 @@ export const WithdrawButtonAndReceipt = ({
{withdrawToken && <Tag>{withdrawToken?.symbol}</Tag>} {withdrawToken && <Tag>{withdrawToken?.symbol}</Tag>}
</span> </span>
), ),
value: <Output type={OutputType.Asset} value={summary?.toAmount} fractionDigits={TOKEN_DECIMALS} />, value: (
<Output type={OutputType.Asset} value={summary?.toAmount} fractionDigits={TOKEN_DECIMALS} />
),
subitems: [ subitems: [
{ {
key: 'minimum-amount-received', key: 'minimum-amount-received',
@@ -144,13 +148,17 @@ export const WithdrawButtonAndReceipt = ({
</span> </span>
), ),
value: ( value: (
<Output type={OutputType.Asset} value={summary?.toAmountMin} fractionDigits={TOKEN_DECIMALS} /> <Output
type={OutputType.Asset}
value={summary?.toAmountMin}
fractionDigits={TOKEN_DECIMALS}
/>
), ),
tooltip: 'minimum-amount-received', tooltip: 'minimum-amount-received',
}, },
], ],
}, },
{ !exchange && {
key: 'slippage', key: 'slippage',
label: <span>{stringGetter({ key: STRING_KEYS.MAX_SLIPPAGE })}</span>, label: <span>{stringGetter({ key: STRING_KEYS.MAX_SLIPPAGE })}</span>,
value: ( value: (
@@ -175,7 +183,7 @@ export const WithdrawButtonAndReceipt = ({
/> />
), ),
}, },
]; ].filter(isTruthy);
const isFormValid = !isDisabled && !isEditingSlippage; const isFormValid = !isDisabled && !isEditingSlippage;
+7 -2
View File
@@ -14,6 +14,7 @@ import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/b
import { TOKEN_DECIMALS } from '@/constants/numbers'; import { TOKEN_DECIMALS } from '@/constants/numbers';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { MobilePlaceOrderSteps } from '@/constants/trade'; import { MobilePlaceOrderSteps } from '@/constants/trade';
import { useBreakpoints, useIsFirstRender, useStringGetter, useSubaccount } from '@/hooks'; import { useBreakpoints, useIsFirstRender, useStringGetter, useSubaccount } from '@/hooks';
import { useOnLastOrderIndexed } from '@/hooks/useOnLastOrderIndexed'; import { useOnLastOrderIndexed } from '@/hooks/useOnLastOrderIndexed';
@@ -35,7 +36,6 @@ import { Orderbook, orderbookMixins, type OrderbookScrollBehavior } from '@/view
import { PositionPreview } from '@/views/forms/TradeForm/PositionPreview'; import { PositionPreview } from '@/views/forms/TradeForm/PositionPreview';
import { getCurrentMarketPositionData } from '@/state/accountSelectors'; import { getCurrentMarketPositionData } from '@/state/accountSelectors';
import { getCurrentMarketAssetData } from '@/state/assetsSelectors'; import { getCurrentMarketAssetData } from '@/state/assetsSelectors';
import { getClosePositionInputErrors, getInputClosePositionData } from '@/state/inputsSelectors'; import { getClosePositionInputErrors, getInputClosePositionData } from '@/state/inputsSelectors';
import { getCurrentMarketConfig, getCurrentMarketId } from '@/state/perpetualsSelectors'; import { getCurrentMarketConfig, getCurrentMarketId } from '@/state/perpetualsSelectors';
@@ -291,9 +291,14 @@ export const ClosePositionForm = ({
isLoading={isClosingPosition} isLoading={isClosingPosition}
hasValidationErrors={hasInputErrors} hasValidationErrors={hasInputErrors}
actionStringKey={inputAlert?.actionStringKey} actionStringKey={inputAlert?.actionStringKey}
validationErrorString={alertContent}
summary={summary ?? undefined} summary={summary ?? undefined}
currentStep={currentStep} currentStep={currentStep}
isClosePosition confirmButtonConfig={{
stringKey: STRING_KEYS.CLOSE_ORDER,
buttonTextStringKey: STRING_KEYS.CLOSE_POSITION,
buttonAction: ButtonAction.Destroy,
}}
/> />
</Styled.Footer> </Styled.Footer>
</Styled.ClosePositionForm> </Styled.ClosePositionForm>

Some files were not shown because too many files have changed in this diff Show More