Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd5c4a473a | ||
|
|
9815437a80 | ||
|
|
890a4b2c0e | ||
|
|
8d3cc76d14 | ||
|
|
c7ad4b3028 | ||
|
|
c88d5dd411 | ||
|
|
d6ab547055 | ||
|
|
2fe6c2bb3b | ||
|
|
e6858ca917 | ||
|
|
d92b49f4fa | ||
|
|
e7da21962a | ||
|
|
b479531e34 | ||
|
|
0d4c5a547a | ||
|
|
5b235ee53f | ||
|
|
18dee0586b | ||
|
|
9f1f5987ee |
+2
-1
@@ -6,4 +6,5 @@ VITE_PK_ENCRYPTION_KEY=
|
||||
|
||||
VITE_WALLETCONNECT2_PROJECT_ID=
|
||||
|
||||
VITE_V3_TOKEN_ADDRESS=
|
||||
VITE_V3_TOKEN_ADDRESS=0x6d5bb505a4f85c10b122ccc36e30f57e2b86a291
|
||||
VITE_TOKEN_MIGRATION_URI=https://bridge.dydx.trade/
|
||||
@@ -26,7 +26,7 @@ Alternatively, follow the [IPFS Kubo installation guide](https://docs.ipfs.tech
|
||||
|
||||
## Part 1: Setting up your local environment
|
||||
|
||||
### Step 1: Clone the repo
|
||||
### 1. Clone the repo
|
||||
|
||||
Clone the repository and navigate to its directory:
|
||||
|
||||
@@ -35,7 +35,7 @@ git clone https://github.com/dydxprotocol/v4-web.git
|
||||
cd v4-web
|
||||
```
|
||||
|
||||
### Step 2: Install pnpm and dependencies
|
||||
### 2. Install pnpm and dependencies
|
||||
|
||||
Install pnpm and the project dependencies:
|
||||
|
||||
@@ -69,7 +69,9 @@ You'll need to provide a Wallet Connect project id to enable onboarding and wall
|
||||
- Create a project on https://cloud.walletconnect.com/app
|
||||
- Copy over the project ID into this [field](https://github.com/dydxprotocol/v4-web/blob/67ecbd75b43e0c264b7b4d2d9b3d969830b0621c/public/configs/env.json#L822C33-L822C46)
|
||||
|
||||
## Part 4: Deploying with Vercel
|
||||
# Deployments
|
||||
|
||||
## Deploying with Vercel
|
||||
|
||||
### Step 1: Connect your repository to Vercel
|
||||
|
||||
@@ -86,7 +88,7 @@ If you wish to incorporate analytics via Amplitude and Bugsnag, you can use our
|
||||
|
||||
For more details, check out Vercel's [official documentation](https://vercel.com/docs).
|
||||
|
||||
## Part 5: Deploying to IPFS
|
||||
## Deploying to IPFS
|
||||
|
||||
### web3.storage: deploy to IPFS via web3.storage using the provided script
|
||||
|
||||
@@ -118,3 +120,18 @@ To access your content on IPFS:
|
||||
2. **Public IPFS gateway:** Access your content via a public IPFS gateway, such as [https://dweb.link](https://dweb.link/) or [https://w3s.link/](https://w3s.link/). Use the gateway URL with your CID appended, like `https://dweb.link/ipfs/your_cid`.
|
||||
|
||||
Replace `your_cid` with the actual CID.
|
||||
|
||||
## Cloudflare Settings
|
||||
|
||||
We recommend that you add your website to Cloudflare for additional security settings.
|
||||
|
||||
To block OFAC Sanctioned countries:
|
||||
1. Navigate Websites > Domain > Security > WAF
|
||||
|
||||
2. Create Rule with the following settings:
|
||||
* If incoming requests match
|
||||
`(ip.geoip.country eq "CU") or (ip.geoip.country eq "IR") or (ip.geoip.country eq "KP") or (ip.geoip.country eq "SY") or (ip.geoip.country eq "MM") or (ip.geoip.subdivision_1_iso_code eq "UA-09") or (ip.geoip.subdivision_1_iso_code eq "UA-14") or (ip.geoip.subdivision_1_iso_code eq "UA-43")`
|
||||
* This rule will bring up a Cloudflare page when a restricted geography tries to access your site. You will have the option to display:
|
||||
1. Custom Text
|
||||
- (e.g. `Because you appear to be a resident of, or trading from, a jurisdiction that violates our terms of use, or have engaged in activity that violates our terms of use, you have been blocked. You may withdraw your funds from the protocol at any time.`)
|
||||
2. Default Cloudflare WAF block page
|
||||
|
||||
+3
-3
@@ -39,9 +39,9 @@
|
||||
"@cosmjs/proto-signing": "^0.31.0",
|
||||
"@cosmjs/stargate": "^0.31.0",
|
||||
"@cosmjs/tendermint-rpc": "^0.31.0",
|
||||
"@dydxprotocol/v4-abacus": "^0.7.6",
|
||||
"@dydxprotocol/v4-client-js": "^0.40.3",
|
||||
"@dydxprotocol/v4-localization": "^0.1.32",
|
||||
"@dydxprotocol/v4-abacus": "^1.0.3",
|
||||
"@dydxprotocol/v4-client-js": "^1.0.0",
|
||||
"@dydxprotocol/v4-localization": "^1.0.0",
|
||||
"@ethersproject/providers": "^5.7.2",
|
||||
"@js-joda/core": "^5.5.3",
|
||||
"@radix-ui/react-collapsible": "^1.0.3",
|
||||
|
||||
Generated
+16
-16
@@ -1,9 +1,5 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@0xsquid/sdk':
|
||||
specifier: ^1.10.0
|
||||
@@ -27,14 +23,14 @@ dependencies:
|
||||
specifier: ^0.31.0
|
||||
version: 0.31.0
|
||||
'@dydxprotocol/v4-abacus':
|
||||
specifier: ^0.7.6
|
||||
version: 0.7.6
|
||||
specifier: ^1.0.3
|
||||
version: 1.0.3
|
||||
'@dydxprotocol/v4-client-js':
|
||||
specifier: ^0.40.3
|
||||
version: 0.40.3
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
'@dydxprotocol/v4-localization':
|
||||
specifier: ^0.1.32
|
||||
version: 0.1.32
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
'@ethersproject/providers':
|
||||
specifier: ^5.7.2
|
||||
version: 5.7.2
|
||||
@@ -979,12 +975,12 @@ packages:
|
||||
resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==}
|
||||
dev: true
|
||||
|
||||
/@dydxprotocol/v4-abacus@0.7.6:
|
||||
resolution: {integrity: sha512-FdDXRaFMfxhyrJJs0dNrb1KG6X95lPHelua1yCn/SVgzwGaAqlJczrzmdh98INntuRAgIxOu69R5mCGyekumyg==}
|
||||
/@dydxprotocol/v4-abacus@1.0.3:
|
||||
resolution: {integrity: sha512-GuX37/DMMNSke8ZFcw52II7IVJfUgVGHqoi1Uw2cUb+hgIjjSG3OtbyRWedm5r6nqWq8BNLOexI0VVFB6OMVYg==}
|
||||
dev: false
|
||||
|
||||
/@dydxprotocol/v4-client-js@0.40.3:
|
||||
resolution: {integrity: sha512-nMQd6tYisKDKIbfx0BGJSYEOSt+I+t5SsaBSmlgTdso+w2rCRO9vyxS+u6MF9h4w5FsVRfhHo3e6uTWX48WpGQ==}
|
||||
/@dydxprotocol/v4-client-js@1.0.0:
|
||||
resolution: {integrity: sha512-ehfHO+zQy795TcJRwtiawadFHZyh4HnpJNP26hCGsIjLE5q6LLHweQHpK/1N/sXU1PBOuQwc7iaJnFop6gYauQ==}
|
||||
dependencies:
|
||||
'@cosmjs/amino': 0.30.1
|
||||
'@cosmjs/encoding': 0.31.1
|
||||
@@ -1011,8 +1007,8 @@ packages:
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
/@dydxprotocol/v4-localization@0.1.32:
|
||||
resolution: {integrity: sha512-ZtXScxgFLjUt0Ag52RyFnQe9aEl1Aky5fp+vMGOBduVkFiCEMRMkSsSgg6grMdyw/x0AbnxOTRkhYoX2MsK9Ow==}
|
||||
/@dydxprotocol/v4-localization@1.0.0:
|
||||
resolution: {integrity: sha512-U7QjOlzpfSAVEchkNDB3xYRx7okzdeYi83XsnHwI+EHa2quFm/2Y2dhrBkT5fqpZmhcwxb/vG8AZmFSToOf9hA==}
|
||||
dev: false
|
||||
|
||||
/@dydxprotocol/v4-proto@0.4.1:
|
||||
@@ -14100,3 +14096,7 @@ packages:
|
||||
/zwitch@2.0.4:
|
||||
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||
dev: true
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
@@ -8,67 +8,43 @@
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"/": "/trade/*"
|
||||
"/": "/"
|
||||
},
|
||||
{
|
||||
"/": "/r/*"
|
||||
"/": "/#/"
|
||||
},
|
||||
{
|
||||
"/": "/portfolio/overview"
|
||||
"/": "*/markets"
|
||||
},
|
||||
{
|
||||
"/": "/portfolio/positions"
|
||||
"/": "*/trade"
|
||||
},
|
||||
{
|
||||
"/": "/portfolio/orders"
|
||||
"/": "*/trade/*"
|
||||
},
|
||||
{
|
||||
"/": "/portfolio/fees"
|
||||
"/": "*/portfolio"
|
||||
},
|
||||
{
|
||||
"/": "/portfolio/history"
|
||||
"/": "*/portfolio/overview"
|
||||
},
|
||||
{
|
||||
"/": "/portfolio/history/trades"
|
||||
"/": "*/portfolio/positions"
|
||||
},
|
||||
{
|
||||
"/": "/portfolio/history/transfers"
|
||||
"/": "*/portfolio/orders"
|
||||
},
|
||||
{
|
||||
"/": "/portfolio/history/funding"
|
||||
"/": "*/portfolio/fees"
|
||||
},
|
||||
{
|
||||
"/": "/rewards"
|
||||
"/": "*/portfolio/history/trades"
|
||||
},
|
||||
{
|
||||
"/": "/my-profile"
|
||||
"/": "*/portfolio/history/transfers"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/hedgies"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/competition"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/diamond"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/platinum"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/gold"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/silver"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/bronze"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/pnl-absolute"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/pnl-percent"
|
||||
"/": "*/portfolio/history/funding"
|
||||
},
|
||||
{
|
||||
"/": "/walletsegue",
|
||||
|
||||
+290
-85
@@ -22,10 +22,11 @@
|
||||
"dydxprotocol-staging-west",
|
||||
"dydxprotocol-testnet",
|
||||
"dydxprotocol-testnet-dydx",
|
||||
"dydxprotocol-testnet-dsrv",
|
||||
"dydxprotocol-testnet-nodefleet",
|
||||
"dydxprotocol-testnet-kingnodes",
|
||||
"dydxprotocol-testnet-liquify"
|
||||
"dydxprotocol-testnet-liquify",
|
||||
"dydxprotocol-testnet-polkachu",
|
||||
"dydxprotocol-testnet-bware"
|
||||
],
|
||||
"default": "dydxprotocol-testnet"
|
||||
}
|
||||
@@ -70,7 +71,19 @@
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
"privacy": "https://dydx.exchange/privacy",
|
||||
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
|
||||
"blogs": "https://www.dydx.foundation/blog"
|
||||
"blogs": "https://www.dydx.foundation/blog",
|
||||
"foundation": "https://www.dydx.foundation",
|
||||
"help": "https://help.dydx.exchange/",
|
||||
"initialMarginFractionLearnMore": "https://help.dydx.exchange/articles/5232637-maximum-position-sizes",
|
||||
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
|
||||
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
|
||||
"documentation": "https://docs.dydx.exchange/",
|
||||
"community": "https://discord.com/invite/dydx",
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
"walletconnect": {
|
||||
@@ -129,7 +142,19 @@
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
"privacy": "https://dydx.exchange/privacy",
|
||||
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
|
||||
"blogs": "https://www.dydx.foundation/blog"
|
||||
"blogs": "https://www.dydx.foundation/blog",
|
||||
"foundation": "https://www.dydx.foundation",
|
||||
"help": "https://help.dydx.exchange/",
|
||||
"initialMarginFractionLearnMore": "https://help.dydx.exchange/articles/5232637-maximum-position-sizes",
|
||||
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
|
||||
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
|
||||
"documentation": "https://docs.dydx.exchange/",
|
||||
"community": "https://discord.com/invite/dydx",
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
"walletconnect": {
|
||||
@@ -188,7 +213,19 @@
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
"privacy": "https://dydx.exchange/privacy",
|
||||
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
|
||||
"blogs": "https://www.dydx.foundation/blog"
|
||||
"blogs": "https://www.dydx.foundation/blog",
|
||||
"foundation": "https://www.dydx.foundation",
|
||||
"help": "https://help.dydx.exchange/",
|
||||
"initialMarginFractionLearnMore": "https://help.dydx.exchange/articles/5232637-maximum-position-sizes",
|
||||
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
|
||||
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
|
||||
"documentation": "https://docs.dydx.exchange/",
|
||||
"community": "https://discord.com/invite/dydx",
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
"walletconnect": {
|
||||
@@ -247,7 +284,19 @@
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
"privacy": "https://dydx.exchange/privacy",
|
||||
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
|
||||
"blogs": "https://www.dydx.foundation/blog"
|
||||
"blogs": "https://www.dydx.foundation/blog",
|
||||
"foundation": "https://www.dydx.foundation",
|
||||
"help": "https://help.dydx.exchange/",
|
||||
"initialMarginFractionLearnMore": "https://help.dydx.exchange/articles/5232637-maximum-position-sizes",
|
||||
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
|
||||
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
|
||||
"documentation": "https://docs.dydx.exchange/",
|
||||
"community": "https://discord.com/invite/dydx",
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
"walletconnect": {
|
||||
@@ -307,10 +356,20 @@
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
"privacy": "https://dydx.exchange/privacy",
|
||||
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
|
||||
"documentation": "https://v4-teacher.vercel.app/",
|
||||
"documentation": "https://docs.dydx.exchange/",
|
||||
"community": "https://discord.com/invite/dydx",
|
||||
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
|
||||
"blogs": "https://www.dydx.foundation/blog"
|
||||
"blogs": "https://www.dydx.foundation/blog",
|
||||
"help": "https://help.dydx.exchange",
|
||||
"foundation": "https://www.dydx.foundation",
|
||||
"initialMarginFractionLearnMore": "https://help.dydx.exchange/articles/5232637-maximum-position-sizes",
|
||||
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
|
||||
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
"walletconnect": {
|
||||
@@ -370,10 +429,20 @@
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
"privacy": "https://dydx.exchange/privacy",
|
||||
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
|
||||
"documentation": "https://v4-teacher.vercel.app/",
|
||||
"documentation": "https://docs.dydx.exchange/",
|
||||
"community": "https://discord.com/invite/dydx",
|
||||
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
|
||||
"blogs": "https://www.dydx.foundation/blog"
|
||||
"blogs": "https://www.dydx.foundation/blog",
|
||||
"help": "https://help.dydx.exchange",
|
||||
"foundation": "https://www.dydx.foundation",
|
||||
"initialMarginFractionLearnMore": "https://help.dydx.exchange/articles/5232637-maximum-position-sizes",
|
||||
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
|
||||
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
"walletconnect": {
|
||||
@@ -424,6 +493,8 @@
|
||||
}
|
||||
],
|
||||
"validators": [
|
||||
"https://dydx-testnet-full-rpc.public.blastapi.io/",
|
||||
"https://dydx-testnet-rpc.polkachu.com/",
|
||||
"https://dydx-testnet.nodefleet.org",
|
||||
"https://test-dydx.kingnodes.com",
|
||||
"https://dydx-rpc.liquify.com/api=8878132/dydx"
|
||||
@@ -435,10 +506,20 @@
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
"privacy": "https://dydx.exchange/privacy",
|
||||
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
|
||||
"documentation": "https://v4-teacher.vercel.app/",
|
||||
"documentation": "https://docs.dydx.exchange/",
|
||||
"community": "https://discord.com/invite/dydx",
|
||||
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
|
||||
"blogs": "https://www.dydx.foundation/blog"
|
||||
"blogs": "https://www.dydx.foundation/blog",
|
||||
"foundation": "https://www.dydx.foundation",
|
||||
"help": "https://help.dydx.exchange/",
|
||||
"initialMarginFractionLearnMore": "https://help.dydx.exchange/articles/5232637-maximum-position-sizes",
|
||||
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
|
||||
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
"walletconnect": {
|
||||
@@ -499,74 +580,20 @@
|
||||
"privacy": "https://dydx.exchange/privacy",
|
||||
"statusPage": "https://status.v4testnet.dydx.exchange/",
|
||||
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
|
||||
"documentation": "https://v4-teacher.vercel.app/",
|
||||
"documentation": "https://docs.dydx.exchange/",
|
||||
"community": "https://discord.com/invite/dydx",
|
||||
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
|
||||
"blogs": "https://www.dydx.foundation/blog"
|
||||
},
|
||||
"wallets": {
|
||||
"walletconnect": {
|
||||
"client": {
|
||||
"name": "dYdX v4",
|
||||
"description": "dYdX v4 App",
|
||||
"iconUrl": "/logos/dydx-x.png"
|
||||
},
|
||||
"v2": {
|
||||
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
|
||||
}
|
||||
},
|
||||
"walletSegue": {
|
||||
"callbackUrl": "/walletsegue"
|
||||
},
|
||||
"images": "/wallets/",
|
||||
"signTypedDataAction": "dYdX Chain Onboarding",
|
||||
"signTypedDataDomainName": "dYdX V4"
|
||||
}
|
||||
},
|
||||
"dydxprotocol-testnet-dsrv": {
|
||||
"name": "v4 Public Testnet/DSRV",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydx-testnet-4",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
"chain": {
|
||||
"name": "Dv4TNT",
|
||||
"denom": "adv4tnt",
|
||||
"decimals": 18,
|
||||
"image": "/currencies/dydx.png"
|
||||
},
|
||||
"usdc": {
|
||||
"name": "USDC",
|
||||
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
|
||||
"gasDenom": "uusdc",
|
||||
"decimals": 6,
|
||||
"image": "/currencies/usdc.png"
|
||||
}
|
||||
},
|
||||
"endpoints": {
|
||||
"indexers": [
|
||||
{
|
||||
"api": "https://dydx-testnet.imperator.co",
|
||||
"socket": "wss://dydx-testnet.imperator.co"
|
||||
}
|
||||
],
|
||||
"validators": [
|
||||
"https://dydx-testnet-archive.allthatnode.com:26657/XZvMM41hESf8PJrEQiTzbCOMVyFca79R"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.squidrouter.com",
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"links": {
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
"privacy": "https://dydx.exchange/privacy",
|
||||
"statusPage": "https://status.v4testnet.dydx.exchange/",
|
||||
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
|
||||
"documentation": "https://v4-teacher.vercel.app/",
|
||||
"community": "https://discord.com/invite/dydx",
|
||||
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
|
||||
"blogs": "https://www.dydx.foundation/blog"
|
||||
"blogs": "https://www.dydx.foundation/blog",
|
||||
"foundation": "https://www.dydx.foundation",
|
||||
"help": "https://help.dydx.exchange/",
|
||||
"initialMarginFractionLearnMore": "https://help.dydx.exchange/articles/5232637-maximum-position-sizes",
|
||||
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
|
||||
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
"walletconnect": {
|
||||
@@ -627,10 +654,20 @@
|
||||
"privacy": "https://dydx.exchange/privacy",
|
||||
"statusPage": "https://status.v4testnet.dydx.exchange/",
|
||||
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
|
||||
"documentation": "https://v4-teacher.vercel.app/",
|
||||
"documentation": "https://docs.dydx.exchange/",
|
||||
"community": "https://discord.com/invite/dydx",
|
||||
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
|
||||
"blogs": "https://www.dydx.foundation/blog"
|
||||
"blogs": "https://www.dydx.foundation/blog",
|
||||
"foundation": "https://www.dydx.foundation",
|
||||
"help": "https://help.dydx.exchange/",
|
||||
"initialMarginFractionLearnMore": "https://help.dydx.exchange/articles/5232637-maximum-position-sizes",
|
||||
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
|
||||
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
"walletconnect": {
|
||||
@@ -691,10 +728,20 @@
|
||||
"privacy": "https://dydx.exchange/privacy",
|
||||
"statusPage": "https://status.v4testnet.dydx.exchange/",
|
||||
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
|
||||
"documentation": "https://v4-teacher.vercel.app/",
|
||||
"documentation": "https://docs.dydx.exchange/",
|
||||
"community": "https://discord.com/invite/dydx",
|
||||
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
|
||||
"blogs": "https://www.dydx.foundation/blog"
|
||||
"blogs": "https://www.dydx.foundation/blog",
|
||||
"foundation": "https://www.dydx.foundation",
|
||||
"help": "https://help.dydx.exchange/",
|
||||
"initialMarginFractionLearnMore": "https://help.dydx.exchange/articles/5232637-maximum-position-sizes",
|
||||
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
|
||||
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
"walletconnect": {
|
||||
@@ -755,7 +802,81 @@
|
||||
"privacy": "https://dydx.exchange/privacy",
|
||||
"statusPage": "https://status.v4testnet.dydx.exchange/",
|
||||
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
|
||||
"documentation": "https://v4-teacher.vercel.app/",
|
||||
"documentation": "https://docs.dydx.exchange/",
|
||||
"community": "https://discord.com/invite/dydx",
|
||||
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
|
||||
"blogs": "https://www.dydx.foundation/blog",
|
||||
"foundation": "https://www.dydx.foundation",
|
||||
"help": "https://help.dydx.exchange/",
|
||||
"initialMarginFractionLearnMore": "https://help.dydx.exchange/articles/5232637-maximum-position-sizes",
|
||||
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
|
||||
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
|
||||
"governanceLearnMore": "https://help.dydx.exchange",
|
||||
"stakingLearnMore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnMore": "https://help.dydx.exchange",
|
||||
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
"walletconnect": {
|
||||
"client": {
|
||||
"name": "dYdX v4",
|
||||
"description": "dYdX v4 App",
|
||||
"iconUrl": "/logos/dydx-x.png"
|
||||
},
|
||||
"v2": {
|
||||
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
|
||||
}
|
||||
},
|
||||
"walletSegue": {
|
||||
"callbackUrl": "/walletsegue"
|
||||
},
|
||||
"images": "/wallets/",
|
||||
"signTypedDataAction": "dYdX Chain Onboarding",
|
||||
"signTypedDataDomainName": "dYdX V4"
|
||||
}
|
||||
},
|
||||
"dydxprotocol-testnet-polkachu": {
|
||||
"name": "v4 Public Testnet/Polkahcu",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydx-testnet-4",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
"chain": {
|
||||
"name": "Dv4TNT",
|
||||
"denom": "adv4tnt",
|
||||
"decimals": 18,
|
||||
"image": "/currencies/dydx.png"
|
||||
},
|
||||
"usdc": {
|
||||
"name": "USDC",
|
||||
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
|
||||
"gasDenom": "uusdc",
|
||||
"decimals": 6,
|
||||
"image": "/currencies/usdc.png"
|
||||
}
|
||||
},
|
||||
"endpoints": {
|
||||
"indexers": [
|
||||
{
|
||||
"api": "https://dydx-testnet.imperator.co",
|
||||
"socket": "wss://dydx-testnet.imperator.co"
|
||||
}
|
||||
],
|
||||
"validators": [
|
||||
"https://dydx-testnet-rpc.polkachu.com/"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.squidrouter.com",
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"links": {
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
"privacy": "https://dydx.exchange/privacy",
|
||||
"statusPage": "https://status.v4testnet.dydx.exchange/",
|
||||
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
|
||||
"documentation": "https://docs.dydx.exchange/",
|
||||
"community": "https://discord.com/invite/dydx",
|
||||
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
|
||||
"blogs": "https://www.dydx.foundation/blog"
|
||||
@@ -779,6 +900,80 @@
|
||||
"signTypedDataDomainName": "dYdX V4"
|
||||
}
|
||||
},
|
||||
"dydxprotocol-testnet-bware": {
|
||||
"name": "v4 Public Testnet/BWare",
|
||||
"ethereumChainId": "5",
|
||||
"dydxChainId": "dydx-testnet-4",
|
||||
"chainName": "dYdX Chain",
|
||||
"chainLogo": "dydx-chain.png",
|
||||
"isMainNet": false,
|
||||
"tokens": {
|
||||
"chain": {
|
||||
"name": "Dv4TNT",
|
||||
"denom": "adv4tnt",
|
||||
"decimals": 18,
|
||||
"image": "/currencies/dydx.png"
|
||||
},
|
||||
"usdc": {
|
||||
"name": "USDC",
|
||||
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
|
||||
"gasDenom": "uusdc",
|
||||
"decimals": 6,
|
||||
"image": "/currencies/usdc.png"
|
||||
}
|
||||
},
|
||||
"endpoints": {
|
||||
"indexers": [
|
||||
{
|
||||
"api": "https://dydx-testnet.imperator.co",
|
||||
"socket": "wss://dydx-testnet.imperator.co"
|
||||
}
|
||||
],
|
||||
"validators": [
|
||||
"https://dydx-testnet-full-rpc.public.blastapi.io/"
|
||||
],
|
||||
"0xsquid": "https://testnet.api.squidrouter.com",
|
||||
"faucet": "https://faucet.v4testnet.dydx.exchange"
|
||||
},
|
||||
"links": {
|
||||
"tos": "https://dydx.exchange/v4-terms",
|
||||
"privacy": "https://dydx.exchange/privacy",
|
||||
"statusPage": "https://status.v4testnet.dydx.exchange/",
|
||||
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
|
||||
"documentation": "https://docs.dydx.exchange/",
|
||||
"community": "https://discord.com/invite/dydx",
|
||||
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
|
||||
"blogs": "https://www.dydx.foundation/blog",
|
||||
"foundation": "https://www.dydx.foundation",
|
||||
"help": "https://help.dydx.exchange/",
|
||||
"initialMarginFractionLearnmore": "https://help.dydx.exchange/articles/5232637-maximum-position-sizes",
|
||||
"reduceOnlyLearnmore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
|
||||
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
|
||||
"governanceLearnmore": "https://help.dydx.exchange",
|
||||
"stakingLearnmore": "https://help.dydx.exchange",
|
||||
"keplrDashboard": "https://testnet.keplr.app/",
|
||||
"accountExportLearnmore": "https://help.dydx.exchange",
|
||||
"walletLearnmore": "https://www.dydx.academy/video/defi-wallet"
|
||||
},
|
||||
"wallets": {
|
||||
"walletconnect": {
|
||||
"client": {
|
||||
"name": "dYdX v4",
|
||||
"description": "dYdX v4 App",
|
||||
"iconUrl": "/logos/dydx-x.png"
|
||||
},
|
||||
"v2": {
|
||||
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
|
||||
}
|
||||
},
|
||||
"walletSegue": {
|
||||
"callbackUrl": "/walletsegue"
|
||||
},
|
||||
"images": "/wallets/",
|
||||
"signTypedDataAction": "dYdX Chain Onboarding",
|
||||
"signTypedDataDomainName": "dYdX V4"
|
||||
}
|
||||
},
|
||||
"dydxprotocol-mainnet": {
|
||||
"name": "v4",
|
||||
"ethereumChainId": "1",
|
||||
@@ -819,10 +1014,20 @@
|
||||
"tos": "[HTTP link to TOS]",
|
||||
"privacy": "[HTTP link to Privacy Policy]",
|
||||
"mintscan": "[HTTP link to Mintscan, with {tx_hash} placeholder]",
|
||||
"mintscanBase": "[HTTP link to TOS mintsacn base url]",
|
||||
"feedback": "[HTTP link to feedback form, can be null]",
|
||||
"blogs": "[HTTP link to blogs, can be null]",
|
||||
"foundation": "[HTTP link to foundation, can be null]",
|
||||
"initialMarginFractionLearnMore": "[HTTP link to initial margin fraction learn more, can be null]",
|
||||
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
|
||||
"documentation": "[HTTP link to documentation, can be null]",
|
||||
"community": "[HTTP link to community, can be null]",
|
||||
"feedback": "[HTTP link to feedback form, can be null]",
|
||||
"blogs": "[HTTP link to blogs, can be null]"
|
||||
"help": "[HTTP link to help page, can be null]",
|
||||
"governanceLearnMore": "[HTTP link to governance learn more, can be null]",
|
||||
"stakingLearnMore": "[HTTP link to staking learn more, can be null]",
|
||||
"keplrDashboard": "[HTTP link to keplr dashboard, can be null]",
|
||||
"accountExportLearnMore": "[HTTP link to account export learn more, can be null]",
|
||||
"walletLearnMore": "[HTTP link to wallet learn more, can be null]"
|
||||
},
|
||||
"wallets": {
|
||||
"walletconnect": {
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"iosMinVersion": "0",
|
||||
"imageUrl": "",
|
||||
"encoding": "=\"#%/<>?@\\^`{|}:&",
|
||||
"androidPackage": "io.metamask",
|
||||
"connections": [
|
||||
{
|
||||
"type": "walletConnectV2",
|
||||
@@ -98,6 +99,7 @@
|
||||
"iosMinVersion": "1.8.0",
|
||||
"imageUrl": "",
|
||||
"encoding": "=\"#%/<>?@\\^`{|}:&",
|
||||
"androidPackage": "org.toshi",
|
||||
"connections": [
|
||||
{
|
||||
"type": "walletSegue",
|
||||
@@ -155,6 +157,7 @@
|
||||
"comment": "imToken",
|
||||
"iosMinVersion": "0",
|
||||
"imageUrl": "",
|
||||
"androidPackage": "im.token.app",
|
||||
"connections": [
|
||||
{
|
||||
"type": "walletConnectV2",
|
||||
@@ -214,6 +217,7 @@
|
||||
"iosMinVersion": "0",
|
||||
"encoding": "=\"#%/<>?@\\^`{|}:&",
|
||||
"imageUrl": "",
|
||||
"androidPackage": "com.wallet.crypto.trustapp",
|
||||
"connections": [
|
||||
{
|
||||
"type": "walletConnectV2",
|
||||
@@ -271,6 +275,7 @@
|
||||
"iosMinVersion": "0",
|
||||
"encoding": "=\"#%/<>?@\\^`{|}:&",
|
||||
"imageUrl": "",
|
||||
"androidPackage": "io.zerion.android",
|
||||
"connections": [
|
||||
{
|
||||
"type": "walletConnectV2",
|
||||
@@ -327,6 +332,7 @@
|
||||
"comment": "TokenPocket",
|
||||
"iosMinVersion": "0",
|
||||
"imageUrl": "",
|
||||
"androidPackage": "vip.mytokenpocket",
|
||||
"connections": [
|
||||
{
|
||||
"type": "walletConnectV2",
|
||||
@@ -383,6 +389,7 @@
|
||||
"comment": "Rainbow",
|
||||
"iosMinVersion": "0",
|
||||
"encoding": "=\"#%/<>?@\\^`{|}:&",
|
||||
"androidPackage": "me.rainbow",
|
||||
"backlinked": true,
|
||||
"imageUrl": "",
|
||||
"connections": [
|
||||
@@ -442,6 +449,7 @@
|
||||
"comment": "1inch",
|
||||
"iosMinVersion": "0",
|
||||
"encoding": "=\"#%/<>?@\\^`{|}:&",
|
||||
"androidPackage": "io.oneinch.android",
|
||||
"imageUrl": "",
|
||||
"connections": [
|
||||
{
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
## App Integrations
|
||||
### Amplitude
|
||||
Analytics service used to collect data to help the dYdX Product team make informed product decisions.
|
||||
|
||||
<b>To use with dydxprotocol/v4-web:</b>
|
||||
1. Amplitude account with API key.
|
||||
2. Add API key in Github > Secrets and Variables > Actions as `AMPLITUDE_API_KEY`
|
||||
3. In your deploy scripts add `pnpm run build:inject-amplitude` after your pnpm build / vite build command.
|
||||
|
||||
### Bugsnag
|
||||
Error handling service used to collect handled/unhandled errors within the app. The information collected is used to help debug and alert the engineering team of crashes/unhandled errors within the app to improve stability.
|
||||
|
||||
<b>To use with dydxprotocol/v4-web:</b>
|
||||
1. Bugsnag account with API key.
|
||||
2. Add API key in Github > Secrets and Variables > Actions as `BUGSNAG_API_KEY`
|
||||
3. In your deploy scripts add `pnpm run build:inject-bugsnag` after your pnpm build / vite build command.
|
||||
4. If you are using with the Amplitude deployment scripts, your build command may look like the following: `pnpm build && pnpm run build:inject-amplitude && pnpm run build:inject-bugsnag`
|
||||
|
||||
### StatusPage
|
||||
Service used to inform users of any status updates to our platform. These status updates are manually set and updated by the On Call engineers.
|
||||
|
||||
<b>To use with dydxprotocol/v4-web:</b>
|
||||
1. StatusPage account and script URI
|
||||
2. Add API key in Github > Secrets and Variables > Actions as `STATUS_PAGE_SCRIPT_URI`
|
||||
3. In your deploy scripts add `pnpm run build:inject-statuspage` after your pnpm build / vite build command.
|
||||
4. If you are using with the Amplitude deployment scripts, your build command may look like the following: `pnpm build && pnpm run build:inject-amplitude && pnpm run build:inject-statuspage`
|
||||
|
||||
### Intercom
|
||||
Service used for live customer support (chat/inbox) as well as home for Help Articles.
|
||||
|
||||
<b>To use with dydxprotocol/v4-web:</b>
|
||||
1. Create Intercom account
|
||||
2. In Intercom UI
|
||||
Getting started > Set up Messenger > will give you your API Key on Step 2
|
||||
Customize Intercom Messenger by adding logo and brand colors
|
||||
3. Add API key in Github > Secrets and Variables > Actions as `INTERCOM_APP_ID`
|
||||
4. In your deploy scripts add `pnpm run build:inject-intercom` after your pnpm build / vite build command.
|
||||
5. If you are using with the Amplitude deployment scripts, your build command may look like the following: `pnpm build && pnpm run build:inject-amplitude && pnpm run build:inject-intercom`
|
||||
@@ -80,7 +80,7 @@ Styled.TriggerIcon = styled.span`
|
||||
width: var(--trigger-icon-width);
|
||||
|
||||
display: inline-flex;
|
||||
transition: transform 0.3s var(--ease-out-expo);
|
||||
transition: rotate 0.3s var(--ease-out-expo);
|
||||
color: var(--trigger-icon-color);
|
||||
|
||||
${Styled.Trigger}[data-state='open'] & {
|
||||
|
||||
@@ -225,6 +225,7 @@ Styled.CollapsibleContent = styled(CollapsibleContent)`
|
||||
`;
|
||||
|
||||
Styled.IconButton = styled(IconButton)`
|
||||
--button-icon-size: 1em;
|
||||
${Styled.CollapsibleRoot}[data-state='closed'] & {
|
||||
rotate: -0.5turn;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ type PickDialogProps = Pick<
|
||||
| 'slotHeaderInner'
|
||||
| 'slotTrigger'
|
||||
| 'slotFooter'
|
||||
| 'preventClose'
|
||||
>;
|
||||
|
||||
export const ComboboxDialogMenu = <
|
||||
@@ -57,6 +58,7 @@ export const ComboboxDialogMenu = <
|
||||
children,
|
||||
|
||||
placement = DialogPlacement.Default,
|
||||
preventClose,
|
||||
className,
|
||||
}: ElementProps<MenuItemValue, MenuGroupValue> &
|
||||
PickComboxMenuProps<MenuItemValue, MenuGroupValue> &
|
||||
@@ -72,6 +74,7 @@ export const ComboboxDialogMenu = <
|
||||
slotTrigger={slotTrigger}
|
||||
slotFooter={slotFooter}
|
||||
placement={placement}
|
||||
preventClose={preventClose}
|
||||
className={className}
|
||||
>
|
||||
<Styled.ComboboxMenu
|
||||
|
||||
@@ -33,9 +33,11 @@ export const DropdownHeaderMenu = <MenuItemValue extends string>({
|
||||
|
||||
return (
|
||||
<Root>
|
||||
<Styled.Trigger className={className}>
|
||||
<Styled.Trigger className={className} asChild>
|
||||
<div>
|
||||
{children}
|
||||
<Styled.DropdownIconButton iconName={IconName.Caret} isToggle />
|
||||
</div>
|
||||
</Styled.Trigger>
|
||||
<Portal>
|
||||
<Styled.Content
|
||||
|
||||
+10
-1
@@ -24,9 +24,10 @@ import {
|
||||
CommentIcon,
|
||||
CopyIcon,
|
||||
CubeIcon,
|
||||
DepositIcon,
|
||||
DepthChartIcon,
|
||||
DiscordIcon,
|
||||
EtherscanIcon,
|
||||
DepthChartIcon,
|
||||
ExportKeysIcon,
|
||||
FeedbackIcon,
|
||||
FileIcon,
|
||||
@@ -40,6 +41,7 @@ import {
|
||||
LockIcon,
|
||||
LogoShortIcon,
|
||||
MenuIcon,
|
||||
MigrateIcon,
|
||||
MintscanIcon,
|
||||
OrderbookIcon,
|
||||
OrderCanceledIcon,
|
||||
@@ -68,6 +70,7 @@ import {
|
||||
WarningIcon,
|
||||
WebsiteIcon,
|
||||
WhitepaperIcon,
|
||||
WithdrawIcon,
|
||||
} from '@/icons';
|
||||
|
||||
export enum IconName {
|
||||
@@ -92,6 +95,7 @@ export enum IconName {
|
||||
Comment = 'Comment',
|
||||
Copy = 'Copy',
|
||||
Cube = 'Cube',
|
||||
Deposit = 'Deposit',
|
||||
DepthChart = 'DepthChart',
|
||||
Discord = 'Discord',
|
||||
Etherscan = 'Etherscan',
|
||||
@@ -108,6 +112,7 @@ export enum IconName {
|
||||
Lock = 'Lock',
|
||||
LogoShort = 'LogoShort',
|
||||
Menu = 'Menu',
|
||||
Migrate = 'Migrate',
|
||||
Mintscan = 'Mintscan',
|
||||
Onboarding = 'Onboarding',
|
||||
Orderbook = 'OrderbookIcon',
|
||||
@@ -137,6 +142,7 @@ export enum IconName {
|
||||
Warning = 'Warning',
|
||||
Website = 'Website',
|
||||
Whitepaper = 'Whitepaper',
|
||||
Withdraw = 'Withdraw',
|
||||
}
|
||||
|
||||
const icons = {
|
||||
@@ -161,6 +167,7 @@ const icons = {
|
||||
[IconName.Comment]: CommentIcon,
|
||||
[IconName.Copy]: CopyIcon,
|
||||
[IconName.Cube]: CubeIcon,
|
||||
[IconName.Deposit]: DepositIcon,
|
||||
[IconName.DepthChart]: DepthChartIcon,
|
||||
[IconName.Discord]: DiscordIcon,
|
||||
[IconName.Etherscan]: EtherscanIcon,
|
||||
@@ -177,6 +184,7 @@ const icons = {
|
||||
[IconName.Lock]: LockIcon,
|
||||
[IconName.LogoShort]: LogoShortIcon,
|
||||
[IconName.Menu]: MenuIcon,
|
||||
[IconName.Migrate]: MigrateIcon,
|
||||
[IconName.Mintscan]: MintscanIcon,
|
||||
[IconName.Orderbook]: OrderbookIcon,
|
||||
[IconName.OrderCanceled]: OrderCanceledIcon,
|
||||
@@ -205,6 +213,7 @@ const icons = {
|
||||
[IconName.Warning]: WarningIcon,
|
||||
[IconName.Website]: WebsiteIcon,
|
||||
[IconName.Whitepaper]: WhitepaperIcon,
|
||||
[IconName.Withdraw]: WithdrawIcon,
|
||||
} as Record<IconName, ElementType>;
|
||||
|
||||
type ElementProps = {
|
||||
|
||||
@@ -67,6 +67,7 @@ const Styled: Record<string, AnyStyledComponent> = {};
|
||||
const buttonMixin = css`
|
||||
// Params
|
||||
--button-icon-size: 1.125em;
|
||||
--button-padding: 0;
|
||||
|
||||
// Rules
|
||||
> * {
|
||||
|
||||
@@ -71,7 +71,7 @@ const NavItem = forwardRef(
|
||||
)}
|
||||
</span>
|
||||
{slotAfter}
|
||||
{subitems?.length && <Styled.Icon iconName={IconName.Caret} />}
|
||||
{subitems?.length && <Styled.Icon iconName={IconName.Triangle} />}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -525,6 +525,9 @@ Styled.NavItem = styled(NavItem)<{ orientation: 'horizontal' | 'vertical' }>`
|
||||
`;
|
||||
|
||||
Styled.Icon = styled(Icon)`
|
||||
font-size: 0.375em;
|
||||
transition: rotate 0.3s var(--ease-out-expo);
|
||||
|
||||
${Styled.List}[data-orientation="menu"] & {
|
||||
rotate: -0.25turn;
|
||||
}
|
||||
|
||||
+52
-27
@@ -8,6 +8,7 @@ import { layoutMixins } from '@/styles/layoutMixins';
|
||||
type PanelProps = {
|
||||
slotHeaderContent?: string;
|
||||
slotHeader?: React.ReactNode;
|
||||
slotRight?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
href?: string;
|
||||
onHeaderClick?: () => void;
|
||||
@@ -22,6 +23,7 @@ type PanelStyleProps = {
|
||||
export const Panel = ({
|
||||
slotHeaderContent,
|
||||
slotHeader,
|
||||
slotRight,
|
||||
children,
|
||||
href,
|
||||
onHeaderClick,
|
||||
@@ -29,56 +31,80 @@ export const Panel = ({
|
||||
hasSeparator,
|
||||
className,
|
||||
}: PanelProps & PanelStyleProps) => (
|
||||
<Styled.Panel onClick={onClick}>
|
||||
{href ? (
|
||||
<Link to={href}>
|
||||
{slotHeader ? (
|
||||
slotHeader
|
||||
) : (
|
||||
<Styled.Panel onClick={onClick} className={className}>
|
||||
<Styled.Left>
|
||||
{href ? (
|
||||
<Link to={href}>
|
||||
{slotHeader ? (
|
||||
slotHeader
|
||||
) : (
|
||||
<Styled.Header role="button" onClick={onHeaderClick} hasSeparator={hasSeparator}>
|
||||
{slotHeaderContent}
|
||||
<Styled.Icon iconName={IconName.ChevronRight} />
|
||||
</Styled.Header>
|
||||
)}
|
||||
</Link>
|
||||
) : slotHeader ? (
|
||||
slotHeader
|
||||
) : (
|
||||
slotHeaderContent && (
|
||||
<Styled.Header role="button" onClick={onHeaderClick} hasSeparator={hasSeparator}>
|
||||
{slotHeaderContent}
|
||||
<Styled.Icon iconName={IconName.ChevronRight} />
|
||||
</Styled.Header>
|
||||
)}
|
||||
</Link>
|
||||
) : slotHeader ? (
|
||||
slotHeader
|
||||
) : (
|
||||
slotHeaderContent && (
|
||||
<Styled.Header role="button" onClick={onHeaderClick} hasSeparator={hasSeparator}>
|
||||
{slotHeaderContent}
|
||||
</Styled.Header>
|
||||
)
|
||||
)}
|
||||
<Styled.Content className={className}>{children}</Styled.Content>
|
||||
)
|
||||
)}
|
||||
<Styled.Content>{children}</Styled.Content>
|
||||
</Styled.Left>
|
||||
{slotRight}
|
||||
</Styled.Panel>
|
||||
);
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Panel = styled.section`
|
||||
${layoutMixins.flexColumn}
|
||||
Styled.Panel = styled.section<{ onClick?: () => void }>`
|
||||
--panel-paddingY: 1rem;
|
||||
--panel-paddingX: 1rem;
|
||||
--panel-content-paddingY: var(--panel-paddingY);
|
||||
--panel-content-paddingX: var(--panel-paddingX);
|
||||
|
||||
${layoutMixins.row}
|
||||
|
||||
background-color: var(--color-layer-3);
|
||||
border-radius: 0.875rem;
|
||||
|
||||
${({ onClick }) =>
|
||||
onClick &&
|
||||
css`
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
button:not(:disabled) {
|
||||
color: var(--button-hover-textColor);
|
||||
filter: var(--button-hover-filter);
|
||||
}
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
Styled.Left = styled.div`
|
||||
${layoutMixins.flexColumn}
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
Styled.Header = styled.header<{ hasSeparator?: boolean }>`
|
||||
${layoutMixins.spacedRow}
|
||||
|
||||
padding: 0.875rem 1rem 0.625rem;
|
||||
font-size: 0.875rem;
|
||||
padding: var(--panel-paddingY) var(--panel-paddingX);
|
||||
|
||||
${({ hasSeparator }) =>
|
||||
hasSeparator &&
|
||||
css`
|
||||
padding-bottom: 0.625rem;
|
||||
box-shadow: 0 var(--border-width) var(--border-color);
|
||||
`}
|
||||
`;
|
||||
|
||||
Styled.Icon = styled(Icon)`
|
||||
color: var(--color-text-0);
|
||||
|
||||
font-size: 0.625rem;
|
||||
`;
|
||||
|
||||
@@ -86,6 +112,5 @@ Styled.Content = styled.div`
|
||||
${layoutMixins.scrollArea}
|
||||
${layoutMixins.stickyArea0}
|
||||
--stickyArea0-background: transparent;
|
||||
|
||||
padding: 0.5rem 1rem;
|
||||
padding: var(--panel-content-paddingY) var(--panel-content-paddingX);
|
||||
`;
|
||||
|
||||
@@ -7,13 +7,15 @@ import { ButtonShape, ButtonSize } from '@/constants/buttons';
|
||||
import { useBreakpoints } from '@/hooks';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { type BaseButtonProps } from '@/components/BaseButton';
|
||||
import { type BaseButtonProps } from '@/components/BaseButton';
|
||||
import { ToggleButton } from '@/components/ToggleButton';
|
||||
|
||||
type ElementProps<MenuItemValue extends string> = {
|
||||
items: MenuItem<MenuItemValue>[];
|
||||
value: MenuItemValue;
|
||||
onValueChange: (value: MenuItemValue) => void;
|
||||
onValueChange: (value: any) => void;
|
||||
onInteraction?: () => void;
|
||||
ensureSelected?: boolean;
|
||||
};
|
||||
|
||||
type StyleProps = {
|
||||
@@ -25,10 +27,14 @@ export const ToggleGroup = forwardRef(
|
||||
{
|
||||
items,
|
||||
value,
|
||||
ensureSelected = true,
|
||||
onValueChange,
|
||||
onInteraction,
|
||||
|
||||
className,
|
||||
size,
|
||||
shape = ButtonShape.Pill,
|
||||
|
||||
...buttonProps
|
||||
}: ElementProps<MenuItemValue> & StyleProps & BaseButtonProps,
|
||||
ref: Ref<HTMLDivElement>
|
||||
@@ -40,12 +46,17 @@ export const ToggleGroup = forwardRef(
|
||||
ref={ref}
|
||||
type="single"
|
||||
value={value}
|
||||
onValueChange={onValueChange}
|
||||
onValueChange={(newValue: MenuItemValue) => {
|
||||
if ((ensureSelected && newValue) || !ensureSelected) {
|
||||
onValueChange(newValue);
|
||||
}
|
||||
onInteraction?.();
|
||||
}}
|
||||
className={className}
|
||||
loop
|
||||
>
|
||||
{items.map((item) => (
|
||||
<Item key={item.value} value={item.value} aria-label={item.label} asChild>
|
||||
<Item key={item.value} value={item.value} asChild>
|
||||
<ToggleButton
|
||||
size={size ? size : isTablet ? ButtonSize.Small : ButtonSize.XSmall}
|
||||
shape={shape}
|
||||
|
||||
@@ -2,11 +2,13 @@ import type { ReactNode } from 'react';
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
import { Content, Portal, Provider, Root, Trigger, Arrow } from '@radix-ui/react-tooltip';
|
||||
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { tooltipStrings } from '@/constants/tooltips';
|
||||
|
||||
import { useStringGetter } from '@/hooks';
|
||||
import { useStringGetter, useURLConfigs } from '@/hooks';
|
||||
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
import { Link } from '@/components/Link';
|
||||
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import { popoverMixins } from '@/styles/popoverMixins';
|
||||
@@ -38,20 +40,24 @@ export const WithTooltip = ({
|
||||
slotTooltip,
|
||||
}: ElementProps & StyleProps) => {
|
||||
const stringGetter = useStringGetter();
|
||||
const urlConfigs = useURLConfigs();
|
||||
|
||||
const getTooltipStrings = tooltip && tooltipStrings[tooltip];
|
||||
if (!getTooltipStrings && !tooltipString && !slotTooltip) return <>{children}</>;
|
||||
|
||||
let tooltipTitle;
|
||||
let tooltipBody;
|
||||
let tooltipLearnMore;
|
||||
|
||||
if (getTooltipStrings) {
|
||||
const { title, body } = getTooltipStrings({
|
||||
const { title, body, learnMoreLink } = getTooltipStrings({
|
||||
stringGetter,
|
||||
stringParams,
|
||||
urlConfigs,
|
||||
});
|
||||
tooltipTitle = title;
|
||||
tooltipBody = body;
|
||||
tooltipLearnMore = learnMoreLink;
|
||||
} else {
|
||||
tooltipBody = tooltipString;
|
||||
}
|
||||
@@ -72,6 +78,13 @@ export const WithTooltip = ({
|
||||
<dl>
|
||||
{tooltipTitle && <dt>{tooltipTitle}</dt>}
|
||||
{tooltipBody && <dd>{tooltipBody}</dd>}
|
||||
{tooltipLearnMore && (
|
||||
<dd>
|
||||
<Styled.LearnMore href={tooltipLearnMore}>
|
||||
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
|
||||
</Styled.LearnMore>
|
||||
</dd>
|
||||
)}
|
||||
<Styled.Arrow />
|
||||
</dl>
|
||||
)}
|
||||
@@ -142,3 +155,7 @@ Styled.Arrow = styled(Arrow)`
|
||||
Styled.Icon = styled(Icon)`
|
||||
color: var(--color-text-0);
|
||||
`;
|
||||
|
||||
Styled.LearnMore = styled(Link)`
|
||||
--link-color: var(--color-accent);
|
||||
`;
|
||||
|
||||
@@ -8,6 +8,8 @@ import {
|
||||
WARNINGS_STRING_KEYS,
|
||||
} from '@dydxprotocol/v4-localization';
|
||||
|
||||
import { type LinksConfigs } from '@/hooks/useURLConfigs';
|
||||
|
||||
export { TOOLTIP_STRING_KEYS } from '@dydxprotocol/v4-localization';
|
||||
|
||||
export enum SupportedLocales {
|
||||
@@ -79,9 +81,11 @@ export type TooltipStrings = {
|
||||
[key: string]: ({
|
||||
stringGetter,
|
||||
stringParams,
|
||||
urlConfigs,
|
||||
}: {
|
||||
stringGetter: StringGetterFunction;
|
||||
stringParams?: any;
|
||||
urlConfigs?: LinksConfigs;
|
||||
}) => {
|
||||
title: string;
|
||||
body: string;
|
||||
|
||||
@@ -37,7 +37,3 @@ export const HISTORY_ROUTE = `${AppRoute.Portfolio}/${PortfolioRoute.History}/:s
|
||||
export const DEFAULT_TRADE_ROUTE = `${AppRoute.Trade}/${DEFAULT_MARKETID}`;
|
||||
export const SETTINGS_ROUTE = `${AppRoute.Settings}/*`;
|
||||
export const DEFAULT_DOCUMENT_TITLE = 'dYdX';
|
||||
|
||||
export enum ExternalLink {
|
||||
Foundation = 'https://dydx.foundation',
|
||||
}
|
||||
|
||||
@@ -57,10 +57,10 @@ export const tradeTooltips: TooltipStrings = {
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.INDEX_PRICE_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.INDEX_PRICE_BODY }),
|
||||
}),
|
||||
'initial-margin-fraction': ({ stringGetter }) => ({
|
||||
'initial-margin-fraction': ({ stringGetter, urlConfigs }) => ({
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.INITIAL_MARGIN_FRACTION_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.INITIAL_MARGIN_FRACTION_BODY }),
|
||||
learnMoreLink: 'https://help.dydx.exchange/articles/5232637-maximum-position-sizes',
|
||||
learnMoreLink: urlConfigs?.initialMarginFractionLearnMore,
|
||||
}),
|
||||
'initial-stop': ({ stringGetter }) => ({
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.INITIAL_STOP_TITLE }),
|
||||
@@ -146,10 +146,10 @@ export const tradeTooltips: TooltipStrings = {
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.REALIZED_PNL_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.REALIZED_PNL_BODY }),
|
||||
}),
|
||||
'reduce-only': ({ stringGetter }) => ({
|
||||
'reduce-only': ({ stringGetter, urlConfigs }) => ({
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.REDUCE_ONLY_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.REDUCE_ONLY_BODY }),
|
||||
learnMoreLink: 'https://help.dydx.exchange/articles/6345793-reduce-only-orders',
|
||||
learnMoreLink: urlConfigs?.reduceOnlyLearnMore,
|
||||
}),
|
||||
spread: () => ({
|
||||
title: 'Spread',
|
||||
|
||||
@@ -22,6 +22,7 @@ import { useShouldShowFooter } from './useShouldShowFooter';
|
||||
import { useSelectedNetwork } from './useSelectedNetwork';
|
||||
import { useStringGetter } from './useStringGetter';
|
||||
import { useSubaccount } from './useSubaccount';
|
||||
import { useURLConfigs } from './useURLConfigs';
|
||||
|
||||
export {
|
||||
useApiState,
|
||||
@@ -48,4 +49,5 @@ export {
|
||||
useSelectedNetwork,
|
||||
useStringGetter,
|
||||
useSubaccount,
|
||||
useURLConfigs,
|
||||
};
|
||||
|
||||
@@ -6,8 +6,10 @@ import { MarketFilters, MARKET_FILTER_LABELS, type MarketData } from '@/constant
|
||||
import { getAssets } from '@/state/assetsSelectors';
|
||||
import { getPerpetualMarkets } from '@/state/perpetualsSelectors';
|
||||
|
||||
import { isTruthy } from '@/lib/isTruthy';
|
||||
|
||||
const filterFunctions = {
|
||||
[MarketFilters.ALL]: (market: MarketData) => true,
|
||||
[MarketFilters.ALL]: () => true,
|
||||
[MarketFilters.LAYER_1]: (market: MarketData) => {
|
||||
return market.asset.tags?.toArray().includes('Layer 1');
|
||||
},
|
||||
@@ -28,13 +30,15 @@ export const useMarketsData = (
|
||||
const allAssets = useSelector(getAssets, shallowEqual) || {};
|
||||
|
||||
const markets = useMemo(() => {
|
||||
return Object.values(allPerpetualMarkets).map((marketData) => ({
|
||||
asset: allAssets[marketData.assetId],
|
||||
tickSizeDecimals: marketData.configs?.tickSizeDecimals,
|
||||
...marketData,
|
||||
...marketData.perpetual,
|
||||
...marketData.configs,
|
||||
})) as MarketData[];
|
||||
return Object.values(allPerpetualMarkets)
|
||||
.filter(isTruthy)
|
||||
.map((marketData) => ({
|
||||
asset: allAssets[marketData.assetId],
|
||||
tickSizeDecimals: marketData.configs?.tickSizeDecimals,
|
||||
...marketData,
|
||||
...marketData.perpetual,
|
||||
...marketData.configs,
|
||||
})) as MarketData[];
|
||||
}, [allPerpetualMarkets, allAssets]);
|
||||
|
||||
const filteredMarkets = useMemo(() => {
|
||||
|
||||
@@ -313,11 +313,6 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
||||
placeOrderParams = abacusStateManager.placeOrder(callback);
|
||||
}
|
||||
|
||||
track(AnalyticsEvent.TradePlaceOrder, {
|
||||
...placeOrderParams,
|
||||
isClosePosition,
|
||||
} as HumanReadablePlaceOrderPayload & { isClosePosition: boolean });
|
||||
|
||||
return placeOrderParams;
|
||||
},
|
||||
[subaccountClient]
|
||||
@@ -346,7 +341,6 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
||||
}) => {
|
||||
const callback = (success: boolean, parsingError?: Nullable<ParsingError>) => {
|
||||
if (success) {
|
||||
track(AnalyticsEvent.TradeCancelOrder);
|
||||
onSuccess?.();
|
||||
} else {
|
||||
onError?.({ errorStringKey: parsingError?.stringKey });
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
|
||||
import { useSelectedNetwork } from '@/hooks';
|
||||
|
||||
const FALLBACK_URL = 'https://help.dydx.exchange/';
|
||||
|
||||
export interface LinksConfigs {
|
||||
tos: string,
|
||||
privacy: string,
|
||||
mintscan: string,
|
||||
mintscanBase: string,
|
||||
feedback?: string,
|
||||
help?: string,
|
||||
blogs?: string,
|
||||
foundation?: string,
|
||||
initialMarginFractionLearnMore?: string,
|
||||
reduceOnlyLearnMore?: string,
|
||||
documentation?: string,
|
||||
community?: string,
|
||||
governanceLearnMore?: string,
|
||||
stakingLearnMore?: string,
|
||||
keplrDashboard?: string,
|
||||
accountExportLearnMore?: string,
|
||||
walletLearnMore?: string
|
||||
}
|
||||
|
||||
export const useURLConfigs = (): LinksConfigs => {
|
||||
const { selectedNetwork } = useSelectedNetwork();
|
||||
const linksConfigs = ENVIRONMENT_CONFIG_MAP[selectedNetwork].links as LinksConfigs;
|
||||
|
||||
return {
|
||||
tos: linksConfigs.tos,
|
||||
privacy: linksConfigs.privacy,
|
||||
mintscan: linksConfigs.mintscan,
|
||||
mintscanBase: linksConfigs.mintscanBase,
|
||||
feedback: linksConfigs.feedback || FALLBACK_URL,
|
||||
help: linksConfigs.help || FALLBACK_URL,
|
||||
blogs: linksConfigs.blogs || FALLBACK_URL,
|
||||
foundation: linksConfigs.foundation || FALLBACK_URL,
|
||||
initialMarginFractionLearnMore: linksConfigs.initialMarginFractionLearnMore || FALLBACK_URL,
|
||||
reduceOnlyLearnMore: linksConfigs.reduceOnlyLearnMore || FALLBACK_URL,
|
||||
documentation: linksConfigs.documentation || FALLBACK_URL,
|
||||
community: linksConfigs.community || FALLBACK_URL,
|
||||
governanceLearnMore: linksConfigs.governanceLearnMore || FALLBACK_URL,
|
||||
stakingLearnMore: linksConfigs.stakingLearnMore || FALLBACK_URL,
|
||||
keplrDashboard: linksConfigs.keplrDashboard || FALLBACK_URL,
|
||||
accountExportLearnMore: linksConfigs.accountExportLearnMore || FALLBACK_URL,
|
||||
walletLearnMore: linksConfigs.walletLearnMore || FALLBACK_URL,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
|
||||
<g clip-path="url(#clip0_18204_36775)">
|
||||
<path d="M13.2426 6.25736C14.0817 7.09647 14.6531 8.16557 14.8846 9.32946C15.1162 10.4933 14.9973 11.6997 14.5432 12.7961C14.0891 13.8925 13.32 14.8295 12.3334 15.4888C11.3467 16.1481 10.1866 16.5 8.99993 16.5C7.81324 16.5 6.6532 16.1481 5.66651 15.4888C4.67981 14.8295 3.91078 13.8925 3.45665 12.7961C3.00253 11.6997 2.88371 10.4933 3.11522 9.32946C3.34673 8.16557 3.91817 7.09647 4.75729 6.25736" stroke="#C8C7D8" stroke-width="1.35" stroke-linecap="round"/>
|
||||
<path d="M11.25 9L9 11.25M9 11.25L6.75 9M9 11.25V1.6875" stroke="#C8C7D8" stroke-width="1.35" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_18204_36775">
|
||||
<rect width="18" height="18" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 886 B |
@@ -19,6 +19,7 @@ export { default as CoinsIcon } from './coins.svg';
|
||||
export { default as CommentIcon } from './comment.svg';
|
||||
export { default as CopyIcon } from './copy.svg';
|
||||
export { default as CubeIcon } from './cube.svg';
|
||||
export { default as DepositIcon } from './deposit.svg';
|
||||
export { default as DepthChartIcon } from './depth-chart.svg';
|
||||
export { default as DiscordIcon } from './discord.svg';
|
||||
export { default as ExportKeysIcon } from './export-keys.svg';
|
||||
@@ -35,6 +36,7 @@ export { default as LockIcon } from './lock.svg';
|
||||
export { default as LogoShortIcon } from './logo-short';
|
||||
export { default as MarketsIcon } from './markets.svg';
|
||||
export { default as MenuIcon } from './menu.svg';
|
||||
export { default as MigrateIcon } from './migrate.svg';
|
||||
export { default as MintscanIcon } from './logos/mintscan.svg';
|
||||
export { default as OrderbookIcon } from './orderbook.svg';
|
||||
export { default as OverviewIcon } from './overview.svg';
|
||||
@@ -58,6 +60,7 @@ export { default as TransferIcon } from './transfer.svg';
|
||||
export { default as TriangleIcon } from './triangle.svg';
|
||||
export { default as TryAgainIcon } from './try-again.svg';
|
||||
export { default as WarningIcon } from './warning.svg';
|
||||
export { default as WithdrawIcon } from './withdraw.svg';
|
||||
|
||||
// Wallets
|
||||
export { default as BitkeepIcon } from './wallets/bitkeep.svg';
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 694 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
|
||||
<path d="M13.2426 6.2573C14.0817 7.09641 14.6531 8.16551 14.8846 9.3294C15.1162 10.4933 14.9973 11.6997 14.5432 12.796C14.0891 13.8924 13.32 14.8295 12.3334 15.4888C11.3467 16.148 10.1866 16.4999 8.99993 16.4999C7.81324 16.4999 6.6532 16.148 5.66651 15.4888C4.67981 14.8295 3.91078 13.8924 3.45665 12.796C3.00253 11.6997 2.88371 10.4933 3.11522 9.3294C3.34673 8.16551 3.91817 7.09641 4.75729 6.2573" stroke="#C8C7D8" stroke-width="1.35" stroke-linecap="round"/>
|
||||
<path d="M11.25 2.9375L9 0.6875M9 0.6875L6.75 2.9375M9 0.6875V10.25" stroke="#C8C7D8" stroke-width="1.35" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 723 B |
@@ -8,7 +8,7 @@ import { STRING_KEYS } from '@/constants/localization';
|
||||
import { AppRoute } from '@/constants/routes';
|
||||
import { LogoShortIcon, BellStrokeIcon } from '@/icons';
|
||||
|
||||
import { useTokenConfigs, useStringGetter } from '@/hooks';
|
||||
import { useTokenConfigs, useStringGetter, useURLConfigs } from '@/hooks';
|
||||
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
import { IconButton } from '@/components/IconButton';
|
||||
@@ -24,9 +24,11 @@ import { openDialog } from '@/state/dialogs';
|
||||
|
||||
import { headerMixins } from '@/styles/headerMixins';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import breakpoints from '@/styles/breakpoints';
|
||||
|
||||
export const HeaderDesktop = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
const { documentation, community, mintscanBase } = useURLConfigs();
|
||||
const dispatch = useDispatch();
|
||||
const { chainTokenLabel } = useTokenConfigs();
|
||||
|
||||
@@ -34,26 +36,26 @@ export const HeaderDesktop = () => {
|
||||
{
|
||||
group: 'navigation',
|
||||
items: [
|
||||
{
|
||||
value: 'MARKET',
|
||||
label: stringGetter({ key: STRING_KEYS.MARKET }),
|
||||
href: AppRoute.Markets,
|
||||
},
|
||||
{
|
||||
value: 'TRADE',
|
||||
label: stringGetter({ key: STRING_KEYS.TRADE }),
|
||||
href: AppRoute.Trade,
|
||||
},
|
||||
{
|
||||
value: chainTokenLabel,
|
||||
label: chainTokenLabel,
|
||||
href: `/${chainTokenLabel}`,
|
||||
},
|
||||
{
|
||||
value: 'PORTFOLIO',
|
||||
label: stringGetter({ key: STRING_KEYS.PORTFOLIO }),
|
||||
href: AppRoute.Portfolio,
|
||||
},
|
||||
{
|
||||
value: 'MARKETS',
|
||||
label: stringGetter({ key: STRING_KEYS.MARKETS }),
|
||||
href: AppRoute.Markets,
|
||||
},
|
||||
{
|
||||
value: chainTokenLabel,
|
||||
label: chainTokenLabel,
|
||||
href: `/${chainTokenLabel}`,
|
||||
},
|
||||
{
|
||||
value: 'MORE',
|
||||
label: stringGetter({ key: STRING_KEYS.MORE }),
|
||||
@@ -62,19 +64,19 @@ export const HeaderDesktop = () => {
|
||||
value: 'DOCUMENTATION',
|
||||
slotBefore: <Icon iconName={IconName.Terminal} />,
|
||||
label: stringGetter({ key: STRING_KEYS.DOCUMENTATION }),
|
||||
href: 'https://v4-teacher.vercel.app/',
|
||||
href: documentation,
|
||||
},
|
||||
{
|
||||
value: 'MINTSCAN',
|
||||
slotBefore: <Icon iconName={IconName.Mintscan} />,
|
||||
label: stringGetter({ key: STRING_KEYS.MINTSCAN }),
|
||||
href: 'https://testnet.mintscan.io/dydx-testnet',
|
||||
href: mintscanBase,
|
||||
},
|
||||
{
|
||||
value: 'COMMUNITY',
|
||||
slotBefore: <Icon iconName={IconName.Discord} />,
|
||||
label: stringGetter({ key: STRING_KEYS.COMMUNITY }),
|
||||
href: 'https://discord.gg/dydx',
|
||||
href: community,
|
||||
},
|
||||
{
|
||||
value: 'TERMS_OF_USE',
|
||||
@@ -150,6 +152,7 @@ const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Header = styled.header`
|
||||
--header-horizontal-padding-mobile: 0.5rem;
|
||||
--trigger-height: 2.25rem;
|
||||
--logo-width: 3.5rem;
|
||||
|
||||
${layoutMixins.container}
|
||||
@@ -157,6 +160,8 @@ Styled.Header = styled.header`
|
||||
${layoutMixins.scrollSnapItem}
|
||||
backdrop-filter: none;
|
||||
|
||||
height: var(--page-currentHeaderHeight);
|
||||
|
||||
grid-area: Header;
|
||||
|
||||
display: grid;
|
||||
@@ -169,6 +174,14 @@ Styled.Header = styled.header`
|
||||
)
|
||||
var(--border-width) 1fr var(--border-width) auto;
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
--trigger-height: 3rem;
|
||||
}
|
||||
|
||||
@media ${breakpoints.mobile} {
|
||||
--navBefore-width: 7rem;
|
||||
}
|
||||
|
||||
font-size: 0.9375rem;
|
||||
|
||||
:before {
|
||||
@@ -179,6 +192,7 @@ Styled.Header = styled.header`
|
||||
Styled.NavigationMenu = styled(NavigationMenu)`
|
||||
& {
|
||||
--navigationMenu-height: var(--stickyArea-topHeight);
|
||||
--navigationMenu-item-height: var(--trigger-height);
|
||||
}
|
||||
|
||||
${layoutMixins.scrollArea}
|
||||
|
||||
+16
-8
@@ -46,6 +46,8 @@ import {
|
||||
WALLET_CONNECT_EXPLORER_RECOMMENDED_IDS,
|
||||
} from '@/constants/wallets';
|
||||
|
||||
import { isTruthy } from './isTruthy';
|
||||
|
||||
// Config
|
||||
|
||||
export const WAGMI_SUPPORTED_CHAINS: Chain[] = [
|
||||
@@ -78,13 +80,17 @@ export const WAGMI_SUPPORTED_CHAINS: Chain[] = [
|
||||
celoAlfajores,
|
||||
];
|
||||
|
||||
const { chains, publicClient, webSocketPublicClient } = configureChains(WAGMI_SUPPORTED_CHAINS, [
|
||||
// alchemyProvider({ apiKey: import.meta.env.VITE_ALCHEMY_API_KEY }),
|
||||
jsonRpcProvider({
|
||||
rpc: (chain) => ({ http: chain.rpcUrls.default.http[0] }),
|
||||
}),
|
||||
publicProvider(),
|
||||
]);
|
||||
const { chains, publicClient, webSocketPublicClient } = configureChains(
|
||||
WAGMI_SUPPORTED_CHAINS,
|
||||
[
|
||||
import.meta.env.VITE_ALCHEMY_API_KEY &&
|
||||
alchemyProvider({ apiKey: import.meta.env.VITE_ALCHEMY_API_KEY }),
|
||||
jsonRpcProvider({
|
||||
rpc: (chain) => ({ http: chain.rpcUrls.default.http[0] }),
|
||||
}),
|
||||
publicProvider(),
|
||||
].filter(isTruthy)
|
||||
);
|
||||
|
||||
const injectedConnectorOptions = {
|
||||
chains,
|
||||
@@ -202,5 +208,7 @@ export const resolveWagmiConnector = ({
|
||||
? createInjectedConnectorWithProvider(walletConnection.provider)
|
||||
: walletConnection.type === WalletConnectionType.WalletConnect2 && walletConfig.walletconnect2Id
|
||||
? createWalletConnect2ConnectorWithId(walletConfig.walletconnect2Id, walletConnectConfig)
|
||||
: getConnectors(walletConnectConfig).find(({ id }: { id: string }) => id === walletConnectionConfig.wagmiConnectorId);
|
||||
: getConnectors(walletConnectConfig).find(
|
||||
({ id }: { id: string }) => id === walletConnectionConfig.wagmiConnectorId
|
||||
);
|
||||
};
|
||||
|
||||
+21
-13
@@ -17,7 +17,7 @@ import { DialogTypes } from '@/constants/dialogs';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { AppRoute, PortfolioRoute, HistoryRoute } from '@/constants/routes';
|
||||
import { wallets, WalletType } from '@/constants/wallets';
|
||||
import { useAccounts, useStringGetter } from '@/hooks';
|
||||
import { useAccounts, useStringGetter, useTokenConfigs } from '@/hooks';
|
||||
|
||||
import { getOnboardingState } from '@/state/accountSelectors';
|
||||
import { openDialog } from '@/state/dialogs';
|
||||
@@ -37,6 +37,7 @@ const Profile = () => {
|
||||
const isConnected = onboardingState !== OnboardingState.Disconnected;
|
||||
|
||||
const { evmAddress, dydxAddress, walletType } = useAccounts();
|
||||
const { chainTokenLabel } = useTokenConfigs();
|
||||
|
||||
const { data: ensName } = useEnsName({
|
||||
address: evmAddress,
|
||||
@@ -137,7 +138,11 @@ const Profile = () => {
|
||||
layout="grid"
|
||||
/>
|
||||
</Panel>
|
||||
<Styled.RewardsPanel header={stringGetter({ key: STRING_KEYS.REWARDS })} hasSeparator>
|
||||
<Styled.RewardsPanel
|
||||
slotHeaderContent={stringGetter({ key: STRING_KEYS.REWARDS })}
|
||||
href={`/${chainTokenLabel}`}
|
||||
hasSeparator
|
||||
>
|
||||
<Styled.Details
|
||||
items={[
|
||||
{ key: 'maker', label: stringGetter({ key: STRING_KEYS.YOU_WILL_EARN }), value: '-' },
|
||||
@@ -149,8 +154,8 @@ const Profile = () => {
|
||||
</Styled.EqualGrid>
|
||||
|
||||
<Styled.TablePanel
|
||||
header={stringGetter({ key: STRING_KEYS.HISTORY })}
|
||||
linkto={`${AppRoute.Portfolio}/${PortfolioRoute.History}/${HistoryRoute.Trades}`}
|
||||
slotHeaderContent={stringGetter({ key: STRING_KEYS.HISTORY })}
|
||||
href={`${AppRoute.Portfolio}/${PortfolioRoute.History}/${HistoryRoute.Trades}`}
|
||||
hasSeparator
|
||||
>
|
||||
<FillsTable
|
||||
@@ -175,7 +180,7 @@ Styled.MobileProfileLayout = styled.div`
|
||||
${layoutMixins.contentContainerPage}
|
||||
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
padding: 1.25rem;
|
||||
`;
|
||||
|
||||
Styled.Header = styled.header`
|
||||
@@ -273,22 +278,25 @@ Styled.RewardsPanel = styled(Panel)`
|
||||
`;
|
||||
|
||||
Styled.TablePanel = styled(Panel)`
|
||||
padding: 0;
|
||||
max-width: calc(100vw - 2rem);
|
||||
max-height: 10rem;
|
||||
--panel-content-paddingY: 0;
|
||||
--panel-content-paddingX: 0;
|
||||
|
||||
> div > div {
|
||||
margin-top: 0.5rem;
|
||||
--scrollArea-height: 10rem;
|
||||
border-radius: 0.875rem;
|
||||
}
|
||||
|
||||
table {
|
||||
max-height: 10rem;
|
||||
--tableCell-padding: 0.25rem 1rem;
|
||||
|
||||
background-color: transparent;
|
||||
|
||||
--tableRow-backgroundColor: var(--color-layer-3);
|
||||
background-color: var(--color-layer-3);
|
||||
thead {
|
||||
color: var(--color-text-0);
|
||||
}
|
||||
|
||||
tbody {
|
||||
background-color: transparent;
|
||||
|
||||
tr {
|
||||
td:nth-child(2),
|
||||
td:nth-child(3) {
|
||||
|
||||
+131
-106
@@ -1,12 +1,12 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { Nullable } from '@dydxprotocol/v4-abacus';
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
|
||||
import { FeeTier } from '@/constants/abacus';
|
||||
import { FEE_DECIMALS, QUANTUM_MULTIPLIER } from '@/constants/numbers';
|
||||
import { FEE_DECIMALS } from '@/constants/numbers';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { useAccounts, useBreakpoints, useStringGetter } from '@/hooks';
|
||||
import { useBreakpoints, useStringGetter } from '@/hooks';
|
||||
import { breakpoints } from '@/styles';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import { tableMixins } from '@/styles/tableMixins';
|
||||
@@ -29,9 +29,8 @@ const EQUALITY_SYMBOL_MAP = {
|
||||
};
|
||||
|
||||
export const Fees = () => {
|
||||
const { dydxAddress } = useAccounts();
|
||||
const stringGetter = useStringGetter();
|
||||
const { isNotTablet } = useBreakpoints();
|
||||
const { isTablet, isNotTablet } = useBreakpoints();
|
||||
const userFeeTier = useSelector(getUserFeeTier, shallowEqual);
|
||||
const userStats = useSelector(getUserStats, shallowEqual);
|
||||
const feeTiers = useSelector(getFeeTiers, shallowEqual);
|
||||
@@ -56,28 +55,28 @@ export const Fees = () => {
|
||||
<Styled.AdditionalConditions>
|
||||
{!isAdditional && !totalShare && !makerShare && <Output type={OutputType.Text} />}
|
||||
{!!totalShare && (
|
||||
<Styled.TextRow>
|
||||
{isAdditional && <span>{stringGetter({ key: STRING_KEYS.AND })}</span>}
|
||||
<span>{stringGetter({ key: STRING_KEYS.EXCHANGE_MARKET_SHARE })}</span>
|
||||
<Styled.Highlighted>{'>'}</Styled.Highlighted>
|
||||
<Styled.AdditionalConditionsText>
|
||||
{isAdditional && stringGetter({ key: STRING_KEYS.AND })}{' '}
|
||||
{stringGetter({ key: STRING_KEYS.EXCHANGE_MARKET_SHARE })}{' '}
|
||||
<Styled.Highlighted>{'>'}</Styled.Highlighted>{' '}
|
||||
<Styled.HighlightOutput
|
||||
type={OutputType.Percent}
|
||||
value={totalShare}
|
||||
fractionDigits={0}
|
||||
/>
|
||||
</Styled.TextRow>
|
||||
</Styled.AdditionalConditionsText>
|
||||
)}
|
||||
{!!makerShare && (
|
||||
<Styled.TextRow>
|
||||
{isAdditional && <span>{stringGetter({ key: STRING_KEYS.AND })}</span>}
|
||||
<span>{stringGetter({ key: STRING_KEYS.MAKER_MARKET_SHARE })}</span>
|
||||
<Styled.Highlighted>{'>'}</Styled.Highlighted>
|
||||
<Styled.AdditionalConditionsText>
|
||||
{isAdditional && stringGetter({ key: STRING_KEYS.AND })}{' '}
|
||||
{stringGetter({ key: STRING_KEYS.MAKER_MARKET_SHARE })}{' '}
|
||||
<Styled.Highlighted>{'>'}</Styled.Highlighted>{' '}
|
||||
<Styled.HighlightOutput
|
||||
type={OutputType.Percent}
|
||||
value={makerShare}
|
||||
fractionDigits={0}
|
||||
/>
|
||||
</Styled.TextRow>
|
||||
</Styled.AdditionalConditionsText>
|
||||
)}
|
||||
</Styled.AdditionalConditions>
|
||||
),
|
||||
@@ -103,91 +102,90 @@ export const Fees = () => {
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<div>
|
||||
<Styled.FeeTable
|
||||
label="Fee Tiers"
|
||||
data={feeTiers || []}
|
||||
getRowKey={(row: FeeTier) => row.tier}
|
||||
getRowAttributes={(row: FeeTier) => ({
|
||||
'data-yours': row.tier === userFeeTier,
|
||||
})}
|
||||
columns={(
|
||||
[
|
||||
{
|
||||
columnKey: 'tier',
|
||||
getCellValue: (row) => row.tier,
|
||||
label: stringGetter({ key: STRING_KEYS.TIER }),
|
||||
allowsSorting: false,
|
||||
renderCell: ({ tier }) => (
|
||||
<Styled.Tier>
|
||||
<Styled.Output type={OutputType.Text} value={tier} />
|
||||
{tier === userFeeTier && (
|
||||
<Styled.YouTag size={TagSize.Medium}>
|
||||
{stringGetter({ key: STRING_KEYS.YOU })}
|
||||
</Styled.YouTag>
|
||||
)}
|
||||
</Styled.Tier>
|
||||
),
|
||||
},
|
||||
{
|
||||
columnKey: 'volume',
|
||||
getCellValue: (row) => row.volume,
|
||||
label: stringGetter({ key: STRING_KEYS.VOLUME_30D }),
|
||||
allowsSorting: false,
|
||||
renderCell: ({ symbol, volume, makerShare, totalShare }) => (
|
||||
<>
|
||||
<span>{`${
|
||||
symbol in EQUALITY_SYMBOL_MAP
|
||||
? EQUALITY_SYMBOL_MAP[symbol as keyof typeof EQUALITY_SYMBOL_MAP]
|
||||
: symbol
|
||||
} `}</span>
|
||||
<Styled.HighlightOutput type={OutputType.CompactFiat} value={volume} />
|
||||
{!isNotTablet &&
|
||||
AdditionalConditions({ totalShare, makerShare, isAdditional: true })}
|
||||
</>
|
||||
),
|
||||
},
|
||||
isNotTablet && {
|
||||
columnKey: 'condition',
|
||||
getCellValue: (row) => row.volume,
|
||||
label: stringGetter({ key: STRING_KEYS.ADDITIONAL_CONDITION }),
|
||||
allowsSorting: false,
|
||||
renderCell: ({ totalShare, makerShare }) =>
|
||||
AdditionalConditions({ totalShare, makerShare }),
|
||||
},
|
||||
{
|
||||
columnKey: 'maker',
|
||||
getCellValue: (row) => row.maker,
|
||||
label: stringGetter({ key: STRING_KEYS.MAKER }),
|
||||
allowsSorting: false,
|
||||
renderCell: ({ maker }) => (
|
||||
<Styled.HighlightOutput
|
||||
type={OutputType.SmallPercent}
|
||||
value={maker}
|
||||
fractionDigits={FEE_DECIMALS}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
columnKey: 'taker',
|
||||
getCellValue: (row) => row.taker,
|
||||
label: stringGetter({ key: STRING_KEYS.TAKER }),
|
||||
allowsSorting: false,
|
||||
renderCell: ({ taker }) => (
|
||||
<Styled.HighlightOutput
|
||||
type={OutputType.SmallPercent}
|
||||
value={taker}
|
||||
fractionDigits={FEE_DECIMALS}
|
||||
/>
|
||||
),
|
||||
},
|
||||
] as ColumnDef<FeeTier>[]
|
||||
).filter(isTruthy)}
|
||||
selectionBehavior="replace"
|
||||
withOuterBorder={isNotTablet}
|
||||
withInnerBorders
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Styled.FeeTable
|
||||
label="Fee Tiers"
|
||||
data={feeTiers || []}
|
||||
getRowKey={(row: FeeTier) => row.tier}
|
||||
getRowAttributes={(row: FeeTier) => ({
|
||||
'data-yours': row.tier === userFeeTier,
|
||||
})}
|
||||
columns={(
|
||||
[
|
||||
{
|
||||
columnKey: 'tier',
|
||||
getCellValue: (row) => row.tier,
|
||||
label: stringGetter({ key: STRING_KEYS.TIER }),
|
||||
allowsSorting: false,
|
||||
renderCell: ({ tier }) => (
|
||||
<Styled.Tier>
|
||||
<Styled.Output type={OutputType.Text} value={tier} />
|
||||
{tier === userFeeTier && (
|
||||
<Styled.YouTag size={TagSize.Medium}>
|
||||
{stringGetter({ key: STRING_KEYS.YOU })}
|
||||
</Styled.YouTag>
|
||||
)}
|
||||
</Styled.Tier>
|
||||
),
|
||||
},
|
||||
{
|
||||
columnKey: 'volume',
|
||||
getCellValue: (row) => row.volume,
|
||||
label: stringGetter({ key: STRING_KEYS.VOLUME_30D }),
|
||||
allowsSorting: false,
|
||||
renderCell: ({ symbol, volume, makerShare, totalShare }) => (
|
||||
<>
|
||||
<span>{`${
|
||||
symbol in EQUALITY_SYMBOL_MAP
|
||||
? EQUALITY_SYMBOL_MAP[symbol as keyof typeof EQUALITY_SYMBOL_MAP]
|
||||
: symbol
|
||||
} `}</span>
|
||||
<Styled.HighlightOutput type={OutputType.CompactFiat} value={volume} />
|
||||
{isTablet &&
|
||||
AdditionalConditions({ totalShare, makerShare, isAdditional: true })}
|
||||
</>
|
||||
),
|
||||
},
|
||||
isNotTablet && {
|
||||
columnKey: 'condition',
|
||||
getCellValue: (row) => row.volume,
|
||||
label: stringGetter({ key: STRING_KEYS.ADDITIONAL_CONDITION }),
|
||||
allowsSorting: false,
|
||||
renderCell: ({ totalShare, makerShare }) =>
|
||||
AdditionalConditions({ totalShare, makerShare }),
|
||||
},
|
||||
{
|
||||
columnKey: 'maker',
|
||||
getCellValue: (row) => row.maker,
|
||||
label: stringGetter({ key: STRING_KEYS.MAKER }),
|
||||
allowsSorting: false,
|
||||
renderCell: ({ maker }) => (
|
||||
<Styled.HighlightOutput
|
||||
type={OutputType.SmallPercent}
|
||||
value={maker}
|
||||
fractionDigits={FEE_DECIMALS}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
columnKey: 'taker',
|
||||
getCellValue: (row) => row.taker,
|
||||
label: stringGetter({ key: STRING_KEYS.TAKER }),
|
||||
allowsSorting: false,
|
||||
renderCell: ({ taker }) => (
|
||||
<Styled.HighlightOutput
|
||||
type={OutputType.SmallPercent}
|
||||
value={taker}
|
||||
fractionDigits={FEE_DECIMALS}
|
||||
/>
|
||||
),
|
||||
},
|
||||
] as ColumnDef<FeeTier>[]
|
||||
).filter(isTruthy)}
|
||||
selectionBehavior="replace"
|
||||
withOuterBorder={isNotTablet}
|
||||
withInnerBorders
|
||||
/>
|
||||
</Styled.ContentWrapper>
|
||||
</AttachedExpandingSection>
|
||||
);
|
||||
@@ -198,21 +196,43 @@ const Styled: Record<string, AnyStyledComponent> = {};
|
||||
Styled.ContentWrapper = styled.div`
|
||||
${layoutMixins.flexColumn}
|
||||
gap: 1.5rem;
|
||||
max-width: 100vw;
|
||||
`;
|
||||
|
||||
Styled.AdditionalConditions = styled.div`
|
||||
${tableMixins.cellContentColumn}
|
||||
justify-content: end;
|
||||
|
||||
color: var(--color-text-0);
|
||||
font: var(--font-small-book);
|
||||
|
||||
> :nth-child(2) {
|
||||
font: var(--font-base-book);
|
||||
font: var(--font-small-book);
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.AdditionalConditionsText = styled.span`
|
||||
display: flex;
|
||||
gap: 0.5ch;
|
||||
|
||||
@media ${breakpoints.mobile} {
|
||||
display: inline;
|
||||
max-width: 8rem;
|
||||
min-width: 0;
|
||||
|
||||
output {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.FeesDetails = styled(Details)`
|
||||
gap: 1rem;
|
||||
|
||||
@media ${breakpoints.notTablet} {
|
||||
margin: 0 1.25rem;
|
||||
}
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
padding: 1rem 1rem 0 1rem;
|
||||
--details-grid-numColumns: 1;
|
||||
@@ -237,11 +257,7 @@ Styled.FeesDetails = styled(Details)`
|
||||
}
|
||||
|
||||
output {
|
||||
font: var(--font-medium-book);
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
font: var(--font-base-book);
|
||||
}
|
||||
font: var(--font-base-book);
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -271,6 +287,11 @@ Styled.FeeTable = styled(Table)`
|
||||
|
||||
font: var(--font-base-book);
|
||||
|
||||
@media ${breakpoints.mobile} {
|
||||
--tableCell-padding: 1rem 1.25rem;
|
||||
font: var(--font-small-book);
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
&[data-yours='true'] {
|
||||
background-color: var(--color-layer-3);
|
||||
@@ -283,6 +304,10 @@ Styled.FeeTable = styled(Table)`
|
||||
|
||||
@media ${breakpoints.notTablet} {
|
||||
--tableHeader-backgroundColor: var(--color-layer-1);
|
||||
|
||||
thead tr {
|
||||
--border-width: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -32,11 +32,11 @@ export const DYDXBalancePanel = () => {
|
||||
const { chainTokenLabel } = useTokenConfigs();
|
||||
|
||||
return (
|
||||
<Panel
|
||||
<Styled.Panel
|
||||
slotHeader={
|
||||
<Styled.Header>
|
||||
<Styled.Title>
|
||||
{/* <AssetIcon symbol="DYDX" /> */}
|
||||
<AssetIcon symbol={chainTokenLabel} />
|
||||
{chainTokenLabel}
|
||||
</Styled.Title>
|
||||
<Styled.ReceiveAndTransferButtons>
|
||||
@@ -117,14 +117,19 @@ export const DYDXBalancePanel = () => {
|
||||
]}
|
||||
/>
|
||||
</Styled.Content>
|
||||
</Panel>
|
||||
</Styled.Panel>
|
||||
);
|
||||
};
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Panel = styled(Panel)`
|
||||
--panel-paddingX: 1.5rem;
|
||||
`;
|
||||
|
||||
Styled.Header = styled.div`
|
||||
${layoutMixins.spacedRow}
|
||||
gap: 1rem;
|
||||
padding: 1rem 1.5rem 0;
|
||||
`;
|
||||
|
||||
@@ -177,7 +182,7 @@ Styled.WalletAndStakedBalance = styled(Details)`
|
||||
--details-item-backgroundColor: var(--color-layer-6);
|
||||
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 0.5rem;
|
||||
gap: 1rem;
|
||||
|
||||
> div {
|
||||
gap: 1rem;
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
|
||||
|
||||
import { useAccountBalance, useBreakpoints, useStringGetter } from '@/hooks';
|
||||
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { Details } from '@/components/Details';
|
||||
import { Panel } from '@/components/Panel';
|
||||
import { Button } from '@/components/Button';
|
||||
import { Link } from '@/components/Link';
|
||||
import { IconName, Icon } from '@/components/Icon';
|
||||
import { Output, OutputType } from '@/components/Output';
|
||||
import { VerticalSeparator } from '@/components/Separator';
|
||||
import { Tag } from '@/components/Tag';
|
||||
import { WithReceipt } from '@/components/WithReceipt';
|
||||
|
||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||
|
||||
import { MustBigNumber } from '@/lib/numbers';
|
||||
|
||||
const TOKEN_MIGRATION_LEARN_MORE_LINK =
|
||||
'https://www.dydx.foundation/blog/update-on-exploring-the-future-of-dydx';
|
||||
|
||||
export const MigratePanel = () => {
|
||||
const { isNotTablet } = useBreakpoints();
|
||||
const stringGetter = useStringGetter();
|
||||
|
||||
const selectedNetwork = useSelector(getSelectedNetwork);
|
||||
|
||||
const chainId = Number(ENVIRONMENT_CONFIG_MAP[selectedNetwork].ethereumChainId);
|
||||
|
||||
// v3 token is only on mainnet
|
||||
const { balance: tokenBalance } = useAccountBalance({
|
||||
addressOrDenom: chainId === 1 ? import.meta.env.VITE_V3_TOKEN_ADDRESS : undefined,
|
||||
chainId: 1,
|
||||
isCosmosChain: false,
|
||||
});
|
||||
|
||||
return isNotTablet ? (
|
||||
<Styled.MigratePanel
|
||||
slotHeader={<Styled.Title>{stringGetter({ key: STRING_KEYS.MIGRATE })}</Styled.Title>}
|
||||
slotRight={
|
||||
<Styled.MigrateAction>
|
||||
<div>
|
||||
<div>{stringGetter({ key: STRING_KEYS.AVAILABLE_TO_MIGRATE })}</div>
|
||||
<Styled.Token type={OutputType.Asset} value={tokenBalance} />
|
||||
</div>
|
||||
{import.meta.env.VITE_TOKEN_MIGRATION_URI && (
|
||||
<Button
|
||||
action={MustBigNumber(tokenBalance).gt(0) ? ButtonAction.Primary : ButtonAction.Base}
|
||||
type={ButtonType.Link}
|
||||
href={import.meta.env.VITE_TOKEN_MIGRATION_URI}
|
||||
slotRight={<Icon iconName={IconName.LinkOut} />}
|
||||
>
|
||||
{stringGetter({ key: STRING_KEYS.MIGRATE_NOW })}
|
||||
</Button>
|
||||
)}
|
||||
</Styled.MigrateAction>
|
||||
}
|
||||
>
|
||||
<Styled.Description>
|
||||
{stringGetter({ key: STRING_KEYS.MIGRATE_DESCRIPTION })}
|
||||
<Link href={TOKEN_MIGRATION_LEARN_MORE_LINK}>
|
||||
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
|
||||
</Link>
|
||||
</Styled.Description>
|
||||
</Styled.MigratePanel>
|
||||
) : (
|
||||
<Styled.MigratePanel
|
||||
slotHeader={
|
||||
<Styled.MobileMigrateHeader>
|
||||
<h3>
|
||||
<Icon iconName={IconName.Migrate} />
|
||||
{stringGetter({ key: STRING_KEYS.MIGRATE })}
|
||||
</h3>
|
||||
<Styled.VerticalSeparator />
|
||||
<span>
|
||||
{stringGetter({
|
||||
key: STRING_KEYS.FROM_TO,
|
||||
params: { FROM: <b>Ethereum</b>, TO: <b>dYdX Chain</b> },
|
||||
})}
|
||||
</span>
|
||||
</Styled.MobileMigrateHeader>
|
||||
}
|
||||
>
|
||||
<Styled.Column>
|
||||
<Styled.WithReceipt
|
||||
slotReceipt={
|
||||
<Styled.Details
|
||||
items={[
|
||||
{
|
||||
key: 'available-to-migrate',
|
||||
label: (
|
||||
<Styled.InlineRow>
|
||||
{stringGetter({ key: STRING_KEYS.AVAILABLE_TO_MIGRATE })}
|
||||
<Tag>DYDX</Tag>
|
||||
</Styled.InlineRow>
|
||||
),
|
||||
value: <Output type={OutputType.Asset} value={tokenBalance} />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{import.meta.env.VITE_TOKEN_MIGRATION_URI && (
|
||||
<Button
|
||||
action={tokenBalance ? ButtonAction.Primary : ButtonAction.Base}
|
||||
size={ButtonSize.Medium}
|
||||
slotRight={<Icon iconName={IconName.LinkOut} />}
|
||||
href={import.meta.env.VITE_TOKEN_MIGRATION_URI}
|
||||
>
|
||||
{stringGetter({ key: STRING_KEYS.MIGRATE_NOW })}
|
||||
</Button>
|
||||
)}
|
||||
</Styled.WithReceipt>
|
||||
<Styled.InlineRow>
|
||||
{stringGetter({ key: STRING_KEYS.WANT_TO_LEARN })}
|
||||
<Link href={TOKEN_MIGRATION_LEARN_MORE_LINK} withIcon>
|
||||
{stringGetter({ key: STRING_KEYS.CLICK_HERE })}
|
||||
</Link>
|
||||
</Styled.InlineRow>
|
||||
</Styled.Column>
|
||||
</Styled.MigratePanel>
|
||||
);
|
||||
};
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.MigratePanel = styled(Panel)`
|
||||
--panel-paddingX: 1.5rem;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
Styled.Title = styled.h3`
|
||||
font: var(--font-medium-book);
|
||||
color: var(--color-text-2);
|
||||
|
||||
padding: 1rem 1.5rem 0;
|
||||
`;
|
||||
|
||||
Styled.MigrateAction = styled.div`
|
||||
${layoutMixins.flexEqualColumns}
|
||||
align-items: center;
|
||||
margin-right: 1rem;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
|
||||
background-color: var(--color-layer-2);
|
||||
border: solid var(--border-width) var(--color-border);
|
||||
border-radius: 0.75rem;
|
||||
`;
|
||||
|
||||
Styled.Token = styled(Output)`
|
||||
font: var(--font-large-book);
|
||||
color: var(--color-text-2);
|
||||
`;
|
||||
|
||||
Styled.Description = styled.div`
|
||||
color: var(--color-text-0);
|
||||
--link-color: var(--color-text-1);
|
||||
`;
|
||||
|
||||
Styled.Column = styled.div`
|
||||
${layoutMixins.flexColumn}
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
Styled.MobileMigrateHeader = styled.div`
|
||||
${layoutMixins.inlineRow}
|
||||
gap: 1ch;
|
||||
|
||||
font: var(--font-small-book);
|
||||
color: var(--color-text-0);
|
||||
|
||||
padding: 1rem 1.5rem 0;
|
||||
|
||||
h3 {
|
||||
${layoutMixins.inlineRow}
|
||||
font: var(--font-large-book);
|
||||
color: var(--color-text-2);
|
||||
|
||||
svg {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
margin-top: 0.2rem;
|
||||
|
||||
b {
|
||||
font-weight: var(--fontWeight-book);
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.VerticalSeparator = styled(VerticalSeparator)`
|
||||
z-index: 1;
|
||||
|
||||
&& {
|
||||
height: 1.5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Details = styled(Details)`
|
||||
padding: 0.5rem 1rem;
|
||||
`;
|
||||
|
||||
Styled.WithReceipt = styled(WithReceipt)`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
Styled.InlineRow = styled.div`
|
||||
${layoutMixins.inlineRow}
|
||||
color: var(--color-text-0);
|
||||
--link-color: var(--color-text-1);
|
||||
`;
|
||||
@@ -1,186 +1,74 @@
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { ButtonAction, ButtonSize, ButtonState, ButtonType } from '@/constants/buttons';
|
||||
import { ButtonAction, ButtonSize } from '@/constants/buttons';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
|
||||
import { useAccountBalance, useBreakpoints, useStringGetter } from '@/hooks';
|
||||
import { useBreakpoints, useStringGetter, useURLConfigs } from '@/hooks';
|
||||
|
||||
import { breakpoints } from '@/styles';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { Details } from '@/components/Details';
|
||||
import { Panel } from '@/components/Panel';
|
||||
import { IconName } from '@/components/Icon';
|
||||
import { Button } from '@/components/Button';
|
||||
import { IconButton } from '@/components/IconButton';
|
||||
import { Link } from '@/components/Link';
|
||||
import { Output, OutputType } from '@/components/Output';
|
||||
import { VerticalSeparator } from '@/components/Separator';
|
||||
import { WithReceipt } from '@/components/WithReceipt';
|
||||
|
||||
import { openDialog } from '@/state/dialogs';
|
||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||
|
||||
import { DYDXBalancePanel } from './DYDXBalancePanel';
|
||||
|
||||
// TODO: replace placeholder URL with real URLs when avaialble
|
||||
const GOVERNANCE_HELP_URL = 'https://help.dydx.exchange/';
|
||||
const STAKING_HELP_URL = 'https://help.dydx.exchange/';
|
||||
import { MigratePanel } from './MigratePanel';
|
||||
|
||||
export const RewardsPage = () => {
|
||||
const dispatch = useDispatch();
|
||||
const stringGetter = useStringGetter();
|
||||
const { governanceLearnMore, stakingLearnMore } = useURLConfigs();
|
||||
const { isTablet, isNotTablet } = useBreakpoints();
|
||||
|
||||
const selectedNetwork = useSelector(getSelectedNetwork);
|
||||
|
||||
// const chainId = Number(ENVIRONMENT_CONFIG_MAP[selectedNetwork].ethereumChainId);
|
||||
|
||||
// const { balance } = useAccountBalance({
|
||||
// addressOrDenom: import.meta.env.VITE_V3_TOKEN_ADDRESS,
|
||||
// assetSymbol: 'DYDX',
|
||||
// chainId,
|
||||
// isCosmosChain: false,
|
||||
// });
|
||||
|
||||
// const tokenBalance = import.meta.env.VITE_V3_TOKEN_ADDRESS ? balance : 0;
|
||||
const panelArrow = (
|
||||
<Styled.Arrow>
|
||||
<Styled.IconButton
|
||||
action={ButtonAction.Base}
|
||||
iconName={IconName.Arrow}
|
||||
size={ButtonSize.Small}
|
||||
/>
|
||||
</Styled.Arrow>
|
||||
);
|
||||
|
||||
return (
|
||||
<Styled.Page>
|
||||
{/* {isNotTablet ? (
|
||||
<Styled.Migrate>
|
||||
<Styled.TwoItemRow>
|
||||
<div>
|
||||
<Styled.MigrateTitle>
|
||||
{stringGetter({ key: STRING_KEYS.MIGRATE })}
|
||||
</Styled.MigrateTitle>
|
||||
<Styled.Description>
|
||||
{stringGetter({ key: STRING_KEYS.MIGRATE_DESCRIPTION })}
|
||||
<Link href={MIGRATE_HELP_URL}>
|
||||
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
|
||||
</Link>
|
||||
</Styled.Description>
|
||||
</div>
|
||||
<Styled.MigrateAction>
|
||||
<div>
|
||||
<div>{stringGetter({ key: STRING_KEYS.AVAILABLE_TO_MIGRATE })}</div>
|
||||
<div>
|
||||
<Styled.Token type={OutputType.Asset} value={tokenBalance} />
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
action={tokenBalance ? ButtonAction.Primary : ButtonAction.Base}
|
||||
// type={ButtonType.Link}
|
||||
// href={BRIDGE_URL}
|
||||
disabled
|
||||
>
|
||||
{tokenBalance
|
||||
? `${stringGetter({ key: STRING_KEYS.MIGRATE_NOW })} →`
|
||||
: stringGetter({ key: STRING_KEYS.NO_TOKENS_TO_MIGRATE })}
|
||||
</Button>
|
||||
</Styled.MigrateAction>
|
||||
</Styled.TwoItemRow>
|
||||
</Styled.Migrate>
|
||||
) : (
|
||||
<Styled.MobileMigrateCard
|
||||
slotHeader={
|
||||
<Styled.MobileMigrateHeader>
|
||||
<h3>{stringGetter({ key: STRING_KEYS.MIGRATE })}</h3>
|
||||
<VerticalSeparator />
|
||||
<span>
|
||||
{stringGetter({
|
||||
key: STRING_KEYS.FROM_TO,
|
||||
params: { FROM: <strong>Ethereum</strong>, TO: <strong>dYdX Chain</strong> },
|
||||
})}
|
||||
</span>
|
||||
</Styled.MobileMigrateHeader>
|
||||
}
|
||||
>
|
||||
<Styled.WithReceipt
|
||||
slotReceipt={
|
||||
<Styled.Details
|
||||
items={[
|
||||
{
|
||||
key: 'available-to-migrate',
|
||||
label: stringGetter({ key: STRING_KEYS.AVAILABLE_TO_MIGRATE }),
|
||||
value: <Output type={OutputType.Asset} value={tokenBalance} />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
action={tokenBalance ? ButtonAction.Primary : ButtonAction.Base}
|
||||
size={ButtonSize.Medium}
|
||||
disabled
|
||||
>
|
||||
{tokenBalance
|
||||
? `${stringGetter({ key: STRING_KEYS.MIGRATE_NOW })} →`
|
||||
: stringGetter({ key: STRING_KEYS.NO_TOKENS_TO_MIGRATE })}
|
||||
</Button>
|
||||
</Styled.WithReceipt>
|
||||
<Styled.LearnMore>
|
||||
{stringGetter({ key: STRING_KEYS.WANT_TO_LEARN })}
|
||||
<Link href={MIGRATE_HELP_URL}>{stringGetter({ key: STRING_KEYS.CLICK_HERE })}</Link>
|
||||
</Styled.LearnMore>
|
||||
</Styled.MobileMigrateCard>
|
||||
)} */}
|
||||
|
||||
{import.meta.env.VITE_V3_TOKEN_ADDRESS && <MigratePanel />}
|
||||
<Styled.PanelRow>
|
||||
{isTablet && (
|
||||
<Styled.BalancePanelContainer>
|
||||
<DYDXBalancePanel />
|
||||
</Styled.BalancePanelContainer>
|
||||
)}
|
||||
{isTablet && <DYDXBalancePanel />}
|
||||
|
||||
<Styled.Panel
|
||||
slotHeader={<Styled.Title>{stringGetter({ key: STRING_KEYS.GOVERNANCE })}</Styled.Title>}
|
||||
slotRight={panelArrow}
|
||||
onClick={() => dispatch(openDialog({ type: DialogTypes.ExternalNavKeplr }))}
|
||||
>
|
||||
<Styled.Row>
|
||||
<Styled.Description>
|
||||
{stringGetter({ key: STRING_KEYS.GOVERNANCE_DESCRIPTION })}
|
||||
<Link href={GOVERNANCE_HELP_URL} onClick={(e) => e.stopPropagation()}>
|
||||
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
|
||||
</Link>
|
||||
</Styled.Description>
|
||||
{/* TODO: vertically center based on Panel height */}
|
||||
<Styled.IconButton
|
||||
action={ButtonAction.Base}
|
||||
iconName={IconName.Arrow}
|
||||
size={ButtonSize.Small}
|
||||
/>
|
||||
</Styled.Row>
|
||||
<Styled.Description>
|
||||
{stringGetter({ key: STRING_KEYS.GOVERNANCE_DESCRIPTION })}
|
||||
<Link href={governanceLearnMore} onClick={(e) => e.stopPropagation()}>
|
||||
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
|
||||
</Link>
|
||||
</Styled.Description>
|
||||
</Styled.Panel>
|
||||
|
||||
<Styled.Panel
|
||||
slotHeader={<Styled.Title>{stringGetter({ key: STRING_KEYS.STAKING })}</Styled.Title>}
|
||||
slotRight={panelArrow}
|
||||
onClick={() => dispatch(openDialog({ type: DialogTypes.ExternalNavKeplr }))}
|
||||
>
|
||||
<Styled.Row>
|
||||
<Styled.Description>
|
||||
{stringGetter({ key: STRING_KEYS.STAKING_DESCRIPTION })}
|
||||
<Link href={STAKING_HELP_URL} onClick={(e) => e.stopPropagation()}>
|
||||
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
|
||||
</Link>
|
||||
</Styled.Description>
|
||||
{/* TODO: vertically center based on Panel height */}
|
||||
<Styled.IconButton
|
||||
action={ButtonAction.Base}
|
||||
iconName={IconName.Arrow}
|
||||
size={ButtonSize.Small}
|
||||
/>
|
||||
</Styled.Row>
|
||||
<Styled.Description>
|
||||
{stringGetter({ key: STRING_KEYS.STAKING_DESCRIPTION })}
|
||||
<Link href={stakingLearnMore} onClick={(e) => e.stopPropagation()}>
|
||||
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
|
||||
</Link>
|
||||
</Styled.Description>
|
||||
</Styled.Panel>
|
||||
|
||||
{isNotTablet && (
|
||||
<Styled.BalancePanelContainer>
|
||||
<DYDXBalancePanel />
|
||||
</Styled.BalancePanelContainer>
|
||||
)}
|
||||
{isNotTablet && <DYDXBalancePanel />}
|
||||
</Styled.PanelRow>
|
||||
</Styled.Page>
|
||||
);
|
||||
@@ -191,143 +79,56 @@ const Styled: Record<string, AnyStyledComponent> = {};
|
||||
Styled.Page = styled.div`
|
||||
${layoutMixins.contentContainerPage}
|
||||
gap: 1.5rem;
|
||||
padding: 2rem;
|
||||
align-items: center;
|
||||
|
||||
> * {
|
||||
--content-max-width: 70rem;
|
||||
max-width: min(calc(100vw - 4rem), var(--content-max-width));
|
||||
}
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
padding: 1rem;
|
||||
padding: 1.25rem;
|
||||
|
||||
> * {
|
||||
max-width: calc(100vw - 2.5rem);
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Panel = styled(Panel)`
|
||||
padding: 0 1.5rem 1rem;
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
max-width: calc(100vw - 2rem);
|
||||
}
|
||||
--panel-paddingX: 1.5rem;
|
||||
`;
|
||||
|
||||
Styled.Row = styled.div`
|
||||
${layoutMixins.spacedRow}
|
||||
gap: 1rem;
|
||||
|
||||
align-items: center;
|
||||
Styled.Title = styled.h3`
|
||||
font: var(--font-medium-book);
|
||||
color: var(--color-text-2);
|
||||
padding: 1rem 1.5rem 0;
|
||||
`;
|
||||
|
||||
Styled.Description = styled.div`
|
||||
color: var(--color-text-0);
|
||||
|
||||
a {
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
--link-color: var(--color-text-1);
|
||||
`;
|
||||
|
||||
Styled.Migrate = styled.section`
|
||||
max-width: min(100vw, var(--content-max-width));
|
||||
|
||||
padding: 1.5rem;
|
||||
|
||||
background-color: var(--color-layer-3);
|
||||
border-radius: 0.875rem;
|
||||
`;
|
||||
|
||||
Styled.TwoItemRow = styled(Styled.Row)`
|
||||
grid-template-columns: 1fr 1fr;
|
||||
`;
|
||||
|
||||
Styled.MigrateAction = styled(Styled.TwoItemRow)`
|
||||
padding: 1rem;
|
||||
|
||||
background-color: var(--color-layer-2);
|
||||
border: solid var(--border-width) var(--color-border);
|
||||
border-radius: 0.75rem;
|
||||
`;
|
||||
|
||||
Styled.Token = styled(Output)`
|
||||
font: var(--font-large-book);
|
||||
`;
|
||||
|
||||
Styled.PanelRow = styled(Styled.Row)`
|
||||
Styled.PanelRow = styled.div`
|
||||
${layoutMixins.gridEqualColumns}
|
||||
gap: 1.5rem;
|
||||
max-width: min(100vw, var(--content-max-width));
|
||||
align-items: flex-start;
|
||||
|
||||
> section {
|
||||
cursor: pointer;
|
||||
}
|
||||
align-items: flex-start;
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
grid-auto-flow: row;
|
||||
grid-template-columns: 1fr;
|
||||
max-width: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.BalancePanelContainer = styled.div`
|
||||
width: 21.25rem;
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
width: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Title = styled.h3`
|
||||
${layoutMixins.inlineRow}
|
||||
padding: 1.25rem 1.5rem 0.5rem;
|
||||
|
||||
font: var(--font-medium-book);
|
||||
color: var(--color-text-2);
|
||||
`;
|
||||
|
||||
Styled.MigrateTitle = styled(Styled.Title)`
|
||||
padding: 0 0 0.5rem;
|
||||
`;
|
||||
|
||||
Styled.MobileMigrateCard = styled(Styled.Panel)`
|
||||
${layoutMixins.flexColumn}
|
||||
gap: 1rem;
|
||||
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
Styled.MobileMigrateHeader = styled(Styled.Title)`
|
||||
${layoutMixins.inlineRow}
|
||||
gap: 1ch;
|
||||
padding-bottom: 1rem;
|
||||
|
||||
font: var(--font-small-book);
|
||||
color: var(--color-text-0);
|
||||
|
||||
h3 {
|
||||
${layoutMixins.inlineRow}
|
||||
font: var(--font-large-book);
|
||||
color: var(--color-text-2);
|
||||
|
||||
svg {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
margin-top: 0.2rem;
|
||||
b {
|
||||
font-weight: var(--fontWeight-book);
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Details = styled(Details)`
|
||||
padding: 0.5rem 1rem;
|
||||
`;
|
||||
|
||||
Styled.WithReceipt = styled(WithReceipt)`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
Styled.LearnMore = styled(Styled.Description)`
|
||||
${layoutMixins.row}
|
||||
gap: 1ch;
|
||||
`;
|
||||
|
||||
Styled.IconButton = styled(IconButton)`
|
||||
color: var(--color-text-0);
|
||||
--color-border: var(--color-layer-6);
|
||||
`;
|
||||
|
||||
Styled.Arrow = styled.div`
|
||||
padding: 1rem;
|
||||
`;
|
||||
|
||||
@@ -227,8 +227,8 @@ export const HorizontalPanel = ({ isOpen = true, setIsOpen }: ElementProps) => {
|
||||
},
|
||||
]}
|
||||
value={view}
|
||||
onValueChange={(newView: string) => {
|
||||
setView((newView || view) as PanelView);
|
||||
onValueChange={setView}
|
||||
onInteraction={() => {
|
||||
setIsOpen?.(true);
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -17,7 +17,7 @@ import { MustBigNumber } from '@/lib/numbers';
|
||||
import { MidMarketPrice } from '@/views/MidMarketPrice';
|
||||
|
||||
export const TradeHeaderMobile = () => {
|
||||
const { name, symbol } = useSelector(getCurrentMarketAssetData, shallowEqual) ?? {};
|
||||
const { name, id } = useSelector(getCurrentMarketAssetData, shallowEqual) ?? {};
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { market, priceChange24H, priceChange24HPercent } =
|
||||
@@ -27,7 +27,7 @@ export const TradeHeaderMobile = () => {
|
||||
<Styled.Header>
|
||||
<BackButton onClick={() => navigate(AppRoute.Markets)} />
|
||||
<Styled.MarketName>
|
||||
<Styled.AssetIcon symbol={symbol} />
|
||||
<Styled.AssetIcon symbol={id} />
|
||||
<Styled.Name>
|
||||
<h3>{name}</h3>
|
||||
<span>{market}</span>
|
||||
|
||||
@@ -29,7 +29,7 @@ export const MarketFilter = ({
|
||||
<>
|
||||
{!isSearch && (
|
||||
<ToggleGroup
|
||||
items={Object.entries(filters).map(([key, value]) => ({
|
||||
items={Object.values(filters).map((value) => ({
|
||||
label: stringGetter({ key: MARKET_FILTER_LABELS[value] }),
|
||||
value,
|
||||
}))}
|
||||
|
||||
@@ -198,8 +198,6 @@ Styled.Details = styled(Details)`
|
||||
@media ${breakpoints.tablet} {
|
||||
${layoutMixins.withOuterAndInnerBorders}
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
|
||||
font: var(--font-small-book);
|
||||
|
||||
> * {
|
||||
|
||||
@@ -168,7 +168,7 @@ Styled.TriggerIcon = styled.span`
|
||||
width: 0.75em;
|
||||
|
||||
display: inline-flex;
|
||||
transition: transform 0.3s var(--ease-out-expo);
|
||||
transition: rotate 0.3s var(--ease-out-expo);
|
||||
|
||||
${Styled.Trigger}[data-state='open'] & {
|
||||
rotate: -0.5turn;
|
||||
|
||||
@@ -246,9 +246,7 @@ export const FundingChart = ({ selectedLocale }: ElementProps) => {
|
||||
}[rate] || '',
|
||||
}))}
|
||||
value={fundingRateView}
|
||||
onValueChange={(newRate: FundingRateResolution) => {
|
||||
if (newRate) setFundingRateView(newRate);
|
||||
}}
|
||||
onValueChange={setFundingRateView}
|
||||
size={ButtonSize.XSmall}
|
||||
/>
|
||||
</Styled.FundingRateToggle>
|
||||
|
||||
@@ -2,7 +2,7 @@ import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { useBreakpoints, useStringGetter } from '@/hooks';
|
||||
import { useBreakpoints, useStringGetter, useURLConfigs } from '@/hooks';
|
||||
|
||||
import { Button } from '@/components/Button';
|
||||
import { Dialog, DialogPlacement } from '@/components/Dialog';
|
||||
@@ -15,12 +15,10 @@ type ElementProps = {
|
||||
setIsOpen: (open: boolean) => void;
|
||||
};
|
||||
|
||||
// TODO: replace placeholder URL with real URLs when avaialble
|
||||
const KEPLR_DASHBOARD_URL = 'https://testnet.keplr.app/';
|
||||
const HELP_URL = 'https://help.dydx.exchange/en/articles/2921366-how-do-i-create-an-account-or-sign-up';
|
||||
|
||||
export const ExternalNavKeplrDialog = ({ setIsOpen }: ElementProps) => {
|
||||
const stringGetter = useStringGetter();
|
||||
const { keplrDashboard, accountExportLearnMore } = useURLConfigs();
|
||||
|
||||
const { isTablet } = useBreakpoints();
|
||||
|
||||
return (
|
||||
@@ -31,7 +29,7 @@ export const ExternalNavKeplrDialog = ({ setIsOpen }: ElementProps) => {
|
||||
placement={isTablet ? DialogPlacement.FullScreen : DialogPlacement.Default}
|
||||
>
|
||||
<Styled.Content>
|
||||
<Styled.Button type={ButtonType.Link} size={ButtonSize.XLarge} href={KEPLR_DASHBOARD_URL}>
|
||||
<Styled.Button type={ButtonType.Link} size={ButtonSize.XLarge} href={keplrDashboard}>
|
||||
<span>
|
||||
{stringGetter({
|
||||
key: STRING_KEYS.NAVIGATE_TO_KEPLR,
|
||||
@@ -48,7 +46,7 @@ export const ExternalNavKeplrDialog = ({ setIsOpen }: ElementProps) => {
|
||||
/>
|
||||
</Styled.Button>
|
||||
|
||||
<Styled.Button type={ButtonType.Link} size={ButtonSize.XLarge} href={HELP_URL}>
|
||||
<Styled.Button type={ButtonType.Link} size={ButtonSize.XLarge} href={accountExportLearnMore}>
|
||||
<span>
|
||||
{stringGetter({
|
||||
key: STRING_KEYS.LEARN_TO_EXPORT,
|
||||
@@ -104,4 +102,5 @@ Styled.Button = styled(Button)`
|
||||
|
||||
Styled.IconButton = styled(IconButton)`
|
||||
color: var(--color-text-0);
|
||||
--color-border: var(--color-layer-6);
|
||||
`;
|
||||
|
||||
@@ -2,10 +2,10 @@ import { useMemo } from 'react';
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { useStringGetter } from '@/hooks';
|
||||
import { ChatIcon, FeedbackIcon, FileIcon, TerminalIcon } from '@/icons';
|
||||
import { useStringGetter, useURLConfigs } from '@/hooks';
|
||||
|
||||
import { ComboboxDialogMenu } from '@/components/ComboboxDialogMenu';
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
|
||||
import { isTruthy } from '@/lib/isTruthy';
|
||||
|
||||
@@ -13,39 +13,24 @@ type ElementProps = {
|
||||
setIsOpen: (open: boolean) => void;
|
||||
};
|
||||
|
||||
const HELP_LINKS = {
|
||||
apiDocumentation: 'https://v4-teacher.vercel.app/',
|
||||
helpCenter: null,
|
||||
feedback: null,
|
||||
};
|
||||
|
||||
export const HelpDialog = ({ setIsOpen }: ElementProps) => {
|
||||
const stringGetter = useStringGetter();
|
||||
const { help: helpCenter, community } = useURLConfigs();
|
||||
|
||||
const HELP_ITEMS = useMemo(
|
||||
() => [
|
||||
{
|
||||
group: 'help-items',
|
||||
items: [
|
||||
HELP_LINKS.helpCenter && {
|
||||
helpCenter && {
|
||||
value: 'help-center',
|
||||
label: stringGetter({ key: STRING_KEYS.HELP_CENTER }),
|
||||
description: stringGetter({ key: STRING_KEYS.HELP_CENTER_DESCRIPTION }),
|
||||
onSelect: () => {
|
||||
HELP_LINKS.helpCenter && globalThis.open(HELP_LINKS.helpCenter, '_blank');
|
||||
helpCenter && globalThis.open(helpCenter, '_blank');
|
||||
setIsOpen(false);
|
||||
},
|
||||
slotBefore: <FileIcon />,
|
||||
},
|
||||
HELP_LINKS.apiDocumentation && {
|
||||
value: 'api-documentation',
|
||||
label: stringGetter({ key: STRING_KEYS.API_DOCUMENTATION }),
|
||||
description: stringGetter({ key: STRING_KEYS.API_DOCUMENTATION_DESCRIPTION }),
|
||||
onSelect: () => {
|
||||
HELP_LINKS.apiDocumentation && globalThis.open(HELP_LINKS.apiDocumentation, '_blank');
|
||||
setIsOpen(false);
|
||||
},
|
||||
slotBefore: <TerminalIcon />,
|
||||
slotBefore: <Icon iconName={IconName.File} />,
|
||||
},
|
||||
globalThis?.Intercom && {
|
||||
value: 'live-chat',
|
||||
@@ -55,22 +40,22 @@ export const HelpDialog = ({ setIsOpen }: ElementProps) => {
|
||||
globalThis.Intercom('show');
|
||||
setIsOpen(false);
|
||||
},
|
||||
slotBefore: <ChatIcon />,
|
||||
slotBefore: <Icon iconName={IconName.Chat} />,
|
||||
},
|
||||
HELP_LINKS.feedback && {
|
||||
value: 'feedback',
|
||||
label: stringGetter({ key: STRING_KEYS.PROVIDE_FEEDBACK }),
|
||||
description: stringGetter({ key: STRING_KEYS.PROVIDE_FEEDBACK_DESCRIPTION }),
|
||||
community && {
|
||||
value: 'community',
|
||||
label: stringGetter({ key: STRING_KEYS.COMMUNITY }),
|
||||
description: stringGetter({ key: STRING_KEYS.COMMUNITY_DESCRIPTION }),
|
||||
onSelect: () => {
|
||||
HELP_LINKS.feedback && globalThis.open(HELP_LINKS.feedback, '_blank');
|
||||
community && globalThis.open(community, '_blank');
|
||||
setIsOpen(false);
|
||||
},
|
||||
slotBefore: <FeedbackIcon />,
|
||||
slotBefore: <Icon iconName={IconName.Discord} />,
|
||||
},
|
||||
].filter(isTruthy),
|
||||
},
|
||||
],
|
||||
[stringGetter]
|
||||
[stringGetter, helpCenter, community]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -13,16 +13,15 @@ import { Button } from '@/components/Button';
|
||||
import { Icon } from '@/components/Icon';
|
||||
import { Link } from '@/components/Link';
|
||||
|
||||
import { useAccounts, useStringGetter } from '@/hooks';
|
||||
import { useAccounts, useStringGetter, useURLConfigs } from '@/hooks';
|
||||
import { useDisplayedWallets } from '@/hooks/useDisplayedWallets';
|
||||
|
||||
import { breakpoints } from '@/styles';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
const aboutWalletsLink = `https://www.dydx.academy/video/defi-wallet`;
|
||||
|
||||
export const ChooseWallet = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
const { walletLearnMore } = useURLConfigs();
|
||||
|
||||
const displayedWallets = useDisplayedWallets();
|
||||
|
||||
@@ -63,7 +62,7 @@ export const ChooseWallet = () => {
|
||||
</Styled.Wallets>
|
||||
|
||||
<Styled.Footer>
|
||||
<Link href={aboutWalletsLink} withIcon>
|
||||
<Link href={walletLearnMore} withIcon>
|
||||
{stringGetter({ key: STRING_KEYS.ABOUT_WALLETS })}
|
||||
</Link>
|
||||
</Styled.Footer>
|
||||
|
||||
@@ -130,12 +130,14 @@ export const GenerateKeys = ({
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
setStatus(EvmDerivedAccountStatus.NotDerived);
|
||||
const { message } = parseWalletError({ error, stringGetter });
|
||||
|
||||
if (message) {
|
||||
track(AnalyticsEvent.OnboardingWalletIsNonDeterministic);
|
||||
setError(message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
await setWalletFromEvmSignature(signature);
|
||||
|
||||
@@ -37,7 +37,7 @@ export const TradeDialog = ({ isOpen, setIsOpen, slotTrigger }: ElementProps) =>
|
||||
const selectedTradeType = getSelectedTradeType(type);
|
||||
const { typeOptions } = useSelector(getInputTradeOptions, shallowEqual) ?? {};
|
||||
|
||||
const allTradeTypeItems = typeOptions?.toArray()?.map(({ type, stringKey }) => ({
|
||||
const allTradeTypeItems = (typeOptions?.toArray() ?? []).map(({ type, stringKey }) => ({
|
||||
value: type,
|
||||
label: stringGetter({
|
||||
key: stringKey as StringKey,
|
||||
@@ -73,9 +73,7 @@ export const TradeDialog = ({ isOpen, setIsOpen, slotTrigger }: ElementProps) =>
|
||||
<Styled.ToggleGroup
|
||||
items={allTradeTypeItems}
|
||||
value={selectedTradeType}
|
||||
onValueChange={(tradeType: TradeTypes) =>
|
||||
onTradeTypeChange(tradeType || selectedTradeType)
|
||||
}
|
||||
onValueChange={onTradeTypeChange}
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -209,7 +209,7 @@ export const ClosePositionForm = ({
|
||||
value: value.toString(),
|
||||
}))}
|
||||
value={percent?.toString() ?? ''}
|
||||
onValueChange={(value: any) => onSelectPercentage((value || percent?.toString()) ?? '')}
|
||||
onValueChange={onSelectPercentage}
|
||||
shape={ButtonShape.Rectangle}
|
||||
/>
|
||||
|
||||
|
||||
@@ -322,7 +322,6 @@ export const TradeForm = ({
|
||||
summary={summary ?? undefined}
|
||||
currentStep={currentStep}
|
||||
showDeposit={inputAlert?.errorAction === TradeInputErrorAction.DEPOSIT}
|
||||
showConnectWallet={inputAlert?.errorAction === TradeInputErrorAction.CONNECT_WALLET}
|
||||
/>
|
||||
</Styled.Footer>
|
||||
</Styled.TradeForm>
|
||||
|
||||
@@ -153,9 +153,7 @@ export const MarketLeverageInput = ({
|
||||
value: MustBigNumber(leverageAmount).toFixed(LEVERAGE_DECIMALS),
|
||||
}))}
|
||||
value={MustBigNumber(formattedLeverageValue).abs().toFixed(LEVERAGE_DECIMALS)} // sign agnostic
|
||||
onValueChange={(option?: number) =>
|
||||
updateLeverage(option === undefined ? formattedLeverageValue : option)
|
||||
}
|
||||
onValueChange={updateLeverage}
|
||||
shape={ButtonShape.Rectangle}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -7,9 +7,8 @@ import { DialogTypes } from '@/constants/dialogs';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { TRADE_TYPE_STRINGS, MobilePlaceOrderSteps } from '@/constants/trade';
|
||||
|
||||
import { useStringGetter } from '@/hooks';
|
||||
import { useStringGetter, useTokenConfigs } from '@/hooks';
|
||||
|
||||
import { AssetIcon } from '@/components/AssetIcon';
|
||||
import { Button } from '@/components/Button';
|
||||
import { Output, OutputType, ShowSign } from '@/components/Output';
|
||||
import { WithDetailsReceipt } from '@/components/WithDetailsReceipt';
|
||||
@@ -47,6 +46,7 @@ export const PlaceOrderButtonAndReceipt = ({
|
||||
}: ElementProps) => {
|
||||
const stringGetter = useStringGetter();
|
||||
const dispatch = useDispatch();
|
||||
const { chainTokenLabel } = useTokenConfigs();
|
||||
|
||||
const canAccountTrade = useSelector(calculateCanAccountTrade);
|
||||
const subaccountNumber = useSelector(getSubaccountId);
|
||||
@@ -88,7 +88,14 @@ export const PlaceOrderButtonAndReceipt = ({
|
||||
{/* <AssetIcon symbol="DYDX" /> */}
|
||||
</>
|
||||
),
|
||||
value: <Output type={OutputType.Asset} value={reward} useGrouping tag={reward ? "Dv4TNT" : ''} />,
|
||||
value: (
|
||||
<Output
|
||||
type={OutputType.Asset}
|
||||
value={reward}
|
||||
useGrouping
|
||||
tag={reward ? chainTokenLabel : ''}
|
||||
/>
|
||||
),
|
||||
tooltip: 'max-reward',
|
||||
},
|
||||
{
|
||||
@@ -111,7 +118,7 @@ export const PlaceOrderButtonAndReceipt = ({
|
||||
? actionStringKey
|
||||
: STRING_KEYS.UNAVAILABLE,
|
||||
buttonAction: ButtonAction.Primary,
|
||||
buttonState: { isDisabled: !shouldEnableTrade },
|
||||
buttonState: { isDisabled: !shouldEnableTrade, isLoading: hasMissingData },
|
||||
},
|
||||
|
||||
[MobilePlaceOrderSteps.PreviewOrder]: {
|
||||
@@ -148,7 +155,10 @@ export const PlaceOrderButtonAndReceipt = ({
|
||||
|
||||
const buttonState = currentStep
|
||||
? buttonStatesPerStep[currentStep].buttonState
|
||||
: { isDisabled: !shouldEnableTrade || isLoading, isLoading };
|
||||
: {
|
||||
isDisabled: !shouldEnableTrade || isLoading,
|
||||
isLoading: isLoading || hasMissingData,
|
||||
};
|
||||
|
||||
return (
|
||||
<WithDetailsReceipt detailItems={items}>
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
useTokenConfigs,
|
||||
useStringGetter,
|
||||
useAccountBalance,
|
||||
useURLConfigs,
|
||||
} from '@/hooks';
|
||||
|
||||
import { OnboardingTriggerButton } from '@/views/dialogs/OnboardingTriggerButton';
|
||||
@@ -36,10 +37,9 @@ import { truncateAddress } from '@/lib/wallet';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import { headerMixins } from '@/styles/headerMixins';
|
||||
|
||||
const explorerBaseUri = 'https://testnet.mintscan.io/dydx-testnet';
|
||||
|
||||
export const AccountMenu = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
const { mintscanBase } = useURLConfigs();
|
||||
const { isTablet } = useBreakpoints();
|
||||
const dispatch = useDispatch();
|
||||
const onboardingState = useSelector(getOnboardingState);
|
||||
@@ -92,7 +92,7 @@ export const AccountMenu = () => {
|
||||
/>
|
||||
<IconButton
|
||||
action={ButtonAction.Base}
|
||||
href={`${explorerBaseUri}/account/${dydxAddress}`}
|
||||
href={`${mintscanBase}/account/${dydxAddress}`}
|
||||
iconName={IconName.LinkOut}
|
||||
shape={ButtonShape.Square}
|
||||
type={ButtonType.Link}
|
||||
@@ -139,7 +139,7 @@ export const AccountMenu = () => {
|
||||
fractionDigits={2}
|
||||
/>
|
||||
</div>
|
||||
<AssetActions asset={DydxChainAsset.USDC} dispatch={dispatch} />
|
||||
<AssetActions asset={DydxChainAsset.USDC} dispatch={dispatch} withOnboarding />
|
||||
</div>
|
||||
</Styled.Balances>
|
||||
</Styled.AccountInfo>
|
||||
@@ -167,7 +167,7 @@ export const AccountMenu = () => {
|
||||
// value: 'ViewInExplorer',
|
||||
// icon: <Icon iconName={IconName.Etherscan} />,
|
||||
// label: stringGetter({ key: STRING_KEYS.OPEN_IN_ETHERSCAN }),
|
||||
// onSelect: () => globalThis.open(`${explorerBaseUri}/address/${address}`),
|
||||
// onSelect: () => globalThis.open(`${mintscanBase}/address/${address}`),
|
||||
// separator: true,
|
||||
// },
|
||||
...(onboardingState === OnboardingState.AccountConnected && hdKey
|
||||
@@ -212,28 +212,45 @@ export const AccountMenu = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const AssetActions = memo(({ asset, dispatch }: { asset: DydxChainAsset; dispatch: Dispatch }) => (
|
||||
<Styled.InlineRow>
|
||||
{[
|
||||
// TODO(@rosepuppy): Add withdraw action for USDC
|
||||
{
|
||||
dialogType: DialogTypes.Receive,
|
||||
iconName: IconName.Qr,
|
||||
},
|
||||
{ dialogType: DialogTypes.Transfer, iconName: IconName.Send },
|
||||
].map(({ iconName, dialogType }) => (
|
||||
<IconButton
|
||||
key={dialogType}
|
||||
action={ButtonAction.Base}
|
||||
shape={ButtonShape.Square}
|
||||
iconName={iconName}
|
||||
onClick={() =>
|
||||
dispatch(openDialog({ type: dialogType, dialogProps: { selectedAsset: asset } }))
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Styled.InlineRow>
|
||||
));
|
||||
const AssetActions = memo(
|
||||
({
|
||||
asset,
|
||||
dispatch,
|
||||
withOnboarding,
|
||||
}: {
|
||||
asset: DydxChainAsset;
|
||||
dispatch: Dispatch;
|
||||
withOnboarding?: boolean;
|
||||
}) => (
|
||||
<Styled.InlineRow>
|
||||
{[
|
||||
withOnboarding && {
|
||||
dialogType: DialogTypes.Withdraw,
|
||||
iconName: IconName.Withdraw,
|
||||
},
|
||||
withOnboarding && {
|
||||
dialogType: DialogTypes.Deposit,
|
||||
iconName: IconName.Deposit,
|
||||
},
|
||||
{
|
||||
dialogType: DialogTypes.Transfer,
|
||||
dialogProps: { selectedAsset: asset },
|
||||
iconName: IconName.Send,
|
||||
},
|
||||
]
|
||||
.filter(isTruthy)
|
||||
.map(({ iconName, dialogType, dialogProps }) => (
|
||||
<Styled.IconButton
|
||||
key={dialogType}
|
||||
action={ButtonAction.Base}
|
||||
shape={ButtonShape.Square}
|
||||
iconName={iconName}
|
||||
onClick={() => dispatch(openDialog({ type: dialogType, dialogProps }))}
|
||||
/>
|
||||
))}
|
||||
</Styled.InlineRow>
|
||||
)
|
||||
);
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
@@ -351,3 +368,7 @@ Styled.ConnectToChain = styled(Styled.Column)`
|
||||
font: var(--font-small-book);
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.IconButton = styled(IconButton)`
|
||||
--button-padding: 0 0.25rem;
|
||||
`;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import styled from 'styled-components';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import { groupBy } from 'lodash';
|
||||
|
||||
import { type Notification, NotificationStatus } from '@/constants/notifications';
|
||||
|
||||
import { useBreakpoints } from '@/hooks';
|
||||
import { useNotifications } from '@/hooks/useNotifications';
|
||||
import { CloseIcon } from '@/icons';
|
||||
|
||||
import { Button } from '@/components/Button';
|
||||
import { ButtonAction, ButtonSize } from '@/constants/buttons';
|
||||
@@ -13,10 +16,6 @@ import { DialogPlacement } from '@/components/Dialog';
|
||||
import { Output, OutputType } from '@/components/Output';
|
||||
import { IconButton } from '@/components/IconButton';
|
||||
import { Toolbar } from '@/components/Toolbar';
|
||||
import { CloseIcon } from '@/icons';
|
||||
|
||||
import styled from 'styled-components';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
type ElementProps = {
|
||||
slotTrigger?: React.ReactNode;
|
||||
@@ -27,6 +26,7 @@ export const NotificationsMenu = ({
|
||||
slotTrigger,
|
||||
placement = DialogPlacement.Sidebar,
|
||||
}: ElementProps) => {
|
||||
const { isTablet } = useBreakpoints();
|
||||
const {
|
||||
notifications,
|
||||
getDisplayData,
|
||||
@@ -173,6 +173,7 @@ export const NotificationsMenu = ({
|
||||
</$FooterToolbar>
|
||||
}
|
||||
placement={placement}
|
||||
preventClose={isTablet}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -231,7 +231,7 @@ const getOrdersTableColumnDef = ({
|
||||
key: STRING_KEYS.FILL,
|
||||
})}`,
|
||||
renderCell: ({
|
||||
asset: { symbol: assetSymbol },
|
||||
asset,
|
||||
createdAtMilliseconds,
|
||||
size,
|
||||
status,
|
||||
@@ -254,7 +254,7 @@ const getOrdersTableColumnDef = ({
|
||||
value={createdAtMilliseconds}
|
||||
/>
|
||||
<Styled.AssetIconWithStatus>
|
||||
<Styled.AssetIcon symbol={assetSymbol} />
|
||||
<Styled.AssetIcon symbol={asset?.id} />
|
||||
<Styled.StatusDot color={statusIconColor} />
|
||||
</Styled.AssetIconWithStatus>
|
||||
</>
|
||||
@@ -276,7 +276,7 @@ const getOrdersTableColumnDef = ({
|
||||
type={OutputType.Asset}
|
||||
value={size}
|
||||
fractionDigits={TOKEN_DECIMALS}
|
||||
tag={assetSymbol}
|
||||
tag={asset?.id}
|
||||
/>
|
||||
</Styled.InlineRow>
|
||||
</TableCell>
|
||||
|
||||
Reference in New Issue
Block a user