Compare commits

..
Author SHA1 Message Date
jaredvu 3631f51878 Add withdrawalGateLearnMore to v1/env.json 2024-02-20 10:57:17 -08:00
jaredvu 8518cb6837 Add telemetry log fn 2024-02-20 10:48:29 -08:00
jaredvu b9024ca6df Add useEnvFeatures to gate fetch when unsupported on env 2024-02-20 10:41:33 -08:00
jaredvu 88fa4e26e4 TransferType prop 2024-02-20 10:10:42 -08:00
jaredvu daf862aa40 Merge branch 'main' into withdrawal-safety 2024-02-20 10:00:22 -08:00
jaredvu 4e6742ba9d nits: Add fadedWarning and fix globalStyle type 2024-02-09 20:35:39 -08:00
jaredvu 1ffbead4b3 nits 2024-02-09 20:21:52 -08:00
jaredvu 8cfe4e9d85 ✏️ withdrawalGateLearnMore 2024-02-09 20:18:52 -08:00
jaredvu d8383c1088 🚧 withdrawal gate and capacity 2024-02-09 20:18:32 -08:00
jaredvu 279a5239e5 Merge branch 'main' into withdrawal-safety 2024-02-09 09:12:06 -08:00
jaredvu c4b42a77df 🚧 withdrwal-safety client calls 2024-02-05 11:32:24 -08:00
24 changed files with 561 additions and 300 deletions
+1 -1
View File
@@ -42,7 +42,7 @@
"@cosmjs/tendermint-rpc": "^0.32.1",
"@dydxprotocol/v4-abacus": "^1.4.6",
"@dydxprotocol/v4-client-js": "^1.0.20",
"@dydxprotocol/v4-localization": "^1.1.34",
"@dydxprotocol/v4-localization": "^1.1.31",
"@ethersproject/providers": "^5.7.2",
"@js-joda/core": "^5.5.3",
"@radix-ui/react-accordion": "^1.1.2",
+8 -8
View File
@@ -1,9 +1,5 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
overrides:
follow-redirects: 1.15.3
@@ -36,8 +32,8 @@ dependencies:
specifier: ^1.0.20
version: 1.0.20
'@dydxprotocol/v4-localization':
specifier: ^1.1.34
version: 1.1.34
specifier: ^1.1.31
version: 1.1.31
'@ethersproject/providers':
specifier: ^5.7.2
version: 5.7.2
@@ -1323,8 +1319,8 @@ packages:
- utf-8-validate
dev: false
/@dydxprotocol/v4-localization@1.1.34:
resolution: {integrity: sha512-I7zivjv8gS+6b9n7/wh7PY9QEUDIIyLx9ugZ5K6ybar0xT/06yupOVdVE6iLivQ/IAsVi/RFHkD8bQd0DWCetg==}
/@dydxprotocol/v4-localization@1.1.31:
resolution: {integrity: sha512-plJVIgFAKq9/hA/gk5GgKgCQFsH3pNtDWfG/yHLDXyiGX0M0mMEi1bTNVs4podFVoHJu1nSL9YPFlpJ00FteGw==}
dev: false
/@dydxprotocol/v4-proto@4.0.0-dev.0:
@@ -16053,3 +16049,7 @@ packages:
/zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
dev: true
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
+61 -31
View File
@@ -98,7 +98,8 @@
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet",
"withdrawalGateLearnMore": "https://help.dydx.exchange"
},
"wallets": {
"walletconnect": {
@@ -126,7 +127,8 @@
}
},
"featureFlags": {
"reduceOnlySupported": true
"reduceOnlySupported": true,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-dev-2": {
@@ -182,7 +184,8 @@
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet",
"withdrawalGateLearnMore": "https://help.dydx.exchange"
},
"wallets": {
"walletconnect": {
@@ -210,7 +213,8 @@
}
},
"featureFlags": {
"reduceOnlySupported": true
"reduceOnlySupported": true,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-dev-4": {
@@ -267,7 +271,8 @@
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet",
"withdrawalGateLearnMore": "https://help.dydx.exchange"
},
"wallets": {
"walletconnect": {
@@ -295,7 +300,8 @@
}
},
"featureFlags": {
"reduceOnlySupported": true
"reduceOnlySupported": true,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-dev-5": {
@@ -351,7 +357,8 @@
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet",
"withdrawalGateLearnMore": "https://help.dydx.exchange"
},
"wallets": {
"walletconnect": {
@@ -379,7 +386,8 @@
}
},
"featureFlags": {
"reduceOnlySupported": true
"reduceOnlySupported": true,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-staging": {
@@ -437,7 +445,8 @@
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet",
"withdrawalGateLearnMore": "https://help.dydx.exchange"
},
"wallets": {
"walletconnect": {
@@ -465,7 +474,8 @@
}
},
"featureFlags": {
"reduceOnlySupported": true
"reduceOnlySupported": true,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-staging-forced-update": {
@@ -514,7 +524,8 @@
"community": "https://discord.com/invite/dydx",
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
"blogs": "https://www.dydx.foundation/blog",
"newMarketProposalLearnMore": "https://dydx.exchange/blog/new-market-proposals"
"newMarketProposalLearnMore": "https://dydx.exchange/blog/new-market-proposals",
"withdrawalGateLearnMore": "https://help.dydx.exchange"
},
"wallets": {
"walletconnect": {
@@ -549,7 +560,8 @@
}
},
"featureFlags": {
"reduceOnlySupported": true
"reduceOnlySupported": true,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-staging-west": {
@@ -607,7 +619,8 @@
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet",
"withdrawalGateLearnMore": "https://help.dydx.exchange"
},
"wallets": {
"walletconnect": {
@@ -635,7 +648,8 @@
}
},
"featureFlags": {
"reduceOnlySupported": true
"reduceOnlySupported": true,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-testnet": {
@@ -697,7 +711,8 @@
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet",
"withdrawalGateLearnMore": "https://help.dydx.exchange"
},
"wallets": {
"walletconnect": {
@@ -725,7 +740,8 @@
}
},
"featureFlags": {
"reduceOnlySupported": false
"reduceOnlySupported": false,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-testnet-dydx": {
@@ -784,7 +800,8 @@
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet",
"withdrawalGateLearnMore": "https://help.dydx.exchange"
},
"wallets": {
"walletconnect": {
@@ -812,7 +829,8 @@
}
},
"featureFlags": {
"reduceOnlySupported": false
"reduceOnlySupported": false,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-testnet-nodefleet": {
@@ -871,7 +889,8 @@
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet",
"withdrawalGateLearnMore": "https://help.dydx.exchange"
},
"wallets": {
"walletconnect": {
@@ -899,7 +918,8 @@
}
},
"featureFlags": {
"reduceOnlySupported": false
"reduceOnlySupported": false,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-testnet-kingnodes": {
@@ -958,7 +978,8 @@
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet",
"withdrawalGateLearnMore": "https://help.dydx.exchange"
},
"wallets": {
"walletconnect": {
@@ -986,7 +1007,8 @@
}
},
"featureFlags": {
"reduceOnlySupported": false
"reduceOnlySupported": false,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-testnet-liquify": {
@@ -1045,7 +1067,8 @@
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet",
"withdrawalGateLearnMore": "https://help.dydx.exchange"
},
"wallets": {
"walletconnect": {
@@ -1073,7 +1096,8 @@
}
},
"featureFlags": {
"reduceOnlySupported": false
"reduceOnlySupported": false,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-testnet-polkachu": {
@@ -1123,7 +1147,8 @@
"community": "https://discord.com/invite/dydx",
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
"blogs": "https://www.dydx.foundation/blog",
"newMarketProposalLearnMore": "https://dydx.exchange/blog/new-market-proposals"
"newMarketProposalLearnMore": "https://dydx.exchange/blog/new-market-proposals",
"withdrawalGateLearnMore": "https://help.dydx.exchange"
},
"wallets": {
"walletconnect": {
@@ -1151,7 +1176,8 @@
}
},
"featureFlags": {
"reduceOnlySupported": false
"reduceOnlySupported": false,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-testnet-bware": {
@@ -1210,7 +1236,8 @@
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnmore": "https://help.dydx.exchange",
"walletLearnmore": "https://www.dydx.academy/video/defi-wallet"
"walletLearnmore": "https://www.dydx.academy/video/defi-wallet",
"withdrawalGateLearnMore": "https://help.dydx.exchange"
},
"wallets": {
"walletconnect": {
@@ -1238,7 +1265,8 @@
}
},
"featureFlags": {
"reduceOnlySupported": false
"reduceOnlySupported": false,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-mainnet": {
@@ -1297,7 +1325,8 @@
"keplrDashboard": "[HTTP link to keplr dashboard, can be null]",
"strideZoneApp": "[HTTP link to stride zone app, can be null]",
"accountExportLearnMore": "[HTTP link to account export learn more, can be null]",
"walletLearnMore": "[HTTP link to wallet learn more, can be null]"
"walletLearnMore": "[HTTP link to wallet learn more, can be null]",
"withdrawalGateLearnMore": "[HTTP link to withdrawal gate learn more, can be null]"
},
"wallets": {
"walletconnect": {
@@ -1325,8 +1354,9 @@
}
},
"featureFlags": {
"reduceOnlySupported": false
"reduceOnlySupported": false,
"withdrawalSafetyEnabled": false
}
}
}
}
}
-7
View File
@@ -428,13 +428,6 @@
"whitepaperLink": "https://solana.com/solana-whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/solana/"
},
"STRK-USD": {
"name": "Starknet",
"tags": ["Layer 2"],
"websiteLink": "https://www.starknet.io/en",
"whitepaperLink": "https://docs.starknet.io/documentation/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/starknet-token/"
},
"STX-USD": {
"name": "Stacks",
"tags": ["Layer 2"],
@@ -513,14 +513,6 @@
{ "exchangeName": "Okx", "ticker": "SOL-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "ticker": "SOL_USDT", "adjustByMarket": "USDT-USD" }
],
"STRK": [
{ "exchangeName": "Binance", "ticker": "STRKUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "STRKUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Kraken", "ticker": "STRKUSD" },
{ "exchangeName": "Kucoin", "ticker": "STRK-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Okx", "ticker": "STRK-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Gate", "ticker": "STRK_USDT", "adjustByMarket": "USDT-USD" }
],
"STX": [
{ "exchangeName": "Binance", "ticker": "STXUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "STXUSDT", "adjustByMarket": "USDT-USD" },
-17
View File
@@ -968,23 +968,6 @@
"minOrderSize": 1000000,
"quantumConversionExponent": -9
},
{
"baseAsset": "STRK",
"referencePrice": 2.05,
"numOracles": 6,
"liquidityTier": 2,
"assetName": "Starknet",
"p": 0.0,
"atomicResolution": -6.0,
"minExchanges": 3,
"minPriceChangePpm": 4000,
"priceExponent": -9.0,
"stepBaseQuantum": 1000000,
"ticksizeExponent": -3,
"subticksPerTick": 1000000,
"minOrderSize": 1000000,
"quantumConversionExponent": -9
},
{
"baseAsset": "STX",
"referencePrice": 1.5144048703611412,
+37 -19
View File
@@ -71,7 +71,8 @@
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet",
"withdrawalGateLearnMore": "https://help.dydx.exchange"
},
"dydx-testnet-4": {
"tos": "https://dydx.exchange/v4-terms",
@@ -92,7 +93,8 @@
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet",
"withdrawalGateLearnMore": "https://help.dydx.exchange"
},
"[mainnet chain id]": {
"tos": "[HTTP link to TOS]",
@@ -113,7 +115,8 @@
"keplrDashboard": "[HTTP link to keplr dashboard, can be null]",
"strideZoneApp": "[HTTP link to stride zone app, can be null]",
"accountExportLearnMore": "[HTTP link to account export learn more, can be null]",
"walletLearnMore": "[HTTP link to wallet learn more, can be null]"
"walletLearnMore": "[HTTP link to wallet learn more, can be null]",
"withdrawalGateLearnMore": "[HTTP link to withdrawal gate learn more, can be null]"
}
},
"wallets": {
@@ -258,7 +261,8 @@
"faucet": "https://faucet.v4dev.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": true
"reduceOnlySupported": true,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-dev-2": {
@@ -283,7 +287,8 @@
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"featureFlags": {
"reduceOnlySupported": true
"reduceOnlySupported": true,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-dev-4": {
@@ -309,7 +314,8 @@
"faucet": "https://faucet.v4dev4.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": true
"reduceOnlySupported": true,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-dev-5": {
@@ -334,7 +340,8 @@
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"featureFlags": {
"reduceOnlySupported": true
"reduceOnlySupported": true,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-staging": {
@@ -360,7 +367,8 @@
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"featureFlags": {
"reduceOnlySupported": true
"reduceOnlySupported": true,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-staging-forced-update": {
@@ -393,7 +401,8 @@
}
},
"featureFlags": {
"reduceOnlySupported": true
"reduceOnlySupported": true,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-staging-west": {
@@ -419,7 +428,8 @@
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"featureFlags": {
"reduceOnlySupported": true
"reduceOnlySupported": true,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-testnet": {
@@ -449,7 +459,8 @@
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": false
"reduceOnlySupported": false,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-testnet-dydx": {
@@ -475,7 +486,8 @@
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": false
"reduceOnlySupported": false,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-testnet-nodefleet": {
@@ -501,7 +513,8 @@
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": false
"reduceOnlySupported": false,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-testnet-kingnodes": {
@@ -527,7 +540,8 @@
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": false
"reduceOnlySupported": false,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-testnet-liquify": {
@@ -553,7 +567,8 @@
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": false
"reduceOnlySupported": false,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-testnet-polkachu": {
@@ -579,7 +594,8 @@
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": false
"reduceOnlySupported": false,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-testnet-bware": {
@@ -605,7 +621,8 @@
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"featureFlags": {
"reduceOnlySupported": false
"reduceOnlySupported": false,
"withdrawalSafetyEnabled": true
}
},
"dydxprotocol-mainnet": {
@@ -631,8 +648,9 @@
"nobleValidator": "[noble validator endpoint for mainnet]"
},
"featureFlags": {
"reduceOnlySupported": false
"reduceOnlySupported": false,
"withdrawalSafetyEnabled": false
}
}
}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

+7 -1
View File
@@ -7,7 +7,12 @@ type ElementProps = {
onClick?: () => void;
};
type StyleProps = {
className?: string;
};
export const BackButton = ({
className,
onClick = () => {
// @ts-ignore
const navigation = globalThis.navigation;
@@ -21,8 +26,9 @@ export const BackButton = ({
navigation.navigate('/', { replace: true });
}
},
}: ElementProps) => (
}: ElementProps & StyleProps) => (
<IconButton
className={className}
onClick={onClick}
iconName={IconName.ChevronLeft}
size={ButtonSize.Small}
+76 -16
View File
@@ -47,6 +47,7 @@ type StyleProps = {
hasHeaderBorder?: boolean;
children?: React.ReactNode;
className?: string;
stacked?: boolean;
withAnimation?: boolean;
};
@@ -81,6 +82,7 @@ export const Dialog = ({
slotTrigger,
slotHeaderInner,
slotFooter,
stacked,
withClose = true,
placement = DialogPlacement.Default,
portalContainer,
@@ -109,27 +111,48 @@ export const Dialog = ({
e.preventDefault();
}
}}
$stacked={stacked}
$withAnimation={withAnimation}
>
<Styled.Header $withBorder={hasHeaderBorder}>
<Styled.HeaderTopRow>
{onBack && <BackButton onClick={onBack} />}
{stacked ? (
<Styled.StackedHeaderTopRow $withBorder={hasHeaderBorder}>
{onBack && <Styled.BackButton onClick={onBack} />}
{slotIcon && <Styled.Icon>{slotIcon}</Styled.Icon>}
{title && <Styled.Title>{title}</Styled.Title>}
{slotIcon}
{!preventClose && withClose && (
<Styled.Close ref={closeButtonRef}>
<Styled.Close ref={closeButtonRef} $absolute={stacked}>
<Icon iconName={IconName.Close} />
</Styled.Close>
)}
</Styled.HeaderTopRow>
{description && <Styled.Description>{description}</Styled.Description>}
{title && <Styled.Title>{title}</Styled.Title>}
{slotHeaderInner}
</Styled.Header>
{description && <Styled.Description>{description}</Styled.Description>}
{slotHeaderInner}
</Styled.StackedHeaderTopRow>
) : (
<Styled.Header $withBorder={hasHeaderBorder}>
<Styled.HeaderTopRow>
{onBack && <BackButton onClick={onBack} />}
{slotIcon && <Styled.Icon>{slotIcon}</Styled.Icon>}
{title && <Styled.Title>{title}</Styled.Title>}
{!preventClose && withClose && (
<Styled.Close ref={closeButtonRef}>
<Icon iconName={IconName.Close} />
</Styled.Close>
)}
</Styled.HeaderTopRow>
{description && <Styled.Description>{description}</Styled.Description>}
{slotHeaderInner}
</Styled.Header>
)}
<Styled.Content>{children}</Styled.Content>
@@ -173,7 +196,11 @@ Styled.Overlay = styled(Overlay)`
}
`;
Styled.Container = styled(Content)<{ placement: DialogPlacement; $withAnimation?: boolean }>`
Styled.Container = styled(Content)<{
placement: DialogPlacement;
$stacked?: boolean;
$withAnimation?: boolean;
}>`
/* Params */
--dialog-inset: 1rem;
--dialog-width: 30rem;
@@ -353,6 +380,13 @@ Styled.Container = styled(Content)<{ placement: DialogPlacement; $withAnimation?
bottom: 0;
`,
}[placement])}
${({ $stacked }) =>
$stacked &&
css`
justify-content: center;
text-align: center;
`}
`;
Styled.Header = styled.header<{ $withBorder: boolean }>`
@@ -379,9 +413,21 @@ Styled.HeaderTopRow = styled.div`
gap: var(--dialog-title-gap);
`;
Styled.HeaderTopRow = styled.div`
${layoutMixins.row}
gap: var(--dialog-title-gap);
Styled.StackedHeaderTopRow = styled.div<{ $withBorder: boolean }>`
${layoutMixins.flexColumn}
align-items: center;
justify-content: center;
padding: var(--dialog-header-paddingTop) var(--dialog-header-paddingLeft)
var(--dialog-header-paddingBottom) var(--dialog-header-paddingRight);
border-top-left-radius: inherit;
border-top-right-radius: inherit;
${({ $withBorder }) =>
$withBorder &&
css`
${layoutMixins.withOuterBorder};
background: var(--dialog-backgroundColor);
`};
`;
Styled.Content = styled.div`
@@ -412,7 +458,7 @@ Styled.Icon = styled.div`
line-height: 1;
`;
Styled.Close = styled(Close)`
Styled.Close = styled(Close)<{ $absolute?: boolean }>`
width: 0.7813rem;
height: 0.7813rem;
@@ -438,6 +484,14 @@ Styled.Close = styled(Close)`
color: var(--color-text-2);
}
${({ $absolute }) =>
$absolute &&
css`
position: absolute;
right: var(--dialog-header-paddingRight);
top: var(--dialog-header-paddingTop);
`}
@media ${breakpoints.tablet} {
width: 1rem;
height: 1rem;
@@ -445,6 +499,12 @@ Styled.Close = styled(Close)`
}
`;
Styled.BackButton = styled(BackButton)`
position: absolute;
left: var(--dialog-header-paddingLeft);
top: var(--dialog-header-paddingTop);
`;
Styled.Title = styled(Title)`
flex: 1;
+4 -4
View File
@@ -9,9 +9,11 @@ export enum DialogTypes {
FillDetails = 'FillDetails',
Help = 'Help',
ExternalNavKeplr = 'ExternalNavKeplr',
ManageFunds = 'ManageFunds',
MnemonicExport = 'MnemonicExport',
MobileSignIn = 'MobileSignIn',
MobileDownload = 'MobileDownload',
NewMarketAgreement = 'NewMarketAgreement',
NewMarketMessageDetails = 'NewMarketMessageDetails',
Onboarding = 'Onboarding',
OrderDetails = 'OrderDetails',
Preferences = 'Preferences',
@@ -21,9 +23,7 @@ export enum DialogTypes {
Trade = 'Trade',
Transfer = 'Transfer',
Withdraw = 'Withdraw',
ManageFunds = 'ManageFunds',
NewMarketMessageDetails = 'NewMarketMessageDetails',
NewMarketAgreement = 'NewMarketAgreement',
WithdrawalGated = 'WithdrawalGated',
}
export enum TradeBoxDialogTypes {
+4
View File
@@ -7,6 +7,7 @@ import { useDebounce } from './useDebounce';
import { useInterval } from './useInterval';
import { useDocumentTitle } from './useDocumentTitle';
import { useDydxClient } from './useDydxClient';
import { useEnvFeatures } from './useEnvFeatures';
import { useGovernanceVariables } from './useGovernanceVariables';
import { useAccountBalance } from './useAccountBalance';
import { useAccounts } from './useAccounts';
@@ -26,6 +27,7 @@ import { useStringGetter } from './useStringGetter';
import { useSubaccount } from './useSubaccount';
import { useTradeFormInputs } from './useTradeFormInputs';
import { useURLConfigs } from './useURLConfigs';
import { useWithdrawalInfo } from './useWithdrawalInfo';
export {
useApiState,
@@ -36,6 +38,7 @@ export {
useDebounce,
useDocumentTitle,
useDydxClient,
useEnvFeatures,
useGovernanceVariables,
useAccountBalance,
useAccounts,
@@ -56,4 +59,5 @@ export {
useSubaccount,
useTradeFormInputs,
useURLConfigs,
useWithdrawalInfo,
};
+13
View File
@@ -251,6 +251,17 @@ const useDydxClientContext = () => {
[compositeClient]
);
const getWithdrawalAndTransferGatingStatus = useCallback(async () => {
return await compositeClient?.validatorClient.get.GetWithdrawalAndTransferGatingStatus();
}, [compositeClient]);
const getWithdrawalCapacityByDenom = useCallback(
async ({ denom }: { denom: string }) => {
return await compositeClient?.validatorClient.get.getWithdrawalCapacityByDenom(denom);
},
[compositeClient]
);
return {
// Client initialization
connect: setNetworkConfig,
@@ -267,5 +278,7 @@ const useDydxClientContext = () => {
requestAllGovernanceProposals,
getCandlesForDatafeed,
screenAddresses,
getWithdrawalAndTransferGatingStatus,
getWithdrawalCapacityByDenom,
};
};
+15
View File
@@ -0,0 +1,15 @@
import { useSelector } from 'react-redux';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { getSelectedNetwork } from '@/state/appSelectors';
export interface EnvironmentFeatures {
reduceOnlySupported: boolean;
withdrawalSafetyEnabled: boolean;
}
export const useEnvFeatures = (): EnvironmentFeatures => {
const selectedNetwork = useSelector(getSelectedNetwork);
return ENVIRONMENT_CONFIG_MAP[selectedNetwork].featureFlags;
};
+2
View File
@@ -27,6 +27,7 @@ export interface LinksConfigs {
strideZoneApp?: string;
accountExportLearnMore?: string;
walletLearnMore?: string;
withdrawalGateLearnMore?: string;
}
export const useURLConfigs = (): LinksConfigs => {
@@ -54,5 +55,6 @@ export const useURLConfigs = (): LinksConfigs => {
strideZoneApp: linksConfigs.strideZoneApp || FALLBACK_URL,
accountExportLearnMore: linksConfigs.accountExportLearnMore || FALLBACK_URL,
walletLearnMore: linksConfigs.walletLearnMore || FALLBACK_URL,
withdrawalGateLearnMore: linksConfigs.withdrawalGateLearnMore || FALLBACK_URL,
};
};
+131
View File
@@ -0,0 +1,131 @@
import { useEffect, useMemo } from 'react';
import { useQuery } from 'react-query';
import BigNumber from 'bignumber.js';
import { encodeJson } from '@dydxprotocol/v4-client-js';
import { ByteArrayEncoding } from '@dydxprotocol/v4-client-js/build/src/lib/helpers';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { DialogTypes } from '@/constants/dialogs';
import { isMainnet } from '@/constants/networks';
import { useEnvFeatures } from './useEnvFeatures';
import { getApiState } from '@/state/appSelectors';
import { closeDialog, openDialog } from '@/state/dialogs';
import { getSelectedLocale } from '@/state/localizationSelectors';
import { formatRelativeTime } from '@/lib/dateTime';
import { BIG_NUMBERS, MustBigNumber } from '@/lib/numbers';
import { log } from '@/lib/telemetry';
import { useDydxClient } from './useDydxClient';
import { useTokenConfigs } from './useTokenConfigs';
const BLOCK_TIME = isMainnet ? 1_000 : 1_500;
export const useWithdrawalInfo = ({
transferType,
}: {
transferType: 'withdrawal' | 'transfer';
}) => {
const { getWithdrawalAndTransferGatingStatus, getWithdrawalCapacityByDenom } = useDydxClient();
const { usdcDenom, usdcDecimals } = useTokenConfigs();
const apiState = useSelector(getApiState, shallowEqual);
const { height } = apiState || {};
const selectedLocale = useSelector(getSelectedLocale);
const dispatch = useDispatch();
const { withdrawalSafetyEnabled } = useEnvFeatures();
const { data: usdcWithdrawalCapacity } = useQuery({
enabled: withdrawalSafetyEnabled,
queryKey: 'usdcWithdrawalCapacity',
queryFn: async () => {
try {
const response = await getWithdrawalCapacityByDenom({ denom: usdcDenom });
return JSON.parse(encodeJson(response, ByteArrayEncoding.BIGINT));
} catch (error) {
log('useWithdrawalInfo/getWithdrawalCapacityByDenom', error);
}
},
refetchInterval: 60_000,
staleTime: 60_000,
});
const { data: withdrawalAndTransferGatingStatus } = useQuery({
enabled: withdrawalSafetyEnabled,
queryKey: 'withdrawalTransferGateStatus',
queryFn: async () => {
try {
return await getWithdrawalAndTransferGatingStatus();
} catch (error) {
log('useWithdrawalInfo/getWithdrawalAndTransferGatingStatus', error);
}
},
refetchInterval: 60_000,
staleTime: 60_000,
});
const capacity = useMemo(() => {
const capacityList = usdcWithdrawalCapacity?.limiterCapacityList;
if (!capacityList || capacityList.length < 2) {
if (!withdrawalSafetyEnabled) {
return BigNumber(Infinity);
}
return BIG_NUMBERS.ZERO;
}
const [{ capacity: daily }, { capacity: weekly }] = capacityList;
const dailyBN = MustBigNumber(daily);
const weeklyBN = MustBigNumber(weekly);
return BigNumber.minimum(dailyBN, weeklyBN).div(10 ** usdcDecimals);
}, [usdcDecimals, usdcWithdrawalCapacity]);
const withdrawalAndTransferGatingStatusValue = useMemo(() => {
const { withdrawalsAndTransfersUnblockedAtBlock } = withdrawalAndTransferGatingStatus ?? {};
if (
height &&
withdrawalsAndTransfersUnblockedAtBlock &&
height < withdrawalsAndTransfersUnblockedAtBlock &&
withdrawalSafetyEnabled
) {
return {
estimatedUnblockTime: formatRelativeTime(
Date.now() + (withdrawalsAndTransfersUnblockedAtBlock - height) * BLOCK_TIME,
{
locale: selectedLocale,
largestUnit: 'day',
}
),
isGated: true,
};
}
return {
estimatedUnblockTime: null,
isGated: false,
};
}, [height, withdrawalAndTransferGatingStatus, withdrawalSafetyEnabled]);
useEffect(() => {
if (
withdrawalAndTransferGatingStatusValue.isGated &&
withdrawalAndTransferGatingStatusValue.estimatedUnblockTime &&
withdrawalSafetyEnabled
) {
dispatch(closeDialog());
dispatch(
openDialog({
type: DialogTypes.WithdrawalGated,
dialogProps: {
transferType,
estimatedUnblockTime: withdrawalAndTransferGatingStatusValue.estimatedUnblockTime,
},
})
);
}
}, [transferType, withdrawalAndTransferGatingStatusValue.isGated, withdrawalSafetyEnabled]);
return {
usdcWithdrawalCapacity: capacity,
withdrawalAndTransferGatingStatus,
};
};
+7 -7
View File
@@ -11,27 +11,27 @@ import { DepositDialog } from '@/views/dialogs/DepositDialog';
import { DisconnectDialog } from '@/views/dialogs/DisconnectDialog';
import { DisplaySettingsDialog } from '@/views/dialogs/DisplaySettingsDialog';
import { ExchangeOfflineDialog } from '@/views/dialogs/ExchangeOfflineDialog';
import { ExternalNavStrideDialog } from '@/views/dialogs/ExternalNavStrideDialog';
import { HelpDialog } from '@/views/dialogs/HelpDialog';
import { ExternalLinkDialog } from '@/views/dialogs/ExternalLinkDialog';
import { ExternalNavKeplrDialog } from '@/views/dialogs/ExternalNavKeplrDialog';
import { ManageFundsDialog } from '@/views/dialogs/ManageFundsDialog';
import { MnemonicExportDialog } from '@/views/dialogs/MnemonicExportDialog';
import { MobileSignInDialog } from '@/views/dialogs/MobileSignInDialog';
import { NewMarketAgreementDialog } from '@/views/dialogs/NewMarketAgreementDialog';
import { NewMarketMessageDetailsDialog } from '@/views/dialogs/NewMarketMessageDetailsDialog';
import { OnboardingDialog } from '@/views/dialogs/OnboardingDialog';
import { PreferencesDialog } from '@/views/dialogs/PreferencesDialog';
import { RateLimitDialog } from '@/views/dialogs/RateLimitDialog';
import { RestrictedGeoDialog } from '@/views/dialogs/RestrictedGeoDialog';
import { RestrictedWalletDialog } from '@/views/dialogs/RestrictedWalletDialog';
import { TradeDialog } from '@/views/dialogs/TradeDialog';
import { TransferDialog } from '@/views/dialogs/TransferDialog';
import { RestrictedWalletDialog } from '@/views/dialogs/RestrictedWalletDialog';
import { WithdrawDialog } from '@/views/dialogs/WithdrawDialog';
import { ManageFundsDialog } from '@/views/dialogs/ManageFundsDialog';
import { WithdrawalGateDialog } from '@/views/dialogs/WithdrawalGateDialog';
import { OrderDetailsDialog } from '@/views/dialogs/DetailsDialog/OrderDetailsDialog';
import { FillDetailsDialog } from '@/views/dialogs/DetailsDialog/FillDetailsDialog';
import { NewMarketMessageDetailsDialog } from '@/views/dialogs/NewMarketMessageDetailsDialog';
import { NewMarketAgreementDialog } from '@/views/dialogs/NewMarketAgreementDialog';
import { ExternalNavStrideDialog } from '@/views/dialogs/ExternalNavStrideDialog';
import { MobileDownloadDialog } from '@/views/dialogs/MobileDownloadDialog';
export const DialogManager = () => {
const dispatch = useDispatch();
@@ -64,7 +64,6 @@ export const DialogManager = () => {
[DialogTypes.ExternalNavStride]: <ExternalNavStrideDialog {...modalProps} />,
[DialogTypes.MnemonicExport]: <MnemonicExportDialog {...modalProps} />,
[DialogTypes.MobileSignIn]: <MobileSignInDialog {...modalProps} />,
[DialogTypes.MobileDownload]: <MobileDownloadDialog {...modalProps} />,
[DialogTypes.Onboarding]: <OnboardingDialog {...modalProps} />,
[DialogTypes.OrderDetails]: <OrderDetailsDialog {...modalProps} />,
[DialogTypes.Preferences]: <PreferencesDialog {...modalProps} />,
@@ -74,6 +73,7 @@ export const DialogManager = () => {
[DialogTypes.Trade]: <TradeDialog {...modalProps} />,
[DialogTypes.Transfer]: <TransferDialog {...modalProps} />,
[DialogTypes.Withdraw]: <WithdrawDialog {...modalProps} />,
[DialogTypes.WithdrawalGated]: <WithdrawalGateDialog {...modalProps} />,
[DialogTypes.ManageFunds]: <ManageFundsDialog {...modalProps} />,
[DialogTypes.NewMarketMessageDetails]: <NewMarketMessageDetailsDialog {...modalProps} />,
[DialogTypes.NewMarketAgreement]: <NewMarketAgreementDialog {...modalProps} />,
+5
View File
@@ -0,0 +1,5 @@
import { ThemeColorBase } from '@/constants/styles/colors';
declare module 'styled-components' {
export interface DefaultTheme extends ThemeColorBase {}
}
-140
View File
@@ -1,140 +0,0 @@
import styled, { AnyStyledComponent, css } from 'styled-components';
import { layoutMixins } from '@/styles/layoutMixins';
import { Dialog } from '@/components/Dialog';
import { QrCode } from '@/components/QrCode';
import { useStringGetter } from '@/hooks';
import { STRING_KEYS } from '@/constants/localization';
type ElementProps = {
setIsOpen: (open: boolean) => void;
};
/*
When/if deployer deploys the web app with smartbanner, "smartbanner:button-url-apple" and/or
"smartbanner:button-url-google" <meta> are set.
This implementation assumes "smartbanner:button-url-apple" and "smartbanner:button-url-google"
are set to the same value with onelink or other redirect URL.
Since there is no way for the desktop web app to know what mobile device the user is using,
we should give a onelink URL which redirects to either iOS or Android app store depending on
the mobile device used to scan the link.
*/
// for testing only
// export const mobileAppUrl = "http://example.com";
let mobileAppUrl: string | undefined | null = undefined;
export const getMobileAppUrl = () => {
if (!mobileAppUrl) {
mobileAppUrl =
// for testing to verify <meta> is retrieved by name, QR code should show "@dYdX" as value
// document.querySelector('meta[name="twitter:creator"]')?.getAttribute('content') ??
document.querySelector('meta[name="smartbanner:button-url-apple"]')?.getAttribute('content') ??
document.querySelector('meta[name="smartbanner:button-url-google"]')?.getAttribute('content');
}
return mobileAppUrl;
}
const MobileQrCode = ({
url,
}: {
url: string;
}) => {
return (
<Styled.QrCodeContainer isShowing={true}>
<QrCode hasLogo size={432} value={url} />
</Styled.QrCodeContainer>
);
};
/*
MobileDownloadDialog should only been shown on desktop when mobileAppUrl has value. That's controlled by AccountMenu.tsx.
*/
export const MobileDownloadDialog = ({ setIsOpen }: ElementProps) => {
const stringGetter = useStringGetter();
const content = (
<MobileQrCode url={mobileAppUrl!} />
);
return (
<Dialog isOpen setIsOpen={setIsOpen} title={
stringGetter({ key: STRING_KEYS.DOWNLOAD_MOBILE_APP })
}>
<Styled.Content>{content}</Styled.Content>
</Dialog>
);
};
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Content = styled.div`
${layoutMixins.column}
gap: 1rem;
strong {
font-weight: 900;
color: var(--color-text-2);
}
footer {
${layoutMixins.row}
justify-content: space-between;
svg {
width: auto;
}
}
`;
Styled.WaitingSpan = styled.span`
strong {
color: var(--color-warning);
}
`;
Styled.QrCodeContainer = styled.figure<{ isShowing: boolean }>`
${layoutMixins.stack}
overflow: hidden;
border-radius: 0.75em;
cursor: pointer;
transition: 0.2s;
&:hover {
filter: brightness(var(--hover-filter-base));
}
> * {
position: relative;
transition: 0.16s;
}
> :first-child {
pointer-events: none;
${({ isShowing }) =>
!isShowing &&
css`
filter: blur(1rem) brightness(1.4);
will-change: filter;
`}
}
> span {
place-self: center;
font-size: 1.4em;
color: var(--color-text-2);
${({ isShowing }) =>
isShowing &&
css`
opacity: 0;
`}
}
`;
+101
View File
@@ -0,0 +1,101 @@
import styled, { AnyStyledComponent } from 'styled-components';
import { ButtonAction, ButtonType } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { useStringGetter, useURLConfigs } from '@/hooks';
import { LinkOutIcon } from '@/icons';
import { layoutMixins } from '@/styles/layoutMixins';
import { Button } from '@/components/Button';
import { Dialog } from '@/components/Dialog';
import { Icon, IconName } from '@/components/Icon';
type ElementProps = {
setIsOpen: (open: boolean) => void;
transferType: 'withdrawal' | 'transfer';
estimatedUnblockTime?: string | null;
};
export const WithdrawalGateDialog = ({
setIsOpen,
estimatedUnblockTime,
transferType,
}: ElementProps) => {
const stringGetter = useStringGetter();
const { withdrawalGateLearnMore } = useURLConfigs();
return (
<Dialog
isOpen
stacked
setIsOpen={setIsOpen}
title={
{
withdrawal: stringGetter({ key: STRING_KEYS.WITHDRAWALS_PAUSED }),
transfer: stringGetter({ key: STRING_KEYS.TRANSFERS_PAUSED }),
}[transferType]
}
slotIcon={
<Styled.IconContainer>
<Styled.Icon iconName={IconName.Warning} />
</Styled.IconContainer>
}
slotFooter={
<Styled.ButtonRow>
<Button
type={ButtonType.Link}
action={ButtonAction.Secondary}
href={withdrawalGateLearnMore}
>
{stringGetter({ key: STRING_KEYS.LEARN_MORE })}
<LinkOutIcon />
</Button>
<Button action={ButtonAction.Primary} onClick={() => setIsOpen(false)}>
{stringGetter({ key: STRING_KEYS.CLOSE })}
</Button>
</Styled.ButtonRow>
}
>
<Styled.Content>
{stringGetter({
key: STRING_KEYS.WITHDRAWALS_PAUSED_DESC,
params: {
ESTIMATED_DURATION: estimatedUnblockTime,
},
})}
</Styled.Content>
</Dialog>
);
};
const Styled: Record<string, AnyStyledComponent> = {};
Styled.IconContainer = styled.div`
display: flex;
justify-content: center;
align-items: center;
width: 4.5rem;
height: 4.5rem;
border-radius: 50%;
min-width: 4.5rem;
min-height: 4.5rem;
background-color: var(--color-gradient-warning);
`;
Styled.Icon = styled(Icon)`
color: var(--color-warning);
font-size: 2.5rem;
margin-bottom: 0.125rem;
`;
Styled.Content = styled.div`
${layoutMixins.column}
gap: 1rem;
`;
Styled.ButtonRow = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
`;
@@ -17,6 +17,7 @@ import {
MAX_PRICE_IMPACT,
MIN_CCTP_TRANSFER_AMOUNT,
NumberSign,
TOKEN_DECIMALS,
} from '@/constants/numbers';
import {
@@ -27,6 +28,8 @@ import {
useSelectedNetwork,
useStringGetter,
useSubaccount,
useTokenConfigs,
useWithdrawalInfo,
} from '@/hooks';
import { useLocalNotifications } from '@/hooks/useLocalNotifications';
@@ -85,6 +88,8 @@ export const WithdrawForm = () => {
const [withdrawAmount, setWithdrawAmount] = useState('');
const [slippage, setSlippage] = useState(isCctp ? 0 : 0.01); // 0.1% slippage
const debouncedAmount = useDebounce<string>(withdrawAmount, 500);
const { usdcLabel } = useTokenConfigs();
const { usdcWithdrawalCapacity } = useWithdrawalInfo({ transferType: 'withdrawal' });
const isValidAddress = toAddress && isAddress(toAddress);
@@ -321,61 +326,102 @@ export const WithdrawForm = () => {
const { sanctionedAddresses } = useRestrictions();
const errorMessage = useMemo(() => {
const { alertType, errorMessage } = useMemo(() => {
if (error) {
return error;
return {
errorMessage: error,
};
}
if (routeErrors) {
return routeErrorMessage
? stringGetter({
key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE,
params: { ERROR_MESSAGE: routeErrorMessage },
})
: stringGetter({ key: STRING_KEYS.SOMETHING_WENT_WRONG });
return {
errorMessage: routeErrorMessage
? stringGetter({
key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE,
params: { ERROR_MESSAGE: routeErrorMessage },
})
: stringGetter({ key: STRING_KEYS.SOMETHING_WENT_WRONG }),
};
}
if (!toAddress) return stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_ADDRESS });
if (!toAddress) {
return {
alertType: AlertType.Warning,
errorMessage: stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_ADDRESS }),
};
}
if (sanctionedAddresses.has(toAddress))
return stringGetter({
key: STRING_KEYS.TRANSFER_INVALID_DYDX_ADDRESS,
});
return {
errorMessage: stringGetter({
key: STRING_KEYS.TRANSFER_INVALID_DYDX_ADDRESS,
}),
};
if (debouncedAmountBN) {
if (!chainIdStr && !exchange) {
return stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_CHAIN });
return {
errorMessage: stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_CHAIN }),
};
} else if (!toToken) {
return stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_ASSET });
return {
errorMessage: stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_ASSET }),
};
}
}
if (MustBigNumber(debouncedAmountBN).gt(MustBigNumber(freeCollateralBN))) {
return stringGetter({ key: STRING_KEYS.WITHDRAW_MORE_THAN_FREE });
if (debouncedAmountBN.gt(MustBigNumber(freeCollateralBN))) {
return {
errorMessage: 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 (debouncedAmountBN.gte(MAX_CCTP_TRANSFER_AMOUNT)) {
return {
errorMessage: stringGetter({
key: STRING_KEYS.MAX_CCTP_TRANSFER_LIMIT_EXCEEDED,
params: {
MAX_CCTP_TRANSFER_AMOUNT: MAX_CCTP_TRANSFER_AMOUNT,
},
}),
};
}
if (
!debouncedAmountBN.isZero() &&
MustBigNumber(debouncedAmountBN).lte(MIN_CCTP_TRANSFER_AMOUNT)
) {
return 'Amount must be greater than 10 USDC';
return {
errorMessage: 'Amount must be greater than 10 USDC',
};
}
}
if (isMainnet && MustBigNumber(summary?.aggregatePriceImpact).gte(MAX_PRICE_IMPACT)) {
return stringGetter({ key: STRING_KEYS.PRICE_IMPACT_TOO_HIGH });
return { errorMessage: stringGetter({ key: STRING_KEYS.PRICE_IMPACT_TOO_HIGH }) };
}
return undefined;
// Withdrawal Safety
if (usdcWithdrawalCapacity.gt(0) && debouncedAmountBN.gt(usdcWithdrawalCapacity)) {
return {
alertType: AlertType.Warning,
errorMessage: stringGetter({
key: STRING_KEYS.WITHDRAWAL_LIMIT_OVER,
params: {
USDC_LIMIT: (
<span>
{usdcWithdrawalCapacity.toFormat(TOKEN_DECIMALS)}
<Styled.Tag>{usdcLabel}</Styled.Tag>
</span>
),
},
}),
};
}
return {
errorMessage: undefined,
};
}, [
error,
routeErrors,
@@ -388,6 +434,7 @@ export const WithdrawForm = () => {
sanctionedAddresses,
stringGetter,
summary,
usdcWithdrawalCapacity,
]);
const isInvalidNobleAddress = Boolean(
@@ -448,7 +495,11 @@ export const WithdrawForm = () => {
}
/>
</Styled.WithDetailsReceipt>
{errorMessage && <AlertMessage type={AlertType.Error}>{errorMessage}</AlertMessage>}
{errorMessage && (
<Styled.AlertMessage type={alertType ?? AlertType.Error}>
{errorMessage}
</Styled.AlertMessage>
)}
<Styled.Footer>
<WithdrawButtonAndReceipt
isDisabled={isDisabled}
@@ -465,6 +516,10 @@ export const WithdrawForm = () => {
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Tag = styled(Tag)`
margin-left: 0.5ch;
`;
Styled.DiffOutput = styled(DiffOutput)`
--diffOutput-valueWithDiff-fontSize: 1em;
`;
@@ -484,6 +539,10 @@ Styled.DestinationRow = styled.div`
gap: 1rem;
`;
Styled.AlertMessage = styled(AlertMessage)`
display: inline;
`;
Styled.WithDetailsReceipt = styled(WithDetailsReceipt)`
--withReceipt-backgroundColor: var(--color-layer-2);
`;
+2
View File
@@ -20,6 +20,7 @@ import {
useStringGetter,
useSubaccount,
useTokenConfigs,
useWithdrawalInfo,
} from '@/hooks';
import { formMixins } from '@/styles/formMixins';
@@ -64,6 +65,7 @@ export const TransferForm = ({
const { nativeTokenBalance, usdcBalance } = useAccountBalance();
const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const { tokensConfigs, usdcLabel, chainTokenLabel } = useTokenConfigs();
useWithdrawalInfo({ transferType: 'transfer' });
const {
address: recipientAddress,
-13
View File
@@ -40,7 +40,6 @@ import { getAppTheme } from '@/state/configsSelectors';
import { isTruthy } from '@/lib/isTruthy';
import { truncateAddress } from '@/lib/wallet';
import { MustBigNumber } from '@/lib/numbers';
import { getMobileAppUrl } from '../dialogs/MobileDownloadDialog';
export const AccountMenu = () => {
const stringGetter = useStringGetter();
@@ -190,18 +189,6 @@ export const AccountMenu = () => {
label: stringGetter({ key: STRING_KEYS.DISPLAY_SETTINGS }),
onSelect: () => dispatch(openDialog({ type: DialogTypes.DisplaySettings })),
},
...(getMobileAppUrl()
? [
{
value: 'MobileDownload',
icon: <Icon iconName={IconName.Qr} />,
label: stringGetter({ key: STRING_KEYS.DOWNLOAD_MOBILE_APP }),
onSelect: () => {
dispatch(openDialog({ type: DialogTypes.MobileDownload }));
},
},
]
: []),
...(onboardingState === OnboardingState.AccountConnected && hdKey
? [
{
+1 -1
View File
@@ -22,6 +22,6 @@
"@/*": ["src/*"]
}
},
"include": ["src", "scripts"],
"include": ["src", "scripts", "styled.d.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}