Compare commits

...
Author SHA1 Message Date
jaredvu 5a91e56ac3 🚧 WAGMI 2 2024-01-09 13:28:21 -08:00
BillandAleka Cheung 4a64d59546 Update launch incentives to season 2 (#218)
* Update launch incentives to season 2

* update api url

---------

Co-authored-by: Aleka Cheung <aforaleka@gmail.com>
2024-01-04 19:14:23 -08:00
Jared Vu 3fc43e8a12 Remove base from baseInitialMarginFraction (#209) 2024-01-04 15:35:05 -08:00
Jared Vu c24a9229a6 Imperator testnet indexer to be scaled down (#219) 2024-01-04 15:34:18 -08:00
aleka 90329d44d0 update lock file (#217) 2024-01-03 13:57:21 -08:00
Bill 9a02bd3f1c remove testnet cctp chains (#216) 2024-01-03 12:55:58 -08:00
aleka adba11b462 add accordion component and rewards help panel (#214) 2024-01-03 14:20:30 -05:00
Jared Vu 29526b927f Update deposit and withdrawal receipts (#212)
* Update receipts

* Remove duplicate const

* Restore env

* format

* Try using raw

* Use 3 spaces T.T

* copy from main

* Update abacus, display proper toAmount and toAmountMin

* nits
2024-01-03 09:56:01 -08:00
Jared Vu 503e089efa temp hide fees for cctp route (#213) 2023-12-23 01:17:41 -08:00
Jared Vu c3495f462c Additional Withdraw/Deposit validation (#211)
* Add cctp amount check and price impact check

* 2.5% -> 2%
2023-12-22 17:31:32 -08:00
Billandjaredvu 6da6eeb2a4 Enable CCTP (#194)
* Enable CCTP

* Handle base64 encode

* Update localization package

---------

Co-authored-by: jaredvu <jaredvu@gmail.com>
2023-12-22 11:12:41 -08:00
mike-dydx 0cf3e81bd7 Create documentation.json (#205) 2023-12-21 15:08:08 -05:00
aleka f8cf1d364b update coinbase connect app name (#208) 2023-12-21 10:58:57 -08:00
Jared Vu 6f344c6b5d Update comparison function in Table (#207) 2023-12-21 10:53:56 -08:00
Jared Vu 133856afca Export secret phrase help article (#206)
* Update config with correct article link

* Remove auto-format of json
2023-12-21 10:49:52 -08:00
Jared Vu 33c543daaf Update bridgeFee in Withdraw/Deposit forms (#202)
* Update Withdraw form receipt

* Bump abacus 1.1.27

* pnpm-lock

* bump v4-localization
2023-12-19 23:36:13 -08:00
Jared Vu c77e727d2b TRCL-3406: firefox mobile position table row has stretched icon (#204)
* Fix PositionsTable assetIcon

* Mobile width of HelpDialog
2023-12-19 23:33:36 -08:00
Jared Vu b94538711d Lazy load Pages (#203)
* Lazy import pages

* Fix import order
2023-12-19 23:30:51 -08:00
Jared Vu b83479f45c Handle New Markets added by Gov (#201)
* Add celestia assets

* Handle symbol not yet added
2023-12-19 23:25:14 -08:00
Jared Vu 001aad3abc Navigate to the Markets page if the current marketId is not included in subscribed markets (#198)
* Navigate to markets if current url is invalid

* nits
2023-12-19 15:58:49 -08:00
Jared Vu f8db5e1ba0 Update abacus to 1.1.25: Good til time fix (#199) 2023-12-19 09:49:51 -08:00
Jared Vu f8bec0fb3d Update Withdraw form receipt (#200) 2023-12-19 09:49:37 -08:00
Bill 8bf23b7e9c bump abacus (#197) 2023-12-15 15:12:35 -08:00
Jared Vu 3f10e045f4 bump abacus & wagmi (#196) 2023-12-15 09:26:41 -08:00
Bill 71d1a0321b Do not block website if NobleClient cannot init, and other onboarding fixes (#193) 2023-12-13 08:11:19 -08:00
Bill 0f16396c8d Implement CCTP withdraw (#188)
* Implement CCTP withdraw

* bump packages

* address comments
2023-12-12 10:12:37 -08:00
Jared Vu 2709d79f59 Firefox issues (#190)
* remove row flicker on hover

* Fix border for AccountInfoConnectedState on mobile

* Remove sticky position to allow button click

* Fix: AccountInfo not displaying on mobile firefox

* Fix css var name

* Restore filter on Table row hover

* Remove PerspectiveArea. broken on firefox

* Symbol no longer exists on AbacusType

* HorizontalPanel: memoize tabConfig

* input: firefox cant click between chars
2023-12-12 08:37:50 -08:00
Bill ab0eec5d4f Update noble tesnet rpc (#192) 2023-12-12 11:35:21 -05:00
Jared Vu 4a044da38f Size inputs (#191)
* use formattedValue to update redux state

* Separate updateLeverage and onLeverageInput

* Remove console.log
2023-12-12 07:11:51 -08:00
Jared Vu fbbfbc6580 Bump v4-abacus and v4-localization (#189) 2023-12-08 15:41:07 -08:00
Rui 2714c2b084 Fix path to chainLogo (#187) 2023-12-07 13:03:44 -08:00
74 changed files with 6784 additions and 2077 deletions
+8 -6
View File
@@ -39,11 +39,12 @@
"@cosmjs/proto-signing": "^0.31.0", "@cosmjs/proto-signing": "^0.31.0",
"@cosmjs/stargate": "^0.31.0", "@cosmjs/stargate": "^0.31.0",
"@cosmjs/tendermint-rpc": "^0.31.0", "@cosmjs/tendermint-rpc": "^0.31.0",
"@dydxprotocol/v4-abacus": "^1.1.4", "@dydxprotocol/v4-abacus": "^1.1.32",
"@dydxprotocol/v4-client-js": "^1.0.6", "@dydxprotocol/v4-client-js": "^1.0.11",
"@dydxprotocol/v4-localization": "^1.0.17", "@dydxprotocol/v4-localization": "^1.1.6",
"@ethersproject/providers": "^5.7.2", "@ethersproject/providers": "^5.7.2",
"@js-joda/core": "^5.5.3", "@js-joda/core": "^5.5.3",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-collapsible": "^1.0.3", "@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-dialog": "^1.0.4", "@radix-ui/react-dialog": "^1.0.4",
@@ -71,6 +72,7 @@
"@reduxjs/toolkit": "^1.9.5", "@reduxjs/toolkit": "^1.9.5",
"@scure/bip32": "^1.3.0", "@scure/bip32": "^1.3.0",
"@scure/bip39": "^1.2.0", "@scure/bip39": "^1.2.0",
"@tanstack/react-query": "^5.17.9",
"@types/lodash": "^4.14.195", "@types/lodash": "^4.14.195",
"@types/styled-components": "^5.1.26", "@types/styled-components": "^5.1.26",
"@visx/axis": "^3.1.0", "@visx/axis": "^3.1.0",
@@ -103,15 +105,15 @@
"react-aria": "^3.25.0", "react-aria": "^3.25.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-number-format": "^5.2.2", "react-number-format": "^5.2.2",
"react-query": "^3.39.3",
"react-redux": "^8.1.1", "react-redux": "^8.1.1",
"react-router-dom": "^6.14.0", "react-router-dom": "^6.14.0",
"react-stately": "^3.23.0", "react-stately": "^3.23.0",
"reselect": "^4.1.8", "reselect": "^4.1.8",
"stream-browserify": "^3.0.0",
"styled-components": "^5.3.11", "styled-components": "^5.3.11",
"use-latest": "^1.2.1", "use-latest": "^1.2.1",
"viem": "^1.12.2", "viem": "^2.0.0",
"wagmi": "^1.4.5" "wagmi": "^2.0.3"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.22.5", "@babel/core": "^7.22.5",
+5133 -813
View File
File diff suppressed because it is too large Load Diff
-49
View File
@@ -1,61 +1,12 @@
[ [
{
"chainId": "42161",
"tokenAddress": "0xff970a61a04b1ca14834a43f5de4533ebddb5cc8",
"name": "Arbitrum"
},
{
"chainId": "43114",
"tokenAddress": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"name": "Avalanche"
},
{
"chainId": "8453",
"tokenAddress": "0x66627F389ae46D881773B7131139b2411980E09E",
"name": "Base"
},
{ {
"chainId": "1", "chainId": "1",
"tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"name": "Ethereum" "name": "Ethereum"
}, },
{
"chainId": "10",
"tokenAddress": "0x7F5c764cBc14f9669B88837ca1490cCa17c31607",
"name": "OP Mainnet"
},
{
"chainId": "421613",
"tokenAddress": "0xfd064a18f3bf249cf1f87fc203e90d8f650f2d63",
"name": "Arbitrum Goerli"
},
{
"chainId": "43113",
"tokenAddress": "0x5425890298aed601595a70AB815c96711a31Bc65",
"name": "Avalanche Fuji"
},
{
"chainId": "84531",
"tokenAddress": "0xf175520c52418dfe19c8098071a252da48cd1c19",
"name": "Base Goerli"
},
{ {
"chainId": "5", "chainId": "5",
"tokenAddress": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F", "tokenAddress": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F",
"name": "Ethereum Goerli" "name": "Ethereum Goerli"
},
{
"chainId": "grand-1",
"tokenAddress": "uusdc",
"name": "Noble Testnet"
},
{
"chainId": "420",
"tokenAddress": "0xe05606174bac4a6364b31bd0eca4bf4dd368f8c6",
"name": "OP Goerli"
},
{
"chainId": "103",
"name": "Solana Devnet"
} }
] ]
+16
View File
@@ -0,0 +1,16 @@
{
"tradingRewardsFAQs": [
{
"questionLocalizationKey": "APP.TRADING_REWARDS.FAQ_WHO_IS_ELIGIBLE_QUESTION",
"answerLocalizationKey": "APP.TRADING_REWARDS.FAQ_WHO_IS_ELIGIBLE_ANSWER"
},
{
"questionLocalizationKey": "APP.TRADING_REWARDS.FAQ_HOW_DO_TRADING_REWARDS_WORK_QUESTION",
"answerLocalizationKey": "APP.TRADING_REWARDS.FAQ_HOW_DO_TRADING_REWARDS_WORK_ANSWER"
},
{
"questionLocalizationKey": "APP.TRADING_REWARDS.FAQ_HOW_DO_I_CLAIM_MY_REWARDS_QUESTION",
"answerLocalizationKey": "APP.TRADING_REWARDS.FAQ_HOW_DO_I_CLAIM_MY_REWARDS_ANSWER"
}
]
}
+67 -67
View File
@@ -39,10 +39,10 @@
"environments": { "environments": {
"dydxprotocol-dev": { "dydxprotocol-dev": {
"name": "v4 Dev", "name": "v4 Dev",
"ethereumChainId": "5", "ethereumChainId": "11155111",
"dydxChainId": "dydxprotocol-testnet", "dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -71,7 +71,7 @@
"https://validator.v4dev.dydx.exchange" "https://validator.v4dev.dydx.exchange"
], ],
"0xsquid": "https://testnet.api.0xsquid.com", "0xsquid": "https://testnet.api.0xsquid.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love", "nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4dev.dydx.exchange" "faucet": "https://faucet.v4dev.dydx.exchange"
}, },
"links": { "links": {
@@ -89,7 +89,7 @@
"governanceLearnMore": "https://help.dydx.exchange", "governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange", "stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/", "keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange", "accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet" "walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
}, },
"wallets": { "wallets": {
@@ -113,10 +113,10 @@
}, },
"dydxprotocol-dev-2": { "dydxprotocol-dev-2": {
"name": "v4 Dev 2", "name": "v4 Dev 2",
"ethereumChainId": "5", "ethereumChainId": "11155111",
"dydxChainId": "dydxprotocol-testnet", "dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -145,7 +145,7 @@
"http://54.92.118.111" "http://54.92.118.111"
], ],
"0xsquid": "https://testnet.api.0xsquid.com", "0xsquid": "https://testnet.api.0xsquid.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love" "nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
}, },
"links": { "links": {
"tos": "https://dydx.exchange/v4-terms", "tos": "https://dydx.exchange/v4-terms",
@@ -162,7 +162,7 @@
"governanceLearnMore": "https://help.dydx.exchange", "governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange", "stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/", "keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange", "accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet" "walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
}, },
"wallets": { "wallets": {
@@ -186,10 +186,10 @@
}, },
"dydxprotocol-dev-4": { "dydxprotocol-dev-4": {
"name": "v4 Dev 4", "name": "v4 Dev 4",
"ethereumChainId": "5", "ethereumChainId": "11155111",
"dydxChainId": "dydxprotocol-testnet", "dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -218,7 +218,7 @@
"http://validator.v4dev4.dydx.exchange" "http://validator.v4dev4.dydx.exchange"
], ],
"0xsquid": "https://testnet.api.0xsquid.com", "0xsquid": "https://testnet.api.0xsquid.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love" "nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
}, },
"links": { "links": {
"tos": "https://dydx.exchange/v4-terms", "tos": "https://dydx.exchange/v4-terms",
@@ -235,7 +235,7 @@
"governanceLearnMore": "https://help.dydx.exchange", "governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange", "stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/", "keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange", "accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet" "walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
}, },
"wallets": { "wallets": {
@@ -259,10 +259,10 @@
}, },
"dydxprotocol-dev-5": { "dydxprotocol-dev-5": {
"name": "v4 Dev 5", "name": "v4 Dev 5",
"ethereumChainId": "5", "ethereumChainId": "11155111",
"dydxChainId": "dydxprotocol-testnet", "dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -291,7 +291,7 @@
"http://18.223.78.50" "http://18.223.78.50"
], ],
"0xsquid": "https://testnet.api.0xsquid.com", "0xsquid": "https://testnet.api.0xsquid.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love" "nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
}, },
"links": { "links": {
"tos": "https://dydx.exchange/v4-terms", "tos": "https://dydx.exchange/v4-terms",
@@ -308,7 +308,7 @@
"governanceLearnMore": "https://help.dydx.exchange", "governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange", "stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/", "keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange", "accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet" "walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
}, },
"wallets": { "wallets": {
@@ -332,10 +332,10 @@
}, },
"dydxprotocol-staging": { "dydxprotocol-staging": {
"name": "v4 Staging", "name": "v4 Staging",
"ethereumChainId": "5", "ethereumChainId": "11155111",
"dydxChainId": "dydxprotocol-testnet", "dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -365,7 +365,7 @@
"https://validator.v4staging.dydx.exchange" "https://validator.v4staging.dydx.exchange"
], ],
"0xsquid": "https://testnet.api.squidrouter.com", "0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love" "nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
}, },
"links": { "links": {
"tos": "https://dydx.exchange/v4-terms", "tos": "https://dydx.exchange/v4-terms",
@@ -383,7 +383,7 @@
"governanceLearnMore": "https://help.dydx.exchange", "governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange", "stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/", "keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange", "accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet" "walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
}, },
"wallets": { "wallets": {
@@ -407,10 +407,10 @@
}, },
"dydxprotocol-staging-forced-update": { "dydxprotocol-staging-forced-update": {
"name": "v4 Staging Forced Update", "name": "v4 Staging Forced Update",
"ethereumChainId": "5", "ethereumChainId": "11155111",
"dydxChainId": "dydxprotocol-testnet", "dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -440,7 +440,7 @@
"https://validator.v4staging.dydx.exchange" "https://validator.v4staging.dydx.exchange"
], ],
"0xsquid": "https://testnet.api.squidrouter.com", "0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love" "nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
}, },
"links": { "links": {
"tos": "https://dydx.exchange/v4-terms", "tos": "https://dydx.exchange/v4-terms",
@@ -480,10 +480,10 @@
}, },
"dydxprotocol-staging-west": { "dydxprotocol-staging-west": {
"name": "v4 Staging West", "name": "v4 Staging West",
"ethereumChainId": "5", "ethereumChainId": "11155111",
"dydxChainId": "dydxprotocol-testnet", "dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -513,7 +513,7 @@
"https://validator-uswest1.v4staging.dydx.exchange" "https://validator-uswest1.v4staging.dydx.exchange"
], ],
"0xsquid": "https://testnet.api.squidrouter.com", "0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love" "nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
}, },
"links": { "links": {
"tos": "https://dydx.exchange/v4-terms", "tos": "https://dydx.exchange/v4-terms",
@@ -531,7 +531,7 @@
"governanceLearnMore": "https://help.dydx.exchange", "governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange", "stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/", "keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange", "accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet" "walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
}, },
"wallets": { "wallets": {
@@ -555,10 +555,10 @@
}, },
"dydxprotocol-testnet": { "dydxprotocol-testnet": {
"name": "v4 Public Testnet", "name": "v4 Public Testnet",
"ethereumChainId": "5", "ethereumChainId": "11155111",
"dydxChainId": "dydx-testnet-4", "dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -579,8 +579,8 @@
"endpoints": { "endpoints": {
"indexers": [ "indexers": [
{ {
"api": "https://dydx-testnet.imperator.co", "api": "https://indexer.v4testnet.dydx.exchange",
"socket": "wss://dydx-testnet.imperator.co" "socket": "wss://indexer.v4testnet.dydx.exchange"
} }
], ],
"validators": [ "validators": [
@@ -591,7 +591,7 @@
"https://dydx-rpc.liquify.com/api=8878132/dydx" "https://dydx-rpc.liquify.com/api=8878132/dydx"
], ],
"0xsquid": "https://testnet.api.squidrouter.com", "0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love", "nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange" "faucet": "https://faucet.v4testnet.dydx.exchange"
}, },
"links": { "links": {
@@ -610,7 +610,7 @@
"governanceLearnMore": "https://help.dydx.exchange", "governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange", "stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/", "keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange", "accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet" "walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
}, },
"wallets": { "wallets": {
@@ -634,10 +634,10 @@
}, },
"dydxprotocol-testnet-dydx": { "dydxprotocol-testnet-dydx": {
"name": "v4 Public Testnet/dYdX", "name": "v4 Public Testnet/dYdX",
"ethereumChainId": "5", "ethereumChainId": "11155111",
"dydxChainId": "dydx-testnet-4", "dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -666,7 +666,7 @@
"https://validator.v4testnet.dydx.exchange" "https://validator.v4testnet.dydx.exchange"
], ],
"0xsquid": "https://testnet.api.squidrouter.com", "0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love", "nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange" "faucet": "https://faucet.v4testnet.dydx.exchange"
}, },
"links": { "links": {
@@ -686,7 +686,7 @@
"governanceLearnMore": "https://help.dydx.exchange", "governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange", "stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/", "keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange", "accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet" "walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
}, },
"wallets": { "wallets": {
@@ -710,10 +710,10 @@
}, },
"dydxprotocol-testnet-nodefleet": { "dydxprotocol-testnet-nodefleet": {
"name": "v4 Public Testnet/nodefleet", "name": "v4 Public Testnet/nodefleet",
"ethereumChainId": "5", "ethereumChainId": "11155111",
"dydxChainId": "dydx-testnet-4", "dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -734,15 +734,15 @@
"endpoints": { "endpoints": {
"indexers": [ "indexers": [
{ {
"api": "https://dydx-testnet.imperator.co", "api": "https://indexer.v4testnet.dydx.exchange",
"socket": "wss://dydx-testnet.imperator.co" "socket": "wss://indexer.v4testnet.dydx.exchange"
} }
], ],
"validators": [ "validators": [
"https://dydx-testnet.nodefleet.org" "https://dydx-testnet.nodefleet.org"
], ],
"0xsquid": "https://testnet.api.squidrouter.com", "0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love", "nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange" "faucet": "https://faucet.v4testnet.dydx.exchange"
}, },
"links": { "links": {
@@ -762,7 +762,7 @@
"governanceLearnMore": "https://help.dydx.exchange", "governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange", "stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/", "keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange", "accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet" "walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
}, },
"wallets": { "wallets": {
@@ -786,10 +786,10 @@
}, },
"dydxprotocol-testnet-kingnodes": { "dydxprotocol-testnet-kingnodes": {
"name": "v4 Public Testnet/KingNodes", "name": "v4 Public Testnet/KingNodes",
"ethereumChainId": "5", "ethereumChainId": "11155111",
"dydxChainId": "dydx-testnet-4", "dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -810,15 +810,15 @@
"endpoints": { "endpoints": {
"indexers": [ "indexers": [
{ {
"api": "https://dydx-testnet.imperator.co", "api": "https://indexer.v4testnet.dydx.exchange",
"socket": "wss://dydx-testnet.imperator.co" "socket": "wss://indexer.v4testnet.dydx.exchange"
} }
], ],
"validators": [ "validators": [
"https://test-dydx.kingnodes.com" "https://test-dydx.kingnodes.com"
], ],
"0xsquid": "https://testnet.api.squidrouter.com", "0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love", "nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange" "faucet": "https://faucet.v4testnet.dydx.exchange"
}, },
"links": { "links": {
@@ -838,7 +838,7 @@
"governanceLearnMore": "https://help.dydx.exchange", "governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange", "stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/", "keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange", "accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet" "walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
}, },
"wallets": { "wallets": {
@@ -862,10 +862,10 @@
}, },
"dydxprotocol-testnet-liquify": { "dydxprotocol-testnet-liquify": {
"name": "v4 Public Testnet/Liquify", "name": "v4 Public Testnet/Liquify",
"ethereumChainId": "5", "ethereumChainId": "11155111",
"dydxChainId": "dydx-testnet-4", "dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -886,15 +886,15 @@
"endpoints": { "endpoints": {
"indexers": [ "indexers": [
{ {
"api": "https://dydx-testnet.imperator.co", "api": "https://indexer.v4testnet.dydx.exchange",
"socket": "wss://dydx-testnet.imperator.co" "socket": "wss://indexer.v4testnet.dydx.exchange"
} }
], ],
"validators": [ "validators": [
"https://dydx-rpc.liquify.com/api=8878132/dydx" "https://dydx-rpc.liquify.com/api=8878132/dydx"
], ],
"0xsquid": "https://testnet.api.squidrouter.com", "0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love", "nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange" "faucet": "https://faucet.v4testnet.dydx.exchange"
}, },
"links": { "links": {
@@ -914,7 +914,7 @@
"governanceLearnMore": "https://help.dydx.exchange", "governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange", "stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/", "keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange", "accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet" "walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
}, },
"wallets": { "wallets": {
@@ -938,10 +938,10 @@
}, },
"dydxprotocol-testnet-polkachu": { "dydxprotocol-testnet-polkachu": {
"name": "v4 Public Testnet/Polkahcu", "name": "v4 Public Testnet/Polkahcu",
"ethereumChainId": "5", "ethereumChainId": "11155111",
"dydxChainId": "dydx-testnet-4", "dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -962,15 +962,15 @@
"endpoints": { "endpoints": {
"indexers": [ "indexers": [
{ {
"api": "https://dydx-testnet.imperator.co", "api": "https://indexer.v4testnet.dydx.exchange",
"socket": "wss://dydx-testnet.imperator.co" "socket": "wss://indexer.v4testnet.dydx.exchange"
} }
], ],
"validators": [ "validators": [
"https://dydx-testnet-rpc.polkachu.com/" "https://dydx-testnet-rpc.polkachu.com/"
], ],
"0xsquid": "https://testnet.api.squidrouter.com", "0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love", "nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange" "faucet": "https://faucet.v4testnet.dydx.exchange"
}, },
"links": { "links": {
@@ -1005,10 +1005,10 @@
}, },
"dydxprotocol-testnet-bware": { "dydxprotocol-testnet-bware": {
"name": "v4 Public Testnet/BWare", "name": "v4 Public Testnet/BWare",
"ethereumChainId": "5", "ethereumChainId": "11155111",
"dydxChainId": "dydx-testnet-4", "dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -1029,15 +1029,15 @@
"endpoints": { "endpoints": {
"indexers": [ "indexers": [
{ {
"api": "https://dydx-testnet.imperator.co", "api": "https://indexer.v4testnet.dydx.exchange",
"socket": "wss://dydx-testnet.imperator.co" "socket": "wss://indexer.v4testnet.dydx.exchange"
} }
], ],
"validators": [ "validators": [
"https://dydx-testnet-full-rpc.public.blastapi.io/" "https://dydx-testnet-full-rpc.public.blastapi.io/"
], ],
"0xsquid": "https://testnet.api.squidrouter.com", "0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love", "nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange" "faucet": "https://faucet.v4testnet.dydx.exchange"
}, },
"links": { "links": {
@@ -1084,7 +1084,7 @@
"ethereumChainId": "1", "ethereumChainId": "1",
"dydxChainId": "[mainnet chain id]", "dydxChainId": "[mainnet chain id]",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "[mainnet squid integrator id]", "squidIntegratorId": "[mainnet squid integrator id]",
"isMainNet": true, "isMainNet": true,
"tokens": { "tokens": {
@@ -1126,7 +1126,7 @@
"blogs": "[HTTP link to blogs, can be null]", "blogs": "[HTTP link to blogs, can be null]",
"foundation": "[HTTP link to foundation, can be null]", "foundation": "[HTTP link to foundation, can be null]",
"initialMarginFractionLearnMore": "[HTTP link to initial margin fraction learn more, can be null]", "initialMarginFractionLearnMore": "[HTTP link to initial margin fraction learn more, can be null]",
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders", "reduceOnlyLearnMore": "[HTTP link to reduce-only learn more, can be null]",
"documentation": "[HTTP link to documentation, can be null]", "documentation": "[HTTP link to documentation, can be null]",
"community": "[HTTP link to community, can be null]", "community": "[HTTP link to community, can be null]",
"help": "[HTTP link to help page, can be null]", "help": "[HTTP link to help page, can be null]",
+57 -151
View File
@@ -1,189 +1,147 @@
{ {
"1INCH-USD": { "1INCH-USD": {
"name": "1inch", "name": "1inch",
"tags":[ "tags": ["Defi"],
"Defi"
],
"websiteLink": "https://1inch.io/", "websiteLink": "https://1inch.io/",
"whitepaperLink": "https://github.com/1inch", "whitepaperLink": "https://github.com/1inch",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/1inch/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/1inch/"
}, },
"AAVE-USD": { "AAVE-USD": {
"name": "Aave", "name": "Aave",
"tags":[ "tags": ["Defi"],
"Defi"
],
"websiteLink": "https://aave.com/", "websiteLink": "https://aave.com/",
"whitepaperLink": "https://github.com/aave/protocol-v2/blob/master/aave-v2-whitepaper.pdf", "whitepaperLink": "https://github.com/aave/protocol-v2/blob/master/aave-v2-whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/aave/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/aave/"
}, },
"ADA-USD": { "ADA-USD": {
"name": "Cardano", "name": "Cardano",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://cardano.org/", "websiteLink": "https://cardano.org/",
"whitepaperLink": "https://why.cardano.org/en/introduction/motivation/", "whitepaperLink": "https://why.cardano.org/en/introduction/motivation/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/cardano/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/cardano/"
}, },
"ALGO-USD": { "ALGO-USD": {
"name": "Algorand", "name": "Algorand",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://algorand.com/", "websiteLink": "https://algorand.com/",
"whitepaperLink": "https://algorand.com/technology/white-papers", "whitepaperLink": "https://algorand.com/technology/white-papers",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/algorand/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/algorand/"
}, },
"APE-USD": { "APE-USD": {
"name": "ApeCoin", "name": "ApeCoin",
"tags":[ "tags": [],
],
"websiteLink": "https://apecoin.com/", "websiteLink": "https://apecoin.com/",
"whitepaperLink": "https://apecoin.com/about", "whitepaperLink": "https://apecoin.com/about",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/apecoin-ape/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/apecoin-ape/"
}, },
"APT-USD": { "APT-USD": {
"name": "Aptos", "name": "Aptos",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://aptoslabs.com/", "websiteLink": "https://aptoslabs.com/",
"whitepaperLink": "https://aptos.dev/aptos-white-paper/", "whitepaperLink": "https://aptos.dev/aptos-white-paper/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/aptos/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/aptos/"
}, },
"ARB-USD": { "ARB-USD": {
"name": "Arbitrum", "name": "Arbitrum",
"tags":[ "tags": [],
],
"websiteLink": "https://arbitrum.io/", "websiteLink": "https://arbitrum.io/",
"whitepaperLink": "https://github.com/OffchainLabs/nitro", "whitepaperLink": "https://github.com/OffchainLabs/nitro",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/arbitrum/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/arbitrum/"
}, },
"ATOM-USD": { "ATOM-USD": {
"name": "Cosmos", "name": "Cosmos",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://cosmos.network/", "websiteLink": "https://cosmos.network/",
"whitepaperLink": "https://v1.cosmos.network/resources/whitepaper", "whitepaperLink": "https://v1.cosmos.network/resources/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/cosmos/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/cosmos/"
}, },
"AVAX-USD": { "AVAX-USD": {
"name": "Avalanche", "name": "Avalanche",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://www.avalabs.org/", "websiteLink": "https://www.avalabs.org/",
"whitepaperLink": "https://assets.website-files.com/5d80307810123f5ffbb34d6e/6008d7bbf8b10d1eb01e7e16_Avalanche%20Platform%20Whitepaper.pdf", "whitepaperLink": "https://assets.website-files.com/5d80307810123f5ffbb34d6e/6008d7bbf8b10d1eb01e7e16_Avalanche%20Platform%20Whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/avalanche/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/avalanche/"
}, },
"BCH-USD": { "BCH-USD": {
"name": "Bitcoin Cash", "name": "Bitcoin Cash",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://bitcoincash.org/", "websiteLink": "https://bitcoincash.org/",
"whitepaperLink": "https://bitcoincash.org/", "whitepaperLink": "https://bitcoincash.org/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/bitcoin-cash/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/bitcoin-cash/"
}, },
"BLUR-USD": { "BLUR-USD": {
"name": "Blur", "name": "Blur",
"tags":[ "tags": [],
],
"websiteLink": "https://blur.io/", "websiteLink": "https://blur.io/",
"whitepaperLink": "https://docs.blur.foundation/", "whitepaperLink": "https://docs.blur.foundation/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/blur-token/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/blur-token/"
}, },
"CELO-USD": { "CELO-USD": {
"name": "Celo", "name": "Celo",
"tags":[ "tags": [],
],
"websiteLink": "https://celo.org", "websiteLink": "https://celo.org",
"whitepaperLink": "https://docs.celo.org", "whitepaperLink": "https://docs.celo.org",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/celo/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/celo/"
}, },
"BTC-USD": { "BTC-USD": {
"name": "Bitcoin", "name": "Bitcoin",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://bitcoin.org/", "websiteLink": "https://bitcoin.org/",
"whitepaperLink": "https://bitcoin.org/bitcoin.pdf", "whitepaperLink": "https://bitcoin.org/bitcoin.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/bitcoin/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/bitcoin/"
}, },
"COMP-USD": { "COMP-USD": {
"name": "Compound", "name": "Compound",
"tags":[ "tags": ["Defi"],
"Defi"
],
"websiteLink": "https://compound.finance/", "websiteLink": "https://compound.finance/",
"whitepaperLink": "https://compound.finance/documents/Compound.Whitepaper.pdf", "whitepaperLink": "https://compound.finance/documents/Compound.Whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/compound/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/compound/"
}, },
"CRV-USD": { "CRV-USD": {
"name": "Curve", "name": "Curve",
"tags":[ "tags": ["Governance"],
"Governance"
],
"websiteLink": "https://curve.fi/", "websiteLink": "https://curve.fi/",
"whitepaperLink": "https://curve.fi/whitepaper", "whitepaperLink": "https://curve.fi/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/curve-dao-token/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/curve-dao-token/"
}, },
"DOGE-USD": { "DOGE-USD": {
"name": "Dogecoin", "name": "Dogecoin",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://dogecoin.com/", "websiteLink": "https://dogecoin.com/",
"whitepaperLink": "https://github.com/dogecoin/dogecoin", "whitepaperLink": "https://github.com/dogecoin/dogecoin",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/dogecoin/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/dogecoin/"
}, },
"DOT-USD": { "DOT-USD": {
"name": "Polkadot", "name": "Polkadot",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://polkadot.network/", "websiteLink": "https://polkadot.network/",
"whitepaperLink": "https://polkadot.network/PolkaDotPaper.pdf", "whitepaperLink": "https://polkadot.network/PolkaDotPaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/polkadot-new/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/polkadot-new/"
}, },
"ENJ-USD": { "ENJ-USD": {
"name": "Enjin", "name": "Enjin",
"tags":[ "tags": [],
],
"websiteLink": "https://enjin.io/", "websiteLink": "https://enjin.io/",
"whitepaperLink": "https://cdn.enjin.io/downloads/whitepapers/enjin-coin/en.pdf/", "whitepaperLink": "https://cdn.enjin.io/downloads/whitepapers/enjin-coin/en.pdf/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/enjin-coin/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/enjin-coin/"
}, },
"EOS-USD": { "EOS-USD": {
"name": "EOS", "name": "EOS",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://eos.io/", "websiteLink": "https://eos.io/",
"whitepaperLink": "https://github.com/EOSIO/Documentation/blob/master/TechnicalWhitePaper.md", "whitepaperLink": "https://github.com/EOSIO/Documentation/blob/master/TechnicalWhitePaper.md",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/eos/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/eos/"
}, },
"ETC-USD": { "ETC-USD": {
"name": "Ethereum Classic", "name": "Ethereum Classic",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://ethereumclassic.org/", "websiteLink": "https://ethereumclassic.org/",
"whitepaperLink": "https://ethereumclassic.org/why-classic", "whitepaperLink": "https://ethereumclassic.org/why-classic",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/ethereum-classic/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/ethereum-classic/"
}, },
"ETH-USD": { "ETH-USD": {
"name": "Ethereum", "name": "Ethereum",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://ethereum.org/", "websiteLink": "https://ethereum.org/",
"whitepaperLink": "https://ethereum.org/whitepaper/", "whitepaperLink": "https://ethereum.org/whitepaper/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/ethereum/", "coinMarketCapsLink": "https://coinmarketcap.com/currencies/ethereum/",
@@ -192,261 +150,209 @@
}, },
"FIL-USD": { "FIL-USD": {
"name": "Filecoin", "name": "Filecoin",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://filecoin.io/", "websiteLink": "https://filecoin.io/",
"whitepaperLink": "https://filecoin.io/filecoin.pdf", "whitepaperLink": "https://filecoin.io/filecoin.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/filecoin/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/filecoin/"
}, },
"HNT-USD": { "HNT-USD": {
"name": "Helium", "name": "Helium",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://www.helium.com", "websiteLink": "https://www.helium.com",
"whitepaperLink": "http://whitepaper.helium.com", "whitepaperLink": "http://whitepaper.helium.com",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/helium/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/helium/"
}, },
"ICP-USD": { "ICP-USD": {
"name": "Internet Computer", "name": "Internet Computer",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://dfinity.org", "websiteLink": "https://dfinity.org",
"whitepaperLink": "https://dfinity.org/whitepaper.pdf", "whitepaperLink": "https://dfinity.org/whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/internet-computer/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/internet-computer/"
}, },
"LDO-USD": { "LDO-USD": {
"name": "Lido DAO", "name": "Lido DAO",
"tags":[ "tags": ["Defi"],
"Defi"
],
"websiteLink": "https://lido.fi/", "websiteLink": "https://lido.fi/",
"whitepaperLink": "https://lido.fi/static/Lido:Ethereum-Liquid-Staking.pdf", "whitepaperLink": "https://lido.fi/static/Lido:Ethereum-Liquid-Staking.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/lido-dao/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/lido-dao/"
}, },
"LINK-USD": { "LINK-USD": {
"name": "Chainlink", "name": "Chainlink",
"tags":[ "tags": ["Defi"],
"Defi"
],
"websiteLink": "https://chain.link/", "websiteLink": "https://chain.link/",
"whitepaperLink": "https://link.smartcontract.com/whitepaper", "whitepaperLink": "https://link.smartcontract.com/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/chainlink/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/chainlink/"
}, },
"LTC-USD": { "LTC-USD": {
"name": "Litecoin", "name": "Litecoin",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://litecoin.org/", "websiteLink": "https://litecoin.org/",
"whitepaperLink": "https://litecoin.info/index.php/Main_Page", "whitepaperLink": "https://litecoin.info/index.php/Main_Page",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/litecoin/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/litecoin/"
}, },
"MATIC-USD": { "MATIC-USD": {
"name": "Polygon", "name": "Polygon",
"tags":[ "tags": ["Layer 2"],
"Layer 2"
],
"websiteLink": "https://polygon.technology/", "websiteLink": "https://polygon.technology/",
"whitepaperLink": "https://polygon.technology/lightpaper-polygon.pdf", "whitepaperLink": "https://polygon.technology/lightpaper-polygon.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/polygon/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/polygon/"
}, },
"MKR-USD": { "MKR-USD": {
"name": "Maker", "name": "Maker",
"tags":[ "tags": ["Governance"],
"Governance"
],
"websiteLink": "https://makerdao.com", "websiteLink": "https://makerdao.com",
"whitepaperLink": "https://makerdao.com/whitepaper", "whitepaperLink": "https://makerdao.com/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/maker/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/maker/"
}, },
"NEAR-USD": { "NEAR-USD": {
"name": "NEAR Protocol", "name": "NEAR Protocol",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://near.org", "websiteLink": "https://near.org",
"whitepaperLink": "https://near.org/papers/the-official-near-white-paper/", "whitepaperLink": "https://near.org/papers/the-official-near-white-paper/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/near-protocol/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/near-protocol/"
}, },
"OP-USD": { "OP-USD": {
"name": "Optimism", "name": "Optimism",
"tags":[ "tags": [],
],
"websiteLink": "https://www.optimism.io/", "websiteLink": "https://www.optimism.io/",
"whitepaperLink": "https://github.com/ethereum-optimism", "whitepaperLink": "https://github.com/ethereum-optimism",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/optimism-ethereum/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/optimism-ethereum/"
}, },
"PEPE-USD": { "PEPE-USD": {
"name": "Pepe", "name": "Pepe",
"tags":[ "tags": [],
],
"websiteLink": "https://www.pepe.vip/", "websiteLink": "https://www.pepe.vip/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/pepe/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/pepe/"
}, },
"RUNE-USD": { "RUNE-USD": {
"name": "THORChain", "name": "THORChain",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://thorchain.org", "websiteLink": "https://thorchain.org",
"whitepaperLink": "https://whitepaper.io/document/709/thorchain-whitepaper", "whitepaperLink": "https://whitepaper.io/document/709/thorchain-whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/thorchain/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/thorchain/"
}, },
"SEI-USD": { "SEI-USD": {
"name": "Sei", "name": "Sei",
"tags":[ "tags": ["Layer 1", "Defi"],
"Layer 1",
"Defi"
],
"websiteLink": "https://www.sei.io/", "websiteLink": "https://www.sei.io/",
"whitepaperLink": "https://github.com/sei-protocol/sei-chain/blob/3c9576fee3494ce039df684624f918dd8066ba3f/whitepaper/Sei_Whitepaper.pdf", "whitepaperLink": "https://github.com/sei-protocol/sei-chain/blob/3c9576fee3494ce039df684624f918dd8066ba3f/whitepaper/Sei_Whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/sei/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/sei/"
}, },
"SHIB-USD": { "SHIB-USD": {
"name": "Shiba Inu", "name": "Shiba Inu",
"tags":[ "tags": [],
],
"websiteLink": "https://shibatoken.com/", "websiteLink": "https://shibatoken.com/",
"whitepaperLink": "https://docs.shibatoken.com/", "whitepaperLink": "https://docs.shibatoken.com/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/shiba-inu/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/shiba-inu/"
}, },
"SNX-USD": { "SNX-USD": {
"name": "Synthetix", "name": "Synthetix",
"tags":[ "tags": ["Defi"],
"Defi"
],
"websiteLink": "https://synthetix.io/", "websiteLink": "https://synthetix.io/",
"whitepaperLink": "https://docs.synthetix.io/litepaper", "whitepaperLink": "https://docs.synthetix.io/litepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/synthetix-network-token/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/synthetix-network-token/"
}, },
"SOL-USD": { "SOL-USD": {
"name": "Solana", "name": "Solana",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://solana.com/", "websiteLink": "https://solana.com/",
"whitepaperLink": "https://solana.com/solana-whitepaper.pdf", "whitepaperLink": "https://solana.com/solana-whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/solana/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/solana/"
}, },
"SUI-USD": { "SUI-USD": {
"name": "Sui", "name": "Sui",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://sui.io/", "websiteLink": "https://sui.io/",
"whitepaperLink": "https://github.com/MystenLabs", "whitepaperLink": "https://github.com/MystenLabs",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/sui/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/sui/"
}, },
"SUSHI-USD": { "SUSHI-USD": {
"name": "Sushi", "name": "Sushi",
"tags":[ "tags": ["Defi"],
"Defi"
],
"websiteLink": "https://sushi.com/", "websiteLink": "https://sushi.com/",
"whitepaperLink": "https://docs.sushi.com/", "whitepaperLink": "https://docs.sushi.com/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/sushiswap/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/sushiswap/"
}, },
"TIA-USD": {
"name": "Celestia",
"tags": ["Layer 1"],
"websiteLink": "https://celestia.org/",
"whitepaperLink": "https://arxiv.org/pdf/1905.09274.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/celestia/"
},
"TRX-USD": { "TRX-USD": {
"name": "TRON", "name": "TRON",
"tags":[ "tags": ["Defi"],
"Defi"
],
"websiteLink": "https://tron.network/", "websiteLink": "https://tron.network/",
"whitepaperLink": "https://tron.network/static/doc/white_paper_v_2_0.pdf", "whitepaperLink": "https://tron.network/static/doc/white_paper_v_2_0.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/tron/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/tron/"
}, },
"UMA-USD": { "UMA-USD": {
"name": "UMA", "name": "UMA",
"tags":[ "tags": ["Defi"],
"Defi"
],
"websiteLink": "https://umaproject.org/", "websiteLink": "https://umaproject.org/",
"whitepaperLink": "https://github.com/UMAprotocol/whitepaper", "whitepaperLink": "https://github.com/UMAprotocol/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/uma/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/uma/"
}, },
"UNI-USD": { "UNI-USD": {
"name": "Uniswap", "name": "Uniswap",
"tags":[ "tags": ["Defi"],
"Defi"
],
"websiteLink": "https://uniswap.org/", "websiteLink": "https://uniswap.org/",
"whitepaperLink": "https://uniswap.org/whitepaper-v3.pdf", "whitepaperLink": "https://uniswap.org/whitepaper-v3.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/uniswap/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/uniswap/"
}, },
"WLD-USD": { "WLD-USD": {
"name": "Worldcoin", "name": "Worldcoin",
"tags":[ "tags": [],
],
"websiteLink": "https://worldcoin.org/", "websiteLink": "https://worldcoin.org/",
"whitepaperLink": "https://whitepaper.worldcoin.org/", "whitepaperLink": "https://whitepaper.worldcoin.org/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/worldcoin-org/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/worldcoin-org/"
}, },
"XLM-USD": { "XLM-USD": {
"name": "Stellar", "name": "Stellar",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://www.stellar.org/", "websiteLink": "https://www.stellar.org/",
"whitepaperLink": "https://www.stellar.org/papers/stellar-consensus-protocol", "whitepaperLink": "https://www.stellar.org/papers/stellar-consensus-protocol",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/stellar/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/stellar/"
}, },
"XMR-USD": { "XMR-USD": {
"name": "Monero", "name": "Monero",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://www.getmonero.org/", "websiteLink": "https://www.getmonero.org/",
"whitepaperLink": "https://www.getmonero.org/resources/research-lab/", "whitepaperLink": "https://www.getmonero.org/resources/research-lab/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/monero/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/monero/"
}, },
"XRP-USD": { "XRP-USD": {
"name": "Ripple", "name": "Ripple",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://ripple.com/currency/", "websiteLink": "https://ripple.com/currency/",
"whitepaperLink": "https://github.com/ripple", "whitepaperLink": "https://github.com/ripple",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/xrp/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/xrp/"
}, },
"XTZ-USD": { "XTZ-USD": {
"name": "Tezos", "name": "Tezos",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://tezos.com", "websiteLink": "https://tezos.com",
"whitepaperLink": "https://tezos.com/whitepaper.pdf", "whitepaperLink": "https://tezos.com/whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/tezos/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/tezos/"
}, },
"YFI-USD": { "YFI-USD": {
"name": "Yearn", "name": "Yearn",
"tags":[ "tags": ["Defi"],
"Defi"
],
"websiteLink": "https://yearn.finance/", "websiteLink": "https://yearn.finance/",
"whitepaperLink": "https://docs.yearn.finance/", "whitepaperLink": "https://docs.yearn.finance/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/yearn-finance/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/yearn-finance/"
}, },
"ZEC-USD": { "ZEC-USD": {
"name": "Zcash", "name": "Zcash",
"tags":[ "tags": ["Layer 1"],
"Layer 1"
],
"websiteLink": "https://z.cash/", "websiteLink": "https://z.cash/",
"whitepaperLink": "https://z.cash/technology/", "whitepaperLink": "https://z.cash/technology/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/zcash/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/zcash/"
}, },
"ZRX-USD": { "ZRX-USD": {
"name": "0x", "name": "0x",
"tags":[ "tags": ["Defi"],
"Defi"
],
"websiteLink": "https://0x.org/", "websiteLink": "https://0x.org/",
"whitepaperLink": "https://0x.org/pdfs/0x_white_paper.pdf", "whitepaperLink": "https://0x.org/pdfs/0x_white_paper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/0x/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/0x/"
Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

+16 -15
View File
@@ -1,8 +1,8 @@
import { Suspense } from 'react'; import { lazy, Suspense } from 'react';
import { Navigate, Route, Routes } from 'react-router-dom'; import { Navigate, Route, Routes } from 'react-router-dom';
import styled, { AnyStyledComponent, css } from 'styled-components'; import styled, { AnyStyledComponent, css } from 'styled-components';
import { WagmiConfig } from 'wagmi'; import { WagmiProvider } from 'wagmi';
import { QueryClient, QueryClientProvider } from 'react-query'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { GrazProvider } from 'graz'; import { GrazProvider } from 'graz';
import { AppRoute, DEFAULT_TRADE_ROUTE } from '@/constants/routes'; import { AppRoute, DEFAULT_TRADE_ROUTE } from '@/constants/routes';
@@ -25,16 +25,6 @@ import { SubaccountProvider } from '@/hooks/useSubaccount';
import { GuardedMobileRoute } from '@/components/GuardedMobileRoute'; import { GuardedMobileRoute } from '@/components/GuardedMobileRoute';
import MarketsPage from '@/pages/markets/Markets';
import PortfolioPage from '@/pages/portfolio/Portfolio';
import { AlertsPage } from '@/pages/AlertsPage';
import ProfilePage from '@/pages/Profile';
import { SettingsPage } from '@/pages/settings/Settings';
import TradePage from '@/pages/trade/Trade';
import { RewardsPage } from '@/pages/rewards/RewardsPage';
import { TermsOfUsePage } from '@/pages/TermsOfUsePage';
import { PrivacyPolicyPage } from '@/pages/PrivacyPolicyPage';
import { HeaderDesktop } from '@/layout/Header/HeaderDesktop'; import { HeaderDesktop } from '@/layout/Header/HeaderDesktop';
import { FooterDesktop } from '@/layout/Footer/FooterDesktop'; import { FooterDesktop } from '@/layout/Footer/FooterDesktop';
import { FooterMobile } from '@/layout/Footer/FooterMobile'; import { FooterMobile } from '@/layout/Footer/FooterMobile';
@@ -46,11 +36,22 @@ import { config } from '@/lib/wagmi';
import { breakpoints } from '@/styles'; import { breakpoints } from '@/styles';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
import { LoadingSpace } from './components/Loading/LoadingSpinner';
import '@/styles/constants.css'; import '@/styles/constants.css';
import '@/styles/fonts.css'; import '@/styles/fonts.css';
import '@/styles/web3modal.css'; import '@/styles/web3modal.css';
const MarketsPage = lazy(() => import('@/pages/markets/Markets'));
const PortfolioPage = lazy(() => import('@/pages/portfolio/Portfolio'));
const AlertsPage = lazy(() => import('@/pages/AlertsPage'));
const ProfilePage = lazy(() => import('@/pages/Profile'));
const SettingsPage = lazy(() => import('@/pages/settings/Settings'));
const TradePage = lazy(() => import('@/pages/trade/Trade'));
const RewardsPage = lazy(() => import('@/pages/rewards/RewardsPage'));
const TermsOfUsePage = lazy(() => import('@/pages/TermsOfUsePage'));
const PrivacyPolicyPage = lazy(() => import('@/pages/PrivacyPolicyPage'));
const queryClient = new QueryClient(); const queryClient = new QueryClient();
const Content = () => { const Content = () => {
@@ -69,7 +70,7 @@ const Content = () => {
{isNotTablet && <HeaderDesktop />} {isNotTablet && <HeaderDesktop />}
<Styled.Main> <Styled.Main>
<Suspense fallback={null}> <Suspense fallback={<LoadingSpace id="main" />}>
<Routes> <Routes>
<Route path={AppRoute.Trade}> <Route path={AppRoute.Trade}>
<Route path=":market" element={<TradePage />} /> <Route path=":market" element={<TradePage />} />
@@ -119,9 +120,9 @@ const wrapProvider = (Component: React.ComponentType<any>, props?: any) => {
}; };
const providers = [ const providers = [
wrapProvider(WagmiProvider, { config }),
wrapProvider(QueryClientProvider, { client: queryClient }), wrapProvider(QueryClientProvider, { client: queryClient }),
wrapProvider(GrazProvider), wrapProvider(GrazProvider),
wrapProvider(WagmiConfig, { config }),
wrapProvider(LocaleProvider), wrapProvider(LocaleProvider),
wrapProvider(RestrictionProvider), wrapProvider(RestrictionProvider),
wrapProvider(DydxProvider), wrapProvider(DydxProvider),
+119
View File
@@ -0,0 +1,119 @@
import styled, { keyframes, type AnyStyledComponent } from 'styled-components';
import { Root, Item, Header, Trigger, Content } from '@radix-ui/react-accordion';
import { layoutMixins } from '@/styles/layoutMixins';
import { PlusIcon } from '@/icons';
export type AccordionItem = {
header: React.ReactNode;
content: React.ReactNode;
};
type AccordionProps = {
items: AccordionItem[];
className?: string;
};
export const Accordion = ({ items, className }: AccordionProps) => (
<Styled.Root className={className} type="single" collapsible>
{items.map(({ header, content }, idx) => (
<Styled.Item key={idx} value={idx.toString()}>
<Header>
<Styled.Trigger>
{header}
<Styled.Icon>
<PlusIcon />
</Styled.Icon>
</Styled.Trigger>
</Header>
<Styled.Content>{content}</Styled.Content>
</Styled.Item>
))}
</Styled.Root>
);
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Root = styled(Root)`
> *:not(:last-child) {
border-bottom: var(--border-width) solid var(--border-color);
}
`;
Styled.Item = styled(Item)``;
Styled.Icon = styled.div`
display: inline-flex;
justify-content: center;
align-items: center;
width: 2.25rem;
height: 2.25rem;
--color-border: var(--color-layer-6);
color: var(--color-text-1);
background-color: var(--color-layer-5);
border: solid var(--border-width) var(--color-border);
border-radius: 50%;
font: var(--font-small-book);
svg {
height: 1.125em;
width: 1.125em;
}
`;
Styled.Trigger = styled(Trigger)`
${layoutMixins.spacedRow}
width: 100%;
padding: 1rem 0.75rem;
color: var(--color-text-1);
text-align: start;
&:hover {
${Styled.Icon} {
color: var(--color-text-2);
filter: brightness(1.1);
}
}
svg {
color: var(--color-text-0);
transition: transform 0.3s var(--ease-out-expo);
}
&[data-state='open'] svg {
transform: rotate(45deg);
}
`;
Styled.Content = styled(Content)`
overflow: hidden;
margin: 0 0.75rem 1rem;
color: var(--color-text-0);
&[data-state='open'] {
animation: ${keyframes`
from {
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
`} 0.3s var(--ease-out-expo);
}
&[data-state='closed'] {
animation: ${keyframes`
from {
height: var(--radix-accordion-content-height);
}
to {
height: 0;
}
`} 0.1s var(--ease-in-expo);
}
`;
+8 -2
View File
@@ -45,6 +45,7 @@ const assetIcons = {
SOL: '/currencies/sol.png', SOL: '/currencies/sol.png',
SUI: '/currencies/sui.png', SUI: '/currencies/sui.png',
SUSHI: '/currencies/sushi.png', SUSHI: '/currencies/sushi.png',
TIA: '/currencies/tia.png',
TRX: '/currencies/trx.png', TRX: '/currencies/trx.png',
UMA: '/currencies/uma.png', UMA: '/currencies/uma.png',
UNI: '/currencies/uni.png', UNI: '/currencies/uni.png',
@@ -71,8 +72,13 @@ export const AssetIcon = ({
}: { }: {
symbol?: Nullable<string>; symbol?: Nullable<string>;
className?: string; className?: string;
}) => }) => (
isAssetSymbol(symbol) ? <Styled.Img src={assetIcons[symbol]} className={className} /> : null; <Styled.Img
src={isAssetSymbol(symbol) ? assetIcons[symbol] : '/currencies/unavailable.png'}
className={className}
alt={symbol}
/>
);
const Styled: Record<string, AnyStyledComponent> = {}; const Styled: Record<string, AnyStyledComponent> = {};
+7 -5
View File
@@ -31,10 +31,12 @@ type ElementProps<TabItemsValue> = {
}; };
type StyleProps = { type StyleProps = {
fullWidthTabs?: boolean;
className?: string; className?: string;
fullWidthTabs?: boolean;
}; };
export type CollapsibleTabsProps<TabItemsValue> = ElementProps<TabItemsValue> & StyleProps;
export const CollapsibleTabs = <TabItemsValue extends string>({ export const CollapsibleTabs = <TabItemsValue extends string>({
defaultValue, defaultValue,
items, items,
@@ -43,8 +45,9 @@ export const CollapsibleTabs = <TabItemsValue extends string>({
onOpenChange, onOpenChange,
fullWidthTabs, fullWidthTabs,
className, className,
}: ElementProps<TabItemsValue> & StyleProps) => { }: CollapsibleTabsProps<TabItemsValue>) => {
const [value, setValue] = useState(defaultValue); const [value, setValue] = useState(defaultValue);
const currentItem = items.find((item) => item.value === value); const currentItem = items.find((item) => item.value === value);
@@ -86,8 +89,8 @@ export const CollapsibleTabs = <TabItemsValue extends string>({
</Styled.Header> </Styled.Header>
<Styled.CollapsibleContent> <Styled.CollapsibleContent>
{items.map(({ value, content }) => ( {items.map(({ asChild, value, content }) => (
<Styled.TabsContent key={value} value={value}> <Styled.TabsContent key={value} asChild={asChild} value={value}>
{content} {content}
</Styled.TabsContent> </Styled.TabsContent>
))} ))}
@@ -219,7 +222,6 @@ Styled.Header = styled.header`
Styled.CollapsibleContent = styled(CollapsibleContent)` Styled.CollapsibleContent = styled(CollapsibleContent)`
${layoutMixins.stack} ${layoutMixins.stack}
${layoutMixins.perspectiveArea}
box-shadow: none; box-shadow: none;
`; `;
+3
View File
@@ -55,6 +55,7 @@ import {
OverviewIcon, OverviewIcon,
PencilIcon, PencilIcon,
PlayIcon, PlayIcon,
PlusIcon,
PositionsIcon, PositionsIcon,
PriceChartIcon, PriceChartIcon,
PrivacyIcon, PrivacyIcon,
@@ -130,6 +131,7 @@ export enum IconName {
Overview = 'Overview', Overview = 'Overview',
Pencil = 'Pencil', Pencil = 'Pencil',
Play = 'Play', Play = 'Play',
Plus = 'Plus',
Positions = 'Positions', Positions = 'Positions',
PriceChart = 'PriceChart', PriceChart = 'PriceChart',
Privacy = 'Privacy', Privacy = 'Privacy',
@@ -204,6 +206,7 @@ const icons = {
[IconName.Overview]: OverviewIcon, [IconName.Overview]: OverviewIcon,
[IconName.Pencil]: PencilIcon, [IconName.Pencil]: PencilIcon,
[IconName.Play]: PlayIcon, [IconName.Play]: PlayIcon,
[IconName.Plus]: PlusIcon,
[IconName.Positions]: PositionsIcon, [IconName.Positions]: PositionsIcon,
[IconName.PriceChart]: PriceChartIcon, [IconName.PriceChart]: PriceChartIcon,
[IconName.Privacy]: PrivacyIcon, [IconName.Privacy]: PrivacyIcon,
-1
View File
@@ -198,7 +198,6 @@ Styled.InputContainer = styled.div`
border-radius: inherit; border-radius: inherit;
input { input {
user-select: all;
flex: 1; flex: 1;
width: 100%; width: 100%;
} }
+2 -4
View File
@@ -43,6 +43,7 @@ import { layoutMixins } from '@/styles/layoutMixins';
import { Icon, IconName } from './Icon'; import { Icon, IconName } from './Icon';
import { Tag } from './Tag'; import { Tag } from './Tag';
import { MustBigNumber } from '@/lib/numbers';
export { TableCell } from './Table/TableCell'; export { TableCell } from './Table/TableCell';
export { TableColumnHeader } from './Table/TableColumnHeader'; export { TableColumnHeader } from './Table/TableColumnHeader';
@@ -161,10 +162,7 @@ export const Table = <TableRowData extends object, TableRowKey extends Key>({
? // String ? // String
collator.compare(first as string, second as string) collator.compare(first as string, second as string)
: // Number : // Number
Math.sign( MustBigNumber(first).comparedTo(MustBigNumber(second))) *
(parseInt(first as string) || (first as number)) -
(parseInt(second as string) || (second as number))
)) *
// Flip the direction if descending order is specified. // Flip the direction if descending order is specified.
(sortDirection === 'descending' ? -1 : 1) (sortDirection === 'descending' ? -1 : 1)
); );
+5 -2
View File
@@ -21,6 +21,7 @@ export type TabItem<TabItemsValue> = {
content?: React.ReactNode; content?: React.ReactNode;
subitems?: TabItem<TabItemsValue>[]; subitems?: TabItem<TabItemsValue>[];
customTrigger?: ReactNode; customTrigger?: ReactNode;
asChild?: boolean;
}; };
type ElementProps<TabItemsValue> = { type ElementProps<TabItemsValue> = {
@@ -41,6 +42,8 @@ type StyleProps = {
className?: string; className?: string;
}; };
export type TabsProps<TabItemsValue> = ElementProps<TabItemsValue> & StyleProps;
export const Tabs = <TabItemsValue extends string>({ export const Tabs = <TabItemsValue extends string>({
defaultValue, defaultValue,
value, value,
@@ -107,9 +110,10 @@ export const Tabs = <TabItemsValue extends string>({
sharedContent sharedContent
) : ( ) : (
<Styled.Stack> <Styled.Stack>
{items.map(({ value, content, forceMount }) => ( {items.map(({ asChild, value, content, forceMount }) => (
<Styled.Content <Styled.Content
key={value} key={value}
asChild={asChild}
value={value} value={value}
forceMount={forceMount} forceMount={forceMount}
$hide={forceMount && currentItem?.value !== value} $hide={forceMount && currentItem?.value !== value}
@@ -242,7 +246,6 @@ Styled.Trigger = styled(Trigger)<{ $withBorders?: boolean }>`
Styled.Stack = styled.div` Styled.Stack = styled.div`
${layoutMixins.stack} ${layoutMixins.stack}
${layoutMixins.perspectiveArea}
box-shadow: none; box-shadow: none;
`; `;
-36
View File
@@ -26,39 +26,3 @@ export enum FundingDirection {
ToShort = 'ToShort', ToShort = 'ToShort',
ToLong = 'ToLong', ToLong = 'ToLong',
} }
export const MARKETS_TO_DISPLAY = [
'BTC-USD',
'ETH-USD',
'LINK-USD',
'SOL-USD',
'MATIC-USD',
'ATOM-USD',
'AVAX-USD',
'APE-USD',
'XRP-USD',
'UNI-USD',
'ADA-USD',
'TRX-USD',
'OP-USD',
'MKR-USD',
'DOGE-USD',
'SHIB-USD',
'COMP-USD',
'LDO-USD',
'NEAR-USD',
'APT-USD',
'SUI-USD',
'DOT-USD',
'ETC-USD',
'ARB-USD',
'CRV-USD',
'BLUR-USD',
'FIL-USD',
'XLM-USD',
'PEPE-USD',
'WLD-USD',
'SEI-USD',
'LTC-USD',
'BCH-USD',
];
+6
View File
@@ -122,9 +122,15 @@ export type NotificationDisplayData = {
toastDuration?: number; toastDuration?: number;
}; };
export enum TransferNotificationTypes {
Withdrawal = 'withdrawal',
Deposit = 'deposit',
}
// Notification types // Notification types
export type TransferNotifcation = { export type TransferNotifcation = {
txHash: string; txHash: string;
type?: TransferNotificationTypes;
toChainId?: string; toChainId?: string;
fromChainId?: string; fromChainId?: string;
toAmount?: number; toAmount?: number;
+5
View File
@@ -10,6 +10,7 @@ export const LEVERAGE_DECIMALS = 2;
export const TOKEN_DECIMALS = 4; export const TOKEN_DECIMALS = 4;
export const LARGE_TOKEN_DECIMALS = 2; export const LARGE_TOKEN_DECIMALS = 2;
export const FEE_DECIMALS = 3; export const FEE_DECIMALS = 3;
export const FUNDING_DECIMALS = 6;
export const QUANTUM_MULTIPLIER = 1_000_000; export const QUANTUM_MULTIPLIER = 1_000_000;
@@ -18,3 +19,7 @@ export enum NumberSign {
Negative = 'Negative', Negative = 'Negative',
Neutral = 'Neutral', Neutral = 'Neutral',
} }
// Deposit/Withdraw
export const MAX_CCTP_TRANSFER_AMOUNT = 1_000_000;
export const MAX_PRICE_IMPACT = 0.02; // 2%
+5
View File
@@ -0,0 +1,5 @@
export enum QueryKeys {
ACCOUNT_BALANCE = 'ACCOUNT_BALANCE',
LAUNCH_INCENTIVES = 'LAUNCH_INCENTIVES',
TRANSACTION_STATUS = 'TRANSACTION_STATUS',
}
+4
View File
@@ -1,6 +1,10 @@
import { type TooltipStrings, TOOLTIP_STRING_KEYS } from '@/constants/localization'; import { type TooltipStrings, TOOLTIP_STRING_KEYS } from '@/constants/localization';
export const depositTooltips: TooltipStrings = { export const depositTooltips: TooltipStrings = {
'minimum-deposit-amount': ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.MINIMUM_DEPOSIT_AMOUNT_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.MINIMUM_DEPOSIT_AMOUNT_BODY }),
}),
swap: ({ stringGetter }) => ({ swap: ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.SWAP_TITLE }), title: stringGetter({ key: TOOLTIP_STRING_KEYS.SWAP_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.SWAP_BODY }), body: stringGetter({ key: TOOLTIP_STRING_KEYS.SWAP_BODY }),
+4
View File
@@ -5,6 +5,10 @@ export const withdrawTooltips: TooltipStrings = {
title: stringGetter({ key: TOOLTIP_STRING_KEYS.FAST_WITHDRAW_FEE_TITLE }), title: stringGetter({ key: TOOLTIP_STRING_KEYS.FAST_WITHDRAW_FEE_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.FAST_WITHDRAW_FEE_BODY }), body: stringGetter({ key: TOOLTIP_STRING_KEYS.FAST_WITHDRAW_FEE_BODY }),
}), }),
'minimum-amount-received': ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.MINIMUM_AMOUNT_RECEIVED_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.MINIMUM_AMOUNT_RECEIVED_BODY }),
}),
'withdraw-types': ({ stringGetter }) => ({ 'withdraw-types': ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.WITHDRAW_TYPES_TITLE }), title: stringGetter({ key: TOOLTIP_STRING_KEYS.WITHDRAW_TYPES_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.WITHDRAW_TYPES_BODY }), body: stringGetter({ key: TOOLTIP_STRING_KEYS.WITHDRAW_TYPES_BODY }),
+13 -2
View File
@@ -52,7 +52,7 @@ type WalletConnectionTypeConfig = {
wagmiConnectorId?: string; wagmiConnectorId?: string;
}; };
export const walletConnectionTypes: Record<WalletConnectionType, WalletConnectionTypeConfig> = { export const WALLET_CONNECTION_TYPES: Record<WalletConnectionType, WalletConnectionTypeConfig> = {
[WalletConnectionType.CoinbaseWalletSdk]: { [WalletConnectionType.CoinbaseWalletSdk]: {
name: 'Coinbase Wallet SDK', name: 'Coinbase Wallet SDK',
wagmiConnectorId: 'coinbaseWallet', wagmiConnectorId: 'coinbaseWallet',
@@ -110,7 +110,7 @@ export const WALLET_CONNECT_EXPLORER_RECOMMENDED_IDS = Object.values(
WALLET_CONNECT_EXPLORER_RECOMMENDED_WALLETS WALLET_CONNECT_EXPLORER_RECOMMENDED_WALLETS
); );
type WalletConfig = { export type WalletConfig = {
type: WalletType; type: WalletType;
stringKey: string; stringKey: string;
icon: string; icon: string;
@@ -274,6 +274,17 @@ export type WalletConnection = {
provider?: ExternalProvider; provider?: ExternalProvider;
}; };
export type WalletConnectConfig = {
client: {
name: string;
description: string;
iconUrl: string;
};
v2: {
projectId: string;
};
};
// dYdX Chain wallets // dYdX Chain wallets
export const COSMOS_DERIVATION_PATH = "m/44'/118'/0'/0/0"; export const COSMOS_DERIVATION_PATH = "m/44'/118'/0'/0/0";
+26 -10
View File
@@ -1,11 +1,12 @@
import { useCallback } from 'react'; import { useCallback, useEffect } from 'react';
import { shallowEqual, useSelector } from 'react-redux'; import { shallowEqual, useSelector } from 'react-redux';
import { useBalance } from 'wagmi'; import { useBalance, useBlockNumber } from 'wagmi';
import { StargateClient } from '@cosmjs/stargate'; import { StargateClient } from '@cosmjs/stargate';
import { useQuery } from 'react-query'; import { useQuery, useQueryClient } from '@tanstack/react-query';
import { formatUnits } from 'viem'; import { formatUnits } from 'viem';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { QueryKeys } from '@/constants/queries';
import { EvmAddress } from '@/constants/wallets'; import { EvmAddress } from '@/constants/wallets';
import { convertBech32Address } from '@/lib/addressUtils'; import { convertBech32Address } from '@/lib/addressUtils';
@@ -52,15 +53,27 @@ export const useAccountBalance = ({
const evmChainId = Number(ENVIRONMENT_CONFIG_MAP[selectedNetwork].ethereumChainId); const evmChainId = Number(ENVIRONMENT_CONFIG_MAP[selectedNetwork].ethereumChainId);
const stakingBalances = useSelector(getStakingBalances, shallowEqual); const stakingBalances = useSelector(getStakingBalances, shallowEqual);
const evmQuery = useBalance({ const queryClient = useQueryClient();
const { data: blockNumber } = useBlockNumber({ watch: true });
const {
data: evmQuery,
status: evmBalanceStatus,
fetchStatus: evmBalanceFetchStatus,
queryKey,
} = useBalance({
query: {
enabled: Boolean(!isCosmosChain && addressOrDenom?.startsWith('0x')), enabled: Boolean(!isCosmosChain && addressOrDenom?.startsWith('0x')),
},
address: evmAddress, address: evmAddress,
chainId: typeof chainId === 'number' ? chainId : Number(evmChainId), chainId: typeof chainId === 'number' ? chainId : Number(evmChainId),
token: token:
addressOrDenom === CHAIN_DEFAULT_TOKEN_ADDRESS ? undefined : (addressOrDenom as EvmAddress), addressOrDenom === CHAIN_DEFAULT_TOKEN_ADDRESS ? undefined : (addressOrDenom as EvmAddress),
watch: true,
}); });
useEffect(() => {
queryClient.invalidateQueries({ queryKey });
}, [blockNumber]);
const cosmosQueryFn = useCallback(async () => { const cosmosQueryFn = useCallback(async () => {
if (dydxAddress && bech32AddrPrefix && rpc && addressOrDenom) { if (dydxAddress && bech32AddrPrefix && rpc && addressOrDenom) {
const address = convertBech32Address({ const address = convertBech32Address({
@@ -78,7 +91,7 @@ export const useAccountBalance = ({
const cosmosQuery = useQuery({ const cosmosQuery = useQuery({
enabled: Boolean(isCosmosChain && dydxAddress && bech32AddrPrefix && rpc && addressOrDenom), enabled: Boolean(isCosmosChain && dydxAddress && bech32AddrPrefix && rpc && addressOrDenom),
queryKey: `accountBalances_${chainId}_${addressOrDenom}`, queryKey: [QueryKeys.ACCOUNT_BALANCE, chainId, addressOrDenom],
queryFn: cosmosQueryFn, queryFn: cosmosQueryFn,
refetchOnWindowFocus: false, refetchOnWindowFocus: false,
refetchOnMount: false, refetchOnMount: false,
@@ -87,8 +100,11 @@ export const useAccountBalance = ({
staleTime: 10_000, staleTime: 10_000,
}); });
const { formatted: evmBalance } = evmQuery.data || {}; const balance = isCosmosChain
const balance = isCosmosChain ? cosmosQuery.data : evmBalance; ? cosmosQuery.data
: evmQuery
? formatUnits(evmQuery?.value, evmQuery?.decimals)
: undefined;
const nativeTokenCoinBalance = balances?.[chainTokenDenom]; const nativeTokenCoinBalance = balances?.[chainTokenDenom];
const nativeTokenBalance = MustBigNumber(nativeTokenCoinBalance?.amount); const nativeTokenBalance = MustBigNumber(nativeTokenCoinBalance?.amount);
@@ -104,7 +120,7 @@ export const useAccountBalance = ({
nativeTokenBalance, nativeTokenBalance,
nativeStakingBalance, nativeStakingBalance,
usdcBalance, usdcBalance,
queryStatus: isCosmosChain ? cosmosQuery.status : evmQuery.status, queryStatus: isCosmosChain ? cosmosQuery.status : evmBalanceStatus,
isQueryFetching: isCosmosChain ? cosmosQuery.isFetching : evmQuery.fetchStatus === 'fetching', isQueryFetching: isCosmosChain ? cosmosQuery.isFetching : evmBalanceFetchStatus === 'fetching',
}; };
}; };
-3
View File
@@ -39,7 +39,6 @@ const useAccountsContext = () => {
walletConnectionType, walletConnectionType,
selectWalletType, selectWalletType,
selectedWalletType, selectedWalletType,
selectedWalletError,
evmAddress, evmAddress,
signerWagmi, signerWagmi,
publicClientWagmi, publicClientWagmi,
@@ -300,9 +299,7 @@ const useAccountsContext = () => {
walletConnectionType, walletConnectionType,
// Wallet selection // Wallet selection
selectWalletType,
selectedWalletType, selectedWalletType,
selectedWalletError,
// Wallet connection (EVM) // Wallet connection (EVM)
evmAddress, evmAddress,
+22 -5
View File
@@ -5,16 +5,15 @@ import { useMatch, useNavigate } from 'react-router-dom';
import { LocalStorageKey } from '@/constants/localStorage'; import { LocalStorageKey } from '@/constants/localStorage';
import { DEFAULT_MARKETID } from '@/constants/markets'; import { DEFAULT_MARKETID } from '@/constants/markets';
import { AppRoute } from '@/constants/routes'; import { AppRoute } from '@/constants/routes';
import { useLocalStorage } from '@/hooks/useLocalStorage';
import { getSelectedNetwork } from '@/state/appSelectors'; import { getSelectedNetwork } from '@/state/appSelectors';
import { closeDialogInTradeBox } from '@/state/dialogs'; import { closeDialogInTradeBox } from '@/state/dialogs';
import { setCurrentMarketId } from '@/state/perpetuals'; import { setCurrentMarketId } from '@/state/perpetuals';
import { getMarketIds } from '@/state/perpetualsSelectors';
import abacusStateManager from '@/lib/abacus'; import abacusStateManager from '@/lib/abacus';
import { useLocalStorage } from './useLocalStorage';
import { getMarketIds } from '@/state/perpetualsSelectors';
export const useCurrentMarketId = () => { export const useCurrentMarketId = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const match = useMatch(`/${AppRoute.Trade}/:marketId`); const match = useMatch(`/${AppRoute.Trade}/:marketId`);
@@ -33,17 +32,35 @@ export const useCurrentMarketId = () => {
if (marketIds.length === 0) return marketId ?? lastViewedMarket; if (marketIds.length === 0) return marketId ?? lastViewedMarket;
if (!marketIds.includes(marketId ?? lastViewedMarket)) return DEFAULT_MARKETID; if (!marketIds.includes(marketId ?? lastViewedMarket)) return DEFAULT_MARKETID;
return marketId ?? lastViewedMarket; return marketId ?? lastViewedMarket;
}, [marketIds, marketId]); }, [hasMarketIds, marketId]);
useEffect(() => { useEffect(() => {
// If v4_markets has not been subscribed to yet or marketId is not specified, default to validId
if (!hasMarketIds || !marketId) {
setLastViewedMarket(validId); setLastViewedMarket(validId);
dispatch(setCurrentMarketId(validId)); dispatch(setCurrentMarketId(validId));
dispatch(closeDialogInTradeBox()); dispatch(closeDialogInTradeBox());
if (validId !== marketId) {
navigate(`${AppRoute.Trade}/${validId}`, { navigate(`${AppRoute.Trade}/${validId}`, {
replace: true, replace: true,
}); });
}, [validId]); }
} else {
// If v4_markets has been subscribed to, check if marketId is valid
if (!marketIds.includes(marketId)) {
// If marketId is not valid (i.e. final settlement), navigate to markets page
navigate(AppRoute.Markets, {
replace: true,
});
} else {
// If marketId is valid, set currentMarketId
setLastViewedMarket(marketId);
dispatch(setCurrentMarketId(marketId));
dispatch(closeDialogInTradeBox());
}
}
}, [hasMarketIds, marketId]);
useEffect(() => { useEffect(() => {
// Check for marketIds otherwise Abacus will silently fail its isMarketValid check // Check for marketIds otherwise Abacus will silently fail its isMarketValid check
+18 -7
View File
@@ -1,8 +1,9 @@
import { createContext, useContext, useCallback, useEffect, useMemo } from 'react'; import { createContext, useContext, useCallback, useEffect, useMemo } from 'react';
import { useQuery } from 'react-query'; import { useQuery } from '@tanstack/react-query';
import { LOCAL_STORAGE_VERSIONS, LocalStorageKey } from '@/constants/localStorage'; import { LOCAL_STORAGE_VERSIONS, LocalStorageKey } from '@/constants/localStorage';
import { type TransferNotifcation } from '@/constants/notifications'; import { type TransferNotifcation } from '@/constants/notifications';
import { QueryKeys } from '@/constants/queries';
import { useAccounts } from '@/hooks/useAccounts'; import { useAccounts } from '@/hooks/useAccounts';
import { fetchSquidStatus, STATUS_ERROR_GRACE_PERIOD } from '@/lib/squid'; import { fetchSquidStatus, STATUS_ERROR_GRACE_PERIOD } from '@/lib/squid';
@@ -68,8 +69,8 @@ const useLocalNotificationsContext = () => {
[transferNotifications] [transferNotifications]
); );
useQuery({ const { data: newTransferNotifications } = useQuery({
queryKey: 'getTransactionStatus', queryKey: [QueryKeys.TRANSACTION_STATUS],
queryFn: async () => { queryFn: async () => {
const processTransferNotifications = async (transferNotifications: TransferNotifcation[]) => { const processTransferNotifications = async (transferNotifications: TransferNotifcation[]) => {
const newTransferNotifications = await Promise.all( const newTransferNotifications = await Promise.all(
@@ -84,9 +85,10 @@ const useLocalNotificationsContext = () => {
status: currentStatus, status: currentStatus,
} = transferNotification; } = transferNotification;
const hasErrors =
// @ts-ignore status.errors is not in the type definition but can be returned // @ts-ignore status.errors is not in the type definition but can be returned
// also error can some time come back as an empty object so we need to ignore for that // also error can some time come back as an empty object so we need to ignore for that
const hasErrors = !!currentStatus?.errors || !!currentStatus?.errors ||
(currentStatus?.error && Object.keys(currentStatus.error).length !== 0); (currentStatus?.error && Object.keys(currentStatus.error).length !== 0);
if ( if (
@@ -95,11 +97,14 @@ const useLocalNotificationsContext = () => {
currentStatus?.squidTransactionStatus === 'ongoing') currentStatus?.squidTransactionStatus === 'ongoing')
) { ) {
try { try {
const status = await fetchSquidStatus({ const status = await fetchSquidStatus(
{
transactionId: txHash, transactionId: txHash,
toChainId, toChainId,
fromChainId, fromChainId,
}, isCctp); },
isCctp
);
if (status) { if (status) {
transferNotification.status = status; transferNotification.status = status;
@@ -121,12 +126,18 @@ const useLocalNotificationsContext = () => {
return newTransferNotifications; return newTransferNotifications;
}; };
const newTransferNotifications = await processTransferNotifications(transferNotifications); const newTransferNotifications = await processTransferNotifications(transferNotifications);
setTransferNotifications(newTransferNotifications); return newTransferNotifications;
}, },
refetchInterval: TRANSFER_STATUS_FETCH_INTERVAL, refetchInterval: TRANSFER_STATUS_FETCH_INTERVAL,
}); });
useEffect(() => {
if (!newTransferNotifications) return;
setTransferNotifications(newTransferNotifications);
}, [newTransferNotifications]);
return { return {
transferNotifications, transferNotifications,
addTransferNotification, addTransferNotification,
+2 -11
View File
@@ -1,14 +1,7 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useSelector, shallowEqual } from 'react-redux'; import { useSelector, shallowEqual } from 'react-redux';
import { import { MarketFilters, MARKET_FILTER_LABELS, type MarketData } from '@/constants/markets';
MarketFilters,
MARKET_FILTER_LABELS,
type MarketData,
MARKETS_TO_DISPLAY,
} from '@/constants/markets';
import { testFlags } from '@/lib/testFlags';
import { getAssets } from '@/state/assetsSelectors'; import { getAssets } from '@/state/assetsSelectors';
import { getPerpetualMarkets } from '@/state/perpetualsSelectors'; import { getPerpetualMarkets } from '@/state/perpetualsSelectors';
@@ -49,9 +42,7 @@ export const useMarketsData = (
}, [allPerpetualMarkets, allAssets]); }, [allPerpetualMarkets, allAssets]);
const filteredMarkets = useMemo(() => { const filteredMarkets = useMemo(() => {
const filtered = markets const filtered = markets.filter(filterFunctions[filter]);
.filter(filterFunctions[filter])
.filter(({ id }) => (testFlags.displayAllMarkets ? true : MARKETS_TO_DISPLAY.includes(id)));
if (searchFilter) { if (searchFilter) {
return filtered.filter( return filtered.filter(
+13 -6
View File
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo } from 'react'; import { useCallback, useEffect, useMemo } from 'react';
import { useNetwork, useSwitchNetwork } from 'wagmi'; import { UseSwitchChainReturnType, useAccount, useSwitchChain } from 'wagmi';
export const useMatchingEvmNetwork = ({ export const useMatchingEvmNetwork = ({
chainId, chainId,
@@ -8,10 +8,10 @@ export const useMatchingEvmNetwork = ({
}: { }: {
chainId?: string | number; chainId?: string | number;
switchAutomatically?: boolean; switchAutomatically?: boolean;
onError?: (error: Error) => void; onError?: Parameters<UseSwitchChainReturnType['switchChainAsync']>[1]['onError'];
}) => { }) => {
const { chain } = useNetwork(); const { chain } = useAccount();
const { isLoading, switchNetworkAsync } = useSwitchNetwork({ onError }); const { switchChainAsync, isPending } = useSwitchChain();
// If chainId is not a number, we can assume it is a non EVM compatible chain // If chainId is not a number, we can assume it is a non EVM compatible chain
const isMatchingNetwork = useMemo( const isMatchingNetwork = useMemo(
@@ -21,7 +21,14 @@ export const useMatchingEvmNetwork = ({
const matchNetwork = useCallback(async () => { const matchNetwork = useCallback(async () => {
if (!isMatchingNetwork) { if (!isMatchingNetwork) {
await switchNetworkAsync?.(Number(chainId)); await switchChainAsync?.(
{
chainId: Number(chainId),
},
{
onError,
}
);
} }
}, [chainId, chain]); }, [chainId, chain]);
@@ -34,6 +41,6 @@ export const useMatchingEvmNetwork = ({
return { return {
isMatchingNetwork, isMatchingNetwork,
matchNetwork, matchNetwork,
isSwitchingNetwork: isLoading, isSwitchingNetwork: isPending,
}; };
}; };
+8 -7
View File
@@ -20,6 +20,7 @@ import {
type NotificationTypeConfig, type NotificationTypeConfig,
NotificationType, NotificationType,
DEFAULT_TOAST_AUTO_CLOSE_MS, DEFAULT_TOAST_AUTO_CLOSE_MS,
TransferNotificationTypes,
} from '@/constants/notifications'; } from '@/constants/notifications';
import { useSelectedNetwork, useStringGetter } from '@/hooks'; import { useSelectedNetwork, useStringGetter } from '@/hooks';
@@ -152,20 +153,20 @@ export const notificationTypes: NotificationTypeConfig[] = [
useEffect(() => { useEffect(() => {
for (const transfer of transferNotifications) { for (const transfer of transferNotifications) {
const { fromChainId, status, txHash, toAmount } = transfer; const { fromChainId, status, txHash, toAmount, type } = transfer;
const isFinished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing'; const isFinished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing';
const icon = <Icon iconName={isFinished ? IconName.Transfer : IconName.Clock} />; const icon = <Icon iconName={isFinished ? IconName.Transfer : IconName.Clock} />;
const type = const transferType =
fromChainId === ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId type ?? fromChainId === ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId
? 'withdrawal' ? TransferNotificationTypes.Withdrawal
: 'deposit'; : TransferNotificationTypes.Deposit;
const title = stringGetter({ const title = stringGetter({
key: { key: {
deposit: isFinished ? STRING_KEYS.DEPOSIT : STRING_KEYS.DEPOSIT_IN_PROGRESS, deposit: isFinished ? STRING_KEYS.DEPOSIT : STRING_KEYS.DEPOSIT_IN_PROGRESS,
withdrawal: isFinished ? STRING_KEYS.WITHDRAW : STRING_KEYS.WITHDRAW_IN_PROGRESS, withdrawal: isFinished ? STRING_KEYS.WITHDRAW : STRING_KEYS.WITHDRAW_IN_PROGRESS,
}[type], }[transferType],
}); });
const toChainEta = status?.toChain?.chainData?.estimatedRouteDuration || 0; const toChainEta = status?.toChain?.chainData?.estimatedRouteDuration || 0;
@@ -190,7 +191,7 @@ export const notificationTypes: NotificationTypeConfig[] = [
slotIcon={icon} slotIcon={icon}
slotTitle={title} slotTitle={title}
transfer={transfer} transfer={transfer}
type={type} type={transferType}
triggeredAt={transfer.triggeredAt} triggeredAt={transfer.triggeredAt}
notification={notification} notification={notification}
/> />
+21 -3
View File
@@ -31,6 +31,7 @@ import { log } from '@/lib/telemetry';
import { useAccounts } from './useAccounts'; import { useAccounts } from './useAccounts';
import { useTokenConfigs } from './useTokenConfigs'; import { useTokenConfigs } from './useTokenConfigs';
import { useDydxClient } from './useDydxClient'; import { useDydxClient } from './useDydxClient';
import { hashFromTx } from '@/lib/hashfromTx';
type SubaccountContextType = ReturnType<typeof useSubaccountContext>; type SubaccountContextType = ReturnType<typeof useSubaccountContext>;
const SubaccountContext = createContext<SubaccountContextType>({} as SubaccountContextType); const SubaccountContext = createContext<SubaccountContextType>({} as SubaccountContextType);
@@ -293,12 +294,29 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
); );
const sendSquidWithdraw = useCallback( const sendSquidWithdraw = useCallback(
async (amount: number, payload: string) => { async (amount: number, payload: string, isCctp?: boolean) => {
const cctpWithdraw = () => {
return new Promise<string>((resolve, reject) =>
abacusStateManager.cctpWithdraw((success, error, data) => {
const parsedData = JSON.parse(data);
if (success && parsedData?.code == 0) {
resolve(parsedData?.transactionHash);
} else {
reject(error);
}
})
)
}
if (isCctp) {
return await cctpWithdraw();
}
if (!subaccountClient) { if (!subaccountClient) {
return; return;
} }
const tx = await sendSquidWithdrawFromSubaccount({ subaccountClient, amount, payload });
return await sendSquidWithdrawFromSubaccount({ subaccountClient, amount, payload }); return hashFromTx(tx?.hash);
}, },
[subaccountClient, sendSquidWithdrawFromSubaccount] [subaccountClient, sendSquidWithdrawFromSubaccount]
); );
+11 -7
View File
@@ -104,12 +104,13 @@ export const useWalletConnection = () => {
[walletConnectConfig, walletType, walletConnectionType] [walletConnectConfig, walletType, walletConnectionType]
); );
const { connectAsync: connectWagmi } = useConnectWagmi({ connector: wagmiConnector }) const { connectAsync: connectWagmi } = useConnectWagmi();
const { suggestAndConnect: connectGraz } = useConnectGraz(); const { suggestAndConnect: connectGraz } = useConnectGraz();
const connectWallet = useCallback( const connectWallet = useCallback(
async ({ walletType }: { walletType: WalletType }) => { async ({ walletType }: { walletType: WalletType }) => {
const walletConnection = getWalletConnection({ walletType }); const walletConnection = getWalletConnection({ walletType });
console.log({ walletConnection });
try { try {
if (!walletConnection) { if (!walletConnection) {
@@ -133,13 +134,17 @@ export const useWalletConnection = () => {
} }
} else { } else {
if (!isConnectedWagmi) { if (!isConnectedWagmi) {
await connectWagmi({ const connector = resolveWagmiConnector({
connector: resolveWagmiConnector({
walletType, walletType,
walletConnection, walletConnection,
walletConnectConfig, walletConnectConfig,
}),
}); });
if (connector) {
await connectWagmi({
connector,
});
}
} }
} }
} catch (error) { } catch (error) {
@@ -156,7 +161,7 @@ export const useWalletConnection = () => {
walletConnectionType: walletConnection.type, walletConnectionType: walletConnection.type,
}; };
}, },
[isConnectedGraz, signerGraz, isConnectedWagmi, signerWagmi] [isConnectedGraz, signerGraz, isConnectedWagmi, signerWagmi, walletConnectConfig]
); );
const disconnectWallet = useCallback(async () => { const disconnectWallet = useCallback(async () => {
@@ -204,10 +209,9 @@ export const useWalletConnection = () => {
})(); })();
}, [selectedWalletType, signerWagmi, signerGraz]); }, [selectedWalletType, signerWagmi, signerGraz]);
const selectWalletType = async (walletType: WalletType | undefined) => { const selectWalletType = (walletType: WalletType | undefined) => {
if (selectedWalletType) { if (selectedWalletType) {
setSelectedWalletType(undefined); setSelectedWalletType(undefined);
await new Promise(requestAnimationFrame);
} }
setSelectedWalletType(walletType); setSelectedWalletType(walletType);
+1
View File
@@ -44,6 +44,7 @@ export { default as OrderbookIcon } from './orderbook.svg';
export { default as OverviewIcon } from './overview.svg'; export { default as OverviewIcon } from './overview.svg';
export { default as PencilIcon } from './pencil.svg'; export { default as PencilIcon } from './pencil.svg';
export { default as PlayIcon } from './play.svg'; export { default as PlayIcon } from './play.svg';
export { default as PlusIcon } from './plus.svg';
export { default as PortfolioIcon } from './portfolio.svg'; export { default as PortfolioIcon } from './portfolio.svg';
export { default as PositionsIcon } from './positions.svg'; export { default as PositionsIcon } from './positions.svg';
export { default as PriceChartIcon } from './price-chart.svg'; export { default as PriceChartIcon } from './price-chart.svg';
+1
View File
@@ -0,0 +1 @@
<svg fill="none" height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m6.75.75c0-.198912-.07902-.389678-.21967-.53033-.14065-.1406525-.33142-.21967-.53033-.21967s-.38968.0790175-.53033.21967c-.14065.140652-.21967.331418-.21967.53033v4.5h-4.5c-.198912 0-.389678.07902-.53033.21967-.1406525.14065-.21967.33142-.21967.53033s.0790175.38968.21967.53033c.140652.14065.331418.21967.53033.21967h4.5v4.5c0 .1989.07902.3897.21967.5303.14065.1407.33142.2197.53033.2197s.38968-.079.53033-.2197c.14065-.1406.21967-.3314.21967-.5303v-4.5h4.5c.1989 0 .3897-.07902.5303-.21967.1407-.14065.2197-.33142.2197-.53033s-.079-.38968-.2197-.53033c-.1406-.14065-.3314-.21967-.5303-.21967h-4.5z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 731 B

+107 -9
View File
@@ -2,6 +2,7 @@ import Abacus, { type Nullable } from '@dydxprotocol/v4-abacus';
import Long from 'long'; import Long from 'long';
import type { IndexedTx } from '@cosmjs/stargate'; import type { IndexedTx } from '@cosmjs/stargate';
import { GAS_MULTIPLIER, encodeJson } from '@dydxprotocol/v4-client-js'; import { GAS_MULTIPLIER, encodeJson } from '@dydxprotocol/v4-client-js';
import { EncodeObject } from '@cosmjs/proto-signing';
import { import {
CompositeClient, CompositeClient,
@@ -41,6 +42,7 @@ import { openDialog } from '@/state/dialogs';
import { StatefulOrderError } from '../errors'; import { StatefulOrderError } from '../errors';
import { bytesToBigInt } from '../numbers'; import { bytesToBigInt } from '../numbers';
import { log } from '../telemetry'; import { log } from '../telemetry';
import { hashFromTx } from '../hashfromTx';
class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol { class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
private compositeClient: CompositeClient | undefined; private compositeClient: CompositeClient | undefined;
@@ -63,10 +65,14 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
} }
setNobleWallet(nobleWallet: LocalWallet) { setNobleWallet(nobleWallet: LocalWallet) {
try {
this.nobleWallet = nobleWallet; this.nobleWallet = nobleWallet;
if (this.nobleClient) { if (this.nobleClient) {
this.nobleClient.connect(nobleWallet); this.nobleClient.connect(nobleWallet);
} }
} catch (e) {
log('DydxChainTransactions/setNobleWallet', e);
}
} }
async connectNetwork( async connectNetwork(
@@ -112,10 +118,15 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
this.compositeClient = compositeClient; this.compositeClient = compositeClient;
try {
if (nobleValidatorUrl) { if (nobleValidatorUrl) {
this.nobleClient = new NobleClient(nobleValidatorUrl); this.nobleClient = new NobleClient(nobleValidatorUrl);
if (this.nobleWallet) await this.nobleClient.connect(this.nobleWallet); if (this.nobleWallet) await this.nobleClient.connect(this.nobleWallet);
} }
} catch (e) {
log('DydxChainTransactions/connectNetwork/NobleClient', e);
}
// Dispatch custom event to notify other parts of the app that the network has been connected // Dispatch custom event to notify other parts of the app that the network has been connected
const customEvent = new CustomEvent('abacus:connectNetwork', { const customEvent = new CustomEvent('abacus:connectNetwork', {
detail: parsedParams, detail: parsedParams,
@@ -201,8 +212,8 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
timeInForce as OrderTimeInForce, timeInForce as OrderTimeInForce,
goodTilTimeInSeconds ?? 0, goodTilTimeInSeconds ?? 0,
execution as OrderExecution, execution as OrderExecution,
postOnly, postOnly ?? undefined,
reduceOnly, reduceOnly ?? undefined,
triggerPrice ?? undefined triggerPrice ?? undefined
); );
@@ -340,12 +351,7 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
} }
} }
async sendNobleIBC( async sendNobleIBC(params: { msgTypeUrl: string; msg: any }): Promise<string> {
params: {
msgTypeUrl: string,
msg: any,
}
): Promise<string> {
if (!this.nobleClient?.isConnected) { if (!this.nobleClient?.isConnected) {
throw new Error('Missing nobleClient or localWallet'); throw new Error('Missing nobleClient or localWallet');
} }
@@ -358,7 +364,8 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
const fee = await this.nobleClient.simulateTransaction([ibcMsg]); const fee = await this.nobleClient.simulateTransaction([ibcMsg]);
// take out fee from amount before sweeping // take out fee from amount before sweeping
const amount = parseInt(ibcMsg.value.token.amount, 10) - const amount =
parseInt(ibcMsg.value.token.amount, 10) -
Math.floor(parseInt(fee.amount[0].amount, 10) * GAS_MULTIPLIER); Math.floor(parseInt(fee.amount[0].amount, 10) * GAS_MULTIPLIER);
if (amount <= 0) { if (amount <= 0) {
@@ -380,6 +387,86 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
} }
} }
async withdrawToNobleIBC(params: {
subaccountNumber: number;
amount: string;
ibcPayload: string;
}): Promise<string> {
if (!this.compositeClient || !this.localWallet) {
throw new Error('Missing compositeClient or localWallet');
}
const { subaccountNumber, amount, ibcPayload } = params ?? {};
const parsedIbcPayload: {
msgTypeUrl: string;
msg: any;
} = ibcPayload ? JSON.parse(atob(ibcPayload)) : undefined;
try {
const msg = this.compositeClient.withdrawFromSubaccountMessage(
new SubaccountClient(this.localWallet, subaccountNumber),
parseFloat(amount).toFixed(this.compositeClient.validatorClient.config.denoms.USDC_DECIMALS)
);
const ibcMsg: EncodeObject = {
typeUrl: parsedIbcPayload.msgTypeUrl,
value: parsedIbcPayload.msg,
};
const tx = await this.compositeClient.send(
this.localWallet,
() => Promise.resolve([msg, ibcMsg]),
false
);
return JSON.stringify({
txHash: hashFromTx(tx?.hash),
});
} catch (error) {
log('DydxChainTransactions/withdrawToNobleIBC', error);
return JSON.stringify({
error,
});
}
}
async cctpWithdraw(params: { typeUrl: string; value: any }): Promise<string> {
if (!this.nobleClient?.isConnected) {
throw new Error('Missing nobleClient or localWallet');
}
try {
const ibcMsg = {
typeUrl: params.typeUrl, // '/circle.cctp.v1.MsgDepositForBurn',
value: params.value,
};
const fee = await this.nobleClient.simulateTransaction([ibcMsg]);
// take out fee from amount before sweeping
const amount =
parseInt(ibcMsg.value.amount, 10) -
Math.floor(parseInt(fee.amount[0].amount, 10) * GAS_MULTIPLIER);
if (amount <= 0) {
throw new Error('noble balance does not cover fees');
}
ibcMsg.value.amount = amount.toString();
const tx = await this.nobleClient.send([ibcMsg]);
const parsedTx = this.parseToPrimitives(tx);
return JSON.stringify(parsedTx);
} catch (error) {
log('DydxChainTransactions/cctpWithdraw', error);
return JSON.stringify({
error,
});
}
}
async transaction( async transaction(
type: TransactionTypes, type: TransactionTypes,
paramsInJson: Abacus.Nullable<string>, paramsInJson: Abacus.Nullable<string>,
@@ -414,6 +501,17 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
callback(result); callback(result);
break; break;
} }
case TransactionType.WithdrawToNobleIBC: {
const result = await this.withdrawToNobleIBC(params);
callback(result);
break;
}
case TransactionType.CctpWithdraw: {
const result = await this.cctpWithdraw(params);
callback(result);
break;
break;
}
default: { default: {
break; break;
} }
+9 -2
View File
@@ -84,8 +84,7 @@ class AbacusStateManager {
); );
const appConfigs = AbacusAppConfig.Companion.forWeb; const appConfigs = AbacusAppConfig.Companion.forWeb;
if (!isMainnet || testFlags.withCCTP) appConfigs.squidVersion = AbacusAppConfig.SquidVersion.V2;
appConfigs.squidVersion = AbacusAppConfig.SquidVersion.V2DepositOnly;
this.stateManager = new AsyncAbacusStateManager( this.stateManager = new AsyncAbacusStateManager(
'', '',
@@ -262,6 +261,14 @@ class AbacusStateManager {
) => void ) => void
) => this.stateManager.cancelOrder(orderId, callback); ) => this.stateManager.cancelOrder(orderId, callback);
cctpWithdraw = (
callback: (
success: boolean,
parsingError: Nullable<ParsingError>,
data: string,
) => void
): void => this.stateManager.commitCCTPWithdraw(callback);
// ------ Utils ------ // // ------ Utils ------ //
getHistoricalPnlPeriod = (): Nullable<HistoricalPnlPeriods> => getHistoricalPnlPeriod = (): Nullable<HistoricalPnlPeriods> =>
this.stateManager.historicalPnlPeriod; this.stateManager.historicalPnlPeriod;
+3
View File
@@ -0,0 +1,3 @@
export const hashFromTx = (
txHash: string | Uint8Array
): string => `0x${Buffer.from(txHash).toString('hex')}`;
+1 -5
View File
@@ -5,7 +5,7 @@ class TestFlags {
this.queryParams = {}; this.queryParams = {};
const hash = window.location.hash; const hash = window.location.hash;
const queryIndex = hash.indexOf('?'); const queryIndex = hash.indexOf('?');
if (queryIndex === -1) return if (queryIndex === -1) return;
const queryParamsString = hash.substring(queryIndex + 1); const queryParamsString = hash.substring(queryIndex + 1);
const params = new URLSearchParams(queryParamsString); const params = new URLSearchParams(queryParamsString);
@@ -19,10 +19,6 @@ class TestFlags {
return !!this.queryParams.displayInitializingMarkets; return !!this.queryParams.displayInitializingMarkets;
} }
get displayAllMarkets() {
return !!this.queryParams.displayAllMarkets;
}
get withCCTP() { get withCCTP() {
return !!this.queryParams.withCCTP; return !!this.queryParams.withCCTP;
} }
+109 -115
View File
@@ -1,7 +1,6 @@
import { createConfig, configureChains, mainnet, Chain, Connector, usePublicClient } from 'wagmi'; import { createConfig, createConnector, http, usePublicClient } from 'wagmi';
import { goerli } from 'wagmi/chains';
import { import {
type Chain,
arbitrum, arbitrum,
arbitrumGoerli, arbitrumGoerli,
avalanche, avalanche,
@@ -26,34 +25,33 @@ import {
fantomTestnet, fantomTestnet,
celo, celo,
celoAlfajores, celoAlfajores,
} from 'viem/chains'; mainnet,
goerli,
sepolia,
} from 'wagmi/chains';
import { alchemyProvider } from 'wagmi/providers/alchemy'; import { coinbaseWallet, injected, metaMask, walletConnect } from 'wagmi/connectors';
import { jsonRpcProvider } from 'wagmi/providers/jsonRpc'; import type { ExternalProvider } from '@ethersproject/providers';
import { publicProvider } from 'wagmi/providers/public';
import { CoinbaseWalletConnector } from 'wagmi/connectors/coinbaseWallet';
import { InjectedConnector } from 'wagmi/connectors/injected';
import { MetaMaskConnector } from 'wagmi/connectors/metaMask';
import { WalletConnectConnector } from 'wagmi/connectors/walletConnect';
import { import {
type WalletConnectConfig,
type WalletConnection, type WalletConnection,
WalletConnectionType, WalletConnectionType,
type WalletType, WalletType,
walletConnectionTypes, WALLET_CONNECTION_TYPES,
wallets, wallets,
WALLET_CONNECT_EXPLORER_RECOMMENDED_IDS, WALLET_CONNECT_EXPLORER_RECOMMENDED_IDS,
WalletConfig,
} from '@/constants/wallets'; } from '@/constants/wallets';
import { isTruthy } from './isTruthy'; import { isTruthy } from './isTruthy';
import { log } from './telemetry';
// Config // Config
const WAGMI_SUPPORTED_CHAINS: Parameters<typeof createConfig>[0]['chains'] = [
export const WAGMI_SUPPORTED_CHAINS: Chain[] = [
mainnet, mainnet,
goerli, goerli,
sepolia,
arbitrum, arbitrum,
arbitrumGoerli, arbitrumGoerli,
avalanche, avalanche,
@@ -80,118 +78,104 @@ export const WAGMI_SUPPORTED_CHAINS: Chain[] = [
celoAlfajores, celoAlfajores,
]; ];
const { chains, publicClient, webSocketPublicClient } = configureChains( export const config = createConfig({
WAGMI_SUPPORTED_CHAINS, chains: WAGMI_SUPPORTED_CHAINS,
[ transports: Object.fromEntries(
import.meta.env.VITE_ALCHEMY_API_KEY && Object.values(WAGMI_SUPPORTED_CHAINS).map(({ id }) => [id, http()])
alchemyProvider({ apiKey: import.meta.env.VITE_ALCHEMY_API_KEY }), ),
jsonRpcProvider({ });
rpc: (chain) => ({ http: chain.rpcUrls.default.http[0] }),
}),
publicProvider(),
].filter(isTruthy)
);
const injectedConnectorOptions = { const createdInjectedConnectorWithProvider = ({ provider }: { provider: ExternalProvider }) => {
chains, console.log(provider);
options: { return injected({
target() {
return {
id: 'windowProvider',
name: 'Injected', name: 'Injected',
shimDisconnect: true, provider,
shimChainChangedDisconnect: false, };
}, },
shimDisconnect: true,
});
}; };
type WalletConnectConfig = { const getConnector = ({
client: { walletConnectionType,
name: string; walletConnectConfig,
description: string; }: {
iconUrl: string; walletConnectionType: WalletConnectionType;
}; walletConnectConfig: WalletConnectConfig;
v2: { }) => {
projectId: string; console.log(walletConnectionType);
switch (walletConnectionType) {
case WalletConnectionType.WalletConnect2:
return [
walletConnect(
getConnectorInfoForWc2({
walletConnectConfig,
})
),
];
case WalletConnectionType.CoinbaseWalletSdk:
return [
coinbaseWallet({
appName: 'dYdX',
appLogoUrl: walletConnectConfig.client.iconUrl,
reloadOnDisconnect: false,
darkMode: true,
}),
];
case WalletConnectionType.InjectedEip1193:
return injected({
target() {
return {
id: 'windowProvider',
name: 'Injected',
provider: window.ethereum,
}; };
},
shimDisconnect: true,
});
case WalletConnectionType.CosmosSigner:
default: {
return undefined;
}
}
}; };
const getWalletconnect2ConnectorOptions = ( const getConnectorInfoForWc2 = ({
config: WalletConnectConfig walletConnectId,
): ConstructorParameters<typeof WalletConnectConnector>[0] => ({ walletConnectConfig,
chains, }: {
options: { walletConnectId?: string;
projectId: config.v2.projectId, walletConnectConfig: WalletConnectConfig;
}) => {
const explorerRecommendedWalletIds = walletConnectId
? [walletConnectId]
: WALLET_CONNECT_EXPLORER_RECOMMENDED_IDS;
return {
projectId: walletConnectConfig.v2.projectId,
metadata: { metadata: {
name: config.client.name, name: walletConnectConfig.client.name,
description: config.client.description, description: walletConnectConfig.client.description,
url: import.meta.env.VITE_BASE_URL, url: import.meta.env.VITE_BASE_URL,
icons: [config.client.iconUrl], icons: [walletConnectConfig.client.iconUrl],
}, },
showQrModal: true, showQrModal: true,
qrModalOptions: { qrModalOptions: {
themeMode: 'dark' as const, themeMode: 'dark' as const,
themeVariables: { themeVariables: {
'--wcm-accent-color': '#5973fe', '--wcm-accent-color': 'var(--color-accent)',
'--wcm-font-family': 'var(--fontFamily-base)', '--wcm-font-family': 'var(--fontFamily-base)',
'--wcm-background-color': 'var(--color-accent)',
}, },
explorerRecommendedWalletIds: WALLET_CONNECT_EXPLORER_RECOMMENDED_IDS, explorerRecommendedWalletIds,
}, },
}, };
});
const getConnectors = (walletConnectConfig: WalletConnectConfig) => [
new MetaMaskConnector({
chains,
options: {
shimDisconnect: true,
},
}),
new CoinbaseWalletConnector({
chains,
options: {
appName: 'wagmi',
reloadOnDisconnect: false,
},
}),
new WalletConnectConnector(getWalletconnect2ConnectorOptions(walletConnectConfig)),
new InjectedConnector(injectedConnectorOptions),
];
export const config = createConfig({
autoConnect: true,
// connectors,
publicClient,
webSocketPublicClient,
});
// Custom connectors
import type { ExternalProvider } from '@ethersproject/providers';
// Create a custom wagmi InjectedConnector using a specific injected EIP-1193 provider (instead of wagmi's default detection logic)
const createInjectedConnectorWithProvider = (provider: ExternalProvider) =>
new (class extends InjectedConnector {
getProvider = async () =>
provider as unknown as Awaited<ReturnType<InjectedConnector['getProvider']>>;
})(injectedConnectorOptions) as InjectedConnector;
const createWalletConnect2ConnectorWithId = (
walletconnectId: string,
walletConnectConfig: WalletConnectConfig
) => {
const walletconnect2ConnectorOptions = getWalletconnect2ConnectorOptions(walletConnectConfig);
return new WalletConnectConnector({
...walletconnect2ConnectorOptions,
options: {
...walletconnect2ConnectorOptions.options,
qrModalOptions: {
...walletconnect2ConnectorOptions.options.qrModalOptions,
explorerRecommendedWalletIds: [walletconnectId],
explorerExcludedWalletIds: 'ALL',
},
},
});
}; };
// Custom connector from wallet selection // Custom connector from wallet selection
export const resolveWagmiConnector = ({ export const resolveWagmiConnector = ({
walletType, walletType,
walletConnection, walletConnection,
@@ -202,13 +186,23 @@ export const resolveWagmiConnector = ({
walletConnectConfig: WalletConnectConfig; walletConnectConfig: WalletConnectConfig;
}) => { }) => {
const walletConfig = wallets[walletType]; const walletConfig = wallets[walletType];
const walletConnectionConfig = walletConnectionTypes[walletConnection.type];
return walletConnection.type === WalletConnectionType.InjectedEip1193 && walletConnection.provider if (walletConnection.type === WalletConnectionType.InjectedEip1193 && walletConnection.provider) {
? createInjectedConnectorWithProvider(walletConnection.provider) if (walletType === WalletType.MetaMask) {
: walletConnection.type === WalletConnectionType.WalletConnect2 && walletConfig.walletconnect2Id return metaMask();
? createWalletConnect2ConnectorWithId(walletConfig.walletconnect2Id, walletConnectConfig) }
: getConnectors(walletConnectConfig).find( return createdInjectedConnectorWithProvider({ provider: walletConnection.provider });
({ id }: { id: string }) => id === walletConnectionConfig.wagmiConnectorId } else if (
walletConnection.type === WalletConnectionType.WalletConnect2 &&
walletConfig.walletconnect2Id
) {
return walletConnect(
getConnectorInfoForWc2({
walletConnectId: walletConfig.walletconnect2Id,
walletConnectConfig,
})
); );
} else {
return getConnector({ walletConnectionType: walletConnection.type, walletConnectConfig });
}
}; };
+3 -1
View File
@@ -1,4 +1,6 @@
import { DialogPlacement } from '@/components/Dialog'; import { DialogPlacement } from '@/components/Dialog';
import { NotificationsMenu } from '@/views/menus/NotificationsMenu'; import { NotificationsMenu } from '@/views/menus/NotificationsMenu';
export const AlertsPage = () => <NotificationsMenu placement={DialogPlacement.Inline} />; const AlertsPage = () => <NotificationsMenu placement={DialogPlacement.Inline} />;
export default AlertsPage;
+3 -1
View File
@@ -2,7 +2,7 @@ import styled, { AnyStyledComponent } from 'styled-components';
import { articleMixins } from '@/styles/articleMixins'; import { articleMixins } from '@/styles/articleMixins';
export const PrivacyPolicyPage = () => ( const PrivacyPolicyPage = () => (
<Styled.Article> <Styled.Article>
<header> <header>
<h1>Privacy Policy</h1> <h1>Privacy Policy</h1>
@@ -10,6 +10,8 @@ export const PrivacyPolicyPage = () => (
</Styled.Article> </Styled.Article>
); );
export default PrivacyPolicyPage;
const Styled: Record<string, AnyStyledComponent> = {}; const Styled: Record<string, AnyStyledComponent> = {};
Styled.Article = styled.article` Styled.Article = styled.article`
+3 -1
View File
@@ -2,7 +2,7 @@ import styled, { AnyStyledComponent } from 'styled-components';
import { articleMixins } from '@/styles/articleMixins'; import { articleMixins } from '@/styles/articleMixins';
export const TermsOfUsePage = () => ( const TermsOfUsePage = () => (
<Styled.Article> <Styled.Article>
<header> <header>
<h1>Sample Terms of Use</h1> <h1>Sample Terms of Use</h1>
@@ -1076,6 +1076,8 @@ export const TermsOfUsePage = () => (
</Styled.Article> </Styled.Article>
); );
export default TermsOfUsePage;
const Styled: Record<string, AnyStyledComponent> = {}; const Styled: Record<string, AnyStyledComponent> = {};
Styled.Article = styled.article` Styled.Article = styled.article`
+14 -9
View File
@@ -1,17 +1,20 @@
import { lazy, Suspense } from 'react';
import { shallowEqual, useDispatch, useSelector } from 'react-redux'; import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import styled, { type AnyStyledComponent } from 'styled-components'; import styled, { type AnyStyledComponent } from 'styled-components';
import { Navigate, Route, Routes } from 'react-router-dom'; import { Navigate, Route, Routes } from 'react-router-dom';
import { OnboardingState } from '@/constants/account'; import { OnboardingState } from '@/constants/account';
import { ButtonAction } from '@/constants/buttons';
import { DialogTypes } from '@/constants/dialogs'; import { DialogTypes } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { HistoryRoute, PortfolioRoute } from '@/constants/routes'; import { HistoryRoute, PortfolioRoute } from '@/constants/routes';
import { useAccountBalance, useBreakpoints, useDocumentTitle, useStringGetter } from '@/hooks'; import { useAccountBalance, useBreakpoints, useDocumentTitle, useStringGetter } from '@/hooks';
import { layoutMixins } from '@/styles/layoutMixins';
import { FillsTable, FillsTableColumnKey } from '@/views/tables/FillsTable'; import { FillsTable, FillsTableColumnKey } from '@/views/tables/FillsTable';
import { FundingPaymentsTable } from '@/views/tables/FundingPaymentsTable'; import { FundingPaymentsTable } from '@/views/tables/FundingPaymentsTable';
import { TransferHistoryTable } from '@/views/tables/TransferHistoryTable'; import { TransferHistoryTable } from '@/views/tables/TransferHistoryTable';
import { Button } from '@/components/Button';
import { Icon, IconName } from '@/components/Icon'; import { Icon, IconName } from '@/components/Icon';
import { NavigationMenu } from '@/components/NavigationMenu'; import { NavigationMenu } from '@/components/NavigationMenu';
import { WithSidebar } from '@/components/WithSidebar'; import { WithSidebar } from '@/components/WithSidebar';
@@ -20,15 +23,15 @@ import { getOnboardingState, getSubaccount } from '@/state/accountSelectors';
import { openDialog } from '@/state/dialogs'; import { openDialog } from '@/state/dialogs';
import { PortfolioNavMobile } from './PortfolioNavMobile'; import { PortfolioNavMobile } from './PortfolioNavMobile';
import { Overview } from './Overview'; import { LoadingSpace } from '@/components/Loading/LoadingSpinner';
import { Positions } from './Positions';
import { Orders } from './Orders';
import { Fees } from './Fees';
import { History } from './History';
import { layoutMixins } from '@/styles/layoutMixins'; const Overview = lazy(() => import('./Overview').then((module) => ({ default: module.Overview })));
import { Button } from '@/components/Button'; const Positions = lazy(() =>
import { ButtonAction } from '@/constants/buttons'; import('./Positions').then((module) => ({ default: module.Positions }))
);
const Orders = lazy(() => import('./Orders').then((module) => ({ default: module.Orders })));
const Fees = lazy(() => import('./Fees').then((module) => ({ default: module.Fees })));
const History = lazy(() => import('./History').then((module) => ({ default: module.History })));
export default () => { export default () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -44,6 +47,7 @@ export default () => {
useDocumentTitle(stringGetter({ key: STRING_KEYS.PORTFOLIO })); useDocumentTitle(stringGetter({ key: STRING_KEYS.PORTFOLIO }));
const routesComponent = ( const routesComponent = (
<Suspense fallback={<LoadingSpace id="portfolio" />}>
<Routes> <Routes>
<Route path={PortfolioRoute.Overview} element={<Overview />} /> <Route path={PortfolioRoute.Overview} element={<Overview />} />
<Route path={PortfolioRoute.Positions} element={<Positions />} /> <Route path={PortfolioRoute.Positions} element={<Positions />} />
@@ -87,6 +91,7 @@ export default () => {
</Route> </Route>
<Route path="*" element={<Navigate to={PortfolioRoute.Overview} replace />} /> <Route path="*" element={<Navigate to={PortfolioRoute.Overview} replace />} />
</Routes> </Routes>
</Suspense>
); );
return isTablet ? ( return isTablet ? (
+16 -8
View File
@@ -1,11 +1,12 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { useQuery } from 'react-query'; import { useQuery } from '@tanstack/react-query';
import styled, { AnyStyledComponent } from 'styled-components'; import styled, { AnyStyledComponent } from 'styled-components';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { ButtonAction } from '@/constants/buttons'; import { ButtonAction } from '@/constants/buttons';
import { DialogTypes } from '@/constants/dialogs'; import { DialogTypes } from '@/constants/dialogs';
import { QueryKeys } from '@/constants/queries';
import breakpoints from '@/styles/breakpoints'; import breakpoints from '@/styles/breakpoints';
import { useAccounts, useBreakpoints, useStringGetter } from '@/hooks'; import { useAccounts, useBreakpoints, useStringGetter } from '@/hooks';
@@ -24,6 +25,8 @@ import { openDialog } from '@/state/dialogs';
import { log } from '@/lib/telemetry'; import { log } from '@/lib/telemetry';
const SEASON_NUMBER = 2;
export const LaunchIncentivesPanel = () => { export const LaunchIncentivesPanel = () => {
const { isNotTablet } = useBreakpoints(); const { isNotTablet } = useBreakpoints();
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -66,17 +69,24 @@ const EstimatedRewards = () => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { dydxAddress } = useAccounts(); const { dydxAddress } = useAccounts();
const { data, isLoading } = useQuery({ const { data, isLoading, status, error } = useQuery({
enabled: !!dydxAddress, enabled: !!dydxAddress,
queryKey: `launch_incentives_rewards_${dydxAddress ?? ''}`, queryKey: [QueryKeys.LAUNCH_INCENTIVES, dydxAddress, SEASON_NUMBER],
queryFn: async () => { queryFn: async () => {
if (!dydxAddress) return undefined; if (!dydxAddress) return undefined;
const resp = await fetch(`https://cloud.chaoslabs.co/query/api/dydx/points/${dydxAddress}`); const resp = await fetch(
`https://cloud.chaoslabs.co/query/api/dydx/points/${dydxAddress}?n=${SEASON_NUMBER}`
);
return (await resp.json())?.incentivePoints; return (await resp.json())?.incentivePoints;
}, },
onError: (error: Error) => log('LaunchIncentives/fetchPoints', error),
}); });
useEffect(() => {
if (status === 'error') {
log('LaunchIncentives/fetchPoints', error);
}
}, [status]);
return ( return (
<Styled.EstimatedRewardsCard> <Styled.EstimatedRewardsCard>
<Styled.EstimatedRewardsCardContent> <Styled.EstimatedRewardsCardContent>
@@ -85,9 +95,7 @@ const EstimatedRewards = () => {
<Styled.Season> <Styled.Season>
{stringGetter({ {stringGetter({
key: STRING_KEYS.LAUNCH_INCENTIVES_SEASON_NUM, key: STRING_KEYS.LAUNCH_INCENTIVES_SEASON_NUM,
params: { params: { SEASON_NUMBER },
SEASON_NUMBER: 1,
},
})} })}
</Styled.Season> </Styled.Season>
</div> </div>
+80
View File
@@ -0,0 +1,80 @@
import styled, { AnyStyledComponent } from 'styled-components';
import { STRING_KEYS } from '@/constants/localization';
import { layoutMixins } from '@/styles/layoutMixins';
import { useStringGetter } from '@/hooks';
import { Accordion } from '@/components/Accordion';
import { Link } from '@/components/Link';
import { Panel } from '@/components/Panel';
const REWARDS_LEARN_MORE_LINK = ''; // to be configured
export const RewardsHelpPanel = () => {
const stringGetter = useStringGetter();
return (
<Styled.HelpCard
slotHeader={
<Styled.Header>
<h3>{stringGetter({ key: STRING_KEYS.HELP })}</h3>
{REWARDS_LEARN_MORE_LINK && (
<Link withIcon href={REWARDS_LEARN_MORE_LINK}>
{stringGetter({ key: STRING_KEYS.LEARN_MORE })}
</Link>
)}
</Styled.Header>
}
>
<Accordion
items={[
{
header: 'Who is eligible for trading rewards?',
content: 'All traders are eligible for trading rewards.',
},
{
header: 'How do trading rewards work?',
content:
'Immediately after each fill, trading rewards are sent directly to the traders dYdX Chain address, based on the amount of fees paid by the trader.',
},
{
header: 'How do I claim my rewards?',
content:
'Each block, trading rewards are automatically sent directly to the traders dYdX Chain address.',
},
]}
/>
</Styled.HelpCard>
);
};
const Styled: Record<string, AnyStyledComponent> = {};
Styled.HelpCard = styled(Panel)`
--panel-content-paddingY: 0;
width: 100%;
height: max-content;
padding: 0;
gap: 0;
text-align: start;
`;
Styled.Header = styled.div`
${layoutMixins.spacedRow}
gap: 1ch;
padding: 1.25rem 1.5rem;
border-bottom: var(--border-width) solid var(--border-color);
font: var(--font-small-book);
h3 {
font: var(--font-medium-book);
color: var(--color-text-2);
}
`;
Styled.Link = styled(Link)`
display: inline-flex;
`;
+3 -1
View File
@@ -21,7 +21,7 @@ import { DYDXBalancePanel } from './DYDXBalancePanel';
import { MigratePanel } from './MigratePanel'; import { MigratePanel } from './MigratePanel';
import { LaunchIncentivesPanel } from './LaunchIncentivesPanel'; import { LaunchIncentivesPanel } from './LaunchIncentivesPanel';
export const RewardsPage = () => { const RewardsPage = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { governanceLearnMore, stakingLearnMore } = useURLConfigs(); const { governanceLearnMore, stakingLearnMore } = useURLConfigs();
@@ -84,6 +84,8 @@ export const RewardsPage = () => {
); );
}; };
export default RewardsPage;
const Styled: Record<string, AnyStyledComponent> = {}; const Styled: Record<string, AnyStyledComponent> = {};
Styled.Page = styled.div` Styled.Page = styled.div`
+3 -1
View File
@@ -25,7 +25,7 @@ import { useNetworks } from '@/views/menus/useNetworks';
import { SettingsHeader } from './SettingsHeader'; import { SettingsHeader } from './SettingsHeader';
import { ComingSoonSpace } from '@/components/ComingSoon'; import { ComingSoonSpace } from '@/components/ComingSoon';
export const SettingsPage = () => { const SettingsPage = () => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { pathname } = useLocation(); const { pathname } = useLocation();
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -105,3 +105,5 @@ export const SettingsPage = () => {
</> </>
); );
}; };
export default SettingsPage;
+19 -5
View File
@@ -1,8 +1,7 @@
import { useState } from 'react'; import { useMemo, useState } from 'react';
import { shallowEqual, useSelector } from 'react-redux'; import { shallowEqual, useSelector } from 'react-redux';
import styled, { type AnyStyledComponent } from 'styled-components'; import styled, { type AnyStyledComponent } from 'styled-components';
import { AbacusOrderStatus } from '@/constants/abacus';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { useBreakpoints, useStringGetter } from '@/hooks'; import { useBreakpoints, useStringGetter } from '@/hooks';
@@ -28,7 +27,6 @@ import {
getCurrentMarketTradeInfoNumbers, getCurrentMarketTradeInfoNumbers,
getHasUnseenFillUpdates, getHasUnseenFillUpdates,
getHasUnseenOrderUpdates, getHasUnseenOrderUpdates,
getLatestOrderStatus,
getTradeInfoNumbers, getTradeInfoNumbers,
} from '@/state/accountSelectors'; } from '@/state/accountSelectors';
@@ -82,7 +80,8 @@ export const HorizontalPanel = ({ isOpen = true, setIsOpen }: ElementProps) => {
showCurrentMarket ? numOpenOrders : numTotalOpenOrders showCurrentMarket ? numOpenOrders : numTotalOpenOrders
); );
const tabItems = [ const tabItems = useMemo(
() => [
{ {
value: InfoSection.Position, value: InfoSection.Position,
label: stringGetter({ label: stringGetter({
@@ -118,6 +117,7 @@ export const HorizontalPanel = ({ isOpen = true, setIsOpen }: ElementProps) => {
), ),
}, },
{ {
asChild: true,
value: InfoSection.Orders, value: InfoSection.Orders,
label: stringGetter({ key: STRING_KEYS.ORDERS }), label: stringGetter({ key: STRING_KEYS.ORDERS }),
@@ -152,6 +152,7 @@ export const HorizontalPanel = ({ isOpen = true, setIsOpen }: ElementProps) => {
), ),
}, },
{ {
asChild: true,
value: InfoSection.Fills, value: InfoSection.Fills,
label: stringGetter({ key: STRING_KEYS.FILLS }), label: stringGetter({ key: STRING_KEYS.FILLS }),
@@ -200,7 +201,20 @@ export const HorizontalPanel = ({ isOpen = true, setIsOpen }: ElementProps) => {
// <FundingPaymentsTable currentMarket={showCurrentMarket ? currentMarket?.id : undefined} /> // <FundingPaymentsTable currentMarket={showCurrentMarket ? currentMarket?.id : undefined} />
// ), // ),
// }, // },
]; ],
[
stringGetter,
currentMarketId,
showCurrentMarket,
isTablet,
isWaitingForOrderToIndex,
isAccountViewOnly,
ordersTagNumber,
fillsTagNumber,
hasUnseenFillUpdates,
hasUnseenOrderUpdates,
]
);
return isTablet ? ( return isTablet ? (
<MobileTabs defaultValue={InfoSection.Position} items={tabItems} withBorders={false} /> <MobileTabs defaultValue={InfoSection.Position} items={tabItems} withBorders={false} />
-5
View File
@@ -877,11 +877,6 @@ export const layoutMixins: Record<
} }
`, `,
perspectiveArea: css`
perspective: 100rem;
transform-style: preserve-3d;
`,
absolute: css` absolute: css`
position: absolute; position: absolute;
top: 0; top: 0;
-1
View File
@@ -63,7 +63,6 @@ Styled.AccountInfoSectionContainer = styled.div<{ showAccountInfo?: boolean }>`
${layoutMixins.column} ${layoutMixins.column}
height: var(--account-info-section-height); height: var(--account-info-section-height);
min-height: var(--account-info-section-height); min-height: var(--account-info-section-height);
overflow-x: clip;
${({ showAccountInfo }) => ${({ showAccountInfo }) =>
!showAccountInfo && !showAccountInfo &&
@@ -203,7 +203,6 @@ const Styled: Record<string, AnyStyledComponent> = {};
Styled.Stack = styled.div` Styled.Stack = styled.div`
${layoutMixins.stack} ${layoutMixins.stack}
${layoutMixins.perspectiveArea}
`; `;
Styled.CornerButton = styled(Button)` Styled.CornerButton = styled(Button)`
@@ -293,7 +292,14 @@ Styled.TransferButtons = styled.div`
Styled.ConnectedAccountInfoContainer = styled.div<{ $showHeader?: boolean }>` Styled.ConnectedAccountInfoContainer = styled.div<{ $showHeader?: boolean }>`
${layoutMixins.column} ${layoutMixins.column}
@media ${breakpoints.notTablet} {
${layoutMixins.withOuterAndInnerBorders} ${layoutMixins.withOuterAndInnerBorders}
}
@media ${breakpoints.tablet} {
${layoutMixins.withInnerBorder}
}
${({ $showHeader }) => ${({ $showHeader }) =>
$showHeader && $showHeader &&
-3
View File
@@ -2,9 +2,7 @@ import styled, { type AnyStyledComponent } from 'styled-components';
import { shallowEqual, useSelector } from 'react-redux'; import { shallowEqual, useSelector } from 'react-redux';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { MARKETS_TO_DISPLAY } from '@/constants/markets';
import { useBreakpoints, useStringGetter } from '@/hooks'; import { useBreakpoints, useStringGetter } from '@/hooks';
import { testFlags } from '@/lib/testFlags';
import { breakpoints } from '@/styles'; import { breakpoints } from '@/styles';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
@@ -34,7 +32,6 @@ export const ExchangeBillboards: React.FC<ExchangeBillboardsProps> = ({
Object.values(perpetualMarkets) Object.values(perpetualMarkets)
.filter(Boolean) .filter(Boolean)
.filter(({ id }) => (testFlags.displayAllMarkets ? true : MARKETS_TO_DISPLAY.includes(id)))
.forEach(({ oraclePrice, perpetual }) => { .forEach(({ oraclePrice, perpetual }) => {
const { volume24H, trades24H, openInterest = 0 } = perpetual || {}; const { volume24H, trades24H, openInterest = 0 } = perpetual || {};
volume24HUSDC += volume24H ?? 0; volume24HUSDC += volume24H ?? 0;
+2 -7
View File
@@ -114,8 +114,8 @@ export const MarketDetails: React.FC = () => {
), ),
}, },
{ {
key: 'base-initial-margin-fraction', key: 'initial-margin-fraction',
label: stringGetter({ key: STRING_KEYS.BASE_INITIAL_MARGIN_FRACTION }), label: stringGetter({ key: STRING_KEYS.INITIAL_MARGIN_FRACTION }),
tooltip: 'initial-margin-fraction', tooltip: 'initial-margin-fraction',
value: <Output useGrouping value={initialMarginFraction} type={OutputType.SmallPercent} />, value: <Output useGrouping value={initialMarginFraction} type={OutputType.SmallPercent} />,
}, },
@@ -213,13 +213,8 @@ Styled.MarketDetails = styled.div`
`; `;
Styled.Header = styled.header` Styled.Header = styled.header`
/* max-width: fit-content; */
${layoutMixins.column} ${layoutMixins.column}
gap: 1.25rem; gap: 1.25rem;
position: sticky;
bottom: 3rem;
`; `;
Styled.WrapRow = styled.div` Styled.WrapRow = styled.div`
+1 -7
View File
@@ -453,13 +453,6 @@ Styled.Actions = styled.footer`
> :last-child { > :last-child {
flex: 2; flex: 2;
} }
position: sticky;
bottom: clamp(0.5rem, 7.5%, 2rem);
@media ${breakpoints.tablet} {
bottom: 0;
}
`; `;
Styled.Output = styled(Output)<{ sign: NumberSign; smallText?: boolean; margin?: string }>` Styled.Output = styled(Output)<{ sign: NumberSign; smallText?: boolean; margin?: string }>`
@@ -514,6 +507,7 @@ Styled.PositionInfo = styled.div`
Styled.DetachedSection = styled(DetachedSection)` Styled.DetachedSection = styled(DetachedSection)`
padding: 0 1.5rem; padding: 0 1.5rem;
position: relative;
`; `;
Styled.DetachedScrollableSection = styled(DetachedScrollableSection)` Styled.DetachedScrollableSection = styled(DetachedScrollableSection)`
-1
View File
@@ -82,7 +82,6 @@ const Styled: Record<string, AnyStyledComponent> = {};
Styled.PriceChart = styled.div<{ isChartReady?: boolean }>` Styled.PriceChart = styled.div<{ isChartReady?: boolean }>`
${layoutMixins.stack} ${layoutMixins.stack}
${layoutMixins.perspectiveArea}
height: 100%; height: 100%;
@@ -94,7 +94,7 @@ export const FillDetailsDialog = ({ fillId, setIsOpen }: ElementProps) => {
return ( return (
<DetailsDialog <DetailsDialog
slotIcon={<Styled.AssetIcon symbol={asset.id} />} slotIcon={<Styled.AssetIcon symbol={asset?.id} />}
title={resources.typeStringKey && stringGetter({ key: resources.typeStringKey })} title={resources.typeStringKey && stringGetter({ key: resources.typeStringKey })}
items={detailItems} items={detailItems}
setIsOpen={setIsOpen} setIsOpen={setIsOpen}
@@ -194,7 +194,7 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
return ( return (
<DetailsDialog <DetailsDialog
slotIcon={<Styled.AssetIcon symbol={asset.id} />} slotIcon={<Styled.AssetIcon symbol={asset?.id} />}
title={!resources.typeStringKey ? '' : stringGetter({ key: resources.typeStringKey })} title={!resources.typeStringKey ? '' : stringGetter({ key: resources.typeStringKey })}
slotFooter={ slotFooter={
isAccountViewOnly ? null : isOrderStatusClearable(status) ? ( isAccountViewOnly ? null : isOrderStatusClearable(status) ? (
+5 -1
View File
@@ -8,6 +8,7 @@ import { ComboboxDialogMenu } from '@/components/ComboboxDialogMenu';
import { Icon, IconName } from '@/components/Icon'; import { Icon, IconName } from '@/components/Icon';
import { isTruthy } from '@/lib/isTruthy'; import { isTruthy } from '@/lib/isTruthy';
import { breakpoints } from '@/styles';
type ElementProps = { type ElementProps = {
setIsOpen: (open: boolean) => void; setIsOpen: (open: boolean) => void;
@@ -72,8 +73,11 @@ export const HelpDialog = ({ setIsOpen }: ElementProps) => {
const Styled: Record<string, AnyStyledComponent> = {}; const Styled: Record<string, AnyStyledComponent> = {};
Styled.ComboboxDialogMenu = styled(ComboboxDialogMenu)` Styled.ComboboxDialogMenu = styled(ComboboxDialogMenu)`
--dialog-width: var(--dialog-small-width);
--dialog-content-paddingTop: 1rem; --dialog-content-paddingTop: 1rem;
--dialog-content-paddingBottom: 1rem; --dialog-content-paddingBottom: 1rem;
--comboxDialogMenu-item-gap: 1rem; --comboxDialogMenu-item-gap: 1rem;
@media ${breakpoints.notMobile} {
--dialog-width: var(--dialog-small-width);
}
`; `;
@@ -3,19 +3,18 @@ import styled, { AnyStyledComponent } from 'styled-components';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { AlertType } from '@/constants/alerts'; import { AlertType } from '@/constants/alerts';
import { STRING_KEYS } from '@/constants/localization';
import { WalletType, wallets } from '@/constants/wallets';
import { ButtonAction, ButtonSize } from '@/constants/buttons'; import { ButtonAction, ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { WalletType, wallets } from '@/constants/wallets';
import { useStringGetter, useURLConfigs } from '@/hooks';
import { useDisplayedWallets } from '@/hooks/useDisplayedWallets';
import { useWalletConnection } from '@/hooks/useWalletConnection';
import { AlertMessage } from '@/components/AlertMessage'; import { AlertMessage } from '@/components/AlertMessage';
import { Button } from '@/components/Button'; import { Button } from '@/components/Button';
import { Icon } from '@/components/Icon'; import { Icon } from '@/components/Icon';
import { Link } from '@/components/Link'; import { Link } from '@/components/Link';
import { useAccounts, useStringGetter, useURLConfigs } from '@/hooks';
import { useDisplayedWallets } from '@/hooks/useDisplayedWallets';
import { breakpoints } from '@/styles'; import { breakpoints } from '@/styles';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
@@ -25,7 +24,7 @@ export const ChooseWallet = () => {
const displayedWallets = useDisplayedWallets(); const displayedWallets = useDisplayedWallets();
const { selectWalletType, selectedWalletType, selectedWalletError } = useAccounts(); const { selectWalletType, selectedWalletType, selectedWalletError } = useWalletConnection();
return ( return (
<> <>
@@ -86,13 +86,7 @@ export const GenerateKeys = ({
].includes(status); ].includes(status);
const signTypedData = getSignTypedData(selectedNetwork); const signTypedData = getSignTypedData(selectedNetwork);
const { signTypedDataAsync } = useSignTypedData({ const { signTypedDataAsync } = useSignTypedData();
...signTypedData,
domain: {
...signTypedData.domain,
chainId,
},
});
const staticEncryptionKey = import.meta.env.VITE_PK_ENCRYPTION_KEY; const staticEncryptionKey = import.meta.env.VITE_PK_ENCRYPTION_KEY;
@@ -103,7 +97,13 @@ export const GenerateKeys = ({
// 1. First signature // 1. First signature
setStatus(EvmDerivedAccountStatus.Deriving); setStatus(EvmDerivedAccountStatus.Deriving);
const signature = await signTypedDataAsync(); const signature = await signTypedDataAsync({
...signTypedData,
domain: {
...signTypedData.domain,
chainId,
},
});
const { wallet: dydxWallet } = await getWalletFromEvmSignature({ signature }); const { wallet: dydxWallet } = await getWalletFromEvmSignature({ signature });
// 2. Ensure signature is deterministic // 2. Ensure signature is deterministic
@@ -121,7 +121,13 @@ export const GenerateKeys = ({
setStatus(EvmDerivedAccountStatus.EnsuringDeterminism); setStatus(EvmDerivedAccountStatus.EnsuringDeterminism);
// Second signature // Second signature
const additionalSignature = await signTypedDataAsync(); const additionalSignature = await signTypedDataAsync({
...signTypedData,
domain: {
...signTypedData.domain,
chainId,
},
});
if (signature !== additionalSignature) { if (signature !== additionalSignature) {
throw new Error( throw new Error(
@@ -18,11 +18,7 @@ type ElementProps = {
onSelectChain: (chain: string) => void; onSelectChain: (chain: string) => void;
}; };
export const ChainSelectMenu = ({ export const ChainSelectMenu = ({ label, selectedChain, onSelectChain }: ElementProps) => {
label,
selectedChain,
onSelectChain,
}: ElementProps) => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { type, depositOptions, withdrawalOptions, resources } = const { type, depositOptions, withdrawalOptions, resources } =
useSelector(getTransferInputs, shallowEqual) || {}; useSelector(getTransferInputs, shallowEqual) || {};
@@ -10,9 +10,8 @@ import { TransferInputField, TransferInputTokenResource, TransferType } from '@/
import { AlertType } from '@/constants/alerts'; import { AlertType } from '@/constants/alerts';
import { ButtonSize } from '@/constants/buttons'; import { ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { ENVIRONMENT_CONFIG_MAP, isMainnet } from '@/constants/networks';
import { NotificationStatus } from '@/constants/notifications'; import { MAX_CCTP_TRANSFER_AMOUNT, MAX_PRICE_IMPACT, NumberSign } from '@/constants/numbers';
import { NumberSign } from '@/constants/numbers';
import type { EvmAddress } from '@/constants/wallets'; import type { EvmAddress } from '@/constants/wallets';
import { useAccounts, useDebounce, useStringGetter, useSelectedNetwork } from '@/hooks'; import { useAccounts, useDebounce, useStringGetter, useSelectedNetwork } from '@/hooks';
@@ -253,7 +252,9 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
if (txHash) { if (txHash) {
addTransferNotification({ addTransferNotification({
txHash: txHash, txHash: txHash,
toChainId: !isCctp ? ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId : getNobleChainId(), toChainId: !isCctp
? ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId
: getNobleChainId(),
fromChainId: chainIdStr || undefined, fromChainId: chainIdStr || undefined,
toAmount: summary?.usdcSize || undefined, toAmount: summary?.usdcSize || undefined,
triggeredAt: Date.now(), triggeredAt: Date.now(),
@@ -324,6 +325,21 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
return stringGetter({ key: STRING_KEYS.DEPOSIT_MORE_THAN_BALANCE }); return stringGetter({ key: STRING_KEYS.DEPOSIT_MORE_THAN_BALANCE });
} }
if (isCctp) {
if (MustBigNumber(debouncedAmountBN).gte(MAX_CCTP_TRANSFER_AMOUNT)) {
return stringGetter({
key: STRING_KEYS.MAX_CCTP_TRANSFER_LIMIT_EXCEEDED,
params: {
MAX_CCTP_TRANSFER_AMOUNT: MAX_CCTP_TRANSFER_AMOUNT,
},
});
}
}
if (isMainnet && MustBigNumber(summary?.aggregatePriceImpact).gte(MAX_PRICE_IMPACT)) {
return stringGetter({ key: STRING_KEYS.PRICE_IMPACT_TOO_HIGH });
}
return undefined; return undefined;
}, [ }, [
error, error,
@@ -334,6 +350,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
fromAmount, fromAmount,
sourceToken, sourceToken,
stringGetter, stringGetter,
summary,
]); ]);
const isDisabled = const isDisabled =
@@ -1,21 +1,16 @@
import { type Dispatch, type SetStateAction, useState, type ReactNode } from 'react'; import { type Dispatch, type ReactNode, type SetStateAction, useState, useMemo } from 'react';
import styled, { type AnyStyledComponent } from 'styled-components'; import styled, { type AnyStyledComponent } from 'styled-components';
import { shallowEqual, useSelector } from 'react-redux'; import { shallowEqual, useSelector } from 'react-redux';
import type { RouteData } from '@0xsquid/sdk'; import type { RouteData } from '@0xsquid/sdk';
import { formatUnits } from 'viem';
import { import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons';
ButtonAction,
ButtonShape,
ButtonSize,
ButtonState,
ButtonType,
} from '@/constants/buttons';
import { TransferInputTokenResource } from '@/constants/abacus'; import { TransferInputTokenResource } from '@/constants/abacus';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { NumberSign } from '@/constants/numbers'; import { NumberSign, TOKEN_DECIMALS } from '@/constants/numbers';
import { useStringGetter } from '@/hooks'; import { useStringGetter, useTokenConfigs } from '@/hooks';
import { useMatchingEvmNetwork } from '@/hooks/useMatchingEvmNetwork'; import { useMatchingEvmNetwork } from '@/hooks/useMatchingEvmNetwork';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
@@ -34,6 +29,7 @@ import { calculateCanAccountTrade } from '@/state/accountCalculators';
import { getSubaccountBuyingPower, getSubaccountEquity } from '@/state/accountSelectors'; import { getSubaccountBuyingPower, getSubaccountEquity } from '@/state/accountSelectors';
import { getTransferInputs } from '@/state/inputsSelectors'; import { getTransferInputs } from '@/state/inputsSelectors';
import { isTruthy } from '@/lib/isTruthy';
import { MustBigNumber } from '@/lib/numbers'; import { MustBigNumber } from '@/lib/numbers';
import { SlippageEditor } from '../SlippageEditor'; import { SlippageEditor } from '../SlippageEditor';
@@ -83,7 +79,8 @@ export const DepositButtonAndReceipt = ({
const { current: buyingPower, postOrder: newBuyingPower } = const { current: buyingPower, postOrder: newBuyingPower } =
useSelector(getSubaccountBuyingPower, shallowEqual) || {}; useSelector(getSubaccountBuyingPower, shallowEqual) || {};
const { summary, requestPayload } = useSelector(getTransferInputs, shallowEqual) || {}; const { isCctp, summary, requestPayload } = useSelector(getTransferInputs, shallowEqual) || {};
const { usdcDecimals, usdcLabel } = useTokenConfigs();
const feeSubitems: DetailsItem[] = []; const feeSubitems: DetailsItem[] = [];
@@ -98,7 +95,7 @@ export const DepositButtonAndReceipt = ({
if (typeof summary?.bridgeFee === 'number') { if (typeof summary?.bridgeFee === 'number') {
feeSubitems.push({ feeSubitems.push({
key: 'bridge-fees', key: 'bridge-fees',
label: <span>Bridge Fee</span>, label: <span>{stringGetter({ key: STRING_KEYS.BRIDGE_FEE })}</span>,
value: <Output type={OutputType.Fiat} value={summary?.bridgeFee} />, value: <Output type={OutputType.Fiat} value={summary?.bridgeFee} />,
}); });
} }
@@ -111,12 +108,68 @@ export const DepositButtonAndReceipt = ({
const totalFees = (summary?.bridgeFee || 0) + (summary?.gasFee || 0); const totalFees = (summary?.bridgeFee || 0) + (summary?.gasFee || 0);
const { toAmount, toAmountMin } = useMemo(() => {
if (isCctp) {
return {
toAmount: summary?.toAmount,
toAmountMin: summary?.toAmountMin,
};
} else {
return {
toAmount: summary?.toAmount && formatUnits(BigInt(summary.toAmount), usdcDecimals),
toAmountMin: summary?.toAmountMin && formatUnits(BigInt(summary.toAmountMin), usdcDecimals),
};
}
}, [isCctp, summary]);
const submitButtonReceipt = [ const submitButtonReceipt = [
{
key: 'expected-deposit-amount',
label: (
<span>
{stringGetter({ key: STRING_KEYS.EXPECTED_DEPOSIT_AMOUNT })} <Tag>{usdcLabel}</Tag>
</span>
),
value: <Output type={OutputType.Fiat} fractionDigits={TOKEN_DECIMALS} value={toAmount} />,
subitems: [
{
key: 'minimum-deposit-amount',
label: (
<span>
{stringGetter({ key: STRING_KEYS.MINIMUM_DEPOSIT_AMOUNT })} <Tag>{usdcLabel}</Tag>
</span>
),
value: (
<Output type={OutputType.Fiat} fractionDigits={TOKEN_DECIMALS} value={toAmountMin} />
),
tooltip: 'minimum-deposit-amount',
},
],
},
{
key: 'exchange-rate',
label: <span>{stringGetter({ key: STRING_KEYS.EXCHANGE_RATE })}</span>,
value:
typeof summary?.exchangeRate === 'number' ? (
<Styled.ExchangeRate>
<Output
type={OutputType.Asset}
value={1}
fractionDigits={0}
tag={sourceToken?.symbol}
/>
=
<Output type={OutputType.Asset} value={summary?.exchangeRate} tag={usdcLabel} />
</Styled.ExchangeRate>
) : (
<Output type={OutputType.Asset} />
),
},
{ {
key: 'equity', key: 'equity',
label: ( label: (
<span> <span>
{stringGetter({ key: STRING_KEYS.EQUITY })} <Tag>USDC</Tag> {stringGetter({ key: STRING_KEYS.EQUITY })} <Tag>{usdcLabel}</Tag>
</span> </span>
), ),
value: ( value: (
@@ -133,7 +186,7 @@ export const DepositButtonAndReceipt = ({
key: 'buying-power', key: 'buying-power',
label: ( label: (
<span> <span>
{stringGetter({ key: STRING_KEYS.BUYING_POWER })} <Tag>USDC</Tag> {stringGetter({ key: STRING_KEYS.BUYING_POWER })} <Tag>{usdcLabel}</Tag>
</span> </span>
), ),
value: ( value: (
@@ -146,18 +199,7 @@ export const DepositButtonAndReceipt = ({
/> />
), ),
}, },
{ !isCctp && {
key: 'exchange-rate',
label: <span>{stringGetter({ key: STRING_KEYS.EXCHANGE_RATE })}</span>,
value: typeof summary?.exchangeRate === 'number' && (
<Styled.ExchangeRate>
<Output type={OutputType.Asset} value={1} fractionDigits={0} tag={sourceToken?.symbol} />
=
<Output type={OutputType.Asset} value={summary?.exchangeRate} tag="USDC" />
</Styled.ExchangeRate>
),
},
{
key: 'total-fees', key: 'total-fees',
label: <span>{stringGetter({ key: STRING_KEYS.TOTAL_FEES })}</span>, label: <span>{stringGetter({ key: STRING_KEYS.TOTAL_FEES })}</span>,
value: <Output type={OutputType.Fiat} value={totalFees} />, value: <Output type={OutputType.Fiat} value={totalFees} />,
@@ -165,7 +207,7 @@ export const DepositButtonAndReceipt = ({
}, },
{ {
key: 'slippage', key: 'slippage',
label: <span>{stringGetter({ key: STRING_KEYS.SLIPPAGE })}</span>, label: <span>{stringGetter({ key: STRING_KEYS.MAX_SLIPPAGE })}</span>,
value: ( value: (
<SlippageEditor <SlippageEditor
disabled disabled
@@ -178,10 +220,12 @@ export const DepositButtonAndReceipt = ({
{ {
key: 'estimatedRouteDuration', key: 'estimatedRouteDuration',
label: <span>{stringGetter({ key: STRING_KEYS.ESTIMATED_TIME })}</span>, label: <span>{stringGetter({ key: STRING_KEYS.ESTIMATED_TIME })}</span>,
value: typeof summary?.estimatedRouteDuration === 'number' && ( value: (
<Output <Output
type={OutputType.Text} type={OutputType.Text}
value={stringGetter({ value={
typeof summary?.estimatedRouteDuration === 'number'
? stringGetter({
key: STRING_KEYS.X_MINUTES_LOWERCASED, key: STRING_KEYS.X_MINUTES_LOWERCASED,
params: { params: {
X: X:
@@ -189,11 +233,13 @@ export const DepositButtonAndReceipt = ({
? '< 1' ? '< 1'
: Math.round(summary?.estimatedRouteDuration / 60), : Math.round(summary?.estimatedRouteDuration / 60),
}, },
})} })
: null
}
/> />
), ),
}, },
]; ].filter(isTruthy);
const isFormValid = !isDisabled && !isEditingSlippage; const isFormValid = !isDisabled && !isEditingSlippage;
@@ -57,7 +57,6 @@ export const TokenSelectMenu = ({ selectedToken, onSelectToken }: ElementProps)
<Tag>{type === TransferType.deposit ? 'USDC' : selectedToken?.symbol}</Tag> <Tag>{type === TransferType.deposit ? 'USDC' : selectedToken?.symbol}</Tag>
</> </>
), ),
tooltip: 'swap',
}, },
]} ]}
> >
@@ -9,9 +9,9 @@ import { TransferInputField, TransferInputTokenResource, TransferType } from '@/
import { AlertType } from '@/constants/alerts'; import { AlertType } from '@/constants/alerts';
import { ButtonSize } from '@/constants/buttons'; import { ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { ENVIRONMENT_CONFIG_MAP, isMainnet } from '@/constants/networks';
import { NotificationStatus } from '@/constants/notifications'; import { TransferNotificationTypes } from '@/constants/notifications';
import { NumberSign } from '@/constants/numbers'; import { MAX_CCTP_TRANSFER_AMOUNT, MAX_PRICE_IMPACT, NumberSign } from '@/constants/numbers';
import { import {
useAccounts, useAccounts,
@@ -45,6 +45,7 @@ import { getTransferInputs } from '@/state/inputsSelectors';
import abacusStateManager from '@/lib/abacus'; import abacusStateManager from '@/lib/abacus';
import { MustBigNumber } from '@/lib/numbers'; import { MustBigNumber } from '@/lib/numbers';
import { getNobleChainId } from '@/lib/squid';
import { TokenSelectMenu } from './TokenSelectMenu'; import { TokenSelectMenu } from './TokenSelectMenu';
import { WithdrawButtonAndReceipt } from './WithdrawForm/WithdrawButtonAndReceipt'; import { WithdrawButtonAndReceipt } from './WithdrawForm/WithdrawButtonAndReceipt';
@@ -66,7 +67,8 @@ export const WithdrawForm = () => {
resources, resources,
errors: routeErrors, errors: routeErrors,
errorMessage: routeErrorMessage, errorMessage: routeErrorMessage,
isCctp isCctp,
summary,
} = useSelector(getTransferInputs, shallowEqual) || {}; } = useSelector(getTransferInputs, shallowEqual) || {};
// User input // User input
@@ -74,7 +76,6 @@ export const WithdrawForm = () => {
const [slippage, setSlippage] = useState(isCctp ? 0 : 0.01); // 0.1% slippage const [slippage, setSlippage] = useState(isCctp ? 0 : 0.01); // 0.1% slippage
const debouncedAmount = useDebounce<string>(withdrawAmount, 500); const debouncedAmount = useDebounce<string>(withdrawAmount, 500);
const isValidAddress = toAddress && isAddress(toAddress); const isValidAddress = toAddress && isAddress(toAddress);
const toToken = useMemo( const toToken = useMemo(
@@ -167,16 +168,22 @@ export const WithdrawForm = () => {
}) })
); );
} else { } else {
const txHash = await sendSquidWithdraw(debouncedAmountBN.toNumber(), requestPayload.data); const txHash = await sendSquidWithdraw(
if (txHash?.hash) { debouncedAmountBN.toNumber(),
const hash = `0x${Buffer.from(txHash.hash).toString('hex')}`; requestPayload.data,
isCctp
);
if (txHash) {
addTransferNotification({ addTransferNotification({
txHash: hash, txHash: txHash,
fromChainId: ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId, type: TransferNotificationTypes.Withdrawal,
fromChainId: !isCctp
? ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId
: getNobleChainId(),
toChainId: chainIdStr || undefined, toChainId: chainIdStr || undefined,
toAmount: debouncedAmountBN.toNumber(), toAmount: debouncedAmountBN.toNumber(),
triggeredAt: Date.now(), triggeredAt: Date.now(),
notificationStatus: NotificationStatus.Triggered, isCctp,
}); });
abacusStateManager.clearTransferInputValues(); abacusStateManager.clearTransferInputValues();
setWithdrawAmount(''); setWithdrawAmount('');
@@ -282,10 +289,7 @@ export const WithdrawForm = () => {
const errorMessage = useMemo(() => { const errorMessage = useMemo(() => {
if (error) { if (error) {
return stringGetter({ return error;
key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE,
params: { ERROR_MESSAGE: error },
});
} }
if (routeErrors) { if (routeErrors) {
@@ -316,6 +320,21 @@ export const WithdrawForm = () => {
return stringGetter({ key: STRING_KEYS.WITHDRAW_MORE_THAN_FREE }); return stringGetter({ key: STRING_KEYS.WITHDRAW_MORE_THAN_FREE });
} }
if (isCctp) {
if (MustBigNumber(debouncedAmountBN).gte(MAX_CCTP_TRANSFER_AMOUNT)) {
return stringGetter({
key: STRING_KEYS.MAX_CCTP_TRANSFER_LIMIT_EXCEEDED,
params: {
MAX_CCTP_TRANSFER_AMOUNT: MAX_CCTP_TRANSFER_AMOUNT,
},
});
}
}
if (isMainnet && MustBigNumber(summary?.aggregatePriceImpact).gte(MAX_PRICE_IMPACT)) {
return stringGetter({ key: STRING_KEYS.PRICE_IMPACT_TOO_HIGH });
}
return undefined; return undefined;
}, [ }, [
error, error,
@@ -328,6 +347,7 @@ export const WithdrawForm = () => {
toAddress, toAddress,
sanctionedAddresses, sanctionedAddresses,
stringGetter, stringGetter,
summary,
]); ]);
const isDisabled = const isDisabled =
@@ -1,4 +1,4 @@
import { useState } from 'react'; import { useMemo, useState } from 'react';
import { shallowEqual, useSelector } from 'react-redux'; import { shallowEqual, useSelector } from 'react-redux';
import styled, { type AnyStyledComponent } from 'styled-components'; import styled, { type AnyStyledComponent } from 'styled-components';
import { formatUnits } from 'viem'; import { formatUnits } from 'viem';
@@ -6,14 +6,11 @@ import { formatUnits } from 'viem';
import { TransferInputTokenResource } from '@/constants/abacus'; import { TransferInputTokenResource } from '@/constants/abacus';
import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons'; import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { NumberSign } from '@/constants/numbers'; import { NumberSign, TOKEN_DECIMALS } from '@/constants/numbers';
import { formatSeconds } from '@/lib/timeUtils';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
import { useStringGetter } from '@/hooks'; import { useStringGetter, useTokenConfigs } from '@/hooks';
import { useAccountBalance } from '@/hooks/useAccountBalance';
import { Button } from '@/components/Button'; import { Button } from '@/components/Button';
@@ -58,13 +55,9 @@ export const WithdrawButtonAndReceipt = ({
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { leverage } = useSelector(getSubaccount, shallowEqual) || {}; const { leverage } = useSelector(getSubaccount, shallowEqual) || {};
const { summary, requestPayload } = useSelector(getTransferInputs, shallowEqual) || {}; const { isCctp, summary, requestPayload } = useSelector(getTransferInputs, shallowEqual) || {};
const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual); const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual);
const { usdcLabel } = useTokenConfigs();
const toAmount =
summary?.toAmount &&
withdrawToken?.decimals &&
formatUnits(BigInt(summary.toAmount), withdrawToken?.decimals);
const feeSubitems: DetailsItem[] = []; const feeSubitems: DetailsItem[] = [];
@@ -92,6 +85,26 @@ export const WithdrawButtonAndReceipt = ({
const totalFees = (summary?.bridgeFee || 0) + (summary?.gasFee || 0); const totalFees = (summary?.bridgeFee || 0) + (summary?.gasFee || 0);
const { toAmount, toAmountMin } = useMemo(() => {
if (isCctp) {
return {
toAmount: summary?.toAmount,
toAmountMin: summary?.toAmountMin,
};
} else {
return {
toAmount:
summary?.toAmount &&
withdrawToken?.decimals &&
formatUnits(BigInt(summary.toAmount), withdrawToken.decimals),
toAmountMin:
summary?.toAmountMin &&
withdrawToken?.decimals &&
formatUnits(BigInt(summary.toAmountMin), withdrawToken.decimals),
};
}
}, [isCctp, summary, withdrawToken]);
const submitButtonReceipt = [ const submitButtonReceipt = [
{ {
key: 'total-fees', key: 'total-fees',
@@ -99,43 +112,12 @@ export const WithdrawButtonAndReceipt = ({
value: <Output type={OutputType.Fiat} value={totalFees} />, value: <Output type={OutputType.Fiat} value={totalFees} />,
subitems: feeSubitems, subitems: feeSubitems,
}, },
{
key: 'wallet',
label: (
<span>
{stringGetter({ key: STRING_KEYS.AMOUNT_RECEIVED })}{' '}
{withdrawToken && <Tag>{withdrawToken?.symbol}</Tag>}
</span>
),
value: (
<Styled.DiffOutput
type={OutputType.Asset}
value={'0'}
newValue={toAmount}
sign={NumberSign.Positive}
withDiff={Boolean(toAmount)}
/>
),
},
{
key: 'leverage',
label: <span>{stringGetter({ key: STRING_KEYS.ACCOUNT_LEVERAGE })}</span>,
value: (
<Styled.DiffOutput
type={OutputType.Multiple}
value={leverage?.current}
newValue={leverage?.postOrder}
sign={NumberSign.Negative}
withDiff={Boolean(leverage?.current && leverage.current !== leverage?.postOrder)}
/>
),
},
{ {
key: 'exchange-rate', key: 'exchange-rate',
label: <span>{stringGetter({ key: STRING_KEYS.EXCHANGE_RATE })}</span>, label: <span>{stringGetter({ key: STRING_KEYS.EXCHANGE_RATE })}</span>,
value: withdrawToken && typeof summary?.exchangeRate === 'number' && ( value: withdrawToken && typeof summary?.exchangeRate === 'number' && (
<Styled.ExchangeRate> <Styled.ExchangeRate>
<Output type={OutputType.Asset} value={1} fractionDigits={0} tag="USDC" /> <Output type={OutputType.Asset} value={1} fractionDigits={0} tag={usdcLabel} />
= =
<Output <Output
type={OutputType.Asset} type={OutputType.Asset}
@@ -146,19 +128,7 @@ export const WithdrawButtonAndReceipt = ({
), ),
}, },
{ {
key: 'slippage', key: 'estimated-route-duration',
label: <span>{stringGetter({ key: STRING_KEYS.SLIPPAGE })}</span>,
value: (
<SlippageEditor
disabled
slippage={slippage}
setIsEditing={setIsEditingSlipapge}
setSlippage={setSlippage}
/>
),
},
{
key: 'estimatedRouteDuration',
label: <span>{stringGetter({ key: STRING_KEYS.ESTIMATED_TIME })}</span>, label: <span>{stringGetter({ key: STRING_KEYS.ESTIMATED_TIME })}</span>,
value: typeof summary?.estimatedRouteDuration === 'number' && ( value: typeof summary?.estimatedRouteDuration === 'number' && (
<Output <Output
@@ -175,6 +145,56 @@ export const WithdrawButtonAndReceipt = ({
/> />
), ),
}, },
{
key: 'expected-amount-received',
label: (
<span>
{stringGetter({ key: STRING_KEYS.EXPECTED_AMOUNT_RECEIVED })}{' '}
{withdrawToken && <Tag>{withdrawToken?.symbol}</Tag>}
</span>
),
value: <Output type={OutputType.Asset} value={toAmount} fractionDigits={TOKEN_DECIMALS} />,
subitems: [
{
key: 'minimum-amount-received',
label: (
<span>
{stringGetter({ key: STRING_KEYS.MINIMUM_AMOUNT_RECEIVED })}{' '}
{withdrawToken && <Tag>{withdrawToken?.symbol}</Tag>}
</span>
),
value: (
<Output type={OutputType.Asset} value={toAmountMin} fractionDigits={TOKEN_DECIMALS} />
),
tooltip: 'minimum-amount-received',
},
],
},
{
key: 'slippage',
label: <span>{stringGetter({ key: STRING_KEYS.MAX_SLIPPAGE })}</span>,
value: (
<SlippageEditor
disabled
slippage={slippage}
setIsEditing={setIsEditingSlipapge}
setSlippage={setSlippage}
/>
),
},
{
key: 'leverage',
label: <span>{stringGetter({ key: STRING_KEYS.ACCOUNT_LEVERAGE })}</span>,
value: (
<Styled.DiffOutput
type={OutputType.Multiple}
value={leverage?.current}
newValue={leverage?.postOrder}
sign={NumberSign.Negative}
withDiff={Boolean(leverage?.current && leverage.current !== leverage?.postOrder)}
/>
),
},
]; ];
const isFormValid = !isDisabled && !isEditingSlippage; const isFormValid = !isDisabled && !isEditingSlippage;
+8 -7
View File
@@ -1,10 +1,10 @@
import { type Dispatch, type SetStateAction, useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import styled, { AnyStyledComponent, css } from 'styled-components'; import styled, { AnyStyledComponent, css } from 'styled-components';
import { Root, Thumb, Track } from '@radix-ui/react-slider'; import { Root, Thumb, Track } from '@radix-ui/react-slider';
import _ from 'lodash'; import _ from 'lodash';
import { OrderSide } from '@dydxprotocol/v4-client-js'; import { OrderSide } from '@dydxprotocol/v4-client-js';
import { type Nullable, TradeInputField } from '@/constants/abacus'; import { TradeInputField } from '@/constants/abacus';
import { PositionSide } from '@/constants/trade'; import { PositionSide } from '@/constants/trade';
import { MustBigNumber, type BigNumberish } from '@/lib/numbers'; import { MustBigNumber, type BigNumberish } from '@/lib/numbers';
@@ -12,11 +12,11 @@ import abacusStateManager from '@/lib/abacus';
type ElementProps = { type ElementProps = {
leverage?: BigNumberish | null; leverage?: BigNumberish | null;
leverageInputValue: Nullable<number>; leverageInputValue: string;
maxLeverage: BigNumberish | null; maxLeverage: BigNumberish | null;
orderSide: OrderSide; orderSide: OrderSide;
positionSide: PositionSide; positionSide: PositionSide;
setLeverageInputValue: Dispatch<SetStateAction<Nullable<number>>>; setLeverageInputValue: (value: string) => void;
}; };
type StyleProps = { className?: string }; type StyleProps = { className?: string };
@@ -33,6 +33,7 @@ export const LeverageSlider = ({
const leverageBN = MustBigNumber(leverage); const leverageBN = MustBigNumber(leverage);
const maxLeverageBN = MustBigNumber(maxLeverage); const maxLeverageBN = MustBigNumber(maxLeverage);
const leverageInputBN = MustBigNumber(leverageInputValue || leverage); const leverageInputBN = MustBigNumber(leverageInputValue || leverage);
const leverageInputNumber = isNaN(leverageInputBN.toNumber()) ? 0 : leverageInputBN.toNumber();
const sliderConfig = useMemo( const sliderConfig = useMemo(
() => ({ () => ({
@@ -79,12 +80,12 @@ export const LeverageSlider = ({
); );
const onSliderDrag = ([newLeverage]: number[]) => { const onSliderDrag = ([newLeverage]: number[]) => {
setLeverageInputValue(newLeverage); setLeverageInputValue(`${newLeverage}`);
debouncedSetAbacusLeverage(newLeverage); debouncedSetAbacusLeverage(newLeverage);
}; };
const onValueCommit = ([newLeverage]: number[]) => { const onValueCommit = ([newLeverage]: number[]) => {
setLeverageInputValue(newLeverage); setLeverageInputValue(`${newLeverage}`);
// Ensure Abacus is updated with the latest, committed value // Ensure Abacus is updated with the latest, committed value
debouncedSetAbacusLeverage.cancel(); debouncedSetAbacusLeverage.cancel();
@@ -102,7 +103,7 @@ export const LeverageSlider = ({
min={min} min={min}
max={max} max={max}
step={0.1} step={0.1}
value={[Math.min(Math.max(leverageInputBN.toNumber(), min), max)]} value={[Math.min(Math.max(leverageInputNumber, min), max)]}
onValueChange={onSliderDrag} onValueChange={onSliderDrag}
onValueCommit={onValueCommit} onValueCommit={onValueCommit}
> >
@@ -1,9 +1,8 @@
import type { Dispatch, SetStateAction } from 'react';
import { shallowEqual, useSelector } from 'react-redux'; import { shallowEqual, useSelector } from 'react-redux';
import styled, { AnyStyledComponent } from 'styled-components'; import styled, { AnyStyledComponent } from 'styled-components';
import { OrderSide } from '@dydxprotocol/v4-client-js'; import { OrderSide } from '@dydxprotocol/v4-client-js';
import { TradeInputField, Nullable } from '@/constants/abacus'; import { TradeInputField } from '@/constants/abacus';
import { ButtonShape } from '@/constants/buttons'; import { ButtonShape } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { LEVERAGE_DECIMALS } from '@/constants/numbers'; import { LEVERAGE_DECIMALS } from '@/constants/numbers';
@@ -62,21 +61,7 @@ export const MarketLeverageInput = ({
const leveragePosition = postOrderLeverage ? newPositionSide : currentPositionSide; const leveragePosition = postOrderLeverage ? newPositionSide : currentPositionSide;
const onLeverageInput = ({ value, floatValue }: { value: string; floatValue?: number }) => { const getSignedLeverage = (newLeverage: string | number) => {
const newLeverage = MustBigNumber(floatValue).toFixed();
if (value === '' || newLeverage === 'NaN' || !floatValue) {
setLeverageInputValue('');
abacusStateManager.setTradeValue({
value: null,
field: TradeInputField.leverage,
});
} else {
updateLeverage(floatValue);
}
};
const updateLeverage = (newLeverage: string | number) => {
const newLeverageBN = MustBigNumber(newLeverage); const newLeverageBN = MustBigNumber(newLeverage);
const newLeverageSignedBN = const newLeverageSignedBN =
leveragePosition === PositionSide.Short || leveragePosition === PositionSide.Short ||
@@ -84,10 +69,33 @@ export const MarketLeverageInput = ({
? newLeverageBN.abs().negated() ? newLeverageBN.abs().negated()
: newLeverageBN.abs(); : newLeverageBN.abs();
setLeverageInputValue(newLeverageSignedBN.toString()); return newLeverageSignedBN.toFixed(LEVERAGE_DECIMALS);
};
const onLeverageInput = ({
floatValue,
formattedValue,
}: {
floatValue?: number;
formattedValue: string;
}) => {
setLeverageInputValue(formattedValue);
const newLeverage = MustBigNumber(floatValue).toFixed();
abacusStateManager.setTradeValue({ abacusStateManager.setTradeValue({
value: newLeverageSignedBN.toFixed(LEVERAGE_DECIMALS), value:
formattedValue === '' || newLeverage === 'NaN' ? null : getSignedLeverage(formattedValue),
field: TradeInputField.leverage,
});
};
const updateLeverage = (newLeverage: string | number) => {
const newLeverageSigned = getSignedLeverage(newLeverage);
setLeverageInputValue(newLeverageSigned);
abacusStateManager.setTradeValue({
value: newLeverageSigned,
field: TradeInputField.leverage, field: TradeInputField.leverage,
}); });
}; };
@@ -130,7 +138,7 @@ export const MarketLeverageInput = ({
> >
<Styled.LeverageSlider <Styled.LeverageSlider
leverage={currentLeverage} leverage={currentLeverage}
leverageInputValue={leverageInputValue} leverageInputValue={getSignedLeverage(leverageInputValue)}
maxLeverage={maxLeverage} maxLeverage={maxLeverage}
orderSide={orderSide} orderSide={orderSide}
positionSide={currentPositionSide} positionSide={currentPositionSide}
@@ -77,7 +77,7 @@ export const TradeSizeInputs = () => {
floatValue?: number; floatValue?: number;
formattedValue: string; formattedValue: string;
}) => { }) => {
dispatch(setTradeFormInputs({ amountInput: MustBigNumber(floatValue).toString() })); dispatch(setTradeFormInputs({ amountInput: formattedValue }));
const newAmount = MustBigNumber(floatValue).toFixed(decimals); const newAmount = MustBigNumber(floatValue).toFixed(decimals);
abacusStateManager.setTradeValue({ abacusStateManager.setTradeValue({
@@ -93,7 +93,7 @@ export const TradeSizeInputs = () => {
floatValue?: number; floatValue?: number;
formattedValue: string; formattedValue: string;
}) => { }) => {
dispatch(setTradeFormInputs({ usdAmountInput: MustBigNumber(floatValue).toString() })); dispatch(setTradeFormInputs({ usdAmountInput: formattedValue }));
const newUsdcAmount = MustBigNumber(floatValue).toFixed(tickSizeDecimals || USD_DECIMALS); const newUsdcAmount = MustBigNumber(floatValue).toFixed(tickSizeDecimals || USD_DECIMALS);
abacusStateManager.setTradeValue({ abacusStateManager.setTradeValue({
@@ -12,10 +12,11 @@ import { LoadingSpinner } from '@/components/Loading/LoadingSpinner';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { TransferNotificationTypes } from '@/constants/notifications';
type ElementProps = { type ElementProps = {
status?: StatusResponse; status?: StatusResponse;
type: 'withdrawal' | 'deposit'; type: TransferNotificationTypes;
}; };
type StyleProps = { type StyleProps = {
@@ -46,11 +47,13 @@ export const TransferStatusSteps = ({ className, status, type }: ElementProps &
{ {
label: stringGetter({ label: stringGetter({
key: key:
type === 'deposit' ? STRING_KEYS.INITIATED_DEPOSIT : STRING_KEYS.INITIATED_WITHDRAWAL, type === TransferNotificationTypes.Deposit
? STRING_KEYS.INITIATED_DEPOSIT
: STRING_KEYS.INITIATED_WITHDRAWAL,
}), }),
step: TransferStatusStep.FromChain, step: TransferStatusStep.FromChain,
link: link:
type === 'deposit' type === TransferNotificationTypes.Deposit
? status?.fromChain?.transactionUrl ? status?.fromChain?.transactionUrl
: routeStatus?.[0]?.chainId === dydxChainId && routeStatus[0].txHash : routeStatus?.[0]?.chainId === dydxChainId && routeStatus[0].txHash
? `${mintscanTxUrl?.replace('{tx_hash}', routeStatus[0].txHash.replace('0x', ''))}` ? `${mintscanTxUrl?.replace('{tx_hash}', routeStatus[0].txHash.replace('0x', ''))}`
@@ -63,14 +66,20 @@ export const TransferStatusSteps = ({ className, status, type }: ElementProps &
}, },
{ {
label: stringGetter({ label: stringGetter({
key: type === 'deposit' ? STRING_KEYS.DEPOSIT_TO_CHAIN : STRING_KEYS.WITHDRAW_TO_CHAIN, key:
type === TransferNotificationTypes.Deposit
? STRING_KEYS.DEPOSIT_TO_CHAIN
: STRING_KEYS.WITHDRAW_TO_CHAIN,
params: { params: {
CHAIN: type === 'deposit' ? 'dYdX' : status?.toChain?.chainData?.chainName, CHAIN:
type === TransferNotificationTypes.Deposit
? 'dYdX'
: status?.toChain?.chainData?.chainName,
}, },
}), }),
step: TransferStatusStep.ToChain, step: TransferStatusStep.ToChain,
link: link:
type === 'withdrawal' type === TransferNotificationTypes.Withdrawal
? status?.toChain?.transactionUrl ? status?.toChain?.transactionUrl
: currentStatus?.chainId === dydxChainId && currentStatus?.txHash : currentStatus?.chainId === dydxChainId && currentStatus?.txHash
? `${mintscanTxUrl?.replace('{tx_hash}', currentStatus.txHash.replace('0x', ''))}` ? `${mintscanTxUrl?.replace('{tx_hash}', currentStatus.txHash.replace('0x', ''))}`
@@ -5,7 +5,7 @@ import { useInterval, useStringGetter } from '@/hooks';
import { AlertType } from '@/constants/alerts'; import { AlertType } from '@/constants/alerts';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { TransferNotifcation } from '@/constants/notifications'; import { TransferNotifcation, TransferNotificationTypes } from '@/constants/notifications';
import { formatSeconds } from '@/lib/timeUtils'; import { formatSeconds } from '@/lib/timeUtils';
@@ -22,7 +22,7 @@ import { layoutMixins } from '@/styles/layoutMixins';
import { TransferStatusSteps } from './TransferStatusSteps'; import { TransferStatusSteps } from './TransferStatusSteps';
type ElementProps = { type ElementProps = {
type: 'withdrawal' | 'deposit'; type: TransferNotificationTypes;
transfer: TransferNotifcation; transfer: TransferNotifcation;
triggeredAt?: number; triggeredAt?: number;
}; };
@@ -55,7 +55,7 @@ export const TransferStatusNotification = ({
useInterval({ callback: updateSecondsLeft }); useInterval({ callback: updateSecondsLeft });
const inProgressStatusString = const inProgressStatusString =
type === 'deposit' type === TransferNotificationTypes.Deposit
? secondsLeft > 0 ? secondsLeft > 0
? STRING_KEYS.DEPOSIT_STATUS ? STRING_KEYS.DEPOSIT_STATUS
: STRING_KEYS.DEPOSIT_STATUS_SHORTLY : STRING_KEYS.DEPOSIT_STATUS_SHORTLY
@@ -64,7 +64,7 @@ export const TransferStatusNotification = ({
: STRING_KEYS.WITHDRAW_STATUS_SHORTLY; : STRING_KEYS.WITHDRAW_STATUS_SHORTLY;
const statusString = const statusString =
type === 'deposit' type === TransferNotificationTypes.Deposit
? status?.squidTransactionStatus === 'success' ? status?.squidTransactionStatus === 'success'
? STRING_KEYS.DEPOSIT_COMPLETE ? STRING_KEYS.DEPOSIT_COMPLETE
: inProgressStatusString : inProgressStatusString
+3 -2
View File
@@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { MarketFilters, type MarketData } from '@/constants/markets'; import { MarketFilters, type MarketData } from '@/constants/markets';
import { LARGE_TOKEN_DECIMALS } from '@/constants/numbers'; import { FUNDING_DECIMALS, LARGE_TOKEN_DECIMALS } from '@/constants/numbers';
import { AppRoute } from '@/constants/routes'; import { AppRoute } from '@/constants/routes';
import { useBreakpoints, useStringGetter } from '@/hooks'; import { useBreakpoints, useStringGetter } from '@/hooks';
@@ -128,7 +128,8 @@ export const MarketsTable = ({ className }: { className?: string }) => {
label: stringGetter({ key: STRING_KEYS.FUNDING_RATE_1H_SHORT }), label: stringGetter({ key: STRING_KEYS.FUNDING_RATE_1H_SHORT }),
renderCell: (row) => ( renderCell: (row) => (
<Styled.Output <Styled.Output
type={OutputType.SmallPercent} type={OutputType.Percent}
fractionDigits={FUNDING_DECIMALS}
value={row.nextFundingRate} value={row.nextFundingRate}
isPositive={MustBigNumber(row.nextFundingRate).gt(0)} isPositive={MustBigNumber(row.nextFundingRate).gt(0)}
isNegative={MustBigNumber(row.nextFundingRate).isNegative()} isNegative={MustBigNumber(row.nextFundingRate).isNegative()}
+2 -11
View File
@@ -230,14 +230,7 @@ const getOrdersTableColumnDef = ({
label: `${stringGetter({ key: STRING_KEYS.STATUS })} / ${stringGetter({ label: `${stringGetter({ key: STRING_KEYS.STATUS })} / ${stringGetter({
key: STRING_KEYS.FILL, key: STRING_KEYS.FILL,
})}`, })}`,
renderCell: ({ renderCell: ({ asset, createdAtMilliseconds, size, status, totalFilled, resources }) => {
asset,
createdAtMilliseconds,
size,
status,
totalFilled,
resources,
}) => {
const { statusIconColor, statusStringKey } = getStatusIconInfo({ const { statusIconColor, statusStringKey } = getStatusIconInfo({
status, status,
totalFilled, totalFilled,
@@ -345,9 +338,7 @@ export const OrdersTable = ({
if (hasUnseenOrderUpdates) dispatch(viewedOrders()); if (hasUnseenOrderUpdates) dispatch(viewedOrders());
}, [hasUnseenOrderUpdates]); }, [hasUnseenOrderUpdates]);
const symbol = currentMarket const symbol = currentMarket ? allAssets[allPerpetualMarkets[currentMarket]?.assetId]?.id : null;
? allAssets[allPerpetualMarkets[currentMarket]?.assetId]?.symbol
: null;
const ordersData = orders.map((order: SubaccountOrder) => const ordersData = orders.map((order: SubaccountOrder) =>
getHydratedTradingData({ getHydratedTradingData({
+1
View File
@@ -366,6 +366,7 @@ Styled.InlineRow = styled.div`
Styled.AssetIcon = styled(AssetIcon)` Styled.AssetIcon = styled(AssetIcon)`
${layoutMixins.inlineRow} ${layoutMixins.inlineRow}
min-width: unset;
font-size: 2.25rem; font-size: 2.25rem;
`; `;