Compare commits
2 Commits
main
...
fix-swallo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed7db77a34 | ||
|
|
854f12fcf1 |
@ -3,7 +3,6 @@ VITE_BASE_URL=
|
||||
VITE_ALCHEMY_API_KEY=
|
||||
|
||||
VITE_PK_ENCRYPTION_KEY=
|
||||
VITE_ROUTER_TYPE=
|
||||
|
||||
VITE_V3_TOKEN_ADDRESS=
|
||||
VITE_TOKEN_MIGRATION_URI=
|
||||
|
||||
26
README.md
26
README.md
@ -66,12 +66,10 @@ This will automatically open your default browser at `http://localhost:61000`.
|
||||
|
||||
Add or modify the relevant endpoints, links and options in `/public/configs/env.json`.
|
||||
You'll need to provide a Wallet Connect project id to enable onboarding and wallet connection:
|
||||
|
||||
- Create a project on https://cloud.walletconnect.com/app
|
||||
- Copy over the project ID into this [field](https://github.com/dydxprotocol/v4-web/blob/67ecbd75b43e0c264b7b4d2d9b3d969830b0621c/public/configs/env.json#L822C33-L822C46)
|
||||
|
||||
## Part 4: Set Enviornment variables
|
||||
|
||||
Set environment variables via `.env`.
|
||||
|
||||
- `VITE_BASE_URL` (required): The base URL of the deployment (e.g., `https://www.example.com`).
|
||||
@ -98,12 +96,10 @@ Select "Import Git Repository" from your dashboard, and provide the URL of this
|
||||
### Step 2: Configure your project
|
||||
|
||||
For the "Build & Development Settings", we recommend the following:
|
||||
|
||||
- Framework Preset: `Vite`
|
||||
- Build Command (override): `pnpm run build`
|
||||
|
||||
By default, the dev server runs in development mode and the build command runs in production mode. To override the default mode, you can pass in the `--mode` option flag. For example, if you want to build your app for testnet:
|
||||
|
||||
```
|
||||
pnpm run build --mode testnet
|
||||
```
|
||||
@ -118,14 +114,6 @@ For more details, check out Vercel's [official documentation](https://vercel.com
|
||||
|
||||
## Deploying to IPFS
|
||||
|
||||
### Must Enable HashRouting
|
||||
|
||||
Add the following to `.env` file
|
||||
|
||||
```
|
||||
VITE_ROUTER_TYPE=hash
|
||||
```
|
||||
|
||||
### web3.storage: deploy to IPFS via web3.storage using the provided script
|
||||
|
||||
Export the API token as an environment variable (replace `your_token` with the generated token), and run the script to build and deploy to IPFS:
|
||||
@ -162,14 +150,12 @@ Replace `your_cid` with the actual CID.
|
||||
We recommend that you add your website to Cloudflare for additional security settings.
|
||||
|
||||
To block OFAC Sanctioned countries:
|
||||
|
||||
1. Navigate Websites > Domain > Security > WAF
|
||||
|
||||
2. Create Rule with the following settings:
|
||||
|
||||
- If incoming requests match
|
||||
`(ip.geoip.country eq "CU") or (ip.geoip.country eq "IR") or (ip.geoip.country eq "KP") or (ip.geoip.country eq "SY") or (ip.geoip.country eq "MM") or (ip.geoip.subdivision_1_iso_code eq "UA-09") or (ip.geoip.subdivision_1_iso_code eq "UA-14") or (ip.geoip.subdivision_1_iso_code eq "UA-43")`
|
||||
- This rule will bring up a Cloudflare page when a restricted geography tries to access your site. You will have the option to display:
|
||||
1. Custom Text
|
||||
- (e.g. `Because you appear to be a resident of, or trading from, a jurisdiction that violates our terms of use, or have engaged in activity that violates our terms of use, you have been blocked. You may withdraw your funds from the protocol at any time.`)
|
||||
2. Default Cloudflare WAF block page
|
||||
* If incoming requests match
|
||||
`(ip.geoip.country eq "CU") or (ip.geoip.country eq "IR") or (ip.geoip.country eq "KP") or (ip.geoip.country eq "SY") or (ip.geoip.country eq "MM") or (ip.geoip.subdivision_1_iso_code eq "UA-09") or (ip.geoip.subdivision_1_iso_code eq "UA-14") or (ip.geoip.subdivision_1_iso_code eq "UA-43")`
|
||||
* This rule will bring up a Cloudflare page when a restricted geography tries to access your site. You will have the option to display:
|
||||
1. Custom Text
|
||||
- (e.g. `Because you appear to be a resident of, or trading from, a jurisdiction that violates our terms of use, or have engaged in activity that violates our terms of use, you have been blocked. You may withdraw your funds from the protocol at any time.`)
|
||||
2. Default Cloudflare WAF block page
|
||||
|
||||
@ -40,9 +40,9 @@
|
||||
"@cosmjs/proto-signing": "^0.32.1",
|
||||
"@cosmjs/stargate": "^0.32.1",
|
||||
"@cosmjs/tendermint-rpc": "^0.32.1",
|
||||
"@dydxprotocol/v4-abacus": "^1.4.12",
|
||||
"@dydxprotocol/v4-abacus": "^1.4.5",
|
||||
"@dydxprotocol/v4-client-js": "^1.0.20",
|
||||
"@dydxprotocol/v4-localization": "^1.1.35",
|
||||
"@dydxprotocol/v4-localization": "^1.1.30",
|
||||
"@ethersproject/providers": "^5.7.2",
|
||||
"@js-joda/core": "^5.5.3",
|
||||
"@radix-ui/react-accordion": "^1.1.2",
|
||||
|
||||
24
pnpm-lock.yaml
generated
24
pnpm-lock.yaml
generated
@ -1,9 +1,5 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
follow-redirects: 1.15.3
|
||||
|
||||
@ -30,14 +26,14 @@ dependencies:
|
||||
specifier: ^0.32.1
|
||||
version: 0.32.2
|
||||
'@dydxprotocol/v4-abacus':
|
||||
specifier: ^1.4.12
|
||||
version: 1.4.12
|
||||
specifier: ^1.4.5
|
||||
version: 1.4.5
|
||||
'@dydxprotocol/v4-client-js':
|
||||
specifier: ^1.0.20
|
||||
version: 1.0.20
|
||||
'@dydxprotocol/v4-localization':
|
||||
specifier: ^1.1.35
|
||||
version: 1.1.35
|
||||
specifier: ^1.1.30
|
||||
version: 1.1.30
|
||||
'@ethersproject/providers':
|
||||
specifier: ^5.7.2
|
||||
version: 5.7.2
|
||||
@ -1290,8 +1286,8 @@ packages:
|
||||
resolution: {integrity: sha512-Gg5t+eR7vPJMAmhkFt6CZrzPd0EKpAslWwk5rFVYZpJsM8JG5KT9XQ99hgNM3Ov6ScNoIWbXkpX27F6A9cXR4Q==}
|
||||
dev: false
|
||||
|
||||
/@dydxprotocol/v4-abacus@1.4.12:
|
||||
resolution: {integrity: sha512-dNQFaNrLTujyViMH2JEtf9Vn2ew2le+qTWexktK5h7AeADBbS9uxQknMIFhfwzGkbS1QqqszBT1plrDmQ+E6nQ==}
|
||||
/@dydxprotocol/v4-abacus@1.4.5:
|
||||
resolution: {integrity: sha512-LhJmpIaUkHCsSiHx+jdk+59euvGp2E+gGFelpfmOYpH1+enZgEXDQvWsgHSFEQxYP3mRiGG4uo+ZYNGdvffg7g==}
|
||||
dev: false
|
||||
|
||||
/@dydxprotocol/v4-client-js@1.0.20:
|
||||
@ -1323,8 +1319,8 @@ packages:
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
/@dydxprotocol/v4-localization@1.1.35:
|
||||
resolution: {integrity: sha512-q5JFYoL/QanHXOtFqRa2owBZJibi1sMpSm3dAcxs9x0/xe8mo6fWcnbQfhl8k7g0/tv7PsBc+e3rbWD0EfvGiA==}
|
||||
/@dydxprotocol/v4-localization@1.1.30:
|
||||
resolution: {integrity: sha512-TZfWWRSOxcjLHs972wlJVVHkE7+DVqAUnGZSs24HYHsPtUkPhZiNXMOA2Vk9YddQxumhM79xIRH0cmJSe5DDUg==}
|
||||
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
|
||||
|
||||
@ -1,22 +1,12 @@
|
||||
[
|
||||
{
|
||||
"chainId": "1",
|
||||
"tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"name": "Ethereum"
|
||||
},
|
||||
{
|
||||
"chainId": "5",
|
||||
"tokenAddress": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F",
|
||||
"name": "Ethereum Goerli"
|
||||
},
|
||||
{
|
||||
"chainId": "43114",
|
||||
"tokenAddress": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
||||
"name": "Avalanche"
|
||||
},
|
||||
{
|
||||
"chainId": "10",
|
||||
"tokenAddress": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
||||
"name": "optimism"
|
||||
}
|
||||
]
|
||||
{
|
||||
"chainId": "1",
|
||||
"tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"name": "Ethereum"
|
||||
},
|
||||
{
|
||||
"chainId": "5",
|
||||
"tokenAddress": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F",
|
||||
"name": "Ethereum Goerli"
|
||||
}
|
||||
]
|
||||
@ -725,7 +725,7 @@
|
||||
}
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true
|
||||
"reduceOnlySupported": false
|
||||
}
|
||||
},
|
||||
"dydxprotocol-testnet-dydx": {
|
||||
@ -812,7 +812,7 @@
|
||||
}
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true
|
||||
"reduceOnlySupported": false
|
||||
}
|
||||
},
|
||||
"dydxprotocol-testnet-nodefleet": {
|
||||
@ -899,7 +899,7 @@
|
||||
}
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true
|
||||
"reduceOnlySupported": false
|
||||
}
|
||||
},
|
||||
"dydxprotocol-testnet-kingnodes": {
|
||||
@ -986,7 +986,7 @@
|
||||
}
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true
|
||||
"reduceOnlySupported": false
|
||||
}
|
||||
},
|
||||
"dydxprotocol-testnet-liquify": {
|
||||
@ -1073,7 +1073,7 @@
|
||||
}
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true
|
||||
"reduceOnlySupported": false
|
||||
}
|
||||
},
|
||||
"dydxprotocol-testnet-polkachu": {
|
||||
@ -1151,7 +1151,7 @@
|
||||
}
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true
|
||||
"reduceOnlySupported": false
|
||||
}
|
||||
},
|
||||
"dydxprotocol-testnet-bware": {
|
||||
@ -1238,7 +1238,7 @@
|
||||
}
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true
|
||||
"reduceOnlySupported": false
|
||||
}
|
||||
},
|
||||
"dydxprotocol-mainnet": {
|
||||
|
||||
@ -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" },
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -71,8 +71,7 @@
|
||||
"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",
|
||||
"launchIncentive": "https://cloud.chaoslabs.co"
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"dydx-testnet-4": {
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
@ -93,8 +92,7 @@
|
||||
"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",
|
||||
"launchIncentive": "https://cloud.chaoslabs.co"
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"[mainnet chain id]": {
|
||||
"tos": "[HTTP link to TOS]",
|
||||
@ -115,8 +113,7 @@
|
||||
"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]",
|
||||
"launchIncentive": "[HTTP link to launch incentive host, can be null]"
|
||||
"walletLearnMore": "[HTTP link to wallet learn more, can be null]"
|
||||
}
|
||||
},
|
||||
"wallets": {
|
||||
@ -261,8 +258,7 @@
|
||||
"faucet": "https://faucet.v4dev.dydx.exchange"
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true,
|
||||
"usePessimisticCollateralCheck": false
|
||||
"reduceOnlySupported": true
|
||||
}
|
||||
},
|
||||
"dydxprotocol-dev-2": {
|
||||
@ -287,8 +283,7 @@
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true,
|
||||
"usePessimisticCollateralCheck": false
|
||||
"reduceOnlySupported": true
|
||||
}
|
||||
},
|
||||
"dydxprotocol-dev-4": {
|
||||
@ -314,8 +309,7 @@
|
||||
"faucet": "https://faucet.v4dev4.dydx.exchange"
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true,
|
||||
"usePessimisticCollateralCheck": false
|
||||
"reduceOnlySupported": true
|
||||
}
|
||||
},
|
||||
"dydxprotocol-dev-5": {
|
||||
@ -340,8 +334,7 @@
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true,
|
||||
"usePessimisticCollateralCheck": false
|
||||
"reduceOnlySupported": true
|
||||
}
|
||||
},
|
||||
"dydxprotocol-staging": {
|
||||
@ -367,8 +360,7 @@
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true,
|
||||
"usePessimisticCollateralCheck": false
|
||||
"reduceOnlySupported": true
|
||||
}
|
||||
},
|
||||
"dydxprotocol-staging-forced-update": {
|
||||
@ -401,8 +393,7 @@
|
||||
}
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true,
|
||||
"usePessimisticCollateralCheck": false
|
||||
"reduceOnlySupported": true
|
||||
}
|
||||
},
|
||||
"dydxprotocol-staging-west": {
|
||||
@ -428,8 +419,7 @@
|
||||
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true,
|
||||
"usePessimisticCollateralCheck": false
|
||||
"reduceOnlySupported": true
|
||||
}
|
||||
},
|
||||
"dydxprotocol-testnet": {
|
||||
@ -459,8 +449,7 @@
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true,
|
||||
"usePessimisticCollateralCheck": true
|
||||
"reduceOnlySupported": false
|
||||
}
|
||||
},
|
||||
"dydxprotocol-testnet-dydx": {
|
||||
@ -486,8 +475,7 @@
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true,
|
||||
"usePessimisticCollateralCheck": true
|
||||
"reduceOnlySupported": false
|
||||
}
|
||||
},
|
||||
"dydxprotocol-testnet-nodefleet": {
|
||||
@ -513,8 +501,7 @@
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true,
|
||||
"usePessimisticCollateralCheck": true
|
||||
"reduceOnlySupported": false
|
||||
}
|
||||
},
|
||||
"dydxprotocol-testnet-kingnodes": {
|
||||
@ -540,8 +527,7 @@
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true,
|
||||
"usePessimisticCollateralCheck": true
|
||||
"reduceOnlySupported": false
|
||||
}
|
||||
},
|
||||
"dydxprotocol-testnet-liquify": {
|
||||
@ -567,8 +553,7 @@
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true,
|
||||
"usePessimisticCollateralCheck": true
|
||||
"reduceOnlySupported": false
|
||||
}
|
||||
},
|
||||
"dydxprotocol-testnet-polkachu": {
|
||||
@ -594,8 +579,7 @@
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true,
|
||||
"usePessimisticCollateralCheck": true
|
||||
"reduceOnlySupported": false
|
||||
}
|
||||
},
|
||||
"dydxprotocol-testnet-bware": {
|
||||
@ -621,8 +605,7 @@
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": true,
|
||||
"usePessimisticCollateralCheck": true
|
||||
"reduceOnlySupported": false
|
||||
}
|
||||
},
|
||||
"dydxprotocol-mainnet": {
|
||||
@ -648,8 +631,7 @@
|
||||
"nobleValidator": "[noble validator endpoint for mainnet]"
|
||||
},
|
||||
"featureFlags": {
|
||||
"reduceOnlySupported": false,
|
||||
"usePessimisticCollateralCheck": true
|
||||
"reduceOnlySupported": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.9 KiB |
21
src/App.tsx
21
src/App.tsx
@ -1,5 +1,5 @@
|
||||
import { lazy, Suspense, useMemo } from 'react';
|
||||
import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
import styled, { AnyStyledComponent, css } from 'styled-components';
|
||||
import { WagmiConfig } from 'wagmi';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
@ -26,7 +26,6 @@ import { RestrictionProvider } from '@/hooks/useRestrictions';
|
||||
import { SubaccountProvider } from '@/hooks/useSubaccount';
|
||||
|
||||
import { GuardedMobileRoute } from '@/components/GuardedMobileRoute';
|
||||
import { LoadingSpace } from '@/components/Loading/LoadingSpinner';
|
||||
|
||||
import { HeaderDesktop } from '@/layout/Header/HeaderDesktop';
|
||||
import { FooterDesktop } from '@/layout/Footer/FooterDesktop';
|
||||
@ -35,12 +34,12 @@ import { NotificationsToastArea } from '@/layout/NotificationsToastArea';
|
||||
import { DialogManager } from '@/layout/DialogManager';
|
||||
import { GlobalCommandDialog } from '@/views/dialogs/GlobalCommandDialog';
|
||||
|
||||
import { parseLocationHash } from '@/lib/urlUtils';
|
||||
import { config } from '@/lib/wagmi';
|
||||
|
||||
import { breakpoints } from '@/styles';
|
||||
import { GlobalStyle } from '@/styles/globalStyle';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import { LoadingSpace } from './components/Loading/LoadingSpinner';
|
||||
|
||||
import '@/styles/constants.css';
|
||||
import '@/styles/fonts.css';
|
||||
@ -69,14 +68,6 @@ const Content = () => {
|
||||
const isShowingHeader = isNotTablet;
|
||||
const isShowingFooter = useShouldShowFooter();
|
||||
const { chainTokenLabel } = useTokenConfigs();
|
||||
const location = useLocation();
|
||||
|
||||
const pathFromHash = useMemo(() => {
|
||||
if (location.hash === '') {
|
||||
return '';
|
||||
}
|
||||
return parseLocationHash(location.hash);
|
||||
}, [location.hash]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -111,10 +102,8 @@ const Content = () => {
|
||||
|
||||
<Route path={AppRoute.Terms} element={<TermsOfUsePage />} />
|
||||
<Route path={AppRoute.Privacy} element={<PrivacyPolicyPage />} />
|
||||
<Route
|
||||
path="*"
|
||||
element={<Navigate to={pathFromHash || DEFAULT_TRADE_ROUTE} replace />}
|
||||
/>
|
||||
|
||||
<Route path="*" element={<Navigate to={DEFAULT_TRADE_ROUTE} replace />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
</Styled.Main>
|
||||
|
||||
@ -39,7 +39,7 @@ export const SearchSelectMenu = ({
|
||||
disabled,
|
||||
label,
|
||||
items,
|
||||
withSearch = true,
|
||||
withSearch,
|
||||
withReceiptItems,
|
||||
}: SearchSelectMenuProps) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
@ -77,7 +77,6 @@ export const SearchSelectMenu = ({
|
||||
withSearch={withSearch}
|
||||
onItemSelected={() => setOpen(false)}
|
||||
withStickyLayout
|
||||
$withSearch={withSearch}
|
||||
/>
|
||||
</Styled.Popover>
|
||||
</Styled.WithDetailsReceipt>
|
||||
@ -128,7 +127,7 @@ Styled.Popover = styled(Popover)`
|
||||
box-shadow: none;
|
||||
`;
|
||||
|
||||
Styled.ComboboxMenu = styled(ComboboxMenu)<{ $withSearch?: boolean }>`
|
||||
Styled.ComboboxMenu = styled(ComboboxMenu)`
|
||||
${layoutMixins.withInnerHorizontalBorders}
|
||||
|
||||
--comboboxMenu-backgroundColor: var(--color-layer-4);
|
||||
@ -141,8 +140,7 @@ Styled.ComboboxMenu = styled(ComboboxMenu)<{ $withSearch?: boolean }>`
|
||||
--comboboxMenu-item-checked-textColor: var(--color-text-2);
|
||||
--comboboxMenu-item-highlighted-textColor: var(--color-text-2);
|
||||
|
||||
--stickyArea1-topHeight: ${({ $withSearch }) =>
|
||||
!$withSearch ? '0' : 'var(--form-input-height)'};
|
||||
--stickyArea1-topHeight: var(--form-input-height);
|
||||
|
||||
input:focus-visible {
|
||||
outline: none;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { type ReactNode, useState, useEffect } from 'react';
|
||||
import { type ReactNode, useState } from 'react';
|
||||
|
||||
import { ButtonAction, ButtonState } from '@/constants/buttons';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
@ -8,7 +8,6 @@ import { Button, type ButtonStateConfig, type ButtonProps } from '@/components/B
|
||||
|
||||
type ElementProps = {
|
||||
timeoutInSeconds: number;
|
||||
onTimeOut?: () => void;
|
||||
slotFinal?: ReactNode;
|
||||
} & ButtonProps;
|
||||
|
||||
@ -17,7 +16,6 @@ export type TimeoutButtonProps = ElementProps;
|
||||
export const TimeoutButton = ({
|
||||
children,
|
||||
timeoutInSeconds,
|
||||
onTimeOut,
|
||||
slotFinal,
|
||||
...otherProps
|
||||
}: TimeoutButtonProps) => {
|
||||
@ -27,11 +25,6 @@ export const TimeoutButton = ({
|
||||
|
||||
const secondsLeft = Math.max(0, (timeoutDeadline - now) / 1000);
|
||||
|
||||
useEffect(() => {
|
||||
if (secondsLeft > 0) return;
|
||||
onTimeOut?.();
|
||||
}, [secondsLeft]);
|
||||
|
||||
if (slotFinal && secondsLeft <= 0) return slotFinal;
|
||||
|
||||
return (
|
||||
|
||||
@ -81,9 +81,6 @@ export enum AnalyticsEvent {
|
||||
TradePlaceOrderConfirmed = 'TradePlaceOrderConfirmed',
|
||||
TradeCancelOrder = 'TradeCancelOrder',
|
||||
TradeCancelOrderConfirmed = 'TradeCancelOrderConfirmed',
|
||||
|
||||
// Notification
|
||||
NotificationAction = 'NotificationAction',
|
||||
}
|
||||
|
||||
export type AnalyticsEventData<T extends AnalyticsEvent> =
|
||||
@ -147,17 +144,9 @@ export type AnalyticsEventData<T extends AnalyticsEvent> =
|
||||
validatorUrl: string;
|
||||
}
|
||||
: T extends AnalyticsEvent.TransferDeposit
|
||||
? {
|
||||
chainId?: string;
|
||||
tokenAddress?: string;
|
||||
tokenSymbol?: string;
|
||||
}
|
||||
? {}
|
||||
: T extends AnalyticsEvent.TransferWithdraw
|
||||
? {
|
||||
chainId?: string;
|
||||
tokenAddress?: string;
|
||||
tokenSymbol?: string;
|
||||
}
|
||||
? {}
|
||||
: // Trading
|
||||
T extends AnalyticsEvent.TradeOrderTypeSelected
|
||||
? {
|
||||
@ -183,12 +172,6 @@ export type AnalyticsEventData<T extends AnalyticsEvent> =
|
||||
/** URL/IP of node the order was sent to */
|
||||
validatorUrl: string;
|
||||
}
|
||||
: // Notifcation
|
||||
T extends AnalyticsEvent.NotificationAction
|
||||
? {
|
||||
type: string;
|
||||
id: string;
|
||||
}
|
||||
: never;
|
||||
|
||||
export const DEFAULT_TRANSACTION_MEMO = 'dYdX Frontend (web)';
|
||||
|
||||
@ -11,7 +11,6 @@ export enum DialogTypes {
|
||||
ExternalNavKeplr = 'ExternalNavKeplr',
|
||||
MnemonicExport = 'MnemonicExport',
|
||||
MobileSignIn = 'MobileSignIn',
|
||||
MobileDownload = 'MobileDownload',
|
||||
Onboarding = 'Onboarding',
|
||||
OrderDetails = 'OrderDetails',
|
||||
Preferences = 'Preferences',
|
||||
|
||||
@ -140,14 +140,8 @@ export type TransferNotifcation = {
|
||||
isCctp?: boolean;
|
||||
errorCount?: number;
|
||||
status?: StatusResponse;
|
||||
isExchange?: boolean;
|
||||
};
|
||||
|
||||
export enum ReleaseUpdateNotificationIds {
|
||||
RewardsAndFullTradingLive = 'rewards-and-full-trading-live',
|
||||
IncentivesS3 = 'incentives-s3',
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Struct to store whether a NotificationType should be triggered
|
||||
*/
|
||||
|
||||
@ -41,7 +41,6 @@ export enum MobileSettingsRoute {
|
||||
Network = 'network',
|
||||
}
|
||||
|
||||
export const BASE_ROUTE = import.meta.env.VITE_ROUTER_TYPE === 'hash' ? '/#' : '';
|
||||
export const TRADE_ROUTE = `${AppRoute.Trade}/:market`;
|
||||
export const PORTFOLIO_ROUTE = `${AppRoute.Portfolio}/:subroute`;
|
||||
export const HISTORY_ROUTE = `${AppRoute.Portfolio}/${PortfolioRoute.History}/:subroute`;
|
||||
|
||||
@ -16,7 +16,6 @@ import {
|
||||
KeplrIcon,
|
||||
MathWalletIcon,
|
||||
MetaMaskIcon,
|
||||
OkxWalletIcon,
|
||||
RainbowIcon,
|
||||
TokenPocketIcon,
|
||||
TrustWalletIcon,
|
||||
@ -90,7 +89,6 @@ export enum WalletType {
|
||||
// Ledger = 'LEDGER',
|
||||
MathWallet = 'MATH_WALLET',
|
||||
MetaMask = 'METAMASK',
|
||||
OkxWallet = 'OKX_WALLET',
|
||||
Rainbow = 'RAINBOW_WALLET',
|
||||
TokenPocket = 'TOKEN_POCKET',
|
||||
TrustWallet = 'TRUST_WALLET',
|
||||
@ -104,7 +102,6 @@ const WALLET_CONNECT_EXPLORER_RECOMMENDED_WALLETS = {
|
||||
imToken: 'ef333840daf915aafdc4a004525502d6d49d77bd9c65e0642dbaefb3c2893bef',
|
||||
TokenPocket: '20459438007b75f4f4acb98bf29aa3b800550309646d375da5fd4aac6c2a2c66',
|
||||
Trust: '4622a2b2d6af1c9844944291e5e7351a6aa24cd7b23099efac1b2fd875da31a0',
|
||||
OkxWallet: '971e689d0a5be527bac79629b4ee9b925e82208e5168b733496a09c0faed0709',
|
||||
Rainbow: '1ae92b26df02f0abca6304df07debccd18262fdf5fe82daa81593582dac9a369',
|
||||
Zerion: 'ecc4036f814562b41a5268adc86270fba1365471402006302e70169465b7ac18',
|
||||
Ledger: '19177a98252e07ddfc9af2083ba8e07ef627cb6103467ffebb3f8f4205fd7927',
|
||||
@ -215,14 +212,6 @@ export const wallets: Record<WalletType, WalletConfig> = {
|
||||
matchesInjectedEip1193: isMetaMask,
|
||||
walletconnect2Id: WALLET_CONNECT_EXPLORER_RECOMMENDED_WALLETS.Metamask,
|
||||
},
|
||||
[WalletType.OkxWallet]: {
|
||||
type: WalletType.OkxWallet,
|
||||
stringKey: STRING_KEYS.OKX_WALLET,
|
||||
icon: OkxWalletIcon,
|
||||
connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
|
||||
matchesInjectedEip1193: (provider) => provider.isOkxWallet,
|
||||
walletconnect2Id: WALLET_CONNECT_EXPLORER_RECOMMENDED_WALLETS.OkxWallet,
|
||||
},
|
||||
[WalletType.Rainbow]: {
|
||||
type: WalletType.Rainbow,
|
||||
stringKey: STRING_KEYS.RAINBOW_WALLET,
|
||||
@ -289,10 +278,6 @@ export type WithInjectedWeb3Provider = {
|
||||
};
|
||||
};
|
||||
|
||||
export type WithInjectedOkxWalletProvider = {
|
||||
okxwallet: InjectedWeb3Provider;
|
||||
};
|
||||
|
||||
// Wallet connections
|
||||
|
||||
export type WalletConnection = {
|
||||
|
||||
@ -57,15 +57,13 @@ export const useChartLines = ({
|
||||
useEffect(() => {
|
||||
if (tvWidget && isChartReady) {
|
||||
tvWidget.onChartReady(() => {
|
||||
tvWidget.headerReady().then(() => {
|
||||
tvWidget.chart().dataReady(() => {
|
||||
if (showOrderLines) {
|
||||
drawOrderLines();
|
||||
drawPositionLine();
|
||||
} else {
|
||||
deleteChartLines();
|
||||
}
|
||||
});
|
||||
tvWidget.chart().dataReady(() => {
|
||||
if (showOrderLines) {
|
||||
drawOrderLines();
|
||||
drawPositionLine();
|
||||
} else {
|
||||
deleteChartLines();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -10,10 +10,9 @@ export const useDisplayedWallets = () => {
|
||||
isDev && WalletType.Keplr,
|
||||
|
||||
WalletType.WalletConnect2,
|
||||
|
||||
|
||||
WalletType.CoinbaseWallet,
|
||||
|
||||
WalletType.OkxWallet,
|
||||
|
||||
// Hide these wallet options until they can be properly tested on mainnet
|
||||
// WalletType.ImToken,
|
||||
// WalletType.Rainbow,
|
||||
|
||||
@ -82,30 +82,24 @@ const useLocalNotificationsContext = () => {
|
||||
isCctp,
|
||||
errorCount,
|
||||
status: currentStatus,
|
||||
isExchange,
|
||||
} = 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);
|
||||
// @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
|
||||
const hasErrors = !!currentStatus?.errors ||
|
||||
(currentStatus?.error && Object.keys(currentStatus.error).length !== 0);
|
||||
|
||||
if (
|
||||
!isExchange &&
|
||||
!hasErrors &&
|
||||
(!currentStatus?.squidTransactionStatus ||
|
||||
currentStatus?.squidTransactionStatus === 'ongoing')
|
||||
) {
|
||||
try {
|
||||
const status = await fetchSquidStatus(
|
||||
{
|
||||
transactionId: txHash,
|
||||
toChainId,
|
||||
fromChainId,
|
||||
},
|
||||
isCctp
|
||||
);
|
||||
const status = await fetchSquidStatus({
|
||||
transactionId: txHash,
|
||||
toChainId,
|
||||
fromChainId,
|
||||
}, isCctp);
|
||||
|
||||
if (status) {
|
||||
transferNotification.status = status;
|
||||
|
||||
@ -5,7 +5,7 @@ import { isEqual, groupBy } from 'lodash';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
import { AppRoute, TokenRoute } from '@/constants/routes';
|
||||
import { AppRoute } from '@/constants/routes';
|
||||
import { DydxChainAsset } from '@/constants/wallets';
|
||||
|
||||
import {
|
||||
@ -20,10 +20,9 @@ import {
|
||||
NotificationType,
|
||||
DEFAULT_TOAST_AUTO_CLOSE_MS,
|
||||
TransferNotificationTypes,
|
||||
ReleaseUpdateNotificationIds,
|
||||
} from '@/constants/notifications';
|
||||
|
||||
import { useStringGetter, useTokenConfigs } from '@/hooks';
|
||||
import { useStringGetter } from '@/hooks';
|
||||
import { useLocalNotifications } from '@/hooks/useLocalNotifications';
|
||||
|
||||
import { AssetIcon } from '@/components/AssetIcon';
|
||||
@ -180,9 +179,8 @@ export const notificationTypes: NotificationTypeConfig[] = [
|
||||
|
||||
useEffect(() => {
|
||||
for (const transfer of transferNotifications) {
|
||||
const { fromChainId, status, txHash, toAmount, type, isExchange } = transfer;
|
||||
const isFinished =
|
||||
(Boolean(status) && status?.squidTransactionStatus !== 'ongoing') || isExchange;
|
||||
const { fromChainId, status, txHash, toAmount, type } = transfer;
|
||||
const isFinished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing';
|
||||
const icon = <Icon iconName={isFinished ? IconName.Transfer : IconName.Clock} />;
|
||||
|
||||
const transferType =
|
||||
@ -239,16 +237,13 @@ export const notificationTypes: NotificationTypeConfig[] = [
|
||||
{
|
||||
type: NotificationType.ReleaseUpdates,
|
||||
useTrigger: ({ trigger }) => {
|
||||
const { chainTokenLabel } = useTokenConfigs();
|
||||
const stringGetter = useStringGetter();
|
||||
const expirationDate = new Date('2024-03-08T23:59:59');
|
||||
const currentDate = new Date();
|
||||
|
||||
useEffect(() => {
|
||||
trigger(
|
||||
ReleaseUpdateNotificationIds.RewardsAndFullTradingLive,
|
||||
'rewards-and-full-trading-live',
|
||||
{
|
||||
icon: <AssetIcon symbol={chainTokenLabel} />,
|
||||
icon: <AssetIcon symbol="DYDX" />,
|
||||
title: stringGetter({ key: 'NOTIFICATIONS.RELEASE_REWARDS_AND_FULL_TRADING.TITLE' }),
|
||||
body: stringGetter({
|
||||
key: 'NOTIFICATIONS.RELEASE_REWARDS_AND_FULL_TRADING.BODY',
|
||||
@ -274,41 +269,14 @@ export const notificationTypes: NotificationTypeConfig[] = [
|
||||
},
|
||||
}),
|
||||
toastSensitivity: 'foreground',
|
||||
groupKey: ReleaseUpdateNotificationIds.RewardsAndFullTradingLive,
|
||||
groupKey: NotificationType.ReleaseUpdates,
|
||||
},
|
||||
[]
|
||||
);
|
||||
if (currentDate <= expirationDate) {
|
||||
trigger(
|
||||
ReleaseUpdateNotificationIds.IncentivesS3,
|
||||
{
|
||||
icon: <AssetIcon symbol={chainTokenLabel} />,
|
||||
title: stringGetter({ key: 'NOTIFICATIONS.INCENTIVES_SEASON_BEGUN.TITLE' }),
|
||||
body: stringGetter({
|
||||
key: 'NOTIFICATIONS.INCENTIVES_SEASON_BEGUN.BODY',
|
||||
params: {
|
||||
SEASON_NUMBER: '3',
|
||||
PREV_SEASON_NUMBER: '1',
|
||||
DYDX_AMOUNT: '34',
|
||||
USDC_AMOUNT: '100',
|
||||
},
|
||||
}),
|
||||
toastSensitivity: 'foreground',
|
||||
groupKey: ReleaseUpdateNotificationIds.IncentivesS3,
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
}, [stringGetter]);
|
||||
},
|
||||
useNotificationAction: () => {
|
||||
const { chainTokenLabel } = useTokenConfigs();
|
||||
const navigate = useNavigate();
|
||||
return (notificationId: string) => {
|
||||
if (notificationId === ReleaseUpdateNotificationIds.IncentivesS3) {
|
||||
navigate(`${chainTokenLabel}/${TokenRoute.TradingRewards}`);
|
||||
}
|
||||
};
|
||||
return () => {};
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { AnalyticsEvent } from '@/constants/analytics';
|
||||
import { LOCAL_STORAGE_VERSIONS, LocalStorageKey } from '@/constants/localStorage';
|
||||
import {
|
||||
type Notification,
|
||||
@ -14,8 +13,7 @@ import {
|
||||
import { useLocalStorage } from './useLocalStorage';
|
||||
import { notificationTypes } from './useNotificationTypes';
|
||||
|
||||
import { track } from '@/lib/analytics';
|
||||
import { renderSvgToDataUrl } from '@/lib/renderSvgToDataUrl';
|
||||
import { renderSvgToDataUrl } from '../lib/renderSvgToDataUrl';
|
||||
|
||||
type NotificationsContextType = ReturnType<typeof useNotificationsContext>;
|
||||
|
||||
@ -50,7 +48,6 @@ const useNotificationsContext = () => {
|
||||
defaultValue: {
|
||||
[NotificationType.AbacusGenerated]: true,
|
||||
[NotificationType.SquidTransfer]: true,
|
||||
[NotificationType.ReleaseUpdates]: true,
|
||||
version: LOCAL_STORAGE_VERSIONS[LocalStorageKey.NotificationPreferences],
|
||||
},
|
||||
});
|
||||
@ -179,10 +176,8 @@ const useNotificationsContext = () => {
|
||||
)
|
||||
);
|
||||
|
||||
const onNotificationAction = async (notification: Notification) => {
|
||||
track(AnalyticsEvent.NotificationAction, { type: notification.type, id: notification.id });
|
||||
return await actions[notification.type]?.(notification.id);
|
||||
};
|
||||
const onNotificationAction = async (notification: Notification) =>
|
||||
await actions[notification.type]?.(notification.id);
|
||||
|
||||
// Push notifications
|
||||
const [hasEnabledPush, setHasEnabledPush] = useLocalStorage({
|
||||
|
||||
@ -83,7 +83,6 @@ export { default as LedgerIcon } from './wallets/ledger.svg';
|
||||
export { default as MagicIcon } from './wallets/magic.svg';
|
||||
export { default as MathWalletIcon } from './wallets/mathwallet.svg';
|
||||
export { default as MetaMaskIcon } from './wallets/metamask.svg';
|
||||
export { default as OkxWalletIcon } from './wallets/okx-wallet.svg';
|
||||
export { default as RainbowIcon } from './wallets/rainbow-wallet.svg';
|
||||
export { default as TestWalletIcon } from './wallets/test-wallet.svg';
|
||||
export { default as TokenPocketIcon } from './wallets/tokenpocket.svg';
|
||||
|
||||
@ -1 +0,0 @@
|
||||
<svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400" viewBox="0, 0, 400,400"><g id="svgg"><path id="path0" d="M85.200 76.394 C 80.665 76.883,78.308 78.483,76.929 82.006 C 76.085 84.161,75.592 142.346,76.360 149.217 C 76.910 154.145,78.020 156.078,81.141 157.541 L 83.400 158.600 117.600 158.600 C 154.653 158.600,154.238 158.623,156.431 156.431 C 158.623 154.238,158.600 154.653,158.600 117.600 L 158.600 83.400 157.541 81.141 C 156.025 77.909,154.089 76.841,148.902 76.377 C 144.325 75.968,89.012 75.983,85.200 76.394 M249.890 76.409 C 245.967 76.882,243.812 78.255,242.459 81.141 L 241.400 83.400 241.400 117.600 C 241.400 154.653,241.377 154.238,243.569 156.431 C 245.762 158.623,245.347 158.600,282.400 158.600 L 316.600 158.600 318.859 157.541 C 324.048 155.108,324.091 154.742,323.907 114.679 C 323.739 78.113,323.917 79.248,317.999 76.930 C 316.018 76.154,255.815 75.693,249.890 76.409 M163.813 159.382 C 158.668 160.777,158.800 159.708,158.800 200.000 C 158.800 240.779,158.632 239.561,164.419 240.762 C 168.838 241.680,235.468 241.166,237.395 240.200 C 241.459 238.161,241.400 238.753,241.400 200.000 C 241.400 161.247,241.459 161.839,237.395 159.800 C 235.329 158.764,167.515 158.378,163.813 159.382 M84.192 241.586 C 75.955 242.833,75.887 243.209,76.093 286.273 C 76.252 319.496,76.167 318.455,78.918 321.120 C 81.744 323.860,80.445 323.758,114.679 323.911 C 154.743 324.090,155.109 324.047,157.541 318.859 L 158.600 316.600 158.600 282.400 C 158.600 242.256,158.762 243.612,153.768 241.955 C 151.993 241.366,87.894 241.026,84.192 241.586 M248.251 241.633 C 241.106 242.699,241.228 241.990,241.215 282.600 C 241.202 321.601,241.152 321.289,247.760 323.301 C 250.708 324.198,315.213 324.005,317.830 323.091 C 323.901 320.971,323.748 321.925,323.911 285.321 C 324.090 245.267,324.075 245.137,319.000 242.600 L 316.600 241.400 283.600 241.341 C 265.450 241.309,249.543 241.440,248.251 241.633 " stroke="none" fill="#fafafa" fill-rule="evenodd"></path><path id="path1" d="M0.000 200.000 L 0.000 400.000 200.000 400.000 L 400.000 400.000 400.000 200.000 L 400.000 0.000 200.000 0.000 L 0.000 0.000 0.000 200.000 M148.902 76.377 C 154.089 76.841,156.025 77.909,157.541 81.141 L 158.600 83.400 158.600 117.600 C 158.600 154.653,158.623 154.238,156.431 156.431 C 154.238 158.623,154.653 158.600,117.600 158.600 L 83.400 158.600 81.141 157.541 C 75.953 155.109,75.910 154.743,76.089 114.679 C 76.250 78.643,76.131 79.509,81.200 77.288 C 83.883 76.112,137.816 75.387,148.902 76.377 M317.999 76.930 C 323.917 79.248,323.739 78.113,323.907 114.679 C 324.091 154.742,324.048 155.108,318.859 157.541 L 316.600 158.600 282.400 158.600 C 245.347 158.600,245.762 158.623,243.569 156.431 C 241.377 154.238,241.400 154.653,241.400 117.600 L 241.400 83.400 242.459 81.141 C 243.812 78.255,245.967 76.882,249.890 76.409 C 255.815 75.693,316.018 76.154,317.999 76.930 M237.395 159.800 C 241.459 161.839,241.400 161.247,241.400 200.000 C 241.400 238.753,241.459 238.161,237.395 240.200 C 235.468 241.166,168.838 241.680,164.419 240.762 C 158.632 239.561,158.800 240.779,158.800 200.000 C 158.800 159.708,158.668 160.777,163.813 159.382 C 167.515 158.378,235.329 158.764,237.395 159.800 M153.768 241.955 C 158.762 243.612,158.600 242.256,158.600 282.400 L 158.600 316.600 157.541 318.859 C 155.109 324.047,154.743 324.090,114.679 323.911 C 80.445 323.758,81.744 323.860,78.918 321.120 C 76.167 318.455,76.252 319.496,76.093 286.273 C 75.887 243.209,75.955 242.833,84.192 241.586 C 87.894 241.026,151.993 241.366,153.768 241.955 M319.000 242.600 C 324.075 245.137,324.090 245.267,323.911 285.321 C 323.748 321.925,323.901 320.971,317.830 323.091 C 315.213 324.005,250.708 324.198,247.760 323.301 C 244.032 322.166,242.292 320.037,241.590 315.751 C 241.084 312.662,241.101 252.756,241.608 248.844 C 242.629 240.980,241.034 241.265,283.600 241.341 L 316.600 241.400 319.000 242.600 " stroke="none" fill="#040404" fill-rule="evenodd"></path></g></svg>
|
||||
|
Before Width: | Height: | Size: 3.9 KiB |
@ -31,7 +31,6 @@ import { FillDetailsDialog } from '@/views/dialogs/DetailsDialog/FillDetailsDial
|
||||
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 +63,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} />,
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { parseLocationHash } from '@/lib/urlUtils';
|
||||
|
||||
describe('parseLocationHash', () => {
|
||||
it('returns the path separated from hash', () => {
|
||||
const hash = '#/markets';
|
||||
expect(parseLocationHash(hash)).toEqual('/markets');
|
||||
});
|
||||
it('returns the path and query string separated from hash', () => {
|
||||
const hash = '#/markets?displayinitializingmarkets=true';
|
||||
expect(parseLocationHash(hash)).toEqual('/markets?displayinitializingmarkets=true');
|
||||
});
|
||||
});
|
||||
@ -25,22 +25,3 @@ export function convertBech32Address({
|
||||
}): string {
|
||||
return toBech32(bech32Prefix, fromHex(toHex(fromBech32(address).data)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a Cosmos address with a specific prefix.
|
||||
* @param {string} address The Cosmos address to validate.
|
||||
* @param {string} prefix The expected prefix for the address.
|
||||
* @returns {boolean} True if the address is valid and matches the prefix, false otherwise.
|
||||
*/
|
||||
export function validateCosmosAddress(address: string, prefix: string) {
|
||||
try {
|
||||
// Decode the address to verify its structure and prefix
|
||||
const { prefix: decodedPrefix } = fromBech32(address);
|
||||
|
||||
// Check if the decoded address has the expected prefix
|
||||
return decodedPrefix === prefix;
|
||||
} catch (error) {
|
||||
// If decoding fails, the address is not valid
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,13 +31,13 @@ export const fetchSquidStatus = async (
|
||||
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
'x-integrator-id': integratorId || 'dYdX-api',
|
||||
"x-integrator-id": integratorId || 'dYdX-api'
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw error;
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
@ -45,4 +45,4 @@ export const fetchSquidStatus = async (
|
||||
|
||||
export const getNobleChainId = () => {
|
||||
return isMainnet ? 'noble-1' : 'grand-1';
|
||||
};
|
||||
}
|
||||
|
||||
@ -19,10 +19,10 @@ class TestFlags {
|
||||
return !!this.queryParams.displayinitializingmarkets;
|
||||
}
|
||||
|
||||
get addressOverride(): string {
|
||||
get addressOverride():string {
|
||||
return this.queryParams.address;
|
||||
}
|
||||
|
||||
|
||||
get showTradingRewards() {
|
||||
return !!this.queryParams.tradingrewards;
|
||||
}
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
/**
|
||||
* @param hash location.hash
|
||||
* @returns path and query string if hash parameter is not empty
|
||||
*/
|
||||
export const parseLocationHash = (hash: string) => {
|
||||
if (!hash || hash.length === 0) return '';
|
||||
|
||||
// Remove '#' and split by '?'
|
||||
const [path, queryString] = hash.substring(1).split('?');
|
||||
|
||||
// Reconstruct path and query string
|
||||
return `${path}${queryString ? `?${queryString}` : ''}`;
|
||||
};
|
||||
@ -5,7 +5,6 @@ import {
|
||||
type InjectedCoinbaseWalletExtensionProvider,
|
||||
type WithInjectedEthereumProvider,
|
||||
type WithInjectedWeb3Provider,
|
||||
type WithInjectedOkxWalletProvider,
|
||||
} from '@/constants/wallets';
|
||||
|
||||
import { isTruthy } from '../isTruthy';
|
||||
@ -19,6 +18,15 @@ export const isMetaMask = (provider: ExternalProvider) => (
|
||||
&& (
|
||||
!(provider as InjectedCoinbaseWalletExtensionProvider).overrideIsMetaMask
|
||||
)
|
||||
|
||||
/* not a MetaMask wannabe! */
|
||||
&& (
|
||||
Reflect.ownKeys(provider).filter((key) =>
|
||||
typeof key === 'string'
|
||||
&& key.match(/^is/)
|
||||
&& !['isConnected', 'isMetaMask'].includes(key)
|
||||
).length === 0
|
||||
)
|
||||
)
|
||||
|
||||
/*
|
||||
@ -46,10 +54,5 @@ export const detectInjectedEip1193Providers = (): ExternalProvider[] => {
|
||||
? ethereumProvider.providers
|
||||
: [];
|
||||
|
||||
const okxWalletProvider = (globalThis as typeof globalThis & WithInjectedOkxWalletProvider)
|
||||
?.okxwallet;
|
||||
|
||||
return [...displacedProviders, ethereumProvider, web3Provider, okxWalletProvider].filter(
|
||||
isTruthy
|
||||
);
|
||||
return [...displacedProviders, ethereumProvider, web3Provider].filter(isTruthy);
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import './polyfills';
|
||||
import { StrictMode } from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { BrowserRouter, HashRouter } from 'react-router-dom';
|
||||
import { HashRouter } from 'react-router-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import { store } from '@/state/_store';
|
||||
@ -12,13 +12,11 @@ import './index.css';
|
||||
|
||||
import App from './App';
|
||||
|
||||
const Router = import.meta.env.VITE_ROUTER_TYPE === 'hash' ? HashRouter : BrowserRouter;
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<ErrorBoundary>
|
||||
<StrictMode>
|
||||
<Provider store={store}>
|
||||
<Router children={<App />} />
|
||||
<HashRouter children={<App />} />
|
||||
</Provider>
|
||||
</StrictMode>
|
||||
</ErrorBoundary>
|
||||
|
||||
@ -13,6 +13,8 @@ export const DepositDialog = ({ setIsOpen }: ElementProps) => {
|
||||
const stringGetter = useStringGetter();
|
||||
const { isMobile } = useBreakpoints();
|
||||
|
||||
const closeDialog = () => setIsOpen?.(false);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
isOpen
|
||||
@ -20,7 +22,7 @@ export const DepositDialog = ({ setIsOpen }: ElementProps) => {
|
||||
title={stringGetter({ key: STRING_KEYS.DEPOSIT })}
|
||||
placement={isMobile ? DialogPlacement.FullScreen : DialogPlacement.Default}
|
||||
>
|
||||
<DepositDialogContent />
|
||||
<DepositDialogContent onDeposit={closeDialog} />
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
@ -2,7 +2,6 @@ import { useEffect, useState } from 'react';
|
||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { TransferInputField, TransferType } from '@/constants/abacus';
|
||||
import { AnalyticsEvent } from '@/constants/analytics';
|
||||
import { isMainnet } from '@/constants/networks';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
@ -10,7 +9,6 @@ import { DepositForm } from '@/views/forms/AccountManagementForms/DepositForm';
|
||||
import { TestnetDepositForm } from '@/views/forms/AccountManagementForms/TestnetDepositForm';
|
||||
|
||||
import abacusStateManager from '@/lib/abacus';
|
||||
import { track } from '@/lib/analytics';
|
||||
|
||||
type ElementProps = {
|
||||
onDeposit?: () => void;
|
||||
@ -36,19 +34,9 @@ export const DepositDialogContent = ({ onDeposit }: ElementProps) => {
|
||||
return (
|
||||
<Styled.Content>
|
||||
{isMainnet || !showFaucet ? (
|
||||
<DepositForm
|
||||
onDeposit={(event) => {
|
||||
track(AnalyticsEvent.TransferDeposit, event);
|
||||
onDeposit?.();
|
||||
}}
|
||||
/>
|
||||
<DepositForm onDeposit={onDeposit} />
|
||||
) : (
|
||||
<TestnetDepositForm
|
||||
onDeposit={() => {
|
||||
track(AnalyticsEvent.TransferFaucet);
|
||||
onDeposit?.();
|
||||
}}
|
||||
/>
|
||||
<TestnetDepositForm onDeposit={onDeposit} />
|
||||
)}
|
||||
{!isMainnet && (
|
||||
<Styled.TextToggle onClick={() => setShowFaucet(!showFaucet)}>
|
||||
|
||||
@ -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;
|
||||
`}
|
||||
}
|
||||
`;
|
||||
@ -2,7 +2,7 @@ import { useState } from 'react';
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { ButtonAction } from '@/constants/buttons';
|
||||
import { AppRoute, BASE_ROUTE } from '@/constants/routes';
|
||||
import { AppRoute } from '@/constants/routes';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { useStringGetter } from '@/hooks';
|
||||
import breakpoints from '@/styles/breakpoints';
|
||||
@ -40,7 +40,7 @@ export const NewMarketAgreementDialog = ({ acceptTerms, setIsOpen }: ElementProp
|
||||
</Styled.Link>
|
||||
),
|
||||
TERMS_OF_USE: (
|
||||
<Styled.Link href={`${BASE_ROUTE}${AppRoute.Terms}`}>
|
||||
<Styled.Link href={`/#${AppRoute.Terms}`}>
|
||||
{stringGetter({ key: STRING_KEYS.TERMS_OF_USE })}
|
||||
</Styled.Link>
|
||||
),
|
||||
|
||||
@ -105,8 +105,8 @@ export const OnboardingDialog = ({ setIsOpen }: ElementProps) => {
|
||||
<Styled.Content>
|
||||
{isMainnet ? (
|
||||
<DepositForm
|
||||
onDeposit={(event) => {
|
||||
track(AnalyticsEvent.TransferDeposit, event);
|
||||
onDeposit={() => {
|
||||
track(AnalyticsEvent.TransferDeposit);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@ -2,7 +2,7 @@ import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { useAccounts, useStringGetter } from '@/hooks';
|
||||
|
||||
import { AppRoute, BASE_ROUTE } from '@/constants/routes';
|
||||
import { AppRoute } from '@/constants/routes';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { ButtonAction } from '@/constants/buttons';
|
||||
|
||||
@ -34,12 +34,12 @@ export const AcknowledgeTerms = ({ onClose, onContinue }: ElementProps) => {
|
||||
key: STRING_KEYS.TOS_TITLE,
|
||||
params: {
|
||||
TERMS_LINK: (
|
||||
<Styled.Link href={`${BASE_ROUTE}${AppRoute.Terms}`}>
|
||||
<Styled.Link href={`/#${AppRoute.Terms}`}>
|
||||
{stringGetter({ key: STRING_KEYS.TERMS_OF_USE })}
|
||||
</Styled.Link>
|
||||
),
|
||||
PRIVACY_POLICY_LINK: (
|
||||
<Styled.Link href={`${BASE_ROUTE}${AppRoute.Privacy}`}>
|
||||
<Styled.Link href={`/#${AppRoute.Privacy}`}>
|
||||
{stringGetter({ key: STRING_KEYS.PRIVACY_POLICY })}
|
||||
</Styled.Link>
|
||||
),
|
||||
|
||||
@ -7,7 +7,6 @@ import { Abi, parseUnits } from 'viem';
|
||||
import erc20 from '@/abi/erc20.json';
|
||||
import erc20_usdt from '@/abi/erc20_usdt.json';
|
||||
import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus';
|
||||
import { AnalyticsEvent, AnalyticsEventData } from '@/constants/analytics';
|
||||
import { AlertType } from '@/constants/alerts';
|
||||
import { ButtonSize } from '@/constants/buttons';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
@ -49,7 +48,7 @@ import { DepositButtonAndReceipt } from './DepositForm/DepositButtonAndReceipt';
|
||||
import { NobleDeposit } from '../NobleDeposit';
|
||||
|
||||
type DepositFormProps = {
|
||||
onDeposit?: (event?: AnalyticsEventData<AnalyticsEvent.TransferDeposit>) => void;
|
||||
onDeposit?: () => void;
|
||||
onError?: () => void;
|
||||
};
|
||||
|
||||
@ -133,7 +132,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
||||
if (error) onError?.();
|
||||
}, [error]);
|
||||
|
||||
const onSelectNetwork = useCallback((name: string, type: 'chain' | 'exchange') => {
|
||||
const onSelectChain = useCallback((name: string, type: 'chain' | 'exchange') => {
|
||||
if (name) {
|
||||
abacusStateManager.clearTransferInputValues();
|
||||
setFromAmount('');
|
||||
@ -259,6 +258,8 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
||||
};
|
||||
const txHash = await signerWagmi.sendTransaction(tx);
|
||||
|
||||
onDeposit?.();
|
||||
|
||||
if (txHash) {
|
||||
addTransferNotification({
|
||||
txHash: txHash,
|
||||
@ -270,12 +271,6 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
||||
});
|
||||
abacusStateManager.clearTransferInputValues();
|
||||
setFromAmount('');
|
||||
|
||||
onDeposit?.({
|
||||
chainId: chainIdStr || undefined,
|
||||
tokenAddress: sourceToken?.address || undefined,
|
||||
tokenSymbol: sourceToken?.symbol || undefined,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
log('DepositForm/onSubmit', error);
|
||||
@ -284,7 +279,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
||||
setIsLoading(false);
|
||||
}
|
||||
},
|
||||
[requestPayload, signerWagmi, chainId, sourceToken, sourceChain]
|
||||
[requestPayload, signerWagmi, chainId]
|
||||
);
|
||||
|
||||
const amountInputReceipt = [
|
||||
@ -383,7 +378,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
||||
<SourceSelectMenu
|
||||
selectedChain={chainIdStr || undefined}
|
||||
selectedExchange={exchange || undefined}
|
||||
onSelect={onSelectNetwork}
|
||||
onSelect={onSelectChain}
|
||||
/>
|
||||
{exchange && nobleAddress ? (
|
||||
<NobleDeposit />
|
||||
|
||||
@ -14,7 +14,6 @@ import { popoverMixins } from '@/styles/popoverMixins';
|
||||
import { getTransferInputs } from '@/state/inputsSelectors';
|
||||
|
||||
import { isTruthy } from '@/lib/isTruthy';
|
||||
import { testFlags } from '@/lib/testFlags';
|
||||
|
||||
type ElementProps = {
|
||||
label?: string;
|
||||
|
||||
@ -17,10 +17,9 @@ import { getTransferInputs } from '@/state/inputsSelectors';
|
||||
type ElementProps = {
|
||||
selectedToken?: TransferInputTokenResource;
|
||||
onSelectToken: (token: TransferInputTokenResource) => void;
|
||||
isExchange?: boolean;
|
||||
};
|
||||
|
||||
export const TokenSelectMenu = ({ selectedToken, onSelectToken, isExchange }: ElementProps) => {
|
||||
export const TokenSelectMenu = ({ selectedToken, onSelectToken }: ElementProps) => {
|
||||
const stringGetter = useStringGetter();
|
||||
const { type, depositOptions, withdrawalOptions, resources } =
|
||||
useSelector(getTransferInputs, shallowEqual) || {};
|
||||
@ -48,24 +47,19 @@ export const TokenSelectMenu = ({ selectedToken, onSelectToken, isExchange }: El
|
||||
},
|
||||
]}
|
||||
label={stringGetter({ key: STRING_KEYS.ASSET })}
|
||||
withSearch={!isExchange}
|
||||
withReceiptItems={
|
||||
!isExchange
|
||||
? [
|
||||
{
|
||||
key: 'swap',
|
||||
label: stringGetter({ key: STRING_KEYS.SWAP }),
|
||||
value: selectedToken && (
|
||||
<>
|
||||
<Tag>{type === TransferType.deposit ? selectedToken?.symbol : 'USDC'}</Tag>
|
||||
<DiffArrow />
|
||||
<Tag>{type === TransferType.deposit ? 'USDC' : selectedToken?.symbol}</Tag>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]
|
||||
: undefined
|
||||
}
|
||||
withReceiptItems={[
|
||||
{
|
||||
key: 'swap',
|
||||
label: stringGetter({ key: STRING_KEYS.SWAP }),
|
||||
value: selectedToken && (
|
||||
<>
|
||||
<Tag>{type === TransferType.deposit ? selectedToken?.symbol : 'USDC'}</Tag>
|
||||
<DiffArrow />
|
||||
<Tag>{type === TransferType.deposit ? 'USDC' : selectedToken?.symbol}</Tag>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Styled.AssetRow>
|
||||
{selectedToken ? (
|
||||
|
||||
@ -7,7 +7,6 @@ import { isAddress } from 'viem';
|
||||
|
||||
import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus';
|
||||
import { AlertType } from '@/constants/alerts';
|
||||
import { AnalyticsEvent } from '@/constants/analytics';
|
||||
import { ButtonSize } from '@/constants/buttons';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { isMainnet } from '@/constants/networks';
|
||||
@ -56,8 +55,6 @@ import { getNobleChainId } from '@/lib/squid';
|
||||
|
||||
import { TokenSelectMenu } from './TokenSelectMenu';
|
||||
import { WithdrawButtonAndReceipt } from './WithdrawForm/WithdrawButtonAndReceipt';
|
||||
import { validateCosmosAddress } from '@/lib/addressUtils';
|
||||
import { track } from '@/lib/analytics';
|
||||
|
||||
export const WithdrawForm = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
@ -71,7 +68,6 @@ export const WithdrawForm = () => {
|
||||
const {
|
||||
requestPayload,
|
||||
token,
|
||||
exchange,
|
||||
chain: chainIdStr,
|
||||
address: toAddress,
|
||||
resources,
|
||||
@ -183,27 +179,18 @@ export const WithdrawForm = () => {
|
||||
requestPayload.data,
|
||||
isCctp
|
||||
);
|
||||
const nobleChainId = getNobleChainId();
|
||||
const toChainId = Boolean(exchange) ? nobleChainId : chainIdStr || undefined;
|
||||
if (txHash && toChainId) {
|
||||
if (txHash) {
|
||||
addTransferNotification({
|
||||
txHash: txHash,
|
||||
type: TransferNotificationTypes.Withdrawal,
|
||||
fromChainId: !isCctp ? selectedDydxChainId : nobleChainId,
|
||||
toChainId,
|
||||
fromChainId: !isCctp ? selectedDydxChainId : getNobleChainId(),
|
||||
toChainId: chainIdStr || undefined,
|
||||
toAmount: debouncedAmountBN.toNumber(),
|
||||
triggeredAt: Date.now(),
|
||||
isCctp,
|
||||
isExchange: Boolean(exchange),
|
||||
});
|
||||
abacusStateManager.clearTransferInputValues();
|
||||
setWithdrawAmount('');
|
||||
|
||||
track(AnalyticsEvent.TransferWithdraw, {
|
||||
chainId: toChainId,
|
||||
tokenAddress: toToken?.address || undefined,
|
||||
tokenSymbol: toToken?.symbol || undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@ -226,17 +213,7 @@ export const WithdrawForm = () => {
|
||||
setIsLoading(false);
|
||||
}
|
||||
},
|
||||
[
|
||||
requestPayload,
|
||||
debouncedAmountBN,
|
||||
chainIdStr,
|
||||
toAddress,
|
||||
selectedDydxChainId,
|
||||
exchange,
|
||||
toToken,
|
||||
screenAddresses,
|
||||
stringGetter,
|
||||
]
|
||||
[requestPayload, debouncedAmountBN, chainIdStr, toAddress, screenAddresses, stringGetter]
|
||||
);
|
||||
|
||||
const onChangeAddress = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
@ -269,20 +246,13 @@ export const WithdrawForm = () => {
|
||||
setWithdrawAmount(freeCollateralBN.toString());
|
||||
}, [freeCollateralBN, setWithdrawAmount]);
|
||||
|
||||
const onSelectNetwork = useCallback((name: string, type: 'chain' | 'exchange') => {
|
||||
if (name) {
|
||||
const onSelectChain = useCallback((chain: string) => {
|
||||
if (chain) {
|
||||
abacusStateManager.setTransferValue({
|
||||
field: TransferInputField.chain,
|
||||
value: chain,
|
||||
});
|
||||
setWithdrawAmount('');
|
||||
if (type === 'chain') {
|
||||
abacusStateManager.setTransferValue({
|
||||
field: TransferInputField.chain,
|
||||
value: name,
|
||||
});
|
||||
} else {
|
||||
abacusStateManager.setTransferValue({
|
||||
field: TransferInputField.exchange,
|
||||
value: name,
|
||||
});
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
@ -343,7 +313,7 @@ export const WithdrawForm = () => {
|
||||
});
|
||||
|
||||
if (debouncedAmountBN) {
|
||||
if (!chainIdStr && !exchange) {
|
||||
if (!chainIdStr) {
|
||||
return stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_CHAIN });
|
||||
} else if (!toToken) {
|
||||
return stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_ASSET });
|
||||
@ -390,19 +360,14 @@ export const WithdrawForm = () => {
|
||||
summary,
|
||||
]);
|
||||
|
||||
const isInvalidNobleAddress = Boolean(
|
||||
exchange && toAddress && !validateCosmosAddress(toAddress, 'noble')
|
||||
);
|
||||
|
||||
const isDisabled =
|
||||
!!errorMessage ||
|
||||
!toToken ||
|
||||
(!chainIdStr && !exchange) ||
|
||||
!chainIdStr ||
|
||||
!toAddress ||
|
||||
debouncedAmountBN.isNaN() ||
|
||||
debouncedAmountBN.isZero() ||
|
||||
isLoading ||
|
||||
isInvalidNobleAddress;
|
||||
isLoading;
|
||||
|
||||
return (
|
||||
<Styled.Form onSubmit={onSubmit}>
|
||||
@ -420,21 +385,12 @@ export const WithdrawForm = () => {
|
||||
}
|
||||
/>
|
||||
<SourceSelectMenu
|
||||
selectedExchange={exchange || undefined}
|
||||
label={stringGetter({ key: STRING_KEYS.NETWORK })}
|
||||
selectedChain={chainIdStr || undefined}
|
||||
onSelect={onSelectNetwork}
|
||||
onSelect={onSelectChain}
|
||||
/>
|
||||
</Styled.DestinationRow>
|
||||
{isInvalidNobleAddress && (
|
||||
<AlertMessage type={AlertType.Error}>
|
||||
{stringGetter({ key: STRING_KEYS.NOBLE_ADDRESS_VALIDATION })}
|
||||
</AlertMessage>
|
||||
)}
|
||||
<TokenSelectMenu
|
||||
selectedToken={toToken || undefined}
|
||||
onSelectToken={onSelectToken}
|
||||
isExchange={Boolean(exchange)}
|
||||
/>
|
||||
<TokenSelectMenu selectedToken={toToken || undefined} onSelectToken={onSelectToken} />
|
||||
<Styled.WithDetailsReceipt side="bottom" detailItems={amountInputReceipt}>
|
||||
<FormInput
|
||||
type={InputType.Number}
|
||||
|
||||
@ -27,8 +27,6 @@ import { calculateCanAccountTrade } from '@/state/accountCalculators';
|
||||
import { getSubaccount } from '@/state/accountSelectors';
|
||||
import { getTransferInputs } from '@/state/inputsSelectors';
|
||||
|
||||
import { isTruthy } from '@/lib/isTruthy';
|
||||
|
||||
import { SlippageEditor } from '../SlippageEditor';
|
||||
|
||||
type ElementProps = {
|
||||
@ -57,7 +55,7 @@ export const WithdrawButtonAndReceipt = ({
|
||||
const stringGetter = useStringGetter();
|
||||
|
||||
const { leverage } = useSelector(getSubaccount, shallowEqual) || {};
|
||||
const { summary, requestPayload, exchange } = useSelector(getTransferInputs, shallowEqual) || {};
|
||||
const { summary, requestPayload } = useSelector(getTransferInputs, shallowEqual) || {};
|
||||
const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual);
|
||||
const { usdcLabel } = useTokenConfigs();
|
||||
|
||||
@ -94,7 +92,7 @@ export const WithdrawButtonAndReceipt = ({
|
||||
value: <Output type={OutputType.Fiat} value={totalFees} />,
|
||||
subitems: feeSubitems,
|
||||
},
|
||||
!exchange && {
|
||||
{
|
||||
key: 'exchange-rate',
|
||||
label: <span>{stringGetter({ key: STRING_KEYS.EXCHANGE_RATE })}</span>,
|
||||
value: withdrawToken && typeof summary?.exchangeRate === 'number' && (
|
||||
@ -135,9 +133,7 @@ export const WithdrawButtonAndReceipt = ({
|
||||
{withdrawToken && <Tag>{withdrawToken?.symbol}</Tag>}
|
||||
</span>
|
||||
),
|
||||
value: (
|
||||
<Output type={OutputType.Asset} value={summary?.toAmount} fractionDigits={TOKEN_DECIMALS} />
|
||||
),
|
||||
value: <Output type={OutputType.Asset} value={summary?.toAmount} fractionDigits={TOKEN_DECIMALS} />,
|
||||
subitems: [
|
||||
{
|
||||
key: 'minimum-amount-received',
|
||||
@ -148,17 +144,13 @@ export const WithdrawButtonAndReceipt = ({
|
||||
</span>
|
||||
),
|
||||
value: (
|
||||
<Output
|
||||
type={OutputType.Asset}
|
||||
value={summary?.toAmountMin}
|
||||
fractionDigits={TOKEN_DECIMALS}
|
||||
/>
|
||||
<Output type={OutputType.Asset} value={summary?.toAmountMin} fractionDigits={TOKEN_DECIMALS} />
|
||||
),
|
||||
tooltip: 'minimum-amount-received',
|
||||
},
|
||||
],
|
||||
},
|
||||
!exchange && {
|
||||
{
|
||||
key: 'slippage',
|
||||
label: <span>{stringGetter({ key: STRING_KEYS.MAX_SLIPPAGE })}</span>,
|
||||
value: (
|
||||
@ -183,7 +175,7 @@ export const WithdrawButtonAndReceipt = ({
|
||||
/>
|
||||
),
|
||||
},
|
||||
].filter(isTruthy);
|
||||
];
|
||||
|
||||
const isFormValid = !isDisabled && !isEditingSlippage;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import styled, { type AnyStyledComponent, css } from 'styled-components';
|
||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { OpacityToken } from '@/constants/styles/base';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
@ -10,6 +10,7 @@ import { useAccounts, useStringGetter } from '@/hooks';
|
||||
import { CopyButton } from '@/components/CopyButton';
|
||||
import { QrCode } from '@/components/QrCode';
|
||||
import { Checkbox } from '@/components/Checkbox';
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
import { TimeoutButton } from '@/components/TimeoutButton';
|
||||
import { WithDetailsReceipt } from '@/components/WithDetailsReceipt';
|
||||
import { WithReceipt } from '@/components/WithReceipt';
|
||||
@ -18,7 +19,6 @@ import { generateFadedColorVariant } from '@/lib/styles';
|
||||
|
||||
export const NobleDeposit = () => {
|
||||
const [hasAcknowledged, setHasAcknowledged] = useState(false);
|
||||
const [hasTimedout, setHasTimedout] = useState(false);
|
||||
const stringGetter = useStringGetter();
|
||||
const { nobleAddress } = useAccounts();
|
||||
|
||||
@ -30,21 +30,23 @@ export const NobleDeposit = () => {
|
||||
{
|
||||
key: 'nobleAddress',
|
||||
label: stringGetter({ key: STRING_KEYS.NOBLE_ADDRESS }),
|
||||
value:
|
||||
hasAcknowledged && hasTimedout
|
||||
? nobleAddress
|
||||
: stringGetter({ key: STRING_KEYS.ACKNOWLEDGE_TO_REVEAL }),
|
||||
value: nobleAddress,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Styled.QrCode
|
||||
hasLogo
|
||||
size={432}
|
||||
value={nobleAddress || ''}
|
||||
blurred={!hasAcknowledged || !hasTimedout}
|
||||
/>
|
||||
<Styled.QrCodeContainer>
|
||||
<Styled.QrCode size={432} value={nobleAddress || ''} />
|
||||
</Styled.QrCodeContainer>
|
||||
</WithDetailsReceipt>
|
||||
|
||||
<Styled.WaitingSpan>
|
||||
<Styled.CautionIconContainer>
|
||||
<Icon iconName={IconName.CautionCircleStroked} />
|
||||
</Styled.CautionIconContainer>
|
||||
|
||||
<p>{stringGetter({ key: STRING_KEYS.NOBLE_WARNING })}</p>
|
||||
</Styled.WaitingSpan>
|
||||
|
||||
<Styled.WithReceipt
|
||||
slotReceipt={
|
||||
<Styled.CheckboxContainer>
|
||||
@ -61,12 +63,7 @@ export const NobleDeposit = () => {
|
||||
>
|
||||
<TimeoutButton
|
||||
timeoutInSeconds={8}
|
||||
onTimeOut={() => setHasTimedout(true)}
|
||||
slotFinal={
|
||||
<CopyButton state={{ isDisabled: !hasAcknowledged }} value={nobleAddress}>
|
||||
{!hasAcknowledged ? stringGetter({ key: STRING_KEYS.ACKNOWLEDGE_RISKS }) : undefined}
|
||||
</CopyButton>
|
||||
}
|
||||
slotFinal={<CopyButton state={{ isDisabled: !hasAcknowledged }} value={nobleAddress} />}
|
||||
/>
|
||||
</Styled.WithReceipt>
|
||||
</>
|
||||
@ -85,14 +82,23 @@ Styled.WithReceipt = styled(WithReceipt)`
|
||||
--withReceipt-backgroundColor: var(--color-layer-2);
|
||||
`;
|
||||
|
||||
Styled.QrCode = styled(QrCode)<{ blurred: boolean }>`
|
||||
border-radius: 0.5em;
|
||||
Styled.QrCodeContainer = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
${({ blurred }) =>
|
||||
blurred &&
|
||||
css`
|
||||
filter: blur(8px);
|
||||
`}
|
||||
padding: 0.5rem;
|
||||
|
||||
background-color: var(--color-layer-2);
|
||||
border-radius: 0.5rem;
|
||||
`;
|
||||
|
||||
Styled.QrCode = styled(QrCode)`
|
||||
max-height: 20rem;
|
||||
width: fit-content;
|
||||
|
||||
svg {
|
||||
max-height: 20rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.CheckboxContainer = styled.div`
|
||||
|
||||
@ -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
|
||||
? [
|
||||
{
|
||||
|
||||
@ -40,7 +40,7 @@ export const TransferStatusNotification = ({
|
||||
const stringGetter = useStringGetter();
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
const [secondsLeft, setSecondsLeft] = useState<number>(0);
|
||||
const { fromChainId, status, txHash, toAmount, isExchange } = transfer;
|
||||
const { fromChainId, status, txHash, toAmount } = transfer;
|
||||
|
||||
// @ts-ignore status.errors is not in the type definition but can be returned
|
||||
const error = status?.errors?.length ? status?.errors[0] : status?.error;
|
||||
@ -54,8 +54,6 @@ export const TransferStatusNotification = ({
|
||||
|
||||
useInterval({ callback: updateSecondsLeft });
|
||||
|
||||
const isComplete = status?.squidTransactionStatus === 'success' || isExchange;
|
||||
|
||||
const inProgressStatusString =
|
||||
type === TransferNotificationTypes.Deposit
|
||||
? secondsLeft > 0
|
||||
@ -67,10 +65,10 @@ export const TransferStatusNotification = ({
|
||||
|
||||
const statusString =
|
||||
type === TransferNotificationTypes.Deposit
|
||||
? isComplete
|
||||
? status?.squidTransactionStatus === 'success'
|
||||
? STRING_KEYS.DEPOSIT_COMPLETE
|
||||
: inProgressStatusString
|
||||
: isComplete
|
||||
: status?.squidTransactionStatus === 'success'
|
||||
? STRING_KEYS.WITHDRAW_COMPLETE
|
||||
: inProgressStatusString;
|
||||
|
||||
@ -110,12 +108,12 @@ export const TransferStatusNotification = ({
|
||||
slotIcon={isToast && slotIcon}
|
||||
slotTitle={slotTitle}
|
||||
slotCustomContent={
|
||||
!status && !isExchange ? (
|
||||
!status ? (
|
||||
<LoadingDots size={3} />
|
||||
) : (
|
||||
<Styled.BridgingStatus>
|
||||
{content}
|
||||
{!isToast && !isComplete && !hasError && (
|
||||
{!isToast && status?.squidTransactionStatus !== 'success' && !hasError && (
|
||||
<Styled.TransferStatusSteps status={status} type={type} />
|
||||
)}
|
||||
</Styled.BridgingStatus>
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user