Compare commits

...
Author SHA1 Message Date
Bill He b0971126bb update sha 2024-02-15 10:12:56 -08:00
Bill He f0c69f1111 host amplitude script ourselves 2024-02-15 09:55:23 -08: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
Jared Vu 5fb2f278da Liquid staking language and localization (#289)
* ✏️ Liquid staking language and localization

* import nits
2024-02-08 06:44:50 -08:00
Jared Vu bd788f22e8 Restore env format (#288)
* restore formatting

* copy from prev commit
2024-02-07 14:22:12 -08:00
57 changed files with 3799 additions and 5724 deletions
+1
View File
@@ -1 +1,2 @@
node_modules/
public/
+1 -1
View File
@@ -42,7 +42,7 @@
"@cosmjs/tendermint-rpc": "^0.32.1",
"@dydxprotocol/v4-abacus": "^1.4.2",
"@dydxprotocol/v4-client-js": "^1.0.20",
"@dydxprotocol/v4-localization": "^1.1.25",
"@dydxprotocol/v4-localization": "^1.1.30",
"@ethersproject/providers": "^5.7.2",
"@js-joda/core": "^5.5.3",
"@radix-ui/react-accordion": "^1.1.2",
+10 -10
View File
@@ -36,8 +36,8 @@ dependencies:
specifier: ^1.0.20
version: 1.0.20
'@dydxprotocol/v4-localization':
specifier: ^1.1.25
version: 1.1.25
specifier: ^1.1.30
version: 1.1.30
'@ethersproject/providers':
specifier: ^5.7.2
version: 5.7.2
@@ -1323,8 +1323,8 @@ packages:
- utf-8-validate
dev: false
/@dydxprotocol/v4-localization@1.1.25:
resolution: {integrity: sha512-84BJMybnZBrRnDpnHlMUcOYT1r0ODtgcdgrjdzTw8b0sViTY6ne9ob690fcn/vQO+6sEdDsdt+08ud0H+ZvyYg==}
/@dydxprotocol/v4-localization@1.1.30:
resolution: {integrity: sha512-TZfWWRSOxcjLHs972wlJVVHkE7+DVqAUnGZSs24HYHsPtUkPhZiNXMOA2Vk9YddQxumhM79xIRH0cmJSe5DDUg==}
dev: false
/@dydxprotocol/v4-proto@4.0.0-dev.0:
@@ -8882,7 +8882,7 @@ packages:
resolution: {integrity: sha512-WIdaQ8uW1vIbYvNnAVunkC6yxTrneJC7VQ5UUQ0kuw8b0C0A39KTIpoQHCfc8tV7o9vF4niwRhdXEdfAgQEsQQ==}
dependencies:
cosmos-directory-types: 0.0.6
node-fetch-native: 1.6.1
node-fetch-native: 1.6.2
dev: false
/cosmos-directory-types@0.0.6:
@@ -12888,8 +12888,8 @@ packages:
engines: {node: '>=10.5.0'}
dev: true
/node-fetch-native@1.6.1:
resolution: {integrity: sha512-bW9T/uJDPAJB2YNYEpWzE54U5O3MQidXsOyTfnbKYtTtFexRvGzb1waphBN4ZwP6EcIvYYEOwW0b72BpAqydTw==}
/node-fetch-native@1.6.2:
resolution: {integrity: sha512-69mtXOFZ6hSkYiXAVB5SqaRvrbITC/NPyqv7yuu/qw0nmgPyYbIMYYNIDhNtwPrzk0ptrimrLz/hhjvm4w5Z+w==}
dev: false
/node-fetch@2.6.12:
@@ -13045,7 +13045,7 @@ packages:
resolution: {integrity: sha512-s1ZCMmQWXy4b5K/TW9i/DtiN8Ku+xCiHcjQ6/J/nDdssirrQNOoB165Zu8EqLMA2lln1JUth9a0aW9Ap2ctrUg==}
dependencies:
destr: 2.0.2
node-fetch-native: 1.6.1
node-fetch-native: 1.6.2
ufo: 1.3.2
dev: false
@@ -15025,7 +15025,7 @@ packages:
consola: 3.2.3
defu: 6.1.3
mime: 3.0.0
node-fetch-native: 1.6.1
node-fetch-native: 1.6.2
pathe: 1.1.1
dev: false
@@ -15171,7 +15171,7 @@ packages:
listhen: 1.5.5
lru-cache: 10.1.0
mri: 1.2.0
node-fetch-native: 1.6.1
node-fetch-native: 1.6.2
ofetch: 1.3.3
ufo: 1.3.2
transitivePeerDependencies:
+1319 -1302
View File
File diff suppressed because it is too large Load Diff
+22
View File
@@ -83,6 +83,14 @@
"whitepaperLink": "https://docs.blur.foundation/",
"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/"
},
"CELO-USD": {
"name": "Celo",
"tags": [],
@@ -125,6 +133,13 @@
"whitepaperLink": "https://polkadot.network/PolkaDotPaper.pdf",
"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": {
"name": "Enjin",
"tags": [],
@@ -364,6 +379,13 @@
"whitepaperLink": "https://z.cash/technology/",
"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": {
"name": "0x",
"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,608 @@
{
"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" }
],
"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
+635
View File
@@ -0,0 +1,635 @@
{
"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",
"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",
"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]",
"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 V4"
},
"[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: 56 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 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>
`;
+3
View File
@@ -16,6 +16,7 @@ const assetIcons = {
AVAX: '/currencies/avax.png',
BCH: '/currencies/bch.png',
BLUR: '/currencies/blur.png',
BNB: '/currencies/bnb.png',
BONK: '/currencies/bonk.png',
BTC: '/currencies/btc.png',
CELO: '/currencies/celo.png',
@@ -25,6 +26,7 @@ const assetIcons = {
DOGE: '/currencies/doge.png',
DOT: '/currencies/dot.png',
DYDX: '/currencies/dydx.png',
DYM: '/currencies/dym.png',
ENJ: '/currencies/enj.png',
EOS: '/currencies/eos.png',
ETC: '/currencies/etc.png',
@@ -62,6 +64,7 @@ const assetIcons = {
XTZ: '/currencies/xtz.png',
YFI: '/currencies/yfi.png',
ZEC: '/currencies/zec.png',
ZETA: '/currencies/zeta.png',
ZRX: '/currencies/zrx.png',
} as const;
+9 -5
View File
@@ -133,6 +133,7 @@ const detailsLayoutVariants = {
row: css`
${layoutMixins.row}
align-self: stretch;
white-space: nowrap;
`,
rowColumns: css`
@@ -159,12 +160,16 @@ const itemLayoutVariants: Record<string, FlattenInterpolation<ThemeProps<any>>>
${layoutMixins.spacedRow}
gap: 0.5rem;
align-items: start;
padding: 0.5rem 0;
min-height: var(--details-item-height);
> :last-child {
align-self: stretch;
> :first-child > abbr {
min-width: auto;
}
> :last-child {
${layoutMixins.row}
${layoutMixins.stickyRight}
@@ -217,8 +222,6 @@ Styled.Details = styled.dl<{
--details-grid-numColumns: 2;
${({ layout }) => layout && detailsLayoutVariants[layout]}
white-space: nowrap;
`;
Styled.Item = styled.div<{
@@ -231,7 +234,7 @@ Styled.Item = styled.div<{
${({ justifyItems }) =>
justifyItems === 'end' &&
css`
&:nth-child(even) {
> :nth-child(even) {
justify-items: end;
text-align: end;
}
@@ -243,6 +246,7 @@ Styled.Item = styled.div<{
{
column: css`
&:not(:hover) > :first-child {
white-space: nowrap;
overflow-x: hidden;
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}
>
<Styled.Content>
<Styled.Details withSeparators items={items} />
<Styled.Details withSeparators justifyItems="end" items={items} />
<Styled.Footer>{slotFooter}</Styled.Footer>
</Styled.Content>
+5
View File
@@ -33,4 +33,9 @@ TagStory.argTypes = {
control: { type: 'select' },
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 &&
css`
background-color: var(--color-accent);
color: var(--color-text-button);
`}
`;
+1
View File
@@ -22,4 +22,5 @@ export enum NumberSign {
// Deposit/Withdraw
export const MAX_CCTP_TRANSFER_AMOUNT = 1_000_000;
export const MIN_CCTP_TRANSFER_AMOUNT = 10;
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 = {
[AppColorMode.GreenUp]: ThemeColorBase;
+54 -20
View File
@@ -4,7 +4,6 @@ import { AlertType } from '@/constants/alerts';
import { STRING_KEYS } from '@/constants/localization';
import { TimeUnitShort } from '@/constants/time';
// TODO: rename to OrderType
export enum TradeTypes {
MARKET = 'MARKET',
LIMIT = 'LIMIT',
@@ -15,6 +14,16 @@ export enum TradeTypes {
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 {
GTT = 'GTT',
FOK = 'FOK',
@@ -40,49 +49,74 @@ export const POSITION_SIDE_STRINGS: Record<PositionSide, string> = {
[PositionSide.Short]: STRING_KEYS.SHORT_POSITION_SHORT,
};
export const TRADE_TYPE_STRINGS: Record<
TradeTypes,
export const ORDER_TYPE_STRINGS: Record<
OrderType,
{
tradeTypeKeyShort: string;
tradeTypeKey: string;
descriptionKey: string;
orderTypeKeyShort: string;
orderTypeKey: string;
descriptionKey: string | null;
}
> = {
[TradeTypes.LIMIT]: {
tradeTypeKeyShort: STRING_KEYS.LIMIT_ORDER_SHORT,
tradeTypeKey: STRING_KEYS.LIMIT_ORDER,
orderTypeKeyShort: STRING_KEYS.LIMIT_ORDER_SHORT,
orderTypeKey: STRING_KEYS.LIMIT_ORDER,
descriptionKey: STRING_KEYS.LIMIT_ORDER_DESCRIPTION,
},
[TradeTypes.MARKET]: {
tradeTypeKeyShort: STRING_KEYS.MARKET_ORDER_SHORT,
tradeTypeKey: STRING_KEYS.MARKET_ORDER,
orderTypeKeyShort: STRING_KEYS.MARKET_ORDER_SHORT,
orderTypeKey: STRING_KEYS.MARKET_ORDER,
descriptionKey: STRING_KEYS.MARKET_ORDER_DESCRIPTION,
},
[TradeTypes.STOP_LIMIT]: {
tradeTypeKeyShort: STRING_KEYS.STOP_LIMIT,
tradeTypeKey: STRING_KEYS.STOP_LIMIT,
orderTypeKeyShort: STRING_KEYS.STOP_LIMIT,
orderTypeKey: STRING_KEYS.STOP_LIMIT,
descriptionKey: STRING_KEYS.STOP_LIMIT_DESCRIPTION,
},
[TradeTypes.STOP_MARKET]: {
tradeTypeKeyShort: STRING_KEYS.STOP_MARKET,
tradeTypeKey: STRING_KEYS.STOP_MARKET,
orderTypeKeyShort: STRING_KEYS.STOP_MARKET,
orderTypeKey: STRING_KEYS.STOP_MARKET,
descriptionKey: STRING_KEYS.STOP_MARKET_DESCRIPTION,
},
[TradeTypes.TAKE_PROFIT]: {
tradeTypeKeyShort: STRING_KEYS.TAKE_PROFIT_LIMIT,
tradeTypeKey: STRING_KEYS.TAKE_PROFIT_LIMIT,
orderTypeKeyShort: STRING_KEYS.TAKE_PROFIT_LIMIT,
orderTypeKey: STRING_KEYS.TAKE_PROFIT_LIMIT,
descriptionKey: STRING_KEYS.TAKE_PROFIT_LIMIT_DESCRIPTION,
},
[TradeTypes.TAKE_PROFIT_MARKET]: {
tradeTypeKeyShort: STRING_KEYS.TAKE_PROFIT_MARKET,
tradeTypeKey: STRING_KEYS.TAKE_PROFIT_MARKET,
orderTypeKeyShort: STRING_KEYS.TAKE_PROFIT_MARKET,
orderTypeKey: STRING_KEYS.TAKE_PROFIT_MARKET,
descriptionKey: STRING_KEYS.TAKE_PROFIT_MARKET_DESCRIPTION,
},
[TradeTypes.TRAILING_STOP]: {
tradeTypeKeyShort: STRING_KEYS.TRAILING_STOP,
tradeTypeKey: STRING_KEYS.TRAILING_STOP,
orderTypeKeyShort: STRING_KEYS.TRAILING_STOP,
orderTypeKey: STRING_KEYS.TRAILING_STOP,
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> = {
+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;
};
+1
View File
@@ -40,6 +40,7 @@ export enum WalletErrorType {
// General
ChainMismatch,
UserCanceled,
SwitchChainMethodMissing,
// Non-Deterministic
NonDeterministicWallet,
+2
View File
@@ -1,2 +1,4 @@
export { useChartLines } from './useChartLines';
export { useChartMarketAndResolution } from './useChartMarketAndResolution';
export { useTradingView } from './useTradingView';
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 isEmpty from 'lodash/isEmpty';
import { LanguageCode, ResolutionString, widget } from 'public/tradingview/charting_library';
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 { useDydxClient, useLocalStorage } from '@/hooks';
import { store } from '@/state/_store';
import type { TvWidget } from '@/constants/tvchart';
import { useDydxClient, useLocalStorage, useStringGetter } from '@/hooks';
import { store } from '@/state/_store';
import { getSelectedNetwork } from '@/state/appSelectors';
import { getAppTheme, getAppColorMode } from '@/state/configsSelectors';
import { getSelectedLocale } from '@/state/localizationSelectors';
@@ -23,14 +27,19 @@ import { getSavedResolution, getWidgetOptions, getWidgetOverrides } from '@/lib/
*/
export const useTradingView = ({
tvWidgetRef,
displayButtonRef,
setIsChartReady,
}: {
tvWidgetRef: React.MutableRefObject<any>;
tvWidgetRef: React.MutableRefObject<TvWidget | null>;
displayButtonRef: React.MutableRefObject<HTMLElement | null>;
setIsChartReady: React.Dispatch<React.SetStateAction<boolean>>;
}) => {
const marketId = useSelector(getCurrentMarketId);
const stringGetter = useStringGetter();
const appTheme = useSelector(getAppTheme);
const appColorMode = useSelector(getAppColorMode);
const marketId = useSelector(getCurrentMarketId);
const marketIds = useSelector(getMarketIds, shallowEqual);
const selectedLocale = useSelector(getSelectedLocale);
const selectedNetwork = useSelector(getSelectedNetwork);
@@ -49,7 +58,6 @@ export const useTradingView = ({
const widgetOptions = getWidgetOptions();
const widgetOverrides = getWidgetOverrides({ appTheme, appColorMode });
const options = {
// debug: true,
...widgetOptions,
...widgetOverrides,
datafeed: getDydxDatafeed(store, getCandlesForDatafeed),
@@ -63,6 +71,19 @@ export const useTradingView = ({
tvWidgetRef.current = tvChartWidget;
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?.save((chartConfig: object) => setTvChartConfig(chartConfig))
);
@@ -72,6 +93,8 @@ export const useTradingView = ({
}
return () => {
displayButtonRef.current?.remove();
displayButtonRef.current = null;
tvWidgetRef.current?.remove();
tvWidgetRef.current = null;
setIsChartReady(false);
+56 -35
View File
@@ -1,12 +1,14 @@
import { useEffect } from 'react';
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 { 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
@@ -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.
*/
export const useTradingViewTheme = ({
chartLines,
tvWidget,
isWidgetReady,
}: {
tvWidget: (IChartingLibraryWidget & { _id?: string; _ready?: boolean }) | null;
chartLines: Record<string, ChartLine>;
tvWidget: TvWidget | null;
isWidgetReady?: boolean;
}) => {
const appTheme: AppTheme = useSelector(getAppTheme);
@@ -33,47 +37,64 @@ export const useTradingViewTheme = ({
useEffect(() => {
if (tvWidget && isWidgetReady) {
tvWidget
.changeTheme?.(
{
[AppTheme.Classic]: '',
[AppTheme.Dark]: 'Dark',
[AppTheme.Light]: 'Light',
}[appTheme] as ThemeName
)
.then(() => {
const tvChartId = tvWidget?._id;
tvWidget.changeTheme?.(THEME_NAMES[appTheme]).then(() => {
const tvChartId = tvWidget?._id;
if (tvChartId) {
const frame = document?.getElementById(tvChartId);
if (tvChartId) {
const frame = document?.getElementById(tvChartId);
if (isIFrame(frame) && frame.contentWindow) {
const innerHtml = frame.contentWindow.document.documentElement;
if (appTheme === AppTheme.Classic) {
if (isIFrame(frame) && frame.contentWindow) {
const innerHtml = frame.contentWindow.document.documentElement;
switch (appTheme) {
case AppTheme.Classic:
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 });
tvWidget?.applyOverrides(overrides);
tvWidget?.applyStudiesOverrides(studies_overrides);
const { overrides, studies_overrides } = getWidgetOverrides({ appTheme, appColorMode });
tvWidget?.applyOverrides(overrides);
tvWidget?.applyStudiesOverrides(studies_overrides);
// Necessary to update existing indicators
const volumeStudyId = tvWidget
?.activeChart()
?.getAllStudies()
?.find((x) => x.name === 'Volume')?.id;
// Necessary to update existing indicators
const volumeStudyId = tvWidget
?.activeChart()
?.getAllStudies()
?.find((x) => x.name === 'Volume')?.id;
if (volumeStudyId) {
const volume = tvWidget?.activeChart()?.getStudyById(volumeStudyId);
volume.applyOverrides({
'volume.color.0': studies_overrides['volume.volume.color.0'],
'volume.color.1': studies_overrides['volume.volume.color.1'],
});
if (volumeStudyId) {
const volume = tvWidget?.activeChart()?.getStudyById(volumeStudyId);
volume.applyOverrides({
'volume.color.0': studies_overrides['volume.volume.color.0'],
'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]);
};
+12 -6
View File
@@ -4,8 +4,11 @@ import { useSelector } from 'react-redux';
import { DEFAULT_DOCUMENT_TITLE } from '@/constants/routes';
import { getSelectedLocale } from '@/state/localizationSelectors';
import { getCurrentMarketData, getCurrentMarketId } from '@/state/perpetualsSelectors';
import type { RootState } from '@/state/_store';
import {
getCurrentMarketId,
getCurrentMarketMidMarketPrice,
getCurrentMarketOraclePrice,
} from '@/state/perpetualsSelectors';
import { useBreakpoints } from './useBreakpoints';
@@ -13,11 +16,14 @@ export const usePageTitlePriceUpdates = () => {
const selectedLocale = useSelector(getSelectedLocale);
const { isNotTablet } = useBreakpoints();
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(() => {
if (id && oraclePrice && isNotTablet) {
const priceString = oraclePrice.toLocaleString(selectedLocale);
if (id && price && isNotTablet) {
const priceString = price.toLocaleString(selectedLocale);
document.title = `$${priceString} ${id} · ${DEFAULT_DOCUMENT_TITLE}`;
} else {
document.title = DEFAULT_DOCUMENT_TITLE;
@@ -26,5 +32,5 @@ export const usePageTitlePriceUpdates = () => {
return () => {
document.title = DEFAULT_DOCUMENT_TITLE;
};
}, [oraclePrice]);
}, [price]);
};
+4 -3
View File
@@ -47,8 +47,9 @@ export const PotentialMarketsProvider = ({ ...props }) => (
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 = () => {
const stringGetter = useStringGetter();
@@ -88,7 +89,7 @@ export const usePotentialMarketsContext = () => {
impactNotional: 10_000,
},
1: {
label: 'Mid-cap',
label: stringGetter({ key: STRING_KEYS.MID_CAP }),
initialMarginFraction: 0.1,
maintenanceMarginFraction: 0.05,
impactNotional: 5_000,
+10 -7
View File
@@ -163,17 +163,20 @@ export const useWalletConnection = () => {
}
}
} catch (error) {
throw Object.assign(
new Error([error.message, error.cause?.message].filter(Boolean).join('\n')),
{
walletConnectionType: walletConnection?.type,
}
);
const { isErrorExpected } = parseWalletError({ error, stringGetter });
if (!isErrorExpected) {
throw Object.assign(
new Error([error.message, error.cause?.message].filter(Boolean).join('\n')),
{
walletConnectionType: walletConnection?.type,
}
);
}
}
return {
walletType,
walletConnectionType: walletConnection.type,
walletConnectionType: walletConnection?.type,
};
},
[isConnectedGraz, signerGraz, isConnectedWagmi, signerWagmi]
+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';
@@ -47,6 +51,31 @@ export const getHistorySlice = ({
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 = ({
appTheme,
appColorMode,
@@ -57,6 +86,7 @@ export const getWidgetOverrides = ({
const theme = Themes[appTheme][appColorMode];
return {
theme: THEME_NAMES[appTheme],
overrides: {
'paneProperties.background': theme.layer2,
'paneProperties.horzGridProperties.color': theme.layer3,
+11 -1
View File
@@ -92,6 +92,10 @@ export const getWalletErrorType = ({ error }: { error: Error }) => {
return WalletErrorType.ChainMismatch;
}
if (messageLower.includes('Missing or invalid. request() method: wallet_switchEthereumChain')) {
return WalletErrorType.SwitchChainMethodMissing;
}
// ImToken - User canceled
if (messageLower.includes('用户取消了操作')) {
return WalletErrorType.UserCanceled;
@@ -113,13 +117,18 @@ export const parseWalletError = ({
}) => {
const walletErrorType = getWalletErrorType({ error });
let message;
let isErrorExpected;
switch (walletErrorType) {
case WalletErrorType.ChainMismatch:
case WalletErrorType.UserCanceled: {
case WalletErrorType.UserCanceled:
case WalletErrorType.SwitchChainMethodMissing: {
isErrorExpected = true;
message = error.message;
break;
}
default: {
isErrorExpected = false;
message = stringGetter({
key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE,
params: {
@@ -132,5 +141,6 @@ export const parseWalletError = ({
return {
walletErrorType,
message,
isErrorExpected,
};
};
+1 -1
View File
@@ -19,7 +19,7 @@ export default () => {
<Styled.HeaderSection>
<ContentSectionHeader
title={stringGetter({ key: STRING_KEYS.GOVERNANCE })}
subtitle="Participate in the ecosystem by voting on Governance proposals or submitting your own."
subtitle={stringGetter({ key: STRING_KEYS.GOVERNANCE_PAGE_SUBTITLE })}
/>
</Styled.HeaderSection>
+1 -1
View File
@@ -59,7 +59,7 @@ export default () => {
<Icon iconName={IconName.CurrencySign} />
</Styled.IconContainer>
),
label: 'Staking Rewards', // stringGetter({ key: STRING_KEYS.STAKING_REWARDS }),
label: stringGetter({ key: STRING_KEYS.STAKING_REWARDS }),
href: TokenRoute.StakingRewards,
tag: stringGetter({ key: STRING_KEYS.NEW }),
},
+24 -1
View File
@@ -9,10 +9,13 @@ import {
HistoricalTradingRewardsPeriods,
} from '@/constants/abacus';
import { isMainnet } from '@/constants/networks';
import { STRING_KEYS } from '@/constants/localization';
import breakpoints from '@/styles/breakpoints';
import { layoutMixins } from '@/styles/layoutMixins';
import { Output, OutputType } from '@/components/Output';
import { Panel } from '@/components/Panel';
import { ToggleGroup } from '@/components/ToggleGroup';
import { WithTooltip } from '@/components/WithTooltip';
@@ -20,6 +23,9 @@ import { TradingRewardHistoryTable } from '@/views/tables/TradingRewardHistoryTa
import abacusStateManager from '@/lib/abacus';
// TODO: set in env featureFlag config
const REWARDS_HISTORY_START_DATE_MS = isMainnet ? 1706486400000 : 1704844800000;
export const RewardHistoryPanel = () => {
const stringGetter = useStringGetter();
@@ -47,7 +53,20 @@ export const RewardHistoryPanel = () => {
<WithTooltip tooltip="reward-history">
<h3>{stringGetter({ key: STRING_KEYS.REWARD_HISTORY })}</h3>
</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>
<ToggleGroup
items={[
@@ -103,3 +122,7 @@ Styled.Content = styled.div`
${layoutMixins.flexColumn}
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 { useNavigate } from 'react-router-dom';
import { isMainnet } from '@/constants/networks';
import { STRING_KEYS } from '@/constants/localization';
import { AppRoute } from '@/constants/routes';
@@ -27,6 +28,8 @@ const RewardsPage = () => {
const { isTablet, isNotTablet } = useBreakpoints();
const navigate = useNavigate();
const showTradingRewards = testFlags.showTradingRewards || !isMainnet;
return (
<div>
{isTablet && (
@@ -37,7 +40,7 @@ const RewardsPage = () => {
)}
<DetachedSection>
<Styled.GridLayout
showTradingRewards={testFlags.showTradingRewards}
showTradingRewards={showTradingRewards}
showMigratePanel={import.meta.env.VITE_V3_TOKEN_ADDRESS && isNotTablet}
>
{import.meta.env.VITE_V3_TOKEN_ADDRESS && isNotTablet && <Styled.MigratePanel />}
@@ -51,7 +54,7 @@ const RewardsPage = () => {
</>
)}
{testFlags.showTradingRewards && (
{showTradingRewards && (
<Styled.TradingRewardsColumn>
<TradingRewardsSummaryPanel />
{isTablet && <RewardsHelpPanel />}
@@ -60,7 +63,7 @@ const RewardsPage = () => {
)}
{isNotTablet && (
<Styled.OtherColumn showTradingRewards={testFlags.showTradingRewards}>
<Styled.OtherColumn showTradingRewards={showTradingRewards}>
<RewardsHelpPanel />
</Styled.OtherColumn>
)}
+5 -2
View File
@@ -5,18 +5,21 @@ import { layoutMixins } from '@/styles/layoutMixins';
import { DetachedSection } from '@/components/ContentSection';
import { ContentSectionHeader } from '@/components/ContentSectionHeader';
import { useStringGetter } from '@/hooks';
import { StakingPanel } from './StakingPanel';
import { StrideStakingPanel } from './StrideStakingPanel';
import { DYDXBalancePanel } from '../rewards/DYDXBalancePanel';
import { STRING_KEYS } from '@/constants/localization';
export default () => {
const stringGetter = useStringGetter();
return (
<DetachedSection>
<Styled.HeaderSection>
<ContentSectionHeader
title="Staking Rewards"
subtitle="Stake to earn APR. Unstaking can take up to 30 days."
title={stringGetter({ key: STRING_KEYS.STAKING_REWARDS })}
subtitle={stringGetter({ key: STRING_KEYS.STAKING_PAGE_SUBTITLE })}
/>
</Styled.HeaderSection>
+1 -1
View File
@@ -23,7 +23,7 @@ export const StakingPanel = ({ className }: { className?: string }) => {
className={className}
slotHeaderContent={
<Styled.Header>
<Styled.Title>Stake with Keplr</Styled.Title>
<Styled.Title>{stringGetter({ key: STRING_KEYS.STAKE_WITH_KEPLR })}</Styled.Title>
<Styled.Img src="/third-party/keplr.png" alt={stringGetter({ key: STRING_KEYS.KEPLR })} />
</Styled.Header>
}
@@ -24,7 +24,7 @@ export const StrideStakingPanel = ({ className }: { className?: string }) => {
slotHeaderContent={
<Styled.Header>
<Styled.Title>
Liquid Stake with Stride
{stringGetter({ key: STRING_KEYS.LIQUID_STAKE_W_STRIDE })}
<Tag isHighlighted>{stringGetter({ key: STRING_KEYS.NEW })}</Tag>
</Styled.Title>
<Styled.Img src="/third-party/stride.png" alt="Stride" />
@@ -33,7 +33,10 @@ export const StrideStakingPanel = ({ className }: { className?: string }) => {
onClick={() => dispatch(openDialog({ type: DialogTypes.ExternalNavStride }))}
>
<Styled.Description>
{`Stake your ${chainTokenLabel} tokens for st${chainTokenLabel} which you can deploy around the ecosystem.`}
{stringGetter({
key: STRING_KEYS.LIQUID_STAKE_STRIDE_DESCRIPTION,
params: { TOKEN_DENOM: chainTokenLabel },
})}
<Link href={stakingLearnMore} onClick={(e) => e.stopPropagation()}>
{stringGetter({ key: STRING_KEYS.LEARN_MORE })}
</Link>
+2 -2
View File
@@ -3,7 +3,7 @@ import { useSelector, shallowEqual } from 'react-redux';
import styled, { AnyStyledComponent } from 'styled-components';
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';
@@ -43,7 +43,7 @@ export const TradeDialogTrigger = () => {
<Styled.TradeSummary>
<Styled.TradeType>
<span>
{stringGetter({ key: TRADE_TYPE_STRINGS[selectedTradeType].tradeTypeKey })}
{stringGetter({ key: ORDER_TYPE_STRINGS[selectedTradeType].orderTypeKey })}
</span>
<OrderSideTag size={TagSize.Medium} orderSide={selectedOrderSide} />
</Styled.TradeType>
+6
View File
@@ -107,6 +107,12 @@ export const getCurrentMarketHistoricalFundings = createSelector(
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
*/
+5 -5
View File
@@ -48,12 +48,12 @@ export const DepthChartTooltipContent = ({
const depthChartSeries = nearestDatum.key as DepthChartSeries;
return {
[DepthChartSeries.Bids]: MustBigNumber(nearestDatum?.datum.price)
.minus(chartPointAtPointer.price)
.div(nearestDatum?.datum.price),
[DepthChartSeries.Asks]: MustBigNumber(chartPointAtPointer.price)
[DepthChartSeries.Bids]: MustBigNumber(midMarketPrice)
.minus(nearestDatum?.datum.price)
.div(chartPointAtPointer.price),
.div(midMarketPrice),
[DepthChartSeries.Asks]: MustBigNumber(nearestDatum?.datum.price)
.minus(midMarketPrice)
.div(midMarketPrice),
[DepthChartSeries.MidMarket]: undefined,
}[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 { 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 { DEFAULT_RESOLUTION, RESOLUTION_CHART_CONFIGS } from '@/constants/candles';
import { useTradingView, useTradingViewTheme } from '@/hooks/tradingView';
import type { TvWidget } from '@/constants/tvchart';
import {
useChartLines,
useChartMarketAndResolution,
useTradingView,
useTradingViewTheme,
} from '@/hooks/tradingView';
import { LoadingSpace } from '@/components/Loading/LoadingSpinner';
import { setTvChartResolution } from '@/state/perpetuals';
import { getCurrentMarketId, getSelectedResolutionForMarket } from '@/state/perpetualsSelectors';
import { layoutMixins } from '@/styles/layoutMixins';
type TvWidget = IChartingLibraryWidget & { _id?: string; _ready?: boolean };
export const TvChart = () => {
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 tvWidget = tvWidgetRef.current;
const isWidgetReady = tvWidget?._ready;
const chart = isWidgetReady ? tvWidget?.chart() : undefined;
const chartResolution = chart?.resolution?.();
const { savedResolution } = useTradingView({ tvWidgetRef, setIsChartReady });
useTradingViewTheme({ tvWidget, isWidgetReady });
const displayButtonRef = useRef<HTMLElement | null>(null);
const displayButton = displayButtonRef.current;
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 });
};
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]);
const { savedResolution } = useTradingView({ tvWidgetRef, displayButtonRef, setIsChartReady });
useChartMarketAndResolution({
tvWidget,
isWidgetReady,
savedResolution: savedResolution as ResolutionString | undefined,
});
const { chartLines } = useChartLines({ tvWidget, displayButton, isChartReady });
useTradingViewTheme({ tvWidget, isWidgetReady, chartLines });
return (
<Styled.PriceChart isChartReady={isChartReady}>
+3
View File
@@ -15,6 +15,7 @@ type ElementProps = {
link: string;
linkDescription?: string;
title?: ReactNode;
slotContent?: ReactNode;
setIsOpen: (open: boolean) => void;
};
@@ -24,6 +25,7 @@ export const ExternalLinkDialog = ({
link,
linkDescription,
title,
slotContent,
}: ElementProps) => {
const stringGetter = useStringGetter();
return (
@@ -36,6 +38,7 @@ export const ExternalLinkDialog = ({
}
>
<Styled.Content>
{slotContent}
<p>{stringGetter({ key: STRING_KEYS.LEAVING_WEBSITE_DISCLAIMER })}.</p>
<Button type={ButtonType.Link} action={ButtonAction.Primary} href={link}>
{buttonText ?? stringGetter({ key: STRING_KEYS.CONTINUE })}
+36 -3
View File
@@ -1,6 +1,9 @@
import { useCallback } from 'react';
import styled, { type AnyStyledComponent } from 'styled-components';
import { useDispatch } from 'react-redux';
import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
import { DialogTypes } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { useBreakpoints, useStringGetter, useURLConfigs } from '@/hooks';
@@ -9,6 +12,8 @@ import { Dialog, DialogPlacement } from '@/components/Dialog';
import { IconName } from '@/components/Icon';
import { IconButton } from '@/components/IconButton';
import { closeDialog, openDialog } from '@/state/dialogs';
import { layoutMixins } from '@/styles/layoutMixins';
type ElementProps = {
@@ -18,9 +23,29 @@ type ElementProps = {
export const ExternalNavKeplrDialog = ({ setIsOpen }: ElementProps) => {
const stringGetter = useStringGetter();
const { keplrDashboard, accountExportLearnMore } = useURLConfigs();
const dispatch = useDispatch();
const { isTablet } = useBreakpoints();
const onExternalNavDialog = useCallback(() => {
dispatch(closeDialog());
dispatch(
openDialog({
type: DialogTypes.ExternalLink,
dialogProps: {
buttonText: stringGetter({ key: STRING_KEYS.CONTINUE }),
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 }),
},
}),
},
})
);
}, [dispatch]);
return (
<Dialog
isOpen
@@ -29,7 +54,11 @@ export const ExternalNavKeplrDialog = ({ setIsOpen }: ElementProps) => {
placement={isTablet ? DialogPlacement.FullScreen : DialogPlacement.Default}
>
<Styled.Content>
<Styled.Button type={ButtonType.Link} size={ButtonSize.XLarge} href={keplrDashboard}>
<Styled.Button
type={ButtonType.Button}
size={ButtonSize.XLarge}
onClick={onExternalNavDialog}
>
<span>
{stringGetter({
key: STRING_KEYS.NAVIGATE_TO_KEPLR,
@@ -46,7 +75,11 @@ export const ExternalNavKeplrDialog = ({ setIsOpen }: ElementProps) => {
/>
</Styled.Button>
<Styled.Button type={ButtonType.Link} size={ButtonSize.XLarge} href={accountExportLearnMore}>
<Styled.Button
type={ButtonType.Link}
size={ButtonSize.XLarge}
href={accountExportLearnMore}
>
<span>
{stringGetter({
key: STRING_KEYS.LEARN_TO_EXPORT,
+16 -4
View File
@@ -33,15 +33,22 @@ export const ExternalNavStrideDialog = ({ setIsOpen }: ElementProps) => {
dialogProps: {
buttonText: (
<Styled.Span>
Liquid Stake on Stride <Icon iconName={IconName.LinkOut} />
{stringGetter({ key: STRING_KEYS.LIQUID_STAKE_ON_STRIDE })}
<Icon iconName={IconName.LinkOut} />
</Styled.Span>
),
link: strideZoneApp,
title: 'Liquid staking and leaving website',
title: stringGetter({ key: STRING_KEYS.LIQUID_STAKING_AND_LEAVING }),
slotContent: stringGetter({
key: STRING_KEYS.LIQUID_STAKING_AND_LEAVING_DESCRIPTION,
params: {
CTA: stringGetter({ key: STRING_KEYS.LIQUID_STAKE_ON_STRIDE }),
},
}),
},
})
);
}, [dispatch]);
}, [dispatch, stringGetter]);
return (
<Dialog
@@ -57,7 +64,12 @@ export const ExternalNavStrideDialog = ({ setIsOpen }: ElementProps) => {
onClick={openExternalNavDialog}
>
<span>
<strong>{stringGetter({ key: STRING_KEYS.YES })}</strong>, Navigate to Stride Zone.
{stringGetter({
key: STRING_KEYS.NAVIGATE_TO_STRIDE,
params: {
STRONG_YES: <strong>{stringGetter({ key: STRING_KEYS.YES })}</strong>,
},
})}
</span>
<Styled.IconButton
@@ -44,7 +44,6 @@ export const GenerateKeys = ({
onKeysDerived = () => {},
}: ElementProps) => {
const stringGetter = useStringGetter();
const { isMobile } = useBreakpoints();
const [shouldRememberMe, setShouldRememberMe] = useState(false);
@@ -66,17 +65,30 @@ export const GenerateKeys = ({
try {
await matchNetwork?.();
return true;
} catch (error) {
const { message, walletErrorType } = parseWalletError({ error, stringGetter });
const { message, walletErrorType, isErrorExpected } = parseWalletError({
error,
stringGetter,
});
if (!isErrorExpected) {
log('GenerateKeys/switchNetwork', error, { walletErrorType });
}
if (message) {
log('GenerateKeys/switchNetwork', error, { walletErrorType });
setError(message);
throw error;
}
return false;
}
};
const switchNetworkAndDeriveKeys = async () => {
const networkSwitched = await switchNetwork();
if (networkSwitched) await deriveKeys();
};
// 2. Derive keys from EVM account
const { getWalletFromEvmSignature } = useDydxClient();
const { getSubaccounts } = useAccounts();
@@ -154,11 +166,16 @@ export const GenerateKeys = ({
setStatus(EvmDerivedAccountStatus.Derived);
} catch (error) {
setStatus(EvmDerivedAccountStatus.NotDerived);
const { message, walletErrorType } = parseWalletError({ error, stringGetter });
const { message, walletErrorType, isErrorExpected } = parseWalletError({
error,
stringGetter,
});
if (message) {
setError(message);
log('GenerateKeys/deriveKeys', error, { walletErrorType });
if (!isErrorExpected) {
log('GenerateKeys/deriveKeys', error, { walletErrorType });
}
}
}
};
@@ -231,7 +248,7 @@ export const GenerateKeys = ({
{!isMatchingNetwork ? (
<Button
action={ButtonAction.Primary}
onClick={() => switchNetwork().then(deriveKeys).then(onKeysDerived)}
onClick={() => switchNetworkAndDeriveKeys().then(onKeysDerived)}
state={{ isLoading: isSwitchingNetwork }}
>
{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 { 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 { layoutMixins } from '@/styles/layoutMixins';
@@ -11,7 +11,12 @@ import { ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP, isMainnet } from '@/constants/networks';
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 {
useAccounts,
@@ -329,6 +334,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)) {
+7 -2
View File
@@ -14,6 +14,7 @@ import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/b
import { TOKEN_DECIMALS } from '@/constants/numbers';
import { STRING_KEYS } from '@/constants/localization';
import { MobilePlaceOrderSteps } from '@/constants/trade';
import { useBreakpoints, useIsFirstRender, useStringGetter, useSubaccount } from '@/hooks';
import { useOnLastOrderIndexed } from '@/hooks/useOnLastOrderIndexed';
@@ -35,7 +36,6 @@ import { Orderbook, orderbookMixins, type OrderbookScrollBehavior } from '@/view
import { PositionPreview } from '@/views/forms/TradeForm/PositionPreview';
import { getCurrentMarketPositionData } from '@/state/accountSelectors';
import { getCurrentMarketAssetData } from '@/state/assetsSelectors';
import { getClosePositionInputErrors, getInputClosePositionData } from '@/state/inputsSelectors';
import { getCurrentMarketConfig, getCurrentMarketId } from '@/state/perpetualsSelectors';
@@ -291,9 +291,14 @@ export const ClosePositionForm = ({
isLoading={isClosingPosition}
hasValidationErrors={hasInputErrors}
actionStringKey={inputAlert?.actionStringKey}
validationErrorString={alertContent}
summary={summary ?? undefined}
currentStep={currentStep}
isClosePosition
confirmButtonConfig={{
stringKey: STRING_KEYS.CLOSE_ORDER,
buttonTextStringKey: STRING_KEYS.CLOSE_POSITION,
buttonAction: ButtonAction.Destroy,
}}
/>
</Styled.Footer>
</Styled.ClosePositionForm>
+41 -5
View File
@@ -1,8 +1,10 @@
import { type FormEvent, useState, Ref, useCallback, useEffect } from 'react';
import { type FormEvent, useState, Ref, useCallback } from 'react';
import styled, { AnyStyledComponent, css } from 'styled-components';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import type { NumberFormatValues, SourceInfo } from 'react-number-format';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { AlertType } from '@/constants/alerts';
import {
@@ -16,7 +18,12 @@ import {
import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { USD_DECIMALS } from '@/constants/numbers';
import { InputErrorData, TradeBoxKeys, MobilePlaceOrderSteps } from '@/constants/trade';
import {
InputErrorData,
TradeBoxKeys,
MobilePlaceOrderSteps,
ORDER_TYPE_STRINGS,
} from '@/constants/trade';
import { breakpoints } from '@/styles';
import { useStringGetter, useSubaccount } from '@/hooks';
@@ -37,11 +44,16 @@ import { WithTooltip } from '@/components/WithTooltip';
import { Orderbook } from '@/views/tables/Orderbook';
import { setTradeFormInputs } from '@/state/inputs';
import { getCurrentInput, getTradeFormInputs, useTradeFormData } from '@/state/inputsSelectors';
import {
getCurrentInput,
getInputTradeData,
getTradeFormInputs,
useTradeFormData,
} from '@/state/inputsSelectors';
import { getCurrentMarketConfig } from '@/state/perpetualsSelectors';
import abacusStateManager from '@/lib/abacus';
import { getTradeInputAlert } from '@/lib/tradeData';
import { getSelectedOrderSide, getSelectedTradeType, getTradeInputAlert } from '@/lib/tradeData';
import { AdvancedTradeOptions } from './TradeForm/AdvancedTradeOptions';
import { TradeSizeInputs } from './TradeForm/TradeSizeInputs';
@@ -108,8 +120,21 @@ export const TradeForm = ({
const tradeFormInputValues = useSelector(getTradeFormInputs, shallowEqual);
const { limitPriceInput, triggerPriceInput, trailingPercentInput } = tradeFormInputValues;
const currentTradeData = useSelector(getInputTradeData, shallowEqual);
const { side, type } = currentTradeData || {};
const selectedTradeType = getSelectedTradeType(type);
const selectedOrderSide = getSelectedOrderSide(side);
const needsAdvancedOptions =
needsGoodUntil || timeInForceOptions || executionOptions || (needsPostOnly || postOnlyTooltip) || (needsReduceOnly || reduceOnlyTooltip);
needsGoodUntil ||
timeInForceOptions ||
executionOptions ||
needsPostOnly ||
postOnlyTooltip ||
needsReduceOnly ||
reduceOnlyTooltip;
const tradeFormInputs: TradeBoxInputConfig[] = [];
@@ -139,6 +164,11 @@ export const TradeForm = ({
alertType = inputAlert?.type;
}
const orderSideAction = {
[OrderSide.BUY]: ButtonAction.Create,
[OrderSide.SELL]: ButtonAction.Destroy,
}[selectedOrderSide];
const onSubmit = async (e: FormEvent) => {
e.preventDefault();
@@ -319,9 +349,15 @@ export const TradeForm = ({
isLoading={isPlacingOrder}
hasValidationErrors={hasInputErrors}
actionStringKey={inputAlert?.actionStringKey}
validationErrorString={alertContent}
summary={summary ?? undefined}
currentStep={currentStep}
showDeposit={inputAlert?.errorAction === TradeInputErrorAction.DEPOSIT}
confirmButtonConfig={{
stringKey: ORDER_TYPE_STRINGS[selectedTradeType].orderTypeKey,
buttonTextStringKey: STRING_KEYS.PLACE_ORDER,
buttonAction: orderSideAction,
}}
/>
</Styled.Footer>
</Styled.TradeForm>
@@ -1,16 +1,17 @@
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { useDispatch, useSelector } from 'react-redux';
import styled, { type AnyStyledComponent, css } from 'styled-components';
import type { TradeInputSummary } from '@/constants/abacus';
import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
import { DialogTypes } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { TRADE_TYPE_STRINGS, MobilePlaceOrderSteps } from '@/constants/trade';
import { MobilePlaceOrderSteps } from '@/constants/trade';
import { useStringGetter, useTokenConfigs } from '@/hooks';
import { AssetIcon } from '@/components/AssetIcon';
import { Button } from '@/components/Button';
import { Icon, IconName } from '@/components/Icon';
import { Output, OutputType, ShowSign } from '@/components/Output';
import { WithDetailsReceipt } from '@/components/WithDetailsReceipt';
import { WithTooltip } from '@/components/WithTooltip';
@@ -20,30 +21,34 @@ import { OnboardingTriggerButton } from '@/views/dialogs/OnboardingTriggerButton
import { calculateCanAccountTrade } from '@/state/accountCalculators';
import { getSubaccountId } from '@/state/accountSelectors';
import { openDialog } from '@/state/dialogs';
import { getCurrentInput, getInputTradeData } from '@/state/inputsSelectors';
import { getCurrentInput } from '@/state/inputsSelectors';
import { getSelectedOrderSide, getSelectedTradeType } from '@/lib/tradeData';
type ConfirmButtonConfig = {
stringKey: string;
buttonTextStringKey: string;
buttonAction: ButtonAction;
};
type ElementProps = {
isLoading: boolean;
isClosePosition?: boolean;
actionStringKey?: string;
summary?: TradeInputSummary;
hasValidationErrors?: boolean;
validationErrorString?: string;
currentStep?: MobilePlaceOrderSteps;
showDeposit?: boolean;
showConnectWallet?: boolean;
confirmButtonConfig: ConfirmButtonConfig;
};
export const PlaceOrderButtonAndReceipt = ({
isLoading,
isClosePosition,
actionStringKey,
summary,
hasValidationErrors,
validationErrorString,
currentStep,
showDeposit,
showConnectWallet,
confirmButtonConfig,
}: ElementProps) => {
const stringGetter = useStringGetter();
const dispatch = useDispatch();
@@ -52,18 +57,12 @@ export const PlaceOrderButtonAndReceipt = ({
const canAccountTrade = useSelector(calculateCanAccountTrade);
const subaccountNumber = useSelector(getSubaccountId);
const currentInput = useSelector(getCurrentInput);
const currentTradeData = useSelector(getInputTradeData, shallowEqual);
const hasMissingData = subaccountNumber === undefined;
const shouldEnableTrade =
canAccountTrade && !hasMissingData && !hasValidationErrors && currentInput !== 'transfer';
const { side, type } = currentTradeData || {};
const selectedTradeType = getSelectedTradeType(type);
const selectedOrderSide = getSelectedOrderSide(side);
const { fee, price: expectedPrice, total, reward } = summary || {};
const items = [
@@ -110,11 +109,6 @@ export const PlaceOrderButtonAndReceipt = ({
},
];
const orderSideAction = {
[OrderSide.BUY]: ButtonAction.Create,
[OrderSide.SELL]: ButtonAction.Destroy,
}[selectedOrderSide];
const buttonStatesPerStep = {
[MobilePlaceOrderSteps.EditOrder]: {
buttonTextStringKey: shouldEnableTrade
@@ -128,7 +122,7 @@ export const PlaceOrderButtonAndReceipt = ({
[MobilePlaceOrderSteps.PreviewOrder]: {
buttonTextStringKey: STRING_KEYS.CONFIRM_ORDER,
buttonAction: isClosePosition ? ButtonAction.Destroy : orderSideAction,
buttonAction: confirmButtonConfig.buttonAction,
buttonState: { isLoading },
},
[MobilePlaceOrderSteps.PlacingOrder]: {
@@ -145,15 +139,13 @@ export const PlaceOrderButtonAndReceipt = ({
const buttonAction = currentStep
? buttonStatesPerStep[currentStep].buttonAction
: isClosePosition
? ButtonAction.Destroy
: orderSideAction;
: confirmButtonConfig.buttonAction;
let buttonTextStringKey = STRING_KEYS.UNAVAILABLE;
if (currentStep) {
buttonTextStringKey = buttonStatesPerStep[currentStep].buttonTextStringKey;
} else if (shouldEnableTrade) {
buttonTextStringKey = isClosePosition ? STRING_KEYS.CLOSE_POSITION : STRING_KEYS.PLACE_ORDER;
buttonTextStringKey = confirmButtonConfig.buttonTextStringKey;
} else if (actionStringKey) {
buttonTextStringKey = actionStringKey;
}
@@ -165,31 +157,56 @@ export const PlaceOrderButtonAndReceipt = ({
isLoading: isLoading || hasMissingData,
};
const depositButton = (
<Button
action={ButtonAction.Primary}
onClick={() => dispatch(openDialog({ type: DialogTypes.Deposit }))}
>
{stringGetter({ key: STRING_KEYS.DEPOSIT_FUNDS })}
</Button>
);
const submitButton = (
<Styled.Button
state={buttonState}
type={ButtonType.Submit}
action={buttonAction}
slotLeft={
hasValidationErrors ? <Styled.WarningIcon iconName={IconName.Warning} /> : undefined
}
>
{stringGetter({
key: buttonTextStringKey,
params: {
ORDER: stringGetter({
key: confirmButtonConfig.stringKey,
}),
},
})}
</Styled.Button>
);
return (
<WithDetailsReceipt detailItems={items}>
{!canAccountTrade || showConnectWallet ? (
{!canAccountTrade ? (
<OnboardingTriggerButton size={ButtonSize.Base} />
) : showDeposit ? (
<Button
action={ButtonAction.Primary}
onClick={() => dispatch(openDialog({ type: DialogTypes.Deposit }))}
>
{stringGetter({ key: STRING_KEYS.DEPOSIT_FUNDS })}
</Button>
depositButton
) : (
<Button state={buttonState} type={ButtonType.Submit} action={buttonAction}>
{stringGetter({
key: buttonTextStringKey,
params: {
ORDER: stringGetter({
key: isClosePosition
? STRING_KEYS.CLOSE_ORDER
: TRADE_TYPE_STRINGS[selectedTradeType].tradeTypeKey,
}),
},
})}
</Button>
<WithTooltip tooltipString={hasValidationErrors ? validationErrorString : undefined}>
{submitButton}
</WithTooltip>
)}
</WithDetailsReceipt>
);
};
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Button = styled(Button)`
width: 100%;
`;
Styled.WarningIcon = styled(Icon)`
color: var(--color-warning);
`;
@@ -11,7 +11,7 @@ import {
} from '@/constants/abacus';
import { STRING_KEYS } from '@/constants/localization';
import { TRADE_TYPE_STRINGS, TradeTypes } from '@/constants/trade';
import { ORDER_TYPE_STRINGS, TradeTypes } from '@/constants/trade';
import { useStringGetter } from '@/hooks';
import { layoutMixins } from '@/styles/layoutMixins';
@@ -48,7 +48,7 @@ export const TradeNotification = ({ isToast, data, notification }: TradeNotifica
const { assetId } = marketData ?? {};
const orderType = ORDER_TYPE as KotlinIrEnumValues<typeof AbacusOrderType>;
const tradeType = TRADE_TYPES[orderType];
const titleKey = tradeType && TRADE_TYPE_STRINGS[tradeType]?.tradeTypeKey;
const titleKey = tradeType && ORDER_TYPE_STRINGS[tradeType]?.orderTypeKey;
const orderStatus = ORDER_STATUS as KotlinIrEnumValues<typeof AbacusOrderStatus>;
return (
Binary file not shown.