Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d17a9005c0 |
@@ -2,5 +2,17 @@ VITE_BASE_URL=https://v4.testnet.dydx.exchange
|
|||||||
|
|
||||||
VITE_ALCHEMY_API_KEY=FP275q327Yh7qswtZWenbPXdZZdnAFmC
|
VITE_ALCHEMY_API_KEY=FP275q327Yh7qswtZWenbPXdZZdnAFmC
|
||||||
|
|
||||||
|
VITE_FAUCET_URL_DEV=https://faucet.v4dev.dydx.exchange/
|
||||||
|
VITE_FAUCET_URL_STAGE=https://faucet.v4staging.dydx.exchange/
|
||||||
|
VITE_FAUCET_URL_TESTNET2=https://faucet.v4testnet2.dydx.exchange/
|
||||||
|
|
||||||
|
VITE_VALIDATOR_URL_DEV=http://52.192.187.113:26657/
|
||||||
|
VITE_VALIDATOR_URL_STAGE=https://validator.v4staging.dydx.exchange/
|
||||||
|
VITE_VALIDATOR_URL_TESTNET2=https://validator.v4testnet2.dydx.exchange/
|
||||||
|
|
||||||
|
VITE_INDEXER_URL_DEV=http://dev-indexer-apne1-lb-public-890774175.ap-northeast-1.elb.amazonaws.com
|
||||||
|
VITE_INDEXER_URL_STAGE=https://indexer.v4staging.dydx.exchange
|
||||||
|
VITE_INDEXER_URL_TESTNET2=https://indexer.v4testnet2.dydx.exchange
|
||||||
|
|
||||||
VITE_WALLETCONNECT1_BRIDGE=wss://api.dydx.exchange/wc/
|
VITE_WALLETCONNECT1_BRIDGE=wss://api.dydx.exchange/wc/
|
||||||
VITE_WALLETCONNECT2_PROJECT_ID=fbe94eaa691fa8d929561f8567062b32
|
VITE_WALLETCONNECT2_PROJECT_ID=fbe94eaa691fa8d929561f8567062b32
|
||||||
|
|||||||
+12
-3
@@ -1,8 +1,17 @@
|
|||||||
VITE_BASE_URL=
|
VITE_BASE_URL=
|
||||||
|
|
||||||
VITE_ALCHEMY_API_KEY=
|
VITE_ALCHEMY_API_KEY=
|
||||||
|
|
||||||
VITE_PK_ENCRYPTION_KEY=
|
VITE_PK_ENCRYPTION_KEY=
|
||||||
|
|
||||||
VITE_WALLETCONNECT1_BRIDGE=
|
|
||||||
VITE_WALLETCONNECT2_PROJECT_ID=
|
VITE_WALLETCONNECT2_PROJECT_ID=
|
||||||
|
|
||||||
|
VITE_FAUCET_URL_DEV=
|
||||||
|
VITE_FAUCET_URL_STAGE=
|
||||||
|
VITE_FAUCET_URL_TESTNET2=
|
||||||
|
|
||||||
|
VITE_VALIDATOR_URL_DEV=
|
||||||
|
VITE_VALIDATOR_URL_STAGE=
|
||||||
|
VITE_VALIDATOR_URL_TESTNET2=
|
||||||
|
|
||||||
|
VITE_INDEXER_URL_DEV=
|
||||||
|
VITE_INDEXER_URL_STAGE=
|
||||||
|
VITE_INDEXER_URL_TESTNET2=
|
||||||
|
|||||||
@@ -5,10 +5,15 @@ vite.config.ts
|
|||||||
|
|
||||||
# Temporarily ignore, we will slowly remove each directory as we fix files to follow ESLint rules
|
# Temporarily ignore, we will slowly remove each directory as we fix files to follow ESLint rules
|
||||||
/src/components
|
/src/components
|
||||||
|
/src/dialogs
|
||||||
/src/forms
|
/src/forms
|
||||||
/src/hooks
|
/src/hooks
|
||||||
|
/src/history.ts
|
||||||
|
/src/icons
|
||||||
/src/lib
|
/src/lib
|
||||||
/src/main.tsx
|
/src/main.tsx
|
||||||
/src/menus
|
/src/menus
|
||||||
/src/pages
|
/src/pages
|
||||||
|
/src/state
|
||||||
|
/src/styles
|
||||||
/src/views
|
/src/views
|
||||||
|
|||||||
+6
-4
@@ -33,11 +33,14 @@
|
|||||||
"jsx-a11y/anchor-is-valid": "off",
|
"jsx-a11y/anchor-is-valid": "off",
|
||||||
"jsx-a11y/click-events-have-key-events": "off",
|
"jsx-a11y/click-events-have-key-events": "off",
|
||||||
"no-continue": "off",
|
"no-continue": "off",
|
||||||
"no-console": ["error"],
|
"no-console": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"devDependencies": ["./scripts/*.js"]
|
||||||
|
}
|
||||||
|
],
|
||||||
"no-lonely-if": "off",
|
"no-lonely-if": "off",
|
||||||
"no-multi-assign": "off",
|
|
||||||
"no-nested-ternary": "off",
|
"no-nested-ternary": "off",
|
||||||
"no-param-reassign": ["error", { "props": false }],
|
|
||||||
"no-return-assign": "off",
|
"no-return-assign": "off",
|
||||||
"no-return-await": "off",
|
"no-return-await": "off",
|
||||||
"no-underscore-dangle": "off",
|
"no-underscore-dangle": "off",
|
||||||
@@ -62,7 +65,6 @@
|
|||||||
"react/sort-comp": "off",
|
"react/sort-comp": "off",
|
||||||
"@typescript-eslint/no-use-before-define": "off",
|
"@typescript-eslint/no-use-before-define": "off",
|
||||||
"@typescript-eslint/comma-dangle": "off",
|
"@typescript-eslint/comma-dangle": "off",
|
||||||
"@typescript-eslint/no-unused-vars": "error",
|
|
||||||
"@typescript-eslint/member-delimiter-style": [
|
"@typescript-eslint/member-delimiter-style": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,12 +1,4 @@
|
|||||||
<p align="center"><img src="https://dydx.exchange/icon.svg?" width="256" /></p>
|
# v4 Web App
|
||||||
|
|
||||||
<h1 align="center">v4 Web</h1>
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
<a href='https://github.com/dydxprotocol/v4-web/blob/main/LICENSE'>
|
|
||||||
<img src='https://img.shields.io/badge/License-BSL_1.1-blue' alt='License' />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
@@ -110,3 +102,47 @@ To access your content on IPFS:
|
|||||||
2. **Public IPFS gateway:** Access your content via a public IPFS gateway, such as [https://dweb.link](https://dweb.link/) or [https://w3s.link/](https://w3s.link/). Use the gateway URL with your CID appended, like `https://dweb.link/ipfs/your_cid`.
|
2. **Public IPFS gateway:** Access your content via a public IPFS gateway, such as [https://dweb.link](https://dweb.link/) or [https://w3s.link/](https://w3s.link/). Use the gateway URL with your CID appended, like `https://dweb.link/ipfs/your_cid`.
|
||||||
|
|
||||||
Replace `your_cid` with the actual CID.
|
Replace `your_cid` with the actual CID.
|
||||||
|
|
||||||
|
|
||||||
|
## Part 5: Customization
|
||||||
|
|
||||||
|
### Using CircularXX font
|
||||||
|
|
||||||
|
1. **Procure a license for CircularXX:** Visit [Lineto](https://lineto.com/shop/select?family=circular&set=DOPhtVQR-8m) and add `Circular/Family Package` + `Circular Mono/Regular` to your cart. Check the applicable boxes in the `Configure` step to fill out licensing information. After the `Payment` step, download the `.woff2` files.
|
||||||
|
|
||||||
|
2. **Add the fonts to `v4-web`:** Add `CircularXXWeb-Bold.woff2`, `CircularXXWeb-Book.woff2`, `CircularXXWeb-Medium.woff2`, and `CircularXXMonoWeb-Regular.woff2` to the `src/styles/fonts` directory.
|
||||||
|
|
||||||
|
3. **Add font-family to stylesheets:** In `src/styles/fonts.css`, add the following code to import your newly purchased `Circular` font.
|
||||||
|
```css
|
||||||
|
@font-face {
|
||||||
|
src: url("fonts/CircularXXWeb-Book.woff2") format("woff2");
|
||||||
|
font-family: "Circular";
|
||||||
|
font-weight: 450;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
src: url("fonts/CircularXXWeb-Medium.woff2") format("woff2");
|
||||||
|
font-family: "Circular";
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
src: url("fonts/CircularXXWeb-Bold.woff2") format("woff2");
|
||||||
|
font-family: "Circular";
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
src: url("fonts/CircularXXMonoWeb-Regular.woff2") format("woff2");
|
||||||
|
font-family: "CircularMono";
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In `src/styles/text.css`, update lines `4` and `5` to
|
||||||
|
```css
|
||||||
|
--fontFamily-base: "Circular", "Satoshi", system-ui, -apple-system, Helvetica, Arial, sans-serif;
|
||||||
|
--fontFamily-monospace: "CircularMono", Courier, monospace, var(--fontFamily-base);
|
||||||
|
```
|
||||||
|
|
||||||
|
Enjoy the newly installed font.
|
||||||
|
|||||||
+3
-3
@@ -37,9 +37,9 @@
|
|||||||
"@cosmjs/proto-signing": "^0.31.0",
|
"@cosmjs/proto-signing": "^0.31.0",
|
||||||
"@cosmjs/stargate": "^0.31.0",
|
"@cosmjs/stargate": "^0.31.0",
|
||||||
"@cosmjs/tendermint-rpc": "^0.31.0",
|
"@cosmjs/tendermint-rpc": "^0.31.0",
|
||||||
"@dydxprotocol/v4-abacus": "^0.5.4",
|
"@dydxprotocol/abacus": "^0.4.16",
|
||||||
"@dydxprotocol/v4-client-js": "^0.36.1",
|
"@dydxprotocol/v4-client-js": "^0.33.1",
|
||||||
"@dydxprotocol/v4-localization": "^0.1.11",
|
"@dydxprotocol/v4-localization": "^0.0.25",
|
||||||
"@ethersproject/providers": "^5.7.2",
|
"@ethersproject/providers": "^5.7.2",
|
||||||
"@js-joda/core": "^5.5.3",
|
"@js-joda/core": "^5.5.3",
|
||||||
"@radix-ui/react-collapsible": "^1.0.3",
|
"@radix-ui/react-collapsible": "^1.0.3",
|
||||||
|
|||||||
Generated
+238
-32
@@ -26,15 +26,15 @@ dependencies:
|
|||||||
'@cosmjs/tendermint-rpc':
|
'@cosmjs/tendermint-rpc':
|
||||||
specifier: ^0.31.0
|
specifier: ^0.31.0
|
||||||
version: 0.31.0
|
version: 0.31.0
|
||||||
'@dydxprotocol/v4-abacus':
|
'@dydxprotocol/abacus':
|
||||||
specifier: ^0.5.4
|
specifier: ^0.4.16
|
||||||
version: 0.5.4
|
version: 0.4.16
|
||||||
'@dydxprotocol/v4-client-js':
|
'@dydxprotocol/v4-client-js':
|
||||||
specifier: ^0.36.1
|
specifier: ^0.33.1
|
||||||
version: 0.36.1
|
version: 0.33.1
|
||||||
'@dydxprotocol/v4-localization':
|
'@dydxprotocol/v4-localization':
|
||||||
specifier: ^0.1.11
|
specifier: ^0.0.25
|
||||||
version: 0.1.11
|
version: 0.0.25
|
||||||
'@ethersproject/providers':
|
'@ethersproject/providers':
|
||||||
specifier: ^5.7.2
|
specifier: ^5.7.2
|
||||||
version: 5.7.2
|
version: 5.7.2
|
||||||
@@ -614,6 +614,74 @@ packages:
|
|||||||
'@babel/helper-validator-identifier': 7.22.5
|
'@babel/helper-validator-identifier': 7.22.5
|
||||||
to-fast-properties: 2.0.0
|
to-fast-properties: 2.0.0
|
||||||
|
|
||||||
|
/@bufbuild/buf-darwin-arm64@1.19.0-1:
|
||||||
|
resolution: {integrity: sha512-HsWPii21wm3QSyuxrNq9+Yf8iAgpnC4rNCy4x3d6P1fd/LmgE1NPzQW0ghEZvl9dgAQKkL/4S5bKhlm7kbUdmQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@bufbuild/buf-darwin-x64@1.19.0-1:
|
||||||
|
resolution: {integrity: sha512-2+Ig7ylYpVh4kms/OeJJVY+X0KX4awPA6hYr7L7aZOIcHwZEM8lWtSTO/se5pQc7dc8FXNiC4YUqHC8yfxxX6Q==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@bufbuild/buf-linux-aarch64@1.19.0-1:
|
||||||
|
resolution: {integrity: sha512-g/Vxg3WiBr3nhsxsRr2Q81xXJD+0ktHIO3ZJggTG2Sbbl3dh8kyg1iKM6MjJiMP7su5RKCylLigzoEJzVTShyA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@bufbuild/buf-linux-x64@1.19.0-1:
|
||||||
|
resolution: {integrity: sha512-anYuGx8k/2kp8GPX3eHNUf3IY/01Zpnyw0HaLPXK1Btqyy6XkapVywrDqg7YUzMd1ySFEp1wD9UqRNdEFNCQ4A==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@bufbuild/buf-win32-arm64@1.19.0-1:
|
||||||
|
resolution: {integrity: sha512-xXgF1qYnCfRKbGx1FqvPbpZ6ajh4ddxpXhSxI3VCeb3MsMBuIbiLqX4fQAL3ls/Zwz8tVIITuSwOhYmSEGcpBA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@bufbuild/buf-win32-x64@1.19.0-1:
|
||||||
|
resolution: {integrity: sha512-futmqgpMQCR1lcAzZJEGjPr7ECw1gYTPIV8crm5SY+iCJ7sOeStOBNt7q5hV4LKmmeWmvm03XIMZPjhQzjH5NQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
requiresBuild: true
|
||||||
|
dev: false
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
/@bufbuild/buf@1.19.0-1:
|
||||||
|
resolution: {integrity: sha512-TIsLTTQUntr/Xq/IMSULv3dlC3/ZsVwQtWgxmJ++IzSuOW79TFQfq59vFeTWrPa6+QXFMz5t6jkMyD4ghzO5nw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
hasBin: true
|
||||||
|
requiresBuild: true
|
||||||
|
optionalDependencies:
|
||||||
|
'@bufbuild/buf-darwin-arm64': 1.19.0-1
|
||||||
|
'@bufbuild/buf-darwin-x64': 1.19.0-1
|
||||||
|
'@bufbuild/buf-linux-aarch64': 1.19.0-1
|
||||||
|
'@bufbuild/buf-linux-x64': 1.19.0-1
|
||||||
|
'@bufbuild/buf-win32-arm64': 1.19.0-1
|
||||||
|
'@bufbuild/buf-win32-x64': 1.19.0-1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@coinbase/wallet-sdk@3.7.1:
|
/@coinbase/wallet-sdk@3.7.1:
|
||||||
resolution: {integrity: sha512-LjyoDCB+7p0waQXfK+fUgcAs3Ezk6S6e+LYaoFjpJ6c9VTop3NyZF40Pi7df4z7QJohCwzuIDjz0Rhtig6Y7Pg==}
|
resolution: {integrity: sha512-LjyoDCB+7p0waQXfK+fUgcAs3Ezk6S6e+LYaoFjpJ6c9VTop3NyZF40Pi7df4z7QJohCwzuIDjz0Rhtig6Y7Pg==}
|
||||||
engines: {node: '>= 10.0.0'}
|
engines: {node: '>= 10.0.0'}
|
||||||
@@ -658,6 +726,15 @@ packages:
|
|||||||
'@cosmjs/utils': 0.27.1
|
'@cosmjs/utils': 0.27.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@cosmjs/amino@0.29.3:
|
||||||
|
resolution: {integrity: sha512-BFz1++ERerIggiFc7iGHhGe1CeV3rCv8BvkoBQTBN/ZwzHOaKvqQj8smDlRGlQxX3HWlTwgiLN2A+OB5yX4ZRw==}
|
||||||
|
dependencies:
|
||||||
|
'@cosmjs/crypto': 0.29.5
|
||||||
|
'@cosmjs/encoding': 0.29.5
|
||||||
|
'@cosmjs/math': 0.29.5
|
||||||
|
'@cosmjs/utils': 0.29.5
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@cosmjs/amino@0.30.1:
|
/@cosmjs/amino@0.30.1:
|
||||||
resolution: {integrity: sha512-yNHnzmvAlkETDYIpeCTdVqgvrdt1qgkOXwuRVi8s27UKI5hfqyE9fJ/fuunXE6ZZPnKkjIecDznmuUOMrMvw4w==}
|
resolution: {integrity: sha512-yNHnzmvAlkETDYIpeCTdVqgvrdt1qgkOXwuRVi8s27UKI5hfqyE9fJ/fuunXE6ZZPnKkjIecDznmuUOMrMvw4w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -711,6 +788,18 @@ packages:
|
|||||||
sha.js: 2.4.11
|
sha.js: 2.4.11
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@cosmjs/crypto@0.29.5:
|
||||||
|
resolution: {integrity: sha512-2bKkaLGictaNL0UipQCL6C1afaisv6k8Wr/GCLx9FqiyFkh9ZgRHDyetD64ZsjnWV/N/D44s/esI+k6oPREaiQ==}
|
||||||
|
dependencies:
|
||||||
|
'@cosmjs/encoding': 0.29.5
|
||||||
|
'@cosmjs/math': 0.29.5
|
||||||
|
'@cosmjs/utils': 0.29.5
|
||||||
|
'@noble/hashes': 1.3.1
|
||||||
|
bn.js: 5.2.1
|
||||||
|
elliptic: 6.5.4
|
||||||
|
libsodium-wrappers: 0.7.11
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@cosmjs/crypto@0.30.1:
|
/@cosmjs/crypto@0.30.1:
|
||||||
resolution: {integrity: sha512-rAljUlake3MSXs9xAm87mu34GfBLN0h/1uPPV6jEwClWjNkAMotzjC0ab9MARy5FFAvYHL3lWb57bhkbt2GtzQ==}
|
resolution: {integrity: sha512-rAljUlake3MSXs9xAm87mu34GfBLN0h/1uPPV6jEwClWjNkAMotzjC0ab9MARy5FFAvYHL3lWb57bhkbt2GtzQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -743,6 +832,14 @@ packages:
|
|||||||
readonly-date: 1.0.0
|
readonly-date: 1.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@cosmjs/encoding@0.29.5:
|
||||||
|
resolution: {integrity: sha512-G4rGl/Jg4dMCw5u6PEZHZcoHnUBlukZODHbm/wcL4Uu91fkn5jVo5cXXZcvs4VCkArVGrEj/52eUgTZCmOBGWQ==}
|
||||||
|
dependencies:
|
||||||
|
base64-js: 1.5.1
|
||||||
|
bech32: 1.1.4
|
||||||
|
readonly-date: 1.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@cosmjs/encoding@0.30.1:
|
/@cosmjs/encoding@0.30.1:
|
||||||
resolution: {integrity: sha512-rXmrTbgqwihORwJ3xYhIgQFfMSrwLu1s43RIK9I8EBudPx3KmnmyAKzMOVsRDo9edLFNuZ9GIvysUCwQfq3WlQ==}
|
resolution: {integrity: sha512-rXmrTbgqwihORwJ3xYhIgQFfMSrwLu1s43RIK9I8EBudPx3KmnmyAKzMOVsRDo9edLFNuZ9GIvysUCwQfq3WlQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -759,12 +856,11 @@ packages:
|
|||||||
readonly-date: 1.0.0
|
readonly-date: 1.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@cosmjs/encoding@0.31.1:
|
/@cosmjs/json-rpc@0.29.5:
|
||||||
resolution: {integrity: sha512-IuxP6ewwX6vg9sUJ8ocJD92pkerI4lyG8J5ynAM3NaX3q+n+uMoPRSQXNeL9bnlrv01FF1kIm8if/f5F7ZPtkA==}
|
resolution: {integrity: sha512-C78+X06l+r9xwdM1yFWIpGl03LhB9NdM1xvZpQHwgCOl0Ir/WV8pw48y3Ez2awAoUBRfTeejPe4KvrE6NoIi/w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
base64-js: 1.5.1
|
'@cosmjs/stream': 0.29.5
|
||||||
bech32: 1.1.4
|
xstream: 11.14.0
|
||||||
readonly-date: 1.0.0
|
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@cosmjs/json-rpc@0.30.1:
|
/@cosmjs/json-rpc@0.30.1:
|
||||||
@@ -801,6 +897,12 @@ packages:
|
|||||||
bn.js: 5.2.1
|
bn.js: 5.2.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@cosmjs/math@0.29.5:
|
||||||
|
resolution: {integrity: sha512-2GjKcv+A9f86MAWYLUkjhw1/WpRl2R1BTb3m9qPG7lzMA7ioYff9jY5SPCfafKdxM4TIQGxXQlYGewQL16O68Q==}
|
||||||
|
dependencies:
|
||||||
|
bn.js: 5.2.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@cosmjs/math@0.30.1:
|
/@cosmjs/math@0.30.1:
|
||||||
resolution: {integrity: sha512-yaoeI23pin9ZiPHIisa6qqLngfnBR/25tSaWpkTm8Cy10MX70UF5oN4+/t1heLaM6SSmRrhk3psRkV4+7mH51Q==}
|
resolution: {integrity: sha512-yaoeI23pin9ZiPHIisa6qqLngfnBR/25tSaWpkTm8Cy10MX70UF5oN4+/t1heLaM6SSmRrhk3psRkV4+7mH51Q==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -813,10 +915,16 @@ packages:
|
|||||||
bn.js: 5.2.1
|
bn.js: 5.2.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@cosmjs/math@0.31.1:
|
/@cosmjs/proto-signing@0.29.3:
|
||||||
resolution: {integrity: sha512-kiuHV6m6DSB8/4UV1qpFhlc4ul8SgLXTGRlYkYiIIP4l0YNeJ+OpPYaOlEgx4Unk2mW3/O2FWYj7Jc93+BWXng==}
|
resolution: {integrity: sha512-Ai3l9THjMOrLJ4Ebn1Dgptwg6W5ZIRJqtnJjijHhGwTVC1WT0WdYU3aMZ7+PwubcA/cA1rH4ZTK7jrfYbra63g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
bn.js: 5.2.1
|
'@cosmjs/amino': 0.29.3
|
||||||
|
'@cosmjs/crypto': 0.29.5
|
||||||
|
'@cosmjs/encoding': 0.29.5
|
||||||
|
'@cosmjs/math': 0.29.5
|
||||||
|
'@cosmjs/utils': 0.29.5
|
||||||
|
cosmjs-types: 0.5.2
|
||||||
|
long: 4.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@cosmjs/proto-signing@0.30.1:
|
/@cosmjs/proto-signing@0.30.1:
|
||||||
@@ -843,6 +951,18 @@ packages:
|
|||||||
long: 4.0.0
|
long: 4.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@cosmjs/socket@0.29.5:
|
||||||
|
resolution: {integrity: sha512-5VYDupIWbIXq3ftPV1LkS5Ya/T7Ol/AzWVhNxZ79hPe/mBfv1bGau/LqIYOm2zxGlgm9hBHOTmWGqNYDwr9LNQ==}
|
||||||
|
dependencies:
|
||||||
|
'@cosmjs/stream': 0.29.5
|
||||||
|
isomorphic-ws: 4.0.1(ws@7.5.9)
|
||||||
|
ws: 7.5.9
|
||||||
|
xstream: 11.14.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- bufferutil
|
||||||
|
- utf-8-validate
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@cosmjs/socket@0.30.1:
|
/@cosmjs/socket@0.30.1:
|
||||||
resolution: {integrity: sha512-r6MpDL+9N+qOS/D5VaxnPaMJ3flwQ36G+vPvYJsXArj93BjgyFB7BwWwXCQDzZ+23cfChPUfhbINOenr8N2Kow==}
|
resolution: {integrity: sha512-r6MpDL+9N+qOS/D5VaxnPaMJ3flwQ36G+vPvYJsXArj93BjgyFB7BwWwXCQDzZ+23cfChPUfhbINOenr8N2Kow==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -867,6 +987,27 @@ packages:
|
|||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@cosmjs/stargate@0.29.3:
|
||||||
|
resolution: {integrity: sha512-455TgXStCi6E8KDjnhDAM8wt6aLSjobH4Dixvd7Up1DfCH6UB9NkC/G0fMJANNcNXMaM4wSX14niTXwD1d31BA==}
|
||||||
|
dependencies:
|
||||||
|
'@confio/ics23': 0.6.8
|
||||||
|
'@cosmjs/amino': 0.29.3
|
||||||
|
'@cosmjs/encoding': 0.29.5
|
||||||
|
'@cosmjs/math': 0.29.5
|
||||||
|
'@cosmjs/proto-signing': 0.29.3
|
||||||
|
'@cosmjs/stream': 0.29.5
|
||||||
|
'@cosmjs/tendermint-rpc': 0.29.5
|
||||||
|
'@cosmjs/utils': 0.29.5
|
||||||
|
cosmjs-types: 0.5.2
|
||||||
|
long: 4.0.0
|
||||||
|
protobufjs: 6.11.4
|
||||||
|
xstream: 11.14.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- bufferutil
|
||||||
|
- debug
|
||||||
|
- utf-8-validate
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@cosmjs/stargate@0.30.1:
|
/@cosmjs/stargate@0.30.1:
|
||||||
resolution: {integrity: sha512-RdbYKZCGOH8gWebO7r6WvNnQMxHrNXInY/gPHPzMjbQF6UatA6fNM2G2tdgS5j5u7FTqlCI10stNXrknaNdzog==}
|
resolution: {integrity: sha512-RdbYKZCGOH8gWebO7r6WvNnQMxHrNXInY/gPHPzMjbQF6UatA6fNM2G2tdgS5j5u7FTqlCI10stNXrknaNdzog==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -909,6 +1050,12 @@ packages:
|
|||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@cosmjs/stream@0.29.5:
|
||||||
|
resolution: {integrity: sha512-TToTDWyH1p05GBtF0Y8jFw2C+4783ueDCmDyxOMM6EU82IqpmIbfwcdMOCAm0JhnyMh+ocdebbFvnX/sGKzRAA==}
|
||||||
|
dependencies:
|
||||||
|
xstream: 11.14.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@cosmjs/stream@0.30.1:
|
/@cosmjs/stream@0.30.1:
|
||||||
resolution: {integrity: sha512-Fg0pWz1zXQdoxQZpdHRMGvUH5RqS6tPv+j9Eh7Q953UjMlrwZVo0YFLC8OTf/HKVf10E4i0u6aM8D69Q6cNkgQ==}
|
resolution: {integrity: sha512-Fg0pWz1zXQdoxQZpdHRMGvUH5RqS6tPv+j9Eh7Q953UjMlrwZVo0YFLC8OTf/HKVf10E4i0u6aM8D69Q6cNkgQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -921,6 +1068,25 @@ packages:
|
|||||||
xstream: 11.14.0
|
xstream: 11.14.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@cosmjs/tendermint-rpc@0.29.5:
|
||||||
|
resolution: {integrity: sha512-ar80twieuAxsy0x2za/aO3kBr2DFPAXDmk2ikDbmkda+qqfXgl35l9CVAAjKRqd9d+cRvbQyb5M4wy6XQpEV6w==}
|
||||||
|
dependencies:
|
||||||
|
'@cosmjs/crypto': 0.29.5
|
||||||
|
'@cosmjs/encoding': 0.29.5
|
||||||
|
'@cosmjs/json-rpc': 0.29.5
|
||||||
|
'@cosmjs/math': 0.29.5
|
||||||
|
'@cosmjs/socket': 0.29.5
|
||||||
|
'@cosmjs/stream': 0.29.5
|
||||||
|
'@cosmjs/utils': 0.29.5
|
||||||
|
axios: 0.21.4
|
||||||
|
readonly-date: 1.0.0
|
||||||
|
xstream: 11.14.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- bufferutil
|
||||||
|
- debug
|
||||||
|
- utf-8-validate
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@cosmjs/tendermint-rpc@0.30.1:
|
/@cosmjs/tendermint-rpc@0.30.1:
|
||||||
resolution: {integrity: sha512-Z3nCwhXSbPZJ++v85zHObeUggrEHVfm1u18ZRwXxFE9ZMl5mXTybnwYhczuYOl7KRskgwlB+rID0WYACxj4wdQ==}
|
resolution: {integrity: sha512-Z3nCwhXSbPZJ++v85zHObeUggrEHVfm1u18ZRwXxFE9ZMl5mXTybnwYhczuYOl7KRskgwlB+rID0WYACxj4wdQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -963,6 +1129,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-VG7QPDiMUzVPxRdJahDV8PXxVdnuAHiIuG56hldV4yPnOz/si/DLNd7VAUUA5923b6jS1Hhev0Hr6AhEkcxBMg==}
|
resolution: {integrity: sha512-VG7QPDiMUzVPxRdJahDV8PXxVdnuAHiIuG56hldV4yPnOz/si/DLNd7VAUUA5923b6jS1Hhev0Hr6AhEkcxBMg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@cosmjs/utils@0.29.5:
|
||||||
|
resolution: {integrity: sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@cosmjs/utils@0.30.1:
|
/@cosmjs/utils@0.30.1:
|
||||||
resolution: {integrity: sha512-KvvX58MGMWh7xA+N+deCfunkA/ZNDvFLw4YbOmX3f/XBIkqrVY7qlotfy2aNb1kgp6h4B6Yc8YawJPDTfvWX7g==}
|
resolution: {integrity: sha512-KvvX58MGMWh7xA+N+deCfunkA/ZNDvFLw4YbOmX3f/XBIkqrVY7qlotfy2aNb1kgp6h4B6Yc8YawJPDTfvWX7g==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -971,29 +1141,22 @@ packages:
|
|||||||
resolution: {integrity: sha512-nNcycZWUYLNJlrIXgpcgVRqdl6BXjF4YlXdxobQWpW9Tikk61bEGeAFhDYtC0PwHlokCNw0KxWiHGJL4nL7Q5A==}
|
resolution: {integrity: sha512-nNcycZWUYLNJlrIXgpcgVRqdl6BXjF4YlXdxobQWpW9Tikk61bEGeAFhDYtC0PwHlokCNw0KxWiHGJL4nL7Q5A==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@cosmjs/utils@0.31.1:
|
|
||||||
resolution: {integrity: sha512-n4Se1wu4GnKwztQHNFfJvUeWcpvx3o8cWhSbNs9JQShEuB3nv3R5lqFBtDCgHZF/emFQAP+ZjF8bTfCs9UBGhA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@cush/relative@1.0.0:
|
/@cush/relative@1.0.0:
|
||||||
resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==}
|
resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@dydxprotocol/v4-abacus@0.5.4:
|
/@dydxprotocol/abacus@0.4.16:
|
||||||
resolution: {integrity: sha512-LMwmyXCih2bBdLX3vO5OwbdGWg3oZ8WSGj+4nqr8tn/bn1qCSKdYdZZ9/U20Yir2rMAcoC0jmznMcBwthlSp/w==}
|
resolution: {integrity: sha512-FTvzgVZ0xjMRmfHwlyoluUSG4Uu4FUHPnaTvLLPl0ER0junmaVDrmBydAUMWMEYGiLyx/k0ln1cLUDrYF2aX6A==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@dydxprotocol/v4-client-js@0.36.1:
|
/@dydxprotocol/v4-client-js@0.33.1:
|
||||||
resolution: {integrity: sha512-KJ3MtWI61wmm+xZtXQd7/hYKIoVFG32TRdyyVaXXO8vpW9Oqd0JD8QSM/wuegkciAh5YmP/BrDzYuu77zHO6yg==}
|
resolution: {integrity: sha512-yFDyiG5aVo4avF5u98yz6BEmWW66SscV2RgmdDwV+LfyKfcubVqKfmv3FtuqE0WKZuzzwiCuFat7YMSK125zyQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@cosmjs/amino': 0.30.1
|
'@cosmjs/amino': 0.30.1
|
||||||
'@cosmjs/encoding': 0.31.1
|
|
||||||
'@cosmjs/math': 0.31.1
|
|
||||||
'@cosmjs/proto-signing': 0.30.1
|
'@cosmjs/proto-signing': 0.30.1
|
||||||
'@cosmjs/stargate': 0.30.1
|
'@cosmjs/stargate': 0.30.1
|
||||||
'@cosmjs/tendermint-rpc': 0.30.1
|
'@cosmjs/tendermint-rpc': 0.30.1
|
||||||
'@cosmjs/utils': 0.31.1
|
'@dydxprotocol/v4-proto': 0.1.2
|
||||||
'@dydxprotocol/v4-proto': 0.2.1
|
|
||||||
'@osmonauts/lcd': 0.6.0
|
'@osmonauts/lcd': 0.6.0
|
||||||
'@scure/bip32': 1.3.1
|
'@scure/bip32': 1.3.1
|
||||||
'@scure/bip39': 1.2.1
|
'@scure/bip39': 1.2.1
|
||||||
@@ -1002,7 +1165,6 @@ packages:
|
|||||||
bignumber.js: 9.1.1
|
bignumber.js: 9.1.1
|
||||||
ethereum-cryptography: 2.1.2
|
ethereum-cryptography: 2.1.2
|
||||||
long: 4.0.0
|
long: 4.0.0
|
||||||
protobufjs: 6.11.4
|
|
||||||
ws: 8.13.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)
|
ws: 8.13.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
@@ -1010,14 +1172,25 @@ packages:
|
|||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@dydxprotocol/v4-localization@0.1.11:
|
/@dydxprotocol/v4-localization@0.0.25:
|
||||||
resolution: {integrity: sha512-eW88t8KJuDT0fNEgaw615+n3gEYxDZYPeHhdBYquVDXjnkhLNznaH8ftmlLPVPyfu7o5aFeU/cw8ZiqRrf/Stw==}
|
resolution: {integrity: sha512-Rp6VHA8z+nwgvTjjB3fvL8gY7bd4DYDHV+NPoL0MFuRm39vhFxGlBVAGDkXPkQTh6YU5BlZV2yXLkDeSfFqEBQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@dydxprotocol/v4-proto@0.2.1:
|
/@dydxprotocol/v4-proto@0.1.2:
|
||||||
resolution: {integrity: sha512-Q5b/g331DoyMjBpO2/jtrxUX3WF5UaIKZp/unK332xuefHNFnkMYtZ05p0V71iSdO35tBdz8yt0VYP2WtUsp+w==}
|
resolution: {integrity: sha512-kcwdVdAexWB1nq/elFKCO4Su2ONQxBCJbN1TMeIAQiIAXxa7CnmYwdbLt4AIIxjgRhu4cHP8F6sGdwft6cjkCA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@babel/runtime': 7.22.10
|
||||||
|
'@bufbuild/buf': 1.19.0-1
|
||||||
|
'@cosmjs/amino': 0.30.1
|
||||||
|
'@cosmjs/proto-signing': 0.30.1
|
||||||
|
'@cosmjs/stargate': 0.30.1
|
||||||
|
'@cosmjs/tendermint-rpc': 0.30.1
|
||||||
|
osmojs: 15.5.0
|
||||||
protobufjs: 6.11.4
|
protobufjs: 6.11.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- bufferutil
|
||||||
|
- debug
|
||||||
|
- utf-8-validate
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@emotion/is-prop-valid@1.2.1:
|
/@emotion/is-prop-valid@1.2.1:
|
||||||
@@ -2073,6 +2246,15 @@ packages:
|
|||||||
fastq: 1.15.0
|
fastq: 1.15.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@osmonauts/lcd@0.10.0:
|
||||||
|
resolution: {integrity: sha512-PzmXk9x9MHyLn2fUztpAqWqvDmMiEJaQv/JcAoAOE8VdHrD9Hf/KWnE1RZtamuS2ngQRqvQPD0xotCGXW7eTxA==}
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.22.10
|
||||||
|
axios: 0.27.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- debug
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@osmonauts/lcd@0.6.0:
|
/@osmonauts/lcd@0.6.0:
|
||||||
resolution: {integrity: sha512-vz9VavXrEfxZoXbSAfNfk90MLpn34XtBYPV3L9YilE+s56AhqYxUh83nne9J5somnTRfGnyR3oeV8C+lHkqiuA==}
|
resolution: {integrity: sha512-vz9VavXrEfxZoXbSAfNfk90MLpn34XtBYPV3L9YilE+s56AhqYxUh83nne9J5somnTRfGnyR3oeV8C+lHkqiuA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -7605,6 +7787,13 @@ packages:
|
|||||||
path-type: 4.0.0
|
path-type: 4.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/cosmjs-types@0.5.2:
|
||||||
|
resolution: {integrity: sha512-zxCtIJj8v3Di7s39uN4LNcN3HIE1z0B9Z0SPE8ZNQR0oSzsuSe1ACgxoFkvhkS7WBasCAFcglS11G2hyfd5tPg==}
|
||||||
|
dependencies:
|
||||||
|
long: 4.0.0
|
||||||
|
protobufjs: 6.11.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
/cosmjs-types@0.7.2:
|
/cosmjs-types@0.7.2:
|
||||||
resolution: {integrity: sha512-vf2uLyktjr/XVAgEq0DjMxeAWh1yYREe7AMHDKd7EiHVqxBPCaBS+qEEQUkXbR9ndnckqr1sUG8BQhazh4X5lA==}
|
resolution: {integrity: sha512-vf2uLyktjr/XVAgEq0DjMxeAWh1yYREe7AMHDKd7EiHVqxBPCaBS+qEEQUkXbR9ndnckqr1sUG8BQhazh4X5lA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -11452,6 +11641,23 @@ packages:
|
|||||||
type-check: 0.4.0
|
type-check: 0.4.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/osmojs@15.5.0:
|
||||||
|
resolution: {integrity: sha512-7wy3QEYu+R+c8imfLEPS1PliYA8uw0qdFYjc2PToHteYG4hzYt4Noa7X0lM9O59s7nxo9ozT1N9z7uwB/1qHkA==}
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.22.10
|
||||||
|
'@cosmjs/amino': 0.29.3
|
||||||
|
'@cosmjs/proto-signing': 0.29.3
|
||||||
|
'@cosmjs/stargate': 0.29.3
|
||||||
|
'@cosmjs/tendermint-rpc': 0.29.5
|
||||||
|
'@osmonauts/lcd': 0.10.0
|
||||||
|
long: 5.2.3
|
||||||
|
protobufjs: 6.11.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- bufferutil
|
||||||
|
- debug
|
||||||
|
- utf-8-validate
|
||||||
|
dev: false
|
||||||
|
|
||||||
/p-defer@3.0.0:
|
/p-defer@3.0.0:
|
||||||
resolution: {integrity: sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw==}
|
resolution: {integrity: sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|||||||
+5
-6
@@ -10,11 +10,11 @@ import { AppRoute, DEFAULT_TRADE_ROUTE } from '@/constants/routes';
|
|||||||
import { useBreakpoints, useInitializePage, useShouldShowFooter, useAnalytics } from '@/hooks';
|
import { useBreakpoints, useInitializePage, useShouldShowFooter, useAnalytics } from '@/hooks';
|
||||||
import { DydxProvider } from '@/hooks/useDydxClient';
|
import { DydxProvider } from '@/hooks/useDydxClient';
|
||||||
import { AccountsProvider } from '@/hooks/useAccounts';
|
import { AccountsProvider } from '@/hooks/useAccounts';
|
||||||
import { DialogAreaProvider, useDialogArea } from '@/hooks/useDialogArea';
|
import { DialogAreaProvider, useDialogArea } from './hooks/useDialogArea';
|
||||||
import { LocaleProvider } from '@/hooks/useLocaleSeparators';
|
import { LocaleProvider } from './hooks/useLocaleSeparators';
|
||||||
import { NotificationsProvider } from '@/hooks/useNotifications';
|
import { NotificationsProvider } from './hooks/useNotifications';
|
||||||
import { LocalNotificationsProvider } from '@/hooks/useLocalNotifications';
|
import { LocalNotificationsProvider } from './hooks/useLocalNotifications';
|
||||||
import { SubaccountProvider } from '@/hooks/useSubaccount';
|
import { SubaccountProvider } from './hooks/useSubaccount';
|
||||||
import { SquidProvider } from '@/hooks/useSquid';
|
import { SquidProvider } from '@/hooks/useSquid';
|
||||||
|
|
||||||
import { GuardedMobileRoute } from '@/components/GuardedMobileRoute';
|
import { GuardedMobileRoute } from '@/components/GuardedMobileRoute';
|
||||||
@@ -100,7 +100,6 @@ const Content = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const wrapProvider = (Component: React.ComponentType<any>, props?: any) => {
|
const wrapProvider = (Component: React.ComponentType<any>, props?: any) => {
|
||||||
// eslint-disable-next-line react/display-name
|
|
||||||
return ({ children }: { children: React.ReactNode }) => (
|
return ({ children }: { children: React.ReactNode }) => (
|
||||||
<Component {...props}>{children}</Component>
|
<Component {...props}>{children}</Component>
|
||||||
);
|
);
|
||||||
|
|||||||
+1
-10
@@ -1,4 +1,4 @@
|
|||||||
import Abacus, { kollections } from '@dydxprotocol/v4-abacus';
|
import Abacus, { kollections } from '@dydxprotocol/abacus';
|
||||||
import { OrderSide } from '@dydxprotocol/v4-client-js';
|
import { OrderSide } from '@dydxprotocol/v4-client-js';
|
||||||
import { PositionSide, TradeTypes } from './trade';
|
import { PositionSide, TradeTypes } from './trade';
|
||||||
import { STRING_KEYS } from './localization';
|
import { STRING_KEYS } from './localization';
|
||||||
@@ -40,10 +40,6 @@ export type AbacusFileSystemProtocol = Omit<
|
|||||||
Abacus.exchange.dydx.abacus.protocols.FileSystemProtocol,
|
Abacus.exchange.dydx.abacus.protocols.FileSystemProtocol,
|
||||||
'__doNotUseOrImplementIt'
|
'__doNotUseOrImplementIt'
|
||||||
>;
|
>;
|
||||||
export type AbacusTrackingProtocol = Omit<
|
|
||||||
Abacus.exchange.dydx.abacus.protocols.TrackingProtocol,
|
|
||||||
'__doNotUseOrImplementIt'
|
|
||||||
>;
|
|
||||||
|
|
||||||
export type FileLocation = Abacus.exchange.dydx.abacus.protocols.FileLocation;
|
export type FileLocation = Abacus.exchange.dydx.abacus.protocols.FileLocation;
|
||||||
export type ThreadingType = Abacus.exchange.dydx.abacus.protocols.ThreadingType;
|
export type ThreadingType = Abacus.exchange.dydx.abacus.protocols.ThreadingType;
|
||||||
@@ -122,7 +118,6 @@ export const InputSelectionOption = Abacus.exchange.dydx.abacus.output.input.Sel
|
|||||||
|
|
||||||
// ------ Wallet ------ //
|
// ------ Wallet ------ //
|
||||||
export type Wallet = Abacus.exchange.dydx.abacus.output.Wallet;
|
export type Wallet = Abacus.exchange.dydx.abacus.output.Wallet;
|
||||||
export type AccountBalance = Abacus.exchange.dydx.abacus.output.AccountBalance;
|
|
||||||
export type Subaccount = Abacus.exchange.dydx.abacus.output.Subaccount;
|
export type Subaccount = Abacus.exchange.dydx.abacus.output.Subaccount;
|
||||||
export type SubaccountPosition = Abacus.exchange.dydx.abacus.output.SubaccountPosition;
|
export type SubaccountPosition = Abacus.exchange.dydx.abacus.output.SubaccountPosition;
|
||||||
export type SubaccountOrder = Abacus.exchange.dydx.abacus.output.SubaccountOrder;
|
export type SubaccountOrder = Abacus.exchange.dydx.abacus.output.SubaccountOrder;
|
||||||
@@ -189,10 +184,6 @@ export type HumanReadablePlaceOrderPayload =
|
|||||||
Abacus.exchange.dydx.abacus.state.manager.HumanReadablePlaceOrderPayload;
|
Abacus.exchange.dydx.abacus.state.manager.HumanReadablePlaceOrderPayload;
|
||||||
export type HumanReadableCancelOrderPayload =
|
export type HumanReadableCancelOrderPayload =
|
||||||
Abacus.exchange.dydx.abacus.state.manager.HumanReadableCancelOrderPayload;
|
Abacus.exchange.dydx.abacus.state.manager.HumanReadableCancelOrderPayload;
|
||||||
export type HumanReadableWithdrawPayload =
|
|
||||||
Abacus.exchange.dydx.abacus.state.manager.HumanReadableWithdrawPayload;
|
|
||||||
export type HumanReadableTransferPayload =
|
|
||||||
Abacus.exchange.dydx.abacus.state.manager.HumanReadableTransferPayload;
|
|
||||||
|
|
||||||
// ------ Helpers ------ //
|
// ------ Helpers ------ //
|
||||||
export const AbacusHelper = Abacus.exchange.dydx.abacus.utils.AbacusHelper;
|
export const AbacusHelper = Abacus.exchange.dydx.abacus.utils.AbacusHelper;
|
||||||
|
|||||||
@@ -1,5 +1,18 @@
|
|||||||
import type { DydxAddress, EvmAddress } from './wallets';
|
/**
|
||||||
|
* OnboardingSteps
|
||||||
|
* 1. Choose between 3 options
|
||||||
|
* a. Ethereum EOA (current)
|
||||||
|
* b. Keplr or Other Cosmos (future)
|
||||||
|
* c. Social (future)
|
||||||
|
* 2. Key derivation
|
||||||
|
* a. If wallet has no dYdX Chain transactions and not on whitelist, sign twice (future)
|
||||||
|
* i. Success
|
||||||
|
* ii. Signatures don't match error (Wallet is non-deterministic)
|
||||||
|
* b. Success
|
||||||
|
* 3. Post Registration items (Only on first onboarding)
|
||||||
|
* a. Acknowledge Terms
|
||||||
|
* b. DepositFunds
|
||||||
|
*/
|
||||||
export enum OnboardingSteps {
|
export enum OnboardingSteps {
|
||||||
ChooseWallet = 'ChooseWallet',
|
ChooseWallet = 'ChooseWallet',
|
||||||
KeyDerivation = 'KeyDerivation',
|
KeyDerivation = 'KeyDerivation',
|
||||||
@@ -36,12 +49,14 @@ export enum EvmDerivedAccountStatus {
|
|||||||
Derived,
|
Derived,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import type { DydxAddress, EvmAddress } from './wallets';
|
||||||
|
|
||||||
export type EvmDerivedAddresses = {
|
export type EvmDerivedAddresses = {
|
||||||
version?: string;
|
version?: string;
|
||||||
[EvmAddress: EvmAddress]: {
|
[EvmAddress: EvmAddress]: {
|
||||||
encryptedSignature?: string;
|
encryptedSignature?: string;
|
||||||
dydxAddress?: DydxAddress;
|
dydxAddress?: DydxAddress;
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export const AMOUNT_RESERVED_FOR_GAS_USDC = 100_000;
|
export const AMOUNT_RESERVED_FOR_GAS_USDC = 100_000;
|
||||||
@@ -71,7 +71,6 @@ export enum AnalyticsEvent {
|
|||||||
|
|
||||||
// Transfers
|
// Transfers
|
||||||
TransferFaucet = 'TransferFaucet',
|
TransferFaucet = 'TransferFaucet',
|
||||||
TransferFaucetConfirmed = 'TransferFaucetConfirmed',
|
|
||||||
TransferDeposit = 'TransferDeposit',
|
TransferDeposit = 'TransferDeposit',
|
||||||
TransferWithdraw = 'TransferWithdraw',
|
TransferWithdraw = 'TransferWithdraw',
|
||||||
|
|
||||||
@@ -79,7 +78,6 @@ export enum AnalyticsEvent {
|
|||||||
TradeOrderTypeSelected = 'TradeOrderTypeSelected',
|
TradeOrderTypeSelected = 'TradeOrderTypeSelected',
|
||||||
TradePlaceOrder = 'TradePlaceOrder',
|
TradePlaceOrder = 'TradePlaceOrder',
|
||||||
TradePlaceOrderConfirmed = 'TradePlaceOrderConfirmed',
|
TradePlaceOrderConfirmed = 'TradePlaceOrderConfirmed',
|
||||||
TradeCancelOrder = 'TradeCancelOrder',
|
|
||||||
TradeCancelOrderConfirmed = 'TradeCancelOrderConfirmed',
|
TradeCancelOrderConfirmed = 'TradeCancelOrderConfirmed',
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,13 +133,6 @@ export type AnalyticsEventData<T extends AnalyticsEvent> =
|
|||||||
: // Transfers
|
: // Transfers
|
||||||
T extends AnalyticsEvent.TransferFaucet
|
T extends AnalyticsEvent.TransferFaucet
|
||||||
? {}
|
? {}
|
||||||
: T extends AnalyticsEvent.TransferFaucetConfirmed
|
|
||||||
? {
|
|
||||||
/** roundtrip time between user placing an order and confirmation from indexer (client → validator → indexer → client) */
|
|
||||||
roundtripMs: number;
|
|
||||||
/** URL/IP of node the order was sent to */
|
|
||||||
validatorUrl: string;
|
|
||||||
}
|
|
||||||
: T extends AnalyticsEvent.TransferDeposit
|
: T extends AnalyticsEvent.TransferDeposit
|
||||||
? {}
|
? {}
|
||||||
: T extends AnalyticsEvent.TransferWithdraw
|
: T extends AnalyticsEvent.TransferWithdraw
|
||||||
@@ -160,15 +151,13 @@ export type AnalyticsEventData<T extends AnalyticsEvent> =
|
|||||||
/** roundtrip time between user placing an order and confirmation from indexer (client → validator → indexer → client) */
|
/** roundtrip time between user placing an order and confirmation from indexer (client → validator → indexer → client) */
|
||||||
roundtripMs: number;
|
roundtripMs: number;
|
||||||
/** URL/IP of node the order was sent to */
|
/** URL/IP of node the order was sent to */
|
||||||
validatorUrl: string;
|
validator: string;
|
||||||
}
|
}
|
||||||
: T extends AnalyticsEvent.TradeCancelOrder
|
|
||||||
? {}
|
|
||||||
: T extends AnalyticsEvent.TradeCancelOrderConfirmed
|
: T extends AnalyticsEvent.TradeCancelOrderConfirmed
|
||||||
? {
|
? {
|
||||||
/** roundtrip time between user canceling an order and confirmation from indexer (client → validator → indexer → client) */
|
/** roundtrip time between user canceling an order and confirmation from indexer (client → validator → indexer → client) */
|
||||||
roundtripMs: number;
|
roundtripMs: number;
|
||||||
/** URL/IP of node the order was sent to */
|
/** URL/IP of node the order was sent to */
|
||||||
validatorUrl: string;
|
validator: string;
|
||||||
}
|
}
|
||||||
: never;
|
: never;
|
||||||
|
|||||||
@@ -88,3 +88,19 @@ export type TooltipStrings = {
|
|||||||
learnMoreLink?: string;
|
learnMoreLink?: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ORDER_ERROR_CODE_MAP: { [key: number]: string } = {
|
||||||
|
2000: STRING_KEYS['2000_FILL_OR_KILL_ORDER_COULD_NOT_BE_FULLY_FILLED'],
|
||||||
|
2001: STRING_KEYS['2001_REDUCE_ONLY_WOULD_INCREASE_POSITION_SIZE'],
|
||||||
|
2002: STRING_KEYS['2002_REDUCE_ONLY_WOULD_CHANGE_POSITION_SIDE'],
|
||||||
|
2003: STRING_KEYS['2003_POST_ONLY_WOULD_CROSS_MAKER_ORDER'],
|
||||||
|
3000: STRING_KEYS['3000_INVALID_ORDER_FLAGS'],
|
||||||
|
3001: STRING_KEYS['3001_INVALID_STATEFUL_ORDER_GOOD_TIL_BLOCK_TIME'],
|
||||||
|
3002: STRING_KEYS['3002_STATEFUL_ORDERS_CANNOT_REQUIRE_IMMEDIATE_EXECUTION'],
|
||||||
|
3003: STRING_KEYS['3003_TIME_EXCEEDS_GOOD_TIL_BLOCK_TIME'],
|
||||||
|
3004: STRING_KEYS['3004_GOOD_TIL_BLOCK_TIME_EXCEEDS_STATEFUL_ORDER_TIME_WINDOW'],
|
||||||
|
3005: STRING_KEYS['3005_STATEFUL_ORDER_ALREADY_EXISTS'],
|
||||||
|
3006: STRING_KEYS['3006_STATEFUL_ORDER_DOES_NOT_EXIST'],
|
||||||
|
3007: STRING_KEYS['3007_STATEFUL_ORDER_COLLATERALIZATION_CHECK_FAILED'],
|
||||||
|
3008: STRING_KEYS['3008_STATEFUL_ORDER_PREVIOUSLY_CANCELLED'],
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { StatusResponse } from '@0xsquid/sdk';
|
import { StatusResponse } from "@0xsquid/sdk";
|
||||||
|
|
||||||
/** implemented in useNotificationTypes */
|
/** implemented in useNotificationTypes */
|
||||||
export enum NotificationType {
|
export enum NotificationType {
|
||||||
@@ -11,7 +11,7 @@ export enum NotificationComponentType {}
|
|||||||
export type NotificationId = string | number;
|
export type NotificationId = string | number;
|
||||||
|
|
||||||
export type NotificationTypeConfig<
|
export type NotificationTypeConfig<
|
||||||
NotificationIdType extends NotificationId = string,
|
_NotificationId extends NotificationId = string,
|
||||||
NotificationUpdateKey = any
|
NotificationUpdateKey = any
|
||||||
> = {
|
> = {
|
||||||
type: NotificationType;
|
type: NotificationType;
|
||||||
@@ -20,7 +20,7 @@ export type NotificationTypeConfig<
|
|||||||
useTrigger: (_: {
|
useTrigger: (_: {
|
||||||
trigger: (
|
trigger: (
|
||||||
/** Unique ID for the triggered notification */
|
/** Unique ID for the triggered notification */
|
||||||
id: NotificationIdType,
|
id: _NotificationId,
|
||||||
|
|
||||||
/** Display data for the triggered notification */
|
/** Display data for the triggered notification */
|
||||||
displayData: NotificationDisplayData,
|
displayData: NotificationDisplayData,
|
||||||
@@ -43,7 +43,7 @@ export type NotificationTypeConfig<
|
|||||||
}) => void;
|
}) => void;
|
||||||
|
|
||||||
/** Callback for notification action (Toast action button click, NotificationsMenu item click, or native push notification interaction) */
|
/** Callback for notification action (Toast action button click, NotificationsMenu item click, or native push notification interaction) */
|
||||||
useNotificationAction?: () => (id: NotificationIdType) => any;
|
useNotificationAction?: () => (id: _NotificationId) => any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum NotificationStatus {
|
export enum NotificationStatus {
|
||||||
@@ -65,10 +65,10 @@ export enum NotificationStatus {
|
|||||||
|
|
||||||
/** Notification state. Serialized and cached into localStorage. */
|
/** Notification state. Serialized and cached into localStorage. */
|
||||||
export type Notification<
|
export type Notification<
|
||||||
NotificationIdType extends NotificationId = string,
|
_NotificationId extends NotificationId = string,
|
||||||
NotificationUpdateKey = any
|
NotificationUpdateKey = any
|
||||||
> = {
|
> = {
|
||||||
id: NotificationIdType;
|
id: _NotificationId;
|
||||||
type: NotificationType;
|
type: NotificationType;
|
||||||
status: NotificationStatus;
|
status: NotificationStatus;
|
||||||
timestamps: Partial<Record<NotificationStatus, number>>;
|
timestamps: Partial<Record<NotificationStatus, number>>;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { getSeparator } from '@/lib/numbers';
|
||||||
|
|
||||||
export const USD_DECIMALS = 2;
|
export const USD_DECIMALS = 2;
|
||||||
export const SMALL_USD_DECIMALS = 4;
|
export const SMALL_USD_DECIMALS = 4;
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,6 @@ export const tradeTooltips: TooltipStrings = {
|
|||||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.ACCOUNT_LEVERAGE_TITLE }),
|
title: stringGetter({ key: TOOLTIP_STRING_KEYS.ACCOUNT_LEVERAGE_TITLE }),
|
||||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.ACCOUNT_LEVERAGE_BODY }),
|
body: stringGetter({ key: TOOLTIP_STRING_KEYS.ACCOUNT_LEVERAGE_BODY }),
|
||||||
}),
|
}),
|
||||||
'base-position-notional': ({ stringGetter }) => ({
|
|
||||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.BASE_POSITION_NOTIONAL_TITLE }),
|
|
||||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.BASE_POSITION_NOTIONAL_BODY }),
|
|
||||||
}),
|
|
||||||
'bracket-sl': ({ stringGetter }) => ({
|
'bracket-sl': ({ stringGetter }) => ({
|
||||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.BRACKET_ORDER_SL_TITLE }),
|
title: stringGetter({ key: TOOLTIP_STRING_KEYS.BRACKET_ORDER_SL_TITLE }),
|
||||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.BRACKET_ORDER_SL_BODY }),
|
body: stringGetter({ key: TOOLTIP_STRING_KEYS.BRACKET_ORDER_SL_BODY }),
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export enum PositionSide {
|
|||||||
Short = 'SHORT',
|
Short = 'SHORT',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UNCOMMITTED_ORDER_TIMEOUT_MS = 10_000;
|
export const UNCOMMITTED_ORDER_TIMEOUT = 10_000;
|
||||||
|
|
||||||
export const ORDER_SIDE_STRINGS = {
|
export const ORDER_SIDE_STRINGS = {
|
||||||
[OrderSide.BUY]: STRING_KEYS.BUY,
|
[OrderSide.BUY]: STRING_KEYS.BUY,
|
||||||
|
|||||||
+10
-14
@@ -1,6 +1,4 @@
|
|||||||
import type { ExternalProvider } from '@ethersproject/providers';
|
import type { ExternalProvider } from '@ethersproject/providers';
|
||||||
import { USDC_DENOM, type onboarding, DYDX_DENOM } from '@dydxprotocol/v4-client-js';
|
|
||||||
import type { suggestChain } from 'graz';
|
|
||||||
|
|
||||||
import { STRING_KEYS } from '@/constants/localization';
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
|
|
||||||
@@ -315,6 +313,10 @@ export type WalletConnection = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// dYdX Chain wallets
|
// dYdX Chain wallets
|
||||||
|
|
||||||
|
import { USDC_DENOM, type onboarding, DYDX_DENOM } from '@dydxprotocol/v4-client-js';
|
||||||
|
import type { suggestChain } from 'graz';
|
||||||
|
|
||||||
export const COSMOS_DERIVATION_PATH = "m/44'/118'/0'/0/0";
|
export const COSMOS_DERIVATION_PATH = "m/44'/118'/0'/0/0";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -339,12 +341,12 @@ export type EvmAddress = `0x${string}`;
|
|||||||
export type DydxAddress = `dydx${string}`;
|
export type DydxAddress = `dydx${string}`;
|
||||||
|
|
||||||
export const DYDX_CHAIN_INFO: Parameters<typeof suggestChain>[0] = {
|
export const DYDX_CHAIN_INFO: Parameters<typeof suggestChain>[0] = {
|
||||||
rpc: 'https://dydx-testnet-archive.allthatnode.com:26657',
|
rpc: '13.59.4.93:26657',
|
||||||
rest: 'https://dydx-testnet-archive.allthatnode.com:1317',
|
rest: '13.59.4.93:1317',
|
||||||
chainId: 'dydx-testnet-3',
|
chainId: 'dydx-testnet-2',
|
||||||
chainName: 'dYdX Public Testnet',
|
chainName: 'dYdX Public Testnet',
|
||||||
chainSymbolImageUrl:
|
chainSymbolImageUrl:
|
||||||
'https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/dydx-testnet-3/chain.png',
|
'https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/dydx-testnet-2/chain.png',
|
||||||
bech32Config: {
|
bech32Config: {
|
||||||
bech32PrefixAccPub: 'dydxpub',
|
bech32PrefixAccPub: 'dydxpub',
|
||||||
bech32PrefixValPub: 'dydxvaloperpub',
|
bech32PrefixValPub: 'dydxvaloperpub',
|
||||||
@@ -383,18 +385,12 @@ export const DYDX_CHAIN_INFO: Parameters<typeof suggestChain>[0] = {
|
|||||||
features: [],
|
features: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: export this type from abacus instead
|
|
||||||
export enum DydxChainAsset {
|
export enum DydxChainAsset {
|
||||||
USDC = 'usdc',
|
USDC = 'USDC',
|
||||||
DYDX = 'dydx',
|
DYDX = 'Dv4TNT',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DYDX_CHAIN_ASSET_COIN_DENOM: Record<DydxChainAsset, string> = {
|
export const DYDX_CHAIN_ASSET_COIN_DENOM: Record<DydxChainAsset, string> = {
|
||||||
[DydxChainAsset.USDC]: USDC_DENOM,
|
[DydxChainAsset.USDC]: USDC_DENOM,
|
||||||
[DydxChainAsset.DYDX]: DYDX_DENOM,
|
[DydxChainAsset.DYDX]: DYDX_DENOM,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DYDX_CHAIN_ASSET_TAGS: Record<DydxChainAsset, string> = {
|
|
||||||
[DydxChainAsset.USDC]: 'USDC',
|
|
||||||
[DydxChainAsset.DYDX]: 'Dv4TNT',
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { shallowEqual, useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { useBalance } from 'wagmi';
|
import { useBalance } from 'wagmi';
|
||||||
import { StargateClient } from '@cosmjs/stargate';
|
import { StargateClient } from '@cosmjs/stargate';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
import { formatUnits } from 'viem';
|
import { formatUnits } from 'viem';
|
||||||
|
|
||||||
import { USDC_DENOM, DYDX_DENOM } from '@dydxprotocol/v4-client-js';
|
|
||||||
|
|
||||||
import { CLIENT_NETWORK_CONFIGS } from '@/constants/networks';
|
import { CLIENT_NETWORK_CONFIGS } from '@/constants/networks';
|
||||||
import { QUANTUM_MULTIPLIER } from '@/constants/numbers';
|
import { QUANTUM_MULTIPLIER } from '@/constants/numbers';
|
||||||
import { EvmAddress } from '@/constants/wallets';
|
import { EvmAddress } from '@/constants/wallets';
|
||||||
@@ -14,10 +12,11 @@ import { EvmAddress } from '@/constants/wallets';
|
|||||||
import { convertBech32Address } from '@/lib/addressUtils';
|
import { convertBech32Address } from '@/lib/addressUtils';
|
||||||
import { MustBigNumber } from '@/lib/numbers';
|
import { MustBigNumber } from '@/lib/numbers';
|
||||||
|
|
||||||
import { getBalances } from '@/state/accountSelectors';
|
|
||||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||||
|
|
||||||
import { useAccounts } from './useAccounts';
|
import { useAccounts } from './useAccounts';
|
||||||
|
import { usePollNativeTokenBalance } from './usePollNativeTokenBalance';
|
||||||
|
import { usePollUSDCBalance } from './usePollUSDCBalance';
|
||||||
|
|
||||||
type UseAccountBalanceProps = {
|
type UseAccountBalanceProps = {
|
||||||
// Token Items
|
// Token Items
|
||||||
@@ -51,7 +50,6 @@ export const useAccountBalance = ({
|
|||||||
const { evmAddress, dydxAddress } = useAccounts();
|
const { evmAddress, dydxAddress } = useAccounts();
|
||||||
|
|
||||||
const selectedNetwork = useSelector(getSelectedNetwork);
|
const selectedNetwork = useSelector(getSelectedNetwork);
|
||||||
const balances = useSelector(getBalances, shallowEqual);
|
|
||||||
const evmChainId = Number(CLIENT_NETWORK_CONFIGS[selectedNetwork].ethereumChainId);
|
const evmChainId = Number(CLIENT_NETWORK_CONFIGS[selectedNetwork].ethereumChainId);
|
||||||
|
|
||||||
const evmQuery = useBalance({
|
const evmQuery = useBalance({
|
||||||
@@ -94,12 +92,12 @@ export const useAccountBalance = ({
|
|||||||
const { formatted: evmBalance } = evmQuery.data || {};
|
const { formatted: evmBalance } = evmQuery.data || {};
|
||||||
const balance = !assetSymbol ? '0' : isCosmosChain ? cosmosQuery.data : evmBalance;
|
const balance = !assetSymbol ? '0' : isCosmosChain ? cosmosQuery.data : evmBalance;
|
||||||
|
|
||||||
const nativeTokenCoinBalance = balances?.[DYDX_DENOM];
|
const nativeTokenCoinBalance = usePollNativeTokenBalance({ dydxAddress });
|
||||||
const nativeTokenBalance = MustBigNumber(nativeTokenCoinBalance?.amount)
|
const nativeTokenBalance = MustBigNumber(nativeTokenCoinBalance?.amount)
|
||||||
.div(QUANTUM_MULTIPLIER)
|
.div(QUANTUM_MULTIPLIER)
|
||||||
.toNumber();
|
.toNumber();
|
||||||
|
|
||||||
const usdcCoinBalance = balances?.[USDC_DENOM];
|
const usdcCoinBalance = usePollUSDCBalance({ dydxAddress });
|
||||||
const usdcBalance = MustBigNumber(usdcCoinBalance?.amount).div(QUANTUM_MULTIPLIER).toNumber();
|
const usdcBalance = MustBigNumber(usdcCoinBalance?.amount).div(QUANTUM_MULTIPLIER).toNumber();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ import { OnboardingGuard, OnboardingState, type EvmDerivedAddresses } from '@/co
|
|||||||
import { LocalStorageKey, LOCAL_STORAGE_VERSIONS } from '@/constants/localStorage';
|
import { LocalStorageKey, LOCAL_STORAGE_VERSIONS } from '@/constants/localStorage';
|
||||||
import { DydxAddress, EvmAddress, PrivateInformation } from '@/constants/wallets';
|
import { DydxAddress, EvmAddress, PrivateInformation } from '@/constants/wallets';
|
||||||
|
|
||||||
import { setOnboardingState, setOnboardingGuard } from '@/state/account';
|
import {
|
||||||
|
setOnboardingState,
|
||||||
|
setOnboardingGuard,
|
||||||
|
} from '@/state/account';
|
||||||
|
|
||||||
import abacusStateManager from '@/lib/abacus';
|
import abacusStateManager from '@/lib/abacus';
|
||||||
import { log } from '@/lib/telemetry';
|
import { log } from '@/lib/telemetry';
|
||||||
@@ -219,9 +222,9 @@ const useAccountsContext = () => {
|
|||||||
// abacus
|
// abacus
|
||||||
// TODO: useAbacus({ dydxAddress })
|
// TODO: useAbacus({ dydxAddress })
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dydxAddress) abacusStateManager.setAccount(localDydxWallet);
|
if (dydxAddress) abacusStateManager.setAccount(dydxAddress);
|
||||||
else abacusStateManager.attemptDisconnectAccount();
|
else abacusStateManager.attemptDisconnectAccount();
|
||||||
}, [localDydxWallet]);
|
}, [dydxAddress]);
|
||||||
|
|
||||||
// clear subaccounts when no dydxAddress is set
|
// clear subaccounts when no dydxAddress is set
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ const useDydxClientContext = () => {
|
|||||||
}): Promise<Candle[]> => {
|
}): Promise<Candle[]> => {
|
||||||
try {
|
try {
|
||||||
const { candles } =
|
const { candles } =
|
||||||
(await compositeClient?.indexerClient.markets.getPerpetualMarketCandles(
|
(await compositeClient!.indexerClient.markets.getPerpetualMarketCandles(
|
||||||
marketId,
|
marketId,
|
||||||
RESOLUTION_MAP[resolution],
|
RESOLUTION_MAP[resolution],
|
||||||
fromIso,
|
fromIso,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { LocalStorageKey } from '@/constants/localStorage';
|
|||||||
import { type TransferNotifcation } from '@/constants/notifications';
|
import { type TransferNotifcation } from '@/constants/notifications';
|
||||||
|
|
||||||
import { useAccounts } from '@/hooks/useAccounts';
|
import { useAccounts } from '@/hooks/useAccounts';
|
||||||
import { SQUID_ERROR_TYPES, useSquid } from '@/hooks/useSquid';
|
import { useSquid } from '@/hooks/useSquid';
|
||||||
import { useLocalStorage } from './useLocalStorage';
|
import { useLocalStorage } from './useLocalStorage';
|
||||||
|
|
||||||
const LocalNotificationsContext = createContext<
|
const LocalNotificationsContext = createContext<
|
||||||
@@ -66,14 +66,11 @@ const useLocalNotificationsContext = () => {
|
|||||||
} of transferNotifications) {
|
} of transferNotifications) {
|
||||||
try {
|
try {
|
||||||
if (currentStatus && currentStatus?.squidTransactionStatus !== 'ongoing') continue;
|
if (currentStatus && currentStatus?.squidTransactionStatus !== 'ongoing') continue;
|
||||||
|
|
||||||
const status = await squid?.getStatus({ transactionId: txHash, toChainId, fromChainId });
|
const status = await squid?.getStatus({ transactionId: txHash, toChainId, fromChainId });
|
||||||
if (status) statuses[txHash] = status;
|
if (status) statuses[txHash] = status;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// ignore not found errors since the route might not be available yet
|
console.error(error);
|
||||||
if (error?.errors?.length && error.errors[0].errorType !== SQUID_ERROR_TYPES.NotFoundError) {
|
|
||||||
statuses[txHash] = error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return statuses;
|
return statuses;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const useMarketsData = (
|
|||||||
|
|
||||||
const markets = useMemo(() => {
|
const markets = useMemo(() => {
|
||||||
return Object.values(allPerpetualMarkets)
|
return Object.values(allPerpetualMarkets)
|
||||||
.filter((market) => allAssets[market?.assetId]) // Filter out markets with no asset information
|
.filter(({ assetId }) => allAssets[assetId]) // Filter out markets with no asset information
|
||||||
.map((marketData) => ({
|
.map((marketData) => ({
|
||||||
asset: allAssets[marketData.assetId],
|
asset: allAssets[marketData.assetId],
|
||||||
tickSizeDecimals: marketData.configs?.tickSizeDecimals,
|
tickSizeDecimals: marketData.configs?.tickSizeDecimals,
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import { useCallback, useEffect, useMemo } from 'react';
|
import { useCallback, useEffect, useMemo } from 'react';
|
||||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
|
||||||
import { useSelector, shallowEqual, useDispatch } from 'react-redux';
|
import { useSelector, shallowEqual, useDispatch } from 'react-redux';
|
||||||
import { groupBy } from 'lodash';
|
import { groupBy } from 'lodash';
|
||||||
|
|
||||||
import { AlertType } from '@/constants/alerts';
|
|
||||||
import { AbacusOrderStatus, ORDER_SIDES, ORDER_STATUS_STRINGS } from '@/constants/abacus';
|
import { AbacusOrderStatus, ORDER_SIDES, ORDER_STATUS_STRINGS } from '@/constants/abacus';
|
||||||
import { DialogTypes } from '@/constants/dialogs';
|
import { DialogTypes } from '@/constants/dialogs';
|
||||||
import { STRING_KEYS } from '@/constants/localization';
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
@@ -12,7 +10,6 @@ import { ORDER_SIDE_STRINGS, TRADE_TYPE_STRINGS, TradeTypes } from '@/constants/
|
|||||||
|
|
||||||
import { useLocalNotifications } from '@/hooks/useLocalNotifications';
|
import { useLocalNotifications } from '@/hooks/useLocalNotifications';
|
||||||
|
|
||||||
import { AlertMessage } from '@/components/AlertMessage';
|
|
||||||
import { Icon, IconName } from '@/components/Icon';
|
import { Icon, IconName } from '@/components/Icon';
|
||||||
import { Output, OutputType } from '@/components/Output';
|
import { Output, OutputType } from '@/components/Output';
|
||||||
import { TransferStatusToast } from '@/views/TransferStatus';
|
import { TransferStatusToast } from '@/views/TransferStatus';
|
||||||
@@ -122,36 +119,19 @@ export const notificationTypes = [
|
|||||||
const { toChainId, status, txHash, toAmount } = transfer;
|
const { toChainId, status, txHash, toAmount } = transfer;
|
||||||
const finished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing';
|
const finished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing';
|
||||||
const type = toChainId === TESTNET_CHAIN_ID ? 'deposit' : 'withdraw';
|
const type = toChainId === TESTNET_CHAIN_ID ? 'deposit' : 'withdraw';
|
||||||
// @ts-ignore status.errors is not in the type definition but can be returned
|
|
||||||
const error = status?.errors?.length ? status?.errors[0] : status?.error;
|
|
||||||
|
|
||||||
// TODO: confirm with design what the description should be
|
|
||||||
const description = (
|
|
||||||
<div>
|
|
||||||
<Styled.TransferText>
|
|
||||||
{type === 'deposit' ? 'Deposit of ' : 'Withdraw of '}
|
|
||||||
<Output type={OutputType.Fiat} value={toAmount} />
|
|
||||||
</Styled.TransferText>
|
|
||||||
|
|
||||||
{error && (
|
|
||||||
<Styled.ErrorMessage type={AlertType.Error}>
|
|
||||||
{stringGetter({
|
|
||||||
key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE,
|
|
||||||
params: {
|
|
||||||
ERROR_MESSAGE: error.message || stringGetter({ key: STRING_KEYS.UNKNOWN_ERROR }),
|
|
||||||
},
|
|
||||||
})}
|
|
||||||
</Styled.ErrorMessage>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
trigger(
|
trigger(
|
||||||
txHash,
|
txHash,
|
||||||
{
|
{
|
||||||
icon: <Icon iconName={finished ? IconName.Transfer : IconName.Clock} />,
|
icon: <Icon iconName={finished ? IconName.Transfer : IconName.Clock} />,
|
||||||
title: stringGetter({ key: getTitleStringKey(type, finished) }),
|
title: stringGetter({ key: getTitleStringKey(type, finished) }),
|
||||||
description: description,
|
// TODO: confirm with design what the description should be
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
<span>{type === 'deposit' ? 'Deposit of ' : 'Withdraw of'}</span>
|
||||||
|
<Output type={OutputType.Fiat} value={toAmount} />
|
||||||
|
</>
|
||||||
|
),
|
||||||
customContent: (
|
customContent: (
|
||||||
<TransferStatusToast
|
<TransferStatusToast
|
||||||
toAmount={transfer.toAmount}
|
toAmount={transfer.toAmount}
|
||||||
@@ -159,12 +139,7 @@ export const notificationTypes = [
|
|||||||
status={transfer.status}
|
status={transfer.status}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
customMenuContent: !finished && (
|
customMenuContent: !finished && <TransferStatusSteps status={transfer.status} />,
|
||||||
<div>
|
|
||||||
{description}
|
|
||||||
<TransferStatusSteps status={transfer.status} />
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
toastSensitivity: 'foreground',
|
toastSensitivity: 'foreground',
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
@@ -174,15 +149,3 @@ export const notificationTypes = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
] satisfies NotificationTypeConfig[];
|
] satisfies NotificationTypeConfig[];
|
||||||
|
|
||||||
const Styled: Record<string, AnyStyledComponent> = {};
|
|
||||||
|
|
||||||
Styled.TransferText = styled.span`
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5ch;
|
|
||||||
`
|
|
||||||
|
|
||||||
Styled.ErrorMessage = styled.div`
|
|
||||||
max-width: 13rem;
|
|
||||||
`;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
|
|
||||||
import { getLatestOrderClientId } from '@/state/accountSelectors';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description This hook will fire a callback when the latest order has been updated to a non-pending state from the Indexer.
|
|
||||||
* @param { callback: () => void }
|
|
||||||
*/
|
|
||||||
export const useOnLastOrderIndexed = ({ callback }: { callback: () => void }) => {
|
|
||||||
const [unIndexedClientId, setUnIndexedClientId] = useState<number | undefined>();
|
|
||||||
const latestOrderClientId = useSelector(getLatestOrderClientId);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (unIndexedClientId) {
|
|
||||||
if (unIndexedClientId === latestOrderClientId) {
|
|
||||||
callback();
|
|
||||||
setUnIndexedClientId(undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [callback, latestOrderClientId, unIndexedClientId]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
setUnIndexedClientId,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
|
import { useAccounts } from '@/hooks';
|
||||||
|
|
||||||
|
import { DydxAddress } from '@/constants/wallets';
|
||||||
|
import { DYDX_DENOM } from '@dydxprotocol/v4-client-js';
|
||||||
|
|
||||||
|
const ACCOUNT_BALANCE_POLLING_INTERVAL = 60_000;
|
||||||
|
|
||||||
|
export const usePollNativeTokenBalance = ({
|
||||||
|
dydxAddress,
|
||||||
|
interval = ACCOUNT_BALANCE_POLLING_INTERVAL,
|
||||||
|
}: {
|
||||||
|
dydxAddress?: DydxAddress;
|
||||||
|
interval?: number;
|
||||||
|
}) => {
|
||||||
|
const { getAccountBalance } = useAccounts();
|
||||||
|
|
||||||
|
const { data } = useQuery({
|
||||||
|
enabled: dydxAddress !== undefined,
|
||||||
|
queryKey: ['usePollNativeTokenBalance', { dydxAddress }],
|
||||||
|
queryFn: async () => {
|
||||||
|
if (!dydxAddress) return;
|
||||||
|
return await getAccountBalance({ dydxAddress, denom: DYDX_DENOM });
|
||||||
|
},
|
||||||
|
refetchInterval: interval,
|
||||||
|
staleTime: interval,
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
|
import { useAccounts } from '@/hooks';
|
||||||
|
|
||||||
|
import { DydxAddress } from '@/constants/wallets';
|
||||||
|
|
||||||
|
const ACCOUNT_BALANCE_POLLING_INTERVAL = 60_000;
|
||||||
|
|
||||||
|
export const usePollUSDCBalance = ({
|
||||||
|
dydxAddress,
|
||||||
|
interval = ACCOUNT_BALANCE_POLLING_INTERVAL,
|
||||||
|
}: {
|
||||||
|
dydxAddress?: DydxAddress;
|
||||||
|
interval?: number;
|
||||||
|
}) => {
|
||||||
|
const { getAccountBalance } = useAccounts();
|
||||||
|
|
||||||
|
const { data } = useQuery({
|
||||||
|
enabled: dydxAddress !== undefined,
|
||||||
|
queryKey: ['usePollUSDCBalance', { dydxAddress }],
|
||||||
|
queryFn: async () => {
|
||||||
|
if (!dydxAddress) return;
|
||||||
|
return await getAccountBalance({ dydxAddress });
|
||||||
|
},
|
||||||
|
refetchInterval: interval,
|
||||||
|
staleTime: interval,
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
@@ -1,17 +1,14 @@
|
|||||||
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
|
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client-js';
|
||||||
import { Squid } from '@0xsquid/sdk';
|
import { Squid } from '@0xsquid/sdk';
|
||||||
|
|
||||||
import { CLIENT_NETWORK_CONFIGS, isDydxV4Network } from '@/constants/networks';
|
import { CLIENT_NETWORK_CONFIGS, DydxV4Network, isDydxV4Network } from '@/constants/networks';
|
||||||
|
|
||||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||||
|
|
||||||
export const NATIVE_TOKEN_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
export const NATIVE_TOKEN_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
||||||
|
|
||||||
export enum SQUID_ERROR_TYPES {
|
|
||||||
NotFoundError = 'NotFoundError',
|
|
||||||
}
|
|
||||||
|
|
||||||
const useSquidContext = () => {
|
const useSquidContext = () => {
|
||||||
const selectedNetwork = useSelector(getSelectedNetwork);
|
const selectedNetwork = useSelector(getSelectedNetwork);
|
||||||
const [_, setInitialized] = useState(false);
|
const [_, setInitialized] = useState(false);
|
||||||
|
|||||||
+332
-57
@@ -1,41 +1,53 @@
|
|||||||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||||
import { shallowEqual, useSelector, useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import type { Nullable } from '@dydxprotocol/v4-abacus';
|
import type { Nullable } from '@dydxprotocol/abacus';
|
||||||
import Long from 'long';
|
import Long from 'long';
|
||||||
import type { IndexedTx } from '@cosmjs/stargate';
|
import type { IndexedTx } from '@cosmjs/stargate';
|
||||||
import type { EncodeObject, Coin } from '@cosmjs/proto-signing';
|
import type { EncodeObject, Coin } from '@cosmjs/proto-signing';
|
||||||
import { Method } from '@cosmjs/tendermint-rpc';
|
import { Method } from '@cosmjs/tendermint-rpc';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type LocalWallet,
|
LocalWallet,
|
||||||
|
OrderExecution,
|
||||||
|
OrderFlags,
|
||||||
|
OrderSide,
|
||||||
|
OrderTimeInForce,
|
||||||
|
OrderType,
|
||||||
SubaccountClient,
|
SubaccountClient,
|
||||||
DYDX_DENOM,
|
DYDX_DENOM,
|
||||||
USDC_DENOM,
|
USDC_DENOM,
|
||||||
|
GAS_PRICE_DYDX_DENOM,
|
||||||
} from '@dydxprotocol/v4-client-js';
|
} from '@dydxprotocol/v4-client-js';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
AccountBalance,
|
HumanReadableCancelOrderPayload,
|
||||||
HumanReadablePlaceOrderPayload,
|
HumanReadablePlaceOrderPayload,
|
||||||
ParsingError,
|
|
||||||
SubAccountHistoricalPNLs,
|
SubAccountHistoricalPNLs,
|
||||||
} from '@/constants/abacus';
|
} from '@/constants/abacus';
|
||||||
|
|
||||||
import { AMOUNT_RESERVED_FOR_GAS_USDC } from '@/constants/account';
|
import { AMOUNT_RESERVED_FOR_GAS_USDC } from '@/constants/account';
|
||||||
import { AnalyticsEvent } from '@/constants/analytics';
|
import { AnalyticsEvent } from '@/constants/analytics';
|
||||||
|
import { ORDER_ERROR_CODE_MAP } from '@/constants/localization';
|
||||||
import { QUANTUM_MULTIPLIER } from '@/constants/numbers';
|
import { QUANTUM_MULTIPLIER } from '@/constants/numbers';
|
||||||
|
import { UNCOMMITTED_ORDER_TIMEOUT } from '@/constants/trade';
|
||||||
import { DydxAddress } from '@/constants/wallets';
|
import { DydxAddress } from '@/constants/wallets';
|
||||||
|
|
||||||
import { setSubaccount, setHistoricalPnl, removeUncommittedOrderClientId } from '@/state/account';
|
import {
|
||||||
import { getBalances } from '@/state/accountSelectors';
|
addUncommittedOrderClientId,
|
||||||
|
removeUncommittedOrderClientId,
|
||||||
|
setSubaccount,
|
||||||
|
setHistoricalPnl,
|
||||||
|
} from '@/state/account';
|
||||||
|
|
||||||
import abacusStateManager from '@/lib/abacus';
|
import abacusStateManager from '@/lib/abacus';
|
||||||
import { track } from '@/lib/analytics';
|
import { track } from '@/lib/analytics';
|
||||||
|
import { StatefulOrderError } from '@/lib/errors';
|
||||||
import { MustBigNumber } from '@/lib/numbers';
|
import { MustBigNumber } from '@/lib/numbers';
|
||||||
import { log } from '@/lib/telemetry';
|
import { log } from '@/lib/telemetry';
|
||||||
|
|
||||||
import { useAccounts } from './useAccounts';
|
import { useAccounts } from './useAccounts';
|
||||||
import { useDydxClient } from './useDydxClient';
|
import { useDydxClient } from './useDydxClient';
|
||||||
|
import { usePollUSDCBalance } from './usePollUSDCBalance';
|
||||||
|
|
||||||
type SubaccountContextType = ReturnType<typeof useSubaccountContext>;
|
type SubaccountContextType = ReturnType<typeof useSubaccountContext>;
|
||||||
const SubaccountContext = createContext<SubaccountContextType>({} as SubaccountContextType);
|
const SubaccountContext = createContext<SubaccountContextType>({} as SubaccountContextType);
|
||||||
@@ -71,8 +83,12 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
|||||||
const {
|
const {
|
||||||
depositToSubaccount,
|
depositToSubaccount,
|
||||||
withdrawFromSubaccount,
|
withdrawFromSubaccount,
|
||||||
|
simulateWithdrawFromSubaccount,
|
||||||
transferFromSubaccountToAddress,
|
transferFromSubaccountToAddress,
|
||||||
transferNativeToken,
|
transferNativeToken,
|
||||||
|
simulateTransferNativeToken,
|
||||||
|
placeOrderForSubaccount,
|
||||||
|
cancelOrderForSubaccount,
|
||||||
sendSquidWithdrawFromSubaccount,
|
sendSquidWithdrawFromSubaccount,
|
||||||
} = useMemo(
|
} = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -94,6 +110,31 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
|||||||
amount: number;
|
amount: number;
|
||||||
}) => await compositeClient?.withdrawFromSubaccount(subaccountClient, amount),
|
}) => await compositeClient?.withdrawFromSubaccount(subaccountClient, amount),
|
||||||
|
|
||||||
|
simulateWithdrawFromSubaccount: async ({
|
||||||
|
subaccountClient,
|
||||||
|
amount,
|
||||||
|
recipient,
|
||||||
|
}: {
|
||||||
|
subaccountClient: SubaccountClient;
|
||||||
|
amount: number;
|
||||||
|
recipient?: string;
|
||||||
|
}) => {
|
||||||
|
return await compositeClient?.simulate(
|
||||||
|
subaccountClient?.wallet,
|
||||||
|
() =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
const msg = compositeClient?.withdrawFromSubaccountMessage(
|
||||||
|
subaccountClient,
|
||||||
|
amount,
|
||||||
|
recipient
|
||||||
|
);
|
||||||
|
|
||||||
|
resolve([msg]);
|
||||||
|
}),
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
transferFromSubaccountToAddress: async ({
|
transferFromSubaccountToAddress: async ({
|
||||||
subaccountClient,
|
subaccountClient,
|
||||||
assetId = 0,
|
assetId = 0,
|
||||||
@@ -144,6 +185,125 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
|||||||
Method.BroadcastTxCommit
|
Method.BroadcastTxCommit
|
||||||
),
|
),
|
||||||
|
|
||||||
|
simulateTransferNativeToken: async ({
|
||||||
|
subaccountClient,
|
||||||
|
amount,
|
||||||
|
recipient,
|
||||||
|
}: {
|
||||||
|
subaccountClient: SubaccountClient;
|
||||||
|
amount: number;
|
||||||
|
recipient: string;
|
||||||
|
}) =>
|
||||||
|
await compositeClient?.simulate(
|
||||||
|
subaccountClient?.wallet,
|
||||||
|
() =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
const msg = compositeClient?.validatorClient.post.composer.composeMsgSendToken(
|
||||||
|
subaccountClient.address,
|
||||||
|
recipient,
|
||||||
|
DYDX_DENOM,
|
||||||
|
Long.fromNumber(amount * QUANTUM_MULTIPLIER)
|
||||||
|
);
|
||||||
|
|
||||||
|
resolve([msg]);
|
||||||
|
}),
|
||||||
|
GAS_PRICE_DYDX_DENOM,
|
||||||
|
undefined
|
||||||
|
),
|
||||||
|
|
||||||
|
placeOrderForSubaccount: async ({
|
||||||
|
subaccount,
|
||||||
|
marketId,
|
||||||
|
type,
|
||||||
|
side,
|
||||||
|
price,
|
||||||
|
triggerPrice,
|
||||||
|
size,
|
||||||
|
clientId,
|
||||||
|
timeInForce,
|
||||||
|
goodTilTimeInSeconds,
|
||||||
|
execution,
|
||||||
|
postOnly,
|
||||||
|
reduceOnly,
|
||||||
|
}: {
|
||||||
|
subaccount: SubaccountClient;
|
||||||
|
marketId: string;
|
||||||
|
type: OrderType;
|
||||||
|
side: OrderSide;
|
||||||
|
price: number;
|
||||||
|
triggerPrice: Nullable<number>;
|
||||||
|
size: number;
|
||||||
|
clientId: number;
|
||||||
|
timeInForce: OrderTimeInForce;
|
||||||
|
goodTilTimeInSeconds: number;
|
||||||
|
execution: OrderExecution;
|
||||||
|
postOnly: boolean;
|
||||||
|
reduceOnly: boolean;
|
||||||
|
}) => {
|
||||||
|
const startTimestamp = performance.now();
|
||||||
|
|
||||||
|
const result = await compositeClient?.placeOrder(
|
||||||
|
subaccount,
|
||||||
|
marketId,
|
||||||
|
type,
|
||||||
|
side,
|
||||||
|
price,
|
||||||
|
size,
|
||||||
|
clientId,
|
||||||
|
timeInForce,
|
||||||
|
goodTilTimeInSeconds,
|
||||||
|
execution,
|
||||||
|
postOnly,
|
||||||
|
reduceOnly,
|
||||||
|
triggerPrice ?? undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
const endTimestamp = performance.now();
|
||||||
|
|
||||||
|
track(AnalyticsEvent.TradePlaceOrderConfirmed, {
|
||||||
|
roundtripMs: endTimestamp - startTimestamp,
|
||||||
|
validator: compositeClient!.validatorClient.config.restEndpoint,
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelOrderForSubaccount: async ({
|
||||||
|
subaccount,
|
||||||
|
clientId,
|
||||||
|
clobPairId,
|
||||||
|
orderFlags,
|
||||||
|
goodTilBlock,
|
||||||
|
goodTilBlockTime,
|
||||||
|
}: {
|
||||||
|
subaccount: SubaccountClient;
|
||||||
|
clientId: number;
|
||||||
|
orderFlags: OrderFlags;
|
||||||
|
clobPairId: number;
|
||||||
|
goodTilBlock?: number;
|
||||||
|
goodTilBlockTime?: number;
|
||||||
|
}) => {
|
||||||
|
const startTimestamp = performance.now();
|
||||||
|
|
||||||
|
const result = await compositeClient?.cancelOrder(
|
||||||
|
subaccount,
|
||||||
|
clientId,
|
||||||
|
orderFlags,
|
||||||
|
clobPairId,
|
||||||
|
goodTilBlock,
|
||||||
|
goodTilBlockTime
|
||||||
|
);
|
||||||
|
|
||||||
|
const endTimestamp = performance.now();
|
||||||
|
|
||||||
|
track(AnalyticsEvent.TradeCancelOrderConfirmed, {
|
||||||
|
roundtripMs: endTimestamp - startTimestamp,
|
||||||
|
validator: compositeClient!.validatorClient.config.restEndpoint,
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
sendSquidWithdrawFromSubaccount: async ({
|
sendSquidWithdrawFromSubaccount: async ({
|
||||||
subaccountClient,
|
subaccountClient,
|
||||||
amount,
|
amount,
|
||||||
@@ -193,10 +353,10 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
|||||||
|
|
||||||
// ------ Deposit/Withdraw Methods ------ //
|
// ------ Deposit/Withdraw Methods ------ //
|
||||||
const depositFunds = useCallback(
|
const depositFunds = useCallback(
|
||||||
async (balance: AccountBalance) => {
|
async (balance?: Coin) => {
|
||||||
if (!localDydxWallet) return;
|
if (!localDydxWallet) return;
|
||||||
|
|
||||||
const amountAfterDust = MustBigNumber(balance.amount)
|
const amountAfterDust = MustBigNumber(balance?.amount)
|
||||||
.minus(AMOUNT_RESERVED_FOR_GAS_USDC) // keep 0.1 USDC in user's wallet for gas
|
.minus(AMOUNT_RESERVED_FOR_GAS_USDC) // keep 0.1 USDC in user's wallet for gas
|
||||||
.toString();
|
.toString();
|
||||||
|
|
||||||
@@ -210,14 +370,11 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
|||||||
[localDydxWallet, depositToSubaccount]
|
[localDydxWallet, depositToSubaccount]
|
||||||
);
|
);
|
||||||
|
|
||||||
const balances = useSelector(getBalances, shallowEqual);
|
const balance = usePollUSDCBalance({ dydxAddress });
|
||||||
const usdcCoinBalance = balances?.[USDC_DENOM];
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (usdcCoinBalance) {
|
depositFunds(balance);
|
||||||
depositFunds(usdcCoinBalance);
|
}, [balance]);
|
||||||
}
|
|
||||||
}, [usdcCoinBalance]);
|
|
||||||
|
|
||||||
const deposit = useCallback(
|
const deposit = useCallback(
|
||||||
async (amount: Long) => {
|
async (amount: Long) => {
|
||||||
@@ -256,6 +413,30 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
|||||||
[subaccountClient, transferFromSubaccountToAddress, transferNativeToken]
|
[subaccountClient, transferFromSubaccountToAddress, transferNativeToken]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const simulateTransfer = useCallback(
|
||||||
|
async (amount: number, recipient: string, coinDenom: string) => {
|
||||||
|
if (!subaccountClient) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await (coinDenom === USDC_DENOM
|
||||||
|
? simulateWithdrawFromSubaccount
|
||||||
|
: simulateTransferNativeToken)({ subaccountClient, amount, recipient });
|
||||||
|
},
|
||||||
|
[subaccountClient, simulateWithdrawFromSubaccount, simulateTransferNativeToken]
|
||||||
|
);
|
||||||
|
|
||||||
|
const simulateWithdraw = useCallback(
|
||||||
|
async (amount: number) => {
|
||||||
|
if (!subaccountClient) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await simulateWithdrawFromSubaccount({ subaccountClient, amount });
|
||||||
|
},
|
||||||
|
[subaccountClient, simulateWithdrawFromSubaccount]
|
||||||
|
);
|
||||||
|
|
||||||
const sendSquidWithdraw = useCallback(
|
const sendSquidWithdraw = useCallback(
|
||||||
async (amount: number, payload: string) => {
|
async (amount: number, payload: string) => {
|
||||||
if (!subaccountClient) {
|
if (!subaccountClient) {
|
||||||
@@ -286,41 +467,101 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
|||||||
onSuccess,
|
onSuccess,
|
||||||
}: {
|
}: {
|
||||||
isClosePosition?: boolean;
|
isClosePosition?: boolean;
|
||||||
onError?: (onErrorParams?: { errorStringKey?: Nullable<string> }) => void;
|
onError?: (onErrorParams?: { errorStringKey?: string }) => void;
|
||||||
onSuccess?: (placeOrderPayload: Nullable<HumanReadablePlaceOrderPayload>) => void;
|
onSuccess?: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const callback = (
|
let orderParams: Nullable<HumanReadablePlaceOrderPayload>;
|
||||||
success: boolean,
|
|
||||||
parsingError?: Nullable<ParsingError>,
|
|
||||||
data?: Nullable<HumanReadablePlaceOrderPayload>
|
|
||||||
) => {
|
|
||||||
if (success) {
|
|
||||||
onSuccess?.(data);
|
|
||||||
} else {
|
|
||||||
onError?.({ errorStringKey: parsingError?.stringKey });
|
|
||||||
|
|
||||||
if (data?.clientId !== undefined) {
|
if (!subaccountClient) return;
|
||||||
dispatch(removeUncommittedOrderClientId(data.clientId));
|
|
||||||
}
|
try {
|
||||||
|
orderParams = isClosePosition
|
||||||
|
? abacusStateManager.closePositionPayload()
|
||||||
|
: abacusStateManager.placeOrderPayload();
|
||||||
|
|
||||||
|
if (!orderParams) {
|
||||||
|
throw new Error('Missing order params');
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let placeOrderParams;
|
const {
|
||||||
|
marketId,
|
||||||
|
type,
|
||||||
|
side,
|
||||||
|
price,
|
||||||
|
triggerPrice,
|
||||||
|
size,
|
||||||
|
clientId,
|
||||||
|
timeInForce,
|
||||||
|
goodTilTimeInSeconds,
|
||||||
|
execution,
|
||||||
|
postOnly,
|
||||||
|
reduceOnly,
|
||||||
|
} = orderParams;
|
||||||
|
|
||||||
if (isClosePosition) {
|
dispatch(addUncommittedOrderClientId(clientId));
|
||||||
placeOrderParams = abacusStateManager.closePosition(callback);
|
|
||||||
} else {
|
// Remove uncommitted order after timeout if it hasn't already been removed
|
||||||
placeOrderParams = abacusStateManager.placeOrder(callback);
|
setTimeout(() => {
|
||||||
|
dispatch(removeUncommittedOrderClientId(clientId));
|
||||||
|
}, UNCOMMITTED_ORDER_TIMEOUT);
|
||||||
|
|
||||||
|
console.log('useSubaccount/placeOrder', {
|
||||||
|
...orderParams,
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await placeOrderForSubaccount({
|
||||||
|
subaccount: subaccountClient,
|
||||||
|
marketId,
|
||||||
|
type: type as OrderType,
|
||||||
|
side: side as OrderSide,
|
||||||
|
price,
|
||||||
|
triggerPrice,
|
||||||
|
size,
|
||||||
|
clientId,
|
||||||
|
timeInForce: timeInForce as OrderTimeInForce,
|
||||||
|
goodTilTimeInSeconds: goodTilTimeInSeconds ?? 0,
|
||||||
|
execution: execution as OrderExecution,
|
||||||
|
postOnly,
|
||||||
|
reduceOnly,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle Stateful orders
|
||||||
|
if ((response as IndexedTx)?.code !== 0) {
|
||||||
|
throw new StatefulOrderError('Stateful order has failed to commit.', response);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orderParams?.clientId) {
|
||||||
|
dispatch(removeUncommittedOrderClientId(orderParams.clientId));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response?.hash) {
|
||||||
|
console.log(
|
||||||
|
isClosePosition
|
||||||
|
? 'useSubaccount/closePosition'
|
||||||
|
: 'useSubaccount/placeOrderForSubaccount',
|
||||||
|
{
|
||||||
|
txHash: Buffer.from(response.hash).toString('hex').toUpperCase(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
track(AnalyticsEvent.TradePlaceOrder, {
|
||||||
|
...orderParams,
|
||||||
|
isClosePosition,
|
||||||
|
} as HumanReadablePlaceOrderPayload & { isClosePosition: boolean });
|
||||||
|
onSuccess?.();
|
||||||
|
} catch (error) {
|
||||||
|
const errorCode: number | undefined = error?.code;
|
||||||
|
const errorStringKey = errorCode ? ORDER_ERROR_CODE_MAP[errorCode] : undefined;
|
||||||
|
onError?.({ errorStringKey });
|
||||||
|
|
||||||
|
log('useSubaccount/placeOrder', error, {
|
||||||
|
orderParams,
|
||||||
|
isClosePosition,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
track(AnalyticsEvent.TradePlaceOrder, {
|
|
||||||
...placeOrderParams,
|
|
||||||
isClosePosition,
|
|
||||||
} as HumanReadablePlaceOrderPayload & { isClosePosition: boolean });
|
|
||||||
|
|
||||||
return placeOrderParams;
|
|
||||||
},
|
},
|
||||||
[subaccountClient]
|
[subaccountClient, placeOrderForSubaccount]
|
||||||
);
|
);
|
||||||
|
|
||||||
const closePosition = useCallback(
|
const closePosition = useCallback(
|
||||||
@@ -328,8 +569,8 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
|||||||
onError,
|
onError,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}: {
|
}: {
|
||||||
onError: (onErrorParams?: { errorStringKey?: Nullable<string> }) => void;
|
onError: (onErrorParams?: { errorStringKey?: string }) => void;
|
||||||
onSuccess?: (placeOrderPayload: Nullable<HumanReadablePlaceOrderPayload>) => void;
|
onSuccess?: () => void;
|
||||||
}) => await placeOrder({ isClosePosition: true, onError, onSuccess }),
|
}) => await placeOrder({ isClosePosition: true, onError, onSuccess }),
|
||||||
[placeOrder]
|
[placeOrder]
|
||||||
);
|
);
|
||||||
@@ -341,21 +582,53 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
|||||||
onSuccess,
|
onSuccess,
|
||||||
}: {
|
}: {
|
||||||
orderId: string;
|
orderId: string;
|
||||||
onError?: ({ errorStringKey }?: { errorStringKey?: Nullable<string> }) => void;
|
onError?: ({ errorMsg }?: { errorMsg?: string }) => void;
|
||||||
onSuccess?: () => void;
|
onSuccess?: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const callback = (success: boolean, parsingError?: Nullable<ParsingError>) => {
|
let cancelOrderParams: Nullable<HumanReadableCancelOrderPayload>;
|
||||||
if (success) {
|
|
||||||
track(AnalyticsEvent.TradeCancelOrder);
|
|
||||||
onSuccess?.();
|
|
||||||
} else {
|
|
||||||
onError?.({ errorStringKey: parsingError?.stringKey });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
abacusStateManager.cancelOrder(orderId, callback);
|
if (!subaccountClient) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cancelOrderParams = abacusStateManager.cancelOrderPayload(orderId);
|
||||||
|
|
||||||
|
if (!cancelOrderParams) {
|
||||||
|
throw new Error('Missing cancel order params');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { clientId, clobPairId, goodTilBlock, goodTilBlockTime, orderFlags } =
|
||||||
|
cancelOrderParams;
|
||||||
|
|
||||||
|
// Keep for debugging
|
||||||
|
console.log('useSubaccount/cancelOrder', cancelOrderParams);
|
||||||
|
|
||||||
|
const response = await cancelOrderForSubaccount({
|
||||||
|
subaccount: subaccountClient,
|
||||||
|
clientId,
|
||||||
|
orderFlags,
|
||||||
|
clobPairId,
|
||||||
|
goodTilBlock: goodTilBlock || undefined,
|
||||||
|
goodTilBlockTime: goodTilBlockTime || undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle Stateful orders
|
||||||
|
if ((response as IndexedTx)?.code !== 0) {
|
||||||
|
throw new StatefulOrderError('Stateful cancel has failed to commit.', response);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response?.hash) {
|
||||||
|
console.log('useSubaccount/cancelOrderForSubaccount', {
|
||||||
|
txHash: Buffer.from(response.hash).toString('hex').toUpperCase(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSuccess?.();
|
||||||
|
} catch (error) {
|
||||||
|
onError?.();
|
||||||
|
log('useSubaccount/cancelOrder', error, cancelOrderParams);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[subaccountClient]
|
[subaccountClient, cancelOrderForSubaccount]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -365,7 +638,9 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
|||||||
requestFaucetFunds,
|
requestFaucetFunds,
|
||||||
|
|
||||||
// Transfer Methods
|
// Transfer Methods
|
||||||
|
simulateTransfer,
|
||||||
transfer,
|
transfer,
|
||||||
|
simulateWithdraw,
|
||||||
sendSquidWithdraw,
|
sendSquidWithdraw,
|
||||||
|
|
||||||
// Trading Methods
|
// Trading Methods
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const LogoShortIcon: React.FC<{ id?: string }> = ({ id }: { id?: string }) => (
|
const LogoShortIcon: React.FC<{ id?: string }> = ({ id }) => (
|
||||||
<svg
|
<svg
|
||||||
width="135"
|
width="135"
|
||||||
height="145"
|
height="145"
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ import { DialogTypes } from '@/constants/dialogs';
|
|||||||
import { STRING_KEYS } from '@/constants/localization';
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
import { AppRoute } from '@/constants/routes';
|
import { AppRoute } from '@/constants/routes';
|
||||||
import { useStringGetter } from '@/hooks';
|
import { useStringGetter } from '@/hooks';
|
||||||
import { LogoShortIcon, BellIcon } from '@/icons';
|
import { LogoShortIcon } from '@/icons';
|
||||||
|
|
||||||
import { Icon, IconName } from '@/components/Icon';
|
import { Icon, IconName } from '@/components/Icon';
|
||||||
|
import { BellIcon } from '@/icons';
|
||||||
import { IconButton } from '@/components/IconButton';
|
import { IconButton } from '@/components/IconButton';
|
||||||
import { NavigationMenu } from '@/components/NavigationMenu';
|
import { NavigationMenu } from '@/components/NavigationMenu';
|
||||||
import { VerticalSeparator } from '@/components/Separator';
|
import { VerticalSeparator } from '@/components/Separator';
|
||||||
|
|||||||
@@ -15,20 +15,12 @@ type StyleProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const NotificationsToastArea = ({ className }: StyleProps) => {
|
export const NotificationsToastArea = ({ className }: StyleProps) => {
|
||||||
const {
|
const { notifications, getKey, getDisplayData, markUnseen, markSeen, isMenuOpen, onNotificationAction } = useNotifications();
|
||||||
notifications,
|
|
||||||
getKey,
|
|
||||||
getDisplayData,
|
|
||||||
markUnseen,
|
|
||||||
markSeen,
|
|
||||||
isMenuOpen,
|
|
||||||
onNotificationAction,
|
|
||||||
} = useNotifications();
|
|
||||||
|
|
||||||
const { isMobile } = useBreakpoints();
|
const { isMobile } = useBreakpoints();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledToastArea swipeDirection={isMobile ? 'up' : 'right'} className={className}>
|
<$ToastArea swipeDirection={isMobile ? 'up' : 'right'} className={className}>
|
||||||
{Object.values(notifications)
|
{Object.values(notifications)
|
||||||
// Sort by time of first trigger
|
// Sort by time of first trigger
|
||||||
.sort(
|
.sort(
|
||||||
@@ -61,20 +53,19 @@ export const NotificationsToastArea = ({ className }: StyleProps) => {
|
|||||||
sensitivity={displayData.toastSensitivity}
|
sensitivity={displayData.toastSensitivity}
|
||||||
setIsOpen={(isOpen, isClosedFromTimeout) => {
|
setIsOpen={(isOpen, isClosedFromTimeout) => {
|
||||||
if (!isOpen)
|
if (!isOpen)
|
||||||
if (isClosedFromTimeout)
|
// Toast timer expired without user interaction
|
||||||
// Toast timer expired without user interaction
|
if (isClosedFromTimeout) markUnseen(notification);
|
||||||
markUnseen(notification);
|
|
||||||
// Toast interacted with or dismissed
|
// Toast interacted with or dismissed
|
||||||
else markSeen(notification);
|
else markSeen(notification);
|
||||||
}}
|
}}
|
||||||
lastUpdated={notification.timestamps[notification.status]}
|
lastUpdated={notification.timestamps[notification.status]}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</StyledToastArea>
|
</$ToastArea>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledToastArea = styled(ToastArea)`
|
const $ToastArea = styled(ToastArea)`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: min(16rem, 100%);
|
width: min(16rem, 100%);
|
||||||
inset: 0 0 0 auto;
|
inset: 0 0 0 auto;
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
import type { AbacusTrackingProtocol, Nullable } from '@/constants/abacus';
|
|
||||||
import type { AnalyticsEvent } from '@/constants/analytics';
|
|
||||||
|
|
||||||
import { track } from '../analytics';
|
|
||||||
import { log as telemetryLog } from '../telemetry';
|
|
||||||
|
|
||||||
class AbacusAnalytics implements AbacusTrackingProtocol {
|
|
||||||
log(event: string, data: Nullable<string>) {
|
|
||||||
try {
|
|
||||||
const parsedData = data ? JSON.parse(data) : {};
|
|
||||||
track(event as AnalyticsEvent, parsedData);
|
|
||||||
} catch (error) {
|
|
||||||
telemetryLog('AbacusAnalytics/log', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AbacusAnalytics;
|
|
||||||
@@ -1,63 +1,28 @@
|
|||||||
import Abacus, { type Nullable } from '@dydxprotocol/v4-abacus';
|
import Abacus, { Nullable } from '@dydxprotocol/abacus';
|
||||||
import Long from 'long';
|
import Long from 'long';
|
||||||
import type { IndexedTx } from '@cosmjs/stargate';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CompositeClient,
|
CompositeClient,
|
||||||
IndexerConfig,
|
IndexerConfig,
|
||||||
type LocalWallet,
|
|
||||||
Network,
|
Network,
|
||||||
NetworkOptimizer,
|
NetworkOptimizer,
|
||||||
SubaccountClient,
|
|
||||||
ValidatorConfig,
|
ValidatorConfig,
|
||||||
OrderType,
|
|
||||||
OrderSide,
|
|
||||||
OrderTimeInForce,
|
|
||||||
OrderExecution,
|
|
||||||
DYDX_DENOM,
|
|
||||||
GAS_PRICE_DYDX_DENOM,
|
|
||||||
} from '@dydxprotocol/v4-client-js';
|
} from '@dydxprotocol/v4-client-js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type AbacusDYDXChainTransactionsProtocol,
|
type AbacusDYDXChainTransactionsProtocol,
|
||||||
QueryType,
|
QueryType,
|
||||||
type QueryTypes,
|
type QueryTypes,
|
||||||
TransactionType,
|
|
||||||
type TransactionTypes,
|
type TransactionTypes,
|
||||||
type HumanReadablePlaceOrderPayload,
|
|
||||||
type HumanReadableCancelOrderPayload,
|
|
||||||
type HumanReadableWithdrawPayload,
|
|
||||||
type HumanReadableTransferPayload,
|
|
||||||
} from '@/constants/abacus';
|
} from '@/constants/abacus';
|
||||||
|
|
||||||
import { DialogTypes } from '@/constants/dialogs';
|
|
||||||
import { UNCOMMITTED_ORDER_TIMEOUT_MS } from '@/constants/trade';
|
|
||||||
import { QUANTUM_MULTIPLIER } from '@/constants/numbers';
|
|
||||||
|
|
||||||
import { RootStore } from '@/state/_store';
|
|
||||||
import { addUncommittedOrderClientId, removeUncommittedOrderClientId } from '@/state/account';
|
|
||||||
import { openDialog } from '@/state/dialogs';
|
|
||||||
|
|
||||||
import { StatefulOrderError } from '../errors';
|
|
||||||
import { bytesToBigInt } from '../numbers';
|
|
||||||
import { log } from '../telemetry';
|
import { log } from '../telemetry';
|
||||||
|
|
||||||
class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
||||||
private compositeClient: CompositeClient | undefined;
|
private compositeClient: CompositeClient | undefined;
|
||||||
private store: RootStore | undefined;
|
|
||||||
private localWallet: LocalWallet | undefined;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.compositeClient = undefined;
|
this.compositeClient = undefined;
|
||||||
this.store = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
setStore(store: RootStore): void {
|
|
||||||
this.store = store;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLocalWallet(localWallet: LocalWallet) {
|
|
||||||
this.localWallet = localWallet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async connectNetwork(
|
async connectNetwork(
|
||||||
@@ -68,41 +33,30 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
|||||||
faucetUrl: Nullable<string> | undefined,
|
faucetUrl: Nullable<string> | undefined,
|
||||||
callback: (p0: Nullable<string>) => void
|
callback: (p0: Nullable<string>) => void
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
this.compositeClient = await CompositeClient.connect(
|
||||||
const compositeClient = await CompositeClient.connect(
|
new Network(
|
||||||
new Network(
|
chainId,
|
||||||
chainId,
|
new IndexerConfig(indexerUrl, indexerSocketUrl),
|
||||||
new IndexerConfig(indexerUrl, indexerSocketUrl),
|
new ValidatorConfig(validatorUrl, chainId, {
|
||||||
new ValidatorConfig(validatorUrl, chainId, {
|
broadcastPollIntervalMs: 3_000,
|
||||||
broadcastPollIntervalMs: 3_000,
|
broadcastTimeoutMs: 60_000,
|
||||||
broadcastTimeoutMs: 60_000,
|
})
|
||||||
})
|
)
|
||||||
)
|
);
|
||||||
);
|
|
||||||
|
|
||||||
this.compositeClient = compositeClient;
|
// Dispatch custom event to notify other parts of the app that the network has been connected
|
||||||
|
const customEvent = new CustomEvent('abacus:connectNetwork', {
|
||||||
|
detail: {
|
||||||
|
indexerUrl,
|
||||||
|
indexerSocketUrl,
|
||||||
|
validatorUrl,
|
||||||
|
chainId,
|
||||||
|
faucetUrl,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// Dispatch custom event to notify other parts of the app that the network has been connected
|
globalThis.dispatchEvent(customEvent);
|
||||||
const customEvent = new CustomEvent('abacus:connectNetwork', {
|
callback(JSON.stringify({ success: true }));
|
||||||
detail: {
|
|
||||||
indexerUrl,
|
|
||||||
indexerSocketUrl,
|
|
||||||
validatorUrl,
|
|
||||||
chainId,
|
|
||||||
faucetUrl,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
globalThis.dispatchEvent(customEvent);
|
|
||||||
callback(JSON.stringify({ success: true }));
|
|
||||||
} catch (error) {
|
|
||||||
this.store?.dispatch(
|
|
||||||
openDialog({ type: DialogTypes.ExchangeOffline, dialogProps: { preventClose: true } })
|
|
||||||
);
|
|
||||||
|
|
||||||
log('DydxChainTransactions/connectNetwork', error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parseToPrimitives<T>(x: T): T {
|
parseToPrimitives<T>(x: T): T {
|
||||||
@@ -118,10 +72,6 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
|||||||
return x.toString() as T;
|
return x.toString() as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x instanceof Uint8Array) {
|
|
||||||
return bytesToBigInt(x).toString() as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof x === 'object') {
|
if (typeof x === 'object') {
|
||||||
const parsedObj: { [key: string]: any } = {};
|
const parsedObj: { [key: string]: any } = {};
|
||||||
for (const key in x) {
|
for (const key in x) {
|
||||||
@@ -135,218 +85,14 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
|||||||
throw new Error(`Unsupported data type: ${typeof x}`);
|
throw new Error(`Unsupported data type: ${typeof x}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async placeOrderTransaction(params: HumanReadablePlaceOrderPayload): Promise<string> {
|
|
||||||
if (!this.compositeClient || !this.localWallet)
|
|
||||||
throw new Error('Missing compositeClient or localWallet');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const {
|
|
||||||
subaccountNumber,
|
|
||||||
marketId,
|
|
||||||
type,
|
|
||||||
side,
|
|
||||||
price,
|
|
||||||
size,
|
|
||||||
clientId,
|
|
||||||
timeInForce,
|
|
||||||
goodTilTimeInSeconds,
|
|
||||||
execution,
|
|
||||||
postOnly,
|
|
||||||
reduceOnly,
|
|
||||||
triggerPrice,
|
|
||||||
} = params || {};
|
|
||||||
|
|
||||||
// Observe uncommitted order
|
|
||||||
this.store?.dispatch(addUncommittedOrderClientId(clientId));
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.store?.dispatch(removeUncommittedOrderClientId(clientId));
|
|
||||||
}, UNCOMMITTED_ORDER_TIMEOUT_MS);
|
|
||||||
|
|
||||||
// Place order
|
|
||||||
const tx = await this.compositeClient?.placeOrder(
|
|
||||||
new SubaccountClient(this.localWallet, subaccountNumber),
|
|
||||||
marketId,
|
|
||||||
type as OrderType,
|
|
||||||
side as OrderSide,
|
|
||||||
price,
|
|
||||||
size,
|
|
||||||
clientId,
|
|
||||||
timeInForce as OrderTimeInForce,
|
|
||||||
goodTilTimeInSeconds ?? 0,
|
|
||||||
execution as OrderExecution,
|
|
||||||
postOnly,
|
|
||||||
reduceOnly,
|
|
||||||
triggerPrice ?? undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
// Handle stateful orders
|
|
||||||
if ((tx as IndexedTx)?.code !== 0) {
|
|
||||||
throw new StatefulOrderError('Stateful order has failed to commit.', tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsedTx = this.parseToPrimitives(tx);
|
|
||||||
const hash = parsedTx?.hash;
|
|
||||||
|
|
||||||
if (import.meta.env.MODE === 'production') {
|
|
||||||
console.log(`https://testnet.mintscan.io/dydx-testnet/txs/${hash}`);
|
|
||||||
} else console.log(`txHash: ${hash}`);
|
|
||||||
|
|
||||||
return JSON.stringify(parsedTx);
|
|
||||||
} catch (error) {
|
|
||||||
if (error?.name !== 'BroadcastError') {
|
|
||||||
log('DydxChainTransactions/placeOrderTransaction', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return JSON.stringify({
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async cancelOrderTransaction(params: HumanReadableCancelOrderPayload): Promise<string> {
|
|
||||||
if (!this.compositeClient || !this.localWallet) {
|
|
||||||
throw new Error('Missing compositeClient or localWallet');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { subaccountNumber, clientId, orderFlags, clobPairId, goodTilBlock, goodTilBlockTime } =
|
|
||||||
params ?? {};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const tx = await this.compositeClient?.cancelOrder(
|
|
||||||
new SubaccountClient(this.localWallet, subaccountNumber),
|
|
||||||
clientId,
|
|
||||||
orderFlags,
|
|
||||||
clobPairId,
|
|
||||||
goodTilBlock ?? undefined,
|
|
||||||
goodTilBlockTime ?? undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
const parsedTx = this.parseToPrimitives(tx);
|
|
||||||
|
|
||||||
return JSON.stringify(parsedTx);
|
|
||||||
} catch (error) {
|
|
||||||
log('DydxChainTransactions/cancelOrderTransaction', error);
|
|
||||||
|
|
||||||
return JSON.stringify({
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async simulateWithdrawTransaction(params: HumanReadableWithdrawPayload): Promise<string> {
|
|
||||||
if (!this.compositeClient || !this.localWallet) {
|
|
||||||
throw new Error('Missing compositeClient or localWallet');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { subaccountNumber, amount } = params ?? {};
|
|
||||||
const compositeClient = this.compositeClient;
|
|
||||||
const subaccountClient = new SubaccountClient(this.localWallet, subaccountNumber);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const tx = await compositeClient.simulate(
|
|
||||||
this.localWallet,
|
|
||||||
() =>
|
|
||||||
new Promise((resolve) => {
|
|
||||||
const msg = compositeClient.withdrawFromSubaccountMessage(subaccountClient, amount);
|
|
||||||
|
|
||||||
resolve([msg]);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const parsedTx = this.parseToPrimitives(tx);
|
|
||||||
|
|
||||||
return JSON.stringify(parsedTx);
|
|
||||||
} catch (error) {
|
|
||||||
log('DydxChainTransactions/simulateWithdrawTransaction', error);
|
|
||||||
|
|
||||||
return JSON.stringify({
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async simulateTransferNativeTokenTransaction(params: HumanReadableTransferPayload): Promise<string> {
|
|
||||||
if (!this.compositeClient || !this.localWallet) {
|
|
||||||
throw new Error('Missing compositeClient or localWallet');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { subaccountNumber, amount, recipient } = params ?? {};
|
|
||||||
const compositeClient = this.compositeClient;
|
|
||||||
const subaccountClient = new SubaccountClient(this.localWallet, subaccountNumber);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const tx = await compositeClient.simulate(
|
|
||||||
this.localWallet,
|
|
||||||
() =>
|
|
||||||
new Promise((resolve) => {
|
|
||||||
const msg = compositeClient?.validatorClient.post.composer.composeMsgSendToken(
|
|
||||||
subaccountClient.address,
|
|
||||||
recipient,
|
|
||||||
DYDX_DENOM,
|
|
||||||
Long.fromNumber(amount * QUANTUM_MULTIPLIER)
|
|
||||||
);
|
|
||||||
|
|
||||||
resolve([msg]);
|
|
||||||
}),
|
|
||||||
GAS_PRICE_DYDX_DENOM,
|
|
||||||
);
|
|
||||||
|
|
||||||
const parsedTx = this.parseToPrimitives(tx);
|
|
||||||
|
|
||||||
return JSON.stringify(parsedTx);
|
|
||||||
} catch (error) {
|
|
||||||
log('DydxChainTransactions/simulateTransferNativeTokenTransaction', error);
|
|
||||||
|
|
||||||
return JSON.stringify({
|
|
||||||
error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async transaction(
|
async transaction(
|
||||||
type: TransactionTypes,
|
type: TransactionTypes,
|
||||||
paramsInJson: Abacus.Nullable<string>,
|
paramsInJson: Abacus.Nullable<string>,
|
||||||
callback: (p0: Abacus.Nullable<string>) => void
|
callback: (p0: Abacus.Nullable<string>) => void
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
// To be implemented
|
||||||
const params = paramsInJson ? JSON.parse(paramsInJson) : undefined;
|
return;
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case TransactionType.PlaceOrder: {
|
|
||||||
const result = await this.placeOrderTransaction(params);
|
|
||||||
callback(result);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TransactionType.CancelOrder: {
|
|
||||||
const result = await this.cancelOrderTransaction(params);
|
|
||||||
callback(result);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TransactionType.simulateWithdraw: {
|
|
||||||
const result = await this.simulateWithdrawTransaction(params);
|
|
||||||
callback(result);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TransactionType.simulateTransferNativeToken: {
|
|
||||||
const result = await this.simulateTransferNativeTokenTransaction(params);
|
|
||||||
callback(result);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
try {
|
|
||||||
const serializedError = JSON.stringify(error);
|
|
||||||
callback(serializedError);
|
|
||||||
} catch (parseError) {
|
|
||||||
log('DydxChainTransactions/transaction', parseError);
|
|
||||||
}
|
|
||||||
|
|
||||||
log('DydxChainTransactions/transaction', error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(
|
async get(
|
||||||
@@ -370,12 +116,6 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
|||||||
);
|
);
|
||||||
callback(JSON.stringify({ url: optimalNode }));
|
callback(JSON.stringify({ url: optimalNode }));
|
||||||
break;
|
break;
|
||||||
case QueryType.EquityTiers:
|
|
||||||
const equityTiers =
|
|
||||||
await this.compositeClient?.validatorClient.get.getEquityTierLimitConfiguration();
|
|
||||||
const parsedEquityTiers = this.parseToPrimitives(equityTiers);
|
|
||||||
callback(JSON.stringify(parsedEquityTiers));
|
|
||||||
break;
|
|
||||||
case QueryType.FeeTiers:
|
case QueryType.FeeTiers:
|
||||||
const feeTiers = await this.compositeClient?.validatorClient.get.getFeeTiers();
|
const feeTiers = await this.compositeClient?.validatorClient.get.getFeeTiers();
|
||||||
const parsedFeeTiers = this.parseToPrimitives(feeTiers);
|
const parsedFeeTiers = this.parseToPrimitives(feeTiers);
|
||||||
@@ -395,14 +135,6 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
|||||||
const parsedUserStats = this.parseToPrimitives(userStats);
|
const parsedUserStats = this.parseToPrimitives(userStats);
|
||||||
callback(JSON.stringify(parsedUserStats));
|
callback(JSON.stringify(parsedUserStats));
|
||||||
break;
|
break;
|
||||||
case QueryType.GetAccountBalances:
|
|
||||||
if (!this.localWallet?.address) throw new Error('Missing localWallet');
|
|
||||||
const accountBalances = await this.compositeClient?.validatorClient.get.getAccountBalances(
|
|
||||||
this.localWallet.address
|
|
||||||
);
|
|
||||||
const parsedAccountBalances = this.parseToPrimitives(accountBalances);
|
|
||||||
callback(JSON.stringify(parsedAccountBalances));
|
|
||||||
break;
|
|
||||||
// Do not implement Transfers (yet)
|
// Do not implement Transfers (yet)
|
||||||
case QueryType.Transfers:
|
case QueryType.Transfers:
|
||||||
default:
|
default:
|
||||||
|
|||||||
+13
-43
@@ -1,5 +1,3 @@
|
|||||||
import type { LocalWallet } from '@dydxprotocol/v4-client-js';
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ClosePositionInputFields,
|
ClosePositionInputFields,
|
||||||
Nullable,
|
Nullable,
|
||||||
@@ -8,7 +6,6 @@ import type {
|
|||||||
TradeInputFields,
|
TradeInputFields,
|
||||||
TransferInputFields,
|
TransferInputFields,
|
||||||
HistoricalPnlPeriods,
|
HistoricalPnlPeriods,
|
||||||
ParsingError,
|
|
||||||
} from '@/constants/abacus';
|
} from '@/constants/abacus';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -33,7 +30,6 @@ import { getInputTradeOptions } from '@/state/inputsSelectors';
|
|||||||
import { getTransferInputs } from '@/state/inputsSelectors';
|
import { getTransferInputs } from '@/state/inputsSelectors';
|
||||||
|
|
||||||
import AbacusRest from './rest';
|
import AbacusRest from './rest';
|
||||||
import AbacusAnalytics from './analytics';
|
|
||||||
import AbacusWebsocket from './websocket';
|
import AbacusWebsocket from './websocket';
|
||||||
import AbacusChainTransaction from './dydxChainTransactions';
|
import AbacusChainTransaction from './dydxChainTransactions';
|
||||||
import AbacusStateNotifier from './stateNotification';
|
import AbacusStateNotifier from './stateNotification';
|
||||||
@@ -49,25 +45,21 @@ class AbacusStateManager {
|
|||||||
stateManager: InstanceType<typeof AsyncAbacusStateManager>;
|
stateManager: InstanceType<typeof AsyncAbacusStateManager>;
|
||||||
websocket: AbacusWebsocket;
|
websocket: AbacusWebsocket;
|
||||||
stateNotifier: AbacusStateNotifier;
|
stateNotifier: AbacusStateNotifier;
|
||||||
analytics: AbacusAnalytics;
|
|
||||||
abacusFormatter: AbacusFormatter;
|
abacusFormatter: AbacusFormatter;
|
||||||
chainTransactions: AbacusChainTransaction;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.store = undefined;
|
this.store = undefined;
|
||||||
this.currentMarket = undefined;
|
this.currentMarket = undefined;
|
||||||
this.stateNotifier = new AbacusStateNotifier();
|
this.stateNotifier = new AbacusStateNotifier();
|
||||||
this.analytics = new AbacusAnalytics();
|
|
||||||
this.websocket = new AbacusWebsocket();
|
this.websocket = new AbacusWebsocket();
|
||||||
this.abacusFormatter = new AbacusFormatter();
|
this.abacusFormatter = new AbacusFormatter();
|
||||||
this.chainTransactions = new AbacusChainTransaction();
|
|
||||||
|
|
||||||
const ioImplementations = new IOImplementations(
|
const ioImplementations = new IOImplementations(
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
new AbacusRest(),
|
new AbacusRest(),
|
||||||
this.websocket,
|
this.websocket,
|
||||||
this.chainTransactions,
|
new AbacusChainTransaction(),
|
||||||
this.analytics,
|
null,
|
||||||
new AbacusThreading(),
|
new AbacusThreading(),
|
||||||
new CoroutineTimer(),
|
new CoroutineTimer(),
|
||||||
new AbacusFileSystem()
|
new AbacusFileSystem()
|
||||||
@@ -163,14 +155,10 @@ class AbacusStateManager {
|
|||||||
setStore = (store: RootStore) => {
|
setStore = (store: RootStore) => {
|
||||||
this.store = store;
|
this.store = store;
|
||||||
this.stateNotifier.setStore(store);
|
this.stateNotifier.setStore(store);
|
||||||
this.chainTransactions.setStore(store);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
setAccount = (localWallet?: LocalWallet) => {
|
setAccount = (walletAddress: string) => {
|
||||||
if (localWallet) {
|
this.stateManager.accountAddress = walletAddress;
|
||||||
this.stateManager.accountAddress = localWallet.address;
|
|
||||||
this.chainTransactions.setLocalWallet(localWallet);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
setTransfersSourceAddress = (evmAddress: string) => {
|
setTransfersSourceAddress = (evmAddress: string) => {
|
||||||
@@ -228,34 +216,16 @@ class AbacusStateManager {
|
|||||||
this.stateManager.transferStatus(hash, fromChainId, toChainId);
|
this.stateManager.transferStatus(hash, fromChainId, toChainId);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ------ Transactions ------ //
|
|
||||||
|
|
||||||
placeOrder = (
|
|
||||||
callback: (
|
|
||||||
success: boolean,
|
|
||||||
parsingError: Nullable<ParsingError>,
|
|
||||||
data: Nullable<HumanReadablePlaceOrderPayload>
|
|
||||||
) => void
|
|
||||||
): Nullable<HumanReadablePlaceOrderPayload> => this.stateManager.commitPlaceOrder(callback);
|
|
||||||
|
|
||||||
closePosition = (
|
|
||||||
callback: (
|
|
||||||
success: boolean,
|
|
||||||
parsingError: Nullable<ParsingError>,
|
|
||||||
data: Nullable<HumanReadablePlaceOrderPayload>
|
|
||||||
) => void
|
|
||||||
): Nullable<HumanReadablePlaceOrderPayload> => this.stateManager.commitClosePosition(callback);
|
|
||||||
|
|
||||||
cancelOrder = (
|
|
||||||
orderId: string,
|
|
||||||
callback: (
|
|
||||||
success: boolean,
|
|
||||||
parsingError: Nullable<ParsingError>,
|
|
||||||
data: Nullable<HumanReadableCancelOrderPayload>
|
|
||||||
) => void
|
|
||||||
) => this.stateManager.cancelOrder(orderId, callback);
|
|
||||||
|
|
||||||
// ------ Utils ------ //
|
// ------ Utils ------ //
|
||||||
|
placeOrderPayload = (): Nullable<HumanReadablePlaceOrderPayload> =>
|
||||||
|
this.stateManager.placeOrderPayload();
|
||||||
|
|
||||||
|
closePositionPayload = (): Nullable<HumanReadablePlaceOrderPayload> =>
|
||||||
|
this.stateManager.closePositionPayload();
|
||||||
|
|
||||||
|
cancelOrderPayload = (orderId: string): Nullable<HumanReadableCancelOrderPayload> =>
|
||||||
|
this.stateManager.cancelOrderPayload(orderId);
|
||||||
|
|
||||||
getHistoricalPnlPeriod = (): Nullable<HistoricalPnlPeriods> =>
|
getHistoricalPnlPeriod = (): Nullable<HistoricalPnlPeriods> =>
|
||||||
this.stateManager.historicalPnlPeriod;
|
this.stateManager.historicalPnlPeriod;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Nullable, kollections } from '@dydxprotocol/v4-abacus';
|
import type { Nullable, kollections } from '@dydxprotocol/abacus';
|
||||||
|
|
||||||
import type { AbacusRestProtocol } from '@/constants/abacus';
|
import type { AbacusRestProtocol } from '@/constants/abacus';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { kollections } from '@dydxprotocol/v4-abacus';
|
import { kollections } from '@dydxprotocol/abacus';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
AccountBalance,
|
|
||||||
AbacusApiState,
|
AbacusApiState,
|
||||||
AbacusNotification,
|
AbacusNotification,
|
||||||
AbacusStateNotificationProtocol,
|
AbacusStateNotificationProtocol,
|
||||||
@@ -19,11 +18,9 @@ import { Changes } from '@/constants/abacus';
|
|||||||
import type { RootStore } from '@/state/_store';
|
import type { RootStore } from '@/state/_store';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
setBalances,
|
|
||||||
setFills,
|
setFills,
|
||||||
setFundingPayments,
|
setFundingPayments,
|
||||||
setHistoricalPnl,
|
setHistoricalPnl,
|
||||||
setLatestOrder,
|
|
||||||
setSubaccount,
|
setSubaccount,
|
||||||
setTransfers,
|
setTransfers,
|
||||||
setWallet,
|
setWallet,
|
||||||
@@ -74,16 +71,6 @@ class AbacusStateNotifier implements AbacusStateNotificationProtocol {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changes.has(Changes.accountBalances)) {
|
|
||||||
if (updatedState.account?.balances) {
|
|
||||||
const balances: Record<string, AccountBalance> = {}
|
|
||||||
for (const { k, v } of updatedState.account.balances.toArray()) {
|
|
||||||
balances[k] = v;
|
|
||||||
}
|
|
||||||
dispatch(setBalances(balances));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changes.has(Changes.configs)) {
|
if (changes.has(Changes.configs)) {
|
||||||
dispatch(setConfigs(updatedState.configs));
|
dispatch(setConfigs(updatedState.configs));
|
||||||
}
|
}
|
||||||
@@ -168,9 +155,7 @@ class AbacusStateNotifier implements AbacusStateNotificationProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lastOrderChanged(order: SubaccountOrder) {
|
lastOrderChanged(order: SubaccountOrder) {}
|
||||||
this.store?.dispatch(setLatestOrder(order));
|
|
||||||
}
|
|
||||||
|
|
||||||
errorsEmitted(errors: ParsingErrors) {
|
errorsEmitted(errors: ParsingErrors) {
|
||||||
console.error('parse errors', errors.toArray());
|
console.error('parse errors', errors.toArray());
|
||||||
|
|||||||
@@ -5,15 +5,10 @@ import type {
|
|||||||
AnalyticsEventData,
|
AnalyticsEventData,
|
||||||
} from '@/constants/analytics';
|
} from '@/constants/analytics';
|
||||||
|
|
||||||
const DEBUG_ANALYTICS = false;
|
|
||||||
|
|
||||||
export const identify = <T extends AnalyticsUserProperty>(
|
export const identify = <T extends AnalyticsUserProperty>(
|
||||||
property: T,
|
property: T,
|
||||||
propertyValue: AnalyticsUserPropertyValue<T>
|
propertyValue: AnalyticsUserPropertyValue<T>
|
||||||
) => {
|
) => {
|
||||||
if (DEBUG_ANALYTICS) {
|
|
||||||
console.log(`[Analytics:Identify] ${property}`, propertyValue);
|
|
||||||
}
|
|
||||||
const customEvent = new CustomEvent('dydx:identify', {
|
const customEvent = new CustomEvent('dydx:identify', {
|
||||||
detail: { property, propertyValue },
|
detail: { property, propertyValue },
|
||||||
});
|
});
|
||||||
@@ -25,9 +20,6 @@ export const track = <T extends AnalyticsEvent>(
|
|||||||
eventType: T,
|
eventType: T,
|
||||||
eventData?: AnalyticsEventData<T>
|
eventData?: AnalyticsEventData<T>
|
||||||
) => {
|
) => {
|
||||||
if (DEBUG_ANALYTICS) {
|
|
||||||
console.log(`[Analytics] ${eventType}`, eventData);
|
|
||||||
}
|
|
||||||
const customEvent = new CustomEvent('dydx:track', {
|
const customEvent = new CustomEvent('dydx:track', {
|
||||||
detail: { eventType, eventData },
|
detail: { eventType, eventData },
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-15
@@ -48,7 +48,7 @@ export const isNumber = (value: any): value is number =>
|
|||||||
/**
|
/**
|
||||||
* @description Returns null if input is 0 or null, '99+' if input is greater than 99, otherwise original input number
|
* @description Returns null if input is 0 or null, '99+' if input is greater than 99, otherwise original input number
|
||||||
*/
|
*/
|
||||||
export const shortenNumberForDisplay = (num?: number) =>
|
export const shorternNumberForDisplay = (num?: number) =>
|
||||||
MustBigNumber(num).eq(0) ? null : MustBigNumber(num).gt(99) ? '99+' : num;
|
MustBigNumber(num).eq(0) ? null : MustBigNumber(num).gt(99) ? '99+' : num;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,17 +66,3 @@ export const getSeparator = ({
|
|||||||
Intl.NumberFormat(browserLanguage)
|
Intl.NumberFormat(browserLanguage)
|
||||||
.formatToParts(1000.1)
|
.formatToParts(1000.1)
|
||||||
.find?.((part) => part.type === separatorType)?.value;
|
.find?.((part) => part.type === separatorType)?.value;
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a byte array (representing an arbitrary-size signed integer) into a bigint.
|
|
||||||
* @param u Array of bytes represented as a Uint8Array.
|
|
||||||
*/
|
|
||||||
export function bytesToBigInt(u: Uint8Array): bigint {
|
|
||||||
if (u.length <= 1) {
|
|
||||||
return BigInt(0);
|
|
||||||
}
|
|
||||||
const negated: boolean = (u[0] & 1) === 1;
|
|
||||||
const hex: string = Buffer.from(u.slice(1)).toString('hex');
|
|
||||||
const abs: bigint = BigInt(`0x${hex}`);
|
|
||||||
return negated ? -abs : abs;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -58,12 +58,6 @@ export const getStatusIconInfo = ({
|
|||||||
statusIconColor: `var(--color-negative)`,
|
statusIconColor: `var(--color-negative)`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case AbacusOrderStatus.untriggered: {
|
|
||||||
return {
|
|
||||||
statusIcon: IconName.OrderUntriggered,
|
|
||||||
statusIconColor: `var(--color-text-2)`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
case AbacusOrderStatus.pending:
|
case AbacusOrderStatus.pending:
|
||||||
default: {
|
default: {
|
||||||
return {
|
return {
|
||||||
|
|||||||
+3
-11
@@ -1,7 +1,5 @@
|
|||||||
export const log = (location: string, error: Error, metadata?: any) => {
|
export const log = (location: string, error: Error, metadata?: any) => {
|
||||||
if (import.meta.env.MODE !== 'production') {
|
// console.warn(error);
|
||||||
console.warn('telemetry/log:', { location, error, metadata });
|
|
||||||
}
|
|
||||||
|
|
||||||
const customEvent = new CustomEvent('dydx:log', {
|
const customEvent = new CustomEvent('dydx:log', {
|
||||||
detail: {
|
detail: {
|
||||||
@@ -18,10 +16,7 @@ export const log = (location: string, error: Error, metadata?: any) => {
|
|||||||
globalThis.addEventListener('unhandledrejection', (event) => {
|
globalThis.addEventListener('unhandledrejection', (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
log(
|
log('window/onunhandledrejection', new Error(`Promise rejected and uncaught; reason: ${event.reason}`));
|
||||||
'window/onunhandledrejection',
|
|
||||||
new Error(`Promise rejected and uncaught; reason: ${event.reason}`)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Log rejected Promises that are caught late (handler attached at a later time)
|
// Log rejected Promises that are caught late (handler attached at a later time)
|
||||||
@@ -30,10 +25,7 @@ globalThis.addEventListener(
|
|||||||
(event) => {
|
(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
log(
|
log('window/onrejectionhandled', new Error(`Promise rejected and caught late; reason: ${event.reason}`));
|
||||||
'window/onrejectionhandled',
|
|
||||||
new Error(`Promise rejected and caught late; reason: ${event.reason}`)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { Nullable } from '@dydxprotocol/v4-abacus';
|
import { Nullable } from '@dydxprotocol/abacus';
|
||||||
import styled, { AnyStyledComponent } from 'styled-components';
|
import styled, { AnyStyledComponent } from 'styled-components';
|
||||||
import { shallowEqual, useSelector } from 'react-redux';
|
import { shallowEqual, useSelector } from 'react-redux';
|
||||||
|
|
||||||
@@ -270,11 +270,11 @@ Styled.FeeTable = styled(Table)`
|
|||||||
--table-cell-align: end;
|
--table-cell-align: end;
|
||||||
|
|
||||||
font: var(--font-base-book);
|
font: var(--font-base-book);
|
||||||
|
|
||||||
tbody tr {
|
tbody tr {
|
||||||
&[data-yours='true'] {
|
&[data-yours='true'] {
|
||||||
background-color: var(--color-layer-3);
|
background-color: var(--color-layer-3);
|
||||||
|
|
||||||
td:first-child {
|
td:first-child {
|
||||||
border-left: 0.125rem solid var(--color-accent);
|
border-left: 0.125rem solid var(--color-accent);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { useState } from 'react';
|
|||||||
import { shallowEqual, useSelector } from 'react-redux';
|
import { shallowEqual, useSelector } from 'react-redux';
|
||||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||||
|
|
||||||
import { AbacusOrderStatus } from '@/constants/abacus';
|
|
||||||
import { STRING_KEYS } from '@/constants/localization';
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
|
|
||||||
import { useBreakpoints, useStringGetter } from '@/hooks';
|
import { useBreakpoints, useStringGetter } from '@/hooks';
|
||||||
@@ -19,23 +18,20 @@ import { FillsTable, FillsTableColumnKey } from '@/views/tables/FillsTable';
|
|||||||
import { OrdersTable, OrdersTableColumnKey } from '@/views/tables/OrdersTable';
|
import { OrdersTable, OrdersTableColumnKey } from '@/views/tables/OrdersTable';
|
||||||
import { PositionsTable, PositionsTableColumnKey } from '@/views/tables/PositionsTable';
|
import { PositionsTable, PositionsTableColumnKey } from '@/views/tables/PositionsTable';
|
||||||
|
|
||||||
import {
|
import { calculateIsAccountViewOnly } from '@/state/accountCalculators';
|
||||||
calculateHasUncommittedOrders,
|
|
||||||
calculateIsAccountViewOnly,
|
|
||||||
} from '@/state/accountCalculators';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
calculateHasUncommittedOrders,
|
||||||
getCurrentMarketTradeInfoNumbers,
|
getCurrentMarketTradeInfoNumbers,
|
||||||
getHasUnseenFillUpdates,
|
getHasUnseenFillUpdates,
|
||||||
getHasUnseenOrderUpdates,
|
getHasUnseenOrderUpdates,
|
||||||
getLatestOrderStatus,
|
|
||||||
getTradeInfoNumbers,
|
getTradeInfoNumbers,
|
||||||
} from '@/state/accountSelectors';
|
} from '@/state/accountSelectors';
|
||||||
|
|
||||||
import { getCurrentMarketAssetId, getCurrentMarketId } from '@/state/perpetualsSelectors';
|
import { getCurrentMarketAssetId, getCurrentMarketId } from '@/state/perpetualsSelectors';
|
||||||
|
|
||||||
import { isTruthy } from '@/lib/isTruthy';
|
import { isTruthy } from '@/lib/isTruthy';
|
||||||
import { shortenNumberForDisplay } from '@/lib/numbers';
|
import { shorternNumberForDisplay } from '@/lib/numbers';
|
||||||
|
|
||||||
import { PositionInfo } from '@/views/PositionInfo';
|
import { PositionInfo } from '@/views/PositionInfo';
|
||||||
|
|
||||||
@@ -73,12 +69,13 @@ export const HorizontalPanel = ({ isOpen = true, setIsOpen }: ElementProps) => {
|
|||||||
const hasUnseenOrderUpdates = useSelector(getHasUnseenOrderUpdates);
|
const hasUnseenOrderUpdates = useSelector(getHasUnseenOrderUpdates);
|
||||||
const hasUnseenFillUpdates = useSelector(getHasUnseenFillUpdates);
|
const hasUnseenFillUpdates = useSelector(getHasUnseenFillUpdates);
|
||||||
const isAccountViewOnly = useSelector(calculateIsAccountViewOnly);
|
const isAccountViewOnly = useSelector(calculateIsAccountViewOnly);
|
||||||
const isWaitingForOrderToIndex = useSelector(calculateHasUncommittedOrders);
|
const hasUncommittedOrders = useSelector(calculateHasUncommittedOrders);
|
||||||
|
|
||||||
const showCurrentMarket = isTablet || view === PanelView.CurrentMarket;
|
const showCurrentMarket = isTablet || view === PanelView.CurrentMarket;
|
||||||
|
|
||||||
const fillsTagNumber = shortenNumberForDisplay(showCurrentMarket ? numFills : numTotalFills);
|
const fillsTagNumber = shorternNumberForDisplay(showCurrentMarket ? numFills : numTotalFills);
|
||||||
|
|
||||||
const ordersTagNumber = shortenNumberForDisplay(
|
const ordersTagNumber = shorternNumberForDisplay(
|
||||||
showCurrentMarket ? numOpenOrders : numTotalOpenOrders
|
showCurrentMarket ? numOpenOrders : numTotalOpenOrders
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -89,7 +86,7 @@ export const HorizontalPanel = ({ isOpen = true, setIsOpen }: ElementProps) => {
|
|||||||
key: showCurrentMarket ? STRING_KEYS.POSITION : STRING_KEYS.POSITIONS,
|
key: showCurrentMarket ? STRING_KEYS.POSITION : STRING_KEYS.POSITIONS,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
tag: showCurrentMarket ? null : shortenNumberForDisplay(numTotalPositions),
|
tag: showCurrentMarket ? null : shorternNumberForDisplay(numTotalPositions),
|
||||||
|
|
||||||
content: showCurrentMarket ? (
|
content: showCurrentMarket ? (
|
||||||
<PositionInfo showNarrowVariation={isTablet} />
|
<PositionInfo showNarrowVariation={isTablet} />
|
||||||
@@ -121,7 +118,7 @@ export const HorizontalPanel = ({ isOpen = true, setIsOpen }: ElementProps) => {
|
|||||||
value: InfoSection.Orders,
|
value: InfoSection.Orders,
|
||||||
label: stringGetter({ key: STRING_KEYS.ORDERS }),
|
label: stringGetter({ key: STRING_KEYS.ORDERS }),
|
||||||
|
|
||||||
slotRight: isWaitingForOrderToIndex ? (
|
slotRight: hasUncommittedOrders ? (
|
||||||
<Styled.LoadingSpinner />
|
<Styled.LoadingSpinner />
|
||||||
) : (
|
) : (
|
||||||
ordersTagNumber && (
|
ordersTagNumber && (
|
||||||
@@ -193,7 +190,7 @@ export const HorizontalPanel = ({ isOpen = true, setIsOpen }: ElementProps) => {
|
|||||||
// value: InfoSection.Payments,
|
// value: InfoSection.Payments,
|
||||||
// label: stringGetter({ key: STRING_KEYS.PAYMENTS }),
|
// label: stringGetter({ key: STRING_KEYS.PAYMENTS }),
|
||||||
|
|
||||||
// tag: shortenNumberForDisplay(
|
// tag: shorternNumberForDisplay(
|
||||||
// showCurrentMarket ? numFundingPayments : numTotalFundingPayments
|
// showCurrentMarket ? numFundingPayments : numTotalFundingPayments
|
||||||
// ),
|
// ),
|
||||||
// content: (
|
// content: (
|
||||||
|
|||||||
+4
-1
@@ -1,9 +1,9 @@
|
|||||||
import { configureStore } from '@reduxjs/toolkit';
|
import { configureStore } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
import abacusStateManager from '@/lib/abacus';
|
|
||||||
import appMiddleware from './appMiddleware';
|
import appMiddleware from './appMiddleware';
|
||||||
import localizationMiddleware from './localizationMiddleware';
|
import localizationMiddleware from './localizationMiddleware';
|
||||||
import routerMiddleware from './routerMiddleware';
|
import routerMiddleware from './routerMiddleware';
|
||||||
|
import tradeMiddleware from './tradeMiddleware';
|
||||||
|
|
||||||
import { accountSlice } from './account';
|
import { accountSlice } from './account';
|
||||||
import { appSlice } from './app';
|
import { appSlice } from './app';
|
||||||
@@ -16,6 +16,8 @@ import { localizationSlice } from './localization';
|
|||||||
import { navigationSlice } from './navigation';
|
import { navigationSlice } from './navigation';
|
||||||
import { perpetualsSlice } from './perpetuals';
|
import { perpetualsSlice } from './perpetuals';
|
||||||
|
|
||||||
|
import abacusStateManager from '@/lib/abacus';
|
||||||
|
|
||||||
export const commandMenuSlices = [layoutSlice, localizationSlice];
|
export const commandMenuSlices = [layoutSlice, localizationSlice];
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
@@ -39,6 +41,7 @@ export const store = configureStore({
|
|||||||
appMiddleware,
|
appMiddleware,
|
||||||
localizationMiddleware,
|
localizationMiddleware,
|
||||||
routerMiddleware,
|
routerMiddleware,
|
||||||
|
tradeMiddleware,
|
||||||
],
|
],
|
||||||
|
|
||||||
devTools: process.env.NODE_ENV !== 'production',
|
devTools: process.env.NODE_ENV !== 'production',
|
||||||
|
|||||||
+15
-31
@@ -1,7 +1,7 @@
|
|||||||
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
|
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
AccountBalance,
|
|
||||||
SubaccountFill,
|
SubaccountFill,
|
||||||
Nullable,
|
Nullable,
|
||||||
Subaccount,
|
Subaccount,
|
||||||
@@ -25,18 +25,16 @@ export type AccountState = {
|
|||||||
fundingPayments?: SubaccountFundingPayments;
|
fundingPayments?: SubaccountFundingPayments;
|
||||||
transfers?: SubaccountTransfers;
|
transfers?: SubaccountTransfers;
|
||||||
clearedOrderIds?: string[];
|
clearedOrderIds?: string[];
|
||||||
|
uncommittedOrderClientIds?: number[];
|
||||||
hasUnseenFillUpdates: boolean;
|
hasUnseenFillUpdates: boolean;
|
||||||
hasUnseenOrderUpdates: boolean;
|
hasUnseenOrderUpdates: boolean;
|
||||||
historicalPnl?: SubAccountHistoricalPNLs;
|
historicalPnl?: SubAccountHistoricalPNLs;
|
||||||
latestOrder?: Nullable<SubaccountOrder>;
|
|
||||||
onboardingGuards: Record<OnboardingGuard, boolean | undefined>;
|
onboardingGuards: Record<OnboardingGuard, boolean | undefined>;
|
||||||
onboardingState: OnboardingState;
|
onboardingState: OnboardingState;
|
||||||
subaccount?: Nullable<Subaccount>;
|
subaccount?: Nullable<Subaccount>;
|
||||||
uncommittedOrderClientIds: number[];
|
|
||||||
wallet?: Nullable<Wallet>;
|
wallet?: Nullable<Wallet>;
|
||||||
walletType?: WalletType;
|
walletType?: WalletType;
|
||||||
historicalPnlPeriod?: HistoricalPnlPeriods;
|
historicalPnlPeriod?: HistoricalPnlPeriods;
|
||||||
balances?: Record<string, AccountBalance>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialState: AccountState = {
|
const initialState: AccountState = {
|
||||||
@@ -44,10 +42,10 @@ const initialState: AccountState = {
|
|||||||
fundingPayments: undefined,
|
fundingPayments: undefined,
|
||||||
transfers: undefined,
|
transfers: undefined,
|
||||||
clearedOrderIds: undefined,
|
clearedOrderIds: undefined,
|
||||||
|
uncommittedOrderClientIds: undefined,
|
||||||
hasUnseenFillUpdates: false,
|
hasUnseenFillUpdates: false,
|
||||||
hasUnseenOrderUpdates: false,
|
hasUnseenOrderUpdates: false,
|
||||||
historicalPnl: undefined,
|
historicalPnl: undefined,
|
||||||
latestOrder: undefined,
|
|
||||||
onboardingGuards: {
|
onboardingGuards: {
|
||||||
[OnboardingGuard.hasAcknowledgedTerms]: Boolean(
|
[OnboardingGuard.hasAcknowledgedTerms]: Boolean(
|
||||||
getLocalStorage<boolean>({
|
getLocalStorage<boolean>({
|
||||||
@@ -59,7 +57,6 @@ const initialState: AccountState = {
|
|||||||
},
|
},
|
||||||
onboardingState: OnboardingState.Disconnected,
|
onboardingState: OnboardingState.Disconnected,
|
||||||
subaccount: undefined,
|
subaccount: undefined,
|
||||||
uncommittedOrderClientIds: [],
|
|
||||||
wallet: undefined,
|
wallet: undefined,
|
||||||
walletType: getLocalStorage<WalletType>({
|
walletType: getLocalStorage<WalletType>({
|
||||||
key: LocalStorageKey.OnboardingSelectedWalletType,
|
key: LocalStorageKey.OnboardingSelectedWalletType,
|
||||||
@@ -90,16 +87,6 @@ export const accountSlice = createSlice({
|
|||||||
setTransfers: (state, action: PayloadAction<any>) => {
|
setTransfers: (state, action: PayloadAction<any>) => {
|
||||||
state.transfers = action.payload;
|
state.transfers = action.payload;
|
||||||
},
|
},
|
||||||
setLatestOrder: (state, action: PayloadAction<Nullable<SubaccountOrder>>) => {
|
|
||||||
const { clientId } = action.payload ?? {};
|
|
||||||
state.latestOrder = action.payload;
|
|
||||||
|
|
||||||
if (clientId) {
|
|
||||||
state.uncommittedOrderClientIds = state.uncommittedOrderClientIds.filter(
|
|
||||||
(id) => id !== clientId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
clearOrder: (state, action: PayloadAction<string>) => ({
|
clearOrder: (state, action: PayloadAction<string>) => ({
|
||||||
...state,
|
...state,
|
||||||
clearedOrderIds: [...(state.clearedOrderIds || []), action.payload],
|
clearedOrderIds: [...(state.clearedOrderIds || []), action.payload],
|
||||||
@@ -145,23 +132,22 @@ export const accountSlice = createSlice({
|
|||||||
...state,
|
...state,
|
||||||
wallet: action.payload,
|
wallet: action.payload,
|
||||||
}),
|
}),
|
||||||
|
addUncommittedOrderClientId: (state, action: PayloadAction<number>) => {
|
||||||
|
state.uncommittedOrderClientIds = state.uncommittedOrderClientIds
|
||||||
|
? [...state.uncommittedOrderClientIds, action.payload]
|
||||||
|
: [action.payload];
|
||||||
|
},
|
||||||
|
removeUncommittedOrderClientId: (state, action: PayloadAction<number>) => {
|
||||||
|
state.uncommittedOrderClientIds = state.uncommittedOrderClientIds?.filter(
|
||||||
|
(clientId) => clientId !== action.payload
|
||||||
|
);
|
||||||
|
},
|
||||||
viewedFills: (state) => {
|
viewedFills: (state) => {
|
||||||
state.hasUnseenFillUpdates = false;
|
state.hasUnseenFillUpdates = false;
|
||||||
},
|
},
|
||||||
viewedOrders: (state) => {
|
viewedOrders: (state) => {
|
||||||
state.hasUnseenOrderUpdates = false;
|
state.hasUnseenOrderUpdates = false;
|
||||||
},
|
},
|
||||||
setBalances: (state, action: PayloadAction<Record<string, AccountBalance>>) => {
|
|
||||||
state.balances = action.payload;
|
|
||||||
},
|
|
||||||
addUncommittedOrderClientId: (state, action: PayloadAction<number>) => {
|
|
||||||
state.uncommittedOrderClientIds.push(action.payload);
|
|
||||||
},
|
|
||||||
removeUncommittedOrderClientId: (state, action: PayloadAction<number>) => {
|
|
||||||
state.uncommittedOrderClientIds = state.uncommittedOrderClientIds.filter(
|
|
||||||
(id) => id !== action.payload
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -169,16 +155,14 @@ export const {
|
|||||||
setFills,
|
setFills,
|
||||||
setFundingPayments,
|
setFundingPayments,
|
||||||
setTransfers,
|
setTransfers,
|
||||||
setLatestOrder,
|
|
||||||
clearOrder,
|
clearOrder,
|
||||||
setOnboardingGuard,
|
setOnboardingGuard,
|
||||||
setOnboardingState,
|
setOnboardingState,
|
||||||
setHistoricalPnl,
|
setHistoricalPnl,
|
||||||
setSubaccount,
|
setSubaccount,
|
||||||
setWallet,
|
setWallet,
|
||||||
viewedFills,
|
|
||||||
viewedOrders,
|
|
||||||
setBalances,
|
|
||||||
addUncommittedOrderClientId,
|
addUncommittedOrderClientId,
|
||||||
removeUncommittedOrderClientId,
|
removeUncommittedOrderClientId,
|
||||||
|
viewedFills,
|
||||||
|
viewedOrders,
|
||||||
} = accountSlice.actions;
|
} = accountSlice.actions;
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
getOnboardingGuards,
|
getOnboardingGuards,
|
||||||
getOnboardingState,
|
getOnboardingState,
|
||||||
getSubaccountId,
|
getSubaccountId,
|
||||||
getUncommittedOrderClientIds,
|
|
||||||
} from '@/state/accountSelectors';
|
} from '@/state/accountSelectors';
|
||||||
|
|
||||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||||
@@ -70,11 +69,6 @@ export const calculateHasOpenPositions = createSelector(
|
|||||||
(openPositions?: SubaccountPosition[]) => (openPositions?.length || 0) > 0
|
(openPositions?: SubaccountPosition[]) => (openPositions?.length || 0) > 0
|
||||||
);
|
);
|
||||||
|
|
||||||
export const calculateHasUncommittedOrders = createSelector(
|
|
||||||
[getUncommittedOrderClientIds],
|
|
||||||
(uncommittedOrderClientIds: number[]) => uncommittedOrderClientIds.length > 0
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description calculate whether the client is loading info.
|
* @description calculate whether the client is loading info.
|
||||||
* (account is connected but subaccountId is till missing)
|
* (account is connected but subaccountId is till missing)
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ import {
|
|||||||
|
|
||||||
import { OnboardingState } from '@/constants/account';
|
import { OnboardingState } from '@/constants/account';
|
||||||
|
|
||||||
import { getHydratedTradingData } from '@/lib/orders';
|
|
||||||
import type { RootState } from './_store';
|
import type { RootState } from './_store';
|
||||||
|
|
||||||
|
import { getHydratedTradingData } from '@/lib/orders';
|
||||||
|
|
||||||
import { getCurrentMarketId, getPerpetualMarkets } from './perpetualsSelectors';
|
import { getCurrentMarketId, getPerpetualMarkets } from './perpetualsSelectors';
|
||||||
import { getAssets } from './assetsSelectors';
|
import { getAssets } from './assetsSelectors';
|
||||||
|
|
||||||
@@ -78,12 +79,6 @@ export const getCurrentMarketPositionData = (state: RootState) => {
|
|||||||
export const getSubaccountOrders = (state: RootState) =>
|
export const getSubaccountOrders = (state: RootState) =>
|
||||||
state.account.subaccount?.orders?.toArray();
|
state.account.subaccount?.orders?.toArray();
|
||||||
|
|
||||||
/**
|
|
||||||
* @param state
|
|
||||||
* @returns latestOrder of the currently connected subaccount throughout this session
|
|
||||||
*/
|
|
||||||
export const getLatestOrder = (state: RootState) => state.account?.latestOrder;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param state
|
* @param state
|
||||||
* @returns list of order ids that user has cleared and should be hidden
|
* @returns list of order ids that user has cleared and should be hidden
|
||||||
@@ -111,46 +106,25 @@ export const getSubaccountOpenOrdersBySideAndPrice = createSelector(
|
|||||||
[getSubaccountOpenOrders],
|
[getSubaccountOpenOrders],
|
||||||
(openOrders = []) => {
|
(openOrders = []) => {
|
||||||
const ordersBySide: Partial<Record<OrderSide, Record<number, SubaccountOrder>>> = {};
|
const ordersBySide: Partial<Record<OrderSide, Record<number, SubaccountOrder>>> = {};
|
||||||
openOrders.forEach((order) => {
|
for (const order of openOrders) {
|
||||||
const side = ORDER_SIDES[order.side.name];
|
const side = ORDER_SIDES[order.side.name];
|
||||||
const byPrice = (ordersBySide[side] ??= {});
|
const byPrice = (ordersBySide[side] ??= {});
|
||||||
byPrice[order.price] = order;
|
byPrice[order.price] = order;
|
||||||
});
|
}
|
||||||
return ordersBySide;
|
return ordersBySide;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns the clientId of the latest order
|
* @param state
|
||||||
*/
|
|
||||||
export const getLatestOrderClientId = createSelector([getLatestOrder], (order) => order?.clientId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns the rawValue status of the latest order
|
|
||||||
*/
|
|
||||||
export const getLatestOrderStatus = createSelector(
|
|
||||||
[getLatestOrder],
|
|
||||||
(order) => order?.status.rawValue
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns a list of clientIds belonging to uncommmited orders
|
|
||||||
*/
|
|
||||||
export const getUncommittedOrderClientIds = (state: RootState) =>
|
|
||||||
state.account.uncommittedOrderClientIds;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param orderId
|
|
||||||
* @returns order details with the given orderId
|
* @returns order details with the given orderId
|
||||||
*/
|
*/
|
||||||
export const getOrderDetails = (orderId: string) =>
|
export const getOrderDetails = (orderId: string) =>
|
||||||
createSelector(
|
createSelector(
|
||||||
[getSubaccountOrders, getAssets, getPerpetualMarkets],
|
[getSubaccountOrders, getAssets, getPerpetualMarkets],
|
||||||
(orders, assets, perpetualMarkets) => {
|
(orders, assets, perpetualMarkets) => {
|
||||||
const matchingOrder = orders?.find((order) => order.id === orderId);
|
const order = orders?.find((order) => order.id === orderId);
|
||||||
return matchingOrder
|
return order ? getHydratedTradingData({ data: order, assets, perpetualMarkets }) : undefined;
|
||||||
? getHydratedTradingData({ data: matchingOrder, assets, perpetualMarkets })
|
|
||||||
: undefined;
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -204,10 +178,8 @@ export const getFillDetails = (fillId: string) =>
|
|||||||
createSelector(
|
createSelector(
|
||||||
[getSubaccountFills, getAssets, getPerpetualMarkets],
|
[getSubaccountFills, getAssets, getPerpetualMarkets],
|
||||||
(fills, assets, perpetualMarkets) => {
|
(fills, assets, perpetualMarkets) => {
|
||||||
const matchingFill = fills?.find((fill) => fill.id === fillId);
|
const fill = fills?.find((fill) => fill.id === fillId);
|
||||||
return matchingFill
|
return fill ? getHydratedTradingData({ data: fill, assets, perpetualMarkets }) : undefined;
|
||||||
? getHydratedTradingData({ data: matchingFill, assets, perpetualMarkets })
|
|
||||||
: undefined;
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -308,6 +280,21 @@ export const getIsAccountConnected = (state: RootState) =>
|
|||||||
*/
|
*/
|
||||||
export const getOnboardingGuards = (state: RootState) => state.account.onboardingGuards;
|
export const getOnboardingGuards = (state: RootState) => state.account.onboardingGuards;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param state
|
||||||
|
* @returns a boolean indicating whether the user has uncommitted orders
|
||||||
|
*/
|
||||||
|
export const calculateHasUncommittedOrders = (state: RootState) => {
|
||||||
|
return !!state.account.uncommittedOrderClientIds?.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param state
|
||||||
|
* @returns List of uncommitted order clientIds
|
||||||
|
*/
|
||||||
|
export const getUncommittedOrderClientIds = (state: RootState) =>
|
||||||
|
state.account.uncommittedOrderClientIds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param state
|
* @param state
|
||||||
* @returns Whether there are unseen fill updates
|
* @returns Whether there are unseen fill updates
|
||||||
@@ -332,8 +319,3 @@ export const getUserStats = (state: RootState) => ({
|
|||||||
makerVolume30D: state.account?.wallet?.user?.makerVolume30D,
|
makerVolume30D: state.account?.wallet?.user?.makerVolume30D,
|
||||||
takerVolume30D: state.account?.wallet?.user?.takerVolume30D,
|
takerVolume30D: state.account?.wallet?.user?.takerVolume30D,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns user wallet balances
|
|
||||||
*/
|
|
||||||
export const getBalances = (state: RootState) => state.account?.balances;
|
|
||||||
|
|||||||
+6
-2
@@ -47,5 +47,9 @@ export const appSlice = createSlice({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { initializeLocalization, initializeWebsocket, setApiState, setSelectedNetwork } =
|
export const {
|
||||||
appSlice.actions;
|
initializeLocalization,
|
||||||
|
initializeWebsocket,
|
||||||
|
setApiState,
|
||||||
|
setSelectedNetwork,
|
||||||
|
} = appSlice.actions;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
|
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
|
||||||
import type { kollections } from '@dydxprotocol/v4-abacus';
|
import type { kollections } from '@dydxprotocol/abacus';
|
||||||
|
|
||||||
import type { Configs, FeeDiscount, FeeTier, NetworkConfigs, Nullable } from '@/constants/abacus';
|
import type { Configs, FeeDiscount, FeeTier, NetworkConfigs, Nullable } from '@/constants/abacus';
|
||||||
import { LocalStorageKey } from '@/constants/localStorage';
|
import { LocalStorageKey } from '@/constants/localStorage';
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ import { type RootState } from './_store';
|
|||||||
|
|
||||||
export const getActiveDialog = (state: RootState) => state.dialogs.activeDialog;
|
export const getActiveDialog = (state: RootState) => state.dialogs.activeDialog;
|
||||||
|
|
||||||
export const getActiveTradeBoxDialog = (state: RootState) => state.dialogs.activeTradeBoxDialog;
|
export const getActiveTradeBoxDialog = (state: RootState) => state.dialogs.activeTradeBoxDialog;
|
||||||
@@ -40,7 +40,7 @@ export const getStringGetterForLocaleData = (localeData: LocaleData) => {
|
|||||||
params?: { [key: string]: string | React.ReactNode };
|
params?: { [key: string]: string | React.ReactNode };
|
||||||
}): string | Array<string | React.ReactNode> => {
|
}): string | Array<string | React.ReactNode> => {
|
||||||
// Fallback to english whenever a key doesn't exist for other languages
|
// Fallback to english whenever a key doesn't exist for other languages
|
||||||
const formattedString: string = _.get(localeData, key) || _.get(EN_LOCALE_DATA, key) || '';
|
let formattedString: string = _.get(localeData, key) || _.get(EN_LOCALE_DATA, key) || '';
|
||||||
|
|
||||||
return formatString(formattedString, params);
|
return formatString(formattedString, params);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
|
import { matchPath } from 'react-router-dom';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
import { Candle, TradingViewBar } from '@/constants/candles';
|
import { Candle, TradingViewBar } from '@/constants/candles';
|
||||||
|
import { TRADE_ROUTE } from '@/constants/routes';
|
||||||
import { mapCandle } from '@/lib/tradingView/utils';
|
|
||||||
|
|
||||||
import type { RootState } from './_store';
|
import type { RootState } from './_store';
|
||||||
|
import { mapCandle } from '@/lib/tradingView/utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param state
|
* @param state
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
import type { SubaccountOrder } from '@/constants/abacus';
|
||||||
|
|
||||||
|
import { removeUncommittedOrderClientId, setSubaccount } from '@/state/account';
|
||||||
|
|
||||||
|
import { getSubaccountOrders, getUncommittedOrderClientIds } from '@/state/accountSelectors';
|
||||||
|
|
||||||
|
export default (store: any) => (next: any) => async (action: PayloadAction<any>) => {
|
||||||
|
const { type, payload } = action;
|
||||||
|
const state = store.getState();
|
||||||
|
next(action);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case setSubaccount.type: {
|
||||||
|
const updatedOrders = payload?.orders?.toArray() || [];
|
||||||
|
const orders = getSubaccountOrders(state);
|
||||||
|
const uncommittedOrderClientIds = getUncommittedOrderClientIds(state);
|
||||||
|
|
||||||
|
if (uncommittedOrderClientIds?.length && updatedOrders?.length !== orders?.length) {
|
||||||
|
const updatedOrdersClientIds = Object.fromEntries(
|
||||||
|
updatedOrders.map((order: SubaccountOrder) => [order.clientId, order])
|
||||||
|
);
|
||||||
|
|
||||||
|
const receivedClientId = uncommittedOrderClientIds.find(
|
||||||
|
(clientId) => updatedOrdersClientIds[clientId]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (receivedClientId) {
|
||||||
|
store.dispatch(removeUncommittedOrderClientId(receivedClientId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { css } from 'styled-components';
|
import styled, { css, type FlattenSimpleInterpolation } from 'styled-components';
|
||||||
|
|
||||||
import breakpoints from './breakpoints';
|
import breakpoints from './breakpoints';
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -6,6 +6,8 @@ import {
|
|||||||
type ThemeProps,
|
type ThemeProps,
|
||||||
} from 'styled-components';
|
} from 'styled-components';
|
||||||
|
|
||||||
|
import breakpoints from './breakpoints';
|
||||||
|
|
||||||
export const layoutMixins: Record<
|
export const layoutMixins: Record<
|
||||||
string,
|
string,
|
||||||
FlattenSimpleInterpolation | FlattenInterpolation<ThemeProps<any>>
|
FlattenSimpleInterpolation | FlattenInterpolation<ThemeProps<any>>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
type ThemeProps,
|
type ThemeProps,
|
||||||
} from 'styled-components';
|
} from 'styled-components';
|
||||||
|
|
||||||
|
import breakpoints from './breakpoints';
|
||||||
import { layoutMixins } from './layoutMixins';
|
import { layoutMixins } from './layoutMixins';
|
||||||
|
|
||||||
export const tableMixins: Record<
|
export const tableMixins: Record<
|
||||||
|
|||||||
@@ -62,10 +62,8 @@ export const AccountInfoConnectedState = () => {
|
|||||||
const { buyingPower, equity, marginUsage, leverage } = subAccount || {};
|
const { buyingPower, equity, marginUsage, leverage } = subAccount || {};
|
||||||
|
|
||||||
const hasDiff =
|
const hasDiff =
|
||||||
(marginUsage?.postOrder !== null &&
|
marginUsage?.postOrder != null &&
|
||||||
!MustBigNumber(marginUsage?.postOrder).eq(MustBigNumber(marginUsage?.current))) ||
|
!MustBigNumber(marginUsage?.postOrder).eq(MustBigNumber(marginUsage?.current));
|
||||||
(buyingPower?.postOrder !== null &&
|
|
||||||
!MustBigNumber(buyingPower?.postOrder).eq(MustBigNumber(buyingPower?.current)));
|
|
||||||
|
|
||||||
const showHeader = !hasDiff && !isTablet;
|
const showHeader = !hasDiff && !isTablet;
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ export const MarketDetails: React.FC = () => {
|
|||||||
if (!configs) return null;
|
if (!configs) return null;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
basePositionNotional,
|
|
||||||
tickSize,
|
tickSize,
|
||||||
stepSize,
|
stepSize,
|
||||||
initialMarginFraction,
|
initialMarginFraction,
|
||||||
@@ -114,17 +113,11 @@ export const MarketDetails: React.FC = () => {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'base-initial-margin-fraction',
|
key: 'initial-margin-fraction',
|
||||||
label: stringGetter({ key: STRING_KEYS.BASE_INITIAL_MARGIN_FRACTION }),
|
label: stringGetter({ key: STRING_KEYS.INITIAL_MARGIN_FRACTION }),
|
||||||
tooltip: 'initial-margin-fraction',
|
tooltip: 'initial-margin-fraction',
|
||||||
value: <Output useGrouping value={initialMarginFraction} type={OutputType.SmallPercent} />,
|
value: <Output useGrouping value={initialMarginFraction} type={OutputType.SmallPercent} />,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: 'base-position-notional',
|
|
||||||
label: stringGetter({ key: STRING_KEYS.BASE_POSITION_NOTIONAL }),
|
|
||||||
tooltip: 'base-position-notional',
|
|
||||||
value: <Output useGrouping value={basePositionNotional} type={OutputType.Number} />,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -15,17 +15,14 @@ export const MarketLinks = () => {
|
|||||||
|
|
||||||
const linkItems = [
|
const linkItems = [
|
||||||
{
|
{
|
||||||
key: 'coinmarketcap',
|
|
||||||
href: coinMarketCapsLink,
|
href: coinMarketCapsLink,
|
||||||
icon: IconName.CoinMarketCap,
|
icon: IconName.CoinMarketCap,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'whitepaper',
|
|
||||||
href: whitepaperLink,
|
href: whitepaperLink,
|
||||||
icon: IconName.Whitepaper,
|
icon: IconName.Whitepaper,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'project-website',
|
|
||||||
href: websiteLink,
|
href: websiteLink,
|
||||||
icon: IconName.Website,
|
icon: IconName.Website,
|
||||||
},
|
},
|
||||||
@@ -34,8 +31,8 @@ export const MarketLinks = () => {
|
|||||||
return (
|
return (
|
||||||
<Styled.MarketLinks>
|
<Styled.MarketLinks>
|
||||||
{linkItems.map(
|
{linkItems.map(
|
||||||
({ key, href, icon }) =>
|
({ href, icon }) =>
|
||||||
href && <IconButton key={key} href={href} iconName={icon} type={ButtonType.Link} />
|
href && <IconButton key={href} href={href} iconName={icon} type={ButtonType.Link} />
|
||||||
)}
|
)}
|
||||||
</Styled.MarketLinks>
|
</Styled.MarketLinks>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ const useTradeTypeOptions = () => {
|
|||||||
value && {
|
value && {
|
||||||
value: value as TradeTypes,
|
value: value as TradeTypes,
|
||||||
label,
|
label,
|
||||||
|
disabled: import.meta.env.MODE === 'production', // TODO: Remove this when stop orders are supported on testnet
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.filter(isTruthy),
|
.filter(isTruthy),
|
||||||
|
|||||||
@@ -7,11 +7,9 @@ import { StatusResponse } from '@0xsquid/sdk';
|
|||||||
import { useInterval, useStringGetter } from '@/hooks';
|
import { useInterval, useStringGetter } from '@/hooks';
|
||||||
|
|
||||||
import { STRING_KEYS } from '@/constants/localization';
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
import { AlertType } from '@/constants/alerts';
|
|
||||||
|
|
||||||
import { formatSeconds } from '@/lib/timeUtils';
|
import { formatSeconds } from '@/lib/timeUtils';
|
||||||
|
|
||||||
import { AlertMessage } from '@/components/AlertMessage';
|
|
||||||
import { Output, OutputType } from '@/components/Output';
|
import { Output, OutputType } from '@/components/Output';
|
||||||
import { WithReceipt } from '@/components/WithReceipt';
|
import { WithReceipt } from '@/components/WithReceipt';
|
||||||
import { Icon, IconName } from '@/components/Icon';
|
import { Icon, IconName } from '@/components/Icon';
|
||||||
@@ -35,9 +33,6 @@ export const TransferStatusToast = ({
|
|||||||
const [open, setOpen] = useState<boolean>(false);
|
const [open, setOpen] = useState<boolean>(false);
|
||||||
const [secondsLeft, setSecondsLeft] = useState<number | undefined>();
|
const [secondsLeft, setSecondsLeft] = useState<number | undefined>();
|
||||||
|
|
||||||
// @ts-ignore status.errors is not in the type definition but can be returned
|
|
||||||
const error = status?.errors?.length ? status?.errors[0] : status?.error;
|
|
||||||
|
|
||||||
const type = useMemo(
|
const type = useMemo(
|
||||||
() => (status?.toChain?.chainData?.chainId === TESTNET_CHAIN_ID ? 'deposit' : 'withdrawal'),
|
() => (status?.toChain?.chainData?.chainId === TESTNET_CHAIN_ID ? 'deposit' : 'withdrawal'),
|
||||||
[status]
|
[status]
|
||||||
@@ -79,16 +74,6 @@ export const TransferStatusToast = ({
|
|||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
</Styled.Status>
|
</Styled.Status>
|
||||||
{error && (
|
|
||||||
<AlertMessage type={AlertType.Error}>
|
|
||||||
{stringGetter({
|
|
||||||
key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE,
|
|
||||||
params: {
|
|
||||||
ERROR_MESSAGE: error.message || stringGetter({ key: STRING_KEYS.UNKNOWN_ERROR }),
|
|
||||||
},
|
|
||||||
})}
|
|
||||||
</AlertMessage>
|
|
||||||
)}
|
|
||||||
<Styled.Trigger>
|
<Styled.Trigger>
|
||||||
<Styled.TriggerIcon>
|
<Styled.TriggerIcon>
|
||||||
<Icon iconName={IconName.Caret} />
|
<Icon iconName={IconName.Caret} />
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export const TransferStatusSteps = ({ status }: ElementProps) => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const currentStatus = routeStatus?.[routeStatus?.length - 1];
|
const currentStatus = routeStatus[routeStatus?.length - 1];
|
||||||
|
|
||||||
let currentStep = TransferStatusStep.Bridge;
|
let currentStep = TransferStatusStep.Bridge;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { layoutMixins } from '@/styles/layoutMixins';
|
|||||||
|
|
||||||
import { AbacusOrderStatus, AbacusOrderTypes, type Nullable } from '@/constants/abacus';
|
import { AbacusOrderStatus, AbacusOrderTypes, type Nullable } from '@/constants/abacus';
|
||||||
import { ButtonAction } from '@/constants/buttons';
|
import { ButtonAction } from '@/constants/buttons';
|
||||||
import { STRING_KEYS, type StringKey } from '@/constants/localization';
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
|
|
||||||
import { AssetIcon } from '@/components/AssetIcon';
|
import { AssetIcon } from '@/components/AssetIcon';
|
||||||
import { Button } from '@/components/Button';
|
import { Button } from '@/components/Button';
|
||||||
@@ -47,7 +47,7 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
|
|||||||
const { cancelOrder } = useSubaccount();
|
const { cancelOrder } = useSubaccount();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
asset,
|
asset = {},
|
||||||
cancelReason,
|
cancelReason,
|
||||||
createdAtMilliseconds,
|
createdAtMilliseconds,
|
||||||
expiresAtMilliseconds,
|
expiresAtMilliseconds,
|
||||||
@@ -57,7 +57,7 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
|
|||||||
price,
|
price,
|
||||||
reduceOnly,
|
reduceOnly,
|
||||||
totalFilled,
|
totalFilled,
|
||||||
resources,
|
resources = {},
|
||||||
size,
|
size,
|
||||||
status,
|
status,
|
||||||
stepSizeDecimals,
|
stepSizeDecimals,
|
||||||
@@ -121,9 +121,7 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
|
|||||||
{
|
{
|
||||||
key: 'cancel-reason',
|
key: 'cancel-reason',
|
||||||
label: stringGetter({ key: STRING_KEYS.CANCEL_REASON }),
|
label: stringGetter({ key: STRING_KEYS.CANCEL_REASON }),
|
||||||
value: cancelReason
|
value: cancelReason ? stringGetter({ key: STRING_KEYS[cancelReason] }) : undefined,
|
||||||
? stringGetter({ key: STRING_KEYS[cancelReason as StringKey] })
|
|
||||||
: undefined,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'amount',
|
key: 'amount',
|
||||||
@@ -184,7 +182,8 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
|
|||||||
|
|
||||||
const onCancelClick = async () => {
|
const onCancelClick = async () => {
|
||||||
setIsPlacingCancel(true);
|
setIsPlacingCancel(true);
|
||||||
await cancelOrder({ orderId, onError: () => setIsPlacingCancel(false) });
|
await cancelOrder({ orderId });
|
||||||
|
setIsPlacingCancel(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClearClick = () => {
|
const onClearClick = () => {
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import styled, { AnyStyledComponent } from 'styled-components';
|
import styled, { AnyStyledComponent } from 'styled-components';
|
||||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
import { AbacusApiStatus } from '@/constants/abacus';
|
import { AbacusApiStatus } from '@/constants/abacus';
|
||||||
import { DialogTypes } from '@/constants/dialogs';
|
import { CLIENT_NETWORK_CONFIGS } from '@/constants/networks';
|
||||||
|
import { UNICODE } from '@/constants/unicode';
|
||||||
|
|
||||||
import { STRING_KEYS } from '@/constants/localization';
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
import { useApiState, useStringGetter } from '@/hooks';
|
import { useApiState, useStringGetter } from '@/hooks';
|
||||||
import { layoutMixins } from '@/styles/layoutMixins';
|
import { layoutMixins } from '@/styles/layoutMixins';
|
||||||
@@ -15,7 +17,6 @@ import { NetworkSelectMenu } from '@/views/menus/NetworkSelectMenu';
|
|||||||
import { closeDialog } from '@/state/dialogs';
|
import { closeDialog } from '@/state/dialogs';
|
||||||
|
|
||||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||||
import { getActiveDialog } from '@/state/dialogsSelectors';
|
|
||||||
|
|
||||||
type ElementProps = {
|
type ElementProps = {
|
||||||
preventClose?: boolean;
|
preventClose?: boolean;
|
||||||
@@ -27,10 +28,9 @@ export const ExchangeOfflineDialog = ({ preventClose, setIsOpen }: ElementProps)
|
|||||||
const stringGetter = useStringGetter();
|
const stringGetter = useStringGetter();
|
||||||
const { status, statusErrorMessage } = useApiState();
|
const { status, statusErrorMessage } = useApiState();
|
||||||
const selectedNetwork = useSelector(getSelectedNetwork);
|
const selectedNetwork = useSelector(getSelectedNetwork);
|
||||||
const activeDialog = useSelector(getActiveDialog, shallowEqual);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeDialog?.type === DialogTypes.ExchangeOffline && status === AbacusApiStatus.NORMAL) {
|
if (status === AbacusApiStatus.NORMAL) {
|
||||||
dispatch(closeDialog());
|
dispatch(closeDialog());
|
||||||
}
|
}
|
||||||
}, [status, selectedNetwork]);
|
}, [status, selectedNetwork]);
|
||||||
@@ -44,6 +44,11 @@ export const ExchangeOfflineDialog = ({ preventClose, setIsOpen }: ElementProps)
|
|||||||
>
|
>
|
||||||
<Styled.Content>
|
<Styled.Content>
|
||||||
<p>{statusErrorMessage}</p>
|
<p>{statusErrorMessage}</p>
|
||||||
|
{CLIENT_NETWORK_CONFIGS[selectedNetwork].dydxChainId === 'dydx-testnet-2' && (
|
||||||
|
<Styled.Link href="https://status.v4testnet.dydx.exchange">
|
||||||
|
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} {UNICODE.ARROW_RIGHT}
|
||||||
|
</Styled.Link>
|
||||||
|
)}
|
||||||
{import.meta.env.MODE !== 'production' && <NetworkSelectMenu />}
|
{import.meta.env.MODE !== 'production' && <NetworkSelectMenu />}
|
||||||
</Styled.Content>
|
</Styled.Content>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { Ring } from '@/components/Ring';
|
|||||||
import { ToggleGroup } from '@/components/ToggleGroup';
|
import { ToggleGroup } from '@/components/ToggleGroup';
|
||||||
|
|
||||||
import { getCurrentMarketAssetData } from '@/state/assetsSelectors';
|
import { getCurrentMarketAssetData } from '@/state/assetsSelectors';
|
||||||
import { getInputTradeData, getInputTradeOptions } from '@/state/inputsSelectors';
|
import { getInputTradeData } from '@/state/inputsSelectors';
|
||||||
|
|
||||||
import abacusStateManager from '@/lib/abacus';
|
import abacusStateManager from '@/lib/abacus';
|
||||||
import { getSelectedTradeType } from '@/lib/tradeData';
|
import { getSelectedTradeType } from '@/lib/tradeData';
|
||||||
@@ -35,15 +35,6 @@ export const TradeDialog = ({ isOpen, setIsOpen, slotTrigger }: ElementProps) =>
|
|||||||
const currentTradeData = useSelector(getInputTradeData, shallowEqual);
|
const currentTradeData = useSelector(getInputTradeData, shallowEqual);
|
||||||
const { type } = currentTradeData || {};
|
const { type } = currentTradeData || {};
|
||||||
const selectedTradeType = getSelectedTradeType(type);
|
const selectedTradeType = getSelectedTradeType(type);
|
||||||
const { typeOptions } = useSelector(getInputTradeOptions, shallowEqual) ?? {};
|
|
||||||
|
|
||||||
const allTradeTypeItems = typeOptions?.toArray()?.map(({ type, stringKey }) => ({
|
|
||||||
value: type,
|
|
||||||
label: stringGetter({
|
|
||||||
key: stringKey,
|
|
||||||
}),
|
|
||||||
slotBefore: <AssetIcon symbol={symbol} />,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const [currentStep, setCurrentStep] = useState<MobilePlaceOrderSteps>(
|
const [currentStep, setCurrentStep] = useState<MobilePlaceOrderSteps>(
|
||||||
MobilePlaceOrderSteps.EditOrder
|
MobilePlaceOrderSteps.EditOrder
|
||||||
@@ -71,7 +62,11 @@ export const TradeDialog = ({ isOpen, setIsOpen, slotTrigger }: ElementProps) =>
|
|||||||
[MobilePlaceOrderSteps.EditOrder]: {
|
[MobilePlaceOrderSteps.EditOrder]: {
|
||||||
title: (
|
title: (
|
||||||
<Styled.ToggleGroup
|
<Styled.ToggleGroup
|
||||||
items={allTradeTypeItems}
|
items={[TradeTypes.LIMIT, TradeTypes.MARKET].map((tradeType: TradeTypes) => ({
|
||||||
|
value: tradeType,
|
||||||
|
label: stringGetter({ key: TRADE_TYPE_STRINGS[tradeType].tradeTypeKey }),
|
||||||
|
slotBefore: <AssetIcon symbol={symbol} />,
|
||||||
|
}))}
|
||||||
value={selectedTradeType}
|
value={selectedTradeType}
|
||||||
onValueChange={(tradeType: TradeTypes) =>
|
onValueChange={(tradeType: TradeTypes) =>
|
||||||
onTradeTypeChange(tradeType || selectedTradeType)
|
onTradeTypeChange(tradeType || selectedTradeType)
|
||||||
@@ -131,8 +126,6 @@ Styled.Dialog = styled(Dialog)<{ currentStep: MobilePlaceOrderSteps }>`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
Styled.ToggleGroup = styled(ToggleGroup)`
|
Styled.ToggleGroup = styled(ToggleGroup)`
|
||||||
overflow-x: auto;
|
|
||||||
|
|
||||||
button[data-state='off'] {
|
button[data-state='off'] {
|
||||||
gap: 0;
|
gap: 0;
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ import { getTransferInputs } from '@/state/inputsSelectors';
|
|||||||
import abacusStateManager from '@/lib/abacus';
|
import abacusStateManager from '@/lib/abacus';
|
||||||
import { MustBigNumber } from '@/lib/numbers';
|
import { MustBigNumber } from '@/lib/numbers';
|
||||||
import { log } from '@/lib/telemetry';
|
import { log } from '@/lib/telemetry';
|
||||||
import { parseWalletError } from '@/lib/wallet';
|
|
||||||
|
|
||||||
import { ChainSelectMenu } from './ChainSelectMenu';
|
import { ChainSelectMenu } from './ChainSelectMenu';
|
||||||
import { TokenSelectMenu } from './TokenSelectMenu';
|
import { TokenSelectMenu } from './TokenSelectMenu';
|
||||||
@@ -287,7 +286,12 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
|||||||
|
|
||||||
const errorMessage = useMemo(() => {
|
const errorMessage = useMemo(() => {
|
||||||
if (error) {
|
if (error) {
|
||||||
return parseWalletError({ error, stringGetter }).message;
|
return error?.message
|
||||||
|
? stringGetter({
|
||||||
|
key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE,
|
||||||
|
params: { ERROR_MESSAGE: error.message },
|
||||||
|
})
|
||||||
|
: stringGetter({ key: STRING_KEYS.SOMETHING_WENT_WRONG });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fromAmount) {
|
if (fromAmount) {
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export const WithdrawForm = () => {
|
|||||||
const [error, setError] = useState<Error | null>(null);
|
const [error, setError] = useState<Error | null>(null);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const { sendSquidWithdraw } = useSubaccount();
|
const { simulateWithdraw, sendSquidWithdraw } = useSubaccount();
|
||||||
const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {};
|
const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {};
|
||||||
|
|
||||||
// User input
|
// User input
|
||||||
@@ -97,10 +97,16 @@ export const WithdrawForm = () => {
|
|||||||
field: TransferInputField.usdcSize,
|
field: TransferInputField.usdcSize,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
const stdFee = await simulateWithdraw(parseFloat(debouncedAmount));
|
||||||
|
const amount =
|
||||||
|
parseFloat(debouncedAmount) -
|
||||||
|
parseFloat(stdFee?.amount[0]?.amount || '0') / QUANTUM_MULTIPLIER;
|
||||||
|
|
||||||
abacusStateManager.setTransferValue({
|
abacusStateManager.setTransferValue({
|
||||||
value: debouncedAmount,
|
value: amount.toString(),
|
||||||
field: TransferInputField.usdcSize,
|
field: TransferInputField.usdcSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
setError(null);
|
setError(null);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -49,15 +49,15 @@ export const WithdrawButtonAndReceipt = ({
|
|||||||
const [isEditingSlippage, setIsEditingSlipapge] = useState(false);
|
const [isEditingSlippage, setIsEditingSlipapge] = useState(false);
|
||||||
const stringGetter = useStringGetter();
|
const stringGetter = useStringGetter();
|
||||||
|
|
||||||
// TODO: uncomment when we have a way to get token amount estimate from abacus
|
const { balance, queryStatus } = useAccountBalance({
|
||||||
// const { balance, queryStatus } = useAccountBalance({
|
addressOrDenom: withdrawToken?.address || undefined,
|
||||||
// addressOrDenom: withdrawToken?.address || undefined,
|
assetSymbol: withdrawToken?.symbol || undefined,
|
||||||
// assetSymbol: withdrawToken?.symbol || undefined,
|
chainId: withdrawChain ? parseInt(withdrawChain) : undefined,
|
||||||
// chainId: withdrawChain ? parseInt(withdrawChain) : undefined,
|
decimals: withdrawToken?.decimals || undefined,
|
||||||
// decimals: withdrawToken?.decimals || undefined,
|
isCosmosChain: false,
|
||||||
// isCosmosChain: false,
|
});
|
||||||
// });
|
|
||||||
|
|
||||||
|
// TODO: uncomment when we have a way to get token amount estimate from abacus
|
||||||
// const balanceBN = MustBigNumber(balance);
|
// const balanceBN = MustBigNumber(balance);
|
||||||
// const newBalance =
|
// const newBalance =
|
||||||
// // toAmountMin && withdrawToken && parseUnits(toAmountMin, withdrawToken.decimals).toString();
|
// // toAmountMin && withdrawToken && parseUnits(toAmountMin, withdrawToken.decimals).toString();
|
||||||
@@ -158,7 +158,7 @@ export const WithdrawButtonAndReceipt = ({
|
|||||||
// },
|
// },
|
||||||
];
|
];
|
||||||
|
|
||||||
const isFormValid = !isDisabled && !isEditingSlippage;
|
const isFormValid = !isDisabled && !isEditingSlippage && queryStatus !== 'error';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Styled.WithReceipt
|
<Styled.WithReceipt
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
import { type FormEvent, useCallback, useEffect, useState } from 'react';
|
import { type FormEvent, useEffect, useState } from 'react';
|
||||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
import {
|
import { ClosePositionInputField } from '@/constants/abacus';
|
||||||
ClosePositionInputField,
|
|
||||||
type HumanReadablePlaceOrderPayload,
|
|
||||||
type Nullable,
|
|
||||||
} from '@/constants/abacus';
|
|
||||||
import { AlertType } from '@/constants/alerts';
|
import { AlertType } from '@/constants/alerts';
|
||||||
import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons';
|
import { ButtonAction, ButtonShape, ButtonSize, ButtonType } from '@/constants/buttons';
|
||||||
import { TOKEN_DECIMALS } from '@/constants/numbers';
|
import { TOKEN_DECIMALS } from '@/constants/numbers';
|
||||||
import { STRING_KEYS } from '@/constants/localization';
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
import { MobilePlaceOrderSteps } from '@/constants/trade';
|
import { MobilePlaceOrderSteps } from '@/constants/trade';
|
||||||
import { useBreakpoints, useIsFirstRender, useStringGetter, useSubaccount } from '@/hooks';
|
import { useBreakpoints, useIsFirstRender, useStringGetter, useSubaccount } from '@/hooks';
|
||||||
import { useOnLastOrderIndexed } from '@/hooks/useOnLastOrderIndexed';
|
|
||||||
|
|
||||||
import { breakpoints } from '@/styles';
|
import { breakpoints } from '@/styles';
|
||||||
import { layoutMixins } from '@/styles/layoutMixins';
|
import { layoutMixins } from '@/styles/layoutMixins';
|
||||||
@@ -32,7 +27,10 @@ import { Orderbook, orderbookMixins, type OrderbookScrollBehavior } from '@/view
|
|||||||
|
|
||||||
import { PositionPreview } from '@/views/forms/TradeForm/PositionPreview';
|
import { PositionPreview } from '@/views/forms/TradeForm/PositionPreview';
|
||||||
|
|
||||||
import { getCurrentMarketPositionData } from '@/state/accountSelectors';
|
import {
|
||||||
|
calculateHasUncommittedOrders,
|
||||||
|
getCurrentMarketPositionData,
|
||||||
|
} from '@/state/accountSelectors';
|
||||||
|
|
||||||
import { getCurrentMarketAssetData } from '@/state/assetsSelectors';
|
import { getCurrentMarketAssetData } from '@/state/assetsSelectors';
|
||||||
import { getInputClosePositionData } from '@/state/inputsSelectors';
|
import { getInputClosePositionData } from '@/state/inputsSelectors';
|
||||||
@@ -84,7 +82,9 @@ export const ClosePositionForm = ({
|
|||||||
const { stepSizeDecimals } = useSelector(getCurrentMarketConfig, shallowEqual) || {};
|
const { stepSizeDecimals } = useSelector(getCurrentMarketConfig, shallowEqual) || {};
|
||||||
const { size: sizeData, summary } = useSelector(getInputClosePositionData, shallowEqual) || {};
|
const { size: sizeData, summary } = useSelector(getInputClosePositionData, shallowEqual) || {};
|
||||||
const { size, percent } = sizeData || {};
|
const { size, percent } = sizeData || {};
|
||||||
|
const hasUncommittedOrders = useSelector(calculateHasUncommittedOrders);
|
||||||
const currentInput = useSelector(getCurrentInput);
|
const currentInput = useSelector(getCurrentInput);
|
||||||
|
|
||||||
const currentPositionData = useSelector(getCurrentMarketPositionData, shallowEqual);
|
const currentPositionData = useSelector(getCurrentMarketPositionData, shallowEqual);
|
||||||
const { size: currentPositionSize } = currentPositionData || {};
|
const { size: currentPositionSize } = currentPositionData || {};
|
||||||
const { current: currentSize } = currentPositionSize || {};
|
const { current: currentSize } = currentPositionSize || {};
|
||||||
@@ -106,22 +106,17 @@ export const ClosePositionForm = ({
|
|||||||
}
|
}
|
||||||
}, [currentInput, market, currentStep]);
|
}, [currentInput, market, currentStep]);
|
||||||
|
|
||||||
const onLastOrderIndexed = useCallback(() => {
|
useEffect(() => {
|
||||||
if (!isFirstRender) {
|
// close has been placed
|
||||||
|
if (!isFirstRender && !hasUncommittedOrders) {
|
||||||
abacusStateManager.clearClosePositionInputValues({ shouldFocusOnTradeInput: true });
|
abacusStateManager.clearClosePositionInputValues({ shouldFocusOnTradeInput: true });
|
||||||
onClosePositionSuccess?.();
|
onClosePositionSuccess?.();
|
||||||
|
|
||||||
if (currentStep === MobilePlaceOrderSteps.PlacingOrder) {
|
if (currentStep === MobilePlaceOrderSteps.PlacingOrder) {
|
||||||
setCurrentStep?.(MobilePlaceOrderSteps.Confirmation);
|
setCurrentStep?.(MobilePlaceOrderSteps.Confirmation);
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsClosingPosition(false);
|
|
||||||
}
|
}
|
||||||
}, [currentStep, isFirstRender]);
|
}, [hasUncommittedOrders]);
|
||||||
|
|
||||||
const { setUnIndexedClientId } = useOnLastOrderIndexed({
|
|
||||||
callback: onLastOrderIndexed,
|
|
||||||
});
|
|
||||||
|
|
||||||
const onAmountInput = ({ floatValue }: { floatValue?: number }) => {
|
const onAmountInput = ({ floatValue }: { floatValue?: number }) => {
|
||||||
if (currentSize == null) return;
|
if (currentSize == null) return;
|
||||||
@@ -170,16 +165,14 @@ export const ClosePositionForm = ({
|
|||||||
setIsClosingPosition(true);
|
setIsClosingPosition(true);
|
||||||
|
|
||||||
await closePosition({
|
await closePosition({
|
||||||
onError: (errorParams?: { errorStringKey?: Nullable<string> }) => {
|
onError: (errorParams?: { errorStringKey?: string }) => {
|
||||||
setClosePositionError(
|
setClosePositionError(
|
||||||
stringGetter({ key: errorParams?.errorStringKey || STRING_KEYS.SOMETHING_WENT_WRONG })
|
stringGetter({ key: errorParams?.errorStringKey || STRING_KEYS.SOMETHING_WENT_WRONG })
|
||||||
);
|
);
|
||||||
setIsClosingPosition(false);
|
|
||||||
},
|
|
||||||
onSuccess: (placeOrderPayload: Nullable<HumanReadablePlaceOrderPayload>) => {
|
|
||||||
setUnIndexedClientId(placeOrderPayload?.clientId);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setIsClosingPosition(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const alertMessage = closePositionError && (
|
const alertMessage = closePositionError && (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type FormEvent, useState, Ref, useCallback } from 'react';
|
import { type FormEvent, useState, useEffect, Ref } from 'react';
|
||||||
import styled, { AnyStyledComponent, css } from 'styled-components';
|
import styled, { AnyStyledComponent, css } from 'styled-components';
|
||||||
import { shallowEqual, useSelector } from 'react-redux';
|
import { shallowEqual, useSelector } from 'react-redux';
|
||||||
import type { NumberFormatValues, SourceInfo } from 'react-number-format';
|
import type { NumberFormatValues, SourceInfo } from 'react-number-format';
|
||||||
@@ -7,8 +7,6 @@ import { AlertType } from '@/constants/alerts';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
ErrorType,
|
ErrorType,
|
||||||
type HumanReadablePlaceOrderPayload,
|
|
||||||
type Nullable,
|
|
||||||
TradeInputErrorAction,
|
TradeInputErrorAction,
|
||||||
TradeInputField,
|
TradeInputField,
|
||||||
ValidationError,
|
ValidationError,
|
||||||
@@ -21,7 +19,6 @@ import { InputErrorData, TradeBoxKeys, MobilePlaceOrderSteps } from '@/constants
|
|||||||
|
|
||||||
import { breakpoints } from '@/styles';
|
import { breakpoints } from '@/styles';
|
||||||
import { useStringGetter, useSubaccount } from '@/hooks';
|
import { useStringGetter, useSubaccount } from '@/hooks';
|
||||||
import { useOnLastOrderIndexed } from '@/hooks/useOnLastOrderIndexed';
|
|
||||||
|
|
||||||
import { layoutMixins } from '@/styles/layoutMixins';
|
import { layoutMixins } from '@/styles/layoutMixins';
|
||||||
import { formMixins } from '@/styles/formMixins';
|
import { formMixins } from '@/styles/formMixins';
|
||||||
@@ -37,6 +34,7 @@ import { WithTooltip } from '@/components/WithTooltip';
|
|||||||
|
|
||||||
import { Orderbook } from '@/views/tables/Orderbook';
|
import { Orderbook } from '@/views/tables/Orderbook';
|
||||||
|
|
||||||
|
import { calculateHasUncommittedOrders } from '@/state/accountSelectors';
|
||||||
import { getCurrentInput, useTradeFormData } from '@/state/inputsSelectors';
|
import { getCurrentInput, useTradeFormData } from '@/state/inputsSelectors';
|
||||||
import { getCurrentMarketConfig } from '@/state/perpetualsSelectors';
|
import { getCurrentMarketConfig } from '@/state/perpetualsSelectors';
|
||||||
|
|
||||||
@@ -99,6 +97,7 @@ export const TradeForm = ({
|
|||||||
} = useTradeFormData();
|
} = useTradeFormData();
|
||||||
|
|
||||||
const { limitPrice, triggerPrice, trailingPercent } = price || {};
|
const { limitPrice, triggerPrice, trailingPercent } = price || {};
|
||||||
|
const hasUncommittedOrders = useSelector(calculateHasUncommittedOrders);
|
||||||
const currentInput = useSelector(getCurrentInput);
|
const currentInput = useSelector(getCurrentInput);
|
||||||
const { tickSizeDecimals, stepSizeDecimals } =
|
const { tickSizeDecimals, stepSizeDecimals } =
|
||||||
useSelector(getCurrentMarketConfig, shallowEqual) || {};
|
useSelector(getCurrentMarketConfig, shallowEqual) || {};
|
||||||
@@ -155,33 +154,30 @@ export const TradeForm = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onLastOrderIndexed = useCallback(() => {
|
useEffect(() => {
|
||||||
if (!currentStep || currentStep === MobilePlaceOrderSteps.PlacingOrder) {
|
// order has been placed
|
||||||
|
if (
|
||||||
|
(!currentStep || currentStep === MobilePlaceOrderSteps.PlacingOrder) &&
|
||||||
|
!hasUncommittedOrders
|
||||||
|
) {
|
||||||
setIsPlacingOrder(false);
|
setIsPlacingOrder(false);
|
||||||
abacusStateManager.clearTradeInputValues({ shouldResetSize: true });
|
abacusStateManager.clearTradeInputValues({ shouldResetSize: true });
|
||||||
setCurrentStep?.(MobilePlaceOrderSteps.Confirmation);
|
setCurrentStep?.(MobilePlaceOrderSteps.Confirmation);
|
||||||
}
|
}
|
||||||
}, [currentStep]);
|
}, [currentStep, hasUncommittedOrders]);
|
||||||
|
|
||||||
const { setUnIndexedClientId } = useOnLastOrderIndexed({
|
|
||||||
callback: onLastOrderIndexed,
|
|
||||||
});
|
|
||||||
|
|
||||||
const onPlaceOrder = async () => {
|
const onPlaceOrder = async () => {
|
||||||
setPlaceOrderError(undefined);
|
setPlaceOrderError(undefined);
|
||||||
setIsPlacingOrder(true);
|
setIsPlacingOrder(true);
|
||||||
|
|
||||||
await placeOrder({
|
await placeOrder({
|
||||||
onError: (errorParams?: { errorStringKey?: Nullable<string> }) => {
|
onError: (errorParams?: { errorStringKey?: string }) => {
|
||||||
setPlaceOrderError(
|
setPlaceOrderError(
|
||||||
stringGetter({ key: errorParams?.errorStringKey || STRING_KEYS.SOMETHING_WENT_WRONG })
|
stringGetter({ key: errorParams?.errorStringKey || STRING_KEYS.SOMETHING_WENT_WRONG })
|
||||||
);
|
);
|
||||||
|
|
||||||
setIsPlacingOrder(false);
|
setIsPlacingOrder(false);
|
||||||
},
|
},
|
||||||
onSuccess: (placeOrderPayload?: Nullable<HumanReadablePlaceOrderPayload>) => {
|
|
||||||
setUnIndexedClientId(placeOrderPayload?.clientId);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ export const PlaceOrderButtonAndReceipt = ({
|
|||||||
|
|
||||||
const buttonState = currentStep
|
const buttonState = currentStep
|
||||||
? buttonStatesPerStep[currentStep].buttonState
|
? buttonStatesPerStep[currentStep].buttonState
|
||||||
: { isDisabled: !shouldEnableTrade || isLoading, isLoading };
|
: { isDisabled: !shouldEnableTrade, isLoading };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WithDetailsReceipt detailItems={items}>
|
<WithDetailsReceipt detailItems={items}>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { ButtonShape, ButtonSize } from '@/constants/buttons';
|
|||||||
import { STRING_KEYS } from '@/constants/localization';
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
import { CLIENT_NETWORK_CONFIGS } from '@/constants/networks';
|
import { CLIENT_NETWORK_CONFIGS } from '@/constants/networks';
|
||||||
import { NumberSign, QUANTUM_MULTIPLIER } from '@/constants/numbers';
|
import { NumberSign, QUANTUM_MULTIPLIER } from '@/constants/numbers';
|
||||||
import { DYDX_CHAIN_ASSET_COIN_DENOM, DYDX_CHAIN_ASSET_TAGS, DydxChainAsset } from '@/constants/wallets';
|
import { DYDX_CHAIN_ASSET_COIN_DENOM, DYDX_CHAIN_INFO, DydxChainAsset } from '@/constants/wallets';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useAccountBalance,
|
useAccountBalance,
|
||||||
@@ -52,6 +52,28 @@ type TransferFormProps = {
|
|||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const debouncedEstimateFee = debounce(
|
||||||
|
async ({ amount, recipientAddress, asset, setFees, simulateTransfer }) => {
|
||||||
|
if (!amount || !recipientAddress) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const coinDenom = DYDX_CHAIN_ASSET_COIN_DENOM[asset as DydxChainAsset];
|
||||||
|
const stdFee: StdFee = await simulateTransfer(amount, recipientAddress, coinDenom);
|
||||||
|
|
||||||
|
const fee = stdFee?.amount.find((coin) => coin.denom === coinDenom)?.amount;
|
||||||
|
const feeAmount = MustBigNumber(fee).div(QUANTUM_MULTIPLIER).toNumber();
|
||||||
|
|
||||||
|
setFees(feeAmount);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('TransferForm > : debouncedEstimateFee > ', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
1000,
|
||||||
|
{ trailing: true }
|
||||||
|
);
|
||||||
|
|
||||||
export const TransferForm = ({
|
export const TransferForm = ({
|
||||||
selectedAsset = DydxChainAsset.DYDX,
|
selectedAsset = DydxChainAsset.DYDX,
|
||||||
onDone,
|
onDone,
|
||||||
@@ -60,8 +82,8 @@ export const TransferForm = ({
|
|||||||
const stringGetter = useStringGetter();
|
const stringGetter = useStringGetter();
|
||||||
const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {};
|
const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {};
|
||||||
const { dydxAddress } = useAccounts();
|
const { dydxAddress } = useAccounts();
|
||||||
const { address: recipientAddress, size, fee } = useSelector(getTransferInputs, shallowEqual) || {};
|
const { address: recipientAddress, size } = useSelector(getTransferInputs, shallowEqual) || {};
|
||||||
const { transfer } = useSubaccount();
|
const { transfer, simulateTransfer } = useSubaccount();
|
||||||
const { nativeTokenBalance, usdcBalance } = useAccountBalance();
|
const { nativeTokenBalance, usdcBalance } = useAccountBalance();
|
||||||
const { selectedNetwork } = useSelectedNetwork();
|
const { selectedNetwork } = useSelectedNetwork();
|
||||||
|
|
||||||
@@ -71,6 +93,7 @@ export const TransferForm = ({
|
|||||||
// Form states
|
// Form states
|
||||||
const [error, setError] = useState<Error | undefined>();
|
const [error, setError] = useState<Error | undefined>();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [fees, setFees] = useState<number>();
|
||||||
|
|
||||||
const balance = asset === DydxChainAsset.USDC ? freeCollateral?.current : nativeTokenBalance;
|
const balance = asset === DydxChainAsset.USDC ? freeCollateral?.current : nativeTokenBalance;
|
||||||
const newBalance =
|
const newBalance =
|
||||||
@@ -81,7 +104,7 @@ export const TransferForm = ({
|
|||||||
.toNumber();
|
.toNumber();
|
||||||
const amount = asset === DydxChainAsset.USDC ? size?.usdcSize : size?.size;
|
const amount = asset === DydxChainAsset.USDC ? size?.usdcSize : size?.size;
|
||||||
|
|
||||||
const showNotEnoughGasWarning = fee && asset === DydxChainAsset.USDC && usdcBalance < fee;
|
const showNotEnoughGasWarning = fees && asset === DydxChainAsset.USDC && usdcBalance < fees;
|
||||||
|
|
||||||
// BN
|
// BN
|
||||||
const amountBN = MustBigNumber(amount);
|
const amountBN = MustBigNumber(amount);
|
||||||
@@ -111,21 +134,34 @@ export const TransferForm = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
abacusStateManager.setTransferValue({
|
debouncedEstimateFee.cancel();
|
||||||
value: asset,
|
|
||||||
field: TransferInputField.token,
|
if (isAmountValid && isAddressValid) {
|
||||||
});
|
debouncedEstimateFee({
|
||||||
|
amount,
|
||||||
|
recipientAddress,
|
||||||
|
asset,
|
||||||
|
setFees,
|
||||||
|
simulateTransfer,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setFees(undefined);
|
||||||
|
}
|
||||||
|
}, [asset, amount, recipientAddress]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setError(undefined);
|
||||||
}, [asset]);
|
}, [asset]);
|
||||||
|
|
||||||
const onTransfer = async () => {
|
const onTransfer = async () => {
|
||||||
if (!isAmountValid || !isAddressValid || !fee) return;
|
if (!isAmountValid || !isAddressValid || !fees) return;
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Subtract fees from amount if sending native tokens
|
// Subtract fees from amount if sending native tokens
|
||||||
const amountToTransfer = (
|
const amountToTransfer = (
|
||||||
asset === DydxChainAsset.DYDX ? amountBN.minus(fee) : amountBN
|
asset === DydxChainAsset.DYDX ? amountBN.minus(fees) : amountBN
|
||||||
).toNumber();
|
).toNumber();
|
||||||
|
|
||||||
const txResponse = await transfer(
|
const txResponse = await transfer(
|
||||||
@@ -176,7 +212,7 @@ export const TransferForm = ({
|
|||||||
value: DydxChainAsset.USDC,
|
value: DydxChainAsset.USDC,
|
||||||
label: (
|
label: (
|
||||||
<Styled.InlineRow>
|
<Styled.InlineRow>
|
||||||
<AssetIcon symbol="USDC" /> {DYDX_CHAIN_ASSET_TAGS[DydxChainAsset.USDC]}
|
<AssetIcon symbol="USDC" /> USDC
|
||||||
</Styled.InlineRow>
|
</Styled.InlineRow>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -185,7 +221,7 @@ export const TransferForm = ({
|
|||||||
label: (
|
label: (
|
||||||
<Styled.InlineRow>
|
<Styled.InlineRow>
|
||||||
{/* <AssetIcon symbol="DYDX" /> */}
|
{/* <AssetIcon symbol="DYDX" /> */}
|
||||||
{DYDX_CHAIN_ASSET_TAGS[DydxChainAsset.DYDX]}
|
Dv4TNT
|
||||||
</Styled.InlineRow>
|
</Styled.InlineRow>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -207,7 +243,7 @@ export const TransferForm = ({
|
|||||||
key: 'amount',
|
key: 'amount',
|
||||||
label: (
|
label: (
|
||||||
<span>
|
<span>
|
||||||
{stringGetter({ key: STRING_KEYS.AVAILABLE })} <Tag>{DYDX_CHAIN_ASSET_TAGS[asset]}</Tag>
|
{stringGetter({ key: STRING_KEYS.AVAILABLE })} <Tag>{asset}</Tag>
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
value: (
|
value: (
|
||||||
@@ -339,9 +375,9 @@ export const TransferForm = ({
|
|||||||
<Styled.Footer>
|
<Styled.Footer>
|
||||||
<TransferButtonAndReceipt
|
<TransferButtonAndReceipt
|
||||||
selectedAsset={asset}
|
selectedAsset={asset}
|
||||||
fees={fee || undefined}
|
fees={fees}
|
||||||
isDisabled={!isAmountValid || !isAddressValid || !fee}
|
isDisabled={!isAmountValid || !isAddressValid || !fees}
|
||||||
isLoading={isLoading || Boolean(isAmountValid && isAddressValid && !fee)}
|
isLoading={isLoading || Boolean(isAmountValid && isAddressValid && !fees)}
|
||||||
/>
|
/>
|
||||||
</Styled.Footer>
|
</Styled.Footer>
|
||||||
</Styled.Form>
|
</Styled.Form>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { shallowEqual, useSelector } from 'react-redux';
|
|||||||
import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
|
import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
|
||||||
import { STRING_KEYS } from '@/constants/localization';
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
import { NumberSign } from '@/constants/numbers';
|
import { NumberSign } from '@/constants/numbers';
|
||||||
import { DYDX_CHAIN_ASSET_TAGS, DydxChainAsset } from '@/constants/wallets';
|
import { DydxChainAsset } from '@/constants/wallets';
|
||||||
|
|
||||||
import { useAccountBalance, useStringGetter } from '@/hooks';
|
import { useAccountBalance, useStringGetter } from '@/hooks';
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ export const TransferButtonAndReceipt = ({
|
|||||||
key: 'fees',
|
key: 'fees',
|
||||||
label: (
|
label: (
|
||||||
<span>
|
<span>
|
||||||
{stringGetter({ key: STRING_KEYS.FEES })} <Tag>{DYDX_CHAIN_ASSET_TAGS[selectedAsset]}</Tag>
|
{stringGetter({ key: STRING_KEYS.FEES })} <Tag>{selectedAsset}</Tag>
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
value: <Output type={OutputType.Asset} value={fees} />,
|
value: <Output type={OutputType.Asset} value={fees} />,
|
||||||
@@ -69,7 +69,7 @@ export const TransferButtonAndReceipt = ({
|
|||||||
key: 'balance',
|
key: 'balance',
|
||||||
label: (
|
label: (
|
||||||
<span>
|
<span>
|
||||||
{stringGetter({ key: STRING_KEYS.BALANCE })} <Tag>{DYDX_CHAIN_ASSET_TAGS[selectedAsset]}</Tag>
|
{stringGetter({ key: STRING_KEYS.BALANCE })} <Tag>{selectedAsset}</Tag>
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
value: (
|
value: (
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import styled, { type AnyStyledComponent, css } from 'styled-components';
|
|||||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||||
import { OrderSide } from '@dydxprotocol/v4-client-js';
|
import { OrderSide } from '@dydxprotocol/v4-client-js';
|
||||||
import type { ColumnSize } from '@react-types/table';
|
import type { ColumnSize } from '@react-types/table';
|
||||||
import { Nullable } from '@dydxprotocol/v4-abacus';
|
|
||||||
|
|
||||||
import { type Asset, type SubaccountFill } from '@/constants/abacus';
|
import { type Asset, type SubaccountFill } from '@/constants/abacus';
|
||||||
import { DialogTypes } from '@/constants/dialogs';
|
import { DialogTypes } from '@/constants/dialogs';
|
||||||
import { STRING_KEYS, StringGetterFunction } from '@/constants/localization';
|
import { STRING_KEYS, StringGetterFunction } from '@/constants/localization';
|
||||||
|
|
||||||
|
import { Nullable } from '@dydxprotocol/abacus';
|
||||||
import { useBreakpoints, useStringGetter } from '@/hooks';
|
import { useBreakpoints, useStringGetter } from '@/hooks';
|
||||||
|
|
||||||
import { layoutMixins } from '@/styles/layoutMixins';
|
import { layoutMixins } from '@/styles/layoutMixins';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import styled, { AnyStyledComponent } from 'styled-components';
|
import styled, { AnyStyledComponent } from 'styled-components';
|
||||||
|
|
||||||
import { AbacusOrderStatus, type OrderStatus } from '@/constants/abacus';
|
import { AbacusOrderStatus, type OrderStatus } from '@/constants/abacus';
|
||||||
@@ -29,7 +29,8 @@ export const OrderActionsCell = ({ orderId, status, isDisabled }: ElementProps)
|
|||||||
|
|
||||||
const onCancel = useCallback(async () => {
|
const onCancel = useCallback(async () => {
|
||||||
setIsCanceling(true);
|
setIsCanceling(true);
|
||||||
await cancelOrder({ orderId, onError: () => setIsCanceling(false) });
|
await cancelOrder({ orderId });
|
||||||
|
setIsCanceling(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user