Merge branch 'main' into canvas-orderbook

This commit is contained in:
jaredvu 2024-01-03 13:44:50 -08:00
commit 29a894e124
No known key found for this signature in database
GPG Key ID: B9FE2F3F0A5D523C
67 changed files with 2501 additions and 1504 deletions

View File

@ -39,11 +39,12 @@
"@cosmjs/proto-signing": "^0.31.0",
"@cosmjs/stargate": "^0.31.0",
"@cosmjs/tendermint-rpc": "^0.31.0",
"@dydxprotocol/v4-abacus": "^1.1.4",
"@dydxprotocol/v4-client-js": "^1.0.6",
"@dydxprotocol/v4-localization": "^1.0.17",
"@dydxprotocol/v4-abacus": "^1.1.32",
"@dydxprotocol/v4-client-js": "^1.0.11",
"@dydxprotocol/v4-localization": "^1.1.5",
"@ethersproject/providers": "^5.7.2",
"@js-joda/core": "^5.5.3",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-dialog": "^1.0.4",
@ -111,7 +112,7 @@
"styled-components": "^5.3.11",
"use-latest": "^1.2.1",
"viem": "^1.12.2",
"wagmi": "^1.4.5"
"wagmi": "^1.4.12"
},
"devDependencies": {
"@babel/core": "^7.22.5",

1402
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,61 +1,12 @@
[
{
"chainId": "42161",
"tokenAddress": "0xff970a61a04b1ca14834a43f5de4533ebddb5cc8",
"name": "Arbitrum"
},
{
"chainId": "43114",
"tokenAddress": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
"name": "Avalanche"
},
{
"chainId": "8453",
"tokenAddress": "0x66627F389ae46D881773B7131139b2411980E09E",
"name": "Base"
},
{
"chainId": "1",
"tokenAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"name": "Ethereum"
},
{
"chainId": "10",
"tokenAddress": "0x7F5c764cBc14f9669B88837ca1490cCa17c31607",
"name": "OP Mainnet"
},
{
"chainId": "421613",
"tokenAddress": "0xfd064a18f3bf249cf1f87fc203e90d8f650f2d63",
"name": "Arbitrum Goerli"
},
{
"chainId": "43113",
"tokenAddress": "0x5425890298aed601595a70AB815c96711a31Bc65",
"name": "Avalanche Fuji"
},
{
"chainId": "84531",
"tokenAddress": "0xf175520c52418dfe19c8098071a252da48cd1c19",
"name": "Base Goerli"
},
{
"chainId": "5",
"tokenAddress": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F",
"name": "Ethereum Goerli"
},
{
"chainId": "grand-1",
"tokenAddress": "uusdc",
"name": "Noble Testnet"
},
{
"chainId": "420",
"tokenAddress": "0xe05606174bac4a6364b31bd0eca4bf4dd368f8c6",
"name": "OP Goerli"
},
{
"chainId": "103",
"name": "Solana Devnet"
}
]

View File

@ -0,0 +1,16 @@
{
"tradingRewardsFAQs": [
{
"questionLocalizationKey": "APP.TRADING_REWARDS.FAQ_WHO_IS_ELIGIBLE_QUESTION",
"answerLocalizationKey": "APP.TRADING_REWARDS.FAQ_WHO_IS_ELIGIBLE_ANSWER"
},
{
"questionLocalizationKey": "APP.TRADING_REWARDS.FAQ_HOW_DO_TRADING_REWARDS_WORK_QUESTION",
"answerLocalizationKey": "APP.TRADING_REWARDS.FAQ_HOW_DO_TRADING_REWARDS_WORK_ANSWER"
},
{
"questionLocalizationKey": "APP.TRADING_REWARDS.FAQ_HOW_DO_I_CLAIM_MY_REWARDS_QUESTION",
"answerLocalizationKey": "APP.TRADING_REWARDS.FAQ_HOW_DO_I_CLAIM_MY_REWARDS_ANSWER"
}
]
}

View File

@ -42,7 +42,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@ -71,7 +71,7 @@
"https://validator.v4dev.dydx.exchange"
],
"0xsquid": "https://testnet.api.0xsquid.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4dev.dydx.exchange"
},
"links": {
@ -89,7 +89,7 @@
"governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
},
"wallets": {
@ -116,7 +116,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@ -145,7 +145,7 @@
"http://54.92.118.111"
],
"0xsquid": "https://testnet.api.0xsquid.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love"
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
@ -162,7 +162,7 @@
"governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
},
"wallets": {
@ -189,7 +189,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@ -218,7 +218,7 @@
"http://validator.v4dev4.dydx.exchange"
],
"0xsquid": "https://testnet.api.0xsquid.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love"
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
@ -235,7 +235,7 @@
"governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
},
"wallets": {
@ -262,7 +262,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@ -291,7 +291,7 @@
"http://18.223.78.50"
],
"0xsquid": "https://testnet.api.0xsquid.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love"
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
@ -308,7 +308,7 @@
"governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
},
"wallets": {
@ -335,7 +335,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@ -365,7 +365,7 @@
"https://validator.v4staging.dydx.exchange"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love"
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
@ -383,7 +383,7 @@
"governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
},
"wallets": {
@ -410,7 +410,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@ -440,7 +440,7 @@
"https://validator.v4staging.dydx.exchange"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love"
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
@ -483,7 +483,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@ -513,7 +513,7 @@
"https://validator-uswest1.v4staging.dydx.exchange"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love"
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
@ -531,7 +531,7 @@
"governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
},
"wallets": {
@ -558,7 +558,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@ -591,7 +591,7 @@
"https://dydx-rpc.liquify.com/api=8878132/dydx"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
@ -610,7 +610,7 @@
"governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
},
"wallets": {
@ -637,7 +637,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@ -666,7 +666,7 @@
"https://validator.v4testnet.dydx.exchange"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
@ -686,7 +686,7 @@
"governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
},
"wallets": {
@ -713,7 +713,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@ -742,7 +742,7 @@
"https://dydx-testnet.nodefleet.org"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
@ -762,7 +762,7 @@
"governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
},
"wallets": {
@ -789,7 +789,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@ -818,7 +818,7 @@
"https://test-dydx.kingnodes.com"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
@ -838,7 +838,7 @@
"governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
},
"wallets": {
@ -865,7 +865,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@ -894,7 +894,7 @@
"https://dydx-rpc.liquify.com/api=8878132/dydx"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
@ -914,7 +914,7 @@
"governanceLearnMore": "https://help.dydx.exchange",
"stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/",
"accountExportLearnMore": "https://help.dydx.exchange",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
},
"wallets": {
@ -941,7 +941,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@ -970,7 +970,7 @@
"https://dydx-testnet-rpc.polkachu.com/"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
@ -1008,7 +1008,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@ -1037,7 +1037,7 @@
"https://dydx-testnet-full-rpc.public.blastapi.io/"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
@ -1084,7 +1084,7 @@
"ethereumChainId": "1",
"dydxChainId": "[mainnet chain id]",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "[mainnet squid integrator id]",
"isMainNet": true,
"tokens": {
@ -1126,7 +1126,7 @@
"blogs": "[HTTP link to blogs, can be null]",
"foundation": "[HTTP link to foundation, can be null]",
"initialMarginFractionLearnMore": "[HTTP link to initial margin fraction learn more, can be null]",
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
"reduceOnlyLearnMore": "[HTTP link to reduce-only learn more, can be null]",
"documentation": "[HTTP link to documentation, can be null]",
"community": "[HTTP link to community, can be null]",
"help": "[HTTP link to help page, can be null]",

View File

@ -1,454 +1,360 @@
{
"1INCH-USD":{
"name":"1inch",
"tags":[
"Defi"
],
"websiteLink":"https://1inch.io/",
"whitepaperLink":"https://github.com/1inch",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/1inch/"
},
"AAVE-USD":{
"name":"Aave",
"tags":[
"Defi"
],
"websiteLink":"https://aave.com/",
"whitepaperLink":"https://github.com/aave/protocol-v2/blob/master/aave-v2-whitepaper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/aave/"
},
"ADA-USD":{
"name":"Cardano",
"tags":[
"Layer 1"
],
"websiteLink":"https://cardano.org/",
"whitepaperLink":"https://why.cardano.org/en/introduction/motivation/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/cardano/"
},
"ALGO-USD":{
"name":"Algorand",
"tags":[
"Layer 1"
],
"websiteLink":"https://algorand.com/",
"whitepaperLink":"https://algorand.com/technology/white-papers",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/algorand/"
},
"APE-USD":{
"name":"ApeCoin",
"tags":[
],
"websiteLink":"https://apecoin.com/",
"whitepaperLink":"https://apecoin.com/about",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/apecoin-ape/"
},
"APT-USD":{
"name":"Aptos",
"tags":[
"Layer 1"
],
"websiteLink":"https://aptoslabs.com/",
"whitepaperLink":"https://aptos.dev/aptos-white-paper/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/aptos/"
},
"ARB-USD":{
"name":"Arbitrum",
"tags":[
],
"websiteLink":"https://arbitrum.io/",
"whitepaperLink":"https://github.com/OffchainLabs/nitro",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/arbitrum/"
},
"ATOM-USD":{
"name":"Cosmos",
"tags":[
"Layer 1"
],
"websiteLink":"https://cosmos.network/",
"whitepaperLink":"https://v1.cosmos.network/resources/whitepaper",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/cosmos/"
},
"AVAX-USD":{
"name":"Avalanche",
"tags":[
"Layer 1"
],
"websiteLink":"https://www.avalabs.org/",
"whitepaperLink":"https://assets.website-files.com/5d80307810123f5ffbb34d6e/6008d7bbf8b10d1eb01e7e16_Avalanche%20Platform%20Whitepaper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/avalanche/"
},
"BCH-USD":{
"name":"Bitcoin Cash",
"tags":[
"Layer 1"
],
"websiteLink":"https://bitcoincash.org/",
"whitepaperLink":"https://bitcoincash.org/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/bitcoin-cash/"
},
"BLUR-USD":{
"name":"Blur",
"tags":[
],
"websiteLink":"https://blur.io/",
"whitepaperLink":"https://docs.blur.foundation/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/blur-token/"
},
"CELO-USD":{
"name":"Celo",
"tags":[
],
"websiteLink":"https://celo.org",
"whitepaperLink":"https://docs.celo.org",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/celo/"
},
"BTC-USD":{
"name":"Bitcoin",
"tags":[
"Layer 1"
],
"websiteLink":"https://bitcoin.org/",
"whitepaperLink":"https://bitcoin.org/bitcoin.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/bitcoin/"
},
"COMP-USD":{
"name":"Compound",
"tags":[
"Defi"
],
"websiteLink":"https://compound.finance/",
"whitepaperLink":"https://compound.finance/documents/Compound.Whitepaper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/compound/"
},
"CRV-USD":{
"name":"Curve",
"tags":[
"Governance"
],
"websiteLink":"https://curve.fi/",
"whitepaperLink":"https://curve.fi/whitepaper",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/curve-dao-token/"
},
"DOGE-USD":{
"name":"Dogecoin",
"tags":[
"Layer 1"
],
"websiteLink":"https://dogecoin.com/",
"whitepaperLink":"https://github.com/dogecoin/dogecoin",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/dogecoin/"
},
"DOT-USD":{
"name":"Polkadot",
"tags":[
"Layer 1"
],
"websiteLink":"https://polkadot.network/",
"whitepaperLink":"https://polkadot.network/PolkaDotPaper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/polkadot-new/"
},
"ENJ-USD":{
"name":"Enjin",
"tags":[
],
"websiteLink":"https://enjin.io/",
"whitepaperLink":"https://cdn.enjin.io/downloads/whitepapers/enjin-coin/en.pdf/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/enjin-coin/"
},
"EOS-USD":{
"name":"EOS",
"tags":[
"Layer 1"
],
"websiteLink":"https://eos.io/",
"whitepaperLink":"https://github.com/EOSIO/Documentation/blob/master/TechnicalWhitePaper.md",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/eos/"
},
"ETC-USD":{
"name":"Ethereum Classic",
"tags":[
"Layer 1"
],
"websiteLink":"https://ethereumclassic.org/",
"whitepaperLink":"https://ethereumclassic.org/why-classic",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/ethereum-classic/"
},
"ETH-USD":{
"name":"Ethereum",
"tags":[
"Layer 1"
],
"websiteLink":"https://ethereum.org/",
"whitepaperLink":"https://ethereum.org/whitepaper/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/ethereum/",
"displayStepSize":"0.001",
"displayTickSize":"0.1"
},
"FIL-USD":{
"name":"Filecoin",
"tags":[
"Layer 1"
],
"websiteLink":"https://filecoin.io/",
"whitepaperLink":"https://filecoin.io/filecoin.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/filecoin/"
},
"HNT-USD":{
"name":"Helium",
"tags":[
"Layer 1"
],
"websiteLink":"https://www.helium.com",
"whitepaperLink":"http://whitepaper.helium.com",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/helium/"
},
"ICP-USD":{
"name":"Internet Computer",
"tags":[
"Layer 1"
],
"websiteLink":"https://dfinity.org",
"whitepaperLink":"https://dfinity.org/whitepaper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/internet-computer/"
},
"LDO-USD":{
"name":"Lido DAO",
"tags":[
"Defi"
],
"websiteLink":"https://lido.fi/",
"whitepaperLink":"https://lido.fi/static/Lido:Ethereum-Liquid-Staking.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/lido-dao/"
},
"LINK-USD":{
"name":"Chainlink",
"tags":[
"Defi"
],
"websiteLink":"https://chain.link/",
"whitepaperLink":"https://link.smartcontract.com/whitepaper",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/chainlink/"
},
"LTC-USD":{
"name":"Litecoin",
"tags":[
"Layer 1"
],
"websiteLink":"https://litecoin.org/",
"whitepaperLink":"https://litecoin.info/index.php/Main_Page",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/litecoin/"
},
"MATIC-USD":{
"name":"Polygon",
"tags":[
"Layer 2"
],
"websiteLink":"https://polygon.technology/",
"whitepaperLink":"https://polygon.technology/lightpaper-polygon.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/polygon/"
},
"MKR-USD":{
"name":"Maker",
"tags":[
"Governance"
],
"websiteLink":"https://makerdao.com",
"whitepaperLink":"https://makerdao.com/whitepaper",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/maker/"
},
"NEAR-USD":{
"name":"NEAR Protocol",
"tags":[
"Layer 1"
],
"websiteLink":"https://near.org",
"whitepaperLink":"https://near.org/papers/the-official-near-white-paper/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/near-protocol/"
},
"OP-USD":{
"name":"Optimism",
"tags":[
],
"websiteLink":"https://www.optimism.io/",
"whitepaperLink":"https://github.com/ethereum-optimism",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/optimism-ethereum/"
},
"PEPE-USD":{
"name":"Pepe",
"tags":[
],
"websiteLink":"https://www.pepe.vip/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/pepe/"
},
"RUNE-USD":{
"name":"THORChain",
"tags":[
"Layer 1"
],
"websiteLink":"https://thorchain.org",
"whitepaperLink":"https://whitepaper.io/document/709/thorchain-whitepaper",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/thorchain/"
},
"SEI-USD":{
"name":"Sei",
"tags":[
"Layer 1",
"Defi"
],
"websiteLink":"https://www.sei.io/",
"whitepaperLink":"https://github.com/sei-protocol/sei-chain/blob/3c9576fee3494ce039df684624f918dd8066ba3f/whitepaper/Sei_Whitepaper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/sei/"
},
"SHIB-USD":{
"name":"Shiba Inu",
"tags":[
],
"websiteLink":"https://shibatoken.com/",
"whitepaperLink":"https://docs.shibatoken.com/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/shiba-inu/"
},
"SNX-USD":{
"name":"Synthetix",
"tags":[
"Defi"
],
"websiteLink":"https://synthetix.io/",
"whitepaperLink":"https://docs.synthetix.io/litepaper",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/synthetix-network-token/"
},
"SOL-USD":{
"name":"Solana",
"tags":[
"Layer 1"
],
"websiteLink":"https://solana.com/",
"whitepaperLink":"https://solana.com/solana-whitepaper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/solana/"
},
"SUI-USD":{
"name":"Sui",
"tags":[
"Layer 1"
],
"websiteLink":"https://sui.io/",
"whitepaperLink":"https://github.com/MystenLabs",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/sui/"
},
"SUSHI-USD":{
"name":"Sushi",
"tags":[
"Defi"
],
"websiteLink":"https://sushi.com/",
"whitepaperLink":"https://docs.sushi.com/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/sushiswap/"
},
"TRX-USD":{
"name":"TRON",
"tags":[
"Defi"
],
"websiteLink":"https://tron.network/",
"whitepaperLink":"https://tron.network/static/doc/white_paper_v_2_0.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/tron/"
},
"UMA-USD":{
"name":"UMA",
"tags":[
"Defi"
],
"websiteLink":"https://umaproject.org/",
"whitepaperLink":"https://github.com/UMAprotocol/whitepaper",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/uma/"
},
"UNI-USD":{
"name":"Uniswap",
"tags":[
"Defi"
],
"websiteLink":"https://uniswap.org/",
"whitepaperLink":"https://uniswap.org/whitepaper-v3.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/uniswap/"
},
"WLD-USD":{
"name":"Worldcoin",
"tags":[
],
"websiteLink":"https://worldcoin.org/",
"whitepaperLink":"https://whitepaper.worldcoin.org/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/worldcoin-org/"
},
"XLM-USD":{
"name":"Stellar",
"tags":[
"Layer 1"
],
"websiteLink":"https://www.stellar.org/",
"whitepaperLink":"https://www.stellar.org/papers/stellar-consensus-protocol",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/stellar/"
},
"XMR-USD":{
"name":"Monero",
"tags":[
"Layer 1"
],
"websiteLink":"https://www.getmonero.org/",
"whitepaperLink":"https://www.getmonero.org/resources/research-lab/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/monero/"
},
"XRP-USD":{
"name":"Ripple",
"tags":[
"Layer 1"
],
"websiteLink":"https://ripple.com/currency/",
"whitepaperLink":"https://github.com/ripple",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/xrp/"
},
"XTZ-USD":{
"name":"Tezos",
"tags":[
"Layer 1"
],
"websiteLink":"https://tezos.com",
"whitepaperLink":"https://tezos.com/whitepaper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/tezos/"
},
"YFI-USD":{
"name":"Yearn",
"tags":[
"Defi"
],
"websiteLink":"https://yearn.finance/",
"whitepaperLink":"https://docs.yearn.finance/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/yearn-finance/"
},
"ZEC-USD":{
"name":"Zcash",
"tags":[
"Layer 1"
],
"websiteLink":"https://z.cash/",
"whitepaperLink":"https://z.cash/technology/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/zcash/"
},
"ZRX-USD":{
"name":"0x",
"tags":[
"Defi"
],
"websiteLink":"https://0x.org/",
"whitepaperLink":"https://0x.org/pdfs/0x_white_paper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/0x/"
}
}
"1INCH-USD": {
"name": "1inch",
"tags": ["Defi"],
"websiteLink": "https://1inch.io/",
"whitepaperLink": "https://github.com/1inch",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/1inch/"
},
"AAVE-USD": {
"name": "Aave",
"tags": ["Defi"],
"websiteLink": "https://aave.com/",
"whitepaperLink": "https://github.com/aave/protocol-v2/blob/master/aave-v2-whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/aave/"
},
"ADA-USD": {
"name": "Cardano",
"tags": ["Layer 1"],
"websiteLink": "https://cardano.org/",
"whitepaperLink": "https://why.cardano.org/en/introduction/motivation/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/cardano/"
},
"ALGO-USD": {
"name": "Algorand",
"tags": ["Layer 1"],
"websiteLink": "https://algorand.com/",
"whitepaperLink": "https://algorand.com/technology/white-papers",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/algorand/"
},
"APE-USD": {
"name": "ApeCoin",
"tags": [],
"websiteLink": "https://apecoin.com/",
"whitepaperLink": "https://apecoin.com/about",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/apecoin-ape/"
},
"APT-USD": {
"name": "Aptos",
"tags": ["Layer 1"],
"websiteLink": "https://aptoslabs.com/",
"whitepaperLink": "https://aptos.dev/aptos-white-paper/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/aptos/"
},
"ARB-USD": {
"name": "Arbitrum",
"tags": [],
"websiteLink": "https://arbitrum.io/",
"whitepaperLink": "https://github.com/OffchainLabs/nitro",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/arbitrum/"
},
"ATOM-USD": {
"name": "Cosmos",
"tags": ["Layer 1"],
"websiteLink": "https://cosmos.network/",
"whitepaperLink": "https://v1.cosmos.network/resources/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/cosmos/"
},
"AVAX-USD": {
"name": "Avalanche",
"tags": ["Layer 1"],
"websiteLink": "https://www.avalabs.org/",
"whitepaperLink": "https://assets.website-files.com/5d80307810123f5ffbb34d6e/6008d7bbf8b10d1eb01e7e16_Avalanche%20Platform%20Whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/avalanche/"
},
"BCH-USD": {
"name": "Bitcoin Cash",
"tags": ["Layer 1"],
"websiteLink": "https://bitcoincash.org/",
"whitepaperLink": "https://bitcoincash.org/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/bitcoin-cash/"
},
"BLUR-USD": {
"name": "Blur",
"tags": [],
"websiteLink": "https://blur.io/",
"whitepaperLink": "https://docs.blur.foundation/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/blur-token/"
},
"CELO-USD": {
"name": "Celo",
"tags": [],
"websiteLink": "https://celo.org",
"whitepaperLink": "https://docs.celo.org",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/celo/"
},
"BTC-USD": {
"name": "Bitcoin",
"tags": ["Layer 1"],
"websiteLink": "https://bitcoin.org/",
"whitepaperLink": "https://bitcoin.org/bitcoin.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/bitcoin/"
},
"COMP-USD": {
"name": "Compound",
"tags": ["Defi"],
"websiteLink": "https://compound.finance/",
"whitepaperLink": "https://compound.finance/documents/Compound.Whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/compound/"
},
"CRV-USD": {
"name": "Curve",
"tags": ["Governance"],
"websiteLink": "https://curve.fi/",
"whitepaperLink": "https://curve.fi/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/curve-dao-token/"
},
"DOGE-USD": {
"name": "Dogecoin",
"tags": ["Layer 1"],
"websiteLink": "https://dogecoin.com/",
"whitepaperLink": "https://github.com/dogecoin/dogecoin",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/dogecoin/"
},
"DOT-USD": {
"name": "Polkadot",
"tags": ["Layer 1"],
"websiteLink": "https://polkadot.network/",
"whitepaperLink": "https://polkadot.network/PolkaDotPaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/polkadot-new/"
},
"ENJ-USD": {
"name": "Enjin",
"tags": [],
"websiteLink": "https://enjin.io/",
"whitepaperLink": "https://cdn.enjin.io/downloads/whitepapers/enjin-coin/en.pdf/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/enjin-coin/"
},
"EOS-USD": {
"name": "EOS",
"tags": ["Layer 1"],
"websiteLink": "https://eos.io/",
"whitepaperLink": "https://github.com/EOSIO/Documentation/blob/master/TechnicalWhitePaper.md",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/eos/"
},
"ETC-USD": {
"name": "Ethereum Classic",
"tags": ["Layer 1"],
"websiteLink": "https://ethereumclassic.org/",
"whitepaperLink": "https://ethereumclassic.org/why-classic",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/ethereum-classic/"
},
"ETH-USD": {
"name": "Ethereum",
"tags": ["Layer 1"],
"websiteLink": "https://ethereum.org/",
"whitepaperLink": "https://ethereum.org/whitepaper/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/ethereum/",
"displayStepSize": "0.001",
"displayTickSize": "0.1"
},
"FIL-USD": {
"name": "Filecoin",
"tags": ["Layer 1"],
"websiteLink": "https://filecoin.io/",
"whitepaperLink": "https://filecoin.io/filecoin.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/filecoin/"
},
"HNT-USD": {
"name": "Helium",
"tags": ["Layer 1"],
"websiteLink": "https://www.helium.com",
"whitepaperLink": "http://whitepaper.helium.com",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/helium/"
},
"ICP-USD": {
"name": "Internet Computer",
"tags": ["Layer 1"],
"websiteLink": "https://dfinity.org",
"whitepaperLink": "https://dfinity.org/whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/internet-computer/"
},
"LDO-USD": {
"name": "Lido DAO",
"tags": ["Defi"],
"websiteLink": "https://lido.fi/",
"whitepaperLink": "https://lido.fi/static/Lido:Ethereum-Liquid-Staking.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/lido-dao/"
},
"LINK-USD": {
"name": "Chainlink",
"tags": ["Defi"],
"websiteLink": "https://chain.link/",
"whitepaperLink": "https://link.smartcontract.com/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/chainlink/"
},
"LTC-USD": {
"name": "Litecoin",
"tags": ["Layer 1"],
"websiteLink": "https://litecoin.org/",
"whitepaperLink": "https://litecoin.info/index.php/Main_Page",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/litecoin/"
},
"MATIC-USD": {
"name": "Polygon",
"tags": ["Layer 2"],
"websiteLink": "https://polygon.technology/",
"whitepaperLink": "https://polygon.technology/lightpaper-polygon.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/polygon/"
},
"MKR-USD": {
"name": "Maker",
"tags": ["Governance"],
"websiteLink": "https://makerdao.com",
"whitepaperLink": "https://makerdao.com/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/maker/"
},
"NEAR-USD": {
"name": "NEAR Protocol",
"tags": ["Layer 1"],
"websiteLink": "https://near.org",
"whitepaperLink": "https://near.org/papers/the-official-near-white-paper/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/near-protocol/"
},
"OP-USD": {
"name": "Optimism",
"tags": [],
"websiteLink": "https://www.optimism.io/",
"whitepaperLink": "https://github.com/ethereum-optimism",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/optimism-ethereum/"
},
"PEPE-USD": {
"name": "Pepe",
"tags": [],
"websiteLink": "https://www.pepe.vip/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/pepe/"
},
"RUNE-USD": {
"name": "THORChain",
"tags": ["Layer 1"],
"websiteLink": "https://thorchain.org",
"whitepaperLink": "https://whitepaper.io/document/709/thorchain-whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/thorchain/"
},
"SEI-USD": {
"name": "Sei",
"tags": ["Layer 1", "Defi"],
"websiteLink": "https://www.sei.io/",
"whitepaperLink": "https://github.com/sei-protocol/sei-chain/blob/3c9576fee3494ce039df684624f918dd8066ba3f/whitepaper/Sei_Whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/sei/"
},
"SHIB-USD": {
"name": "Shiba Inu",
"tags": [],
"websiteLink": "https://shibatoken.com/",
"whitepaperLink": "https://docs.shibatoken.com/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/shiba-inu/"
},
"SNX-USD": {
"name": "Synthetix",
"tags": ["Defi"],
"websiteLink": "https://synthetix.io/",
"whitepaperLink": "https://docs.synthetix.io/litepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/synthetix-network-token/"
},
"SOL-USD": {
"name": "Solana",
"tags": ["Layer 1"],
"websiteLink": "https://solana.com/",
"whitepaperLink": "https://solana.com/solana-whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/solana/"
},
"SUI-USD": {
"name": "Sui",
"tags": ["Layer 1"],
"websiteLink": "https://sui.io/",
"whitepaperLink": "https://github.com/MystenLabs",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/sui/"
},
"SUSHI-USD": {
"name": "Sushi",
"tags": ["Defi"],
"websiteLink": "https://sushi.com/",
"whitepaperLink": "https://docs.sushi.com/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/sushiswap/"
},
"TIA-USD": {
"name": "Celestia",
"tags": ["Layer 1"],
"websiteLink": "https://celestia.org/",
"whitepaperLink": "https://arxiv.org/pdf/1905.09274.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/celestia/"
},
"TRX-USD": {
"name": "TRON",
"tags": ["Defi"],
"websiteLink": "https://tron.network/",
"whitepaperLink": "https://tron.network/static/doc/white_paper_v_2_0.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/tron/"
},
"UMA-USD": {
"name": "UMA",
"tags": ["Defi"],
"websiteLink": "https://umaproject.org/",
"whitepaperLink": "https://github.com/UMAprotocol/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/uma/"
},
"UNI-USD": {
"name": "Uniswap",
"tags": ["Defi"],
"websiteLink": "https://uniswap.org/",
"whitepaperLink": "https://uniswap.org/whitepaper-v3.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/uniswap/"
},
"WLD-USD": {
"name": "Worldcoin",
"tags": [],
"websiteLink": "https://worldcoin.org/",
"whitepaperLink": "https://whitepaper.worldcoin.org/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/worldcoin-org/"
},
"XLM-USD": {
"name": "Stellar",
"tags": ["Layer 1"],
"websiteLink": "https://www.stellar.org/",
"whitepaperLink": "https://www.stellar.org/papers/stellar-consensus-protocol",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/stellar/"
},
"XMR-USD": {
"name": "Monero",
"tags": ["Layer 1"],
"websiteLink": "https://www.getmonero.org/",
"whitepaperLink": "https://www.getmonero.org/resources/research-lab/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/monero/"
},
"XRP-USD": {
"name": "Ripple",
"tags": ["Layer 1"],
"websiteLink": "https://ripple.com/currency/",
"whitepaperLink": "https://github.com/ripple",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/xrp/"
},
"XTZ-USD": {
"name": "Tezos",
"tags": ["Layer 1"],
"websiteLink": "https://tezos.com",
"whitepaperLink": "https://tezos.com/whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/tezos/"
},
"YFI-USD": {
"name": "Yearn",
"tags": ["Defi"],
"websiteLink": "https://yearn.finance/",
"whitepaperLink": "https://docs.yearn.finance/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/yearn-finance/"
},
"ZEC-USD": {
"name": "Zcash",
"tags": ["Layer 1"],
"websiteLink": "https://z.cash/",
"whitepaperLink": "https://z.cash/technology/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/zcash/"
},
"ZRX-USD": {
"name": "0x",
"tags": ["Defi"],
"websiteLink": "https://0x.org/",
"whitepaperLink": "https://0x.org/pdfs/0x_white_paper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/0x/"
}
}

BIN
public/currencies/tia.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,4 +1,4 @@
import { Suspense } from 'react';
import { lazy, Suspense } from 'react';
import { Navigate, Route, Routes } from 'react-router-dom';
import styled, { AnyStyledComponent, css } from 'styled-components';
import { WagmiConfig } from 'wagmi';
@ -25,16 +25,6 @@ import { SubaccountProvider } from '@/hooks/useSubaccount';
import { GuardedMobileRoute } from '@/components/GuardedMobileRoute';
import MarketsPage from '@/pages/markets/Markets';
import PortfolioPage from '@/pages/portfolio/Portfolio';
import { AlertsPage } from '@/pages/AlertsPage';
import ProfilePage from '@/pages/Profile';
import { SettingsPage } from '@/pages/settings/Settings';
import TradePage from '@/pages/trade/Trade';
import { RewardsPage } from '@/pages/rewards/RewardsPage';
import { TermsOfUsePage } from '@/pages/TermsOfUsePage';
import { PrivacyPolicyPage } from '@/pages/PrivacyPolicyPage';
import { HeaderDesktop } from '@/layout/Header/HeaderDesktop';
import { FooterDesktop } from '@/layout/Footer/FooterDesktop';
import { FooterMobile } from '@/layout/Footer/FooterMobile';
@ -46,11 +36,22 @@ import { config } from '@/lib/wagmi';
import { breakpoints } from '@/styles';
import { layoutMixins } from '@/styles/layoutMixins';
import { LoadingSpace } from './components/Loading/LoadingSpinner';
import '@/styles/constants.css';
import '@/styles/fonts.css';
import '@/styles/web3modal.css';
const MarketsPage = lazy(() => import('@/pages/markets/Markets'));
const PortfolioPage = lazy(() => import('@/pages/portfolio/Portfolio'));
const AlertsPage = lazy(() => import('@/pages/AlertsPage'));
const ProfilePage = lazy(() => import('@/pages/Profile'));
const SettingsPage = lazy(() => import('@/pages/settings/Settings'));
const TradePage = lazy(() => import('@/pages/trade/Trade'));
const RewardsPage = lazy(() => import('@/pages/rewards/RewardsPage'));
const TermsOfUsePage = lazy(() => import('@/pages/TermsOfUsePage'));
const PrivacyPolicyPage = lazy(() => import('@/pages/PrivacyPolicyPage'));
const queryClient = new QueryClient();
const Content = () => {
@ -69,7 +70,7 @@ const Content = () => {
{isNotTablet && <HeaderDesktop />}
<Styled.Main>
<Suspense fallback={null}>
<Suspense fallback={<LoadingSpace id="main" />}>
<Routes>
<Route path={AppRoute.Trade}>
<Route path=":market" element={<TradePage />} />

View File

@ -0,0 +1,119 @@
import styled, { keyframes, type AnyStyledComponent } from 'styled-components';
import { Root, Item, Header, Trigger, Content } from '@radix-ui/react-accordion';
import { layoutMixins } from '@/styles/layoutMixins';
import { PlusIcon } from '@/icons';
export type AccordionItem = {
header: React.ReactNode;
content: React.ReactNode;
};
type AccordionProps = {
items: AccordionItem[];
className?: string;
};
export const Accordion = ({ items, className }: AccordionProps) => (
<Styled.Root className={className} type="single" collapsible>
{items.map(({ header, content }, idx) => (
<Styled.Item key={idx} value={idx.toString()}>
<Header>
<Styled.Trigger>
{header}
<Styled.Icon>
<PlusIcon />
</Styled.Icon>
</Styled.Trigger>
</Header>
<Styled.Content>{content}</Styled.Content>
</Styled.Item>
))}
</Styled.Root>
);
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Root = styled(Root)`
> *:not(:last-child) {
border-bottom: var(--border-width) solid var(--border-color);
}
`;
Styled.Item = styled(Item)``;
Styled.Icon = styled.div`
display: inline-flex;
justify-content: center;
align-items: center;
width: 2.25rem;
height: 2.25rem;
--color-border: var(--color-layer-6);
color: var(--color-text-1);
background-color: var(--color-layer-5);
border: solid var(--border-width) var(--color-border);
border-radius: 50%;
font: var(--font-small-book);
svg {
height: 1.125em;
width: 1.125em;
}
`;
Styled.Trigger = styled(Trigger)`
${layoutMixins.spacedRow}
width: 100%;
padding: 1rem 0.75rem;
color: var(--color-text-1);
text-align: start;
&:hover {
${Styled.Icon} {
color: var(--color-text-2);
filter: brightness(1.1);
}
}
svg {
color: var(--color-text-0);
transition: transform 0.3s var(--ease-out-expo);
}
&[data-state='open'] svg {
transform: rotate(45deg);
}
`;
Styled.Content = styled(Content)`
overflow: hidden;
margin: 0 0.75rem 1rem;
color: var(--color-text-0);
&[data-state='open'] {
animation: ${keyframes`
from {
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
`} 0.3s var(--ease-out-expo);
}
&[data-state='closed'] {
animation: ${keyframes`
from {
height: var(--radix-accordion-content-height);
}
to {
height: 0;
}
`} 0.1s var(--ease-in-expo);
}
`;

View File

@ -45,6 +45,7 @@ const assetIcons = {
SOL: '/currencies/sol.png',
SUI: '/currencies/sui.png',
SUSHI: '/currencies/sushi.png',
TIA: '/currencies/tia.png',
TRX: '/currencies/trx.png',
UMA: '/currencies/uma.png',
UNI: '/currencies/uni.png',
@ -71,8 +72,13 @@ export const AssetIcon = ({
}: {
symbol?: Nullable<string>;
className?: string;
}) =>
isAssetSymbol(symbol) ? <Styled.Img src={assetIcons[symbol]} className={className} /> : null;
}) => (
<Styled.Img
src={isAssetSymbol(symbol) ? assetIcons[symbol] : '/currencies/unavailable.png'}
className={className}
alt={symbol}
/>
);
const Styled: Record<string, AnyStyledComponent> = {};

View File

@ -31,10 +31,12 @@ type ElementProps<TabItemsValue> = {
};
type StyleProps = {
fullWidthTabs?: boolean;
className?: string;
fullWidthTabs?: boolean;
};
export type CollapsibleTabsProps<TabItemsValue> = ElementProps<TabItemsValue> & StyleProps;
export const CollapsibleTabs = <TabItemsValue extends string>({
defaultValue,
items,
@ -43,8 +45,9 @@ export const CollapsibleTabs = <TabItemsValue extends string>({
onOpenChange,
fullWidthTabs,
className,
}: ElementProps<TabItemsValue> & StyleProps) => {
}: CollapsibleTabsProps<TabItemsValue>) => {
const [value, setValue] = useState(defaultValue);
const currentItem = items.find((item) => item.value === value);
@ -86,8 +89,8 @@ export const CollapsibleTabs = <TabItemsValue extends string>({
</Styled.Header>
<Styled.CollapsibleContent>
{items.map(({ value, content }) => (
<Styled.TabsContent key={value} value={value}>
{items.map(({ asChild, value, content }) => (
<Styled.TabsContent key={value} asChild={asChild} value={value}>
{content}
</Styled.TabsContent>
))}
@ -219,7 +222,6 @@ Styled.Header = styled.header`
Styled.CollapsibleContent = styled(CollapsibleContent)`
${layoutMixins.stack}
${layoutMixins.perspectiveArea}
box-shadow: none;
`;

View File

@ -55,6 +55,7 @@ import {
OverviewIcon,
PencilIcon,
PlayIcon,
PlusIcon,
PositionsIcon,
PriceChartIcon,
PrivacyIcon,
@ -130,6 +131,7 @@ export enum IconName {
Overview = 'Overview',
Pencil = 'Pencil',
Play = 'Play',
Plus = 'Plus',
Positions = 'Positions',
PriceChart = 'PriceChart',
Privacy = 'Privacy',
@ -204,6 +206,7 @@ const icons = {
[IconName.Overview]: OverviewIcon,
[IconName.Pencil]: PencilIcon,
[IconName.Play]: PlayIcon,
[IconName.Plus]: PlusIcon,
[IconName.Positions]: PositionsIcon,
[IconName.PriceChart]: PriceChartIcon,
[IconName.Privacy]: PrivacyIcon,

View File

@ -10,7 +10,7 @@ import {
USD_DECIMALS,
} from '@/constants/numbers';
import { BIG_NUMBERS, MustBigNumber } from '@/lib/numbers';
import { BIG_NUMBERS } from '@/lib/numbers';
import { useLocaleSeparators } from '@/hooks';
export enum InputType {
@ -29,31 +29,38 @@ type StyleProps = {
type ElementProps = {
type?: InputType;
value?: string | number | null;
allowNegative?: boolean;
decimals?: number;
disabled?: boolean;
id?: string;
max?: number;
onBlur?: () => void;
onChange?:
| Dispatch<SetStateAction<string>>
| React.ReactEventHandler<HTMLInputElement>
| ((values: NumberFormatValues, e: SourceInfo) => void);
onFocus?: () => void;
onInput?: ({
value,
floatValue,
formattedValue,
}: {
value: string;
floatValue?: number;
formattedValue: string;
}) => void;
placeholder?: string;
};
export type InputProps = ElementProps & StyleProps;
type ConditionalProps =
| {
allowNegative?: boolean;
decimals?: number;
max?: number;
onChange?: (values: NumberFormatValues, e: SourceInfo) => void;
onInput?: ({
value,
floatValue,
formattedValue,
}: {
value: string;
floatValue?: number;
formattedValue: string;
}) => void;
}
| {
allowNegative?: never;
decimals?: never;
max?: never;
onChange?: Dispatch<SetStateAction<string>> | React.ReactEventHandler<HTMLInputElement>;
onInput?: (e: SyntheticInputEvent) => void;
};
export type InputProps = ElementProps & StyleProps & ConditionalProps;
export const Input = forwardRef<HTMLInputElement, InputProps>(
(
@ -138,6 +145,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
ref={ref as React.Ref<typeof NumericFormat<unknown>>}
id={id}
// NumericFormat
valueIsNumericString
allowNegative={allowNegative}
decimalScale={decimals}
decimalSeparator={LOCALE_DECIMAL_SEPARATOR}
@ -161,7 +169,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
? undefined
: Number(formattedValue.replace(',', '.'));
onInput?.({ value, floatValue, formattedValue });
onInput?.({ value, floatValue, formattedValue, ...e });
}}
// Native
disabled={disabled}
@ -190,7 +198,6 @@ Styled.InputContainer = styled.div`
border-radius: inherit;
input {
user-select: all;
flex: 1;
width: 100%;
}

View File

@ -43,6 +43,7 @@ import { layoutMixins } from '@/styles/layoutMixins';
import { Icon, IconName } from './Icon';
import { Tag } from './Tag';
import { MustBigNumber } from '@/lib/numbers';
export { TableCell } from './Table/TableCell';
export { TableColumnHeader } from './Table/TableColumnHeader';
@ -161,10 +162,7 @@ export const Table = <TableRowData extends object, TableRowKey extends Key>({
? // String
collator.compare(first as string, second as string)
: // Number
Math.sign(
(parseInt(first as string) || (first as number)) -
(parseInt(second as string) || (second as number))
)) *
MustBigNumber(first).comparedTo(MustBigNumber(second))) *
// Flip the direction if descending order is specified.
(sortDirection === 'descending' ? -1 : 1)
);

View File

@ -42,6 +42,8 @@ type StyleProps = {
className?: string;
};
export type TabsProps<TabItemsValue> = ElementProps<TabItemsValue> & StyleProps;
export const Tabs = <TabItemsValue extends string>({
defaultValue,
value,
@ -111,6 +113,7 @@ export const Tabs = <TabItemsValue extends string>({
{items.map(({ asChild, value, content, forceMount }) => (
<Styled.Content
key={value}
asChild={asChild}
value={value}
forceMount={forceMount}
$hide={forceMount && currentItem?.value !== value}
@ -244,7 +247,6 @@ Styled.Trigger = styled(Trigger)<{ $withBorders?: boolean }>`
Styled.Stack = styled.div`
${layoutMixins.stack}
${layoutMixins.perspectiveArea}
box-shadow: none;
`;

View File

@ -26,34 +26,3 @@ export enum FundingDirection {
ToShort = 'ToShort',
ToLong = 'ToLong',
}
export const MARKETS_TO_DISPLAY = [
'BTC-USD',
'ETH-USD',
'LINK-USD',
'SOL-USD',
'MATIC-USD',
'ATOM-USD',
'AVAX-USD',
'APE-USD',
'XRP-USD',
'UNI-USD',
'ADA-USD',
'TRX-USD',
'OP-USD',
'MKR-USD',
'DOGE-USD',
'SHIB-USD',
'COMP-USD',
'LDO-USD',
'NEAR-USD',
'APT-USD',
'SUI-USD',
'DOT-USD',
'ETC-USD',
'ARB-USD',
'CRV-USD',
'BLUR-USD',
'FIL-USD',
'XLM-USD',
];

View File

@ -122,9 +122,15 @@ export type NotificationDisplayData = {
toastDuration?: number;
};
export enum TransferNotificationTypes {
Withdrawal = 'withdrawal',
Deposit = 'deposit',
}
// Notification types
export type TransferNotifcation = {
txHash: string;
type?: TransferNotificationTypes;
toChainId?: string;
fromChainId?: string;
toAmount?: number;

View File

@ -10,6 +10,7 @@ export const LEVERAGE_DECIMALS = 2;
export const TOKEN_DECIMALS = 4;
export const LARGE_TOKEN_DECIMALS = 2;
export const FEE_DECIMALS = 3;
export const FUNDING_DECIMALS = 6;
export const QUANTUM_MULTIPLIER = 1_000_000;
@ -18,3 +19,7 @@ export enum NumberSign {
Negative = 'Negative',
Neutral = 'Neutral',
}
// Deposit/Withdraw
export const MAX_CCTP_TRANSFER_AMOUNT = 1_000_000;
export const MAX_PRICE_IMPACT = 0.02; // 2%

View File

@ -1,6 +1,10 @@
import { type TooltipStrings, TOOLTIP_STRING_KEYS } from '@/constants/localization';
export const depositTooltips: TooltipStrings = {
'minimum-deposit-amount': ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.MINIMUM_DEPOSIT_AMOUNT_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.MINIMUM_DEPOSIT_AMOUNT_BODY }),
}),
swap: ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.SWAP_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.SWAP_BODY }),

View File

@ -5,6 +5,10 @@ export const withdrawTooltips: TooltipStrings = {
title: stringGetter({ key: TOOLTIP_STRING_KEYS.FAST_WITHDRAW_FEE_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.FAST_WITHDRAW_FEE_BODY }),
}),
'minimum-amount-received': ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.MINIMUM_AMOUNT_RECEIVED_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.MINIMUM_AMOUNT_RECEIVED_BODY }),
}),
'withdraw-types': ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.WITHDRAW_TYPES_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.WITHDRAW_TYPES_BODY }),

View File

@ -5,16 +5,15 @@ import { useMatch, useNavigate } from 'react-router-dom';
import { LocalStorageKey } from '@/constants/localStorage';
import { DEFAULT_MARKETID } from '@/constants/markets';
import { AppRoute } from '@/constants/routes';
import { useLocalStorage } from '@/hooks/useLocalStorage';
import { getSelectedNetwork } from '@/state/appSelectors';
import { closeDialogInTradeBox } from '@/state/dialogs';
import { setCurrentMarketId } from '@/state/perpetuals';
import { getMarketIds } from '@/state/perpetualsSelectors';
import abacusStateManager from '@/lib/abacus';
import { useLocalStorage } from './useLocalStorage';
import { getMarketIds } from '@/state/perpetualsSelectors';
export const useCurrentMarketId = () => {
const navigate = useNavigate();
const match = useMatch(`/${AppRoute.Trade}/:marketId`);
@ -33,17 +32,35 @@ export const useCurrentMarketId = () => {
if (marketIds.length === 0) return marketId ?? lastViewedMarket;
if (!marketIds.includes(marketId ?? lastViewedMarket)) return DEFAULT_MARKETID;
return marketId ?? lastViewedMarket;
}, [marketIds, marketId]);
}, [hasMarketIds, marketId]);
useEffect(() => {
setLastViewedMarket(validId);
dispatch(setCurrentMarketId(validId));
dispatch(closeDialogInTradeBox());
// If v4_markets has not been subscribed to yet or marketId is not specified, default to validId
if (!hasMarketIds || !marketId) {
setLastViewedMarket(validId);
dispatch(setCurrentMarketId(validId));
dispatch(closeDialogInTradeBox());
navigate(`${AppRoute.Trade}/${validId}`, {
replace: true,
});
}, [validId]);
if (validId !== marketId) {
navigate(`${AppRoute.Trade}/${validId}`, {
replace: true,
});
}
} else {
// If v4_markets has been subscribed to, check if marketId is valid
if (!marketIds.includes(marketId)) {
// If marketId is not valid (i.e. final settlement), navigate to markets page
navigate(AppRoute.Markets, {
replace: true,
});
} else {
// If marketId is valid, set currentMarketId
setLastViewedMarket(marketId);
dispatch(setCurrentMarketId(marketId));
dispatch(closeDialogInTradeBox());
}
}
}, [hasMarketIds, marketId]);
useEffect(() => {
// Check for marketIds otherwise Abacus will silently fail its isMarketValid check

View File

@ -1,14 +1,7 @@
import { useMemo } from 'react';
import { useSelector, shallowEqual } from 'react-redux';
import {
MarketFilters,
MARKET_FILTER_LABELS,
type MarketData,
MARKETS_TO_DISPLAY,
} from '@/constants/markets';
import { testFlags } from '@/lib/testFlags';
import { MarketFilters, MARKET_FILTER_LABELS, type MarketData } from '@/constants/markets';
import { getAssets } from '@/state/assetsSelectors';
import { getPerpetualMarkets } from '@/state/perpetualsSelectors';
@ -49,9 +42,7 @@ export const useMarketsData = (
}, [allPerpetualMarkets, allAssets]);
const filteredMarkets = useMemo(() => {
const filtered = markets
.filter(filterFunctions[filter])
.filter(({ id }) => (testFlags.displayAllMarkets ? true : MARKETS_TO_DISPLAY.includes(id)));
const filtered = markets.filter(filterFunctions[filter]);
if (searchFilter) {
return filtered.filter(

View File

@ -20,6 +20,7 @@ import {
type NotificationTypeConfig,
NotificationType,
DEFAULT_TOAST_AUTO_CLOSE_MS,
TransferNotificationTypes,
} from '@/constants/notifications';
import { useSelectedNetwork, useStringGetter } from '@/hooks';
@ -152,20 +153,20 @@ export const notificationTypes: NotificationTypeConfig[] = [
useEffect(() => {
for (const transfer of transferNotifications) {
const { fromChainId, status, txHash, toAmount } = transfer;
const { fromChainId, status, txHash, toAmount, type } = transfer;
const isFinished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing';
const icon = <Icon iconName={isFinished ? IconName.Transfer : IconName.Clock} />;
const type =
fromChainId === ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId
? 'withdrawal'
: 'deposit';
const transferType =
type ?? fromChainId === ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId
? TransferNotificationTypes.Withdrawal
: TransferNotificationTypes.Deposit;
const title = stringGetter({
key: {
deposit: isFinished ? STRING_KEYS.DEPOSIT : STRING_KEYS.DEPOSIT_IN_PROGRESS,
withdrawal: isFinished ? STRING_KEYS.WITHDRAW : STRING_KEYS.WITHDRAW_IN_PROGRESS,
}[type],
}[transferType],
});
const toChainEta = status?.toChain?.chainData?.estimatedRouteDuration || 0;
@ -190,7 +191,7 @@ export const notificationTypes: NotificationTypeConfig[] = [
slotIcon={icon}
slotTitle={title}
transfer={transfer}
type={type}
type={transferType}
triggeredAt={transfer.triggeredAt}
notification={notification}
/>

View File

@ -31,6 +31,7 @@ import { log } from '@/lib/telemetry';
import { useAccounts } from './useAccounts';
import { useTokenConfigs } from './useTokenConfigs';
import { useDydxClient } from './useDydxClient';
import { hashFromTx } from '@/lib/hashfromTx';
type SubaccountContextType = ReturnType<typeof useSubaccountContext>;
const SubaccountContext = createContext<SubaccountContextType>({} as SubaccountContextType);
@ -293,12 +294,29 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
);
const sendSquidWithdraw = useCallback(
async (amount: number, payload: string) => {
async (amount: number, payload: string, isCctp?: boolean) => {
const cctpWithdraw = () => {
return new Promise<string>((resolve, reject) =>
abacusStateManager.cctpWithdraw((success, error, data) => {
const parsedData = JSON.parse(data);
if (success && parsedData?.code == 0) {
resolve(parsedData?.transactionHash);
} else {
reject(error);
}
})
)
}
if (isCctp) {
return await cctpWithdraw();
}
if (!subaccountClient) {
return;
}
return await sendSquidWithdrawFromSubaccount({ subaccountClient, amount, payload });
const tx = await sendSquidWithdrawFromSubaccount({ subaccountClient, amount, payload });
return hashFromTx(tx?.hash);
},
[subaccountClient, sendSquidWithdrawFromSubaccount]
);

View File

@ -11,22 +11,19 @@ export const useTradeFormInputs = () => {
const { limitPriceInput, triggerPriceInput, trailingPercentInput } = tradeFormInputValues;
useEffect(() => {
const floatValue = parseFloat(triggerPriceInput);
abacusStateManager.setTradeValue({
value: floatValue,
value: triggerPriceInput,
field: TradeInputField.triggerPrice,
});
}, [triggerPriceInput]);
useEffect(() => {
const floatValue = parseFloat(limitPriceInput);
abacusStateManager.setTradeValue({ value: floatValue, field: TradeInputField.limitPrice });
abacusStateManager.setTradeValue({ value: limitPriceInput, field: TradeInputField.limitPrice });
}, [limitPriceInput]);
useEffect(() => {
const floatValue = parseFloat(trailingPercentInput);
abacusStateManager.setTradeValue({
value: floatValue,
value: trailingPercentInput,
field: TradeInputField.trailingPercent,
});
}, [trailingPercentInput]);

View File

@ -44,6 +44,7 @@ export { default as OrderbookIcon } from './orderbook.svg';
export { default as OverviewIcon } from './overview.svg';
export { default as PencilIcon } from './pencil.svg';
export { default as PlayIcon } from './play.svg';
export { default as PlusIcon } from './plus.svg';
export { default as PortfolioIcon } from './portfolio.svg';
export { default as PositionsIcon } from './positions.svg';
export { default as PriceChartIcon } from './price-chart.svg';

1
src/icons/plus.svg Normal file
View File

@ -0,0 +1 @@
<svg fill="none" height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m6.75.75c0-.198912-.07902-.389678-.21967-.53033-.14065-.1406525-.33142-.21967-.53033-.21967s-.38968.0790175-.53033.21967c-.14065.140652-.21967.331418-.21967.53033v4.5h-4.5c-.198912 0-.389678.07902-.53033.21967-.1406525.14065-.21967.33142-.21967.53033s.0790175.38968.21967.53033c.140652.14065.331418.21967.53033.21967h4.5v4.5c0 .1989.07902.3897.21967.5303.14065.1407.33142.2197.53033.2197s.38968-.079.53033-.2197c.14065-.1406.21967-.3314.21967-.5303v-4.5h4.5c.1989 0 .3897-.07902.5303-.21967.1407-.14065.2197-.33142.2197-.53033s-.079-.38968-.2197-.53033c-.1406-.14065-.3314-.21967-.5303-.21967h-4.5z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 731 B

View File

@ -2,6 +2,7 @@ import Abacus, { type Nullable } from '@dydxprotocol/v4-abacus';
import Long from 'long';
import type { IndexedTx } from '@cosmjs/stargate';
import { GAS_MULTIPLIER, encodeJson } from '@dydxprotocol/v4-client-js';
import { EncodeObject } from '@cosmjs/proto-signing';
import {
CompositeClient,
@ -41,6 +42,7 @@ import { openDialog } from '@/state/dialogs';
import { StatefulOrderError } from '../errors';
import { bytesToBigInt } from '../numbers';
import { log } from '../telemetry';
import { hashFromTx } from '../hashfromTx';
class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
private compositeClient: CompositeClient | undefined;
@ -63,9 +65,13 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
}
setNobleWallet(nobleWallet: LocalWallet) {
this.nobleWallet = nobleWallet;
if (this.nobleClient) {
this.nobleClient.connect(nobleWallet);
try {
this.nobleWallet = nobleWallet;
if (this.nobleClient) {
this.nobleClient.connect(nobleWallet);
}
} catch (e) {
log('DydxChainTransactions/setNobleWallet', e);
}
}
@ -112,10 +118,15 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
this.compositeClient = compositeClient;
if (nobleValidatorUrl) {
this.nobleClient = new NobleClient(nobleValidatorUrl);
if (this.nobleWallet) await this.nobleClient.connect(this.nobleWallet);
try {
if (nobleValidatorUrl) {
this.nobleClient = new NobleClient(nobleValidatorUrl);
if (this.nobleWallet) await this.nobleClient.connect(this.nobleWallet);
}
} catch (e) {
log('DydxChainTransactions/connectNetwork/NobleClient', e);
}
// Dispatch custom event to notify other parts of the app that the network has been connected
const customEvent = new CustomEvent('abacus:connectNetwork', {
detail: parsedParams,
@ -201,8 +212,8 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
timeInForce as OrderTimeInForce,
goodTilTimeInSeconds ?? 0,
execution as OrderExecution,
postOnly,
reduceOnly,
postOnly ?? undefined,
reduceOnly ?? undefined,
triggerPrice ?? undefined
);
@ -340,12 +351,7 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
}
}
async sendNobleIBC(
params: {
msgTypeUrl: string,
msg: any,
}
): Promise<string> {
async sendNobleIBC(params: { msgTypeUrl: string; msg: any }): Promise<string> {
if (!this.nobleClient?.isConnected) {
throw new Error('Missing nobleClient or localWallet');
}
@ -356,15 +362,16 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
value: params.msg,
};
const fee = await this.nobleClient.simulateTransaction([ibcMsg]);
// take out fee from amount before sweeping
const amount = parseInt(ibcMsg.value.token.amount, 10) -
const amount =
parseInt(ibcMsg.value.token.amount, 10) -
Math.floor(parseInt(fee.amount[0].amount, 10) * GAS_MULTIPLIER);
if (amount <= 0) {
throw new Error('noble balance does not cover fees');
}
ibcMsg.value.token.amount = amount.toString();
const tx = await this.nobleClient.send([ibcMsg]);
@ -380,6 +387,86 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
}
}
async withdrawToNobleIBC(params: {
subaccountNumber: number;
amount: string;
ibcPayload: string;
}): Promise<string> {
if (!this.compositeClient || !this.localWallet) {
throw new Error('Missing compositeClient or localWallet');
}
const { subaccountNumber, amount, ibcPayload } = params ?? {};
const parsedIbcPayload: {
msgTypeUrl: string;
msg: any;
} = ibcPayload ? JSON.parse(atob(ibcPayload)) : undefined;
try {
const msg = this.compositeClient.withdrawFromSubaccountMessage(
new SubaccountClient(this.localWallet, subaccountNumber),
parseFloat(amount).toFixed(this.compositeClient.validatorClient.config.denoms.USDC_DECIMALS)
);
const ibcMsg: EncodeObject = {
typeUrl: parsedIbcPayload.msgTypeUrl,
value: parsedIbcPayload.msg,
};
const tx = await this.compositeClient.send(
this.localWallet,
() => Promise.resolve([msg, ibcMsg]),
false
);
return JSON.stringify({
txHash: hashFromTx(tx?.hash),
});
} catch (error) {
log('DydxChainTransactions/withdrawToNobleIBC', error);
return JSON.stringify({
error,
});
}
}
async cctpWithdraw(params: { typeUrl: string; value: any }): Promise<string> {
if (!this.nobleClient?.isConnected) {
throw new Error('Missing nobleClient or localWallet');
}
try {
const ibcMsg = {
typeUrl: params.typeUrl, // '/circle.cctp.v1.MsgDepositForBurn',
value: params.value,
};
const fee = await this.nobleClient.simulateTransaction([ibcMsg]);
// take out fee from amount before sweeping
const amount =
parseInt(ibcMsg.value.amount, 10) -
Math.floor(parseInt(fee.amount[0].amount, 10) * GAS_MULTIPLIER);
if (amount <= 0) {
throw new Error('noble balance does not cover fees');
}
ibcMsg.value.amount = amount.toString();
const tx = await this.nobleClient.send([ibcMsg]);
const parsedTx = this.parseToPrimitives(tx);
return JSON.stringify(parsedTx);
} catch (error) {
log('DydxChainTransactions/cctpWithdraw', error);
return JSON.stringify({
error,
});
}
}
async transaction(
type: TransactionTypes,
paramsInJson: Abacus.Nullable<string>,
@ -414,6 +501,17 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
callback(result);
break;
}
case TransactionType.WithdrawToNobleIBC: {
const result = await this.withdrawToNobleIBC(params);
callback(result);
break;
}
case TransactionType.CctpWithdraw: {
const result = await this.cctpWithdraw(params);
callback(result);
break;
break;
}
default: {
break;
}

View File

@ -84,8 +84,7 @@ class AbacusStateManager {
);
const appConfigs = AbacusAppConfig.Companion.forWeb;
if (!isMainnet || testFlags.withCCTP)
appConfigs.squidVersion = AbacusAppConfig.SquidVersion.V2DepositOnly;
appConfigs.squidVersion = AbacusAppConfig.SquidVersion.V2;
this.stateManager = new AsyncAbacusStateManager(
'',
@ -262,6 +261,14 @@ class AbacusStateManager {
) => void
) => this.stateManager.cancelOrder(orderId, callback);
cctpWithdraw = (
callback: (
success: boolean,
parsingError: Nullable<ParsingError>,
data: string,
) => void
): void => this.stateManager.commitCCTPWithdraw(callback);
// ------ Utils ------ //
getHistoricalPnlPeriod = (): Nullable<HistoricalPnlPeriods> =>
this.stateManager.historicalPnlPeriod;

3
src/lib/hashfromTx.ts Normal file
View File

@ -0,0 +1,3 @@
export const hashFromTx = (
txHash: string | Uint8Array
): string => `0x${Buffer.from(txHash).toString('hex')}`;

View File

@ -5,13 +5,13 @@ class TestFlags {
this.queryParams = {};
const hash = window.location.hash;
const queryIndex = hash.indexOf('?');
if (queryIndex === -1) return
if (queryIndex === -1) return;
const queryParamsString = hash.substring(queryIndex + 1);
const params = new URLSearchParams(queryParamsString);
for (const [key, value] of params) {
this.queryParams[key] = value;
this.queryParams[key] = value;
}
}
@ -19,10 +19,6 @@ class TestFlags {
return !!this.queryParams.displayInitializingMarkets;
}
get displayAllMarkets() {
return !!this.queryParams.displayAllMarkets;
}
get withCCTP() {
return !!this.queryParams.withCCTP;
}

View File

@ -146,7 +146,7 @@ const getConnectors = (walletConnectConfig: WalletConnectConfig) => [
new CoinbaseWalletConnector({
chains,
options: {
appName: 'wagmi',
appName: 'dYdX',
reloadOnDisconnect: false,
},
}),

View File

@ -1,4 +1,6 @@
import { DialogPlacement } from '@/components/Dialog';
import { NotificationsMenu } from '@/views/menus/NotificationsMenu';
export const AlertsPage = () => <NotificationsMenu placement={DialogPlacement.Inline} />;
const AlertsPage = () => <NotificationsMenu placement={DialogPlacement.Inline} />;
export default AlertsPage;

View File

@ -2,7 +2,7 @@ import styled, { AnyStyledComponent } from 'styled-components';
import { articleMixins } from '@/styles/articleMixins';
export const PrivacyPolicyPage = () => (
const PrivacyPolicyPage = () => (
<Styled.Article>
<header>
<h1>Privacy Policy</h1>
@ -10,6 +10,8 @@ export const PrivacyPolicyPage = () => (
</Styled.Article>
);
export default PrivacyPolicyPage;
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Article = styled.article`

View File

@ -2,7 +2,7 @@ import styled, { AnyStyledComponent } from 'styled-components';
import { articleMixins } from '@/styles/articleMixins';
export const TermsOfUsePage = () => (
const TermsOfUsePage = () => (
<Styled.Article>
<header>
<h1>Sample Terms of Use</h1>
@ -1076,6 +1076,8 @@ export const TermsOfUsePage = () => (
</Styled.Article>
);
export default TermsOfUsePage;
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Article = styled.article`

View File

@ -1,17 +1,20 @@
import { lazy, Suspense } from 'react';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import styled, { type AnyStyledComponent } from 'styled-components';
import { Navigate, Route, Routes } from 'react-router-dom';
import { OnboardingState } from '@/constants/account';
import { ButtonAction } from '@/constants/buttons';
import { DialogTypes } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { HistoryRoute, PortfolioRoute } from '@/constants/routes';
import { useAccountBalance, useBreakpoints, useDocumentTitle, useStringGetter } from '@/hooks';
import { layoutMixins } from '@/styles/layoutMixins';
import { FillsTable, FillsTableColumnKey } from '@/views/tables/FillsTable';
import { FundingPaymentsTable } from '@/views/tables/FundingPaymentsTable';
import { TransferHistoryTable } from '@/views/tables/TransferHistoryTable';
import { Button } from '@/components/Button';
import { Icon, IconName } from '@/components/Icon';
import { NavigationMenu } from '@/components/NavigationMenu';
import { WithSidebar } from '@/components/WithSidebar';
@ -20,15 +23,15 @@ import { getOnboardingState, getSubaccount } from '@/state/accountSelectors';
import { openDialog } from '@/state/dialogs';
import { PortfolioNavMobile } from './PortfolioNavMobile';
import { Overview } from './Overview';
import { Positions } from './Positions';
import { Orders } from './Orders';
import { Fees } from './Fees';
import { History } from './History';
import { LoadingSpace } from '@/components/Loading/LoadingSpinner';
import { layoutMixins } from '@/styles/layoutMixins';
import { Button } from '@/components/Button';
import { ButtonAction } from '@/constants/buttons';
const Overview = lazy(() => import('./Overview').then((module) => ({ default: module.Overview })));
const Positions = lazy(() =>
import('./Positions').then((module) => ({ default: module.Positions }))
);
const Orders = lazy(() => import('./Orders').then((module) => ({ default: module.Orders })));
const Fees = lazy(() => import('./Fees').then((module) => ({ default: module.Fees })));
const History = lazy(() => import('./History').then((module) => ({ default: module.History })));
export default () => {
const dispatch = useDispatch();
@ -44,49 +47,51 @@ export default () => {
useDocumentTitle(stringGetter({ key: STRING_KEYS.PORTFOLIO }));
const routesComponent = (
<Routes>
<Route path={PortfolioRoute.Overview} element={<Overview />} />
<Route path={PortfolioRoute.Positions} element={<Positions />} />
<Route path={PortfolioRoute.Orders} element={<Orders />} />
<Route path={PortfolioRoute.Fees} element={<Fees />} />
<Route path={PortfolioRoute.History} element={<History />}>
<Route index path="*" element={<Navigate to={HistoryRoute.Trades} />} />
<Route
path={HistoryRoute.Trades}
element={
<FillsTable
columnKeys={
isTablet
? [
FillsTableColumnKey.Time,
FillsTableColumnKey.TypeAmount,
FillsTableColumnKey.PriceFee,
]
: [
FillsTableColumnKey.Time,
FillsTableColumnKey.Market,
FillsTableColumnKey.Side,
FillsTableColumnKey.AmountPrice,
FillsTableColumnKey.TotalFee,
FillsTableColumnKey.Type,
FillsTableColumnKey.Liquidity,
]
}
withOuterBorder={isNotTablet}
/>
}
/>
<Route
path={HistoryRoute.Transfers}
element={<TransferHistoryTable withOuterBorder={isNotTablet} />}
/>
<Route
path={HistoryRoute.Payments}
element={<FundingPaymentsTable withOuterBorder={isNotTablet} />}
/>
</Route>
<Route path="*" element={<Navigate to={PortfolioRoute.Overview} replace />} />
</Routes>
<Suspense fallback={<LoadingSpace id="portfolio" />}>
<Routes>
<Route path={PortfolioRoute.Overview} element={<Overview />} />
<Route path={PortfolioRoute.Positions} element={<Positions />} />
<Route path={PortfolioRoute.Orders} element={<Orders />} />
<Route path={PortfolioRoute.Fees} element={<Fees />} />
<Route path={PortfolioRoute.History} element={<History />}>
<Route index path="*" element={<Navigate to={HistoryRoute.Trades} />} />
<Route
path={HistoryRoute.Trades}
element={
<FillsTable
columnKeys={
isTablet
? [
FillsTableColumnKey.Time,
FillsTableColumnKey.TypeAmount,
FillsTableColumnKey.PriceFee,
]
: [
FillsTableColumnKey.Time,
FillsTableColumnKey.Market,
FillsTableColumnKey.Side,
FillsTableColumnKey.AmountPrice,
FillsTableColumnKey.TotalFee,
FillsTableColumnKey.Type,
FillsTableColumnKey.Liquidity,
]
}
withOuterBorder={isNotTablet}
/>
}
/>
<Route
path={HistoryRoute.Transfers}
element={<TransferHistoryTable withOuterBorder={isNotTablet} />}
/>
<Route
path={HistoryRoute.Payments}
element={<FundingPaymentsTable withOuterBorder={isNotTablet} />}
/>
</Route>
<Route path="*" element={<Navigate to={PortfolioRoute.Overview} replace />} />
</Routes>
</Suspense>
);
return isTablet ? (
@ -191,7 +196,7 @@ Styled.SideBar = styled.div`
Styled.Footer = styled.div`
${layoutMixins.row}
flex-wrap: wrap;
padding: 1rem;
gap: 0.5rem;

View File

@ -0,0 +1,80 @@
import styled, { AnyStyledComponent } from 'styled-components';
import { STRING_KEYS } from '@/constants/localization';
import { layoutMixins } from '@/styles/layoutMixins';
import { useStringGetter } from '@/hooks';
import { Accordion } from '@/components/Accordion';
import { Link } from '@/components/Link';
import { Panel } from '@/components/Panel';
const REWARDS_LEARN_MORE_LINK = ''; // to be configured
export const RewardsHelpPanel = () => {
const stringGetter = useStringGetter();
return (
<Styled.HelpCard
slotHeader={
<Styled.Header>
<h3>{stringGetter({ key: STRING_KEYS.HELP })}</h3>
{REWARDS_LEARN_MORE_LINK && (
<Link withIcon href={REWARDS_LEARN_MORE_LINK}>
{stringGetter({ key: STRING_KEYS.LEARN_MORE })}
</Link>
)}
</Styled.Header>
}
>
<Accordion
items={[
{
header: 'Who is eligible for trading rewards?',
content: 'All traders are eligible for trading rewards.',
},
{
header: 'How do trading rewards work?',
content:
'Immediately after each fill, trading rewards are sent directly to the traders dYdX Chain address, based on the amount of fees paid by the trader.',
},
{
header: 'How do I claim my rewards?',
content:
'Each block, trading rewards are automatically sent directly to the traders dYdX Chain address.',
},
]}
/>
</Styled.HelpCard>
);
};
const Styled: Record<string, AnyStyledComponent> = {};
Styled.HelpCard = styled(Panel)`
--panel-content-paddingY: 0;
width: 100%;
height: max-content;
padding: 0;
gap: 0;
text-align: start;
`;
Styled.Header = styled.div`
${layoutMixins.spacedRow}
gap: 1ch;
padding: 1.25rem 1.5rem;
border-bottom: var(--border-width) solid var(--border-color);
font: var(--font-small-book);
h3 {
font: var(--font-medium-book);
color: var(--color-text-2);
}
`;
Styled.Link = styled(Link)`
display: inline-flex;
`;

View File

@ -21,7 +21,7 @@ import { DYDXBalancePanel } from './DYDXBalancePanel';
import { MigratePanel } from './MigratePanel';
import { LaunchIncentivesPanel } from './LaunchIncentivesPanel';
export const RewardsPage = () => {
const RewardsPage = () => {
const dispatch = useDispatch();
const stringGetter = useStringGetter();
const { governanceLearnMore, stakingLearnMore } = useURLConfigs();
@ -84,6 +84,8 @@ export const RewardsPage = () => {
);
};
export default RewardsPage;
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Page = styled.div`

View File

@ -25,7 +25,7 @@ import { useNetworks } from '@/views/menus/useNetworks';
import { SettingsHeader } from './SettingsHeader';
import { ComingSoonSpace } from '@/components/ComingSoon';
export const SettingsPage = () => {
const SettingsPage = () => {
const stringGetter = useStringGetter();
const { pathname } = useLocation();
const dispatch = useDispatch();
@ -105,3 +105,5 @@ export const SettingsPage = () => {
</>
);
};
export default SettingsPage;

View File

@ -1,8 +1,7 @@
import { useState } from 'react';
import { useMemo, useState } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import styled, { type AnyStyledComponent } from 'styled-components';
import { AbacusOrderStatus } from '@/constants/abacus';
import { STRING_KEYS } from '@/constants/localization';
import { useBreakpoints, useStringGetter } from '@/hooks';
@ -28,7 +27,6 @@ import {
getCurrentMarketTradeInfoNumbers,
getHasUnseenFillUpdates,
getHasUnseenOrderUpdates,
getLatestOrderStatus,
getTradeInfoNumbers,
} from '@/state/accountSelectors';
@ -82,125 +80,141 @@ export const HorizontalPanel = ({ isOpen = true, setIsOpen }: ElementProps) => {
showCurrentMarket ? numOpenOrders : numTotalOpenOrders
);
const tabItems = [
{
value: InfoSection.Position,
label: stringGetter({
key: showCurrentMarket ? STRING_KEYS.POSITION : STRING_KEYS.POSITIONS,
}),
const tabItems = useMemo(
() => [
{
value: InfoSection.Position,
label: stringGetter({
key: showCurrentMarket ? STRING_KEYS.POSITION : STRING_KEYS.POSITIONS,
}),
tag: showCurrentMarket ? null : shortenNumberForDisplay(numTotalPositions),
tag: showCurrentMarket ? null : shortenNumberForDisplay(numTotalPositions),
content: showCurrentMarket ? (
<PositionInfo showNarrowVariation={isTablet} />
) : (
<PositionsTable
columnKeys={
isTablet
? [
PositionsTableColumnKey.Details,
PositionsTableColumnKey.IndexEntry,
PositionsTableColumnKey.PnL,
]
: [
PositionsTableColumnKey.Market,
PositionsTableColumnKey.Side,
PositionsTableColumnKey.Size,
PositionsTableColumnKey.Leverage,
PositionsTableColumnKey.LiquidationAndOraclePrice,
PositionsTableColumnKey.UnrealizedPnl,
PositionsTableColumnKey.RealizedPnl,
PositionsTableColumnKey.AverageOpenAndClose,
]
}
onNavigate={() => setView(PanelView.CurrentMarket)}
/>
),
},
{
value: InfoSection.Orders,
label: stringGetter({ key: STRING_KEYS.ORDERS }),
content: showCurrentMarket ? (
<PositionInfo showNarrowVariation={isTablet} />
) : (
<PositionsTable
columnKeys={
isTablet
? [
PositionsTableColumnKey.Details,
PositionsTableColumnKey.IndexEntry,
PositionsTableColumnKey.PnL,
]
: [
PositionsTableColumnKey.Market,
PositionsTableColumnKey.Side,
PositionsTableColumnKey.Size,
PositionsTableColumnKey.Leverage,
PositionsTableColumnKey.LiquidationAndOraclePrice,
PositionsTableColumnKey.UnrealizedPnl,
PositionsTableColumnKey.RealizedPnl,
PositionsTableColumnKey.AverageOpenAndClose,
]
}
onNavigate={() => setView(PanelView.CurrentMarket)}
/>
),
},
{
asChild: true,
value: InfoSection.Orders,
label: stringGetter({ key: STRING_KEYS.ORDERS }),
slotRight: isWaitingForOrderToIndex ? (
<Styled.LoadingSpinner />
) : (
ordersTagNumber && (
<Tag type={TagType.Number} isHighlighted={hasUnseenOrderUpdates}>
{ordersTagNumber}
slotRight: isWaitingForOrderToIndex ? (
<Styled.LoadingSpinner />
) : (
ordersTagNumber && (
<Tag type={TagType.Number} isHighlighted={hasUnseenOrderUpdates}>
{ordersTagNumber}
</Tag>
)
),
content: (
<OrdersTable
currentMarket={showCurrentMarket ? currentMarketId : undefined}
columnKeys={
isTablet
? [OrdersTableColumnKey.StatusFill, OrdersTableColumnKey.PriceType]
: [
!showCurrentMarket && OrdersTableColumnKey.Market,
OrdersTableColumnKey.Status,
OrdersTableColumnKey.Side,
OrdersTableColumnKey.AmountFill,
OrdersTableColumnKey.Price,
OrdersTableColumnKey.Trigger,
OrdersTableColumnKey.GoodTil,
!isAccountViewOnly && OrdersTableColumnKey.Actions,
].filter(isTruthy)
}
/>
),
},
{
asChild: true,
value: InfoSection.Fills,
label: stringGetter({ key: STRING_KEYS.FILLS }),
slotRight: fillsTagNumber && (
<Tag type={TagType.Number} isHighlighted={hasUnseenFillUpdates}>
{fillsTagNumber}
</Tag>
)
),
),
content: (
<OrdersTable
currentMarket={showCurrentMarket ? currentMarketId : undefined}
columnKeys={
isTablet
? [OrdersTableColumnKey.StatusFill, OrdersTableColumnKey.PriceType]
: [
!showCurrentMarket && OrdersTableColumnKey.Market,
OrdersTableColumnKey.Status,
OrdersTableColumnKey.Side,
OrdersTableColumnKey.AmountFill,
OrdersTableColumnKey.Price,
OrdersTableColumnKey.Trigger,
OrdersTableColumnKey.GoodTil,
!isAccountViewOnly && OrdersTableColumnKey.Actions,
].filter(isTruthy)
}
/>
),
},
{
value: InfoSection.Fills,
label: stringGetter({ key: STRING_KEYS.FILLS }),
content: (
<FillsTable
currentMarket={showCurrentMarket ? currentMarketId : undefined}
columnKeys={
isTablet
? [
FillsTableColumnKey.Time,
FillsTableColumnKey.TypeAmount,
FillsTableColumnKey.PriceFee,
]
: [
!showCurrentMarket && FillsTableColumnKey.Market,
FillsTableColumnKey.Time,
FillsTableColumnKey.Type,
FillsTableColumnKey.Side,
FillsTableColumnKey.AmountTag,
FillsTableColumnKey.Price,
FillsTableColumnKey.TotalFee,
FillsTableColumnKey.Liquidity,
].filter(isTruthy)
}
columnWidths={{
[FillsTableColumnKey.TypeAmount]: '100%',
}}
/>
),
},
// TODO - TRCL-1693 - re-enable when funding payments are supported
// {
// value: InfoSection.Payments,
// label: stringGetter({ key: STRING_KEYS.PAYMENTS }),
slotRight: fillsTagNumber && (
<Tag type={TagType.Number} isHighlighted={hasUnseenFillUpdates}>
{fillsTagNumber}
</Tag>
),
content: (
<FillsTable
currentMarket={showCurrentMarket ? currentMarketId : undefined}
columnKeys={
isTablet
? [
FillsTableColumnKey.Time,
FillsTableColumnKey.TypeAmount,
FillsTableColumnKey.PriceFee,
]
: [
!showCurrentMarket && FillsTableColumnKey.Market,
FillsTableColumnKey.Time,
FillsTableColumnKey.Type,
FillsTableColumnKey.Side,
FillsTableColumnKey.AmountTag,
FillsTableColumnKey.Price,
FillsTableColumnKey.TotalFee,
FillsTableColumnKey.Liquidity,
].filter(isTruthy)
}
columnWidths={{
[FillsTableColumnKey.TypeAmount]: '100%',
}}
/>
),
},
// TODO - TRCL-1693 - re-enable when funding payments are supported
// {
// value: InfoSection.Payments,
// label: stringGetter({ key: STRING_KEYS.PAYMENTS }),
// tag: shortenNumberForDisplay(
// showCurrentMarket ? numFundingPayments : numTotalFundingPayments
// ),
// content: (
// <FundingPaymentsTable currentMarket={showCurrentMarket ? currentMarket?.id : undefined} />
// ),
// },
];
// tag: shortenNumberForDisplay(
// showCurrentMarket ? numFundingPayments : numTotalFundingPayments
// ),
// content: (
// <FundingPaymentsTable currentMarket={showCurrentMarket ? currentMarket?.id : undefined} />
// ),
// },
],
[
stringGetter,
currentMarketId,
showCurrentMarket,
isTablet,
isWaitingForOrderToIndex,
isAccountViewOnly,
ordersTagNumber,
fillsTagNumber,
hasUnseenFillUpdates,
hasUnseenOrderUpdates,
]
);
return isTablet ? (
<MobileTabs defaultValue={InfoSection.Position} items={tabItems} withBorders={false} />

View File

@ -877,11 +877,6 @@ export const layoutMixins: Record<
}
`,
perspectiveArea: css`
perspective: 100rem;
transform-style: preserve-3d;
`,
absolute: css`
position: absolute;
top: 0;

View File

@ -63,7 +63,6 @@ Styled.AccountInfoSectionContainer = styled.div<{ showAccountInfo?: boolean }>`
${layoutMixins.column}
height: var(--account-info-section-height);
min-height: var(--account-info-section-height);
overflow-x: clip;
${({ showAccountInfo }) =>
!showAccountInfo &&

View File

@ -203,7 +203,6 @@ const Styled: Record<string, AnyStyledComponent> = {};
Styled.Stack = styled.div`
${layoutMixins.stack}
${layoutMixins.perspectiveArea}
`;
Styled.CornerButton = styled(Button)`
@ -293,7 +292,14 @@ Styled.TransferButtons = styled.div`
Styled.ConnectedAccountInfoContainer = styled.div<{ $showHeader?: boolean }>`
${layoutMixins.column}
${layoutMixins.withOuterAndInnerBorders}
@media ${breakpoints.notTablet} {
${layoutMixins.withOuterAndInnerBorders}
}
@media ${breakpoints.tablet} {
${layoutMixins.withInnerBorder}
}
${({ $showHeader }) =>
$showHeader &&

View File

@ -2,9 +2,7 @@ import styled, { type AnyStyledComponent } from 'styled-components';
import { shallowEqual, useSelector } from 'react-redux';
import { STRING_KEYS } from '@/constants/localization';
import { MARKETS_TO_DISPLAY } from '@/constants/markets';
import { useBreakpoints, useStringGetter } from '@/hooks';
import { testFlags } from '@/lib/testFlags';
import { breakpoints } from '@/styles';
import { layoutMixins } from '@/styles/layoutMixins';
@ -34,7 +32,6 @@ export const ExchangeBillboards: React.FC<ExchangeBillboardsProps> = ({
Object.values(perpetualMarkets)
.filter(Boolean)
.filter(({ id }) => (testFlags.displayAllMarkets ? true : MARKETS_TO_DISPLAY.includes(id)))
.forEach(({ oraclePrice, perpetual }) => {
const { volume24H, trades24H, openInterest = 0 } = perpetual || {};
volume24HUSDC += volume24H ?? 0;

View File

@ -213,13 +213,8 @@ Styled.MarketDetails = styled.div`
`;
Styled.Header = styled.header`
/* max-width: fit-content; */
${layoutMixins.column}
gap: 1.25rem;
position: sticky;
bottom: 3rem;
`;
Styled.WrapRow = styled.div`

View File

@ -453,13 +453,6 @@ Styled.Actions = styled.footer`
> :last-child {
flex: 2;
}
position: sticky;
bottom: clamp(0.5rem, 7.5%, 2rem);
@media ${breakpoints.tablet} {
bottom: 0;
}
`;
Styled.Output = styled(Output)<{ sign: NumberSign; smallText?: boolean; margin?: string }>`
@ -514,6 +507,7 @@ Styled.PositionInfo = styled.div`
Styled.DetachedSection = styled(DetachedSection)`
padding: 0 1.5rem;
position: relative;
`;
Styled.DetachedScrollableSection = styled(DetachedScrollableSection)`

View File

@ -82,7 +82,6 @@ const Styled: Record<string, AnyStyledComponent> = {};
Styled.PriceChart = styled.div<{ isChartReady?: boolean }>`
${layoutMixins.stack}
${layoutMixins.perspectiveArea}
height: 100%;

View File

@ -94,7 +94,7 @@ export const FillDetailsDialog = ({ fillId, setIsOpen }: ElementProps) => {
return (
<DetailsDialog
slotIcon={<Styled.AssetIcon symbol={asset.id} />}
slotIcon={<Styled.AssetIcon symbol={asset?.id} />}
title={resources.typeStringKey && stringGetter({ key: resources.typeStringKey })}
items={detailItems}
setIsOpen={setIsOpen}

View File

@ -194,7 +194,7 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
return (
<DetailsDialog
slotIcon={<Styled.AssetIcon symbol={asset.id} />}
slotIcon={<Styled.AssetIcon symbol={asset?.id} />}
title={!resources.typeStringKey ? '' : stringGetter({ key: resources.typeStringKey })}
slotFooter={
isAccountViewOnly ? null : isOrderStatusClearable(status) ? (

View File

@ -8,6 +8,7 @@ import { ComboboxDialogMenu } from '@/components/ComboboxDialogMenu';
import { Icon, IconName } from '@/components/Icon';
import { isTruthy } from '@/lib/isTruthy';
import { breakpoints } from '@/styles';
type ElementProps = {
setIsOpen: (open: boolean) => void;
@ -72,8 +73,11 @@ export const HelpDialog = ({ setIsOpen }: ElementProps) => {
const Styled: Record<string, AnyStyledComponent> = {};
Styled.ComboboxDialogMenu = styled(ComboboxDialogMenu)`
--dialog-width: var(--dialog-small-width);
--dialog-content-paddingTop: 1rem;
--dialog-content-paddingBottom: 1rem;
--comboxDialogMenu-item-gap: 1rem;
@media ${breakpoints.notMobile} {
--dialog-width: var(--dialog-small-width);
}
`;

View File

@ -18,11 +18,7 @@ type ElementProps = {
onSelectChain: (chain: string) => void;
};
export const ChainSelectMenu = ({
label,
selectedChain,
onSelectChain,
}: ElementProps) => {
export const ChainSelectMenu = ({ label, selectedChain, onSelectChain }: ElementProps) => {
const stringGetter = useStringGetter();
const { type, depositOptions, withdrawalOptions, resources } =
useSelector(getTransferInputs, shallowEqual) || {};
@ -38,7 +34,7 @@ export const ChainSelectMenu = ({
slotBefore: <Styled.Img src={chain.iconUrl} alt="" />,
}));
const selectedOption = chains.find((item) => item.type === selectedChain);
const selectedOption = chains.find((item) => item.type === selectedChain);
return (
<SearchSelectMenu
@ -81,4 +77,4 @@ Styled.ChainRow = styled.div`
gap: 0.5rem;
color: var(--color-text-2);
font: var(--font-base-book);
`;
`;

View File

@ -10,9 +10,8 @@ import { TransferInputField, TransferInputTokenResource, TransferType } from '@/
import { AlertType } from '@/constants/alerts';
import { ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { NotificationStatus } from '@/constants/notifications';
import { NumberSign } from '@/constants/numbers';
import { ENVIRONMENT_CONFIG_MAP, isMainnet } from '@/constants/networks';
import { MAX_CCTP_TRANSFER_AMOUNT, MAX_PRICE_IMPACT, NumberSign } from '@/constants/numbers';
import type { EvmAddress } from '@/constants/wallets';
import { useAccounts, useDebounce, useStringGetter, useSelectedNetwork } from '@/hooks';
@ -253,7 +252,9 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
if (txHash) {
addTransferNotification({
txHash: txHash,
toChainId: !isCctp ? ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId : getNobleChainId(),
toChainId: !isCctp
? ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId
: getNobleChainId(),
fromChainId: chainIdStr || undefined,
toAmount: summary?.usdcSize || undefined,
triggeredAt: Date.now(),
@ -324,6 +325,21 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
return stringGetter({ key: STRING_KEYS.DEPOSIT_MORE_THAN_BALANCE });
}
if (isCctp) {
if (MustBigNumber(debouncedAmountBN).gte(MAX_CCTP_TRANSFER_AMOUNT)) {
return stringGetter({
key: STRING_KEYS.MAX_CCTP_TRANSFER_LIMIT_EXCEEDED,
params: {
MAX_CCTP_TRANSFER_AMOUNT: MAX_CCTP_TRANSFER_AMOUNT,
},
});
}
}
if (isMainnet && MustBigNumber(summary?.aggregatePriceImpact).gte(MAX_PRICE_IMPACT)) {
return stringGetter({ key: STRING_KEYS.PRICE_IMPACT_TOO_HIGH });
}
return undefined;
}, [
error,
@ -334,6 +350,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
fromAmount,
sourceToken,
stringGetter,
summary,
]);
const isDisabled =

View File

@ -1,21 +1,16 @@
import { type Dispatch, type SetStateAction, useState, type ReactNode } from 'react';
import { type Dispatch, type ReactNode, type SetStateAction, useState, useMemo } from 'react';
import styled, { type AnyStyledComponent } from 'styled-components';
import { shallowEqual, useSelector } from 'react-redux';
import type { RouteData } from '@0xsquid/sdk';
import { formatUnits } from 'viem';
import {
ButtonAction,
ButtonShape,
ButtonSize,
ButtonState,
ButtonType,
} from '@/constants/buttons';
import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons';
import { TransferInputTokenResource } from '@/constants/abacus';
import { STRING_KEYS } from '@/constants/localization';
import { NumberSign } from '@/constants/numbers';
import { NumberSign, TOKEN_DECIMALS } from '@/constants/numbers';
import { useStringGetter } from '@/hooks';
import { useStringGetter, useTokenConfigs } from '@/hooks';
import { useMatchingEvmNetwork } from '@/hooks/useMatchingEvmNetwork';
import { layoutMixins } from '@/styles/layoutMixins';
@ -34,6 +29,7 @@ import { calculateCanAccountTrade } from '@/state/accountCalculators';
import { getSubaccountBuyingPower, getSubaccountEquity } from '@/state/accountSelectors';
import { getTransferInputs } from '@/state/inputsSelectors';
import { isTruthy } from '@/lib/isTruthy';
import { MustBigNumber } from '@/lib/numbers';
import { SlippageEditor } from '../SlippageEditor';
@ -83,7 +79,8 @@ export const DepositButtonAndReceipt = ({
const { current: buyingPower, postOrder: newBuyingPower } =
useSelector(getSubaccountBuyingPower, shallowEqual) || {};
const { summary, requestPayload } = useSelector(getTransferInputs, shallowEqual) || {};
const { isCctp, summary, requestPayload } = useSelector(getTransferInputs, shallowEqual) || {};
const { usdcDecimals, usdcLabel } = useTokenConfigs();
const feeSubitems: DetailsItem[] = [];
@ -98,7 +95,7 @@ export const DepositButtonAndReceipt = ({
if (typeof summary?.bridgeFee === 'number') {
feeSubitems.push({
key: 'bridge-fees',
label: <span>Bridge Fee</span>,
label: <span>{stringGetter({ key: STRING_KEYS.BRIDGE_FEE })}</span>,
value: <Output type={OutputType.Fiat} value={summary?.bridgeFee} />,
});
}
@ -111,12 +108,68 @@ export const DepositButtonAndReceipt = ({
const totalFees = (summary?.bridgeFee || 0) + (summary?.gasFee || 0);
const { toAmount, toAmountMin } = useMemo(() => {
if (isCctp) {
return {
toAmount: summary?.toAmount,
toAmountMin: summary?.toAmountMin,
};
} else {
return {
toAmount: summary?.toAmount && formatUnits(BigInt(summary.toAmount), usdcDecimals),
toAmountMin: summary?.toAmountMin && formatUnits(BigInt(summary.toAmountMin), usdcDecimals),
};
}
}, [isCctp, summary]);
const submitButtonReceipt = [
{
key: 'expected-deposit-amount',
label: (
<span>
{stringGetter({ key: STRING_KEYS.EXPECTED_DEPOSIT_AMOUNT })} <Tag>{usdcLabel}</Tag>
</span>
),
value: <Output type={OutputType.Fiat} fractionDigits={TOKEN_DECIMALS} value={toAmount} />,
subitems: [
{
key: 'minimum-deposit-amount',
label: (
<span>
{stringGetter({ key: STRING_KEYS.MINIMUM_DEPOSIT_AMOUNT })} <Tag>{usdcLabel}</Tag>
</span>
),
value: (
<Output type={OutputType.Fiat} fractionDigits={TOKEN_DECIMALS} value={toAmountMin} />
),
tooltip: 'minimum-deposit-amount',
},
],
},
{
key: 'exchange-rate',
label: <span>{stringGetter({ key: STRING_KEYS.EXCHANGE_RATE })}</span>,
value:
typeof summary?.exchangeRate === 'number' ? (
<Styled.ExchangeRate>
<Output
type={OutputType.Asset}
value={1}
fractionDigits={0}
tag={sourceToken?.symbol}
/>
=
<Output type={OutputType.Asset} value={summary?.exchangeRate} tag={usdcLabel} />
</Styled.ExchangeRate>
) : (
<Output type={OutputType.Asset} />
),
},
{
key: 'equity',
label: (
<span>
{stringGetter({ key: STRING_KEYS.EQUITY })} <Tag>USDC</Tag>
{stringGetter({ key: STRING_KEYS.EQUITY })} <Tag>{usdcLabel}</Tag>
</span>
),
value: (
@ -133,7 +186,7 @@ export const DepositButtonAndReceipt = ({
key: 'buying-power',
label: (
<span>
{stringGetter({ key: STRING_KEYS.BUYING_POWER })} <Tag>USDC</Tag>
{stringGetter({ key: STRING_KEYS.BUYING_POWER })} <Tag>{usdcLabel}</Tag>
</span>
),
value: (
@ -146,18 +199,7 @@ export const DepositButtonAndReceipt = ({
/>
),
},
{
key: 'exchange-rate',
label: <span>{stringGetter({ key: STRING_KEYS.EXCHANGE_RATE })}</span>,
value: typeof summary?.exchangeRate === 'number' && (
<Styled.ExchangeRate>
<Output type={OutputType.Asset} value={1} fractionDigits={0} tag={sourceToken?.symbol} />
=
<Output type={OutputType.Asset} value={summary?.exchangeRate} tag="USDC" />
</Styled.ExchangeRate>
),
},
{
!isCctp && {
key: 'total-fees',
label: <span>{stringGetter({ key: STRING_KEYS.TOTAL_FEES })}</span>,
value: <Output type={OutputType.Fiat} value={totalFees} />,
@ -165,7 +207,7 @@ export const DepositButtonAndReceipt = ({
},
{
key: 'slippage',
label: <span>{stringGetter({ key: STRING_KEYS.SLIPPAGE })}</span>,
label: <span>{stringGetter({ key: STRING_KEYS.MAX_SLIPPAGE })}</span>,
value: (
<SlippageEditor
disabled
@ -178,22 +220,26 @@ export const DepositButtonAndReceipt = ({
{
key: 'estimatedRouteDuration',
label: <span>{stringGetter({ key: STRING_KEYS.ESTIMATED_TIME })}</span>,
value: typeof summary?.estimatedRouteDuration === 'number' && (
value: (
<Output
type={OutputType.Text}
value={stringGetter({
key: STRING_KEYS.X_MINUTES_LOWERCASED,
params: {
X:
summary?.estimatedRouteDuration < 60
? '< 1'
: Math.round(summary?.estimatedRouteDuration / 60),
},
})}
value={
typeof summary?.estimatedRouteDuration === 'number'
? stringGetter({
key: STRING_KEYS.X_MINUTES_LOWERCASED,
params: {
X:
summary?.estimatedRouteDuration < 60
? '< 1'
: Math.round(summary?.estimatedRouteDuration / 60),
},
})
: null
}
/>
),
},
];
].filter(isTruthy);
const isFormValid = !isDisabled && !isEditingSlippage;

View File

@ -57,7 +57,6 @@ export const TokenSelectMenu = ({ selectedToken, onSelectToken }: ElementProps)
<Tag>{type === TransferType.deposit ? 'USDC' : selectedToken?.symbol}</Tag>
</>
),
tooltip: 'swap',
},
]}
>

View File

@ -9,9 +9,9 @@ import { TransferInputField, TransferInputTokenResource, TransferType } from '@/
import { AlertType } from '@/constants/alerts';
import { ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { NotificationStatus } from '@/constants/notifications';
import { NumberSign } from '@/constants/numbers';
import { ENVIRONMENT_CONFIG_MAP, isMainnet } from '@/constants/networks';
import { TransferNotificationTypes } from '@/constants/notifications';
import { MAX_CCTP_TRANSFER_AMOUNT, MAX_PRICE_IMPACT, NumberSign } from '@/constants/numbers';
import {
useAccounts,
@ -45,6 +45,7 @@ import { getTransferInputs } from '@/state/inputsSelectors';
import abacusStateManager from '@/lib/abacus';
import { MustBigNumber } from '@/lib/numbers';
import { getNobleChainId } from '@/lib/squid';
import { TokenSelectMenu } from './TokenSelectMenu';
import { WithdrawButtonAndReceipt } from './WithdrawForm/WithdrawButtonAndReceipt';
@ -66,7 +67,8 @@ export const WithdrawForm = () => {
resources,
errors: routeErrors,
errorMessage: routeErrorMessage,
isCctp
isCctp,
summary,
} = useSelector(getTransferInputs, shallowEqual) || {};
// User input
@ -74,7 +76,6 @@ export const WithdrawForm = () => {
const [slippage, setSlippage] = useState(isCctp ? 0 : 0.01); // 0.1% slippage
const debouncedAmount = useDebounce<string>(withdrawAmount, 500);
const isValidAddress = toAddress && isAddress(toAddress);
const toToken = useMemo(
@ -167,16 +168,22 @@ export const WithdrawForm = () => {
})
);
} else {
const txHash = await sendSquidWithdraw(debouncedAmountBN.toNumber(), requestPayload.data);
if (txHash?.hash) {
const hash = `0x${Buffer.from(txHash.hash).toString('hex')}`;
const txHash = await sendSquidWithdraw(
debouncedAmountBN.toNumber(),
requestPayload.data,
isCctp
);
if (txHash) {
addTransferNotification({
txHash: hash,
fromChainId: ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId,
txHash: txHash,
type: TransferNotificationTypes.Withdrawal,
fromChainId: !isCctp
? ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId
: getNobleChainId(),
toChainId: chainIdStr || undefined,
toAmount: debouncedAmountBN.toNumber(),
triggeredAt: Date.now(),
notificationStatus: NotificationStatus.Triggered,
isCctp,
});
abacusStateManager.clearTransferInputValues();
setWithdrawAmount('');
@ -282,10 +289,7 @@ export const WithdrawForm = () => {
const errorMessage = useMemo(() => {
if (error) {
return stringGetter({
key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE,
params: { ERROR_MESSAGE: error },
});
return error;
}
if (routeErrors) {
@ -316,6 +320,21 @@ export const WithdrawForm = () => {
return stringGetter({ key: STRING_KEYS.WITHDRAW_MORE_THAN_FREE });
}
if (isCctp) {
if (MustBigNumber(debouncedAmountBN).gte(MAX_CCTP_TRANSFER_AMOUNT)) {
return stringGetter({
key: STRING_KEYS.MAX_CCTP_TRANSFER_LIMIT_EXCEEDED,
params: {
MAX_CCTP_TRANSFER_AMOUNT: MAX_CCTP_TRANSFER_AMOUNT,
},
});
}
}
if (isMainnet && MustBigNumber(summary?.aggregatePriceImpact).gte(MAX_PRICE_IMPACT)) {
return stringGetter({ key: STRING_KEYS.PRICE_IMPACT_TOO_HIGH });
}
return undefined;
}, [
error,
@ -328,6 +347,7 @@ export const WithdrawForm = () => {
toAddress,
sanctionedAddresses,
stringGetter,
summary,
]);
const isDisabled =

View File

@ -1,4 +1,4 @@
import { useState } from 'react';
import { useMemo, useState } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import styled, { type AnyStyledComponent } from 'styled-components';
import { formatUnits } from 'viem';
@ -6,14 +6,11 @@ import { formatUnits } from 'viem';
import { TransferInputTokenResource } from '@/constants/abacus';
import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { NumberSign } from '@/constants/numbers';
import { formatSeconds } from '@/lib/timeUtils';
import { NumberSign, TOKEN_DECIMALS } from '@/constants/numbers';
import { layoutMixins } from '@/styles/layoutMixins';
import { useStringGetter } from '@/hooks';
import { useAccountBalance } from '@/hooks/useAccountBalance';
import { useStringGetter, useTokenConfigs } from '@/hooks';
import { Button } from '@/components/Button';
@ -58,13 +55,9 @@ export const WithdrawButtonAndReceipt = ({
const stringGetter = useStringGetter();
const { leverage } = useSelector(getSubaccount, shallowEqual) || {};
const { summary, requestPayload } = useSelector(getTransferInputs, shallowEqual) || {};
const { isCctp, summary, requestPayload } = useSelector(getTransferInputs, shallowEqual) || {};
const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual);
const toAmount =
summary?.toAmount &&
withdrawToken?.decimals &&
formatUnits(BigInt(summary.toAmount), withdrawToken?.decimals);
const { usdcLabel } = useTokenConfigs();
const feeSubitems: DetailsItem[] = [];
@ -89,9 +82,29 @@ export const WithdrawButtonAndReceipt = ({
const showSubitemsToggle = showFeeBreakdown
? stringGetter({ key: STRING_KEYS.HIDE_ALL_DETAILS })
: stringGetter({ key: STRING_KEYS.SHOW_ALL_DETAILS });
const totalFees = (summary?.bridgeFee || 0) + (summary?.gasFee || 0);
const { toAmount, toAmountMin } = useMemo(() => {
if (isCctp) {
return {
toAmount: summary?.toAmount,
toAmountMin: summary?.toAmountMin,
};
} else {
return {
toAmount:
summary?.toAmount &&
withdrawToken?.decimals &&
formatUnits(BigInt(summary.toAmount), withdrawToken.decimals),
toAmountMin:
summary?.toAmountMin &&
withdrawToken?.decimals &&
formatUnits(BigInt(summary.toAmountMin), withdrawToken.decimals),
};
}
}, [isCctp, summary, withdrawToken]);
const submitButtonReceipt = [
{
key: 'total-fees',
@ -99,43 +112,12 @@ export const WithdrawButtonAndReceipt = ({
value: <Output type={OutputType.Fiat} value={totalFees} />,
subitems: feeSubitems,
},
{
key: 'wallet',
label: (
<span>
{stringGetter({ key: STRING_KEYS.AMOUNT_RECEIVED })}{' '}
{withdrawToken && <Tag>{withdrawToken?.symbol}</Tag>}
</span>
),
value: (
<Styled.DiffOutput
type={OutputType.Asset}
value={'0'}
newValue={toAmount}
sign={NumberSign.Positive}
withDiff={Boolean(toAmount)}
/>
),
},
{
key: 'leverage',
label: <span>{stringGetter({ key: STRING_KEYS.ACCOUNT_LEVERAGE })}</span>,
value: (
<Styled.DiffOutput
type={OutputType.Multiple}
value={leverage?.current}
newValue={leverage?.postOrder}
sign={NumberSign.Negative}
withDiff={Boolean(leverage?.current && leverage.current !== leverage?.postOrder)}
/>
),
},
{
key: 'exchange-rate',
label: <span>{stringGetter({ key: STRING_KEYS.EXCHANGE_RATE })}</span>,
value: withdrawToken && typeof summary?.exchangeRate === 'number' && (
<Styled.ExchangeRate>
<Output type={OutputType.Asset} value={1} fractionDigits={0} tag="USDC" />
<Output type={OutputType.Asset} value={1} fractionDigits={0} tag={usdcLabel} />
=
<Output
type={OutputType.Asset}
@ -146,19 +128,7 @@ export const WithdrawButtonAndReceipt = ({
),
},
{
key: 'slippage',
label: <span>{stringGetter({ key: STRING_KEYS.SLIPPAGE })}</span>,
value: (
<SlippageEditor
disabled
slippage={slippage}
setIsEditing={setIsEditingSlipapge}
setSlippage={setSlippage}
/>
),
},
{
key: 'estimatedRouteDuration',
key: 'estimated-route-duration',
label: <span>{stringGetter({ key: STRING_KEYS.ESTIMATED_TIME })}</span>,
value: typeof summary?.estimatedRouteDuration === 'number' && (
<Output
@ -175,6 +145,56 @@ export const WithdrawButtonAndReceipt = ({
/>
),
},
{
key: 'expected-amount-received',
label: (
<span>
{stringGetter({ key: STRING_KEYS.EXPECTED_AMOUNT_RECEIVED })}{' '}
{withdrawToken && <Tag>{withdrawToken?.symbol}</Tag>}
</span>
),
value: <Output type={OutputType.Asset} value={toAmount} fractionDigits={TOKEN_DECIMALS} />,
subitems: [
{
key: 'minimum-amount-received',
label: (
<span>
{stringGetter({ key: STRING_KEYS.MINIMUM_AMOUNT_RECEIVED })}{' '}
{withdrawToken && <Tag>{withdrawToken?.symbol}</Tag>}
</span>
),
value: (
<Output type={OutputType.Asset} value={toAmountMin} fractionDigits={TOKEN_DECIMALS} />
),
tooltip: 'minimum-amount-received',
},
],
},
{
key: 'slippage',
label: <span>{stringGetter({ key: STRING_KEYS.MAX_SLIPPAGE })}</span>,
value: (
<SlippageEditor
disabled
slippage={slippage}
setIsEditing={setIsEditingSlipapge}
setSlippage={setSlippage}
/>
),
},
{
key: 'leverage',
label: <span>{stringGetter({ key: STRING_KEYS.ACCOUNT_LEVERAGE })}</span>,
value: (
<Styled.DiffOutput
type={OutputType.Multiple}
value={leverage?.current}
newValue={leverage?.postOrder}
sign={NumberSign.Negative}
withDiff={Boolean(leverage?.current && leverage.current !== leverage?.postOrder)}
/>
),
},
];
const isFormValid = !isDisabled && !isEditingSlippage;

View File

@ -206,7 +206,7 @@ export const TradeForm = ({
dispatch(setTradeFormInputs({ triggerPriceInput: value }));
},
value: triggerPriceInput ?? '',
decimals: tickSizeDecimals || USD_DECIMALS,
decimals: tickSizeDecimals ?? USD_DECIMALS,
});
}
@ -226,7 +226,7 @@ export const TradeForm = ({
dispatch(setTradeFormInputs({ limitPriceInput: value }));
},
value: limitPriceInput,
decimals: tickSizeDecimals || USD_DECIMALS,
decimals: tickSizeDecimals ?? USD_DECIMALS,
});
}

View File

@ -1,10 +1,10 @@
import { type Dispatch, type SetStateAction, useCallback, useMemo } from 'react';
import { useCallback, useMemo } from 'react';
import styled, { AnyStyledComponent, css } from 'styled-components';
import { Root, Thumb, Track } from '@radix-ui/react-slider';
import _ from 'lodash';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { type Nullable, TradeInputField } from '@/constants/abacus';
import { TradeInputField } from '@/constants/abacus';
import { PositionSide } from '@/constants/trade';
import { MustBigNumber, type BigNumberish } from '@/lib/numbers';
@ -12,11 +12,11 @@ import abacusStateManager from '@/lib/abacus';
type ElementProps = {
leverage?: BigNumberish | null;
leverageInputValue: Nullable<number>;
leverageInputValue: string;
maxLeverage: BigNumberish | null;
orderSide: OrderSide;
positionSide: PositionSide;
setLeverageInputValue: Dispatch<SetStateAction<Nullable<number>>>;
setLeverageInputValue: (value: string) => void;
};
type StyleProps = { className?: string };
@ -33,6 +33,7 @@ export const LeverageSlider = ({
const leverageBN = MustBigNumber(leverage);
const maxLeverageBN = MustBigNumber(maxLeverage);
const leverageInputBN = MustBigNumber(leverageInputValue || leverage);
const leverageInputNumber = isNaN(leverageInputBN.toNumber()) ? 0 : leverageInputBN.toNumber();
const sliderConfig = useMemo(
() => ({
@ -79,12 +80,12 @@ export const LeverageSlider = ({
);
const onSliderDrag = ([newLeverage]: number[]) => {
setLeverageInputValue(newLeverage);
setLeverageInputValue(`${newLeverage}`);
debouncedSetAbacusLeverage(newLeverage);
};
const onValueCommit = ([newLeverage]: number[]) => {
setLeverageInputValue(newLeverage);
setLeverageInputValue(`${newLeverage}`);
// Ensure Abacus is updated with the latest, committed value
debouncedSetAbacusLeverage.cancel();
@ -102,7 +103,7 @@ export const LeverageSlider = ({
min={min}
max={max}
step={0.1}
value={[Math.min(Math.max(leverageInputBN.toNumber(), min), max)]}
value={[Math.min(Math.max(leverageInputNumber, min), max)]}
onValueChange={onSliderDrag}
onValueCommit={onValueCommit}
>

View File

@ -1,9 +1,8 @@
import type { Dispatch, SetStateAction } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import styled, { AnyStyledComponent } from 'styled-components';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { TradeInputField, Nullable } from '@/constants/abacus';
import { TradeInputField } from '@/constants/abacus';
import { ButtonShape } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { LEVERAGE_DECIMALS } from '@/constants/numbers';
@ -62,21 +61,7 @@ export const MarketLeverageInput = ({
const leveragePosition = postOrderLeverage ? newPositionSide : currentPositionSide;
const onLeverageInput = ({ value, floatValue }: { value: string; floatValue?: number }) => {
const newLeverage = MustBigNumber(floatValue).toFixed();
if (value === '' || newLeverage === 'NaN' || !floatValue) {
setLeverageInputValue('');
abacusStateManager.setTradeValue({
value: null,
field: TradeInputField.leverage,
});
} else {
updateLeverage(floatValue);
}
};
const updateLeverage = (newLeverage: string | number) => {
const getSignedLeverage = (newLeverage: string | number) => {
const newLeverageBN = MustBigNumber(newLeverage);
const newLeverageSignedBN =
leveragePosition === PositionSide.Short ||
@ -84,10 +69,33 @@ export const MarketLeverageInput = ({
? newLeverageBN.abs().negated()
: newLeverageBN.abs();
setLeverageInputValue(newLeverageSignedBN.toString());
return newLeverageSignedBN.toFixed(LEVERAGE_DECIMALS);
};
const onLeverageInput = ({
floatValue,
formattedValue,
}: {
floatValue?: number;
formattedValue: string;
}) => {
setLeverageInputValue(formattedValue);
const newLeverage = MustBigNumber(floatValue).toFixed();
abacusStateManager.setTradeValue({
value: newLeverageSignedBN.toFixed(LEVERAGE_DECIMALS),
value:
formattedValue === '' || newLeverage === 'NaN' ? null : getSignedLeverage(formattedValue),
field: TradeInputField.leverage,
});
};
const updateLeverage = (newLeverage: string | number) => {
const newLeverageSigned = getSignedLeverage(newLeverage);
setLeverageInputValue(newLeverageSigned);
abacusStateManager.setTradeValue({
value: newLeverageSigned,
field: TradeInputField.leverage,
});
};
@ -130,7 +138,7 @@ export const MarketLeverageInput = ({
>
<Styled.LeverageSlider
leverage={currentLeverage}
leverageInputValue={leverageInputValue}
leverageInputValue={getSignedLeverage(leverageInputValue)}
maxLeverage={maxLeverage}
orderSide={orderSide}
positionSide={currentPositionSide}

View File

@ -49,7 +49,7 @@ export const TradeSizeInputs = () => {
useSelector(getCurrentMarketConfig, shallowEqual) || {};
const { size, usdcSize, leverage, input: lastEditedInput } = inputTradeSizeData || {};
const { needsLeverage } = currentTradeInputOptions || {};
const decimals = stepSizeDecimals || TOKEN_DECIMALS;
const decimals = stepSizeDecimals ?? TOKEN_DECIMALS;
const { amountInput, usdAmountInput, leverageInput } = useSelector(
getTradeFormInputs,
@ -70,22 +70,34 @@ export const TradeSizeInputs = () => {
}
}, [size, usdcSize, leverage, lastEditedInput]);
const onSizeInput = ({ value, floatValue }: { value: string; floatValue?: number }) => {
dispatch(setTradeFormInputs({ amountInput: value }));
const onSizeInput = ({
floatValue,
formattedValue,
}: {
floatValue?: number;
formattedValue: string;
}) => {
dispatch(setTradeFormInputs({ amountInput: formattedValue }));
const newAmount = MustBigNumber(floatValue).toFixed(decimals);
abacusStateManager.setTradeValue({
value: value === '' || newAmount === 'NaN' ? null : newAmount,
value: formattedValue === '' || newAmount === 'NaN' ? null : newAmount,
field: TradeInputField.size,
});
};
const onUSDCInput = ({ value, floatValue }: { value: string; floatValue?: number }) => {
dispatch(setTradeFormInputs({ usdAmountInput: value }));
const newUsdcAmount = MustBigNumber(floatValue).toFixed();
const onUSDCInput = ({
floatValue,
formattedValue,
}: {
floatValue?: number;
formattedValue: string;
}) => {
dispatch(setTradeFormInputs({ usdAmountInput: formattedValue }));
const newUsdcAmount = MustBigNumber(floatValue).toFixed(tickSizeDecimals || USD_DECIMALS);
abacusStateManager.setTradeValue({
value: value === '' || newUsdcAmount === 'NaN' ? null : newUsdcAmount,
value: formattedValue === '' || newUsdcAmount === 'NaN' ? null : newUsdcAmount,
field: TradeInputField.usdcSize,
});
};

View File

@ -12,10 +12,11 @@ import { LoadingSpinner } from '@/components/Loading/LoadingSpinner';
import { layoutMixins } from '@/styles/layoutMixins';
import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { TransferNotificationTypes } from '@/constants/notifications';
type ElementProps = {
status?: StatusResponse;
type: 'withdrawal' | 'deposit';
type: TransferNotificationTypes;
};
type StyleProps = {
@ -46,11 +47,13 @@ export const TransferStatusSteps = ({ className, status, type }: ElementProps &
{
label: stringGetter({
key:
type === 'deposit' ? STRING_KEYS.INITIATED_DEPOSIT : STRING_KEYS.INITIATED_WITHDRAWAL,
type === TransferNotificationTypes.Deposit
? STRING_KEYS.INITIATED_DEPOSIT
: STRING_KEYS.INITIATED_WITHDRAWAL,
}),
step: TransferStatusStep.FromChain,
link:
type === 'deposit'
link:
type === TransferNotificationTypes.Deposit
? status?.fromChain?.transactionUrl
: routeStatus?.[0]?.chainId === dydxChainId && routeStatus[0].txHash
? `${mintscanTxUrl?.replace('{tx_hash}', routeStatus[0].txHash.replace('0x', ''))}`
@ -63,14 +66,20 @@ export const TransferStatusSteps = ({ className, status, type }: ElementProps &
},
{
label: stringGetter({
key: type === 'deposit' ? STRING_KEYS.DEPOSIT_TO_CHAIN : STRING_KEYS.WITHDRAW_TO_CHAIN,
key:
type === TransferNotificationTypes.Deposit
? STRING_KEYS.DEPOSIT_TO_CHAIN
: STRING_KEYS.WITHDRAW_TO_CHAIN,
params: {
CHAIN: type === 'deposit' ? 'dYdX' : status?.toChain?.chainData?.chainName,
CHAIN:
type === TransferNotificationTypes.Deposit
? 'dYdX'
: status?.toChain?.chainData?.chainName,
},
}),
step: TransferStatusStep.ToChain,
link:
type === 'withdrawal'
type === TransferNotificationTypes.Withdrawal
? status?.toChain?.transactionUrl
: currentStatus?.chainId === dydxChainId && currentStatus?.txHash
? `${mintscanTxUrl?.replace('{tx_hash}', currentStatus.txHash.replace('0x', ''))}`

View File

@ -5,7 +5,7 @@ import { useInterval, useStringGetter } from '@/hooks';
import { AlertType } from '@/constants/alerts';
import { STRING_KEYS } from '@/constants/localization';
import { TransferNotifcation } from '@/constants/notifications';
import { TransferNotifcation, TransferNotificationTypes } from '@/constants/notifications';
import { formatSeconds } from '@/lib/timeUtils';
@ -22,7 +22,7 @@ import { layoutMixins } from '@/styles/layoutMixins';
import { TransferStatusSteps } from './TransferStatusSteps';
type ElementProps = {
type: 'withdrawal' | 'deposit';
type: TransferNotificationTypes;
transfer: TransferNotifcation;
triggeredAt?: number;
};
@ -55,7 +55,7 @@ export const TransferStatusNotification = ({
useInterval({ callback: updateSecondsLeft });
const inProgressStatusString =
type === 'deposit'
type === TransferNotificationTypes.Deposit
? secondsLeft > 0
? STRING_KEYS.DEPOSIT_STATUS
: STRING_KEYS.DEPOSIT_STATUS_SHORTLY
@ -64,7 +64,7 @@ export const TransferStatusNotification = ({
: STRING_KEYS.WITHDRAW_STATUS_SHORTLY;
const statusString =
type === 'deposit'
type === TransferNotificationTypes.Deposit
? status?.squidTransactionStatus === 'success'
? STRING_KEYS.DEPOSIT_COMPLETE
: inProgressStatusString

View File

@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom';
import { STRING_KEYS } from '@/constants/localization';
import { MarketFilters, type MarketData } from '@/constants/markets';
import { LARGE_TOKEN_DECIMALS } from '@/constants/numbers';
import { FUNDING_DECIMALS, LARGE_TOKEN_DECIMALS } from '@/constants/numbers';
import { AppRoute } from '@/constants/routes';
import { useBreakpoints, useStringGetter } from '@/hooks';
@ -128,7 +128,8 @@ export const MarketsTable = ({ className }: { className?: string }) => {
label: stringGetter({ key: STRING_KEYS.FUNDING_RATE_1H_SHORT }),
renderCell: (row) => (
<Styled.Output
type={OutputType.SmallPercent}
type={OutputType.Percent}
fractionDigits={FUNDING_DECIMALS}
value={row.nextFundingRate}
isPositive={MustBigNumber(row.nextFundingRate).gt(0)}
isNegative={MustBigNumber(row.nextFundingRate).isNegative()}

View File

@ -42,7 +42,11 @@ type StyleProps = {
className?: string;
};
type RowData = OrderbookLine & { side: 'bid' | 'ask'; mine?: number };
type RowData = Pick<OrderbookLine, 'depth' | 'offset' | 'price' | 'size'> & {
side: 'bid' | 'ask';
mine?: number;
key: string;
};
const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number }) => {
const orderbook = useSelector(getCurrentMarketOrderbook, shallowEqual);
@ -53,8 +57,9 @@ const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number
return useMemo(() => {
const asks = (orderbook?.asks?.toArray() ?? [])
.map(
(row: OrderbookLine) =>
(row: OrderbookLine, idx: number) =>
({
key: `ask-${idx}`,
side: 'ask',
mine: openOrdersBySideAndPrice[OrderSide.SELL]?.[row.price]?.size,
...row,
@ -64,8 +69,9 @@ const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number
const bids = (orderbook?.bids?.toArray() ?? [])
.map(
(row: OrderbookLine) =>
(row: OrderbookLine, idx: number) =>
({
key: `bid-${idx}`,
side: 'bid',
mine: openOrdersBySideAndPrice[OrderSide.BUY]?.[row.price]?.size,
...row,
@ -97,15 +103,44 @@ const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number
}
const spread =
asks[0] && bids[0] ? MustBigNumber(asks[0]?.price ?? 0).minus(bids[0]?.price ?? 0) : null;
asks[0]?.price && bids[0]?.price ? MustBigNumber(asks[0].price).minus(bids[0].price) : null;
const spreadPercent = orderbook?.spreadPercent;
const histogramRange = Math.max(
Number(bids[bids.length - 1]?.depth),
Number(asks[asks.length - 1]?.depth)
isNaN(Number(bids[bids.length - 1]?.depth)) ? 0 : Number(bids[bids.length - 1]?.depth),
isNaN(Number(asks[asks.length - 1]?.depth)) ? 0 : Number(asks[asks.length - 1]?.depth)
);
// Ensure asks and bids are of length maxRowsPerSide by adding empty rows.
let idx = asks.length - 1;
while (asks.length < maxRowsPerSide) {
idx += 1;
asks.push({
key: `ask-${idx}`,
side: 'ask',
size: 0,
price: 0,
offset: 0,
depth: 0,
});
}
idx = bids.length - 1;
while (bids.length < maxRowsPerSide) {
idx += 1;
bids.push({
key: `bid-${idx}`,
side: 'bid',
size: 0,
price: 0,
offset: 0,
depth: 0,
});
}
return { asks, bids, spread, spreadPercent, histogramRange, hasOrderbook: !!orderbook };
}, [orderbook, openOrdersBySideAndPrice]);
};
@ -142,31 +177,33 @@ const OrderbookTable = ({
getCellValue: (row: RowData) => row.size,
label: stringGetter({ key: STRING_KEYS.ORDERBOOK_ORDER_SIZE }),
tag: symbol,
renderCell: (row: RowData) => (
<Styled.HistogramOutput
highlightText
type={OutputType.Asset}
value={row.size}
fractionDigits={stepSizeDecimals}
histogramSide={histogramSide === 'left' && 'left'}
useGrouping={false}
/>
),
renderCell: (row: RowData) =>
row.size > 0 && (
<Styled.HistogramOutput
highlightText
type={OutputType.Asset}
value={row.size}
fractionDigits={stepSizeDecimals}
histogramSide={histogramSide === 'left' && 'left'}
useGrouping={false}
/>
),
},
{
columnKey: 'price',
getCellValue: (row: RowData) => row.price,
label: stringGetter({ key: STRING_KEYS.PRICE }),
tag: 'USD',
renderCell: (row: RowData) => (
<OrderbookTradesOutput
highlightText
type={OutputType.Number}
value={row.price}
fractionDigits={tickSizeDecimals}
useGrouping={false}
/>
),
renderCell: (row: RowData) =>
row.price > 0 && (
<OrderbookTradesOutput
highlightText
type={OutputType.Number}
value={row.price}
fractionDigits={tickSizeDecimals}
useGrouping={false}
/>
),
},
{
columnKey: 'subaccount-orders',
@ -194,12 +231,13 @@ const OrderbookTable = ({
label="Orderbook"
data={data}
columns={columns}
getRowKey={(row: RowData) => `${row.side}-${row.price}`}
getRowKey={(row: RowData) => row.key}
getRowAttributes={(row: RowData) => ({
'data-side': row.side,
style: {
'--histogram-bucket-size': row.size,
'--histogram-bucket-depth': row.depth,
'--tr-pointerEvents': row.price ? 'auto' : 'none',
},
})}
onRowAction={onRowAction}
@ -238,19 +276,10 @@ export const Orderbook = ({
maxRowsPerSide,
});
const [showRowsPerSide, setShowRowsPerSide] = useState(0);
// Make rows visible one by one so avoid jumps in initial scroll position
useEffect(() => {
if (showRowsPerSide < maxRowsPerSide) {
setShowRowsPerSide((showRows) => showRows + 1);
}
}, [showRowsPerSide]);
const data = useMemo(
() =>
[
...bids.slice(0, showRowsPerSide).reverse(),
...bids.reverse(),
{
key: 'spread',
slotCustomRow: (props) => (
@ -267,9 +296,9 @@ export const Orderbook = ({
</Styled.SpreadTableRow>
),
} as CustomRowConfig,
...asks.slice(0, showRowsPerSide),
...asks,
].reverse(),
[asks, bids, spread, spreadPercent, showRowsPerSide, isTablet]
[asks, bids, spread, spreadPercent, isTablet]
);
const onRowAction = useCallback(
@ -325,7 +354,7 @@ export const Orderbook = ({
</Styled.Header>
<Styled.SplitOrderbook>
<OrderbookTable data={asks} histogramSide="right" {...orderbookTableProps} />
<OrderbookTable data={bids} histogramSide="left" {...orderbookTableProps} />
<OrderbookTable data={bids.reverse()} histogramSide="left" {...orderbookTableProps} />
</Styled.SplitOrderbook>
</Styled.HorizontalLayout>
);
@ -495,6 +524,7 @@ Styled.OrderbookTable = styled(OrderbookTradesTable)<StyleProps>`
tr {
--histogram-bucket-depth: 0;
pointer-events: var(--tr-pointerEvents);
&[data-side='bid'] {
--accent-color: var(--color-positive);

View File

@ -230,14 +230,7 @@ const getOrdersTableColumnDef = ({
label: `${stringGetter({ key: STRING_KEYS.STATUS })} / ${stringGetter({
key: STRING_KEYS.FILL,
})}`,
renderCell: ({
asset,
createdAtMilliseconds,
size,
status,
totalFilled,
resources,
}) => {
renderCell: ({ asset, createdAtMilliseconds, size, status, totalFilled, resources }) => {
const { statusIconColor, statusStringKey } = getStatusIconInfo({
status,
totalFilled,
@ -345,9 +338,7 @@ export const OrdersTable = ({
if (hasUnseenOrderUpdates) dispatch(viewedOrders());
}, [hasUnseenOrderUpdates]);
const symbol = currentMarket
? allAssets[allPerpetualMarkets[currentMarket]?.assetId]?.symbol
: null;
const symbol = currentMarket ? allAssets[allPerpetualMarkets[currentMarket]?.assetId]?.id : null;
const ordersData = orders.map((order: SubaccountOrder) =>
getHydratedTradingData({

View File

@ -366,6 +366,7 @@ Styled.InlineRow = styled.div`
Styled.AssetIcon = styled(AssetIcon)`
${layoutMixins.inlineRow}
min-width: unset;
font-size: 2.25rem;
`;