Compare commits

..
29 Commits
Author SHA1 Message Date
zramsay 2212e607cf critical typo
Publish ApplicationRecord to Registry / cns_publish (push) Successful in 1m27s
2024-02-27 18:26:06 -05:00
zramsay a50ff841a0 use npm not pnpm for install reg-cli
Publish ApplicationRecord to Registry / cns_publish (push) Successful in 1m14s
2024-02-27 18:23:54 -05:00
zramsay d2020664d5 https://github.com/nodejs/corepack/issues/157
Publish ApplicationRecord to Registry / cns_publish (push) Successful in 1m14s
2024-02-27 18:20:06 -05:00
zramsay e2b2af6680 hrm
Publish ApplicationRecord to Registry / cns_publish (push) Failing after 36s
2024-02-27 18:14:18 -05:00
zramsay 3b4af29bd5 install corepack
Publish ApplicationRecord to Registry / cns_publish (push) Failing after 36s
2024-02-27 18:11:37 -05:00
zramsay 37ca83b29f ok
Publish ApplicationRecord to Registry / cns_publish (push) Failing after 36s
2024-02-27 17:55:34 -05:00
zramsay 8959364dee publish to registry 2024-02-27 17:50:12 -05:00
Jared Vu d24a02f7b9 Add BrowserRouter support (#258)
* Add BrowserRouter support

* Handle base route for hashrouter/browserrouter

* Add hashstring fallback

* parseHash -> parseLocationHash
2024-02-23 09:44:01 -08:00
mike-dydx caaea7e475 remove trailing slash (#325) 2024-02-23 08:49:10 -08:00
Bill 9d3a124485 Add support for OKX wallet (#312)
* Add support for OKX wallet

* strings
2024-02-22 13:54:58 -08:00
aleka 038eb08e2e pessimistic collat check in non-dev/staging envs (#323)
Signed-off-by: aforaleka <aforaleka@gmail.com>
2024-02-22 15:11:42 -05:00
moo-onthelawn e1d2a5eda4 Update env config to enable reduce only in testnet (#321)
* update flag

* update new env.json
2024-02-22 13:07:06 -05:00
moo-onthelawn 56be9ba9cc traced back to innerWindow error (#320) 2024-02-21 19:17:33 -05:00
Bill 859e7dde80 Add notification for season 3 incentives (#322)
* Add notification for season 3 incentives

* add type

* address comments
2024-02-21 16:04:56 -08:00
John Huang d9daf9f583 Feature/mob 193 install app dlg (#308)
* getOS and get meta from header

* Clean up

* Update localization

* Comments

* Rename URL variable

* MOB-193 PR

* PR
2024-02-21 12:52:11 -08:00
John Huang 5be9d2b1b2 TRCL-3553 Having the launch incentive api in env.json (#317) 2024-02-21 12:51:36 -08:00
Bill d2c5b7945b Remove CEX withdraw testflag (#316) 2024-02-21 09:50:11 -08:00
aleka fc5d8538fe bump abacus for new env config path usage (#314)
* bump abacus for new env config path usage

* bump abacus + localization for good til date
2024-02-20 22:42:18 -05:00
yujin512 fdc262f59f Update otherMarketExchangeConfig.json (#315)
* Update otherMarketExchangeConfig.json

add strk

* Update otherMarketExchangeConfig.json

remove huobi, add kraken

* Update otherMarketParameters.json

add strk to parameters

* add strk icon and metadata
2024-02-20 16:08:11 -05:00
Bill 7f947804bd Enable CCTP onboarding for avalanche, optimism (#310)
* Enable CCTP for arbitrum avalanche optimism

* Enable CCTP onboarding for arbitrum, avalanche, optimism

* remove arb
2024-02-19 20:18:57 -08:00
aleka 6620a70a5d fix parsing error and surface tx broadcast error (#313)
* fix parsing error and surface tx broadcast error

* fix network as chain id
2024-02-19 22:06:13 -05:00
Bill bff779631b Cex withdraw (#301)
* Coinbase withdrawal and deposit UI

* analytics

* packages bumps

* address comments

* address comments

* add test flag
2024-02-16 17:56:06 -08:00
yujin512 b115bf33a0 Update markets.json (#311)
update ORDI asset name
2024-02-16 10:54:13 -08:00
aleka b544b8202b fetch chaos labs latest season (#309)
* fetch chaos labs latest season

* safer access
2024-02-16 11:09:48 -05:00
yujin512 ad6f99ffdf add icons and metadata (#305)
* add icons

* Update otherMarketParameters.json

update numOracles for markets with robust DEX oracles (not currently queryable)

* Update otherMarketParameters.json

update numOracles for markets with robust DEX oracles

* update asset name to match cmc

* Update otherMarketParameters.json

* add icons for other markets

* fix url and market name
2024-02-16 09:44:08 -05:00
Bill a7bd6a3ee8 host amplitude script ourselves (#307)
* host amplitude script ourselves

* update sha
2024-02-15 15:45:44 -08:00
aleka c92f218493 bump abacus + use new env config format (#298)
* bump abacus + use new env config format

* use new config path

* bump abacus to 1.4.5

* util function for getting mintscan link
2024-02-15 13:38:05 -05:00
moo-onthelawn b09fe9ebb1 fix bug (#304) 2024-02-14 15:19:37 -08:00
Bill 1c28505ec0 Set minimum for CCTP withdraws (#306) 2024-02-14 14:59:04 -08:00
99 changed files with 1559 additions and 698 deletions
+1
View File
@@ -3,6 +3,7 @@ VITE_BASE_URL=
VITE_ALCHEMY_API_KEY= VITE_ALCHEMY_API_KEY=
VITE_PK_ENCRYPTION_KEY= VITE_PK_ENCRYPTION_KEY=
VITE_ROUTER_TYPE=
VITE_V3_TOKEN_ADDRESS= VITE_V3_TOKEN_ADDRESS=
VITE_TOKEN_MIGRATION_URI= VITE_TOKEN_MIGRATION_URI=
+41
View File
@@ -0,0 +1,41 @@
name: Publish ApplicationRecord to Registry
on:
release:
types: [published]
push:
branches:
- registry
- test
env:
CERC_REGISTRY_USER_KEY: ${{ secrets.CICD_LACONIC_USER_KEY }}
CERC_REGISTRY_BOND_ID: ${{ secrets.CICD_LACONIC_BOND_ID }}
COREPACK_ENABLE_STRICT: 0
jobs:
cns_publish:
runs-on: ubuntu-latest
steps:
- name: "Clone project repository"
uses: actions/checkout@v3
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: "Install Yarn"
run: npm install -g yarn
- name: "Install Corepack"
run: npm install -g corepack
- name: "Enable Corepack"
run: corepack enable
- name: "Install registry CLI"
run: |
npm config set @cerc-io:registry https://git.vdb.to/api/packages/cerc-io/npm/
yarn global add @cerc-io/laconic-registry-cli
- name: "Install jq"
run: apt -y update && apt -y install jq
- name: "Publish Application Record"
run: scripts/publish-app-record.sh
- name: "Request Deployment"
run: scripts/request-app-deployment.sh
+1
View File
@@ -1 +1,2 @@
node_modules/ node_modules/
public/
+20 -6
View File
@@ -66,10 +66,12 @@ 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`. 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: 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 - 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) - 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 ## Part 4: Set Enviornment variables
Set environment variables via `.env`. Set environment variables via `.env`.
- `VITE_BASE_URL` (required): The base URL of the deployment (e.g., `https://www.example.com`). - `VITE_BASE_URL` (required): The base URL of the deployment (e.g., `https://www.example.com`).
@@ -96,10 +98,12 @@ Select "Import Git Repository" from your dashboard, and provide the URL of this
### Step 2: Configure your project ### Step 2: Configure your project
For the "Build & Development Settings", we recommend the following: For the "Build & Development Settings", we recommend the following:
- Framework Preset: `Vite` - Framework Preset: `Vite`
- Build Command (override): `pnpm run build` - 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: 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 pnpm run build --mode testnet
``` ```
@@ -114,6 +118,14 @@ For more details, check out Vercel's [official documentation](https://vercel.com
## Deploying to IPFS ## 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 ### 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: 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:
@@ -150,12 +162,14 @@ Replace `your_cid` with the actual CID.
We recommend that you add your website to Cloudflare for additional security settings. We recommend that you add your website to Cloudflare for additional security settings.
To block OFAC Sanctioned countries: To block OFAC Sanctioned countries:
1. Navigate Websites > Domain > Security > WAF 1. Navigate Websites > Domain > Security > WAF
2. Create Rule with the following settings: 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")` - If incoming requests match
* This rule will bring up a Cloudflare page when a restricted geography tries to access your site. You will have the option to display: `(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")`
1. Custom Text - This rule will bring up a Cloudflare page when a restricted geography tries to access your site. You will have the option to display:
- (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.`) 1. Custom Text
2. Default Cloudflare WAF block page - (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
+4 -3
View File
@@ -1,5 +1,6 @@
{ {
"name": "dydx-chain-web", "name": "@lx-mars/dydx-chain-web",
"repository": "https://git.vdb.to/LaconicNetwork/dydx-v4-web",
"private": true, "private": true,
"version": "1.0.0", "version": "1.0.0",
"type": "module", "type": "module",
@@ -40,9 +41,9 @@
"@cosmjs/proto-signing": "^0.32.1", "@cosmjs/proto-signing": "^0.32.1",
"@cosmjs/stargate": "^0.32.1", "@cosmjs/stargate": "^0.32.1",
"@cosmjs/tendermint-rpc": "^0.32.1", "@cosmjs/tendermint-rpc": "^0.32.1",
"@dydxprotocol/v4-abacus": "^1.4.2", "@dydxprotocol/v4-abacus": "^1.4.12",
"@dydxprotocol/v4-client-js": "^1.0.20", "@dydxprotocol/v4-client-js": "^1.0.20",
"@dydxprotocol/v4-localization": "^1.1.30", "@dydxprotocol/v4-localization": "^1.1.35",
"@ethersproject/providers": "^5.7.2", "@ethersproject/providers": "^5.7.2",
"@js-joda/core": "^5.5.3", "@js-joda/core": "^5.5.3",
"@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-accordion": "^1.1.2",
+8 -8
View File
@@ -30,14 +30,14 @@ dependencies:
specifier: ^0.32.1 specifier: ^0.32.1
version: 0.32.2 version: 0.32.2
'@dydxprotocol/v4-abacus': '@dydxprotocol/v4-abacus':
specifier: ^1.4.2 specifier: ^1.4.12
version: 1.4.2 version: 1.4.12
'@dydxprotocol/v4-client-js': '@dydxprotocol/v4-client-js':
specifier: ^1.0.20 specifier: ^1.0.20
version: 1.0.20 version: 1.0.20
'@dydxprotocol/v4-localization': '@dydxprotocol/v4-localization':
specifier: ^1.1.30 specifier: ^1.1.35
version: 1.1.30 version: 1.1.35
'@ethersproject/providers': '@ethersproject/providers':
specifier: ^5.7.2 specifier: ^5.7.2
version: 5.7.2 version: 5.7.2
@@ -1290,8 +1290,8 @@ packages:
resolution: {integrity: sha512-Gg5t+eR7vPJMAmhkFt6CZrzPd0EKpAslWwk5rFVYZpJsM8JG5KT9XQ99hgNM3Ov6ScNoIWbXkpX27F6A9cXR4Q==} resolution: {integrity: sha512-Gg5t+eR7vPJMAmhkFt6CZrzPd0EKpAslWwk5rFVYZpJsM8JG5KT9XQ99hgNM3Ov6ScNoIWbXkpX27F6A9cXR4Q==}
dev: false dev: false
/@dydxprotocol/v4-abacus@1.4.2: /@dydxprotocol/v4-abacus@1.4.12:
resolution: {integrity: sha512-+hugk0RulMwMthR2xCMYXohcC3sEYqVW/lmiq0RUuHZ9yrjmgy48xl0aZUmXGUYXyoiHXPS4AULhRKHQ4OOLwg==} resolution: {integrity: sha512-dNQFaNrLTujyViMH2JEtf9Vn2ew2le+qTWexktK5h7AeADBbS9uxQknMIFhfwzGkbS1QqqszBT1plrDmQ+E6nQ==}
dev: false dev: false
/@dydxprotocol/v4-client-js@1.0.20: /@dydxprotocol/v4-client-js@1.0.20:
@@ -1323,8 +1323,8 @@ packages:
- utf-8-validate - utf-8-validate
dev: false dev: false
/@dydxprotocol/v4-localization@1.1.30: /@dydxprotocol/v4-localization@1.1.35:
resolution: {integrity: sha512-TZfWWRSOxcjLHs972wlJVVHkE7+DVqAUnGZSs24HYHsPtUkPhZiNXMOA2Vk9YddQxumhM79xIRH0cmJSe5DDUg==} resolution: {integrity: sha512-q5JFYoL/QanHXOtFqRa2owBZJibi1sMpSm3dAcxs9x0/xe8mo6fWcnbQfhl8k7g0/tv7PsBc+e3rbWD0EfvGiA==}
dev: false dev: false
/@dydxprotocol/v4-proto@4.0.0-dev.0: /@dydxprotocol/v4-proto@4.0.0-dev.0:
+21 -11
View File
@@ -1,12 +1,22 @@
[ [
{ {
"chainId": "1", "chainId": "1",
"tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"name": "Ethereum" "name": "Ethereum"
}, },
{ {
"chainId": "5", "chainId": "5",
"tokenAddress": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F", "tokenAddress": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F",
"name": "Ethereum Goerli" "name": "Ethereum Goerli"
} },
] {
"chainId": "43114",
"tokenAddress": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"name": "Avalanche"
},
{
"chainId": "10",
"tokenAddress": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
"name": "optimism"
}
]
+8 -8
View File
@@ -725,7 +725,7 @@
} }
}, },
"featureFlags": { "featureFlags": {
"reduceOnlySupported": false "reduceOnlySupported": true
} }
}, },
"dydxprotocol-testnet-dydx": { "dydxprotocol-testnet-dydx": {
@@ -812,7 +812,7 @@
} }
}, },
"featureFlags": { "featureFlags": {
"reduceOnlySupported": false "reduceOnlySupported": true
} }
}, },
"dydxprotocol-testnet-nodefleet": { "dydxprotocol-testnet-nodefleet": {
@@ -899,7 +899,7 @@
} }
}, },
"featureFlags": { "featureFlags": {
"reduceOnlySupported": false "reduceOnlySupported": true
} }
}, },
"dydxprotocol-testnet-kingnodes": { "dydxprotocol-testnet-kingnodes": {
@@ -986,7 +986,7 @@
} }
}, },
"featureFlags": { "featureFlags": {
"reduceOnlySupported": false "reduceOnlySupported": true
} }
}, },
"dydxprotocol-testnet-liquify": { "dydxprotocol-testnet-liquify": {
@@ -1073,7 +1073,7 @@
} }
}, },
"featureFlags": { "featureFlags": {
"reduceOnlySupported": false "reduceOnlySupported": true
} }
}, },
"dydxprotocol-testnet-polkachu": { "dydxprotocol-testnet-polkachu": {
@@ -1151,7 +1151,7 @@
} }
}, },
"featureFlags": { "featureFlags": {
"reduceOnlySupported": false "reduceOnlySupported": true
} }
}, },
"dydxprotocol-testnet-bware": { "dydxprotocol-testnet-bware": {
@@ -1238,7 +1238,7 @@
} }
}, },
"featureFlags": { "featureFlags": {
"reduceOnlySupported": false "reduceOnlySupported": true
} }
}, },
"dydxprotocol-mainnet": { "dydxprotocol-mainnet": {
@@ -1329,4 +1329,4 @@
} }
} }
} }
} }
+162 -1
View File
@@ -20,6 +20,13 @@
"whitepaperLink": "https://why.cardano.org/en/introduction/motivation/", "whitepaperLink": "https://why.cardano.org/en/introduction/motivation/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/cardano/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/cardano/"
}, },
"AGIX-USD": {
"name": "SingularityNET",
"tags": ["AI"],
"websiteLink": "https://public.singularitynet.io/whitepaper.pdf",
"whitepaperLink": "https://public.singularitynet.io/whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/singularitynet/"
},
"ALGO-USD": { "ALGO-USD": {
"name": "Algorand", "name": "Algorand",
"tags": ["Layer 1"], "tags": ["Layer 1"],
@@ -78,7 +85,7 @@
}, },
"BLUR-USD": { "BLUR-USD": {
"name": "Blur", "name": "Blur",
"tags": [], "tags": ["NFT"],
"websiteLink": "https://blur.io/", "websiteLink": "https://blur.io/",
"whitepaperLink": "https://docs.blur.foundation/", "whitepaperLink": "https://docs.blur.foundation/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/blur-token/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/blur-token/"
@@ -91,6 +98,13 @@
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/bnb/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/bnb/"
}, },
"CHZ-USD": {
"name": "Chiliz",
"tags": ["Layer 1"],
"websiteLink": "https://www.chiliz.com/",
"whitepaperLink": "https://www.chiliz.com/docs/litepaper-v1.1-20230703.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/chiliz/"
},
"CELO-USD": { "CELO-USD": {
"name": "Celo", "name": "Celo",
"tags": [], "tags": [],
@@ -147,6 +161,13 @@
"whitepaperLink": "https://cdn.enjin.io/downloads/whitepapers/enjin-coin/en.pdf/", "whitepaperLink": "https://cdn.enjin.io/downloads/whitepapers/enjin-coin/en.pdf/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/enjin-coin/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/enjin-coin/"
}, },
"ENS-USD": {
"name": "Ethereum Name Service",
"tags": [],
"websiteLink": "https://coinmarketcap.com/currencies/ethereum-name-service/",
"whitepaperLink": "https://docs.ens.domains/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/ethereum-name-service/"
},
"EOS-USD": { "EOS-USD": {
"name": "EOS", "name": "EOS",
"tags": ["Layer 1"], "tags": ["Layer 1"],
@@ -170,6 +191,13 @@
"displayStepSize": "0.001", "displayStepSize": "0.001",
"displayTickSize": "0.1" "displayTickSize": "0.1"
}, },
"FET-USD": {
"name": "Fetch.ai",
"tags": ["AI"],
"websiteLink": "https://fetch.ai/",
"whitepaperLink": "https://www.dropbox.com/s/gxptsecwdl3jjtn/David%20Minarsch%20-%202021-04-26%2010.34.17%20-%20paper_21_finalversion.pdf?e=1&dl=0",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/fetch/"
},
"FIL-USD": { "FIL-USD": {
"name": "Filecoin", "name": "Filecoin",
"tags": ["Layer 1"], "tags": ["Layer 1"],
@@ -177,6 +205,34 @@
"whitepaperLink": "https://filecoin.io/filecoin.pdf", "whitepaperLink": "https://filecoin.io/filecoin.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/filecoin/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/filecoin/"
}, },
"FTM-USD": {
"name": "Fantom",
"tags": [],
"websiteLink": "https://fantom.foundation/",
"whitepaperLink": "https://fantom.foundation/_next/static/media/wp_fantom_v1.6.39329cdc5d0ee59684cbc6f228516383.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/fantom/"
},
"GALA-USD": {
"name": "Gala",
"tags": ["Gaming", "Layer 1"],
"websiteLink": "https://gala.com/",
"whitepaperLink": "https://galahackathon.com/v1.0.0/pdf/sdk-documentation.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/gala/"
},
"GMT-USD": {
"name": "GMT",
"tags": ["Gaming"],
"websiteLink": "https://stepn.com/",
"whitepaperLink/": "https://whitepaper.stepn.com/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/green-metaverse-token/"
},
"GRT-USD": {
"name": "The Graph",
"tags": [],
"websiteLink": "https://thegraph.com/",
"whitepaperLink/": "https://github.com/graphprotocol/research/blob/master/papers/whitepaper/the-graph-whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/the-graph/"
},
"HNT-USD": { "HNT-USD": {
"name": "Helium", "name": "Helium",
"tags": ["Layer 1"], "tags": ["Layer 1"],
@@ -184,6 +240,13 @@
"whitepaperLink": "http://whitepaper.helium.com", "whitepaperLink": "http://whitepaper.helium.com",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/helium/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/helium/"
}, },
"HBAR-USD": {
"name": "Hedera",
"tags": [],
"websiteLink": "https://hedera.com/",
"whitepaperLink/": "https://files.hedera.com/hh_whitepaper_v2.2-20230918.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/hedera/"
},
"ICP-USD": { "ICP-USD": {
"name": "Internet Computer", "name": "Internet Computer",
"tags": ["Layer 1"], "tags": ["Layer 1"],
@@ -191,6 +254,27 @@
"whitepaperLink": "https://dfinity.org/whitepaper.pdf", "whitepaperLink": "https://dfinity.org/whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/internet-computer/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/internet-computer/"
}, },
"IMX-USD": {
"name": "Immutable X",
"tags": ["Gaming", "Layer 2", "NFT"],
"websiteLink": "https://www.immutable.com/",
"whitepaperLink": "https://assets.website-files.com/646557ee455c3e16e4a9bcb3/6499367de527dd82ab7475a3_Immutable%20Whitepaper%20Update%202023%20(3).pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/immutable-x/"
},
"INJ-USD": {
"name": "Injective",
"tags": ["Layer 1", "Defi"],
"websiteLink": "https://injective.com/",
"whitepaperLink": "https://docs.injective.network/intro/01_overview.html",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/injective/"
},
"JTO-USD": {
"name": "Jito",
"tags": ["Defi"],
"websiteLink": "https://www.jito.network/",
"whitepaperLink": "https://github.com/jito-foundation",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/jito/"
},
"JUP-USD": { "JUP-USD": {
"name": "Jupiter", "name": "Jupiter",
"tags": ["Defi"], "tags": ["Defi"],
@@ -198,6 +282,13 @@
"whitepaperLink": "https://station.jup.ag/blog/green-paper", "whitepaperLink": "https://station.jup.ag/blog/green-paper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/jupiter-ag/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/jupiter-ag/"
}, },
"KAVA-USD": {
"name": "Kava",
"tags": ["Layer 1"],
"websiteLink": "https://www.kava.io/",
"whitepaperLink": "https://docsend.com/view/gwbwpc3",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/kava/"
},
"LDO-USD": { "LDO-USD": {
"name": "Lido DAO", "name": "Lido DAO",
"tags": ["Defi"], "tags": ["Defi"],
@@ -219,6 +310,20 @@
"whitepaperLink": "https://litecoin.info/index.php/Main_Page", "whitepaperLink": "https://litecoin.info/index.php/Main_Page",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/litecoin/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/litecoin/"
}, },
"MANA-USD": {
"name": "Decentraland",
"tags": ["AR/VR"],
"websiteLink": "https://decentraland.org/",
"whitepaperLink": "https://decentraland.org/whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/decentraland/"
},
"MASK-USD": {
"name": "Mask Network",
"tags": [],
"websiteLink": "https://mask.io/",
"whitepaperLink": "https://masknetwork.medium.com/introducing-mask-network-maskbook-the-future-of-the-internet-5a973d874edd",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/mask-network/"
},
"MATIC-USD": { "MATIC-USD": {
"name": "Polygon", "name": "Polygon",
"tags": ["Layer 2"], "tags": ["Layer 2"],
@@ -226,6 +331,13 @@
"whitepaperLink": "https://polygon.technology/lightpaper-polygon.pdf", "whitepaperLink": "https://polygon.technology/lightpaper-polygon.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/polygon/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/polygon/"
}, },
"MINA-USD": {
"name": "Mina",
"tags": ["Layer 1"],
"websiteLink": "https://minaprotocol.com/",
"whitepaperLink": "https://docs.minaprotocol.com/assets/economicsWhitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/mina/"
},
"MKR-USD": { "MKR-USD": {
"name": "Maker", "name": "Maker",
"tags": ["Governance"], "tags": ["Governance"],
@@ -240,6 +352,13 @@
"whitepaperLink": "https://near.org/papers/the-official-near-white-paper/", "whitepaperLink": "https://near.org/papers/the-official-near-white-paper/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/near-protocol/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/near-protocol/"
}, },
"ORDI-USD": {
"name": "Ordinals",
"tags": ["NFT"],
"websiteLink": "https://ordinals.com/",
"whitepaperLink": "https://rodarmor.com/blog/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/ordi/"
},
"OP-USD": { "OP-USD": {
"name": "Optimism", "name": "Optimism",
"tags": [], "tags": [],
@@ -253,6 +372,20 @@
"websiteLink": "https://www.pepe.vip/", "websiteLink": "https://www.pepe.vip/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/pepe/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/pepe/"
}, },
"PYTH-USD": {
"name": "Pyth Network",
"tags": [],
"websiteLink": "https://pyth.network/",
"whitepaperLink": "https://pyth.network/whitepaper_v2.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/pyth-network/"
},
"RNDR-USD": {
"name": "Render Token",
"tags": ["AI"],
"websiteLink": "https://rendernetwork.com/",
"whitepaperLink": "https://renderfoundation.com/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/render/"
},
"RUNE-USD": { "RUNE-USD": {
"name": "THORChain", "name": "THORChain",
"tags": ["Layer 1"], "tags": ["Layer 1"],
@@ -260,6 +393,13 @@
"whitepaperLink": "https://whitepaper.io/document/709/thorchain-whitepaper", "whitepaperLink": "https://whitepaper.io/document/709/thorchain-whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/thorchain/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/thorchain/"
}, },
"SAND-USD": {
"name": "The Sandbox",
"tags": ["Gaming"],
"websiteLink": "https://www.sandbox.game/en/",
"whitepaperLink": "https://installers.sandbox.game/The_Sandbox_Whitepaper_2020.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/the-sandbox/"
},
"SEI-USD": { "SEI-USD": {
"name": "Sei", "name": "Sei",
"tags": ["Layer 1", "Defi"], "tags": ["Layer 1", "Defi"],
@@ -288,6 +428,20 @@
"whitepaperLink": "https://solana.com/solana-whitepaper.pdf", "whitepaperLink": "https://solana.com/solana-whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/solana/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/solana/"
}, },
"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"],
"websiteLink": "https://www.stacks.co/",
"whitepaperLink": "https://gaia.blockstack.org/hub/1AxyPunHHAHiEffXWESKfbvmBpGQv138Fp/stacks.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/stacks/"
},
"SUI-USD": { "SUI-USD": {
"name": "Sui", "name": "Sui",
"tags": ["Layer 1"], "tags": ["Layer 1"],
@@ -337,6 +491,13 @@
"whitepaperLink": "https://whitepaper.worldcoin.org/", "whitepaperLink": "https://whitepaper.worldcoin.org/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/worldcoin-org/" "coinMarketCapsLink": "https://coinmarketcap.com/currencies/worldcoin-org/"
}, },
"WOO-USD": {
"name": "WOO Network",
"tags": ["Defi"],
"websiteLink": "https://woo.org/",
"whitepaperLink": "https://woo.org/Litepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/wootrade/"
},
"XLM-USD": { "XLM-USD": {
"name": "Stellar", "name": "Stellar",
"tags": ["Layer 1"], "tags": ["Layer 1"],
@@ -513,6 +513,14 @@
{ "exchangeName": "Okx", "ticker": "SOL-USDT", "adjustByMarket": "USDT-USD" }, { "exchangeName": "Okx", "ticker": "SOL-USDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Mexc", "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": [ "STX": [
{ "exchangeName": "Binance", "ticker": "STXUSDT", "adjustByMarket": "USDT-USD" }, { "exchangeName": "Binance", "ticker": "STXUSDT", "adjustByMarket": "USDT-USD" },
{ "exchangeName": "Bybit", "ticker": "STXUSDT", "adjustByMarket": "USDT-USD" }, { "exchangeName": "Bybit", "ticker": "STXUSDT", "adjustByMarket": "USDT-USD" },
+29 -12
View File
@@ -274,7 +274,7 @@
{ {
"baseAsset": "CRV", "baseAsset": "CRV",
"referencePrice": 0.48346754113361784, "referencePrice": 0.48346754113361784,
"numOracles": 5, "numOracles": 6,
"liquidityTier": 2, "liquidityTier": 2,
"assetName": "Curve DAO Token", "assetName": "Curve DAO Token",
"p": -1.0, "p": -1.0,
@@ -325,7 +325,7 @@
{ {
"baseAsset": "DYM", "baseAsset": "DYM",
"referencePrice": 5.544949130771594, "referencePrice": 5.544949130771594,
"numOracles": 5, "numOracles": 6,
"liquidityTier": 2, "liquidityTier": 2,
"assetName": "Dymension", "assetName": "Dymension",
"p": 0.0, "p": 0.0,
@@ -412,7 +412,7 @@
"referencePrice": 0.5560893147613742, "referencePrice": 0.5560893147613742,
"numOracles": 6, "numOracles": 6,
"liquidityTier": 2, "liquidityTier": 2,
"assetName": "Fetch AI", "assetName": "Fetch.ai",
"p": -1.0, "p": -1.0,
"atomicResolution": -5.0, "atomicResolution": -5.0,
"minExchanges": 3, "minExchanges": 3,
@@ -480,7 +480,7 @@
"referencePrice": 0.20404992804891367, "referencePrice": 0.20404992804891367,
"numOracles": 6, "numOracles": 6,
"liquidityTier": 2, "liquidityTier": 2,
"assetName": "STEPN", "assetName": "GMT",
"p": -1.0, "p": -1.0,
"atomicResolution": -5.0, "atomicResolution": -5.0,
"minExchanges": 3, "minExchanges": 3,
@@ -514,7 +514,7 @@
"referencePrice": 0.07686278766140874, "referencePrice": 0.07686278766140874,
"numOracles": 6, "numOracles": 6,
"liquidityTier": 2, "liquidityTier": 2,
"assetName": "Hedera Hashgraph", "assetName": "Hedera",
"p": -2.0, "p": -2.0,
"atomicResolution": -4.0, "atomicResolution": -4.0,
"minExchanges": 3, "minExchanges": 3,
@@ -565,7 +565,7 @@
"referencePrice": 33.427379051845875, "referencePrice": 33.427379051845875,
"numOracles": 7, "numOracles": 7,
"liquidityTier": 2, "liquidityTier": 2,
"assetName": "Injective Protocol", "assetName": "Injective",
"p": 1.0, "p": 1.0,
"atomicResolution": -7.0, "atomicResolution": -7.0,
"minExchanges": 3, "minExchanges": 3,
@@ -597,7 +597,7 @@
{ {
"baseAsset": "JUP", "baseAsset": "JUP",
"referencePrice": 0.4339790660244292, "referencePrice": 0.4339790660244292,
"numOracles": 5, "numOracles": 6,
"liquidityTier": 2, "liquidityTier": 2,
"assetName": "Jupiter", "assetName": "Jupiter",
"p": -1.0, "p": -1.0,
@@ -750,7 +750,7 @@
{ {
"baseAsset": "MKR", "baseAsset": "MKR",
"referencePrice": 1943.1865605831852, "referencePrice": 1943.1865605831852,
"numOracles": 5, "numOracles": 6,
"liquidityTier": 2, "liquidityTier": 2,
"assetName": "Maker", "assetName": "Maker",
"p": 3.0, "p": 3.0,
@@ -869,7 +869,7 @@
{ {
"baseAsset": "RUNE", "baseAsset": "RUNE",
"referencePrice": 3.7264809096406806, "referencePrice": 3.7264809096406806,
"numOracles": 5, "numOracles": 6,
"liquidityTier": 2, "liquidityTier": 2,
"assetName": "Thorchain", "assetName": "Thorchain",
"p": 0.0, "p": 0.0,
@@ -888,7 +888,7 @@
"referencePrice": 0.37297439115035663, "referencePrice": 0.37297439115035663,
"numOracles": 6, "numOracles": 6,
"liquidityTier": 2, "liquidityTier": 2,
"assetName": "SAND", "assetName": "The Sandbox",
"p": -1.0, "p": -1.0,
"atomicResolution": -5.0, "atomicResolution": -5.0,
"minExchanges": 3, "minExchanges": 3,
@@ -968,6 +968,23 @@
"minOrderSize": 1000000, "minOrderSize": 1000000,
"quantumConversionExponent": -9 "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", "baseAsset": "STX",
"referencePrice": 1.5144048703611412, "referencePrice": 1.5144048703611412,
@@ -1073,9 +1090,9 @@
{ {
"baseAsset": "WOO", "baseAsset": "WOO",
"referencePrice": 0.2944897275608197, "referencePrice": 0.2944897275608197,
"numOracles": 5, "numOracles": 6,
"liquidityTier": 2, "liquidityTier": 2,
"assetName": "Wootrade Network", "assetName": "WOO Network",
"p": -1.0, "p": -1.0,
"atomicResolution": -5.0, "atomicResolution": -5.0,
"minExchanges": 3, "minExchanges": 3,
+429 -408
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

+1 -1
View File
@@ -14,7 +14,7 @@ if (AMPLITUDE_API_KEY) {
const html = await fs.readFile(htmlFilePath, 'utf-8'); const html = await fs.readFile(htmlFilePath, 'utf-8');
const amplitudeCdnScript = `<script type="text/javascript"> const amplitudeCdnScript = `<script type="text/javascript">
!function(){"use strict";!function(e,t){var n=e.amplitude||{_q:[],_iq:{}};if(n.invoked)e.console&&console.error&&console.error("Amplitude snippet has been loaded.");else{var r=function(e,t){e.prototype[t]=function(){return this._q.push({name:t,args:Array.prototype.slice.call(arguments,0)}),this}},s=function(e,t,n){return function(r){e._q.push({name:t,args:Array.prototype.slice.call(n,0),resolve:r})}},o=function(e,t,n){e[t]=function(){if(n)return{promise:new Promise(s(e,t,Array.prototype.slice.call(arguments)))}}},i=function(e){for(var t=0;t<m.length;t++)o(e,m[t],!1);for(var n=0;n<g.length;n++)o(e,g[n],!0)};n.invoked=!0;var u=t.createElement("script");u.type="text/javascript",u.integrity="sha384-x0ik2D45ZDEEEpYpEuDpmj05fY91P7EOZkgdKmq4dKL/ZAVcufJ+nULFtGn0HIZE",u.crossOrigin="anonymous",u.async=!0,u.src="https://cdn.amplitude.com/libs/analytics-browser-2.0.0-min.js.gz",u.onload=function(){e.amplitude.runQueuedFunctions||console.log("[Amplitude] Error: could not load SDK")};var a=t.getElementsByTagName("script")[0];a.parentNode.insertBefore(u,a);for(var c=function(){return this._q=[],this},p=["add","append","clearAll","prepend","set","setOnce","unset","preInsert","postInsert","remove","getUserProperties"],l=0;l<p.length;l++)r(c,p[l]);n.Identify=c;for(var d=function(){return this._q=[],this},f=["getEventProperties","setProductId","setQuantity","setPrice","setRevenue","setRevenueType","setEventProperties"],v=0;v<f.length;v++)r(d,f[v]);n.Revenue=d;var m=["getDeviceId","setDeviceId","getSessionId","setSessionId","getUserId","setUserId","setOptOut","setTransport","reset","extendSession"],g=["init","add","remove","track","logEvent","identify","groupIdentify","setGroup","revenue","flush"];i(n),n.createInstance=function(e){return n._iq[e]={_q:[]},i(n._iq[e]),n._iq[e]},e.amplitude=n}}(window,document)}(); !function(){"use strict";!function(e,t){var n=e.amplitude||{_q:[],_iq:{}};if(n.invoked)e.console&&console.error&&console.error("Amplitude snippet has been loaded.");else{var r=function(e,t){e.prototype[t]=function(){return this._q.push({name:t,args:Array.prototype.slice.call(arguments,0)}),this}},s=function(e,t,n){return function(r){e._q.push({name:t,args:Array.prototype.slice.call(n,0),resolve:r})}},o=function(e,t,n){e[t]=function(){if(n)return{promise:new Promise(s(e,t,Array.prototype.slice.call(arguments)))}}},i=function(e){for(var t=0;t<m.length;t++)o(e,m[t],!1);for(var n=0;n<g.length;n++)o(e,g[n],!0)};n.invoked=!0;var u=t.createElement("script");u.type="text/javascript",u.integrity="sha384-BVo5ZjsjH373rWbcjz9Qjb2L6BgLwLADcZtZZPu3nMl8+7LPDhi1NcUEf0Ate41Y",u.crossOrigin="anonymous",u.async=!0,u.src="/libs/amplitude-analytics-browser-2.0.0-min.js",u.onload=function(){e.amplitude.runQueuedFunctions||console.log("[Amplitude] Error: could not load SDK")};var a=t.getElementsByTagName("script")[0];a.parentNode.insertBefore(u,a);for(var c=function(){return this._q=[],this},p=["add","append","clearAll","prepend","set","setOnce","unset","preInsert","postInsert","remove","getUserProperties"],l=0;l<p.length;l++)r(c,p[l]);n.Identify=c;for(var d=function(){return this._q=[],this},f=["getEventProperties","setProductId","setQuantity","setPrice","setRevenue","setRevenueType","setEventProperties"],v=0;v<f.length;v++)r(d,f[v]);n.Revenue=d;var m=["getDeviceId","setDeviceId","getSessionId","setSessionId","getUserId","setUserId","setOptOut","setTransport","reset","extendSession"],g=["init","add","remove","track","logEvent","identify","groupIdentify","setGroup","revenue","flush"];i(n),n.createInstance=function(e){return n._iq[e]={_q:[]},i(n._iq[e]),n._iq[e]},e.amplitude=n}}(window,document)}();
</script> </script>
`; `;
+70
View File
@@ -0,0 +1,70 @@
#!/bin/bash
set -e
RECORD_FILE=tmp.rf.$$
CONFIG_FILE=`mktemp`
CERC_APP_TYPE=${CERC_APP_TYPE:-"webapp"}
CERC_REPO_REF=${CERC_REPO_REF:-${GITHUB_SHA:-`git log -1 --format="%H"`}}
CERC_IS_LATEST_RELEASE=${CERC_IS_LATEST_RELEASE:-"true"}
rcd_name=$(jq -r '.name' package.json | sed 's/null//')
rcd_desc=$(jq -r '.description' package.json | sed 's/null//')
rcd_repository=$(jq -r '.repository' package.json | sed 's/null//')
rcd_homepage=$(jq -r '.homepage' package.json | sed 's/null//')
rcd_license=$(jq -r '.license' package.json | sed 's/null//')
rcd_author=$(jq -r '.author' package.json | sed 's/null//')
rcd_app_version=$(jq -r '.version' package.json | sed 's/null//')
cat <<EOF > "$CONFIG_FILE"
services:
cns:
restEndpoint: '${CERC_REGISTRY_REST_ENDPOINT:-http://console.laconic.com:1317}'
gqlEndpoint: '${CERC_REGISTRY_GQL_ENDPOINT:-http://console.laconic.com:9473/api}'
chainId: ${CERC_REGISTRY_CHAIN_ID:-laconic_9000-1}
gas: 1750000
fees: 20000aphoton
EOF
next_ver=$(laconic -c $CONFIG_FILE cns record list --type ApplicationRecord --all --name "$rcd_name" 2>/dev/null | jq -r -s ".[] | sort_by(.createTime) | reverse | [ .[] | select(.bondId == \"$CERC_REGISTRY_BOND_ID\") ] | .[0].attributes.version" | awk -F. -v OFS=. '{$NF += 1 ; print}')
if [ -z "$next_ver" ] || [ "1" == "$next_ver" ]; then
next_ver=0.0.1
fi
cat <<EOF | sed '/.*: ""$/d' > "$RECORD_FILE"
record:
type: ApplicationRecord
version: ${next_ver}
name: "$rcd_name"
description: "$rcd_desc"
homepage: "$rcd_homepage"
license: "$rcd_license"
author: "$rcd_author"
repository:
- "$rcd_repository"
repository_ref: "$CERC_REPO_REF"
app_version: "$rcd_app_version"
app_type: "$CERC_APP_TYPE"
EOF
cat $RECORD_FILE
RECORD_ID=$(laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE --user-key "${CERC_REGISTRY_USER_KEY}" --bond-id ${CERC_REGISTRY_BOND_ID} | jq -r '.id')
echo $RECORD_ID
if [ -z "$CERC_REGISTRY_APP_CRN" ]; then
authority=$(echo "$rcd_name" | cut -d'/' -f1 | sed 's/@//')
app=$(echo "$rcd_name" | cut -d'/' -f2-)
CERC_REGISTRY_APP_CRN="crn://$authority/applications/$app"
fi
laconic -c $CONFIG_FILE cns name set --user-key "${CERC_REGISTRY_USER_KEY}" --bond-id ${CERC_REGISTRY_BOND_ID} "$CERC_REGISTRY_APP_CRN@${rcd_app_version}" "$RECORD_ID"
laconic -c $CONFIG_FILE cns name set --user-key "${CERC_REGISTRY_USER_KEY}" --bond-id ${CERC_REGISTRY_BOND_ID} "$CERC_REGISTRY_APP_CRN@${CERC_REPO_REF}" "$RECORD_ID"
if [ "true" == "$CERC_IS_LATEST_RELEASE" ]; then
laconic -c $CONFIG_FILE cns name set --user-key "${CERC_REGISTRY_USER_KEY}" --bond-id ${CERC_REGISTRY_BOND_ID} "$CERC_REGISTRY_APP_CRN" "$RECORD_ID"
fi
rm -f $RECORD_FILE $CONFIG_FILE
+57
View File
@@ -0,0 +1,57 @@
#!/bin/bash
set -e
RECORD_FILE=tmp.rf.$$
CONFIG_FILE=`mktemp`
rcd_name=$(jq -r '.name' package.json | sed 's/null//' | sed 's/^@//')
rcd_app_version=$(jq -r '.version' package.json | sed 's/null//')
cat <<EOF > "$CONFIG_FILE"
services:
cns:
restEndpoint: '${CERC_REGISTRY_REST_ENDPOINT:-http://console.laconic.com:1317}'
gqlEndpoint: '${CERC_REGISTRY_GQL_ENDPOINT:-http://console.laconic.com:9473/api}'
chainId: ${CERC_REGISTRY_CHAIN_ID:-laconic_9000-1}
gas: 1550000
fees: 20000aphoton
EOF
if [ -z "$CERC_REGISTRY_APP_CRN" ]; then
authority=$(echo "$rcd_name" | cut -d'/' -f1 | sed 's/@//')
app=$(echo "$rcd_name" | cut -d'/' -f2-)
CERC_REGISTRY_APP_CRN="crn://$authority/applications/$app"
fi
APP_RECORD=$(laconic -c $CONFIG_FILE cns name resolve "$CERC_REGISTRY_APP_CRN" | jq '.[0]')
if [ -z "$APP_RECORD" ] || [ "null" == "$APP_RECORD" ]; then
echo "No record found for $CERC_REGISTRY_APP_CRN."
exit 1
fi
cat <<EOF | sed '/.*: ""$/d' > "$RECORD_FILE"
record:
type: ApplicationDeploymentRequest
version: 1.0.0
name: "$rcd_name@$rcd_app_version"
application: "$CERC_REGISTRY_APP_CRN@$rcd_app_version"
dns: "$CERC_REGISTRY_DEPLOYMENT_SHORT_HOSTNAME"
deployment: "$CERC_REGISTRY_DEPLOYMENT_CRN"
config:
env:
CERC_WEBAPP_DEBUG: "$rcd_app_version"
CERC_MAX_GENERATE_TIME: 180
CERC_NEXT_VERSION: 13.4.2
meta:
note: "Added by CI @ `date`"
repository: "`git remote get-url origin`"
repository_ref: "${GITHUB_SHA:-`git log -1 --format="%H"`}"
EOF
cat $RECORD_FILE
RECORD_ID=$(laconic -c $CONFIG_FILE cns record publish --filename $RECORD_FILE --user-key "${CERC_REGISTRY_USER_KEY}" --bond-id ${CERC_REGISTRY_BOND_ID} | jq -r '.id')
echo $RECORD_ID
rm -f $RECORD_FILE $CONFIG_FILE
+16 -5
View File
@@ -1,5 +1,5 @@
import { lazy, Suspense } from 'react'; import { lazy, Suspense, useMemo } from 'react';
import { Navigate, Route, Routes } from 'react-router-dom'; import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
import styled, { AnyStyledComponent, css } from 'styled-components'; import styled, { AnyStyledComponent, css } from 'styled-components';
import { WagmiConfig } from 'wagmi'; import { WagmiConfig } from 'wagmi';
import { QueryClient, QueryClientProvider } from 'react-query'; import { QueryClient, QueryClientProvider } from 'react-query';
@@ -26,6 +26,7 @@ import { RestrictionProvider } from '@/hooks/useRestrictions';
import { SubaccountProvider } from '@/hooks/useSubaccount'; import { SubaccountProvider } from '@/hooks/useSubaccount';
import { GuardedMobileRoute } from '@/components/GuardedMobileRoute'; import { GuardedMobileRoute } from '@/components/GuardedMobileRoute';
import { LoadingSpace } from '@/components/Loading/LoadingSpinner';
import { HeaderDesktop } from '@/layout/Header/HeaderDesktop'; import { HeaderDesktop } from '@/layout/Header/HeaderDesktop';
import { FooterDesktop } from '@/layout/Footer/FooterDesktop'; import { FooterDesktop } from '@/layout/Footer/FooterDesktop';
@@ -34,12 +35,12 @@ import { NotificationsToastArea } from '@/layout/NotificationsToastArea';
import { DialogManager } from '@/layout/DialogManager'; import { DialogManager } from '@/layout/DialogManager';
import { GlobalCommandDialog } from '@/views/dialogs/GlobalCommandDialog'; import { GlobalCommandDialog } from '@/views/dialogs/GlobalCommandDialog';
import { parseLocationHash } from '@/lib/urlUtils';
import { config } from '@/lib/wagmi'; import { config } from '@/lib/wagmi';
import { breakpoints } from '@/styles'; import { breakpoints } from '@/styles';
import { GlobalStyle } from '@/styles/globalStyle'; import { GlobalStyle } from '@/styles/globalStyle';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
import { LoadingSpace } from './components/Loading/LoadingSpinner';
import '@/styles/constants.css'; import '@/styles/constants.css';
import '@/styles/fonts.css'; import '@/styles/fonts.css';
@@ -68,6 +69,14 @@ const Content = () => {
const isShowingHeader = isNotTablet; const isShowingHeader = isNotTablet;
const isShowingFooter = useShouldShowFooter(); const isShowingFooter = useShouldShowFooter();
const { chainTokenLabel } = useTokenConfigs(); const { chainTokenLabel } = useTokenConfigs();
const location = useLocation();
const pathFromHash = useMemo(() => {
if (location.hash === '') {
return '';
}
return parseLocationHash(location.hash);
}, [location.hash]);
return ( return (
<> <>
@@ -102,8 +111,10 @@ const Content = () => {
<Route path={AppRoute.Terms} element={<TermsOfUsePage />} /> <Route path={AppRoute.Terms} element={<TermsOfUsePage />} />
<Route path={AppRoute.Privacy} element={<PrivacyPolicyPage />} /> <Route path={AppRoute.Privacy} element={<PrivacyPolicyPage />} />
<Route
<Route path="*" element={<Navigate to={DEFAULT_TRADE_ROUTE} replace />} /> path="*"
element={<Navigate to={pathFromHash || DEFAULT_TRADE_ROUTE} replace />}
/>
</Routes> </Routes>
</Suspense> </Suspense>
</Styled.Main> </Styled.Main>
+22
View File
@@ -8,6 +8,7 @@ const assetIcons = {
'1INCH': '/currencies/1inch.png', '1INCH': '/currencies/1inch.png',
AAVE: '/currencies/aave.png', AAVE: '/currencies/aave.png',
ADA: '/currencies/ada.png', ADA: '/currencies/ada.png',
AGIX: '/currencies/agix.png',
ALGO: '/currencies/algo.png', ALGO: '/currencies/algo.png',
APE: '/currencies/ape.png', APE: '/currencies/ape.png',
APT: '/currencies/apt.png', APT: '/currencies/apt.png',
@@ -20,6 +21,7 @@ const assetIcons = {
BONK: '/currencies/bonk.png', BONK: '/currencies/bonk.png',
BTC: '/currencies/btc.png', BTC: '/currencies/btc.png',
CELO: '/currencies/celo.png', CELO: '/currencies/celo.png',
CHZ: '/currencies/chz.png',
COMP: '/currencies/comp.png', COMP: '/currencies/comp.png',
CRV: '/currencies/crv.png', CRV: '/currencies/crv.png',
DAI: '/currencies/dai.png', DAI: '/currencies/dai.png',
@@ -28,25 +30,44 @@ const assetIcons = {
DYDX: '/currencies/dydx.png', DYDX: '/currencies/dydx.png',
DYM: '/currencies/dym.png', DYM: '/currencies/dym.png',
ENJ: '/currencies/enj.png', ENJ: '/currencies/enj.png',
ENS: '/currencies/ens.png',
EOS: '/currencies/eos.png', EOS: '/currencies/eos.png',
ETC: '/currencies/etc.png', ETC: '/currencies/etc.png',
ETH: '/currencies/eth.png', ETH: '/currencies/eth.png',
FET: '/currencies/fet.png',
FIL: '/currencies/fil.png', FIL: '/currencies/fil.png',
FTM: '/currencies/ftm.png',
GALA: "/currencies/gala.png",
GMT: "/currencies/gmt.png",
GRT: "/currencies/grt.png",
HBAR: "/currencies/hbar.png",
ICP: '/currencies/icp.png', ICP: '/currencies/icp.png',
IMX: '/currencies/imx.png',
INJ: '/currencies/inj.png',
JTO: '/currencies/jto.png',
JUP: '/currencies/jup.png', JUP: '/currencies/jup.png',
KAVA: '/currencies/kava.png',
LDO: '/currencies/ldo.png', LDO: '/currencies/ldo.png',
LINK: '/currencies/link.png', LINK: '/currencies/link.png',
LTC: '/currencies/ltc.png', LTC: '/currencies/ltc.png',
MANA: '/currencies/mana.png',
MATIC: '/currencies/matic.png', MATIC: '/currencies/matic.png',
MASK: '/currencies/mask.png',
MINA: '/currencies/mina.png',
MKR: '/currencies/mkr.png', MKR: '/currencies/mkr.png',
NEAR: '/currencies/near.png', NEAR: '/currencies/near.png',
ORDI: "/currencies/ordi.png",
OP: '/currencies/op.png', OP: '/currencies/op.png',
PEPE: '/currencies/pepe.png', PEPE: '/currencies/pepe.png',
PYTH: '/currencies/pyth.png',
RNDR: '/currencies/rndr.png',
RUNE: '/currencies/rune.png', RUNE: '/currencies/rune.png',
SAND: '/currencies/sand.png',
SEI: '/currencies/sei.png', SEI: '/currencies/sei.png',
SHIB: '/currencies/shib.png', SHIB: '/currencies/shib.png',
SNX: '/currencies/snx.png', SNX: '/currencies/snx.png',
SOL: '/currencies/sol.png', SOL: '/currencies/sol.png',
STX: '/currencies/stx.png',
SUI: '/currencies/sui.png', SUI: '/currencies/sui.png',
SUSHI: '/currencies/sushi.png', SUSHI: '/currencies/sushi.png',
TIA: '/currencies/tia.png', TIA: '/currencies/tia.png',
@@ -57,6 +78,7 @@ const assetIcons = {
USDT: '/currencies/usdt.png', USDT: '/currencies/usdt.png',
WBTC: '/currencies/wbtc.png', WBTC: '/currencies/wbtc.png',
WETH: '/currencies/weth.png', WETH: '/currencies/weth.png',
WOO: '/currencies/woo.png',
WLD: '/currencies/wld.png', WLD: '/currencies/wld.png',
XLM: '/currencies/xlm.png', XLM: '/currencies/xlm.png',
XMR: '/currencies/xmr.png', XMR: '/currencies/xmr.png',
+5 -3
View File
@@ -39,7 +39,7 @@ export const SearchSelectMenu = ({
disabled, disabled,
label, label,
items, items,
withSearch, withSearch = true,
withReceiptItems, withReceiptItems,
}: SearchSelectMenuProps) => { }: SearchSelectMenuProps) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -77,6 +77,7 @@ export const SearchSelectMenu = ({
withSearch={withSearch} withSearch={withSearch}
onItemSelected={() => setOpen(false)} onItemSelected={() => setOpen(false)}
withStickyLayout withStickyLayout
$withSearch={withSearch}
/> />
</Styled.Popover> </Styled.Popover>
</Styled.WithDetailsReceipt> </Styled.WithDetailsReceipt>
@@ -127,7 +128,7 @@ Styled.Popover = styled(Popover)`
box-shadow: none; box-shadow: none;
`; `;
Styled.ComboboxMenu = styled(ComboboxMenu)` Styled.ComboboxMenu = styled(ComboboxMenu)<{ $withSearch?: boolean }>`
${layoutMixins.withInnerHorizontalBorders} ${layoutMixins.withInnerHorizontalBorders}
--comboboxMenu-backgroundColor: var(--color-layer-4); --comboboxMenu-backgroundColor: var(--color-layer-4);
@@ -140,7 +141,8 @@ Styled.ComboboxMenu = styled(ComboboxMenu)`
--comboboxMenu-item-checked-textColor: var(--color-text-2); --comboboxMenu-item-checked-textColor: var(--color-text-2);
--comboboxMenu-item-highlighted-textColor: var(--color-text-2); --comboboxMenu-item-highlighted-textColor: var(--color-text-2);
--stickyArea1-topHeight: var(--form-input-height); --stickyArea1-topHeight: ${({ $withSearch }) =>
!$withSearch ? '0' : 'var(--form-input-height)'};
input:focus-visible { input:focus-visible {
outline: none; outline: none;
+8 -1
View File
@@ -1,4 +1,4 @@
import { type ReactNode, useState } from 'react'; import { type ReactNode, useState, useEffect } from 'react';
import { ButtonAction, ButtonState } from '@/constants/buttons'; import { ButtonAction, ButtonState } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
@@ -8,6 +8,7 @@ import { Button, type ButtonStateConfig, type ButtonProps } from '@/components/B
type ElementProps = { type ElementProps = {
timeoutInSeconds: number; timeoutInSeconds: number;
onTimeOut?: () => void;
slotFinal?: ReactNode; slotFinal?: ReactNode;
} & ButtonProps; } & ButtonProps;
@@ -16,6 +17,7 @@ export type TimeoutButtonProps = ElementProps;
export const TimeoutButton = ({ export const TimeoutButton = ({
children, children,
timeoutInSeconds, timeoutInSeconds,
onTimeOut,
slotFinal, slotFinal,
...otherProps ...otherProps
}: TimeoutButtonProps) => { }: TimeoutButtonProps) => {
@@ -25,6 +27,11 @@ export const TimeoutButton = ({
const secondsLeft = Math.max(0, (timeoutDeadline - now) / 1000); const secondsLeft = Math.max(0, (timeoutDeadline - now) / 1000);
useEffect(() => {
if (secondsLeft > 0) return;
onTimeOut?.();
}, [secondsLeft]);
if (slotFinal && secondsLeft <= 0) return slotFinal; if (slotFinal && secondsLeft <= 0) return slotFinal;
return ( return (
+19 -2
View File
@@ -81,6 +81,9 @@ export enum AnalyticsEvent {
TradePlaceOrderConfirmed = 'TradePlaceOrderConfirmed', TradePlaceOrderConfirmed = 'TradePlaceOrderConfirmed',
TradeCancelOrder = 'TradeCancelOrder', TradeCancelOrder = 'TradeCancelOrder',
TradeCancelOrderConfirmed = 'TradeCancelOrderConfirmed', TradeCancelOrderConfirmed = 'TradeCancelOrderConfirmed',
// Notification
NotificationAction = 'NotificationAction',
} }
export type AnalyticsEventData<T extends AnalyticsEvent> = export type AnalyticsEventData<T extends AnalyticsEvent> =
@@ -144,9 +147,17 @@ export type AnalyticsEventData<T extends AnalyticsEvent> =
validatorUrl: string; validatorUrl: string;
} }
: T extends AnalyticsEvent.TransferDeposit : T extends AnalyticsEvent.TransferDeposit
? {} ? {
chainId?: string;
tokenAddress?: string;
tokenSymbol?: string;
}
: T extends AnalyticsEvent.TransferWithdraw : T extends AnalyticsEvent.TransferWithdraw
? {} ? {
chainId?: string;
tokenAddress?: string;
tokenSymbol?: string;
}
: // Trading : // Trading
T extends AnalyticsEvent.TradeOrderTypeSelected T extends AnalyticsEvent.TradeOrderTypeSelected
? { ? {
@@ -172,6 +183,12 @@ export type AnalyticsEventData<T extends AnalyticsEvent> =
/** URL/IP of node the order was sent to */ /** URL/IP of node the order was sent to */
validatorUrl: string; validatorUrl: string;
} }
: // Notifcation
T extends AnalyticsEvent.NotificationAction
? {
type: string;
id: string;
}
: never; : never;
export const DEFAULT_TRANSACTION_MEMO = 'dYdX Frontend (web)'; export const DEFAULT_TRANSACTION_MEMO = 'dYdX Frontend (web)';
+1
View File
@@ -11,6 +11,7 @@ export enum DialogTypes {
ExternalNavKeplr = 'ExternalNavKeplr', ExternalNavKeplr = 'ExternalNavKeplr',
MnemonicExport = 'MnemonicExport', MnemonicExport = 'MnemonicExport',
MobileSignIn = 'MobileSignIn', MobileSignIn = 'MobileSignIn',
MobileDownload = 'MobileDownload',
Onboarding = 'Onboarding', Onboarding = 'Onboarding',
OrderDetails = 'OrderDetails', OrderDetails = 'OrderDetails',
Preferences = 'Preferences', Preferences = 'Preferences',
+6 -1
View File
@@ -1,4 +1,4 @@
import environments from '../../public/configs/env.json'; import environments from '../../public/configs/v1/env.json';
const CURRENT_MODE = ({ const CURRENT_MODE = ({
production: 'MAINNET', production: 'MAINNET',
@@ -14,5 +14,10 @@ export const isDev = CURRENT_MODE === 'DEV';
export const AVAILABLE_ENVIRONMENTS = environments.deployments[CURRENT_MODE]; export const AVAILABLE_ENVIRONMENTS = environments.deployments[CURRENT_MODE];
export const CURRENT_ABACUS_DEPLOYMENT = CURRENT_MODE; export const CURRENT_ABACUS_DEPLOYMENT = CURRENT_MODE;
export const ENVIRONMENT_CONFIG_MAP = environments.environments; export const ENVIRONMENT_CONFIG_MAP = environments.environments;
export const TOKEN_CONFIG_MAP = environments.tokens;
export const LINKS_CONFIG_MAP = environments.links;
export const WALLETS_CONFIG_MAP = environments.wallets;
export const GOVERNANCE_CONFIG_MAP = environments.governance;
export type DydxNetwork = keyof typeof ENVIRONMENT_CONFIG_MAP; export type DydxNetwork = keyof typeof ENVIRONMENT_CONFIG_MAP;
export type DydxChainId = keyof typeof TOKEN_CONFIG_MAP;
export const DEFAULT_APP_ENVIRONMENT = AVAILABLE_ENVIRONMENTS.default as DydxNetwork; export const DEFAULT_APP_ENVIRONMENT = AVAILABLE_ENVIRONMENTS.default as DydxNetwork;
+6
View File
@@ -140,8 +140,14 @@ export type TransferNotifcation = {
isCctp?: boolean; isCctp?: boolean;
errorCount?: number; errorCount?: number;
status?: StatusResponse; 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 * @description Struct to store whether a NotificationType should be triggered
*/ */
+1
View File
@@ -41,6 +41,7 @@ export enum MobileSettingsRoute {
Network = 'network', Network = 'network',
} }
export const BASE_ROUTE = import.meta.env.VITE_ROUTER_TYPE === 'hash' ? '/#' : '';
export const TRADE_ROUTE = `${AppRoute.Trade}/:market`; export const TRADE_ROUTE = `${AppRoute.Trade}/:market`;
export const PORTFOLIO_ROUTE = `${AppRoute.Portfolio}/:subroute`; export const PORTFOLIO_ROUTE = `${AppRoute.Portfolio}/:subroute`;
export const HISTORY_ROUTE = `${AppRoute.Portfolio}/${PortfolioRoute.History}/:subroute`; export const HISTORY_ROUTE = `${AppRoute.Portfolio}/${PortfolioRoute.History}/:subroute`;
+19 -4
View File
@@ -16,6 +16,7 @@ import {
KeplrIcon, KeplrIcon,
MathWalletIcon, MathWalletIcon,
MetaMaskIcon, MetaMaskIcon,
OkxWalletIcon,
RainbowIcon, RainbowIcon,
TokenPocketIcon, TokenPocketIcon,
TrustWalletIcon, TrustWalletIcon,
@@ -24,7 +25,7 @@ import {
import { isMetaMask } from '@/lib/wallet/providers'; import { isMetaMask } from '@/lib/wallet/providers';
import { DydxNetwork, ENVIRONMENT_CONFIG_MAP } from './networks'; import { DydxChainId, WALLETS_CONFIG_MAP } from './networks';
// Wallet connection types // Wallet connection types
@@ -89,6 +90,7 @@ export enum WalletType {
// Ledger = 'LEDGER', // Ledger = 'LEDGER',
MathWallet = 'MATH_WALLET', MathWallet = 'MATH_WALLET',
MetaMask = 'METAMASK', MetaMask = 'METAMASK',
OkxWallet = 'OKX_WALLET',
Rainbow = 'RAINBOW_WALLET', Rainbow = 'RAINBOW_WALLET',
TokenPocket = 'TOKEN_POCKET', TokenPocket = 'TOKEN_POCKET',
TrustWallet = 'TRUST_WALLET', TrustWallet = 'TRUST_WALLET',
@@ -102,6 +104,7 @@ const WALLET_CONNECT_EXPLORER_RECOMMENDED_WALLETS = {
imToken: 'ef333840daf915aafdc4a004525502d6d49d77bd9c65e0642dbaefb3c2893bef', imToken: 'ef333840daf915aafdc4a004525502d6d49d77bd9c65e0642dbaefb3c2893bef',
TokenPocket: '20459438007b75f4f4acb98bf29aa3b800550309646d375da5fd4aac6c2a2c66', TokenPocket: '20459438007b75f4f4acb98bf29aa3b800550309646d375da5fd4aac6c2a2c66',
Trust: '4622a2b2d6af1c9844944291e5e7351a6aa24cd7b23099efac1b2fd875da31a0', Trust: '4622a2b2d6af1c9844944291e5e7351a6aa24cd7b23099efac1b2fd875da31a0',
OkxWallet: '971e689d0a5be527bac79629b4ee9b925e82208e5168b733496a09c0faed0709',
Rainbow: '1ae92b26df02f0abca6304df07debccd18262fdf5fe82daa81593582dac9a369', Rainbow: '1ae92b26df02f0abca6304df07debccd18262fdf5fe82daa81593582dac9a369',
Zerion: 'ecc4036f814562b41a5268adc86270fba1365471402006302e70169465b7ac18', Zerion: 'ecc4036f814562b41a5268adc86270fba1365471402006302e70169465b7ac18',
Ledger: '19177a98252e07ddfc9af2083ba8e07ef627cb6103467ffebb3f8f4205fd7927', Ledger: '19177a98252e07ddfc9af2083ba8e07ef627cb6103467ffebb3f8f4205fd7927',
@@ -212,6 +215,14 @@ export const wallets: Record<WalletType, WalletConfig> = {
matchesInjectedEip1193: isMetaMask, matchesInjectedEip1193: isMetaMask,
walletconnect2Id: WALLET_CONNECT_EXPLORER_RECOMMENDED_WALLETS.Metamask, 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]: { [WalletType.Rainbow]: {
type: WalletType.Rainbow, type: WalletType.Rainbow,
stringKey: STRING_KEYS.RAINBOW_WALLET, stringKey: STRING_KEYS.RAINBOW_WALLET,
@@ -278,6 +289,10 @@ export type WithInjectedWeb3Provider = {
}; };
}; };
export type WithInjectedOkxWalletProvider = {
okxwallet: InjectedWeb3Provider;
};
// Wallet connections // Wallet connections
export type WalletConnection = { export type WalletConnection = {
@@ -291,17 +306,17 @@ export const COSMOS_DERIVATION_PATH = "m/44'/118'/0'/0/0";
/** /**
* @description typed data to sign for dYdX Chain onboarding * @description typed data to sign for dYdX Chain onboarding
*/ */
export const getSignTypedData = (selectedNetwork: DydxNetwork) => export const getSignTypedData = (selectedDydxChainId: DydxChainId) =>
({ ({
primaryType: 'dYdX', primaryType: 'dYdX',
domain: { domain: {
name: ENVIRONMENT_CONFIG_MAP[selectedNetwork].wallets.signTypedDataDomainName, name: WALLETS_CONFIG_MAP[selectedDydxChainId].signTypedDataDomainName,
}, },
types: { types: {
dYdX: [{ name: 'action', type: 'string' }], dYdX: [{ name: 'action', type: 'string' }],
}, },
message: { message: {
action: ENVIRONMENT_CONFIG_MAP[selectedNetwork].wallets.signTypedDataAction, action: WALLETS_CONFIG_MAP[selectedDydxChainId].signTypedDataAction,
}, },
} as const); } as const);
+9 -7
View File
@@ -57,13 +57,15 @@ export const useChartLines = ({
useEffect(() => { useEffect(() => {
if (tvWidget && isChartReady) { if (tvWidget && isChartReady) {
tvWidget.onChartReady(() => { tvWidget.onChartReady(() => {
tvWidget.chart().dataReady(() => { tvWidget.headerReady().then(() => {
if (showOrderLines) { tvWidget.chart().dataReady(() => {
drawOrderLines(); if (showOrderLines) {
drawPositionLine(); drawOrderLines();
} else { drawPositionLine();
deleteChartLines(); } else {
} deleteChartLines();
}
});
}); });
}); });
} }
@@ -27,19 +27,26 @@ export const useChartMarketAndResolution = ({
const dispatch = useDispatch(); const dispatch = useDispatch();
const currentMarketId: string = useSelector(getCurrentMarketId) || DEFAULT_MARKETID; const currentMarketId: string = useSelector(getCurrentMarketId) || DEFAULT_MARKETID;
const selectedResolution: string = const selectedResolution: string =
useSelector(getSelectedResolutionForMarket(currentMarketId)) || DEFAULT_RESOLUTION; useSelector(getSelectedResolutionForMarket(currentMarketId)) || DEFAULT_RESOLUTION;
const chart = isWidgetReady ? tvWidget?.chart() : undefined; const chart = isWidgetReady ? tvWidget?.chart() : undefined;
const chartResolution = chart?.resolution?.(); const chartResolution = chart?.resolution?.();
/**
* @description Hook to handle changing markets - intentionally should avoid triggering on change of resolutions.
*/
useEffect(() => { useEffect(() => {
if (currentMarketId && isWidgetReady) { if (currentMarketId && isWidgetReady) {
const resolution = savedResolution || selectedResolution; const resolution = savedResolution || selectedResolution;
tvWidget?.setSymbol(currentMarketId, resolution as ResolutionString, () => {}); tvWidget?.setSymbol(currentMarketId, resolution as ResolutionString, () => {});
} }
}, [currentMarketId, isWidgetReady, savedResolution, selectedResolution]); }, [currentMarketId, isWidgetReady]);
/**
* @description Hook to handle changing chart resolution
*/
useEffect(() => { useEffect(() => {
if (chartResolution) { if (chartResolution) {
if (chartResolution !== selectedResolution) { if (chartResolution !== selectedResolution) {
+3 -2
View File
@@ -10,9 +10,10 @@ export const useDisplayedWallets = () => {
isDev && WalletType.Keplr, isDev && WalletType.Keplr,
WalletType.WalletConnect2, WalletType.WalletConnect2,
WalletType.CoinbaseWallet, WalletType.CoinbaseWallet,
WalletType.OkxWallet,
// Hide these wallet options until they can be properly tested on mainnet // Hide these wallet options until they can be properly tested on mainnet
// WalletType.ImToken, // WalletType.ImToken,
// WalletType.Rainbow, // WalletType.Rainbow,
+8 -8
View File
@@ -18,14 +18,13 @@ import type { ResolutionString } from 'public/tradingview/charting_library';
import type { ConnectNetworkEvent, NetworkConfig } from '@/constants/abacus'; import type { ConnectNetworkEvent, NetworkConfig } from '@/constants/abacus';
import { DEFAULT_TRANSACTION_MEMO } from '@/constants/analytics'; import { DEFAULT_TRANSACTION_MEMO } from '@/constants/analytics';
import { type Candle, RESOLUTION_MAP } from '@/constants/candles'; import { type Candle, RESOLUTION_MAP } from '@/constants/candles';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { DydxChainAsset } from '@/constants/wallets';
import { getSelectedNetwork } from '@/state/appSelectors'; import { getSelectedNetwork } from '@/state/appSelectors';
import { log } from '@/lib/telemetry'; import { log } from '@/lib/telemetry';
import { useRestrictions } from './useRestrictions'; import { useRestrictions } from './useRestrictions';
import { useTokenConfigs } from './useTokenConfigs';
type DydxContextType = ReturnType<typeof useDydxClientContext>; type DydxContextType = ReturnType<typeof useDydxClientContext>;
const DydxContext = createContext<DydxContextType>({} as DydxContextType); const DydxContext = createContext<DydxContextType>({} as DydxContextType);
@@ -41,7 +40,8 @@ const useDydxClientContext = () => {
// ------ Network ------ // // ------ Network ------ //
const selectedNetwork = useSelector(getSelectedNetwork); const selectedNetwork = useSelector(getSelectedNetwork);
const tokensConfigs = ENVIRONMENT_CONFIG_MAP[selectedNetwork].tokens; const { usdcDenom, usdcDecimals, usdcGasDenom, chainTokenDenom, chainTokenDecimals } =
useTokenConfigs();
const [networkConfig, setNetworkConfig] = useState<NetworkConfig>(); const [networkConfig, setNetworkConfig] = useState<NetworkConfig>();
@@ -75,11 +75,11 @@ const useDydxClientContext = () => {
networkConfig.validatorUrl, networkConfig.validatorUrl,
networkConfig.chainId, networkConfig.chainId,
{ {
USDC_DENOM: tokensConfigs[DydxChainAsset.USDC].denom, USDC_DENOM: usdcDenom,
USDC_DECIMALS: tokensConfigs[DydxChainAsset.USDC].decimals, USDC_DECIMALS: usdcDecimals,
USDC_GAS_DENOM: tokensConfigs[DydxChainAsset.USDC].gasDenom, USDC_GAS_DENOM: usdcGasDenom,
CHAINTOKEN_DENOM: tokensConfigs[DydxChainAsset.CHAINTOKEN].denom, CHAINTOKEN_DENOM: chainTokenDenom,
CHAINTOKEN_DECIMALS: tokensConfigs[DydxChainAsset.CHAINTOKEN].decimals, CHAINTOKEN_DECIMALS: chainTokenDecimals,
}, },
{ {
broadcastPollIntervalMs: 3_000, broadcastPollIntervalMs: 3_000,
+7 -4
View File
@@ -1,5 +1,8 @@
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { useSelector } from 'react-redux';
import { useSelectedNetwork } from '@/hooks';
import { GOVERNANCE_CONFIG_MAP } from '@/constants/networks';
import { getSelectedDydxChainId } from '@/state/appSelectors';
export interface GovernanceVariables { export interface GovernanceVariables {
newMarketProposal: { newMarketProposal: {
@@ -10,7 +13,7 @@ export interface GovernanceVariables {
} }
export const useGovernanceVariables = (): GovernanceVariables => { export const useGovernanceVariables = (): GovernanceVariables => {
const { selectedNetwork } = useSelectedNetwork(); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const governanceVars = ENVIRONMENT_CONFIG_MAP[selectedNetwork].governance as GovernanceVariables; const governanceVars = GOVERNANCE_CONFIG_MAP[selectedDydxChainId] as GovernanceVariables;
return governanceVars; return governanceVars;
}; };
+15 -9
View File
@@ -82,24 +82,30 @@ const useLocalNotificationsContext = () => {
isCctp, isCctp,
errorCount, errorCount,
status: currentStatus, status: currentStatus,
isExchange,
} = transferNotification; } = transferNotification;
// @ts-ignore status.errors is not in the type definition but can be returned const hasErrors =
// also error can some time come back as an empty object so we need to ignore for that // @ts-ignore status.errors is not in the type definition but can be returned
const hasErrors = !!currentStatus?.errors || // also error can some time come back as an empty object so we need to ignore for that
(currentStatus?.error && Object.keys(currentStatus.error).length !== 0); !!currentStatus?.errors ||
(currentStatus?.error && Object.keys(currentStatus.error).length !== 0);
if ( if (
!isExchange &&
!hasErrors && !hasErrors &&
(!currentStatus?.squidTransactionStatus || (!currentStatus?.squidTransactionStatus ||
currentStatus?.squidTransactionStatus === 'ongoing') currentStatus?.squidTransactionStatus === 'ongoing')
) { ) {
try { try {
const status = await fetchSquidStatus({ const status = await fetchSquidStatus(
transactionId: txHash, {
toChainId, transactionId: txHash,
fromChainId, toChainId,
}, isCctp); fromChainId,
},
isCctp
);
if (status) { if (status) {
transferNotification.status = status; transferNotification.status = status;
+43 -11
View File
@@ -5,8 +5,7 @@ import { isEqual, groupBy } from 'lodash';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { DialogTypes } from '@/constants/dialogs'; import { DialogTypes } from '@/constants/dialogs';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { AppRoute, TokenRoute } from '@/constants/routes';
import { AppRoute } from '@/constants/routes';
import { DydxChainAsset } from '@/constants/wallets'; import { DydxChainAsset } from '@/constants/wallets';
import { import {
@@ -21,9 +20,10 @@ import {
NotificationType, NotificationType,
DEFAULT_TOAST_AUTO_CLOSE_MS, DEFAULT_TOAST_AUTO_CLOSE_MS,
TransferNotificationTypes, TransferNotificationTypes,
ReleaseUpdateNotificationIds,
} from '@/constants/notifications'; } from '@/constants/notifications';
import { useSelectedNetwork, useStringGetter } from '@/hooks'; import { useStringGetter, useTokenConfigs } from '@/hooks';
import { useLocalNotifications } from '@/hooks/useLocalNotifications'; import { useLocalNotifications } from '@/hooks/useLocalNotifications';
import { AssetIcon } from '@/components/AssetIcon'; import { AssetIcon } from '@/components/AssetIcon';
@@ -36,6 +36,7 @@ import { getSubaccountFills, getSubaccountOrders } from '@/state/accountSelector
import { openDialog } from '@/state/dialogs'; import { openDialog } from '@/state/dialogs';
import { getAbacusNotifications } from '@/state/notificationsSelectors'; import { getAbacusNotifications } from '@/state/notificationsSelectors';
import { getMarketIds } from '@/state/perpetualsSelectors'; import { getMarketIds } from '@/state/perpetualsSelectors';
import { getSelectedDydxChainId } from '@/state/appSelectors';
import { formatSeconds } from '@/lib/timeUtils'; import { formatSeconds } from '@/lib/timeUtils';
@@ -175,16 +176,17 @@ export const notificationTypes: NotificationTypeConfig[] = [
useTrigger: ({ trigger }) => { useTrigger: ({ trigger }) => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { transferNotifications } = useLocalNotifications(); const { transferNotifications } = useLocalNotifications();
const { selectedNetwork } = useSelectedNetwork(); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
useEffect(() => { useEffect(() => {
for (const transfer of transferNotifications) { for (const transfer of transferNotifications) {
const { fromChainId, status, txHash, toAmount, type } = transfer; const { fromChainId, status, txHash, toAmount, type, isExchange } = transfer;
const isFinished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing'; const isFinished =
(Boolean(status) && status?.squidTransactionStatus !== 'ongoing') || isExchange;
const icon = <Icon iconName={isFinished ? IconName.Transfer : IconName.Clock} />; const icon = <Icon iconName={isFinished ? IconName.Transfer : IconName.Clock} />;
const transferType = const transferType =
type ?? fromChainId === ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId type ?? fromChainId === selectedDydxChainId
? TransferNotificationTypes.Withdrawal ? TransferNotificationTypes.Withdrawal
: TransferNotificationTypes.Deposit; : TransferNotificationTypes.Deposit;
@@ -237,13 +239,16 @@ export const notificationTypes: NotificationTypeConfig[] = [
{ {
type: NotificationType.ReleaseUpdates, type: NotificationType.ReleaseUpdates,
useTrigger: ({ trigger }) => { useTrigger: ({ trigger }) => {
const { chainTokenLabel } = useTokenConfigs();
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const expirationDate = new Date('2024-03-08T23:59:59');
const currentDate = new Date();
useEffect(() => { useEffect(() => {
trigger( trigger(
'rewards-and-full-trading-live', ReleaseUpdateNotificationIds.RewardsAndFullTradingLive,
{ {
icon: <AssetIcon symbol="DYDX" />, icon: <AssetIcon symbol={chainTokenLabel} />,
title: stringGetter({ key: 'NOTIFICATIONS.RELEASE_REWARDS_AND_FULL_TRADING.TITLE' }), title: stringGetter({ key: 'NOTIFICATIONS.RELEASE_REWARDS_AND_FULL_TRADING.TITLE' }),
body: stringGetter({ body: stringGetter({
key: 'NOTIFICATIONS.RELEASE_REWARDS_AND_FULL_TRADING.BODY', key: 'NOTIFICATIONS.RELEASE_REWARDS_AND_FULL_TRADING.BODY',
@@ -269,14 +274,41 @@ export const notificationTypes: NotificationTypeConfig[] = [
}, },
}), }),
toastSensitivity: 'foreground', toastSensitivity: 'foreground',
groupKey: NotificationType.ReleaseUpdates, groupKey: ReleaseUpdateNotificationIds.RewardsAndFullTradingLive,
}, },
[] []
); );
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]); }, [stringGetter]);
}, },
useNotificationAction: () => { useNotificationAction: () => {
return () => {}; const { chainTokenLabel } = useTokenConfigs();
const navigate = useNavigate();
return (notificationId: string) => {
if (notificationId === ReleaseUpdateNotificationIds.IncentivesS3) {
navigate(`${chainTokenLabel}/${TokenRoute.TradingRewards}`);
}
};
}, },
}, },
]; ];
+8 -3
View File
@@ -1,5 +1,6 @@
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { AnalyticsEvent } from '@/constants/analytics';
import { LOCAL_STORAGE_VERSIONS, LocalStorageKey } from '@/constants/localStorage'; import { LOCAL_STORAGE_VERSIONS, LocalStorageKey } from '@/constants/localStorage';
import { import {
type Notification, type Notification,
@@ -13,7 +14,8 @@ import {
import { useLocalStorage } from './useLocalStorage'; import { useLocalStorage } from './useLocalStorage';
import { notificationTypes } from './useNotificationTypes'; import { notificationTypes } from './useNotificationTypes';
import { renderSvgToDataUrl } from '../lib/renderSvgToDataUrl'; import { track } from '@/lib/analytics';
import { renderSvgToDataUrl } from '@/lib/renderSvgToDataUrl';
type NotificationsContextType = ReturnType<typeof useNotificationsContext>; type NotificationsContextType = ReturnType<typeof useNotificationsContext>;
@@ -48,6 +50,7 @@ const useNotificationsContext = () => {
defaultValue: { defaultValue: {
[NotificationType.AbacusGenerated]: true, [NotificationType.AbacusGenerated]: true,
[NotificationType.SquidTransfer]: true, [NotificationType.SquidTransfer]: true,
[NotificationType.ReleaseUpdates]: true,
version: LOCAL_STORAGE_VERSIONS[LocalStorageKey.NotificationPreferences], version: LOCAL_STORAGE_VERSIONS[LocalStorageKey.NotificationPreferences],
}, },
}); });
@@ -176,8 +179,10 @@ const useNotificationsContext = () => {
) )
); );
const onNotificationAction = async (notification: Notification) => const onNotificationAction = async (notification: Notification) => {
await actions[notification.type]?.(notification.id); track(AnalyticsEvent.NotificationAction, { type: notification.type, id: notification.id });
return await actions[notification.type]?.(notification.id);
};
// Push notifications // Push notifications
const [hasEnabledPush, setHasEnabledPush] = useLocalStorage({ const [hasEnabledPush, setHasEnabledPush] = useLocalStorage({
+1 -1
View File
@@ -2,7 +2,7 @@ import { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { LocalStorageKey } from '@/constants/localStorage'; import { LocalStorageKey } from '@/constants/localStorage';
import { DEFAULT_APP_ENVIRONMENT, DydxNetwork, ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { DEFAULT_APP_ENVIRONMENT, DydxNetwork } from '@/constants/networks';
import { useAccounts, useLocalStorage } from '@/hooks'; import { useAccounts, useLocalStorage } from '@/hooks';
+1 -1
View File
@@ -28,7 +28,7 @@ import { setSubaccount, setHistoricalPnl, removeUncommittedOrderClientId } from
import { getBalances } from '@/state/accountSelectors'; import { getBalances } from '@/state/accountSelectors';
import abacusStateManager from '@/lib/abacus'; import abacusStateManager from '@/lib/abacus';
import { hashFromTx } from '@/lib/hashfromTx'; import { hashFromTx } from '@/lib/txUtils';
import { log } from '@/lib/telemetry'; import { log } from '@/lib/telemetry';
import { useAccounts } from './useAccounts'; import { useAccounts } from './useAccounts';
+14 -10
View File
@@ -1,7 +1,9 @@
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { useSelector } from 'react-redux';
import { TOKEN_CONFIG_MAP } from '@/constants/networks';
import { DydxChainAsset } from '@/constants/wallets'; import { DydxChainAsset } from '@/constants/wallets';
import { useSelectedNetwork } from '@/hooks'; import { getSelectedDydxChainId } from '@/state/appSelectors';
export const useTokenConfigs = (): { export const useTokenConfigs = (): {
tokensConfigs: { tokensConfigs: {
@@ -10,31 +12,33 @@ export const useTokenConfigs = (): {
name: string; name: string;
decimals: number; decimals: number;
gasDenom?: string; gasDenom?: string;
}, };
[DydxChainAsset.CHAINTOKEN]: { [DydxChainAsset.CHAINTOKEN]: {
denom: string; denom: string;
name: string; name: string;
decimals: number; decimals: number;
gasDenom?: string; gasDenom?: string;
}, };
}; };
usdcDenom: string; usdcDenom: string;
usdcDecimals: number; usdcDecimals: number;
usdcGasDenom: string;
usdcLabel: string; usdcLabel: string;
chainTokenDenom: string; chainTokenDenom: string;
chainTokenDecimals: number; chainTokenDecimals: number;
chainTokenLabel: string; chainTokenLabel: string;
} => { } => {
const { selectedNetwork } = useSelectedNetwork(); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const tokensConfigs = ENVIRONMENT_CONFIG_MAP[selectedNetwork].tokens; const tokensConfigs = TOKEN_CONFIG_MAP[selectedDydxChainId];
return { return {
tokensConfigs, tokensConfigs,
usdcDenom: tokensConfigs[DydxChainAsset.USDC].denom, usdcDenom: tokensConfigs[DydxChainAsset.USDC].denom,
usdcDecimals: tokensConfigs[DydxChainAsset.USDC].decimals, usdcDecimals: tokensConfigs[DydxChainAsset.USDC].decimals,
usdcGasDenom: tokensConfigs[DydxChainAsset.USDC].gasDenom,
usdcLabel: tokensConfigs[DydxChainAsset.USDC].name, usdcLabel: tokensConfigs[DydxChainAsset.USDC].name,
chainTokenDenom: tokensConfigs[DydxChainAsset.CHAINTOKEN].denom, chainTokenDenom: tokensConfigs[DydxChainAsset.CHAINTOKEN].denom,
chainTokenDecimals: tokensConfigs[DydxChainAsset.CHAINTOKEN].decimals, chainTokenDecimals: tokensConfigs[DydxChainAsset.CHAINTOKEN].decimals,
chainTokenLabel: tokensConfigs[DydxChainAsset.CHAINTOKEN].name, chainTokenLabel: tokensConfigs[DydxChainAsset.CHAINTOKEN].name,
}; };
}; };
+8 -4
View File
@@ -1,12 +1,15 @@
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { useSelector } from 'react-redux';
import { useSelectedNetwork } from '@/hooks'; import { LINKS_CONFIG_MAP } from '@/constants/networks';
import { getSelectedDydxChainId } from '@/state/appSelectors';
const FALLBACK_URL = 'https://help.dydx.exchange/'; const FALLBACK_URL = 'https://help.dydx.exchange/';
export interface LinksConfigs { export interface LinksConfigs {
tos: string; tos: string;
privacy: string; privacy: string;
statusPage: string;
mintscan: string; mintscan: string;
mintscanBase: string; mintscanBase: string;
feedback?: string; feedback?: string;
@@ -27,12 +30,13 @@ export interface LinksConfigs {
} }
export const useURLConfigs = (): LinksConfigs => { export const useURLConfigs = (): LinksConfigs => {
const { selectedNetwork } = useSelectedNetwork(); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const linksConfigs = ENVIRONMENT_CONFIG_MAP[selectedNetwork].links as LinksConfigs; const linksConfigs = LINKS_CONFIG_MAP[selectedDydxChainId] as LinksConfigs;
return { return {
tos: linksConfigs.tos, tos: linksConfigs.tos,
privacy: linksConfigs.privacy, privacy: linksConfigs.privacy,
statusPage: linksConfigs.statusPage,
mintscan: linksConfigs.mintscan, mintscan: linksConfigs.mintscan,
mintscanBase: linksConfigs.mintscanBase, mintscanBase: linksConfigs.mintscanBase,
feedback: linksConfigs.feedback || FALLBACK_URL, feedback: linksConfigs.feedback || FALLBACK_URL,
+4 -4
View File
@@ -4,7 +4,7 @@ import { useSelector } from 'react-redux';
import { EvmDerivedAddresses } from '@/constants/account'; import { EvmDerivedAddresses } from '@/constants/account';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { LocalStorageKey } from '@/constants/localStorage'; import { LocalStorageKey } from '@/constants/localStorage';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { ENVIRONMENT_CONFIG_MAP, WALLETS_CONFIG_MAP } from '@/constants/networks';
import { import {
type DydxAddress, type DydxAddress,
@@ -32,7 +32,7 @@ import {
WalletType as CosmosWalletType, WalletType as CosmosWalletType,
} from 'graz'; } from 'graz';
import { getSelectedNetwork } from '@/state/appSelectors'; import { getSelectedDydxChainId } from '@/state/appSelectors';
import { resolveWagmiConnector } from '@/lib/wagmi'; import { resolveWagmiConnector } from '@/lib/wagmi';
import { getWalletConnection, parseWalletError } from '@/lib/wallet'; import { getWalletConnection, parseWalletError } from '@/lib/wallet';
@@ -91,8 +91,8 @@ export const useWalletConnection = () => {
// Wallet connection // Wallet connection
const selectedNetwork = useSelector(getSelectedNetwork); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const walletConnectConfig = ENVIRONMENT_CONFIG_MAP[selectedNetwork].wallets.walletconnect; const walletConnectConfig = WALLETS_CONFIG_MAP[selectedDydxChainId].walletconnect;
const wagmiConnector = useMemo( const wagmiConnector = useMemo(
() => () =>
walletType && walletConnectionType walletType && walletConnectionType
+1
View File
@@ -83,6 +83,7 @@ export { default as LedgerIcon } from './wallets/ledger.svg';
export { default as MagicIcon } from './wallets/magic.svg'; export { default as MagicIcon } from './wallets/magic.svg';
export { default as MathWalletIcon } from './wallets/mathwallet.svg'; export { default as MathWalletIcon } from './wallets/mathwallet.svg';
export { default as MetaMaskIcon } from './wallets/metamask.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 RainbowIcon } from './wallets/rainbow-wallet.svg';
export { default as TestWalletIcon } from './wallets/test-wallet.svg'; export { default as TestWalletIcon } from './wallets/test-wallet.svg';
export { default as TokenPocketIcon } from './wallets/tokenpocket.svg'; export { default as TokenPocketIcon } from './wallets/tokenpocket.svg';
+1
View File
@@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 3.9 KiB

+2
View File
@@ -31,6 +31,7 @@ import { FillDetailsDialog } from '@/views/dialogs/DetailsDialog/FillDetailsDial
import { NewMarketMessageDetailsDialog } from '@/views/dialogs/NewMarketMessageDetailsDialog'; import { NewMarketMessageDetailsDialog } from '@/views/dialogs/NewMarketMessageDetailsDialog';
import { NewMarketAgreementDialog } from '@/views/dialogs/NewMarketAgreementDialog'; import { NewMarketAgreementDialog } from '@/views/dialogs/NewMarketAgreementDialog';
import { ExternalNavStrideDialog } from '@/views/dialogs/ExternalNavStrideDialog'; import { ExternalNavStrideDialog } from '@/views/dialogs/ExternalNavStrideDialog';
import { MobileDownloadDialog } from '@/views/dialogs/MobileDownloadDialog';
export const DialogManager = () => { export const DialogManager = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -63,6 +64,7 @@ export const DialogManager = () => {
[DialogTypes.ExternalNavStride]: <ExternalNavStrideDialog {...modalProps} />, [DialogTypes.ExternalNavStride]: <ExternalNavStrideDialog {...modalProps} />,
[DialogTypes.MnemonicExport]: <MnemonicExportDialog {...modalProps} />, [DialogTypes.MnemonicExport]: <MnemonicExportDialog {...modalProps} />,
[DialogTypes.MobileSignIn]: <MobileSignInDialog {...modalProps} />, [DialogTypes.MobileSignIn]: <MobileSignInDialog {...modalProps} />,
[DialogTypes.MobileDownload]: <MobileDownloadDialog {...modalProps} />,
[DialogTypes.Onboarding]: <OnboardingDialog {...modalProps} />, [DialogTypes.Onboarding]: <OnboardingDialog {...modalProps} />,
[DialogTypes.OrderDetails]: <OrderDetailsDialog {...modalProps} />, [DialogTypes.OrderDetails]: <OrderDetailsDialog {...modalProps} />,
[DialogTypes.Preferences]: <PreferencesDialog {...modalProps} />, [DialogTypes.Preferences]: <PreferencesDialog {...modalProps} />,
+3 -4
View File
@@ -3,9 +3,9 @@ import styled, { type AnyStyledComponent, css } from 'styled-components';
import { AbacusApiStatus } from '@/constants/abacus'; import { AbacusApiStatus } from '@/constants/abacus';
import { ButtonSize, ButtonType } from '@/constants/buttons'; import { ButtonSize, ButtonType } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP, isDev } from '@/constants/networks'; import { isDev } from '@/constants/networks';
import { useApiState, useSelectedNetwork, useStringGetter } from '@/hooks'; import { useApiState, useStringGetter, useURLConfigs } from '@/hooks';
import { ChatIcon, LinkOutIcon } from '@/icons'; import { ChatIcon, LinkOutIcon } from '@/icons';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
@@ -28,8 +28,7 @@ enum ExchangeStatus {
export const FooterDesktop = () => { export const FooterDesktop = () => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { height, indexerHeight, status, statusErrorMessage } = useApiState(); const { height, indexerHeight, status, statusErrorMessage } = useApiState();
const { selectedNetwork } = useSelectedNetwork(); const { statusPage } = useURLConfigs();
const { statusPage } = ENVIRONMENT_CONFIG_MAP[selectedNetwork].links;
const { exchangeStatus, label } = const { exchangeStatus, label } =
!status || status === AbacusApiStatus.NORMAL !status || status === AbacusApiStatus.NORMAL
+14
View File
@@ -0,0 +1,14 @@
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');
});
});
+7 -6
View File
@@ -34,7 +34,7 @@ import {
import { DEFAULT_TRANSACTION_MEMO } from '@/constants/analytics'; import { DEFAULT_TRANSACTION_MEMO } from '@/constants/analytics';
import { DialogTypes } from '@/constants/dialogs'; import { DialogTypes } from '@/constants/dialogs';
import { UNCOMMITTED_ORDER_TIMEOUT_MS } from '@/constants/trade'; import { UNCOMMITTED_ORDER_TIMEOUT_MS } from '@/constants/trade';
import { ENVIRONMENT_CONFIG_MAP, DydxNetwork, isTestnet } from '@/constants/networks'; import { DydxChainId, isTestnet } from '@/constants/networks';
import { RootStore } from '@/state/_store'; import { RootStore } from '@/state/_store';
import { addUncommittedOrderClientId, removeUncommittedOrderClientId } from '@/state/account'; import { addUncommittedOrderClientId, removeUncommittedOrderClientId } from '@/state/account';
@@ -43,7 +43,11 @@ import { openDialog } from '@/state/dialogs';
import { StatefulOrderError } from '../errors'; import { StatefulOrderError } from '../errors';
import { bytesToBigInt } from '../numbers'; import { bytesToBigInt } from '../numbers';
import { log } from '../telemetry'; import { log } from '../telemetry';
import { hashFromTx } from '../hashfromTx'; import { hashFromTx, getMintscanTxLink } from '../txUtils';
(BigInt.prototype as any).toJSON = function () {
return this.toString();
};
class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol { class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
private compositeClient: CompositeClient | undefined; private compositeClient: CompositeClient | undefined;
@@ -238,9 +242,7 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
if (isTestnet) { if (isTestnet) {
console.log( console.log(
`${ENVIRONMENT_CONFIG_MAP[ getMintscanTxLink(this.compositeClient.network.getString() as DydxChainId, hash)
this.compositeClient.network.getString() as DydxNetwork
]?.links?.mintscan?.replace('{tx_hash}', hash.toString())}`
); );
} else console.log(`txHash: ${hash}`); } else console.log(`txHash: ${hash}`);
@@ -536,7 +538,6 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
const result = await this.cctpWithdraw(params); const result = await this.cctpWithdraw(params);
callback(result); callback(result);
break; break;
break;
} }
default: { default: {
break; break;
+19
View File
@@ -25,3 +25,22 @@ export function convertBech32Address({
}): string { }): string {
return toBech32(bech32Prefix, fromHex(toHex(fromBech32(address).data))); 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;
}
}
-3
View File
@@ -1,3 +0,0 @@
export const hashFromTx = (
txHash: string | Uint8Array
): string => `0x${Buffer.from(txHash).toString('hex')}`;
+2 -2
View File
@@ -1,4 +1,4 @@
import { type DydxNetwork, ENVIRONMENT_CONFIG_MAP } from '@/constants/networks'; import { type DydxNetwork, ENVIRONMENT_CONFIG_MAP, type DydxChainId } from '@/constants/networks';
export const validateAgainstAvailableEnvironments = (value: DydxNetwork) => export const validateAgainstAvailableEnvironments = (value: DydxNetwork) =>
Object.keys(ENVIRONMENT_CONFIG_MAP).includes(value); Object.keys(ENVIRONMENT_CONFIG_MAP).includes(value);
+3 -3
View File
@@ -31,13 +31,13 @@ export const fetchSquidStatus = async (
const response = await fetch(url, { const response = await fetch(url, {
headers: { headers: {
"x-integrator-id": integratorId || 'dYdX-api' 'x-integrator-id': integratorId || 'dYdX-api',
}, },
}); });
if (!response.ok) { if (!response.ok) {
const error = await response.json(); const error = await response.json();
throw new Error(error); throw error;
} }
return response.json(); return response.json();
@@ -45,4 +45,4 @@ export const fetchSquidStatus = async (
export const getNobleChainId = () => { export const getNobleChainId = () => {
return isMainnet ? 'noble-1' : 'grand-1'; return isMainnet ? 'noble-1' : 'grand-1';
} };
+2 -2
View File
@@ -19,10 +19,10 @@ class TestFlags {
return !!this.queryParams.displayinitializingmarkets; return !!this.queryParams.displayinitializingmarkets;
} }
get addressOverride():string { get addressOverride(): string {
return this.queryParams.address; return this.queryParams.address;
} }
get showTradingRewards() { get showTradingRewards() {
return !!this.queryParams.tradingrewards; return !!this.queryParams.tradingrewards;
} }
+7
View File
@@ -0,0 +1,7 @@
import { DydxChainId, LINKS_CONFIG_MAP } from '@/constants/networks';
export const hashFromTx = (txHash: string | Uint8Array): string =>
`0x${Buffer.from(txHash).toString('hex')}`;
export const getMintscanTxLink = (dydxChainId: DydxChainId, txHash: string): string =>
`${LINKS_CONFIG_MAP[dydxChainId]?.mintscan?.replace('{tx_hash}', txHash.toString())}`;
+13
View File
@@ -0,0 +1,13 @@
/**
* @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}` : ''}`;
};
+7 -10
View File
@@ -5,6 +5,7 @@ import {
type InjectedCoinbaseWalletExtensionProvider, type InjectedCoinbaseWalletExtensionProvider,
type WithInjectedEthereumProvider, type WithInjectedEthereumProvider,
type WithInjectedWeb3Provider, type WithInjectedWeb3Provider,
type WithInjectedOkxWalletProvider,
} from '@/constants/wallets'; } from '@/constants/wallets';
import { isTruthy } from '../isTruthy'; import { isTruthy } from '../isTruthy';
@@ -18,15 +19,6 @@ export const isMetaMask = (provider: ExternalProvider) => (
&& ( && (
!(provider as InjectedCoinbaseWalletExtensionProvider).overrideIsMetaMask !(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
)
) )
/* /*
@@ -54,5 +46,10 @@ export const detectInjectedEip1193Providers = (): ExternalProvider[] => {
? ethereumProvider.providers ? ethereumProvider.providers
: []; : [];
return [...displacedProviders, ethereumProvider, web3Provider].filter(isTruthy); const okxWalletProvider = (globalThis as typeof globalThis & WithInjectedOkxWalletProvider)
?.okxwallet;
return [...displacedProviders, ethereumProvider, web3Provider, okxWalletProvider].filter(
isTruthy
);
}; };
+4 -2
View File
@@ -1,7 +1,7 @@
import './polyfills'; import './polyfills';
import { StrictMode } from 'react'; import { StrictMode } from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import { HashRouter } from 'react-router-dom'; import { BrowserRouter, HashRouter } from 'react-router-dom';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { store } from '@/state/_store'; import { store } from '@/state/_store';
@@ -12,11 +12,13 @@ import './index.css';
import App from './App'; import App from './App';
const Router = import.meta.env.VITE_ROUTER_TYPE === 'hash' ? HashRouter : BrowserRouter;
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<ErrorBoundary> <ErrorBoundary>
<StrictMode> <StrictMode>
<Provider store={store}> <Provider store={store}>
<HashRouter children={<App />} /> <Router children={<App />} />
</Provider> </Provider>
</StrictMode> </StrictMode>
</ErrorBoundary> </ErrorBoundary>
@@ -26,8 +26,6 @@ import { openDialog } from '@/state/dialogs';
import { log } from '@/lib/telemetry'; import { log } from '@/lib/telemetry';
const SEASON_NUMBER = 2;
export const LaunchIncentivesPanel = ({ className }: { className?: string }) => { export const LaunchIncentivesPanel = ({ className }: { className?: string }) => {
const { isNotTablet } = useBreakpoints(); const { isNotTablet } = useBreakpoints();
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -74,14 +72,38 @@ const EstimatedRewards = () => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { dydxAddress } = useAccounts(); const { dydxAddress } = useAccounts();
const { data: seasonNumber } = useQuery({
queryKey: 'chaos_labs_season_number',
queryFn: async () => {
const resp = await fetch('https://cloud.chaoslabs.co/query/ccar-perpetuals', {
method: 'POST',
headers: {
'apollographql-client-name': 'dydx-v4',
'content-type': 'application/json',
protocol: 'dydx-v4',
},
body: JSON.stringify({
operationName: 'TradingSeasons',
variables: {},
query: `query TradingSeasons {
tradingSeasons {
label
}
}`,
}),
});
const seasons = (await resp.json())?.data?.tradingSeasons;
return seasons && seasons.length > 0 ? seasons[seasons.length - 1].label : undefined;
},
onError: (error: Error) => log('LaunchIncentives/fetchSeasonNumber', error),
});
const { data, isLoading } = useQuery({ const { data, isLoading } = useQuery({
enabled: !!dydxAddress, enabled: !!dydxAddress,
queryKey: `launch_incentives_rewards_${dydxAddress ?? ''}_${SEASON_NUMBER}`, queryKey: `launch_incentives_rewards_${dydxAddress ?? ''}`,
queryFn: async () => { queryFn: async () => {
if (!dydxAddress) return undefined; if (!dydxAddress) return undefined;
const resp = await fetch( const resp = await fetch(`https://cloud.chaoslabs.co/query/api/dydx/points/${dydxAddress}`);
`https://cloud.chaoslabs.co/query/api/dydx/points/${dydxAddress}?n=${SEASON_NUMBER}`
);
return (await resp.json())?.incentivePoints; return (await resp.json())?.incentivePoints;
}, },
onError: (error: Error) => log('LaunchIncentives/fetchPoints', error), onError: (error: Error) => log('LaunchIncentives/fetchPoints', error),
@@ -92,12 +114,14 @@ const EstimatedRewards = () => {
<Styled.EstimatedRewardsCardContent> <Styled.EstimatedRewardsCardContent>
<div> <div>
<span>{stringGetter({ key: STRING_KEYS.ESTIMATED_REWARDS })}</span> <span>{stringGetter({ key: STRING_KEYS.ESTIMATED_REWARDS })}</span>
<Styled.Season> {seasonNumber !== undefined && (
{stringGetter({ <Styled.Season>
key: STRING_KEYS.LAUNCH_INCENTIVES_SEASON_NUM, {stringGetter({
params: { SEASON_NUMBER }, key: STRING_KEYS.LAUNCH_INCENTIVES_SEASON_NUM,
})} params: { SEASON_NUMBER: seasonNumber },
</Styled.Season> })}
</Styled.Season>
)}
</div> </div>
<Styled.Points> <Styled.Points>
+4
View File
@@ -1,4 +1,8 @@
import { DydxChainId, ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import type { RootState } from './_store'; import type { RootState } from './_store';
export const getApiState = (state: RootState) => state.app.apiState; export const getApiState = (state: RootState) => state.app.apiState;
export const getSelectedNetwork = (state: RootState) => state.app.selectedNetwork; export const getSelectedNetwork = (state: RootState) => state.app.selectedNetwork;
export const getSelectedDydxChainId = (state: RootState) =>
ENVIRONMENT_CONFIG_MAP[state.app.selectedNetwork].dydxChainId as DydxChainId;
+1 -3
View File
@@ -13,8 +13,6 @@ export const DepositDialog = ({ setIsOpen }: ElementProps) => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { isMobile } = useBreakpoints(); const { isMobile } = useBreakpoints();
const closeDialog = () => setIsOpen?.(false);
return ( return (
<Dialog <Dialog
isOpen isOpen
@@ -22,7 +20,7 @@ export const DepositDialog = ({ setIsOpen }: ElementProps) => {
title={stringGetter({ key: STRING_KEYS.DEPOSIT })} title={stringGetter({ key: STRING_KEYS.DEPOSIT })}
placement={isMobile ? DialogPlacement.FullScreen : DialogPlacement.Default} placement={isMobile ? DialogPlacement.FullScreen : DialogPlacement.Default}
> >
<DepositDialogContent onDeposit={closeDialog} /> <DepositDialogContent />
</Dialog> </Dialog>
); );
}; };
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
import styled, { type AnyStyledComponent } from 'styled-components'; import styled, { type AnyStyledComponent } from 'styled-components';
import { TransferInputField, TransferType } from '@/constants/abacus'; import { TransferInputField, TransferType } from '@/constants/abacus';
import { AnalyticsEvent } from '@/constants/analytics';
import { isMainnet } from '@/constants/networks'; import { isMainnet } from '@/constants/networks';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
@@ -9,6 +10,7 @@ import { DepositForm } from '@/views/forms/AccountManagementForms/DepositForm';
import { TestnetDepositForm } from '@/views/forms/AccountManagementForms/TestnetDepositForm'; import { TestnetDepositForm } from '@/views/forms/AccountManagementForms/TestnetDepositForm';
import abacusStateManager from '@/lib/abacus'; import abacusStateManager from '@/lib/abacus';
import { track } from '@/lib/analytics';
type ElementProps = { type ElementProps = {
onDeposit?: () => void; onDeposit?: () => void;
@@ -34,9 +36,19 @@ export const DepositDialogContent = ({ onDeposit }: ElementProps) => {
return ( return (
<Styled.Content> <Styled.Content>
{isMainnet || !showFaucet ? ( {isMainnet || !showFaucet ? (
<DepositForm onDeposit={onDeposit} /> <DepositForm
onDeposit={(event) => {
track(AnalyticsEvent.TransferDeposit, event);
onDeposit?.();
}}
/>
) : ( ) : (
<TestnetDepositForm onDeposit={onDeposit} /> <TestnetDepositForm
onDeposit={() => {
track(AnalyticsEvent.TransferFaucet);
onDeposit?.();
}}
/>
)} )}
{!isMainnet && ( {!isMainnet && (
<Styled.TextToggle onClick={() => setShowFaucet(!showFaucet)}> <Styled.TextToggle onClick={() => setShowFaucet(!showFaucet)}>
+140
View File
@@ -0,0 +1,140 @@
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 styled, { AnyStyledComponent } from 'styled-components';
import { ButtonAction } from '@/constants/buttons'; import { ButtonAction } from '@/constants/buttons';
import { AppRoute } from '@/constants/routes'; import { AppRoute, BASE_ROUTE } from '@/constants/routes';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { useStringGetter } from '@/hooks'; import { useStringGetter } from '@/hooks';
import breakpoints from '@/styles/breakpoints'; import breakpoints from '@/styles/breakpoints';
@@ -40,7 +40,7 @@ export const NewMarketAgreementDialog = ({ acceptTerms, setIsOpen }: ElementProp
</Styled.Link> </Styled.Link>
), ),
TERMS_OF_USE: ( TERMS_OF_USE: (
<Styled.Link href={`/#${AppRoute.Terms}`}> <Styled.Link href={`${BASE_ROUTE}${AppRoute.Terms}`}>
{stringGetter({ key: STRING_KEYS.TERMS_OF_USE })} {stringGetter({ key: STRING_KEYS.TERMS_OF_USE })}
</Styled.Link> </Styled.Link>
), ),
+2 -2
View File
@@ -105,8 +105,8 @@ export const OnboardingDialog = ({ setIsOpen }: ElementProps) => {
<Styled.Content> <Styled.Content>
{isMainnet ? ( {isMainnet ? (
<DepositForm <DepositForm
onDeposit={() => { onDeposit={(event) => {
track(AnalyticsEvent.TransferDeposit); track(AnalyticsEvent.TransferDeposit, event);
}} }}
/> />
) : ( ) : (
@@ -2,7 +2,7 @@ import styled, { type AnyStyledComponent } from 'styled-components';
import { useAccounts, useStringGetter } from '@/hooks'; import { useAccounts, useStringGetter } from '@/hooks';
import { AppRoute } from '@/constants/routes'; import { AppRoute, BASE_ROUTE } from '@/constants/routes';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { ButtonAction } from '@/constants/buttons'; import { ButtonAction } from '@/constants/buttons';
@@ -34,12 +34,12 @@ export const AcknowledgeTerms = ({ onClose, onContinue }: ElementProps) => {
key: STRING_KEYS.TOS_TITLE, key: STRING_KEYS.TOS_TITLE,
params: { params: {
TERMS_LINK: ( TERMS_LINK: (
<Styled.Link href={`/#${AppRoute.Terms}`}> <Styled.Link href={`${BASE_ROUTE}${AppRoute.Terms}`}>
{stringGetter({ key: STRING_KEYS.TERMS_OF_USE })} {stringGetter({ key: STRING_KEYS.TERMS_OF_USE })}
</Styled.Link> </Styled.Link>
), ),
PRIVACY_POLICY_LINK: ( PRIVACY_POLICY_LINK: (
<Styled.Link href={`/#${AppRoute.Privacy}`}> <Styled.Link href={`${BASE_ROUTE}${AppRoute.Privacy}`}>
{stringGetter({ key: STRING_KEYS.PRIVACY_POLICY })} {stringGetter({ key: STRING_KEYS.PRIVACY_POLICY })}
</Styled.Link> </Styled.Link>
), ),
@@ -25,7 +25,7 @@ import { Switch } from '@/components/Switch';
import { WithReceipt } from '@/components/WithReceipt'; import { WithReceipt } from '@/components/WithReceipt';
import { WithTooltip } from '@/components/WithTooltip'; import { WithTooltip } from '@/components/WithTooltip';
import { getSelectedNetwork } from '@/state/appSelectors'; import { getSelectedNetwork, getSelectedDydxChainId } from '@/state/appSelectors';
import { track } from '@/lib/analytics'; import { track } from '@/lib/analytics';
import { isTruthy } from '@/lib/isTruthy'; import { isTruthy } from '@/lib/isTruthy';
@@ -98,7 +98,8 @@ export const GenerateKeys = ({
EvmDerivedAccountStatus.Derived, EvmDerivedAccountStatus.Derived,
].includes(status); ].includes(status);
const signTypedData = getSignTypedData(selectedNetwork); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const signTypedData = getSignTypedData(selectedDydxChainId);
const { signTypedDataAsync } = useSignTypedData({ const { signTypedDataAsync } = useSignTypedData({
...signTypedData, ...signTypedData,
domain: { domain: {
@@ -7,10 +7,11 @@ import { Abi, parseUnits } from 'viem';
import erc20 from '@/abi/erc20.json'; import erc20 from '@/abi/erc20.json';
import erc20_usdt from '@/abi/erc20_usdt.json'; import erc20_usdt from '@/abi/erc20_usdt.json';
import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus'; import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus';
import { AnalyticsEvent, AnalyticsEventData } from '@/constants/analytics';
import { AlertType } from '@/constants/alerts'; import { AlertType } from '@/constants/alerts';
import { ButtonSize } from '@/constants/buttons'; import { ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP, isMainnet } from '@/constants/networks'; import { isMainnet } from '@/constants/networks';
import { MAX_CCTP_TRANSFER_AMOUNT, MAX_PRICE_IMPACT, NumberSign } from '@/constants/numbers'; import { MAX_CCTP_TRANSFER_AMOUNT, MAX_PRICE_IMPACT, NumberSign } from '@/constants/numbers';
import type { EvmAddress } from '@/constants/wallets'; import type { EvmAddress } from '@/constants/wallets';
@@ -32,6 +33,7 @@ import { OutputType } from '@/components/Output';
import { Tag } from '@/components/Tag'; import { Tag } from '@/components/Tag';
import { WithDetailsReceipt } from '@/components/WithDetailsReceipt'; import { WithDetailsReceipt } from '@/components/WithDetailsReceipt';
import { getSelectedDydxChainId } from '@/state/appSelectors';
import { getTransferInputs } from '@/state/inputsSelectors'; import { getTransferInputs } from '@/state/inputsSelectors';
import abacusStateManager from '@/lib/abacus'; import abacusStateManager from '@/lib/abacus';
@@ -47,7 +49,7 @@ import { DepositButtonAndReceipt } from './DepositForm/DepositButtonAndReceipt';
import { NobleDeposit } from '../NobleDeposit'; import { NobleDeposit } from '../NobleDeposit';
type DepositFormProps = { type DepositFormProps = {
onDeposit?: () => void; onDeposit?: (event?: AnalyticsEventData<AnalyticsEvent.TransferDeposit>) => void;
onError?: () => void; onError?: () => void;
}; };
@@ -56,7 +58,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
const [error, setError] = useState<Error | null>(null); const [error, setError] = useState<Error | null>(null);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [requireUserActionInWallet, setRequireUserActionInWallet] = useState(false); const [requireUserActionInWallet, setRequireUserActionInWallet] = useState(false);
const { selectedNetwork } = useSelectedNetwork(); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const { evmAddress, signerWagmi, publicClientWagmi, nobleAddress } = useAccounts(); const { evmAddress, signerWagmi, publicClientWagmi, nobleAddress } = useAccounts();
@@ -131,7 +133,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
if (error) onError?.(); if (error) onError?.();
}, [error]); }, [error]);
const onSelectChain = useCallback((name: string, type: 'chain' | 'exchange') => { const onSelectNetwork = useCallback((name: string, type: 'chain' | 'exchange') => {
if (name) { if (name) {
abacusStateManager.clearTransferInputValues(); abacusStateManager.clearTransferInputValues();
setFromAmount(''); setFromAmount('');
@@ -257,14 +259,10 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
}; };
const txHash = await signerWagmi.sendTransaction(tx); const txHash = await signerWagmi.sendTransaction(tx);
onDeposit?.();
if (txHash) { if (txHash) {
addTransferNotification({ addTransferNotification({
txHash: txHash, txHash: txHash,
toChainId: !isCctp toChainId: !isCctp ? selectedDydxChainId : getNobleChainId(),
? ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId
: getNobleChainId(),
fromChainId: chainIdStr || undefined, fromChainId: chainIdStr || undefined,
toAmount: summary?.usdcSize || undefined, toAmount: summary?.usdcSize || undefined,
triggeredAt: Date.now(), triggeredAt: Date.now(),
@@ -272,6 +270,12 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
}); });
abacusStateManager.clearTransferInputValues(); abacusStateManager.clearTransferInputValues();
setFromAmount(''); setFromAmount('');
onDeposit?.({
chainId: chainIdStr || undefined,
tokenAddress: sourceToken?.address || undefined,
tokenSymbol: sourceToken?.symbol || undefined,
});
} }
} catch (error) { } catch (error) {
log('DepositForm/onSubmit', error); log('DepositForm/onSubmit', error);
@@ -280,7 +284,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
setIsLoading(false); setIsLoading(false);
} }
}, },
[requestPayload, signerWagmi, chainId] [requestPayload, signerWagmi, chainId, sourceToken, sourceChain]
); );
const amountInputReceipt = [ const amountInputReceipt = [
@@ -379,7 +383,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
<SourceSelectMenu <SourceSelectMenu
selectedChain={chainIdStr || undefined} selectedChain={chainIdStr || undefined}
selectedExchange={exchange || undefined} selectedExchange={exchange || undefined}
onSelect={onSelectChain} onSelect={onSelectNetwork}
/> />
{exchange && nobleAddress ? ( {exchange && nobleAddress ? (
<NobleDeposit /> <NobleDeposit />
@@ -14,6 +14,7 @@ import { popoverMixins } from '@/styles/popoverMixins';
import { getTransferInputs } from '@/state/inputsSelectors'; import { getTransferInputs } from '@/state/inputsSelectors';
import { isTruthy } from '@/lib/isTruthy'; import { isTruthy } from '@/lib/isTruthy';
import { testFlags } from '@/lib/testFlags';
type ElementProps = { type ElementProps = {
label?: string; label?: string;
@@ -17,9 +17,10 @@ import { getTransferInputs } from '@/state/inputsSelectors';
type ElementProps = { type ElementProps = {
selectedToken?: TransferInputTokenResource; selectedToken?: TransferInputTokenResource;
onSelectToken: (token: TransferInputTokenResource) => void; onSelectToken: (token: TransferInputTokenResource) => void;
isExchange?: boolean;
}; };
export const TokenSelectMenu = ({ selectedToken, onSelectToken }: ElementProps) => { export const TokenSelectMenu = ({ selectedToken, onSelectToken, isExchange }: ElementProps) => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { type, depositOptions, withdrawalOptions, resources } = const { type, depositOptions, withdrawalOptions, resources } =
useSelector(getTransferInputs, shallowEqual) || {}; useSelector(getTransferInputs, shallowEqual) || {};
@@ -47,19 +48,24 @@ export const TokenSelectMenu = ({ selectedToken, onSelectToken }: ElementProps)
}, },
]} ]}
label={stringGetter({ key: STRING_KEYS.ASSET })} label={stringGetter({ key: STRING_KEYS.ASSET })}
withReceiptItems={[ withSearch={!isExchange}
{ withReceiptItems={
key: 'swap', !isExchange
label: stringGetter({ key: STRING_KEYS.SWAP }), ? [
value: selectedToken && ( {
<> key: 'swap',
<Tag>{type === TransferType.deposit ? selectedToken?.symbol : 'USDC'}</Tag> label: stringGetter({ key: STRING_KEYS.SWAP }),
<DiffArrow /> value: selectedToken && (
<Tag>{type === TransferType.deposit ? 'USDC' : selectedToken?.symbol}</Tag> <>
</> <Tag>{type === TransferType.deposit ? selectedToken?.symbol : 'USDC'}</Tag>
), <DiffArrow />
}, <Tag>{type === TransferType.deposit ? 'USDC' : selectedToken?.symbol}</Tag>
]} </>
),
},
]
: undefined
}
> >
<Styled.AssetRow> <Styled.AssetRow>
{selectedToken ? ( {selectedToken ? (
@@ -7,9 +7,10 @@ import { isAddress } from 'viem';
import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus'; import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus';
import { AlertType } from '@/constants/alerts'; import { AlertType } from '@/constants/alerts';
import { AnalyticsEvent } from '@/constants/analytics';
import { ButtonSize } from '@/constants/buttons'; import { ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP, isMainnet } from '@/constants/networks'; import { isMainnet } from '@/constants/networks';
import { TransferNotificationTypes } from '@/constants/notifications'; import { TransferNotificationTypes } from '@/constants/notifications';
import { import {
MAX_CCTP_TRANSFER_AMOUNT, MAX_CCTP_TRANSFER_AMOUNT,
@@ -45,6 +46,7 @@ import { Icon, IconName } from '@/components/Icon';
import { SourceSelectMenu } from '@/views/forms/AccountManagementForms/SourceSelectMenu'; import { SourceSelectMenu } from '@/views/forms/AccountManagementForms/SourceSelectMenu';
import { getSelectedDydxChainId } from '@/state/appSelectors';
import { getSubaccount } from '@/state/accountSelectors'; import { getSubaccount } from '@/state/accountSelectors';
import { getTransferInputs } from '@/state/inputsSelectors'; import { getTransferInputs } from '@/state/inputsSelectors';
@@ -54,12 +56,14 @@ import { getNobleChainId } from '@/lib/squid';
import { TokenSelectMenu } from './TokenSelectMenu'; import { TokenSelectMenu } from './TokenSelectMenu';
import { WithdrawButtonAndReceipt } from './WithdrawForm/WithdrawButtonAndReceipt'; import { WithdrawButtonAndReceipt } from './WithdrawForm/WithdrawButtonAndReceipt';
import { validateCosmosAddress } from '@/lib/addressUtils';
import { track } from '@/lib/analytics';
export const WithdrawForm = () => { export const WithdrawForm = () => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const [error, setError] = useState<string>(); const [error, setError] = useState<string>();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { selectedNetwork } = useSelectedNetwork(); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const { sendSquidWithdraw } = useSubaccount(); const { sendSquidWithdraw } = useSubaccount();
const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {}; const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {};
@@ -67,6 +71,7 @@ export const WithdrawForm = () => {
const { const {
requestPayload, requestPayload,
token, token,
exchange,
chain: chainIdStr, chain: chainIdStr,
address: toAddress, address: toAddress,
resources, resources,
@@ -178,20 +183,27 @@ export const WithdrawForm = () => {
requestPayload.data, requestPayload.data,
isCctp isCctp
); );
if (txHash) { const nobleChainId = getNobleChainId();
const toChainId = Boolean(exchange) ? nobleChainId : chainIdStr || undefined;
if (txHash && toChainId) {
addTransferNotification({ addTransferNotification({
txHash: txHash, txHash: txHash,
type: TransferNotificationTypes.Withdrawal, type: TransferNotificationTypes.Withdrawal,
fromChainId: !isCctp fromChainId: !isCctp ? selectedDydxChainId : nobleChainId,
? ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId toChainId,
: getNobleChainId(),
toChainId: chainIdStr || undefined,
toAmount: debouncedAmountBN.toNumber(), toAmount: debouncedAmountBN.toNumber(),
triggeredAt: Date.now(), triggeredAt: Date.now(),
isCctp, isCctp,
isExchange: Boolean(exchange),
}); });
abacusStateManager.clearTransferInputValues(); abacusStateManager.clearTransferInputValues();
setWithdrawAmount(''); setWithdrawAmount('');
track(AnalyticsEvent.TransferWithdraw, {
chainId: toChainId,
tokenAddress: toToken?.address || undefined,
tokenSymbol: toToken?.symbol || undefined,
});
} }
} }
} catch (error) { } catch (error) {
@@ -214,7 +226,17 @@ export const WithdrawForm = () => {
setIsLoading(false); setIsLoading(false);
} }
}, },
[requestPayload, debouncedAmountBN, chainIdStr, toAddress, screenAddresses, stringGetter] [
requestPayload,
debouncedAmountBN,
chainIdStr,
toAddress,
selectedDydxChainId,
exchange,
toToken,
screenAddresses,
stringGetter,
]
); );
const onChangeAddress = useCallback((e: ChangeEvent<HTMLInputElement>) => { const onChangeAddress = useCallback((e: ChangeEvent<HTMLInputElement>) => {
@@ -247,13 +269,20 @@ export const WithdrawForm = () => {
setWithdrawAmount(freeCollateralBN.toString()); setWithdrawAmount(freeCollateralBN.toString());
}, [freeCollateralBN, setWithdrawAmount]); }, [freeCollateralBN, setWithdrawAmount]);
const onSelectChain = useCallback((chain: string) => { const onSelectNetwork = useCallback((name: string, type: 'chain' | 'exchange') => {
if (chain) { if (name) {
abacusStateManager.setTransferValue({
field: TransferInputField.chain,
value: chain,
});
setWithdrawAmount(''); setWithdrawAmount('');
if (type === 'chain') {
abacusStateManager.setTransferValue({
field: TransferInputField.chain,
value: name,
});
} else {
abacusStateManager.setTransferValue({
field: TransferInputField.exchange,
value: name,
});
}
} }
}, []); }, []);
@@ -314,7 +343,7 @@ export const WithdrawForm = () => {
}); });
if (debouncedAmountBN) { if (debouncedAmountBN) {
if (!chainIdStr) { if (!chainIdStr && !exchange) {
return stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_CHAIN }); return stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_CHAIN });
} else if (!toToken) { } else if (!toToken) {
return stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_ASSET }); return stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_ASSET });
@@ -361,14 +390,19 @@ export const WithdrawForm = () => {
summary, summary,
]); ]);
const isInvalidNobleAddress = Boolean(
exchange && toAddress && !validateCosmosAddress(toAddress, 'noble')
);
const isDisabled = const isDisabled =
!!errorMessage || !!errorMessage ||
!toToken || !toToken ||
!chainIdStr || (!chainIdStr && !exchange) ||
!toAddress || !toAddress ||
debouncedAmountBN.isNaN() || debouncedAmountBN.isNaN() ||
debouncedAmountBN.isZero() || debouncedAmountBN.isZero() ||
isLoading; isLoading ||
isInvalidNobleAddress;
return ( return (
<Styled.Form onSubmit={onSubmit}> <Styled.Form onSubmit={onSubmit}>
@@ -386,12 +420,21 @@ export const WithdrawForm = () => {
} }
/> />
<SourceSelectMenu <SourceSelectMenu
label={stringGetter({ key: STRING_KEYS.NETWORK })} selectedExchange={exchange || undefined}
selectedChain={chainIdStr || undefined} selectedChain={chainIdStr || undefined}
onSelect={onSelectChain} onSelect={onSelectNetwork}
/> />
</Styled.DestinationRow> </Styled.DestinationRow>
<TokenSelectMenu selectedToken={toToken || undefined} onSelectToken={onSelectToken} /> {isInvalidNobleAddress && (
<AlertMessage type={AlertType.Error}>
{stringGetter({ key: STRING_KEYS.NOBLE_ADDRESS_VALIDATION })}
</AlertMessage>
)}
<TokenSelectMenu
selectedToken={toToken || undefined}
onSelectToken={onSelectToken}
isExchange={Boolean(exchange)}
/>
<Styled.WithDetailsReceipt side="bottom" detailItems={amountInputReceipt}> <Styled.WithDetailsReceipt side="bottom" detailItems={amountInputReceipt}>
<FormInput <FormInput
type={InputType.Number} type={InputType.Number}
@@ -27,6 +27,8 @@ import { calculateCanAccountTrade } from '@/state/accountCalculators';
import { getSubaccount } from '@/state/accountSelectors'; import { getSubaccount } from '@/state/accountSelectors';
import { getTransferInputs } from '@/state/inputsSelectors'; import { getTransferInputs } from '@/state/inputsSelectors';
import { isTruthy } from '@/lib/isTruthy';
import { SlippageEditor } from '../SlippageEditor'; import { SlippageEditor } from '../SlippageEditor';
type ElementProps = { type ElementProps = {
@@ -55,7 +57,7 @@ export const WithdrawButtonAndReceipt = ({
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { leverage } = useSelector(getSubaccount, shallowEqual) || {}; const { leverage } = useSelector(getSubaccount, shallowEqual) || {};
const { summary, requestPayload } = useSelector(getTransferInputs, shallowEqual) || {}; const { summary, requestPayload, exchange } = useSelector(getTransferInputs, shallowEqual) || {};
const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual); const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual);
const { usdcLabel } = useTokenConfigs(); const { usdcLabel } = useTokenConfigs();
@@ -92,7 +94,7 @@ export const WithdrawButtonAndReceipt = ({
value: <Output type={OutputType.Fiat} value={totalFees} />, value: <Output type={OutputType.Fiat} value={totalFees} />,
subitems: feeSubitems, subitems: feeSubitems,
}, },
{ !exchange && {
key: 'exchange-rate', key: 'exchange-rate',
label: <span>{stringGetter({ key: STRING_KEYS.EXCHANGE_RATE })}</span>, label: <span>{stringGetter({ key: STRING_KEYS.EXCHANGE_RATE })}</span>,
value: withdrawToken && typeof summary?.exchangeRate === 'number' && ( value: withdrawToken && typeof summary?.exchangeRate === 'number' && (
@@ -133,7 +135,9 @@ export const WithdrawButtonAndReceipt = ({
{withdrawToken && <Tag>{withdrawToken?.symbol}</Tag>} {withdrawToken && <Tag>{withdrawToken?.symbol}</Tag>}
</span> </span>
), ),
value: <Output type={OutputType.Asset} value={summary?.toAmount} fractionDigits={TOKEN_DECIMALS} />, value: (
<Output type={OutputType.Asset} value={summary?.toAmount} fractionDigits={TOKEN_DECIMALS} />
),
subitems: [ subitems: [
{ {
key: 'minimum-amount-received', key: 'minimum-amount-received',
@@ -144,13 +148,17 @@ export const WithdrawButtonAndReceipt = ({
</span> </span>
), ),
value: ( 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', tooltip: 'minimum-amount-received',
}, },
], ],
}, },
{ !exchange && {
key: 'slippage', key: 'slippage',
label: <span>{stringGetter({ key: STRING_KEYS.MAX_SLIPPAGE })}</span>, label: <span>{stringGetter({ key: STRING_KEYS.MAX_SLIPPAGE })}</span>,
value: ( value: (
@@ -175,7 +183,7 @@ export const WithdrawButtonAndReceipt = ({
/> />
), ),
}, },
]; ].filter(isTruthy);
const isFormValid = !isDisabled && !isEditingSlippage; const isFormValid = !isDisabled && !isEditingSlippage;
+25 -31
View File
@@ -1,5 +1,5 @@
import { useState } from 'react'; import { useState } from 'react';
import styled, { type AnyStyledComponent } from 'styled-components'; import styled, { type AnyStyledComponent, css } from 'styled-components';
import { OpacityToken } from '@/constants/styles/base'; import { OpacityToken } from '@/constants/styles/base';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
@@ -10,7 +10,6 @@ import { useAccounts, useStringGetter } from '@/hooks';
import { CopyButton } from '@/components/CopyButton'; import { CopyButton } from '@/components/CopyButton';
import { QrCode } from '@/components/QrCode'; import { QrCode } from '@/components/QrCode';
import { Checkbox } from '@/components/Checkbox'; import { Checkbox } from '@/components/Checkbox';
import { Icon, IconName } from '@/components/Icon';
import { TimeoutButton } from '@/components/TimeoutButton'; import { TimeoutButton } from '@/components/TimeoutButton';
import { WithDetailsReceipt } from '@/components/WithDetailsReceipt'; import { WithDetailsReceipt } from '@/components/WithDetailsReceipt';
import { WithReceipt } from '@/components/WithReceipt'; import { WithReceipt } from '@/components/WithReceipt';
@@ -19,6 +18,7 @@ import { generateFadedColorVariant } from '@/lib/styles';
export const NobleDeposit = () => { export const NobleDeposit = () => {
const [hasAcknowledged, setHasAcknowledged] = useState(false); const [hasAcknowledged, setHasAcknowledged] = useState(false);
const [hasTimedout, setHasTimedout] = useState(false);
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { nobleAddress } = useAccounts(); const { nobleAddress } = useAccounts();
@@ -30,23 +30,21 @@ export const NobleDeposit = () => {
{ {
key: 'nobleAddress', key: 'nobleAddress',
label: stringGetter({ key: STRING_KEYS.NOBLE_ADDRESS }), label: stringGetter({ key: STRING_KEYS.NOBLE_ADDRESS }),
value: nobleAddress, value:
hasAcknowledged && hasTimedout
? nobleAddress
: stringGetter({ key: STRING_KEYS.ACKNOWLEDGE_TO_REVEAL }),
}, },
]} ]}
> >
<Styled.QrCodeContainer> <Styled.QrCode
<Styled.QrCode size={432} value={nobleAddress || ''} /> hasLogo
</Styled.QrCodeContainer> size={432}
value={nobleAddress || ''}
blurred={!hasAcknowledged || !hasTimedout}
/>
</WithDetailsReceipt> </WithDetailsReceipt>
<Styled.WaitingSpan>
<Styled.CautionIconContainer>
<Icon iconName={IconName.CautionCircleStroked} />
</Styled.CautionIconContainer>
<p>{stringGetter({ key: STRING_KEYS.NOBLE_WARNING })}</p>
</Styled.WaitingSpan>
<Styled.WithReceipt <Styled.WithReceipt
slotReceipt={ slotReceipt={
<Styled.CheckboxContainer> <Styled.CheckboxContainer>
@@ -63,7 +61,12 @@ export const NobleDeposit = () => {
> >
<TimeoutButton <TimeoutButton
timeoutInSeconds={8} timeoutInSeconds={8}
slotFinal={<CopyButton state={{ isDisabled: !hasAcknowledged }} value={nobleAddress} />} onTimeOut={() => setHasTimedout(true)}
slotFinal={
<CopyButton state={{ isDisabled: !hasAcknowledged }} value={nobleAddress}>
{!hasAcknowledged ? stringGetter({ key: STRING_KEYS.ACKNOWLEDGE_RISKS }) : undefined}
</CopyButton>
}
/> />
</Styled.WithReceipt> </Styled.WithReceipt>
</> </>
@@ -82,23 +85,14 @@ Styled.WithReceipt = styled(WithReceipt)`
--withReceipt-backgroundColor: var(--color-layer-2); --withReceipt-backgroundColor: var(--color-layer-2);
`; `;
Styled.QrCodeContainer = styled.div` Styled.QrCode = styled(QrCode)<{ blurred: boolean }>`
display: flex; border-radius: 0.5em;
justify-content: center;
padding: 0.5rem; ${({ blurred }) =>
blurred &&
background-color: var(--color-layer-2); css`
border-radius: 0.5rem; filter: blur(8px);
`; `}
Styled.QrCode = styled(QrCode)`
max-height: 20rem;
width: fit-content;
svg {
max-height: 20rem;
}
`; `;
Styled.CheckboxContainer = styled.div` Styled.CheckboxContainer = styled.div`
+4 -5
View File
@@ -9,7 +9,6 @@ import { TransferInputField, TransferType } from '@/constants/abacus';
import { AlertType } from '@/constants/alerts'; import { AlertType } from '@/constants/alerts';
import { ButtonShape, ButtonSize } from '@/constants/buttons'; import { ButtonShape, ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { NumberSign } from '@/constants/numbers'; import { NumberSign } from '@/constants/numbers';
import { DydxChainAsset } from '@/constants/wallets'; import { DydxChainAsset } from '@/constants/wallets';
@@ -18,7 +17,6 @@ import {
useAccounts, useAccounts,
useDydxClient, useDydxClient,
useRestrictions, useRestrictions,
useSelectedNetwork,
useStringGetter, useStringGetter,
useSubaccount, useSubaccount,
useTokenConfigs, useTokenConfigs,
@@ -41,6 +39,7 @@ import { TransferButtonAndReceipt } from '@/views/forms/TransferForm/TransferBut
import { WithDetailsReceipt } from '@/components/WithDetailsReceipt'; import { WithDetailsReceipt } from '@/components/WithDetailsReceipt';
import { getSubaccount } from '@/state/accountSelectors'; import { getSubaccount } from '@/state/accountSelectors';
import { getSelectedDydxChainId } from '@/state/appSelectors';
import { getTransferInputs } from '@/state/inputsSelectors'; import { getTransferInputs } from '@/state/inputsSelectors';
import abacusStateManager from '@/lib/abacus'; import abacusStateManager from '@/lib/abacus';
@@ -63,7 +62,7 @@ export const TransferForm = ({
const { dydxAddress } = useAccounts(); const { dydxAddress } = useAccounts();
const { transfer } = useSubaccount(); const { transfer } = useSubaccount();
const { nativeTokenBalance, usdcBalance } = useAccountBalance(); const { nativeTokenBalance, usdcBalance } = useAccountBalance();
const { selectedNetwork } = useSelectedNetwork(); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const { tokensConfigs, usdcLabel, chainTokenLabel } = useTokenConfigs(); const { tokensConfigs, usdcLabel, chainTokenLabel } = useTokenConfigs();
const { const {
@@ -239,7 +238,7 @@ export const TransferForm = ({
const networkOptions = [ const networkOptions = [
{ {
chainId: ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId, chainId: selectedDydxChainId,
label: ( label: (
<Styled.InlineRow> <Styled.InlineRow>
<AssetIcon symbol="DYDX" /> {stringGetter({ key: STRING_KEYS.DYDX_CHAIN })} <AssetIcon symbol="DYDX" /> {stringGetter({ key: STRING_KEYS.DYDX_CHAIN })}
@@ -322,7 +321,7 @@ export const TransferForm = ({
/> />
<Styled.NetworkSelectMenu <Styled.NetworkSelectMenu
label={stringGetter({ key: STRING_KEYS.NETWORK })} label={stringGetter({ key: STRING_KEYS.NETWORK })}
value={ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId} value={selectedDydxChainId}
slotTriggerAfter={null} slotTriggerAfter={null}
> >
{networkOptions.map(({ chainId, label }) => ( {networkOptions.map(({ chainId, label }) => (
+13
View File
@@ -40,6 +40,7 @@ import { getAppTheme } from '@/state/configsSelectors';
import { isTruthy } from '@/lib/isTruthy'; import { isTruthy } from '@/lib/isTruthy';
import { truncateAddress } from '@/lib/wallet'; import { truncateAddress } from '@/lib/wallet';
import { MustBigNumber } from '@/lib/numbers'; import { MustBigNumber } from '@/lib/numbers';
import { getMobileAppUrl } from '../dialogs/MobileDownloadDialog';
export const AccountMenu = () => { export const AccountMenu = () => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
@@ -189,6 +190,18 @@ export const AccountMenu = () => {
label: stringGetter({ key: STRING_KEYS.DISPLAY_SETTINGS }), label: stringGetter({ key: STRING_KEYS.DISPLAY_SETTINGS }),
onSelect: () => dispatch(openDialog({ type: DialogTypes.DisplaySettings })), 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 ...(onboardingState === OnboardingState.AccountConnected && hdKey
? [ ? [
{ {
@@ -1,18 +1,21 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useSelector } from 'react-redux';
import styled, { css, type AnyStyledComponent } from 'styled-components'; import styled, { css, type AnyStyledComponent } from 'styled-components';
import { StatusResponse } from '@0xsquid/sdk'; import { StatusResponse } from '@0xsquid/sdk';
import { useStringGetter, useSelectedNetwork, useURLConfigs } from '@/hooks'; import { STRING_KEYS } from '@/constants/localization';
import { TransferNotificationTypes } from '@/constants/notifications';
import { useStringGetter, useURLConfigs } from '@/hooks';
import { layoutMixins } from '@/styles/layoutMixins';
import { Link } from '@/components/Link'; import { Link } from '@/components/Link';
import { Icon, IconName } from '@/components/Icon'; import { Icon, IconName } from '@/components/Icon';
import { LoadingDots } from '@/components/Loading/LoadingDots'; import { LoadingDots } from '@/components/Loading/LoadingDots';
import { LoadingSpinner } from '@/components/Loading/LoadingSpinner'; import { LoadingSpinner } from '@/components/Loading/LoadingSpinner';
import { layoutMixins } from '@/styles/layoutMixins'; import { getSelectedDydxChainId } from '@/state/appSelectors';
import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { TransferNotificationTypes } from '@/constants/notifications';
type ElementProps = { type ElementProps = {
status?: StatusResponse; status?: StatusResponse;
@@ -32,9 +35,8 @@ enum TransferStatusStep {
export const TransferStatusSteps = ({ className, status, type }: ElementProps & StyleProps) => { export const TransferStatusSteps = ({ className, status, type }: ElementProps & StyleProps) => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { selectedNetwork } = useSelectedNetwork(); const selectedDydxChainId = useSelector(getSelectedDydxChainId);
const { mintscan: mintscanTxUrl } = useURLConfigs(); const { mintscan: mintscanTxUrl } = useURLConfigs();
const dydxChainId = ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId;
const { currentStep, steps } = useMemo(() => { const { currentStep, steps } = useMemo(() => {
const routeStatus = status?.routeStatus; const routeStatus = status?.routeStatus;
@@ -55,7 +57,7 @@ export const TransferStatusSteps = ({ className, status, type }: ElementProps &
link: link:
type === TransferNotificationTypes.Deposit type === TransferNotificationTypes.Deposit
? status?.fromChain?.transactionUrl ? status?.fromChain?.transactionUrl
: routeStatus?.[0]?.chainId === dydxChainId && routeStatus[0].txHash : routeStatus?.[0]?.chainId === selectedDydxChainId && routeStatus[0].txHash
? `${mintscanTxUrl?.replace('{tx_hash}', routeStatus[0].txHash.replace('0x', ''))}` ? `${mintscanTxUrl?.replace('{tx_hash}', routeStatus[0].txHash.replace('0x', ''))}`
: undefined, : undefined,
}, },
@@ -81,7 +83,7 @@ export const TransferStatusSteps = ({ className, status, type }: ElementProps &
link: link:
type === TransferNotificationTypes.Withdrawal type === TransferNotificationTypes.Withdrawal
? status?.toChain?.transactionUrl ? status?.toChain?.transactionUrl
: currentStatus?.chainId === dydxChainId && currentStatus?.txHash : currentStatus?.chainId === selectedDydxChainId && currentStatus?.txHash
? `${mintscanTxUrl?.replace('{tx_hash}', currentStatus.txHash.replace('0x', ''))}` ? `${mintscanTxUrl?.replace('{tx_hash}', currentStatus.txHash.replace('0x', ''))}`
: undefined, : undefined,
}, },
@@ -40,7 +40,7 @@ export const TransferStatusNotification = ({
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const [open, setOpen] = useState<boolean>(false); const [open, setOpen] = useState<boolean>(false);
const [secondsLeft, setSecondsLeft] = useState<number>(0); const [secondsLeft, setSecondsLeft] = useState<number>(0);
const { fromChainId, status, txHash, toAmount } = transfer; const { fromChainId, status, txHash, toAmount, isExchange } = transfer;
// @ts-ignore status.errors is not in the type definition but can be returned // @ts-ignore status.errors is not in the type definition but can be returned
const error = status?.errors?.length ? status?.errors[0] : status?.error; const error = status?.errors?.length ? status?.errors[0] : status?.error;
@@ -54,6 +54,8 @@ export const TransferStatusNotification = ({
useInterval({ callback: updateSecondsLeft }); useInterval({ callback: updateSecondsLeft });
const isComplete = status?.squidTransactionStatus === 'success' || isExchange;
const inProgressStatusString = const inProgressStatusString =
type === TransferNotificationTypes.Deposit type === TransferNotificationTypes.Deposit
? secondsLeft > 0 ? secondsLeft > 0
@@ -65,10 +67,10 @@ export const TransferStatusNotification = ({
const statusString = const statusString =
type === TransferNotificationTypes.Deposit type === TransferNotificationTypes.Deposit
? status?.squidTransactionStatus === 'success' ? isComplete
? STRING_KEYS.DEPOSIT_COMPLETE ? STRING_KEYS.DEPOSIT_COMPLETE
: inProgressStatusString : inProgressStatusString
: status?.squidTransactionStatus === 'success' : isComplete
? STRING_KEYS.WITHDRAW_COMPLETE ? STRING_KEYS.WITHDRAW_COMPLETE
: inProgressStatusString; : inProgressStatusString;
@@ -108,12 +110,12 @@ export const TransferStatusNotification = ({
slotIcon={isToast && slotIcon} slotIcon={isToast && slotIcon}
slotTitle={slotTitle} slotTitle={slotTitle}
slotCustomContent={ slotCustomContent={
!status ? ( !status && !isExchange ? (
<LoadingDots size={3} /> <LoadingDots size={3} />
) : ( ) : (
<Styled.BridgingStatus> <Styled.BridgingStatus>
{content} {content}
{!isToast && status?.squidTransactionStatus !== 'success' && !hasError && ( {!isToast && !isComplete && !hasError && (
<Styled.TransferStatusSteps status={status} type={type} /> <Styled.TransferStatusSteps status={status} type={type} />
)} )}
</Styled.BridgingStatus> </Styled.BridgingStatus>
+3
View File
@@ -0,0 +1,3 @@
{
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}