Compare commits

..
Author SHA1 Message Date
Aleka Cheung 1f8cc17cf1 show trading rewards in testnet 2024-02-13 14:36:52 -05:00
49 changed files with 364 additions and 1424 deletions
+1 -1
View File
@@ -42,7 +42,7 @@
"@cosmjs/tendermint-rpc": "^0.32.1",
"@dydxprotocol/v4-abacus": "^1.4.2",
"@dydxprotocol/v4-client-js": "^1.0.20",
"@dydxprotocol/v4-localization": "^1.1.30",
"@dydxprotocol/v4-localization": "^1.1.29",
"@ethersproject/providers": "^5.7.2",
"@js-joda/core": "^5.5.3",
"@radix-ui/react-accordion": "^1.1.2",
+8 -8
View File
@@ -1,9 +1,5 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
overrides:
follow-redirects: 1.15.3
@@ -36,8 +32,8 @@ dependencies:
specifier: ^1.0.20
version: 1.0.20
'@dydxprotocol/v4-localization':
specifier: ^1.1.30
version: 1.1.30
specifier: ^1.1.29
version: 1.1.29
'@ethersproject/providers':
specifier: ^5.7.2
version: 5.7.2
@@ -1323,8 +1319,8 @@ packages:
- utf-8-validate
dev: false
/@dydxprotocol/v4-localization@1.1.30:
resolution: {integrity: sha512-TZfWWRSOxcjLHs972wlJVVHkE7+DVqAUnGZSs24HYHsPtUkPhZiNXMOA2Vk9YddQxumhM79xIRH0cmJSe5DDUg==}
/@dydxprotocol/v4-localization@1.1.29:
resolution: {integrity: sha512-o9lxsYl5ObqO9OUyxiKuUxoBmYZk0qGDeNxGScY8uMRq4Ahhz3BPxAC7jUYypCNAMpieiQjVGxMnnhIbwEkm2w==}
dev: false
/@dydxprotocol/v4-proto@4.0.0-dev.0:
@@ -16053,3 +16049,7 @@ packages:
/zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
dev: true
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
+1 -155
View File
@@ -20,13 +20,6 @@
"whitepaperLink": "https://why.cardano.org/en/introduction/motivation/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/cardano/"
},
"AGIX-USD": {
"name": "SingularityNET",
"tags": ["AI"],
"websiteLink": "https://public.singularitynet.io/whitepaper.pdf",
"whitepaperLink": "https://public.singularitynet.io/whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/singularitynet/"
},
"ALGO-USD": {
"name": "Algorand",
"tags": ["Layer 1"],
@@ -85,7 +78,7 @@
},
"BLUR-USD": {
"name": "Blur",
"tags": ["NFT"],
"tags": [],
"websiteLink": "https://blur.io/",
"whitepaperLink": "https://docs.blur.foundation/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/blur-token/"
@@ -98,13 +91,6 @@
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/bnb/"
},
"CHZ-USD": {
"name": "Chiliz",
"tags": ["Layer 1"],
"websiteLink": "https://www.chiliz.com/",
"whitepaperLink": "https://www.chiliz.com/docs/litepaper-v1.1-20230703.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/chiliz/"
},
"CELO-USD": {
"name": "Celo",
"tags": [],
@@ -161,13 +147,6 @@
"whitepaperLink": "https://cdn.enjin.io/downloads/whitepapers/enjin-coin/en.pdf/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/enjin-coin/"
},
"ENS-USD": {
"name": "Ethereum Name Service",
"tags": [],
"websiteLink": "https://coinmarketcap.com/currencies/ethereum-name-service/",
"whitepaperLink": "https://docs.ens.domains/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/ethereum-name-service/"
},
"EOS-USD": {
"name": "EOS",
"tags": ["Layer 1"],
@@ -191,13 +170,6 @@
"displayStepSize": "0.001",
"displayTickSize": "0.1"
},
"FET-USD": {
"name": "Fetch.ai",
"tags": ["AI"],
"websiteLink": "https://fetch.ai/",
"whitepaperLink": "https://www.dropbox.com/s/gxptsecwdl3jjtn/David%20Minarsch%20-%202021-04-26%2010.34.17%20-%20paper_21_finalversion.pdf?e=1&dl=0",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/fetch/"
},
"FIL-USD": {
"name": "Filecoin",
"tags": ["Layer 1"],
@@ -205,34 +177,6 @@
"whitepaperLink": "https://filecoin.io/filecoin.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/filecoin/"
},
"FTM-USD": {
"name": "Fantom",
"tags": [],
"websiteLink": "https://fantom.foundation/",
"whitepaperLink": "https://fantom.foundation/_next/static/media/wp_fantom_v1.6.39329cdc5d0ee59684cbc6f228516383.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/fantom/"
},
"GALA-USD": {
"name": "Gala",
"tags": ["Gaming", "Layer 1"],
"websiteLink": "https://gala.com/",
"whitepaperLink": "https://galahackathon.com/v1.0.0/pdf/sdk-documentation.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/gala/"
},
"GMT-USD": {
"name": "GMT",
"tags": ["Gaming"],
"websiteLink": "https://stepn.com/",
"whitepaperLink/": "https://whitepaper.stepn.com/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/green-metaverse-token/"
},
"GRT-USD": {
"name": "The Graph",
"tags": [],
"websiteLink": "https://thegraph.com/",
"whitepaperLink/": "https://github.com/graphprotocol/research/blob/master/papers/whitepaper/the-graph-whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/the-graph/"
},
"HNT-USD": {
"name": "Helium",
"tags": ["Layer 1"],
@@ -240,13 +184,6 @@
"whitepaperLink": "http://whitepaper.helium.com",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/helium/"
},
"HBAR-USD": {
"name": "Hedera",
"tags": [],
"websiteLink": "https://hedera.com/",
"whitepaperLink/": "https://files.hedera.com/hh_whitepaper_v2.2-20230918.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/hedera/"
},
"ICP-USD": {
"name": "Internet Computer",
"tags": ["Layer 1"],
@@ -254,27 +191,6 @@
"whitepaperLink": "https://dfinity.org/whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/internet-computer/"
},
"IMX-USD": {
"name": "Immutable X",
"tags": ["Gaming", "Layer 2", "NFT"],
"websiteLink": "https://www.immutable.com/",
"whitepaperLink": "https://assets.website-files.com/646557ee455c3e16e4a9bcb3/6499367de527dd82ab7475a3_Immutable%20Whitepaper%20Update%202023%20(3).pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/immutable-x/"
},
"INJ-USD": {
"name": "Injective",
"tags": ["Layer 1", "Defi"],
"websiteLink": "https://injective.com/",
"whitepaperLink": "https://docs.injective.network/intro/01_overview.html",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/injective/"
},
"JTO-USD": {
"name": "Jito",
"tags": ["Defi"],
"websiteLink": "https://www.jito.network/",
"whitepaperLink": "https://github.com/jito-foundation",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/jito/"
},
"JUP-USD": {
"name": "Jupiter",
"tags": ["Defi"],
@@ -282,13 +198,6 @@
"whitepaperLink": "https://station.jup.ag/blog/green-paper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/jupiter-ag/"
},
"KAVA-USD": {
"name": "Kava",
"tags": ["Layer 1"],
"websiteLink": "https://www.kava.io/",
"whitepaperLink": "https://docsend.com/view/gwbwpc3",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/kava/"
},
"LDO-USD": {
"name": "Lido DAO",
"tags": ["Defi"],
@@ -310,20 +219,6 @@
"whitepaperLink": "https://litecoin.info/index.php/Main_Page",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/litecoin/"
},
"MANA-USD": {
"name": "Decentraland",
"tags": ["AR/VR"],
"websiteLink": "https://decentraland.org/",
"whitepaperLink": "https://decentraland.org/whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/decentraland/"
},
"MASK-USD": {
"name": "Mask Network",
"tags": [],
"websiteLink": "https://mask.io/",
"whitepaperLink": "https://masknetwork.medium.com/introducing-mask-network-maskbook-the-future-of-the-internet-5a973d874edd",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/mask-network/"
},
"MATIC-USD": {
"name": "Polygon",
"tags": ["Layer 2"],
@@ -331,13 +226,6 @@
"whitepaperLink": "https://polygon.technology/lightpaper-polygon.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/polygon/"
},
"MINA-USD": {
"name": "Mina",
"tags": ["Layer 1"],
"websiteLink": "https://minaprotocol.com/",
"whitepaperLink": "https://docs.minaprotocol.com/assets/economicsWhitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/mina/"
},
"MKR-USD": {
"name": "Maker",
"tags": ["Governance"],
@@ -352,13 +240,6 @@
"whitepaperLink": "https://near.org/papers/the-official-near-white-paper/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/near-protocol/"
},
"ORDI-USD": {
"name": "ORDI",
"tags": ["NFT"],
"websiteLink": "https://ordinals.com/",
"whitepaperLink": "https://rodarmor.com/blog/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/ordi/"
},
"OP-USD": {
"name": "Optimism",
"tags": [],
@@ -372,20 +253,6 @@
"websiteLink": "https://www.pepe.vip/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/pepe/"
},
"PYTH-USD": {
"name": "Pyth Network",
"tags": [],
"websiteLink": "https://pyth.network/",
"whitepaperLink": "https://pyth.network/whitepaper_v2.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/pyth-network/"
},
"RNDR-USD": {
"name": "Render Token",
"tags": ["AI"],
"websiteLink": "https://rendernetwork.com/",
"whitepaperLink": "https://renderfoundation.com/whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/render/"
},
"RUNE-USD": {
"name": "THORChain",
"tags": ["Layer 1"],
@@ -393,13 +260,6 @@
"whitepaperLink": "https://whitepaper.io/document/709/thorchain-whitepaper",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/thorchain/"
},
"SAND-USD": {
"name": "The Sandbox",
"tags": ["Gaming"],
"websiteLink": "https://www.sandbox.game/en/",
"whitepaperLink": "https://installers.sandbox.game/The_Sandbox_Whitepaper_2020.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/the-sandbox/"
},
"SEI-USD": {
"name": "Sei",
"tags": ["Layer 1", "Defi"],
@@ -428,13 +288,6 @@
"whitepaperLink": "https://solana.com/solana-whitepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/solana/"
},
"STX-USD": {
"name": "Stacks",
"tags": ["Layer 2"],
"websiteLink": "https://www.stacks.co/",
"whitepaperLink": "https://gaia.blockstack.org/hub/1AxyPunHHAHiEffXWESKfbvmBpGQv138Fp/stacks.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/stacks/"
},
"SUI-USD": {
"name": "Sui",
"tags": ["Layer 1"],
@@ -484,13 +337,6 @@
"whitepaperLink": "https://whitepaper.worldcoin.org/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/worldcoin-org/"
},
"WOO-USD": {
"name": "WOO Network",
"tags": ["Defi"],
"websiteLink": "https://woo.org/",
"whitepaperLink": "https://woo.org/Litepaper.pdf",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/wootrade/"
},
"XLM-USD": {
"name": "Stellar",
"tags": ["Layer 1"],
+12 -12
View File
@@ -274,7 +274,7 @@
{
"baseAsset": "CRV",
"referencePrice": 0.48346754113361784,
"numOracles": 6,
"numOracles": 5,
"liquidityTier": 2,
"assetName": "Curve DAO Token",
"p": -1.0,
@@ -325,7 +325,7 @@
{
"baseAsset": "DYM",
"referencePrice": 5.544949130771594,
"numOracles": 6,
"numOracles": 5,
"liquidityTier": 2,
"assetName": "Dymension",
"p": 0.0,
@@ -412,7 +412,7 @@
"referencePrice": 0.5560893147613742,
"numOracles": 6,
"liquidityTier": 2,
"assetName": "Fetch.ai",
"assetName": "Fetch AI",
"p": -1.0,
"atomicResolution": -5.0,
"minExchanges": 3,
@@ -480,7 +480,7 @@
"referencePrice": 0.20404992804891367,
"numOracles": 6,
"liquidityTier": 2,
"assetName": "GMT",
"assetName": "STEPN",
"p": -1.0,
"atomicResolution": -5.0,
"minExchanges": 3,
@@ -514,7 +514,7 @@
"referencePrice": 0.07686278766140874,
"numOracles": 6,
"liquidityTier": 2,
"assetName": "Hedera",
"assetName": "Hedera Hashgraph",
"p": -2.0,
"atomicResolution": -4.0,
"minExchanges": 3,
@@ -565,7 +565,7 @@
"referencePrice": 33.427379051845875,
"numOracles": 7,
"liquidityTier": 2,
"assetName": "Injective",
"assetName": "Injective Protocol",
"p": 1.0,
"atomicResolution": -7.0,
"minExchanges": 3,
@@ -597,7 +597,7 @@
{
"baseAsset": "JUP",
"referencePrice": 0.4339790660244292,
"numOracles": 6,
"numOracles": 5,
"liquidityTier": 2,
"assetName": "Jupiter",
"p": -1.0,
@@ -750,7 +750,7 @@
{
"baseAsset": "MKR",
"referencePrice": 1943.1865605831852,
"numOracles": 6,
"numOracles": 5,
"liquidityTier": 2,
"assetName": "Maker",
"p": 3.0,
@@ -869,7 +869,7 @@
{
"baseAsset": "RUNE",
"referencePrice": 3.7264809096406806,
"numOracles": 6,
"numOracles": 5,
"liquidityTier": 2,
"assetName": "Thorchain",
"p": 0.0,
@@ -888,7 +888,7 @@
"referencePrice": 0.37297439115035663,
"numOracles": 6,
"liquidityTier": 2,
"assetName": "The Sandbox",
"assetName": "SAND",
"p": -1.0,
"atomicResolution": -5.0,
"minExchanges": 3,
@@ -1073,9 +1073,9 @@
{
"baseAsset": "WOO",
"referencePrice": 0.2944897275608197,
"numOracles": 6,
"numOracles": 5,
"liquidityTier": 2,
"assetName": "WOO Network",
"assetName": "Wootrade Network",
"p": -1.0,
"atomicResolution": -5.0,
"minExchanges": 3,
-635
View File
@@ -1,635 +0,0 @@
{
"apps":{
"ios":{
"scheme":"dydx-t-v4"
}
},
"tokens":{
"dydxprotocol-testnet":{
"chain":{
"name":"Dv4TNT",
"denom":"adv4tnt",
"decimals":18,
"image":"/currencies/dydx.png"
},
"usdc":{
"name":"USDC",
"denom":"ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom":"uusdc",
"decimals":6,
"image":"/currencies/usdc.png"
}
},
"dydx-testnet-4":{
"chain": {
"name": "Dv4TNT",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"decimals": 6,
"image": "/currencies/usdc.png"
}
},
"[mainnet chain id]":{
"comment":"Change according to mainnet release",
"chain":{
"name":"TokenName",
"denom":"tokenDenom",
"decimals":18,
"image":"/currencies/dydx.png"
},
"usdc":{
"name":"USDC",
"denom":"ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom":"uusdc",
"decimals":6,
"image":"/currencies/usdc.png"
}
}
},
"links":{
"dydxprotocol-testnet":{
"tos":"https://dydx.exchange/v4-terms",
"privacy":"https://dydx.exchange/privacy",
"mintscan":"https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"blogs":"https://www.dydx.foundation/blog",
"foundation":"https://www.dydx.foundation",
"help":"https://help.dydx.exchange/",
"reduceOnlyLearnMore":"https://help.dydx.exchange/articles/6345793-reduce-only-orders",
"mintscanBase":"https://testnet.mintscan.io/dydx-testnet",
"documentation":"https://docs.dydx.exchange/",
"community":"https://discord.com/invite/dydx",
"governanceLearnMore":"https://help.dydx.exchange",
"newMarketProposalLearnMore":"https://dydx.exchange/blog/new-market-proposals",
"stakingLearnMore":"https://help.dydx.exchange",
"keplrDashboard":"https://testnet.keplr.app/",
"strideZoneApp":"https://testnet.stride.zone",
"accountExportLearnMore":"https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore":"https://www.dydx.academy/video/defi-wallet"
},
"dydx-testnet-4":{
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"documentation": "https://docs.dydx.exchange/",
"community": "https://discord.com/invite/dydx",
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
"blogs": "https://www.dydx.foundation/blog",
"foundation": "https://www.dydx.foundation",
"help": "https://help.dydx.exchange/",
"reduceOnlyLearnMore": "https://help.dydx.exchange/articles/6345793-reduce-only-orders",
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
"governanceLearnMore": "https://help.dydx.exchange",
"newMarketProposalLearnMore": "https://dydx.exchange/blog/new-market-proposals",
"stakingLearnMore": "https://help.dydx.exchange",
"keplrDashboard": "https://testnet.keplr.app/",
"strideZoneApp": "https://testnet.stride.zone",
"accountExportLearnMore": "https://help.dydx.exchange/en/articles/8565867-secret-phrase-on-dydx-chain",
"walletLearnMore": "https://www.dydx.academy/video/defi-wallet"
},
"[mainnet chain id]":{
"tos":"[HTTP link to TOS]",
"privacy":"[HTTP link to Privacy Policy]",
"mintscan":"[HTTP link to Mintscan, with {tx_hash} placeholder]",
"mintscanBase":"[HTTP link to TOS mintscan base url]",
"feedback":"[HTTP link to feedback form, can be null]",
"blogs":"[HTTP link to blogs, can be null]",
"foundation":"[HTTP link to foundation, can be null]",
"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]",
"governanceLearnMore":"[HTTP link to governance learn more, can be null]",
"newMarketProposalLearnMore":"[HTTP link to new market proposal learn more, can be null]",
"stakingLearnMore":"[HTTP link to staking learn more, can be null]",
"keplrDashboard":"[HTTP link to keplr dashboard, can be null]",
"strideZoneApp":"[HTTP link to stride zone app, can be null]",
"accountExportLearnMore":"[HTTP link to account export learn more, can be null]",
"walletLearnMore":"[HTTP link to wallet learn more, can be null]"
}
},
"wallets":{
"dydxprotocol-testnet":{
"walletconnect":{
"client":{
"name":"dYdX v4",
"description":"dYdX v4 App",
"iconUrl":"/logos/dydx-x.png"
},
"v2":{
"projectId":"47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue":{
"callbackUrl":"/walletsegue"
},
"images":"/wallets/",
"signTypedDataAction":"dYdX Chain Onboarding",
"signTypedDataDomainName":"dYdX Chain"
},
"dydx-testnet-4":{
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX Chain Onboarding",
"signTypedDataDomainName": "dYdX V4"
},
"[mainnet chain id]":{
"walletconnect":{
"client":{
"name":"[Name of the app]",
"description":"[Description of the app]",
"iconUrl":"[Relative URL of the icon URL]"
},
"v2":{
"projectId":"[Project ID]"
}
},
"walletSegue":{
"callbackUrl":"[Relative callback URL for WalletSegue, should match apple-app-site-association]"
},
"images":"[Relative URL for wallet images]",
"signTypedDataAction":"dYdX Chain Onboarding",
"signTypedDataDomainName":"dYdX Chain"
}
},
"governance":{
"dydxprotocol-testnet":{
"newMarketProposal":{
"initialDepositAmount":10000000,
"delayBlocks":900,
"newMarketsMethodology":"https://docs.google.com/spreadsheets/d/1zjkV9R7R_7KMItuzqzvKGwefSBRfE-ZNAx1LH55OcqY/edit?usp=sharing"
}
},
"dydx-testnet-4":{
"newMarketProposal": {
"initialDepositAmount": 10000000,
"delayBlocks": 900,
"newMarketsMethodology": "https://docs.google.com/spreadsheets/d/1zjkV9R7R_7KMItuzqzvKGwefSBRfE-ZNAx1LH55OcqY/edit?usp=sharing"
}
},
"[mainnet chain id]":{
"newMarketProposal":{
"initialDepositAmount":0,
"delayBlocks":0,
"newMarketsMethodology":"[URL to spreadsheet or document that explains methodology]"
}
}
},
"deployments":{
"MAINNET":{
"environments":[
"dydxprotocol-mainnet"
],
"default":"dydxprotocol-mainnet"
},
"TESTFLIGHT":{
"environments":[
"dydxprotocol-mainnet",
"dydxprotocol-testnet"
],
"default":"dydxprotocol-mainnet"
},
"TESTNET":{
"environments":[
"dydxprotocol-testnet"
],
"default":"dydxprotocol-testnet"
},
"DEV":{
"environments":[
"dydxprotocol-dev",
"dydxprotocol-dev-2",
"dydxprotocol-dev-4",
"dydxprotocol-dev-5",
"dydxprotocol-staging",
"dydxprotocol-staging-west",
"dydxprotocol-testnet",
"dydxprotocol-testnet-dydx",
"dydxprotocol-testnet-nodefleet",
"dydxprotocol-testnet-kingnodes",
"dydxprotocol-testnet-liquify",
"dydxprotocol-testnet-polkachu",
"dydxprotocol-testnet-bware"
],
"default":"dydxprotocol-testnet"
}
},
"environments":{
"dydxprotocol-dev":{
"name":"v4 Dev",
"ethereumChainId":"11155111",
"dydxChainId":"dydxprotocol-testnet",
"chainName":"dYdX Chain",
"chainLogo":"/dydx-chain.png",
"squidIntegratorId":"dYdX-api",
"isMainNet":false,
"endpoints":{
"indexers":[
{
"api":"https://indexer.v4dev.dydx.exchange",
"socket":"wss://indexer.v4dev.dydx.exchange"
}
],
"validators":[
"https://validator.v4dev.dydx.exchange"
],
"0xsquid":"https://testnet.api.0xsquid.com",
"nobleValidator":"https://noble-testnet-rpc.polkachu.com/",
"faucet":"https://faucet.v4dev.dydx.exchange"
},
"featureFlags":{
"reduceOnlySupported":true
}
},
"dydxprotocol-dev-2":{
"name":"v4 Dev 2",
"ethereumChainId":"11155111",
"dydxChainId":"dydxprotocol-testnet",
"chainName":"dYdX Chain",
"chainLogo":"/dydx-chain.png",
"squidIntegratorId":"dYdX-api",
"isMainNet":false,
"endpoints":{
"indexers":[
{
"api":"http://dev2-indexer-apne1-lb-public-2076363889.ap-northeast-1.elb.amazonaws.com",
"socket":"ws://dev2-indexer-apne1-lb-public-2076363889.ap-northeast-1.elb.amazonaws.com"
}
],
"validators":[
"http://54.92.118.111"
],
"0xsquid":"https://testnet.api.0xsquid.com",
"nobleValidator":"https://noble-testnet-rpc.polkachu.com/"
},
"featureFlags":{
"reduceOnlySupported":true
}
},
"dydxprotocol-dev-4":{
"name":"v4 Dev 4",
"ethereumChainId":"11155111",
"dydxChainId":"dydxprotocol-testnet",
"chainName":"dYdX Chain",
"chainLogo":"/dydx-chain.png",
"squidIntegratorId":"dYdX-api",
"isMainNet":false,
"endpoints":{
"indexers":[
{
"api":"https://indexer.v4dev4.dydx.exchange",
"socket":"wss://indexer.v4dev4.dydx.exchange"
}
],
"validators":[
"https://validator.v4dev4.dydx.exchange"
],
"0xsquid":"https://testnet.api.0xsquid.com",
"nobleValidator":"https://noble-testnet-rpc.polkachu.com/",
"faucet":"https://faucet.v4dev4.dydx.exchange"
},
"featureFlags":{
"reduceOnlySupported":true
}
},
"dydxprotocol-dev-5":{
"name":"v4 Dev 5",
"ethereumChainId":"11155111",
"dydxChainId":"dydxprotocol-testnet",
"chainName":"dYdX Chain",
"chainLogo":"/dydx-chain.png",
"squidIntegratorId":"dYdX-api",
"isMainNet":false,
"endpoints":{
"indexers":[
{
"api":"http://dev5-indexer-apne1-lb-public-1721328151.ap-northeast-1.elb.amazonaws.com",
"socket":"ws://dev5-indexer-apne1-lb-public-1721328151.ap-northeast-1.elb.amazonaws.com"
}
],
"validators":[
"http://18.223.78.50"
],
"0xsquid":"https://testnet.api.0xsquid.com",
"nobleValidator":"https://noble-testnet-rpc.polkachu.com/"
},
"featureFlags":{
"reduceOnlySupported":true
}
},
"dydxprotocol-staging":{
"name":"v4 Staging",
"ethereumChainId":"11155111",
"dydxChainId":"dydxprotocol-testnet",
"chainName":"dYdX Chain",
"chainLogo":"/dydx-chain.png",
"squidIntegratorId":"dYdX-api",
"isMainNet":false,
"endpoints":{
"indexers":[
{
"api":"https://indexer.v4staging.dydx.exchange",
"socket":"wss://indexer.v4staging.dydx.exchange"
}
],
"faucet":"https://faucet.v4staging.dydx.exchange",
"validators":[
"https://validator.v4staging.dydx.exchange"
],
"0xsquid":"https://testnet.api.squidrouter.com",
"nobleValidator":"https://noble-testnet-rpc.polkachu.com/"
},
"featureFlags":{
"reduceOnlySupported":true
}
},
"dydxprotocol-staging-forced-update":{
"name":"v4 Staging Forced Update",
"ethereumChainId":"11155111",
"dydxChainId":"dydxprotocol-testnet",
"chainName":"dYdX Chain",
"chainLogo":"/dydx-chain.png",
"squidIntegratorId":"dYdX-api",
"isMainNet":false,
"endpoints":{
"indexers":[
{
"api":"https://indexer.v4staging.dydx.exchange",
"socket":"wss://indexer.v4staging.dydx.exchange"
}
],
"faucet":"https://faucet.v4staging.dydx.exchange",
"validators":[
"https://validator.v4staging.dydx.exchange"
],
"0xsquid":"https://testnet.api.squidrouter.com",
"nobleValidator":"https://noble-testnet-rpc.polkachu.com/"
},
"apps":{
"ios":{
"minimalVersion":"1.0",
"build":40000,
"url":"https://apps.apple.com/app/dydx/id1564787350"
}
},
"featureFlags":{
"reduceOnlySupported":true
}
},
"dydxprotocol-staging-west":{
"name":"v4 Staging West",
"ethereumChainId":"11155111",
"dydxChainId":"dydxprotocol-testnet",
"chainName":"dYdX Chain",
"chainLogo":"/dydx-chain.png",
"squidIntegratorId":"dYdX-api",
"isMainNet":false,
"endpoints":{
"indexers":[
{
"api":"https://indexer.v4staging.dydx.exchange",
"socket":"wss://indexer.v4staging.dydx.exchange"
}
],
"faucet":"https://faucet.v4staging.dydx.exchange",
"validators":[
"https://validator-uswest1.v4staging.dydx.exchange"
],
"0xsquid":"https://testnet.api.squidrouter.com",
"nobleValidator":"https://noble-testnet-rpc.polkachu.com/"
},
"featureFlags":{
"reduceOnlySupported":true
}
},
"dydxprotocol-testnet":{
"name":"v4 Public Testnet",
"ethereumChainId":"11155111",
"dydxChainId":"dydx-testnet-4",
"chainName":"dYdX Chain",
"chainLogo":"/dydx-chain.png",
"squidIntegratorId":"dYdX-api",
"isMainNet":false,
"endpoints":{
"indexers":[
{
"api":"https://indexer.v4testnet.dydx.exchange",
"socket":"wss://indexer.v4testnet.dydx.exchange"
}
],
"validators":[
"https://dydx-testnet-full-rpc.public.blastapi.io/",
"https://dydx-testnet-rpc.polkachu.com/",
"https://dydx-testnet.nodefleet.org",
"https://test-dydx.kingnodes.com",
"https://dydx-rpc.liquify.com/api=8878132/dydx"
],
"0xsquid":"https://testnet.api.squidrouter.com",
"nobleValidator":"https://noble-testnet-rpc.polkachu.com/",
"faucet":"https://faucet.v4testnet.dydx.exchange"
},
"featureFlags":{
"reduceOnlySupported":false
}
},
"dydxprotocol-testnet-dydx":{
"name":"v4 Public Testnet/dYdX",
"ethereumChainId":"11155111",
"dydxChainId":"dydx-testnet-4",
"chainName":"dYdX Chain",
"chainLogo":"/dydx-chain.png",
"squidIntegratorId":"dYdX-api",
"isMainNet":false,
"endpoints":{
"indexers":[
{
"api":"https://indexer.v4testnet.dydx.exchange",
"socket":"wss://indexer.v4testnet.dydx.exchange"
}
],
"validators":[
"https://validator.v4testnet.dydx.exchange"
],
"0xsquid":"https://testnet.api.squidrouter.com",
"nobleValidator":"https://noble-testnet-rpc.polkachu.com/",
"faucet":"https://faucet.v4testnet.dydx.exchange"
},
"featureFlags":{
"reduceOnlySupported":false
}
},
"dydxprotocol-testnet-nodefleet":{
"name":"v4 Public Testnet/nodefleet",
"ethereumChainId":"11155111",
"dydxChainId":"dydx-testnet-4",
"chainName":"dYdX Chain",
"chainLogo":"/dydx-chain.png",
"squidIntegratorId":"dYdX-api",
"isMainNet":false,
"endpoints":{
"indexers":[
{
"api":"https://indexer.v4testnet.dydx.exchange",
"socket":"wss://indexer.v4testnet.dydx.exchange"
}
],
"validators":[
"https://dydx-testnet.nodefleet.org"
],
"0xsquid":"https://testnet.api.squidrouter.com",
"nobleValidator":"https://noble-testnet-rpc.polkachu.com/",
"faucet":"https://faucet.v4testnet.dydx.exchange"
},
"featureFlags":{
"reduceOnlySupported":false
}
},
"dydxprotocol-testnet-kingnodes":{
"name":"v4 Public Testnet/KingNodes",
"ethereumChainId":"11155111",
"dydxChainId":"dydx-testnet-4",
"chainName":"dYdX Chain",
"chainLogo":"/dydx-chain.png",
"squidIntegratorId":"dYdX-api",
"isMainNet":false,
"endpoints":{
"indexers":[
{
"api":"https://indexer.v4testnet.dydx.exchange",
"socket":"wss://indexer.v4testnet.dydx.exchange"
}
],
"validators":[
"https://test-dydx.kingnodes.com"
],
"0xsquid":"https://testnet.api.squidrouter.com",
"nobleValidator":"https://noble-testnet-rpc.polkachu.com/",
"faucet":"https://faucet.v4testnet.dydx.exchange"
},
"featureFlags":{
"reduceOnlySupported":false
}
},
"dydxprotocol-testnet-liquify":{
"name":"v4 Public Testnet/Liquify",
"ethereumChainId":"11155111",
"dydxChainId":"dydx-testnet-4",
"chainName":"dYdX Chain",
"chainLogo":"/dydx-chain.png",
"squidIntegratorId":"dYdX-api",
"isMainNet":false,
"endpoints":{
"indexers":[
{
"api":"https://indexer.v4testnet.dydx.exchange",
"socket":"wss://indexer.v4testnet.dydx.exchange"
}
],
"validators":[
"https://dydx-rpc.liquify.com/api=8878132/dydx"
],
"0xsquid":"https://testnet.api.squidrouter.com",
"nobleValidator":"https://noble-testnet-rpc.polkachu.com/",
"faucet":"https://faucet.v4testnet.dydx.exchange"
},
"featureFlags":{
"reduceOnlySupported":false
}
},
"dydxprotocol-testnet-polkachu":{
"name":"v4 Public Testnet/Polkahcu",
"ethereumChainId":"11155111",
"dydxChainId":"dydx-testnet-4",
"chainName":"dYdX Chain",
"chainLogo":"/dydx-chain.png",
"squidIntegratorId":"dYdX-api",
"isMainNet":false,
"endpoints":{
"indexers":[
{
"api":"https://indexer.v4testnet.dydx.exchange",
"socket":"wss://indexer.v4testnet.dydx.exchange"
}
],
"validators":[
"https://dydx-testnet-rpc.polkachu.com/"
],
"0xsquid":"https://testnet.api.squidrouter.com",
"nobleValidator":"https://noble-testnet-rpc.polkachu.com/",
"faucet":"https://faucet.v4testnet.dydx.exchange"
},
"featureFlags":{
"reduceOnlySupported":false
}
},
"dydxprotocol-testnet-bware":{
"name":"v4 Public Testnet/BWare",
"ethereumChainId":"11155111",
"dydxChainId":"dydx-testnet-4",
"chainName":"dYdX Chain",
"chainLogo":"/dydx-chain.png",
"squidIntegratorId":"dYdX-api",
"isMainNet":false,
"endpoints":{
"indexers":[
{
"api":"https://indexer.v4testnet.dydx.exchange",
"socket":"wss://indexer.v4testnet.dydx.exchange"
}
],
"validators":[
"https://dydx-testnet-full-rpc.public.blastapi.io/"
],
"0xsquid":"https://testnet.api.squidrouter.com",
"nobleValidator":"https://noble-testnet-rpc.polkachu.com/",
"faucet":"https://faucet.v4testnet.dydx.exchange"
},
"featureFlags":{
"reduceOnlySupported":false
}
},
"dydxprotocol-mainnet":{
"name":"v4",
"ethereumChainId":"1",
"dydxChainId":"[mainnet chain id]",
"chainName":"dYdX Chain",
"chainLogo":"/dydx-chain.png",
"squidIntegratorId":"[mainnet squid integrator id]",
"isMainNet":true,
"endpoints":{
"indexers":[
{
"api":"[REST endpoint]",
"socket":"[Websocket endpoint]"
}
],
"validators":[
"[Validator endpoint 1",
"[Validator endpoint n]"
],
"0xsquid":"[0xSquid endpoint for mainnet]",
"nobleValidator":"[noble validator endpoint for mainnet]"
},
"featureFlags":{
"reduceOnlySupported":false
}
}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

-22
View File
@@ -8,7 +8,6 @@ const assetIcons = {
'1INCH': '/currencies/1inch.png',
AAVE: '/currencies/aave.png',
ADA: '/currencies/ada.png',
AGIX: '/currencies/agix.png',
ALGO: '/currencies/algo.png',
APE: '/currencies/ape.png',
APT: '/currencies/apt.png',
@@ -21,7 +20,6 @@ const assetIcons = {
BONK: '/currencies/bonk.png',
BTC: '/currencies/btc.png',
CELO: '/currencies/celo.png',
CHZ: '/currencies/chz.png',
COMP: '/currencies/comp.png',
CRV: '/currencies/crv.png',
DAI: '/currencies/dai.png',
@@ -30,44 +28,25 @@ const assetIcons = {
DYDX: '/currencies/dydx.png',
DYM: '/currencies/dym.png',
ENJ: '/currencies/enj.png',
ENS: '/currencies/ens.png',
EOS: '/currencies/eos.png',
ETC: '/currencies/etc.png',
ETH: '/currencies/eth.png',
FET: '/currencies/fet.png',
FIL: '/currencies/fil.png',
FTM: '/currencies/ftm.png',
GALA: "/currencies/gala.png",
GMT: "/currencies/gmt.png",
GRT: "/currencies/grt.png",
HBAR: "/currencies/hbar.png",
ICP: '/currencies/icp.png',
IMX: '/currencies/imx.png',
INJ: '/currencies/inj.png',
JTO: '/currencies/jto.png',
JUP: '/currencies/jup.png',
KAVA: '/currencies/kava.png',
LDO: '/currencies/ldo.png',
LINK: '/currencies/link.png',
LTC: '/currencies/ltc.png',
MANA: '/currencies/mana.png',
MATIC: '/currencies/matic.png',
MASK: '/currencies/mask.png',
MINA: '/currencies/mina.png',
MKR: '/currencies/mkr.png',
NEAR: '/currencies/near.png',
ORDI: "/currencies/ordi.png",
OP: '/currencies/op.png',
PEPE: '/currencies/pepe.png',
PYTH: '/currencies/pyth.png',
RNDR: '/currencies/rndr.png',
RUNE: '/currencies/rune.png',
SAND: '/currencies/sand.png',
SEI: '/currencies/sei.png',
SHIB: '/currencies/shib.png',
SNX: '/currencies/snx.png',
SOL: '/currencies/sol.png',
STX: '/currencies/stx.png',
SUI: '/currencies/sui.png',
SUSHI: '/currencies/sushi.png',
TIA: '/currencies/tia.png',
@@ -78,7 +57,6 @@ const assetIcons = {
USDT: '/currencies/usdt.png',
WBTC: '/currencies/wbtc.png',
WETH: '/currencies/weth.png',
WOO: '/currencies/woo.png',
WLD: '/currencies/wld.png',
XLM: '/currencies/xlm.png',
XMR: '/currencies/xmr.png',
-5
View File
@@ -33,9 +33,4 @@ TagStory.argTypes = {
control: { type: 'select' },
defaultValue: undefined,
},
isHighlighted: {
options: [true, false],
control: { type: 'select' },
defaultValue: false,
},
};
-1
View File
@@ -76,6 +76,5 @@ export const Tag = styled.span<StyleProps>`
isHighlighted &&
css`
background-color: var(--color-accent);
color: var(--color-text-button);
`}
`;
+1 -9
View File
@@ -1,12 +1,4 @@
import type { ThemeName } from 'public/tradingview/charting_library';
import { AppColorMode, AppTheme } from '@/state/configs';
export const THEME_NAMES: Record<AppTheme, ThemeName> = {
[AppTheme.Classic]: 'Classic',
[AppTheme.Dark]: 'Dark',
[AppTheme.Light]: 'Light',
};
import { AppColorMode } from '@/state/configs';
export type Theme = {
[AppColorMode.GreenUp]: ThemeColorBase;
-16
View File
@@ -1,16 +0,0 @@
import { OrderSide } from '@dydxprotocol/v4-client-js';
import type {
IChartingLibraryWidget,
IOrderLineAdapter,
IPositionLineAdapter,
} from 'public/tradingview/charting_library';
export type TvWidget = IChartingLibraryWidget & { _id?: string; _ready?: boolean };
export type ChartLineType = OrderSide | 'position';
export type ChartLine = {
line: IOrderLineAdapter | IPositionLineAdapter;
chartLineType: ChartLineType;
};
-1
View File
@@ -40,7 +40,6 @@ export enum WalletErrorType {
// General
ChainMismatch,
UserCanceled,
SwitchChainMethodMissing,
// Non-Deterministic
NonDeterministicWallet,
-2
View File
@@ -1,4 +1,2 @@
export { useChartLines } from './useChartLines';
export { useChartMarketAndResolution } from './useChartMarketAndResolution';
export { useTradingView } from './useTradingView';
export { useTradingViewTheme } from './useTradingViewTheme';
-209
View File
@@ -1,209 +0,0 @@
import { useEffect, useState } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import { AbacusOrderStatus, ORDER_SIDES, SubaccountOrder } from '@/constants/abacus';
import { STRING_KEYS } from '@/constants/localization';
import { type OrderType, ORDER_TYPE_STRINGS } from '@/constants/trade';
import type { ChartLine, TvWidget } from '@/constants/tvchart';
import { useStringGetter } from '@/hooks';
import { getCurrentMarketOrders, getCurrentMarketPositionData } from '@/state/accountSelectors';
import { getAppTheme, getAppColorMode } from '@/state/configsSelectors';
import { MustBigNumber } from '@/lib/numbers';
import { getChartLineColors } from '@/lib/tradingView/utils';
let chartLines: Record<string, ChartLine> = {};
/**
* @description Hook to handle drawing chart lines
*/
export const useChartLines = ({
tvWidget,
displayButton,
isChartReady,
}: {
tvWidget: TvWidget | null;
displayButton: HTMLElement | null;
isChartReady?: boolean;
}) => {
const [showOrderLines, setShowOrderLines] = useState(false);
const stringGetter = useStringGetter();
const appTheme = useSelector(getAppTheme);
const appColorMode = useSelector(getAppColorMode);
const currentMarketPositionData = useSelector(getCurrentMarketPositionData, shallowEqual);
const currentMarketOrders: SubaccountOrder[] = useSelector(getCurrentMarketOrders, shallowEqual);
useEffect(() => {
if (isChartReady && displayButton) {
displayButton.onclick = () => {
const newShowOrderLinesState = !showOrderLines;
if (newShowOrderLinesState) {
displayButton?.classList?.add('order-lines-active');
} else {
displayButton?.classList?.remove('order-lines-active');
}
setShowOrderLines(newShowOrderLinesState);
};
}
}, [isChartReady, showOrderLines]);
useEffect(() => {
if (tvWidget && isChartReady) {
tvWidget.onChartReady(() => {
tvWidget.chart().dataReady(() => {
if (showOrderLines) {
drawOrderLines();
drawPositionLine();
} else {
deleteChartLines();
}
});
});
}
}, [isChartReady, showOrderLines, currentMarketPositionData, currentMarketOrders]);
const drawPositionLine = () => {
if (!currentMarketPositionData) return;
const entryPrice = currentMarketPositionData.entryPrice?.current;
const size = currentMarketPositionData.size?.current;
const key = currentMarketPositionData.id;
const price = MustBigNumber(entryPrice).toNumber();
const maybePositionLine = chartLines[key]?.line;
const shouldShow = size && size !== 0;
if (!shouldShow) {
if (maybePositionLine) {
maybePositionLine.remove();
delete chartLines[key];
return;
}
} else {
const quantity = size.toString();
if (maybePositionLine) {
if (maybePositionLine.getQuantity() !== quantity) {
maybePositionLine.setQuantity(quantity);
}
if (maybePositionLine.getPrice() !== price) {
maybePositionLine.setPrice(price);
}
} else {
const positionLine = tvWidget
?.chart()
.createPositionLine({ disableUndo: false })
.setText(stringGetter({ key: STRING_KEYS.ENTRY_PRICE_SHORT }))
.setPrice(price)
.setQuantity(quantity);
if (positionLine) {
const chartLine = { line: positionLine, chartLineType: 'position' };
setLineColors({ chartLine: chartLine });
chartLines[key] = chartLine;
}
}
}
};
const drawOrderLines = () => {
if (!currentMarketOrders) return;
currentMarketOrders.forEach(
({
id,
type,
status,
side,
cancelReason,
remainingSize,
size,
triggerPrice,
price,
trailingPercent,
}) => {
const key = id;
const quantity = (remainingSize ?? size).toString();
const orderType = type.rawValue as OrderType;
const orderLabel = stringGetter({
key: ORDER_TYPE_STRINGS[orderType].orderTypeKey,
});
const orderString = trailingPercent ? `${orderLabel} ${trailingPercent}%` : orderLabel;
const shouldShow =
!cancelReason &&
(status === AbacusOrderStatus.open || status === AbacusOrderStatus.untriggered);
const maybeOrderLine = chartLines[key]?.line;
if (!shouldShow) {
if (maybeOrderLine) {
maybeOrderLine.remove();
delete chartLines[key];
return;
}
} else {
if (maybeOrderLine) {
if (maybeOrderLine.getQuantity() !== quantity) {
maybeOrderLine.setQuantity(quantity);
}
} else {
const orderLine = tvWidget
?.chart()
.createOrderLine({ disableUndo: false })
.setPrice(MustBigNumber(triggerPrice ?? price).toNumber())
.setQuantity(quantity)
.setText(orderString);
if (orderLine) {
const chartLine: ChartLine = {
line: orderLine,
chartLineType: ORDER_SIDES[side.name],
};
setLineColors({ chartLine: chartLine });
chartLines[key] = chartLine;
}
}
}
}
);
};
const deleteChartLines = () => {
Object.values(chartLines).forEach(({ line }) => {
line.remove();
});
chartLines = {};
};
const setLineColors = ({ chartLine }: { chartLine: ChartLine }) => {
const { line, chartLineType } = chartLine;
const { maybeQuantityColor, borderColor, backgroundColor, textColor, textButtonColor } =
getChartLineColors({
appTheme,
appColorMode,
chartLineType,
});
line
.setQuantityBorderColor(borderColor)
.setBodyBackgroundColor(backgroundColor)
.setBodyBorderColor(borderColor)
.setBodyTextColor(textColor)
.setQuantityTextColor(textButtonColor);
maybeQuantityColor &&
line.setLineColor(maybeQuantityColor).setQuantityBackgroundColor(maybeQuantityColor);
};
return { chartLines };
};
@@ -1,65 +0,0 @@
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import type { ResolutionString } from 'public/tradingview/charting_library';
import { DEFAULT_RESOLUTION, RESOLUTION_CHART_CONFIGS } from '@/constants/candles';
import { DEFAULT_MARKETID } from '@/constants/markets';
import type { TvWidget } from '@/constants/tvchart';
import { setTvChartResolution } from '@/state/perpetuals';
import { getCurrentMarketId, getSelectedResolutionForMarket } from '@/state/perpetualsSelectors';
/**
* @description Hook to handle changing markets and setting chart resolution
*/
export const useChartMarketAndResolution = ({
tvWidget,
isWidgetReady,
savedResolution,
}: {
tvWidget: TvWidget | null;
isWidgetReady?: boolean;
savedResolution?: ResolutionString;
}) => {
const dispatch = useDispatch();
const currentMarketId: string = useSelector(getCurrentMarketId) || DEFAULT_MARKETID;
const selectedResolution: string =
useSelector(getSelectedResolutionForMarket(currentMarketId)) || DEFAULT_RESOLUTION;
const chart = isWidgetReady ? tvWidget?.chart() : undefined;
const chartResolution = chart?.resolution?.();
useEffect(() => {
if (currentMarketId && isWidgetReady) {
const resolution = savedResolution || selectedResolution;
tvWidget?.setSymbol(currentMarketId, resolution as ResolutionString, () => {});
}
}, [currentMarketId, isWidgetReady, savedResolution, selectedResolution]);
useEffect(() => {
if (chartResolution) {
if (chartResolution !== selectedResolution) {
dispatch(setTvChartResolution({ marketId: currentMarketId, resolution: chartResolution }));
}
setVisibleRangeForResolution({ resolution: chartResolution });
}
}, [currentMarketId, chartResolution, selectedResolution]);
const setVisibleRangeForResolution = ({ resolution }: { resolution: ResolutionString }) => {
// Different resolutions have different timeframes to display data efficiently.
const { defaultRange } = RESOLUTION_CHART_CONFIGS[resolution];
// from/to values converted to epoch seconds
const newRange = {
from: (Date.now() - defaultRange) / 1000,
to: Date.now() / 1000,
};
tvWidget?.activeChart().setVisibleRange(newRange, { percentRightMargin: 10 });
};
};
+14 -18
View File
@@ -1,19 +1,17 @@
import React, { useEffect } from 'react';
import { useEffect } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import isEmpty from 'lodash/isEmpty';
import { LanguageCode, ResolutionString, widget } from 'public/tradingview/charting_library';
import { DEFAULT_RESOLUTION } from '@/constants/candles';
import { SUPPORTED_LOCALE_BASE_TAGS, STRING_KEYS } from '@/constants/localization';
import { LocalStorageKey } from '@/constants/localStorage';
import type { TvWidget } from '@/constants/tvchart';
import { useDydxClient, useLocalStorage, useStringGetter } from '@/hooks';
import { store } from '@/state/_store';
import { getSelectedNetwork } from '@/state/appSelectors';
import { getAppTheme, getAppColorMode } from '@/state/configsSelectors';
import { getSelectedLocale } from '@/state/localizationSelectors';
@@ -30,8 +28,8 @@ export const useTradingView = ({
displayButtonRef,
setIsChartReady,
}: {
tvWidgetRef: React.MutableRefObject<TvWidget | null>;
displayButtonRef: React.MutableRefObject<HTMLElement | null>;
tvWidgetRef: React.MutableRefObject<any>;
displayButtonRef: React.MutableRefObject<any>;
setIsChartReady: React.Dispatch<React.SetStateAction<boolean>>;
}) => {
const stringGetter = useStringGetter();
@@ -71,17 +69,15 @@ export const useTradingView = ({
tvWidgetRef.current = tvChartWidget;
tvWidgetRef.current.onChartReady(() => {
tvWidgetRef.current?.headerReady().then(() => {
if (displayButtonRef && tvWidgetRef.current) {
displayButtonRef.current = tvWidgetRef.current.createButton();
displayButtonRef.current.innerHTML = `<span>${stringGetter({
key: STRING_KEYS.ORDER_LINES,
})}</span> <div class="displayOrdersButton-toggle"></div>`;
displayButtonRef.current.setAttribute(
'title',
stringGetter({ key: STRING_KEYS.ORDER_LINES_TOOLTIP })
);
}
tvWidgetRef?.current?.headerReady().then(() => {
displayButtonRef.current = tvWidgetRef?.current?.createButton();
displayButtonRef.current.innerHTML = `<span>${stringGetter({
key: STRING_KEYS.ORDER_LINES,
})}</span> <div class="displayOrdersButton-toggle"></div>`;
displayButtonRef.current.setAttribute(
'title',
stringGetter({ key: STRING_KEYS.ORDER_LINES_TOOLTIP })
);
});
tvWidgetRef?.current?.subscribe('onAutoSaveNeeded', () =>
+63 -55
View File
@@ -1,14 +1,16 @@
import { useEffect } from 'react';
import { useSelector } from 'react-redux';
import { THEME_NAMES } from '@/constants/styles/colors';
import type { ChartLine, TvWidget } from '@/constants/tvchart';
import type {
IChartingLibraryWidget,
IOrderLineAdapter,
ThemeName,
} from 'public/tradingview/charting_library';
import { AppColorMode, AppTheme } from '@/state/configs';
import { getAppTheme, getAppColorMode } from '@/state/configsSelectors';
import { getWidgetOverrides, getChartLineColors } from '@/lib/tradingView/utils';
import { getWidgetOverrides, getOrderLineColors } from '@/lib/tradingView/utils';
/**
* @description Method to define a type guard and check that an element is an IFRAME
@@ -24,12 +26,12 @@ const isIFrame = (element: HTMLElement | null): element is HTMLIFrameElement =>
* In order to support our Classic along with Dark/Light, we are directly accessing the <html> within the iFrame.
*/
export const useTradingViewTheme = ({
chartLines,
orderLines,
tvWidget,
isWidgetReady,
}: {
chartLines: Record<string, ChartLine>;
tvWidget: TvWidget | null;
orderLines: Record<string, IOrderLineAdapter>;
tvWidget: (IChartingLibraryWidget & { _id?: string; _ready?: boolean }) | null;
isWidgetReady?: boolean;
}) => {
const appTheme: AppTheme = useSelector(getAppTheme);
@@ -37,64 +39,70 @@ export const useTradingViewTheme = ({
useEffect(() => {
if (tvWidget && isWidgetReady) {
tvWidget.changeTheme?.(THEME_NAMES[appTheme]).then(() => {
const tvChartId = tvWidget?._id;
tvWidget
.changeTheme?.(
{
[AppTheme.Classic]: '',
[AppTheme.Dark]: 'dark',
[AppTheme.Light]: 'light',
}[appTheme] as ThemeName
)
.then(() => {
const tvChartId = tvWidget?._id;
if (tvChartId) {
const frame = document?.getElementById(tvChartId);
if (tvChartId) {
const frame = document?.getElementById(tvChartId);
if (isIFrame(frame) && frame.contentWindow) {
const innerHtml = frame.contentWindow.document.documentElement;
switch (appTheme) {
case AppTheme.Classic:
innerHtml?.classList.remove('theme-dark', 'theme-light');
break;
case AppTheme.Dark:
innerHtml?.classList.remove('theme-light');
innerHtml?.classList.add('theme-dark');
break;
case AppTheme.Light:
innerHtml?.classList.remove('theme-dark');
innerHtml?.classList.add('theme-light');
if (isIFrame(frame) && frame.contentWindow) {
const innerHtml = frame.contentWindow.document.documentElement;
switch (appTheme) {
case AppTheme.Classic:
innerHtml?.classList.remove('theme-dark', 'theme-light');
break;
case AppTheme.Dark:
innerHtml?.classList.remove('theme-light');
innerHtml?.classList.add('theme-dark');
break;
case AppTheme.Light:
innerHtml?.classList.remove('theme-dark');
innerHtml?.classList.add('theme-light');
}
}
}
}
const { overrides, studies_overrides } = getWidgetOverrides({ appTheme, appColorMode });
tvWidget?.applyOverrides(overrides);
tvWidget?.applyStudiesOverrides(studies_overrides);
const { overrides, studies_overrides } = getWidgetOverrides({ appTheme, appColorMode });
tvWidget?.applyOverrides(overrides);
tvWidget?.applyStudiesOverrides(studies_overrides);
// Necessary to update existing indicators
const volumeStudyId = tvWidget
?.activeChart()
?.getAllStudies()
?.find((x) => x.name === 'Volume')?.id;
// Necessary to update existing indicators
const volumeStudyId = tvWidget
?.activeChart()
?.getAllStudies()
?.find((x) => x.name === 'Volume')?.id;
if (volumeStudyId) {
const volume = tvWidget?.activeChart()?.getStudyById(volumeStudyId);
volume.applyOverrides({
'volume.color.0': studies_overrides['volume.volume.color.0'],
'volume.color.1': studies_overrides['volume.volume.color.1'],
});
}
// Necessary to update existing chart lines
Object.values(chartLines).forEach(({ chartLineType, line }) => {
const { maybeQuantityColor, borderColor, backgroundColor, textColor, textButtonColor } =
getChartLineColors({ chartLineType: chartLineType, appTheme, appColorMode });
if (maybeQuantityColor) {
line.setLineColor(maybeQuantityColor).setQuantityBackgroundColor(maybeQuantityColor);
if (volumeStudyId) {
const volume = tvWidget?.activeChart()?.getStudyById(volumeStudyId);
volume.applyOverrides({
'volume.color.0': studies_overrides['volume.volume.color.0'],
'volume.color.1': studies_overrides['volume.volume.color.1'],
});
}
line
.setQuantityBorderColor(borderColor)
.setBodyBackgroundColor(backgroundColor)
.setBodyBorderColor(borderColor)
.setBodyTextColor(textColor)
.setQuantityTextColor(textButtonColor);
// Necessary to update existing chart lines
Object.entries(orderLines).forEach(([key, line]) => {
const { orderColor, borderColor, backgroundColor, textColor, textButtonColor } =
getOrderLineColors({ side: key.split('-')[0], appTheme, appColorMode });
line
.setLineColor(orderColor)
.setQuantityBackgroundColor(orderColor)
.setQuantityBorderColor(borderColor)
.setBodyBackgroundColor(backgroundColor)
.setBodyBorderColor(borderColor)
.setBodyTextColor(textColor)
.setQuantityTextColor(textButtonColor);
});
});
});
}
}, [appTheme, appColorMode, isWidgetReady]);
};
+6 -12
View File
@@ -4,11 +4,8 @@ import { useSelector } from 'react-redux';
import { DEFAULT_DOCUMENT_TITLE } from '@/constants/routes';
import { getSelectedLocale } from '@/state/localizationSelectors';
import {
getCurrentMarketId,
getCurrentMarketMidMarketPrice,
getCurrentMarketOraclePrice,
} from '@/state/perpetualsSelectors';
import { getCurrentMarketData, getCurrentMarketId } from '@/state/perpetualsSelectors';
import type { RootState } from '@/state/_store';
import { useBreakpoints } from './useBreakpoints';
@@ -16,14 +13,11 @@ export const usePageTitlePriceUpdates = () => {
const selectedLocale = useSelector(getSelectedLocale);
const { isNotTablet } = useBreakpoints();
const id = useSelector(getCurrentMarketId);
const oraclePrice = useSelector(getCurrentMarketOraclePrice);
const orderbookMidMarketPrice = useSelector(getCurrentMarketMidMarketPrice);
const price = orderbookMidMarketPrice ?? oraclePrice;
const oraclePrice = useSelector((state: RootState) => getCurrentMarketData(state)?.oraclePrice);
useEffect(() => {
if (id && price && isNotTablet) {
const priceString = price.toLocaleString(selectedLocale);
if (id && oraclePrice && isNotTablet) {
const priceString = oraclePrice.toLocaleString(selectedLocale);
document.title = `$${priceString} ${id} · ${DEFAULT_DOCUMENT_TITLE}`;
} else {
document.title = DEFAULT_DOCUMENT_TITLE;
@@ -32,5 +26,5 @@ export const usePageTitlePriceUpdates = () => {
return () => {
document.title = DEFAULT_DOCUMENT_TITLE;
};
}, [price]);
}, [oraclePrice]);
};
+7 -10
View File
@@ -163,20 +163,17 @@ export const useWalletConnection = () => {
}
}
} catch (error) {
const { isErrorExpected } = parseWalletError({ error, stringGetter });
if (!isErrorExpected) {
throw Object.assign(
new Error([error.message, error.cause?.message].filter(Boolean).join('\n')),
{
walletConnectionType: walletConnection?.type,
}
);
}
throw Object.assign(
new Error([error.message, error.cause?.message].filter(Boolean).join('\n')),
{
walletConnectionType: walletConnection?.type,
}
);
}
return {
walletType,
walletConnectionType: walletConnection?.type,
walletConnectionType: walletConnection.type,
};
},
[isConnectedGraz, signerGraz, isConnectedWagmi, signerWagmi]
+8 -11
View File
@@ -1,10 +1,8 @@
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { Candle, TradingViewBar, TradingViewSymbol } from '@/constants/candles';
import { THEME_NAMES } from '@/constants/styles/colors';
import type { ChartLineType } from '@/constants/tvchart';
import { type AppColorMode, AppTheme } from '@/state/configs';
import { AppTheme, type AppColorMode } from '@/state/configs';
import { Themes } from '@/styles/themes';
@@ -51,24 +49,23 @@ export const getHistorySlice = ({
return bars.filter(({ time }) => time >= fromMs);
};
export const getChartLineColors = ({
export const getOrderLineColors = ({
appTheme,
appColorMode,
chartLineType,
side,
}: {
appTheme: AppTheme;
appColorMode: AppColorMode;
chartLineType: ChartLineType;
side: OrderSide;
}) => {
const theme = Themes[appTheme][appColorMode];
const orderColors = {
const orderColor = {
[OrderSide.BUY]: theme.positive,
[OrderSide.SELL]: theme.negative,
['position']: null,
};
}[side];
return {
maybeQuantityColor: orderColors[chartLineType],
orderColor,
borderColor: theme.borderDefault,
backgroundColor: theme.layer1,
textColor: theme.textTertiary,
@@ -86,7 +83,7 @@ export const getWidgetOverrides = ({
const theme = Themes[appTheme][appColorMode];
return {
theme: THEME_NAMES[appTheme],
theme: appTheme === AppTheme.Dark ? 'dark' : AppTheme.Light ? 'light' : '',
overrides: {
'paneProperties.background': theme.layer2,
'paneProperties.horzGridProperties.color': theme.layer3,
+1 -11
View File
@@ -92,10 +92,6 @@ export const getWalletErrorType = ({ error }: { error: Error }) => {
return WalletErrorType.ChainMismatch;
}
if (messageLower.includes('Missing or invalid. request() method: wallet_switchEthereumChain')) {
return WalletErrorType.SwitchChainMethodMissing;
}
// ImToken - User canceled
if (messageLower.includes('用户取消了操作')) {
return WalletErrorType.UserCanceled;
@@ -117,18 +113,13 @@ export const parseWalletError = ({
}) => {
const walletErrorType = getWalletErrorType({ error });
let message;
let isErrorExpected;
switch (walletErrorType) {
case WalletErrorType.ChainMismatch:
case WalletErrorType.UserCanceled:
case WalletErrorType.SwitchChainMethodMissing: {
isErrorExpected = true;
message = error.message;
case WalletErrorType.UserCanceled: {
break;
}
default: {
isErrorExpected = false;
message = stringGetter({
key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE,
params: {
@@ -141,6 +132,5 @@ export const parseWalletError = ({
return {
walletErrorType,
message,
isErrorExpected,
};
};
-6
View File
@@ -107,12 +107,6 @@ export const getCurrentMarketHistoricalFundings = createSelector(
currentMarketId ? historicalFundings?.[currentMarketId] ?? [] : []
);
/**
* @returns oracle price of the market the user is currently viewing
*/
export const getCurrentMarketOraclePrice = (state: RootState) =>
getCurrentMarketData(state)?.oraclePrice;
/**
* @returns Mid market price for the market the user is currently viewing
*/
+184 -19
View File
@@ -1,40 +1,205 @@
import { useRef, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import styled, { type AnyStyledComponent, css } from 'styled-components';
import type { ResolutionString } from 'public/tradingview/charting_library';
import type {
IChartingLibraryWidget,
IOrderLineAdapter,
ResolutionString,
} from 'public/tradingview/charting_library';
import type { TvWidget } from '@/constants/tvchart';
import { AbacusOrderStatus } from '@/constants/abacus';
import { DEFAULT_RESOLUTION, RESOLUTION_CHART_CONFIGS } from '@/constants/candles';
import { DEFAULT_MARKETID } from '@/constants/markets';
import { type OrderType, ORDER_TYPE_STRINGS } from '@/constants/trade';
import {
useChartLines,
useChartMarketAndResolution,
useTradingView,
useTradingViewTheme,
} from '@/hooks/tradingView';
import { useStringGetter } from '@/hooks';
import { useTradingView, useTradingViewTheme } from '@/hooks/tradingView';
import { LoadingSpace } from '@/components/Loading/LoadingSpinner';
import { getCurrentMarketOrders } from '@/state/accountSelectors';
import { getAppTheme, getAppColorMode } from '@/state/configsSelectors';
import { setTvChartResolution } from '@/state/perpetuals';
import { getCurrentMarketId, getSelectedResolutionForMarket } from '@/state/perpetualsSelectors';
import { layoutMixins } from '@/styles/layoutMixins';
import { MustBigNumber } from '@/lib/numbers';
import { getOrderLineColors } from '@/lib/tradingView/utils';
type TvWidget = IChartingLibraryWidget & { _id?: string; _ready?: boolean };
let orderLines: Record<string, IOrderLineAdapter> = {};
export const TvChart = () => {
const dispatch = useDispatch();
const stringGetter = useStringGetter();
const [isChartReady, setIsChartReady] = useState(false);
const [showOrderLines, setShowOrderLines] = useState(false);
const displayButtonRef = useRef<HTMLElement | null>(null);
const appTheme = useSelector(getAppTheme);
const appColorMode = useSelector(getAppColorMode);
const currentMarketId: string = useSelector(getCurrentMarketId) || DEFAULT_MARKETID;
const currentMarketOrders = useSelector(getCurrentMarketOrders, shallowEqual);
const selectedResolution: string =
useSelector(getSelectedResolutionForMarket(currentMarketId)) || DEFAULT_RESOLUTION;
const tvWidgetRef = useRef<TvWidget | null>(null);
const tvWidget = tvWidgetRef.current;
const isWidgetReady = tvWidget?._ready;
const displayButtonRef = useRef<HTMLElement | null>(null);
const displayButton = displayButtonRef.current;
const chart = isWidgetReady ? tvWidget?.chart() : undefined;
const chartResolution = chart?.resolution?.();
const { savedResolution } = useTradingView({ tvWidgetRef, displayButtonRef, setIsChartReady });
useChartMarketAndResolution({
tvWidget,
isWidgetReady,
savedResolution: savedResolution as ResolutionString | undefined,
});
const { chartLines } = useChartLines({ tvWidget, displayButton, isChartReady });
useTradingViewTheme({ tvWidget, isWidgetReady, chartLines });
useTradingViewTheme({ tvWidget, isWidgetReady, orderLines });
const setVisibleRangeForResolution = ({ resolution }: { resolution: ResolutionString }) => {
// Different resolutions have different timeframes to display data efficiently.
const { defaultRange } = RESOLUTION_CHART_CONFIGS[resolution];
// from/to values converted to epoch seconds
const newRange = {
from: (Date.now() - defaultRange) / 1000,
to: Date.now() / 1000,
};
tvWidget?.activeChart().setVisibleRange(newRange, { percentRightMargin: 10 });
};
/**
* @description Hook to handle changing chart resolution
*/
useEffect(() => {
if (chartResolution) {
if (chartResolution !== selectedResolution) {
dispatch(setTvChartResolution({ marketId: currentMarketId, resolution: chartResolution }));
}
setVisibleRangeForResolution({ resolution: chartResolution });
}
}, [chartResolution]);
/**
* @description Hook to handle changing markets
*/
useEffect(() => {
if (currentMarketId && isWidgetReady) {
const resolution = savedResolution || selectedResolution;
tvWidget?.setSymbol(currentMarketId, resolution as ResolutionString, () => {});
}
}, [currentMarketId, isWidgetReady]);
/**
* @description Hook to handle order line toggle state
*/
useEffect(() => {
if (isChartReady && displayButtonRef && displayButtonRef.current) {
displayButtonRef.current.onclick = () => {
const newShowOrderLinesState = !showOrderLines;
if (newShowOrderLinesState) {
displayButtonRef.current?.classList?.add('order-lines-active');
} else {
displayButtonRef.current?.classList?.remove('order-lines-active');
}
setShowOrderLines(newShowOrderLinesState);
};
}
}, [isChartReady, showOrderLines]);
/**
* @description Hook to handle drawing order lines
*/
useEffect(() => {
if (tvWidget && isChartReady) {
tvWidget.onChartReady(() => {
tvWidget.chart().dataReady(() => {
if (showOrderLines) {
drawOrderLines();
} else {
deleteOrderLines();
}
});
});
}
}, [isChartReady, showOrderLines, currentMarketOrders, currentMarketId]);
const drawOrderLines = () => {
if (!currentMarketOrders) return;
currentMarketOrders.forEach(
({
id,
type,
status,
side,
cancelReason,
remainingSize,
size,
triggerPrice,
price,
trailingPercent,
}) => {
const key = `${side.rawValue}-${id}`;
const quantity = (remainingSize ?? size).toString();
const orderType = type.rawValue as OrderType;
const orderLabel = stringGetter({
key: ORDER_TYPE_STRINGS[orderType].orderTypeKey,
});
const orderString = trailingPercent ? `${orderLabel} ${trailingPercent}%` : orderLabel;
const shouldShow =
!cancelReason &&
(status === AbacusOrderStatus.open || status === AbacusOrderStatus.untriggered);
const maybeOrderLine = key in orderLines ? orderLines[key] : null;
if (maybeOrderLine) {
if (!shouldShow) {
maybeOrderLine.remove();
delete orderLines[key];
return;
} else if (maybeOrderLine.getQuantity() !== quantity) {
maybeOrderLine.setQuantity(quantity);
return;
}
} else if (shouldShow) {
const { orderColor, borderColor, backgroundColor, textColor, textButtonColor } =
getOrderLineColors({ side: side.rawValue, appTheme, appColorMode });
const orderLine = tvWidget
?.chart()
.createOrderLine({ disableUndo: false })
.setPrice(MustBigNumber(triggerPrice ?? price).toNumber())
.setQuantity(quantity)
.setText(orderString)
.setLineColor(orderColor)
.setQuantityBackgroundColor(orderColor)
.setQuantityBorderColor(borderColor)
.setBodyBackgroundColor(backgroundColor)
.setBodyBorderColor(borderColor)
.setBodyTextColor(textColor)
.setQuantityTextColor(textButtonColor);
if (orderLine) {
orderLines[key] = orderLine;
}
}
}
);
};
const deleteOrderLines = () => {
Object.values(orderLines).forEach((line) => {
line.remove();
});
orderLines = {};
};
return (
<Styled.PriceChart isChartReady={isChartReady}>
@@ -32,15 +32,7 @@ export const ExternalNavKeplrDialog = ({ setIsOpen }: ElementProps) => {
openDialog({
type: DialogTypes.ExternalLink,
dialogProps: {
buttonText: stringGetter({ key: STRING_KEYS.CONTINUE }),
link: keplrDashboard,
title: stringGetter({ key: STRING_KEYS.LEAVING_WEBSITE_STAKING_GOVERNANCE }),
slotContent: stringGetter({
key: STRING_KEYS.STAKE_WITH_KEPLR_AND_LEAVING_DESCRIPTION,
params: {
CTA: stringGetter({ key: STRING_KEYS.CONTINUE }),
},
}),
},
})
);
@@ -44,6 +44,7 @@ export const GenerateKeys = ({
onKeysDerived = () => {},
}: ElementProps) => {
const stringGetter = useStringGetter();
const { isMobile } = useBreakpoints();
const [shouldRememberMe, setShouldRememberMe] = useState(false);
@@ -65,30 +66,17 @@ export const GenerateKeys = ({
try {
await matchNetwork?.();
return true;
} catch (error) {
const { message, walletErrorType, isErrorExpected } = parseWalletError({
error,
stringGetter,
});
if (!isErrorExpected) {
log('GenerateKeys/switchNetwork', error, { walletErrorType });
}
const { message, walletErrorType } = parseWalletError({ error, stringGetter });
if (message) {
log('GenerateKeys/switchNetwork', error, { walletErrorType });
setError(message);
throw error;
}
return false;
}
};
const switchNetworkAndDeriveKeys = async () => {
const networkSwitched = await switchNetwork();
if (networkSwitched) await deriveKeys();
};
// 2. Derive keys from EVM account
const { getWalletFromEvmSignature } = useDydxClient();
const { getSubaccounts } = useAccounts();
@@ -166,16 +154,11 @@ export const GenerateKeys = ({
setStatus(EvmDerivedAccountStatus.Derived);
} catch (error) {
setStatus(EvmDerivedAccountStatus.NotDerived);
const { message, walletErrorType, isErrorExpected } = parseWalletError({
error,
stringGetter,
});
const { message, walletErrorType } = parseWalletError({ error, stringGetter });
if (message) {
setError(message);
if (!isErrorExpected) {
log('GenerateKeys/deriveKeys', error, { walletErrorType });
}
log('GenerateKeys/deriveKeys', error, { walletErrorType });
}
}
};
@@ -248,7 +231,7 @@ export const GenerateKeys = ({
{!isMatchingNetwork ? (
<Button
action={ButtonAction.Primary}
onClick={() => switchNetworkAndDeriveKeys().then(onKeysDerived)}
onClick={() => switchNetwork().then(deriveKeys).then(onKeysDerived)}
state={{ isLoading: isSwitchingNetwork }}
>
{stringGetter({ key: STRING_KEYS.SWITCH_NETWORK })}
+2 -7
View File
@@ -14,7 +14,6 @@ import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/b
import { TOKEN_DECIMALS } from '@/constants/numbers';
import { STRING_KEYS } from '@/constants/localization';
import { MobilePlaceOrderSteps } from '@/constants/trade';
import { useBreakpoints, useIsFirstRender, useStringGetter, useSubaccount } from '@/hooks';
import { useOnLastOrderIndexed } from '@/hooks/useOnLastOrderIndexed';
@@ -36,6 +35,7 @@ import { Orderbook, orderbookMixins, type OrderbookScrollBehavior } from '@/view
import { PositionPreview } from '@/views/forms/TradeForm/PositionPreview';
import { getCurrentMarketPositionData } from '@/state/accountSelectors';
import { getCurrentMarketAssetData } from '@/state/assetsSelectors';
import { getClosePositionInputErrors, getInputClosePositionData } from '@/state/inputsSelectors';
import { getCurrentMarketConfig, getCurrentMarketId } from '@/state/perpetualsSelectors';
@@ -291,14 +291,9 @@ export const ClosePositionForm = ({
isLoading={isClosingPosition}
hasValidationErrors={hasInputErrors}
actionStringKey={inputAlert?.actionStringKey}
validationErrorString={alertContent}
summary={summary ?? undefined}
currentStep={currentStep}
confirmButtonConfig={{
stringKey: STRING_KEYS.CLOSE_ORDER,
buttonTextStringKey: STRING_KEYS.CLOSE_POSITION,
buttonAction: ButtonAction.Destroy,
}}
isClosePosition
/>
</Styled.Footer>
</Styled.ClosePositionForm>
+5 -41
View File
@@ -1,10 +1,8 @@
import { type FormEvent, useState, Ref, useCallback } from 'react';
import { type FormEvent, useState, Ref, useCallback, useEffect } from 'react';
import styled, { AnyStyledComponent, css } from 'styled-components';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import type { NumberFormatValues, SourceInfo } from 'react-number-format';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { AlertType } from '@/constants/alerts';
import {
@@ -18,12 +16,7 @@ import {
import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { USD_DECIMALS } from '@/constants/numbers';
import {
InputErrorData,
TradeBoxKeys,
MobilePlaceOrderSteps,
ORDER_TYPE_STRINGS,
} from '@/constants/trade';
import { InputErrorData, TradeBoxKeys, MobilePlaceOrderSteps } from '@/constants/trade';
import { breakpoints } from '@/styles';
import { useStringGetter, useSubaccount } from '@/hooks';
@@ -44,16 +37,11 @@ import { WithTooltip } from '@/components/WithTooltip';
import { Orderbook } from '@/views/tables/Orderbook';
import { setTradeFormInputs } from '@/state/inputs';
import {
getCurrentInput,
getInputTradeData,
getTradeFormInputs,
useTradeFormData,
} from '@/state/inputsSelectors';
import { getCurrentInput, getTradeFormInputs, useTradeFormData } from '@/state/inputsSelectors';
import { getCurrentMarketConfig } from '@/state/perpetualsSelectors';
import abacusStateManager from '@/lib/abacus';
import { getSelectedOrderSide, getSelectedTradeType, getTradeInputAlert } from '@/lib/tradeData';
import { getTradeInputAlert } from '@/lib/tradeData';
import { AdvancedTradeOptions } from './TradeForm/AdvancedTradeOptions';
import { TradeSizeInputs } from './TradeForm/TradeSizeInputs';
@@ -120,21 +108,8 @@ export const TradeForm = ({
const tradeFormInputValues = useSelector(getTradeFormInputs, shallowEqual);
const { limitPriceInput, triggerPriceInput, trailingPercentInput } = tradeFormInputValues;
const currentTradeData = useSelector(getInputTradeData, shallowEqual);
const { side, type } = currentTradeData || {};
const selectedTradeType = getSelectedTradeType(type);
const selectedOrderSide = getSelectedOrderSide(side);
const needsAdvancedOptions =
needsGoodUntil ||
timeInForceOptions ||
executionOptions ||
needsPostOnly ||
postOnlyTooltip ||
needsReduceOnly ||
reduceOnlyTooltip;
needsGoodUntil || timeInForceOptions || executionOptions || (needsPostOnly || postOnlyTooltip) || (needsReduceOnly || reduceOnlyTooltip);
const tradeFormInputs: TradeBoxInputConfig[] = [];
@@ -164,11 +139,6 @@ export const TradeForm = ({
alertType = inputAlert?.type;
}
const orderSideAction = {
[OrderSide.BUY]: ButtonAction.Create,
[OrderSide.SELL]: ButtonAction.Destroy,
}[selectedOrderSide];
const onSubmit = async (e: FormEvent) => {
e.preventDefault();
@@ -349,15 +319,9 @@ export const TradeForm = ({
isLoading={isPlacingOrder}
hasValidationErrors={hasInputErrors}
actionStringKey={inputAlert?.actionStringKey}
validationErrorString={alertContent}
summary={summary ?? undefined}
currentStep={currentStep}
showDeposit={inputAlert?.errorAction === TradeInputErrorAction.DEPOSIT}
confirmButtonConfig={{
stringKey: ORDER_TYPE_STRINGS[selectedTradeType].orderTypeKey,
buttonTextStringKey: STRING_KEYS.PLACE_ORDER,
buttonAction: orderSideAction,
}}
/>
</Styled.Footer>
</Styled.TradeForm>
@@ -1,17 +1,16 @@
import { useDispatch, useSelector } from 'react-redux';
import styled, { type AnyStyledComponent, css } from 'styled-components';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import type { TradeInputSummary } from '@/constants/abacus';
import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
import { DialogTypes } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { MobilePlaceOrderSteps } from '@/constants/trade';
import { ORDER_TYPE_STRINGS, MobilePlaceOrderSteps } from '@/constants/trade';
import { useStringGetter, useTokenConfigs } from '@/hooks';
import { AssetIcon } from '@/components/AssetIcon';
import { Button } from '@/components/Button';
import { Icon, IconName } from '@/components/Icon';
import { Output, OutputType, ShowSign } from '@/components/Output';
import { WithDetailsReceipt } from '@/components/WithDetailsReceipt';
import { WithTooltip } from '@/components/WithTooltip';
@@ -21,34 +20,30 @@ import { OnboardingTriggerButton } from '@/views/dialogs/OnboardingTriggerButton
import { calculateCanAccountTrade } from '@/state/accountCalculators';
import { getSubaccountId } from '@/state/accountSelectors';
import { openDialog } from '@/state/dialogs';
import { getCurrentInput } from '@/state/inputsSelectors';
import { getCurrentInput, getInputTradeData } from '@/state/inputsSelectors';
type ConfirmButtonConfig = {
stringKey: string;
buttonTextStringKey: string;
buttonAction: ButtonAction;
};
import { getSelectedOrderSide, getSelectedTradeType } from '@/lib/tradeData';
type ElementProps = {
isLoading: boolean;
isClosePosition?: boolean;
actionStringKey?: string;
summary?: TradeInputSummary;
hasValidationErrors?: boolean;
validationErrorString?: string;
currentStep?: MobilePlaceOrderSteps;
showDeposit?: boolean;
confirmButtonConfig: ConfirmButtonConfig;
showConnectWallet?: boolean;
};
export const PlaceOrderButtonAndReceipt = ({
isLoading,
isClosePosition,
actionStringKey,
summary,
hasValidationErrors,
validationErrorString,
currentStep,
showDeposit,
confirmButtonConfig,
showConnectWallet,
}: ElementProps) => {
const stringGetter = useStringGetter();
const dispatch = useDispatch();
@@ -57,12 +52,18 @@ export const PlaceOrderButtonAndReceipt = ({
const canAccountTrade = useSelector(calculateCanAccountTrade);
const subaccountNumber = useSelector(getSubaccountId);
const currentInput = useSelector(getCurrentInput);
const currentTradeData = useSelector(getInputTradeData, shallowEqual);
const hasMissingData = subaccountNumber === undefined;
const shouldEnableTrade =
canAccountTrade && !hasMissingData && !hasValidationErrors && currentInput !== 'transfer';
const { side, type } = currentTradeData || {};
const selectedTradeType = getSelectedTradeType(type);
const selectedOrderSide = getSelectedOrderSide(side);
const { fee, price: expectedPrice, total, reward } = summary || {};
const items = [
@@ -109,6 +110,11 @@ export const PlaceOrderButtonAndReceipt = ({
},
];
const orderSideAction = {
[OrderSide.BUY]: ButtonAction.Create,
[OrderSide.SELL]: ButtonAction.Destroy,
}[selectedOrderSide];
const buttonStatesPerStep = {
[MobilePlaceOrderSteps.EditOrder]: {
buttonTextStringKey: shouldEnableTrade
@@ -122,7 +128,7 @@ export const PlaceOrderButtonAndReceipt = ({
[MobilePlaceOrderSteps.PreviewOrder]: {
buttonTextStringKey: STRING_KEYS.CONFIRM_ORDER,
buttonAction: confirmButtonConfig.buttonAction,
buttonAction: isClosePosition ? ButtonAction.Destroy : orderSideAction,
buttonState: { isLoading },
},
[MobilePlaceOrderSteps.PlacingOrder]: {
@@ -139,13 +145,15 @@ export const PlaceOrderButtonAndReceipt = ({
const buttonAction = currentStep
? buttonStatesPerStep[currentStep].buttonAction
: confirmButtonConfig.buttonAction;
: isClosePosition
? ButtonAction.Destroy
: orderSideAction;
let buttonTextStringKey = STRING_KEYS.UNAVAILABLE;
if (currentStep) {
buttonTextStringKey = buttonStatesPerStep[currentStep].buttonTextStringKey;
} else if (shouldEnableTrade) {
buttonTextStringKey = confirmButtonConfig.buttonTextStringKey;
buttonTextStringKey = isClosePosition ? STRING_KEYS.CLOSE_POSITION : STRING_KEYS.PLACE_ORDER;
} else if (actionStringKey) {
buttonTextStringKey = actionStringKey;
}
@@ -157,56 +165,31 @@ export const PlaceOrderButtonAndReceipt = ({
isLoading: isLoading || hasMissingData,
};
const depositButton = (
<Button
action={ButtonAction.Primary}
onClick={() => dispatch(openDialog({ type: DialogTypes.Deposit }))}
>
{stringGetter({ key: STRING_KEYS.DEPOSIT_FUNDS })}
</Button>
);
const submitButton = (
<Styled.Button
state={buttonState}
type={ButtonType.Submit}
action={buttonAction}
slotLeft={
hasValidationErrors ? <Styled.WarningIcon iconName={IconName.Warning} /> : undefined
}
>
{stringGetter({
key: buttonTextStringKey,
params: {
ORDER: stringGetter({
key: confirmButtonConfig.stringKey,
}),
},
})}
</Styled.Button>
);
return (
<WithDetailsReceipt detailItems={items}>
{!canAccountTrade ? (
{!canAccountTrade || showConnectWallet ? (
<OnboardingTriggerButton size={ButtonSize.Base} />
) : showDeposit ? (
depositButton
<Button
action={ButtonAction.Primary}
onClick={() => dispatch(openDialog({ type: DialogTypes.Deposit }))}
>
{stringGetter({ key: STRING_KEYS.DEPOSIT_FUNDS })}
</Button>
) : (
<WithTooltip tooltipString={hasValidationErrors ? validationErrorString : undefined}>
{submitButton}
</WithTooltip>
<Button state={buttonState} type={ButtonType.Submit} action={buttonAction}>
{stringGetter({
key: buttonTextStringKey,
params: {
ORDER: stringGetter({
key: isClosePosition
? STRING_KEYS.CLOSE_ORDER
: ORDER_TYPE_STRINGS[selectedTradeType].orderTypeKey,
}),
},
})}
</Button>
)}
</WithDetailsReceipt>
);
};
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Button = styled(Button)`
width: 100%;
`;
Styled.WarningIcon = styled(Icon)`
color: var(--color-warning);
`;