Compare commits

..
Author SHA1 Message Date
jaredvu 8f75d893bb remove isConnected state from dydxClient 2023-10-18 09:52:17 -07:00
jaredvu c54a5d6d49 Temp remove notif onClick 2023-10-17 23:52:15 -07:00
jaredvu 2345ef38a1 Fix tradingView initial load 2023-10-17 23:50:38 -07:00
jaredvu 73cbb4e04e Fix goodTilTime string 2023-10-17 23:50:24 -07:00
jaredvu 52e1dcc4b0 Update to abacus0.7.0 2023-10-17 23:50:11 -07:00
jaredvu cc3f7d1c09 Add Notification icon w/ stroke 2023-10-17 22:00:52 -07:00
50 changed files with 354 additions and 808 deletions
+2 -10
View File
@@ -11,7 +11,6 @@
## Prerequisites
- Node.js version 18 and `pnpm` installed on your system
- Wallet Connect account
For deploying with Vercel, create an account with [Vercel](https://vercel.com/signup) if you don't have one already.
@@ -62,14 +61,7 @@ pnpm ladle
This will automatically open your default browser at `http://localhost:61000`.
## Part 3: Configuring environment
Add or modify the relevant endpoints, links and options in `/public/configs/env.json`.
You'll need to provide a Wallet Connect project id to enable onboarding and wallet connection:
- Create a project on https://cloud.walletconnect.com/app
- Copy over the project ID into this [field](https://github.com/dydxprotocol/v4-web/blob/67ecbd75b43e0c264b7b4d2d9b3d969830b0621c/public/configs/env.json#L822C33-L822C46)
## Part 4: Deploying with Vercel
## Part 3: Deploying with Vercel
### Step 1: Connect your repository to Vercel
@@ -86,7 +78,7 @@ If you wish to incorporate analytics via Amplitude and Bugsnag, you can use our
For more details, check out Vercel's [official documentation](https://vercel.com/docs).
## Part 5: Deploying to IPFS
## Part 4: Deploying to IPFS
### web3.storage: deploy to IPFS via web3.storage using the provided script
+3 -4
View File
@@ -13,7 +13,6 @@
"build:inject-app-deeplinks": "sh scripts/inject-app-deeplinks.sh",
"build:inject-amplitude": "node scripts/inject-amplitude.js",
"build:inject-bugsnag": "node scripts/inject-bugsnag.js",
"build:inject-intercom": "node scripts/inject-intercom.js",
"build:inject-statuspage": "node scripts/inject-statuspage.js",
"deploy:ipfs": "node scripts/upload-ipfs.js --verbose",
"deploy:update-ipns": "node scripts/update-ipns.js",
@@ -39,9 +38,9 @@
"@cosmjs/proto-signing": "^0.31.0",
"@cosmjs/stargate": "^0.31.0",
"@cosmjs/tendermint-rpc": "^0.31.0",
"@dydxprotocol/v4-abacus": "^0.7.6",
"@dydxprotocol/v4-client-js": "^0.40.3",
"@dydxprotocol/v4-localization": "^0.1.32",
"@dydxprotocol/v4-abacus": "^0.7.0",
"@dydxprotocol/v4-client-js": "^0.36.1",
"@dydxprotocol/v4-localization": "^0.1.30",
"@ethersproject/providers": "^5.7.2",
"@js-joda/core": "^5.5.3",
"@radix-ui/react-collapsible": "^1.0.3",
+24 -25
View File
@@ -27,14 +27,14 @@ dependencies:
specifier: ^0.31.0
version: 0.31.0
'@dydxprotocol/v4-abacus':
specifier: ^0.7.6
version: 0.7.6
specifier: ^0.7.0
version: 0.7.0
'@dydxprotocol/v4-client-js':
specifier: ^0.40.3
version: 0.40.3
specifier: ^0.36.1
version: 0.36.1
'@dydxprotocol/v4-localization':
specifier: ^0.1.32
version: 0.1.32
specifier: ^0.1.30
version: 0.1.30
'@ethersproject/providers':
specifier: ^5.7.2
version: 5.7.2
@@ -645,7 +645,7 @@ packages:
/@confio/ics23@0.6.8:
resolution: {integrity: sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w==}
dependencies:
'@noble/hashes': 1.3.2
'@noble/hashes': 1.3.1
protobufjs: 6.11.4
dev: false
@@ -717,7 +717,7 @@ packages:
'@cosmjs/encoding': 0.30.1
'@cosmjs/math': 0.30.1
'@cosmjs/utils': 0.30.1
'@noble/hashes': 1.3.2
'@noble/hashes': 1.3.1
bn.js: 5.2.1
elliptic: 6.5.4
libsodium-wrappers: 0.7.11
@@ -979,12 +979,12 @@ packages:
resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==}
dev: true
/@dydxprotocol/v4-abacus@0.7.6:
resolution: {integrity: sha512-FdDXRaFMfxhyrJJs0dNrb1KG6X95lPHelua1yCn/SVgzwGaAqlJczrzmdh98INntuRAgIxOu69R5mCGyekumyg==}
/@dydxprotocol/v4-abacus@0.7.0:
resolution: {integrity: sha512-XheqfIpOODfQFG860oH0fneVgp9qQKPMDTwrvFXaM/JtKH9ekOUUO6De72EsJ7qOuxWwl1WgoALA8b4tRs0tiw==}
dev: false
/@dydxprotocol/v4-client-js@0.40.3:
resolution: {integrity: sha512-nMQd6tYisKDKIbfx0BGJSYEOSt+I+t5SsaBSmlgTdso+w2rCRO9vyxS+u6MF9h4w5FsVRfhHo3e6uTWX48WpGQ==}
/@dydxprotocol/v4-client-js@0.36.1:
resolution: {integrity: sha512-KJ3MtWI61wmm+xZtXQd7/hYKIoVFG32TRdyyVaXXO8vpW9Oqd0JD8QSM/wuegkciAh5YmP/BrDzYuu77zHO6yg==}
dependencies:
'@cosmjs/amino': 0.30.1
'@cosmjs/encoding': 0.31.1
@@ -993,15 +993,14 @@ packages:
'@cosmjs/stargate': 0.30.1
'@cosmjs/tendermint-rpc': 0.30.1
'@cosmjs/utils': 0.31.1
'@dydxprotocol/v4-proto': 0.4.1
'@dydxprotocol/v4-proto': 0.2.1
'@osmonauts/lcd': 0.6.0
'@scure/bip32': 1.3.2
'@scure/bip32': 1.3.1
'@scure/bip39': 1.2.1
axios: 1.1.3
bech32: 1.1.4
bignumber.js: 9.1.1
ethereum-cryptography: 2.1.2
ethers: 6.6.1
long: 4.0.0
protobufjs: 6.11.4
ws: 8.13.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)
@@ -1011,12 +1010,12 @@ packages:
- utf-8-validate
dev: false
/@dydxprotocol/v4-localization@0.1.32:
resolution: {integrity: sha512-ZtXScxgFLjUt0Ag52RyFnQe9aEl1Aky5fp+vMGOBduVkFiCEMRMkSsSgg6grMdyw/x0AbnxOTRkhYoX2MsK9Ow==}
/@dydxprotocol/v4-localization@0.1.30:
resolution: {integrity: sha512-xTlQuQmPz7wfGkjd07giYMAxRrolDbJE3DryJSIMB6R1MBq1wLzAcwp/iR/nELRwilRX9c44Tap0gGt+oAajiQ==}
dev: false
/@dydxprotocol/v4-proto@0.4.1:
resolution: {integrity: sha512-2pBkokufWcxfIpiXE6QuQOBXUe154OTp6ojCIdOPW9TFLxzchoygxXnb5dL6rAQVNj9Qbg+hhXBllutt4m99dw==}
/@dydxprotocol/v4-proto@0.2.1:
resolution: {integrity: sha512-Q5b/g331DoyMjBpO2/jtrxUX3WF5UaIKZp/unK332xuefHNFnkMYtZ05p0V71iSdO35tBdz8yt0VYP2WtUsp+w==}
dependencies:
protobufjs: 6.11.4
dev: false
@@ -4709,8 +4708,8 @@ packages:
resolution: {integrity: sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==}
dependencies:
'@noble/curves': 1.1.0
'@noble/hashes': 1.3.2
'@scure/base': 1.1.3
'@noble/hashes': 1.3.1
'@scure/base': 1.1.1
dev: false
/@scure/bip32@1.3.2:
@@ -4732,7 +4731,7 @@ packages:
resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==}
dependencies:
'@noble/hashes': 1.3.2
'@scure/base': 1.1.3
'@scure/base': 1.1.1
dev: false
/@solana/buffer-layout@4.0.1:
@@ -4746,8 +4745,8 @@ packages:
resolution: {integrity: sha512-up5VG1dK+GPhykmuMIozJZBbVqpm77vbOG6/r5dS7NBGZonwHfTLdBbsYc3rjmaQ4DpCXUa3tUc4RZHRORvZrw==}
dependencies:
'@babel/runtime': 7.22.10
'@noble/curves': 1.2.0
'@noble/hashes': 1.3.2
'@noble/curves': 1.1.0
'@noble/hashes': 1.3.1
'@solana/buffer-layout': 4.0.1
agentkeepalive: 4.5.0
bigint-buffer: 1.1.5
@@ -7145,7 +7144,7 @@ packages:
/bip39@3.1.0:
resolution: {integrity: sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==}
dependencies:
'@noble/hashes': 1.3.2
'@noble/hashes': 1.3.1
dev: false
/bl@5.1.0:
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+19 -89
View File
@@ -40,7 +40,7 @@
"isMainNet": false,
"tokens": {
"chain": {
"name": "Dv4TNT",
"name": "DYDX",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
@@ -56,15 +56,15 @@
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4dev.dydx.exchange",
"api": "http://indexer.v4dev.dydx.exchange",
"socket": "wss://indexer.v4dev.dydx.exchange"
}
],
"validators": [
"https://validator.v4dev.dydx.exchange"
"http://validator.v4dev.dydx.exchange"
],
"0xsquid": "https://testnet.api.0xsquid.com",
"faucet": "https://faucet.v4dev.dydx.exchange"
"faucet": "http://faucet.v4dev.dydx.exchange"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
@@ -100,7 +100,7 @@
"isMainNet": false,
"tokens": {
"chain": {
"name": "Dv4TNT",
"name": "DYDX",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
@@ -121,7 +121,7 @@
}
],
"validators": [
"http://54.92.118.111"
"http://35.75.227.118"
],
"0xsquid": "https://testnet.api.0xsquid.com"
},
@@ -159,7 +159,7 @@
"isMainNet": false,
"tokens": {
"chain": {
"name": "Dv4TNT",
"name": "DYDX",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
@@ -218,7 +218,7 @@
"isMainNet": false,
"tokens": {
"chain": {
"name": "Dv4TNT",
"name": "DYDX",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
@@ -277,7 +277,7 @@
"isMainNet": false,
"tokens": {
"chain": {
"name": "Dv4TNT",
"name": "DYDX",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
@@ -331,76 +331,6 @@
"signTypedDataDomainName": "dYdX Chain"
}
},
"dydxprotocol-staging-forced-update": {
"name": "v4 Staging Forced Update",
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"isMainNet": false,
"tokens": {
"chain": {
"name": "Dv4TNT",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"decimals": 6,
"image": "/currencies/usdc.png"
}
},
"endpoints": {
"indexers": [
{
"api": "https://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"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"documentation": "https://v4-teacher.vercel.app/",
"community": "https://discord.com/invite/dydx",
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
"blogs": "https://www.dydx.foundation/blog"
},
"wallets": {
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX Chain Onboarding",
"signTypedDataDomainName": "dYdX Chain"
},
"apps": {
"ios": {
"minimalVersion": "1.0",
"build":40000,
"url": "https://apps.apple.com/app/dydx/id1564787350"
}
}
},
"dydxprotocol-staging-west": {
"name": "v4 Staging West",
"ethereumChainId": "5",
@@ -410,7 +340,7 @@
"isMainNet": false,
"tokens": {
"chain": {
"name": "Dv4TNT",
"name": "DYDX",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
@@ -473,7 +403,7 @@
"isMainNet": false,
"tokens": {
"chain": {
"name": "Dv4TNT",
"name": "DYDX",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
@@ -538,7 +468,7 @@
"isMainNet": false,
"tokens": {
"chain": {
"name": "Dv4TNT",
"name": "DYDX",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
@@ -602,7 +532,7 @@
"isMainNet": false,
"tokens": {
"chain": {
"name": "Dv4TNT",
"name": "DYDX",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
@@ -666,7 +596,7 @@
"isMainNet": false,
"tokens": {
"chain": {
"name": "Dv4TNT",
"name": "DYDX",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
@@ -730,7 +660,7 @@
"isMainNet": false,
"tokens": {
"chain": {
"name": "Dv4TNT",
"name": "DYDX",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
@@ -794,7 +724,7 @@
"isMainNet": false,
"tokens": {
"chain": {
"name": "Dv4TNT",
"name": "DYDX",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
@@ -859,8 +789,8 @@
"tokens": {
"comment": "Change according to mainnet release",
"chain": {
"name": "TokenName",
"denom": "tokenDenom",
"name": "DYDX",
"denom": "adv4tnt",
"decimals": 18,
"image": "/currencies/dydx.png"
},
@@ -914,4 +844,4 @@
}
}
}
}
}
-76
View File
@@ -1,76 +0,0 @@
/* eslint-disable no-console */
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
const INTERCOM_APP_ID = process.env.INTERCOM_APP_ID;
const currentPath = fileURLToPath(import.meta.url);
const projectRoot = path.dirname(currentPath);
const htmlFilePath = path.resolve(projectRoot, '../dist/index.html');
if (INTERCOM_APP_ID) {
try {
const html = await fs.readFile(htmlFilePath, 'utf-8');
const intercomScripts = `
<!-- Intercom -->
<script>
window.intercomSettings = {
api_base: 'https://api-iam.intercom.io',
app_id: '${INTERCOM_APP_ID}',
custom_launcher_selector: '.custom_intercom',
hide_default_launcher: true,
};
</script>
<script>
// We pre-filled your app ID in the widget URL: 'https://widget.intercom.io/widget/${INTERCOM_APP_ID}'
(function () {
var w = window;
var ic = w.Intercom;
if (typeof ic === 'function') {
ic('reattach_activator');
ic('update', w.intercomSettings);
} else {
var d = document;
var i = function () {
i.c(arguments);
};
i.q = [];
i.c = function (args) {
i.q.push(args);
};
w.Intercom = i;
var l = function () {
var s = d.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'https://widget.intercom.io/widget/${INTERCOM_APP_ID}';
var x = d.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
};
if (document.readyState === 'complete') {
l();
} else if (w.attachEvent) {
w.attachEvent('onload', l);
} else {
w.addEventListener('load', l, false);
}
}
})();
</script>
`;
const injectedHtml = html.replace(
'<div id="root"></div>',
`<div id="root"></div>\n${intercomScripts}\n`
);
await fs.writeFile(htmlFilePath, injectedHtml, 'utf-8');
console.log('Intercom scripts successfully injected.');
} catch (err) {
console.error('Error injecting Intercom scripts:', err);
}
}
+2 -14
View File
@@ -7,13 +7,7 @@ import { GrazProvider } from 'graz';
import { AppRoute, DEFAULT_TRADE_ROUTE } from '@/constants/routes';
import {
useBreakpoints,
useTokenConfigs,
useInitializePage,
useShouldShowFooter,
useAnalytics,
} from '@/hooks';
import { useBreakpoints, useInitializePage, useShouldShowFooter, useAnalytics } from '@/hooks';
import { DydxProvider } from '@/hooks/useDydxClient';
import { AccountsProvider } from '@/hooks/useAccounts';
import { DialogAreaProvider, useDialogArea } from '@/hooks/useDialogArea';
@@ -33,8 +27,6 @@ import ProfilePage from '@/pages/Profile';
import { SettingsPage } from '@/pages/settings/Settings';
import TradePage from '@/pages/trade/Trade';
import { RewardsPage } from '@/pages/rewards/RewardsPage';
import { TermsOfUsePage } from '@/pages/TermsOfUsePage';
import { PrivacyPolicyPage } from '@/pages/PrivacyPolicyPage';
import { HeaderDesktop } from '@/layout/Header/HeaderDesktop';
import { FooterDesktop } from '@/layout/Footer/FooterDesktop';
@@ -63,7 +55,6 @@ const Content = () => {
const { isTablet, isNotTablet } = useBreakpoints();
const isShowingHeader = isNotTablet;
const isShowingFooter = useShouldShowFooter();
const { chainTokenLabel } = useTokenConfigs();
return (
<Styled.Content isShowingHeader={isShowingHeader} isShowingFooter={isShowingFooter}>
@@ -78,7 +69,7 @@ const Content = () => {
</Route>
<Route path={AppRoute.Markets} element={<MarketsPage />} />
<Route path={`/${chainTokenLabel}`} element={<RewardsPage />} />
<Route path={AppRoute.Rewards} element={<RewardsPage />} />
{isTablet && (
<>
<Route path={AppRoute.Alerts} element={<AlertsPage />} />
@@ -91,9 +82,6 @@ const Content = () => {
<Route path={`${AppRoute.Portfolio}/*`} element={<PortfolioPage />} />
</Route>
<Route path={AppRoute.Terms} element={<TermsOfUsePage />} />
<Route path={AppRoute.Privacy} element={<PrivacyPolicyPage />} />
<Route path="*" element={<Navigate to={DEFAULT_TRADE_ROUTE} replace />} />
</Routes>
</Suspense>
+17 -35
View File
@@ -3,43 +3,31 @@ import styled, { AnyStyledComponent } from 'styled-components';
import { type MenuConfig } from '@/constants/menus';
import { Dialog, DialogPlacement, type DialogProps } from '@/components/Dialog';
import { ComboboxMenu, type ComboboxMenuProps } from '@/components/ComboboxMenu';
import { Dialog, DialogPlacement } from '@/components/Dialog';
import { ComboboxMenu } from '@/components/ComboboxMenu';
type ElementProps<MenuItemValue extends string | number, MenuGroupValue extends string | number> = {
isOpen?: boolean;
setIsOpen?: (open: boolean) => void;
title?: React.ReactNode;
description?: React.ReactNode;
slotTrigger?: React.ReactNode;
slotHeaderInner?: React.ReactNode;
slotFooter?: React.ReactNode;
children?: React.ReactNode;
items: MenuConfig<MenuItemValue, MenuGroupValue>;
onItemSelected?: () => void;
inputPlaceholder?: string;
slotEmpty?: React.ReactNode;
};
type StyleProps = {
placement: DialogPlacement,
className?: string;
};
type PickComboxMenuProps<
MenuItemValue extends string | number,
MenuGroupValue extends string | number
> = Pick<
ComboboxMenuProps<MenuItemValue, MenuGroupValue>,
'inputPlaceholder' | 'onItemSelected' | 'slotEmpty' | 'withSearch' | 'withStickyLayout'
>;
type PickDialogProps = Pick<
DialogProps,
| 'description'
| 'isOpen'
| 'placement'
| 'setIsOpen'
| 'slotHeaderInner'
| 'slotTrigger'
| 'slotFooter'
>;
export const ComboboxDialogMenu = <
MenuItemValue extends string | number,
MenuGroupValue extends string | number
>({
export const ComboboxDialogMenu = <MenuItemValue extends string | number, MenuGroupValue extends string | number>({
isOpen = false,
setIsOpen,
title,
@@ -52,16 +40,11 @@ export const ComboboxDialogMenu = <
onItemSelected,
inputPlaceholder,
slotEmpty,
withSearch,
withStickyLayout = true,
children,
placement = DialogPlacement.Default,
className,
}: ElementProps<MenuItemValue, MenuGroupValue> &
PickComboxMenuProps<MenuItemValue, MenuGroupValue> &
PickDialogProps &
StyleProps) => (
}: ElementProps<MenuItemValue, MenuGroupValue> & StyleProps) => (
// TODO: sub-menu state management
<Styled.Dialog
isOpen={isOpen}
@@ -71,6 +54,7 @@ export const ComboboxDialogMenu = <
slotHeaderInner={slotHeaderInner}
slotTrigger={slotTrigger}
slotFooter={slotFooter}
placement={placement}
className={className}
>
@@ -80,8 +64,7 @@ export const ComboboxDialogMenu = <
title={title}
inputPlaceholder={inputPlaceholder}
slotEmpty={slotEmpty}
withSearch={withSearch}
withStickyLayout={withStickyLayout}
withStickyLayout
/>
{children}
</Styled.Dialog>
@@ -115,5 +98,4 @@ Styled.Dialog = styled(Dialog)`
Styled.ComboboxMenu = styled(ComboboxMenu)`
--comboboxMenu-backgroundColor: var(--comboboxDialogMenu-backgroundColor);
--comboboxMenu-item-gap: var(--comboxDialogMenu-item-gap, 0.5rem);
`;
+5 -10
View File
@@ -8,7 +8,7 @@ import { layoutMixins } from '@/styles/layoutMixins';
import { Tag } from '@/components/Tag';
type ElementProps<MenuItemValue extends string | number, MenuGroupValue extends string | number> = {
type ElementProps<MenuItemValue extends string, MenuGroupValue extends string> = {
items: MenuConfig<MenuItemValue, MenuGroupValue>;
onItemSelected?: () => void;
@@ -23,11 +23,6 @@ type StyleProps = {
withStickyLayout?: boolean;
};
export type ComboboxMenuProps<
MenuItemValue extends string | number,
MenuGroupValue extends string | number
> = ElementProps<MenuItemValue, MenuGroupValue> & StyleProps;
export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extends string>({
items,
onItemSelected,
@@ -39,7 +34,7 @@ export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extend
className,
withStickyLayout,
}: ComboboxMenuProps<MenuItemValue, MenuGroupValue>) => {
}: ElementProps<MenuItemValue, MenuGroupValue> & StyleProps) => {
const [highlightedCommand, setHighlightedCommand] = useState<MenuItemValue>();
const [searchValue, setSearchValue] = useState('');
// const inputRef = useRef<HTMLInputElement | null>(null);
@@ -176,7 +171,6 @@ Styled.Command = styled(Command)<{ $withStickyLayout?: boolean }>`
--comboboxMenu-item-highlighted-backgroundColor: var(--color-layer-3);
--comboboxMenu-item-highlighted-textColor: var(--color-text-1);
--comboboxMenu-item-backgroundColor: ;
--comboboxMenu-item-gap: 0.5rem;
display: grid;
align-content: start;
@@ -275,17 +269,18 @@ Styled.List = styled(Command.List)<{ $withStickyLayout?: boolean }>`
`;
Styled.Item = styled(Command.Item)`
${layoutMixins.scrollSnapItem}
${popoverMixins.item}
--item-checked-backgroundColor: var(--comboboxMenu-item-checked-backgroundColor);
--item-checked-textColor: var(--comboboxMenu-item-checked-textColor);
--item-highlighted-textColor: var(--comboboxMenu-item-highlighted-textColor);
--item-gap: var(--comboboxMenu-item-gap);
${layoutMixins.scrollSnapItem}
background-color: var(--comboboxMenu-backgroundColor, inherit);
display: flex;
align-items: center;
gap: 0.5rem;
&[aria-disabled='true'] {
opacity: 0.75;
+1 -3
View File
@@ -48,8 +48,6 @@ type StyleProps = {
className?: string;
};
export type DialogProps = ElementProps & StyleProps;
const DialogPortal = ({
withPortal,
container,
@@ -84,7 +82,7 @@ export const Dialog = ({
hasHeaderBorder = false,
children,
className,
}: DialogProps) => {
}: ElementProps & StyleProps) => {
const closeButtonRef = useRef<HTMLButtonElement>();
const showOverlay = ![DialogPlacement.Inline, DialogPlacement.FullScreen].includes(placement);
-3
View File
@@ -13,7 +13,6 @@ import {
CaretIcon,
CautionCircleStrokeIcon,
CautionCircleIcon,
ChatIcon,
CheckIcon,
ChevronLeftIcon,
ChevronRightIcon,
@@ -81,7 +80,6 @@ export enum IconName {
Caret = 'Caret',
CautionCircle = 'CautionCircle',
CautionCircleStroked = 'CautionCircleStroked',
Chat = 'Chat',
Check = 'Check',
ChevronLeft = 'ChevronLeft',
ChevronRight = 'ChevronRight',
@@ -150,7 +148,6 @@ const icons = {
[IconName.Caret]: CaretIcon,
[IconName.CautionCircle]: CautionCircleIcon,
[IconName.CautionCircleStroked]: CautionCircleStrokeIcon,
[IconName.Chat]: ChatIcon,
[IconName.Check]: CheckIcon,
[IconName.ChevronLeft]: ChevronLeftIcon,
[IconName.ChevronRight]: ChevronRightIcon,
+1 -1
View File
@@ -44,4 +44,4 @@ export type EvmDerivedAddresses = {
};
};
export const AMOUNT_RESERVED_FOR_GAS_USDC = 0.1;
export const AMOUNT_RESERVED_FOR_GAS_USDC = 100_000;
+1 -1
View File
@@ -21,7 +21,7 @@ export type MenuItem<MenuItemValue, MenuItemTypes = string> = {
value: MenuItemValue;
slotBefore?: React.ReactNode;
label: React.ReactNode;
label: string;
labelRight?: React.ReactNode;
tag?: React.ReactNode;
slotAfter?: React.ReactNode;
+1 -2
View File
@@ -7,8 +7,7 @@ export enum AppRoute {
Profile = '/profile',
Alerts = '/alerts',
Settings = '/settings',
Terms = '/terms',
Privacy = '/privacy',
Rewards = '/DV4TNT',
}
export enum PortfolioRoute {
+12 -2
View File
@@ -1,5 +1,5 @@
import type { ExternalProvider } from '@ethersproject/providers';
import { type onboarding } from '@dydxprotocol/v4-client-js';
import { USDC_DENOM, type onboarding, DYDX_DENOM } from '@dydxprotocol/v4-client-js';
import type { suggestChain } from 'graz';
import { STRING_KEYS } from '@/constants/localization';
@@ -352,5 +352,15 @@ export const DYDX_CHAIN_INFO: Parameters<typeof suggestChain>[0] = {
// TODO: export this type from abacus instead
export enum DydxChainAsset {
USDC = 'usdc',
CHAINTOKEN = 'chain',
DYDX = 'dydx',
}
export const DYDX_CHAIN_ASSET_COIN_DENOM: Record<DydxChainAsset, string> = {
[DydxChainAsset.USDC]: USDC_DENOM,
[DydxChainAsset.DYDX]: DYDX_DENOM,
};
export const DYDX_CHAIN_ASSET_TAGS: Record<DydxChainAsset, string> = {
[DydxChainAsset.USDC]: 'USDC',
[DydxChainAsset.DYDX]: 'Dv4TNT',
};
-2
View File
@@ -1,6 +1,5 @@
import { useApiState } from './useApiState';
import { useBreakpoints } from './useBreakpoints';
import { useTokenConfigs } from './useTokenConfigs';
import { useCommandMenu } from './useCommandMenu';
import { useCurrentMarketId } from './useCurrentMarketId';
import { useDebounce } from './useDebounce';
@@ -26,7 +25,6 @@ import { useSubaccount } from './useSubaccount';
export {
useApiState,
useBreakpoints,
useTokenConfigs,
useCommandMenu,
useCurrentMarketId,
useDebounce,
+16 -9
View File
@@ -5,7 +5,10 @@ import { StargateClient } from '@cosmjs/stargate';
import { useQuery } from 'react-query';
import { formatUnits } from 'viem';
import { USDC_DENOM, DYDX_DENOM } from '@dydxprotocol/v4-client-js';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { QUANTUM_MULTIPLIER } from '@/constants/numbers';
import { EvmAddress } from '@/constants/wallets';
import { convertBech32Address } from '@/lib/addressUtils';
@@ -15,11 +18,11 @@ import { getBalances, getStakingBalances } from '@/state/accountSelectors';
import { getSelectedNetwork } from '@/state/appSelectors';
import { useAccounts } from './useAccounts';
import { useTokenConfigs } from './useTokenConfigs';
type UseAccountBalanceProps = {
// Token Items
addressOrDenom?: string;
assetSymbol?: string;
decimals?: number;
// Chain Items
@@ -38,6 +41,7 @@ export const CHAIN_DEFAULT_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeee
export const useAccountBalance = ({
addressOrDenom,
assetSymbol,
bech32AddrPrefix,
chainId,
decimals = 0,
@@ -48,7 +52,6 @@ export const useAccountBalance = ({
const selectedNetwork = useSelector(getSelectedNetwork);
const balances = useSelector(getBalances, shallowEqual);
const { chainTokenDenom, usdcDenom } = useTokenConfigs();
const evmChainId = Number(ENVIRONMENT_CONFIG_MAP[selectedNetwork].ethereumChainId);
const stakingBalances = useSelector(getStakingBalances, shallowEqual);
@@ -88,16 +91,20 @@ export const useAccountBalance = ({
});
const { formatted: evmBalance } = evmQuery.data || {};
const balance = isCosmosChain ? cosmosQuery.data : evmBalance;
const balance = !assetSymbol ? '0' : isCosmosChain ? cosmosQuery.data : evmBalance;
const nativeTokenCoinBalance = balances?.[chainTokenDenom];
const nativeTokenBalance = MustBigNumber(nativeTokenCoinBalance?.amount);
const nativeTokenCoinBalance = balances?.[DYDX_DENOM];
const nativeTokenBalance = MustBigNumber(nativeTokenCoinBalance?.amount)
.div(QUANTUM_MULTIPLIER)
.toNumber();
const usdcCoinBalance = balances?.[usdcDenom];
const usdcBalance = MustBigNumber(usdcCoinBalance?.amount).toNumber();
const usdcCoinBalance = balances?.[USDC_DENOM];
const usdcBalance = MustBigNumber(usdcCoinBalance?.amount).div(QUANTUM_MULTIPLIER).toNumber();
const nativeStakingCoinBalanace = stakingBalances?.[chainTokenDenom];
const nativeStakingBalance = MustBigNumber(nativeStakingCoinBalanace?.amount).toNumber();
const nativeStakingCoinBalanace = stakingBalances?.[DYDX_DENOM];
const nativeStakingBalance = MustBigNumber(nativeStakingCoinBalanace?.amount)
.div(QUANTUM_MULTIPLIER)
.toNumber();
return {
balance,
+11 -2
View File
@@ -2,7 +2,7 @@ import { useCallback, useContext, createContext, useEffect, useState, useMemo }
import { useDispatch } from 'react-redux';
import { AES, enc } from 'crypto-js';
import { LocalWallet, type Subaccount } from '@dydxprotocol/v4-client-js';
import { LocalWallet, USDC_DENOM, type Subaccount } from '@dydxprotocol/v4-client-js';
import { OnboardingGuard, OnboardingState, type EvmDerivedAddresses } from '@/constants/account';
import { DialogTypes } from '@/constants/dialogs';
@@ -130,8 +130,16 @@ const useAccountsContext = () => {
// dYdX subaccounts
const [dydxSubaccounts, setDydxSubaccounts] = useState<Subaccount[] | undefined>();
const { getSubaccounts } = useMemo(
const { getAccountBalance, getSubaccounts } = useMemo(
() => ({
getAccountBalance: async ({
dydxAddress,
denom = USDC_DENOM,
}: {
dydxAddress: DydxAddress;
denom?: string;
}) => await compositeClient?.validatorClient.get.getAccountBalance(dydxAddress, denom),
getSubaccounts: async ({ dydxAddress }: { dydxAddress: DydxAddress }) => {
try {
const response = await compositeClient?.indexerClient.account.getSubaccounts(dydxAddress);
@@ -318,6 +326,7 @@ const useAccountsContext = () => {
disconnect,
// dydxClient Account methods
getAccountBalance,
getSubaccounts,
};
};
+1 -11
View File
@@ -15,8 +15,6 @@ import type { ResolutionString } from 'public/tradingview/charting_library';
import type { ConnectNetworkEvent, NetworkConfig } from '@/constants/abacus';
import { type Candle, RESOLUTION_MAP } from '@/constants/candles';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { DydxChainAsset } from '@/constants/wallets';
import { getSelectedNetwork } from '@/state/appSelectors';
@@ -38,7 +36,6 @@ const useDydxClientContext = () => {
// ------ Network ------ //
const selectedNetwork = useSelector(getSelectedNetwork);
const tokensConfigs = ENVIRONMENT_CONFIG_MAP[selectedNetwork].tokens;
const [networkConfig, setNetworkConfig] = useState<NetworkConfig>();
@@ -68,14 +65,7 @@ const useDydxClientContext = () => {
new Network(
selectedNetwork,
new IndexerConfig(networkConfig.indexerUrl, networkConfig.websocketUrl),
new ValidatorConfig(networkConfig.validatorUrl, networkConfig.chainId,
{
USDC_DENOM: tokensConfigs[DydxChainAsset.USDC].denom,
USDC_DECIMALS: tokensConfigs[DydxChainAsset.USDC].decimals,
USDC_GAS_DENOM: tokensConfigs[DydxChainAsset.USDC].gasDenom,
CHAINTOKEN_DENOM: tokensConfigs[DydxChainAsset.CHAINTOKEN].denom,
CHAINTOKEN_DECIMALS: tokensConfigs[DydxChainAsset.CHAINTOKEN].decimals,
}, {
new ValidatorConfig(networkConfig.validatorUrl, networkConfig.chainId, {
broadcastPollIntervalMs: 3_000,
broadcastTimeoutMs: 60_000,
})
+14 -1
View File
@@ -1,6 +1,7 @@
import { useCallback } from 'react';
import { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { DialogTypes } from '@/constants/dialogs';
import { LocalStorageKey } from '@/constants/localStorage';
import { DEFAULT_APP_ENVIRONMENT, DydxNetwork, ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
@@ -8,6 +9,7 @@ import { useAccounts, useLocalStorage } from '@/hooks';
import { setSelectedNetwork } from '@/state/app';
import { getSelectedNetwork } from '@/state/appSelectors';
import { openDialog } from '@/state/dialogs';
import { validateAgainstAvailableEnvironments } from '@/lib/network';
@@ -25,6 +27,17 @@ export const useSelectedNetwork = (): {
validateFn: validateAgainstAvailableEnvironments,
});
useEffect(() => {
if (
import.meta.env.MODE === 'production' &&
ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId === 'dydx-testnet-4'
) {
dispatch(
openDialog({ type: DialogTypes.ExchangeOffline, dialogProps: { preventClose: true } })
);
}
}, [selectedNetwork]);
const switchNetwork = useCallback(
(network: DydxNetwork) => {
disconnect();
+27 -27
View File
@@ -3,10 +3,15 @@ import { shallowEqual, useSelector, useDispatch } from 'react-redux';
import type { Nullable } from '@dydxprotocol/v4-abacus';
import Long from 'long';
import type { IndexedTx } from '@cosmjs/stargate';
import type { EncodeObject } from '@cosmjs/proto-signing';
import type { EncodeObject, Coin } from '@cosmjs/proto-signing';
import { Method } from '@cosmjs/tendermint-rpc';
import { type LocalWallet, SubaccountClient } from '@dydxprotocol/v4-client-js';
import {
type LocalWallet,
SubaccountClient,
DYDX_DENOM,
USDC_DENOM,
} from '@dydxprotocol/v4-client-js';
import type {
AccountBalance,
@@ -29,7 +34,6 @@ import { MustBigNumber } from '@/lib/numbers';
import { log } from '@/lib/telemetry';
import { useAccounts } from './useAccounts';
import { useTokenConfigs } from './useTokenConfigs';
import { useDydxClient } from './useDydxClient';
type SubaccountContextType = ReturnType<typeof useSubaccountContext>;
@@ -48,7 +52,6 @@ export const useSubaccount = () => useContext(SubaccountContext);
export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: LocalWallet }) => {
const dispatch = useDispatch();
const { usdcDenom } = useTokenConfigs();
const { compositeClient, faucetClient } = useDydxClient();
const { getFaucetFunds } = useMemo(
@@ -74,12 +77,13 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
() => ({
depositToSubaccount: async ({
subaccountClient,
assetId = 0,
amount,
}: {
subaccountClient: SubaccountClient;
assetId?: number;
amount: number;
}) => await compositeClient?.depositToSubaccount(subaccountClient, amount.toString()),
amount: Long;
}) => await compositeClient?.validatorClient.post.deposit(subaccountClient, assetId, amount),
withdrawFromSubaccount: async ({
subaccountClient,
@@ -87,7 +91,7 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
}: {
subaccountClient: SubaccountClient;
amount: number;
}) => await compositeClient?.withdrawFromSubaccount(subaccountClient, amount.toString()),
}) => await compositeClient?.withdrawFromSubaccount(subaccountClient, amount),
transferFromSubaccountToAddress: async ({
subaccountClient,
@@ -130,21 +134,12 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
amount: number;
recipient: string;
}) =>
await compositeClient?.validatorClient.post.send(
subaccountClient.wallet,
() =>
new Promise((resolve) => {
const msg = compositeClient?.sendTokenMessage(
subaccountClient.wallet,
amount.toString(),
recipient,
);
resolve([msg]);
}),
await compositeClient?.validatorClient.post.sendToken(
subaccountClient,
recipient,
DYDX_DENOM,
Long.fromNumber(amount * QUANTUM_MULTIPLIER),
false,
compositeClient?.validatorClient?.post.defaultDydxGasPrice,
undefined,
Method.BroadcastTxCommit
),
@@ -161,7 +156,7 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
const transaction = JSON.parse(payload);
const msg = compositeClient.withdrawFromSubaccountMessage(subaccountClient, amount.toString());
const msg = compositeClient.withdrawFromSubaccountMessage(subaccountClient, amount);
const ibcMsg: EncodeObject = {
typeUrl: transaction.msgTypeUrl,
value: transaction.msg,
@@ -200,9 +195,13 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
async (balance: AccountBalance) => {
if (!localDydxWallet) return;
const amount = parseFloat(balance.amount) - AMOUNT_RESERVED_FOR_GAS_USDC;
const amountAfterDust = MustBigNumber(balance.amount)
.minus(AMOUNT_RESERVED_FOR_GAS_USDC) // keep 0.1 USDC in user's wallet for gas
.toString();
if (amount > 0) {
const amount = Long.fromString(amountAfterDust || '0');
if (amount.greaterThan(Long.ZERO)) {
const subaccountClient = new SubaccountClient(localDydxWallet, 0);
await depositToSubaccount({ amount, subaccountClient });
}
@@ -211,7 +210,7 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
);
const balances = useSelector(getBalances, shallowEqual);
const usdcCoinBalance = balances?.[usdcDenom];
const usdcCoinBalance = balances?.[USDC_DENOM];
useEffect(() => {
if (usdcCoinBalance) {
@@ -220,7 +219,7 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
}, [usdcCoinBalance]);
const deposit = useCallback(
async (amount: number) => {
async (amount: Long) => {
if (!subaccountClient) {
return;
}
@@ -248,7 +247,8 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
if (!subaccountClient) {
return;
}
return (await (coinDenom === usdcDenom
return (await (coinDenom === USDC_DENOM
? transferFromSubaccountToAddress
: transferNativeToken)({ subaccountClient, amount, recipient })) as IndexedTx;
},
-36
View File
@@ -1,36 +0,0 @@
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { DydxChainAsset } from '@/constants/wallets';
import { useSelectedNetwork } from '@/hooks';
export const useTokenConfigs = (): {
tokensConfigs: {
[DydxChainAsset.USDC]: {
denom: string;
name: string;
decimals: number;
gasDenom?: string;
},
[DydxChainAsset.CHAINTOKEN]: {
denom: string;
name: string;
decimals: number;
gasDenom?: string;
},
};
usdcDenom: string;
usdcLabel: string;
chainTokenDenom: string;
chainTokenLabel: string;
} => {
const { selectedNetwork } = useSelectedNetwork();
const tokensConfigs = ENVIRONMENT_CONFIG_MAP[selectedNetwork].tokens;
return {
tokensConfigs,
usdcDenom: tokensConfigs[DydxChainAsset.USDC].denom,
usdcLabel: tokensConfigs[DydxChainAsset.USDC].name,
chainTokenDenom: tokensConfigs[DydxChainAsset.CHAINTOKEN].denom,
chainTokenLabel: tokensConfigs[DydxChainAsset.CHAINTOKEN].name,
};
};
+1 -10
View File
@@ -1,8 +1,6 @@
import { useCallback, useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { LocalStorageKey } from '@/constants/localStorage';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import {
type DydxAddress,
@@ -30,8 +28,6 @@ import {
WalletType as CosmosWalletType,
} from 'graz';
import { getSelectedNetwork } from '@/state/appSelectors';
import { resolveWagmiConnector } from '@/lib/wagmi';
import { getWalletConnection, parseWalletError } from '@/lib/wallet';
import { log } from '@/lib/telemetry';
@@ -88,9 +84,6 @@ export const useWalletConnection = () => {
// Wallet connection
const selectedNetwork = useSelector(getSelectedNetwork);
const walletConnectConfig = ENVIRONMENT_CONFIG_MAP[selectedNetwork].wallets.walletconnect;
const { connectAsync: connectWagmi } =
walletType && walletConnectionType
? useConnectWagmi({
@@ -99,7 +92,6 @@ export const useWalletConnection = () => {
walletConnection: {
type: walletConnectionType,
},
walletConnectConfig,
}),
})
: useConnectWagmi();
@@ -135,7 +127,6 @@ export const useWalletConnection = () => {
connector: resolveWagmiConnector({
walletType,
walletConnection,
walletConnectConfig,
}),
});
}
@@ -226,7 +217,7 @@ export const useWalletConnection = () => {
evmAddressWagmi,
signerWagmi,
publicClientWagmi,
// Wallet connection (Cosmos)
dydxAddress,
dydxAddressGraz,
-1
View File
@@ -1 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5.625 6.1875h6.75m-6.75 2.25H9M1.6875 9.57c0 1.2.84225 2.2455 2.03025 2.4203.84675.1245 1.7025.2197 2.56725.2842.2625.0195.5025.1575.64875.3758L9 15.75l2.0663-3.0997c.0724-.1078.1685-.1977.2809-.2628.1125-.0651.2382-.1038.3678-.113.8589-.0641 1.7152-.1589 2.5672-.2842 1.188-.1748 2.0303-1.2195 2.0303-2.42105v-4.5135c0-1.2015-.8423-2.24625-2.0303-2.421A36.294434 36.294434 0 0 0 9 2.25c-1.794 0-3.558.13125-5.28225.38475-1.188.17475-2.03025 1.22025-2.03025 2.421V9.57Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

Before

Width:  |  Height:  |  Size: 672 B

+1 -1
View File
@@ -1 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M15 8C15 9.53527 14.6867 10.8758 14.2269 11.7954C13.77 12.7092 13.3116 12.9664 13.0527 12.9968L12.6636 12.8953C12.4116 12.7511 12.092 12.4331 11.7731 11.7954C11.3133 10.8758 11 9.53527 11 8C11 6.46473 11.3133 5.12424 11.7731 4.20457C12.0924 3.56607 12.4124 3.24815 12.6645 3.10417L13.0519 3.00311C13.3107 3.0331 13.7696 3.28991 14.2269 4.20457C14.6867 5.12424 15 6.46473 15 8ZM12.2928 2.16768L1.41963 5.00415C0.628571 5.08768 0 6.39699 0 7.99992C0 9.60346 0.629045 10.9131 1.42053 10.9958L2.50023 11.2774V12.9998C2.50023 13.8283 3.17181 14.4998 4.00023 14.4998C4.82866 14.4998 5.50023 13.8283 5.50023 12.9998V12.0601L12.2914 13.8317C12.5186 13.9417 12.7559 14 13 14C14.6569 14 16 11.3137 16 8C16 4.68629 14.6569 2 13 2C12.7564 2 12.5196 2.05808 12.2928 2.16768ZM10.9931 3.5402L2.49607 5.75681C2.80523 6.30634 3 7.10764 3 7.99992C3 8.89214 2.80525 9.69339 2.49614 10.2429L10.9929 12.4595C10.3833 11.3612 10 9.77013 10 8C10 6.22968 10.3834 4.63843 10.9931 3.5402ZM2 7.99992C2 8.70677 1.85462 9.29725 1.66623 9.67403C1.59961 9.80728 1.54246 9.88955 1.5 9.93955C1.45754 9.88955 1.40039 9.80728 1.33377 9.67403C1.14538 9.29725 1 8.70677 1 7.99992C1 7.29308 1.14538 6.70259 1.33377 6.32582C1.40039 6.19256 1.45754 6.11029 1.5 6.06029C1.54246 6.11029 1.59961 6.19256 1.66623 6.32582C1.85462 6.70259 2 7.29308 2 7.99992ZM1.41964 10.0124C1.41962 10.0124 1.42071 10.0118 1.42296 10.0109C1.42079 10.0121 1.41966 10.0125 1.41964 10.0124ZM1.57704 10.0109C1.57929 10.0118 1.58038 10.0124 1.58036 10.0124C1.58033 10.0125 1.57921 10.0121 1.57704 10.0109ZM1.58036 5.9874C1.58038 5.98747 1.57929 5.98808 1.57704 5.98899C1.57921 5.98778 1.58034 5.98732 1.58036 5.9874ZM1.42296 5.98899C1.42071 5.98808 1.41962 5.98747 1.41964 5.9874C1.41966 5.98732 1.42079 5.98778 1.42296 5.98899ZM3.50023 12.9998V11.5383L4.50023 11.7992V12.9998C4.50023 13.276 4.27638 13.4998 4.00023 13.4998C3.72409 13.4998 3.50023 13.276 3.50023 12.9998Z" fill="currentColor"/></svg>
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M15 8C15 9.53527 14.6867 10.8758 14.2269 11.7954C13.77 12.7092 13.3116 12.9664 13.0527 12.9968L12.6636 12.8953C12.4116 12.7511 12.092 12.4331 11.7731 11.7954C11.3133 10.8758 11 9.53527 11 8C11 6.46473 11.3133 5.12424 11.7731 4.20457C12.0924 3.56607 12.4124 3.24815 12.6645 3.10417L13.0519 3.00311C13.3107 3.0331 13.7696 3.28991 14.2269 4.20457C14.6867 5.12424 15 6.46473 15 8ZM12.2928 2.16768L1.41963 5.00415C0.628571 5.08768 0 6.39699 0 7.99992C0 9.60346 0.629045 10.9131 1.42053 10.9958L2.50023 11.2774V12.9998C2.50023 13.8283 3.17181 14.4998 4.00023 14.4998C4.82866 14.4998 5.50023 13.8283 5.50023 12.9998V12.0601L12.2914 13.8317C12.5186 13.9417 12.7559 14 13 14C14.6569 14 16 11.3137 16 8C16 4.68629 14.6569 2 13 2C12.7564 2 12.5196 2.05808 12.2928 2.16768ZM10.9931 3.5402L2.49607 5.75681C2.80523 6.30634 3 7.10764 3 7.99992C3 8.89214 2.80525 9.69339 2.49614 10.2429L10.9929 12.4595C10.3833 11.3612 10 9.77013 10 8C10 6.22968 10.3834 4.63843 10.9931 3.5402ZM2 7.99992C2 8.70677 1.85462 9.29725 1.66623 9.67403C1.59961 9.80728 1.54246 9.88955 1.5 9.93955C1.45754 9.88955 1.40039 9.80728 1.33377 9.67403C1.14538 9.29725 1 8.70677 1 7.99992C1 7.29308 1.14538 6.70259 1.33377 6.32582C1.40039 6.19256 1.45754 6.11029 1.5 6.06029C1.54246 6.11029 1.59961 6.19256 1.66623 6.32582C1.85462 6.70259 2 7.29308 2 7.99992ZM1.41964 10.0124C1.41962 10.0124 1.42071 10.0118 1.42296 10.0109C1.42079 10.0121 1.41966 10.0125 1.41964 10.0124ZM1.57704 10.0109C1.57929 10.0118 1.58038 10.0124 1.58036 10.0124C1.58033 10.0125 1.57921 10.0121 1.57704 10.0109ZM1.58036 5.9874C1.58038 5.98747 1.57929 5.98808 1.57704 5.98899C1.57921 5.98778 1.58034 5.98732 1.58036 5.9874ZM1.42296 5.98899C1.42071 5.98808 1.41962 5.98747 1.41964 5.9874C1.41966 5.98732 1.42079 5.98778 1.42296 5.98899ZM3.50023 12.9998V11.5383L4.50023 11.7992V12.9998C4.50023 13.276 4.27638 13.4998 4.00023 13.4998C3.72409 13.4998 3.50023 13.276 3.50023 12.9998Z" fill="currentColor"/></svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

-1
View File
@@ -8,7 +8,6 @@ export { default as CalculatorIcon } from './calculator.svg';
export { default as CaretIcon } from './caret-down.svg';
export { default as CautionCircleIcon } from './caution-circle.svg';
export { default as CautionCircleStrokeIcon } from './caution-circle-stroke.svg';
export { default as ChatIcon } from './chat-bubble.svg';
export { default as CheckIcon } from './check.svg';
export { default as ChevronLeftIcon } from './chevron-left.svg';
export { default as ChevronRightIcon } from './chevron-right.svg';
+23 -64
View File
@@ -1,16 +1,12 @@
import styled, { type AnyStyledComponent, css } from 'styled-components';
import { AbacusApiStatus } from '@/constants/abacus';
import { ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { useApiState, useSelectedNetwork, useStringGetter } from '@/hooks';
import { ChatIcon, LinkOutIcon } from '@/icons';
import { useApiState, useStringGetter } from '@/hooks';
import { layoutMixins } from '@/styles/layoutMixins';
import { Button } from '@/components/Button';
import { Details } from '@/components/Details';
import { Output, OutputType } from '@/components/Output';
import { WithTooltip } from '@/components/WithTooltip';
@@ -28,8 +24,6 @@ enum ExchangeStatus {
export const FooterDesktop = () => {
const stringGetter = useStringGetter();
const { height, indexerHeight, status, statusErrorMessage } = useApiState();
const { selectedNetwork } = useSelectedNetwork();
const { statusPage } = ENVIRONMENT_CONFIG_MAP[selectedNetwork].links;
const { exchangeStatus, label } =
!status || status === AbacusApiStatus.NORMAL
@@ -44,37 +38,20 @@ export const FooterDesktop = () => {
return (
<Styled.Footer>
<Styled.Row>
<WithTooltip
slotTooltip={
statusErrorMessage && (
<dl>
<dd>{statusErrorMessage}</dd>
</dl>
)
}
>
<Styled.FooterButton
slotLeft={<Styled.StatusDot exchangeStatus={exchangeStatus} />}
slotRight={statusPage && <LinkOutIcon />}
size={ButtonSize.XSmall}
state={{ isDisabled: !statusPage }}
>
{label}
</Styled.FooterButton>
</WithTooltip>
{globalThis?.Intercom && (
<Styled.FooterButton
slotLeft={<ChatIcon />}
size={ButtonSize.XSmall}
onClick={() => globalThis.Intercom('show')}
>
{stringGetter({ key: STRING_KEYS.HELP_AND_SUPPORT })}
</Styled.FooterButton>
)}
</Styled.Row>
<WithTooltip
slotTooltip={
statusErrorMessage && (
<dl>
<dd>{statusErrorMessage}</dd>
</dl>
)
}
>
<Styled.Row>
<Styled.StatusDot exchangeStatus={exchangeStatus} />
<span>{label}</span>
</Styled.Row>
</WithTooltip>
{import.meta.env.MODE !== 'production' && (
<Styled.Details
withSeparators
@@ -105,22 +82,20 @@ Styled.Footer = styled.footer`
${layoutMixins.stickyFooter}
${layoutMixins.spacedRow}
grid-area: Footer;
font-size: 0.66em;
`;
Styled.Row = styled.div`
${layoutMixins.row}
${layoutMixins.spacedRow}
width: var(--sidebar-width);
padding: 0 0.5rem;
border-right: 1px solid var(--color-border);
padding: 0 1rem;
gap: 0.5rem;
`;
Styled.StatusDot = styled.div<{ exchangeStatus: ExchangeStatus }>`
width: 0.5rem;
height: 0.5rem;
width: 1em;
height: 1em;
border-radius: 50%;
margin-right: 0.25rem;
background-color: ${({ exchangeStatus }) =>
({
@@ -129,28 +104,12 @@ Styled.StatusDot = styled.div<{ exchangeStatus: ExchangeStatus }>`
}[exchangeStatus])};
`;
Styled.FooterButton = styled(Button)`
--button-height: 1.5rem;
--button-radius: 0.25rem;
--button-backgroundColor: var(--color-layer-2);
--button-border: none;
--button-textColor: var(--color-text-0);
&:hover:not(:disabled) {
--button-backgroundColor: var(--color-layer-3);
--button-textColor: var(--color-text-1);
}
&:disabled {
cursor: default;
}
`;
Styled.WarningOutput = styled(Output)`
color: var(--color-warning);
`;
Styled.Details = styled(Details)`
${layoutMixins.scrollArea}
font: var(--font-tiny-book);
padding: 0 0.5em;
`;
+6 -8
View File
@@ -6,10 +6,9 @@ import { ButtonShape } from '@/constants/buttons';
import { DialogTypes } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { AppRoute } from '@/constants/routes';
import { useStringGetter } from '@/hooks';
import { LogoShortIcon, BellStrokeIcon } from '@/icons';
import { useTokenConfigs, useStringGetter } from '@/hooks';
import { Icon, IconName } from '@/components/Icon';
import { IconButton } from '@/components/IconButton';
import { NavigationMenu } from '@/components/NavigationMenu';
@@ -28,7 +27,6 @@ import { layoutMixins } from '@/styles/layoutMixins';
export const HeaderDesktop = () => {
const stringGetter = useStringGetter();
const dispatch = useDispatch();
const { chainTokenLabel } = useTokenConfigs();
const navItems = [
{
@@ -45,9 +43,9 @@ export const HeaderDesktop = () => {
href: AppRoute.Trade,
},
{
value: chainTokenLabel,
label: chainTokenLabel,
href: `/${chainTokenLabel}`,
value: 'Dv4TNT',
label: 'Dv4TNT',
href: AppRoute.Rewards,
},
{
value: 'PORTFOLIO',
@@ -80,13 +78,13 @@ export const HeaderDesktop = () => {
value: 'TERMS_OF_USE',
slotBefore: <Icon iconName={IconName.File} />,
label: stringGetter({ key: STRING_KEYS.TERMS_OF_USE }),
href: AppRoute.Terms,
href: 'https://dydx.exchange/terms',
},
{
value: 'PRIVACY_POLICY',
slotBefore: <Icon iconName={IconName.Privacy} />,
label: stringGetter({ key: STRING_KEYS.PRIVACY_POLICY }),
href: AppRoute.Privacy,
href: 'https://dydx.exchange/privacy',
},
{
value: 'HELP',
+15 -36
View File
@@ -14,6 +14,8 @@ import {
OrderSide,
OrderTimeInForce,
OrderExecution,
DYDX_DENOM,
GAS_PRICE_DYDX_DENOM,
} from '@dydxprotocol/v4-client-js';
import {
@@ -64,37 +66,16 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
): Promise<void> {
try {
const parsedParams = paramsInJson ? JSON.parse(paramsInJson) : {};
const {
indexerUrl,
websocketUrl,
validatorUrl,
chainId,
USDC_DENOM,
USDC_DECIMALS,
USDC_GAS_DENOM,
CHAINTOKEN_DENOM,
CHAINTOKEN_DECIMALS,
} = parsedParams;
const { indexerUrl, websocketUrl, validatorUrl, chainId } = parsedParams;
const compositeClient = await CompositeClient.connect(
new Network(
chainId,
new IndexerConfig(indexerUrl, websocketUrl),
new ValidatorConfig(
validatorUrl,
chainId,
{
USDC_DENOM,
USDC_DECIMALS,
USDC_GAS_DENOM,
CHAINTOKEN_DENOM,
CHAINTOKEN_DECIMALS,
},
{
broadcastPollIntervalMs: 3_000,
broadcastTimeoutMs: 60_000,
}
)
new ValidatorConfig(validatorUrl, chainId, {
broadcastPollIntervalMs: 3_000,
broadcastTimeoutMs: 60_000,
})
)
);
@@ -223,13 +204,13 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
params ?? {};
try {
const tx = await this.compositeClient?.cancelRawOrder(
const tx = await this.compositeClient?.cancelOrder(
new SubaccountClient(this.localWallet, subaccountNumber),
clientId,
orderFlags,
clobPairId,
goodTilBlock || undefined,
goodTilBlockTime || undefined,
goodTilBlock ?? undefined,
goodTilBlockTime ?? undefined
);
const parsedTx = this.parseToPrimitives(tx);
@@ -292,18 +273,16 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
this.localWallet,
() =>
new Promise((resolve) => {
if (!this.localWallet) {
throw new Error('Missing compositeClient or localWallet');
}
const msg = compositeClient?.sendTokenMessage(
this.localWallet,
amount,
const msg = compositeClient?.validatorClient.post.composer.composeMsgSendToken(
subaccountClient.address,
recipient,
DYDX_DENOM,
Long.fromNumber(amount * QUANTUM_MULTIPLIER)
);
resolve([msg]);
}),
this.compositeClient?.validatorClient?.post.defaultDydxGasPrice,
GAS_PRICE_DYDX_DENOM
);
const parsedTx = this.parseToPrimitives(tx);
+13 -33
View File
@@ -95,28 +95,15 @@ const injectedConnectorOptions = {
},
};
type WalletConnectConfig = {
client: {
name: string;
description: string;
iconUrl: string;
};
v2: {
projectId: string;
};
};
const getWalletconnect2ConnectorOptions = (
config: WalletConnectConfig
): ConstructorParameters<typeof WalletConnectConnector>[0] => ({
const walletconnect2ConnectorOptions: ConstructorParameters<typeof WalletConnectConnector>[0] = {
chains,
options: {
projectId: config.v2.projectId,
projectId: import.meta.env.VITE_WALLETCONNECT2_PROJECT_ID,
metadata: {
name: config.client.name,
description: config.client.description,
name: 'dYdX',
description: '',
url: import.meta.env.VITE_BASE_URL,
icons: [config.client.iconUrl],
icons: [`${import.meta.env.VITE_BASE_URL}/cbw-image.png}`],
},
showQrModal: true,
qrModalOptions: {
@@ -128,9 +115,9 @@ const getWalletconnect2ConnectorOptions = (
explorerRecommendedWalletIds: WALLET_CONNECT_EXPLORER_RECOMMENDED_IDS,
},
},
});
};
const getConnectors = (walletConnectConfig: WalletConnectConfig) => [
const connectors = [
new MetaMaskConnector({
chains,
options: {
@@ -144,7 +131,7 @@ const getConnectors = (walletConnectConfig: WalletConnectConfig) => [
reloadOnDisconnect: false,
},
}),
new WalletConnectConnector(getWalletconnect2ConnectorOptions(walletConnectConfig)),
new WalletConnectConnector(walletconnect2ConnectorOptions),
new InjectedConnector(injectedConnectorOptions),
];
@@ -166,34 +153,27 @@ const createInjectedConnectorWithProvider = (provider: ExternalProvider) =>
provider as unknown as Awaited<ReturnType<InjectedConnector['getProvider']>>;
})(injectedConnectorOptions) as InjectedConnector;
const createWalletConnect2ConnectorWithId = (
walletconnectId: string,
walletConnectConfig: WalletConnectConfig
) => {
const walletconnect2ConnectorOptions = getWalletconnect2ConnectorOptions(walletConnectConfig);
return new WalletConnectConnector({
const createWalletConnect2ConnectorWithId = (walletconnect2Id: string) =>
new WalletConnectConnector({
...walletconnect2ConnectorOptions,
options: {
...walletconnect2ConnectorOptions.options,
qrModalOptions: {
...walletconnect2ConnectorOptions.options.qrModalOptions,
explorerRecommendedWalletIds: [walletconnectId],
explorerRecommendedWalletIds: [walletconnect2Id],
explorerExcludedWalletIds: 'ALL',
},
},
});
};
// Custom connector from wallet selection
export const resolveWagmiConnector = ({
walletType,
walletConnection,
walletConnectConfig,
}: {
walletType: WalletType;
walletConnection: WalletConnection;
walletConnectConfig: WalletConnectConfig;
}) => {
const walletConfig = wallets[walletType];
const walletConnectionConfig = walletConnectionTypes[walletConnection.type];
@@ -201,6 +181,6 @@ export const resolveWagmiConnector = ({
return walletConnection.type === WalletConnectionType.InjectedEip1193 && walletConnection.provider
? createInjectedConnectorWithProvider(walletConnection.provider)
: walletConnection.type === WalletConnectionType.WalletConnect2 && walletConfig.walletconnect2Id
? createWalletConnect2ConnectorWithId(walletConfig.walletconnect2Id, walletConnectConfig)
: getConnectors(walletConnectConfig).find(({ id }: { id: string }) => id === walletConnectionConfig.wagmiConnectorId);
? createWalletConnect2ConnectorWithId(walletConfig.walletconnect2Id)
: connectors.find(({ id }: { id: string }) => id === walletConnectionConfig.wagmiConnectorId);
};
-17
View File
@@ -1,17 +0,0 @@
import styled, { AnyStyledComponent } from 'styled-components';
import { articleMixins } from '@/styles/articleMixins';
export const PrivacyPolicyPage = () => (
<Styled.Article>
<header>
<h1>Privacy Policy</h1>
</header>
</Styled.Article>
);
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Article = styled.article`
${articleMixins.article}
`;
-17
View File
@@ -1,17 +0,0 @@
import styled, { AnyStyledComponent } from 'styled-components';
import { articleMixins } from '@/styles/articleMixins';
export const TermsOfUsePage = () => (
<Styled.Article>
<header>
<h1>Terms of Use</h1>
</header>
</Styled.Article>
);
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Article = styled.article`
${articleMixins.article}
`;
+7 -11
View File
@@ -3,7 +3,7 @@ import styled, { AnyStyledComponent } from 'styled-components';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { layoutMixins } from '@/styles/layoutMixins';
import { useAccountBalance, useAccounts, useTokenConfigs, useStringGetter } from '@/hooks';
import { useAccountBalance, useAccounts, useStringGetter } from '@/hooks';
import { ButtonAction, ButtonSize } from '@/constants/buttons';
import { DialogTypes } from '@/constants/dialogs';
@@ -22,6 +22,7 @@ import { OnboardingTriggerButton } from '@/views/dialogs/OnboardingTriggerButton
import { openDialog } from '@/state/dialogs';
import { calculateCanAccountTrade } from '@/state/accountCalculators';
// TODO(@aforaleka): replace with real data
export const DYDXBalancePanel = () => {
const dispatch = useDispatch();
const stringGetter = useStringGetter();
@@ -29,7 +30,6 @@ export const DYDXBalancePanel = () => {
const { walletType } = useAccounts();
const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual);
const { nativeTokenBalance, nativeStakingBalance } = useAccountBalance();
const { chainTokenLabel } = useTokenConfigs();
return (
<Panel
@@ -37,7 +37,7 @@ export const DYDXBalancePanel = () => {
<Styled.Header>
<Styled.Title>
{/* <AssetIcon symbol="DYDX" /> */}
{chainTokenLabel}
Dv4TNT
</Styled.Title>
<Styled.ReceiveAndTransferButtons>
{!canAccountTrade ? (
@@ -106,13 +106,7 @@ export const DYDXBalancePanel = () => {
{
key: 'totalBalance',
label: 'Total balance',
value: (
<Output
type={OutputType.Asset}
value={nativeTokenBalance.plus(nativeStakingBalance)}
tag={chainTokenLabel}
/>
),
value: <Output type={OutputType.Asset} value={nativeTokenBalance + nativeStakingBalance} tag="Dv4TNT" />,
},
]}
/>
@@ -125,7 +119,7 @@ const Styled: Record<string, AnyStyledComponent> = {};
Styled.Header = styled.div`
${layoutMixins.spacedRow}
padding: 1rem 1.5rem 0;
padding: 1.25rem 1.5rem 0.5rem;
`;
Styled.Title = styled.h3`
@@ -154,6 +148,7 @@ Styled.ReceiveButton = styled(Button)`
Styled.Content = styled.div`
${layoutMixins.flexColumn}
gap: 0.75rem;
padding: 0 0.5rem 0.5rem;
`;
Styled.IconContainer = styled.div`
@@ -179,6 +174,7 @@ Styled.WalletAndStakedBalance = styled(Details)`
grid-template-columns: 1fr 1fr;
gap: 0.5rem;
> div {
gap: 1rem;
-2
View File
@@ -13,6 +13,4 @@ declare global {
'abacus:connectNetwork': CustomEvent;
}
var Intercom: any;
}
-2
View File
@@ -24,8 +24,6 @@ export const dialogsSlice = createSlice({
initialState,
reducers: {
openDialog: (state: DialogsState, action: PayloadAction<DialogInfo<DialogTypes>>) => {
if (state.activeDialog?.type === action.payload.type) return;
if (state.activeDialog) {
state.dialogQueue.push(action.payload);
} else {
-53
View File
@@ -1,53 +0,0 @@
import {
css,
type FlattenInterpolation,
type FlattenSimpleInterpolation,
type ThemeProps,
} from 'styled-components';
export const articleMixins: Record<
string,
FlattenSimpleInterpolation | FlattenInterpolation<ThemeProps<any>>
> = {
article: css`
padding: 2rem;
overflow-wrap: break-word;
user-select: text;
margin: 0 auto;
width: 60rem;
max-width: 100vw;
header {
color: var(--color-text-0);
margin-bottom: 2rem;
h1 {
font: var(--font-extra-book);
color: var(--color-text-2);
}
}
h2 {
font: var(--font-large-book);
color: var(--color-text-2);
padding: 0.5rem 0;
}
h3,
strong {
font-weight: 700;
}
strong {
color: var(--color-text-2);
}
p {
padding: 0.5rem 0;
}
ul {
padding: 0.5rem 1rem;
}
`,
};
+5 -4
View File
@@ -36,10 +36,11 @@
--single-unit-height: 9.375rem;
/* Modal constants */
--dialog-small-width: 18.75rem;
--dialog-medium-width: 20.5rem;
--dialog-large-width: 26.25rem;
--dialog-header-height: 4rem;
--sidebar-modal-width: 18.75rem;
--modal-small-width: 18.75rem;
--modal-medium-width: 20.5rem;
--modal-large-width: 26.25rem;
--modal-header-height: 4rem;
/* Market Selector constants */
--marketsDropdown-openWidth: 45rem;
+1 -2
View File
@@ -188,14 +188,13 @@ export const popoverMixins = {
--item-highlighted-backgroundColor: var(--color-layer-2);
--item-highlighted-textColor: var(--color-text-2);
--item-gap: 0.5em;
--item-radius: 0px;
--item-padding: 0.5em 1em;
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--item-gap);
gap: 0.5em;
padding: var(--item-padding);
border-radius: var(--item-radius);
@@ -75,14 +75,16 @@ export const AccountInfoConnectedState = () => {
<Styled.Header>
<span>{stringGetter({ key: STRING_KEYS.ACCOUNT })}</span>
<Styled.TransferButtons>
<Styled.Button
state={{ isDisabled: !dydxAccounts }}
onClick={() => dispatch(openDialog({ type: DialogTypes.Withdraw }))}
shape={ButtonShape.Pill}
size={ButtonSize.XSmall}
>
{stringGetter({ key: STRING_KEYS.WITHDRAW })}
</Styled.Button>
{import.meta.env.MODE !== 'production' && (
<Styled.Button
state={{ isDisabled: !dydxAccounts }}
onClick={() => dispatch(openDialog({ type: DialogTypes.Withdraw }))}
shape={ButtonShape.Pill}
size={ButtonSize.XSmall}
>
{stringGetter({ key: STRING_KEYS.WITHDRAW })}
</Styled.Button>
)}
<Styled.Button
state={{ isDisabled: !dydxAccounts }}
onClick={() => dispatch(openDialog({ type: DialogTypes.Deposit }))}
+6 -3
View File
@@ -32,9 +32,12 @@ export const DepositDialog = ({ setIsOpen }: ElementProps) => {
>
<Styled.Content>
{showTestDeposit ? <TestnetDepositForm onDeposit={closeDialog} /> : <DepositForm />}
<Styled.TextToggle onClick={() => setShowTestDeposit(!showTestDeposit)}>
{showTestDeposit ? 'Show deposit form (Under Construction)' : 'Show test faucet'}
</Styled.TextToggle>
{/* TODO TRCL-1693 - uncomment when deposit form is ready */}
{import.meta.env.MODE !== 'production' && (
<Styled.TextToggle onClick={() => setShowTestDeposit(!showTestDeposit)}>
{showTestDeposit ? 'Show deposit form (Under Construction)' : 'Show test faucet'}
</Styled.TextToggle>
)}
</Styled.Content>
</Dialog>
);
+21 -2
View File
@@ -5,6 +5,8 @@ import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { AbacusApiStatus } from '@/constants/abacus';
import { DialogTypes } from '@/constants/dialogs';
import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { UNICODE } from '@/constants/unicode';
import { useApiState, useStringGetter } from '@/hooks';
import { layoutMixins } from '@/styles/layoutMixins';
@@ -30,8 +32,16 @@ export const ExchangeOfflineDialog = ({ preventClose, setIsOpen }: ElementProps)
const selectedNetwork = useSelector(getSelectedNetwork);
const activeDialog = useSelector(getActiveDialog, shallowEqual);
const showOfflineMessage =
import.meta.env.MODE === 'production' &&
ENVIRONMENT_CONFIG_MAP[selectedNetwork].dydxChainId === 'dydx-testnet-4';
useEffect(() => {
if (activeDialog?.type === DialogTypes.ExchangeOffline && status === AbacusApiStatus.NORMAL) {
if (
activeDialog?.type === DialogTypes.ExchangeOffline &&
status === AbacusApiStatus.NORMAL &&
!showOfflineMessage
) {
dispatch(closeDialog());
}
}, [status, selectedNetwork]);
@@ -44,7 +54,16 @@ export const ExchangeOfflineDialog = ({ preventClose, setIsOpen }: ElementProps)
title={stringGetter({ key: STRING_KEYS.UNAVAILABLE })}
>
<Styled.Content>
<p>{statusErrorMessage}</p>
{showOfflineMessage ? (
<>
<p>Testnet is currently offline. Please check back!</p>
<Styled.Link href="https://v4-teacher.vercel.app/testnets/schedule">
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} {UNICODE.ARROW_RIGHT}
</Styled.Link>
</>
) : (
<p>{statusErrorMessage}</p>
)}
{import.meta.env.MODE !== 'production' && <NetworkSelectMenu />}
</Styled.Content>
</Dialog>
+46 -71
View File
@@ -1,94 +1,69 @@
import { useMemo } from 'react';
import styled, { AnyStyledComponent } from 'styled-components';
import { useDispatch } from 'react-redux';
import { Close } from '@radix-ui/react-dialog';
import { STRING_KEYS } from '@/constants/localization';
import { useStringGetter } from '@/hooks';
import { ChatIcon, FeedbackIcon, FileIcon, TerminalIcon } from '@/icons';
import { layoutMixins } from '@/styles/layoutMixins';
import { ComboboxDialogMenu } from '@/components/ComboboxDialogMenu';
import { isTruthy } from '@/lib/isTruthy';
import { Button } from '@/components/Button';
import { Dialog } from '@/components/Dialog';
import { ButtonAction, ButtonType } from '@/constants/buttons';
import { Icon, IconName } from '@/components/Icon';
type ElementProps = {
setIsOpen: (open: boolean) => void;
};
const HELP_LINKS = {
apiDocumentation: 'https://v4-teacher.vercel.app/',
helpCenter: null,
feedback: null,
};
export const HELP_URL = `https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform?usp=sf_link`;
/**
* HelpDialog
* - Will temporarily be used as a 'Give Feedback' dialog.
* - It will ask users to navigate to a Google Form in order to record feedback.
*/
export const HelpDialog = ({ setIsOpen }: ElementProps) => {
const stringGetter = useStringGetter();
const HELP_ITEMS = useMemo(
() => [
{
group: 'help-items',
items: [
HELP_LINKS.helpCenter && {
value: 'help-center',
label: stringGetter({ key: STRING_KEYS.HELP_CENTER }),
description: stringGetter({ key: STRING_KEYS.HELP_CENTER_DESCRIPTION }),
onSelect: () => {
HELP_LINKS.helpCenter && globalThis.open(HELP_LINKS.helpCenter, '_blank');
setIsOpen(false);
},
slotBefore: <FileIcon />,
},
HELP_LINKS.apiDocumentation && {
value: 'api-documentation',
label: stringGetter({ key: STRING_KEYS.API_DOCUMENTATION }),
description: stringGetter({ key: STRING_KEYS.API_DOCUMENTATION_DESCRIPTION }),
onSelect: () => {
HELP_LINKS.apiDocumentation && globalThis.open(HELP_LINKS.apiDocumentation, '_blank');
setIsOpen(false);
},
slotBefore: <TerminalIcon />,
},
globalThis?.Intercom && {
value: 'live-chat',
label: stringGetter({ key: STRING_KEYS.LIVE_CHAT }),
description: stringGetter({ key: STRING_KEYS.LIVE_CHAT_DESCRIPTION }),
onSelect: () => {
globalThis.Intercom('show');
setIsOpen(false);
},
slotBefore: <ChatIcon />,
},
HELP_LINKS.feedback && {
value: 'feedback',
label: stringGetter({ key: STRING_KEYS.PROVIDE_FEEDBACK }),
description: stringGetter({ key: STRING_KEYS.PROVIDE_FEEDBACK_DESCRIPTION }),
onSelect: () => {
HELP_LINKS.feedback && globalThis.open(HELP_LINKS.feedback, '_blank');
setIsOpen(false);
},
slotBefore: <FeedbackIcon />,
},
].filter(isTruthy),
},
],
[stringGetter]
);
return (
<Styled.ComboboxDialogMenu
<Dialog
isOpen
withSearch={false}
setIsOpen={setIsOpen}
title={stringGetter({ key: STRING_KEYS.HELP })}
items={HELP_ITEMS}
/>
title={stringGetter({ key: STRING_KEYS.PROVIDE_FEEDBACK })}
description={stringGetter({ key: STRING_KEYS.PROVIDE_FEEDBACK_DESCRIPTION })}
>
<Styled.Content>
<p>
You will be navigated to a Google Form where you will be able to provide feedback. Thank
you for your contribution!
</p>
<Styled.ButtonRow>
<Close asChild>
<Button
action={ButtonAction.Primary}
type={ButtonType.Link}
href={HELP_URL}
slotRight={<Icon iconName={IconName.LinkOut} />}
>
{stringGetter({ key: STRING_KEYS.CONTINUE })}
</Button>
</Close>
</Styled.ButtonRow>
</Styled.Content>
</Dialog>
);
};
const Styled: Record<string, AnyStyledComponent> = {};
Styled.ComboboxDialogMenu = styled(ComboboxDialogMenu)`
--dialog-width: var(--dialog-small-width);
--dialog-content-paddingTop: 1rem;
--dialog-content-paddingBottom: 1rem;
--comboxDialogMenu-item-gap: 1rem;
Styled.ButtonRow = styled.div`
${layoutMixins.row}
gap: 0.5rem;
justify-content: end;
`;
Styled.Content = styled.div`
${layoutMixins.column}
gap: 1rem;
`;
+4 -11
View File
@@ -37,7 +37,7 @@ export const OnboardingDialog = ({ setIsOpen }: ElementProps) => {
const stringGetter = useStringGetter();
const { isMobile } = useBreakpoints();
const { disconnect, walletType } = useAccounts();
const { walletType } = useAccounts();
const currentOnboardingStep = useSelector(calculateOnboardingStep);
@@ -45,17 +45,10 @@ export const OnboardingDialog = ({ setIsOpen }: ElementProps) => {
if (!currentOnboardingStep) setIsOpen?.(false);
}, [currentOnboardingStep]);
const setIsOpenFromDialog = (open: boolean) => {
if (!open && currentOnboardingStep === OnboardingSteps.AcknowledgeTerms) {
disconnect();
}
setIsOpen?.(open);
}
return (
<Styled.Dialog
isOpen={Boolean(currentOnboardingStep)}
setIsOpen={setIsOpenFromDialog}
setIsOpen={setIsOpen}
{...(currentOnboardingStep &&
{
[OnboardingSteps.ChooseWallet]: {
@@ -94,10 +87,10 @@ export const OnboardingDialog = ({ setIsOpen }: ElementProps) => {
title: stringGetter({ key: STRING_KEYS.ACKNOWLEDGE_TERMS }),
children: (
<Styled.Content>
<AcknowledgeTerms onClose={() => setIsOpenFromDialog?.(false)} />
<AcknowledgeTerms />
</Styled.Content>
),
width: '30rem',
width: '20rem',
},
[OnboardingSteps.DepositFunds]: {
title: stringGetter({ key: STRING_KEYS.DEPOSIT }),
@@ -2,21 +2,17 @@ import styled, { type AnyStyledComponent } from 'styled-components';
import { useAccounts, useStringGetter } from '@/hooks';
import { AppRoute } from '@/constants/routes';
import { STRING_KEYS } from '@/constants/localization';
import { ButtonAction } from '@/constants/buttons';
import { layoutMixins } from '@/styles/layoutMixins';
import { Button } from '@/components/Button';
import { Link } from '@/components/Link';
type ElementProps = {
onClose?: () => void;
onContinue?: () => void;
};
export const AcknowledgeTerms = ({ onClose, onContinue }: ElementProps) => {
export const AcknowledgeTerms = ({ onContinue }: ElementProps) => {
const stringGetter = useStringGetter();
const { saveHasAcknowledgedTerms } = useAccounts();
@@ -28,40 +24,26 @@ export const AcknowledgeTerms = ({ onClose, onContinue }: ElementProps) => {
return (
<>
<p>
<span>
{stringGetter({
key: STRING_KEYS.TOS_TITLE,
key: STRING_KEYS.LEGAL_UPDATES_DESCRIPTION,
params: {
TERMS_LINK: (
<Styled.Link href={`/#${AppRoute.Terms}`}>
TOU: (
<Styled.Link href="https://dydx.exchange/v4-terms">
{stringGetter({ key: STRING_KEYS.TERMS_OF_USE })}
</Styled.Link>
),
PRIVACY_POLICY_LINK: (
<Styled.Link href={`/#${AppRoute.Privacy}`}>
PRIVACY_POLICY: (
<Styled.Link href="https://dydx.exchange/privacy">
{stringGetter({ key: STRING_KEYS.PRIVACY_POLICY })}
</Styled.Link>
),
},
})}
</p>
<Styled.TOS>
<ul>
<li>{stringGetter({ key: STRING_KEYS.TOS_LINE1 })}</li>
<li>{stringGetter({ key: STRING_KEYS.TOS_LINE2 })}</li>
<li>{stringGetter({ key: STRING_KEYS.TOS_LINE3 })}</li>
<li>{stringGetter({ key: STRING_KEYS.TOS_LINE4 })}</li>
<li>{stringGetter({ key: STRING_KEYS.TOS_LINE5 })}</li>
</ul>
</Styled.TOS>
<Styled.Row>
<Styled.Button onClick={onClose} action={ButtonAction.Base}>
{stringGetter({ key: STRING_KEYS.CLOSE })}
</Styled.Button>
<Styled.Button onClick={onAcknowledgement} action={ButtonAction.Primary}>
{stringGetter({ key: STRING_KEYS.I_AGREE })}
</Styled.Button>
</Styled.Row>
</span>
<Button onClick={onAcknowledgement} action={ButtonAction.Primary}>
{stringGetter({ key: STRING_KEYS.I_AGREE })}
</Button>
</>
);
};
@@ -76,23 +58,3 @@ Styled.Link = styled(Link)`
color: var(--color-accent);
}
`;
Styled.TOS = styled.section`
background-color: var(--color-layer-4);
padding: 1rem 1rem 1rem 2rem;
border-radius: 0.875rem;
> ul {
${layoutMixins.column};
gap: 1rem;
}
`;
Styled.Row = styled.div`
${layoutMixins.row};
gap: 1rem;
`;
Styled.Button = styled(Button)`
flex-grow: 1;
`;
@@ -264,6 +264,7 @@ const Styled: Record<string, AnyStyledComponent> = {};
Styled.StatusCardsContainer = styled.div`
display: grid;
margin-top: 1rem;
gap: 1rem;
`;
+5 -7
View File
@@ -6,7 +6,7 @@ import { DydxChainAsset } from '@/constants/wallets';
import { formMixins } from '@/styles/formMixins';
import { layoutMixins } from '@/styles/layoutMixins';
import { useAccounts, useTokenConfigs, useStringGetter } from '@/hooks';
import { useAccounts, useStringGetter } from '@/hooks';
import { AssetIcon } from '@/components/AssetIcon';
import { CopyButton } from '@/components/CopyButton';
@@ -24,10 +24,9 @@ type ElementProps = {
setIsOpen: (open: boolean) => void;
};
export const ReceiveDialog = ({ selectedAsset = DydxChainAsset.CHAINTOKEN, setIsOpen }: ElementProps) => {
export const ReceiveDialog = ({ selectedAsset = DydxChainAsset.DYDX, setIsOpen }: ElementProps) => {
const stringGetter = useStringGetter();
const { dydxAddress } = useAccounts();
const { chainTokenLabel, usdcLabel } = useTokenConfigs();
const [asset, setAsset] = useState(selectedAsset);
@@ -36,17 +35,16 @@ export const ReceiveDialog = ({ selectedAsset = DydxChainAsset.CHAINTOKEN, setIs
value: DydxChainAsset.USDC,
label: (
<Styled.InlineRow>
<AssetIcon symbol="USDC" /> {usdcLabel}
<AssetIcon symbol="USDC" /> USDC
</Styled.InlineRow>
),
},
{
value: DydxChainAsset.CHAINTOKEN,
value: DydxChainAsset.DYDX,
label: (
<Styled.InlineRow>
{/* TODO: conditionally display the native token assetIcon */}
{/* <AssetIcon symbol="DYDX" /> */}
{chainTokenLabel}
Dv4TNT
</Styled.InlineRow>
),
},
@@ -87,6 +87,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
// Async Data
const { balance, queryStatus, isQueryFetching } = useAccountBalance({
addressOrDenom: sourceToken?.address || CHAIN_DEFAULT_TOKEN_ADDRESS,
assetSymbol: sourceToken?.symbol || undefined,
chainId: chainId,
decimals: sourceToken?.decimals || undefined,
isCosmosChain: false,
+12 -10
View File
@@ -11,7 +11,11 @@ import { ButtonShape, ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { NumberSign } from '@/constants/numbers';
import { DydxChainAsset } from '@/constants/wallets';
import {
DYDX_CHAIN_ASSET_COIN_DENOM,
DYDX_CHAIN_ASSET_TAGS,
DydxChainAsset,
} from '@/constants/wallets';
import {
useAccountBalance,
@@ -21,7 +25,6 @@ import {
useSelectedNetwork,
useStringGetter,
useSubaccount,
useTokenConfigs,
} from '@/hooks';
import { formMixins } from '@/styles/formMixins';
@@ -54,7 +57,7 @@ type TransferFormProps = {
};
export const TransferForm = ({
selectedAsset = DydxChainAsset.CHAINTOKEN,
selectedAsset = DydxChainAsset.DYDX,
onDone,
className,
}: TransferFormProps) => {
@@ -69,7 +72,6 @@ export const TransferForm = ({
const { transfer } = useSubaccount();
const { nativeTokenBalance, usdcBalance } = useAccountBalance();
const { selectedNetwork } = useSelectedNetwork();
const { tokensConfigs, usdcLabel, chainTokenLabel } = useTokenConfigs();
// User Input
const [asset, setAsset] = useState<DydxChainAsset>(selectedAsset);
@@ -136,7 +138,7 @@ export const TransferForm = ({
try {
// Subtract fees from amount if sending native tokens
const amountToTransfer = (
asset === DydxChainAsset.CHAINTOKEN ? amountBN.minus(fee) : amountBN
asset === DydxChainAsset.DYDX ? amountBN.minus(fee) : amountBN
).toNumber();
const screenResults = await screenAddresses({
@@ -159,7 +161,7 @@ export const TransferForm = ({
const txResponse = await transfer(
amountToTransfer,
recipientAddress as string,
tokensConfigs[asset].denom,
DYDX_CHAIN_ASSET_COIN_DENOM[asset]
);
if (txResponse?.code === 0) {
@@ -218,16 +220,16 @@ export const TransferForm = ({
value: DydxChainAsset.USDC,
label: (
<Styled.InlineRow>
<AssetIcon symbol="USDC" /> {usdcLabel}
<AssetIcon symbol="USDC" /> {DYDX_CHAIN_ASSET_TAGS[DydxChainAsset.USDC]}
</Styled.InlineRow>
),
},
{
value: DydxChainAsset.CHAINTOKEN,
value: DydxChainAsset.DYDX,
label: (
<Styled.InlineRow>
{/* <AssetIcon symbol="DYDX" /> */}
{chainTokenLabel}
{DYDX_CHAIN_ASSET_TAGS[DydxChainAsset.DYDX]}
</Styled.InlineRow>
),
},
@@ -249,7 +251,7 @@ export const TransferForm = ({
key: 'amount',
label: (
<span>
{stringGetter({ key: STRING_KEYS.AVAILABLE })} <Tag>{tokensConfigs[asset].name}</Tag>
{stringGetter({ key: STRING_KEYS.AVAILABLE })} <Tag>{DYDX_CHAIN_ASSET_TAGS[asset]}</Tag>
</span>
),
value: (
@@ -3,9 +3,9 @@ import { shallowEqual, useSelector } from 'react-redux';
import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { NumberSign } from '@/constants/numbers';
import { DydxChainAsset } from '@/constants/wallets';
import { DYDX_CHAIN_ASSET_TAGS, DydxChainAsset } from '@/constants/wallets';
import { useAccountBalance, useTokenConfigs, useStringGetter } from '@/hooks';
import { useAccountBalance, useStringGetter } from '@/hooks';
import { Button } from '@/components/Button';
import { DiffOutput } from '@/components/DiffOutput';
@@ -37,7 +37,6 @@ export const TransferButtonAndReceipt = ({
const stringGetter = useStringGetter();
const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual);
const { size } = useSelector(getTransferInputs, shallowEqual) || {};
const { tokensConfigs } = useTokenConfigs();
const { equity: equityInfo, leverage: leverageInfo } =
useSelector(getSubaccount, shallowEqual) || {};
@@ -61,7 +60,7 @@ export const TransferButtonAndReceipt = ({
key: 'fees',
label: (
<span>
{stringGetter({ key: STRING_KEYS.FEES })} <Tag>{tokensConfigs[selectedAsset].name}</Tag>
{stringGetter({ key: STRING_KEYS.FEES })} <Tag>{DYDX_CHAIN_ASSET_TAGS[selectedAsset]}</Tag>
</span>
),
value: <Output type={OutputType.Asset} value={fees} />,
@@ -70,7 +69,7 @@ export const TransferButtonAndReceipt = ({
key: 'balance',
label: (
<span>
{stringGetter({ key: STRING_KEYS.BALANCE })} <Tag>{tokensConfigs[selectedAsset].name}</Tag>
{stringGetter({ key: STRING_KEYS.BALANCE })} <Tag>{DYDX_CHAIN_ASSET_TAGS[selectedAsset]}</Tag>
</span>
),
value: (
+4 -17
View File
@@ -9,13 +9,7 @@ import { DialogTypes } from '@/constants/dialogs';
import { STRING_KEYS, TOOLTIP_STRING_KEYS } from '@/constants/localization';
import { DydxChainAsset, wallets } from '@/constants/wallets';
import {
useAccounts,
useBreakpoints,
useTokenConfigs,
useStringGetter,
useAccountBalance,
} from '@/hooks';
import { useAccounts, useBreakpoints, useStringGetter, useAccountBalance } from '@/hooks';
import { OnboardingTriggerButton } from '@/views/dialogs/OnboardingTriggerButton';
@@ -45,7 +39,6 @@ export const AccountMenu = () => {
const onboardingState = useSelector(getOnboardingState);
const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {};
const { nativeTokenBalance } = useAccountBalance();
const { usdcLabel, chainTokenLabel } = useTokenConfigs();
const { evmAddress, walletType, dydxAddress, hdKey } = useAccounts();
@@ -114,23 +107,17 @@ export const AccountMenu = () => {
<div>
<div>
<Styled.label>
{stringGetter({
key: STRING_KEYS.ASSET_BALANCE,
params: { ASSET: chainTokenLabel },
})}
{stringGetter({ key: STRING_KEYS.ASSET_BALANCE, params: { ASSET: 'Dv4TNT' } })}
{/* <AssetIcon symbol="DYDX" /> */}
</Styled.label>
<Styled.BalanceOutput type={OutputType.Asset} value={nativeTokenBalance} />
</div>
<AssetActions asset={DydxChainAsset.CHAINTOKEN} dispatch={dispatch} />
<AssetActions asset={DydxChainAsset.DYDX} dispatch={dispatch} />
</div>
<div>
<div>
<Styled.label>
{stringGetter({
key: STRING_KEYS.ASSET_BALANCE,
params: { ASSET: usdcLabel },
})}
{stringGetter({ key: STRING_KEYS.ASSET_BALANCE, params: { ASSET: 'USDC' } })}
<AssetIcon symbol="USDC" />
</Styled.label>
<Styled.BalanceOutput