Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9058ccf6c1 | ||
|
|
2ab756ad71 |
@@ -39,12 +39,11 @@
|
||||
"@cosmjs/proto-signing": "^0.31.0",
|
||||
"@cosmjs/stargate": "^0.31.0",
|
||||
"@cosmjs/tendermint-rpc": "^0.31.0",
|
||||
"@dydxprotocol/v4-abacus": "^1.1.32",
|
||||
"@dydxprotocol/v4-client-js": "^1.0.11",
|
||||
"@dydxprotocol/v4-localization": "^1.1.6",
|
||||
"@dydxprotocol/v4-abacus": "^1.0.30",
|
||||
"@dydxprotocol/v4-client-js": "^1.0.0",
|
||||
"@dydxprotocol/v4-localization": "^1.0.8",
|
||||
"@ethersproject/providers": "^5.7.2",
|
||||
"@js-joda/core": "^5.5.3",
|
||||
"@radix-ui/react-accordion": "^1.1.2",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-collapsible": "^1.0.3",
|
||||
"@radix-ui/react-dialog": "^1.0.4",
|
||||
@@ -72,7 +71,6 @@
|
||||
"@reduxjs/toolkit": "^1.9.5",
|
||||
"@scure/bip32": "^1.3.0",
|
||||
"@scure/bip39": "^1.2.0",
|
||||
"@tanstack/react-query": "^5.17.9",
|
||||
"@types/lodash": "^4.14.195",
|
||||
"@types/styled-components": "^5.1.26",
|
||||
"@visx/axis": "^3.1.0",
|
||||
@@ -105,15 +103,15 @@
|
||||
"react-aria": "^3.25.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-number-format": "^5.2.2",
|
||||
"react-query": "^3.39.3",
|
||||
"react-redux": "^8.1.1",
|
||||
"react-router-dom": "^6.14.0",
|
||||
"react-stately": "^3.23.0",
|
||||
"reselect": "^4.1.8",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"styled-components": "^5.3.11",
|
||||
"use-latest": "^1.2.1",
|
||||
"viem": "^2.0.0",
|
||||
"wagmi": "^2.0.3"
|
||||
"viem": "^1.12.2",
|
||||
"wagmi": "^1.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.5",
|
||||
|
||||
@@ -1,12 +1,61 @@
|
||||
[
|
||||
{
|
||||
"chainId": "42161",
|
||||
"tokenAddress": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
||||
"name": "Arbitrum"
|
||||
},
|
||||
{
|
||||
"chainId": "43114",
|
||||
"tokenAddress": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
||||
"name": "Avalanche"
|
||||
},
|
||||
{
|
||||
"chainId": "8453",
|
||||
"tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
||||
"name": "Base"
|
||||
},
|
||||
{
|
||||
"chainId": "1",
|
||||
"tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"name": "Ethereum"
|
||||
},
|
||||
{
|
||||
"chainId": "10",
|
||||
"tokenAddress": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
||||
"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",
|
||||
"tokenAddress": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F",
|
||||
"name": "Ethereum Goerli"
|
||||
},
|
||||
{
|
||||
"chainId": "grand-1",
|
||||
"tokenAddress": "uusdc",
|
||||
"name": "Noble Testnet"
|
||||
},
|
||||
{
|
||||
"chainId": "420",
|
||||
"tokenAddress": "0xe05606174bac4a6364b31bd0eca4bf4dd368f8c6",
|
||||
"name": "OP Goerli"
|
||||
},
|
||||
{
|
||||
"chainId": "103",
|
||||
"name": "Solana Devnet"
|
||||
}
|
||||
]
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -39,10 +39,10 @@
|
||||
"environments": {
|
||||
"dydxprotocol-dev": {
|
||||
"name": "v4 Dev",
|
||||
"ethereumChainId": "11155111",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydxprotocol-testnet",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "/dydx-chain.png",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"squidIntegratorId": "dYdX-api",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
@@ -71,7 +71,6 @@
|
||||
"https://validator.v4dev.dydx.exchange"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.0xsquid.com",
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
|
||||
"faucet": "https://faucet.v4dev.dydx.exchange"
|
||||
},
|
||||
"links": {
|
||||
@@ -89,7 +88,7 @@
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
@@ -113,10 +112,10 @@
|
||||
},
|
||||
"dydxprotocol-dev-2": {
|
||||
"name": "v4 Dev 2",
|
||||
"ethereumChainId": "11155111",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydxprotocol-testnet",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "/dydx-chain.png",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"squidIntegratorId": "dYdX-api",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
@@ -144,8 +143,7 @@
|
||||
"validators": [
|
||||
"http://54.92.118.111"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.0xsquid.com",
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
|
||||
"0xsquid": "https://testnet.api.0xsquid.com"
|
||||
},
|
||||
"links": {
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
@@ -162,7 +160,7 @@
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
@@ -186,10 +184,10 @@
|
||||
},
|
||||
"dydxprotocol-dev-4": {
|
||||
"name": "v4 Dev 4",
|
||||
"ethereumChainId": "11155111",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydxprotocol-testnet",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "/dydx-chain.png",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"squidIntegratorId": "dYdX-api",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
@@ -217,8 +215,7 @@
|
||||
"validators": [
|
||||
"http://validator.v4dev4.dydx.exchange"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.0xsquid.com",
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
|
||||
"0xsquid": "https://testnet.api.0xsquid.com"
|
||||
},
|
||||
"links": {
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
@@ -235,7 +232,7 @@
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
@@ -259,10 +256,10 @@
|
||||
},
|
||||
"dydxprotocol-dev-5": {
|
||||
"name": "v4 Dev 5",
|
||||
"ethereumChainId": "11155111",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydxprotocol-testnet",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "/dydx-chain.png",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"squidIntegratorId": "dYdX-api",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
@@ -290,8 +287,7 @@
|
||||
"validators": [
|
||||
"http://18.223.78.50"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.0xsquid.com",
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
|
||||
"0xsquid": "https://testnet.api.0xsquid.com"
|
||||
},
|
||||
"links": {
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
@@ -308,7 +304,7 @@
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
@@ -332,10 +328,10 @@
|
||||
},
|
||||
"dydxprotocol-staging": {
|
||||
"name": "v4 Staging",
|
||||
"ethereumChainId": "11155111",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydxprotocol-testnet",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "/dydx-chain.png",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"squidIntegratorId": "dYdX-api",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
@@ -364,8 +360,7 @@
|
||||
"validators": [
|
||||
"https://validator.v4staging.dydx.exchange"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.squidrouter.com",
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
|
||||
"0xsquid": "https://testnet.api.squidrouter.com"
|
||||
},
|
||||
"links": {
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
@@ -383,7 +378,7 @@
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
@@ -407,11 +402,10 @@
|
||||
},
|
||||
"dydxprotocol-staging-forced-update": {
|
||||
"name": "v4 Staging Forced Update",
|
||||
"ethereumChainId": "11155111",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydxprotocol-testnet",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "/dydx-chain.png",
|
||||
"squidIntegratorId": "dYdX-api",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
"chain": {
|
||||
@@ -439,8 +433,7 @@
|
||||
"validators": [
|
||||
"https://validator.v4staging.dydx.exchange"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.squidrouter.com",
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
|
||||
"0xsquid": "https://testnet.api.squidrouter.com"
|
||||
},
|
||||
"links": {
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
@@ -480,10 +473,10 @@
|
||||
},
|
||||
"dydxprotocol-staging-west": {
|
||||
"name": "v4 Staging West",
|
||||
"ethereumChainId": "11155111",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydxprotocol-testnet",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "/dydx-chain.png",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"squidIntegratorId": "dYdX-api",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
@@ -512,8 +505,7 @@
|
||||
"validators": [
|
||||
"https://validator-uswest1.v4staging.dydx.exchange"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.squidrouter.com",
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
|
||||
"0xsquid": "https://testnet.api.squidrouter.com"
|
||||
},
|
||||
"links": {
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
@@ -531,7 +523,7 @@
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
@@ -555,10 +547,10 @@
|
||||
},
|
||||
"dydxprotocol-testnet": {
|
||||
"name": "v4 Public Testnet",
|
||||
"ethereumChainId": "11155111",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydx-testnet-4",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "/dydx-chain.png",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"squidIntegratorId": "dYdX-api",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
@@ -579,8 +571,8 @@
|
||||
"endpoints": {
|
||||
"indexers": [
|
||||
{
|
||||
"api": "https://indexer.v4testnet.dydx.exchange",
|
||||
"socket": "wss://indexer.v4testnet.dydx.exchange"
|
||||
"api": "https://dydx-testnet.imperator.co",
|
||||
"socket": "wss://dydx-testnet.imperator.co"
|
||||
}
|
||||
],
|
||||
"validators": [
|
||||
@@ -591,7 +583,6 @@
|
||||
"https://dydx-rpc.liquify.com/api=8878132/dydx"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.squidrouter.com",
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"links": {
|
||||
@@ -610,7 +601,7 @@
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
@@ -634,10 +625,10 @@
|
||||
},
|
||||
"dydxprotocol-testnet-dydx": {
|
||||
"name": "v4 Public Testnet/dYdX",
|
||||
"ethereumChainId": "11155111",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydx-testnet-4",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "/dydx-chain.png",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"squidIntegratorId": "dYdX-api",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
@@ -666,7 +657,6 @@
|
||||
"https://validator.v4testnet.dydx.exchange"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.squidrouter.com",
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"links": {
|
||||
@@ -686,7 +676,7 @@
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
@@ -710,10 +700,10 @@
|
||||
},
|
||||
"dydxprotocol-testnet-nodefleet": {
|
||||
"name": "v4 Public Testnet/nodefleet",
|
||||
"ethereumChainId": "11155111",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydx-testnet-4",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "/dydx-chain.png",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"squidIntegratorId": "dYdX-api",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
@@ -734,15 +724,14 @@
|
||||
"endpoints": {
|
||||
"indexers": [
|
||||
{
|
||||
"api": "https://indexer.v4testnet.dydx.exchange",
|
||||
"socket": "wss://indexer.v4testnet.dydx.exchange"
|
||||
"api": "https://dydx-testnet.imperator.co",
|
||||
"socket": "wss://dydx-testnet.imperator.co"
|
||||
}
|
||||
],
|
||||
"validators": [
|
||||
"https://dydx-testnet.nodefleet.org"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.squidrouter.com",
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"links": {
|
||||
@@ -762,7 +751,7 @@
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
@@ -786,10 +775,10 @@
|
||||
},
|
||||
"dydxprotocol-testnet-kingnodes": {
|
||||
"name": "v4 Public Testnet/KingNodes",
|
||||
"ethereumChainId": "11155111",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydx-testnet-4",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "/dydx-chain.png",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"squidIntegratorId": "dYdX-api",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
@@ -810,15 +799,14 @@
|
||||
"endpoints": {
|
||||
"indexers": [
|
||||
{
|
||||
"api": "https://indexer.v4testnet.dydx.exchange",
|
||||
"socket": "wss://indexer.v4testnet.dydx.exchange"
|
||||
"api": "https://dydx-testnet.imperator.co",
|
||||
"socket": "wss://dydx-testnet.imperator.co"
|
||||
}
|
||||
],
|
||||
"validators": [
|
||||
"https://test-dydx.kingnodes.com"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.squidrouter.com",
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"links": {
|
||||
@@ -838,7 +826,7 @@
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
@@ -862,10 +850,10 @@
|
||||
},
|
||||
"dydxprotocol-testnet-liquify": {
|
||||
"name": "v4 Public Testnet/Liquify",
|
||||
"ethereumChainId": "11155111",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydx-testnet-4",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "/dydx-chain.png",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"squidIntegratorId": "dYdX-api",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
@@ -886,15 +874,14 @@
|
||||
"endpoints": {
|
||||
"indexers": [
|
||||
{
|
||||
"api": "https://indexer.v4testnet.dydx.exchange",
|
||||
"socket": "wss://indexer.v4testnet.dydx.exchange"
|
||||
"api": "https://dydx-testnet.imperator.co",
|
||||
"socket": "wss://dydx-testnet.imperator.co"
|
||||
}
|
||||
],
|
||||
"validators": [
|
||||
"https://dydx-rpc.liquify.com/api=8878132/dydx"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.squidrouter.com",
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"links": {
|
||||
@@ -914,7 +901,7 @@
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
@@ -938,10 +925,10 @@
|
||||
},
|
||||
"dydxprotocol-testnet-polkachu": {
|
||||
"name": "v4 Public Testnet/Polkahcu",
|
||||
"ethereumChainId": "11155111",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydx-testnet-4",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "/dydx-chain.png",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"squidIntegratorId": "dYdX-api",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
@@ -962,15 +949,14 @@
|
||||
"endpoints": {
|
||||
"indexers": [
|
||||
{
|
||||
"api": "https://indexer.v4testnet.dydx.exchange",
|
||||
"socket": "wss://indexer.v4testnet.dydx.exchange"
|
||||
"api": "https://dydx-testnet.imperator.co",
|
||||
"socket": "wss://dydx-testnet.imperator.co"
|
||||
}
|
||||
],
|
||||
"validators": [
|
||||
"https://dydx-testnet-rpc.polkachu.com/"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.squidrouter.com",
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"links": {
|
||||
@@ -1005,10 +991,10 @@
|
||||
},
|
||||
"dydxprotocol-testnet-bware": {
|
||||
"name": "v4 Public Testnet/BWare",
|
||||
"ethereumChainId": "11155111",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydx-testnet-4",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "/dydx-chain.png",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"squidIntegratorId": "dYdX-api",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
@@ -1029,15 +1015,14 @@
|
||||
"endpoints": {
|
||||
"indexers": [
|
||||
{
|
||||
"api": "https://indexer.v4testnet.dydx.exchange",
|
||||
"socket": "wss://indexer.v4testnet.dydx.exchange"
|
||||
"api": "https://dydx-testnet.imperator.co",
|
||||
"socket": "wss://dydx-testnet.imperator.co"
|
||||
}
|
||||
],
|
||||
"validators": [
|
||||
"https://dydx-testnet-full-rpc.public.blastapi.io/"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.squidrouter.com",
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"links": {
|
||||
@@ -1084,7 +1069,7 @@
|
||||
"ethereumChainId": "1",
|
||||
"dydxChainId": "[mainnet chain id]",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "/dydx-chain.png",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"squidIntegratorId": "[mainnet squid integrator id]",
|
||||
"isMainNet": true,
|
||||
"tokens": {
|
||||
@@ -1114,8 +1099,7 @@
|
||||
"[Validator endpoint 1",
|
||||
"[Validator endpoint n]"
|
||||
],
|
||||
"0xsquid": "[0xSquid endpoint for mainnet]",
|
||||
"nobleValidator": "[noble validator endpoint for mainnet]"
|
||||
"0xsquid": "[0xSquid endpoint for mainnet]"
|
||||
},
|
||||
"links": {
|
||||
"tos": "[HTTP link to TOS]",
|
||||
@@ -1126,7 +1110,7 @@
|
||||
"blogs": "[HTTP link to blogs, can be null]",
|
||||
"foundation": "[HTTP link to foundation, can be null]",
|
||||
"initialMarginFractionLearnMore": "[HTTP link to initial margin fraction learn more, can be null]",
|
||||
"reduceOnlyLearnMore": "[HTTP link to reduce-only learn more, can be null]",
|
||||
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
|
||||
"documentation": "[HTTP link to documentation, can be null]",
|
||||
"community": "[HTTP link to community, can be null]",
|
||||
"help": "[HTTP link to help page, can be null]",
|
||||
|
||||
@@ -1,360 +1,454 @@
|
||||
{
|
||||
"1INCH-USD": {
|
||||
"name": "1inch",
|
||||
"tags": ["Defi"],
|
||||
"websiteLink": "https://1inch.io/",
|
||||
"whitepaperLink": "https://github.com/1inch",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/1inch/"
|
||||
},
|
||||
"AAVE-USD": {
|
||||
"name": "Aave",
|
||||
"tags": ["Defi"],
|
||||
"websiteLink": "https://aave.com/",
|
||||
"whitepaperLink": "https://github.com/aave/protocol-v2/blob/master/aave-v2-whitepaper.pdf",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/aave/"
|
||||
},
|
||||
"ADA-USD": {
|
||||
"name": "Cardano",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://cardano.org/",
|
||||
"whitepaperLink": "https://why.cardano.org/en/introduction/motivation/",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/cardano/"
|
||||
},
|
||||
"ALGO-USD": {
|
||||
"name": "Algorand",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://algorand.com/",
|
||||
"whitepaperLink": "https://algorand.com/technology/white-papers",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/algorand/"
|
||||
},
|
||||
"APE-USD": {
|
||||
"name": "ApeCoin",
|
||||
"tags": [],
|
||||
"websiteLink": "https://apecoin.com/",
|
||||
"whitepaperLink": "https://apecoin.com/about",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/apecoin-ape/"
|
||||
},
|
||||
"APT-USD": {
|
||||
"name": "Aptos",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://aptoslabs.com/",
|
||||
"whitepaperLink": "https://aptos.dev/aptos-white-paper/",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/aptos/"
|
||||
},
|
||||
"ARB-USD": {
|
||||
"name": "Arbitrum",
|
||||
"tags": [],
|
||||
"websiteLink": "https://arbitrum.io/",
|
||||
"whitepaperLink": "https://github.com/OffchainLabs/nitro",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/arbitrum/"
|
||||
},
|
||||
"ATOM-USD": {
|
||||
"name": "Cosmos",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://cosmos.network/",
|
||||
"whitepaperLink": "https://v1.cosmos.network/resources/whitepaper",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/cosmos/"
|
||||
},
|
||||
"AVAX-USD": {
|
||||
"name": "Avalanche",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://www.avalabs.org/",
|
||||
"whitepaperLink": "https://assets.website-files.com/5d80307810123f5ffbb34d6e/6008d7bbf8b10d1eb01e7e16_Avalanche%20Platform%20Whitepaper.pdf",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/avalanche/"
|
||||
},
|
||||
"BCH-USD": {
|
||||
"name": "Bitcoin Cash",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://bitcoincash.org/",
|
||||
"whitepaperLink": "https://bitcoincash.org/",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/bitcoin-cash/"
|
||||
},
|
||||
"BLUR-USD": {
|
||||
"name": "Blur",
|
||||
"tags": [],
|
||||
"websiteLink": "https://blur.io/",
|
||||
"whitepaperLink": "https://docs.blur.foundation/",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/blur-token/"
|
||||
},
|
||||
"CELO-USD": {
|
||||
"name": "Celo",
|
||||
"tags": [],
|
||||
"websiteLink": "https://celo.org",
|
||||
"whitepaperLink": "https://docs.celo.org",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/celo/"
|
||||
},
|
||||
"BTC-USD": {
|
||||
"name": "Bitcoin",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://bitcoin.org/",
|
||||
"whitepaperLink": "https://bitcoin.org/bitcoin.pdf",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/bitcoin/"
|
||||
},
|
||||
"COMP-USD": {
|
||||
"name": "Compound",
|
||||
"tags": ["Defi"],
|
||||
"websiteLink": "https://compound.finance/",
|
||||
"whitepaperLink": "https://compound.finance/documents/Compound.Whitepaper.pdf",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/compound/"
|
||||
},
|
||||
"CRV-USD": {
|
||||
"name": "Curve",
|
||||
"tags": ["Governance"],
|
||||
"websiteLink": "https://curve.fi/",
|
||||
"whitepaperLink": "https://curve.fi/whitepaper",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/curve-dao-token/"
|
||||
},
|
||||
"DOGE-USD": {
|
||||
"name": "Dogecoin",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://dogecoin.com/",
|
||||
"whitepaperLink": "https://github.com/dogecoin/dogecoin",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/dogecoin/"
|
||||
},
|
||||
"DOT-USD": {
|
||||
"name": "Polkadot",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://polkadot.network/",
|
||||
"whitepaperLink": "https://polkadot.network/PolkaDotPaper.pdf",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/polkadot-new/"
|
||||
},
|
||||
"ENJ-USD": {
|
||||
"name": "Enjin",
|
||||
"tags": [],
|
||||
"websiteLink": "https://enjin.io/",
|
||||
"whitepaperLink": "https://cdn.enjin.io/downloads/whitepapers/enjin-coin/en.pdf/",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/enjin-coin/"
|
||||
},
|
||||
"EOS-USD": {
|
||||
"name": "EOS",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://eos.io/",
|
||||
"whitepaperLink": "https://github.com/EOSIO/Documentation/blob/master/TechnicalWhitePaper.md",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/eos/"
|
||||
},
|
||||
"ETC-USD": {
|
||||
"name": "Ethereum Classic",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://ethereumclassic.org/",
|
||||
"whitepaperLink": "https://ethereumclassic.org/why-classic",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/ethereum-classic/"
|
||||
},
|
||||
"ETH-USD": {
|
||||
"name": "Ethereum",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://ethereum.org/",
|
||||
"whitepaperLink": "https://ethereum.org/whitepaper/",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/ethereum/",
|
||||
"displayStepSize": "0.001",
|
||||
"displayTickSize": "0.1"
|
||||
},
|
||||
"FIL-USD": {
|
||||
"name": "Filecoin",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://filecoin.io/",
|
||||
"whitepaperLink": "https://filecoin.io/filecoin.pdf",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/filecoin/"
|
||||
},
|
||||
"HNT-USD": {
|
||||
"name": "Helium",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://www.helium.com",
|
||||
"whitepaperLink": "http://whitepaper.helium.com",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/helium/"
|
||||
},
|
||||
"ICP-USD": {
|
||||
"name": "Internet Computer",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://dfinity.org",
|
||||
"whitepaperLink": "https://dfinity.org/whitepaper.pdf",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/internet-computer/"
|
||||
},
|
||||
"LDO-USD": {
|
||||
"name": "Lido DAO",
|
||||
"tags": ["Defi"],
|
||||
"websiteLink": "https://lido.fi/",
|
||||
"whitepaperLink": "https://lido.fi/static/Lido:Ethereum-Liquid-Staking.pdf",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/lido-dao/"
|
||||
},
|
||||
"LINK-USD": {
|
||||
"name": "Chainlink",
|
||||
"tags": ["Defi"],
|
||||
"websiteLink": "https://chain.link/",
|
||||
"whitepaperLink": "https://link.smartcontract.com/whitepaper",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/chainlink/"
|
||||
},
|
||||
"LTC-USD": {
|
||||
"name": "Litecoin",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://litecoin.org/",
|
||||
"whitepaperLink": "https://litecoin.info/index.php/Main_Page",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/litecoin/"
|
||||
},
|
||||
"MATIC-USD": {
|
||||
"name": "Polygon",
|
||||
"tags": ["Layer 2"],
|
||||
"websiteLink": "https://polygon.technology/",
|
||||
"whitepaperLink": "https://polygon.technology/lightpaper-polygon.pdf",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/polygon/"
|
||||
},
|
||||
"MKR-USD": {
|
||||
"name": "Maker",
|
||||
"tags": ["Governance"],
|
||||
"websiteLink": "https://makerdao.com",
|
||||
"whitepaperLink": "https://makerdao.com/whitepaper",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/maker/"
|
||||
},
|
||||
"NEAR-USD": {
|
||||
"name": "NEAR Protocol",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://near.org",
|
||||
"whitepaperLink": "https://near.org/papers/the-official-near-white-paper/",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/near-protocol/"
|
||||
},
|
||||
"OP-USD": {
|
||||
"name": "Optimism",
|
||||
"tags": [],
|
||||
"websiteLink": "https://www.optimism.io/",
|
||||
"whitepaperLink": "https://github.com/ethereum-optimism",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/optimism-ethereum/"
|
||||
},
|
||||
"PEPE-USD": {
|
||||
"name": "Pepe",
|
||||
"tags": [],
|
||||
"websiteLink": "https://www.pepe.vip/",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/pepe/"
|
||||
},
|
||||
"RUNE-USD": {
|
||||
"name": "THORChain",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://thorchain.org",
|
||||
"whitepaperLink": "https://whitepaper.io/document/709/thorchain-whitepaper",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/thorchain/"
|
||||
},
|
||||
"SEI-USD": {
|
||||
"name": "Sei",
|
||||
"tags": ["Layer 1", "Defi"],
|
||||
"websiteLink": "https://www.sei.io/",
|
||||
"whitepaperLink": "https://github.com/sei-protocol/sei-chain/blob/3c9576fee3494ce039df684624f918dd8066ba3f/whitepaper/Sei_Whitepaper.pdf",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/sei/"
|
||||
},
|
||||
"SHIB-USD": {
|
||||
"name": "Shiba Inu",
|
||||
"tags": [],
|
||||
"websiteLink": "https://shibatoken.com/",
|
||||
"whitepaperLink": "https://docs.shibatoken.com/",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/shiba-inu/"
|
||||
},
|
||||
"SNX-USD": {
|
||||
"name": "Synthetix",
|
||||
"tags": ["Defi"],
|
||||
"websiteLink": "https://synthetix.io/",
|
||||
"whitepaperLink": "https://docs.synthetix.io/litepaper",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/synthetix-network-token/"
|
||||
},
|
||||
"SOL-USD": {
|
||||
"name": "Solana",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://solana.com/",
|
||||
"whitepaperLink": "https://solana.com/solana-whitepaper.pdf",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/solana/"
|
||||
},
|
||||
"SUI-USD": {
|
||||
"name": "Sui",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://sui.io/",
|
||||
"whitepaperLink": "https://github.com/MystenLabs",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/sui/"
|
||||
},
|
||||
"SUSHI-USD": {
|
||||
"name": "Sushi",
|
||||
"tags": ["Defi"],
|
||||
"websiteLink": "https://sushi.com/",
|
||||
"whitepaperLink": "https://docs.sushi.com/",
|
||||
"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": {
|
||||
"name": "TRON",
|
||||
"tags": ["Defi"],
|
||||
"websiteLink": "https://tron.network/",
|
||||
"whitepaperLink": "https://tron.network/static/doc/white_paper_v_2_0.pdf",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/tron/"
|
||||
},
|
||||
"UMA-USD": {
|
||||
"name": "UMA",
|
||||
"tags": ["Defi"],
|
||||
"websiteLink": "https://umaproject.org/",
|
||||
"whitepaperLink": "https://github.com/UMAprotocol/whitepaper",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/uma/"
|
||||
},
|
||||
"UNI-USD": {
|
||||
"name": "Uniswap",
|
||||
"tags": ["Defi"],
|
||||
"websiteLink": "https://uniswap.org/",
|
||||
"whitepaperLink": "https://uniswap.org/whitepaper-v3.pdf",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/uniswap/"
|
||||
},
|
||||
"WLD-USD": {
|
||||
"name": "Worldcoin",
|
||||
"tags": [],
|
||||
"websiteLink": "https://worldcoin.org/",
|
||||
"whitepaperLink": "https://whitepaper.worldcoin.org/",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/worldcoin-org/"
|
||||
},
|
||||
"XLM-USD": {
|
||||
"name": "Stellar",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://www.stellar.org/",
|
||||
"whitepaperLink": "https://www.stellar.org/papers/stellar-consensus-protocol",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/stellar/"
|
||||
},
|
||||
"XMR-USD": {
|
||||
"name": "Monero",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://www.getmonero.org/",
|
||||
"whitepaperLink": "https://www.getmonero.org/resources/research-lab/",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/monero/"
|
||||
},
|
||||
"XRP-USD": {
|
||||
"name": "Ripple",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://ripple.com/currency/",
|
||||
"whitepaperLink": "https://github.com/ripple",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/xrp/"
|
||||
},
|
||||
"XTZ-USD": {
|
||||
"name": "Tezos",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://tezos.com",
|
||||
"whitepaperLink": "https://tezos.com/whitepaper.pdf",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/tezos/"
|
||||
},
|
||||
"YFI-USD": {
|
||||
"name": "Yearn",
|
||||
"tags": ["Defi"],
|
||||
"websiteLink": "https://yearn.finance/",
|
||||
"whitepaperLink": "https://docs.yearn.finance/",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/yearn-finance/"
|
||||
},
|
||||
"ZEC-USD": {
|
||||
"name": "Zcash",
|
||||
"tags": ["Layer 1"],
|
||||
"websiteLink": "https://z.cash/",
|
||||
"whitepaperLink": "https://z.cash/technology/",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/zcash/"
|
||||
},
|
||||
"ZRX-USD": {
|
||||
"name": "0x",
|
||||
"tags": ["Defi"],
|
||||
"websiteLink": "https://0x.org/",
|
||||
"whitepaperLink": "https://0x.org/pdfs/0x_white_paper.pdf",
|
||||
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/0x/"
|
||||
}
|
||||
}
|
||||
"1INCH-USD":{
|
||||
"name":"1inch",
|
||||
"tags":[
|
||||
"Defi"
|
||||
],
|
||||
"websiteLink":"https://1inch.io/",
|
||||
"whitepaperLink":"https://github.com/1inch",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/1inch/"
|
||||
},
|
||||
"AAVE-USD":{
|
||||
"name":"Aave",
|
||||
"tags":[
|
||||
"Defi"
|
||||
],
|
||||
"websiteLink":"https://aave.com/",
|
||||
"whitepaperLink":"https://github.com/aave/protocol-v2/blob/master/aave-v2-whitepaper.pdf",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/aave/"
|
||||
},
|
||||
"ADA-USD":{
|
||||
"name":"Cardano",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://cardano.org/",
|
||||
"whitepaperLink":"https://why.cardano.org/en/introduction/motivation/",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/cardano/"
|
||||
},
|
||||
"ALGO-USD":{
|
||||
"name":"Algorand",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://algorand.com/",
|
||||
"whitepaperLink":"https://algorand.com/technology/white-papers",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/algorand/"
|
||||
},
|
||||
"APE-USD":{
|
||||
"name":"ApeCoin",
|
||||
"tags":[
|
||||
|
||||
],
|
||||
"websiteLink":"https://apecoin.com/",
|
||||
"whitepaperLink":"https://apecoin.com/about",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/apecoin-ape/"
|
||||
},
|
||||
"APT-USD":{
|
||||
"name":"Aptos",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://aptoslabs.com/",
|
||||
"whitepaperLink":"https://aptos.dev/aptos-white-paper/",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/aptos/"
|
||||
},
|
||||
"ARB-USD":{
|
||||
"name":"Arbitrum",
|
||||
"tags":[
|
||||
|
||||
],
|
||||
"websiteLink":"https://arbitrum.io/",
|
||||
"whitepaperLink":"https://github.com/OffchainLabs/nitro",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/arbitrum/"
|
||||
},
|
||||
"ATOM-USD":{
|
||||
"name":"Cosmos",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://cosmos.network/",
|
||||
"whitepaperLink":"https://v1.cosmos.network/resources/whitepaper",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/cosmos/"
|
||||
},
|
||||
"AVAX-USD":{
|
||||
"name":"Avalanche",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://www.avalabs.org/",
|
||||
"whitepaperLink":"https://assets.website-files.com/5d80307810123f5ffbb34d6e/6008d7bbf8b10d1eb01e7e16_Avalanche%20Platform%20Whitepaper.pdf",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/avalanche/"
|
||||
},
|
||||
"BCH-USD":{
|
||||
"name":"Bitcoin Cash",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://bitcoincash.org/",
|
||||
"whitepaperLink":"https://bitcoincash.org/",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/bitcoin-cash/"
|
||||
},
|
||||
"BLUR-USD":{
|
||||
"name":"Blur",
|
||||
"tags":[
|
||||
|
||||
],
|
||||
"websiteLink":"https://blur.io/",
|
||||
"whitepaperLink":"https://docs.blur.foundation/",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/blur-token/"
|
||||
},
|
||||
"CELO-USD":{
|
||||
"name":"Celo",
|
||||
"tags":[
|
||||
|
||||
],
|
||||
"websiteLink":"https://celo.org",
|
||||
"whitepaperLink":"https://docs.celo.org",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/celo/"
|
||||
},
|
||||
"BTC-USD":{
|
||||
"name":"Bitcoin",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://bitcoin.org/",
|
||||
"whitepaperLink":"https://bitcoin.org/bitcoin.pdf",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/bitcoin/"
|
||||
},
|
||||
"COMP-USD":{
|
||||
"name":"Compound",
|
||||
"tags":[
|
||||
"Defi"
|
||||
],
|
||||
"websiteLink":"https://compound.finance/",
|
||||
"whitepaperLink":"https://compound.finance/documents/Compound.Whitepaper.pdf",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/compound/"
|
||||
},
|
||||
"CRV-USD":{
|
||||
"name":"Curve",
|
||||
"tags":[
|
||||
"Governance"
|
||||
],
|
||||
"websiteLink":"https://curve.fi/",
|
||||
"whitepaperLink":"https://curve.fi/whitepaper",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/curve-dao-token/"
|
||||
},
|
||||
"DOGE-USD":{
|
||||
"name":"Dogecoin",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://dogecoin.com/",
|
||||
"whitepaperLink":"https://github.com/dogecoin/dogecoin",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/dogecoin/"
|
||||
},
|
||||
"DOT-USD":{
|
||||
"name":"Polkadot",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://polkadot.network/",
|
||||
"whitepaperLink":"https://polkadot.network/PolkaDotPaper.pdf",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/polkadot-new/"
|
||||
},
|
||||
"ENJ-USD":{
|
||||
"name":"Enjin",
|
||||
"tags":[
|
||||
|
||||
],
|
||||
"websiteLink":"https://enjin.io/",
|
||||
"whitepaperLink":"https://cdn.enjin.io/downloads/whitepapers/enjin-coin/en.pdf/",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/enjin-coin/"
|
||||
},
|
||||
"EOS-USD":{
|
||||
"name":"EOS",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://eos.io/",
|
||||
"whitepaperLink":"https://github.com/EOSIO/Documentation/blob/master/TechnicalWhitePaper.md",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/eos/"
|
||||
},
|
||||
"ETC-USD":{
|
||||
"name":"Ethereum Classic",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://ethereumclassic.org/",
|
||||
"whitepaperLink":"https://ethereumclassic.org/why-classic",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/ethereum-classic/"
|
||||
},
|
||||
"ETH-USD":{
|
||||
"name":"Ethereum",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://ethereum.org/",
|
||||
"whitepaperLink":"https://ethereum.org/whitepaper/",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/ethereum/",
|
||||
"displayStepSize":"0.001",
|
||||
"displayTickSize":"0.1"
|
||||
},
|
||||
"FIL-USD":{
|
||||
"name":"Filecoin",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://filecoin.io/",
|
||||
"whitepaperLink":"https://filecoin.io/filecoin.pdf",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/filecoin/"
|
||||
},
|
||||
"HNT-USD":{
|
||||
"name":"Helium",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://www.helium.com",
|
||||
"whitepaperLink":"http://whitepaper.helium.com",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/helium/"
|
||||
},
|
||||
"ICP-USD":{
|
||||
"name":"Internet Computer",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://dfinity.org",
|
||||
"whitepaperLink":"https://dfinity.org/whitepaper.pdf",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/internet-computer/"
|
||||
},
|
||||
"LDO-USD":{
|
||||
"name":"Lido DAO",
|
||||
"tags":[
|
||||
"Defi"
|
||||
],
|
||||
"websiteLink":"https://lido.fi/",
|
||||
"whitepaperLink":"https://lido.fi/static/Lido:Ethereum-Liquid-Staking.pdf",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/lido-dao/"
|
||||
},
|
||||
"LINK-USD":{
|
||||
"name":"Chainlink",
|
||||
"tags":[
|
||||
"Defi"
|
||||
],
|
||||
"websiteLink":"https://chain.link/",
|
||||
"whitepaperLink":"https://link.smartcontract.com/whitepaper",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/chainlink/"
|
||||
},
|
||||
"LTC-USD":{
|
||||
"name":"Litecoin",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://litecoin.org/",
|
||||
"whitepaperLink":"https://litecoin.info/index.php/Main_Page",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/litecoin/"
|
||||
},
|
||||
"MATIC-USD":{
|
||||
"name":"Polygon",
|
||||
"tags":[
|
||||
"Layer 2"
|
||||
],
|
||||
"websiteLink":"https://polygon.technology/",
|
||||
"whitepaperLink":"https://polygon.technology/lightpaper-polygon.pdf",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/polygon/"
|
||||
},
|
||||
"MKR-USD":{
|
||||
"name":"Maker",
|
||||
"tags":[
|
||||
"Governance"
|
||||
],
|
||||
"websiteLink":"https://makerdao.com",
|
||||
"whitepaperLink":"https://makerdao.com/whitepaper",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/maker/"
|
||||
},
|
||||
"NEAR-USD":{
|
||||
"name":"NEAR Protocol",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://near.org",
|
||||
"whitepaperLink":"https://near.org/papers/the-official-near-white-paper/",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/near-protocol/"
|
||||
},
|
||||
"OP-USD":{
|
||||
"name":"Optimism",
|
||||
"tags":[
|
||||
|
||||
],
|
||||
"websiteLink":"https://www.optimism.io/",
|
||||
"whitepaperLink":"https://github.com/ethereum-optimism",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/optimism-ethereum/"
|
||||
},
|
||||
"PEPE-USD":{
|
||||
"name":"Pepe",
|
||||
"tags":[
|
||||
|
||||
],
|
||||
"websiteLink":"https://www.pepe.vip/",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/pepe/"
|
||||
},
|
||||
"RUNE-USD":{
|
||||
"name":"THORChain",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://thorchain.org",
|
||||
"whitepaperLink":"https://whitepaper.io/document/709/thorchain-whitepaper",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/thorchain/"
|
||||
},
|
||||
"SEI-USD":{
|
||||
"name":"Sei",
|
||||
"tags":[
|
||||
"Layer 1",
|
||||
"Defi"
|
||||
],
|
||||
"websiteLink":"https://www.sei.io/",
|
||||
"whitepaperLink":"https://github.com/sei-protocol/sei-chain/blob/3c9576fee3494ce039df684624f918dd8066ba3f/whitepaper/Sei_Whitepaper.pdf",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/sei/"
|
||||
},
|
||||
"SHIB-USD":{
|
||||
"name":"Shiba Inu",
|
||||
"tags":[
|
||||
|
||||
],
|
||||
"websiteLink":"https://shibatoken.com/",
|
||||
"whitepaperLink":"https://docs.shibatoken.com/",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/shiba-inu/"
|
||||
},
|
||||
"SNX-USD":{
|
||||
"name":"Synthetix",
|
||||
"tags":[
|
||||
"Defi"
|
||||
],
|
||||
"websiteLink":"https://synthetix.io/",
|
||||
"whitepaperLink":"https://docs.synthetix.io/litepaper",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/synthetix-network-token/"
|
||||
},
|
||||
"SOL-USD":{
|
||||
"name":"Solana",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://solana.com/",
|
||||
"whitepaperLink":"https://solana.com/solana-whitepaper.pdf",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/solana/"
|
||||
},
|
||||
"SUI-USD":{
|
||||
"name":"Sui",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://sui.io/",
|
||||
"whitepaperLink":"https://github.com/MystenLabs",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/sui/"
|
||||
},
|
||||
"SUSHI-USD":{
|
||||
"name":"Sushi",
|
||||
"tags":[
|
||||
"Defi"
|
||||
],
|
||||
"websiteLink":"https://sushi.com/",
|
||||
"whitepaperLink":"https://docs.sushi.com/",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/sushiswap/"
|
||||
},
|
||||
"TRX-USD":{
|
||||
"name":"TRON",
|
||||
"tags":[
|
||||
"Defi"
|
||||
],
|
||||
"websiteLink":"https://tron.network/",
|
||||
"whitepaperLink":"https://tron.network/static/doc/white_paper_v_2_0.pdf",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/tron/"
|
||||
},
|
||||
"UMA-USD":{
|
||||
"name":"UMA",
|
||||
"tags":[
|
||||
"Defi"
|
||||
],
|
||||
"websiteLink":"https://umaproject.org/",
|
||||
"whitepaperLink":"https://github.com/UMAprotocol/whitepaper",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/uma/"
|
||||
},
|
||||
"UNI-USD":{
|
||||
"name":"Uniswap",
|
||||
"tags":[
|
||||
"Defi"
|
||||
],
|
||||
"websiteLink":"https://uniswap.org/",
|
||||
"whitepaperLink":"https://uniswap.org/whitepaper-v3.pdf",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/uniswap/"
|
||||
},
|
||||
"WLD-USD":{
|
||||
"name":"Worldcoin",
|
||||
"tags":[
|
||||
|
||||
],
|
||||
"websiteLink":"https://worldcoin.org/",
|
||||
"whitepaperLink":"https://whitepaper.worldcoin.org/",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/worldcoin-org/"
|
||||
},
|
||||
"XLM-USD":{
|
||||
"name":"Stellar",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://www.stellar.org/",
|
||||
"whitepaperLink":"https://www.stellar.org/papers/stellar-consensus-protocol",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/stellar/"
|
||||
},
|
||||
"XMR-USD":{
|
||||
"name":"Monero",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://www.getmonero.org/",
|
||||
"whitepaperLink":"https://www.getmonero.org/resources/research-lab/",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/monero/"
|
||||
},
|
||||
"XRP-USD":{
|
||||
"name":"Ripple",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://ripple.com/currency/",
|
||||
"whitepaperLink":"https://github.com/ripple",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/xrp/"
|
||||
},
|
||||
"XTZ-USD":{
|
||||
"name":"Tezos",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://tezos.com",
|
||||
"whitepaperLink":"https://tezos.com/whitepaper.pdf",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/tezos/"
|
||||
},
|
||||
"YFI-USD":{
|
||||
"name":"Yearn",
|
||||
"tags":[
|
||||
"Defi"
|
||||
],
|
||||
"websiteLink":"https://yearn.finance/",
|
||||
"whitepaperLink":"https://docs.yearn.finance/",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/yearn-finance/"
|
||||
},
|
||||
"ZEC-USD":{
|
||||
"name":"Zcash",
|
||||
"tags":[
|
||||
"Layer 1"
|
||||
],
|
||||
"websiteLink":"https://z.cash/",
|
||||
"whitepaperLink":"https://z.cash/technology/",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/zcash/"
|
||||
},
|
||||
"ZRX-USD":{
|
||||
"name":"0x",
|
||||
"tags":[
|
||||
"Defi"
|
||||
],
|
||||
"websiteLink":"https://0x.org/",
|
||||
"whitepaperLink":"https://0x.org/pdfs/0x_white_paper.pdf",
|
||||
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/0x/"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 300 KiB |
|
Before Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 481 KiB |
@@ -1,8 +1,8 @@
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { Suspense } from 'react';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
import styled, { AnyStyledComponent, css } from 'styled-components';
|
||||
import { WagmiProvider } from 'wagmi';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { WagmiConfig } from 'wagmi';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
import { GrazProvider } from 'graz';
|
||||
|
||||
import { AppRoute, DEFAULT_TRADE_ROUTE } from '@/constants/routes';
|
||||
@@ -22,9 +22,21 @@ import { NotificationsProvider } from '@/hooks/useNotifications';
|
||||
import { LocalNotificationsProvider } from '@/hooks/useLocalNotifications';
|
||||
import { RestrictionProvider } from '@/hooks/useRestrictions';
|
||||
import { SubaccountProvider } from '@/hooks/useSubaccount';
|
||||
import { SquidProvider } from '@/hooks/useSquid';
|
||||
import { TestFlagsProvider } from '@/hooks/useTestFlags';
|
||||
|
||||
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 { FooterDesktop } from '@/layout/Footer/FooterDesktop';
|
||||
import { FooterMobile } from '@/layout/Footer/FooterMobile';
|
||||
@@ -36,22 +48,11 @@ import { config } from '@/lib/wagmi';
|
||||
|
||||
import { breakpoints } from '@/styles';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import { LoadingSpace } from './components/Loading/LoadingSpinner';
|
||||
|
||||
import '@/styles/constants.css';
|
||||
import '@/styles/fonts.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 Content = () => {
|
||||
@@ -70,7 +71,7 @@ const Content = () => {
|
||||
{isNotTablet && <HeaderDesktop />}
|
||||
|
||||
<Styled.Main>
|
||||
<Suspense fallback={<LoadingSpace id="main" />}>
|
||||
<Suspense fallback={null}>
|
||||
<Routes>
|
||||
<Route path={AppRoute.Trade}>
|
||||
<Route path=":market" element={<TradePage />} />
|
||||
@@ -120,14 +121,16 @@ const wrapProvider = (Component: React.ComponentType<any>, props?: any) => {
|
||||
};
|
||||
|
||||
const providers = [
|
||||
wrapProvider(WagmiProvider, { config }),
|
||||
wrapProvider(QueryClientProvider, { client: queryClient }),
|
||||
wrapProvider(GrazProvider),
|
||||
wrapProvider(WagmiConfig, { config }),
|
||||
wrapProvider(TestFlagsProvider),
|
||||
wrapProvider(LocaleProvider),
|
||||
wrapProvider(RestrictionProvider),
|
||||
wrapProvider(DydxProvider),
|
||||
wrapProvider(AccountsProvider),
|
||||
wrapProvider(SubaccountProvider),
|
||||
wrapProvider(SquidProvider),
|
||||
wrapProvider(LocalNotificationsProvider),
|
||||
wrapProvider(NotificationsProvider),
|
||||
wrapProvider(DialogAreaProvider),
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
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);
|
||||
}
|
||||
`;
|
||||
@@ -45,7 +45,6 @@ const assetIcons = {
|
||||
SOL: '/currencies/sol.png',
|
||||
SUI: '/currencies/sui.png',
|
||||
SUSHI: '/currencies/sushi.png',
|
||||
TIA: '/currencies/tia.png',
|
||||
TRX: '/currencies/trx.png',
|
||||
UMA: '/currencies/uma.png',
|
||||
UNI: '/currencies/uni.png',
|
||||
@@ -72,13 +71,8 @@ export const AssetIcon = ({
|
||||
}: {
|
||||
symbol?: Nullable<string>;
|
||||
className?: string;
|
||||
}) => (
|
||||
<Styled.Img
|
||||
src={isAssetSymbol(symbol) ? assetIcons[symbol] : '/currencies/unavailable.png'}
|
||||
className={className}
|
||||
alt={symbol}
|
||||
/>
|
||||
);
|
||||
}) =>
|
||||
isAssetSymbol(symbol) ? <Styled.Img src={assetIcons[symbol]} className={className} /> : null;
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ export const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonPr
|
||||
|
||||
return (
|
||||
<StyledBaseButton
|
||||
disabled={state[ButtonState.Disabled] || state[ButtonState.Loading]}
|
||||
disabled={state[ButtonState.Disabled]}
|
||||
{...{ ref, action, size, shape, state, ...otherProps }}
|
||||
>
|
||||
{
|
||||
|
||||
@@ -31,12 +31,10 @@ type ElementProps<TabItemsValue> = {
|
||||
};
|
||||
|
||||
type StyleProps = {
|
||||
className?: string;
|
||||
fullWidthTabs?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export type CollapsibleTabsProps<TabItemsValue> = ElementProps<TabItemsValue> & StyleProps;
|
||||
|
||||
export const CollapsibleTabs = <TabItemsValue extends string>({
|
||||
defaultValue,
|
||||
items,
|
||||
@@ -45,9 +43,8 @@ export const CollapsibleTabs = <TabItemsValue extends string>({
|
||||
onOpenChange,
|
||||
|
||||
fullWidthTabs,
|
||||
|
||||
className,
|
||||
}: CollapsibleTabsProps<TabItemsValue>) => {
|
||||
}: ElementProps<TabItemsValue> & StyleProps) => {
|
||||
const [value, setValue] = useState(defaultValue);
|
||||
|
||||
const currentItem = items.find((item) => item.value === value);
|
||||
@@ -89,8 +86,8 @@ export const CollapsibleTabs = <TabItemsValue extends string>({
|
||||
</Styled.Header>
|
||||
|
||||
<Styled.CollapsibleContent>
|
||||
{items.map(({ asChild, value, content }) => (
|
||||
<Styled.TabsContent key={value} asChild={asChild} value={value}>
|
||||
{items.map(({ value, content }) => (
|
||||
<Styled.TabsContent key={value} value={value}>
|
||||
{content}
|
||||
</Styled.TabsContent>
|
||||
))}
|
||||
@@ -222,6 +219,7 @@ Styled.Header = styled.header`
|
||||
|
||||
Styled.CollapsibleContent = styled(CollapsibleContent)`
|
||||
${layoutMixins.stack}
|
||||
${layoutMixins.perspectiveArea}
|
||||
|
||||
box-shadow: none;
|
||||
`;
|
||||
|
||||
@@ -38,7 +38,6 @@ import {
|
||||
HelpCircleIcon,
|
||||
HideIcon,
|
||||
HistoryIcon,
|
||||
LeaderboardIcon,
|
||||
LinkOutIcon,
|
||||
LockIcon,
|
||||
LogoShortIcon,
|
||||
@@ -55,7 +54,6 @@ import {
|
||||
OverviewIcon,
|
||||
PencilIcon,
|
||||
PlayIcon,
|
||||
PlusIcon,
|
||||
PositionsIcon,
|
||||
PriceChartIcon,
|
||||
PrivacyIcon,
|
||||
@@ -113,7 +111,6 @@ export enum IconName {
|
||||
HelpCircle = 'HelpCircle',
|
||||
Hide = 'Hide',
|
||||
History = 'History',
|
||||
Leaderboard = 'Leaderboard',
|
||||
LinkOut = 'LinkOut',
|
||||
Lock = 'Lock',
|
||||
LogoShort = 'LogoShort',
|
||||
@@ -131,7 +128,6 @@ export enum IconName {
|
||||
Overview = 'Overview',
|
||||
Pencil = 'Pencil',
|
||||
Play = 'Play',
|
||||
Plus = 'Plus',
|
||||
Positions = 'Positions',
|
||||
PriceChart = 'PriceChart',
|
||||
Privacy = 'Privacy',
|
||||
@@ -189,7 +185,6 @@ const icons = {
|
||||
[IconName.HelpCircle]: HelpCircleIcon,
|
||||
[IconName.Hide]: HideIcon,
|
||||
[IconName.History]: HistoryIcon,
|
||||
[IconName.Leaderboard]: LeaderboardIcon,
|
||||
[IconName.LinkOut]: LinkOutIcon,
|
||||
[IconName.Lock]: LockIcon,
|
||||
[IconName.LogoShort]: LogoShortIcon,
|
||||
@@ -206,7 +201,6 @@ const icons = {
|
||||
[IconName.Overview]: OverviewIcon,
|
||||
[IconName.Pencil]: PencilIcon,
|
||||
[IconName.Play]: PlayIcon,
|
||||
[IconName.Plus]: PlusIcon,
|
||||
[IconName.Positions]: PositionsIcon,
|
||||
[IconName.PriceChart]: PriceChartIcon,
|
||||
[IconName.Privacy]: PrivacyIcon,
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
USD_DECIMALS,
|
||||
} from '@/constants/numbers';
|
||||
|
||||
import { BIG_NUMBERS } from '@/lib/numbers';
|
||||
import { BIG_NUMBERS, MustBigNumber } from '@/lib/numbers';
|
||||
import { useLocaleSeparators } from '@/hooks';
|
||||
|
||||
export enum InputType {
|
||||
@@ -29,38 +29,31 @@ type StyleProps = {
|
||||
type ElementProps = {
|
||||
type?: InputType;
|
||||
value?: string | number | null;
|
||||
|
||||
allowNegative?: boolean;
|
||||
decimals?: number;
|
||||
disabled?: boolean;
|
||||
id?: string;
|
||||
max?: number;
|
||||
onBlur?: () => void;
|
||||
onChange?:
|
||||
| Dispatch<SetStateAction<string>>
|
||||
| React.ReactEventHandler<HTMLInputElement>
|
||||
| ((values: NumberFormatValues, e: SourceInfo) => void);
|
||||
onFocus?: () => void;
|
||||
onInput?: ({
|
||||
value,
|
||||
floatValue,
|
||||
formattedValue,
|
||||
}: {
|
||||
value: string;
|
||||
floatValue?: number;
|
||||
formattedValue: string;
|
||||
}) => void;
|
||||
placeholder?: string;
|
||||
};
|
||||
|
||||
type ConditionalProps =
|
||||
| {
|
||||
allowNegative?: boolean;
|
||||
decimals?: number;
|
||||
max?: number;
|
||||
onChange?: (values: NumberFormatValues, e: SourceInfo) => void;
|
||||
onInput?: ({
|
||||
value,
|
||||
floatValue,
|
||||
formattedValue,
|
||||
}: {
|
||||
value: string;
|
||||
floatValue?: number;
|
||||
formattedValue: string;
|
||||
}) => void;
|
||||
}
|
||||
| {
|
||||
allowNegative?: never;
|
||||
decimals?: never;
|
||||
max?: never;
|
||||
onChange?: Dispatch<SetStateAction<string>> | React.ReactEventHandler<HTMLInputElement>;
|
||||
onInput?: (e: SyntheticInputEvent) => void;
|
||||
};
|
||||
|
||||
export type InputProps = ElementProps & StyleProps & ConditionalProps;
|
||||
export type InputProps = ElementProps & StyleProps;
|
||||
|
||||
export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
(
|
||||
@@ -136,7 +129,6 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
// Other
|
||||
data-1p-ignore // prevent 1Password fill
|
||||
{...otherProps}
|
||||
/>
|
||||
) : (
|
||||
@@ -145,7 +137,6 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
ref={ref as React.Ref<typeof NumericFormat<unknown>>}
|
||||
id={id}
|
||||
// NumericFormat
|
||||
valueIsNumericString
|
||||
allowNegative={allowNegative}
|
||||
decimalScale={decimals}
|
||||
decimalSeparator={LOCALE_DECIMAL_SEPARATOR}
|
||||
@@ -169,7 +160,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
? undefined
|
||||
: Number(formattedValue.replace(',', '.'));
|
||||
|
||||
onInput?.({ value, floatValue, formattedValue, ...e });
|
||||
onInput?.({ value, floatValue, formattedValue });
|
||||
}}
|
||||
// Native
|
||||
disabled={disabled}
|
||||
@@ -177,7 +168,6 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
value={formattedValue}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
data-1p-ignore // prevent 1Password fill
|
||||
{...otherProps}
|
||||
/>
|
||||
)}
|
||||
@@ -198,6 +188,7 @@ Styled.InputContainer = styled.div`
|
||||
border-radius: inherit;
|
||||
|
||||
input {
|
||||
user-select: all;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { Icon, IconName } from './Icon';
|
||||
import { Tag } from './Tag';
|
||||
import { MustBigNumber } from '@/lib/numbers';
|
||||
|
||||
export { TableCell } from './Table/TableCell';
|
||||
export { TableColumnHeader } from './Table/TableColumnHeader';
|
||||
@@ -162,7 +161,10 @@ export const Table = <TableRowData extends object, TableRowKey extends Key>({
|
||||
? // String
|
||||
collator.compare(first as string, second as string)
|
||||
: // Number
|
||||
MustBigNumber(first).comparedTo(MustBigNumber(second))) *
|
||||
Math.sign(
|
||||
(parseInt(first as string) || (first as number)) -
|
||||
(parseInt(second as string) || (second as number))
|
||||
)) *
|
||||
// Flip the direction if descending order is specified.
|
||||
(sortDirection === 'descending' ? -1 : 1)
|
||||
);
|
||||
|
||||
@@ -21,7 +21,6 @@ export type TabItem<TabItemsValue> = {
|
||||
content?: React.ReactNode;
|
||||
subitems?: TabItem<TabItemsValue>[];
|
||||
customTrigger?: ReactNode;
|
||||
asChild?: boolean;
|
||||
};
|
||||
|
||||
type ElementProps<TabItemsValue> = {
|
||||
@@ -42,8 +41,6 @@ type StyleProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export type TabsProps<TabItemsValue> = ElementProps<TabItemsValue> & StyleProps;
|
||||
|
||||
export const Tabs = <TabItemsValue extends string>({
|
||||
defaultValue,
|
||||
value,
|
||||
@@ -110,10 +107,9 @@ export const Tabs = <TabItemsValue extends string>({
|
||||
sharedContent
|
||||
) : (
|
||||
<Styled.Stack>
|
||||
{items.map(({ asChild, value, content, forceMount }) => (
|
||||
{items.map(({ value, content, forceMount }) => (
|
||||
<Styled.Content
|
||||
key={value}
|
||||
asChild={asChild}
|
||||
value={value}
|
||||
forceMount={forceMount}
|
||||
$hide={forceMount && currentItem?.value !== value}
|
||||
@@ -246,6 +242,7 @@ Styled.Trigger = styled(Trigger)<{ $withBorders?: boolean }>`
|
||||
|
||||
Styled.Stack = styled.div`
|
||||
${layoutMixins.stack}
|
||||
${layoutMixins.perspectiveArea}
|
||||
|
||||
box-shadow: none;
|
||||
`;
|
||||
|
||||
@@ -3,7 +3,6 @@ export enum DialogTypes {
|
||||
Deposit = 'Deposit',
|
||||
DisconnectWallet = 'DisconnectWallet',
|
||||
ExchangeOffline = 'ExchangeOffline',
|
||||
ExternalLink = 'ExternalLink',
|
||||
FillDetails = 'FillDetails',
|
||||
Help = 'Help',
|
||||
ExternalNavKeplr = 'ExternalNavKeplr',
|
||||
|
||||
@@ -22,7 +22,6 @@ export enum LocalStorageKey {
|
||||
SelectedTheme = 'dydx.SelectedTheme',
|
||||
SelectedTradeLayout = 'dydx.SelectedTradeLayout',
|
||||
TradingViewChartConfig = 'dydx.TradingViewChartConfig',
|
||||
HasSeenLaunchIncentives = 'dydx.HasSeenLaunchIncentives',
|
||||
}
|
||||
|
||||
export const LOCAL_STORAGE_VERSIONS = {
|
||||
|
||||
@@ -26,3 +26,15 @@ export enum FundingDirection {
|
||||
ToShort = 'ToShort',
|
||||
ToLong = 'ToLong',
|
||||
}
|
||||
'test'
|
||||
|
||||
export const MARKETS_TO_DISPLAY = [
|
||||
'BTC-USD',
|
||||
'ETH-USD',
|
||||
'LINK-USD',
|
||||
'SOL-USD',
|
||||
'MATIC-USD',
|
||||
'ATOM-USD',
|
||||
'AVAX-USD',
|
||||
'APE-USD',
|
||||
];
|
||||
|
||||
@@ -122,20 +122,13 @@ export type NotificationDisplayData = {
|
||||
toastDuration?: number;
|
||||
};
|
||||
|
||||
export enum TransferNotificationTypes {
|
||||
Withdrawal = 'withdrawal',
|
||||
Deposit = 'deposit',
|
||||
}
|
||||
|
||||
// Notification types
|
||||
export type TransferNotifcation = {
|
||||
txHash: string;
|
||||
type?: TransferNotificationTypes;
|
||||
toChainId?: string;
|
||||
fromChainId?: string;
|
||||
toAmount?: number;
|
||||
triggeredAt?: number;
|
||||
isCctp?: boolean;
|
||||
errorCount?: number;
|
||||
status?: StatusResponse;
|
||||
};
|
||||
|
||||
@@ -10,7 +10,6 @@ export const LEVERAGE_DECIMALS = 2;
|
||||
export const TOKEN_DECIMALS = 4;
|
||||
export const LARGE_TOKEN_DECIMALS = 2;
|
||||
export const FEE_DECIMALS = 3;
|
||||
export const FUNDING_DECIMALS = 6;
|
||||
|
||||
export const QUANTUM_MULTIPLIER = 1_000_000;
|
||||
|
||||
@@ -19,7 +18,3 @@ export enum NumberSign {
|
||||
Negative = 'Negative',
|
||||
Neutral = 'Neutral',
|
||||
}
|
||||
|
||||
// Deposit/Withdraw
|
||||
export const MAX_CCTP_TRANSFER_AMOUNT = 1_000_000;
|
||||
export const MAX_PRICE_IMPACT = 0.02; // 2%
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
export enum QueryKeys {
|
||||
ACCOUNT_BALANCE = 'ACCOUNT_BALANCE',
|
||||
LAUNCH_INCENTIVES = 'LAUNCH_INCENTIVES',
|
||||
TRANSACTION_STATUS = 'TRANSACTION_STATUS',
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
import { type TooltipStrings, TOOLTIP_STRING_KEYS } from '@/constants/localization';
|
||||
|
||||
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 }) => ({
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.SWAP_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.SWAP_BODY }),
|
||||
|
||||
@@ -5,10 +5,6 @@ export const withdrawTooltips: TooltipStrings = {
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.FAST_WITHDRAW_FEE_TITLE }),
|
||||
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 }) => ({
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.WITHDRAW_TYPES_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.WITHDRAW_TYPES_BODY }),
|
||||
|
||||
@@ -52,7 +52,7 @@ type WalletConnectionTypeConfig = {
|
||||
wagmiConnectorId?: string;
|
||||
};
|
||||
|
||||
export const WALLET_CONNECTION_TYPES: Record<WalletConnectionType, WalletConnectionTypeConfig> = {
|
||||
export const walletConnectionTypes: Record<WalletConnectionType, WalletConnectionTypeConfig> = {
|
||||
[WalletConnectionType.CoinbaseWalletSdk]: {
|
||||
name: 'Coinbase Wallet SDK',
|
||||
wagmiConnectorId: 'coinbaseWallet',
|
||||
@@ -110,7 +110,7 @@ export const WALLET_CONNECT_EXPLORER_RECOMMENDED_IDS = Object.values(
|
||||
WALLET_CONNECT_EXPLORER_RECOMMENDED_WALLETS
|
||||
);
|
||||
|
||||
export type WalletConfig = {
|
||||
type WalletConfig = {
|
||||
type: WalletType;
|
||||
stringKey: string;
|
||||
icon: string;
|
||||
@@ -274,17 +274,6 @@ export type WalletConnection = {
|
||||
provider?: ExternalProvider;
|
||||
};
|
||||
|
||||
export type WalletConnectConfig = {
|
||||
client: {
|
||||
name: string;
|
||||
description: string;
|
||||
iconUrl: string;
|
||||
};
|
||||
v2: {
|
||||
projectId: string;
|
||||
};
|
||||
};
|
||||
|
||||
// dYdX Chain wallets
|
||||
export const COSMOS_DERIVATION_PATH = "m/44'/118'/0'/0/0";
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
import { useBalance, useBlockNumber } from 'wagmi';
|
||||
import { useBalance } from 'wagmi';
|
||||
import { StargateClient } from '@cosmjs/stargate';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useQuery } from 'react-query';
|
||||
import { formatUnits } from 'viem';
|
||||
|
||||
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
import { QueryKeys } from '@/constants/queries';
|
||||
import { EvmAddress } from '@/constants/wallets';
|
||||
|
||||
import { convertBech32Address } from '@/lib/addressUtils';
|
||||
@@ -53,27 +52,15 @@ export const useAccountBalance = ({
|
||||
const evmChainId = Number(ENVIRONMENT_CONFIG_MAP[selectedNetwork].ethereumChainId);
|
||||
const stakingBalances = useSelector(getStakingBalances, shallowEqual);
|
||||
|
||||
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')),
|
||||
},
|
||||
const evmQuery = useBalance({
|
||||
enabled: Boolean(!isCosmosChain && addressOrDenom?.startsWith('0x')),
|
||||
address: evmAddress,
|
||||
chainId: typeof chainId === 'number' ? chainId : Number(evmChainId),
|
||||
token:
|
||||
addressOrDenom === CHAIN_DEFAULT_TOKEN_ADDRESS ? undefined : (addressOrDenom as EvmAddress),
|
||||
watch: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
queryClient.invalidateQueries({ queryKey });
|
||||
}, [blockNumber]);
|
||||
|
||||
const cosmosQueryFn = useCallback(async () => {
|
||||
if (dydxAddress && bech32AddrPrefix && rpc && addressOrDenom) {
|
||||
const address = convertBech32Address({
|
||||
@@ -91,7 +78,7 @@ export const useAccountBalance = ({
|
||||
|
||||
const cosmosQuery = useQuery({
|
||||
enabled: Boolean(isCosmosChain && dydxAddress && bech32AddrPrefix && rpc && addressOrDenom),
|
||||
queryKey: [QueryKeys.ACCOUNT_BALANCE, chainId, addressOrDenom],
|
||||
queryKey: `accountBalances_${chainId}_${addressOrDenom}`,
|
||||
queryFn: cosmosQueryFn,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
@@ -100,11 +87,8 @@ export const useAccountBalance = ({
|
||||
staleTime: 10_000,
|
||||
});
|
||||
|
||||
const balance = isCosmosChain
|
||||
? cosmosQuery.data
|
||||
: evmQuery
|
||||
? formatUnits(evmQuery?.value, evmQuery?.decimals)
|
||||
: undefined;
|
||||
const { formatted: evmBalance } = evmQuery.data || {};
|
||||
const balance = isCosmosChain ? cosmosQuery.data : evmBalance;
|
||||
|
||||
const nativeTokenCoinBalance = balances?.[chainTokenDenom];
|
||||
const nativeTokenBalance = MustBigNumber(nativeTokenCoinBalance?.amount);
|
||||
@@ -120,7 +104,7 @@ export const useAccountBalance = ({
|
||||
nativeTokenBalance,
|
||||
nativeStakingBalance,
|
||||
usdcBalance,
|
||||
queryStatus: isCosmosChain ? cosmosQuery.status : evmBalanceStatus,
|
||||
isQueryFetching: isCosmosChain ? cosmosQuery.isFetching : evmBalanceFetchStatus === 'fetching',
|
||||
queryStatus: isCosmosChain ? cosmosQuery.status : evmQuery.status,
|
||||
isQueryFetching: isCosmosChain ? cosmosQuery.isFetching : evmQuery.fetchStatus === 'fetching',
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback, useContext, createContext, useEffect, useState, useMemo }
|
||||
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { AES, enc } from 'crypto-js';
|
||||
import { NOBLE_BECH32_PREFIX, LocalWallet, type Subaccount } from '@dydxprotocol/v4-client-js';
|
||||
import { LocalWallet, type Subaccount } from '@dydxprotocol/v4-client-js';
|
||||
|
||||
import { OnboardingGuard, OnboardingState, type EvmDerivedAddresses } from '@/constants/account';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
@@ -39,6 +39,7 @@ const useAccountsContext = () => {
|
||||
walletConnectionType,
|
||||
selectWalletType,
|
||||
selectedWalletType,
|
||||
selectedWalletError,
|
||||
evmAddress,
|
||||
signerWagmi,
|
||||
publicClientWagmi,
|
||||
@@ -218,16 +219,6 @@ const useAccountsContext = () => {
|
||||
else abacusStateManager.attemptDisconnectAccount();
|
||||
}, [localDydxWallet]);
|
||||
|
||||
useEffect(() => {
|
||||
const setNobleWallet = async () => {
|
||||
if (hdKey?.mnemonic) {
|
||||
const nobleWallet = await LocalWallet.fromMnemonic(hdKey.mnemonic, NOBLE_BECH32_PREFIX);
|
||||
abacusStateManager.setNobleWallet(nobleWallet);
|
||||
}
|
||||
};
|
||||
setNobleWallet();
|
||||
}, [hdKey?.mnemonic]);
|
||||
|
||||
// clear subaccounts when no dydxAddress is set
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
@@ -299,7 +290,9 @@ const useAccountsContext = () => {
|
||||
walletConnectionType,
|
||||
|
||||
// Wallet selection
|
||||
selectWalletType,
|
||||
selectedWalletType,
|
||||
selectedWalletError,
|
||||
|
||||
// Wallet connection (EVM)
|
||||
evmAddress,
|
||||
|
||||
@@ -5,15 +5,16 @@ import { useMatch, useNavigate } from 'react-router-dom';
|
||||
import { LocalStorageKey } from '@/constants/localStorage';
|
||||
import { DEFAULT_MARKETID } from '@/constants/markets';
|
||||
import { AppRoute } from '@/constants/routes';
|
||||
import { useLocalStorage } from '@/hooks/useLocalStorage';
|
||||
|
||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||
import { closeDialogInTradeBox } from '@/state/dialogs';
|
||||
import { setCurrentMarketId } from '@/state/perpetuals';
|
||||
import { getMarketIds } from '@/state/perpetualsSelectors';
|
||||
|
||||
import abacusStateManager from '@/lib/abacus';
|
||||
|
||||
import { useLocalStorage } from './useLocalStorage';
|
||||
import { getMarketIds } from '@/state/perpetualsSelectors';
|
||||
|
||||
export const useCurrentMarketId = () => {
|
||||
const navigate = useNavigate();
|
||||
const match = useMatch(`/${AppRoute.Trade}/:marketId`);
|
||||
@@ -32,35 +33,17 @@ export const useCurrentMarketId = () => {
|
||||
if (marketIds.length === 0) return marketId ?? lastViewedMarket;
|
||||
if (!marketIds.includes(marketId ?? lastViewedMarket)) return DEFAULT_MARKETID;
|
||||
return marketId ?? lastViewedMarket;
|
||||
}, [hasMarketIds, marketId]);
|
||||
}, [marketIds, marketId]);
|
||||
|
||||
useEffect(() => {
|
||||
// If v4_markets has not been subscribed to yet or marketId is not specified, default to validId
|
||||
if (!hasMarketIds || !marketId) {
|
||||
setLastViewedMarket(validId);
|
||||
dispatch(setCurrentMarketId(validId));
|
||||
dispatch(closeDialogInTradeBox());
|
||||
setLastViewedMarket(validId);
|
||||
dispatch(setCurrentMarketId(validId));
|
||||
dispatch(closeDialogInTradeBox());
|
||||
|
||||
if (validId !== marketId) {
|
||||
navigate(`${AppRoute.Trade}/${validId}`, {
|
||||
replace: true,
|
||||
});
|
||||
}
|
||||
} 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]);
|
||||
navigate(`${AppRoute.Trade}/${validId}`, {
|
||||
replace: true,
|
||||
});
|
||||
}, [validId]);
|
||||
|
||||
useEffect(() => {
|
||||
// Check for marketIds otherwise Abacus will silently fail its isMarketValid check
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { createContext, useContext, useCallback, useEffect, useMemo } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useQuery } from 'react-query';
|
||||
import type { StatusResponse } from '@0xsquid/sdk';
|
||||
|
||||
import { LOCAL_STORAGE_VERSIONS, LocalStorageKey } from '@/constants/localStorage';
|
||||
import { type TransferNotifcation } from '@/constants/notifications';
|
||||
import { QueryKeys } from '@/constants/queries';
|
||||
import { useAccounts } from '@/hooks/useAccounts';
|
||||
|
||||
import { fetchSquidStatus, STATUS_ERROR_GRACE_PERIOD } from '@/lib/squid';
|
||||
import { STATUS_ERROR_GRACE_PERIOD, useSquid } from '@/hooks/useSquid';
|
||||
|
||||
import { useLocalStorage } from './useLocalStorage';
|
||||
|
||||
@@ -69,8 +68,10 @@ const useLocalNotificationsContext = () => {
|
||||
[transferNotifications]
|
||||
);
|
||||
|
||||
const { data: newTransferNotifications } = useQuery({
|
||||
queryKey: [QueryKeys.TRANSACTION_STATUS],
|
||||
const squid = useSquid();
|
||||
|
||||
useQuery({
|
||||
queryKey: 'getTransactionStatus',
|
||||
queryFn: async () => {
|
||||
const processTransferNotifications = async (transferNotifications: TransferNotifcation[]) => {
|
||||
const newTransferNotifications = await Promise.all(
|
||||
@@ -80,31 +81,23 @@ const useLocalNotificationsContext = () => {
|
||||
toChainId,
|
||||
fromChainId,
|
||||
triggeredAt,
|
||||
isCctp,
|
||||
errorCount,
|
||||
status: currentStatus,
|
||||
} = transferNotification;
|
||||
|
||||
const hasErrors =
|
||||
// @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
|
||||
!!currentStatus?.errors ||
|
||||
(currentStatus?.error && Object.keys(currentStatus.error).length !== 0);
|
||||
|
||||
if (
|
||||
!hasErrors &&
|
||||
// @ts-ignore status.errors is not in the type definition but can be returned
|
||||
!currentStatus?.errors &&
|
||||
!currentStatus?.error &&
|
||||
(!currentStatus?.squidTransactionStatus ||
|
||||
currentStatus?.squidTransactionStatus === 'ongoing')
|
||||
) {
|
||||
try {
|
||||
const status = await fetchSquidStatus(
|
||||
{
|
||||
transactionId: txHash,
|
||||
toChainId,
|
||||
fromChainId,
|
||||
},
|
||||
isCctp
|
||||
);
|
||||
const status = await squid?.getStatus({
|
||||
transactionId: txHash,
|
||||
toChainId,
|
||||
fromChainId,
|
||||
});
|
||||
|
||||
if (status) {
|
||||
transferNotification.status = status;
|
||||
@@ -126,18 +119,12 @@ const useLocalNotificationsContext = () => {
|
||||
|
||||
return newTransferNotifications;
|
||||
};
|
||||
|
||||
const newTransferNotifications = await processTransferNotifications(transferNotifications);
|
||||
return newTransferNotifications;
|
||||
setTransferNotifications(newTransferNotifications);
|
||||
},
|
||||
refetchInterval: TRANSFER_STATUS_FETCH_INTERVAL,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!newTransferNotifications) return;
|
||||
setTransferNotifications(newTransferNotifications);
|
||||
}, [newTransferNotifications]);
|
||||
|
||||
return {
|
||||
transferNotifications,
|
||||
addTransferNotification,
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useSelector, shallowEqual } from 'react-redux';
|
||||
|
||||
import { MarketFilters, MARKET_FILTER_LABELS, type MarketData } from '@/constants/markets';
|
||||
import {
|
||||
MarketFilters,
|
||||
MARKET_FILTER_LABELS,
|
||||
type MarketData,
|
||||
MARKETS_TO_DISPLAY,
|
||||
} from '@/constants/markets';
|
||||
|
||||
import { testFlags } from '@/hooks/useTestFlags';
|
||||
|
||||
import { getAssets } from '@/state/assetsSelectors';
|
||||
import { getPerpetualMarkets } from '@/state/perpetualsSelectors';
|
||||
@@ -42,7 +49,9 @@ export const useMarketsData = (
|
||||
}, [allPerpetualMarkets, allAssets]);
|
||||
|
||||
const filteredMarkets = useMemo(() => {
|
||||
const filtered = markets.filter(filterFunctions[filter]);
|
||||
const filtered = markets
|
||||
.filter(filterFunctions[filter])
|
||||
.filter(({ id }) => (testFlags.displayAllMarkets ? true : MARKETS_TO_DISPLAY.includes(id)));
|
||||
|
||||
if (searchFilter) {
|
||||
return filtered.filter(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { UseSwitchChainReturnType, useAccount, useSwitchChain } from 'wagmi';
|
||||
import { useNetwork, useSwitchNetwork } from 'wagmi';
|
||||
|
||||
export const useMatchingEvmNetwork = ({
|
||||
chainId,
|
||||
@@ -8,10 +8,10 @@ export const useMatchingEvmNetwork = ({
|
||||
}: {
|
||||
chainId?: string | number;
|
||||
switchAutomatically?: boolean;
|
||||
onError?: Parameters<UseSwitchChainReturnType['switchChainAsync']>[1]['onError'];
|
||||
onError?: (error: Error) => void;
|
||||
}) => {
|
||||
const { chain } = useAccount();
|
||||
const { switchChainAsync, isPending } = useSwitchChain();
|
||||
const { chain } = useNetwork();
|
||||
const { isLoading, switchNetworkAsync } = useSwitchNetwork({ onError });
|
||||
|
||||
// If chainId is not a number, we can assume it is a non EVM compatible chain
|
||||
const isMatchingNetwork = useMemo(
|
||||
@@ -21,14 +21,7 @@ export const useMatchingEvmNetwork = ({
|
||||
|
||||
const matchNetwork = useCallback(async () => {
|
||||
if (!isMatchingNetwork) {
|
||||
await switchChainAsync?.(
|
||||
{
|
||||
chainId: Number(chainId),
|
||||
},
|
||||
{
|
||||
onError,
|
||||
}
|
||||
);
|
||||
await switchNetworkAsync?.(Number(chainId));
|
||||
}
|
||||
}, [chainId, chain]);
|
||||
|
||||
@@ -41,6 +34,6 @@ export const useMatchingEvmNetwork = ({
|
||||
return {
|
||||
isMatchingNetwork,
|
||||
matchNetwork,
|
||||
isSwitchingNetwork: isPending,
|
||||
isSwitchingNetwork: isLoading,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
type NotificationTypeConfig,
|
||||
NotificationType,
|
||||
DEFAULT_TOAST_AUTO_CLOSE_MS,
|
||||
TransferNotificationTypes,
|
||||
} from '@/constants/notifications';
|
||||
|
||||
import { useSelectedNetwork, useStringGetter } from '@/hooks';
|
||||
@@ -153,20 +152,20 @@ export const notificationTypes: NotificationTypeConfig[] = [
|
||||
|
||||
useEffect(() => {
|
||||
for (const transfer of transferNotifications) {
|
||||
const { fromChainId, status, txHash, toAmount, type } = transfer;
|
||||
const { fromChainId, status, txHash, toAmount } = transfer;
|
||||
const isFinished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing';
|
||||
const icon = <Icon iconName={isFinished ? IconName.Transfer : IconName.Clock} />;
|
||||
|
||||
const transferType =
|
||||
type ?? fromChainId === ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId
|
||||
? TransferNotificationTypes.Withdrawal
|
||||
: TransferNotificationTypes.Deposit;
|
||||
const type =
|
||||
fromChainId === ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId
|
||||
? 'withdrawal'
|
||||
: 'deposit';
|
||||
|
||||
const title = stringGetter({
|
||||
key: {
|
||||
deposit: isFinished ? STRING_KEYS.DEPOSIT : STRING_KEYS.DEPOSIT_IN_PROGRESS,
|
||||
withdrawal: isFinished ? STRING_KEYS.WITHDRAW : STRING_KEYS.WITHDRAW_IN_PROGRESS,
|
||||
}[transferType],
|
||||
}[type],
|
||||
});
|
||||
|
||||
const toChainEta = status?.toChain?.chainData?.estimatedRouteDuration || 0;
|
||||
@@ -191,7 +190,7 @@ export const notificationTypes: NotificationTypeConfig[] = [
|
||||
slotIcon={icon}
|
||||
slotTitle={title}
|
||||
transfer={transfer}
|
||||
type={transferType}
|
||||
type={type}
|
||||
triggeredAt={transfer.triggeredAt}
|
||||
notification={notification}
|
||||
/>
|
||||
@@ -221,7 +220,7 @@ export const notificationTypes: NotificationTypeConfig[] = [
|
||||
body: stringGetter({
|
||||
key: 'NOTIFICATIONS.RELEASE_REWARDS_AND_FULL_TRADING.BODY',
|
||||
params: {
|
||||
DOS_BLOGPOST: (
|
||||
BLOGPOST: (
|
||||
<$Link
|
||||
href="https://www.dydxopsdao.com/blog/deep-dive-full-trading"
|
||||
target="_blank"
|
||||
@@ -230,7 +229,8 @@ export const notificationTypes: NotificationTypeConfig[] = [
|
||||
{stringGetter({ key: STRING_KEYS.HERE })}
|
||||
</$Link>
|
||||
),
|
||||
TRADING_BLOGPOST: (
|
||||
// todo: update localization to flip the two
|
||||
DOS_BLOGPOST: (
|
||||
<$Link
|
||||
href="https://dydx.exchange/blog/v4-full-trading"
|
||||
target="_blank"
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Squid } from '@0xsquid/sdk';
|
||||
|
||||
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
|
||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||
|
||||
export const NATIVE_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
|
||||
|
||||
export const STATUS_ERROR_GRACE_PERIOD = 300_000;
|
||||
|
||||
const useSquidContext = () => {
|
||||
const selectedNetwork = useSelector(getSelectedNetwork);
|
||||
const [_, setInitialized] = useState(false);
|
||||
|
||||
const initializeClient = async () => {
|
||||
setInitialized(false);
|
||||
if (!squid) return;
|
||||
await squid.init();
|
||||
setInitialized(true);
|
||||
};
|
||||
|
||||
const squid = useMemo(
|
||||
() =>
|
||||
new Squid({
|
||||
baseUrl: ENVIRONMENT_CONFIG_MAP[selectedNetwork]?.endpoints['0xsquid'],
|
||||
integratorId: ENVIRONMENT_CONFIG_MAP[selectedNetwork]?.squidIntegratorId,
|
||||
}),
|
||||
[selectedNetwork]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (squid) {
|
||||
initializeClient();
|
||||
}
|
||||
}, [squid]);
|
||||
|
||||
return squid;
|
||||
};
|
||||
|
||||
type SquidContextType = ReturnType<typeof useSquidContext>;
|
||||
const SquidContext = createContext<SquidContextType | undefined>(undefined);
|
||||
SquidContext.displayName = '0xSquid';
|
||||
|
||||
export const SquidProvider = ({ ...props }) => (
|
||||
<SquidContext.Provider value={useSquidContext()} {...props} />
|
||||
);
|
||||
|
||||
export const useSquid = () => useContext(SquidContext);
|
||||
@@ -31,7 +31,6 @@ import { log } from '@/lib/telemetry';
|
||||
import { useAccounts } from './useAccounts';
|
||||
import { useTokenConfigs } from './useTokenConfigs';
|
||||
import { useDydxClient } from './useDydxClient';
|
||||
import { hashFromTx } from '@/lib/hashfromTx';
|
||||
|
||||
type SubaccountContextType = ReturnType<typeof useSubaccountContext>;
|
||||
const SubaccountContext = createContext<SubaccountContextType>({} as SubaccountContextType);
|
||||
@@ -294,29 +293,12 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
||||
);
|
||||
|
||||
const sendSquidWithdraw = useCallback(
|
||||
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();
|
||||
}
|
||||
|
||||
async (amount: number, payload: string) => {
|
||||
if (!subaccountClient) {
|
||||
return;
|
||||
}
|
||||
const tx = await sendSquidWithdrawFromSubaccount({ subaccountClient, amount, payload });
|
||||
return hashFromTx(tx?.hash);
|
||||
|
||||
return await sendSquidWithdrawFromSubaccount({ subaccountClient, amount, payload });
|
||||
},
|
||||
[subaccountClient, sendSquidWithdrawFromSubaccount]
|
||||
);
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import queryString from 'query-string';
|
||||
|
||||
class TestFlags {
|
||||
public queryParams: queryString.ParsedQuery<string>;
|
||||
|
||||
constructor() {
|
||||
this.queryParams = {};
|
||||
}
|
||||
|
||||
setQueryParams(flags: queryString.ParsedQuery<string>) {
|
||||
this.queryParams = flags;
|
||||
}
|
||||
|
||||
get displayInitializingMarkets() {
|
||||
return !!this.queryParams.displayInitializingMarkets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description We are temporarily displaying a subset of ACTIVE markets, this flag will allow you to see all ACTIVE markets
|
||||
*/
|
||||
get displayAllMarkets() {
|
||||
return !!this.queryParams.displayAllMarkets;
|
||||
}
|
||||
}
|
||||
|
||||
export const testFlags = new TestFlags();
|
||||
|
||||
const useTestFlagsContext = () => {
|
||||
const [queryParams, setQueryParams] = useState<queryString.ParsedQuery<string>>({});
|
||||
const location = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
const parsedQueryParams = queryString.parse(location.search.substring(-1));
|
||||
testFlags.setQueryParams(parsedQueryParams);
|
||||
setQueryParams(parsedQueryParams);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
...queryParams,
|
||||
};
|
||||
};
|
||||
|
||||
type TestFlagsContextType = ReturnType<typeof useTestFlagsContext>;
|
||||
const TestFlagsContext = createContext<TestFlagsContextType>({} as TestFlagsContextType);
|
||||
TestFlagsContext.displayName = 'TestFlags';
|
||||
|
||||
export const TestFlagsProvider = ({ ...props }) => (
|
||||
<TestFlagsContext.Provider value={useTestFlagsContext()} {...props} />
|
||||
);
|
||||
|
||||
export const useTestFlags = () => useContext(TestFlagsContext);
|
||||
@@ -11,19 +11,22 @@ export const useTradeFormInputs = () => {
|
||||
const { limitPriceInput, triggerPriceInput, trailingPercentInput } = tradeFormInputValues;
|
||||
|
||||
useEffect(() => {
|
||||
const floatValue = parseFloat(triggerPriceInput);
|
||||
abacusStateManager.setTradeValue({
|
||||
value: triggerPriceInput,
|
||||
value: floatValue,
|
||||
field: TradeInputField.triggerPrice,
|
||||
});
|
||||
}, [triggerPriceInput]);
|
||||
|
||||
useEffect(() => {
|
||||
abacusStateManager.setTradeValue({ value: limitPriceInput, field: TradeInputField.limitPrice });
|
||||
const floatValue = parseFloat(limitPriceInput);
|
||||
abacusStateManager.setTradeValue({ value: floatValue, field: TradeInputField.limitPrice });
|
||||
}, [limitPriceInput]);
|
||||
|
||||
useEffect(() => {
|
||||
const floatValue = parseFloat(trailingPercentInput);
|
||||
abacusStateManager.setTradeValue({
|
||||
value: trailingPercentInput,
|
||||
value: floatValue,
|
||||
field: TradeInputField.trailingPercent,
|
||||
});
|
||||
}, [trailingPercentInput]);
|
||||
|
||||
@@ -104,13 +104,12 @@ export const useWalletConnection = () => {
|
||||
[walletConnectConfig, walletType, walletConnectionType]
|
||||
);
|
||||
|
||||
const { connectAsync: connectWagmi } = useConnectWagmi();
|
||||
const { connectAsync: connectWagmi } = useConnectWagmi({ connector: wagmiConnector })
|
||||
const { suggestAndConnect: connectGraz } = useConnectGraz();
|
||||
|
||||
const connectWallet = useCallback(
|
||||
async ({ walletType }: { walletType: WalletType }) => {
|
||||
const walletConnection = getWalletConnection({ walletType });
|
||||
console.log({ walletConnection });
|
||||
|
||||
try {
|
||||
if (!walletConnection) {
|
||||
@@ -134,17 +133,13 @@ export const useWalletConnection = () => {
|
||||
}
|
||||
} else {
|
||||
if (!isConnectedWagmi) {
|
||||
const connector = resolveWagmiConnector({
|
||||
walletType,
|
||||
walletConnection,
|
||||
walletConnectConfig,
|
||||
await connectWagmi({
|
||||
connector: resolveWagmiConnector({
|
||||
walletType,
|
||||
walletConnection,
|
||||
walletConnectConfig,
|
||||
}),
|
||||
});
|
||||
|
||||
if (connector) {
|
||||
await connectWagmi({
|
||||
connector,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -161,7 +156,7 @@ export const useWalletConnection = () => {
|
||||
walletConnectionType: walletConnection.type,
|
||||
};
|
||||
},
|
||||
[isConnectedGraz, signerGraz, isConnectedWagmi, signerWagmi, walletConnectConfig]
|
||||
[isConnectedGraz, signerGraz, isConnectedWagmi, signerWagmi]
|
||||
);
|
||||
|
||||
const disconnectWallet = useCallback(async () => {
|
||||
@@ -209,9 +204,10 @@ export const useWalletConnection = () => {
|
||||
})();
|
||||
}, [selectedWalletType, signerWagmi, signerGraz]);
|
||||
|
||||
const selectWalletType = (walletType: WalletType | undefined) => {
|
||||
const selectWalletType = async (walletType: WalletType | undefined) => {
|
||||
if (selectedWalletType) {
|
||||
setSelectedWalletType(undefined);
|
||||
await new Promise(requestAnimationFrame);
|
||||
}
|
||||
|
||||
setSelectedWalletType(walletType);
|
||||
|
||||
@@ -32,7 +32,6 @@ export { default as GiftboxIcon } from './giftbox.svg';
|
||||
export { default as HelpCircleIcon } from './help-circle.svg';
|
||||
export { default as HideIcon } from './hide.svg';
|
||||
export { default as HistoryIcon } from './history.svg';
|
||||
export { default as LeaderboardIcon } from './leaderboard.svg';
|
||||
export { default as LinkOutIcon } from './link-out.svg';
|
||||
export { default as LockIcon } from './lock.svg';
|
||||
export { default as LogoShortIcon } from './logo-short';
|
||||
@@ -44,7 +43,6 @@ export { default as OrderbookIcon } from './orderbook.svg';
|
||||
export { default as OverviewIcon } from './overview.svg';
|
||||
export { default as PencilIcon } from './pencil.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 PositionsIcon } from './positions.svg';
|
||||
export { default as PriceChartIcon } from './price-chart.svg';
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg width="13" height="14" viewBox="0 0 13 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.0996094 2.60156C0.0996094 2.07113 0.310323 1.56242 0.685396 1.18735C1.06047 0.812276 1.56918 0.601563 2.09961 0.601562H10.8996C11.43 0.601563 11.9387 0.812276 12.3138 1.18735C12.6889 1.56242 12.8996 2.07113 12.8996 2.60156C12.8996 3.132 12.6889 3.6407 12.3138 4.01578C11.9387 4.39085 11.43 4.60156 10.8996 4.60156H2.09961C1.56918 4.60156 1.06047 4.39085 0.685396 4.01578C0.310323 3.6407 0.0996094 3.132 0.0996094 2.60156ZM0.699609 6.26796C0.540479 6.26796 0.387867 6.33118 0.275345 6.4437C0.162823 6.55622 0.0996094 6.70883 0.0996094 6.86796C0.0996094 7.02709 0.162823 7.1797 0.275345 7.29223C0.387867 7.40475 0.540479 7.46796 0.699609 7.46796H12.2996C12.4587 7.46796 12.6114 7.40475 12.7239 7.29223C12.8364 7.1797 12.8996 7.02709 12.8996 6.86796C12.8996 6.70883 12.8364 6.55622 12.7239 6.4437C12.6114 6.33118 12.4587 6.26796 12.2996 6.26796H0.699609ZM0.699609 9.13196C0.540479 9.13196 0.387867 9.19518 0.275345 9.3077C0.162823 9.42022 0.0996094 9.57283 0.0996094 9.73196C0.0996094 9.89109 0.162823 10.0437 0.275345 10.1562C0.387867 10.2687 0.540479 10.332 0.699609 10.332H12.2996C12.4587 10.332 12.6114 10.2687 12.7239 10.1562C12.8364 10.0437 12.8996 9.89109 12.8996 9.73196C12.8996 9.57283 12.8364 9.42022 12.7239 9.3077C12.6114 9.19518 12.4587 9.13196 12.2996 9.13196H0.699609ZM0.699609 12.0016C0.540479 12.0016 0.387867 12.0648 0.275345 12.1773C0.162823 12.2898 0.0996094 12.4424 0.0996094 12.6016C0.0996094 12.7607 0.162823 12.9133 0.275345 13.0258C0.387867 13.1383 0.540479 13.2016 0.699609 13.2016H12.2996C12.4587 13.2016 12.6114 13.1383 12.7239 13.0258C12.8364 12.9133 12.8996 12.7607 12.8996 12.6016C12.8996 12.4424 12.8364 12.2898 12.7239 12.1773C12.6114 12.0648 12.4587 12.0016 12.2996 12.0016H0.699609Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1 +0,0 @@
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 731 B |
@@ -11,7 +11,6 @@ import { DepositDialog } from '@/views/dialogs/DepositDialog';
|
||||
import { DisconnectDialog } from '@/views/dialogs/DisconnectDialog';
|
||||
import { ExchangeOfflineDialog } from '@/views/dialogs/ExchangeOfflineDialog';
|
||||
import { HelpDialog } from '@/views/dialogs/HelpDialog';
|
||||
import { ExternalLinkDialog } from '@/views/dialogs/ExternalLinkDialog';
|
||||
import { ExternalNavKeplrDialog } from '@/views/dialogs/ExternalNavKeplrDialog';
|
||||
import { MnemonicExportDialog } from '@/views/dialogs/MnemonicExportDialog';
|
||||
import { MobileSignInDialog } from '@/views/dialogs/MobileSignInDialog';
|
||||
@@ -54,7 +53,6 @@ export const DialogManager = () => {
|
||||
[DialogTypes.FillDetails]: <FillDetailsDialog {...modalProps} />,
|
||||
[DialogTypes.Help]: <HelpDialog {...modalProps} />,
|
||||
[DialogTypes.ExternalNavKeplr]: <ExternalNavKeplrDialog {...modalProps} />,
|
||||
[DialogTypes.ExternalLink]: <ExternalLinkDialog {...modalProps} />,
|
||||
[DialogTypes.MnemonicExport]: <MnemonicExportDialog {...modalProps} />,
|
||||
[DialogTypes.MobileSignIn]: <MobileSignInDialog {...modalProps} />,
|
||||
[DialogTypes.Onboarding]: <OnboardingDialog {...modalProps} />,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { ButtonShape } from '@/constants/buttons';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
@@ -8,10 +8,6 @@ import { STRING_KEYS } from '@/constants/localization';
|
||||
import { AppRoute } from '@/constants/routes';
|
||||
import { LogoShortIcon, BellStrokeIcon } from '@/icons';
|
||||
|
||||
import { headerMixins } from '@/styles/headerMixins';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import breakpoints from '@/styles/breakpoints';
|
||||
|
||||
import { useTokenConfigs, useStringGetter, useURLConfigs } from '@/hooks';
|
||||
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
@@ -25,7 +21,10 @@ import { NotificationsMenu } from '@/views/menus/NotificationsMenu';
|
||||
import { LanguageSelector } from '@/views/menus/LanguageSelector';
|
||||
|
||||
import { openDialog } from '@/state/dialogs';
|
||||
import { getHasSeenLaunchIncentives } from '@/state/configsSelectors';
|
||||
|
||||
import { headerMixins } from '@/styles/headerMixins';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import breakpoints from '@/styles/breakpoints';
|
||||
|
||||
export const HeaderDesktop = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
@@ -33,8 +32,6 @@ export const HeaderDesktop = () => {
|
||||
const dispatch = useDispatch();
|
||||
const { chainTokenLabel } = useTokenConfigs();
|
||||
|
||||
const hasSeenLaunchIncentives = useSelector(getHasSeenLaunchIncentives);
|
||||
|
||||
const navItems = [
|
||||
{
|
||||
group: 'navigation',
|
||||
@@ -58,7 +55,6 @@ export const HeaderDesktop = () => {
|
||||
value: chainTokenLabel,
|
||||
label: chainTokenLabel,
|
||||
href: `/${chainTokenLabel}`,
|
||||
slotAfter: !hasSeenLaunchIncentives && <Styled.UnreadIndicator />,
|
||||
},
|
||||
{
|
||||
value: 'MORE',
|
||||
@@ -242,10 +238,3 @@ Styled.IconButton = styled(IconButton)<{ size?: string }>`
|
||||
--button-icon-size: 1rem;
|
||||
--button-padding: 0 0.5em;
|
||||
`;
|
||||
|
||||
Styled.UnreadIndicator = styled.div`
|
||||
width: 0.4375rem;
|
||||
height: 0.4375rem;
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-accent);
|
||||
`;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import Abacus, { type Nullable } from '@dydxprotocol/v4-abacus';
|
||||
import Long from 'long';
|
||||
import type { IndexedTx } from '@cosmjs/stargate';
|
||||
import { GAS_MULTIPLIER, encodeJson } from '@dydxprotocol/v4-client-js';
|
||||
import { EncodeObject } from '@cosmjs/proto-signing';
|
||||
import { encodeJson } from '@dydxprotocol/v4-client-js';
|
||||
|
||||
import {
|
||||
CompositeClient,
|
||||
@@ -10,7 +9,6 @@ import {
|
||||
type LocalWallet,
|
||||
Network,
|
||||
NetworkOptimizer,
|
||||
NobleClient,
|
||||
SubaccountClient,
|
||||
ValidatorConfig,
|
||||
OrderType,
|
||||
@@ -42,14 +40,11 @@ import { openDialog } from '@/state/dialogs';
|
||||
import { StatefulOrderError } from '../errors';
|
||||
import { bytesToBigInt } from '../numbers';
|
||||
import { log } from '../telemetry';
|
||||
import { hashFromTx } from '../hashfromTx';
|
||||
|
||||
class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
||||
private compositeClient: CompositeClient | undefined;
|
||||
private nobleClient: NobleClient | undefined;
|
||||
private store: RootStore | undefined;
|
||||
private localWallet: LocalWallet | undefined;
|
||||
private nobleWallet: LocalWallet | undefined;
|
||||
|
||||
constructor() {
|
||||
this.compositeClient = undefined;
|
||||
@@ -64,17 +59,6 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
||||
this.localWallet = localWallet;
|
||||
}
|
||||
|
||||
setNobleWallet(nobleWallet: LocalWallet) {
|
||||
try {
|
||||
this.nobleWallet = nobleWallet;
|
||||
if (this.nobleClient) {
|
||||
this.nobleClient.connect(nobleWallet);
|
||||
}
|
||||
} catch (e) {
|
||||
log('DydxChainTransactions/setNobleWallet', e);
|
||||
}
|
||||
}
|
||||
|
||||
async connectNetwork(
|
||||
paramsInJson: Nullable<string>,
|
||||
callback: (p0: Nullable<string>) => void
|
||||
@@ -86,7 +70,6 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
||||
websocketUrl,
|
||||
validatorUrl,
|
||||
chainId,
|
||||
nobleValidatorUrl,
|
||||
USDC_DENOM,
|
||||
USDC_DECIMALS,
|
||||
USDC_GAS_DENOM,
|
||||
@@ -118,15 +101,6 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
||||
|
||||
this.compositeClient = compositeClient;
|
||||
|
||||
try {
|
||||
if (nobleValidatorUrl) {
|
||||
this.nobleClient = new NobleClient(nobleValidatorUrl);
|
||||
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
|
||||
const customEvent = new CustomEvent('abacus:connectNetwork', {
|
||||
detail: parsedParams,
|
||||
@@ -212,8 +186,8 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
||||
timeInForce as OrderTimeInForce,
|
||||
goodTilTimeInSeconds ?? 0,
|
||||
execution as OrderExecution,
|
||||
postOnly ?? undefined,
|
||||
reduceOnly ?? undefined,
|
||||
postOnly,
|
||||
reduceOnly,
|
||||
triggerPrice ?? undefined
|
||||
);
|
||||
|
||||
@@ -351,122 +325,6 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
async sendNobleIBC(params: { msgTypeUrl: string; msg: any }): Promise<string> {
|
||||
if (!this.nobleClient?.isConnected) {
|
||||
throw new Error('Missing nobleClient or localWallet');
|
||||
}
|
||||
|
||||
try {
|
||||
const ibcMsg = {
|
||||
typeUrl: params.msgTypeUrl, // '/ibc.applications.transfer.v1.MsgTransfer',
|
||||
value: params.msg,
|
||||
};
|
||||
const fee = await this.nobleClient.simulateTransaction([ibcMsg]);
|
||||
|
||||
// take out fee from amount before sweeping
|
||||
const amount =
|
||||
parseInt(ibcMsg.value.token.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.token.amount = amount.toString();
|
||||
const tx = await this.nobleClient.send([ibcMsg]);
|
||||
|
||||
const parsedTx = this.parseToPrimitives(tx);
|
||||
|
||||
return JSON.stringify(parsedTx);
|
||||
} catch (error) {
|
||||
log('DydxChainTransactions/sendNobleIBC', error);
|
||||
|
||||
return JSON.stringify({
|
||||
error,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
type: TransactionTypes,
|
||||
paramsInJson: Abacus.Nullable<string>,
|
||||
@@ -496,22 +354,6 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
||||
callback(result);
|
||||
break;
|
||||
}
|
||||
case TransactionType.SendNobleIBC: {
|
||||
const result = await this.sendNobleIBC(params);
|
||||
callback(result);
|
||||
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: {
|
||||
break;
|
||||
}
|
||||
@@ -599,13 +441,6 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
||||
const parseDelegations = this.parseToPrimitives(delegations);
|
||||
callback(JSON.stringify(parseDelegations));
|
||||
break;
|
||||
case QueryType.GetNobleBalance:
|
||||
if (this.nobleClient?.isConnected) {
|
||||
const nobleBalance = await this.nobleClient.getAccountBalance('uusdc');
|
||||
const parsedNobleBalance = this.parseToPrimitives(nobleBalance);
|
||||
callback(JSON.stringify(parsedNobleBalance));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -26,15 +26,13 @@ import {
|
||||
} from '@/constants/abacus';
|
||||
|
||||
import { DEFAULT_MARKETID } from '@/constants/markets';
|
||||
import { CURRENT_ABACUS_DEPLOYMENT, type DydxNetwork, isMainnet } from '@/constants/networks';
|
||||
import { CURRENT_ABACUS_DEPLOYMENT, type DydxNetwork } from '@/constants/networks';
|
||||
import { CLEARED_SIZE_INPUTS, CLEARED_TRADE_INPUTS } from '@/constants/trade';
|
||||
|
||||
import type { RootStore } from '@/state/_store';
|
||||
import { setTradeFormInputs } from '@/state/inputs';
|
||||
import { getInputTradeOptions, getTransferInputs } from '@/state/inputsSelectors';
|
||||
|
||||
import { testFlags } from '@/lib/testFlags';
|
||||
|
||||
import AbacusRest from './rest';
|
||||
import AbacusAnalytics from './analytics';
|
||||
import AbacusWebsocket from './websocket';
|
||||
@@ -83,13 +81,10 @@ class AbacusStateManager {
|
||||
this.abacusFormatter
|
||||
);
|
||||
|
||||
const appConfigs = AbacusAppConfig.Companion.forWeb;
|
||||
appConfigs.squidVersion = AbacusAppConfig.SquidVersion.V2;
|
||||
|
||||
this.stateManager = new AsyncAbacusStateManager(
|
||||
'',
|
||||
CURRENT_ABACUS_DEPLOYMENT,
|
||||
appConfigs,
|
||||
AbacusAppConfig.Companion.forWeb,
|
||||
ioImplementations,
|
||||
uiImplementations,
|
||||
// @ts-ignore
|
||||
@@ -185,12 +180,6 @@ class AbacusStateManager {
|
||||
}
|
||||
};
|
||||
|
||||
setNobleWallet = (nobleWallet?: LocalWallet) => {
|
||||
if (nobleWallet) {
|
||||
this.chainTransactions.setNobleWallet(nobleWallet);
|
||||
}
|
||||
};
|
||||
|
||||
setTransfersSourceAddress = (evmAddress: string) => {
|
||||
this.stateManager.sourceAddress = evmAddress;
|
||||
};
|
||||
@@ -234,6 +223,18 @@ class AbacusStateManager {
|
||||
this.abacusFormatter.setLocaleSeparators({ group, decimal });
|
||||
};
|
||||
|
||||
setTransferStatus = ({
|
||||
hash,
|
||||
fromChainId,
|
||||
toChainId,
|
||||
}: {
|
||||
hash: string;
|
||||
fromChainId?: string;
|
||||
toChainId?: string;
|
||||
}) => {
|
||||
this.stateManager.transferStatus(hash, fromChainId, toChainId);
|
||||
};
|
||||
|
||||
// ------ Transactions ------ //
|
||||
|
||||
placeOrder = (
|
||||
@@ -261,14 +262,6 @@ class AbacusStateManager {
|
||||
) => void
|
||||
) => this.stateManager.cancelOrder(orderId, callback);
|
||||
|
||||
cctpWithdraw = (
|
||||
callback: (
|
||||
success: boolean,
|
||||
parsingError: Nullable<ParsingError>,
|
||||
data: string,
|
||||
) => void
|
||||
): void => this.stateManager.commitCCTPWithdraw(callback);
|
||||
|
||||
// ------ Utils ------ //
|
||||
getHistoricalPnlPeriod = (): Nullable<HistoricalPnlPeriods> =>
|
||||
this.stateManager.historicalPnlPeriod;
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from '@/constants/websocket';
|
||||
|
||||
import { lastSuccessfulWebsocketRequestByOrigin } from '@/hooks/useAnalytics';
|
||||
import { testFlags } from '@/lib/testFlags';
|
||||
import { testFlags } from '@/hooks/useTestFlags';
|
||||
|
||||
import { subscriptionsByChannelId } from '@/lib/tradingView/dydxfeed/cache';
|
||||
import { mapCandle } from '@/lib/tradingView/utils';
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export const hashFromTx = (
|
||||
txHash: string | Uint8Array
|
||||
): string => `0x${Buffer.from(txHash).toString('hex')}`;
|
||||
@@ -1,48 +0,0 @@
|
||||
import { isMainnet } from '@/constants/networks';
|
||||
import { GetStatus, StatusResponse } from '@0xsquid/sdk';
|
||||
|
||||
export const NATIVE_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
|
||||
|
||||
export const STATUS_ERROR_GRACE_PERIOD = 300_000;
|
||||
|
||||
const getSquidStatusUrl = (isV2: boolean) => {
|
||||
if (isV2) {
|
||||
return isMainnet
|
||||
? 'https://v2.api.squidrouter.com/v2/status'
|
||||
: 'https://testnet.v2.api.squidrouter.com/v2/status';
|
||||
}
|
||||
return isMainnet
|
||||
? 'https://api.squidrouter.com/v1/status'
|
||||
: 'https://testnet.api.squidrouter.com/v1/status';
|
||||
};
|
||||
|
||||
export const fetchSquidStatus = async (
|
||||
params: GetStatus,
|
||||
isV2?: boolean,
|
||||
integratorId?: string
|
||||
): Promise<StatusResponse> => {
|
||||
const parsedParams: { [key: string]: string } = {
|
||||
transactionId: params.transactionId,
|
||||
fromChainId: String(params.fromChainId),
|
||||
toChainId: String(params.toChainId),
|
||||
};
|
||||
if (isV2) parsedParams.bridgeType = 'cctp';
|
||||
const url = `${getSquidStatusUrl(!!isV2)}?${new URLSearchParams(parsedParams).toString()}`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
"x-integrator-id": integratorId || 'dYdX-api'
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
};
|
||||
|
||||
export const getNobleChainId = () => {
|
||||
return isMainnet ? 'noble-1' : 'grand-1';
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
class TestFlags {
|
||||
public queryParams: { [key: string]: string };
|
||||
|
||||
constructor() {
|
||||
this.queryParams = {};
|
||||
const hash = window.location.hash;
|
||||
const queryIndex = hash.indexOf('?');
|
||||
if (queryIndex === -1) return;
|
||||
|
||||
const queryParamsString = hash.substring(queryIndex + 1);
|
||||
const params = new URLSearchParams(queryParamsString);
|
||||
|
||||
for (const [key, value] of params) {
|
||||
this.queryParams[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
get displayInitializingMarkets() {
|
||||
return !!this.queryParams.displayInitializingMarkets;
|
||||
}
|
||||
|
||||
get withCCTP() {
|
||||
return !!this.queryParams.withCCTP;
|
||||
}
|
||||
}
|
||||
|
||||
export const testFlags = new TestFlags();
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createConfig, createConnector, http, usePublicClient } from 'wagmi';
|
||||
import { createConfig, configureChains, mainnet, Chain, Connector, usePublicClient } from 'wagmi';
|
||||
import { goerli } from 'wagmi/chains';
|
||||
|
||||
import {
|
||||
type Chain,
|
||||
arbitrum,
|
||||
arbitrumGoerli,
|
||||
avalanche,
|
||||
@@ -25,33 +26,34 @@ import {
|
||||
fantomTestnet,
|
||||
celo,
|
||||
celoAlfajores,
|
||||
mainnet,
|
||||
goerli,
|
||||
sepolia,
|
||||
} from 'wagmi/chains';
|
||||
} from 'viem/chains';
|
||||
|
||||
import { coinbaseWallet, injected, metaMask, walletConnect } from 'wagmi/connectors';
|
||||
import type { ExternalProvider } from '@ethersproject/providers';
|
||||
import { alchemyProvider } from 'wagmi/providers/alchemy';
|
||||
import { jsonRpcProvider } from 'wagmi/providers/jsonRpc';
|
||||
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 {
|
||||
type WalletConnectConfig,
|
||||
type WalletConnection,
|
||||
WalletConnectionType,
|
||||
WalletType,
|
||||
WALLET_CONNECTION_TYPES,
|
||||
type WalletType,
|
||||
walletConnectionTypes,
|
||||
wallets,
|
||||
WALLET_CONNECT_EXPLORER_RECOMMENDED_IDS,
|
||||
WalletConfig,
|
||||
} from '@/constants/wallets';
|
||||
|
||||
import { isTruthy } from './isTruthy';
|
||||
import { log } from './telemetry';
|
||||
|
||||
// Config
|
||||
const WAGMI_SUPPORTED_CHAINS: Parameters<typeof createConfig>[0]['chains'] = [
|
||||
|
||||
export const WAGMI_SUPPORTED_CHAINS: Chain[] = [
|
||||
mainnet,
|
||||
goerli,
|
||||
sepolia,
|
||||
|
||||
arbitrum,
|
||||
arbitrumGoerli,
|
||||
avalanche,
|
||||
@@ -78,104 +80,118 @@ const WAGMI_SUPPORTED_CHAINS: Parameters<typeof createConfig>[0]['chains'] = [
|
||||
celoAlfajores,
|
||||
];
|
||||
|
||||
export const config = createConfig({
|
||||
chains: WAGMI_SUPPORTED_CHAINS,
|
||||
transports: Object.fromEntries(
|
||||
Object.values(WAGMI_SUPPORTED_CHAINS).map(({ id }) => [id, http()])
|
||||
),
|
||||
});
|
||||
const { chains, publicClient, webSocketPublicClient } = configureChains(
|
||||
WAGMI_SUPPORTED_CHAINS,
|
||||
[
|
||||
import.meta.env.VITE_ALCHEMY_API_KEY &&
|
||||
alchemyProvider({ apiKey: import.meta.env.VITE_ALCHEMY_API_KEY }),
|
||||
jsonRpcProvider({
|
||||
rpc: (chain) => ({ http: chain.rpcUrls.default.http[0] }),
|
||||
}),
|
||||
publicProvider(),
|
||||
].filter(isTruthy)
|
||||
);
|
||||
|
||||
const createdInjectedConnectorWithProvider = ({ provider }: { provider: ExternalProvider }) => {
|
||||
console.log(provider);
|
||||
return injected({
|
||||
target() {
|
||||
return {
|
||||
id: 'windowProvider',
|
||||
name: 'Injected',
|
||||
provider,
|
||||
};
|
||||
},
|
||||
const injectedConnectorOptions = {
|
||||
chains,
|
||||
options: {
|
||||
name: 'Injected',
|
||||
shimDisconnect: true,
|
||||
});
|
||||
shimChainChangedDisconnect: false,
|
||||
},
|
||||
};
|
||||
|
||||
const getConnector = ({
|
||||
walletConnectionType,
|
||||
walletConnectConfig,
|
||||
}: {
|
||||
walletConnectionType: WalletConnectionType;
|
||||
walletConnectConfig: WalletConnectConfig;
|
||||
}) => {
|
||||
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;
|
||||
}
|
||||
}
|
||||
type WalletConnectConfig = {
|
||||
client: {
|
||||
name: string;
|
||||
description: string;
|
||||
iconUrl: string;
|
||||
};
|
||||
v2: {
|
||||
projectId: string;
|
||||
};
|
||||
};
|
||||
|
||||
const getConnectorInfoForWc2 = ({
|
||||
walletConnectId,
|
||||
walletConnectConfig,
|
||||
}: {
|
||||
walletConnectId?: string;
|
||||
walletConnectConfig: WalletConnectConfig;
|
||||
}) => {
|
||||
const explorerRecommendedWalletIds = walletConnectId
|
||||
? [walletConnectId]
|
||||
: WALLET_CONNECT_EXPLORER_RECOMMENDED_IDS;
|
||||
|
||||
return {
|
||||
projectId: walletConnectConfig.v2.projectId,
|
||||
const getWalletconnect2ConnectorOptions = (
|
||||
config: WalletConnectConfig
|
||||
): ConstructorParameters<typeof WalletConnectConnector>[0] => ({
|
||||
chains,
|
||||
options: {
|
||||
projectId: config.v2.projectId,
|
||||
metadata: {
|
||||
name: walletConnectConfig.client.name,
|
||||
description: walletConnectConfig.client.description,
|
||||
name: config.client.name,
|
||||
description: config.client.description,
|
||||
url: import.meta.env.VITE_BASE_URL,
|
||||
icons: [walletConnectConfig.client.iconUrl],
|
||||
icons: [config.client.iconUrl],
|
||||
},
|
||||
showQrModal: true,
|
||||
qrModalOptions: {
|
||||
themeMode: 'dark' as const,
|
||||
themeVariables: {
|
||||
'--wcm-accent-color': 'var(--color-accent)',
|
||||
'--wcm-accent-color': '#5973fe',
|
||||
'--wcm-font-family': 'var(--fontFamily-base)',
|
||||
'--wcm-background-color': 'var(--color-accent)',
|
||||
},
|
||||
explorerRecommendedWalletIds,
|
||||
explorerRecommendedWalletIds: WALLET_CONNECT_EXPLORER_RECOMMENDED_IDS,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
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
|
||||
|
||||
export const resolveWagmiConnector = ({
|
||||
walletType,
|
||||
walletConnection,
|
||||
@@ -186,23 +202,13 @@ export const resolveWagmiConnector = ({
|
||||
walletConnectConfig: WalletConnectConfig;
|
||||
}) => {
|
||||
const walletConfig = wallets[walletType];
|
||||
const walletConnectionConfig = walletConnectionTypes[walletConnection.type];
|
||||
|
||||
if (walletConnection.type === WalletConnectionType.InjectedEip1193 && walletConnection.provider) {
|
||||
if (walletType === WalletType.MetaMask) {
|
||||
return metaMask();
|
||||
}
|
||||
return createdInjectedConnectorWithProvider({ provider: walletConnection.provider });
|
||||
} else if (
|
||||
walletConnection.type === WalletConnectionType.WalletConnect2 &&
|
||||
walletConfig.walletconnect2Id
|
||||
) {
|
||||
return walletConnect(
|
||||
getConnectorInfoForWc2({
|
||||
walletConnectId: walletConfig.walletconnect2Id,
|
||||
walletConnectConfig,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return getConnector({ walletConnectionType: walletConnection.type, walletConnectConfig });
|
||||
}
|
||||
return walletConnection.type === WalletConnectionType.InjectedEip1193 && walletConnection.provider
|
||||
? createInjectedConnectorWithProvider(walletConnection.provider)
|
||||
: walletConnection.type === WalletConnectionType.WalletConnect2 && walletConfig.walletconnect2Id
|
||||
? createWalletConnect2ConnectorWithId(walletConfig.walletconnect2Id, walletConnectConfig)
|
||||
: getConnectors(walletConnectConfig).find(
|
||||
({ id }: { id: string }) => id === walletConnectionConfig.wagmiConnectorId
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { DialogPlacement } from '@/components/Dialog';
|
||||
import { NotificationsMenu } from '@/views/menus/NotificationsMenu';
|
||||
|
||||
const AlertsPage = () => <NotificationsMenu placement={DialogPlacement.Inline} />;
|
||||
|
||||
export default AlertsPage;
|
||||
export const AlertsPage = () => <NotificationsMenu placement={DialogPlacement.Inline} />;
|
||||
|
||||
@@ -2,7 +2,7 @@ import styled, { AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { articleMixins } from '@/styles/articleMixins';
|
||||
|
||||
const PrivacyPolicyPage = () => (
|
||||
export const PrivacyPolicyPage = () => (
|
||||
<Styled.Article>
|
||||
<header>
|
||||
<h1>Privacy Policy</h1>
|
||||
@@ -10,8 +10,6 @@ const PrivacyPolicyPage = () => (
|
||||
</Styled.Article>
|
||||
);
|
||||
|
||||
export default PrivacyPolicyPage;
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Article = styled.article`
|
||||
|
||||
@@ -2,7 +2,7 @@ import styled, { AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { articleMixins } from '@/styles/articleMixins';
|
||||
|
||||
const TermsOfUsePage = () => (
|
||||
export const TermsOfUsePage = () => (
|
||||
<Styled.Article>
|
||||
<header>
|
||||
<h1>Sample Terms of Use</h1>
|
||||
@@ -1076,8 +1076,6 @@ const TermsOfUsePage = () => (
|
||||
</Styled.Article>
|
||||
);
|
||||
|
||||
export default TermsOfUsePage;
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Article = styled.article`
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
|
||||
import { OnboardingState } from '@/constants/account';
|
||||
import { ButtonAction } from '@/constants/buttons';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { HistoryRoute, PortfolioRoute } from '@/constants/routes';
|
||||
|
||||
import { useAccountBalance, useBreakpoints, useDocumentTitle, useStringGetter } from '@/hooks';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { FillsTable, FillsTableColumnKey } from '@/views/tables/FillsTable';
|
||||
import { FundingPaymentsTable } from '@/views/tables/FundingPaymentsTable';
|
||||
import { TransferHistoryTable } from '@/views/tables/TransferHistoryTable';
|
||||
import { Button } from '@/components/Button';
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
import { NavigationMenu } from '@/components/NavigationMenu';
|
||||
import { WithSidebar } from '@/components/WithSidebar';
|
||||
@@ -23,15 +20,15 @@ import { getOnboardingState, getSubaccount } from '@/state/accountSelectors';
|
||||
import { openDialog } from '@/state/dialogs';
|
||||
|
||||
import { PortfolioNavMobile } from './PortfolioNavMobile';
|
||||
import { LoadingSpace } from '@/components/Loading/LoadingSpinner';
|
||||
import { Overview } from './Overview';
|
||||
import { Positions } from './Positions';
|
||||
import { Orders } from './Orders';
|
||||
import { Fees } from './Fees';
|
||||
import { History } from './History';
|
||||
|
||||
const Overview = lazy(() => import('./Overview').then((module) => ({ default: module.Overview })));
|
||||
const Positions = lazy(() =>
|
||||
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 })));
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import { Button } from '@/components/Button';
|
||||
import { ButtonAction } from '@/constants/buttons';
|
||||
|
||||
export default () => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -47,51 +44,49 @@ export default () => {
|
||||
useDocumentTitle(stringGetter({ key: STRING_KEYS.PORTFOLIO }));
|
||||
|
||||
const routesComponent = (
|
||||
<Suspense fallback={<LoadingSpace id="portfolio" />}>
|
||||
<Routes>
|
||||
<Route path={PortfolioRoute.Overview} element={<Overview />} />
|
||||
<Route path={PortfolioRoute.Positions} element={<Positions />} />
|
||||
<Route path={PortfolioRoute.Orders} element={<Orders />} />
|
||||
<Route path={PortfolioRoute.Fees} element={<Fees />} />
|
||||
<Route path={PortfolioRoute.History} element={<History />}>
|
||||
<Route index path="*" element={<Navigate to={HistoryRoute.Trades} />} />
|
||||
<Route
|
||||
path={HistoryRoute.Trades}
|
||||
element={
|
||||
<FillsTable
|
||||
columnKeys={
|
||||
isTablet
|
||||
? [
|
||||
FillsTableColumnKey.Time,
|
||||
FillsTableColumnKey.TypeAmount,
|
||||
FillsTableColumnKey.PriceFee,
|
||||
]
|
||||
: [
|
||||
FillsTableColumnKey.Time,
|
||||
FillsTableColumnKey.Market,
|
||||
FillsTableColumnKey.Side,
|
||||
FillsTableColumnKey.AmountPrice,
|
||||
FillsTableColumnKey.TotalFee,
|
||||
FillsTableColumnKey.Type,
|
||||
FillsTableColumnKey.Liquidity,
|
||||
]
|
||||
}
|
||||
withOuterBorder={isNotTablet}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={HistoryRoute.Transfers}
|
||||
element={<TransferHistoryTable withOuterBorder={isNotTablet} />}
|
||||
/>
|
||||
<Route
|
||||
path={HistoryRoute.Payments}
|
||||
element={<FundingPaymentsTable withOuterBorder={isNotTablet} />}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="*" element={<Navigate to={PortfolioRoute.Overview} replace />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
<Routes>
|
||||
<Route path={PortfolioRoute.Overview} element={<Overview />} />
|
||||
<Route path={PortfolioRoute.Positions} element={<Positions />} />
|
||||
<Route path={PortfolioRoute.Orders} element={<Orders />} />
|
||||
<Route path={PortfolioRoute.Fees} element={<Fees />} />
|
||||
<Route path={PortfolioRoute.History} element={<History />}>
|
||||
<Route index path="*" element={<Navigate to={HistoryRoute.Trades} />} />
|
||||
<Route
|
||||
path={HistoryRoute.Trades}
|
||||
element={
|
||||
<FillsTable
|
||||
columnKeys={
|
||||
isTablet
|
||||
? [
|
||||
FillsTableColumnKey.Time,
|
||||
FillsTableColumnKey.TypeAmount,
|
||||
FillsTableColumnKey.PriceFee,
|
||||
]
|
||||
: [
|
||||
FillsTableColumnKey.Time,
|
||||
FillsTableColumnKey.Market,
|
||||
FillsTableColumnKey.Side,
|
||||
FillsTableColumnKey.AmountPrice,
|
||||
FillsTableColumnKey.TotalFee,
|
||||
FillsTableColumnKey.Type,
|
||||
FillsTableColumnKey.Liquidity,
|
||||
]
|
||||
}
|
||||
withOuterBorder={isNotTablet}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={HistoryRoute.Transfers}
|
||||
element={<TransferHistoryTable withOuterBorder={isNotTablet} />}
|
||||
/>
|
||||
<Route
|
||||
path={HistoryRoute.Payments}
|
||||
element={<FundingPaymentsTable withOuterBorder={isNotTablet} />}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="*" element={<Navigate to={PortfolioRoute.Overview} replace />} />
|
||||
</Routes>
|
||||
);
|
||||
|
||||
return isTablet ? (
|
||||
@@ -196,7 +191,7 @@ Styled.SideBar = styled.div`
|
||||
Styled.Footer = styled.div`
|
||||
${layoutMixins.row}
|
||||
flex-wrap: wrap;
|
||||
|
||||
|
||||
padding: 1rem;
|
||||
|
||||
gap: 0.5rem;
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { ElementType } from 'react';
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import breakpoints from '@/styles/breakpoints';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import { useAccountBalance, useAccounts, useTokenConfigs, useStringGetter } from '@/hooks';
|
||||
|
||||
@@ -117,17 +116,12 @@ const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Panel = styled(Panel)`
|
||||
--panel-paddingX: 1.5rem;
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
--panel-paddingY: 1.5rem;
|
||||
--panel-content-paddingY: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Header = styled.div`
|
||||
${layoutMixins.spacedRow}
|
||||
gap: 1rem;
|
||||
padding: var(--panel-paddingY) var(--panel-paddingX) 0;
|
||||
padding: 1rem 1.5rem 0;
|
||||
`;
|
||||
|
||||
Styled.Title = styled.h3`
|
||||
|
||||
@@ -1,304 +0,0 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { ButtonAction } from '@/constants/buttons';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
import { QueryKeys } from '@/constants/queries';
|
||||
|
||||
import breakpoints from '@/styles/breakpoints';
|
||||
import { useAccounts, useBreakpoints, useStringGetter } from '@/hooks';
|
||||
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { Panel } from '@/components/Panel';
|
||||
import { Button } from '@/components/Button';
|
||||
|
||||
import { Output, OutputType } from '@/components/Output';
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
import { Tag, TagSize } from '@/components/Tag';
|
||||
|
||||
import { markLaunchIncentivesSeen } from '@/state/configs';
|
||||
import { openDialog } from '@/state/dialogs';
|
||||
|
||||
import { log } from '@/lib/telemetry';
|
||||
|
||||
const SEASON_NUMBER = 2;
|
||||
|
||||
export const LaunchIncentivesPanel = () => {
|
||||
const { isNotTablet } = useBreakpoints();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(markLaunchIncentivesSeen());
|
||||
}, []);
|
||||
|
||||
return isNotTablet ? (
|
||||
<Styled.Panel slotHeader={<LaunchIncentivesTitle />} slotRight={<EstimatedRewards />}>
|
||||
<LaunchIncentivesContent />
|
||||
</Styled.Panel>
|
||||
) : (
|
||||
<Styled.Panel>
|
||||
<Styled.Column>
|
||||
<EstimatedRewards />
|
||||
<LaunchIncentivesTitle />
|
||||
<LaunchIncentivesContent />
|
||||
</Styled.Column>
|
||||
</Styled.Panel>
|
||||
);
|
||||
};
|
||||
|
||||
const LaunchIncentivesTitle = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
return (
|
||||
<Styled.Title>
|
||||
{stringGetter({
|
||||
key: STRING_KEYS.LAUNCH_INCENTIVES_TITLE,
|
||||
params: {
|
||||
FOR_V4: <Styled.ForV4>{stringGetter({ key: STRING_KEYS.FOR_V4 })}</Styled.ForV4>,
|
||||
},
|
||||
})}
|
||||
<Styled.NewTag size={TagSize.Medium}>{stringGetter({ key: STRING_KEYS.NEW })}</Styled.NewTag>
|
||||
</Styled.Title>
|
||||
);
|
||||
};
|
||||
|
||||
const EstimatedRewards = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
const { dydxAddress } = useAccounts();
|
||||
|
||||
const { data, isLoading, status, error } = useQuery({
|
||||
enabled: !!dydxAddress,
|
||||
queryKey: [QueryKeys.LAUNCH_INCENTIVES, dydxAddress, SEASON_NUMBER],
|
||||
queryFn: async () => {
|
||||
if (!dydxAddress) return undefined;
|
||||
const resp = await fetch(
|
||||
`https://cloud.chaoslabs.co/query/api/dydx/points/${dydxAddress}?n=${SEASON_NUMBER}`
|
||||
);
|
||||
return (await resp.json())?.incentivePoints;
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (status === 'error') {
|
||||
log('LaunchIncentives/fetchPoints', error);
|
||||
}
|
||||
}, [status]);
|
||||
|
||||
return (
|
||||
<Styled.EstimatedRewardsCard>
|
||||
<Styled.EstimatedRewardsCardContent>
|
||||
<div>
|
||||
<span>{stringGetter({ key: STRING_KEYS.ESTIMATED_REWARDS })}</span>
|
||||
<Styled.Season>
|
||||
{stringGetter({
|
||||
key: STRING_KEYS.LAUNCH_INCENTIVES_SEASON_NUM,
|
||||
params: { SEASON_NUMBER },
|
||||
})}
|
||||
</Styled.Season>
|
||||
</div>
|
||||
|
||||
<Styled.Points>
|
||||
<Output type={OutputType.Number} value={data} isLoading={isLoading} fractionDigits={2} />
|
||||
{data !== undefined && stringGetter({ key: STRING_KEYS.POINTS })}
|
||||
</Styled.Points>
|
||||
</Styled.EstimatedRewardsCardContent>
|
||||
|
||||
<Styled.Image src="/rewards-stars.svg" />
|
||||
</Styled.EstimatedRewardsCard>
|
||||
);
|
||||
};
|
||||
|
||||
const LaunchIncentivesContent = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
return (
|
||||
<Styled.Column>
|
||||
<Styled.Description>
|
||||
{stringGetter({ key: STRING_KEYS.LAUNCH_INCENTIVES_DESCRIPTION })}{' '}
|
||||
</Styled.Description>
|
||||
<Styled.ChaosLabsLogo src="/logos/chaos-labs.svg" />
|
||||
<Styled.ButtonRow>
|
||||
<Styled.AboutButton
|
||||
action={ButtonAction.Base}
|
||||
onClick={() => {
|
||||
dispatch(
|
||||
openDialog({
|
||||
type: DialogTypes.ExternalLink,
|
||||
dialogProps: { link: 'https://dydx.exchange/blog/v4-full-trading' },
|
||||
})
|
||||
);
|
||||
}}
|
||||
slotRight={<Styled.LinkOutIcon iconName={IconName.LinkOut} />}
|
||||
>
|
||||
{stringGetter({ key: STRING_KEYS.ABOUT })}
|
||||
</Styled.AboutButton>
|
||||
<Styled.Button
|
||||
action={ButtonAction.Primary}
|
||||
onClick={() => {
|
||||
dispatch(
|
||||
openDialog({
|
||||
type: DialogTypes.ExternalLink,
|
||||
dialogProps: { link: 'https://community.chaoslabs.xyz/dydx-v4/risk/leaderboard' },
|
||||
})
|
||||
);
|
||||
}}
|
||||
slotRight={<Styled.LinkOutIcon iconName={IconName.LinkOut} />}
|
||||
slotLeft={<Icon iconName={IconName.Leaderboard} />}
|
||||
>
|
||||
{stringGetter({ key: STRING_KEYS.LEADERBOARD })}
|
||||
</Styled.Button>
|
||||
</Styled.ButtonRow>
|
||||
</Styled.Column>
|
||||
);
|
||||
};
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Panel = styled(Panel)`
|
||||
--panel-paddingY: 1rem;
|
||||
--panel-paddingX: 1.5rem;
|
||||
|
||||
background-color: var(--color-layer-4);
|
||||
width: 100%;
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
--panel-paddingY: 1.5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.ForV4 = styled.span`
|
||||
color: var(--color-text-0);
|
||||
`;
|
||||
|
||||
Styled.Title = styled.h3`
|
||||
${layoutMixins.inlineRow}
|
||||
font: var(--font-medium-book);
|
||||
color: var(--color-text-2);
|
||||
|
||||
@media ${breakpoints.notTablet} {
|
||||
padding: var(--panel-paddingY) var(--panel-paddingX) 0;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Description = styled.div`
|
||||
color: var(--color-text-0);
|
||||
--link-color: var(--color-text-1);
|
||||
|
||||
a {
|
||||
display: inline;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 0.25rem;
|
||||
|
||||
::before {
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.ButtonRow = styled.div`
|
||||
${layoutMixins.inlineRow}
|
||||
gap: 0.75rem;
|
||||
margin-top: 0.5rem;
|
||||
|
||||
a:last-child {
|
||||
--button-width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Button = styled(Button)`
|
||||
--button-padding: 0 1rem;
|
||||
`;
|
||||
|
||||
Styled.LinkOutIcon = styled(Icon)`
|
||||
color: var(--color-text-1);
|
||||
`;
|
||||
|
||||
Styled.AboutButton = styled(Styled.Button)`
|
||||
--button-textColor: var(--color-text-2);
|
||||
--button-backgroundColor: var(--color-layer-6);
|
||||
--button-border: solid var(--border-width) var(--color-layer-7);
|
||||
`;
|
||||
|
||||
Styled.Column = styled.div`
|
||||
${layoutMixins.flexColumn}
|
||||
gap: 0.5rem;
|
||||
`;
|
||||
|
||||
Styled.EstimatedRewardsCard = styled.div`
|
||||
${layoutMixins.spacedRow}
|
||||
padding: 1rem 1.25rem;
|
||||
min-width: 21.25rem;
|
||||
height: calc(100% - calc(1.5rem * 2));
|
||||
margin: 1.5rem;
|
||||
|
||||
background-color: var(--color-layer-5);
|
||||
background-image: url('/dots-background.svg');
|
||||
background-size: cover;
|
||||
|
||||
border-radius: 0.75rem;
|
||||
border: solid var(--border-width) var(--color-layer-6);
|
||||
color: var(--color-text-1);
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
margin: 0 0 0.5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.EstimatedRewardsCardContent = styled.div`
|
||||
${layoutMixins.flexColumn}
|
||||
gap: 1rem;
|
||||
height: 100%;
|
||||
justify-content: space-between;
|
||||
|
||||
div {
|
||||
${layoutMixins.flexColumn}
|
||||
gap: 0.15rem;
|
||||
font: var(--font-medium-book);
|
||||
|
||||
:first-child {
|
||||
color: var(--color-text-2);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.BackgroundDots = styled.img`
|
||||
position: absolute;
|
||||
`;
|
||||
|
||||
Styled.Season = styled.span`
|
||||
font: var(--font-small-book);
|
||||
color: var(--color-text-1);
|
||||
`;
|
||||
|
||||
Styled.Points = styled.span`
|
||||
${layoutMixins.inlineRow}
|
||||
gap: 0.25rem;
|
||||
font: var(--font-large-book);
|
||||
color: var(--color-text-0);
|
||||
|
||||
output {
|
||||
color: var(--color-text-2);
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Image = styled.img`
|
||||
position: relative;
|
||||
float: right;
|
||||
|
||||
width: 5.25rem;
|
||||
height: auto;
|
||||
`;
|
||||
|
||||
Styled.ChaosLabsLogo = styled.img`
|
||||
height: 1.25rem;
|
||||
align-self: start;
|
||||
`;
|
||||
|
||||
Styled.NewTag = styled(Tag)`
|
||||
color: var(--color-accent);
|
||||
background-color: var(--color-accent-faded);
|
||||
`;
|
||||
@@ -7,8 +7,6 @@ import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
|
||||
|
||||
import { useAccountBalance, useBreakpoints, useStringGetter } from '@/hooks';
|
||||
|
||||
import { breakpoints } from '@/styles';
|
||||
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { Details } from '@/components/Details';
|
||||
@@ -136,22 +134,13 @@ const Styled: Record<string, AnyStyledComponent> = {};
|
||||
Styled.MigratePanel = styled(Panel)`
|
||||
--panel-paddingX: 1.5rem;
|
||||
width: 100%;
|
||||
|
||||
background-image: url('/dots-background.svg');
|
||||
background-position: right;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
--panel-paddingY: 1.5rem;
|
||||
--panel-content-paddingY: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Title = styled.h3`
|
||||
font: var(--font-medium-book);
|
||||
color: var(--color-text-2);
|
||||
|
||||
padding: var(--panel-paddingY) var(--panel-paddingX) 0;
|
||||
padding: 1rem 1.5rem 0;
|
||||
`;
|
||||
|
||||
Styled.MigrateAction = styled.div`
|
||||
@@ -175,13 +164,6 @@ Styled.Token = styled(Output)`
|
||||
Styled.Description = styled.div`
|
||||
color: var(--color-text-0);
|
||||
--link-color: var(--color-text-1);
|
||||
|
||||
a {
|
||||
display: inline;
|
||||
::before {
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Column = styled.div`
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
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 trader’s 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 trader’s 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;
|
||||
`;
|
||||
@@ -19,9 +19,8 @@ import { openDialog } from '@/state/dialogs';
|
||||
|
||||
import { DYDXBalancePanel } from './DYDXBalancePanel';
|
||||
import { MigratePanel } from './MigratePanel';
|
||||
import { LaunchIncentivesPanel } from './LaunchIncentivesPanel';
|
||||
|
||||
const RewardsPage = () => {
|
||||
export const RewardsPage = () => {
|
||||
const dispatch = useDispatch();
|
||||
const stringGetter = useStringGetter();
|
||||
const { governanceLearnMore, stakingLearnMore } = useURLConfigs();
|
||||
@@ -40,20 +39,9 @@ const RewardsPage = () => {
|
||||
return (
|
||||
<Styled.Page>
|
||||
{import.meta.env.VITE_V3_TOKEN_ADDRESS && <MigratePanel />}
|
||||
|
||||
{isTablet ? (
|
||||
<>
|
||||
<LaunchIncentivesPanel />
|
||||
<DYDXBalancePanel />
|
||||
</>
|
||||
) : (
|
||||
<Styled.PanelRowIncentivesAndBalance>
|
||||
<LaunchIncentivesPanel />
|
||||
<DYDXBalancePanel />
|
||||
</Styled.PanelRowIncentivesAndBalance>
|
||||
)}
|
||||
|
||||
<Styled.PanelRow>
|
||||
{isTablet && <DYDXBalancePanel />}
|
||||
|
||||
<Styled.Panel
|
||||
slotHeader={<Styled.Title>{stringGetter({ key: STRING_KEYS.GOVERNANCE })}</Styled.Title>}
|
||||
slotRight={panelArrow}
|
||||
@@ -79,13 +67,13 @@ const RewardsPage = () => {
|
||||
</Link>
|
||||
</Styled.Description>
|
||||
</Styled.Panel>
|
||||
|
||||
{isNotTablet && <DYDXBalancePanel />}
|
||||
</Styled.PanelRow>
|
||||
</Styled.Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default RewardsPage;
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Page = styled.div`
|
||||
@@ -111,11 +99,6 @@ Styled.Page = styled.div`
|
||||
|
||||
Styled.Panel = styled(Panel)`
|
||||
--panel-paddingX: 1.5rem;
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
--panel-paddingY: 1.5rem;
|
||||
--panel-content-paddingY: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Title = styled.h3`
|
||||
@@ -127,29 +110,20 @@ Styled.Title = styled.h3`
|
||||
Styled.Description = styled.div`
|
||||
color: var(--color-text-0);
|
||||
--link-color: var(--color-text-1);
|
||||
|
||||
a {
|
||||
display: inline;
|
||||
::before {
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.PanelRow = styled.div`
|
||||
${layoutMixins.gridEqualColumns}
|
||||
gap: 1.5rem;
|
||||
|
||||
align-items: flex-start;
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
grid-auto-flow: row;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.PanelRowIncentivesAndBalance = styled(Styled.PanelRow)`
|
||||
grid-template-columns: 2fr 1fr;
|
||||
`;
|
||||
|
||||
Styled.IconButton = styled(IconButton)`
|
||||
color: var(--color-text-0);
|
||||
--color-border: var(--color-layer-6);
|
||||
|
||||
@@ -25,7 +25,7 @@ import { useNetworks } from '@/views/menus/useNetworks';
|
||||
import { SettingsHeader } from './SettingsHeader';
|
||||
import { ComingSoonSpace } from '@/components/ComingSoon';
|
||||
|
||||
const SettingsPage = () => {
|
||||
export const SettingsPage = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
const { pathname } = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
@@ -105,5 +105,3 @@ const SettingsPage = () => {
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsPage;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { AbacusOrderStatus } from '@/constants/abacus';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
|
||||
import { useBreakpoints, useStringGetter } from '@/hooks';
|
||||
@@ -27,6 +28,7 @@ import {
|
||||
getCurrentMarketTradeInfoNumbers,
|
||||
getHasUnseenFillUpdates,
|
||||
getHasUnseenOrderUpdates,
|
||||
getLatestOrderStatus,
|
||||
getTradeInfoNumbers,
|
||||
} from '@/state/accountSelectors';
|
||||
|
||||
@@ -80,141 +82,125 @@ export const HorizontalPanel = ({ isOpen = true, setIsOpen }: ElementProps) => {
|
||||
showCurrentMarket ? numOpenOrders : numTotalOpenOrders
|
||||
);
|
||||
|
||||
const tabItems = useMemo(
|
||||
() => [
|
||||
{
|
||||
value: InfoSection.Position,
|
||||
label: stringGetter({
|
||||
key: showCurrentMarket ? STRING_KEYS.POSITION : STRING_KEYS.POSITIONS,
|
||||
}),
|
||||
const tabItems = [
|
||||
{
|
||||
value: InfoSection.Position,
|
||||
label: stringGetter({
|
||||
key: showCurrentMarket ? STRING_KEYS.POSITION : STRING_KEYS.POSITIONS,
|
||||
}),
|
||||
|
||||
tag: showCurrentMarket ? null : shortenNumberForDisplay(numTotalPositions),
|
||||
tag: showCurrentMarket ? null : shortenNumberForDisplay(numTotalPositions),
|
||||
|
||||
content: showCurrentMarket ? (
|
||||
<PositionInfo showNarrowVariation={isTablet} />
|
||||
) : (
|
||||
<PositionsTable
|
||||
columnKeys={
|
||||
isTablet
|
||||
? [
|
||||
PositionsTableColumnKey.Details,
|
||||
PositionsTableColumnKey.IndexEntry,
|
||||
PositionsTableColumnKey.PnL,
|
||||
]
|
||||
: [
|
||||
PositionsTableColumnKey.Market,
|
||||
PositionsTableColumnKey.Side,
|
||||
PositionsTableColumnKey.Size,
|
||||
PositionsTableColumnKey.Leverage,
|
||||
PositionsTableColumnKey.LiquidationAndOraclePrice,
|
||||
PositionsTableColumnKey.UnrealizedPnl,
|
||||
PositionsTableColumnKey.RealizedPnl,
|
||||
PositionsTableColumnKey.AverageOpenAndClose,
|
||||
]
|
||||
}
|
||||
onNavigate={() => setView(PanelView.CurrentMarket)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
asChild: true,
|
||||
value: InfoSection.Orders,
|
||||
label: stringGetter({ key: STRING_KEYS.ORDERS }),
|
||||
content: showCurrentMarket ? (
|
||||
<PositionInfo showNarrowVariation={isTablet} />
|
||||
) : (
|
||||
<PositionsTable
|
||||
columnKeys={
|
||||
isTablet
|
||||
? [
|
||||
PositionsTableColumnKey.Details,
|
||||
PositionsTableColumnKey.IndexEntry,
|
||||
PositionsTableColumnKey.PnL,
|
||||
]
|
||||
: [
|
||||
PositionsTableColumnKey.Market,
|
||||
PositionsTableColumnKey.Side,
|
||||
PositionsTableColumnKey.Size,
|
||||
PositionsTableColumnKey.Leverage,
|
||||
PositionsTableColumnKey.LiquidationAndOraclePrice,
|
||||
PositionsTableColumnKey.UnrealizedPnl,
|
||||
PositionsTableColumnKey.RealizedPnl,
|
||||
PositionsTableColumnKey.AverageOpenAndClose,
|
||||
]
|
||||
}
|
||||
onNavigate={() => setView(PanelView.CurrentMarket)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: InfoSection.Orders,
|
||||
label: stringGetter({ key: STRING_KEYS.ORDERS }),
|
||||
|
||||
slotRight: isWaitingForOrderToIndex ? (
|
||||
<Styled.LoadingSpinner />
|
||||
) : (
|
||||
ordersTagNumber && (
|
||||
<Tag type={TagType.Number} isHighlighted={hasUnseenOrderUpdates}>
|
||||
{ordersTagNumber}
|
||||
</Tag>
|
||||
)
|
||||
),
|
||||
|
||||
content: (
|
||||
<OrdersTable
|
||||
currentMarket={showCurrentMarket ? currentMarketId : undefined}
|
||||
columnKeys={
|
||||
isTablet
|
||||
? [OrdersTableColumnKey.StatusFill, OrdersTableColumnKey.PriceType]
|
||||
: [
|
||||
!showCurrentMarket && OrdersTableColumnKey.Market,
|
||||
OrdersTableColumnKey.Status,
|
||||
OrdersTableColumnKey.Side,
|
||||
OrdersTableColumnKey.AmountFill,
|
||||
OrdersTableColumnKey.Price,
|
||||
OrdersTableColumnKey.Trigger,
|
||||
OrdersTableColumnKey.GoodTil,
|
||||
!isAccountViewOnly && OrdersTableColumnKey.Actions,
|
||||
].filter(isTruthy)
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
asChild: true,
|
||||
value: InfoSection.Fills,
|
||||
label: stringGetter({ key: STRING_KEYS.FILLS }),
|
||||
|
||||
slotRight: fillsTagNumber && (
|
||||
<Tag type={TagType.Number} isHighlighted={hasUnseenFillUpdates}>
|
||||
{fillsTagNumber}
|
||||
slotRight: isWaitingForOrderToIndex ? (
|
||||
<Styled.LoadingSpinner />
|
||||
) : (
|
||||
ordersTagNumber && (
|
||||
<Tag type={TagType.Number} isHighlighted={hasUnseenOrderUpdates}>
|
||||
{ordersTagNumber}
|
||||
</Tag>
|
||||
),
|
||||
)
|
||||
),
|
||||
|
||||
content: (
|
||||
<FillsTable
|
||||
currentMarket={showCurrentMarket ? currentMarketId : undefined}
|
||||
columnKeys={
|
||||
isTablet
|
||||
? [
|
||||
FillsTableColumnKey.Time,
|
||||
FillsTableColumnKey.TypeAmount,
|
||||
FillsTableColumnKey.PriceFee,
|
||||
]
|
||||
: [
|
||||
!showCurrentMarket && FillsTableColumnKey.Market,
|
||||
FillsTableColumnKey.Time,
|
||||
FillsTableColumnKey.Type,
|
||||
FillsTableColumnKey.Side,
|
||||
FillsTableColumnKey.AmountTag,
|
||||
FillsTableColumnKey.Price,
|
||||
FillsTableColumnKey.TotalFee,
|
||||
FillsTableColumnKey.Liquidity,
|
||||
].filter(isTruthy)
|
||||
}
|
||||
columnWidths={{
|
||||
[FillsTableColumnKey.TypeAmount]: '100%',
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
// TODO - TRCL-1693 - re-enable when funding payments are supported
|
||||
// {
|
||||
// value: InfoSection.Payments,
|
||||
// label: stringGetter({ key: STRING_KEYS.PAYMENTS }),
|
||||
content: (
|
||||
<OrdersTable
|
||||
currentMarket={showCurrentMarket ? currentMarketId : undefined}
|
||||
columnKeys={
|
||||
isTablet
|
||||
? [OrdersTableColumnKey.StatusFill, OrdersTableColumnKey.PriceType]
|
||||
: [
|
||||
!showCurrentMarket && OrdersTableColumnKey.Market,
|
||||
OrdersTableColumnKey.Status,
|
||||
OrdersTableColumnKey.Side,
|
||||
OrdersTableColumnKey.AmountFill,
|
||||
OrdersTableColumnKey.Price,
|
||||
OrdersTableColumnKey.Trigger,
|
||||
OrdersTableColumnKey.GoodTil,
|
||||
!isAccountViewOnly && OrdersTableColumnKey.Actions,
|
||||
].filter(isTruthy)
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
value: InfoSection.Fills,
|
||||
label: stringGetter({ key: STRING_KEYS.FILLS }),
|
||||
|
||||
// tag: shortenNumberForDisplay(
|
||||
// showCurrentMarket ? numFundingPayments : numTotalFundingPayments
|
||||
// ),
|
||||
// content: (
|
||||
// <FundingPaymentsTable currentMarket={showCurrentMarket ? currentMarket?.id : undefined} />
|
||||
// ),
|
||||
// },
|
||||
],
|
||||
[
|
||||
stringGetter,
|
||||
currentMarketId,
|
||||
showCurrentMarket,
|
||||
isTablet,
|
||||
isWaitingForOrderToIndex,
|
||||
isAccountViewOnly,
|
||||
ordersTagNumber,
|
||||
fillsTagNumber,
|
||||
hasUnseenFillUpdates,
|
||||
hasUnseenOrderUpdates,
|
||||
]
|
||||
);
|
||||
slotRight: fillsTagNumber && (
|
||||
<Tag type={TagType.Number} isHighlighted={hasUnseenFillUpdates}>
|
||||
{fillsTagNumber}
|
||||
</Tag>
|
||||
),
|
||||
|
||||
content: (
|
||||
<FillsTable
|
||||
currentMarket={showCurrentMarket ? currentMarketId : undefined}
|
||||
columnKeys={
|
||||
isTablet
|
||||
? [
|
||||
FillsTableColumnKey.Time,
|
||||
FillsTableColumnKey.TypeAmount,
|
||||
FillsTableColumnKey.PriceFee,
|
||||
]
|
||||
: [
|
||||
!showCurrentMarket && FillsTableColumnKey.Market,
|
||||
FillsTableColumnKey.Time,
|
||||
FillsTableColumnKey.Type,
|
||||
FillsTableColumnKey.Side,
|
||||
FillsTableColumnKey.AmountTag,
|
||||
FillsTableColumnKey.Price,
|
||||
FillsTableColumnKey.TotalFee,
|
||||
FillsTableColumnKey.Liquidity,
|
||||
].filter(isTruthy)
|
||||
}
|
||||
columnWidths={{
|
||||
[FillsTableColumnKey.TypeAmount]: '100%',
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
// TODO - TRCL-1693 - re-enable when funding payments are supported
|
||||
// {
|
||||
// value: InfoSection.Payments,
|
||||
// label: stringGetter({ key: STRING_KEYS.PAYMENTS }),
|
||||
|
||||
// tag: shortenNumberForDisplay(
|
||||
// showCurrentMarket ? numFundingPayments : numTotalFundingPayments
|
||||
// ),
|
||||
// content: (
|
||||
// <FundingPaymentsTable currentMarket={showCurrentMarket ? currentMarket?.id : undefined} />
|
||||
// ),
|
||||
// },
|
||||
];
|
||||
|
||||
return isTablet ? (
|
||||
<MobileTabs defaultValue={InfoSection.Position} items={tabItems} withBorders={false} />
|
||||
|
||||
@@ -17,7 +17,6 @@ export interface ConfigsState {
|
||||
feeTiers?: kollections.List<FeeTier>;
|
||||
feeDiscounts?: FeeDiscount[];
|
||||
network?: NetworkConfigs;
|
||||
hasSeenLaunchIncentives: boolean;
|
||||
}
|
||||
|
||||
const DOCUMENT_THEME_MAP = {
|
||||
@@ -44,10 +43,6 @@ const initialState: ConfigsState = {
|
||||
feeDiscounts: undefined,
|
||||
feeTiers: undefined,
|
||||
network: undefined,
|
||||
hasSeenLaunchIncentives: getLocalStorage({
|
||||
key: LocalStorageKey.HasSeenLaunchIncentives,
|
||||
defaultValue: false,
|
||||
}),
|
||||
};
|
||||
|
||||
changeTheme(initialState.appTheme);
|
||||
@@ -65,11 +60,7 @@ export const configsSlice = createSlice({
|
||||
...state,
|
||||
...action.payload,
|
||||
}),
|
||||
markLaunchIncentivesSeen: (state: ConfigsState) => {
|
||||
setLocalStorage({ key: LocalStorageKey.HasSeenLaunchIncentives, value: true });
|
||||
state.hasSeenLaunchIncentives = true;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setAppTheme, setConfigs, markLaunchIncentivesSeen } = configsSlice.actions;
|
||||
export const { setAppTheme, setConfigs } = configsSlice.actions;
|
||||
|
||||
@@ -5,6 +5,3 @@ export const getAppTheme = (state: RootState) => state.configs.appTheme;
|
||||
export const getFeeTiers = (state: RootState) => state.configs.feeTiers?.toArray();
|
||||
|
||||
export const getFeeDiscounts = (state: RootState) => state.configs.feeDiscounts;
|
||||
|
||||
export const getHasSeenLaunchIncentives = (state: RootState) =>
|
||||
state.configs.hasSeenLaunchIncentives;
|
||||
|
||||
@@ -47,10 +47,7 @@ export const inputsSlice = createSlice({
|
||||
inputErrors: errors?.toArray(),
|
||||
tradeInputs: trade,
|
||||
closePositionInputs: closePosition,
|
||||
transferInputs: {
|
||||
...transfer,
|
||||
isCctp: !!transfer?.isCctp,
|
||||
} as Nullable<TransferInputs>,
|
||||
transferInputs: transfer,
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -116,7 +116,6 @@
|
||||
|
||||
--color-gradient-positive: hsla(158, 49%, 48%, 0.16);
|
||||
--color-gradient-negative: hsla(0, 100%, 66%, 0.16);
|
||||
--color-accent-faded: hsla(var(--theme-classic-hue-purple), 100%, 70%, 0.16);
|
||||
|
||||
--color-risk-low: var(--theme-classic-color-green);
|
||||
--color-risk-medium: var(--theme-classic-color-yellow);
|
||||
|
||||
@@ -877,6 +877,11 @@ export const layoutMixins: Record<
|
||||
}
|
||||
`,
|
||||
|
||||
perspectiveArea: css`
|
||||
perspective: 100rem;
|
||||
transform-style: preserve-3d;
|
||||
`,
|
||||
|
||||
absolute: css`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
||||
@@ -63,6 +63,7 @@ Styled.AccountInfoSectionContainer = styled.div<{ showAccountInfo?: boolean }>`
|
||||
${layoutMixins.column}
|
||||
height: var(--account-info-section-height);
|
||||
min-height: var(--account-info-section-height);
|
||||
overflow-x: clip;
|
||||
|
||||
${({ showAccountInfo }) =>
|
||||
!showAccountInfo &&
|
||||
|
||||
@@ -203,6 +203,7 @@ const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Stack = styled.div`
|
||||
${layoutMixins.stack}
|
||||
${layoutMixins.perspectiveArea}
|
||||
`;
|
||||
|
||||
Styled.CornerButton = styled(Button)`
|
||||
@@ -292,14 +293,7 @@ Styled.TransferButtons = styled.div`
|
||||
|
||||
Styled.ConnectedAccountInfoContainer = styled.div<{ $showHeader?: boolean }>`
|
||||
${layoutMixins.column}
|
||||
|
||||
@media ${breakpoints.notTablet} {
|
||||
${layoutMixins.withOuterAndInnerBorders}
|
||||
}
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
${layoutMixins.withInnerBorder}
|
||||
}
|
||||
${layoutMixins.withOuterAndInnerBorders}
|
||||
|
||||
${({ $showHeader }) =>
|
||||
$showHeader &&
|
||||
|
||||
@@ -2,7 +2,9 @@ import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { MARKETS_TO_DISPLAY } from '@/constants/markets';
|
||||
import { useBreakpoints, useStringGetter } from '@/hooks';
|
||||
import { testFlags } from '@/hooks/useTestFlags';
|
||||
import { breakpoints } from '@/styles';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
@@ -32,6 +34,7 @@ export const ExchangeBillboards: React.FC<ExchangeBillboardsProps> = ({
|
||||
|
||||
Object.values(perpetualMarkets)
|
||||
.filter(Boolean)
|
||||
.filter(({ id }) => (testFlags.displayAllMarkets ? true : MARKETS_TO_DISPLAY.includes(id)))
|
||||
.forEach(({ oraclePrice, perpetual }) => {
|
||||
const { volume24H, trades24H, openInterest = 0 } = perpetual || {};
|
||||
volume24HUSDC += volume24H ?? 0;
|
||||
|
||||
@@ -114,8 +114,8 @@ export const MarketDetails: React.FC = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'initial-margin-fraction',
|
||||
label: stringGetter({ key: STRING_KEYS.INITIAL_MARGIN_FRACTION }),
|
||||
key: 'base-initial-margin-fraction',
|
||||
label: stringGetter({ key: STRING_KEYS.BASE_INITIAL_MARGIN_FRACTION }),
|
||||
tooltip: 'initial-margin-fraction',
|
||||
value: <Output useGrouping value={initialMarginFraction} type={OutputType.SmallPercent} />,
|
||||
},
|
||||
@@ -213,8 +213,13 @@ Styled.MarketDetails = styled.div`
|
||||
`;
|
||||
|
||||
Styled.Header = styled.header`
|
||||
/* max-width: fit-content; */
|
||||
|
||||
${layoutMixins.column}
|
||||
gap: 1.25rem;
|
||||
|
||||
position: sticky;
|
||||
bottom: 3rem;
|
||||
`;
|
||||
|
||||
Styled.WrapRow = styled.div`
|
||||
|
||||
@@ -453,6 +453,13 @@ Styled.Actions = styled.footer`
|
||||
> :last-child {
|
||||
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 }>`
|
||||
@@ -507,7 +514,6 @@ Styled.PositionInfo = styled.div`
|
||||
|
||||
Styled.DetachedSection = styled(DetachedSection)`
|
||||
padding: 0 1.5rem;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
Styled.DetachedScrollableSection = styled(DetachedScrollableSection)`
|
||||
|
||||
@@ -82,6 +82,7 @@ const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.PriceChart = styled.div<{ isChartReady?: boolean }>`
|
||||
${layoutMixins.stack}
|
||||
${layoutMixins.perspectiveArea}
|
||||
|
||||
height: 100%;
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ export const FillDetailsDialog = ({ fillId, setIsOpen }: ElementProps) => {
|
||||
|
||||
return (
|
||||
<DetailsDialog
|
||||
slotIcon={<Styled.AssetIcon symbol={asset?.id} />}
|
||||
slotIcon={<Styled.AssetIcon symbol={asset.id} />}
|
||||
title={resources.typeStringKey && stringGetter({ key: resources.typeStringKey })}
|
||||
items={detailItems}
|
||||
setIsOpen={setIsOpen}
|
||||
|
||||
@@ -194,7 +194,7 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
|
||||
|
||||
return (
|
||||
<DetailsDialog
|
||||
slotIcon={<Styled.AssetIcon symbol={asset?.id} />}
|
||||
slotIcon={<Styled.AssetIcon symbol={asset.id} />}
|
||||
title={!resources.typeStringKey ? '' : stringGetter({ key: resources.typeStringKey })}
|
||||
slotFooter={
|
||||
isAccountViewOnly ? null : isOrderStatusClearable(status) ? (
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { ButtonAction, ButtonType } from '@/constants/buttons';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { useStringGetter } from '@/hooks';
|
||||
|
||||
import { Button } from '@/components/Button';
|
||||
import { Dialog } from '@/components/Dialog';
|
||||
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
type ElementProps = {
|
||||
link: string;
|
||||
linkDescription?: string;
|
||||
setIsOpen: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export const ExternalLinkDialog = ({ setIsOpen, link, linkDescription }: ElementProps) => {
|
||||
const stringGetter = useStringGetter();
|
||||
return (
|
||||
<Dialog
|
||||
isOpen
|
||||
setIsOpen={setIsOpen}
|
||||
title={stringGetter({ key: STRING_KEYS.LEAVING_WEBSITE })}
|
||||
description={
|
||||
linkDescription ?? stringGetter({ key: STRING_KEYS.LEAVING_WEBSITE_DESCRIPTION })
|
||||
}
|
||||
>
|
||||
<Styled.Content>
|
||||
<p>{stringGetter({ key: STRING_KEYS.LEAVING_WEBSITE_DISCLAIMER })}.</p>
|
||||
<Button type={ButtonType.Link} action={ButtonAction.Primary} href={link}>
|
||||
{stringGetter({ key: STRING_KEYS.CONTINUE })}
|
||||
</Button>
|
||||
</Styled.Content>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Content = styled.div`
|
||||
${layoutMixins.flexColumn}
|
||||
gap: 1rem;
|
||||
|
||||
font: var(--font-base-book);
|
||||
`;
|
||||
@@ -8,7 +8,6 @@ import { ComboboxDialogMenu } from '@/components/ComboboxDialogMenu';
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
|
||||
import { isTruthy } from '@/lib/isTruthy';
|
||||
import { breakpoints } from '@/styles';
|
||||
|
||||
type ElementProps = {
|
||||
setIsOpen: (open: boolean) => void;
|
||||
@@ -73,11 +72,8 @@ export const HelpDialog = ({ setIsOpen }: ElementProps) => {
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.ComboboxDialogMenu = styled(ComboboxDialogMenu)`
|
||||
--dialog-width: var(--dialog-small-width);
|
||||
--dialog-content-paddingTop: 1rem;
|
||||
--dialog-content-paddingBottom: 1rem;
|
||||
--comboxDialogMenu-item-gap: 1rem;
|
||||
|
||||
@media ${breakpoints.notMobile} {
|
||||
--dialog-width: var(--dialog-small-width);
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -3,18 +3,19 @@ import styled, { AnyStyledComponent } from 'styled-components';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { AlertType } from '@/constants/alerts';
|
||||
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 { ButtonAction, ButtonSize } from '@/constants/buttons';
|
||||
|
||||
import { AlertMessage } from '@/components/AlertMessage';
|
||||
import { Button } from '@/components/Button';
|
||||
import { Icon } from '@/components/Icon';
|
||||
import { Link } from '@/components/Link';
|
||||
|
||||
import { useAccounts, useStringGetter, useURLConfigs } from '@/hooks';
|
||||
import { useDisplayedWallets } from '@/hooks/useDisplayedWallets';
|
||||
|
||||
import { breakpoints } from '@/styles';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
@@ -24,7 +25,7 @@ export const ChooseWallet = () => {
|
||||
|
||||
const displayedWallets = useDisplayedWallets();
|
||||
|
||||
const { selectWalletType, selectedWalletType, selectedWalletError } = useWalletConnection();
|
||||
const { selectWalletType, selectedWalletType, selectedWalletError } = useAccounts();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -86,7 +86,13 @@ export const GenerateKeys = ({
|
||||
].includes(status);
|
||||
|
||||
const signTypedData = getSignTypedData(selectedNetwork);
|
||||
const { signTypedDataAsync } = useSignTypedData();
|
||||
const { signTypedDataAsync } = useSignTypedData({
|
||||
...signTypedData,
|
||||
domain: {
|
||||
...signTypedData.domain,
|
||||
chainId,
|
||||
},
|
||||
});
|
||||
|
||||
const staticEncryptionKey = import.meta.env.VITE_PK_ENCRYPTION_KEY;
|
||||
|
||||
@@ -97,13 +103,7 @@ export const GenerateKeys = ({
|
||||
// 1. First signature
|
||||
setStatus(EvmDerivedAccountStatus.Deriving);
|
||||
|
||||
const signature = await signTypedDataAsync({
|
||||
...signTypedData,
|
||||
domain: {
|
||||
...signTypedData.domain,
|
||||
chainId,
|
||||
},
|
||||
});
|
||||
const signature = await signTypedDataAsync();
|
||||
const { wallet: dydxWallet } = await getWalletFromEvmSignature({ signature });
|
||||
|
||||
// 2. Ensure signature is deterministic
|
||||
@@ -121,13 +121,7 @@ export const GenerateKeys = ({
|
||||
setStatus(EvmDerivedAccountStatus.EnsuringDeterminism);
|
||||
|
||||
// Second signature
|
||||
const additionalSignature = await signTypedDataAsync({
|
||||
...signTypedData,
|
||||
domain: {
|
||||
...signTypedData.domain,
|
||||
chainId,
|
||||
},
|
||||
});
|
||||
const additionalSignature = await signTypedDataAsync();
|
||||
|
||||
if (signature !== additionalSignature) {
|
||||
throw new Error(
|
||||
|
||||
@@ -18,7 +18,11 @@ type ElementProps = {
|
||||
onSelectChain: (chain: string) => void;
|
||||
};
|
||||
|
||||
export const ChainSelectMenu = ({ label, selectedChain, onSelectChain }: ElementProps) => {
|
||||
export const ChainSelectMenu = ({
|
||||
label,
|
||||
selectedChain,
|
||||
onSelectChain,
|
||||
}: ElementProps) => {
|
||||
const stringGetter = useStringGetter();
|
||||
const { type, depositOptions, withdrawalOptions, resources } =
|
||||
useSelector(getTransferInputs, shallowEqual) || {};
|
||||
@@ -34,7 +38,7 @@ export const ChainSelectMenu = ({ label, selectedChain, onSelectChain }: Element
|
||||
slotBefore: <Styled.Img src={chain.iconUrl} alt="" />,
|
||||
}));
|
||||
|
||||
const selectedOption = chains.find((item) => item.type === selectedChain);
|
||||
const selectedOption = chains.find((item) => item.type === selectedChain);
|
||||
|
||||
return (
|
||||
<SearchSelectMenu
|
||||
@@ -77,4 +81,4 @@ Styled.ChainRow = styled.div`
|
||||
gap: 0.5rem;
|
||||
color: var(--color-text-2);
|
||||
font: var(--font-base-book);
|
||||
`;
|
||||
`;
|
||||
@@ -10,13 +10,15 @@ import { TransferInputField, TransferInputTokenResource, TransferType } from '@/
|
||||
import { AlertType } from '@/constants/alerts';
|
||||
import { ButtonSize } from '@/constants/buttons';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { ENVIRONMENT_CONFIG_MAP, isMainnet } from '@/constants/networks';
|
||||
import { MAX_CCTP_TRANSFER_AMOUNT, MAX_PRICE_IMPACT, NumberSign } from '@/constants/numbers';
|
||||
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
import { NotificationStatus } from '@/constants/notifications';
|
||||
import { NumberSign } from '@/constants/numbers';
|
||||
import type { EvmAddress } from '@/constants/wallets';
|
||||
|
||||
import { useAccounts, useDebounce, useStringGetter, useSelectedNetwork } from '@/hooks';
|
||||
import { useAccountBalance, CHAIN_DEFAULT_TOKEN_ADDRESS } from '@/hooks/useAccountBalance';
|
||||
import { useLocalNotifications } from '@/hooks/useLocalNotifications';
|
||||
import { NATIVE_TOKEN_ADDRESS, useSquid } from '@/hooks/useSquid';
|
||||
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import { formMixins } from '@/styles/formMixins';
|
||||
@@ -36,7 +38,6 @@ import { getTransferInputs } from '@/state/inputsSelectors';
|
||||
|
||||
import abacusStateManager from '@/lib/abacus';
|
||||
import { MustBigNumber } from '@/lib/numbers';
|
||||
import { getNobleChainId, NATIVE_TOKEN_ADDRESS } from '@/lib/squid';
|
||||
import { log } from '@/lib/telemetry';
|
||||
import { parseWalletError } from '@/lib/wallet';
|
||||
|
||||
@@ -68,7 +69,6 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
||||
summary,
|
||||
errors: routeErrors,
|
||||
errorMessage: routeErrorMessage,
|
||||
isCctp,
|
||||
} = useSelector(getTransferInputs, shallowEqual) || {};
|
||||
const chainId = chainIdStr ? parseInt(chainIdStr) : undefined;
|
||||
|
||||
@@ -84,7 +84,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
||||
);
|
||||
|
||||
const [fromAmount, setFromAmount] = useState('');
|
||||
const [slippage, setSlippage] = useState(isCctp ? 0 : 0.01); // 1% slippage
|
||||
const [slippage, setSlippage] = useState(0.01); // 1% slippage
|
||||
const debouncedAmount = useDebounce<string>(fromAmount, 500);
|
||||
|
||||
// Async Data
|
||||
@@ -99,8 +99,6 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
||||
const debouncedAmountBN = MustBigNumber(debouncedAmount);
|
||||
const balanceBN = MustBigNumber(balance);
|
||||
|
||||
useEffect(() => setSlippage(isCctp ? 0 : 0.01), [isCctp]);
|
||||
|
||||
useEffect(() => {
|
||||
const hasInvalidInput =
|
||||
debouncedAmountBN.isNaN() || debouncedAmountBN.lte(0) || debouncedAmountBN.gt(balanceBN);
|
||||
@@ -252,13 +250,11 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
||||
if (txHash) {
|
||||
addTransferNotification({
|
||||
txHash: txHash,
|
||||
toChainId: !isCctp
|
||||
? ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId
|
||||
: getNobleChainId(),
|
||||
toChainId: ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId,
|
||||
fromChainId: chainIdStr || undefined,
|
||||
toAmount: summary?.usdcSize || undefined,
|
||||
triggeredAt: Date.now(),
|
||||
isCctp,
|
||||
notificationStatus: NotificationStatus.Triggered,
|
||||
});
|
||||
abacusStateManager.clearTransferInputValues();
|
||||
setFromAmount('');
|
||||
@@ -325,21 +321,6 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
||||
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;
|
||||
}, [
|
||||
error,
|
||||
@@ -350,7 +331,6 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
||||
fromAmount,
|
||||
sourceToken,
|
||||
stringGetter,
|
||||
summary,
|
||||
]);
|
||||
|
||||
const isDisabled =
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
import { type Dispatch, type ReactNode, type SetStateAction, useState, useMemo } from 'react';
|
||||
import { type Dispatch, type SetStateAction, useState, type ReactNode } from 'react';
|
||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
import type { RouteData } from '@0xsquid/sdk';
|
||||
import { formatUnits } from 'viem';
|
||||
|
||||
import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons';
|
||||
import {
|
||||
ButtonAction,
|
||||
ButtonShape,
|
||||
ButtonSize,
|
||||
ButtonState,
|
||||
ButtonType,
|
||||
} from '@/constants/buttons';
|
||||
|
||||
import { TransferInputTokenResource } from '@/constants/abacus';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { NumberSign, TOKEN_DECIMALS } from '@/constants/numbers';
|
||||
import { NumberSign } from '@/constants/numbers';
|
||||
|
||||
import { useStringGetter, useTokenConfigs } from '@/hooks';
|
||||
import { useStringGetter } from '@/hooks';
|
||||
import { useMatchingEvmNetwork } from '@/hooks/useMatchingEvmNetwork';
|
||||
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
@@ -29,7 +34,6 @@ import { calculateCanAccountTrade } from '@/state/accountCalculators';
|
||||
import { getSubaccountBuyingPower, getSubaccountEquity } from '@/state/accountSelectors';
|
||||
import { getTransferInputs } from '@/state/inputsSelectors';
|
||||
|
||||
import { isTruthy } from '@/lib/isTruthy';
|
||||
import { MustBigNumber } from '@/lib/numbers';
|
||||
|
||||
import { SlippageEditor } from '../SlippageEditor';
|
||||
@@ -79,8 +83,7 @@ export const DepositButtonAndReceipt = ({
|
||||
const { current: buyingPower, postOrder: newBuyingPower } =
|
||||
useSelector(getSubaccountBuyingPower, shallowEqual) || {};
|
||||
|
||||
const { isCctp, summary, requestPayload } = useSelector(getTransferInputs, shallowEqual) || {};
|
||||
const { usdcDecimals, usdcLabel } = useTokenConfigs();
|
||||
const { summary, requestPayload } = useSelector(getTransferInputs, shallowEqual) || {};
|
||||
|
||||
const feeSubitems: DetailsItem[] = [];
|
||||
|
||||
@@ -95,7 +98,7 @@ export const DepositButtonAndReceipt = ({
|
||||
if (typeof summary?.bridgeFee === 'number') {
|
||||
feeSubitems.push({
|
||||
key: 'bridge-fees',
|
||||
label: <span>{stringGetter({ key: STRING_KEYS.BRIDGE_FEE })}</span>,
|
||||
label: <span>Bridge Fee</span>,
|
||||
value: <Output type={OutputType.Fiat} value={summary?.bridgeFee} />,
|
||||
});
|
||||
}
|
||||
@@ -106,70 +109,12 @@ export const DepositButtonAndReceipt = ({
|
||||
? stringGetter({ key: STRING_KEYS.HIDE_ALL_DETAILS })
|
||||
: stringGetter({ key: STRING_KEYS.SHOW_ALL_DETAILS });
|
||||
|
||||
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 = [
|
||||
{
|
||||
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',
|
||||
label: (
|
||||
<span>
|
||||
{stringGetter({ key: STRING_KEYS.EQUITY })} <Tag>{usdcLabel}</Tag>
|
||||
{stringGetter({ key: STRING_KEYS.EQUITY })} <Tag>USDC</Tag>
|
||||
</span>
|
||||
),
|
||||
value: (
|
||||
@@ -186,7 +131,7 @@ export const DepositButtonAndReceipt = ({
|
||||
key: 'buying-power',
|
||||
label: (
|
||||
<span>
|
||||
{stringGetter({ key: STRING_KEYS.BUYING_POWER })} <Tag>{usdcLabel}</Tag>
|
||||
{stringGetter({ key: STRING_KEYS.BUYING_POWER })} <Tag>USDC</Tag>
|
||||
</span>
|
||||
),
|
||||
value: (
|
||||
@@ -199,15 +144,28 @@ 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',
|
||||
label: <span>{stringGetter({ key: STRING_KEYS.TOTAL_FEES })}</span>,
|
||||
value: <Output type={OutputType.Fiat} value={totalFees} />,
|
||||
value: typeof summary?.bridgeFee === 'number' && typeof summary?.gasFee === 'number' && (
|
||||
<Output type={OutputType.Fiat} value={summary?.bridgeFee + summary?.gasFee} />
|
||||
),
|
||||
subitems: feeSubitems,
|
||||
},
|
||||
{
|
||||
key: 'slippage',
|
||||
label: <span>{stringGetter({ key: STRING_KEYS.MAX_SLIPPAGE })}</span>,
|
||||
label: <span>{stringGetter({ key: STRING_KEYS.SLIPPAGE })}</span>,
|
||||
value: (
|
||||
<SlippageEditor
|
||||
disabled
|
||||
@@ -220,26 +178,17 @@ export const DepositButtonAndReceipt = ({
|
||||
{
|
||||
key: 'estimatedRouteDuration',
|
||||
label: <span>{stringGetter({ key: STRING_KEYS.ESTIMATED_TIME })}</span>,
|
||||
value: (
|
||||
value: typeof summary?.estimatedRouteDuration === 'number' && (
|
||||
<Output
|
||||
type={OutputType.Text}
|
||||
value={
|
||||
typeof summary?.estimatedRouteDuration === 'number'
|
||||
? stringGetter({
|
||||
key: STRING_KEYS.X_MINUTES_LOWERCASED,
|
||||
params: {
|
||||
X:
|
||||
summary?.estimatedRouteDuration < 60
|
||||
? '< 1'
|
||||
: Math.round(summary?.estimatedRouteDuration / 60),
|
||||
},
|
||||
})
|
||||
: null
|
||||
}
|
||||
value={stringGetter({
|
||||
key: STRING_KEYS.X_MINUTES_LOWERCASED,
|
||||
params: { X: Math.round(summary?.estimatedRouteDuration / 60) },
|
||||
})}
|
||||
/>
|
||||
),
|
||||
},
|
||||
].filter(isTruthy);
|
||||
];
|
||||
|
||||
const isFormValid = !isDisabled && !isEditingSlippage;
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ export const TokenSelectMenu = ({ selectedToken, onSelectToken }: ElementProps)
|
||||
<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 { ButtonSize } from '@/constants/buttons';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { ENVIRONMENT_CONFIG_MAP, isMainnet } from '@/constants/networks';
|
||||
import { TransferNotificationTypes } from '@/constants/notifications';
|
||||
import { MAX_CCTP_TRANSFER_AMOUNT, MAX_PRICE_IMPACT, NumberSign } from '@/constants/numbers';
|
||||
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
import { NotificationStatus } from '@/constants/notifications';
|
||||
import { NumberSign } from '@/constants/numbers';
|
||||
|
||||
import {
|
||||
useAccounts,
|
||||
@@ -45,7 +45,6 @@ import { getTransferInputs } from '@/state/inputsSelectors';
|
||||
|
||||
import abacusStateManager from '@/lib/abacus';
|
||||
import { MustBigNumber } from '@/lib/numbers';
|
||||
import { getNobleChainId } from '@/lib/squid';
|
||||
|
||||
import { TokenSelectMenu } from './TokenSelectMenu';
|
||||
import { WithdrawButtonAndReceipt } from './WithdrawForm/WithdrawButtonAndReceipt';
|
||||
@@ -59,6 +58,11 @@ export const WithdrawForm = () => {
|
||||
const { sendSquidWithdraw } = useSubaccount();
|
||||
const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {};
|
||||
|
||||
// User input
|
||||
const [withdrawAmount, setWithdrawAmount] = useState('');
|
||||
const [slippage, setSlippage] = useState(0.01); // 0.1% slippage
|
||||
const debouncedAmount = useDebounce<string>(withdrawAmount, 500);
|
||||
|
||||
const {
|
||||
requestPayload,
|
||||
token,
|
||||
@@ -67,15 +71,8 @@ export const WithdrawForm = () => {
|
||||
resources,
|
||||
errors: routeErrors,
|
||||
errorMessage: routeErrorMessage,
|
||||
isCctp,
|
||||
summary,
|
||||
} = useSelector(getTransferInputs, shallowEqual) || {};
|
||||
|
||||
// User input
|
||||
const [withdrawAmount, setWithdrawAmount] = useState('');
|
||||
const [slippage, setSlippage] = useState(isCctp ? 0 : 0.01); // 0.1% slippage
|
||||
const debouncedAmount = useDebounce<string>(withdrawAmount, 500);
|
||||
|
||||
const isValidAddress = toAddress && isAddress(toAddress);
|
||||
|
||||
const toToken = useMemo(
|
||||
@@ -92,8 +89,6 @@ export const WithdrawForm = () => {
|
||||
[freeCollateral?.current]
|
||||
);
|
||||
|
||||
useEffect(() => setSlippage(isCctp ? 0 : 0.01), [isCctp]);
|
||||
|
||||
useEffect(() => {
|
||||
abacusStateManager.setTransferValue({
|
||||
field: TransferInputField.type,
|
||||
@@ -168,22 +163,16 @@ export const WithdrawForm = () => {
|
||||
})
|
||||
);
|
||||
} else {
|
||||
const txHash = await sendSquidWithdraw(
|
||||
debouncedAmountBN.toNumber(),
|
||||
requestPayload.data,
|
||||
isCctp
|
||||
);
|
||||
if (txHash) {
|
||||
const txHash = await sendSquidWithdraw(debouncedAmountBN.toNumber(), requestPayload.data);
|
||||
if (txHash?.hash) {
|
||||
const hash = `0x${Buffer.from(txHash.hash).toString('hex')}`;
|
||||
addTransferNotification({
|
||||
txHash: txHash,
|
||||
type: TransferNotificationTypes.Withdrawal,
|
||||
fromChainId: !isCctp
|
||||
? ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId
|
||||
: getNobleChainId(),
|
||||
txHash: hash,
|
||||
fromChainId: ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId,
|
||||
toChainId: chainIdStr || undefined,
|
||||
toAmount: debouncedAmountBN.toNumber(),
|
||||
triggeredAt: Date.now(),
|
||||
isCctp,
|
||||
notificationStatus: NotificationStatus.Triggered,
|
||||
});
|
||||
abacusStateManager.clearTransferInputValues();
|
||||
setWithdrawAmount('');
|
||||
@@ -289,7 +278,10 @@ export const WithdrawForm = () => {
|
||||
|
||||
const errorMessage = useMemo(() => {
|
||||
if (error) {
|
||||
return error;
|
||||
return stringGetter({
|
||||
key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE,
|
||||
params: { ERROR_MESSAGE: error },
|
||||
});
|
||||
}
|
||||
|
||||
if (routeErrors) {
|
||||
@@ -320,21 +312,6 @@ export const WithdrawForm = () => {
|
||||
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;
|
||||
}, [
|
||||
error,
|
||||
@@ -347,7 +324,6 @@ export const WithdrawForm = () => {
|
||||
toAddress,
|
||||
sanctionedAddresses,
|
||||
stringGetter,
|
||||
summary,
|
||||
]);
|
||||
|
||||
const isDisabled =
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
import { formatUnits } from 'viem';
|
||||
@@ -6,11 +6,14 @@ import { formatUnits } from 'viem';
|
||||
import { TransferInputTokenResource } from '@/constants/abacus';
|
||||
import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { NumberSign, TOKEN_DECIMALS } from '@/constants/numbers';
|
||||
import { NumberSign } from '@/constants/numbers';
|
||||
|
||||
import { formatSeconds } from '@/lib/timeUtils';
|
||||
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { useStringGetter, useTokenConfigs } from '@/hooks';
|
||||
import { useStringGetter } from '@/hooks';
|
||||
import { useAccountBalance } from '@/hooks/useAccountBalance';
|
||||
|
||||
import { Button } from '@/components/Button';
|
||||
|
||||
@@ -55,9 +58,13 @@ export const WithdrawButtonAndReceipt = ({
|
||||
const stringGetter = useStringGetter();
|
||||
|
||||
const { leverage } = useSelector(getSubaccount, shallowEqual) || {};
|
||||
const { isCctp, summary, requestPayload } = useSelector(getTransferInputs, shallowEqual) || {};
|
||||
const { summary, requestPayload } = useSelector(getTransferInputs, shallowEqual) || {};
|
||||
const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual);
|
||||
const { usdcLabel } = useTokenConfigs();
|
||||
|
||||
const toAmount =
|
||||
summary?.toAmount &&
|
||||
withdrawToken?.decimals &&
|
||||
formatUnits(BigInt(summary.toAmount), withdrawToken?.decimals);
|
||||
|
||||
const feeSubitems: DetailsItem[] = [];
|
||||
|
||||
@@ -83,102 +90,30 @@ export const WithdrawButtonAndReceipt = ({
|
||||
? stringGetter({ key: STRING_KEYS.HIDE_ALL_DETAILS })
|
||||
: stringGetter({ key: STRING_KEYS.SHOW_ALL_DETAILS });
|
||||
|
||||
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 = [
|
||||
{
|
||||
key: 'total-fees',
|
||||
label: <span>{stringGetter({ key: STRING_KEYS.TOTAL_FEES })}</span>,
|
||||
value: <Output type={OutputType.Fiat} value={totalFees} />,
|
||||
value: typeof summary?.bridgeFee === 'number' && typeof summary?.gasFee === 'number' && (
|
||||
<Output type={OutputType.Fiat} value={summary?.bridgeFee + summary?.gasFee} />
|
||||
),
|
||||
subitems: feeSubitems,
|
||||
},
|
||||
{
|
||||
key: 'exchange-rate',
|
||||
label: <span>{stringGetter({ key: STRING_KEYS.EXCHANGE_RATE })}</span>,
|
||||
value: withdrawToken && typeof summary?.exchangeRate === 'number' && (
|
||||
<Styled.ExchangeRate>
|
||||
<Output type={OutputType.Asset} value={1} fractionDigits={0} tag={usdcLabel} />
|
||||
=
|
||||
<Output
|
||||
type={OutputType.Asset}
|
||||
value={summary?.exchangeRate}
|
||||
tag={withdrawToken?.symbol}
|
||||
/>
|
||||
</Styled.ExchangeRate>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'estimated-route-duration',
|
||||
label: <span>{stringGetter({ key: STRING_KEYS.ESTIMATED_TIME })}</span>,
|
||||
value: typeof summary?.estimatedRouteDuration === 'number' && (
|
||||
<Output
|
||||
type={OutputType.Text}
|
||||
value={stringGetter({
|
||||
key: STRING_KEYS.X_MINUTES_LOWERCASED,
|
||||
params: {
|
||||
X:
|
||||
summary?.estimatedRouteDuration < 60
|
||||
? '< 1'
|
||||
: Math.round(summary?.estimatedRouteDuration / 60),
|
||||
},
|
||||
})}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'expected-amount-received',
|
||||
key: 'wallet',
|
||||
label: (
|
||||
<span>
|
||||
{stringGetter({ key: STRING_KEYS.EXPECTED_AMOUNT_RECEIVED })}{' '}
|
||||
{stringGetter({ key: STRING_KEYS.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}
|
||||
<Styled.DiffOutput
|
||||
type={OutputType.Asset}
|
||||
value={'0'}
|
||||
newValue={toAmount}
|
||||
sign={NumberSign.Positive}
|
||||
withDiff={Boolean(toAmount)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
@@ -195,6 +130,46 @@ export const WithdrawButtonAndReceipt = ({
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'exchange-rate',
|
||||
label: <span>{stringGetter({ key: STRING_KEYS.EXCHANGE_RATE })}</span>,
|
||||
value: withdrawToken && typeof summary?.exchangeRate === 'number' && (
|
||||
<Styled.ExchangeRate>
|
||||
<Output type={OutputType.Asset} value={1} fractionDigits={0} tag="USDC" />
|
||||
=
|
||||
<Output
|
||||
type={OutputType.Asset}
|
||||
value={summary?.exchangeRate}
|
||||
tag={withdrawToken?.symbol}
|
||||
/>
|
||||
</Styled.ExchangeRate>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'slippage',
|
||||
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>,
|
||||
value: typeof summary?.estimatedRouteDuration === 'number' && (
|
||||
<Output
|
||||
type={OutputType.Text}
|
||||
value={stringGetter({
|
||||
key: STRING_KEYS.X_MINUTES_LOWERCASED,
|
||||
params: { X: Math.round(summary?.estimatedRouteDuration / 60) },
|
||||
})}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const isFormValid = !isDisabled && !isEditingSlippage;
|
||||
|
||||
@@ -206,7 +206,7 @@ export const TradeForm = ({
|
||||
dispatch(setTradeFormInputs({ triggerPriceInput: value }));
|
||||
},
|
||||
value: triggerPriceInput ?? '',
|
||||
decimals: tickSizeDecimals ?? USD_DECIMALS,
|
||||
decimals: tickSizeDecimals || USD_DECIMALS,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ export const TradeForm = ({
|
||||
dispatch(setTradeFormInputs({ limitPriceInput: value }));
|
||||
},
|
||||
value: limitPriceInput,
|
||||
decimals: tickSizeDecimals ?? USD_DECIMALS,
|
||||
decimals: tickSizeDecimals || USD_DECIMALS,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { type Dispatch, type SetStateAction, useCallback, useMemo } from 'react';
|
||||
import styled, { AnyStyledComponent, css } from 'styled-components';
|
||||
import { Root, Thumb, Track } from '@radix-ui/react-slider';
|
||||
import _ from 'lodash';
|
||||
import { OrderSide } from '@dydxprotocol/v4-client-js';
|
||||
|
||||
import { TradeInputField } from '@/constants/abacus';
|
||||
import { type Nullable, TradeInputField } from '@/constants/abacus';
|
||||
import { PositionSide } from '@/constants/trade';
|
||||
|
||||
import { MustBigNumber, type BigNumberish } from '@/lib/numbers';
|
||||
@@ -12,11 +12,11 @@ import abacusStateManager from '@/lib/abacus';
|
||||
|
||||
type ElementProps = {
|
||||
leverage?: BigNumberish | null;
|
||||
leverageInputValue: string;
|
||||
leverageInputValue: Nullable<number>;
|
||||
maxLeverage: BigNumberish | null;
|
||||
orderSide: OrderSide;
|
||||
positionSide: PositionSide;
|
||||
setLeverageInputValue: (value: string) => void;
|
||||
setLeverageInputValue: Dispatch<SetStateAction<Nullable<number>>>;
|
||||
};
|
||||
|
||||
type StyleProps = { className?: string };
|
||||
@@ -33,7 +33,6 @@ export const LeverageSlider = ({
|
||||
const leverageBN = MustBigNumber(leverage);
|
||||
const maxLeverageBN = MustBigNumber(maxLeverage);
|
||||
const leverageInputBN = MustBigNumber(leverageInputValue || leverage);
|
||||
const leverageInputNumber = isNaN(leverageInputBN.toNumber()) ? 0 : leverageInputBN.toNumber();
|
||||
|
||||
const sliderConfig = useMemo(
|
||||
() => ({
|
||||
@@ -80,12 +79,12 @@ export const LeverageSlider = ({
|
||||
);
|
||||
|
||||
const onSliderDrag = ([newLeverage]: number[]) => {
|
||||
setLeverageInputValue(`${newLeverage}`);
|
||||
setLeverageInputValue(newLeverage);
|
||||
debouncedSetAbacusLeverage(newLeverage);
|
||||
};
|
||||
|
||||
const onValueCommit = ([newLeverage]: number[]) => {
|
||||
setLeverageInputValue(`${newLeverage}`);
|
||||
setLeverageInputValue(newLeverage);
|
||||
|
||||
// Ensure Abacus is updated with the latest, committed value
|
||||
debouncedSetAbacusLeverage.cancel();
|
||||
@@ -103,7 +102,7 @@ export const LeverageSlider = ({
|
||||
min={min}
|
||||
max={max}
|
||||
step={0.1}
|
||||
value={[Math.min(Math.max(leverageInputNumber, min), max)]}
|
||||
value={[Math.min(Math.max(leverageInputBN.toNumber(), min), max)]}
|
||||
onValueChange={onSliderDrag}
|
||||
onValueCommit={onValueCommit}
|
||||
>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { Dispatch, SetStateAction } from 'react';
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
import { OrderSide } from '@dydxprotocol/v4-client-js';
|
||||
|
||||
import { TradeInputField } from '@/constants/abacus';
|
||||
import { TradeInputField, Nullable } from '@/constants/abacus';
|
||||
import { ButtonShape } from '@/constants/buttons';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { LEVERAGE_DECIMALS } from '@/constants/numbers';
|
||||
@@ -61,7 +62,21 @@ export const MarketLeverageInput = ({
|
||||
|
||||
const leveragePosition = postOrderLeverage ? newPositionSide : currentPositionSide;
|
||||
|
||||
const getSignedLeverage = (newLeverage: string | number) => {
|
||||
const onLeverageInput = ({ value, floatValue }: { value: string; floatValue?: 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 newLeverageSignedBN =
|
||||
leveragePosition === PositionSide.Short ||
|
||||
@@ -69,33 +84,10 @@ export const MarketLeverageInput = ({
|
||||
? newLeverageBN.abs().negated()
|
||||
: newLeverageBN.abs();
|
||||
|
||||
return newLeverageSignedBN.toFixed(LEVERAGE_DECIMALS);
|
||||
};
|
||||
|
||||
const onLeverageInput = ({
|
||||
floatValue,
|
||||
formattedValue,
|
||||
}: {
|
||||
floatValue?: number;
|
||||
formattedValue: string;
|
||||
}) => {
|
||||
setLeverageInputValue(formattedValue);
|
||||
const newLeverage = MustBigNumber(floatValue).toFixed();
|
||||
setLeverageInputValue(newLeverageSignedBN.toString());
|
||||
|
||||
abacusStateManager.setTradeValue({
|
||||
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,
|
||||
value: newLeverageSignedBN.toFixed(LEVERAGE_DECIMALS),
|
||||
field: TradeInputField.leverage,
|
||||
});
|
||||
};
|
||||
@@ -138,7 +130,7 @@ export const MarketLeverageInput = ({
|
||||
>
|
||||
<Styled.LeverageSlider
|
||||
leverage={currentLeverage}
|
||||
leverageInputValue={getSignedLeverage(leverageInputValue)}
|
||||
leverageInputValue={leverageInputValue}
|
||||
maxLeverage={maxLeverage}
|
||||
orderSide={orderSide}
|
||||
positionSide={currentPositionSide}
|
||||
|
||||
@@ -49,7 +49,7 @@ export const TradeSizeInputs = () => {
|
||||
useSelector(getCurrentMarketConfig, shallowEqual) || {};
|
||||
const { size, usdcSize, leverage, input: lastEditedInput } = inputTradeSizeData || {};
|
||||
const { needsLeverage } = currentTradeInputOptions || {};
|
||||
const decimals = stepSizeDecimals ?? TOKEN_DECIMALS;
|
||||
const decimals = stepSizeDecimals || TOKEN_DECIMALS;
|
||||
|
||||
const { amountInput, usdAmountInput, leverageInput } = useSelector(
|
||||
getTradeFormInputs,
|
||||
@@ -70,34 +70,22 @@ export const TradeSizeInputs = () => {
|
||||
}
|
||||
}, [size, usdcSize, leverage, lastEditedInput]);
|
||||
|
||||
const onSizeInput = ({
|
||||
floatValue,
|
||||
formattedValue,
|
||||
}: {
|
||||
floatValue?: number;
|
||||
formattedValue: string;
|
||||
}) => {
|
||||
dispatch(setTradeFormInputs({ amountInput: formattedValue }));
|
||||
const onSizeInput = ({ value, floatValue }: { value: string; floatValue?: number }) => {
|
||||
dispatch(setTradeFormInputs({ amountInput: value }));
|
||||
const newAmount = MustBigNumber(floatValue).toFixed(decimals);
|
||||
|
||||
abacusStateManager.setTradeValue({
|
||||
value: formattedValue === '' || newAmount === 'NaN' ? null : newAmount,
|
||||
value: value === '' || newAmount === 'NaN' ? null : newAmount,
|
||||
field: TradeInputField.size,
|
||||
});
|
||||
};
|
||||
|
||||
const onUSDCInput = ({
|
||||
floatValue,
|
||||
formattedValue,
|
||||
}: {
|
||||
floatValue?: number;
|
||||
formattedValue: string;
|
||||
}) => {
|
||||
dispatch(setTradeFormInputs({ usdAmountInput: formattedValue }));
|
||||
const newUsdcAmount = MustBigNumber(floatValue).toFixed(tickSizeDecimals || USD_DECIMALS);
|
||||
const onUSDCInput = ({ value, floatValue }: { value: string; floatValue?: number }) => {
|
||||
dispatch(setTradeFormInputs({ usdAmountInput: value }));
|
||||
const newUsdcAmount = MustBigNumber(floatValue).toFixed();
|
||||
|
||||
abacusStateManager.setTradeValue({
|
||||
value: formattedValue === '' || newUsdcAmount === 'NaN' ? null : newUsdcAmount,
|
||||
value: value === '' || newUsdcAmount === 'NaN' ? null : newUsdcAmount,
|
||||
field: TradeInputField.usdcSize,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -12,11 +12,10 @@ import { LoadingSpinner } from '@/components/Loading/LoadingSpinner';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
import { TransferNotificationTypes } from '@/constants/notifications';
|
||||
|
||||
type ElementProps = {
|
||||
status?: StatusResponse;
|
||||
type: TransferNotificationTypes;
|
||||
type: 'withdrawal' | 'deposit';
|
||||
};
|
||||
|
||||
type StyleProps = {
|
||||
@@ -47,13 +46,11 @@ export const TransferStatusSteps = ({ className, status, type }: ElementProps &
|
||||
{
|
||||
label: stringGetter({
|
||||
key:
|
||||
type === TransferNotificationTypes.Deposit
|
||||
? STRING_KEYS.INITIATED_DEPOSIT
|
||||
: STRING_KEYS.INITIATED_WITHDRAWAL,
|
||||
type === 'deposit' ? STRING_KEYS.INITIATED_DEPOSIT : STRING_KEYS.INITIATED_WITHDRAWAL,
|
||||
}),
|
||||
step: TransferStatusStep.FromChain,
|
||||
link:
|
||||
type === TransferNotificationTypes.Deposit
|
||||
link:
|
||||
type === 'deposit'
|
||||
? status?.fromChain?.transactionUrl
|
||||
: routeStatus?.[0]?.chainId === dydxChainId && routeStatus[0].txHash
|
||||
? `${mintscanTxUrl?.replace('{tx_hash}', routeStatus[0].txHash.replace('0x', ''))}`
|
||||
@@ -66,20 +63,14 @@ export const TransferStatusSteps = ({ className, status, type }: ElementProps &
|
||||
},
|
||||
{
|
||||
label: stringGetter({
|
||||
key:
|
||||
type === TransferNotificationTypes.Deposit
|
||||
? STRING_KEYS.DEPOSIT_TO_CHAIN
|
||||
: STRING_KEYS.WITHDRAW_TO_CHAIN,
|
||||
key: type === 'deposit' ? STRING_KEYS.DEPOSIT_TO_CHAIN : STRING_KEYS.WITHDRAW_TO_CHAIN,
|
||||
params: {
|
||||
CHAIN:
|
||||
type === TransferNotificationTypes.Deposit
|
||||
? 'dYdX'
|
||||
: status?.toChain?.chainData?.chainName,
|
||||
CHAIN: type === 'deposit' ? 'dYdX' : status?.toChain?.chainData?.chainName,
|
||||
},
|
||||
}),
|
||||
step: TransferStatusStep.ToChain,
|
||||
link:
|
||||
type === TransferNotificationTypes.Withdrawal
|
||||
type === 'withdrawal'
|
||||
? status?.toChain?.transactionUrl
|
||||
: currentStatus?.chainId === dydxChainId && currentStatus?.txHash
|
||||
? `${mintscanTxUrl?.replace('{tx_hash}', currentStatus.txHash.replace('0x', ''))}`
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useInterval, useStringGetter } from '@/hooks';
|
||||
|
||||
import { AlertType } from '@/constants/alerts';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { TransferNotifcation, TransferNotificationTypes } from '@/constants/notifications';
|
||||
import { TransferNotifcation } from '@/constants/notifications';
|
||||
|
||||
import { formatSeconds } from '@/lib/timeUtils';
|
||||
|
||||
@@ -22,7 +22,7 @@ import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import { TransferStatusSteps } from './TransferStatusSteps';
|
||||
|
||||
type ElementProps = {
|
||||
type: TransferNotificationTypes;
|
||||
type: 'withdrawal' | 'deposit';
|
||||
transfer: TransferNotifcation;
|
||||
triggeredAt?: number;
|
||||
};
|
||||
@@ -44,7 +44,6 @@ export const TransferStatusNotification = ({
|
||||
|
||||
// @ts-ignore status.errors is not in the type definition but can be returned
|
||||
const error = status?.errors?.length ? status?.errors[0] : status?.error;
|
||||
const hasError = error && Object.keys(error).length !== 0;
|
||||
|
||||
const updateSecondsLeft = useCallback(() => {
|
||||
const fromChainEta = (status?.fromChain?.chainData?.estimatedRouteDuration || 0) * 1000;
|
||||
@@ -55,7 +54,7 @@ export const TransferStatusNotification = ({
|
||||
useInterval({ callback: updateSecondsLeft });
|
||||
|
||||
const inProgressStatusString =
|
||||
type === TransferNotificationTypes.Deposit
|
||||
type === 'deposit'
|
||||
? secondsLeft > 0
|
||||
? STRING_KEYS.DEPOSIT_STATUS
|
||||
: STRING_KEYS.DEPOSIT_STATUS_SHORTLY
|
||||
@@ -64,7 +63,7 @@ export const TransferStatusNotification = ({
|
||||
: STRING_KEYS.WITHDRAW_STATUS_SHORTLY;
|
||||
|
||||
const statusString =
|
||||
type === TransferNotificationTypes.Deposit
|
||||
type === 'deposit'
|
||||
? status?.squidTransactionStatus === 'success'
|
||||
? STRING_KEYS.DEPOSIT_COMPLETE
|
||||
: inProgressStatusString
|
||||
@@ -88,7 +87,7 @@ export const TransferStatusNotification = ({
|
||||
},
|
||||
})}
|
||||
</Styled.Status>
|
||||
{hasError && (
|
||||
{error && (
|
||||
<AlertMessage type={AlertType.Error}>
|
||||
{stringGetter({
|
||||
key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE,
|
||||
@@ -113,7 +112,7 @@ export const TransferStatusNotification = ({
|
||||
) : (
|
||||
<Styled.BridgingStatus>
|
||||
{content}
|
||||
{!isToast && status?.squidTransactionStatus !== 'success' && !hasError && (
|
||||
{!isToast && status?.squidTransactionStatus !== 'success' && (
|
||||
<Styled.TransferStatusSteps status={status} type={type} />
|
||||
)}
|
||||
</Styled.BridgingStatus>
|
||||
@@ -121,7 +120,8 @@ export const TransferStatusNotification = ({
|
||||
}
|
||||
slotAction={
|
||||
isToast &&
|
||||
status && (
|
||||
status &&
|
||||
!error && (
|
||||
<Styled.Trigger
|
||||
isOpen={open}
|
||||
onClick={(e: MouseEvent) => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { MarketFilters, type MarketData } from '@/constants/markets';
|
||||
import { FUNDING_DECIMALS, LARGE_TOKEN_DECIMALS } from '@/constants/numbers';
|
||||
import { LARGE_TOKEN_DECIMALS } from '@/constants/numbers';
|
||||
import { AppRoute } from '@/constants/routes';
|
||||
|
||||
import { useBreakpoints, useStringGetter } from '@/hooks';
|
||||
@@ -128,8 +128,7 @@ export const MarketsTable = ({ className }: { className?: string }) => {
|
||||
label: stringGetter({ key: STRING_KEYS.FUNDING_RATE_1H_SHORT }),
|
||||
renderCell: (row) => (
|
||||
<Styled.Output
|
||||
type={OutputType.Percent}
|
||||
fractionDigits={FUNDING_DECIMALS}
|
||||
type={OutputType.SmallPercent}
|
||||
value={row.nextFundingRate}
|
||||
isPositive={MustBigNumber(row.nextFundingRate).gt(0)}
|
||||
isNegative={MustBigNumber(row.nextFundingRate).isNegative()}
|
||||
|
||||
@@ -42,11 +42,7 @@ type StyleProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
type RowData = Pick<OrderbookLine, 'depth' | 'offset' | 'price' | 'size'> & {
|
||||
side: 'bid' | 'ask';
|
||||
mine?: number;
|
||||
key: string;
|
||||
};
|
||||
type RowData = OrderbookLine & { side: 'bid' | 'ask'; mine?: number };
|
||||
|
||||
const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number }) => {
|
||||
const orderbook = useSelector(getCurrentMarketOrderbook, shallowEqual);
|
||||
@@ -57,9 +53,8 @@ const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number
|
||||
return useMemo(() => {
|
||||
const asks = (orderbook?.asks?.toArray() ?? [])
|
||||
.map(
|
||||
(row: OrderbookLine, idx: number) =>
|
||||
(row: OrderbookLine) =>
|
||||
({
|
||||
key: `ask-${idx}`,
|
||||
side: 'ask',
|
||||
mine: openOrdersBySideAndPrice[OrderSide.SELL]?.[row.price]?.size,
|
||||
...row,
|
||||
@@ -69,9 +64,8 @@ const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number
|
||||
|
||||
const bids = (orderbook?.bids?.toArray() ?? [])
|
||||
.map(
|
||||
(row: OrderbookLine, idx: number) =>
|
||||
(row: OrderbookLine) =>
|
||||
({
|
||||
key: `bid-${idx}`,
|
||||
side: 'bid',
|
||||
mine: openOrdersBySideAndPrice[OrderSide.BUY]?.[row.price]?.size,
|
||||
...row,
|
||||
@@ -103,44 +97,15 @@ const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number
|
||||
}
|
||||
|
||||
const spread =
|
||||
asks[0]?.price && bids[0]?.price ? MustBigNumber(asks[0].price).minus(bids[0].price) : null;
|
||||
asks[0] && bids[0] ? MustBigNumber(asks[0]?.price ?? 0).minus(bids[0]?.price ?? 0) : null;
|
||||
|
||||
const spreadPercent = orderbook?.spreadPercent;
|
||||
|
||||
const histogramRange = Math.max(
|
||||
isNaN(Number(bids[bids.length - 1]?.depth)) ? 0 : Number(bids[bids.length - 1]?.depth),
|
||||
isNaN(Number(asks[asks.length - 1]?.depth)) ? 0 : Number(asks[asks.length - 1]?.depth)
|
||||
Number(bids[bids.length - 1]?.depth),
|
||||
Number(asks[asks.length - 1]?.depth)
|
||||
);
|
||||
|
||||
// Ensure asks and bids are of length maxRowsPerSide by adding empty rows.
|
||||
let idx = asks.length - 1;
|
||||
while (asks.length < maxRowsPerSide) {
|
||||
idx += 1;
|
||||
|
||||
asks.push({
|
||||
key: `ask-${idx}`,
|
||||
side: 'ask',
|
||||
size: 0,
|
||||
price: 0,
|
||||
offset: 0,
|
||||
depth: 0,
|
||||
});
|
||||
}
|
||||
|
||||
idx = bids.length - 1;
|
||||
while (bids.length < maxRowsPerSide) {
|
||||
idx += 1;
|
||||
|
||||
bids.push({
|
||||
key: `bid-${idx}`,
|
||||
side: 'bid',
|
||||
size: 0,
|
||||
price: 0,
|
||||
offset: 0,
|
||||
depth: 0,
|
||||
});
|
||||
}
|
||||
|
||||
return { asks, bids, spread, spreadPercent, histogramRange, hasOrderbook: !!orderbook };
|
||||
}, [orderbook, openOrdersBySideAndPrice]);
|
||||
};
|
||||
@@ -177,33 +142,31 @@ const OrderbookTable = ({
|
||||
getCellValue: (row: RowData) => row.size,
|
||||
label: stringGetter({ key: STRING_KEYS.ORDERBOOK_ORDER_SIZE }),
|
||||
tag: symbol,
|
||||
renderCell: (row: RowData) =>
|
||||
row.size > 0 && (
|
||||
<Styled.HistogramOutput
|
||||
highlightText
|
||||
type={OutputType.Asset}
|
||||
value={row.size}
|
||||
fractionDigits={stepSizeDecimals}
|
||||
histogramSide={histogramSide === 'left' && 'left'}
|
||||
useGrouping={false}
|
||||
/>
|
||||
),
|
||||
renderCell: (row: RowData) => (
|
||||
<Styled.HistogramOutput
|
||||
highlightText
|
||||
type={OutputType.Asset}
|
||||
value={row.size}
|
||||
fractionDigits={stepSizeDecimals}
|
||||
histogramSide={histogramSide === 'left' && 'left'}
|
||||
useGrouping={false}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
columnKey: 'price',
|
||||
getCellValue: (row: RowData) => row.price,
|
||||
label: stringGetter({ key: STRING_KEYS.PRICE }),
|
||||
tag: 'USD',
|
||||
renderCell: (row: RowData) =>
|
||||
row.price > 0 && (
|
||||
<OrderbookTradesOutput
|
||||
highlightText
|
||||
type={OutputType.Number}
|
||||
value={row.price}
|
||||
fractionDigits={tickSizeDecimals}
|
||||
useGrouping={false}
|
||||
/>
|
||||
),
|
||||
renderCell: (row: RowData) => (
|
||||
<OrderbookTradesOutput
|
||||
highlightText
|
||||
type={OutputType.Number}
|
||||
value={row.price}
|
||||
fractionDigits={tickSizeDecimals}
|
||||
useGrouping={false}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
columnKey: 'subaccount-orders',
|
||||
@@ -231,13 +194,12 @@ const OrderbookTable = ({
|
||||
label="Orderbook"
|
||||
data={data}
|
||||
columns={columns}
|
||||
getRowKey={(row: RowData) => row.key}
|
||||
getRowKey={(row: RowData) => `${row.side}-${row.price}`}
|
||||
getRowAttributes={(row: RowData) => ({
|
||||
'data-side': row.side,
|
||||
style: {
|
||||
'--histogram-bucket-size': row.size,
|
||||
'--histogram-bucket-depth': row.depth,
|
||||
'--tr-pointerEvents': row.price ? 'auto' : 'none',
|
||||
},
|
||||
})}
|
||||
onRowAction={onRowAction}
|
||||
@@ -276,10 +238,19 @@ export const Orderbook = ({
|
||||
maxRowsPerSide,
|
||||
});
|
||||
|
||||
const [showRowsPerSide, setShowRowsPerSide] = useState(0);
|
||||
|
||||
// Make rows visible one by one so avoid jumps in initial scroll position
|
||||
useEffect(() => {
|
||||
if (showRowsPerSide < maxRowsPerSide) {
|
||||
setShowRowsPerSide((showRows) => showRows + 1);
|
||||
}
|
||||
}, [showRowsPerSide]);
|
||||
|
||||
const data = useMemo(
|
||||
() =>
|
||||
[
|
||||
...bids.reverse(),
|
||||
...bids.slice(0, showRowsPerSide).reverse(),
|
||||
{
|
||||
key: 'spread',
|
||||
slotCustomRow: (props) => (
|
||||
@@ -296,9 +267,9 @@ export const Orderbook = ({
|
||||
</Styled.SpreadTableRow>
|
||||
),
|
||||
} as CustomRowConfig,
|
||||
...asks,
|
||||
...asks.slice(0, showRowsPerSide),
|
||||
].reverse(),
|
||||
[asks, bids, spread, spreadPercent, isTablet]
|
||||
[asks, bids, spread, spreadPercent, showRowsPerSide, isTablet]
|
||||
);
|
||||
|
||||
const onRowAction = useCallback(
|
||||
@@ -354,7 +325,7 @@ export const Orderbook = ({
|
||||
</Styled.Header>
|
||||
<Styled.SplitOrderbook>
|
||||
<OrderbookTable data={asks} histogramSide="right" {...orderbookTableProps} />
|
||||
<OrderbookTable data={bids.reverse()} histogramSide="left" {...orderbookTableProps} />
|
||||
<OrderbookTable data={bids} histogramSide="left" {...orderbookTableProps} />
|
||||
</Styled.SplitOrderbook>
|
||||
</Styled.HorizontalLayout>
|
||||
);
|
||||
@@ -524,7 +495,6 @@ Styled.OrderbookTable = styled(OrderbookTradesTable)<StyleProps>`
|
||||
|
||||
tr {
|
||||
--histogram-bucket-depth: 0;
|
||||
pointer-events: var(--tr-pointerEvents);
|
||||
|
||||
&[data-side='bid'] {
|
||||
--accent-color: var(--color-positive);
|
||||
|
||||
@@ -230,7 +230,14 @@ const getOrdersTableColumnDef = ({
|
||||
label: `${stringGetter({ key: STRING_KEYS.STATUS })} / ${stringGetter({
|
||||
key: STRING_KEYS.FILL,
|
||||
})}`,
|
||||
renderCell: ({ asset, createdAtMilliseconds, size, status, totalFilled, resources }) => {
|
||||
renderCell: ({
|
||||
asset,
|
||||
createdAtMilliseconds,
|
||||
size,
|
||||
status,
|
||||
totalFilled,
|
||||
resources,
|
||||
}) => {
|
||||
const { statusIconColor, statusStringKey } = getStatusIconInfo({
|
||||
status,
|
||||
totalFilled,
|
||||
@@ -338,7 +345,9 @@ export const OrdersTable = ({
|
||||
if (hasUnseenOrderUpdates) dispatch(viewedOrders());
|
||||
}, [hasUnseenOrderUpdates]);
|
||||
|
||||
const symbol = currentMarket ? allAssets[allPerpetualMarkets[currentMarket]?.assetId]?.id : null;
|
||||
const symbol = currentMarket
|
||||
? allAssets[allPerpetualMarkets[currentMarket]?.assetId]?.symbol
|
||||
: null;
|
||||
|
||||
const ordersData = orders.map((order: SubaccountOrder) =>
|
||||
getHydratedTradingData({
|
||||
|
||||
@@ -366,7 +366,6 @@ Styled.InlineRow = styled.div`
|
||||
|
||||
Styled.AssetIcon = styled(AssetIcon)`
|
||||
${layoutMixins.inlineRow}
|
||||
min-width: unset;
|
||||
font-size: 2.25rem;
|
||||
`;
|
||||
|
||||
|
||||