Compare commits

..
Author SHA1 Message Date
yujin512 edf40ab99f fix url and market name 2024-02-15 20:49:03 -05:00
yujin512 ff7cfb3655 add icons for other markets 2024-02-15 18:47:52 -05:00
yujin512 6ce57aaa3b Update otherMarketParameters.json 2024-02-15 18:46:18 -05:00
yujin512 3540300574 update asset name to match cmc 2024-02-15 14:15:49 -05:00
yujin512 156168ed18 Update otherMarketParameters.json
update numOracles for markets with robust DEX oracles
2024-02-14 16:14:57 -05:00
yujin512 04741d0b5a Update otherMarketParameters.json
update numOracles for markets with robust DEX oracles (not currently queryable)
2024-02-14 16:13:45 -05:00
yujin512 33fbcf74b8 add icons 2024-02-14 16:10:50 -05:00
moo-onthelawn 5531e21c76 change text color (#302) 2024-02-14 14:29:38 -05:00
John Huang 4ebeb21841 MOB-231 Refactor env.json to env2.json (#303)
* MOB-231 Refactor env.json to env2.json

* Move to v1/env.json

* dydx-testnet-4
2024-02-14 13:37:51 -05:00
moo-onthelawn b638ec1ad6 CT-463 Add Position (entry) line to TradingView (#292)
* wip

* fix deleted line issue

* compress tgz changes

* fix dependency hook

* simplify

* typo

* add trailing percent

* fix bug

* fix order

* merge order types

* wip

* rename variable

* review comments

* remove file

* rebase on new changes

* remove log

* clean up chart line type

* clean up typings a bit more

* fix var rename

* review comments

* clean up logic

* typing
2024-02-14 09:41:04 -05:00
Jared Vu f7a1cfc67e Updated Keplr language (#300) 2024-02-13 17:07:22 -08:00
aleka d66a776ad2 show trading rewards in testnet (#299) 2024-02-13 16:02:10 -05:00
moo-onthelawn e3e00f4f1e CT-145 Add warning icon + tooltip to trade order button CTA (#297)
* update

* rename to config
2024-02-13 16:00:46 -05:00
aleka 9ea578b8e4 avoid logging expected switch chain error (#291)
* avoid logging expected switch chain error

* skip logging more expected errors

* update switch chain error requirements check
2024-02-13 13:51:47 -05:00
moo-onthelawn f47fda45bf CT-428 Update browser tab title to be mid market price (#294)
* calculation

* make new selectors
2024-02-13 13:49:24 -05:00
55 changed files with 1450 additions and 511 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.28",
"@dydxprotocol/v4-localization": "^1.1.30",
"@ethersproject/providers": "^5.7.2",
"@js-joda/core": "^5.5.3",
"@radix-ui/react-accordion": "^1.1.2",
+4 -4
View File
@@ -36,8 +36,8 @@ dependencies:
specifier: ^1.0.20
version: 1.0.20
'@dydxprotocol/v4-localization':
specifier: ^1.1.28
version: 1.1.28
specifier: ^1.1.30
version: 1.1.30
'@ethersproject/providers':
specifier: ^5.7.2
version: 5.7.2
@@ -1323,8 +1323,8 @@ packages:
- utf-8-validate
dev: false
/@dydxprotocol/v4-localization@1.1.28:
resolution: {integrity: sha512-R8hGDbJvZb9HsI0jwYIYDS3aNM3T0aLlXOnogihGrRRibZs874XJy5fCGBW2aRXAyDu56nbjkyuOAQCkF3KP1Q==}
/@dydxprotocol/v4-localization@1.1.30:
resolution: {integrity: sha512-TZfWWRSOxcjLHs972wlJVVHkE7+DVqAUnGZSs24HYHsPtUkPhZiNXMOA2Vk9YddQxumhM79xIRH0cmJSe5DDUg==}
dev: false
/@dydxprotocol/v4-proto@4.0.0-dev.0:
+155 -1
View File
@@ -20,6 +20,13 @@
"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"],
@@ -78,7 +85,7 @@
},
"BLUR-USD": {
"name": "Blur",
"tags": [],
"tags": ["NFT"],
"websiteLink": "https://blur.io/",
"whitepaperLink": "https://docs.blur.foundation/",
"coinMarketCapsLink": "https://coinmarketcap.com/currencies/blur-token/"
@@ -91,6 +98,13 @@
"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": [],
@@ -147,6 +161,13 @@
"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"],
@@ -170,6 +191,13 @@
"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"],
@@ -177,6 +205,34 @@
"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"],
@@ -184,6 +240,13 @@
"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"],
@@ -191,6 +254,27 @@
"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"],
@@ -198,6 +282,13 @@
"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"],
@@ -219,6 +310,20 @@
"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"],
@@ -226,6 +331,13 @@
"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"],
@@ -240,6 +352,13 @@
"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": [],
@@ -253,6 +372,20 @@
"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"],
@@ -260,6 +393,13 @@
"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"],
@@ -288,6 +428,13 @@
"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"],
@@ -337,6 +484,13 @@
"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": 5,
"numOracles": 6,
"liquidityTier": 2,
"assetName": "Curve DAO Token",
"p": -1.0,
@@ -325,7 +325,7 @@
{
"baseAsset": "DYM",
"referencePrice": 5.544949130771594,
"numOracles": 5,
"numOracles": 6,
"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": "STEPN",
"assetName": "GMT",
"p": -1.0,
"atomicResolution": -5.0,
"minExchanges": 3,
@@ -514,7 +514,7 @@
"referencePrice": 0.07686278766140874,
"numOracles": 6,
"liquidityTier": 2,
"assetName": "Hedera Hashgraph",
"assetName": "Hedera",
"p": -2.0,
"atomicResolution": -4.0,
"minExchanges": 3,
@@ -565,7 +565,7 @@
"referencePrice": 33.427379051845875,
"numOracles": 7,
"liquidityTier": 2,
"assetName": "Injective Protocol",
"assetName": "Injective",
"p": 1.0,
"atomicResolution": -7.0,
"minExchanges": 3,
@@ -597,7 +597,7 @@
{
"baseAsset": "JUP",
"referencePrice": 0.4339790660244292,
"numOracles": 5,
"numOracles": 6,
"liquidityTier": 2,
"assetName": "Jupiter",
"p": -1.0,
@@ -750,7 +750,7 @@
{
"baseAsset": "MKR",
"referencePrice": 1943.1865605831852,
"numOracles": 5,
"numOracles": 6,
"liquidityTier": 2,
"assetName": "Maker",
"p": 3.0,
@@ -869,7 +869,7 @@
{
"baseAsset": "RUNE",
"referencePrice": 3.7264809096406806,
"numOracles": 5,
"numOracles": 6,
"liquidityTier": 2,
"assetName": "Thorchain",
"p": 0.0,
@@ -888,7 +888,7 @@
"referencePrice": 0.37297439115035663,
"numOracles": 6,
"liquidityTier": 2,
"assetName": "SAND",
"assetName": "The Sandbox",
"p": -1.0,
"atomicResolution": -5.0,
"minExchanges": 3,
@@ -1073,9 +1073,9 @@
{
"baseAsset": "WOO",
"referencePrice": 0.2944897275608197,
"numOracles": 5,
"numOracles": 6,
"liquidityTier": 2,
"assetName": "Wootrade Network",
"assetName": "WOO Network",
"p": -1.0,
"atomicResolution": -5.0,
"minExchanges": 3,
+635
View File
@@ -0,0 +1,635 @@
{
"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.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

+22
View File
@@ -8,6 +8,7 @@ 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',
@@ -20,6 +21,7 @@ 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',
@@ -28,25 +30,44 @@ 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',
@@ -57,6 +78,7 @@ 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,4 +33,9 @@ TagStory.argTypes = {
control: { type: 'select' },
defaultValue: undefined,
},
isHighlighted: {
options: [true, false],
control: { type: 'select' },
defaultValue: false,
},
};
+1
View File
@@ -76,5 +76,6 @@ export const Tag = styled.span<StyleProps>`
isHighlighted &&
css`
background-color: var(--color-accent);
color: var(--color-text-button);
`}
`;
-1
View File
@@ -10,7 +10,6 @@ export enum DialogTypes {
Help = 'Help',
ExternalNavKeplr = 'ExternalNavKeplr',
MnemonicExport = 'MnemonicExport',
MobileDownload = 'MobileDownload',
MobileSignIn = 'MobileSignIn',
Onboarding = 'Onboarding',
OrderDetails = 'OrderDetails',
+9 -1
View File
@@ -1,4 +1,12 @@
import { AppColorMode } from '@/state/configs';
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',
};
export type Theme = {
[AppColorMode.GreenUp]: ThemeColorBase;
+16
View File
@@ -0,0 +1,16 @@
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,6 +40,7 @@ export enum WalletErrorType {
// General
ChainMismatch,
UserCanceled,
SwitchChainMethodMissing,
// Non-Deterministic
NonDeterministicWallet,
+2
View File
@@ -1,2 +1,4 @@
export { useChartLines } from './useChartLines';
export { useChartMarketAndResolution } from './useChartMarketAndResolution';
export { useTradingView } from './useTradingView';
export { useTradingViewTheme } from './useTradingViewTheme';
+209
View File
@@ -0,0 +1,209 @@
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 };
};
@@ -0,0 +1,65 @@
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 });
};
};
+18 -14
View File
@@ -1,17 +1,19 @@
import { useEffect } from 'react';
import React, { 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 { store } from '@/state/_store';
import { getSelectedNetwork } from '@/state/appSelectors';
import { getAppTheme, getAppColorMode } from '@/state/configsSelectors';
import { getSelectedLocale } from '@/state/localizationSelectors';
@@ -28,8 +30,8 @@ export const useTradingView = ({
displayButtonRef,
setIsChartReady,
}: {
tvWidgetRef: React.MutableRefObject<any>;
displayButtonRef: React.MutableRefObject<any>;
tvWidgetRef: React.MutableRefObject<TvWidget | null>;
displayButtonRef: React.MutableRefObject<HTMLElement | null>;
setIsChartReady: React.Dispatch<React.SetStateAction<boolean>>;
}) => {
const stringGetter = useStringGetter();
@@ -69,15 +71,17 @@ export const useTradingView = ({
tvWidgetRef.current = tvChartWidget;
tvWidgetRef.current.onChartReady(() => {
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?.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?.subscribe('onAutoSaveNeeded', () =>
+55 -63
View File
@@ -1,16 +1,14 @@
import { useEffect } from 'react';
import { useSelector } from 'react-redux';
import type {
IChartingLibraryWidget,
IOrderLineAdapter,
ThemeName,
} from 'public/tradingview/charting_library';
import { THEME_NAMES } from '@/constants/styles/colors';
import type { ChartLine, TvWidget } from '@/constants/tvchart';
import { AppColorMode, AppTheme } from '@/state/configs';
import { getAppTheme, getAppColorMode } from '@/state/configsSelectors';
import { getWidgetOverrides, getOrderLineColors } from '@/lib/tradingView/utils';
import { getWidgetOverrides, getChartLineColors } from '@/lib/tradingView/utils';
/**
* @description Method to define a type guard and check that an element is an IFRAME
@@ -26,12 +24,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 = ({
orderLines,
chartLines,
tvWidget,
isWidgetReady,
}: {
orderLines: Record<string, IOrderLineAdapter>;
tvWidget: (IChartingLibraryWidget & { _id?: string; _ready?: boolean }) | null;
chartLines: Record<string, ChartLine>;
tvWidget: TvWidget | null;
isWidgetReady?: boolean;
}) => {
const appTheme: AppTheme = useSelector(getAppTheme);
@@ -39,70 +37,64 @@ export const useTradingViewTheme = ({
useEffect(() => {
if (tvWidget && isWidgetReady) {
tvWidget
.changeTheme?.(
{
[AppTheme.Classic]: '',
[AppTheme.Dark]: 'dark',
[AppTheme.Light]: 'light',
}[appTheme] as ThemeName
)
.then(() => {
const tvChartId = tvWidget?._id;
tvWidget.changeTheme?.(THEME_NAMES[appTheme]).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'],
});
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);
}
// 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);
});
line
.setQuantityBorderColor(borderColor)
.setBodyBackgroundColor(backgroundColor)
.setBodyBorderColor(borderColor)
.setBodyTextColor(textColor)
.setQuantityTextColor(textButtonColor);
});
});
}
}, [appTheme, appColorMode, isWidgetReady]);
};
+12 -6
View File
@@ -4,8 +4,11 @@ import { useSelector } from 'react-redux';
import { DEFAULT_DOCUMENT_TITLE } from '@/constants/routes';
import { getSelectedLocale } from '@/state/localizationSelectors';
import { getCurrentMarketData, getCurrentMarketId } from '@/state/perpetualsSelectors';
import type { RootState } from '@/state/_store';
import {
getCurrentMarketId,
getCurrentMarketMidMarketPrice,
getCurrentMarketOraclePrice,
} from '@/state/perpetualsSelectors';
import { useBreakpoints } from './useBreakpoints';
@@ -13,11 +16,14 @@ export const usePageTitlePriceUpdates = () => {
const selectedLocale = useSelector(getSelectedLocale);
const { isNotTablet } = useBreakpoints();
const id = useSelector(getCurrentMarketId);
const oraclePrice = useSelector((state: RootState) => getCurrentMarketData(state)?.oraclePrice);
const oraclePrice = useSelector(getCurrentMarketOraclePrice);
const orderbookMidMarketPrice = useSelector(getCurrentMarketMidMarketPrice);
const price = orderbookMidMarketPrice ?? oraclePrice;
useEffect(() => {
if (id && oraclePrice && isNotTablet) {
const priceString = oraclePrice.toLocaleString(selectedLocale);
if (id && price && isNotTablet) {
const priceString = price.toLocaleString(selectedLocale);
document.title = `$${priceString} ${id} · ${DEFAULT_DOCUMENT_TITLE}`;
} else {
document.title = DEFAULT_DOCUMENT_TITLE;
@@ -26,5 +32,5 @@ export const usePageTitlePriceUpdates = () => {
return () => {
document.title = DEFAULT_DOCUMENT_TITLE;
};
}, [oraclePrice]);
}, [price]);
};
+10 -7
View File
@@ -163,17 +163,20 @@ export const useWalletConnection = () => {
}
}
} catch (error) {
throw Object.assign(
new Error([error.message, error.cause?.message].filter(Boolean).join('\n')),
{
walletConnectionType: walletConnection?.type,
}
);
const { isErrorExpected } = parseWalletError({ error, stringGetter });
if (!isErrorExpected) {
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]
-1
View File
@@ -62,7 +62,6 @@ export const DialogManager = () => {
[DialogTypes.ExternalLink]: <ExternalLinkDialog {...modalProps} />,
[DialogTypes.ExternalNavStride]: <ExternalNavStrideDialog {...modalProps} />,
[DialogTypes.MnemonicExport]: <MnemonicExportDialog {...modalProps} />,
[DialogTypes.MobileDownload]: <MobilDownloadDialog {...modalProps} />,
[DialogTypes.MobileSignIn]: <MobileSignInDialog {...modalProps} />,
[DialogTypes.Onboarding]: <OnboardingDialog {...modalProps} />,
[DialogTypes.OrderDetails]: <OrderDetailsDialog {...modalProps} />,
+11 -8
View File
@@ -1,8 +1,10 @@
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 { AppTheme, type AppColorMode } from '@/state/configs';
import { type AppColorMode, AppTheme } from '@/state/configs';
import { Themes } from '@/styles/themes';
@@ -49,23 +51,24 @@ export const getHistorySlice = ({
return bars.filter(({ time }) => time >= fromMs);
};
export const getOrderLineColors = ({
export const getChartLineColors = ({
appTheme,
appColorMode,
side,
chartLineType,
}: {
appTheme: AppTheme;
appColorMode: AppColorMode;
side: OrderSide;
chartLineType: ChartLineType;
}) => {
const theme = Themes[appTheme][appColorMode];
const orderColor = {
const orderColors = {
[OrderSide.BUY]: theme.positive,
[OrderSide.SELL]: theme.negative,
}[side];
['position']: null,
};
return {
orderColor,
maybeQuantityColor: orderColors[chartLineType],
borderColor: theme.borderDefault,
backgroundColor: theme.layer1,
textColor: theme.textTertiary,
@@ -83,7 +86,7 @@ export const getWidgetOverrides = ({
const theme = Themes[appTheme][appColorMode];
return {
theme: appTheme === AppTheme.Dark ? 'dark' : AppTheme.Light ? 'light' : '',
theme: THEME_NAMES[appTheme],
overrides: {
'paneProperties.background': theme.layer2,
'paneProperties.horzGridProperties.color': theme.layer3,
+11 -1
View File
@@ -92,6 +92,10 @@ 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;
@@ -113,13 +117,18 @@ export const parseWalletError = ({
}) => {
const walletErrorType = getWalletErrorType({ error });
let message;
let isErrorExpected;
switch (walletErrorType) {
case WalletErrorType.ChainMismatch:
case WalletErrorType.UserCanceled: {
case WalletErrorType.UserCanceled:
case WalletErrorType.SwitchChainMethodMissing: {
isErrorExpected = true;
message = error.message;
break;
}
default: {
isErrorExpected = false;
message = stringGetter({
key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE,
params: {
@@ -132,5 +141,6 @@ export const parseWalletError = ({
return {
walletErrorType,
message,
isErrorExpected,
};
};
+24 -1
View File
@@ -9,10 +9,13 @@ import {
HistoricalTradingRewardsPeriods,
} from '@/constants/abacus';
import { isMainnet } from '@/constants/networks';
import { STRING_KEYS } from '@/constants/localization';
import breakpoints from '@/styles/breakpoints';
import { layoutMixins } from '@/styles/layoutMixins';
import { Output, OutputType } from '@/components/Output';
import { Panel } from '@/components/Panel';
import { ToggleGroup } from '@/components/ToggleGroup';
import { WithTooltip } from '@/components/WithTooltip';
@@ -20,6 +23,9 @@ import { TradingRewardHistoryTable } from '@/views/tables/TradingRewardHistoryTa
import abacusStateManager from '@/lib/abacus';
// TODO: set in env featureFlag config
const REWARDS_HISTORY_START_DATE_MS = isMainnet ? 1706486400000 : 1704844800000;
export const RewardHistoryPanel = () => {
const stringGetter = useStringGetter();
@@ -47,7 +53,20 @@ export const RewardHistoryPanel = () => {
<WithTooltip tooltip="reward-history">
<h3>{stringGetter({ key: STRING_KEYS.REWARD_HISTORY })}</h3>
</WithTooltip>
<span>{stringGetter({ key: STRING_KEYS.REWARD_HISTORY_DESCRIPTION })}</span>
<span>
{stringGetter({
key: STRING_KEYS.REWARD_HISTORY_DESCRIPTION,
params: {
REWARDS_HISTORY_START_DATE: (
<Styled.Output
type={OutputType.Date}
value={REWARDS_HISTORY_START_DATE_MS}
timeOptions={{ useUTC: true }}
/>
),
},
})}
</span>
</Styled.Title>
<ToggleGroup
items={[
@@ -103,3 +122,7 @@ Styled.Content = styled.div`
${layoutMixins.flexColumn}
gap: 0.75rem;
`;
Styled.Output = styled(Output)`
display: inline;
`;
+6 -3
View File
@@ -1,6 +1,7 @@
import styled, { AnyStyledComponent, css } from 'styled-components';
import { useNavigate } from 'react-router-dom';
import { isMainnet } from '@/constants/networks';
import { STRING_KEYS } from '@/constants/localization';
import { AppRoute } from '@/constants/routes';
@@ -27,6 +28,8 @@ const RewardsPage = () => {
const { isTablet, isNotTablet } = useBreakpoints();
const navigate = useNavigate();
const showTradingRewards = testFlags.showTradingRewards || !isMainnet;
return (
<div>
{isTablet && (
@@ -37,7 +40,7 @@ const RewardsPage = () => {
)}
<DetachedSection>
<Styled.GridLayout
showTradingRewards={testFlags.showTradingRewards}
showTradingRewards={showTradingRewards}
showMigratePanel={import.meta.env.VITE_V3_TOKEN_ADDRESS && isNotTablet}
>
{import.meta.env.VITE_V3_TOKEN_ADDRESS && isNotTablet && <Styled.MigratePanel />}
@@ -51,7 +54,7 @@ const RewardsPage = () => {
</>
)}
{testFlags.showTradingRewards && (
{showTradingRewards && (
<Styled.TradingRewardsColumn>
<TradingRewardsSummaryPanel />
{isTablet && <RewardsHelpPanel />}
@@ -60,7 +63,7 @@ const RewardsPage = () => {
)}
{isNotTablet && (
<Styled.OtherColumn showTradingRewards={testFlags.showTradingRewards}>
<Styled.OtherColumn showTradingRewards={showTradingRewards}>
<RewardsHelpPanel />
</Styled.OtherColumn>
)}
+6
View File
@@ -107,6 +107,12 @@ 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
*/
+19 -184
View File
@@ -1,205 +1,40 @@
import { useEffect, useRef, useState } from 'react';
import { useRef, useState } from 'react';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import styled, { type AnyStyledComponent, css } from 'styled-components';
import type {
IChartingLibraryWidget,
IOrderLineAdapter,
ResolutionString,
} from 'public/tradingview/charting_library';
import type { ResolutionString } from 'public/tradingview/charting_library';
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 type { TvWidget } from '@/constants/tvchart';
import { useStringGetter } from '@/hooks';
import { useTradingView, useTradingViewTheme } from '@/hooks/tradingView';
import {
useChartLines,
useChartMarketAndResolution,
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 chart = isWidgetReady ? tvWidget?.chart() : undefined;
const chartResolution = chart?.resolution?.();
const displayButtonRef = useRef<HTMLElement | null>(null);
const displayButton = displayButtonRef.current;
const { savedResolution } = useTradingView({ tvWidgetRef, displayButtonRef, setIsChartReady });
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 = {};
};
useChartMarketAndResolution({
tvWidget,
isWidgetReady,
savedResolution: savedResolution as ResolutionString | undefined,
});
const { chartLines } = useChartLines({ tvWidget, displayButton, isChartReady });
useTradingViewTheme({ tvWidget, isWidgetReady, chartLines });
return (
<Styled.PriceChart isChartReady={isChartReady}>
@@ -32,7 +32,15 @@ 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 }),
},
}),
},
})
);
-134
View File
@@ -1,134 +0,0 @@
import { useMemo, useState } from 'react';
import styled, { AnyStyledComponent, css } from 'styled-components';
import { AES } from 'crypto-js';
import { AlertType } from '@/constants/alerts';
import { ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { useAccounts, useStringGetter } from '@/hooks';
import { layoutMixins } from '@/styles/layoutMixins';
import { AlertMessage } from '@/components/AlertMessage';
import { Dialog } from '@/components/Dialog';
import { Icon, IconName } from '@/components/Icon';
import { ToggleButton } from '@/components/ToggleButton';
import { QrCode } from '@/components/QrCode';
import { log } from '@/lib/telemetry';
type ElementProps = {
setIsOpen: (open: boolean) => void;
};
const MobileQrCode = ({
}: {
}) => {
const stringGetter = useStringGetter();
const appLink = 'https://apps.apple.com/app/id6475599596';
return (
<Styled.QrCodeContainer isShowing={true}>
<QrCode hasLogo size={432} value={appLink} />
<span>{stringGetter({ key: STRING_KEYS.CLICK_TO_SHOW })}</span>
</Styled.QrCodeContainer>
);
};
export const MobileDownloadDialog = ({ setIsOpen }: ElementProps) => {
const stringGetter = useStringGetter();
const content = (
<>
<p>
{stringGetter({
key: STRING_KEYS.WHILE_ONBOARDING,
params: {
SYNC_WITH_DESKTOP: (
<strong>{stringGetter({ key: STRING_KEYS.SYNC_WITH_DESKTOP })}</strong>
),
},
})}
</p>
<MobileQrCode
/>
</>
);
return (
<Dialog isOpen setIsOpen={setIsOpen} title={'Download mobile app'}>
<Styled.Content>{content}</Styled.Content>
</Dialog>
);
};
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Content = styled.div`
${layoutMixins.column}
gap: 1rem;
strong {
font-weight: 900;
color: var(--color-text-2);
}
footer {
${layoutMixins.row}
justify-content: space-between;
svg {
width: auto;
}
}
`;
Styled.WaitingSpan = styled.span`
strong {
color: var(--color-warning);
}
`;
Styled.QrCodeContainer = styled.figure<{ isShowing: boolean }>`
${layoutMixins.stack}
overflow: hidden;
border-radius: 0.75em;
cursor: pointer;
transition: 0.2s;
&:hover {
filter: brightness(var(--hover-filter-base));
}
> * {
position: relative;
transition: 0.16s;
}
> :first-child {
pointer-events: none;
${({ isShowing }) =>
!isShowing &&
css`
filter: blur(1rem) brightness(1.4);
will-change: filter;
`}
}
> span {
place-self: center;
font-size: 1.4em;
color: var(--color-text-2);
${({ isShowing }) =>
isShowing &&
css`
opacity: 0;
`}
}
`;
@@ -44,7 +44,6 @@ export const GenerateKeys = ({
onKeysDerived = () => {},
}: ElementProps) => {
const stringGetter = useStringGetter();
const { isMobile } = useBreakpoints();
const [shouldRememberMe, setShouldRememberMe] = useState(false);
@@ -66,17 +65,30 @@ export const GenerateKeys = ({
try {
await matchNetwork?.();
return true;
} catch (error) {
const { message, walletErrorType } = parseWalletError({ error, stringGetter });
const { message, walletErrorType, isErrorExpected } = parseWalletError({
error,
stringGetter,
});
if (!isErrorExpected) {
log('GenerateKeys/switchNetwork', error, { walletErrorType });
}
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();
@@ -154,11 +166,16 @@ export const GenerateKeys = ({
setStatus(EvmDerivedAccountStatus.Derived);
} catch (error) {
setStatus(EvmDerivedAccountStatus.NotDerived);
const { message, walletErrorType } = parseWalletError({ error, stringGetter });
const { message, walletErrorType, isErrorExpected } = parseWalletError({
error,
stringGetter,
});
if (message) {
setError(message);
log('GenerateKeys/deriveKeys', error, { walletErrorType });
if (!isErrorExpected) {
log('GenerateKeys/deriveKeys', error, { walletErrorType });
}
}
}
};
@@ -231,7 +248,7 @@ export const GenerateKeys = ({
{!isMatchingNetwork ? (
<Button
action={ButtonAction.Primary}
onClick={() => switchNetwork().then(deriveKeys).then(onKeysDerived)}
onClick={() => switchNetworkAndDeriveKeys().then(onKeysDerived)}
state={{ isLoading: isSwitchingNetwork }}
>
{stringGetter({ key: STRING_KEYS.SWITCH_NETWORK })}
+7 -2
View File
@@ -14,6 +14,7 @@ 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';
@@ -35,7 +36,6 @@ 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,9 +291,14 @@ export const ClosePositionForm = ({
isLoading={isClosingPosition}
hasValidationErrors={hasInputErrors}
actionStringKey={inputAlert?.actionStringKey}
validationErrorString={alertContent}
summary={summary ?? undefined}
currentStep={currentStep}
isClosePosition
confirmButtonConfig={{
stringKey: STRING_KEYS.CLOSE_ORDER,
buttonTextStringKey: STRING_KEYS.CLOSE_POSITION,
buttonAction: ButtonAction.Destroy,
}}
/>
</Styled.Footer>
</Styled.ClosePositionForm>
+41 -5
View File
@@ -1,8 +1,10 @@
import { type FormEvent, useState, Ref, useCallback, useEffect } from 'react';
import { type FormEvent, useState, Ref, useCallback } 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 {
@@ -16,7 +18,12 @@ 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 } from '@/constants/trade';
import {
InputErrorData,
TradeBoxKeys,
MobilePlaceOrderSteps,
ORDER_TYPE_STRINGS,
} from '@/constants/trade';
import { breakpoints } from '@/styles';
import { useStringGetter, useSubaccount } from '@/hooks';
@@ -37,11 +44,16 @@ import { WithTooltip } from '@/components/WithTooltip';
import { Orderbook } from '@/views/tables/Orderbook';
import { setTradeFormInputs } from '@/state/inputs';
import { getCurrentInput, getTradeFormInputs, useTradeFormData } from '@/state/inputsSelectors';
import {
getCurrentInput,
getInputTradeData,
getTradeFormInputs,
useTradeFormData,
} from '@/state/inputsSelectors';
import { getCurrentMarketConfig } from '@/state/perpetualsSelectors';
import abacusStateManager from '@/lib/abacus';
import { getTradeInputAlert } from '@/lib/tradeData';
import { getSelectedOrderSide, getSelectedTradeType, getTradeInputAlert } from '@/lib/tradeData';
import { AdvancedTradeOptions } from './TradeForm/AdvancedTradeOptions';
import { TradeSizeInputs } from './TradeForm/TradeSizeInputs';
@@ -108,8 +120,21 @@ 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[] = [];
@@ -139,6 +164,11 @@ export const TradeForm = ({
alertType = inputAlert?.type;
}
const orderSideAction = {
[OrderSide.BUY]: ButtonAction.Create,
[OrderSide.SELL]: ButtonAction.Destroy,
}[selectedOrderSide];
const onSubmit = async (e: FormEvent) => {
e.preventDefault();
@@ -319,9 +349,15 @@ 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,16 +1,17 @@
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { useDispatch, useSelector } from 'react-redux';
import styled, { type AnyStyledComponent, css } from 'styled-components';
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 { ORDER_TYPE_STRINGS, MobilePlaceOrderSteps } from '@/constants/trade';
import { 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';
@@ -20,30 +21,34 @@ import { OnboardingTriggerButton } from '@/views/dialogs/OnboardingTriggerButton
import { calculateCanAccountTrade } from '@/state/accountCalculators';
import { getSubaccountId } from '@/state/accountSelectors';
import { openDialog } from '@/state/dialogs';
import { getCurrentInput, getInputTradeData } from '@/state/inputsSelectors';
import { getCurrentInput } from '@/state/inputsSelectors';
import { getSelectedOrderSide, getSelectedTradeType } from '@/lib/tradeData';
type ConfirmButtonConfig = {
stringKey: string;
buttonTextStringKey: string;
buttonAction: ButtonAction;
};
type ElementProps = {
isLoading: boolean;
isClosePosition?: boolean;
actionStringKey?: string;
summary?: TradeInputSummary;
hasValidationErrors?: boolean;
validationErrorString?: string;
currentStep?: MobilePlaceOrderSteps;
showDeposit?: boolean;
showConnectWallet?: boolean;
confirmButtonConfig: ConfirmButtonConfig;
};
export const PlaceOrderButtonAndReceipt = ({
isLoading,
isClosePosition,
actionStringKey,
summary,
hasValidationErrors,
validationErrorString,
currentStep,
showDeposit,
showConnectWallet,
confirmButtonConfig,
}: ElementProps) => {
const stringGetter = useStringGetter();
const dispatch = useDispatch();
@@ -52,18 +57,12 @@ 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 = [
@@ -110,11 +109,6 @@ export const PlaceOrderButtonAndReceipt = ({
},
];
const orderSideAction = {
[OrderSide.BUY]: ButtonAction.Create,
[OrderSide.SELL]: ButtonAction.Destroy,
}[selectedOrderSide];
const buttonStatesPerStep = {
[MobilePlaceOrderSteps.EditOrder]: {
buttonTextStringKey: shouldEnableTrade
@@ -128,7 +122,7 @@ export const PlaceOrderButtonAndReceipt = ({
[MobilePlaceOrderSteps.PreviewOrder]: {
buttonTextStringKey: STRING_KEYS.CONFIRM_ORDER,
buttonAction: isClosePosition ? ButtonAction.Destroy : orderSideAction,
buttonAction: confirmButtonConfig.buttonAction,
buttonState: { isLoading },
},
[MobilePlaceOrderSteps.PlacingOrder]: {
@@ -145,15 +139,13 @@ export const PlaceOrderButtonAndReceipt = ({
const buttonAction = currentStep
? buttonStatesPerStep[currentStep].buttonAction
: isClosePosition
? ButtonAction.Destroy
: orderSideAction;
: confirmButtonConfig.buttonAction;
let buttonTextStringKey = STRING_KEYS.UNAVAILABLE;
if (currentStep) {
buttonTextStringKey = buttonStatesPerStep[currentStep].buttonTextStringKey;
} else if (shouldEnableTrade) {
buttonTextStringKey = isClosePosition ? STRING_KEYS.CLOSE_POSITION : STRING_KEYS.PLACE_ORDER;
buttonTextStringKey = confirmButtonConfig.buttonTextStringKey;
} else if (actionStringKey) {
buttonTextStringKey = actionStringKey;
}
@@ -165,31 +157,56 @@ 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 || showConnectWallet ? (
{!canAccountTrade ? (
<OnboardingTriggerButton size={ButtonSize.Base} />
) : showDeposit ? (
<Button
action={ButtonAction.Primary}
onClick={() => dispatch(openDialog({ type: DialogTypes.Deposit }))}
>
{stringGetter({ key: STRING_KEYS.DEPOSIT_FUNDS })}
</Button>
depositButton
) : (
<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>
<WithTooltip tooltipString={hasValidationErrors ? validationErrorString : undefined}>
{submitButton}
</WithTooltip>
)}
</WithDetailsReceipt>
);
};
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Button = styled(Button)`
width: 100%;
`;
Styled.WarningIcon = styled(Icon)`
color: var(--color-warning);
`;
-11
View File
@@ -189,17 +189,6 @@ export const AccountMenu = () => {
label: stringGetter({ key: STRING_KEYS.DISPLAY_SETTINGS }),
onSelect: () => dispatch(openDialog({ type: DialogTypes.DisplaySettings })),
},
{
value: 'MobileDownload',
icon:
theme === AppTheme.Light ? (
<Icon iconName={IconName.Sun} />
) : (
<Icon iconName={IconName.Moon} />
),
label: "Download Mobile App",
onSelect: () => dispatch(openDialog({ type: DialogTypes.MobileDownload })),
},
...(onboardingState === OnboardingState.AccountConnected && hdKey
? [
{