Compare commits

...
Author SHA1 Message Date
Bill He 73eb7aff96 remove wc1 2023-10-12 15:22:42 -07:00
Bill He 6a3c4ec7f6 remove .env 2023-10-12 15:17:56 -07:00
Bill He 47b6618299 gitignore .env 2023-10-12 15:16:11 -07:00
Bill He def44ad66e remove WC 1 2023-10-12 15:07:49 -07:00
Bill He a6eb90952a Hide wallet options 2023-10-12 09:33:35 -07:00
Bill d53e8f693e Update Squid URL (#67) 2023-10-11 11:34:36 -07:00
Rui f9e301b3f0 Add script to inject iOS app ID to public/.well-known/apple-app-site-association (#66)
* Add script to inject iOS app ID to public/.well-known/apple-app-site-association

* package.json

* Fix typo

* Fix typo

* Fix build

* Debug

* Updated to use "dist" folder
2023-10-10 15:58:26 -07:00
John Huang 67ecbd75b4 Added USDC gasDenom, and Staging West label (#65)
* TRCL-2892-configs-changes

* Additional changes for main net template, and WalletConnect payloads

* Added logo file used for WalletConnect, and changed name and description in WC

* Removed a comma

* TRCL-2898 Additional node provider

* Added USDC gasDenom, and Staging West label
2023-10-09 17:08:04 -04:00
Bill a05d88136d Polishes for Onboarding (#57)
* Polishes for Onboarding

* update string

* bump packages

* address nit
2023-10-09 09:34:49 -07:00
Jared Vu f1ca86326d Fix GoodTil var changes (#64) 2023-10-09 08:02:32 -07:00
aleka 5d1d734c59 add config for sign typed data domain name and action for networks (#62) 2023-10-09 10:51:47 -04:00
aleka 9ba6b3d6ba fix typo in walletconnect wallet type (#63) 2023-10-06 15:50:52 -07:00
Jared Vu 62eeab06f5 Fix marketDetails stringKey, do not update state with markets not tracked by Abacus (#61) 2023-10-06 14:12:40 -07:00
John Huang fa621a085b TRCL-2892-configs-changes (#60)
* TRCL-2892-configs-changes

* Additional changes for main net template, and WalletConnect payloads

* Added logo file used for WalletConnect, and changed name and description in WC

* Removed a comma

* TRCL-2898 Additional node provider
2023-10-06 13:50:41 -07:00
Bill 0730e71109 Do not hide rewardpage on prod (#58) 2023-10-04 22:57:39 -07:00
Bill fdd3fbd774 Reward page (#52)
* Implement Rewards Page

* more rewards page panels

* add keplr dialog

* Finish up rewards page

* bump localization

* add to nav

* address comments

* Comment out migration work

* rename

* address comments

* add TODO
2023-10-04 13:11:35 -04:00
Jared Vu 15bf8a1b34 Update Abacus v0.6.0 (#55)
* Abacus 6: New init, deprecate asset.symbol

* deprecate asset.symbol

* Update networks constants

* filesystem

* Update networks.ts

* 0.6.0 -> 0.6.3

* Remove Market filter
2023-10-03 13:10:55 -07:00
Bill 6508a8b857 Add all squid evm networks to wagmi (#50)
* Add all squid evm networks to wagmi

* remove alchemy

* address nit
2023-10-02 18:50:39 -04:00
aleka e10e7c10e0 fix dialog area too long on mobile (#54) 2023-10-02 13:57:41 -04:00
John Huang 175a6fe523 Added tokenDenom to the configs. Also changed the environments payload to a map from list (#53)
* Added tokenDenom to the configs. Also changed the environments payload to a map from list

* Make tokens section more comprehensive
2023-09-28 16:10:09 -07:00
John Huang 54181a1f52 TRCL-2701 Move market configs to web app from Vercel (#51) 2023-09-27 17:53:56 -07:00
John Huang e6750589db Added a config file, as a retrievable public json (#49)
* Added a config file, retrievable as public json

* Change deployment name

* Refactor the configs

* Add more links

* typo fix
2023-09-27 15:28:46 -07:00
Bill 6c63e66bd6 bump abacus (#48)
* bump abacus

* bump again
2023-09-27 13:54:22 -04:00
Bill 50606a750f Add Rewards abacus transactions and trade form receipt (#47)
* temp

* add into reciept

* bump localization

* add tag
2023-09-25 17:45:46 -04:00
Bill fe46a4d927 Do not poll evm unless addressOrDenom is provided (#46) 2023-09-25 14:56:20 -04:00
Bill 68e5cb8680 Do not get token balance on withdraw (#45) 2023-09-22 17:12:54 -07:00
Bill dea36da7bf Use abacus for simulate transactions and balance polling (#43)
* Use abacus for simulate transactions and balance polling

* address comment

* use dydx gas token

* address nits
2023-09-22 14:21:42 -07:00
Jared Vu f7d052f52e Abacus Transactions Protocol (#38)
* Merge main

* MarketLinks: add key to map

* Merge stashed changes

* fix callback usage to fulfill success/error cbs

* Add Abacus analytics class

* Bump Abacus 0.4.27, Update cancelOrder to use Abacus

* Cancel order loading states

* Use latestOrderChanged to determine whether an order has been committed

* nits

* restore uncommittedOrderClientIds

* bump abacus@0.5.0

* fix for handling null testMarket
2023-09-21 09:19:01 -07:00
Bill 51906f1096 Onboarding testnet 3 and error handling (#42)
* temp

* Onboarding error handling

* address comments
2023-09-20 10:29:02 -07:00
aleka 9a230401cd enable conditional orders in order type dropdown in prod (#41) 2023-09-18 11:19:56 -04:00
aleka 342bd2d334 remove offline exchange dialog from testnet3 prod (#40) 2023-09-15 17:49:15 -04:00
Bill cf68740725 bump v4-client and abacus (#39) 2023-09-15 10:56:27 -07:00
aleka 1462e37c43 temporarily shut down prod FE (#37)
* shut down fe for testnet3 prod
2023-09-13 17:01:42 -04:00
aleka d99b0356e9 bump v4-localization to use testnet3 (#36) 2023-09-13 09:36:23 -07:00
aleka 5cd9bb0fc5 bump abacus for transfers ws fix (#35) 2023-09-13 12:19:34 -04:00
Jared Vu 986a134d07 handle composite client error (#33) 2023-09-13 09:15:22 -07:00
Jared Vu ab454d5928 eslint (#26)
* eslint - hooks

* eslint - icons

* eslint - styles

* eslint - state
2023-09-12 09:42:38 -07:00
aleka 0f1491ac33 show exchange offline dialog for testnet2 (#32) 2023-09-12 11:29:38 -04:00
Jared Vu 1f73f3a884 v4-abacus (#31) 2023-09-11 16:56:13 -07:00
Jared Vu 419d05a0e1 Satoshi font 2 (#30)
* Use Satoshi in TvChart

* update tradingview.tgz
2023-09-11 15:59:43 -07:00
Jared Vu 0c4b605666 Add base position notional (#29) 2023-09-11 15:15:03 -07:00
James Jia 710e5ac113 Update README (#27) 2023-09-08 16:32:24 -07:00
Bill 8fdac4dc09 bump v4-client (#28) 2023-09-08 16:31:34 -07:00
122 changed files with 3610 additions and 1555 deletions
-18
View File
@@ -1,18 +0,0 @@
VITE_BASE_URL=https://v4.testnet.dydx.exchange
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_WALLETCONNECT2_PROJECT_ID=fbe94eaa691fa8d929561f8567062b32
+3 -11
View File
@@ -1,17 +1,9 @@
VITE_BASE_URL= VITE_BASE_URL=
VITE_ALCHEMY_API_KEY= VITE_ALCHEMY_API_KEY=
VITE_PK_ENCRYPTION_KEY= VITE_PK_ENCRYPTION_KEY=
VITE_WALLETCONNECT2_PROJECT_ID= VITE_WALLETCONNECT2_PROJECT_ID=
VITE_FAUCET_URL_DEV= VITE_V3_TOKEN_ADDRESS=
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
View File
@@ -5,15 +5,10 @@ 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
+4 -6
View File
@@ -33,14 +33,11 @@
"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": [ "no-console": ["error"],
"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",
@@ -65,6 +62,7 @@
"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",
{ {
+2
View File
@@ -33,11 +33,13 @@ jobs:
AMPLITUDE_API_KEY: ${{ secrets.AMPLITUDE_API_KEY }} AMPLITUDE_API_KEY: ${{ secrets.AMPLITUDE_API_KEY }}
BUGSNAG_API_KEY: ${{ secrets.BUGSNAG_API_KEY }} BUGSNAG_API_KEY: ${{ secrets.BUGSNAG_API_KEY }}
STATUS_PAGE_SCRIPT_URI: ${{ secrets.STATUS_PAGE_SCRIPT_URI }} STATUS_PAGE_SCRIPT_URI: ${{ secrets.STATUS_PAGE_SCRIPT_URI }}
IOS_APP_ID: ${{ secrets.IOS_APP_ID }}
run: | run: |
pnpm run build pnpm run build
pnpm run build:inject-amplitude pnpm run build:inject-amplitude
pnpm run build:inject-bugsnag pnpm run build:inject-bugsnag
pnpm run build:inject-statuspage pnpm run build:inject-statuspage
sh scripts/inject-app-deeplinks.sh
- name: Upload to IPFS via web3.storage - name: Upload to IPFS via web3.storage
uses: dydxprotocol/add-to-web3@v1 uses: dydxprotocol/add-to-web3@v1
+1
View File
@@ -13,6 +13,7 @@ dist
dist-ssr dist-ssr
*.local *.local
*.key *.key
.env
# Editor directories and files # Editor directories and files
.vscode/* .vscode/*
+9 -45
View File
@@ -1,4 +1,12 @@
# v4 Web App <p align="center"><img src="https://dydx.exchange/icon.svg?" width="256" /></p>
<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
@@ -102,47 +110,3 @@ 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.
+5 -4
View File
@@ -10,6 +10,7 @@
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"build:inject-app-deeplinks": "sh scripts/inject-app-deeplinks.sh",
"build:inject-amplitude": "node scripts/inject-amplitude.js", "build:inject-amplitude": "node scripts/inject-amplitude.js",
"build:inject-bugsnag": "node scripts/inject-bugsnag.js", "build:inject-bugsnag": "node scripts/inject-bugsnag.js",
"build:inject-statuspage": "node scripts/inject-statuspage.js", "build:inject-statuspage": "node scripts/inject-statuspage.js",
@@ -37,9 +38,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/abacus": "^0.4.16", "@dydxprotocol/v4-abacus": "^0.6.4",
"@dydxprotocol/v4-client-js": "^0.32.0", "@dydxprotocol/v4-client-js": "^0.36.1",
"@dydxprotocol/v4-localization": "^0.0.25", "@dydxprotocol/v4-localization": "^0.1.24",
"@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",
@@ -105,7 +106,7 @@
"reselect": "^4.1.8", "reselect": "^4.1.8",
"styled-components": "^5.3.11", "styled-components": "^5.3.11",
"use-latest": "^1.2.1", "use-latest": "^1.2.1",
"viem": "^1.1.6", "viem": "^1.12.2",
"wagmi": "^1.3.7" "wagmi": "^1.3.7"
}, },
"devDependencies": { "devDependencies": {
+113 -297
View File
@@ -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/abacus': '@dydxprotocol/v4-abacus':
specifier: ^0.4.16 specifier: ^0.6.4
version: 0.4.16 version: 0.6.4
'@dydxprotocol/v4-client-js': '@dydxprotocol/v4-client-js':
specifier: ^0.32.0 specifier: ^0.36.1
version: 0.32.0 version: 0.36.1
'@dydxprotocol/v4-localization': '@dydxprotocol/v4-localization':
specifier: ^0.0.25 specifier: ^0.1.24
version: 0.0.25 version: 0.1.24
'@ethersproject/providers': '@ethersproject/providers':
specifier: ^5.7.2 specifier: ^5.7.2
version: 5.7.2 version: 5.7.2
@@ -231,11 +231,11 @@ dependencies:
specifier: ^1.2.1 specifier: ^1.2.1
version: 1.2.1(@types/react@18.2.14)(react@18.2.0) version: 1.2.1(@types/react@18.2.14)(react@18.2.0)
viem: viem:
specifier: ^1.1.6 specifier: ^1.12.2
version: 1.1.6(typescript@5.1.3) version: 1.12.2(typescript@5.1.3)
wagmi: wagmi:
specifier: ^1.3.7 specifier: ^1.3.7
version: 1.3.7(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)(typescript@5.1.3)(viem@1.1.6) version: 1.3.7(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)(typescript@5.1.3)(viem@1.12.2)
devDependencies: devDependencies:
'@babel/core': '@babel/core':
@@ -380,14 +380,14 @@ packages:
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: true dev: true
/@adraffy/ens-normalize@1.9.0:
resolution: {integrity: sha512-iowxq3U30sghZotgl4s/oJRci6WPBfNO5YYgk2cIOMCHr3LeGPcsZjCEr+33Q4N+oV3OABDAtA+pyvWjbvBifQ==}
dev: false
/@adraffy/ens-normalize@1.9.2: /@adraffy/ens-normalize@1.9.2:
resolution: {integrity: sha512-0h+FrQDqe2Wn+IIGFkTCd4aAwTJ+7834Ek1COohCyV26AXhwQ7WQaz+4F/nLOeVl/3BtWHOHLPsq46V8YB46Eg==} resolution: {integrity: sha512-0h+FrQDqe2Wn+IIGFkTCd4aAwTJ+7834Ek1COohCyV26AXhwQ7WQaz+4F/nLOeVl/3BtWHOHLPsq46V8YB46Eg==}
dev: false dev: false
/@adraffy/ens-normalize@1.9.4:
resolution: {integrity: sha512-UK0bHA7hh9cR39V+4gl2/NnBBjoXIxkuWAPCaY4X7fbH4L/azIi7ilWOCjMUYfpJgraLUAqkRi2BqrjME8Rynw==}
dev: false
/@ampproject/remapping@2.2.1: /@ampproject/remapping@2.2.1:
resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
@@ -614,74 +614,6 @@ 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'}
@@ -726,15 +658,6 @@ 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:
@@ -788,18 +711,6 @@ 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:
@@ -832,14 +743,6 @@ 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:
@@ -856,11 +759,12 @@ packages:
readonly-date: 1.0.0 readonly-date: 1.0.0
dev: false dev: false
/@cosmjs/json-rpc@0.29.5: /@cosmjs/encoding@0.31.1:
resolution: {integrity: sha512-C78+X06l+r9xwdM1yFWIpGl03LhB9NdM1xvZpQHwgCOl0Ir/WV8pw48y3Ez2awAoUBRfTeejPe4KvrE6NoIi/w==} resolution: {integrity: sha512-IuxP6ewwX6vg9sUJ8ocJD92pkerI4lyG8J5ynAM3NaX3q+n+uMoPRSQXNeL9bnlrv01FF1kIm8if/f5F7ZPtkA==}
dependencies: dependencies:
'@cosmjs/stream': 0.29.5 base64-js: 1.5.1
xstream: 11.14.0 bech32: 1.1.4
readonly-date: 1.0.0
dev: false dev: false
/@cosmjs/json-rpc@0.30.1: /@cosmjs/json-rpc@0.30.1:
@@ -897,12 +801,6 @@ 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:
@@ -915,16 +813,10 @@ packages:
bn.js: 5.2.1 bn.js: 5.2.1
dev: false dev: false
/@cosmjs/proto-signing@0.29.3: /@cosmjs/math@0.31.1:
resolution: {integrity: sha512-Ai3l9THjMOrLJ4Ebn1Dgptwg6W5ZIRJqtnJjijHhGwTVC1WT0WdYU3aMZ7+PwubcA/cA1rH4ZTK7jrfYbra63g==} resolution: {integrity: sha512-kiuHV6m6DSB8/4UV1qpFhlc4ul8SgLXTGRlYkYiIIP4l0YNeJ+OpPYaOlEgx4Unk2mW3/O2FWYj7Jc93+BWXng==}
dependencies: dependencies:
'@cosmjs/amino': 0.29.3 bn.js: 5.2.1
'@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:
@@ -951,18 +843,6 @@ 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:
@@ -987,27 +867,6 @@ 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:
@@ -1050,12 +909,6 @@ 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:
@@ -1068,25 +921,6 @@ 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:
@@ -1129,10 +963,6 @@ 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
@@ -1141,39 +971,29 @@ 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/abacus@0.4.16: /@dydxprotocol/v4-abacus@0.6.4:
resolution: {integrity: sha512-FTvzgVZ0xjMRmfHwlyoluUSG4Uu4FUHPnaTvLLPl0ER0junmaVDrmBydAUMWMEYGiLyx/k0ln1cLUDrYF2aX6A==} resolution: {integrity: sha512-eH+/9Q/OnQtwzBwM1BJyXybFPdivGmFQWrxM71MFWGs6uIQGbZX775mpoa8deweSTyQ/QrRLRQ1mP2BPDgBQYg==}
dev: false dev: false
/@dydxprotocol/dydxjs@0.3.0: /@dydxprotocol/v4-client-js@0.36.1:
resolution: {integrity: sha512-ygNeBs0f3H7sJk1qLUjfNwaXgc5TmjD+qZf7BFDbi7+boHJI9xuOWrGpjfBo7OTUZxS6O5jZG323CgU5XqMSPw==} resolution: {integrity: sha512-KJ3MtWI61wmm+xZtXQd7/hYKIoVFG32TRdyyVaXXO8vpW9Oqd0JD8QSM/wuegkciAh5YmP/BrDzYuu77zHO6yg==}
dependencies: dependencies:
'@babel/runtime': 7.22.10
'@bufbuild/buf': 1.19.0-1
'@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
osmojs: 15.5.0 '@cosmjs/utils': 0.31.1
protobufjs: 6.11.4 '@dydxprotocol/v4-proto': 0.2.1
transitivePeerDependencies:
- bufferutil
- debug
- utf-8-validate
dev: false
/@dydxprotocol/v4-client-js@0.32.0:
resolution: {integrity: sha512-KvKActMgWLcsRQ7RQhpi/F8xeQ7MWUp3G5+4IPNZ4wyRwAxDSbcrfYbTetI/IesHyelRPSeYxam9eYq3wXwZ1A==}
dependencies:
'@cosmjs/amino': 0.30.1
'@cosmjs/proto-signing': 0.30.1
'@cosmjs/stargate': 0.30.1
'@cosmjs/tendermint-rpc': 0.30.1
'@dydxprotocol/dydxjs': 0.3.0
'@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
@@ -1182,6 +1002,7 @@ 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
@@ -1189,8 +1010,14 @@ packages:
- utf-8-validate - utf-8-validate
dev: false dev: false
/@dydxprotocol/v4-localization@0.0.25: /@dydxprotocol/v4-localization@0.1.24:
resolution: {integrity: sha512-Rp6VHA8z+nwgvTjjB3fvL8gY7bd4DYDHV+NPoL0MFuRm39vhFxGlBVAGDkXPkQTh6YU5BlZV2yXLkDeSfFqEBQ==} resolution: {integrity: sha512-zSiu8+/6BWNWZXxe73zUp/UjuLG1Cp5UuhXKtbATqqkeZyVCs01QxbBCTs1MLfgsLh02IrfhXCPHvXnzZyReEw==}
dev: false
/@dydxprotocol/v4-proto@0.2.1:
resolution: {integrity: sha512-Q5b/g331DoyMjBpO2/jtrxUX3WF5UaIKZp/unK332xuefHNFnkMYtZ05p0V71iSdO35tBdz8yt0VYP2WtUsp+w==}
dependencies:
protobufjs: 6.11.4
dev: false dev: false
/@emotion/is-prop-valid@1.2.1: /@emotion/is-prop-valid@1.2.1:
@@ -2205,6 +2032,12 @@ packages:
'@noble/hashes': 1.3.1 '@noble/hashes': 1.3.1
dev: false dev: false
/@noble/curves@1.2.0:
resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==}
dependencies:
'@noble/hashes': 1.3.2
dev: false
/@noble/ed25519@1.7.3: /@noble/ed25519@1.7.3:
resolution: {integrity: sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==} resolution: {integrity: sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==}
dev: true dev: true
@@ -2222,6 +2055,11 @@ packages:
engines: {node: '>= 16'} engines: {node: '>= 16'}
dev: false dev: false
/@noble/hashes@1.3.2:
resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==}
engines: {node: '>= 16'}
dev: false
/@noble/secp256k1@1.7.1: /@noble/secp256k1@1.7.1:
resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==} resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==}
@@ -2246,15 +2084,6 @@ 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:
@@ -4829,7 +4658,7 @@ packages:
resolution: {integrity: sha512-gYw0ki/EAuV1oSyMxpqandHjnthZjYYy+YWpTAzf8BqfXM3ItcZLpjxfg+3+mXW8HIO+3jw6T9iiqEXsqHaMMw==} resolution: {integrity: sha512-gYw0ki/EAuV1oSyMxpqandHjnthZjYYy+YWpTAzf8BqfXM3ItcZLpjxfg+3+mXW8HIO+3jw6T9iiqEXsqHaMMw==}
dependencies: dependencies:
'@safe-global/safe-gateway-typescript-sdk': 3.9.0 '@safe-global/safe-gateway-typescript-sdk': 3.9.0
viem: 1.1.6(typescript@5.1.3) viem: 1.12.2(typescript@5.1.3)
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- encoding - encoding
@@ -4842,7 +4671,7 @@ packages:
resolution: {integrity: sha512-XJbEPuaVc7b9n23MqlF6c+ToYIS3f7P2Sel8f3cSBQ9WORE4xrSuvhMpK9fDSFqJ7by/brc+rmJR/5HViRr0/w==} resolution: {integrity: sha512-XJbEPuaVc7b9n23MqlF6c+ToYIS3f7P2Sel8f3cSBQ9WORE4xrSuvhMpK9fDSFqJ7by/brc+rmJR/5HViRr0/w==}
dependencies: dependencies:
'@safe-global/safe-gateway-typescript-sdk': 3.9.0 '@safe-global/safe-gateway-typescript-sdk': 3.9.0
viem: 1.1.6(typescript@5.1.3) viem: 1.12.2(typescript@5.1.3)
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- encoding - encoding
@@ -4863,6 +4692,10 @@ packages:
resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==} resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==}
dev: false dev: false
/@scure/base@1.1.3:
resolution: {integrity: sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==}
dev: false
/@scure/bip32@1.3.0: /@scure/bip32@1.3.0:
resolution: {integrity: sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==} resolution: {integrity: sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==}
dependencies: dependencies:
@@ -4879,6 +4712,14 @@ packages:
'@scure/base': 1.1.1 '@scure/base': 1.1.1
dev: false dev: false
/@scure/bip32@1.3.2:
resolution: {integrity: sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==}
dependencies:
'@noble/curves': 1.2.0
'@noble/hashes': 1.3.2
'@scure/base': 1.1.3
dev: false
/@scure/bip39@1.2.0: /@scure/bip39@1.2.0:
resolution: {integrity: sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==} resolution: {integrity: sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==}
dependencies: dependencies:
@@ -4889,7 +4730,7 @@ packages:
/@scure/bip39@1.2.1: /@scure/bip39@1.2.1:
resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==} resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==}
dependencies: dependencies:
'@noble/hashes': 1.3.1 '@noble/hashes': 1.3.2
'@scure/base': 1.1.1 '@scure/base': 1.1.1
dev: false dev: false
@@ -5590,6 +5431,12 @@ packages:
'@types/node': 20.3.1 '@types/node': 20.3.1
dev: false dev: false
/@types/ws@8.5.6:
resolution: {integrity: sha512-8B5EO9jLVCy+B58PLHvLDuOD8DRVMgQzq8d55SjLCOn9kqGyqOvy27exVaTio1q1nX5zLu8/6N0n2ThSxOM6tg==}
dependencies:
'@types/node': 20.3.1
dev: false
/@typescript-eslint/eslint-plugin@5.60.0(@typescript-eslint/parser@5.60.0)(eslint@8.43.0)(typescript@5.1.3): /@typescript-eslint/eslint-plugin@5.60.0(@typescript-eslint/parser@5.60.0)(eslint@8.43.0)(typescript@5.1.3):
resolution: {integrity: sha512-78B+anHLF1TI8Jn/cD0Q00TBYdMgjdOn980JfAVa9yw5sop8nyTfVOQAv6LWywkOGLclDBtv5z3oxN4w7jxyNg==} resolution: {integrity: sha512-78B+anHLF1TI8Jn/cD0Q00TBYdMgjdOn980JfAVa9yw5sop8nyTfVOQAv6LWywkOGLclDBtv5z3oxN4w7jxyNg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -6086,17 +5933,6 @@ packages:
pretty-format: 27.5.1 pretty-format: 27.5.1
dev: true dev: true
/@wagmi/chains@1.2.0(typescript@5.1.3):
resolution: {integrity: sha512-dmDRipsE54JfyudOBkuhEexqQWcrZqxn/qiujG8SBzMh/az/AH5xlJSA+j1CPWTx9+QofSMF3B7A4gb6XRmSaQ==}
peerDependencies:
typescript: '>=5.0.4'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
typescript: 5.1.3
dev: false
/@wagmi/chains@1.4.0(typescript@5.1.3): /@wagmi/chains@1.4.0(typescript@5.1.3):
resolution: {integrity: sha512-9HwJrhcZ1TxyrCbE10y7s1eSiSiyfGam7AHIOLYExaOX+vpOZ+MNTt4orFEDbEpz1fxwJDPPI38lanAUix1OSA==} resolution: {integrity: sha512-9HwJrhcZ1TxyrCbE10y7s1eSiSiyfGam7AHIOLYExaOX+vpOZ+MNTt4orFEDbEpz1fxwJDPPI38lanAUix1OSA==}
peerDependencies: peerDependencies:
@@ -6108,7 +5944,7 @@ packages:
typescript: 5.1.3 typescript: 5.1.3
dev: false dev: false
/@wagmi/connectors@2.6.5(@wagmi/chains@1.4.0)(react@18.2.0)(typescript@5.1.3)(viem@1.1.6): /@wagmi/connectors@2.6.5(@wagmi/chains@1.4.0)(react@18.2.0)(typescript@5.1.3)(viem@1.12.2):
resolution: {integrity: sha512-klF31togMDd0qQqEcLl5cCGxjMbL0RRXQ8I1vxmEa3KdGzw6Z3ICVzX7/bDfnNEZcOW7BKyAnZDq7rCt5jTOiw==} resolution: {integrity: sha512-klF31togMDd0qQqEcLl5cCGxjMbL0RRXQ8I1vxmEa3KdGzw6Z3ICVzX7/bDfnNEZcOW7BKyAnZDq7rCt5jTOiw==}
peerDependencies: peerDependencies:
'@wagmi/chains': '>=1.3.0' '@wagmi/chains': '>=1.3.0'
@@ -6132,7 +5968,7 @@ packages:
abitype: 0.8.7(typescript@5.1.3) abitype: 0.8.7(typescript@5.1.3)
eventemitter3: 4.0.7 eventemitter3: 4.0.7
typescript: 5.1.3 typescript: 5.1.3
viem: 1.1.6(typescript@5.1.3) viem: 1.12.2(typescript@5.1.3)
transitivePeerDependencies: transitivePeerDependencies:
- '@react-native-async-storage/async-storage' - '@react-native-async-storage/async-storage'
- bufferutil - bufferutil
@@ -6144,7 +5980,7 @@ packages:
- zod - zod
dev: false dev: false
/@wagmi/core@1.3.6(@types/react@18.2.14)(react@18.2.0)(typescript@5.1.3)(viem@1.1.6): /@wagmi/core@1.3.6(@types/react@18.2.14)(react@18.2.0)(typescript@5.1.3)(viem@1.12.2):
resolution: {integrity: sha512-TXv9ZlRR5aySfERFuWMuo+lKXC/CoqtxVJZVHPqhK1jY+nldMx3AvrWzzF4CccRaMYcVdvPFepvmxzq2A2VvWg==} resolution: {integrity: sha512-TXv9ZlRR5aySfERFuWMuo+lKXC/CoqtxVJZVHPqhK1jY+nldMx3AvrWzzF4CccRaMYcVdvPFepvmxzq2A2VvWg==}
peerDependencies: peerDependencies:
typescript: '>=5.0.4' typescript: '>=5.0.4'
@@ -6154,11 +5990,11 @@ packages:
optional: true optional: true
dependencies: dependencies:
'@wagmi/chains': 1.4.0(typescript@5.1.3) '@wagmi/chains': 1.4.0(typescript@5.1.3)
'@wagmi/connectors': 2.6.5(@wagmi/chains@1.4.0)(react@18.2.0)(typescript@5.1.3)(viem@1.1.6) '@wagmi/connectors': 2.6.5(@wagmi/chains@1.4.0)(react@18.2.0)(typescript@5.1.3)(viem@1.12.2)
abitype: 0.8.7(typescript@5.1.3) abitype: 0.8.7(typescript@5.1.3)
eventemitter3: 4.0.7 eventemitter3: 4.0.7
typescript: 5.1.3 typescript: 5.1.3
viem: 1.1.6(typescript@5.1.3) viem: 1.12.2(typescript@5.1.3)
zustand: 4.4.1(@types/react@18.2.14)(react@18.2.0) zustand: 4.4.1(@types/react@18.2.14)(react@18.2.0)
transitivePeerDependencies: transitivePeerDependencies:
- '@react-native-async-storage/async-storage' - '@react-native-async-storage/async-storage'
@@ -6859,6 +6695,20 @@ packages:
typescript: 5.1.3 typescript: 5.1.3
dev: false dev: false
/abitype@0.9.8(typescript@5.1.3):
resolution: {integrity: sha512-puLifILdm+8sjyss4S+fsUN09obiT1g2YW6CtcQF+QDzxR0euzgEB29MZujC6zMk2a6SVmtttq1fc6+YFA7WYQ==}
peerDependencies:
typescript: '>=5.0.4'
zod: ^3 >=3.19.1
peerDependenciesMeta:
typescript:
optional: true
zod:
optional: true
dependencies:
typescript: 5.1.3
dev: false
/abort-controller@3.0.0: /abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'} engines: {node: '>=6.5'}
@@ -7787,13 +7637,6 @@ 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:
@@ -10175,12 +10018,12 @@ packages:
ws: 7.5.9 ws: 7.5.9
dev: false dev: false
/isomorphic-ws@5.0.0(ws@8.12.0): /isomorphic-ws@5.0.0(ws@8.13.0):
resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==}
peerDependencies: peerDependencies:
ws: '*' ws: '*'
dependencies: dependencies:
ws: 8.12.0 ws: 8.13.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)
dev: false dev: false
/it-all@1.0.6: /it-all@1.0.6:
@@ -11641,23 +11484,6 @@ 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'}
@@ -13726,21 +13552,24 @@ packages:
vfile-message: 3.1.4 vfile-message: 3.1.4
dev: true dev: true
/viem@1.1.6(typescript@5.1.3): /viem@1.12.2(typescript@5.1.3):
resolution: {integrity: sha512-7nK3HMucLr1Yz0QnDXiD6viigKS6QeYD/YhRHpi3Bby/g0hCyZqK8+YJNtp3/Ri64tpl4kaTIeCScJWV2jqXHQ==} resolution: {integrity: sha512-aCaUCyg72ES+jK4s6tVYOMnOt4if71RwzgiUAUpAuaCgvHFfh9DCnwuEfwkxEZLE2vafOsirgJ3fcn7nsDVQoQ==}
peerDependencies: peerDependencies:
typescript: '>=5.0.4' typescript: '>=5.0.4'
peerDependenciesMeta:
typescript:
optional: true
dependencies: dependencies:
'@adraffy/ens-normalize': 1.9.0 '@adraffy/ens-normalize': 1.9.4
'@noble/curves': 1.0.0 '@noble/curves': 1.2.0
'@noble/hashes': 1.3.0 '@noble/hashes': 1.3.2
'@scure/bip32': 1.3.0 '@scure/bip32': 1.3.2
'@scure/bip39': 1.2.0 '@scure/bip39': 1.2.1
'@wagmi/chains': 1.2.0(typescript@5.1.3) '@types/ws': 8.5.6
abitype: 0.8.7(typescript@5.1.3) abitype: 0.9.8(typescript@5.1.3)
isomorphic-ws: 5.0.0(ws@8.12.0) isomorphic-ws: 5.0.0(ws@8.13.0)
typescript: 5.1.3 typescript: 5.1.3
ws: 8.12.0 ws: 8.13.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- utf-8-validate - utf-8-validate
@@ -13908,7 +13737,7 @@ packages:
- supports-color - supports-color
dev: true dev: true
/wagmi@1.3.7(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)(typescript@5.1.3)(viem@1.1.6): /wagmi@1.3.7(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)(typescript@5.1.3)(viem@1.12.2):
resolution: {integrity: sha512-n4Qwby0QB8/JSGjc0gGn4MWZ0BT229agEg6saPGS6VswmPjJLdkLq9qxKn1JptXxPREmb/ZvwHxU++3Z+gQJVA==} resolution: {integrity: sha512-n4Qwby0QB8/JSGjc0gGn4MWZ0BT229agEg6saPGS6VswmPjJLdkLq9qxKn1JptXxPREmb/ZvwHxU++3Z+gQJVA==}
peerDependencies: peerDependencies:
react: '>=17.0.0' react: '>=17.0.0'
@@ -13921,12 +13750,12 @@ packages:
'@tanstack/query-sync-storage-persister': 4.32.6 '@tanstack/query-sync-storage-persister': 4.32.6
'@tanstack/react-query': 4.32.6(react-dom@18.2.0)(react@18.2.0) '@tanstack/react-query': 4.32.6(react-dom@18.2.0)(react@18.2.0)
'@tanstack/react-query-persist-client': 4.32.6(@tanstack/react-query@4.32.6) '@tanstack/react-query-persist-client': 4.32.6(@tanstack/react-query@4.32.6)
'@wagmi/core': 1.3.6(@types/react@18.2.14)(react@18.2.0)(typescript@5.1.3)(viem@1.1.6) '@wagmi/core': 1.3.6(@types/react@18.2.14)(react@18.2.0)(typescript@5.1.3)(viem@1.12.2)
abitype: 0.8.7(typescript@5.1.3) abitype: 0.8.7(typescript@5.1.3)
react: 18.2.0 react: 18.2.0
typescript: 5.1.3 typescript: 5.1.3
use-sync-external-store: 1.2.0(react@18.2.0) use-sync-external-store: 1.2.0(react@18.2.0)
viem: 1.1.6(typescript@5.1.3) viem: 1.12.2(typescript@5.1.3)
transitivePeerDependencies: transitivePeerDependencies:
- '@react-native-async-storage/async-storage' - '@react-native-async-storage/async-storage'
- '@types/react' - '@types/react'
@@ -14151,19 +13980,6 @@ packages:
optional: true optional: true
dev: false dev: false
/ws@8.12.0:
resolution: {integrity: sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
dev: false
/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):
resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
@@ -4,7 +4,7 @@
"details": [ "details": [
{ {
"appIDs": [ "appIDs": [
"75C6UARB5H.exchangeV4.dydx.trading" "{PLACE_YOUR_APP_ID_HERE}"
], ],
"components": [ "components": [
{ {
+834
View File
@@ -0,0 +1,834 @@
{
"deployments": {
"MAINNET": {
"environments": [
"dydxprotocol-mainnet"
],
"default": "dydxprotocol-mainnet"
},
"TESTNET": {
"environments": [
"dydxprotocol-testnet"
],
"default": "dydxprotocol-testnet"
},
"DEV": {
"environments": [
"dydxprotocol-dev",
"dydxprotocol-dev-2",
"dydxprotocol-dev-4",
"dydxprotocol-dev-5",
"dydxprotocol-staging",
"dydxprotocol-staging-west",
"dydxprotocol-testnet",
"dydxprotocol-testnet-dydx",
"dydxprotocol-testnet-dsrv",
"dydxprotocol-testnet-nodefleet",
"dydxprotocol-testnet-kingnodes",
"dydxprotocol-testnet-liquify"
],
"default": "dydxprotocol-testnet"
}
},
"environments": {
"dydxprotocol-dev": {
"name": "v4 Dev",
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"isMainNet": false,
"tokens": {
"chain": {
"name": "DYDX",
"denom": "dv4tnt",
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"image": "/currencies/usdc.png"
}
},
"endpoints": {
"indexers": [
{
"api": "http://indexer.v4dev.dydx.exchange",
"socket": "wss://indexer.v4dev.dydx.exchange"
}
],
"validators": [
"http://validator.v4dev.dydx.exchange"
],
"0xsquid": "https://testnet.api.0xsquid.com",
"faucet": "http://faucet.v4dev.dydx.exchange"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"blogs": "https://www.dydx.foundation/blog"
},
"wallets": {
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v1": {
"bridgeUrl": "wss://api.stage.dydx.exchange/wc/"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX Chain Onboarding",
"signTypedDataDomainName": "dYdX Chain"
}
},
"dydxprotocol-dev-2": {
"name": "v4 Dev 2",
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"isMainNet": false,
"tokens": {
"chain": {
"name": "DYDX",
"denom": "dv4tnt",
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"image": "/currencies/usdc.png"
}
},
"endpoints": {
"indexers": [
{
"api": "http://dev2-indexer-apne1-lb-public-2076363889.ap-northeast-1.elb.amazonaws.com",
"socket": "ws://dev2-indexer-apne1-lb-public-2076363889.ap-northeast-1.elb.amazonaws.com"
}
],
"validators": [
"http://35.75.227.118"
],
"0xsquid": "https://testnet.api.0xsquid.com"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"blogs": "https://www.dydx.foundation/blog"
},
"wallets": {
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v1": {
"bridgeUrl": "wss://api.stage.dydx.exchange/wc/"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX Chain Onboarding",
"signTypedDataDomainName": "dYdX Chain"
}
},
"dydxprotocol-dev-4": {
"name": "v4 Dev 4",
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"isMainNet": false,
"tokens": {
"chain": {
"name": "DYDX",
"denom": "dv4tnt",
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"image": "/currencies/usdc.png"
}
},
"endpoints": {
"indexers": [
{
"api": "http://indexer.v4dev4.dydx.exchange",
"socket": "ws://indexer.v4dev4.dydx.exchange"
}
],
"validators": [
"http://validator.v4dev4.dydx.exchange"
],
"0xsquid": "https://testnet.api.0xsquid.com"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"blogs": "https://www.dydx.foundation/blog"
},
"wallets": {
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v1": {
"bridgeUrl": "wss://api.stage.dydx.exchange/wc/"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX Chain Onboarding",
"signTypedDataDomainName": "dYdX Chain"
}
},
"dydxprotocol-dev-5": {
"name": "v4 Dev 5",
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"isMainNet": false,
"tokens": {
"chain": {
"name": "DYDX",
"denom": "dv4tnt",
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"image": "/currencies/usdc.png"
}
},
"endpoints": {
"indexers": [
{
"api": "http://dev5-indexer-apne1-lb-public-1721328151.ap-northeast-1.elb.amazonaws.com",
"socket": "ws://dev5-indexer-apne1-lb-public-1721328151.ap-northeast-1.elb.amazonaws.com"
}
],
"validators": [
"http://18.223.78.50"
],
"0xsquid": "https://testnet.api.0xsquid.com"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"blogs": "https://www.dydx.foundation/blog"
},
"wallets": {
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v1": {
"bridgeUrl": "wss://api.stage.dydx.exchange/wc/"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX Chain Onboarding",
"signTypedDataDomainName": "dYdX Chain"
}
},
"dydxprotocol-staging": {
"name": "v4 Staging",
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"isMainNet": false,
"tokens": {
"chain": {
"name": "DYDX",
"denom": "dv4tnt",
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"image": "/currencies/usdc.png"
}
},
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4staging.dydx.exchange",
"socket": "wss://indexer.v4staging.dydx.exchange"
}
],
"faucet": "https://faucet.v4staging.dydx.exchange",
"validators": [
"https://validator.v4staging.dydx.exchange"
],
"0xsquid": "https://testnet.api.squidrouter.com"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"documentation": "https://v4-teacher.vercel.app/",
"community": "https://discord.com/invite/dydx",
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
"blogs": "https://www.dydx.foundation/blog"
},
"wallets": {
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v1": {
"bridgeUrl": "wss://api.stage.dydx.exchange/wc/"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX Chain Onboarding",
"signTypedDataDomainName": "dYdX Chain"
}
},
"dydxprotocol-staging-west": {
"name": "v4 Staging West",
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"isMainNet": false,
"tokens": {
"chain": {
"name": "DYDX",
"denom": "dv4tnt",
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"image": "/currencies/usdc.png"
}
},
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4staging.dydx.exchange",
"socket": "wss://indexer.v4staging.dydx.exchange"
}
],
"faucet": "https://faucet.v4staging.dydx.exchange",
"validators": [
"https://validator-uswest1.v4staging.dydx.exchange"
],
"0xsquid": "https://testnet.api.squidrouter.com"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"documentation": "https://v4-teacher.vercel.app/",
"community": "https://discord.com/invite/dydx",
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
"blogs": "https://www.dydx.foundation/blog"
},
"wallets": {
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v1": {
"bridgeUrl": "wss://api.stage.dydx.exchange/wc/"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX Chain Onboarding",
"signTypedDataDomainName": "dYdX Chain"
}
},
"dydxprotocol-testnet": {
"name": "v4 Public Testnet",
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-3",
"isMainNet": false,
"tokens": {
"chain": {
"name": "DYDX",
"denom": "dv4tnt",
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"image": "/currencies/usdc.png"
}
},
"endpoints": {
"indexers": [
{
"api": "https://dydx-testnet.imperator.co",
"socket": "wss://dydx-testnet.imperator.co"
}
],
"validators": [
"https://dydx-testnet.nodefleet.org",
"https://test-dydx.kingnodes.com",
"https://dydx-rpc.liquify.com/api=8878132/dydx"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"documentation": "https://v4-teacher.vercel.app/",
"community": "https://discord.com/invite/dydx",
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
"blogs": "https://www.dydx.foundation/blog"
},
"wallets": {
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v1": {
"bridgeUrl": "wss://api.stage.dydx.exchange/wc/"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX V4 Onboarding",
"signTypedDataDomainName": "dYdX V4"
}
},
"dydxprotocol-testnet-dydx": {
"name": "v4 Public Testnet/dYdX",
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-3",
"isMainNet": false,
"tokens": {
"chain": {
"name": "DYDX",
"denom": "dv4tnt",
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"image": "/currencies/usdc.png"
}
},
"endpoints": {
"indexers": [
{
"api": "https://indexer.v4testnet.dydx.exchange",
"socket": "wss://indexer.v4testnet.dydx.exchange"
}
],
"validators": [
"https://validator.v4testnet.dydx.exchange"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"statusPage": "https://status.v4testnet.dydx.exchange/",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"documentation": "https://v4-teacher.vercel.app/",
"community": "https://discord.com/invite/dydx",
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
"blogs": "https://www.dydx.foundation/blog"
},
"wallets": {
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v1": {
"bridgeUrl": "wss://api.stage.dydx.exchange/wc/"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX V4 Onboarding",
"signTypedDataDomainName": "dYdX V4"
}
},
"dydxprotocol-testnet-dsrv": {
"name": "v4 Public Testnet/DSRV",
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-3",
"isMainNet": false,
"tokens": {
"chain": {
"name": "DYDX",
"denom": "dv4tnt",
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"image": "/currencies/usdc.png"
}
},
"endpoints": {
"indexers": [
{
"api": "https://dydx-testnet.imperator.co",
"socket": "wss://dydx-testnet.imperator.co"
}
],
"validators": [
"https://dydx-testnet-archive.allthatnode.com:26657/XZvMM41hESf8PJrEQiTzbCOMVyFca79R"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"statusPage": "https://status.v4testnet.dydx.exchange/",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"documentation": "https://v4-teacher.vercel.app/",
"community": "https://discord.com/invite/dydx",
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
"blogs": "https://www.dydx.foundation/blog"
},
"wallets": {
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v1": {
"bridgeUrl": "wss://api.stage.dydx.exchange/wc/"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX V4 Onboarding",
"signTypedDataDomainName": "dYdX V4"
}
},
"dydxprotocol-testnet-nodefleet": {
"name": "v4 Public Testnet/nodefleet",
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-3",
"isMainNet": false,
"tokens": {
"chain": {
"name": "DYDX",
"denom": "dv4tnt",
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"image": "/currencies/usdc.png"
}
},
"endpoints": {
"indexers": [
{
"api": "https://dydx-testnet.imperator.co",
"socket": "wss://dydx-testnet.imperator.co"
}
],
"validators": [
"https://dydx-testnet.nodefleet.org"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"statusPage": "https://status.v4testnet.dydx.exchange/",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"documentation": "https://v4-teacher.vercel.app/",
"community": "https://discord.com/invite/dydx",
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
"blogs": "https://www.dydx.foundation/blog"
},
"wallets": {
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v1": {
"bridgeUrl": "wss://api.stage.dydx.exchange/wc/"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX V4 Onboarding",
"signTypedDataDomainName": "dYdX V4"
}
},
"dydxprotocol-testnet-kingnodes": {
"name": "v4 Public Testnet/KingNodes",
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-3",
"isMainNet": false,
"tokens": {
"chain": {
"name": "DYDX",
"denom": "dv4tnt",
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"image": "/currencies/usdc.png"
}
},
"endpoints": {
"indexers": [
{
"api": "https://dydx-testnet.imperator.co",
"socket": "wss://dydx-testnet.imperator.co"
}
],
"validators": [
"https://test-dydx.kingnodes.com"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"statusPage": "https://status.v4testnet.dydx.exchange/",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"documentation": "https://v4-teacher.vercel.app/",
"community": "https://discord.com/invite/dydx",
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
"blogs": "https://www.dydx.foundation/blog"
},
"wallets": {
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v1": {
"bridgeUrl": "wss://api.stage.dydx.exchange/wc/"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX V4 Onboarding",
"signTypedDataDomainName": "dYdX V4"
}
},
"dydxprotocol-testnet-liquify": {
"name": "v4 Public Testnet/Liquify",
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-3",
"isMainNet": false,
"tokens": {
"chain": {
"name": "DYDX",
"denom": "dv4tnt",
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"image": "/currencies/usdc.png"
}
},
"endpoints": {
"indexers": [
{
"api": "https://dydx-testnet.imperator.co",
"socket": "wss://dydx-testnet.imperator.co"
}
],
"validators": [
"https://dydx-rpc.liquify.com/api=8878132/dydx"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
"privacy": "https://dydx.exchange/privacy",
"statusPage": "https://status.v4testnet.dydx.exchange/",
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
"documentation": "https://v4-teacher.vercel.app/",
"community": "https://discord.com/invite/dydx",
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
"blogs": "https://www.dydx.foundation/blog"
},
"wallets": {
"walletconnect": {
"client": {
"name": "dYdX v4",
"description": "dYdX v4 App",
"iconUrl": "/logos/dydx-x.png"
},
"v1": {
"bridgeUrl": "wss://api.stage.dydx.exchange/wc/"
},
"v2": {
"projectId": "47559b2ec96c09aed9ff2cb54a31ab0e"
}
},
"walletSegue": {
"callbackUrl": "/walletsegue"
},
"images": "/wallets/",
"signTypedDataAction": "dYdX V4 Onboarding",
"signTypedDataDomainName": "dYdX V4"
}
},
"dydxprotocol-mainet": {
"name": "v4",
"ethereumChainId": "1",
"dydxChainId": "[mainnet chain id]",
"isMainNet": true,
"tokens": {
"comment": "Change according to mainnet release",
"chain": {
"name": "DYDX",
"denom": "dv4tnt",
"image": "/currencies/dydx.png"
},
"usdc": {
"name": "USDC",
"denom": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5",
"gasDenom": "uusdc",
"image": "/currencies/usdc.png"
}
},
"endpoints": {
"indexers": [
{
"api": "[REST endpoint]",
"socket": "[Websocket endpoint]"
}
],
"validators": [
"[Validator endpoint 1",
"[Validator endpoint n]"
],
"0xsquid": "[0xSquid endpoint for mainnet]"
},
"links": {
"tos": "[HTTP link to TOS]",
"privacy": "[HTTP link to Privacy Policy]",
"mintscan": "[HTTP link to Mintscan, with {tx_hash} placeholder]",
"documentation": "[HTTP link to documentation, can be null]",
"community": "[HTTP link to community, can be null]",
"feedback": "[HTTP link to feedback form, can be null]",
"blogs": "[HTTP link to blogs, can be null]"
},
"wallets": {
"walletconnect": {
"client": {
"name": "[Name of the app]",
"description": "[Description of the app]",
"iconUrl": "[Relative URL of the icon URL]"
},
"v1": {
"bridgeUrl": "[Bridge URL]"
},
"v2": {
"projectId": "[Project ID]"
}
},
"walletSegue": {
"callbackUrl": "[Relative callback URL for WalletSegue, should match apple-app-site-association]"
},
"images": "[Relative URL for wallet images]",
"signTypedDataAction": "dYdX Chain Onboarding",
"signTypedDataDomainName": "dYdX Chain"
}
}
}
}
+454
View File
@@ -0,0 +1,454 @@
{
"1INCH-USD":{
"name":"1inch",
"tags":[
"Defi"
],
"websiteLink":"https://1inch.io/",
"whitepaperLink":"https://github.com/1inch",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/1inch/"
},
"AAVE-USD":{
"name":"Aave",
"tags":[
"Defi"
],
"websiteLink":"https://aave.com/",
"whitepaperLink":"https://github.com/aave/protocol-v2/blob/master/aave-v2-whitepaper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/aave/"
},
"ADA-USD":{
"name":"Cardano",
"tags":[
"Layer 1"
],
"websiteLink":"https://cardano.org/",
"whitepaperLink":"https://why.cardano.org/en/introduction/motivation/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/cardano/"
},
"ALGO-USD":{
"name":"Algorand",
"tags":[
"Layer 1"
],
"websiteLink":"https://algorand.com/",
"whitepaperLink":"https://algorand.com/technology/white-papers",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/algorand/"
},
"APE-USD":{
"name":"ApeCoin",
"tags":[
],
"websiteLink":"https://apecoin.com/",
"whitepaperLink":"https://apecoin.com/about",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/apecoin-ape/"
},
"APT-USD":{
"name":"Aptos",
"tags":[
"Layer 1"
],
"websiteLink":"https://aptoslabs.com/",
"whitepaperLink":"https://aptos.dev/aptos-white-paper/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/aptos/"
},
"ARB-USD":{
"name":"Arbitrum",
"tags":[
],
"websiteLink":"https://arbitrum.io/",
"whitepaperLink":"https://github.com/OffchainLabs/nitro",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/arbitrum/"
},
"ATOM-USD":{
"name":"Cosmos",
"tags":[
"Layer 1"
],
"websiteLink":"https://cosmos.network/",
"whitepaperLink":"https://v1.cosmos.network/resources/whitepaper",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/cosmos/"
},
"AVAX-USD":{
"name":"Avalanche",
"tags":[
"Layer 1"
],
"websiteLink":"https://www.avalabs.org/",
"whitepaperLink":"https://assets.website-files.com/5d80307810123f5ffbb34d6e/6008d7bbf8b10d1eb01e7e16_Avalanche%20Platform%20Whitepaper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/avalanche/"
},
"BCH-USD":{
"name":"Bitcoin Cash",
"tags":[
"Layer 1"
],
"websiteLink":"https://bitcoincash.org/",
"whitepaperLink":"https://bitcoincash.org/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/bitcoin-cash/"
},
"BLUR-USD":{
"name":"Blur",
"tags":[
],
"websiteLink":"https://blur.io/",
"whitepaperLink":"https://docs.blur.foundation/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/blur-token/"
},
"CELO-USD":{
"name":"Celo",
"tags":[
],
"websiteLink":"https://celo.org",
"whitepaperLink":"https://docs.celo.org",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/celo/"
},
"BTC-USD":{
"name":"Bitcoin",
"tags":[
"Layer 1"
],
"websiteLink":"https://bitcoin.org/",
"whitepaperLink":"https://bitcoin.org/bitcoin.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/bitcoin/"
},
"COMP-USD":{
"name":"Compound",
"tags":[
"Defi"
],
"websiteLink":"https://compound.finance/",
"whitepaperLink":"https://compound.finance/documents/Compound.Whitepaper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/compound/"
},
"CRV-USD":{
"name":"Curve",
"tags":[
"Governance"
],
"websiteLink":"https://curve.fi/",
"whitepaperLink":"https://curve.fi/whitepaper",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/curve-dao-token/"
},
"DOGE-USD":{
"name":"Dogecoin",
"tags":[
"Layer 1"
],
"websiteLink":"https://dogecoin.com/",
"whitepaperLink":"https://github.com/dogecoin/dogecoin",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/dogecoin/"
},
"DOT-USD":{
"name":"Polkadot",
"tags":[
"Layer 1"
],
"websiteLink":"https://polkadot.network/",
"whitepaperLink":"https://polkadot.network/PolkaDotPaper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/polkadot-new/"
},
"ENJ-USD":{
"name":"Enjin",
"tags":[
],
"websiteLink":"https://enjin.io/",
"whitepaperLink":"https://cdn.enjin.io/downloads/whitepapers/enjin-coin/en.pdf/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/enjin-coin/"
},
"EOS-USD":{
"name":"EOS",
"tags":[
"Layer 1"
],
"websiteLink":"https://eos.io/",
"whitepaperLink":"https://github.com/EOSIO/Documentation/blob/master/TechnicalWhitePaper.md",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/eos/"
},
"ETC-USD":{
"name":"Ethereum Classic",
"tags":[
"Layer 1"
],
"websiteLink":"https://ethereumclassic.org/",
"whitepaperLink":"https://ethereumclassic.org/why-classic",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/ethereum-classic/"
},
"ETH-USD":{
"name":"Ethereum",
"tags":[
"Layer 1"
],
"websiteLink":"https://ethereum.org/",
"whitepaperLink":"https://ethereum.org/whitepaper/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/ethereum/",
"displayStepSize":"0.001",
"displayTickSize":"0.1"
},
"FIL-USD":{
"name":"Filecoin",
"tags":[
"Layer 1"
],
"websiteLink":"https://filecoin.io/",
"whitepaperLink":"https://filecoin.io/filecoin.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/filecoin/"
},
"HNT-USD":{
"name":"Helium",
"tags":[
"Layer 1"
],
"websiteLink":"https://www.helium.com",
"whitepaperLink":"http://whitepaper.helium.com",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/helium/"
},
"ICP-USD":{
"name":"Internet Computer",
"tags":[
"Layer 1"
],
"websiteLink":"https://dfinity.org",
"whitepaperLink":"https://dfinity.org/whitepaper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/internet-computer/"
},
"LDO-USD":{
"name":"Lido DAO",
"tags":[
"Defi"
],
"websiteLink":"https://lido.fi/",
"whitepaperLink":"https://lido.fi/static/Lido:Ethereum-Liquid-Staking.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/lido-dao/"
},
"LINK-USD":{
"name":"Chainlink",
"tags":[
"Defi"
],
"websiteLink":"https://chain.link/",
"whitepaperLink":"https://link.smartcontract.com/whitepaper",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/chainlink/"
},
"LTC-USD":{
"name":"Litecoin",
"tags":[
"Layer 1"
],
"websiteLink":"https://litecoin.org/",
"whitepaperLink":"https://litecoin.info/index.php/Main_Page",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/litecoin/"
},
"MATIC-USD":{
"name":"Polygon",
"tags":[
"Layer 2"
],
"websiteLink":"https://polygon.technology/",
"whitepaperLink":"https://polygon.technology/lightpaper-polygon.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/polygon/"
},
"MKR-USD":{
"name":"Maker",
"tags":[
"Governance"
],
"websiteLink":"https://makerdao.com",
"whitepaperLink":"https://makerdao.com/whitepaper",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/maker/"
},
"NEAR-USD":{
"name":"NEAR Protocol",
"tags":[
"Layer 1"
],
"websiteLink":"https://near.org",
"whitepaperLink":"https://near.org/papers/the-official-near-white-paper/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/near-protocol/"
},
"OP-USD":{
"name":"Optimism",
"tags":[
],
"websiteLink":"https://www.optimism.io/",
"whitepaperLink":"https://github.com/ethereum-optimism",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/optimism-ethereum/"
},
"PEPE-USD":{
"name":"Pepe",
"tags":[
],
"websiteLink":"https://www.pepe.vip/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/pepe/"
},
"RUNE-USD":{
"name":"THORChain",
"tags":[
"Layer 1"
],
"websiteLink":"https://thorchain.org",
"whitepaperLink":"https://whitepaper.io/document/709/thorchain-whitepaper",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/thorchain/"
},
"SEI-USD":{
"name":"Sei",
"tags":[
"Layer 1",
"Defi"
],
"websiteLink":"https://www.sei.io/",
"whitepaperLink":"https://github.com/sei-protocol/sei-chain/blob/3c9576fee3494ce039df684624f918dd8066ba3f/whitepaper/Sei_Whitepaper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/sei/"
},
"SHIB-USD":{
"name":"Shiba Inu",
"tags":[
],
"websiteLink":"https://shibatoken.com/",
"whitepaperLink":"https://docs.shibatoken.com/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/shiba-inu/"
},
"SNX-USD":{
"name":"Synthetix",
"tags":[
"Defi"
],
"websiteLink":"https://synthetix.io/",
"whitepaperLink":"https://docs.synthetix.io/litepaper",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/synthetix-network-token/"
},
"SOL-USD":{
"name":"Solana",
"tags":[
"Layer 1"
],
"websiteLink":"https://solana.com/",
"whitepaperLink":"https://solana.com/solana-whitepaper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/solana/"
},
"SUI-USD":{
"name":"Sui",
"tags":[
"Layer 1"
],
"websiteLink":"https://sui.io/",
"whitepaperLink":"https://github.com/MystenLabs",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/sui/"
},
"SUSHI-USD":{
"name":"Sushi",
"tags":[
"Defi"
],
"websiteLink":"https://sushi.com/",
"whitepaperLink":"https://docs.sushi.com/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/sushiswap/"
},
"TRX-USD":{
"name":"TRON",
"tags":[
"Defi"
],
"websiteLink":"https://tron.network/",
"whitepaperLink":"https://tron.network/static/doc/white_paper_v_2_0.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/tron/"
},
"UMA-USD":{
"name":"UMA",
"tags":[
"Defi"
],
"websiteLink":"https://umaproject.org/",
"whitepaperLink":"https://github.com/UMAprotocol/whitepaper",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/uma/"
},
"UNI-USD":{
"name":"Uniswap",
"tags":[
"Defi"
],
"websiteLink":"https://uniswap.org/",
"whitepaperLink":"https://uniswap.org/whitepaper-v3.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/uniswap/"
},
"WLD-USD":{
"name":"Worldcoin",
"tags":[
],
"websiteLink":"https://worldcoin.org/",
"whitepaperLink":"https://whitepaper.worldcoin.org/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/worldcoin-org/"
},
"XLM-USD":{
"name":"Stellar",
"tags":[
"Layer 1"
],
"websiteLink":"https://www.stellar.org/",
"whitepaperLink":"https://www.stellar.org/papers/stellar-consensus-protocol",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/stellar/"
},
"XMR-USD":{
"name":"Monero",
"tags":[
"Layer 1"
],
"websiteLink":"https://www.getmonero.org/",
"whitepaperLink":"https://www.getmonero.org/resources/research-lab/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/monero/"
},
"XRP-USD":{
"name":"Ripple",
"tags":[
"Layer 1"
],
"websiteLink":"https://ripple.com/currency/",
"whitepaperLink":"https://github.com/ripple",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/xrp/"
},
"XTZ-USD":{
"name":"Tezos",
"tags":[
"Layer 1"
],
"websiteLink":"https://tezos.com",
"whitepaperLink":"https://tezos.com/whitepaper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/tezos/"
},
"YFI-USD":{
"name":"Yearn",
"tags":[
"Defi"
],
"websiteLink":"https://yearn.finance/",
"whitepaperLink":"https://docs.yearn.finance/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/yearn-finance/"
},
"ZEC-USD":{
"name":"Zcash",
"tags":[
"Layer 1"
],
"websiteLink":"https://z.cash/",
"whitepaperLink":"https://z.cash/technology/",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/zcash/"
},
"ZRX-USD":{
"name":"0x",
"tags":[
"Defi"
],
"websiteLink":"https://0x.org/",
"whitepaperLink":"https://0x.org/pdfs/0x_white_paper.pdf",
"coinMarketCapsLink":"https://coinmarketcap.com/currencies/0x/"
}
}
+474
View File
@@ -0,0 +1,474 @@
[
{
"id":"c57ca95b47569778a828d19178114f4db188b89b763c899ba0be274e97267d96",
"name":"MetaMask",
"description":"",
"homepage":"https://metamask.io/",
"chains":[
"eip155:1",
"eip155:42161",
"eip155:43114"
],
"versions":[
"1"
],
"app":{
"browser":"",
"ios":"https://apps.apple.com/us/app/metamask/id1438144202",
"android":"https://play.google.com/store/apps/details?id=io.metamask",
"mac":"",
"windows":"",
"linux":""
},
"mobile":{
"native":"metamask:",
"universal":"https://metamask.app.link"
},
"desktop":{
"native":"",
"universal":""
},
"metadata":{
"shortName":"MetaMask",
"colors":{
"primary":"rgb(255, 255, 255)",
"secondary":""
}
},
"config":{
"comment":"MetaMask",
"iosMinVersion":"0",
"imageUrl":"https://v4.testnet.dydx.exchange/wallets/metamask.png",
"image":"metamask.png",
"encoding":"=\"#%/<>?@\\^`{|}:&",
"connections":[
{
"type":"walletConnectV2",
"native":"metamask:",
"universal":"https://metamask.app.link"
}
]
},
"userFields":{
"analyticEvent":"METAMASK"
}
},
{
"id":"coinbase-wallet",
"name":"Coinbase Wallet",
"description":"",
"homepage":"https://www.coinbase.com/wallet",
"chains":[
"eip155:1"
],
"versions":[
"1"
],
"app":{
"browser":"",
"ios":"https://apps.apple.com/us/app/coinbase-wallet-nfts-crypto/id1278383455",
"android":"https://play.google.com/store/apps/details?id=org.toshi",
"mac":"",
"windows":"",
"linux":""
},
"mobile":{
"native":"cbwallet:",
"universal":""
},
"desktop":{
"native":"",
"universal":""
},
"metadata":{
"shortName":"Coinbase",
"colors":{
"primary":"rgb(255, 255, 255)",
"secondary":""
}
},
"config":{
"comment":"Coinbase",
"iosMinVersion":"1.8.0",
"imageUrl":"https://v4.testnet.dydx.exchange/wallets/coinbase-wallet.png",
"image":"coinbase-wallet.png",
"encoding":"=\"#%/<>?@\\^`{|}:&",
"connections":[
{
"type":"walletSegue",
"native":"cbwallet:",
"universal":""
}
]
},
"userFields":{
"analyticEvent":"COINBASE_WALLET"
}
},
{
"id":"9d373b43ad4d2cf190fb1a774ec964a1addf406d6fd24af94ab7596e58c291b2",
"name":"imToken",
"description":"",
"homepage":"https://token.im/",
"chains":[
"eip155:1"
],
"versions":[
"1"
],
"app":{
"browser":"",
"ios":"https://itunes.apple.com/us/app/imtoken2/id1384798940",
"android":"https://play.google.com/store/apps/details?id=im.token.app",
"mac":"",
"windows":"",
"linux":""
},
"mobile":{
"native":"imtokenv2:",
"universal":""
},
"desktop":{
"native":"",
"universal":""
},
"metadata":{
"shortName":"imToken",
"colors":{
"primary":"rgb(255, 255, 255)",
"secondary":""
}
},
"config":{
"comment":"imToken",
"iosMinVersion":"0",
"imageUrl":"https://v4.testnet.dydx.exchange/wallets/imtoken.png",
"image":"imtoken.png",
"connections":[
{
"type":"walletConnect",
"native":"imtokenv2:",
"universal":""
}
]
},
"userFields":{
"analyticEvent":"IMTOKEN"
}
},
{
"id":"4622a2b2d6af1c9844944291e5e7351a6aa24cd7b23099efac1b2fd875da31a0",
"name":"Trust Wallet",
"description":"",
"homepage":"https://trustwallet.com/",
"chains":[
"eip155:1",
"eip155:42161",
"eip155:43114"
],
"versions":[
"1"
],
"app":{
"browser":"",
"ios":"https://apps.apple.com/app/apple-store/id1288339409",
"android":"https://play.google.com/store/apps/details?id=com.wallet.crypto.trustapp",
"mac":"",
"windows":"",
"linux":""
},
"mobile":{
"native":"trust:",
"universal":"https://link.trustwallet.com"
},
"desktop":{
"native":"",
"universal":""
},
"metadata":{
"shortName":"Trust",
"colors":{
"primary":"rgb(51, 117, 187)",
"secondary":""
}
},
"config":{
"comment":"Trust Wallet",
"iosMinVersion":"0",
"encoding":"=\"#%/<>?@\\^`{|}:&",
"imageUrl":"https://v4.testnet.dydx.exchange/wallets/trust-wallet.png",
"image":"trust-wallet.png",
"connections":[
{
"type":"walletConnectV2",
"native":"trust:",
"universal":"https://link.trustwallet.com"
}
]
},
"userFields":{
"analyticEvent":"TRUST_WALLET"
}
},
{
"id":"Zerion",
"name":"Zerion",
"description":"",
"homepage":"https://zerion.io/",
"chains":[
"eip155:1"
],
"versions":[
"1"
],
"app":{
"browser":"",
"ios":"https://apps.apple.com/app/apple-store/id1456732565",
"android":"https://play.google.com/store/apps/details?id=io.zerion.android",
"mac":"",
"windows":"",
"linux":""
},
"mobile":{
"native":"zerion:",
"universal":""
},
"desktop":{
"native":"",
"universal":""
},
"metadata":{
"shortName":"Zerion",
"colors":{
"primary":"rgb(51, 117, 187)",
"secondary":""
}
},
"config":{
"comment":"Zerion Wallet",
"iosMinVersion":"0",
"encoding":"=\"#%/<>?@\\^`{|}:&",
"imageUrl":"https://v4.testnet.dydx.exchange/wallets/zerion.png",
"image":"zerion.png",
"connections":[
{
"type":"walletConnectV2",
"native":"zerion:",
"universal":""
}
]
},
"userFields":{
"analyticEvent":"ZERION"
}
},
{
"id":"20459438007b75f4f4acb98bf29aa3b800550309646d375da5fd4aac6c2a2c66",
"name":"TokenPocket",
"description":"",
"homepage":"https://tokenpocket.pro/",
"chains":[
"eip155:1"
],
"versions":[
"1"
],
"app":{
"browser":"",
"ios":"https://apps.apple.com/us/app/tokenpocket-trusted-wallet/id1436028697",
"android":"https://play.google.com/store/apps/details?id=vip.mytokenpocket",
"mac":"",
"windows":"",
"linux":""
},
"mobile":{
"native":"tpoutside:",
"universal":""
},
"desktop":{
"native":"",
"universal":""
},
"metadata":{
"shortName":"TokenPocket",
"colors":{
"primary":"rgb(41, 128, 254)",
"secondary":""
}
},
"config":{
"comment":"TokenPocket",
"iosMinVersion":"0",
"imageUrl":"https://v4.testnet.dydx.exchange/wallets/tokenpocket.png",
"image":"tokenpocket.png",
"connections":[
{
"type":"walletConnect",
"native":"tpoutside:",
"universal":""
}
]
},
"userFields":{
"analyticEvent":"TOKEN_POCKET"
}
},
{
"id":"1ae92b26df02f0abca6304df07debccd18262fdf5fe82daa81593582dac9a369",
"name":"Rainbow",
"description":"",
"homepage":"https://rainbow.me/",
"chains":[
"eip155:1"
],
"versions":[
"1"
],
"app":{
"browser":"",
"ios":"https://apps.apple.com/us/app/rainbow-ethereum-wallet/id1457119021",
"android":"",
"mac":"",
"windows":"",
"linux":""
},
"mobile":{
"native":"rainbow:",
"universal":"https://rnbwapp.com"
},
"desktop":{
"native":"",
"universal":""
},
"metadata":{
"shortName":"Rainbow",
"colors":{
"primary":"rgb(0, 30, 89)",
"secondary":""
}
},
"config":{
"comment":"Rainbow",
"iosMinVersion":"0",
"encoding":"=\"#%/<>?@\\^`{|}:&",
"backlinked":true,
"imageUrl":"https://v4.testnet.dydx.exchange/wallets/rainbow-wallet.png",
"image":"rainbow-wallet.png",
"connections":[
{
"type":"walletConnectV2",
"native":"rainbow:",
"universal":"https://rnbwapp.com"
}
]
},
"userFields":{
"analyticEvent":"RAINBOW_WALLET"
}
},
{
"id":"09102e7bbbd3f92001eda104abe23803181629f695e8f1b95af96d88ff7d5890",
"name":"1inch Wallet",
"description":"",
"homepage":"https://1inch.io/wallet/",
"chains":[
"eip155:1",
"eip155:56"
],
"versions":[
"1"
],
"app":{
"browser":"",
"ios":"https://apps.apple.com/us/app/1inch-defi-wallet/id1546049391",
"android":"",
"mac":"",
"windows":"",
"linux":""
},
"mobile":{
"native":"oneinch:",
"universal":"https://wallet.1inch.io"
},
"desktop":{
"native":"",
"universal":""
},
"metadata":{
"shortName":"1inch",
"colors":{
"primary":"rgb(31, 36, 46)",
"secondary":""
}
},
"config":{
"comment":"1inch",
"iosMinVersion":"0",
"encoding":"=\"#%/<>?@\\^`{|}:&",
"imageUrl":"https://v4.testnet.dydx.exchange/wallets/1inch.png",
"image":"1inch.png",
"connections":[
{
"type":"walletConnect",
"native":"oneinch:",
"universal":"https://wallet.1inch.io"
}
]
},
"userFields":{
"analyticEvent":"1INCH"
}
},
{
"id":"bae74827272509a6d63ea25514d9c68ad235c14e45e183518c7ded4572a1b0c4",
"name":"Huobi Wallet",
"description":"",
"homepage":"https://huobiwallet.com/",
"chains":[
"eip155:1"
],
"versions":[
"1"
],
"app":{
"browser":"",
"ios":"https://apps.apple.com/us/app/id1433883012",
"android":"https://play.google.com/store/apps/details?id=com.huobionchainwallet.gp",
"mac":"",
"windows":"",
"linux":""
},
"mobile":{
"native":"huobiwallet:",
"universal":""
},
"desktop":{
"native":"",
"universal":""
},
"metadata":{
"shortName":"Huobi",
"colors":{
"primary":"rgb(45,101,248)",
"secondary":""
}
},
"config":{
"comment":"Huobi",
"iosMinVersion":"0",
"encoding":"=\"#%/<>?@\\^`{|}:&",
"imageUrl":"https://v4.testnet.dydx.exchange/wallets/huobi-wallet.png",
"image":"huobi-wallet.png",
"connections":[
{
"type":"walletConnect",
"native":"huobiwallet:",
"universal":""
}
]
},
"userFields":{
"analyticEvent":"HUOBI_WALLET"
}
}
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

+19
View File
@@ -0,0 +1,19 @@
#!/bin/sh
source="dist/.well-known/apple-app-site-association"
if [ ! -f "$source" ]; then
echo "Error: $source file not found"
exit 1
fi
app_id=$IOS_APP_ID
if [ -z "$app_id" ]; then
echo "Error: IOS_APP_ID is empty"
exit 1
fi
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "s/{PLACE_YOUR_APP_ID_HERE}/$app_id/g" $source
else
sed -i "s/{PLACE_YOUR_APP_ID_HERE}/$app_id/g" $source
fi
+8 -9
View File
@@ -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';
@@ -67,9 +67,7 @@ const Content = () => {
<Route path={AppRoute.Trade} element={<TradePage />} /> <Route path={AppRoute.Trade} element={<TradePage />} />
</Route> </Route>
<Route path={AppRoute.Markets} element={<MarketsPage />} /> <Route path={AppRoute.Markets} element={<MarketsPage />} />
{import.meta.env.MODE !== 'production' && ( <Route path={AppRoute.Rewards} element={<RewardsPage />} />
<Route path={AppRoute.Rewards} element={<RewardsPage />} />
)}
{isTablet && ( {isTablet && (
<> <>
<Route path={AppRoute.Alerts} element={<AlertsPage />} /> <Route path={AppRoute.Alerts} element={<AlertsPage />} />
@@ -100,6 +98,7 @@ 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>
); );
@@ -196,7 +195,7 @@ Styled.NotificationsToastArea = styled(NotificationsToastArea)`
Styled.DialogArea = styled.aside` Styled.DialogArea = styled.aside`
position: fixed; position: fixed;
height: 100vh; height: 100%;
z-index: 1; z-index: 1;
inset: 0; inset: 0;
overflow: clip; overflow: clip;
+3 -3
View File
@@ -12,7 +12,7 @@ type ElementProps = {
}; };
children: React.ReactNode; children: React.ReactNode;
href?: string; href?: string;
onClick?: () => void; onClick?: (e: MouseEvent) => void;
withIcon?: boolean; withIcon?: boolean;
}; };
@@ -37,12 +37,12 @@ export const Link = forwardRef<HTMLAnchorElement, ElementProps & StyleProps>(
ref={ref} ref={ref}
className={className} className={className}
href={href} href={href}
onClick={() => { onClick={(e: MouseEvent) => {
if (analyticsConfig) { if (analyticsConfig) {
console.log(analyticsConfig); console.log(analyticsConfig);
} }
onClick?.(); onClick?.(e);
}} }}
rel="noopener noreferrer" rel="noopener noreferrer"
target="_blank" target="_blank"
+3 -1
View File
@@ -11,6 +11,7 @@ type PanelProps = {
children?: React.ReactNode; children?: React.ReactNode;
href?: string; href?: string;
onHeaderClick?: () => void; onHeaderClick?: () => void;
onClick?: () => void;
}; };
type PanelStyleProps = { type PanelStyleProps = {
@@ -24,10 +25,11 @@ export const Panel = ({
children, children,
href, href,
onHeaderClick, onHeaderClick,
onClick,
hasSeparator, hasSeparator,
className, className,
}: PanelProps & PanelStyleProps) => ( }: PanelProps & PanelStyleProps) => (
<Styled.Panel> <Styled.Panel onClick={onClick}>
{href ? ( {href ? (
<Link to={href}> <Link to={href}>
{slotHeader ? ( {slotHeader ? (
+1 -1
View File
@@ -24,7 +24,7 @@ export const MarketTableCell = ({
slotLeft={ slotLeft={
<> <>
{showFavorite && <Icon iconName={IconName.Star} />} {showFavorite && <Icon iconName={IconName.Star} />}
<Styled.AssetIcon symbol={asset?.symbol} /> <Styled.AssetIcon symbol={asset?.id} />
</> </>
} }
> >
+14 -10
View File
@@ -1,4 +1,4 @@
import Abacus, { kollections } from '@dydxprotocol/abacus'; import Abacus, { kollections } from '@dydxprotocol/v4-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';
@@ -6,6 +6,7 @@ import { STRING_KEYS } from './localization';
export type Nullable<T> = T | null | undefined; export type Nullable<T> = T | null | undefined;
// ------ V4 Protocols ------ // // ------ V4 Protocols ------ //
export const AbacusAppConfig = Abacus.exchange.dydx.abacus.state.manager.AppConfigs;
export const IOImplementations = Abacus.exchange.dydx.abacus.utils.IOImplementations; export const IOImplementations = Abacus.exchange.dydx.abacus.utils.IOImplementations;
export const UIImplementations = Abacus.exchange.dydx.abacus.utils.UIImplementations; export const UIImplementations = Abacus.exchange.dydx.abacus.utils.UIImplementations;
export type AbacusDYDXChainTransactionsProtocol = Omit< export type AbacusDYDXChainTransactionsProtocol = Omit<
@@ -40,6 +41,10 @@ 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;
@@ -65,8 +70,8 @@ export type NetworkConfig = {
export type ConnectNetworkEvent = CustomEvent<Partial<NetworkConfig>>; export type ConnectNetworkEvent = CustomEvent<Partial<NetworkConfig>>;
// ------ State ------ // ------ State ------
export type AbacusApiState = Abacus.exchange.dydx.abacus.state.app.ApiState; export type AbacusApiState = Abacus.exchange.dydx.abacus.state.manager.ApiState;
export const AbacusApiStatus = Abacus.exchange.dydx.abacus.state.app.ApiStatus; export const AbacusApiStatus = Abacus.exchange.dydx.abacus.state.manager.ApiStatus;
const abacusApiStatuses = [...AbacusApiStatus.values()]; const abacusApiStatuses = [...AbacusApiStatus.values()];
export type AbacusApiStatuses = (typeof abacusApiStatuses)[number]; export type AbacusApiStatuses = (typeof abacusApiStatuses)[number];
export const Changes = Abacus.exchange.dydx.abacus.state.changes.Changes; export const Changes = Abacus.exchange.dydx.abacus.state.changes.Changes;
@@ -118,6 +123,7 @@ 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;
@@ -134,7 +140,7 @@ export type SubaccountTransfers = Abacus.exchange.dydx.abacus.output.SubaccountT
// ------ Historical PnL ------ // // ------ Historical PnL ------ //
export type SubAccountHistoricalPNL = Abacus.exchange.dydx.abacus.output.SubaccountHistoricalPNL; export type SubAccountHistoricalPNL = Abacus.exchange.dydx.abacus.output.SubaccountHistoricalPNL;
export type SubAccountHistoricalPNLs = Abacus.exchange.dydx.abacus.output.SubaccountHistoricalPNL[]; export type SubAccountHistoricalPNLs = Abacus.exchange.dydx.abacus.output.SubaccountHistoricalPNL[];
export const HistoricalPnlPeriod = Abacus.exchange.dydx.abacus.state.app.HistoricalPnlPeriod; export const HistoricalPnlPeriod = Abacus.exchange.dydx.abacus.protocols.HistoricalPnlPeriod;
const historicalPnlPeriod = [...HistoricalPnlPeriod.values()] as const; const historicalPnlPeriod = [...HistoricalPnlPeriod.values()] as const;
export type HistoricalPnlPeriods = (typeof historicalPnlPeriod)[number]; export type HistoricalPnlPeriods = (typeof historicalPnlPeriod)[number];
@@ -174,16 +180,14 @@ export const AbacusOrderSide = Abacus.exchange.dydx.abacus.output.input.OrderSid
export const AbacusPositionSide = Abacus.exchange.dydx.abacus.output.PositionSide; export const AbacusPositionSide = Abacus.exchange.dydx.abacus.output.PositionSide;
export type AbacusPositionSides = Abacus.exchange.dydx.abacus.output.PositionSide; export type AbacusPositionSides = Abacus.exchange.dydx.abacus.output.PositionSide;
export type SubaccountPlaceOrderPayload =
Abacus.exchange.dydx.abacus.state.app.V4SubaccountPlaceOrderPayload2;
export type SubaccountCancelOrderPayload =
Abacus.exchange.dydx.abacus.state.app.V4SubaccountCancelOrderPayload2;
export type HumanReadablePlaceOrderPayload = 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;
+4 -19
View File
@@ -1,18 +1,5 @@
/** 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',
@@ -49,14 +36,12 @@ 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;
+13 -2
View File
@@ -71,6 +71,7 @@ export enum AnalyticsEvent {
// Transfers // Transfers
TransferFaucet = 'TransferFaucet', TransferFaucet = 'TransferFaucet',
TransferFaucetConfirmed = 'TransferFaucetConfirmed',
TransferDeposit = 'TransferDeposit', TransferDeposit = 'TransferDeposit',
TransferWithdraw = 'TransferWithdraw', TransferWithdraw = 'TransferWithdraw',
@@ -78,6 +79,7 @@ export enum AnalyticsEvent {
TradeOrderTypeSelected = 'TradeOrderTypeSelected', TradeOrderTypeSelected = 'TradeOrderTypeSelected',
TradePlaceOrder = 'TradePlaceOrder', TradePlaceOrder = 'TradePlaceOrder',
TradePlaceOrderConfirmed = 'TradePlaceOrderConfirmed', TradePlaceOrderConfirmed = 'TradePlaceOrderConfirmed',
TradeCancelOrder = 'TradeCancelOrder',
TradeCancelOrderConfirmed = 'TradeCancelOrderConfirmed', TradeCancelOrderConfirmed = 'TradeCancelOrderConfirmed',
} }
@@ -133,6 +135,13 @@ 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
@@ -151,13 +160,15 @@ 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 */
validator: string; validatorUrl: 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 */
validator: string; validatorUrl: string;
} }
: never; : never;
+1
View File
@@ -5,6 +5,7 @@ export enum DialogTypes {
ExchangeOffline = 'ExchangeOffline', ExchangeOffline = 'ExchangeOffline',
FillDetails = 'FillDetails', FillDetails = 'FillDetails',
Help = 'Help', Help = 'Help',
ExternalNavKeplr = 'ExternalNavKeplr',
MnemonicExport = 'MnemonicExport', MnemonicExport = 'MnemonicExport',
MobileSignIn = 'MobileSignIn', MobileSignIn = 'MobileSignIn',
Onboarding = 'Onboarding', Onboarding = 'Onboarding',
-16
View File
@@ -88,19 +88,3 @@ 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'],
};
+12
View File
@@ -0,0 +1,12 @@
import environments from '../../public/configs/env.json';
// TODO: Update for Mainnet deployment
export const AVAILABLE_ENVIRONMENTS =
import.meta.env.MODE === 'production'
? environments.deployments.TESTNET
: environments.deployments.DEV;
export const CURRENT_ABACUS_DEPLOYMENT = import.meta.env.MODE === 'production' ? 'TESTNET' : 'DEV';
export const ENVIRONMENT_CONFIG_MAP = environments.environments;
export type DydxNetwork = keyof typeof ENVIRONMENT_CONFIG_MAP;
export const DEFAULT_APP_ENVIRONMENT = AVAILABLE_ENVIRONMENTS.default as DydxNetwork;
-29
View File
@@ -1,29 +0,0 @@
import { ENDPOINTS, DEV_ENDPOINTS } from '@dydxprotocol/v4-localization';
export const NETWORK_ENDPOINTS = import.meta.env.MODE === 'production' ? ENDPOINTS : DEV_ENDPOINTS;
export const CLIENT_NETWORK_CONFIGS: Record<
string,
(typeof NETWORK_ENDPOINTS.environments)[number]
> = Object.fromEntries(
NETWORK_ENDPOINTS.environments.map((environment) => [environment.environment, environment])
);
export const DEFAULT_APP_ENVIRONMENT = NETWORK_ENDPOINTS.defaultEnvironment;
export enum DydxV4Network {
V4Mainnet = 'dydxprotocol-mainnet',
V4Staging = 'dydxprotocol-staging',
V4Testnet2 = 'dydxprotocol-testnet',
V4Local = 'dydxprotocol-dev',
}
export type DydxNetwork = keyof typeof CLIENT_NETWORK_CONFIGS;
export function isDydxV4Network(network: any): boolean {
return CLIENT_NETWORK_CONFIGS[network]?.version === 'v4';
}
export function isValidDydxNetwork(network: any): boolean {
return Object.keys(CLIENT_NETWORK_CONFIGS).includes(network);
}
+6 -6
View File
@@ -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<
_NotificationId extends NotificationId = string, NotificationIdType 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: _NotificationId, id: NotificationIdType,
/** 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: _NotificationId) => any; useNotificationAction?: () => (id: NotificationIdType) => 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<
_NotificationId extends NotificationId = string, NotificationIdType extends NotificationId = string,
NotificationUpdateKey = any NotificationUpdateKey = any
> = { > = {
id: _NotificationId; id: NotificationIdType;
type: NotificationType; type: NotificationType;
status: NotificationStatus; status: NotificationStatus;
timestamps: Partial<Record<NotificationStatus, number>>; timestamps: Partial<Record<NotificationStatus, number>>;
-2
View File
@@ -1,5 +1,3 @@
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;
+8
View File
@@ -5,6 +5,10 @@ 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 }),
@@ -90,6 +94,10 @@ export const tradeTooltips: TooltipStrings = {
title: stringGetter({ key: TOOLTIP_STRING_KEYS.MAINTENANCE_MARGIN_FRACTION_TITLE }), title: stringGetter({ key: TOOLTIP_STRING_KEYS.MAINTENANCE_MARGIN_FRACTION_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.MAINTENANCE_MARGIN_FRACTION_BODY }), body: stringGetter({ key: TOOLTIP_STRING_KEYS.MAINTENANCE_MARGIN_FRACTION_BODY }),
}), }),
'max-reward': ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.MAXIMUM_REWARDS_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.MAXIMUM_REWARDS_BODY }),
}),
'max-withdraw': ({ stringGetter }) => ({ 'max-withdraw': ({ stringGetter }) => ({
title: stringGetter({ key: TOOLTIP_STRING_KEYS.MAX_WITHDRAW_TITLE }), title: stringGetter({ key: TOOLTIP_STRING_KEYS.MAX_WITHDRAW_TITLE }),
body: stringGetter({ key: TOOLTIP_STRING_KEYS.MAX_WITHDRAW_BODY }), body: stringGetter({ key: TOOLTIP_STRING_KEYS.MAX_WITHDRAW_BODY }),
+1 -1
View File
@@ -27,7 +27,7 @@ export enum PositionSide {
Short = 'SHORT', Short = 'SHORT',
} }
export const UNCOMMITTED_ORDER_TIMEOUT = 10_000; export const UNCOMMITTED_ORDER_TIMEOUT_MS = 10_000;
export const ORDER_SIDE_STRINGS = { export const ORDER_SIDE_STRINGS = {
[OrderSide.BUY]: STRING_KEYS.BUY, [OrderSide.BUY]: STRING_KEYS.BUY,
+43 -93
View File
@@ -1,4 +1,6 @@
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';
@@ -22,13 +24,14 @@ import {
import { isMetaMask } from '@/lib/wallet/providers'; import { isMetaMask } from '@/lib/wallet/providers';
import { DydxNetwork, ENVIRONMENT_CONFIG_MAP } from './networks';
// Wallet connection types // Wallet connection types
export enum WalletConnectionType { export enum WalletConnectionType {
CoinbaseWalletSdk = 'coinbaseWalletSdk', CoinbaseWalletSdk = 'coinbaseWalletSdk',
CosmosSigner = 'CosmosSigner', CosmosSigner = 'CosmosSigner',
InjectedEip1193 = 'injectedEip1193', InjectedEip1193 = 'injectedEip1193',
WalletConnect1 = 'walletConnect1',
WalletConnect2 = 'walletConnect2', WalletConnect2 = 'walletConnect2',
} }
@@ -58,10 +61,6 @@ export const walletConnectionTypes: Record<WalletConnectionType, WalletConnectio
name: 'injected EIP-1193 provider', name: 'injected EIP-1193 provider',
wagmiConnectorId: 'injected', wagmiConnectorId: 'injected',
}, },
[WalletConnectionType.WalletConnect1]: {
name: 'WalletConnect 1.0',
wagmiConnectorId: 'walletConnectLegacy',
},
[WalletConnectionType.WalletConnect2]: { [WalletConnectionType.WalletConnect2]: {
name: 'WalletConnect 2.0', name: 'WalletConnect 2.0',
wagmiConnectorId: 'walletConnect', wagmiConnectorId: 'walletConnect',
@@ -88,7 +87,6 @@ export enum WalletType {
Rainbow = 'RAINBOW_WALLET', Rainbow = 'RAINBOW_WALLET',
TokenPocket = 'TOKEN_POCKET', TokenPocket = 'TOKEN_POCKET',
TrustWallet = 'TRUST_WALLET', TrustWallet = 'TRUST_WALLET',
WalletConnect = 'WALLETCONNECT',
WalletConnect2 = 'WALLETCONNECT_2', WalletConnect2 = 'WALLETCONNECT_2',
// TestWallet = 'TEST_WALLET', // TestWallet = 'TEST_WALLET',
OtherWallet = 'OTHER_WALLET', OtherWallet = 'OTHER_WALLET',
@@ -100,7 +98,6 @@ type WalletConfig = {
icon: string; icon: string;
connectionTypes: WalletConnectionType[]; connectionTypes: WalletConnectionType[];
matchesInjectedEip1193?: (provider: ExternalProvider & any) => boolean; matchesInjectedEip1193?: (provider: ExternalProvider & any) => boolean;
walletconnect1Name?: string;
walletconnect2Id?: string; walletconnect2Id?: string;
}; };
@@ -113,7 +110,6 @@ export const wallets: Record<WalletType, WalletConfig> = {
WalletConnectionType.InjectedEip1193, WalletConnectionType.InjectedEip1193,
// WalletConnectionType.CoinbaseWalletSdk, // WalletConnectionType.CoinbaseWalletSdk,
WalletConnectionType.WalletConnect2, WalletConnectionType.WalletConnect2,
WalletConnectionType.WalletConnect1,
], ],
matchesInjectedEip1193: (provider) => matchesInjectedEip1193: (provider) =>
Object.entries(wallets).every( Object.entries(wallets).every(
@@ -127,30 +123,23 @@ export const wallets: Record<WalletType, WalletConfig> = {
type: WalletType.BitKeep, type: WalletType.BitKeep,
stringKey: STRING_KEYS.BITKEEP, stringKey: STRING_KEYS.BITKEEP,
icon: BitkeepIcon, icon: BitkeepIcon,
connectionTypes: [ connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
WalletConnectionType.InjectedEip1193,
WalletConnectionType.WalletConnect2,
WalletConnectionType.WalletConnect1,
],
matchesInjectedEip1193: (provider) => provider.isBitKeep, // isBitKeepChrome, isBitEthereum matchesInjectedEip1193: (provider) => provider.isBitKeep, // isBitKeepChrome, isBitEthereum
walletconnect1Name: 'bitkeep',
walletconnect2Id: '38f5d18bd8522c244bdd70cb4a68e0e718865155811c043f052fb9f1c51de662', walletconnect2Id: '38f5d18bd8522c244bdd70cb4a68e0e718865155811c043f052fb9f1c51de662',
}, },
[WalletType.BitPie]: { [WalletType.BitPie]: {
type: WalletType.BitPie, type: WalletType.BitPie,
stringKey: STRING_KEYS.BITPIE, stringKey: STRING_KEYS.BITPIE,
icon: BitpieIcon, icon: BitpieIcon,
connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect1], connectionTypes: [WalletConnectionType.InjectedEip1193],
matchesInjectedEip1193: (provider) => provider.isBitpie, matchesInjectedEip1193: (provider) => provider.isBitpie,
walletconnect1Name: 'bitpie',
}, },
[WalletType.CloverWallet]: { [WalletType.CloverWallet]: {
type: WalletType.CloverWallet, type: WalletType.CloverWallet,
stringKey: STRING_KEYS.CLOVER_WALLET, stringKey: STRING_KEYS.CLOVER_WALLET,
icon: CloverWalletIcon, icon: CloverWalletIcon,
connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect1], connectionTypes: [WalletConnectionType.InjectedEip1193],
matchesInjectedEip1193: (provider) => provider.isClover, matchesInjectedEip1193: (provider) => provider.isClover,
// walletconnect1Name: 'clover',
}, },
[WalletType.CoinbaseWallet]: { [WalletType.CoinbaseWallet]: {
type: WalletType.CoinbaseWallet, type: WalletType.CoinbaseWallet,
@@ -159,33 +148,23 @@ export const wallets: Record<WalletType, WalletConfig> = {
connectionTypes: [ connectionTypes: [
WalletConnectionType.CoinbaseWalletSdk, WalletConnectionType.CoinbaseWalletSdk,
WalletConnectionType.InjectedEip1193, WalletConnectionType.InjectedEip1193,
WalletConnectionType.WalletConnect1, WalletConnectionType.WalletConnect2,
], ],
matchesInjectedEip1193: (provider) => provider.isCoinbaseWallet, // provider.selectedProvider?.isCoinbaseWallet, matchesInjectedEip1193: (provider) => provider.isCoinbaseWallet, // provider.selectedProvider?.isCoinbaseWallet,
walletconnect1Name: 'coinbase',
}, },
[WalletType.Coin98]: { [WalletType.Coin98]: {
type: WalletType.Coin98, type: WalletType.Coin98,
stringKey: STRING_KEYS.COIN98, stringKey: STRING_KEYS.COIN98,
icon: Coin98Icon, icon: Coin98Icon,
connectionTypes: [ connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
WalletConnectionType.InjectedEip1193,
WalletConnectionType.WalletConnect2,
WalletConnectionType.WalletConnect1,
],
matchesInjectedEip1193: (provider) => provider.isCoin98, matchesInjectedEip1193: (provider) => provider.isCoin98,
walletconnect1Name: 'coin98',
walletconnect2Id: '2a3c89040ac3b723a1972a33a125b1db11e258a6975d3a61252cd64e6ea5ea01', walletconnect2Id: '2a3c89040ac3b723a1972a33a125b1db11e258a6975d3a61252cd64e6ea5ea01',
}, },
[WalletType.HuobiWallet]: { [WalletType.HuobiWallet]: {
type: WalletType.HuobiWallet, type: WalletType.HuobiWallet,
stringKey: STRING_KEYS.HUOBI_WALLET, stringKey: STRING_KEYS.HUOBI_WALLET,
icon: HuobiIcon, icon: HuobiIcon,
connectionTypes: [ connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
WalletConnectionType.InjectedEip1193,
WalletConnectionType.WalletConnect2,
WalletConnectionType.WalletConnect1,
],
matchesInjectedEip1193: (provider) => provider.isHbWallet, matchesInjectedEip1193: (provider) => provider.isHbWallet,
walletconnect2Id: '797c615e2c556b610c048eb35535f212c0dd58de5d03e763120e90a7d1350a77', walletconnect2Id: '797c615e2c556b610c048eb35535f212c0dd58de5d03e763120e90a7d1350a77',
}, },
@@ -193,88 +172,56 @@ export const wallets: Record<WalletType, WalletConfig> = {
type: WalletType.ImToken, type: WalletType.ImToken,
stringKey: STRING_KEYS.IMTOKEN, stringKey: STRING_KEYS.IMTOKEN,
icon: ImTokenIcon, icon: ImTokenIcon,
connectionTypes: [ connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
WalletConnectionType.InjectedEip1193,
WalletConnectionType.WalletConnect2,
WalletConnectionType.WalletConnect1,
],
matchesInjectedEip1193: (provider) => provider.isImToken, matchesInjectedEip1193: (provider) => provider.isImToken,
walletconnect1Name: 'imtoken',
walletconnect2Id: 'ef333840daf915aafdc4a004525502d6d49d77bd9c65e0642dbaefb3c2893bef', walletconnect2Id: 'ef333840daf915aafdc4a004525502d6d49d77bd9c65e0642dbaefb3c2893bef',
}, },
[WalletType.MathWallet]: { [WalletType.MathWallet]: {
type: WalletType.MathWallet, type: WalletType.MathWallet,
stringKey: STRING_KEYS.MATH_WALLET, stringKey: STRING_KEYS.MATH_WALLET,
icon: MathWalletIcon, icon: MathWalletIcon,
connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect1], connectionTypes: [WalletConnectionType.InjectedEip1193],
matchesInjectedEip1193: (provider) => provider.isMathWallet, matchesInjectedEip1193: (provider) => provider.isMathWallet,
walletconnect1Name: 'math',
walletconnect2Id: '7674bb4e353bf52886768a3ddc2a4562ce2f4191c80831291218ebd90f5f5e26', walletconnect2Id: '7674bb4e353bf52886768a3ddc2a4562ce2f4191c80831291218ebd90f5f5e26',
}, },
[WalletType.MetaMask]: { [WalletType.MetaMask]: {
type: WalletType.MetaMask, type: WalletType.MetaMask,
stringKey: STRING_KEYS.METAMASK, stringKey: STRING_KEYS.METAMASK,
icon: MetaMaskIcon, icon: MetaMaskIcon,
connectionTypes: [ connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
WalletConnectionType.InjectedEip1193,
WalletConnectionType.WalletConnect2,
WalletConnectionType.WalletConnect1,
],
matchesInjectedEip1193: isMetaMask, matchesInjectedEip1193: isMetaMask,
walletconnect1Name: 'metamask',
walletconnect2Id: 'c57ca95b47569778a828d19178114f4db188b89b763c899ba0be274e97267d96', walletconnect2Id: 'c57ca95b47569778a828d19178114f4db188b89b763c899ba0be274e97267d96',
}, },
[WalletType.Rainbow]: { [WalletType.Rainbow]: {
type: WalletType.Rainbow, type: WalletType.Rainbow,
stringKey: STRING_KEYS.RAINBOW_WALLET, stringKey: STRING_KEYS.RAINBOW_WALLET,
icon: RainbowIcon, icon: RainbowIcon,
connectionTypes: [ connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
WalletConnectionType.InjectedEip1193,
WalletConnectionType.WalletConnect2,
WalletConnectionType.WalletConnect1,
],
matchesInjectedEip1193: (provider) => provider.isRainbowWallet, matchesInjectedEip1193: (provider) => provider.isRainbowWallet,
walletconnect1Name: 'rainbow',
walletconnect2Id: '1ae92b26df02f0abca6304df07debccd18262fdf5fe82daa81593582dac9a369', walletconnect2Id: '1ae92b26df02f0abca6304df07debccd18262fdf5fe82daa81593582dac9a369',
}, },
[WalletType.TokenPocket]: { [WalletType.TokenPocket]: {
type: WalletType.TokenPocket, type: WalletType.TokenPocket,
stringKey: STRING_KEYS.TOKEN_POCKET, stringKey: STRING_KEYS.TOKEN_POCKET,
icon: TokenPocketIcon, icon: TokenPocketIcon,
connectionTypes: [ connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
WalletConnectionType.InjectedEip1193,
WalletConnectionType.WalletConnect2,
WalletConnectionType.WalletConnect1,
],
matchesInjectedEip1193: (provider) => provider.isTokenPocket, matchesInjectedEip1193: (provider) => provider.isTokenPocket,
walletconnect1Name: 'tokenpocket',
walletconnect2Id: '20459438007b75f4f4acb98bf29aa3b800550309646d375da5fd4aac6c2a2c66', walletconnect2Id: '20459438007b75f4f4acb98bf29aa3b800550309646d375da5fd4aac6c2a2c66',
}, },
[WalletType.TrustWallet]: { [WalletType.TrustWallet]: {
type: WalletType.TrustWallet, type: WalletType.TrustWallet,
stringKey: STRING_KEYS.TRUST_WALLET, stringKey: STRING_KEYS.TRUST_WALLET,
icon: TrustWalletIcon, icon: TrustWalletIcon,
connectionTypes: [ connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
WalletConnectionType.InjectedEip1193,
WalletConnectionType.WalletConnect2,
WalletConnectionType.WalletConnect1,
],
matchesInjectedEip1193: (provider) => provider.isTrust, matchesInjectedEip1193: (provider) => provider.isTrust,
walletconnect1Name: 'trust',
walletconnect2Id: '4622a2b2d6af1c9844944291e5e7351a6aa24cd7b23099efac1b2fd875da31a0', walletconnect2Id: '4622a2b2d6af1c9844944291e5e7351a6aa24cd7b23099efac1b2fd875da31a0',
}, },
[WalletType.WalletConnect2]: { [WalletType.WalletConnect2]: {
type: WalletType.WalletConnect, type: WalletType.WalletConnect2,
stringKey: STRING_KEYS.WALLET_CONNECT_2, stringKey: STRING_KEYS.WALLET_CONNECT_2,
icon: WalletConnectIcon, icon: WalletConnectIcon,
connectionTypes: [WalletConnectionType.WalletConnect2], connectionTypes: [WalletConnectionType.WalletConnect2],
}, },
[WalletType.WalletConnect]: {
type: WalletType.WalletConnect,
stringKey: STRING_KEYS.WALLET_CONNECT,
icon: WalletConnectIcon,
connectionTypes: [WalletConnectionType.WalletConnect1],
},
[WalletType.Keplr]: { [WalletType.Keplr]: {
type: WalletType.Keplr, type: WalletType.Keplr,
stringKey: STRING_KEYS.KEPLR, stringKey: STRING_KEYS.KEPLR,
@@ -313,27 +260,24 @@ 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";
/** /**
* @description typed data to sign for v4 onboarding * @description typed data to sign for dYdX Chain onboarding
*/ */
export const SIGN_TYPED_DATA = { export const getSignTypedData = (selectedNetwork: DydxNetwork) =>
primaryType: 'dYdX', ({
domain: { primaryType: 'dYdX',
name: 'dYdX V4', domain: {
}, name: ENVIRONMENT_CONFIG_MAP[selectedNetwork].wallets.signTypedDataDomainName,
types: { },
dYdX: [{ name: 'action', type: 'string' }], types: {
}, dYdX: [{ name: 'action', type: 'string' }],
message: { },
action: 'dYdX V4 Onboarding', message: {
}, action: ENVIRONMENT_CONFIG_MAP[selectedNetwork].wallets.signTypedDataAction,
} as const; },
} as const);
export type PrivateInformation = ReturnType<typeof onboarding.deriveHDKeyFromEthereumSignature>; export type PrivateInformation = ReturnType<typeof onboarding.deriveHDKeyFromEthereumSignature>;
@@ -341,12 +285,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: '13.59.4.93:26657', rpc: 'https://dydx-testnet-archive.allthatnode.com:26657',
rest: '13.59.4.93:1317', rest: 'https://dydx-testnet-archive.allthatnode.com:1317',
chainId: 'dydx-testnet-2', chainId: 'dydx-testnet-3',
chainName: 'dYdX Public Testnet', chainName: 'dYdX Public Testnet',
chainSymbolImageUrl: chainSymbolImageUrl:
'https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/dydx-testnet-2/chain.png', 'https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/dydx-testnet-3/chain.png',
bech32Config: { bech32Config: {
bech32PrefixAccPub: 'dydxpub', bech32PrefixAccPub: 'dydxpub',
bech32PrefixValPub: 'dydxvaloperpub', bech32PrefixValPub: 'dydxvaloperpub',
@@ -385,12 +329,18 @@ 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 = 'Dv4TNT', DYDX = 'dydx',
} }
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',
};
+19 -12
View File
@@ -1,22 +1,23 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useSelector } from 'react-redux'; import { shallowEqual, 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 { CLIENT_NETWORK_CONFIGS } from '@/constants/networks'; import { USDC_DENOM, DYDX_DENOM } from '@dydxprotocol/v4-client-js';
import { ENVIRONMENT_CONFIG_MAP } 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';
import { convertBech32Address } from '@/lib/addressUtils'; import { convertBech32Address } from '@/lib/addressUtils';
import { MustBigNumber } from '@/lib/numbers'; import { MustBigNumber } from '@/lib/numbers';
import { getBalances, getStakingBalances } 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
@@ -36,10 +37,10 @@ type UseAccountBalanceProps = {
* 0xSquid uses this 0x address as the chain's default token. * 0xSquid uses this 0x address as the chain's default token.
* @todo We will need to add additional logic here if we 'useAccountBalance' on non-Squid related forms. * @todo We will need to add additional logic here if we 'useAccountBalance' on non-Squid related forms.
*/ */
const CHAIN_DEFAULT_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; export const CHAIN_DEFAULT_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
export const useAccountBalance = ({ export const useAccountBalance = ({
addressOrDenom = CHAIN_DEFAULT_TOKEN_ADDRESS, addressOrDenom,
assetSymbol, assetSymbol,
bech32AddrPrefix, bech32AddrPrefix,
chainId, chainId,
@@ -50,16 +51,16 @@ export const useAccountBalance = ({
const { evmAddress, dydxAddress } = useAccounts(); const { evmAddress, dydxAddress } = useAccounts();
const selectedNetwork = useSelector(getSelectedNetwork); const selectedNetwork = useSelector(getSelectedNetwork);
const evmChainId = Number(CLIENT_NETWORK_CONFIGS[selectedNetwork].ethereumChainId); const balances = useSelector(getBalances, shallowEqual);
const evmChainId = Number(ENVIRONMENT_CONFIG_MAP[selectedNetwork].ethereumChainId);
const stakingBalances = useSelector(getStakingBalances, shallowEqual);
const evmQuery = useBalance({ const evmQuery = useBalance({
enabled: Boolean(!isCosmosChain && addressOrDenom?.startsWith('0x')), enabled: Boolean(!isCosmosChain && addressOrDenom?.startsWith('0x')),
address: evmAddress, address: evmAddress,
chainId: typeof chainId === 'number' ? chainId : Number(evmChainId), chainId: typeof chainId === 'number' ? chainId : Number(evmChainId),
token: token:
addressOrDenom === CHAIN_DEFAULT_TOKEN_ADDRESS addressOrDenom === CHAIN_DEFAULT_TOKEN_ADDRESS ? undefined : (addressOrDenom as EvmAddress),
? undefined
: (addressOrDenom as EvmAddress),
watch: true, watch: true,
}); });
@@ -92,17 +93,23 @@ 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 = usePollNativeTokenBalance({ dydxAddress }); const nativeTokenCoinBalance = balances?.[DYDX_DENOM];
const nativeTokenBalance = MustBigNumber(nativeTokenCoinBalance?.amount) const nativeTokenBalance = MustBigNumber(nativeTokenCoinBalance?.amount)
.div(QUANTUM_MULTIPLIER) .div(QUANTUM_MULTIPLIER)
.toNumber(); .toNumber();
const usdcCoinBalance = usePollUSDCBalance({ dydxAddress }); const usdcCoinBalance = balances?.[USDC_DENOM];
const usdcBalance = MustBigNumber(usdcCoinBalance?.amount).div(QUANTUM_MULTIPLIER).toNumber(); const usdcBalance = MustBigNumber(usdcCoinBalance?.amount).div(QUANTUM_MULTIPLIER).toNumber();
const nativeStakingCoinBalanace = stakingBalances?.[DYDX_DENOM];
const nativeStakingBalance = MustBigNumber(nativeStakingCoinBalanace?.amount)
.div(QUANTUM_MULTIPLIER)
.toNumber();
return { return {
balance, balance,
nativeTokenBalance, nativeTokenBalance,
nativeStakingBalance,
usdcBalance, usdcBalance,
queryStatus: isCosmosChain ? cosmosQuery.status : evmQuery.status, queryStatus: isCosmosChain ? cosmosQuery.status : evmQuery.status,
isQueryFetching: isCosmosChain ? cosmosQuery.isFetching : evmQuery.fetchStatus === 'fetching', isQueryFetching: isCosmosChain ? cosmosQuery.isFetching : evmQuery.fetchStatus === 'fetching',
+3 -6
View File
@@ -8,10 +8,7 @@ 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 { import { setOnboardingState, setOnboardingGuard } from '@/state/account';
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';
@@ -222,9 +219,9 @@ const useAccountsContext = () => {
// abacus // abacus
// TODO: useAbacus({ dydxAddress }) // TODO: useAbacus({ dydxAddress })
useEffect(() => { useEffect(() => {
if (dydxAddress) abacusStateManager.setAccount(dydxAddress); if (dydxAddress) abacusStateManager.setAccount(localDydxWallet);
else abacusStateManager.attemptDisconnectAccount(); else abacusStateManager.attemptDisconnectAccount();
}, [dydxAddress]); }, [localDydxWallet]);
// clear subaccounts when no dydxAddress is set // clear subaccounts when no dydxAddress is set
useEffect(() => { useEffect(() => {
+10 -15
View File
@@ -1,29 +1,24 @@
import { isDydxV4Network } from '@/constants/networks';
import { WalletType } from '@/constants/wallets'; import { WalletType } from '@/constants/wallets';
import { isTruthy } from '@/lib/isTruthy'; import { isTruthy } from '@/lib/isTruthy';
import { useSelectedNetwork } from './useSelectedNetwork';
export const useDisplayedWallets = () => { export const useDisplayedWallets = () => {
const { selectedNetwork } = useSelectedNetwork();
return [ return [
WalletType.MetaMask, WalletType.MetaMask,
import.meta.env.MODE !== 'production' && isDydxV4Network(selectedNetwork) && WalletType.Keplr, import.meta.env.MODE !== 'production' && WalletType.Keplr,
WalletType.WalletConnect2, WalletType.WalletConnect2,
WalletType.WalletConnect,
WalletType.CoinbaseWallet, WalletType.CoinbaseWallet,
WalletType.ImToken, // Hide these wallet options until they can be properly tested on mainnet
WalletType.Rainbow, // WalletType.ImToken,
WalletType.TrustWallet, // WalletType.Rainbow,
WalletType.HuobiWallet, // WalletType.TrustWallet,
WalletType.BitKeep, // WalletType.HuobiWallet,
WalletType.Coin98, // WalletType.BitKeep,
// WalletType.Coin98,
WalletType.OtherWallet, WalletType.OtherWallet,
].filter(isTruthy); ].filter(isTruthy);
+1 -1
View File
@@ -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,
+1 -2
View File
@@ -3,7 +3,7 @@ import { useDispatch } from 'react-redux';
import { LocalStorageKey } from '@/constants/localStorage'; import { LocalStorageKey } from '@/constants/localStorage';
import { DEFAULT_APP_ENVIRONMENT, DydxNetwork, isValidDydxNetwork } from '@/constants/networks'; import { DEFAULT_APP_ENVIRONMENT, type DydxNetwork } from '@/constants/networks';
import { useLocalStorage } from '@/hooks'; import { useLocalStorage } from '@/hooks';
@@ -18,7 +18,6 @@ export const useInitializePage = () => {
const [localStorageNetwork] = useLocalStorage<DydxNetwork>({ const [localStorageNetwork] = useLocalStorage<DydxNetwork>({
key: LocalStorageKey.SelectedNetwork, key: LocalStorageKey.SelectedNetwork,
defaultValue: DEFAULT_APP_ENVIRONMENT, defaultValue: DEFAULT_APP_ENVIRONMENT,
validateFn: isValidDydxNetwork, // backwards compatibility
}); });
useEffect(() => { useEffect(() => {
+7 -3
View File
@@ -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 { useSquid } from '@/hooks/useSquid'; import { STATUS_ERROR_GRACE_PERIOD, useSquid } from '@/hooks/useSquid';
import { useLocalStorage } from './useLocalStorage'; import { useLocalStorage } from './useLocalStorage';
const LocalNotificationsContext = createContext< const LocalNotificationsContext = createContext<
@@ -62,15 +62,19 @@ const useLocalNotificationsContext = () => {
txHash, txHash,
toChainId, toChainId,
fromChainId, fromChainId,
triggeredAt,
status: currentStatus, status: currentStatus,
} 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) {
console.error(error); // ignore errors for the first 120s since the status might not be available yet
if (!triggeredAt || Date.now() - triggeredAt > STATUS_ERROR_GRACE_PERIOD) {
statuses[txHash] = error;
}
} }
} }
return statuses; return statuses;
+9 -11
View File
@@ -28,15 +28,13 @@ export const useMarketsData = (
const allAssets = useSelector(getAssets, shallowEqual) || {}; const allAssets = useSelector(getAssets, shallowEqual) || {};
const markets = useMemo(() => { const markets = useMemo(() => {
return Object.values(allPerpetualMarkets) return Object.values(allPerpetualMarkets).map((marketData) => ({
.filter(({ assetId }) => allAssets[assetId]) // Filter out markets with no asset information asset: allAssets[marketData.assetId],
.map((marketData) => ({ tickSizeDecimals: marketData.configs?.tickSizeDecimals,
asset: allAssets[marketData.assetId], ...marketData,
tickSizeDecimals: marketData.configs?.tickSizeDecimals, ...marketData.perpetual,
...marketData, ...marketData.configs,
...marketData.perpetual, })) as MarketData[];
...marketData.configs,
})) as MarketData[];
}, [allPerpetualMarkets, allAssets]); }, [allPerpetualMarkets, allAssets]);
const filteredMarkets = useMemo(() => { const filteredMarkets = useMemo(() => {
@@ -45,8 +43,8 @@ export const useMarketsData = (
if (searchFilter) { if (searchFilter) {
return filtered.filter( return filtered.filter(
({ asset }) => ({ asset }) =>
asset.name?.toLocaleLowerCase().includes(searchFilter.toLowerCase()) || asset?.name?.toLocaleLowerCase().includes(searchFilter.toLowerCase()) ||
asset.symbol?.toLocaleLowerCase().includes(searchFilter.toLowerCase()) asset?.id?.toLocaleLowerCase().includes(searchFilter.toLowerCase())
); );
} }
return filtered; return filtered;
+45 -8
View File
@@ -1,7 +1,9 @@
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';
@@ -10,6 +12,7 @@ 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';
@@ -119,19 +122,36 @@ 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) }),
// TODO: confirm with design what the description should be description: description,
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}
@@ -139,7 +159,12 @@ export const notificationTypes = [
status={transfer.status} status={transfer.status}
/> />
), ),
customMenuContent: !finished && <TransferStatusSteps status={transfer.status} />, customMenuContent: !finished && (
<div>
{description}
<TransferStatusSteps status={transfer.status} />
</div>
),
toastSensitivity: 'foreground', toastSensitivity: 'foreground',
}, },
[] []
@@ -149,3 +174,15 @@ 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;
`;
+26
View File
@@ -0,0 +1,26 @@
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,
};
};
-31
View File
@@ -1,31 +0,0 @@
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;
};
-30
View File
@@ -1,30 +0,0 @@
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;
};
+6 -8
View File
@@ -1,13 +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, DydxV4Network, isDydxV4Network } from '@/constants/networks'; import { ENVIRONMENT_CONFIG_MAP } 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 const STATUS_ERROR_GRACE_PERIOD = 120_000;
const useSquidContext = () => { const useSquidContext = () => {
const selectedNetwork = useSelector(getSelectedNetwork); const selectedNetwork = useSelector(getSelectedNetwork);
@@ -21,10 +22,7 @@ const useSquidContext = () => {
}; };
const squid = useMemo( const squid = useMemo(
() => () => new Squid({ baseUrl: ENVIRONMENT_CONFIG_MAP[selectedNetwork]?.endpoints['0xsquid'] }),
isDydxV4Network(selectedNetwork)
? new Squid({ baseUrl: CLIENT_NETWORK_CONFIGS[selectedNetwork]?.endpoints['0xsquid'] })
: undefined,
[selectedNetwork] [selectedNetwork]
); );
@@ -38,7 +36,7 @@ const useSquidContext = () => {
}; };
type SquidContextType = ReturnType<typeof useSquidContext>; type SquidContextType = ReturnType<typeof useSquidContext>;
const SquidContext = createContext<SquidContextType>(undefined); const SquidContext = createContext<SquidContextType | undefined>(undefined);
SquidContext.displayName = '0xSquid'; SquidContext.displayName = '0xSquid';
export const SquidProvider = ({ ...props }) => ( export const SquidProvider = ({ ...props }) => (
+56 -331
View File
@@ -1,53 +1,41 @@
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { useDispatch } from 'react-redux'; import { shallowEqual, useSelector, useDispatch } from 'react-redux';
import type { Nullable } from '@dydxprotocol/abacus'; import type { Nullable } from '@dydxprotocol/v4-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 {
LocalWallet, type 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 {
HumanReadableCancelOrderPayload, AccountBalance,
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 { import { setSubaccount, setHistoricalPnl, removeUncommittedOrderClientId } from '@/state/account';
addUncommittedOrderClientId, import { getBalances } from '@/state/accountSelectors';
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);
@@ -83,12 +71,8 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
const { const {
depositToSubaccount, depositToSubaccount,
withdrawFromSubaccount, withdrawFromSubaccount,
simulateWithdrawFromSubaccount,
transferFromSubaccountToAddress, transferFromSubaccountToAddress,
transferNativeToken, transferNativeToken,
simulateTransferNativeToken,
placeOrderForSubaccount,
cancelOrderForSubaccount,
sendSquidWithdrawFromSubaccount, sendSquidWithdrawFromSubaccount,
} = useMemo( } = useMemo(
() => ({ () => ({
@@ -110,31 +94,6 @@ 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,
@@ -185,125 +144,6 @@ 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,
@@ -353,10 +193,10 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
// ------ Deposit/Withdraw Methods ------ // // ------ Deposit/Withdraw Methods ------ //
const depositFunds = useCallback( const depositFunds = useCallback(
async (balance?: Coin) => { async (balance: AccountBalance) => {
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();
@@ -370,11 +210,14 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
[localDydxWallet, depositToSubaccount] [localDydxWallet, depositToSubaccount]
); );
const balance = usePollUSDCBalance({ dydxAddress }); const balances = useSelector(getBalances, shallowEqual);
const usdcCoinBalance = balances?.[USDC_DENOM];
useEffect(() => { useEffect(() => {
depositFunds(balance); if (usdcCoinBalance) {
}, [balance]); depositFunds(usdcCoinBalance);
}
}, [usdcCoinBalance]);
const deposit = useCallback( const deposit = useCallback(
async (amount: Long) => { async (amount: Long) => {
@@ -413,30 +256,6 @@ 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) {
@@ -467,101 +286,41 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
onSuccess, onSuccess,
}: { }: {
isClosePosition?: boolean; isClosePosition?: boolean;
onError?: (onErrorParams?: { errorStringKey?: string }) => void; onError?: (onErrorParams?: { errorStringKey?: Nullable<string> }) => void;
onSuccess?: () => void; onSuccess?: (placeOrderPayload: Nullable<HumanReadablePlaceOrderPayload>) => void;
}) => { }) => {
let orderParams: Nullable<HumanReadablePlaceOrderPayload>; const callback = (
success: boolean,
parsingError?: Nullable<ParsingError>,
data?: Nullable<HumanReadablePlaceOrderPayload>
) => {
if (success) {
onSuccess?.(data);
} else {
onError?.({ errorStringKey: parsingError?.stringKey });
if (!subaccountClient) return; if (data?.clientId !== undefined) {
dispatch(removeUncommittedOrderClientId(data.clientId));
try { }
orderParams = isClosePosition
? abacusStateManager.closePositionPayload()
: abacusStateManager.placeOrderPayload();
if (!orderParams) {
throw new Error('Missing order params');
} }
};
const { let placeOrderParams;
marketId,
type,
side,
price,
triggerPrice,
size,
clientId,
timeInForce,
goodTilTimeInSeconds,
execution,
postOnly,
reduceOnly,
} = orderParams;
dispatch(addUncommittedOrderClientId(clientId)); if (isClosePosition) {
placeOrderParams = abacusStateManager.closePosition(callback);
// Remove uncommitted order after timeout if it hasn't already been removed } else {
setTimeout(() => { placeOrderParams = abacusStateManager.placeOrder(callback);
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, placeOrderForSubaccount] [subaccountClient]
); );
const closePosition = useCallback( const closePosition = useCallback(
@@ -569,8 +328,8 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
onError, onError,
onSuccess, onSuccess,
}: { }: {
onError: (onErrorParams?: { errorStringKey?: string }) => void; onError: (onErrorParams?: { errorStringKey?: Nullable<string> }) => void;
onSuccess?: () => void; onSuccess?: (placeOrderPayload: Nullable<HumanReadablePlaceOrderPayload>) => void;
}) => await placeOrder({ isClosePosition: true, onError, onSuccess }), }) => await placeOrder({ isClosePosition: true, onError, onSuccess }),
[placeOrder] [placeOrder]
); );
@@ -582,53 +341,21 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
onSuccess, onSuccess,
}: { }: {
orderId: string; orderId: string;
onError?: ({ errorMsg }?: { errorMsg?: string }) => void; onError?: ({ errorStringKey }?: { errorStringKey?: Nullable<string> }) => void;
onSuccess?: () => void; onSuccess?: () => void;
}) => { }) => {
let cancelOrderParams: Nullable<HumanReadableCancelOrderPayload>; const callback = (success: boolean, parsingError?: Nullable<ParsingError>) => {
if (success) {
if (!subaccountClient) return; track(AnalyticsEvent.TradeCancelOrder);
onSuccess?.();
try { } else {
cancelOrderParams = abacusStateManager.cancelOrderPayload(orderId); onError?.({ errorStringKey: parsingError?.stringKey });
if (!cancelOrderParams) {
throw new Error('Missing cancel order params');
} }
};
const { clientId, clobPairId, goodTilBlock, goodTilBlockTime, orderFlags } = abacusStateManager.cancelOrder(orderId, callback);
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, cancelOrderForSubaccount] [subaccountClient]
); );
return { return {
@@ -638,9 +365,7 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
requestFaucetFunds, requestFaucetFunds,
// Transfer Methods // Transfer Methods
simulateTransfer,
transfer, transfer,
simulateWithdraw,
sendSquidWithdraw, sendSquidWithdraw,
// Trading Methods // Trading Methods
+1 -1
View File
@@ -1,4 +1,4 @@
const LogoShortIcon: React.FC<{ id?: string }> = ({ id }) => ( const LogoShortIcon: React.FC<{ id?: string }> = ({ id }: { id?: string }) => (
<svg <svg
width="135" width="135"
height="145" height="145"
+2
View File
@@ -11,6 +11,7 @@ import { DepositDialog } from '@/views/dialogs/DepositDialog';
import { DisconnectDialog } from '@/views/dialogs/DisconnectDialog'; import { DisconnectDialog } from '@/views/dialogs/DisconnectDialog';
import { ExchangeOfflineDialog } from '@/views/dialogs/ExchangeOfflineDialog'; import { ExchangeOfflineDialog } from '@/views/dialogs/ExchangeOfflineDialog';
import { HelpDialog } from '@/views/dialogs/HelpDialog'; import { HelpDialog } from '@/views/dialogs/HelpDialog';
import { ExternalNavKeplrDialog } from '@/views/dialogs/ExternalNavKeplrDialog';
import { MnemonicExportDialog } from '@/views/dialogs/MnemonicExportDialog'; import { MnemonicExportDialog } from '@/views/dialogs/MnemonicExportDialog';
import { MobileSignInDialog } from '@/views/dialogs/MobileSignInDialog'; import { MobileSignInDialog } from '@/views/dialogs/MobileSignInDialog';
import { OnboardingDialog } from '@/views/dialogs/OnboardingDialog'; import { OnboardingDialog } from '@/views/dialogs/OnboardingDialog';
@@ -47,6 +48,7 @@ export const DialogManager = () => {
[DialogTypes.ExchangeOffline]: <ExchangeOfflineDialog {...modalProps} />, [DialogTypes.ExchangeOffline]: <ExchangeOfflineDialog {...modalProps} />,
[DialogTypes.FillDetails]: <FillDetailsDialog {...modalProps} />, [DialogTypes.FillDetails]: <FillDetailsDialog {...modalProps} />,
[DialogTypes.Help]: <HelpDialog {...modalProps} />, [DialogTypes.Help]: <HelpDialog {...modalProps} />,
[DialogTypes.ExternalNavKeplr]: <ExternalNavKeplrDialog {...modalProps} />,
[DialogTypes.MnemonicExport]: <MnemonicExportDialog {...modalProps} />, [DialogTypes.MnemonicExport]: <MnemonicExportDialog {...modalProps} />,
[DialogTypes.MobileSignIn]: <MobileSignInDialog {...modalProps} />, [DialogTypes.MobileSignIn]: <MobileSignInDialog {...modalProps} />,
[DialogTypes.Onboarding]: <OnboardingDialog {...modalProps} />, [DialogTypes.Onboarding]: <OnboardingDialog {...modalProps} />,
+6 -2
View File
@@ -7,10 +7,9 @@ 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 } from '@/icons'; import { LogoShortIcon, BellIcon } 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';
@@ -43,6 +42,11 @@ export const HeaderDesktop = () => {
label: stringGetter({ key: STRING_KEYS.TRADE }), label: stringGetter({ key: STRING_KEYS.TRADE }),
href: AppRoute.Trade, href: AppRoute.Trade,
}, },
{
value: 'Dv4TNT',
label: 'Dv4TNT',
href: AppRoute.Rewards,
},
{ {
value: 'PORTFOLIO', value: 'PORTFOLIO',
label: stringGetter({ key: STRING_KEYS.PORTFOLIO }), label: stringGetter({ key: STRING_KEYS.PORTFOLIO }),
+15 -6
View File
@@ -15,12 +15,20 @@ type StyleProps = {
}; };
export const NotificationsToastArea = ({ className }: StyleProps) => { export const NotificationsToastArea = ({ className }: StyleProps) => {
const { notifications, getKey, getDisplayData, markUnseen, markSeen, isMenuOpen, onNotificationAction } = useNotifications(); const {
notifications,
getKey,
getDisplayData,
markUnseen,
markSeen,
isMenuOpen,
onNotificationAction,
} = useNotifications();
const { isMobile } = useBreakpoints(); const { isMobile } = useBreakpoints();
return ( return (
<$ToastArea swipeDirection={isMobile ? 'up' : 'right'} className={className}> <StyledToastArea 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(
@@ -53,19 +61,20 @@ export const NotificationsToastArea = ({ className }: StyleProps) => {
sensitivity={displayData.toastSensitivity} sensitivity={displayData.toastSensitivity}
setIsOpen={(isOpen, isClosedFromTimeout) => { setIsOpen={(isOpen, isClosedFromTimeout) => {
if (!isOpen) if (!isOpen)
// Toast timer expired without user interaction if (isClosedFromTimeout)
if (isClosedFromTimeout) markUnseen(notification); // Toast timer expired without user interaction
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]}
/> />
))} ))}
</$ToastArea> </StyledToastArea>
); );
}; };
const $ToastArea = styled(ToastArea)` const StyledToastArea = styled(ToastArea)`
position: absolute; position: absolute;
width: min(16rem, 100%); width: min(16rem, 100%);
inset: 0 0 0 auto; inset: 0 0 0 auto;
+18
View File
@@ -0,0 +1,18 @@
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;
+309 -25
View File
@@ -1,28 +1,63 @@
import Abacus, { Nullable } from '@dydxprotocol/abacus'; import Abacus, { type Nullable } from '@dydxprotocol/v4-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(
@@ -33,30 +68,41 @@ 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> {
this.compositeClient = await CompositeClient.connect( try {
new Network( const compositeClient = await CompositeClient.connect(
chainId, new Network(
new IndexerConfig(indexerUrl, indexerSocketUrl), chainId,
new ValidatorConfig(validatorUrl, chainId, { new IndexerConfig(indexerUrl, indexerSocketUrl),
broadcastPollIntervalMs: 3_000, new ValidatorConfig(validatorUrl, chainId, {
broadcastTimeoutMs: 60_000, broadcastPollIntervalMs: 3_000,
}) broadcastTimeoutMs: 60_000,
) })
); )
);
// Dispatch custom event to notify other parts of the app that the network has been connected this.compositeClient = compositeClient;
const customEvent = new CustomEvent('abacus:connectNetwork', {
detail: {
indexerUrl,
indexerSocketUrl,
validatorUrl,
chainId,
faucetUrl,
},
});
globalThis.dispatchEvent(customEvent); // Dispatch custom event to notify other parts of the app that the network has been connected
callback(JSON.stringify({ success: true })); const customEvent = new CustomEvent('abacus:connectNetwork', {
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 {
@@ -72,6 +118,10 @@ 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) {
@@ -85,14 +135,218 @@ 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> {
// To be implemented try {
return; const params = paramsInJson ? JSON.parse(paramsInJson) : undefined;
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(
@@ -116,6 +370,12 @@ 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);
@@ -135,6 +395,30 @@ 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;
case QueryType.RewardsParams:
const rewardsParams = await this.compositeClient?.validatorClient.get.getRewardsParams();
const parsedRewardsParams = this.parseToPrimitives(rewardsParams);
callback(JSON.stringify(parsedRewardsParams));
break;
case QueryType.GetMarketPrice:
const price = await this.compositeClient?.validatorClient.get.getPrice(params.marketId);
const parsedPrice = this.parseToPrimitives(price);
callback(JSON.stringify(parsedPrice));
break;
case QueryType.GetDelegations:
const delegations =
await this.compositeClient?.validatorClient.get.getDelegatorDelegations(params.address);
const parseDelegations = this.parseToPrimitives(delegations);
callback(JSON.stringify(parseDelegations));
break;
// Do not implement Transfers (yet) // Do not implement Transfers (yet)
case QueryType.Transfers: case QueryType.Transfers:
default: default:
-5
View File
@@ -1,5 +1,4 @@
import type { AbacusFileSystemProtocol, FileLocation, Nullable } from '@/constants/abacus'; import type { AbacusFileSystemProtocol, FileLocation, Nullable } from '@/constants/abacus';
import { NETWORK_ENDPOINTS } from '@/constants/networks';
export const ENDPOINTS_PATH = export const ENDPOINTS_PATH =
import.meta.env.MODE === 'production' import.meta.env.MODE === 'production'
@@ -8,10 +7,6 @@ export const ENDPOINTS_PATH =
class AbacusFileSystem implements AbacusFileSystemProtocol { class AbacusFileSystem implements AbacusFileSystemProtocol {
readTextFile(location: FileLocation, path: string): Nullable<string> { readTextFile(location: FileLocation, path: string): Nullable<string> {
if (path === ENDPOINTS_PATH) {
return JSON.stringify(NETWORK_ENDPOINTS);
}
return null; return null;
} }
+48 -16
View File
@@ -1,3 +1,5 @@
import type { LocalWallet } from '@dydxprotocol/v4-client-js';
import type { import type {
ClosePositionInputFields, ClosePositionInputFields,
Nullable, Nullable,
@@ -6,6 +8,7 @@ import type {
TradeInputFields, TradeInputFields,
TransferInputFields, TransferInputFields,
HistoricalPnlPeriods, HistoricalPnlPeriods,
ParsingError,
} from '@/constants/abacus'; } from '@/constants/abacus';
import { import {
@@ -19,10 +22,11 @@ import {
UIImplementations, UIImplementations,
CoroutineTimer, CoroutineTimer,
TransferType, TransferType,
AbacusAppConfig,
} from '@/constants/abacus'; } from '@/constants/abacus';
import { DEFAULT_MARKETID } from '@/constants/markets'; import { DEFAULT_MARKETID } from '@/constants/markets';
import { type DydxNetwork } from '@/constants/networks'; import { CURRENT_ABACUS_DEPLOYMENT, type DydxNetwork } from '@/constants/networks';
import type { RootStore } from '@/state/_store'; import type { RootStore } from '@/state/_store';
@@ -30,6 +34,7 @@ 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';
@@ -45,21 +50,25 @@ 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,
new AbacusChainTransaction(), this.chainTransactions,
null, this.analytics,
new AbacusThreading(), new AbacusThreading(),
new CoroutineTimer(), new CoroutineTimer(),
new AbacusFileSystem() new AbacusFileSystem()
@@ -72,8 +81,9 @@ class AbacusStateManager {
); );
this.stateManager = new AsyncAbacusStateManager( this.stateManager = new AsyncAbacusStateManager(
import.meta.env.SHARED_RESOURCES_URI, '',
ENDPOINTS_PATH, CURRENT_ABACUS_DEPLOYMENT,
AbacusAppConfig.Companion.forWeb,
ioImplementations, ioImplementations,
uiImplementations, uiImplementations,
// @ts-ignore // @ts-ignore
@@ -155,10 +165,14 @@ 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 = (walletAddress: string) => { setAccount = (localWallet?: LocalWallet) => {
this.stateManager.accountAddress = walletAddress; if (localWallet) {
this.stateManager.accountAddress = localWallet.address;
this.chainTransactions.setLocalWallet(localWallet);
}
}; };
setTransfersSourceAddress = (evmAddress: string) => { setTransfersSourceAddress = (evmAddress: string) => {
@@ -216,16 +230,34 @@ 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 -1
View File
@@ -1,4 +1,4 @@
import type { Nullable, kollections } from '@dydxprotocol/abacus'; import type { Nullable, kollections } from '@dydxprotocol/v4-abacus';
import type { AbacusRestProtocol } from '@/constants/abacus'; import type { AbacusRestProtocol } from '@/constants/abacus';
+32 -6
View File
@@ -1,6 +1,7 @@
import { kollections } from '@dydxprotocol/abacus'; import { kollections } from '@dydxprotocol/v4-abacus';
import type { import type {
AccountBalance,
AbacusApiState, AbacusApiState,
AbacusNotification, AbacusNotification,
AbacusStateNotificationProtocol, AbacusStateNotificationProtocol,
@@ -18,9 +19,12 @@ import { Changes } from '@/constants/abacus';
import type { RootStore } from '@/state/_store'; import type { RootStore } from '@/state/_store';
import { import {
setBalances,
setStakingBalances,
setFills, setFills,
setFundingPayments, setFundingPayments,
setHistoricalPnl, setHistoricalPnl,
setLatestOrder,
setSubaccount, setSubaccount,
setTransfers, setTransfers,
setWallet, setWallet,
@@ -31,6 +35,7 @@ import { setAssets } from '@/state/assets';
import { setConfigs } from '@/state/configs'; import { setConfigs } from '@/state/configs';
import { setInputs } from '@/state/inputs'; import { setInputs } from '@/state/inputs';
import { setHistoricalFundings, setLiveTrades, setMarkets, setOrderbook } from '@/state/perpetuals'; import { setHistoricalFundings, setLiveTrades, setMarkets, setOrderbook } from '@/state/perpetuals';
import { isTruthy } from '../isTruthy';
class AbacusStateNotifier implements AbacusStateNotificationProtocol { class AbacusStateNotifier implements AbacusStateNotificationProtocol {
private store: RootStore | undefined; private store: RootStore | undefined;
@@ -71,6 +76,23 @@ 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 (updatedState.account?.stakingBalances) {
const stakingBalances: Record<string, AccountBalance> = {};
for (const { k, v } of updatedState.account.stakingBalances.toArray()) {
stakingBalances[k] = v;
}
dispatch(setStakingBalances(stakingBalances));
}
}
if (changes.has(Changes.configs)) { if (changes.has(Changes.configs)) {
dispatch(setConfigs(updatedState.configs)); dispatch(setConfigs(updatedState.configs));
} }
@@ -87,10 +109,12 @@ class AbacusStateNotifier implements AbacusStateNotificationProtocol {
dispatch( dispatch(
setMarkets({ setMarkets({
markets: Object.fromEntries( markets: Object.fromEntries(
(marketIds || updatedState.marketIds()?.toArray() || []).map((marketId: string) => { (marketIds || updatedState.marketIds()?.toArray() || [])
const marketData = updatedState.market(marketId); .map((marketId: string) => {
return [marketId, marketData]; const marketData = updatedState.market(marketId);
}) return [marketId, marketData];
})
.filter(isTruthy)
) as Record<string, PerpetualMarket>, ) as Record<string, PerpetualMarket>,
update: !!marketIds, update: !!marketIds,
}) })
@@ -155,7 +179,9 @@ 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());
+8
View File
@@ -5,10 +5,15 @@ 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 },
}); });
@@ -20,6 +25,9 @@ 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 },
}); });
+15 -1
View File
@@ -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 shorternNumberForDisplay = (num?: number) => export const shortenNumberForDisplay = (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,3 +66,17 @@ 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;
}
+6
View File
@@ -58,6 +58,12 @@ 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 {
+11 -3
View File
@@ -1,5 +1,7 @@
export const log = (location: string, error: Error, metadata?: any) => { export const log = (location: string, error: Error, metadata?: any) => {
// console.warn(error); if (import.meta.env.MODE !== 'production') {
console.warn('telemetry/log:', { location, error, metadata });
}
const customEvent = new CustomEvent('dydx:log', { const customEvent = new CustomEvent('dydx:log', {
detail: { detail: {
@@ -16,7 +18,10 @@ export const log = (location: string, error: Error, metadata?: any) => {
globalThis.addEventListener('unhandledrejection', (event) => { globalThis.addEventListener('unhandledrejection', (event) => {
event.preventDefault(); event.preventDefault();
log('window/onunhandledrejection', new Error(`Promise rejected and uncaught; reason: ${event.reason}`)); log(
'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)
@@ -25,7 +30,10 @@ globalThis.addEventListener(
(event) => { (event) => {
event.preventDefault(); event.preventDefault();
log('window/onrejectionhandled', new Error(`Promise rejected and caught late; reason: ${event.reason}`)); log(
'window/onrejectionhandled',
new Error(`Promise rejected and caught late; reason: ${event.reason}`)
);
}, },
false false
); );
+34 -27
View File
@@ -10,7 +10,23 @@ import {
bscTestnet, bscTestnet,
optimism, optimism,
optimismGoerli, optimismGoerli,
} from 'wagmi/chains'; base,
baseGoerli,
polygon,
polygonMumbai,
linea,
lineaTestnet,
mantle,
mantleTestnet,
moonbeam,
moonbaseAlpha,
filecoin,
filecoinHyperspace,
fantom,
fantomTestnet,
celo,
celoAlfajores,
} from 'viem/chains';
import { alchemyProvider } from 'wagmi/providers/alchemy'; import { alchemyProvider } from 'wagmi/providers/alchemy';
import { jsonRpcProvider } from 'wagmi/providers/jsonRpc'; import { jsonRpcProvider } from 'wagmi/providers/jsonRpc';
@@ -36,10 +52,26 @@ export const WAGMI_SUPPORTED_CHAINS: Chain[] = [
bscTestnet, bscTestnet,
optimism, optimism,
optimismGoerli, optimismGoerli,
base,
baseGoerli,
polygon,
polygonMumbai,
linea,
lineaTestnet,
mantle,
mantleTestnet,
moonbeam,
moonbaseAlpha,
filecoin,
filecoinHyperspace,
fantom,
fantomTestnet,
celo,
celoAlfajores,
]; ];
const { chains, publicClient, webSocketPublicClient } = configureChains(WAGMI_SUPPORTED_CHAINS, [ const { chains, publicClient, webSocketPublicClient } = configureChains(WAGMI_SUPPORTED_CHAINS, [
alchemyProvider({ apiKey: import.meta.env.VITE_ALCHEMY_API_KEY }), // alchemyProvider({ apiKey: import.meta.env.VITE_ALCHEMY_API_KEY }),
jsonRpcProvider({ jsonRpcProvider({
rpc: (chain) => ({ http: chain.rpcUrls.default.http[0] }), rpc: (chain) => ({ http: chain.rpcUrls.default.http[0] }),
}), }),
@@ -55,14 +87,6 @@ const injectedConnectorOptions = {
}, },
}; };
const walletconnect1ConnectorOptions = {
chains,
options: {
bridge: import.meta.env.VITE_WALLETCONNECT1_BRIDGE,
qrcode: true,
},
};
const walletconnect2ConnectorOptions: ConstructorParameters<typeof WalletConnectConnector>[0] = { const walletconnect2ConnectorOptions: ConstructorParameters<typeof WalletConnectConnector>[0] = {
chains, chains,
options: { options: {
@@ -111,7 +135,6 @@ const connectors = [
reloadOnDisconnect: false, reloadOnDisconnect: false,
}, },
}), }),
new WalletConnectLegacyConnector(walletconnect1ConnectorOptions),
new WalletConnectConnector(walletconnect2ConnectorOptions), new WalletConnectConnector(walletconnect2ConnectorOptions),
new InjectedConnector(injectedConnectorOptions), new InjectedConnector(injectedConnectorOptions),
]; ];
@@ -134,19 +157,6 @@ const createInjectedConnectorWithProvider = (provider: ExternalProvider) =>
provider as unknown as Awaited<ReturnType<InjectedConnector['getProvider']>>; provider as unknown as Awaited<ReturnType<InjectedConnector['getProvider']>>;
})(injectedConnectorOptions) as InjectedConnector; })(injectedConnectorOptions) as InjectedConnector;
// Create a custom wagmi WalletConnectLegacyConnector with a modal showing only wallet links matching the given name
const createWalletConnect1ConnectorWithName = (walletconnect1Name: string) =>
new WalletConnectLegacyConnector({
...walletconnect1ConnectorOptions,
options: {
...walletconnect1ConnectorOptions.options,
qrcodeModalOptions: {
desktopLinks: [walletconnect1Name],
mobileLinks: [walletconnect1Name],
},
},
});
const createWalletConnect2ConnectorWithId = (walletconnect2Id: string) => const createWalletConnect2ConnectorWithId = (walletconnect2Id: string) =>
new WalletConnectConnector({ new WalletConnectConnector({
...walletconnect2ConnectorOptions, ...walletconnect2ConnectorOptions,
@@ -182,9 +192,6 @@ export const resolveWagmiConnector = ({
return walletConnection.type === WalletConnectionType.InjectedEip1193 && walletConnection.provider return walletConnection.type === WalletConnectionType.InjectedEip1193 && walletConnection.provider
? createInjectedConnectorWithProvider(walletConnection.provider) ? createInjectedConnectorWithProvider(walletConnection.provider)
: walletConnection.type === WalletConnectionType.WalletConnect1 &&
walletConfig.walletconnect1Name
? createWalletConnect1ConnectorWithName(walletConfig.walletconnect1Name)
: walletConnection.type === WalletConnectionType.WalletConnect2 && walletConfig.walletconnect2Id : walletConnection.type === WalletConnectionType.WalletConnect2 && walletConfig.walletconnect2Id
? createWalletConnect2ConnectorWithId(walletConfig.walletconnect2Id) ? createWalletConnect2ConnectorWithId(walletConfig.walletconnect2Id)
: connectors.find(({ id }: { id: string }) => id === walletConnectionConfig.wagmiConnectorId); : connectors.find(({ id }: { id: string }) => id === walletConnectionConfig.wagmiConnectorId);
-6
View File
@@ -45,12 +45,6 @@ export const getWalletConnection = ({
break; break;
} }
case WalletConnectionType.WalletConnect1: {
return {
type: WalletConnectionType.WalletConnect1,
};
}
case WalletConnectionType.WalletConnect2: { case WalletConnectionType.WalletConnect2: {
return { return {
type: WalletConnectionType.WalletConnect2, type: WalletConnectionType.WalletConnect2,
+3 -3
View File
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { Nullable } from '@dydxprotocol/abacus'; import { Nullable } from '@dydxprotocol/v4-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);
} }
+8 -10
View File
@@ -29,10 +29,10 @@ export const DYDXBalancePanel = () => {
const { walletType } = useAccounts(); const { walletType } = useAccounts();
const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual); const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual);
const { nativeTokenBalance } = useAccountBalance(); const { nativeTokenBalance, nativeStakingBalance } = useAccountBalance();
return ( return (
<Styled.TransfersCard <Panel
slotHeader={ slotHeader={
<Styled.Header> <Styled.Header>
<Styled.Title> <Styled.Title>
@@ -97,7 +97,7 @@ export const DYDXBalancePanel = () => {
</Styled.Label> </Styled.Label>
), ),
value: <Output type={OutputType.Asset} value={undefined} />, value: <Output type={OutputType.Asset} value={nativeStakingBalance} />,
}, },
]} ]}
/> />
@@ -106,21 +106,17 @@ export const DYDXBalancePanel = () => {
{ {
key: 'totalBalance', key: 'totalBalance',
label: 'Total balance', label: 'Total balance',
value: <Output type={OutputType.Asset} value={nativeTokenBalance} tag="Dv4TNT" />, value: <Output type={OutputType.Asset} value={nativeTokenBalance + nativeStakingBalance} tag="Dv4TNT" />,
}, },
]} ]}
/> />
</Styled.Content> </Styled.Content>
</Styled.TransfersCard> </Panel>
); );
}; };
const Styled: Record<string, AnyStyledComponent> = {}; const Styled: Record<string, AnyStyledComponent> = {};
Styled.TransfersCard = styled(Panel)`
width: 21.25rem;
`;
Styled.Header = styled.div` Styled.Header = styled.div`
${layoutMixins.spacedRow} ${layoutMixins.spacedRow}
padding: 1.25rem 1.5rem 0.5rem; padding: 1.25rem 1.5rem 0.5rem;
@@ -174,13 +170,15 @@ Styled.IconContainer = styled.div`
Styled.WalletAndStakedBalance = styled(Details)` Styled.WalletAndStakedBalance = styled(Details)`
--details-item-backgroundColor: var(--color-layer-6); --details-item-backgroundColor: var(--color-layer-6);
grid-template-columns: 1fr 1fr;
gap: 0.5rem; gap: 0.5rem;
> div { > div {
gap: 1rem; gap: 1rem;
padding: 1rem; padding: 1rem;
width: 8.625rem;
border-radius: 0.75em; border-radius: 0.75em;
background-color: var(--color-layer-5); background-color: var(--color-layer-5);
+322 -6
View File
@@ -1,17 +1,333 @@
import styled, { AnyStyledComponent } from 'styled-components'; import styled, { AnyStyledComponent } from 'styled-components';
import { useDispatch, useSelector } from 'react-redux';
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { STRING_KEYS } from '@/constants/localization';
import { ButtonAction, ButtonSize, ButtonState, ButtonType } from '@/constants/buttons';
import { DialogTypes } from '@/constants/dialogs';
import { useAccountBalance, useBreakpoints, useStringGetter } from '@/hooks';
import { breakpoints } from '@/styles';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
import { Details } from '@/components/Details';
import { Panel } from '@/components/Panel';
import { IconName } from '@/components/Icon';
import { Button } from '@/components/Button';
import { IconButton } from '@/components/IconButton';
import { Link } from '@/components/Link';
import { Output, OutputType } from '@/components/Output';
import { VerticalSeparator } from '@/components/Separator';
import { WithReceipt } from '@/components/WithReceipt';
import { openDialog } from '@/state/dialogs';
import { getSelectedNetwork } from '@/state/appSelectors';
import { DYDXBalancePanel } from './DYDXBalancePanel'; import { DYDXBalancePanel } from './DYDXBalancePanel';
export const RewardsPage = () => ( // TODO: replace placeholder URL with real URLs when avaialble
<Styled.Page> const GOVERNANCE_HELP_URL = 'https://help.dydx.exchange/';
<DYDXBalancePanel /> const STAKING_HELP_URL = 'https://help.dydx.exchange/';
</Styled.Page>
); export const RewardsPage = () => {
const dispatch = useDispatch();
const stringGetter = useStringGetter();
const { isTablet, isNotTablet } = useBreakpoints();
const selectedNetwork = useSelector(getSelectedNetwork);
// const chainId = Number(ENVIRONMENT_CONFIG_MAP[selectedNetwork].ethereumChainId);
// const { balance } = useAccountBalance({
// addressOrDenom: import.meta.env.VITE_V3_TOKEN_ADDRESS,
// assetSymbol: 'DYDX',
// chainId,
// isCosmosChain: false,
// });
// const tokenBalance = import.meta.env.VITE_V3_TOKEN_ADDRESS ? balance : 0;
return (
<Styled.Page>
{/* {isNotTablet ? (
<Styled.Migrate>
<Styled.TwoItemRow>
<div>
<Styled.MigrateTitle>
{stringGetter({ key: STRING_KEYS.MIGRATE })}
</Styled.MigrateTitle>
<Styled.Description>
{stringGetter({ key: STRING_KEYS.MIGRATE_DESCRIPTION })}
<Link href={MIGRATE_HELP_URL}>
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
</Link>
</Styled.Description>
</div>
<Styled.MigrateAction>
<div>
<div>{stringGetter({ key: STRING_KEYS.AVAILABLE_TO_MIGRATE })}</div>
<div>
<Styled.Token type={OutputType.Asset} value={tokenBalance} />
</div>
</div>
<Button
action={tokenBalance ? ButtonAction.Primary : ButtonAction.Base}
// type={ButtonType.Link}
// href={BRIDGE_URL}
disabled
>
{tokenBalance
? `${stringGetter({ key: STRING_KEYS.MIGRATE_NOW })} →`
: stringGetter({ key: STRING_KEYS.NO_TOKENS_TO_MIGRATE })}
</Button>
</Styled.MigrateAction>
</Styled.TwoItemRow>
</Styled.Migrate>
) : (
<Styled.MobileMigrateCard
slotHeader={
<Styled.MobileMigrateHeader>
<h3>{stringGetter({ key: STRING_KEYS.MIGRATE })}</h3>
<VerticalSeparator />
<span>
{stringGetter({
key: STRING_KEYS.FROM_TO,
params: { FROM: <strong>Ethereum</strong>, TO: <strong>dYdX Chain</strong> },
})}
</span>
</Styled.MobileMigrateHeader>
}
>
<Styled.WithReceipt
slotReceipt={
<Styled.Details
items={[
{
key: 'available-to-migrate',
label: stringGetter({ key: STRING_KEYS.AVAILABLE_TO_MIGRATE }),
value: <Output type={OutputType.Asset} value={tokenBalance} />,
},
]}
/>
}
>
<Button
action={tokenBalance ? ButtonAction.Primary : ButtonAction.Base}
size={ButtonSize.Medium}
disabled
>
{tokenBalance
? `${stringGetter({ key: STRING_KEYS.MIGRATE_NOW })} →`
: stringGetter({ key: STRING_KEYS.NO_TOKENS_TO_MIGRATE })}
</Button>
</Styled.WithReceipt>
<Styled.LearnMore>
{stringGetter({ key: STRING_KEYS.WANT_TO_LEARN })}
<Link href={MIGRATE_HELP_URL}>{stringGetter({ key: STRING_KEYS.CLICK_HERE })}</Link>
</Styled.LearnMore>
</Styled.MobileMigrateCard>
)} */}
<Styled.PanelRow>
{isTablet && (
<Styled.BalancePanelContainer>
<DYDXBalancePanel />
</Styled.BalancePanelContainer>
)}
<Styled.Panel
slotHeader={<Styled.Title>{stringGetter({ key: STRING_KEYS.GOVERNANCE })}</Styled.Title>}
onClick={() => dispatch(openDialog({ type: DialogTypes.ExternalNavKeplr }))}
>
<Styled.Row>
<Styled.Description>
{stringGetter({ key: STRING_KEYS.GOVERNANCE_DESCRIPTION })}
<Link href={GOVERNANCE_HELP_URL} onClick={(e) => e.stopPropagation()}>
{stringGetter({ key: STRING_KEYS.LEARN_MORE })}
</Link>
</Styled.Description>
{/* TODO: vertically center based on Panel height */}
<Styled.IconButton
action={ButtonAction.Base}
iconName={IconName.Arrow}
size={ButtonSize.Small}
/>
</Styled.Row>
</Styled.Panel>
<Styled.Panel
slotHeader={<Styled.Title>{stringGetter({ key: STRING_KEYS.STAKING })}</Styled.Title>}
onClick={() => dispatch(openDialog({ type: DialogTypes.ExternalNavKeplr }))}
>
<Styled.Row>
<Styled.Description>
{stringGetter({ key: STRING_KEYS.STAKING_DESCRIPTION })}
<Link href={STAKING_HELP_URL} onClick={(e) => e.stopPropagation()}>
{stringGetter({ key: STRING_KEYS.LEARN_MORE })}
</Link>
</Styled.Description>
{/* TODO: vertically center based on Panel height */}
<Styled.IconButton
action={ButtonAction.Base}
iconName={IconName.Arrow}
size={ButtonSize.Small}
/>
</Styled.Row>
</Styled.Panel>
{isNotTablet && (
<Styled.BalancePanelContainer>
<DYDXBalancePanel />
</Styled.BalancePanelContainer>
)}
</Styled.PanelRow>
</Styled.Page>
);
};
const Styled: Record<string, AnyStyledComponent> = {}; const Styled: Record<string, AnyStyledComponent> = {};
Styled.Page = styled.div` Styled.Page = styled.div`
${layoutMixins.centered} ${layoutMixins.contentContainerPage}
gap: 1.5rem;
@media ${breakpoints.tablet} {
padding: 1rem;
}
`;
Styled.Panel = styled(Panel)`
padding: 0 1.5rem 1rem;
@media ${breakpoints.tablet} {
max-width: calc(100vw - 2rem);
}
`;
Styled.Row = styled.div`
${layoutMixins.spacedRow}
gap: 1rem;
align-items: center;
`;
Styled.Description = styled.div`
color: var(--color-text-0);
a {
color: var(--color-text-1);
}
`;
Styled.Migrate = styled.section`
max-width: min(100vw, var(--content-max-width));
padding: 1.5rem;
background-color: var(--color-layer-3);
border-radius: 0.875rem;
`;
Styled.TwoItemRow = styled(Styled.Row)`
grid-template-columns: 1fr 1fr;
`;
Styled.MigrateAction = styled(Styled.TwoItemRow)`
padding: 1rem;
background-color: var(--color-layer-2);
border: solid var(--border-width) var(--color-border);
border-radius: 0.75rem;
`;
Styled.Token = styled(Output)`
font: var(--font-large-book);
`;
Styled.PanelRow = styled(Styled.Row)`
gap: 1.5rem;
max-width: min(100vw, var(--content-max-width));
align-items: flex-start;
> section {
cursor: pointer;
}
@media ${breakpoints.tablet} {
grid-auto-flow: row;
grid-template-columns: 1fr;
max-width: auto;
}
`;
Styled.BalancePanelContainer = styled.div`
width: 21.25rem;
@media ${breakpoints.tablet} {
width: auto;
}
`;
Styled.Title = styled.h3`
${layoutMixins.inlineRow}
padding: 1.25rem 1.5rem 0.5rem;
font: var(--font-medium-book);
color: var(--color-text-2);
`;
Styled.MigrateTitle = styled(Styled.Title)`
padding: 0 0 0.5rem;
`;
Styled.MobileMigrateCard = styled(Styled.Panel)`
${layoutMixins.flexColumn}
gap: 1rem;
align-items: center;
`;
Styled.MobileMigrateHeader = styled(Styled.Title)`
${layoutMixins.inlineRow}
gap: 1ch;
padding-bottom: 1rem;
font: var(--font-small-book);
color: var(--color-text-0);
h3 {
${layoutMixins.inlineRow}
font: var(--font-large-book);
color: var(--color-text-2);
svg {
font-size: 1.75rem;
}
}
span {
margin-top: 0.2rem;
b {
font-weight: var(--fontWeight-book);
color: var(--color-text-1);
}
}
`;
Styled.Details = styled(Details)`
padding: 0.5rem 1rem;
`;
Styled.WithReceipt = styled(WithReceipt)`
width: 100%;
`;
Styled.LearnMore = styled(Styled.Description)`
${layoutMixins.row}
gap: 1ch;
`;
Styled.IconButton = styled(IconButton)`
color: var(--color-text-0);
`; `;
+13 -10
View File
@@ -2,6 +2,7 @@ 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';
@@ -18,20 +19,23 @@ 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 { calculateIsAccountViewOnly } from '@/state/accountCalculators';
import { import {
calculateHasUncommittedOrders, calculateHasUncommittedOrders,
calculateIsAccountViewOnly,
} from '@/state/accountCalculators';
import {
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 { shorternNumberForDisplay } from '@/lib/numbers'; import { shortenNumberForDisplay } from '@/lib/numbers';
import { PositionInfo } from '@/views/PositionInfo'; import { PositionInfo } from '@/views/PositionInfo';
@@ -69,13 +73,12 @@ 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 hasUncommittedOrders = useSelector(calculateHasUncommittedOrders); const isWaitingForOrderToIndex = useSelector(calculateHasUncommittedOrders);
const showCurrentMarket = isTablet || view === PanelView.CurrentMarket; const showCurrentMarket = isTablet || view === PanelView.CurrentMarket;
const fillsTagNumber = shorternNumberForDisplay(showCurrentMarket ? numFills : numTotalFills); const fillsTagNumber = shortenNumberForDisplay(showCurrentMarket ? numFills : numTotalFills);
const ordersTagNumber = shorternNumberForDisplay( const ordersTagNumber = shortenNumberForDisplay(
showCurrentMarket ? numOpenOrders : numTotalOpenOrders showCurrentMarket ? numOpenOrders : numTotalOpenOrders
); );
@@ -86,7 +89,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 : shorternNumberForDisplay(numTotalPositions), tag: showCurrentMarket ? null : shortenNumberForDisplay(numTotalPositions),
content: showCurrentMarket ? ( content: showCurrentMarket ? (
<PositionInfo showNarrowVariation={isTablet} /> <PositionInfo showNarrowVariation={isTablet} />
@@ -118,7 +121,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: hasUncommittedOrders ? ( slotRight: isWaitingForOrderToIndex ? (
<Styled.LoadingSpinner /> <Styled.LoadingSpinner />
) : ( ) : (
ordersTagNumber && ( ordersTagNumber && (
@@ -190,7 +193,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: shorternNumberForDisplay( // tag: shortenNumberForDisplay(
// showCurrentMarket ? numFundingPayments : numTotalFundingPayments // showCurrentMarket ? numFundingPayments : numTotalFundingPayments
// ), // ),
// content: ( // content: (
+2 -2
View File
@@ -12,12 +12,12 @@ import { getCurrentMarketAssetData } from '@/state/assetsSelectors';
import { getCurrentMarketId } from '@/state/perpetualsSelectors'; import { getCurrentMarketId } from '@/state/perpetualsSelectors';
export const MarketSelectorAndStats = ({ className }: { className?: string }) => { export const MarketSelectorAndStats = ({ className }: { className?: string }) => {
const { symbol = '' } = useSelector(getCurrentMarketAssetData, shallowEqual) ?? {}; const { id = '' } = useSelector(getCurrentMarketAssetData, shallowEqual) ?? {};
const currentMarketId = useSelector(getCurrentMarketId); const currentMarketId = useSelector(getCurrentMarketId);
return ( return (
<Styled.Container className={className}> <Styled.Container className={className}>
<MarketsDropdown currentMarketId={currentMarketId} symbol={symbol} /> <MarketsDropdown currentMarketId={currentMarketId} symbol={id} />
<VerticalSeparator /> <VerticalSeparator />
+1 -4
View File
@@ -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,8 +16,6 @@ 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({
@@ -41,7 +39,6 @@ export const store = configureStore({
appMiddleware, appMiddleware,
localizationMiddleware, localizationMiddleware,
routerMiddleware, routerMiddleware,
tradeMiddleware,
], ],
devTools: process.env.NODE_ENV !== 'production', devTools: process.env.NODE_ENV !== 'production',
+36 -15
View File
@@ -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,16 +25,19 @@ 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>;
stakingBalances?: Record<string, AccountBalance>;
}; };
const initialState: AccountState = { const initialState: AccountState = {
@@ -42,10 +45,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>({
@@ -57,6 +60,7 @@ 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,
@@ -87,6 +91,16 @@ 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],
@@ -132,22 +146,26 @@ 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;
},
setStakingBalances: (state, action: PayloadAction<Record<string, AccountBalance>>) => {
state.stakingBalances = 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
);
},
}, },
}); });
@@ -155,14 +173,17 @@ export const {
setFills, setFills,
setFundingPayments, setFundingPayments,
setTransfers, setTransfers,
setLatestOrder,
clearOrder, clearOrder,
setOnboardingGuard, setOnboardingGuard,
setOnboardingState, setOnboardingState,
setHistoricalPnl, setHistoricalPnl,
setSubaccount, setSubaccount,
setWallet, setWallet,
addUncommittedOrderClientId,
removeUncommittedOrderClientId,
viewedFills, viewedFills,
viewedOrders, viewedOrders,
setBalances,
setStakingBalances,
addUncommittedOrderClientId,
removeUncommittedOrderClientId,
} = accountSlice.actions; } = accountSlice.actions;
+9 -6
View File
@@ -2,17 +2,15 @@ import { createSelector } from 'reselect';
import { SubaccountPosition } from '@/constants/abacus'; import { SubaccountPosition } from '@/constants/abacus';
import { OnboardingState, OnboardingSteps } from '@/constants/account'; import { OnboardingState, OnboardingSteps } from '@/constants/account';
import { isDydxV4Network } from '@/constants/networks';
import { import {
getExistingOpenPositions, getExistingOpenPositions,
getOnboardingGuards, getOnboardingGuards,
getOnboardingState, getOnboardingState,
getSubaccountId, getSubaccountId,
getUncommittedOrderClientIds,
} from '@/state/accountSelectors'; } from '@/state/accountSelectors';
import { getSelectedNetwork } from '@/state/appSelectors';
export const calculateOnboardingStep = createSelector( export const calculateOnboardingStep = createSelector(
[getOnboardingState, getOnboardingGuards], [getOnboardingState, getOnboardingGuards],
(onboardingState: OnboardingState, onboardingGuards: ReturnType<typeof getOnboardingGuards>) => { (onboardingState: OnboardingState, onboardingGuards: ReturnType<typeof getOnboardingGuards>) => {
@@ -34,9 +32,9 @@ export const calculateOnboardingStep = createSelector(
* @description calculate whether an account is in a state where they can trade * @description calculate whether an account is in a state where they can trade
*/ */
export const calculateCanAccountTrade = createSelector( export const calculateCanAccountTrade = createSelector(
[getOnboardingState, getSelectedNetwork], [getOnboardingState],
(onboardingState: OnboardingState, selectedNetwork: string) => { (onboardingState: OnboardingState) => {
return onboardingState === OnboardingState.AccountConnected && isDydxV4Network(selectedNetwork); return onboardingState === OnboardingState.AccountConnected;
} }
); );
@@ -69,6 +67,11 @@ 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)
+47 -24
View File
@@ -12,9 +12,8 @@ import {
import { OnboardingState } from '@/constants/account'; import { OnboardingState } from '@/constants/account';
import type { RootState } from './_store';
import { getHydratedTradingData } from '@/lib/orders'; import { getHydratedTradingData } from '@/lib/orders';
import type { RootState } from './_store';
import { getCurrentMarketId, getPerpetualMarkets } from './perpetualsSelectors'; import { getCurrentMarketId, getPerpetualMarkets } from './perpetualsSelectors';
import { getAssets } from './assetsSelectors'; import { getAssets } from './assetsSelectors';
@@ -79,6 +78,12 @@ 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
@@ -106,25 +111,46 @@ export const getSubaccountOpenOrdersBySideAndPrice = createSelector(
[getSubaccountOpenOrders], [getSubaccountOpenOrders],
(openOrders = []) => { (openOrders = []) => {
const ordersBySide: Partial<Record<OrderSide, Record<number, SubaccountOrder>>> = {}; const ordersBySide: Partial<Record<OrderSide, Record<number, SubaccountOrder>>> = {};
for (const order of openOrders) { openOrders.forEach((order) => {
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;
} }
); );
/** /**
* @param state * @returns the clientId of the latest order
*/
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 order = orders?.find((order) => order.id === orderId); const matchingOrder = orders?.find((order) => order.id === orderId);
return order ? getHydratedTradingData({ data: order, assets, perpetualMarkets }) : undefined; return matchingOrder
? getHydratedTradingData({ data: matchingOrder, assets, perpetualMarkets })
: undefined;
} }
); );
@@ -178,8 +204,10 @@ export const getFillDetails = (fillId: string) =>
createSelector( createSelector(
[getSubaccountFills, getAssets, getPerpetualMarkets], [getSubaccountFills, getAssets, getPerpetualMarkets],
(fills, assets, perpetualMarkets) => { (fills, assets, perpetualMarkets) => {
const fill = fills?.find((fill) => fill.id === fillId); const matchingFill = fills?.find((fill) => fill.id === fillId);
return fill ? getHydratedTradingData({ data: fill, assets, perpetualMarkets }) : undefined; return matchingFill
? getHydratedTradingData({ data: matchingFill, assets, perpetualMarkets })
: undefined;
} }
); );
@@ -280,21 +308,6 @@ 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
@@ -319,3 +332,13 @@ 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;
/**
* @returns user wallet staking balances
* */
export const getStakingBalances = (state: RootState) => state.account?.stakingBalances;
+3 -12
View File
@@ -3,11 +3,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { AbacusApiState } from '@/constants/abacus'; import { AbacusApiState } from '@/constants/abacus';
import { LocalStorageKey } from '@/constants/localStorage'; import { LocalStorageKey } from '@/constants/localStorage';
import { import { DEFAULT_APP_ENVIRONMENT, type DydxNetwork } from '@/constants/networks';
DEFAULT_APP_ENVIRONMENT,
isValidDydxNetwork,
type DydxNetwork,
} from '@/constants/networks';
import { getLocalStorage } from '@/lib/localStorage'; import { getLocalStorage } from '@/lib/localStorage';
@@ -23,7 +19,6 @@ const initialState: AppState = {
selectedNetwork: getLocalStorage({ selectedNetwork: getLocalStorage({
key: LocalStorageKey.SelectedNetwork, key: LocalStorageKey.SelectedNetwork,
defaultValue: DEFAULT_APP_ENVIRONMENT, defaultValue: DEFAULT_APP_ENVIRONMENT,
validateFn: isValidDydxNetwork,
}), }),
}; };
@@ -47,9 +42,5 @@ export const appSlice = createSlice({
}, },
}); });
export const { export const { initializeLocalization, initializeWebsocket, setApiState, setSelectedNetwork } =
initializeLocalization, appSlice.actions;
initializeWebsocket,
setApiState,
setSelectedNetwork,
} = appSlice.actions;
+1 -1
View File
@@ -1,5 +1,5 @@
import { createSlice, type PayloadAction } from '@reduxjs/toolkit'; import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
import type { kollections } from '@dydxprotocol/abacus'; import type { kollections } from '@dydxprotocol/v4-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';
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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
let formattedString: string = _.get(localeData, key) || _.get(EN_LOCALE_DATA, key) || ''; const formattedString: string = _.get(localeData, key) || _.get(EN_LOCALE_DATA, key) || '';
return formatString(formattedString, params); return formatString(formattedString, params);
}; };
+2 -3
View File
@@ -1,11 +1,10 @@
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
-39
View File
@@ -1,39 +0,0 @@
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 -1
View File
@@ -1,4 +1,4 @@
import styled, { css, type FlattenSimpleInterpolation } from 'styled-components'; import { css } from 'styled-components';
import breakpoints from './breakpoints'; import breakpoints from './breakpoints';
+4 -4
View File
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.
-2
View File
@@ -6,8 +6,6 @@ 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>>
-1
View File
@@ -5,7 +5,6 @@ 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,8 +62,10 @@ 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;
+15 -10
View File
@@ -24,11 +24,12 @@ export const MarketDetails: React.FC = () => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { isTablet } = useBreakpoints(); const { isTablet } = useBreakpoints();
const { configs, market } = useSelector(getCurrentMarketData, shallowEqual) || {}; const { configs, market } = useSelector(getCurrentMarketData, shallowEqual) || {};
const { symbol, name, resources } = useSelector(getCurrentMarketAssetData, shallowEqual) || {}; const { id, name, resources } = useSelector(getCurrentMarketAssetData, shallowEqual) || {};
if (!configs) return null; if (!configs) return null;
const { const {
basePositionNotional,
tickSize, tickSize,
stepSize, stepSize,
initialMarginFraction, initialMarginFraction,
@@ -74,7 +75,7 @@ export const MarketDetails: React.FC = () => {
useGrouping useGrouping
value={stepSize} value={stepSize}
type={OutputType.Asset} type={OutputType.Asset}
tag={symbol} tag={id}
fractionDigits={stepSizeDecimals} fractionDigits={stepSizeDecimals}
/> />
), ),
@@ -87,7 +88,7 @@ export const MarketDetails: React.FC = () => {
useGrouping useGrouping
value={minOrderSize} value={minOrderSize}
type={OutputType.Asset} type={OutputType.Asset}
tag={symbol} tag={id}
fractionDigits={stepSizeDecimals} fractionDigits={stepSizeDecimals}
/> />
), ),
@@ -113,11 +114,17 @@ export const MarketDetails: React.FC = () => {
), ),
}, },
{ {
key: 'initial-margin-fraction', key: 'base-initial-margin-fraction',
label: stringGetter({ key: STRING_KEYS.INITIAL_MARGIN_FRACTION }), label: stringGetter({ key: STRING_KEYS.BASE_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 (
@@ -125,18 +132,16 @@ export const MarketDetails: React.FC = () => {
<Styled.Header> <Styled.Header>
<Styled.WrapRow> <Styled.WrapRow>
<Styled.MarketTitle> <Styled.MarketTitle>
<AssetIcon symbol={symbol} /> <AssetIcon symbol={id} />
{name} {name}
</Styled.MarketTitle> </Styled.MarketTitle>
{isTablet && <Styled.MarketLinks />} {isTablet && <Styled.MarketLinks />}
</Styled.WrapRow> </Styled.WrapRow>
<Styled.MarketDescription> <Styled.MarketDescription>
{primaryDescriptionKey && ( {primaryDescriptionKey && <p>{stringGetter({ key: `APP.${primaryDescriptionKey}` })}</p>}
<p>{stringGetter({ key: STRING_KEYS[primaryDescriptionKey as StringKey] })}</p>
)}
{secondaryDescriptionKey && ( {secondaryDescriptionKey && (
<p>{stringGetter({ key: STRING_KEYS[secondaryDescriptionKey as StringKey] })}</p> <p>{stringGetter({ key: `APP.${secondaryDescriptionKey}` })}</p>
)} )}
</Styled.MarketDescription> </Styled.MarketDescription>
+5 -2
View File
@@ -15,14 +15,17 @@ 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,
}, },
@@ -31,8 +34,8 @@ export const MarketLinks = () => {
return ( return (
<Styled.MarketLinks> <Styled.MarketLinks>
{linkItems.map( {linkItems.map(
({ href, icon }) => ({ key, href, icon }) =>
href && <IconButton key={href} href={href} iconName={icon} type={ButtonType.Link} /> href && <IconButton key={key} href={href} iconName={icon} type={ButtonType.Link} />
)} )}
</Styled.MarketLinks> </Styled.MarketLinks>
); );
+2 -2
View File
@@ -45,7 +45,7 @@ const defaultMarketStatistics = Object.values(MarketStats);
export const MarketStatsDetails = ({ showMidMarketPrice = true }: ElementProps) => { export const MarketStatsDetails = ({ showMidMarketPrice = true }: ElementProps) => {
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { isTablet } = useBreakpoints(); const { isTablet } = useBreakpoints();
const { symbol = '' } = useSelector(getCurrentMarketAssetData, shallowEqual) ?? {}; const { id = '' } = useSelector(getCurrentMarketAssetData, shallowEqual) ?? {};
const { tickSizeDecimals } = useSelector(getCurrentMarketConfig, shallowEqual) ?? {}; const { tickSizeDecimals } = useSelector(getCurrentMarketConfig, shallowEqual) ?? {};
const midMarketPrice = useSelector(getCurrentMarketMidMarketPrice); const midMarketPrice = useSelector(getCurrentMarketMidMarketPrice);
const lastMidMarketPrice = useRef(midMarketPrice); const lastMidMarketPrice = useRef(midMarketPrice);
@@ -116,7 +116,7 @@ export const MarketStatsDetails = ({ showMidMarketPrice = true }: ElementProps)
<Styled.Output <Styled.Output
type={OutputType.Number} type={OutputType.Number}
value={value} value={value}
tag={symbol} tag={id}
fractionDigits={LARGE_TOKEN_DECIMALS} fractionDigits={LARGE_TOKEN_DECIMALS}
/> />
); );
+4 -4
View File
@@ -60,12 +60,12 @@ const MarketsDropdownContent = ({ onRowAction }: { onRowAction?: (market: string
columnKey: 'market', columnKey: 'market',
getCellValue: (row) => row.market, getCellValue: (row) => row.market,
label: stringGetter({ key: STRING_KEYS.MARKET }), label: stringGetter({ key: STRING_KEYS.MARKET }),
renderCell: ({ asset }) => ( renderCell: ({ assetId, id }) => (
<Styled.MarketName isFavorited={false}> <Styled.MarketName isFavorited={false}>
{/* TRCL-1693 <Icon iconName={IconName.Star} /> */} {/* TRCL-1693 <Icon iconName={IconName.Star} /> */}
<AssetIcon symbol={asset.symbol} /> <AssetIcon symbol={assetId} />
<h2>{asset.name}</h2> <h2>{id}</h2>
<Tag>{asset.symbol}</Tag> <Tag>{assetId}</Tag>
</Styled.MarketName> </Styled.MarketName>
), ),
}, },
+4 -4
View File
@@ -68,7 +68,7 @@ export const PositionInfo = ({ showNarrowVariation }: { showNarrowVariation?: bo
const isLoading = useSelector(calculateIsAccountLoading); const isLoading = useSelector(calculateIsAccountLoading);
const { stepSizeDecimals, tickSizeDecimals } = currentMarketConfigs || {}; const { stepSizeDecimals, tickSizeDecimals } = currentMarketConfigs || {};
const { symbol } = currentMarketAssetData || {}; const { id } = currentMarketAssetData || {};
const { type: dialogType } = activeDialog || {}; const { type: dialogType } = activeDialog || {};
const { type: tradeBoxDialogType } = activeTradeBoxDialog || {}; const { type: tradeBoxDialogType } = activeTradeBoxDialog || {};
@@ -188,7 +188,7 @@ export const PositionInfo = ({ showNarrowVariation }: { showNarrowVariation?: bo
label: STRING_KEYS.LIQUIDATION_PRICE, label: STRING_KEYS.LIQUIDATION_PRICE,
tooltip: 'liquidation-price', tooltip: 'liquidation-price',
tooltipParams: { tooltipParams: {
SYMBOL: symbol || '', SYMBOL: id || '',
}, },
fractionDigits: tickSizeDecimals, fractionDigits: tickSizeDecimals,
hasInvalidNewValue: Boolean(newLeverageIsInvalid), hasInvalidNewValue: Boolean(newLeverageIsInvalid),
@@ -326,7 +326,7 @@ export const PositionInfo = ({ showNarrowVariation }: { showNarrowVariation?: bo
notionalTotal={notionalTotal?.current} notionalTotal={notionalTotal?.current}
postOrderSize={size?.postOrder} postOrderSize={size?.postOrder}
stepSizeDecimals={stepSizeDecimals} stepSizeDecimals={stepSizeDecimals}
symbol={symbol || undefined} symbol={id || undefined}
tickSizeDecimals={tickSizeDecimals} tickSizeDecimals={tickSizeDecimals}
showNarrowVariation={showNarrowVariation} showNarrowVariation={showNarrowVariation}
isLoading={isLoading} isLoading={isLoading}
@@ -370,7 +370,7 @@ export const PositionInfo = ({ showNarrowVariation }: { showNarrowVariation?: bo
notionalTotal={notionalTotal?.current} notionalTotal={notionalTotal?.current}
postOrderSize={size?.postOrder} postOrderSize={size?.postOrder}
stepSizeDecimals={stepSizeDecimals} stepSizeDecimals={stepSizeDecimals}
symbol={symbol || undefined} symbol={id || undefined}
tickSizeDecimals={tickSizeDecimals} tickSizeDecimals={tickSizeDecimals}
isLoading={isLoading} isLoading={isLoading}
/> />
-1
View File
@@ -49,7 +49,6 @@ 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),
+25 -1
View File
@@ -7,9 +7,11 @@ 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';
@@ -33,6 +35,9 @@ 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]
@@ -48,6 +53,15 @@ export const TransferStatusToast = ({
if (!status) return <LoadingDots size={3} />; if (!status) return <LoadingDots size={3} />;
const statusString =
type === 'deposit'
? status?.squidTransactionStatus === 'success'
? STRING_KEYS.DEPOSIT_COMPLETE
: STRING_KEYS.DEPOSIT_STATUS
: status?.squidTransactionStatus === 'success'
? STRING_KEYS.WITHDRAW_COMPLETE
: STRING_KEYS.WITHDRAW_STATUS;
return ( return (
<Styled.Root open={open} onOpenChange={setOpen}> <Styled.Root open={open} onOpenChange={setOpen}>
<WithReceipt <WithReceipt
@@ -62,7 +76,7 @@ export const TransferStatusToast = ({
<Styled.BridgingStatus> <Styled.BridgingStatus>
<Styled.Status> <Styled.Status>
{stringGetter({ {stringGetter({
key: type === 'deposit' ? STRING_KEYS.DEPOSIT_STATUS : STRING_KEYS.WITHDRAW_STATUS, key: statusString,
params: { params: {
AMOUNT_USD: <Styled.InlineOutput type={OutputType.Fiat} value={toAmount} />, AMOUNT_USD: <Styled.InlineOutput type={OutputType.Fiat} value={toAmount} />,
ESTIMATED_DURATION: ( ESTIMATED_DURATION: (
@@ -74,6 +88,16 @@ 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} />
+2 -2
View File
@@ -51,7 +51,7 @@ export const TransferStatusSteps = ({ status }: ElementProps) => {
label: stringGetter({ label: stringGetter({
key: type === 'deposit' ? STRING_KEYS.DEPOSIT_TO_CHAIN : STRING_KEYS.WITHDRAW_TO_CHAIN, key: type === 'deposit' ? STRING_KEYS.DEPOSIT_TO_CHAIN : STRING_KEYS.WITHDRAW_TO_CHAIN,
params: { params: {
CHAIN: status?.toChain?.chainData?.chainName, CHAIN: type === 'deposit' ? 'dYdX' : status?.toChain?.chainData?.chainName,
}, },
}), }),
step: TransferStatusStep.ToChain, step: TransferStatusStep.ToChain,
@@ -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;
+4 -4
View File
@@ -98,7 +98,7 @@ export const DepthChart = ({
// Chart data // Chart data
const orderbook = useSelector(getCurrentMarketOrderbook, shallowEqual); const orderbook = useSelector(getCurrentMarketOrderbook, shallowEqual);
const { symbol = '' } = useSelector(getCurrentMarketAssetData, shallowEqual) ?? {}; const { id = '' } = useSelector(getCurrentMarketAssetData, shallowEqual) ?? {};
const { stepSizeDecimals, tickSizeDecimals } = const { stepSizeDecimals, tickSizeDecimals } =
useSelector(getCurrentMarketConfig, shallowEqual) ?? {}; useSelector(getCurrentMarketConfig, shallowEqual) ?? {};
@@ -393,7 +393,7 @@ export const DepthChart = ({
? chartPointAtPointer.size ? chartPointAtPointer.size
: tooltipData!.nearestDatum?.datum.depth : tooltipData!.nearestDatum?.datum.depth
} }
tag={symbol} tag={id}
accentColor={ accentColor={
{ {
[DepthChartSeries.Asks]: 'var(--color-negative)', [DepthChartSeries.Asks]: 'var(--color-negative)',
@@ -476,7 +476,7 @@ export const DepthChart = ({
type={OutputType.Asset} type={OutputType.Asset}
value={chartPointAtPointer.size} value={chartPointAtPointer.size}
fractionDigits={stepSizeDecimals} fractionDigits={stepSizeDecimals}
tag={symbol} tag={id}
useGrouping={false} useGrouping={false}
/> />
), ),
@@ -543,7 +543,7 @@ export const DepthChart = ({
type={OutputType.Asset} type={OutputType.Asset}
value={nearestDatum?.datum.depth} value={nearestDatum?.datum.depth}
fractionDigits={stepSizeDecimals} fractionDigits={stepSizeDecimals}
tag={symbol} tag={id}
useGrouping={false} useGrouping={false}
/> />
), ),
+2 -2
View File
@@ -28,7 +28,7 @@ type ElementProps = {
}; };
export const ClosePositionDialog = ({ setIsOpen }: ElementProps) => { export const ClosePositionDialog = ({ setIsOpen }: ElementProps) => {
const { symbol } = useSelector(getCurrentMarketAssetData, shallowEqual) ?? {}; const { id } = useSelector(getCurrentMarketAssetData, shallowEqual) ?? {};
const { isTablet } = useBreakpoints(); const { isTablet } = useBreakpoints();
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
@@ -46,7 +46,7 @@ export const ClosePositionDialog = ({ setIsOpen }: ElementProps) => {
} = { } = {
[MobilePlaceOrderSteps.EditOrder]: { [MobilePlaceOrderSteps.EditOrder]: {
title: <CloseOrderHeader />, title: <CloseOrderHeader />,
slotIcon: <AssetIcon symbol={symbol} />, slotIcon: <AssetIcon symbol={id} />,
}, },
[MobilePlaceOrderSteps.PreviewOrder]: { [MobilePlaceOrderSteps.PreviewOrder]: {
title: ( title: (
@@ -19,7 +19,7 @@ import { MustBigNumber } from '@/lib/numbers';
type ElementProps = { type ElementProps = {
fillId: string; fillId: string;
setIsOpen?: (open: boolean) => void; setIsOpen: (open: boolean) => void;
}; };
export const FillDetailsDialog = ({ fillId, setIsOpen }: ElementProps) => { export const FillDetailsDialog = ({ fillId, setIsOpen }: ElementProps) => {
@@ -94,7 +94,7 @@ export const FillDetailsDialog = ({ fillId, setIsOpen }: ElementProps) => {
return ( return (
<DetailsDialog <DetailsDialog
slotIcon={<Styled.AssetIcon symbol={asset.symbol} />} slotIcon={<Styled.AssetIcon symbol={asset.id} />}
title={resources.typeStringKey && stringGetter({ key: resources.typeStringKey })} title={resources.typeStringKey && stringGetter({ key: resources.typeStringKey })}
items={detailItems} items={detailItems}
setIsOpen={setIsOpen} setIsOpen={setIsOpen}
@@ -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 } from '@/constants/localization'; import { STRING_KEYS, type StringKey } 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,7 +121,9 @@ 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 ? stringGetter({ key: STRING_KEYS[cancelReason] }) : undefined, value: cancelReason
? stringGetter({ key: STRING_KEYS[cancelReason as StringKey] })
: undefined,
}, },
{ {
key: 'amount', key: 'amount',
@@ -182,8 +184,7 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
const onCancelClick = async () => { const onCancelClick = async () => {
setIsPlacingCancel(true); setIsPlacingCancel(true);
await cancelOrder({ orderId }); await cancelOrder({ orderId, onError: () => setIsPlacingCancel(false) });
setIsPlacingCancel(false);
}; };
const onClearClick = () => { const onClearClick = () => {
@@ -193,7 +194,7 @@ export const OrderDetailsDialog = ({ orderId, setIsOpen }: ElementProps) => {
return ( return (
<DetailsDialog <DetailsDialog
slotIcon={<Styled.AssetIcon symbol={asset.symbol} />} slotIcon={<Styled.AssetIcon symbol={asset.id} />}
title={!resources.typeStringKey ? '' : stringGetter({ key: resources.typeStringKey })} title={!resources.typeStringKey ? '' : stringGetter({ key: resources.typeStringKey })}
slotFooter={ slotFooter={
isAccountViewOnly ? null : isOrderStatusClearable(status) ? ( isAccountViewOnly ? null : isOrderStatusClearable(status) ? (
+5 -10
View File
@@ -1,11 +1,9 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import styled, { AnyStyledComponent } from 'styled-components'; import styled, { AnyStyledComponent } from 'styled-components';
import { useDispatch, useSelector } from 'react-redux'; import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { AbacusApiStatus } from '@/constants/abacus'; import { AbacusApiStatus } from '@/constants/abacus';
import { CLIENT_NETWORK_CONFIGS } from '@/constants/networks'; import { DialogTypes } from '@/constants/dialogs';
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';
@@ -17,6 +15,7 @@ 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;
@@ -28,9 +27,10 @@ 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 (status === AbacusApiStatus.NORMAL) { if (activeDialog?.type === DialogTypes.ExchangeOffline && status === AbacusApiStatus.NORMAL) {
dispatch(closeDialog()); dispatch(closeDialog());
} }
}, [status, selectedNetwork]); }, [status, selectedNetwork]);
@@ -44,11 +44,6 @@ 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>
@@ -0,0 +1,107 @@
import styled, { type AnyStyledComponent } from 'styled-components';
import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { useBreakpoints, useStringGetter } from '@/hooks';
import { Button } from '@/components/Button';
import { Dialog, DialogPlacement } from '@/components/Dialog';
import { IconName } from '@/components/Icon';
import { IconButton } from '@/components/IconButton';
import { layoutMixins } from '@/styles/layoutMixins';
type ElementProps = {
setIsOpen: (open: boolean) => void;
};
// TODO: replace placeholder URL with real URLs when avaialble
const KEPLR_DASHBOARD_URL = 'https://testnet.keplr.app/';
const HELP_URL = 'https://help.dydx.exchange/en/articles/2921366-how-do-i-create-an-account-or-sign-up';
export const ExternalNavKeplrDialog = ({ setIsOpen }: ElementProps) => {
const stringGetter = useStringGetter();
const { isTablet } = useBreakpoints();
return (
<Dialog
isOpen
setIsOpen={setIsOpen}
title={stringGetter({ key: STRING_KEYS.HAVE_YOU_EXPORTED })}
placement={isTablet ? DialogPlacement.FullScreen : DialogPlacement.Default}
>
<Styled.Content>
<Styled.Button type={ButtonType.Link} size={ButtonSize.XLarge} href={KEPLR_DASHBOARD_URL}>
<span>
{stringGetter({
key: STRING_KEYS.NAVIGATE_TO_KEPLR,
params: {
STRONG_YES: <strong>{stringGetter({ key: STRING_KEYS.YES })}</strong>,
},
})}
</span>
<Styled.IconButton
action={ButtonAction.Base}
iconName={IconName.Arrow}
size={ButtonSize.XSmall}
/>
</Styled.Button>
<Styled.Button type={ButtonType.Link} size={ButtonSize.XLarge} href={HELP_URL}>
<span>
{stringGetter({
key: STRING_KEYS.LEARN_TO_EXPORT,
params: {
STRONG_NO: <strong>{stringGetter({ key: STRING_KEYS.NO })}</strong>,
},
})}
</span>
<Styled.IconButton
action={ButtonAction.Base}
iconName={IconName.Arrow}
size={ButtonSize.XSmall}
/>
</Styled.Button>
</Styled.Content>
</Dialog>
);
};
const Styled: Record<string, AnyStyledComponent> = {};
Styled.TextToggle = styled.div`
${layoutMixins.stickyFooter}
color: var(--color-accent);
cursor: pointer;
margin-top: auto;
&:hover {
text-decoration: underline;
}
`;
Styled.Content = styled.div`
${layoutMixins.stickyArea0}
--stickyArea0-bottomHeight: 2rem;
--stickyArea0-bottomGap: 1rem;
--stickyArea0-totalInsetBottom: 0.5rem;
${layoutMixins.flexColumn}
gap: 1rem;
`;
Styled.Button = styled(Button)`
--button-font: var(--font-base-book);
--button-padding: 0 1.5rem;
gap: 0;
justify-content: space-between;
`;
Styled.IconButton = styled(IconButton)`
color: var(--color-text-0);
`;
@@ -9,8 +9,8 @@ import { AlertType } from '@/constants/alerts';
import { AnalyticsEvent } from '@/constants/analytics'; import { AnalyticsEvent } from '@/constants/analytics';
import { ButtonAction } from '@/constants/buttons'; import { ButtonAction } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { CLIENT_NETWORK_CONFIGS } from '@/constants/networks'; import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
import { DydxAddress, SIGN_TYPED_DATA } from '@/constants/wallets'; import { DydxAddress, getSignTypedData } from '@/constants/wallets';
import { useAccounts, useBreakpoints, useDydxClient, useStringGetter } from '@/hooks'; import { useAccounts, useBreakpoints, useDydxClient, useStringGetter } from '@/hooks';
import { useMatchingEvmNetwork } from '@/hooks/useMatchingEvmNetwork'; import { useMatchingEvmNetwork } from '@/hooks/useMatchingEvmNetwork';
@@ -55,7 +55,7 @@ export const GenerateKeys = ({
// 1. Switch network // 1. Switch network
const selectedNetwork = useSelector(getSelectedNetwork); const selectedNetwork = useSelector(getSelectedNetwork);
const chainId = Number(CLIENT_NETWORK_CONFIGS[selectedNetwork].ethereumChainId); const chainId = Number(ENVIRONMENT_CONFIG_MAP[selectedNetwork].ethereumChainId);
const { isMatchingNetwork, matchNetwork, isSwitchingNetwork } = useMatchingEvmNetwork({ const { isMatchingNetwork, matchNetwork, isSwitchingNetwork } = useMatchingEvmNetwork({
chainId, chainId,
@@ -85,10 +85,11 @@ export const GenerateKeys = ({
EvmDerivedAccountStatus.Derived, EvmDerivedAccountStatus.Derived,
].includes(status); ].includes(status);
const signTypedData = getSignTypedData(selectedNetwork);
const { signTypedDataAsync } = useSignTypedData({ const { signTypedDataAsync } = useSignTypedData({
...SIGN_TYPED_DATA, ...signTypedData,
domain: { domain: {
...SIGN_TYPED_DATA.domain, ...signTypedData.domain,
chainId, chainId,
}, },
}); });
@@ -165,8 +166,8 @@ export const GenerateKeys = ({
{[ {[
{ {
status: EvmDerivedAccountStatus.Deriving, status: EvmDerivedAccountStatus.Deriving,
title: stringGetter({ key: STRING_KEYS.GENERATE_COSMOS_WALLET }), title: stringGetter({ key: STRING_KEYS.GENERATE_DYDX_WALLET }),
description: stringGetter({ key: STRING_KEYS.GENERATE_COSMOS_WALLET }), description: stringGetter({ key: STRING_KEYS.VERIFY_WALLET_OWNERSHIP }),
}, },
status === EvmDerivedAccountStatus.EnsuringDeterminism && { status === EvmDerivedAccountStatus.EnsuringDeterminism && {
status: EvmDerivedAccountStatus.EnsuringDeterminism, status: EvmDerivedAccountStatus.EnsuringDeterminism,
+15 -8
View File
@@ -3,7 +3,7 @@ import { shallowEqual, useSelector } from 'react-redux';
import styled, { AnyStyledComponent, css } from 'styled-components'; import styled, { AnyStyledComponent, css } from 'styled-components';
import { TradeInputField } from '@/constants/abacus'; import { TradeInputField } from '@/constants/abacus';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS, StringKey } from '@/constants/localization';
import { TradeTypes, TRADE_TYPE_STRINGS, MobilePlaceOrderSteps } from '@/constants/trade'; import { TradeTypes, TRADE_TYPE_STRINGS, MobilePlaceOrderSteps } from '@/constants/trade';
import { useBreakpoints, useStringGetter } from '@/hooks'; import { useBreakpoints, useStringGetter } from '@/hooks';
@@ -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 } from '@/state/inputsSelectors'; import { getInputTradeData, getInputTradeOptions } from '@/state/inputsSelectors';
import abacusStateManager from '@/lib/abacus'; import abacusStateManager from '@/lib/abacus';
import { getSelectedTradeType } from '@/lib/tradeData'; import { getSelectedTradeType } from '@/lib/tradeData';
@@ -31,10 +31,19 @@ type ElementProps = {
export const TradeDialog = ({ isOpen, setIsOpen, slotTrigger }: ElementProps) => { export const TradeDialog = ({ isOpen, setIsOpen, slotTrigger }: ElementProps) => {
const { isMobile } = useBreakpoints(); const { isMobile } = useBreakpoints();
const stringGetter = useStringGetter(); const stringGetter = useStringGetter();
const { symbol } = useSelector(getCurrentMarketAssetData, shallowEqual) ?? {}; const { id } = useSelector(getCurrentMarketAssetData, shallowEqual) ?? {};
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 as StringKey,
}),
slotBefore: <AssetIcon symbol={id} />,
}));
const [currentStep, setCurrentStep] = useState<MobilePlaceOrderSteps>( const [currentStep, setCurrentStep] = useState<MobilePlaceOrderSteps>(
MobilePlaceOrderSteps.EditOrder MobilePlaceOrderSteps.EditOrder
@@ -62,11 +71,7 @@ export const TradeDialog = ({ isOpen, setIsOpen, slotTrigger }: ElementProps) =>
[MobilePlaceOrderSteps.EditOrder]: { [MobilePlaceOrderSteps.EditOrder]: {
title: ( title: (
<Styled.ToggleGroup <Styled.ToggleGroup
items={[TradeTypes.LIMIT, TradeTypes.MARKET].map((tradeType: TradeTypes) => ({ items={allTradeTypeItems}
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)
@@ -126,6 +131,8 @@ 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;

Some files were not shown because too many files have changed in this diff Show More