Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a123976646 | ||
|
|
788fed9c26 | ||
|
|
564192fa2b | ||
|
|
b8526a5568 | ||
|
|
102b1702ff | ||
|
|
2b888001aa | ||
|
|
d4c149af12 | ||
|
|
b0cd8e71ac | ||
|
|
ffde6ed0fc | ||
|
|
6ac94e5949 | ||
|
|
c067206cd1 | ||
|
|
30187ef69f | ||
|
|
7d1b9eb65f | ||
|
|
1835b42bb0 | ||
|
|
37a50def99 | ||
|
|
a97f296905 | ||
|
|
683d806f44 | ||
|
|
e635778438 | ||
|
|
7a456b33d4 | ||
|
|
6d25a072bd | ||
|
|
d6baa6ef3f | ||
|
|
195ddd5af7 | ||
|
|
2a5373c972 | ||
|
|
64f663ed5b | ||
|
|
983128fd74 | ||
|
|
e15f4b919c | ||
|
|
71c29524b1 | ||
|
|
0e26433e29 | ||
|
|
3afd5ef783 | ||
|
|
b2cac81235 | ||
|
|
e48ec4ca73 | ||
|
|
78f9ddff7a | ||
|
|
c1dcca43c8 | ||
|
|
029476fa19 | ||
|
|
6ecdcd981e | ||
|
|
591166e93d | ||
|
|
e30940e7df | ||
|
|
2d0a944097 | ||
|
|
0dd2ecb6d2 | ||
|
|
76819e0839 | ||
|
|
32b375cfa4 | ||
|
|
3dc87e873d | ||
|
|
ed61b3112e | ||
|
|
42f4142307 | ||
|
|
c5e79980d1 | ||
|
|
6e179191d8 |
@@ -0,0 +1,18 @@
|
||||
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
|
||||
@@ -1,10 +1,17 @@
|
||||
VITE_BASE_URL=
|
||||
|
||||
VITE_ALCHEMY_API_KEY=
|
||||
|
||||
VITE_PK_ENCRYPTION_KEY=
|
||||
|
||||
VITE_WALLETCONNECT2_PROJECT_ID=
|
||||
|
||||
VITE_V3_TOKEN_ADDRESS=
|
||||
VITE_TOKEN_MIGRATION_URI=
|
||||
VITE_FAUCET_URL_DEV=
|
||||
VITE_FAUCET_URL_STAGE=
|
||||
VITE_FAUCET_URL_TESTNET2=
|
||||
|
||||
VITE_VALIDATOR_URL_DEV=
|
||||
VITE_VALIDATOR_URL_STAGE=
|
||||
VITE_VALIDATOR_URL_TESTNET2=
|
||||
|
||||
VITE_INDEXER_URL_DEV=
|
||||
VITE_INDEXER_URL_STAGE=
|
||||
VITE_INDEXER_URL_TESTNET2=
|
||||
|
||||
@@ -5,10 +5,15 @@ vite.config.ts
|
||||
|
||||
# Temporarily ignore, we will slowly remove each directory as we fix files to follow ESLint rules
|
||||
/src/components
|
||||
/src/dialogs
|
||||
/src/forms
|
||||
/src/hooks
|
||||
/src/history.ts
|
||||
/src/icons
|
||||
/src/lib
|
||||
/src/main.tsx
|
||||
/src/menus
|
||||
/src/pages
|
||||
/src/state
|
||||
/src/styles
|
||||
/src/views
|
||||
|
||||
@@ -33,11 +33,14 @@
|
||||
"jsx-a11y/anchor-is-valid": "off",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"no-continue": "off",
|
||||
"no-console": ["error"],
|
||||
"no-console": [
|
||||
"error",
|
||||
{
|
||||
"devDependencies": ["./scripts/*.js"]
|
||||
}
|
||||
],
|
||||
"no-lonely-if": "off",
|
||||
"no-multi-assign": "off",
|
||||
"no-nested-ternary": "off",
|
||||
"no-param-reassign": ["error", { "props": false }],
|
||||
"no-return-assign": "off",
|
||||
"no-return-await": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
@@ -62,7 +65,6 @@
|
||||
"react/sort-comp": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
"@typescript-eslint/comma-dangle": "off",
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"@typescript-eslint/member-delimiter-style": [
|
||||
"error",
|
||||
{
|
||||
|
||||
@@ -33,13 +33,11 @@ jobs:
|
||||
AMPLITUDE_API_KEY: ${{ secrets.AMPLITUDE_API_KEY }}
|
||||
BUGSNAG_API_KEY: ${{ secrets.BUGSNAG_API_KEY }}
|
||||
STATUS_PAGE_SCRIPT_URI: ${{ secrets.STATUS_PAGE_SCRIPT_URI }}
|
||||
IOS_APP_ID: ${{ secrets.IOS_APP_ID }}
|
||||
run: |
|
||||
pnpm run build
|
||||
pnpm run build:inject-amplitude
|
||||
pnpm run build:inject-bugsnag
|
||||
pnpm run build:inject-statuspage
|
||||
sh scripts/inject-app-deeplinks.sh
|
||||
|
||||
- name: Upload to IPFS via web3.storage
|
||||
uses: dydxprotocol/add-to-web3@v1
|
||||
|
||||
@@ -13,7 +13,6 @@ dist
|
||||
dist-ssr
|
||||
*.local
|
||||
*.key
|
||||
.env
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Contributing to V4-web
|
||||
# Contributing to Trader-Fe
|
||||
We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:
|
||||
|
||||
- Reporting a bug
|
||||
@@ -18,10 +18,10 @@ Pull requests are the best way to propose changes to the codebase (we use [Githu
|
||||
4. Issue that pull request!
|
||||
|
||||
## Any contributions you make will be under the same License
|
||||
When you submit code changes, your submissions are understood to be under the same [License](https://github.com/dydxprotocol/v4-web/blob/master/LICENSE) that covers the project.
|
||||
When you submit code changes, your submissions are understood to be under the same [License](https://github.com/dydxprotocol/trader-fe/blob/master/LICENSE) that covers the project.
|
||||
|
||||
## Report bugs using Github's [issues](https://github.com/dydxprotocol/v4-web/issues)
|
||||
Report a bug by [opening a new issue](https://github.com/dydxprotocol/v4-web/issues/new).
|
||||
## Report bugs using Github's [issues](https://github.com/dydxprotocol/trader-fe/issues)
|
||||
Report a bug by [opening a new issue](https://github.com/dydxprotocol/trader-fe/issues/new).
|
||||
|
||||
**Great Bug Reports** tend to have:
|
||||
|
||||
|
||||
@@ -1,6 +1,45 @@
|
||||
Business Source License 1.1
|
||||
License text copyright © 2023 MariaDB plc, All Rights Reserved. “Business Source License” is a trademark of MariaDB plc.
|
||||
Copyright (C) 2023 dYdX Trading Inc.
|
||||
|
||||
Subject to your compliance with applicable law, you are granted the right to use the Licensed Work (defined below) under the terms of the GNU Affero General Public License as set forth below; provided, however, that if you violate any such applicable law in your use of the Licensed Work, all of your rights and licenses to use (including any rights to reproduce, distribute, install or modify) the Licensed Work will automatically and immediately terminate.
|
||||
Use of this software before the Change Date specified in the LICENSE file is governed by the Business Source License included in the LICENSE file and at https://spdx.org/licenses/BUSL-1.1.html.
|
||||
|
||||
On and after the Change Date specified in the LICENSE file, use of this software will be governed by the Change License (GNU Affero GPL v3) as specified in the LICENSE file.
|
||||
|
||||
Parameters
|
||||
Licensor: dYdX Trading Inc.
|
||||
Licensed Work: dydxprotocol/cosmos-sdk, dydxprotocol/cometbft, dydxprotocol/v4-chain, dydxprotocol/v4-clients, dydxprotocol/v4-web, dydxprotocol/v4-abacus, dydxprotocol/v4-localization, dydxprotocol/v4-documentation, and any dYdX or dYdX Trading Inc. github repository reflecting a copy of this license, or link to this license.
|
||||
Additional Use Grant: None
|
||||
Change Date: On the later to occur of (i) the release of the Licensed Work labeled “dYdX Version 1.0 GPL”, and which includes a document uploaded to the relevant github repository stating “The software in this repository is Version 1.0, subject to GPL license”, and (ii) September 30, 2023 (approximate projected release of “Version 1.0” of this software).
|
||||
Change License: GNU Affero GPL License v3
|
||||
|
||||
License text copyright © 2023 MariaDB plc, All Rights Reserved. “Business Source License” is a trademark of MariaDB plc.
|
||||
Terms
|
||||
The Licensor hereby grants you the right to copy, modify, create derivative works, redistribute, and make non-production use of the Licensed Work. The Licensor may make an Additional Use Grant, above, permitting limited production use.
|
||||
Effective on the Change Date, or the fourth anniversary of the first publicly available distribution of a specific version of the Licensed Work under this License, whichever comes first, the Licensor hereby grants you rights under the terms of the Change License, and the rights granted in the paragraph above terminate.
|
||||
If your use of the Licensed Work does not comply with the requirements currently in effect as described in this License, you must purchase a commercial license from the Licensor, its affiliated entities, or authorized resellers, or you must refrain from using the Licensed Work.
|
||||
All copies of the original and modified Licensed Work, and derivative works of the Licensed Work, are subject to this License. This License applies separately for each version of the Licensed Work and the Change Date may vary for each version of the Licensed Work released by Licensor.
|
||||
You must conspicuously display this License on each original or modified copy of the Licensed Work. If you receive the Licensed Work in original or modified form from a third party, the terms and conditions set forth in this License apply to your use of that work.
|
||||
Any use of the Licensed Work in violation of this License will automatically terminate your rights under this License for the current and all other versions of the Licensed Work.
|
||||
This License does not grant you any right in any trademark or logo of Licensor or its affiliates (provided that you may use a trademark or logo of Licensor as expressly required by this License).TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE. MariaDB hereby grants you permission to use this License’s text to license your works, and to refer to it using the trademark “Business Source License”, as long as you comply with the Covenants of Licensor below.
|
||||
Covenants of Licensor
|
||||
In consideration of the right to use this License’s text and the “Business Source License” name and trademark, Licensor covenants to MariaDB, and to all other recipients of the licensed work to be provided by Licensor:
|
||||
To specify as the Change License the GPL Version 2.0 or any later version, or a license that is compatible with GPL Version 2.0 or a later version, where “compatible” means that software provided under the Change License can be included in a program with software provided under GPL Version 2.0 or a later version. Licensor may specify additional Change Licenses without limitation.
|
||||
To either: (a) specify an additional grant of rights to use that does not impose any additional restriction on the right granted in this License, as the Additional Use Grant; or (b) insert the text “None” to specify a Change Date. Not to modify this License in any other way.
|
||||
Notice
|
||||
The Business Source License (this document, or the “License”) is not an Open Source license. However, the Licensed Work will eventually be made available under an Open Source License, as stated in this License.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
OPEN SOURCE LICENSE BELOW - TO BE APPLIED AFTER THE CHANGE DATE.
|
||||
|
||||
|
||||
Copyright (C) 2023 dYdX Trading Inc.
|
||||
|
||||
Use of this software is governed by the The GNU Affero General Public License as set forth below.
|
||||
|
||||
|
||||
The GNU Affero General Public License
|
||||
@@ -796,4 +835,4 @@ copy of the Program in return for a fee.
|
||||
|
||||
|
||||
For more information about this software, see https://dydx.exchange.
|
||||
Copyright (C) 2023 dYdX Trading Inc.
|
||||
Copyright (C) 2023 dYdX Trading Inc.
|
||||
@@ -1,17 +1,8 @@
|
||||
<p align="center"><img src="https://dydx.exchange/icon.svg?" width="256" /></p>
|
||||
|
||||
<h1 align="center">dYdX Chain Web</h1>
|
||||
|
||||
<div align="center">
|
||||
<a href='https://github.com/dydxprotocol/v4-web/blob/main/LICENSE'>
|
||||
<img src='https://img.shields.io/badge/License-AGPL_v3-blue.svg' alt='License' />
|
||||
</a>
|
||||
</div>
|
||||
# v4 Web App
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node.js version 18 and `pnpm` installed on your system
|
||||
- Wallet Connect account
|
||||
|
||||
For deploying with Vercel, create an account with [Vercel](https://vercel.com/signup) if you don't have one already.
|
||||
|
||||
@@ -26,16 +17,16 @@ Alternatively, follow the [IPFS Kubo installation guide](https://docs.ipfs.tech
|
||||
|
||||
## Part 1: Setting up your local environment
|
||||
|
||||
### 1. Clone the repo
|
||||
### Step 1: Clone the repo
|
||||
|
||||
Clone the repository and navigate to its directory:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/dydxprotocol/v4-web.git
|
||||
cd v4-web
|
||||
git clone https://github.com/dydxprotocol/trader-fe.git
|
||||
cd trader-fe
|
||||
```
|
||||
|
||||
### 2. Install pnpm and dependencies
|
||||
### Step 2: Install pnpm and dependencies
|
||||
|
||||
Install pnpm and the project dependencies:
|
||||
|
||||
@@ -62,16 +53,7 @@ pnpm ladle
|
||||
|
||||
This will automatically open your default browser at `http://localhost:61000`.
|
||||
|
||||
## Part 3: Configuring environment
|
||||
|
||||
Add or modify the relevant endpoints, links and options in `/public/configs/env.json`.
|
||||
You'll need to provide a Wallet Connect project id to enable onboarding and wallet connection:
|
||||
- Create a project on https://cloud.walletconnect.com/app
|
||||
- Copy over the project ID into this [field](https://github.com/dydxprotocol/v4-web/blob/67ecbd75b43e0c264b7b4d2d9b3d969830b0621c/public/configs/env.json#L822C33-L822C46)
|
||||
|
||||
# Deployments
|
||||
|
||||
## Deploying with Vercel
|
||||
## Part 3: Deploying with Vercel
|
||||
|
||||
### Step 1: Connect your repository to Vercel
|
||||
|
||||
@@ -88,7 +70,7 @@ If you wish to incorporate analytics via Amplitude and Bugsnag, you can use our
|
||||
|
||||
For more details, check out Vercel's [official documentation](https://vercel.com/docs).
|
||||
|
||||
## Deploying to IPFS
|
||||
## Part 4: Deploying to IPFS
|
||||
|
||||
### web3.storage: deploy to IPFS via web3.storage using the provided script
|
||||
|
||||
@@ -120,18 +102,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`.
|
||||
|
||||
Replace `your_cid` with the actual CID.
|
||||
|
||||
## Cloudflare Settings
|
||||
|
||||
We recommend that you add your website to Cloudflare for additional security settings.
|
||||
|
||||
To block OFAC Sanctioned countries:
|
||||
1. Navigate Websites > Domain > Security > WAF
|
||||
|
||||
2. Create Rule with the following settings:
|
||||
* If incoming requests match
|
||||
`(ip.geoip.country eq "CU") or (ip.geoip.country eq "IR") or (ip.geoip.country eq "KP") or (ip.geoip.country eq "SY") or (ip.geoip.country eq "MM") or (ip.geoip.subdivision_1_iso_code eq "UA-09") or (ip.geoip.subdivision_1_iso_code eq "UA-14") or (ip.geoip.subdivision_1_iso_code eq "UA-43")`
|
||||
* This rule will bring up a Cloudflare page when a restricted geography tries to access your site. You will have the option to display:
|
||||
1. Custom Text
|
||||
- (e.g. `Because you appear to be a resident of, or trading from, a jurisdiction that violates our terms of use, or have engaged in activity that violates our terms of use, you have been blocked. You may withdraw your funds from the protocol at any time.`)
|
||||
2. Default Cloudflare WAF block page
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
{
|
||||
"name": "dydx-chain-web",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"license": "AGPL-3.0",
|
||||
"license": "BSL-1.1",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"build:inject-app-deeplinks": "sh scripts/inject-app-deeplinks.sh",
|
||||
"build:inject-amplitude": "node scripts/inject-amplitude.js",
|
||||
"build:inject-bugsnag": "node scripts/inject-bugsnag.js",
|
||||
"build:inject-intercom": "node scripts/inject-intercom.js",
|
||||
"build:inject-statuspage": "node scripts/inject-statuspage.js",
|
||||
"deploy:ipfs": "node scripts/upload-ipfs.js --verbose",
|
||||
"deploy:update-ipns": "node scripts/update-ipns.js",
|
||||
@@ -39,9 +37,9 @@
|
||||
"@cosmjs/proto-signing": "^0.31.0",
|
||||
"@cosmjs/stargate": "^0.31.0",
|
||||
"@cosmjs/tendermint-rpc": "^0.31.0",
|
||||
"@dydxprotocol/v4-abacus": "^1.0.3",
|
||||
"@dydxprotocol/v4-client-js": "^1.0.0",
|
||||
"@dydxprotocol/v4-localization": "^1.0.0",
|
||||
"@dydxprotocol/abacus": "^0.4.8",
|
||||
"@dydxprotocol/v4-client-js": "^0.32.0",
|
||||
"@dydxprotocol/v4-localization": "^0.0.25",
|
||||
"@ethersproject/providers": "^5.7.2",
|
||||
"@js-joda/core": "^5.5.3",
|
||||
"@radix-ui/react-collapsible": "^1.0.3",
|
||||
@@ -107,7 +105,7 @@
|
||||
"reselect": "^4.1.8",
|
||||
"styled-components": "^5.3.11",
|
||||
"use-latest": "^1.2.1",
|
||||
"viem": "^1.12.2",
|
||||
"viem": "^1.1.6",
|
||||
"wagmi": "^1.3.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@0xsquid/sdk':
|
||||
specifier: ^1.10.0
|
||||
@@ -22,15 +26,15 @@ dependencies:
|
||||
'@cosmjs/tendermint-rpc':
|
||||
specifier: ^0.31.0
|
||||
version: 0.31.0
|
||||
'@dydxprotocol/v4-abacus':
|
||||
specifier: ^1.0.3
|
||||
version: 1.0.3
|
||||
'@dydxprotocol/abacus':
|
||||
specifier: ^0.4.8
|
||||
version: 0.4.8
|
||||
'@dydxprotocol/v4-client-js':
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
specifier: ^0.32.0
|
||||
version: 0.32.0
|
||||
'@dydxprotocol/v4-localization':
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
specifier: ^0.0.25
|
||||
version: 0.0.25
|
||||
'@ethersproject/providers':
|
||||
specifier: ^5.7.2
|
||||
version: 5.7.2
|
||||
@@ -227,11 +231,11 @@ dependencies:
|
||||
specifier: ^1.2.1
|
||||
version: 1.2.1(@types/react@18.2.14)(react@18.2.0)
|
||||
viem:
|
||||
specifier: ^1.12.2
|
||||
version: 1.12.2(typescript@5.1.3)
|
||||
specifier: ^1.1.6
|
||||
version: 1.1.6(typescript@5.1.3)
|
||||
wagmi:
|
||||
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.12.2)
|
||||
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)
|
||||
|
||||
devDependencies:
|
||||
'@babel/core':
|
||||
@@ -376,12 +380,12 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/@adraffy/ens-normalize@1.9.2:
|
||||
resolution: {integrity: sha512-0h+FrQDqe2Wn+IIGFkTCd4aAwTJ+7834Ek1COohCyV26AXhwQ7WQaz+4F/nLOeVl/3BtWHOHLPsq46V8YB46Eg==}
|
||||
/@adraffy/ens-normalize@1.9.0:
|
||||
resolution: {integrity: sha512-iowxq3U30sghZotgl4s/oJRci6WPBfNO5YYgk2cIOMCHr3LeGPcsZjCEr+33Q4N+oV3OABDAtA+pyvWjbvBifQ==}
|
||||
dev: false
|
||||
|
||||
/@adraffy/ens-normalize@1.9.4:
|
||||
resolution: {integrity: sha512-UK0bHA7hh9cR39V+4gl2/NnBBjoXIxkuWAPCaY4X7fbH4L/azIi7ilWOCjMUYfpJgraLUAqkRi2BqrjME8Rynw==}
|
||||
/@adraffy/ens-normalize@1.9.2:
|
||||
resolution: {integrity: sha512-0h+FrQDqe2Wn+IIGFkTCd4aAwTJ+7834Ek1COohCyV26AXhwQ7WQaz+4F/nLOeVl/3BtWHOHLPsq46V8YB46Eg==}
|
||||
dev: false
|
||||
|
||||
/@ampproject/remapping@2.2.1:
|
||||
@@ -610,6 +614,74 @@ packages:
|
||||
'@babel/helper-validator-identifier': 7.22.5
|
||||
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:
|
||||
resolution: {integrity: sha512-LjyoDCB+7p0waQXfK+fUgcAs3Ezk6S6e+LYaoFjpJ6c9VTop3NyZF40Pi7df4z7QJohCwzuIDjz0Rhtig6Y7Pg==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
@@ -641,7 +713,7 @@ packages:
|
||||
/@confio/ics23@0.6.8:
|
||||
resolution: {integrity: sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w==}
|
||||
dependencies:
|
||||
'@noble/hashes': 1.3.2
|
||||
'@noble/hashes': 1.3.1
|
||||
protobufjs: 6.11.4
|
||||
dev: false
|
||||
|
||||
@@ -654,6 +726,15 @@ packages:
|
||||
'@cosmjs/utils': 0.27.1
|
||||
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:
|
||||
resolution: {integrity: sha512-yNHnzmvAlkETDYIpeCTdVqgvrdt1qgkOXwuRVi8s27UKI5hfqyE9fJ/fuunXE6ZZPnKkjIecDznmuUOMrMvw4w==}
|
||||
dependencies:
|
||||
@@ -707,13 +788,25 @@ packages:
|
||||
sha.js: 2.4.11
|
||||
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:
|
||||
resolution: {integrity: sha512-rAljUlake3MSXs9xAm87mu34GfBLN0h/1uPPV6jEwClWjNkAMotzjC0ab9MARy5FFAvYHL3lWb57bhkbt2GtzQ==}
|
||||
dependencies:
|
||||
'@cosmjs/encoding': 0.30.1
|
||||
'@cosmjs/math': 0.30.1
|
||||
'@cosmjs/utils': 0.30.1
|
||||
'@noble/hashes': 1.3.2
|
||||
'@noble/hashes': 1.3.1
|
||||
bn.js: 5.2.1
|
||||
elliptic: 6.5.4
|
||||
libsodium-wrappers: 0.7.11
|
||||
@@ -739,6 +832,14 @@ packages:
|
||||
readonly-date: 1.0.0
|
||||
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:
|
||||
resolution: {integrity: sha512-rXmrTbgqwihORwJ3xYhIgQFfMSrwLu1s43RIK9I8EBudPx3KmnmyAKzMOVsRDo9edLFNuZ9GIvysUCwQfq3WlQ==}
|
||||
dependencies:
|
||||
@@ -755,12 +856,11 @@ packages:
|
||||
readonly-date: 1.0.0
|
||||
dev: false
|
||||
|
||||
/@cosmjs/encoding@0.31.1:
|
||||
resolution: {integrity: sha512-IuxP6ewwX6vg9sUJ8ocJD92pkerI4lyG8J5ynAM3NaX3q+n+uMoPRSQXNeL9bnlrv01FF1kIm8if/f5F7ZPtkA==}
|
||||
/@cosmjs/json-rpc@0.29.5:
|
||||
resolution: {integrity: sha512-C78+X06l+r9xwdM1yFWIpGl03LhB9NdM1xvZpQHwgCOl0Ir/WV8pw48y3Ez2awAoUBRfTeejPe4KvrE6NoIi/w==}
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
bech32: 1.1.4
|
||||
readonly-date: 1.0.0
|
||||
'@cosmjs/stream': 0.29.5
|
||||
xstream: 11.14.0
|
||||
dev: false
|
||||
|
||||
/@cosmjs/json-rpc@0.30.1:
|
||||
@@ -797,6 +897,12 @@ packages:
|
||||
bn.js: 5.2.1
|
||||
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:
|
||||
resolution: {integrity: sha512-yaoeI23pin9ZiPHIisa6qqLngfnBR/25tSaWpkTm8Cy10MX70UF5oN4+/t1heLaM6SSmRrhk3psRkV4+7mH51Q==}
|
||||
dependencies:
|
||||
@@ -809,10 +915,16 @@ packages:
|
||||
bn.js: 5.2.1
|
||||
dev: false
|
||||
|
||||
/@cosmjs/math@0.31.1:
|
||||
resolution: {integrity: sha512-kiuHV6m6DSB8/4UV1qpFhlc4ul8SgLXTGRlYkYiIIP4l0YNeJ+OpPYaOlEgx4Unk2mW3/O2FWYj7Jc93+BWXng==}
|
||||
/@cosmjs/proto-signing@0.29.3:
|
||||
resolution: {integrity: sha512-Ai3l9THjMOrLJ4Ebn1Dgptwg6W5ZIRJqtnJjijHhGwTVC1WT0WdYU3aMZ7+PwubcA/cA1rH4ZTK7jrfYbra63g==}
|
||||
dependencies:
|
||||
bn.js: 5.2.1
|
||||
'@cosmjs/amino': 0.29.3
|
||||
'@cosmjs/crypto': 0.29.5
|
||||
'@cosmjs/encoding': 0.29.5
|
||||
'@cosmjs/math': 0.29.5
|
||||
'@cosmjs/utils': 0.29.5
|
||||
cosmjs-types: 0.5.2
|
||||
long: 4.0.0
|
||||
dev: false
|
||||
|
||||
/@cosmjs/proto-signing@0.30.1:
|
||||
@@ -839,6 +951,18 @@ packages:
|
||||
long: 4.0.0
|
||||
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:
|
||||
resolution: {integrity: sha512-r6MpDL+9N+qOS/D5VaxnPaMJ3flwQ36G+vPvYJsXArj93BjgyFB7BwWwXCQDzZ+23cfChPUfhbINOenr8N2Kow==}
|
||||
dependencies:
|
||||
@@ -863,6 +987,27 @@ packages:
|
||||
- utf-8-validate
|
||||
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:
|
||||
resolution: {integrity: sha512-RdbYKZCGOH8gWebO7r6WvNnQMxHrNXInY/gPHPzMjbQF6UatA6fNM2G2tdgS5j5u7FTqlCI10stNXrknaNdzog==}
|
||||
dependencies:
|
||||
@@ -905,6 +1050,12 @@ packages:
|
||||
- utf-8-validate
|
||||
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:
|
||||
resolution: {integrity: sha512-Fg0pWz1zXQdoxQZpdHRMGvUH5RqS6tPv+j9Eh7Q953UjMlrwZVo0YFLC8OTf/HKVf10E4i0u6aM8D69Q6cNkgQ==}
|
||||
dependencies:
|
||||
@@ -917,6 +1068,25 @@ packages:
|
||||
xstream: 11.14.0
|
||||
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:
|
||||
resolution: {integrity: sha512-Z3nCwhXSbPZJ++v85zHObeUggrEHVfm1u18ZRwXxFE9ZMl5mXTybnwYhczuYOl7KRskgwlB+rID0WYACxj4wdQ==}
|
||||
dependencies:
|
||||
@@ -959,6 +1129,10 @@ packages:
|
||||
resolution: {integrity: sha512-VG7QPDiMUzVPxRdJahDV8PXxVdnuAHiIuG56hldV4yPnOz/si/DLNd7VAUUA5923b6jS1Hhev0Hr6AhEkcxBMg==}
|
||||
dev: false
|
||||
|
||||
/@cosmjs/utils@0.29.5:
|
||||
resolution: {integrity: sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ==}
|
||||
dev: false
|
||||
|
||||
/@cosmjs/utils@0.30.1:
|
||||
resolution: {integrity: sha512-KvvX58MGMWh7xA+N+deCfunkA/ZNDvFLw4YbOmX3f/XBIkqrVY7qlotfy2aNb1kgp6h4B6Yc8YawJPDTfvWX7g==}
|
||||
dev: false
|
||||
@@ -967,39 +1141,47 @@ packages:
|
||||
resolution: {integrity: sha512-nNcycZWUYLNJlrIXgpcgVRqdl6BXjF4YlXdxobQWpW9Tikk61bEGeAFhDYtC0PwHlokCNw0KxWiHGJL4nL7Q5A==}
|
||||
dev: false
|
||||
|
||||
/@cosmjs/utils@0.31.1:
|
||||
resolution: {integrity: sha512-n4Se1wu4GnKwztQHNFfJvUeWcpvx3o8cWhSbNs9JQShEuB3nv3R5lqFBtDCgHZF/emFQAP+ZjF8bTfCs9UBGhA==}
|
||||
dev: false
|
||||
|
||||
/@cush/relative@1.0.0:
|
||||
resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==}
|
||||
dev: true
|
||||
|
||||
/@dydxprotocol/v4-abacus@1.0.3:
|
||||
resolution: {integrity: sha512-GuX37/DMMNSke8ZFcw52II7IVJfUgVGHqoi1Uw2cUb+hgIjjSG3OtbyRWedm5r6nqWq8BNLOexI0VVFB6OMVYg==}
|
||||
/@dydxprotocol/abacus@0.4.8:
|
||||
resolution: {integrity: sha512-hX31m+a5Bwbs4dr5raPDtnflwUzEu/9BIt+LOlQgt1YOViuIse8dozgpgCHvqvA8MttqY3QGPalx5iRB8j8vtw==}
|
||||
dev: false
|
||||
|
||||
/@dydxprotocol/v4-client-js@1.0.0:
|
||||
resolution: {integrity: sha512-ehfHO+zQy795TcJRwtiawadFHZyh4HnpJNP26hCGsIjLE5q6LLHweQHpK/1N/sXU1PBOuQwc7iaJnFop6gYauQ==}
|
||||
/@dydxprotocol/dydxjs@0.3.0:
|
||||
resolution: {integrity: sha512-ygNeBs0f3H7sJk1qLUjfNwaXgc5TmjD+qZf7BFDbi7+boHJI9xuOWrGpjfBo7OTUZxS6O5jZG323CgU5XqMSPw==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.22.10
|
||||
'@bufbuild/buf': 1.19.0-1
|
||||
'@cosmjs/amino': 0.30.1
|
||||
'@cosmjs/encoding': 0.31.1
|
||||
'@cosmjs/math': 0.31.1
|
||||
'@cosmjs/proto-signing': 0.30.1
|
||||
'@cosmjs/stargate': 0.30.1
|
||||
'@cosmjs/tendermint-rpc': 0.30.1
|
||||
'@cosmjs/utils': 0.31.1
|
||||
'@dydxprotocol/v4-proto': 0.4.1
|
||||
osmojs: 15.5.0
|
||||
protobufjs: 6.11.4
|
||||
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
|
||||
'@scure/bip32': 1.3.2
|
||||
'@scure/bip32': 1.3.1
|
||||
'@scure/bip39': 1.2.1
|
||||
axios: 1.1.3
|
||||
bech32: 1.1.4
|
||||
bignumber.js: 9.1.1
|
||||
ethereum-cryptography: 2.1.2
|
||||
ethers: 6.6.1
|
||||
long: 4.0.0
|
||||
protobufjs: 6.11.4
|
||||
ws: 8.13.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
@@ -1007,14 +1189,8 @@ packages:
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
/@dydxprotocol/v4-localization@1.0.0:
|
||||
resolution: {integrity: sha512-U7QjOlzpfSAVEchkNDB3xYRx7okzdeYi83XsnHwI+EHa2quFm/2Y2dhrBkT5fqpZmhcwxb/vG8AZmFSToOf9hA==}
|
||||
dev: false
|
||||
|
||||
/@dydxprotocol/v4-proto@0.4.1:
|
||||
resolution: {integrity: sha512-2pBkokufWcxfIpiXE6QuQOBXUe154OTp6ojCIdOPW9TFLxzchoygxXnb5dL6rAQVNj9Qbg+hhXBllutt4m99dw==}
|
||||
dependencies:
|
||||
protobufjs: 6.11.4
|
||||
/@dydxprotocol/v4-localization@0.0.25:
|
||||
resolution: {integrity: sha512-Rp6VHA8z+nwgvTjjB3fvL8gY7bd4DYDHV+NPoL0MFuRm39vhFxGlBVAGDkXPkQTh6YU5BlZV2yXLkDeSfFqEBQ==}
|
||||
dev: false
|
||||
|
||||
/@emotion/is-prop-valid@1.2.1:
|
||||
@@ -2029,12 +2205,6 @@ packages:
|
||||
'@noble/hashes': 1.3.1
|
||||
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:
|
||||
resolution: {integrity: sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==}
|
||||
dev: true
|
||||
@@ -2052,11 +2222,6 @@ packages:
|
||||
engines: {node: '>= 16'}
|
||||
dev: false
|
||||
|
||||
/@noble/hashes@1.3.2:
|
||||
resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==}
|
||||
engines: {node: '>= 16'}
|
||||
dev: false
|
||||
|
||||
/@noble/secp256k1@1.7.1:
|
||||
resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==}
|
||||
|
||||
@@ -2081,6 +2246,15 @@ packages:
|
||||
fastq: 1.15.0
|
||||
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:
|
||||
resolution: {integrity: sha512-vz9VavXrEfxZoXbSAfNfk90MLpn34XtBYPV3L9YilE+s56AhqYxUh83nne9J5somnTRfGnyR3oeV8C+lHkqiuA==}
|
||||
dependencies:
|
||||
@@ -4655,7 +4829,7 @@ packages:
|
||||
resolution: {integrity: sha512-gYw0ki/EAuV1oSyMxpqandHjnthZjYYy+YWpTAzf8BqfXM3ItcZLpjxfg+3+mXW8HIO+3jw6T9iiqEXsqHaMMw==}
|
||||
dependencies:
|
||||
'@safe-global/safe-gateway-typescript-sdk': 3.9.0
|
||||
viem: 1.12.2(typescript@5.1.3)
|
||||
viem: 1.1.6(typescript@5.1.3)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- encoding
|
||||
@@ -4668,7 +4842,7 @@ packages:
|
||||
resolution: {integrity: sha512-XJbEPuaVc7b9n23MqlF6c+ToYIS3f7P2Sel8f3cSBQ9WORE4xrSuvhMpK9fDSFqJ7by/brc+rmJR/5HViRr0/w==}
|
||||
dependencies:
|
||||
'@safe-global/safe-gateway-typescript-sdk': 3.9.0
|
||||
viem: 1.12.2(typescript@5.1.3)
|
||||
viem: 1.1.6(typescript@5.1.3)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- encoding
|
||||
@@ -4689,10 +4863,6 @@ packages:
|
||||
resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==}
|
||||
dev: false
|
||||
|
||||
/@scure/base@1.1.3:
|
||||
resolution: {integrity: sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==}
|
||||
dev: false
|
||||
|
||||
/@scure/bip32@1.3.0:
|
||||
resolution: {integrity: sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==}
|
||||
dependencies:
|
||||
@@ -4705,16 +4875,8 @@ packages:
|
||||
resolution: {integrity: sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==}
|
||||
dependencies:
|
||||
'@noble/curves': 1.1.0
|
||||
'@noble/hashes': 1.3.2
|
||||
'@scure/base': 1.1.3
|
||||
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
|
||||
'@noble/hashes': 1.3.1
|
||||
'@scure/base': 1.1.1
|
||||
dev: false
|
||||
|
||||
/@scure/bip39@1.2.0:
|
||||
@@ -4727,8 +4889,8 @@ packages:
|
||||
/@scure/bip39@1.2.1:
|
||||
resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==}
|
||||
dependencies:
|
||||
'@noble/hashes': 1.3.2
|
||||
'@scure/base': 1.1.3
|
||||
'@noble/hashes': 1.3.1
|
||||
'@scure/base': 1.1.1
|
||||
dev: false
|
||||
|
||||
/@solana/buffer-layout@4.0.1:
|
||||
@@ -4742,8 +4904,8 @@ packages:
|
||||
resolution: {integrity: sha512-up5VG1dK+GPhykmuMIozJZBbVqpm77vbOG6/r5dS7NBGZonwHfTLdBbsYc3rjmaQ4DpCXUa3tUc4RZHRORvZrw==}
|
||||
dependencies:
|
||||
'@babel/runtime': 7.22.10
|
||||
'@noble/curves': 1.2.0
|
||||
'@noble/hashes': 1.3.2
|
||||
'@noble/curves': 1.1.0
|
||||
'@noble/hashes': 1.3.1
|
||||
'@solana/buffer-layout': 4.0.1
|
||||
agentkeepalive: 4.5.0
|
||||
bigint-buffer: 1.1.5
|
||||
@@ -5428,12 +5590,6 @@ packages:
|
||||
'@types/node': 20.3.1
|
||||
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):
|
||||
resolution: {integrity: sha512-78B+anHLF1TI8Jn/cD0Q00TBYdMgjdOn980JfAVa9yw5sop8nyTfVOQAv6LWywkOGLclDBtv5z3oxN4w7jxyNg==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
@@ -5930,6 +6086,17 @@ packages:
|
||||
pretty-format: 27.5.1
|
||||
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):
|
||||
resolution: {integrity: sha512-9HwJrhcZ1TxyrCbE10y7s1eSiSiyfGam7AHIOLYExaOX+vpOZ+MNTt4orFEDbEpz1fxwJDPPI38lanAUix1OSA==}
|
||||
peerDependencies:
|
||||
@@ -5941,7 +6108,7 @@ packages:
|
||||
typescript: 5.1.3
|
||||
dev: false
|
||||
|
||||
/@wagmi/connectors@2.6.5(@wagmi/chains@1.4.0)(react@18.2.0)(typescript@5.1.3)(viem@1.12.2):
|
||||
/@wagmi/connectors@2.6.5(@wagmi/chains@1.4.0)(react@18.2.0)(typescript@5.1.3)(viem@1.1.6):
|
||||
resolution: {integrity: sha512-klF31togMDd0qQqEcLl5cCGxjMbL0RRXQ8I1vxmEa3KdGzw6Z3ICVzX7/bDfnNEZcOW7BKyAnZDq7rCt5jTOiw==}
|
||||
peerDependencies:
|
||||
'@wagmi/chains': '>=1.3.0'
|
||||
@@ -5965,7 +6132,7 @@ packages:
|
||||
abitype: 0.8.7(typescript@5.1.3)
|
||||
eventemitter3: 4.0.7
|
||||
typescript: 5.1.3
|
||||
viem: 1.12.2(typescript@5.1.3)
|
||||
viem: 1.1.6(typescript@5.1.3)
|
||||
transitivePeerDependencies:
|
||||
- '@react-native-async-storage/async-storage'
|
||||
- bufferutil
|
||||
@@ -5977,7 +6144,7 @@ packages:
|
||||
- zod
|
||||
dev: false
|
||||
|
||||
/@wagmi/core@1.3.6(@types/react@18.2.14)(react@18.2.0)(typescript@5.1.3)(viem@1.12.2):
|
||||
/@wagmi/core@1.3.6(@types/react@18.2.14)(react@18.2.0)(typescript@5.1.3)(viem@1.1.6):
|
||||
resolution: {integrity: sha512-TXv9ZlRR5aySfERFuWMuo+lKXC/CoqtxVJZVHPqhK1jY+nldMx3AvrWzzF4CccRaMYcVdvPFepvmxzq2A2VvWg==}
|
||||
peerDependencies:
|
||||
typescript: '>=5.0.4'
|
||||
@@ -5987,11 +6154,11 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@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.12.2)
|
||||
'@wagmi/connectors': 2.6.5(@wagmi/chains@1.4.0)(react@18.2.0)(typescript@5.1.3)(viem@1.1.6)
|
||||
abitype: 0.8.7(typescript@5.1.3)
|
||||
eventemitter3: 4.0.7
|
||||
typescript: 5.1.3
|
||||
viem: 1.12.2(typescript@5.1.3)
|
||||
viem: 1.1.6(typescript@5.1.3)
|
||||
zustand: 4.4.1(@types/react@18.2.14)(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
- '@react-native-async-storage/async-storage'
|
||||
@@ -6692,20 +6859,6 @@ packages:
|
||||
typescript: 5.1.3
|
||||
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:
|
||||
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
||||
engines: {node: '>=6.5'}
|
||||
@@ -7141,7 +7294,7 @@ packages:
|
||||
/bip39@3.1.0:
|
||||
resolution: {integrity: sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==}
|
||||
dependencies:
|
||||
'@noble/hashes': 1.3.2
|
||||
'@noble/hashes': 1.3.1
|
||||
dev: false
|
||||
|
||||
/bl@5.1.0:
|
||||
@@ -7634,6 +7787,13 @@ packages:
|
||||
path-type: 4.0.0
|
||||
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:
|
||||
resolution: {integrity: sha512-vf2uLyktjr/XVAgEq0DjMxeAWh1yYREe7AMHDKd7EiHVqxBPCaBS+qEEQUkXbR9ndnckqr1sUG8BQhazh4X5lA==}
|
||||
dependencies:
|
||||
@@ -10015,12 +10175,12 @@ packages:
|
||||
ws: 7.5.9
|
||||
dev: false
|
||||
|
||||
/isomorphic-ws@5.0.0(ws@8.13.0):
|
||||
/isomorphic-ws@5.0.0(ws@8.12.0):
|
||||
resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==}
|
||||
peerDependencies:
|
||||
ws: '*'
|
||||
dependencies:
|
||||
ws: 8.13.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)
|
||||
ws: 8.12.0
|
||||
dev: false
|
||||
|
||||
/it-all@1.0.6:
|
||||
@@ -11481,6 +11641,23 @@ packages:
|
||||
type-check: 0.4.0
|
||||
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:
|
||||
resolution: {integrity: sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -13549,24 +13726,21 @@ packages:
|
||||
vfile-message: 3.1.4
|
||||
dev: true
|
||||
|
||||
/viem@1.12.2(typescript@5.1.3):
|
||||
resolution: {integrity: sha512-aCaUCyg72ES+jK4s6tVYOMnOt4if71RwzgiUAUpAuaCgvHFfh9DCnwuEfwkxEZLE2vafOsirgJ3fcn7nsDVQoQ==}
|
||||
/viem@1.1.6(typescript@5.1.3):
|
||||
resolution: {integrity: sha512-7nK3HMucLr1Yz0QnDXiD6viigKS6QeYD/YhRHpi3Bby/g0hCyZqK8+YJNtp3/Ri64tpl4kaTIeCScJWV2jqXHQ==}
|
||||
peerDependencies:
|
||||
typescript: '>=5.0.4'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@adraffy/ens-normalize': 1.9.4
|
||||
'@noble/curves': 1.2.0
|
||||
'@noble/hashes': 1.3.2
|
||||
'@scure/bip32': 1.3.2
|
||||
'@scure/bip39': 1.2.1
|
||||
'@types/ws': 8.5.6
|
||||
abitype: 0.9.8(typescript@5.1.3)
|
||||
isomorphic-ws: 5.0.0(ws@8.13.0)
|
||||
'@adraffy/ens-normalize': 1.9.0
|
||||
'@noble/curves': 1.0.0
|
||||
'@noble/hashes': 1.3.0
|
||||
'@scure/bip32': 1.3.0
|
||||
'@scure/bip39': 1.2.0
|
||||
'@wagmi/chains': 1.2.0(typescript@5.1.3)
|
||||
abitype: 0.8.7(typescript@5.1.3)
|
||||
isomorphic-ws: 5.0.0(ws@8.12.0)
|
||||
typescript: 5.1.3
|
||||
ws: 8.13.0(bufferutil@4.0.7)(utf-8-validate@5.0.10)
|
||||
ws: 8.12.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
@@ -13734,7 +13908,7 @@ packages:
|
||||
- supports-color
|
||||
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.12.2):
|
||||
/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):
|
||||
resolution: {integrity: sha512-n4Qwby0QB8/JSGjc0gGn4MWZ0BT229agEg6saPGS6VswmPjJLdkLq9qxKn1JptXxPREmb/ZvwHxU++3Z+gQJVA==}
|
||||
peerDependencies:
|
||||
react: '>=17.0.0'
|
||||
@@ -13747,12 +13921,12 @@ packages:
|
||||
'@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-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.12.2)
|
||||
'@wagmi/core': 1.3.6(@types/react@18.2.14)(react@18.2.0)(typescript@5.1.3)(viem@1.1.6)
|
||||
abitype: 0.8.7(typescript@5.1.3)
|
||||
react: 18.2.0
|
||||
typescript: 5.1.3
|
||||
use-sync-external-store: 1.2.0(react@18.2.0)
|
||||
viem: 1.12.2(typescript@5.1.3)
|
||||
viem: 1.1.6(typescript@5.1.3)
|
||||
transitivePeerDependencies:
|
||||
- '@react-native-async-storage/async-storage'
|
||||
- '@types/react'
|
||||
@@ -13977,6 +14151,19 @@ packages:
|
||||
optional: true
|
||||
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):
|
||||
resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
@@ -14096,7 +14283,3 @@ packages:
|
||||
/zwitch@2.0.4:
|
||||
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||
dev: true
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
@@ -4,47 +4,71 @@
|
||||
"details": [
|
||||
{
|
||||
"appIDs": [
|
||||
"{PLACE_YOUR_APP_ID_HERE}"
|
||||
"75C6UARB5H.exchangeV4.dydx.trading"
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"/": "/"
|
||||
"/": "/trade/*"
|
||||
},
|
||||
{
|
||||
"/": "/#/"
|
||||
"/": "/r/*"
|
||||
},
|
||||
{
|
||||
"/": "*/markets"
|
||||
"/": "/portfolio/overview"
|
||||
},
|
||||
{
|
||||
"/": "*/trade"
|
||||
"/": "/portfolio/positions"
|
||||
},
|
||||
{
|
||||
"/": "*/trade/*"
|
||||
"/": "/portfolio/orders"
|
||||
},
|
||||
{
|
||||
"/": "*/portfolio"
|
||||
"/": "/portfolio/fees"
|
||||
},
|
||||
{
|
||||
"/": "*/portfolio/overview"
|
||||
"/": "/portfolio/history"
|
||||
},
|
||||
{
|
||||
"/": "*/portfolio/positions"
|
||||
"/": "/portfolio/history/trades"
|
||||
},
|
||||
{
|
||||
"/": "*/portfolio/orders"
|
||||
"/": "/portfolio/history/transfers"
|
||||
},
|
||||
{
|
||||
"/": "*/portfolio/fees"
|
||||
"/": "/portfolio/history/funding"
|
||||
},
|
||||
{
|
||||
"/": "*/portfolio/history/trades"
|
||||
"/": "/rewards"
|
||||
},
|
||||
{
|
||||
"/": "*/portfolio/history/transfers"
|
||||
"/": "/my-profile"
|
||||
},
|
||||
{
|
||||
"/": "*/portfolio/history/funding"
|
||||
"/": "/rankings/hedgies"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/competition"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/diamond"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/platinum"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/gold"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/silver"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/bronze"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/pnl-absolute"
|
||||
},
|
||||
{
|
||||
"/": "/rankings/pnl-percent"
|
||||
},
|
||||
{
|
||||
"/": "/walletsegue",
|
||||
|
||||
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 193 KiB |
@@ -1,454 +0,0 @@
|
||||
{
|
||||
"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/"
|
||||
}
|
||||
}
|
||||
@@ -1,472 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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": "",
|
||||
"encoding": "=\"#%/<>?@\\^`{|}:&",
|
||||
"androidPackage": "io.metamask",
|
||||
"connections": [
|
||||
{
|
||||
"type": "walletConnectV2",
|
||||
"native": "metamask:",
|
||||
"universal": "https://metamask.app.link"
|
||||
}
|
||||
],
|
||||
"methods": [
|
||||
"eth_sendTransaction",
|
||||
"personal_sign",
|
||||
"eth_signTypedData",
|
||||
"wallet_addEthereumChain"
|
||||
]
|
||||
},
|
||||
"userFields": {
|
||||
"analyticEvent": "METAMASK",
|
||||
"imageName": "metamask.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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": "",
|
||||
"encoding": "=\"#%/<>?@\\^`{|}:&",
|
||||
"androidPackage": "org.toshi",
|
||||
"connections": [
|
||||
{
|
||||
"type": "walletSegue",
|
||||
"native": "cbwallet:",
|
||||
"universal": ""
|
||||
}
|
||||
],
|
||||
"methods": [
|
||||
"eth_sendTransaction",
|
||||
"personal_sign",
|
||||
"eth_signTypedData",
|
||||
"wallet_addEthereumChain"
|
||||
]
|
||||
},
|
||||
"userFields": {
|
||||
"analyticEvent": "COINBASE_WALLET",
|
||||
"imageName": "coinbase-wallet.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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": "",
|
||||
"androidPackage": "im.token.app",
|
||||
"connections": [
|
||||
{
|
||||
"type": "walletConnectV2",
|
||||
"native": "imtokenv2:",
|
||||
"universal": ""
|
||||
}
|
||||
],
|
||||
"methods": [
|
||||
"eth_sendTransaction",
|
||||
"personal_sign",
|
||||
"eth_signTypedData"
|
||||
]
|
||||
},
|
||||
"userFields": {
|
||||
"analyticEvent": "IMTOKEN",
|
||||
"imageName": "imtoken.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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": "",
|
||||
"androidPackage": "com.wallet.crypto.trustapp",
|
||||
"connections": [
|
||||
{
|
||||
"type": "walletConnectV2",
|
||||
"native": "trust:",
|
||||
"universal": "https://link.trustwallet.com"
|
||||
}
|
||||
],
|
||||
"methods": [
|
||||
"eth_sendTransaction",
|
||||
"personal_sign",
|
||||
"eth_signTypedData"
|
||||
]
|
||||
},
|
||||
"userFields": {
|
||||
"analyticEvent": "TRUST_WALLET",
|
||||
"imageName": "trust-wallet.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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": "",
|
||||
"androidPackage": "io.zerion.android",
|
||||
"connections": [
|
||||
{
|
||||
"type": "walletConnectV2",
|
||||
"native": "zerion:",
|
||||
"universal": ""
|
||||
}
|
||||
],
|
||||
"methods": [
|
||||
"eth_sendTransaction",
|
||||
"personal_sign",
|
||||
"eth_signTypedData"
|
||||
]
|
||||
},
|
||||
"userFields": {
|
||||
"analyticEvent": "ZERION",
|
||||
"imageName": "zerion.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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": "",
|
||||
"androidPackage": "vip.mytokenpocket",
|
||||
"connections": [
|
||||
{
|
||||
"type": "walletConnectV2",
|
||||
"native": "tpoutside:",
|
||||
"universal": ""
|
||||
}
|
||||
],
|
||||
"methods": [
|
||||
"eth_sendTransaction",
|
||||
"personal_sign",
|
||||
"eth_signTypedData"
|
||||
]
|
||||
},
|
||||
"userFields": {
|
||||
"analyticEvent": "TOKEN_POCKET",
|
||||
"imageName": "tokenpocket.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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": "=\"#%/<>?@\\^`{|}:&",
|
||||
"androidPackage": "me.rainbow",
|
||||
"backlinked": true,
|
||||
"imageUrl": "",
|
||||
"connections": [
|
||||
{
|
||||
"type": "walletConnectV2",
|
||||
"native": "rainbow:",
|
||||
"universal": "https://rnbwapp.com"
|
||||
}
|
||||
],
|
||||
"methods": [
|
||||
"eth_sendTransaction",
|
||||
"personal_sign",
|
||||
"eth_signTypedData"
|
||||
]
|
||||
},
|
||||
"userFields": {
|
||||
"analyticEvent": "RAINBOW_WALLET",
|
||||
"imageName": "rainbow-wallet.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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": "=\"#%/<>?@\\^`{|}:&",
|
||||
"androidPackage": "io.oneinch.android",
|
||||
"imageUrl": "",
|
||||
"connections": [
|
||||
{
|
||||
"type": "walletConnectV2",
|
||||
"native": "oneinch:",
|
||||
"universal": "https://wallet.1inch.io"
|
||||
}
|
||||
],
|
||||
"methods": [
|
||||
"eth_sendTransaction",
|
||||
"personal_sign",
|
||||
"eth_signTypedData"
|
||||
]
|
||||
},
|
||||
"userFields": {
|
||||
"analyticEvent": "1INCH",
|
||||
"imageName": "1inch.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 4.8 KiB |
@@ -1,38 +0,0 @@
|
||||
## App Integrations
|
||||
### Amplitude
|
||||
Analytics service used to collect data to help the dYdX Product team make informed product decisions.
|
||||
|
||||
<b>To use with dydxprotocol/v4-web:</b>
|
||||
1. Amplitude account with API key.
|
||||
2. Add API key in Github > Secrets and Variables > Actions as `AMPLITUDE_API_KEY`
|
||||
3. In your deploy scripts add `pnpm run build:inject-amplitude` after your pnpm build / vite build command.
|
||||
|
||||
### Bugsnag
|
||||
Error handling service used to collect handled/unhandled errors within the app. The information collected is used to help debug and alert the engineering team of crashes/unhandled errors within the app to improve stability.
|
||||
|
||||
<b>To use with dydxprotocol/v4-web:</b>
|
||||
1. Bugsnag account with API key.
|
||||
2. Add API key in Github > Secrets and Variables > Actions as `BUGSNAG_API_KEY`
|
||||
3. In your deploy scripts add `pnpm run build:inject-bugsnag` after your pnpm build / vite build command.
|
||||
4. If you are using with the Amplitude deployment scripts, your build command may look like the following: `pnpm build && pnpm run build:inject-amplitude && pnpm run build:inject-bugsnag`
|
||||
|
||||
### StatusPage
|
||||
Service used to inform users of any status updates to our platform. These status updates are manually set and updated by the On Call engineers.
|
||||
|
||||
<b>To use with dydxprotocol/v4-web:</b>
|
||||
1. StatusPage account and script URI
|
||||
2. Add API key in Github > Secrets and Variables > Actions as `STATUS_PAGE_SCRIPT_URI`
|
||||
3. In your deploy scripts add `pnpm run build:inject-statuspage` after your pnpm build / vite build command.
|
||||
4. If you are using with the Amplitude deployment scripts, your build command may look like the following: `pnpm build && pnpm run build:inject-amplitude && pnpm run build:inject-statuspage`
|
||||
|
||||
### Intercom
|
||||
Service used for live customer support (chat/inbox) as well as home for Help Articles.
|
||||
|
||||
<b>To use with dydxprotocol/v4-web:</b>
|
||||
1. Create Intercom account
|
||||
2. In Intercom UI
|
||||
Getting started > Set up Messenger > will give you your API Key on Step 2
|
||||
Customize Intercom Messenger by adding logo and brand colors
|
||||
3. Add API key in Github > Secrets and Variables > Actions as `INTERCOM_APP_ID`
|
||||
4. In your deploy scripts add `pnpm run build:inject-intercom` after your pnpm build / vite build command.
|
||||
5. If you are using with the Amplitude deployment scripts, your build command may look like the following: `pnpm build && pnpm run build:inject-amplitude && pnpm run build:inject-intercom`
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,76 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const INTERCOM_APP_ID = process.env.INTERCOM_APP_ID;
|
||||
|
||||
const currentPath = fileURLToPath(import.meta.url);
|
||||
const projectRoot = path.dirname(currentPath);
|
||||
const htmlFilePath = path.resolve(projectRoot, '../dist/index.html');
|
||||
|
||||
if (INTERCOM_APP_ID) {
|
||||
try {
|
||||
const html = await fs.readFile(htmlFilePath, 'utf-8');
|
||||
|
||||
const intercomScripts = `
|
||||
<!-- Intercom -->
|
||||
<script>
|
||||
window.intercomSettings = {
|
||||
api_base: 'https://api-iam.intercom.io',
|
||||
app_id: '${INTERCOM_APP_ID}',
|
||||
custom_launcher_selector: '.custom_intercom',
|
||||
hide_default_launcher: true,
|
||||
};
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// We pre-filled your app ID in the widget URL: 'https://widget.intercom.io/widget/${INTERCOM_APP_ID}'
|
||||
(function () {
|
||||
var w = window;
|
||||
var ic = w.Intercom;
|
||||
if (typeof ic === 'function') {
|
||||
ic('reattach_activator');
|
||||
ic('update', w.intercomSettings);
|
||||
} else {
|
||||
var d = document;
|
||||
var i = function () {
|
||||
i.c(arguments);
|
||||
};
|
||||
i.q = [];
|
||||
i.c = function (args) {
|
||||
i.q.push(args);
|
||||
};
|
||||
w.Intercom = i;
|
||||
var l = function () {
|
||||
var s = d.createElement('script');
|
||||
s.type = 'text/javascript';
|
||||
s.async = true;
|
||||
s.src = 'https://widget.intercom.io/widget/${INTERCOM_APP_ID}';
|
||||
var x = d.getElementsByTagName('script')[0];
|
||||
x.parentNode.insertBefore(s, x);
|
||||
};
|
||||
if (document.readyState === 'complete') {
|
||||
l();
|
||||
} else if (w.attachEvent) {
|
||||
w.attachEvent('onload', l);
|
||||
} else {
|
||||
w.addEventListener('load', l, false);
|
||||
}
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
`;
|
||||
|
||||
const injectedHtml = html.replace(
|
||||
'<div id="root"></div>',
|
||||
`<div id="root"></div>\n${intercomScripts}\n`
|
||||
);
|
||||
|
||||
await fs.writeFile(htmlFilePath, injectedHtml, 'utf-8');
|
||||
|
||||
console.log('Intercom scripts successfully injected.');
|
||||
} catch (err) {
|
||||
console.error('Error injecting Intercom scripts:', err);
|
||||
}
|
||||
}
|
||||
@@ -7,21 +7,14 @@ import { GrazProvider } from 'graz';
|
||||
|
||||
import { AppRoute, DEFAULT_TRADE_ROUTE } from '@/constants/routes';
|
||||
|
||||
import {
|
||||
useBreakpoints,
|
||||
useTokenConfigs,
|
||||
useInitializePage,
|
||||
useShouldShowFooter,
|
||||
useAnalytics,
|
||||
} from '@/hooks';
|
||||
import { useBreakpoints, useInitializePage, useShouldShowFooter, useAnalytics } from '@/hooks';
|
||||
import { DydxProvider } from '@/hooks/useDydxClient';
|
||||
import { AccountsProvider } from '@/hooks/useAccounts';
|
||||
import { DialogAreaProvider, useDialogArea } from '@/hooks/useDialogArea';
|
||||
import { LocaleProvider } from '@/hooks/useLocaleSeparators';
|
||||
import { NotificationsProvider } from '@/hooks/useNotifications';
|
||||
import { LocalNotificationsProvider } from '@/hooks/useLocalNotifications';
|
||||
import { RestrictionProvider } from '@/hooks/useRestrictions';
|
||||
import { SubaccountProvider } from '@/hooks/useSubaccount';
|
||||
import { DialogAreaProvider, useDialogArea } from './hooks/useDialogArea';
|
||||
import { LocaleProvider } from './hooks/useLocaleSeparators';
|
||||
import { NotificationsProvider } from './hooks/useNotifications';
|
||||
import { LocalNotificationsProvider } from './hooks/useLocalNotifications';
|
||||
import { SubaccountProvider } from './hooks/useSubaccount';
|
||||
import { SquidProvider } from '@/hooks/useSquid';
|
||||
|
||||
import { GuardedMobileRoute } from '@/components/GuardedMobileRoute';
|
||||
@@ -33,8 +26,6 @@ import ProfilePage from '@/pages/Profile';
|
||||
import { SettingsPage } from '@/pages/settings/Settings';
|
||||
import TradePage from '@/pages/trade/Trade';
|
||||
import { RewardsPage } from '@/pages/rewards/RewardsPage';
|
||||
import { TermsOfUsePage } from '@/pages/TermsOfUsePage';
|
||||
import { PrivacyPolicyPage } from '@/pages/PrivacyPolicyPage';
|
||||
|
||||
import { HeaderDesktop } from '@/layout/Header/HeaderDesktop';
|
||||
import { FooterDesktop } from '@/layout/Footer/FooterDesktop';
|
||||
@@ -63,7 +54,6 @@ const Content = () => {
|
||||
const { isTablet, isNotTablet } = useBreakpoints();
|
||||
const isShowingHeader = isNotTablet;
|
||||
const isShowingFooter = useShouldShowFooter();
|
||||
const { chainTokenLabel } = useTokenConfigs();
|
||||
|
||||
return (
|
||||
<Styled.Content isShowingHeader={isShowingHeader} isShowingFooter={isShowingFooter}>
|
||||
@@ -76,9 +66,10 @@ const Content = () => {
|
||||
<Route path=":market" element={<TradePage />} />
|
||||
<Route path={AppRoute.Trade} element={<TradePage />} />
|
||||
</Route>
|
||||
|
||||
<Route path={AppRoute.Markets} element={<MarketsPage />} />
|
||||
<Route path={`/${chainTokenLabel}`} element={<RewardsPage />} />
|
||||
{import.meta.env.MODE !== 'production' && (
|
||||
<Route path={AppRoute.Rewards} element={<RewardsPage />} />
|
||||
)}
|
||||
{isTablet && (
|
||||
<>
|
||||
<Route path={AppRoute.Alerts} element={<AlertsPage />} />
|
||||
@@ -90,10 +81,6 @@ const Content = () => {
|
||||
<Route element={<GuardedMobileRoute />}>
|
||||
<Route path={`${AppRoute.Portfolio}/*`} element={<PortfolioPage />} />
|
||||
</Route>
|
||||
|
||||
<Route path={AppRoute.Terms} element={<TermsOfUsePage />} />
|
||||
<Route path={AppRoute.Privacy} element={<PrivacyPolicyPage />} />
|
||||
|
||||
<Route path="*" element={<Navigate to={DEFAULT_TRADE_ROUTE} replace />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
@@ -113,7 +100,6 @@ const Content = () => {
|
||||
};
|
||||
|
||||
const wrapProvider = (Component: React.ComponentType<any>, props?: any) => {
|
||||
// eslint-disable-next-line react/display-name
|
||||
return ({ children }: { children: React.ReactNode }) => (
|
||||
<Component {...props}>{children}</Component>
|
||||
);
|
||||
@@ -124,7 +110,6 @@ const providers = [
|
||||
wrapProvider(GrazProvider),
|
||||
wrapProvider(WagmiConfig, { config }),
|
||||
wrapProvider(LocaleProvider),
|
||||
wrapProvider(RestrictionProvider),
|
||||
wrapProvider(DydxProvider),
|
||||
wrapProvider(AccountsProvider),
|
||||
wrapProvider(SubaccountProvider),
|
||||
@@ -211,7 +196,7 @@ Styled.NotificationsToastArea = styled(NotificationsToastArea)`
|
||||
|
||||
Styled.DialogArea = styled.aside`
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
height: 100vh;
|
||||
z-index: 1;
|
||||
inset: 0;
|
||||
overflow: clip;
|
||||
|
||||
@@ -80,7 +80,7 @@ Styled.TriggerIcon = styled.span`
|
||||
width: var(--trigger-icon-width);
|
||||
|
||||
display: inline-flex;
|
||||
transition: rotate 0.3s var(--ease-out-expo);
|
||||
transition: transform 0.3s var(--ease-out-expo);
|
||||
color: var(--trigger-icon-color);
|
||||
|
||||
${Styled.Trigger}[data-state='open'] & {
|
||||
|
||||
@@ -3,43 +3,31 @@ import styled, { AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { type MenuConfig } from '@/constants/menus';
|
||||
|
||||
import { Dialog, DialogPlacement, type DialogProps } from '@/components/Dialog';
|
||||
import { ComboboxMenu, type ComboboxMenuProps } from '@/components/ComboboxMenu';
|
||||
import { Dialog, DialogPlacement } from '@/components/Dialog';
|
||||
import { ComboboxMenu } from '@/components/ComboboxMenu';
|
||||
|
||||
type ElementProps<MenuItemValue extends string | number, MenuGroupValue extends string | number> = {
|
||||
isOpen?: boolean;
|
||||
setIsOpen?: (open: boolean) => void;
|
||||
title?: React.ReactNode;
|
||||
description?: React.ReactNode;
|
||||
slotTrigger?: React.ReactNode;
|
||||
slotHeaderInner?: React.ReactNode;
|
||||
slotFooter?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
|
||||
items: MenuConfig<MenuItemValue, MenuGroupValue>;
|
||||
onItemSelected?: () => void;
|
||||
inputPlaceholder?: string;
|
||||
slotEmpty?: React.ReactNode;
|
||||
};
|
||||
|
||||
type StyleProps = {
|
||||
placement: DialogPlacement,
|
||||
className?: string;
|
||||
};
|
||||
|
||||
type PickComboxMenuProps<
|
||||
MenuItemValue extends string | number,
|
||||
MenuGroupValue extends string | number
|
||||
> = Pick<
|
||||
ComboboxMenuProps<MenuItemValue, MenuGroupValue>,
|
||||
'inputPlaceholder' | 'onItemSelected' | 'slotEmpty' | 'withSearch' | 'withStickyLayout'
|
||||
>;
|
||||
|
||||
type PickDialogProps = Pick<
|
||||
DialogProps,
|
||||
| 'description'
|
||||
| 'isOpen'
|
||||
| 'placement'
|
||||
| 'setIsOpen'
|
||||
| 'slotHeaderInner'
|
||||
| 'slotTrigger'
|
||||
| 'slotFooter'
|
||||
>;
|
||||
|
||||
export const ComboboxDialogMenu = <
|
||||
MenuItemValue extends string | number,
|
||||
MenuGroupValue extends string | number
|
||||
>({
|
||||
export const ComboboxDialogMenu = <MenuItemValue extends string | number, MenuGroupValue extends string | number>({
|
||||
isOpen = false,
|
||||
setIsOpen,
|
||||
title,
|
||||
@@ -52,16 +40,11 @@ export const ComboboxDialogMenu = <
|
||||
onItemSelected,
|
||||
inputPlaceholder,
|
||||
slotEmpty,
|
||||
withSearch,
|
||||
withStickyLayout = true,
|
||||
children,
|
||||
|
||||
|
||||
placement = DialogPlacement.Default,
|
||||
className,
|
||||
}: ElementProps<MenuItemValue, MenuGroupValue> &
|
||||
PickComboxMenuProps<MenuItemValue, MenuGroupValue> &
|
||||
PickDialogProps &
|
||||
StyleProps) => (
|
||||
}: ElementProps<MenuItemValue, MenuGroupValue> & StyleProps) => (
|
||||
// TODO: sub-menu state management
|
||||
<Styled.Dialog
|
||||
isOpen={isOpen}
|
||||
@@ -71,6 +54,7 @@ export const ComboboxDialogMenu = <
|
||||
slotHeaderInner={slotHeaderInner}
|
||||
slotTrigger={slotTrigger}
|
||||
slotFooter={slotFooter}
|
||||
|
||||
placement={placement}
|
||||
className={className}
|
||||
>
|
||||
@@ -80,8 +64,7 @@ export const ComboboxDialogMenu = <
|
||||
title={title}
|
||||
inputPlaceholder={inputPlaceholder}
|
||||
slotEmpty={slotEmpty}
|
||||
withSearch={withSearch}
|
||||
withStickyLayout={withStickyLayout}
|
||||
withStickyLayout
|
||||
/>
|
||||
{children}
|
||||
</Styled.Dialog>
|
||||
@@ -115,5 +98,4 @@ Styled.Dialog = styled(Dialog)`
|
||||
|
||||
Styled.ComboboxMenu = styled(ComboboxMenu)`
|
||||
--comboboxMenu-backgroundColor: var(--comboboxDialogMenu-backgroundColor);
|
||||
--comboboxMenu-item-gap: var(--comboxDialogMenu-item-gap, 0.5rem);
|
||||
`;
|
||||
|
||||
@@ -8,7 +8,7 @@ import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { Tag } from '@/components/Tag';
|
||||
|
||||
type ElementProps<MenuItemValue extends string | number, MenuGroupValue extends string | number> = {
|
||||
type ElementProps<MenuItemValue extends string, MenuGroupValue extends string> = {
|
||||
items: MenuConfig<MenuItemValue, MenuGroupValue>;
|
||||
onItemSelected?: () => void;
|
||||
|
||||
@@ -23,11 +23,6 @@ type StyleProps = {
|
||||
withStickyLayout?: boolean;
|
||||
};
|
||||
|
||||
export type ComboboxMenuProps<
|
||||
MenuItemValue extends string | number,
|
||||
MenuGroupValue extends string | number
|
||||
> = ElementProps<MenuItemValue, MenuGroupValue> & StyleProps;
|
||||
|
||||
export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extends string>({
|
||||
items,
|
||||
onItemSelected,
|
||||
@@ -39,7 +34,7 @@ export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extend
|
||||
|
||||
className,
|
||||
withStickyLayout,
|
||||
}: ComboboxMenuProps<MenuItemValue, MenuGroupValue>) => {
|
||||
}: ElementProps<MenuItemValue, MenuGroupValue> & StyleProps) => {
|
||||
const [highlightedCommand, setHighlightedCommand] = useState<MenuItemValue>();
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
// const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
@@ -85,8 +80,6 @@ export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extend
|
||||
{group.items.map((item) => (
|
||||
<Fragment key={item.value}>
|
||||
<Styled.Item
|
||||
// value={item.value} // search by both value and textContent
|
||||
// value={[group.groupLabel, item.label, item.tag].filter(Boolean).join('|')} // exclude item.value from searchable terms (not guaranteed to be unique)
|
||||
value={[group.groupLabel, item.value, item.description, item.label, item.tag]
|
||||
.filter(Boolean)
|
||||
.join('|')}
|
||||
@@ -121,8 +114,6 @@ export const ComboboxMenu = <MenuItemValue extends string, MenuGroupValue extend
|
||||
item.subitems?.map((subitem) => (
|
||||
<Fragment key={subitem.value}>
|
||||
<Styled.Item
|
||||
// value={subitem.value} // search by both value and textContent
|
||||
// value={[group.groupLabel, item.label, subitem.label, subitem.tag].filter(Boolean).join('|')}
|
||||
value={[
|
||||
group.groupLabel,
|
||||
item.value,
|
||||
@@ -176,7 +167,6 @@ Styled.Command = styled(Command)<{ $withStickyLayout?: boolean }>`
|
||||
--comboboxMenu-item-highlighted-backgroundColor: var(--color-layer-3);
|
||||
--comboboxMenu-item-highlighted-textColor: var(--color-text-1);
|
||||
--comboboxMenu-item-backgroundColor: ;
|
||||
--comboboxMenu-item-gap: 0.5rem;
|
||||
|
||||
display: grid;
|
||||
align-content: start;
|
||||
@@ -275,17 +265,18 @@ Styled.List = styled(Command.List)<{ $withStickyLayout?: boolean }>`
|
||||
`;
|
||||
|
||||
Styled.Item = styled(Command.Item)`
|
||||
${layoutMixins.scrollSnapItem}
|
||||
${popoverMixins.item}
|
||||
--item-checked-backgroundColor: var(--comboboxMenu-item-checked-backgroundColor);
|
||||
--item-checked-textColor: var(--comboboxMenu-item-checked-textColor);
|
||||
--item-highlighted-textColor: var(--comboboxMenu-item-highlighted-textColor);
|
||||
--item-gap: var(--comboboxMenu-item-gap);
|
||||
|
||||
${layoutMixins.scrollSnapItem}
|
||||
|
||||
background-color: var(--comboboxMenu-backgroundColor, inherit);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
|
||||
&[aria-disabled='true'] {
|
||||
opacity: 0.75;
|
||||
|
||||
@@ -48,8 +48,6 @@ type StyleProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export type DialogProps = ElementProps & StyleProps;
|
||||
|
||||
const DialogPortal = ({
|
||||
withPortal,
|
||||
container,
|
||||
@@ -84,7 +82,7 @@ export const Dialog = ({
|
||||
hasHeaderBorder = false,
|
||||
children,
|
||||
className,
|
||||
}: DialogProps) => {
|
||||
}: ElementProps & StyleProps) => {
|
||||
const closeButtonRef = useRef<HTMLButtonElement>();
|
||||
|
||||
const showOverlay = ![DialogPlacement.Inline, DialogPlacement.FullScreen].includes(placement);
|
||||
|
||||
@@ -6,14 +6,11 @@ import {
|
||||
AddressConnectorIcon,
|
||||
ArrowIcon,
|
||||
Bar3Icon,
|
||||
BellIcon,
|
||||
BellStrokeIcon,
|
||||
BoxCloseIcon,
|
||||
CalculatorIcon,
|
||||
CaretIcon,
|
||||
CautionCircleStrokeIcon,
|
||||
CautionCircleIcon,
|
||||
ChatIcon,
|
||||
CheckIcon,
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
@@ -40,7 +37,6 @@ import {
|
||||
LockIcon,
|
||||
LogoShortIcon,
|
||||
MenuIcon,
|
||||
MigrateIcon,
|
||||
MintscanIcon,
|
||||
OrderbookIcon,
|
||||
OrderCanceledIcon,
|
||||
@@ -75,14 +71,11 @@ export enum IconName {
|
||||
AddressConnector = 'AddressConnector',
|
||||
Arrow = 'Arrow',
|
||||
Bar3 = 'Bar3',
|
||||
Bell = 'Bell',
|
||||
BellStroked = 'BellStroked',
|
||||
BoxClose = 'BoxClose',
|
||||
Calculator = 'Calculator',
|
||||
Caret = 'Caret',
|
||||
CautionCircle = 'CautionCircle',
|
||||
CautionCircleStroked = 'CautionCircleStroked',
|
||||
Chat = 'Chat',
|
||||
Check = 'Check',
|
||||
ChevronLeft = 'ChevronLeft',
|
||||
ChevronRight = 'ChevronRight',
|
||||
@@ -109,7 +102,6 @@ export enum IconName {
|
||||
Lock = 'Lock',
|
||||
LogoShort = 'LogoShort',
|
||||
Menu = 'Menu',
|
||||
Migrate = 'Migrate',
|
||||
Mintscan = 'Mintscan',
|
||||
Onboarding = 'Onboarding',
|
||||
Orderbook = 'OrderbookIcon',
|
||||
@@ -145,14 +137,11 @@ const icons = {
|
||||
[IconName.AddressConnector]: AddressConnectorIcon,
|
||||
[IconName.Arrow]: ArrowIcon,
|
||||
[IconName.Bar3]: Bar3Icon,
|
||||
[IconName.Bell]: BellIcon,
|
||||
[IconName.BellStroked]: BellStrokeIcon,
|
||||
[IconName.BoxClose]: BoxCloseIcon,
|
||||
[IconName.Calculator]: CalculatorIcon,
|
||||
[IconName.Caret]: CaretIcon,
|
||||
[IconName.CautionCircle]: CautionCircleIcon,
|
||||
[IconName.CautionCircleStroked]: CautionCircleStrokeIcon,
|
||||
[IconName.Chat]: ChatIcon,
|
||||
[IconName.Check]: CheckIcon,
|
||||
[IconName.ChevronLeft]: ChevronLeftIcon,
|
||||
[IconName.ChevronRight]: ChevronRightIcon,
|
||||
@@ -179,7 +168,6 @@ const icons = {
|
||||
[IconName.Lock]: LockIcon,
|
||||
[IconName.LogoShort]: LogoShortIcon,
|
||||
[IconName.Menu]: MenuIcon,
|
||||
[IconName.Migrate]: MigrateIcon,
|
||||
[IconName.Mintscan]: MintscanIcon,
|
||||
[IconName.Orderbook]: OrderbookIcon,
|
||||
[IconName.OrderCanceled]: OrderCanceledIcon,
|
||||
@@ -225,8 +213,7 @@ export const Icon = styled(
|
||||
iconComponent: Component = iconName && icons[iconName],
|
||||
className,
|
||||
...props
|
||||
}: ElementProps & StyleProps) =>
|
||||
Component ? <Component className={className} {...props} /> : null
|
||||
}: ElementProps & StyleProps) => (Component ? <Component className={className} {...props} /> : null)
|
||||
)`
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
|
||||
@@ -207,16 +207,6 @@ const InputStyle = css`
|
||||
color: var(--color-text-0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
// Input autofill Styles
|
||||
&:-webkit-autofill,
|
||||
&:-webkit-autofill:focus {
|
||||
transition: background-color 600000s 0s, color 600000s 0s;
|
||||
}
|
||||
|
||||
&[data-autocompleted] {
|
||||
background-color: var(--input-backgroundColor) !important;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.NumericFormat = styled(NumericFormat)`
|
||||
|
||||
@@ -12,7 +12,7 @@ type ElementProps = {
|
||||
};
|
||||
children: React.ReactNode;
|
||||
href?: string;
|
||||
onClick?: (e: MouseEvent) => void;
|
||||
onClick?: () => void;
|
||||
withIcon?: boolean;
|
||||
};
|
||||
|
||||
@@ -37,12 +37,12 @@ export const Link = forwardRef<HTMLAnchorElement, ElementProps & StyleProps>(
|
||||
ref={ref}
|
||||
className={className}
|
||||
href={href}
|
||||
onClick={(e: MouseEvent) => {
|
||||
onClick={() => {
|
||||
if (analyticsConfig) {
|
||||
console.log(analyticsConfig);
|
||||
}
|
||||
|
||||
onClick?.(e);
|
||||
onClick?.();
|
||||
}}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
|
||||
@@ -525,9 +525,6 @@ Styled.NavItem = styled(NavItem)<{ orientation: 'horizontal' | 'vertical' }>`
|
||||
`;
|
||||
|
||||
Styled.Icon = styled(Icon)`
|
||||
height: 0.75em;
|
||||
transition: rotate 0.3s var(--ease-out-expo);
|
||||
|
||||
${Styled.List}[data-orientation="menu"] & {
|
||||
rotate: -0.25turn;
|
||||
}
|
||||
|
||||
@@ -8,11 +8,9 @@ import { layoutMixins } from '@/styles/layoutMixins';
|
||||
type PanelProps = {
|
||||
slotHeaderContent?: string;
|
||||
slotHeader?: React.ReactNode;
|
||||
slotRight?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
href?: string;
|
||||
onHeaderClick?: () => void;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
type PanelStyleProps = {
|
||||
@@ -23,70 +21,62 @@ type PanelStyleProps = {
|
||||
export const Panel = ({
|
||||
slotHeaderContent,
|
||||
slotHeader,
|
||||
slotRight,
|
||||
children,
|
||||
href,
|
||||
onHeaderClick,
|
||||
onClick,
|
||||
hasSeparator,
|
||||
className,
|
||||
}: PanelProps & PanelStyleProps) => (
|
||||
<Styled.Panel onClick={onClick}>
|
||||
<Styled.Left>
|
||||
{href ? (
|
||||
<Link to={href}>
|
||||
{slotHeader ? (
|
||||
slotHeader
|
||||
) : (
|
||||
<Styled.Header role="button" onClick={onHeaderClick} hasSeparator={hasSeparator}>
|
||||
{slotHeaderContent}
|
||||
<Styled.Icon iconName={IconName.ChevronRight} />
|
||||
</Styled.Header>
|
||||
)}
|
||||
</Link>
|
||||
) : slotHeader ? (
|
||||
slotHeader
|
||||
) : (
|
||||
slotHeaderContent && (
|
||||
<Styled.Panel>
|
||||
{href ? (
|
||||
<Link to={href}>
|
||||
{slotHeader ? (
|
||||
slotHeader
|
||||
) : (
|
||||
<Styled.Header role="button" onClick={onHeaderClick} hasSeparator={hasSeparator}>
|
||||
{slotHeaderContent}
|
||||
<Styled.Icon iconName={IconName.ChevronRight} />
|
||||
</Styled.Header>
|
||||
)
|
||||
)}
|
||||
<Styled.Content className={className}>{children}</Styled.Content>
|
||||
</Styled.Left>
|
||||
{slotRight}
|
||||
)}
|
||||
</Link>
|
||||
) : slotHeader ? (
|
||||
slotHeader
|
||||
) : (
|
||||
slotHeaderContent && (
|
||||
<Styled.Header role="button" onClick={onHeaderClick} hasSeparator={hasSeparator}>
|
||||
{slotHeaderContent}
|
||||
</Styled.Header>
|
||||
)
|
||||
)}
|
||||
<Styled.Content className={className}>{children}</Styled.Content>
|
||||
</Styled.Panel>
|
||||
);
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Panel = styled.section`
|
||||
${layoutMixins.row}
|
||||
${layoutMixins.flexColumn}
|
||||
|
||||
background-color: var(--color-layer-3);
|
||||
border-radius: 0.875rem;
|
||||
`;
|
||||
|
||||
Styled.Left = styled.div`
|
||||
${layoutMixins.flexColumn}
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
Styled.Header = styled.header<{ hasSeparator?: boolean }>`
|
||||
${layoutMixins.spacedRow}
|
||||
padding: 1rem 1rem;
|
||||
|
||||
padding: 0.875rem 1rem 0.625rem;
|
||||
font-size: 0.875rem;
|
||||
|
||||
${({ hasSeparator }) =>
|
||||
hasSeparator &&
|
||||
css`
|
||||
padding-bottom: 0.625rem;
|
||||
box-shadow: 0 var(--border-width) var(--border-color);
|
||||
`}
|
||||
`;
|
||||
|
||||
Styled.Icon = styled(Icon)`
|
||||
color: var(--color-text-0);
|
||||
|
||||
font-size: 0.625rem;
|
||||
`;
|
||||
|
||||
@@ -94,5 +84,6 @@ Styled.Content = styled.div`
|
||||
${layoutMixins.scrollArea}
|
||||
${layoutMixins.stickyArea0}
|
||||
--stickyArea0-background: transparent;
|
||||
padding: 1rem 1rem;
|
||||
|
||||
padding: 0.5rem 1rem;
|
||||
`;
|
||||
|
||||
@@ -24,7 +24,7 @@ export const MarketTableCell = ({
|
||||
slotLeft={
|
||||
<>
|
||||
{showFavorite && <Icon iconName={IconName.Star} />}
|
||||
<Styled.AssetIcon symbol={asset?.id} />
|
||||
<Styled.AssetIcon symbol={asset?.symbol} />
|
||||
</>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -2,13 +2,11 @@ import type { ReactNode } from 'react';
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
import { Content, Portal, Provider, Root, Trigger, Arrow } from '@radix-ui/react-tooltip';
|
||||
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { tooltipStrings } from '@/constants/tooltips';
|
||||
|
||||
import { useStringGetter, useURLConfigs } from '@/hooks';
|
||||
import { useStringGetter } from '@/hooks';
|
||||
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
import { Link } from '@/components/Link';
|
||||
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import { popoverMixins } from '@/styles/popoverMixins';
|
||||
@@ -40,24 +38,20 @@ export const WithTooltip = ({
|
||||
slotTooltip,
|
||||
}: ElementProps & StyleProps) => {
|
||||
const stringGetter = useStringGetter();
|
||||
const urlConfigs = useURLConfigs();
|
||||
|
||||
const getTooltipStrings = tooltip && tooltipStrings[tooltip];
|
||||
if (!getTooltipStrings && !tooltipString && !slotTooltip) return <>{children}</>;
|
||||
|
||||
let tooltipTitle;
|
||||
let tooltipBody;
|
||||
let tooltipLearnMore;
|
||||
|
||||
if (getTooltipStrings) {
|
||||
const { title, body, learnMoreLink } = getTooltipStrings({
|
||||
const { title, body } = getTooltipStrings({
|
||||
stringGetter,
|
||||
stringParams,
|
||||
urlConfigs,
|
||||
});
|
||||
tooltipTitle = title;
|
||||
tooltipBody = body;
|
||||
tooltipLearnMore = learnMoreLink;
|
||||
} else {
|
||||
tooltipBody = tooltipString;
|
||||
}
|
||||
@@ -78,13 +72,6 @@ export const WithTooltip = ({
|
||||
<dl>
|
||||
{tooltipTitle && <dt>{tooltipTitle}</dt>}
|
||||
{tooltipBody && <dd>{tooltipBody}</dd>}
|
||||
{tooltipLearnMore && (
|
||||
<dd>
|
||||
<Styled.LearnMore href={tooltipLearnMore}>
|
||||
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
|
||||
</Styled.LearnMore>
|
||||
</dd>
|
||||
)}
|
||||
<Styled.Arrow />
|
||||
</dl>
|
||||
)}
|
||||
@@ -155,7 +142,3 @@ Styled.Arrow = styled(Arrow)`
|
||||
Styled.Icon = styled(Icon)`
|
||||
color: var(--color-text-0);
|
||||
`;
|
||||
|
||||
Styled.LearnMore = styled(Link)`
|
||||
--link-color: var(--color-accent);
|
||||
`;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Abacus, { kollections } from '@dydxprotocol/v4-abacus';
|
||||
import Abacus, { kollections } from '@dydxprotocol/abacus';
|
||||
import { OrderSide } from '@dydxprotocol/v4-client-js';
|
||||
import { PositionSide, TradeTypes } from './trade';
|
||||
import { STRING_KEYS } from './localization';
|
||||
@@ -6,7 +6,6 @@ import { STRING_KEYS } from './localization';
|
||||
export type Nullable<T> = T | null | undefined;
|
||||
|
||||
// ------ V4 Protocols ------ //
|
||||
export const AbacusAppConfig = Abacus.exchange.dydx.abacus.state.manager.AppConfigs;
|
||||
export const IOImplementations = Abacus.exchange.dydx.abacus.utils.IOImplementations;
|
||||
export const UIImplementations = Abacus.exchange.dydx.abacus.utils.UIImplementations;
|
||||
export type AbacusDYDXChainTransactionsProtocol = Omit<
|
||||
@@ -45,7 +44,6 @@ export type AbacusTrackingProtocol = Omit<
|
||||
Abacus.exchange.dydx.abacus.protocols.TrackingProtocol,
|
||||
'__doNotUseOrImplementIt'
|
||||
>;
|
||||
|
||||
export type FileLocation = Abacus.exchange.dydx.abacus.protocols.FileLocation;
|
||||
export type ThreadingType = Abacus.exchange.dydx.abacus.protocols.ThreadingType;
|
||||
export const CoroutineTimer = Abacus.exchange.dydx.abacus.utils.CoroutineTimer;
|
||||
@@ -59,22 +57,27 @@ export const TransactionType = Abacus.exchange.dydx.abacus.protocols.Transaction
|
||||
const transactionTypes = [...TransactionType.values()] as const;
|
||||
export type TransactionTypes = (typeof transactionTypes)[number];
|
||||
|
||||
export type NetworkConfig = {
|
||||
chainId: string;
|
||||
indexerUrl: string;
|
||||
indexerSocketUrl: string;
|
||||
validatorUrl: string;
|
||||
faucetUrl?: string | null;
|
||||
};
|
||||
|
||||
export type ConnectNetworkEvent = CustomEvent<Partial<NetworkConfig>>;
|
||||
|
||||
// ------ State ------
|
||||
export type AbacusApiState = Abacus.exchange.dydx.abacus.state.manager.ApiState;
|
||||
export const AbacusApiStatus = Abacus.exchange.dydx.abacus.state.manager.ApiStatus;
|
||||
export type AbacusApiState = Abacus.exchange.dydx.abacus.state.app.ApiState;
|
||||
export const AbacusApiStatus = Abacus.exchange.dydx.abacus.state.app.ApiStatus;
|
||||
const abacusApiStatuses = [...AbacusApiStatus.values()];
|
||||
export type AbacusApiStatuses = (typeof abacusApiStatuses)[number];
|
||||
export const Changes = Abacus.exchange.dydx.abacus.state.changes.Changes;
|
||||
export type PerpetualStateChanges = Abacus.exchange.dydx.abacus.state.changes.StateChanges;
|
||||
export const AsyncAbacusStateManager =
|
||||
Abacus.exchange.dydx.abacus.state.manager.AsyncAbacusStateManager;
|
||||
|
||||
// ------ Parsing Errors ------ //
|
||||
export type ParsingError = Abacus.exchange.dydx.abacus.responses.ParsingError;
|
||||
export type ParsingErrors = kollections.List<ParsingError>;
|
||||
export const ParsingErrorType = Abacus.exchange.dydx.abacus.responses.ParsingErrorType;
|
||||
const parsingErrorTypes = [...ParsingErrorType.values()] as const;
|
||||
export type ParsingErrorTypes = (typeof parsingErrorTypes)[number];
|
||||
|
||||
// ------ Perpetuals/Markets ------ //
|
||||
export type PerpetualState = Abacus.exchange.dydx.abacus.output.PerpetualState;
|
||||
@@ -118,7 +121,6 @@ export const InputSelectionOption = Abacus.exchange.dydx.abacus.output.input.Sel
|
||||
|
||||
// ------ 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 SubaccountPosition = Abacus.exchange.dydx.abacus.output.SubaccountPosition;
|
||||
export type SubaccountOrder = Abacus.exchange.dydx.abacus.output.SubaccountOrder;
|
||||
@@ -135,7 +137,7 @@ export type SubaccountTransfers = Abacus.exchange.dydx.abacus.output.SubaccountT
|
||||
// ------ Historical PnL ------ //
|
||||
export type SubAccountHistoricalPNL = Abacus.exchange.dydx.abacus.output.SubaccountHistoricalPNL;
|
||||
export type SubAccountHistoricalPNLs = Abacus.exchange.dydx.abacus.output.SubaccountHistoricalPNL[];
|
||||
export const HistoricalPnlPeriod = Abacus.exchange.dydx.abacus.protocols.HistoricalPnlPeriod;
|
||||
export const HistoricalPnlPeriod = Abacus.exchange.dydx.abacus.state.app.HistoricalPnlPeriod;
|
||||
const historicalPnlPeriod = [...HistoricalPnlPeriod.values()] as const;
|
||||
export type HistoricalPnlPeriods = (typeof historicalPnlPeriod)[number];
|
||||
|
||||
@@ -175,14 +177,16 @@ export const AbacusOrderSide = Abacus.exchange.dydx.abacus.output.input.OrderSid
|
||||
export const AbacusPositionSide = 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 =
|
||||
Abacus.exchange.dydx.abacus.state.manager.HumanReadablePlaceOrderPayload;
|
||||
export type 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 ------ //
|
||||
export const AbacusHelper = Abacus.exchange.dydx.abacus.utils.AbacusHelper;
|
||||
@@ -194,12 +198,6 @@ export type RiskLevels = (typeof riskLevels)[number];
|
||||
// ------ Notifications ------ //
|
||||
export type AbacusNotification = Abacus.exchange.dydx.abacus.output.Notification;
|
||||
|
||||
// ------ Restrictions ------ //
|
||||
export type UsageRestriction = Abacus.exchange.dydx.abacus.output.UsageRestriction;
|
||||
export const RestrictionType = Abacus.exchange.dydx.abacus.output.Restriction;
|
||||
const restrictionTypes = [...RestrictionType.values()] as const;
|
||||
export type RestrictionTypes = (typeof restrictionTypes)[number];
|
||||
|
||||
// ------ Enum Conversions ------ //
|
||||
type IfEquals<X, Y, A, B> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2
|
||||
? A
|
||||
@@ -257,20 +255,3 @@ export const TRADE_TYPES: Record<
|
||||
[AbacusOrderType.liquidation.name]: null,
|
||||
[AbacusOrderType.trailingStop.name]: null,
|
||||
};
|
||||
|
||||
// Custom types involving Abacus
|
||||
|
||||
export type NetworkConfig = Partial<{
|
||||
indexerUrl: Nullable<string>;
|
||||
websocketUrl: Nullable<string>;
|
||||
validatorUrl: Nullable<string>;
|
||||
chainId: Nullable<string>;
|
||||
faucetUrl: Nullable<string>;
|
||||
USDC_DENOM: Nullable<string>;
|
||||
USDC_DECIMALS: Nullable<number>;
|
||||
USDC_GAS_DENOM: Nullable<string>;
|
||||
CHAINTOKEN_DENOM: Nullable<string>;
|
||||
CHAINTOKEN_DECIMALS: Nullable<number>;
|
||||
}>;
|
||||
|
||||
export type ConnectNetworkEvent = CustomEvent<Partial<NetworkConfig>>;
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
import type { DydxAddress, EvmAddress } from './wallets';
|
||||
|
||||
/**
|
||||
* OnboardingSteps
|
||||
* 1. Choose between 3 options
|
||||
* a. Ethereum EOA (current)
|
||||
* b. Keplr or Other Cosmos (future)
|
||||
* c. Social (future)
|
||||
* 2. Key derivation
|
||||
* a. If wallet has no dYdX Chain transactions and not on whitelist, sign twice (future)
|
||||
* i. Success
|
||||
* ii. Signatures don't match error (Wallet is non-deterministic)
|
||||
* b. Success
|
||||
* 3. Post Registration items (Only on first onboarding)
|
||||
* a. Acknowledge Terms
|
||||
* b. DepositFunds
|
||||
*/
|
||||
export enum OnboardingSteps {
|
||||
ChooseWallet = 'ChooseWallet',
|
||||
KeyDerivation = 'KeyDerivation',
|
||||
@@ -36,12 +49,14 @@ export enum EvmDerivedAccountStatus {
|
||||
Derived,
|
||||
}
|
||||
|
||||
import type { DydxAddress, EvmAddress } from './wallets';
|
||||
|
||||
export type EvmDerivedAddresses = {
|
||||
version?: string;
|
||||
[EvmAddress: EvmAddress]: {
|
||||
encryptedSignature?: string;
|
||||
dydxAddress?: DydxAddress;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const AMOUNT_RESERVED_FOR_GAS_USDC = 0.1;
|
||||
export const AMOUNT_RESERVED_FOR_GAS_USDC = 100_000;
|
||||
@@ -71,7 +71,6 @@ export enum AnalyticsEvent {
|
||||
|
||||
// Transfers
|
||||
TransferFaucet = 'TransferFaucet',
|
||||
TransferFaucetConfirmed = 'TransferFaucetConfirmed',
|
||||
TransferDeposit = 'TransferDeposit',
|
||||
TransferWithdraw = 'TransferWithdraw',
|
||||
|
||||
@@ -79,7 +78,6 @@ export enum AnalyticsEvent {
|
||||
TradeOrderTypeSelected = 'TradeOrderTypeSelected',
|
||||
TradePlaceOrder = 'TradePlaceOrder',
|
||||
TradePlaceOrderConfirmed = 'TradePlaceOrderConfirmed',
|
||||
TradeCancelOrder = 'TradeCancelOrder',
|
||||
TradeCancelOrderConfirmed = 'TradeCancelOrderConfirmed',
|
||||
}
|
||||
|
||||
@@ -135,13 +133,6 @@ export type AnalyticsEventData<T extends AnalyticsEvent> =
|
||||
: // Transfers
|
||||
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.TransferWithdraw
|
||||
@@ -160,15 +151,13 @@ export type AnalyticsEventData<T extends AnalyticsEvent> =
|
||||
/** roundtrip time between user placing an order and confirmation from indexer (client → validator → indexer → client) */
|
||||
roundtripMs: number;
|
||||
/** URL/IP of node the order was sent to */
|
||||
validatorUrl: string;
|
||||
validator: string;
|
||||
}
|
||||
: T extends AnalyticsEvent.TradeCancelOrder
|
||||
? {}
|
||||
: T extends AnalyticsEvent.TradeCancelOrderConfirmed
|
||||
? {
|
||||
/** roundtrip time between user canceling an order and confirmation from indexer (client → validator → indexer → client) */
|
||||
roundtripMs: number;
|
||||
/** URL/IP of node the order was sent to */
|
||||
validatorUrl: string;
|
||||
validator: string;
|
||||
}
|
||||
: never;
|
||||
|
||||
@@ -5,15 +5,11 @@ export enum DialogTypes {
|
||||
ExchangeOffline = 'ExchangeOffline',
|
||||
FillDetails = 'FillDetails',
|
||||
Help = 'Help',
|
||||
ExternalNavKeplr = 'ExternalNavKeplr',
|
||||
MnemonicExport = 'MnemonicExport',
|
||||
MobileSignIn = 'MobileSignIn',
|
||||
Onboarding = 'Onboarding',
|
||||
OrderDetails = 'OrderDetails',
|
||||
RateLimit = 'RateLimit',
|
||||
Receive = 'Receive',
|
||||
RestrictedGeo = 'RestrictedGeo',
|
||||
RestrictedWallet = 'RestrictedWallet',
|
||||
Trade = 'Trade',
|
||||
Transfer = 'Transfer',
|
||||
Withdraw = 'Withdraw',
|
||||
|
||||
@@ -24,6 +24,6 @@ export enum LocalStorageKey {
|
||||
}
|
||||
|
||||
export const LOCAL_STORAGE_VERSIONS = {
|
||||
[LocalStorageKey.EvmDerivedAddresses]: 'v2',
|
||||
[LocalStorageKey.EvmDerivedAddresses]: 'v1',
|
||||
// TODO: version all localStorage keys
|
||||
};
|
||||
|
||||
@@ -8,8 +8,6 @@ import {
|
||||
WARNINGS_STRING_KEYS,
|
||||
} from '@dydxprotocol/v4-localization';
|
||||
|
||||
import { type LinksConfigs } from '@/hooks/useURLConfigs';
|
||||
|
||||
export { TOOLTIP_STRING_KEYS } from '@dydxprotocol/v4-localization';
|
||||
|
||||
export enum SupportedLocales {
|
||||
@@ -81,14 +79,28 @@ export type TooltipStrings = {
|
||||
[key: string]: ({
|
||||
stringGetter,
|
||||
stringParams,
|
||||
urlConfigs,
|
||||
}: {
|
||||
stringGetter: StringGetterFunction;
|
||||
stringParams?: any;
|
||||
urlConfigs?: LinksConfigs;
|
||||
}) => {
|
||||
title: string;
|
||||
body: string;
|
||||
learnMoreLink?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export const ORDER_ERROR_CODE_MAP: { [key: number]: string } = {
|
||||
2000: STRING_KEYS['2000_FILL_OR_KILL_ORDER_COULD_NOT_BE_FULLY_FILLED'],
|
||||
2001: STRING_KEYS['2001_REDUCE_ONLY_WOULD_INCREASE_POSITION_SIZE'],
|
||||
2002: STRING_KEYS['2002_REDUCE_ONLY_WOULD_CHANGE_POSITION_SIDE'],
|
||||
2003: STRING_KEYS['2003_POST_ONLY_WOULD_CROSS_MAKER_ORDER'],
|
||||
3000: STRING_KEYS['3000_INVALID_ORDER_FLAGS'],
|
||||
3001: STRING_KEYS['3001_INVALID_STATEFUL_ORDER_GOOD_TIL_BLOCK_TIME'],
|
||||
3002: STRING_KEYS['3002_STATEFUL_ORDERS_CANNOT_REQUIRE_IMMEDIATE_EXECUTION'],
|
||||
3003: STRING_KEYS['3003_TIME_EXCEEDS_GOOD_TIL_BLOCK_TIME'],
|
||||
3004: STRING_KEYS['3004_GOOD_TIL_BLOCK_TIME_EXCEEDS_STATEFUL_ORDER_TIME_WINDOW'],
|
||||
3005: STRING_KEYS['3005_STATEFUL_ORDER_ALREADY_EXISTS'],
|
||||
3006: STRING_KEYS['3006_STATEFUL_ORDER_DOES_NOT_EXIST'],
|
||||
3007: STRING_KEYS['3007_STATEFUL_ORDER_COLLATERALIZATION_CHECK_FAILED'],
|
||||
3008: STRING_KEYS['3008_STATEFUL_ORDER_PREVIOUSLY_CANCELLED'],
|
||||
};
|
||||
|
||||
@@ -1,6 +1,36 @@
|
||||
import { Asset, PerpetualMarket } from '@/constants/abacus';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
|
||||
export interface Market {
|
||||
market: string;
|
||||
status: string;
|
||||
baseAsset: string;
|
||||
quoteAsset: string;
|
||||
stepSize: string;
|
||||
tickSize: string;
|
||||
oraclePrice: string;
|
||||
priceChange24H: string;
|
||||
nextFundingRate: string;
|
||||
nextFundingAt: string;
|
||||
minOrderSize: string;
|
||||
type: string;
|
||||
initialMarginFraction: string;
|
||||
maintenanceMarginFraction: string;
|
||||
volume24H: string;
|
||||
trades24H: string;
|
||||
openInterest: string;
|
||||
incrementalInitialMarginFraction: string;
|
||||
incrementalPositionSize: string;
|
||||
maxPositionSize: string;
|
||||
baselinePositionSize: string;
|
||||
assetResolution: string;
|
||||
syntheticAssetId: string;
|
||||
}
|
||||
|
||||
export type AllMarkets = {
|
||||
[market: string]: Market;
|
||||
};
|
||||
|
||||
export type MarketData = {
|
||||
asset: Asset;
|
||||
tickSizeDecimals: number;
|
||||
|
||||
@@ -21,7 +21,7 @@ export type MenuItem<MenuItemValue, MenuItemTypes = string> = {
|
||||
value: MenuItemValue;
|
||||
|
||||
slotBefore?: React.ReactNode;
|
||||
label: React.ReactNode;
|
||||
label: string;
|
||||
labelRight?: React.ReactNode;
|
||||
tag?: React.ReactNode;
|
||||
slotAfter?: React.ReactNode;
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
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;
|
||||
@@ -0,0 +1,29 @@
|
||||
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);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { StatusResponse } from '@0xsquid/sdk';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { StatusResponse } from '@0xsquid/sdk';
|
||||
|
||||
/** implemented in useNotificationTypes */
|
||||
export enum NotificationType {
|
||||
@@ -11,39 +12,44 @@ export enum NotificationComponentType {}
|
||||
export type NotificationId = string | number;
|
||||
|
||||
export type NotificationTypeConfig<
|
||||
NotificationIdType extends NotificationId = string,
|
||||
_NotificationId extends NotificationId = string,
|
||||
NotificationUpdateKey = any
|
||||
> = {
|
||||
type: NotificationType;
|
||||
|
||||
/** React hook to trigger notifications based on app state */
|
||||
useTrigger: (_: {
|
||||
trigger: (
|
||||
trigger: ({
|
||||
id,
|
||||
displayData,
|
||||
updateKey,
|
||||
isNew,
|
||||
}: {
|
||||
/** Unique ID for the triggered notification */
|
||||
id: NotificationIdType,
|
||||
id: _NotificationId;
|
||||
|
||||
/** Display data for the triggered notification */
|
||||
displayData: NotificationDisplayData,
|
||||
displayData: NotificationDisplayData;
|
||||
|
||||
/**
|
||||
* JSON-serializable key.
|
||||
* Re-triggers the notification if passed a different value from the last trigger() call (even from a previous browser session).
|
||||
* Suggested usage: data dependency array
|
||||
*/
|
||||
updateKey?: NotificationUpdateKey,
|
||||
updateKey?: NotificationUpdateKey;
|
||||
|
||||
/**
|
||||
* @param true (default): Notification initialized with status NotificationStatus.Triggered
|
||||
* @param false: Notification initialized with status NotificationStatus.Cleared
|
||||
*/
|
||||
isNew?: boolean
|
||||
) => void;
|
||||
isNew?: boolean;
|
||||
}) => void;
|
||||
|
||||
lastUpdated: number;
|
||||
}) => void;
|
||||
|
||||
/** Callback for notification action (Toast action button click, NotificationsMenu item click, or native push notification interaction) */
|
||||
useNotificationAction?: () => (id: NotificationIdType) => any;
|
||||
useNotificationAction?: () => (id: _NotificationId) => any;
|
||||
};
|
||||
|
||||
export enum NotificationStatus {
|
||||
@@ -65,10 +71,10 @@ export enum NotificationStatus {
|
||||
|
||||
/** Notification state. Serialized and cached into localStorage. */
|
||||
export type Notification<
|
||||
NotificationIdType extends NotificationId = string,
|
||||
_NotificationId extends NotificationId = string,
|
||||
NotificationUpdateKey = any
|
||||
> = {
|
||||
id: NotificationIdType;
|
||||
id: _NotificationId;
|
||||
type: NotificationType;
|
||||
status: NotificationStatus;
|
||||
timestamps: Partial<Record<NotificationStatus, number>>;
|
||||
@@ -79,15 +85,15 @@ export type Notifications = Record<NotificationId, Notification<any>>;
|
||||
|
||||
/** Notification display data derived from app state at runtime. */
|
||||
export type NotificationDisplayData = {
|
||||
icon?: React.ReactNode;
|
||||
icon?: React.ReactElement<any, 'svg'>;
|
||||
|
||||
title?: string;
|
||||
|
||||
description?: React.ReactNode;
|
||||
description?: ReactNode;
|
||||
|
||||
customContent?: React.ReactNode;
|
||||
customContent?: ReactNode;
|
||||
|
||||
customMenuContent?: React.ReactNode;
|
||||
customMenuContent?: ReactNode;
|
||||
|
||||
actionDescription?: string;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { getSeparator } from '@/lib/numbers';
|
||||
|
||||
export const USD_DECIMALS = 2;
|
||||
export const SMALL_USD_DECIMALS = 4;
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@ export enum AppRoute {
|
||||
Profile = '/profile',
|
||||
Alerts = '/alerts',
|
||||
Settings = '/settings',
|
||||
Terms = '/terms',
|
||||
Privacy = '/privacy',
|
||||
Rewards = '/DV4TNT',
|
||||
}
|
||||
|
||||
export enum PortfolioRoute {
|
||||
@@ -37,3 +36,7 @@ export const HISTORY_ROUTE = `${AppRoute.Portfolio}/${PortfolioRoute.History}/:s
|
||||
export const DEFAULT_TRADE_ROUTE = `${AppRoute.Trade}/${DEFAULT_MARKETID}`;
|
||||
export const SETTINGS_ROUTE = `${AppRoute.Settings}/*`;
|
||||
export const DEFAULT_DOCUMENT_TITLE = 'dYdX';
|
||||
|
||||
export enum ExternalLink {
|
||||
Foundation = 'https://dydx.foundation',
|
||||
}
|
||||
|
||||
@@ -5,10 +5,6 @@ export const tradeTooltips: TooltipStrings = {
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.ACCOUNT_LEVERAGE_TITLE }),
|
||||
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 }) => ({
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.BRACKET_ORDER_SL_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.BRACKET_ORDER_SL_BODY }),
|
||||
@@ -57,10 +53,10 @@ export const tradeTooltips: TooltipStrings = {
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.INDEX_PRICE_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.INDEX_PRICE_BODY }),
|
||||
}),
|
||||
'initial-margin-fraction': ({ stringGetter, urlConfigs }) => ({
|
||||
'initial-margin-fraction': ({ stringGetter }) => ({
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.INITIAL_MARGIN_FRACTION_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.INITIAL_MARGIN_FRACTION_BODY }),
|
||||
learnMoreLink: urlConfigs?.initialMarginFractionLearnMore,
|
||||
learnMoreLink: 'https://help.dydx.exchange/articles/5232637-maximum-position-sizes',
|
||||
}),
|
||||
'initial-stop': ({ stringGetter }) => ({
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.INITIAL_STOP_TITLE }),
|
||||
@@ -94,10 +90,6 @@ export const tradeTooltips: TooltipStrings = {
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.MAINTENANCE_MARGIN_FRACTION_TITLE }),
|
||||
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 }) => ({
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.MAX_WITHDRAW_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.MAX_WITHDRAW_BODY }),
|
||||
@@ -146,10 +138,10 @@ export const tradeTooltips: TooltipStrings = {
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.REALIZED_PNL_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.REALIZED_PNL_BODY }),
|
||||
}),
|
||||
'reduce-only': ({ stringGetter, urlConfigs }) => ({
|
||||
'reduce-only': ({ stringGetter }) => ({
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.REDUCE_ONLY_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.REDUCE_ONLY_BODY }),
|
||||
learnMoreLink: urlConfigs?.reduceOnlyLearnMore,
|
||||
learnMoreLink: 'https://help.dydx.exchange/articles/6345793-reduce-only-orders',
|
||||
}),
|
||||
spread: () => ({
|
||||
title: 'Spread',
|
||||
|
||||
@@ -27,7 +27,7 @@ export enum PositionSide {
|
||||
Short = 'SHORT',
|
||||
}
|
||||
|
||||
export const UNCOMMITTED_ORDER_TIMEOUT_MS = 10_000;
|
||||
export const UNCOMMITTED_ORDER_TIMEOUT = 10_000;
|
||||
|
||||
export const ORDER_SIDE_STRINGS = {
|
||||
[OrderSide.BUY]: STRING_KEYS.BUY,
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import type { ExternalProvider } from '@ethersproject/providers';
|
||||
import { type onboarding } from '@dydxprotocol/v4-client-js';
|
||||
import type { suggestChain } from 'graz';
|
||||
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
|
||||
@@ -24,14 +22,13 @@ import {
|
||||
|
||||
import { isMetaMask } from '@/lib/wallet/providers';
|
||||
|
||||
import { DydxNetwork, ENVIRONMENT_CONFIG_MAP } from './networks';
|
||||
|
||||
// Wallet connection types
|
||||
|
||||
export enum WalletConnectionType {
|
||||
CoinbaseWalletSdk = 'coinbaseWalletSdk',
|
||||
CosmosSigner = 'CosmosSigner',
|
||||
InjectedEip1193 = 'injectedEip1193',
|
||||
WalletConnect1 = 'walletConnect1',
|
||||
WalletConnect2 = 'walletConnect2',
|
||||
}
|
||||
|
||||
@@ -61,6 +58,10 @@ export const walletConnectionTypes: Record<WalletConnectionType, WalletConnectio
|
||||
name: 'injected EIP-1193 provider',
|
||||
wagmiConnectorId: 'injected',
|
||||
},
|
||||
[WalletConnectionType.WalletConnect1]: {
|
||||
name: 'WalletConnect 1.0',
|
||||
wagmiConnectorId: 'walletConnectLegacy',
|
||||
},
|
||||
[WalletConnectionType.WalletConnect2]: {
|
||||
name: 'WalletConnect 2.0',
|
||||
wagmiConnectorId: 'walletConnect',
|
||||
@@ -87,35 +88,19 @@ export enum WalletType {
|
||||
Rainbow = 'RAINBOW_WALLET',
|
||||
TokenPocket = 'TOKEN_POCKET',
|
||||
TrustWallet = 'TRUST_WALLET',
|
||||
WalletConnect = 'WALLETCONNECT',
|
||||
WalletConnect2 = 'WALLETCONNECT_2',
|
||||
// TestWallet = 'TEST_WALLET',
|
||||
OtherWallet = 'OTHER_WALLET',
|
||||
}
|
||||
|
||||
const WALLET_CONNECT_EXPLORER_RECOMMENDED_WALLETS = {
|
||||
Metamask: 'c57ca95b47569778a828d19178114f4db188b89b763c899ba0be274e97267d96',
|
||||
imToken: 'ef333840daf915aafdc4a004525502d6d49d77bd9c65e0642dbaefb3c2893bef',
|
||||
TokenPocket: '20459438007b75f4f4acb98bf29aa3b800550309646d375da5fd4aac6c2a2c66',
|
||||
Trust: '4622a2b2d6af1c9844944291e5e7351a6aa24cd7b23099efac1b2fd875da31a0',
|
||||
Rainbow: '1ae92b26df02f0abca6304df07debccd18262fdf5fe82daa81593582dac9a369',
|
||||
Zerion: 'ecc4036f814562b41a5268adc86270fba1365471402006302e70169465b7ac18',
|
||||
Ledger: '19177a98252e07ddfc9af2083ba8e07ef627cb6103467ffebb3f8f4205fd7927',
|
||||
Fireblocks: '5864e2ced7c293ed18ac35e0db085c09ed567d67346ccb6f58a0327a75137489',
|
||||
Uniswap: 'c03dfee351b6fcc421b4494ea33b9d4b92a984f87aa76d1663bb28705e95034a',
|
||||
Robinhood: '8837dd9413b1d9b585ee937d27a816590248386d9dbf59f5cd3422dbbb65683e',
|
||||
'1inch': 'c286eebc742a537cd1d6818363e9dc53b21759a1e8e5d9b263d0c03ec7703576',
|
||||
};
|
||||
|
||||
export const WALLET_CONNECT_EXPLORER_RECOMMENDED_IDS = Object.values(
|
||||
WALLET_CONNECT_EXPLORER_RECOMMENDED_WALLETS
|
||||
);
|
||||
|
||||
type WalletConfig = {
|
||||
type: WalletType;
|
||||
stringKey: string;
|
||||
icon: string;
|
||||
connectionTypes: WalletConnectionType[];
|
||||
matchesInjectedEip1193?: (provider: ExternalProvider & any) => boolean;
|
||||
walletconnect1Name?: string;
|
||||
walletconnect2Id?: string;
|
||||
};
|
||||
|
||||
@@ -128,6 +113,7 @@ export const wallets: Record<WalletType, WalletConfig> = {
|
||||
WalletConnectionType.InjectedEip1193,
|
||||
// WalletConnectionType.CoinbaseWalletSdk,
|
||||
WalletConnectionType.WalletConnect2,
|
||||
WalletConnectionType.WalletConnect1,
|
||||
],
|
||||
matchesInjectedEip1193: (provider) =>
|
||||
Object.entries(wallets).every(
|
||||
@@ -141,22 +127,30 @@ export const wallets: Record<WalletType, WalletConfig> = {
|
||||
type: WalletType.BitKeep,
|
||||
stringKey: STRING_KEYS.BITKEEP,
|
||||
icon: BitkeepIcon,
|
||||
connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
|
||||
connectionTypes: [
|
||||
WalletConnectionType.InjectedEip1193,
|
||||
WalletConnectionType.WalletConnect2,
|
||||
WalletConnectionType.WalletConnect1,
|
||||
],
|
||||
matchesInjectedEip1193: (provider) => provider.isBitKeep, // isBitKeepChrome, isBitEthereum
|
||||
walletconnect1Name: 'bitkeep',
|
||||
walletconnect2Id: '38f5d18bd8522c244bdd70cb4a68e0e718865155811c043f052fb9f1c51de662',
|
||||
},
|
||||
[WalletType.BitPie]: {
|
||||
type: WalletType.BitPie,
|
||||
stringKey: STRING_KEYS.BITPIE,
|
||||
icon: BitpieIcon,
|
||||
connectionTypes: [WalletConnectionType.InjectedEip1193],
|
||||
connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect1],
|
||||
matchesInjectedEip1193: (provider) => provider.isBitpie,
|
||||
walletconnect1Name: 'bitpie',
|
||||
},
|
||||
[WalletType.CloverWallet]: {
|
||||
type: WalletType.CloverWallet,
|
||||
stringKey: STRING_KEYS.CLOVER_WALLET,
|
||||
icon: CloverWalletIcon,
|
||||
connectionTypes: [WalletConnectionType.InjectedEip1193],
|
||||
connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect1],
|
||||
matchesInjectedEip1193: (provider) => provider.isClover,
|
||||
// walletconnect1Name: 'clover',
|
||||
},
|
||||
[WalletType.CoinbaseWallet]: {
|
||||
type: WalletType.CoinbaseWallet,
|
||||
@@ -165,78 +159,122 @@ export const wallets: Record<WalletType, WalletConfig> = {
|
||||
connectionTypes: [
|
||||
WalletConnectionType.CoinbaseWalletSdk,
|
||||
WalletConnectionType.InjectedEip1193,
|
||||
WalletConnectionType.WalletConnect2,
|
||||
WalletConnectionType.WalletConnect1,
|
||||
],
|
||||
matchesInjectedEip1193: (provider) => provider.isCoinbaseWallet, // provider.selectedProvider?.isCoinbaseWallet,
|
||||
walletconnect1Name: 'coinbase',
|
||||
},
|
||||
[WalletType.Coin98]: {
|
||||
type: WalletType.Coin98,
|
||||
stringKey: STRING_KEYS.COIN98,
|
||||
icon: Coin98Icon,
|
||||
connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
|
||||
connectionTypes: [
|
||||
WalletConnectionType.InjectedEip1193,
|
||||
WalletConnectionType.WalletConnect2,
|
||||
WalletConnectionType.WalletConnect1,
|
||||
],
|
||||
matchesInjectedEip1193: (provider) => provider.isCoin98,
|
||||
walletconnect1Name: 'coin98',
|
||||
walletconnect2Id: '2a3c89040ac3b723a1972a33a125b1db11e258a6975d3a61252cd64e6ea5ea01',
|
||||
},
|
||||
[WalletType.HuobiWallet]: {
|
||||
type: WalletType.HuobiWallet,
|
||||
stringKey: STRING_KEYS.HUOBI_WALLET,
|
||||
icon: HuobiIcon,
|
||||
connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
|
||||
connectionTypes: [
|
||||
WalletConnectionType.InjectedEip1193,
|
||||
WalletConnectionType.WalletConnect2,
|
||||
WalletConnectionType.WalletConnect1,
|
||||
],
|
||||
matchesInjectedEip1193: (provider) => provider.isHbWallet,
|
||||
walletconnect2Id: '797c615e2c556b610c048eb35535f212c0dd58de5d03e763120e90a7d1350a77',
|
||||
},
|
||||
[WalletType.ImToken]: {
|
||||
type: WalletType.ImToken,
|
||||
stringKey: STRING_KEYS.IMTOKEN,
|
||||
icon: ImTokenIcon,
|
||||
connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
|
||||
connectionTypes: [
|
||||
WalletConnectionType.InjectedEip1193,
|
||||
WalletConnectionType.WalletConnect2,
|
||||
WalletConnectionType.WalletConnect1,
|
||||
],
|
||||
matchesInjectedEip1193: (provider) => provider.isImToken,
|
||||
walletconnect2Id: WALLET_CONNECT_EXPLORER_RECOMMENDED_WALLETS.imToken,
|
||||
walletconnect1Name: 'imtoken',
|
||||
walletconnect2Id: 'ef333840daf915aafdc4a004525502d6d49d77bd9c65e0642dbaefb3c2893bef',
|
||||
},
|
||||
[WalletType.MathWallet]: {
|
||||
type: WalletType.MathWallet,
|
||||
stringKey: STRING_KEYS.MATH_WALLET,
|
||||
icon: MathWalletIcon,
|
||||
connectionTypes: [WalletConnectionType.InjectedEip1193],
|
||||
connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect1],
|
||||
matchesInjectedEip1193: (provider) => provider.isMathWallet,
|
||||
walletconnect1Name: 'math',
|
||||
walletconnect2Id: '7674bb4e353bf52886768a3ddc2a4562ce2f4191c80831291218ebd90f5f5e26',
|
||||
},
|
||||
[WalletType.MetaMask]: {
|
||||
type: WalletType.MetaMask,
|
||||
stringKey: STRING_KEYS.METAMASK,
|
||||
icon: MetaMaskIcon,
|
||||
connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
|
||||
connectionTypes: [
|
||||
WalletConnectionType.InjectedEip1193,
|
||||
WalletConnectionType.WalletConnect2,
|
||||
WalletConnectionType.WalletConnect1,
|
||||
],
|
||||
matchesInjectedEip1193: isMetaMask,
|
||||
walletconnect2Id: WALLET_CONNECT_EXPLORER_RECOMMENDED_WALLETS.Metamask,
|
||||
walletconnect1Name: 'metamask',
|
||||
walletconnect2Id: 'c57ca95b47569778a828d19178114f4db188b89b763c899ba0be274e97267d96',
|
||||
},
|
||||
[WalletType.Rainbow]: {
|
||||
type: WalletType.Rainbow,
|
||||
stringKey: STRING_KEYS.RAINBOW_WALLET,
|
||||
icon: RainbowIcon,
|
||||
connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
|
||||
connectionTypes: [
|
||||
WalletConnectionType.InjectedEip1193,
|
||||
WalletConnectionType.WalletConnect2,
|
||||
WalletConnectionType.WalletConnect1,
|
||||
],
|
||||
matchesInjectedEip1193: (provider) => provider.isRainbowWallet,
|
||||
walletconnect2Id: WALLET_CONNECT_EXPLORER_RECOMMENDED_WALLETS.Rainbow,
|
||||
walletconnect1Name: 'rainbow',
|
||||
walletconnect2Id: '1ae92b26df02f0abca6304df07debccd18262fdf5fe82daa81593582dac9a369',
|
||||
},
|
||||
[WalletType.TokenPocket]: {
|
||||
type: WalletType.TokenPocket,
|
||||
stringKey: STRING_KEYS.TOKEN_POCKET,
|
||||
icon: TokenPocketIcon,
|
||||
connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
|
||||
connectionTypes: [
|
||||
WalletConnectionType.InjectedEip1193,
|
||||
WalletConnectionType.WalletConnect2,
|
||||
WalletConnectionType.WalletConnect1,
|
||||
],
|
||||
matchesInjectedEip1193: (provider) => provider.isTokenPocket,
|
||||
walletconnect2Id: WALLET_CONNECT_EXPLORER_RECOMMENDED_WALLETS.TokenPocket,
|
||||
walletconnect1Name: 'tokenpocket',
|
||||
walletconnect2Id: '20459438007b75f4f4acb98bf29aa3b800550309646d375da5fd4aac6c2a2c66',
|
||||
},
|
||||
[WalletType.TrustWallet]: {
|
||||
type: WalletType.TrustWallet,
|
||||
stringKey: STRING_KEYS.TRUST_WALLET,
|
||||
icon: TrustWalletIcon,
|
||||
connectionTypes: [WalletConnectionType.InjectedEip1193, WalletConnectionType.WalletConnect2],
|
||||
connectionTypes: [
|
||||
WalletConnectionType.InjectedEip1193,
|
||||
WalletConnectionType.WalletConnect2,
|
||||
WalletConnectionType.WalletConnect1,
|
||||
],
|
||||
matchesInjectedEip1193: (provider) => provider.isTrust,
|
||||
walletconnect2Id: WALLET_CONNECT_EXPLORER_RECOMMENDED_WALLETS.Trust,
|
||||
walletconnect1Name: 'trust',
|
||||
walletconnect2Id: '4622a2b2d6af1c9844944291e5e7351a6aa24cd7b23099efac1b2fd875da31a0',
|
||||
},
|
||||
[WalletType.WalletConnect2]: {
|
||||
type: WalletType.WalletConnect2,
|
||||
type: WalletType.WalletConnect,
|
||||
stringKey: STRING_KEYS.WALLET_CONNECT_2,
|
||||
icon: WalletConnectIcon,
|
||||
connectionTypes: [WalletConnectionType.WalletConnect2],
|
||||
},
|
||||
[WalletType.WalletConnect]: {
|
||||
type: WalletType.WalletConnect,
|
||||
stringKey: STRING_KEYS.WALLET_CONNECT,
|
||||
icon: WalletConnectIcon,
|
||||
connectionTypes: [WalletConnectionType.WalletConnect1],
|
||||
},
|
||||
[WalletType.Keplr]: {
|
||||
type: WalletType.Keplr,
|
||||
stringKey: STRING_KEYS.KEPLR,
|
||||
@@ -275,24 +313,27 @@ export type WalletConnection = {
|
||||
};
|
||||
|
||||
// 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";
|
||||
|
||||
/**
|
||||
* @description typed data to sign for dYdX Chain onboarding
|
||||
* @description typed data to sign for v4 onboarding
|
||||
*/
|
||||
export const getSignTypedData = (selectedNetwork: DydxNetwork) =>
|
||||
({
|
||||
primaryType: 'dYdX',
|
||||
domain: {
|
||||
name: ENVIRONMENT_CONFIG_MAP[selectedNetwork].wallets.signTypedDataDomainName,
|
||||
},
|
||||
types: {
|
||||
dYdX: [{ name: 'action', type: 'string' }],
|
||||
},
|
||||
message: {
|
||||
action: ENVIRONMENT_CONFIG_MAP[selectedNetwork].wallets.signTypedDataAction,
|
||||
},
|
||||
} as const);
|
||||
export const SIGN_TYPED_DATA = {
|
||||
primaryType: 'dYdX',
|
||||
domain: {
|
||||
name: 'dYdX V4',
|
||||
},
|
||||
types: {
|
||||
dYdX: [{ name: 'action', type: 'string' }],
|
||||
},
|
||||
message: {
|
||||
action: 'dYdX V4 Onboarding',
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type PrivateInformation = ReturnType<typeof onboarding.deriveHDKeyFromEthereumSignature>;
|
||||
|
||||
@@ -300,12 +341,12 @@ export type EvmAddress = `0x${string}`;
|
||||
export type DydxAddress = `dydx${string}`;
|
||||
|
||||
export const DYDX_CHAIN_INFO: Parameters<typeof suggestChain>[0] = {
|
||||
rpc: 'https://dydx-testnet-archive.allthatnode.com:26657',
|
||||
rest: 'https://dydx-testnet-archive.allthatnode.com:1317',
|
||||
chainId: 'dydx-testnet-4',
|
||||
rpc: '13.59.4.93:26657',
|
||||
rest: '13.59.4.93:1317',
|
||||
chainId: 'dydx-testnet-2',
|
||||
chainName: 'dYdX Public Testnet',
|
||||
chainSymbolImageUrl:
|
||||
'https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/dydx-testnet-4/chain.png',
|
||||
'https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/dydx-testnet-2/chain.png',
|
||||
bech32Config: {
|
||||
bech32PrefixAccPub: 'dydxpub',
|
||||
bech32PrefixValPub: 'dydxvaloperpub',
|
||||
@@ -319,38 +360,37 @@ export const DYDX_CHAIN_INFO: Parameters<typeof suggestChain>[0] = {
|
||||
},
|
||||
stakeCurrency: {
|
||||
coinDenom: 'DV4TNT',
|
||||
coinDecimals: 18,
|
||||
coinMinimalDenom: 'adv4tnt',
|
||||
coinDecimals: 6,
|
||||
coinMinimalDenom: 'dv4tnt',
|
||||
},
|
||||
currencies: [
|
||||
{
|
||||
coinDenom: 'DV4TNT',
|
||||
coinDecimals: 18,
|
||||
coinMinimalDenom: 'adv4tnt',
|
||||
coinDecimals: 6,
|
||||
coinMinimalDenom: 'dv4tnt',
|
||||
},
|
||||
{
|
||||
coinDenom: 'USDC',
|
||||
coinDecimals: 6,
|
||||
coinMinimalDenom: 'ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5',
|
||||
coinDecimals: 6,
|
||||
},
|
||||
],
|
||||
feeCurrencies: [
|
||||
{
|
||||
coinDenom: 'DV4TNT',
|
||||
coinDecimals: 18,
|
||||
coinMinimalDenom: 'adv4tnt',
|
||||
},
|
||||
{
|
||||
coinDenom: 'USDC',
|
||||
coinDecimals: 6,
|
||||
coinMinimalDenom: 'ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5',
|
||||
coinMinimalDenom: 'dv4tnt',
|
||||
},
|
||||
],
|
||||
features: [],
|
||||
};
|
||||
|
||||
// TODO: export this type from abacus instead
|
||||
export enum DydxChainAsset {
|
||||
USDC = 'usdc',
|
||||
CHAINTOKEN = 'chain',
|
||||
USDC = 'USDC',
|
||||
DYDX = 'Dv4TNT',
|
||||
}
|
||||
|
||||
export const DYDX_CHAIN_ASSET_COIN_DENOM: Record<DydxChainAsset, string> = {
|
||||
[DydxChainAsset.USDC]: USDC_DENOM,
|
||||
[DydxChainAsset.DYDX]: DYDX_DENOM,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useApiState } from './useApiState';
|
||||
import { useBreakpoints } from './useBreakpoints';
|
||||
import { useTokenConfigs } from './useTokenConfigs';
|
||||
import { useCommandMenu } from './useCommandMenu';
|
||||
import { useCurrentMarketId } from './useCurrentMarketId';
|
||||
import { useDebounce } from './useDebounce';
|
||||
@@ -17,17 +16,14 @@ import { useLocalStorage } from './useLocalStorage';
|
||||
import { useNow } from './useNow';
|
||||
import { useOnClickOutside } from './useOnClickOutside';
|
||||
import { usePageTitlePriceUpdates } from './usePageTitlePriceUpdates';
|
||||
import { useRestrictions } from './useRestrictions';
|
||||
import { useShouldShowFooter } from './useShouldShowFooter';
|
||||
import { useSelectedNetwork } from './useSelectedNetwork';
|
||||
import { useStringGetter } from './useStringGetter';
|
||||
import { useSubaccount } from './useSubaccount';
|
||||
import { useURLConfigs } from './useURLConfigs';
|
||||
|
||||
export {
|
||||
useApiState,
|
||||
useBreakpoints,
|
||||
useTokenConfigs,
|
||||
useCommandMenu,
|
||||
useCurrentMarketId,
|
||||
useDebounce,
|
||||
@@ -44,10 +40,8 @@ export {
|
||||
useNow,
|
||||
useOnClickOutside,
|
||||
usePageTitlePriceUpdates,
|
||||
useRestrictions,
|
||||
useShouldShowFooter,
|
||||
useSelectedNetwork,
|
||||
useStringGetter,
|
||||
useSubaccount,
|
||||
useURLConfigs,
|
||||
};
|
||||
|
||||
@@ -33,7 +33,7 @@ export const useTradingView = ({
|
||||
const marketIds = useSelector(getMarketIds, shallowEqual);
|
||||
const selectedLocale = useSelector(getSelectedLocale);
|
||||
const selectedNetwork = useSelector(getSelectedNetwork);
|
||||
const { getCandlesForDatafeed, isConnected: isClientConnected } = useDydxClient();
|
||||
const { getCandlesForDatafeed } = useDydxClient();
|
||||
|
||||
const [savedTvChartConfig, setTvChartConfig] = useLocalStorage<object | undefined>({
|
||||
key: LocalStorageKey.TradingViewChartConfig,
|
||||
@@ -44,7 +44,7 @@ export const useTradingView = ({
|
||||
const hasMarkets = marketIds.length > 0;
|
||||
|
||||
useEffect(() => {
|
||||
if (hasMarkets && isClientConnected && marketId) {
|
||||
if (hasMarkets) {
|
||||
const widgetOptions = getWidgetOptions();
|
||||
const widgetOverrides = getWidgetOverrides(appTheme);
|
||||
const options = {
|
||||
@@ -75,7 +75,7 @@ export const useTradingView = ({
|
||||
tvWidgetRef.current = null;
|
||||
setIsChartReady(false);
|
||||
};
|
||||
}, [getCandlesForDatafeed, isClientConnected, hasMarkets, selectedLocale, selectedNetwork, !!marketId]);
|
||||
}, [getCandlesForDatafeed, hasMarkets, selectedLocale, selectedNetwork]);
|
||||
|
||||
return { savedResolution };
|
||||
};
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
import { useCallback } from 'react';
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useBalance } from 'wagmi';
|
||||
import { StargateClient } from '@cosmjs/stargate';
|
||||
import { useQuery } from 'react-query';
|
||||
import { formatUnits } from 'viem';
|
||||
|
||||
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
import { CLIENT_NETWORK_CONFIGS } from '@/constants/networks';
|
||||
import { QUANTUM_MULTIPLIER } from '@/constants/numbers';
|
||||
import { EvmAddress } from '@/constants/wallets';
|
||||
|
||||
import { convertBech32Address } from '@/lib/addressUtils';
|
||||
import { MustBigNumber } from '@/lib/numbers';
|
||||
|
||||
import { getBalances, getStakingBalances } from '@/state/accountSelectors';
|
||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||
|
||||
import { useAccounts } from './useAccounts';
|
||||
import { useTokenConfigs } from './useTokenConfigs';
|
||||
import { usePollNativeTokenBalance } from './usePollNativeTokenBalance';
|
||||
import { usePollUSDCBalance } from './usePollUSDCBalance';
|
||||
|
||||
type UseAccountBalanceProps = {
|
||||
// Token Items
|
||||
addressOrDenom?: string;
|
||||
assetSymbol?: string;
|
||||
decimals?: number;
|
||||
|
||||
// Chain Items
|
||||
@@ -34,10 +36,11 @@ type UseAccountBalanceProps = {
|
||||
* 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.
|
||||
*/
|
||||
export const CHAIN_DEFAULT_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
|
||||
const CHAIN_DEFAULT_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
|
||||
|
||||
export const useAccountBalance = ({
|
||||
addressOrDenom,
|
||||
addressOrDenom = CHAIN_DEFAULT_TOKEN_ADDRESS,
|
||||
assetSymbol,
|
||||
bech32AddrPrefix,
|
||||
chainId,
|
||||
decimals = 0,
|
||||
@@ -47,17 +50,16 @@ export const useAccountBalance = ({
|
||||
const { evmAddress, dydxAddress } = useAccounts();
|
||||
|
||||
const selectedNetwork = useSelector(getSelectedNetwork);
|
||||
const balances = useSelector(getBalances, shallowEqual);
|
||||
const { chainTokenDenom, usdcDenom } = useTokenConfigs();
|
||||
const evmChainId = Number(ENVIRONMENT_CONFIG_MAP[selectedNetwork].ethereumChainId);
|
||||
const stakingBalances = useSelector(getStakingBalances, shallowEqual);
|
||||
const evmChainId = Number(CLIENT_NETWORK_CONFIGS[selectedNetwork].ethereumChainId);
|
||||
|
||||
const evmQuery = useBalance({
|
||||
enabled: Boolean(!isCosmosChain && addressOrDenom?.startsWith('0x')),
|
||||
address: evmAddress,
|
||||
chainId: typeof chainId === 'number' ? chainId : Number(evmChainId),
|
||||
token:
|
||||
addressOrDenom === CHAIN_DEFAULT_TOKEN_ADDRESS ? undefined : (addressOrDenom as EvmAddress),
|
||||
addressOrDenom === CHAIN_DEFAULT_TOKEN_ADDRESS
|
||||
? undefined
|
||||
: (addressOrDenom as EvmAddress),
|
||||
watch: true,
|
||||
});
|
||||
|
||||
@@ -88,21 +90,19 @@ export const useAccountBalance = ({
|
||||
});
|
||||
|
||||
const { formatted: evmBalance } = evmQuery.data || {};
|
||||
const balance = isCosmosChain ? cosmosQuery.data : evmBalance;
|
||||
const balance = !assetSymbol ? '0' : isCosmosChain ? cosmosQuery.data : evmBalance;
|
||||
|
||||
const nativeTokenCoinBalance = balances?.[chainTokenDenom];
|
||||
const nativeTokenBalance = MustBigNumber(nativeTokenCoinBalance?.amount);
|
||||
const nativeTokenCoinBalance = usePollNativeTokenBalance({ dydxAddress });
|
||||
const nativeTokenBalance = MustBigNumber(nativeTokenCoinBalance?.amount)
|
||||
.div(QUANTUM_MULTIPLIER)
|
||||
.toNumber();
|
||||
|
||||
const usdcCoinBalance = balances?.[usdcDenom];
|
||||
const usdcBalance = MustBigNumber(usdcCoinBalance?.amount).toNumber();
|
||||
|
||||
const nativeStakingCoinBalanace = stakingBalances?.[chainTokenDenom];
|
||||
const nativeStakingBalance = MustBigNumber(nativeStakingCoinBalanace?.amount).toNumber();
|
||||
const usdcCoinBalance = usePollUSDCBalance({ dydxAddress });
|
||||
const usdcBalance = MustBigNumber(usdcCoinBalance?.amount).div(QUANTUM_MULTIPLIER).toNumber();
|
||||
|
||||
return {
|
||||
balance,
|
||||
nativeTokenBalance,
|
||||
nativeStakingBalance,
|
||||
usdcBalance,
|
||||
queryStatus: isCosmosChain ? cosmosQuery.status : evmQuery.status,
|
||||
isQueryFetching: isCosmosChain ? cosmosQuery.isFetching : evmQuery.fetchStatus === 'fetching',
|
||||
|
||||
@@ -2,23 +2,24 @@ import { useCallback, useContext, createContext, useEffect, useState, useMemo }
|
||||
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { AES, enc } from 'crypto-js';
|
||||
import { LocalWallet, type Subaccount } from '@dydxprotocol/v4-client-js';
|
||||
import { LocalWallet, USDC_DENOM, type Subaccount } from '@dydxprotocol/v4-client-js';
|
||||
|
||||
import { OnboardingGuard, OnboardingState, type EvmDerivedAddresses } from '@/constants/account';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
import { LocalStorageKey, LOCAL_STORAGE_VERSIONS } from '@/constants/localStorage';
|
||||
import { DydxAddress, EvmAddress, PrivateInformation } from '@/constants/wallets';
|
||||
|
||||
import { setOnboardingState, setOnboardingGuard } from '@/state/account';
|
||||
import { forceOpenDialog } from '@/state/dialogs';
|
||||
import {
|
||||
setOnboardingState,
|
||||
setOnboardingGuard,
|
||||
} from '@/state/account';
|
||||
|
||||
import abacusStateManager from '@/lib/abacus';
|
||||
import { log } from '@/lib/telemetry';
|
||||
|
||||
import { useDydxClient } from './useDydxClient';
|
||||
import { useLocalStorage } from './useLocalStorage';
|
||||
import { useRestrictions } from './useRestrictions';
|
||||
|
||||
import { useWalletConnection } from './useWalletConnection';
|
||||
import { useDydxClient } from './useDydxClient';
|
||||
|
||||
const AccountsContext = createContext<ReturnType<typeof useAccountsContext> | undefined>(undefined);
|
||||
|
||||
@@ -59,9 +60,7 @@ const useAccountsContext = () => {
|
||||
forgetEvmSignature(previousEvmAddress);
|
||||
}
|
||||
|
||||
if (evmAddress) {
|
||||
abacusStateManager.setTransfersSourceAddress(evmAddress);
|
||||
}
|
||||
if (evmAddress) abacusStateManager.setTransfersSourceAddress(evmAddress);
|
||||
|
||||
setPreviousEvmAddress(evmAddress);
|
||||
}, [evmAddress]);
|
||||
@@ -130,8 +129,16 @@ const useAccountsContext = () => {
|
||||
// dYdX subaccounts
|
||||
const [dydxSubaccounts, setDydxSubaccounts] = useState<Subaccount[] | undefined>();
|
||||
|
||||
const { getSubaccounts } = useMemo(
|
||||
const { getAccountBalance, getSubaccounts } = useMemo(
|
||||
() => ({
|
||||
getAccountBalance: async ({
|
||||
dydxAddress,
|
||||
denom = USDC_DENOM,
|
||||
}: {
|
||||
dydxAddress: DydxAddress;
|
||||
denom?: string;
|
||||
}) => await compositeClient?.validatorClient.get.getAccountBalance(dydxAddress, denom),
|
||||
|
||||
getSubaccounts: async ({ dydxAddress }: { dydxAddress: DydxAddress }) => {
|
||||
try {
|
||||
const response = await compositeClient?.indexerClient.account.getSubaccounts(dydxAddress);
|
||||
@@ -213,10 +220,11 @@ const useAccountsContext = () => {
|
||||
}, [evmAddress, evmDerivedAddresses, signerWagmi, connectedDydxAddress, signerGraz]);
|
||||
|
||||
// abacus
|
||||
// TODO: useAbacus({ dydxAddress })
|
||||
useEffect(() => {
|
||||
if (dydxAddress) abacusStateManager.setAccount(localDydxWallet);
|
||||
if (dydxAddress) abacusStateManager.setAccount(dydxAddress);
|
||||
else abacusStateManager.attemptDisconnectAccount();
|
||||
}, [localDydxWallet]);
|
||||
}, [dydxAddress]);
|
||||
|
||||
// clear subaccounts when no dydxAddress is set
|
||||
useEffect(() => {
|
||||
@@ -253,21 +261,6 @@ const useAccountsContext = () => {
|
||||
);
|
||||
}, [dydxSubaccounts]);
|
||||
|
||||
// Restrictions
|
||||
const { isBadActor, sanctionedAddresses } = useRestrictions();
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
dydxAddress &&
|
||||
(isBadActor ||
|
||||
sanctionedAddresses.has(dydxAddress) ||
|
||||
(evmAddress && sanctionedAddresses.has(evmAddress)))
|
||||
) {
|
||||
dispatch(forceOpenDialog({ type: DialogTypes.RestrictedWallet }));
|
||||
disconnect();
|
||||
}
|
||||
}, [isBadActor, evmAddress, dydxAddress, sanctionedAddresses]);
|
||||
|
||||
// Disconnect wallet / accounts
|
||||
const disconnectLocalDydxWallet = () => {
|
||||
setLocalDydxWallet(undefined);
|
||||
@@ -318,6 +311,7 @@ const useAccountsContext = () => {
|
||||
disconnect,
|
||||
|
||||
// dydxClient Account methods
|
||||
getAccountBalance,
|
||||
getSubaccounts,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useMatch, useNavigate } from 'react-router-dom';
|
||||
|
||||
import { LocalStorageKey } from '@/constants/localStorage';
|
||||
import { DEFAULT_MARKETID } from '@/constants/markets';
|
||||
import { AppRoute } from '@/constants/routes';
|
||||
import { AppRoute, DEFAULT_TRADE_ROUTE } from '@/constants/routes';
|
||||
|
||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||
import { closeDialogInTradeBox } from '@/state/dialogs';
|
||||
@@ -44,9 +44,6 @@ export const useCurrentMarketId = () => {
|
||||
}, [validId]);
|
||||
|
||||
useEffect(() => {
|
||||
// Check for marketIds otherwise Abacus will silently fail its isMarketValid check
|
||||
if (marketIds) {
|
||||
abacusStateManager.setMarket(marketId ?? DEFAULT_MARKETID);
|
||||
}
|
||||
}, [selectedNetwork, marketIds, marketId]);
|
||||
abacusStateManager.setMarket(marketId ?? DEFAULT_MARKETID);
|
||||
}, [selectedNetwork, marketId]);
|
||||
};
|
||||
|
||||
@@ -1,24 +1,29 @@
|
||||
import { isDydxV4Network } from '@/constants/networks';
|
||||
import { WalletType } from '@/constants/wallets';
|
||||
|
||||
import { isTruthy } from '@/lib/isTruthy';
|
||||
|
||||
import { useSelectedNetwork } from './useSelectedNetwork';
|
||||
|
||||
export const useDisplayedWallets = () => {
|
||||
const { selectedNetwork } = useSelectedNetwork();
|
||||
|
||||
return [
|
||||
WalletType.MetaMask,
|
||||
|
||||
import.meta.env.MODE !== 'production' && WalletType.Keplr,
|
||||
import.meta.env.MODE !== 'production' && isDydxV4Network(selectedNetwork) && WalletType.Keplr,
|
||||
|
||||
WalletType.WalletConnect2,
|
||||
|
||||
WalletType.WalletConnect,
|
||||
|
||||
WalletType.CoinbaseWallet,
|
||||
|
||||
// Hide these wallet options until they can be properly tested on mainnet
|
||||
// WalletType.ImToken,
|
||||
// WalletType.Rainbow,
|
||||
// WalletType.TrustWallet,
|
||||
// WalletType.HuobiWallet,
|
||||
// WalletType.BitKeep,
|
||||
// WalletType.Coin98,
|
||||
|
||||
WalletType.ImToken,
|
||||
WalletType.Rainbow,
|
||||
WalletType.TrustWallet,
|
||||
WalletType.HuobiWallet,
|
||||
WalletType.BitKeep,
|
||||
WalletType.Coin98,
|
||||
|
||||
WalletType.OtherWallet,
|
||||
].filter(isTruthy);
|
||||
|
||||
@@ -13,17 +13,12 @@ import {
|
||||
|
||||
import type { ResolutionString } from 'public/tradingview/charting_library';
|
||||
|
||||
import type { ConnectNetworkEvent, NetworkConfig } from '@/constants/abacus';
|
||||
import type { NetworkConfig, ConnectNetworkEvent } from '@/constants/abacus';
|
||||
import { type Candle, RESOLUTION_MAP } from '@/constants/candles';
|
||||
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
import { DydxChainAsset } from '@/constants/wallets';
|
||||
|
||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||
|
||||
import { log } from '@/lib/telemetry';
|
||||
|
||||
import { useRestrictions } from './useRestrictions';
|
||||
|
||||
type DydxContextType = ReturnType<typeof useDydxClientContext>;
|
||||
const DydxContext = createContext<DydxContextType>({} as DydxContextType);
|
||||
DydxContext.displayName = 'dYdXClient';
|
||||
@@ -38,9 +33,8 @@ const useDydxClientContext = () => {
|
||||
// ------ Network ------ //
|
||||
|
||||
const selectedNetwork = useSelector(getSelectedNetwork);
|
||||
const tokensConfigs = ENVIRONMENT_CONFIG_MAP[selectedNetwork].tokens;
|
||||
|
||||
const [networkConfig, setNetworkConfig] = useState<NetworkConfig>();
|
||||
const [networkConfig, setNetworkConfig] = useState<Partial<NetworkConfig>>();
|
||||
|
||||
useEffect(() => {
|
||||
const onConnectNetwork = (event: ConnectNetworkEvent) => setNetworkConfig(event.detail);
|
||||
@@ -60,22 +54,15 @@ const useDydxClientContext = () => {
|
||||
if (
|
||||
networkConfig?.chainId &&
|
||||
networkConfig?.indexerUrl &&
|
||||
networkConfig?.websocketUrl &&
|
||||
networkConfig?.indexerSocketUrl &&
|
||||
networkConfig?.validatorUrl
|
||||
) {
|
||||
try {
|
||||
const initializedClient = await CompositeClient.connect(
|
||||
new Network(
|
||||
selectedNetwork,
|
||||
new IndexerConfig(networkConfig.indexerUrl, networkConfig.websocketUrl),
|
||||
new ValidatorConfig(networkConfig.validatorUrl, networkConfig.chainId,
|
||||
{
|
||||
USDC_DENOM: tokensConfigs[DydxChainAsset.USDC].denom,
|
||||
USDC_DECIMALS: tokensConfigs[DydxChainAsset.USDC].decimals,
|
||||
USDC_GAS_DENOM: tokensConfigs[DydxChainAsset.USDC].gasDenom,
|
||||
CHAINTOKEN_DENOM: tokensConfigs[DydxChainAsset.CHAINTOKEN].denom,
|
||||
CHAINTOKEN_DECIMALS: tokensConfigs[DydxChainAsset.CHAINTOKEN].decimals,
|
||||
}, {
|
||||
new IndexerConfig(networkConfig.indexerUrl, networkConfig.indexerSocketUrl),
|
||||
new ValidatorConfig(networkConfig.validatorUrl, networkConfig.chainId, {
|
||||
broadcastPollIntervalMs: 3_000,
|
||||
broadcastTimeoutMs: 60_000,
|
||||
})
|
||||
@@ -127,7 +114,7 @@ const useDydxClientContext = () => {
|
||||
}): Promise<Candle[]> => {
|
||||
try {
|
||||
const { candles } =
|
||||
(await compositeClient?.indexerClient.markets.getPerpetualMarketCandles(
|
||||
(await compositeClient!.indexerClient.markets.getPerpetualMarketCandles(
|
||||
marketId,
|
||||
RESOLUTION_MAP[resolution],
|
||||
fromIso,
|
||||
@@ -191,27 +178,7 @@ const useDydxClientContext = () => {
|
||||
[requestCandles]
|
||||
);
|
||||
|
||||
const { updateSanctionedAddresses } = useRestrictions();
|
||||
|
||||
const screenAddresses = useCallback(
|
||||
async ({ addresses }: { addresses: string[] }) => {
|
||||
if (compositeClient) {
|
||||
const promises = addresses.map((address) =>
|
||||
compositeClient.indexerClient.utility.screen(address)
|
||||
);
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
|
||||
const screenedAddresses = Object.fromEntries(
|
||||
addresses.map((address, index) => [address, results[index]?.restricted])
|
||||
);
|
||||
|
||||
updateSanctionedAddresses(screenedAddresses);
|
||||
return screenedAddresses;
|
||||
}
|
||||
},
|
||||
[compositeClient]
|
||||
);
|
||||
// ------ Subacount Methods ------ //
|
||||
|
||||
return {
|
||||
// Client initialization
|
||||
@@ -219,13 +186,11 @@ const useDydxClientContext = () => {
|
||||
networkConfig,
|
||||
compositeClient,
|
||||
faucetClient,
|
||||
isConnected: !!compositeClient,
|
||||
|
||||
// Wallet Methods
|
||||
getWalletFromEvmSignature,
|
||||
|
||||
// Public Methods
|
||||
getCandlesForDatafeed,
|
||||
screenAddresses,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,14 +3,13 @@ import { useDispatch } from 'react-redux';
|
||||
|
||||
import { LocalStorageKey } from '@/constants/localStorage';
|
||||
|
||||
import { DEFAULT_APP_ENVIRONMENT, type DydxNetwork } from '@/constants/networks';
|
||||
import { DEFAULT_APP_ENVIRONMENT, DydxNetwork, isValidDydxNetwork } from '@/constants/networks';
|
||||
|
||||
import { useLocalStorage } from '@/hooks';
|
||||
|
||||
import { initializeLocalization } from '@/state/app';
|
||||
|
||||
import abacusStateManager from '@/lib/abacus';
|
||||
import { validateAgainstAvailableEnvironments } from '@/lib/network';
|
||||
|
||||
export const useInitializePage = () => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -19,7 +18,7 @@ export const useInitializePage = () => {
|
||||
const [localStorageNetwork] = useLocalStorage<DydxNetwork>({
|
||||
key: LocalStorageKey.SelectedNetwork,
|
||||
defaultValue: DEFAULT_APP_ENVIRONMENT,
|
||||
validateFn: validateAgainstAvailableEnvironments,
|
||||
validateFn: isValidDydxNetwork, // backwards compatibility
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { LocalStorageKey } from '@/constants/localStorage';
|
||||
import { type TransferNotifcation } from '@/constants/notifications';
|
||||
|
||||
import { useAccounts } from '@/hooks/useAccounts';
|
||||
import { STATUS_ERROR_GRACE_PERIOD, useSquid } from '@/hooks/useSquid';
|
||||
import { useSquid } from '@/hooks/useSquid';
|
||||
import { useLocalStorage } from './useLocalStorage';
|
||||
|
||||
const LocalNotificationsContext = createContext<
|
||||
@@ -62,19 +62,15 @@ const useLocalNotificationsContext = () => {
|
||||
txHash,
|
||||
toChainId,
|
||||
fromChainId,
|
||||
triggeredAt,
|
||||
status: currentStatus,
|
||||
} of transferNotifications) {
|
||||
try {
|
||||
if (currentStatus && currentStatus?.squidTransactionStatus !== 'ongoing') continue;
|
||||
|
||||
|
||||
const status = await squid?.getStatus({ transactionId: txHash, toChainId, fromChainId });
|
||||
if (status) statuses[txHash] = status;
|
||||
} catch (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;
|
||||
}
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
return statuses;
|
||||
|
||||
@@ -28,13 +28,15 @@ export const useMarketsData = (
|
||||
const allAssets = useSelector(getAssets, shallowEqual) || {};
|
||||
|
||||
const markets = useMemo(() => {
|
||||
return Object.values(allPerpetualMarkets).map((marketData) => ({
|
||||
asset: allAssets[marketData.assetId],
|
||||
tickSizeDecimals: marketData.configs?.tickSizeDecimals,
|
||||
...marketData,
|
||||
...marketData.perpetual,
|
||||
...marketData.configs,
|
||||
})) as MarketData[];
|
||||
return Object.values(allPerpetualMarkets)
|
||||
.filter(({ assetId }) => allAssets[assetId]) // Filter out markets with no asset information
|
||||
.map((marketData) => ({
|
||||
asset: allAssets[marketData.assetId],
|
||||
tickSizeDecimals: marketData.configs?.tickSizeDecimals,
|
||||
...marketData,
|
||||
...marketData.perpetual,
|
||||
...marketData.configs,
|
||||
})) as MarketData[];
|
||||
}, [allPerpetualMarkets, allAssets]);
|
||||
|
||||
const filteredMarkets = useMemo(() => {
|
||||
@@ -43,8 +45,8 @@ export const useMarketsData = (
|
||||
if (searchFilter) {
|
||||
return filtered.filter(
|
||||
({ asset }) =>
|
||||
asset?.name?.toLocaleLowerCase().includes(searchFilter.toLowerCase()) ||
|
||||
asset?.id?.toLocaleLowerCase().includes(searchFilter.toLowerCase())
|
||||
asset.name?.toLocaleLowerCase().includes(searchFilter.toLowerCase()) ||
|
||||
asset.symbol?.toLocaleLowerCase().includes(searchFilter.toLowerCase())
|
||||
);
|
||||
}
|
||||
return filtered;
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
import { useSelector, shallowEqual, useDispatch } from 'react-redux';
|
||||
import { groupBy } from 'lodash';
|
||||
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client-js';
|
||||
|
||||
import { AlertType } from '@/constants/alerts';
|
||||
import { AbacusOrderStatus, ORDER_SIDES } from '@/constants/abacus';
|
||||
import { AbacusOrderStatus, ORDER_SIDES, ORDER_STATUS_STRINGS } from '@/constants/abacus';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
import { STRING_KEYS, StringKey } from '@/constants/localization';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { type NotificationTypeConfig, NotificationType } from '@/constants/notifications';
|
||||
import { ORDER_SIDE_STRINGS } from '@/constants/trade';
|
||||
|
||||
import { useLocalNotifications } from '@/hooks/useLocalNotifications';
|
||||
|
||||
import { AssetIcon } from '@/components/AssetIcon';
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
import { Output, OutputType } from '@/components/Output';
|
||||
import { OrderStatusIcon } from '@/views/OrderStatusIcon';
|
||||
import { TransferStatusToast } from '@/views/TransferStatus';
|
||||
import { TransferStatusSteps } from '@/views/TransferStatusSteps';
|
||||
|
||||
import { getSubaccountFills, getSubaccountOrders } from '@/state/accountSelectors';
|
||||
import { openDialog } from '@/state/dialogs';
|
||||
|
||||
import { OrderStatusIcon } from '@/views/OrderStatusIcon';
|
||||
|
||||
import { useStringGetter } from './useStringGetter';
|
||||
import { TransferStatusSteps } from '@/views/TransferStatusSteps';
|
||||
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client-js';
|
||||
|
||||
export const notificationTypes = [
|
||||
{
|
||||
@@ -60,64 +58,47 @@ export const notificationTypes = [
|
||||
}
|
||||
: undefined);
|
||||
|
||||
if (order)
|
||||
trigger(
|
||||
order.id,
|
||||
{
|
||||
icon: (
|
||||
<OrderStatusIcon status={order.status} totalFilled={order.totalFilled ?? 0} />
|
||||
),
|
||||
title: `${stringGetter({
|
||||
key: order.resources.typeStringKey as StringKey,
|
||||
})} ${
|
||||
order.status === AbacusOrderStatus.open && (order?.totalFilled ?? 0) > 0
|
||||
? stringGetter({ key: STRING_KEYS.PARTIALLY_FILLED })
|
||||
: stringGetter({ key: order.resources.statusStringKey as StringKey })
|
||||
}`,
|
||||
description: `${stringGetter({
|
||||
key: ORDER_SIDE_STRINGS[ORDER_SIDES[order.side.name]],
|
||||
})} ${order.size} ${order.marketId} @ $${order.price}`,
|
||||
actionDescription: 'View Order',
|
||||
actionAltText: 'View this order in the Orders tab or the Notifications menu.',
|
||||
toastSensitivity:
|
||||
order.status === AbacusOrderStatus.pending ? 'foreground' : 'background',
|
||||
toastDuration: 5000,
|
||||
},
|
||||
[order.status.name, order.size],
|
||||
!order.createdAtMilliseconds || order.createdAtMilliseconds > lastUpdated
|
||||
);
|
||||
if (order) console.log(order);
|
||||
trigger({
|
||||
id: order.id,
|
||||
displayData: {
|
||||
icon: <AssetIcon symbol={order.marketId.split('-')[0]} />, //<OrderStatusIcon status={order.status} totalFilled={order.totalFilled ?? 0} />,
|
||||
title: `${stringGetter({
|
||||
key: order.resources.typeStringKey ?? '',
|
||||
})} ${
|
||||
order.status === AbacusOrderStatus.open && (order?.totalFilled ?? 0) > 0
|
||||
? stringGetter({ key: STRING_KEYS.PARTIALLY_FILLED })
|
||||
: stringGetter({ key: ORDER_STATUS_STRINGS[order.status.name] })
|
||||
}`,
|
||||
|
||||
description: `${stringGetter({
|
||||
key: ORDER_SIDE_STRINGS[ORDER_SIDES[order.side.name]],
|
||||
})} ${order.size} ${order.marketId} @ $${order.price}`,
|
||||
actionDescription: 'View Order',
|
||||
actionAltText: 'View this order in the Orders tab or the Notifications menu.',
|
||||
toastSensitivity:
|
||||
order.status === AbacusOrderStatus.pending ? 'foreground' : 'background',
|
||||
toastDuration: Infinity,
|
||||
},
|
||||
updateKey: [order.status.name, order.size],
|
||||
isNew: !order.createdAtMilliseconds || order.createdAtMilliseconds > lastUpdated,
|
||||
});
|
||||
}
|
||||
}, [orderIds]);
|
||||
}, [orderIds, stringGetter]);
|
||||
},
|
||||
|
||||
// useNotificationAction: () => {
|
||||
// const dispatch = useDispatch();
|
||||
// const orders = useSelector(getSubaccountOrders, shallowEqual) || [];
|
||||
// const ordersByOrderId = Object.fromEntries(orders.map((order) => [order.id, order]));
|
||||
useNotificationAction: () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
// const fills = useSelector(getSubaccountFills, shallowEqual) || [];
|
||||
// const fillsByOrderId = groupBy(fills, (fill) => fill.orderId);
|
||||
|
||||
// return (id) => {
|
||||
// if (ordersByOrderId[id]) {
|
||||
// dispatch(
|
||||
// openDialog({
|
||||
// type: DialogTypes.OrderDetails,
|
||||
// dialogProps: { orderId: id },
|
||||
// })
|
||||
// );
|
||||
// } else if (fillsByOrderId[id]) {
|
||||
// const fillId = fillsByOrderId[id][0].id;
|
||||
|
||||
// dispatch(
|
||||
// openDialog({
|
||||
// type: DialogTypes.FillDetails,
|
||||
// dialogProps: { fillId },
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
// };
|
||||
// },
|
||||
return (orderId) => {
|
||||
dispatch(
|
||||
openDialog({
|
||||
type: DialogTypes.OrderDetails,
|
||||
dialogProps: { orderId },
|
||||
})
|
||||
);
|
||||
};
|
||||
},
|
||||
} as NotificationTypeConfig<string, [string, number]>,
|
||||
{
|
||||
type: NotificationType.SquidTransfer,
|
||||
@@ -125,81 +106,45 @@ export const notificationTypes = [
|
||||
const stringGetter = useStringGetter();
|
||||
const { transferNotifications } = useLocalNotifications();
|
||||
|
||||
const getTitleStringKey = useCallback((type: 'deposit' | 'withdrawal', finished: boolean) => {
|
||||
const getTitleStringKey = useCallback((type: 'deposit' | 'withdraw', finished: boolean) => {
|
||||
if (type === 'deposit' && !finished) return STRING_KEYS.DEPOSIT_IN_PROGRESS;
|
||||
if (type === 'deposit' && finished) return STRING_KEYS.DEPOSIT;
|
||||
if (type === 'withdrawal' && !finished) return STRING_KEYS.WITHDRAW_IN_PROGRESS;
|
||||
if (type === 'withdraw' && !finished) return STRING_KEYS.WITHDRAW_IN_PROGRESS;
|
||||
return STRING_KEYS.WITHDRAW;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
for (const transfer of transferNotifications) {
|
||||
const { fromChainId, status, txHash, toAmount } = transfer;
|
||||
const { toChainId, status, txHash, toAmount } = transfer;
|
||||
const finished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing';
|
||||
const type = fromChainId === TESTNET_CHAIN_ID ? 'withdrawal' : 'deposit';
|
||||
// @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 = toChainId === TESTNET_CHAIN_ID ? 'deposit' : 'withdraw';
|
||||
|
||||
// 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(
|
||||
txHash,
|
||||
{
|
||||
trigger({
|
||||
id: txHash,
|
||||
displayData: {
|
||||
icon: <Icon iconName={finished ? IconName.Transfer : IconName.Clock} />,
|
||||
title: stringGetter({ key: getTitleStringKey(type, finished) }),
|
||||
description: description,
|
||||
// TODO: confirm with design what the description should be
|
||||
description: (
|
||||
<>
|
||||
<span>{type === 'deposit' ? 'Deposit of ' : 'Withdraw of'}</span>
|
||||
<Output type={OutputType.Fiat} value={toAmount} />
|
||||
</>
|
||||
),
|
||||
customContent: (
|
||||
<TransferStatusToast
|
||||
type={type}
|
||||
toAmount={transfer.toAmount}
|
||||
triggeredAt={transfer.triggeredAt}
|
||||
status={transfer.status}
|
||||
/>
|
||||
),
|
||||
customMenuContent: !finished && (
|
||||
<div>
|
||||
{description}
|
||||
<TransferStatusSteps status={transfer.status} type={type} />
|
||||
</div>
|
||||
),
|
||||
customMenuContent: !finished && <TransferStatusSteps status={transfer.status} />,
|
||||
toastSensitivity: 'foreground',
|
||||
},
|
||||
[]
|
||||
);
|
||||
updateKey: [],
|
||||
});
|
||||
}
|
||||
}, [transferNotifications]);
|
||||
}, [transferNotifications, stringGetter]);
|
||||
},
|
||||
},
|
||||
} as NotificationTypeConfig<string, []>,
|
||||
] 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;
|
||||
`;
|
||||
|
||||
@@ -92,7 +92,7 @@ const useNotificationsContext = () => {
|
||||
for (const { type, useTrigger } of notificationTypes)
|
||||
useTrigger({
|
||||
trigger: useCallback(
|
||||
(id, displayData, updateKey, isNew = true) => {
|
||||
({ id, displayData, updateKey, isNew = true }) => {
|
||||
const key = getKey({ type, id });
|
||||
|
||||
const notification = notifications[key];
|
||||
@@ -182,28 +182,23 @@ const useNotificationsContext = () => {
|
||||
const iconUrl =
|
||||
displayData.icon && (await renderSvgToDataUrl(displayData.icon).catch(() => undefined));
|
||||
|
||||
const pushNotification = new globalThis.Notification(displayData.title, {
|
||||
renotify: true,
|
||||
tag: getKey(notification),
|
||||
data: notification,
|
||||
body: displayData.description,
|
||||
icon: iconUrl ?? '/favicon.svg',
|
||||
badge: iconUrl ?? '/favicon.svg',
|
||||
image: iconUrl ?? '/favicon.svg',
|
||||
vibrate: displayData.toastSensitivity === 'foreground',
|
||||
requireInteraction: displayData.toastDuration === Infinity,
|
||||
// actions: [
|
||||
// {
|
||||
// action: displayData.actionDescription,
|
||||
// title: displayData.actionDescription,
|
||||
// }
|
||||
// ].slice(0, globalThis.Notification.maxActions),
|
||||
});
|
||||
if (displayData.title) {
|
||||
const pushNotification = new globalThis.Notification(displayData.title, {
|
||||
renotify: true,
|
||||
tag: getKey(notification),
|
||||
data: notification,
|
||||
body: displayData.description ?? '',
|
||||
icon: (iconUrl as string) ?? '/favicon.svg',
|
||||
badge: (iconUrl as string) ?? '/favicon.svg',
|
||||
image: (iconUrl as string) ?? '/favicon.svg',
|
||||
requireInteraction: displayData.toastDuration === Infinity,
|
||||
});
|
||||
|
||||
pushNotification.addEventListener('click', () => {
|
||||
onNotificationAction(notification);
|
||||
markSeen(notification);
|
||||
});
|
||||
pushNotification.addEventListener('click', () => {
|
||||
onNotificationAction(notification);
|
||||
markSeen(notification);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setPushNotificationsLastUpdated(Date.now());
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { getLatestOrderClientId } from '@/state/accountSelectors';
|
||||
|
||||
/**
|
||||
* @description This hook will fire a callback when the latest order has been updated to a non-pending state from the Indexer.
|
||||
* @param { callback: () => void }
|
||||
*/
|
||||
export const useOnLastOrderIndexed = ({ callback }: { callback: () => void }) => {
|
||||
const [unIndexedClientId, setUnIndexedClientId] = useState<number | undefined>();
|
||||
const latestOrderClientId = useSelector(getLatestOrderClientId);
|
||||
|
||||
useEffect(() => {
|
||||
if (unIndexedClientId) {
|
||||
if (unIndexedClientId === latestOrderClientId) {
|
||||
callback();
|
||||
setUnIndexedClientId(undefined);
|
||||
}
|
||||
}
|
||||
}, [callback, latestOrderClientId, unIndexedClientId]);
|
||||
|
||||
return {
|
||||
setUnIndexedClientId,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
import { useAccounts } from '@/hooks';
|
||||
|
||||
import { DydxAddress } from '@/constants/wallets';
|
||||
import { DYDX_DENOM } from '@dydxprotocol/v4-client-js';
|
||||
|
||||
const ACCOUNT_BALANCE_POLLING_INTERVAL = 60_000;
|
||||
|
||||
export const usePollNativeTokenBalance = ({
|
||||
dydxAddress,
|
||||
interval = ACCOUNT_BALANCE_POLLING_INTERVAL,
|
||||
}: {
|
||||
dydxAddress?: DydxAddress;
|
||||
interval?: number;
|
||||
}) => {
|
||||
const { getAccountBalance } = useAccounts();
|
||||
|
||||
const { data } = useQuery({
|
||||
enabled: dydxAddress !== undefined,
|
||||
queryKey: ['usePollNativeTokenBalance', { dydxAddress }],
|
||||
queryFn: async () => {
|
||||
if (!dydxAddress) return;
|
||||
return await getAccountBalance({ dydxAddress, denom: DYDX_DENOM });
|
||||
},
|
||||
refetchInterval: interval,
|
||||
staleTime: interval,
|
||||
});
|
||||
|
||||
return data;
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
import { useAccounts } from '@/hooks';
|
||||
|
||||
import { DydxAddress } from '@/constants/wallets';
|
||||
|
||||
const ACCOUNT_BALANCE_POLLING_INTERVAL = 60_000;
|
||||
|
||||
export const usePollUSDCBalance = ({
|
||||
dydxAddress,
|
||||
interval = ACCOUNT_BALANCE_POLLING_INTERVAL,
|
||||
}: {
|
||||
dydxAddress?: DydxAddress;
|
||||
interval?: number;
|
||||
}) => {
|
||||
const { getAccountBalance } = useAccounts();
|
||||
|
||||
const { data } = useQuery({
|
||||
enabled: dydxAddress !== undefined,
|
||||
queryKey: ['usePollUSDCBalance', { dydxAddress }],
|
||||
queryFn: async () => {
|
||||
if (!dydxAddress) return;
|
||||
return await getAccountBalance({ dydxAddress });
|
||||
},
|
||||
refetchInterval: interval,
|
||||
staleTime: interval,
|
||||
});
|
||||
|
||||
return data;
|
||||
};
|
||||
@@ -1,53 +0,0 @@
|
||||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { RestrictionType } from '@/constants/abacus';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
|
||||
import { getRestrictionType } from '@/state/accountSelectors';
|
||||
import { forceOpenDialog } from '@/state/dialogs';
|
||||
import { isTruthy } from '@/lib/isTruthy';
|
||||
|
||||
const useRestrictionContext = () => {
|
||||
const dispatch = useDispatch();
|
||||
const restrictionType = useSelector(getRestrictionType, shallowEqual);
|
||||
const [sanctionedAddresses, setSanctionedAddresses] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (restrictionType === RestrictionType.GEO_RESTRICTED) {
|
||||
dispatch(
|
||||
forceOpenDialog({ type: DialogTypes.RestrictedGeo, dialogProps: { preventClose: true } })
|
||||
);
|
||||
}
|
||||
}, [restrictionType, dispatch]);
|
||||
|
||||
const updateSanctionedAddresses = useCallback(
|
||||
(screenedAddresses: { [address: string]: boolean }) => {
|
||||
const toAdd = Object.entries(screenedAddresses)
|
||||
.filter(([, isSanctioned]) => isSanctioned)
|
||||
.map(([address]) => address);
|
||||
|
||||
if (toAdd.length) {
|
||||
setSanctionedAddresses([...sanctionedAddresses, ...toAdd]);
|
||||
}
|
||||
},
|
||||
[sanctionedAddresses]
|
||||
);
|
||||
|
||||
return {
|
||||
isBadActor: restrictionType === RestrictionType.USER_RESTRICTED,
|
||||
isGeoRestricted: restrictionType === RestrictionType.GEO_RESTRICTED,
|
||||
updateSanctionedAddresses,
|
||||
sanctionedAddresses: useMemo(() => new Set(sanctionedAddresses), [sanctionedAddresses]),
|
||||
};
|
||||
};
|
||||
|
||||
type RestrictionContextType = ReturnType<typeof useRestrictionContext>;
|
||||
const RestrictionContext = createContext<RestrictionContextType>({} as RestrictionContextType);
|
||||
RestrictionContext.displayName = 'Restriction';
|
||||
|
||||
export const RestrictionProvider = ({ ...props }) => (
|
||||
<RestrictionContext.Provider value={useRestrictionContext()} {...props} />
|
||||
);
|
||||
|
||||
export const useRestrictions = () => useContext(RestrictionContext);
|
||||
@@ -2,15 +2,13 @@ import { useCallback } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { LocalStorageKey } from '@/constants/localStorage';
|
||||
import { DEFAULT_APP_ENVIRONMENT, DydxNetwork, ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
import { DEFAULT_APP_ENVIRONMENT, DydxNetwork } from '@/constants/networks';
|
||||
|
||||
import { useAccounts, useLocalStorage } from '@/hooks';
|
||||
|
||||
import { setSelectedNetwork } from '@/state/app';
|
||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||
|
||||
import { validateAgainstAvailableEnvironments } from '@/lib/network';
|
||||
|
||||
export const useSelectedNetwork = (): {
|
||||
switchNetwork: (network: DydxNetwork) => void;
|
||||
selectedNetwork: DydxNetwork;
|
||||
@@ -22,7 +20,6 @@ export const useSelectedNetwork = (): {
|
||||
const [, setLocalStorageNetwork] = useLocalStorage<DydxNetwork>({
|
||||
key: LocalStorageKey.SelectedNetwork,
|
||||
defaultValue: DEFAULT_APP_ENVIRONMENT,
|
||||
validateFn: validateAgainstAvailableEnvironments,
|
||||
});
|
||||
|
||||
const switchNetwork = useCallback(
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { TESTNET_CHAIN_ID } from '@dydxprotocol/v4-client-js';
|
||||
import { Squid } from '@0xsquid/sdk';
|
||||
|
||||
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
import { CLIENT_NETWORK_CONFIGS, DydxV4Network, isDydxV4Network } from '@/constants/networks';
|
||||
|
||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||
|
||||
export const NATIVE_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
|
||||
|
||||
export const STATUS_ERROR_GRACE_PERIOD = 120_000;
|
||||
export const NATIVE_TOKEN_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
||||
|
||||
const useSquidContext = () => {
|
||||
const selectedNetwork = useSelector(getSelectedNetwork);
|
||||
@@ -22,7 +21,10 @@ const useSquidContext = () => {
|
||||
};
|
||||
|
||||
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]
|
||||
);
|
||||
|
||||
@@ -36,7 +38,7 @@ const useSquidContext = () => {
|
||||
};
|
||||
|
||||
type SquidContextType = ReturnType<typeof useSquidContext>;
|
||||
const SquidContext = createContext<SquidContextType | undefined>(undefined);
|
||||
const SquidContext = createContext<SquidContextType>(undefined);
|
||||
SquidContext.displayName = '0xSquid';
|
||||
|
||||
export const SquidProvider = ({ ...props }) => (
|
||||
|
||||
@@ -1,36 +1,53 @@
|
||||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { shallowEqual, useSelector, useDispatch } from 'react-redux';
|
||||
import type { Nullable } from '@dydxprotocol/v4-abacus';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import type { Nullable } from '@dydxprotocol/abacus';
|
||||
import Long from 'long';
|
||||
import type { IndexedTx } from '@cosmjs/stargate';
|
||||
import type { EncodeObject } from '@cosmjs/proto-signing';
|
||||
import type { EncodeObject, Coin } from '@cosmjs/proto-signing';
|
||||
import { Method } from '@cosmjs/tendermint-rpc';
|
||||
|
||||
import { type LocalWallet, SubaccountClient } from '@dydxprotocol/v4-client-js';
|
||||
import {
|
||||
LocalWallet,
|
||||
OrderExecution,
|
||||
OrderFlags,
|
||||
OrderSide,
|
||||
OrderTimeInForce,
|
||||
OrderType,
|
||||
SubaccountClient,
|
||||
DYDX_DENOM,
|
||||
USDC_DENOM,
|
||||
GAS_PRICE_DYDX_DENOM,
|
||||
} from '@dydxprotocol/v4-client-js';
|
||||
|
||||
import type {
|
||||
AccountBalance,
|
||||
HumanReadableCancelOrderPayload,
|
||||
HumanReadablePlaceOrderPayload,
|
||||
ParsingError,
|
||||
SubAccountHistoricalPNLs,
|
||||
} from '@/constants/abacus';
|
||||
|
||||
import { AMOUNT_RESERVED_FOR_GAS_USDC } from '@/constants/account';
|
||||
import { AnalyticsEvent } from '@/constants/analytics';
|
||||
import { ORDER_ERROR_CODE_MAP } from '@/constants/localization';
|
||||
import { QUANTUM_MULTIPLIER } from '@/constants/numbers';
|
||||
import { UNCOMMITTED_ORDER_TIMEOUT } from '@/constants/trade';
|
||||
import { DydxAddress } from '@/constants/wallets';
|
||||
|
||||
import { setSubaccount, setHistoricalPnl, removeUncommittedOrderClientId } from '@/state/account';
|
||||
import { getBalances } from '@/state/accountSelectors';
|
||||
import {
|
||||
addUncommittedOrderClientId,
|
||||
removeUncommittedOrderClientId,
|
||||
setSubaccount,
|
||||
setHistoricalPnl,
|
||||
} from '@/state/account';
|
||||
|
||||
import abacusStateManager from '@/lib/abacus';
|
||||
import { track } from '@/lib/analytics';
|
||||
import { StatefulOrderError } from '@/lib/errors';
|
||||
import { MustBigNumber } from '@/lib/numbers';
|
||||
import { log } from '@/lib/telemetry';
|
||||
|
||||
import { useAccounts } from './useAccounts';
|
||||
import { useTokenConfigs } from './useTokenConfigs';
|
||||
import { useDydxClient } from './useDydxClient';
|
||||
import { usePollUSDCBalance } from './usePollUSDCBalance';
|
||||
|
||||
type SubaccountContextType = ReturnType<typeof useSubaccountContext>;
|
||||
const SubaccountContext = createContext<SubaccountContextType>({} as SubaccountContextType);
|
||||
@@ -48,7 +65,6 @@ export const useSubaccount = () => useContext(SubaccountContext);
|
||||
|
||||
export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: LocalWallet }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { usdcDenom } = useTokenConfigs();
|
||||
const { compositeClient, faucetClient } = useDydxClient();
|
||||
|
||||
const { getFaucetFunds } = useMemo(
|
||||
@@ -67,19 +83,24 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
||||
const {
|
||||
depositToSubaccount,
|
||||
withdrawFromSubaccount,
|
||||
simulateWithdrawFromSubaccount,
|
||||
transferFromSubaccountToAddress,
|
||||
transferNativeToken,
|
||||
simulateTransferNativeToken,
|
||||
placeOrderForSubaccount,
|
||||
cancelOrderForSubaccount,
|
||||
sendSquidWithdrawFromSubaccount,
|
||||
} = useMemo(
|
||||
() => ({
|
||||
depositToSubaccount: async ({
|
||||
subaccountClient,
|
||||
assetId = 0,
|
||||
amount,
|
||||
}: {
|
||||
subaccountClient: SubaccountClient;
|
||||
assetId?: number;
|
||||
amount: number;
|
||||
}) => await compositeClient?.depositToSubaccount(subaccountClient, amount.toString()),
|
||||
amount: Long;
|
||||
}) => await compositeClient?.validatorClient.post.deposit(subaccountClient, assetId, amount),
|
||||
|
||||
withdrawFromSubaccount: async ({
|
||||
subaccountClient,
|
||||
@@ -87,7 +108,32 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
||||
}: {
|
||||
subaccountClient: SubaccountClient;
|
||||
amount: number;
|
||||
}) => await compositeClient?.withdrawFromSubaccount(subaccountClient, amount.toString()),
|
||||
}) => 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 ({
|
||||
subaccountClient,
|
||||
@@ -130,24 +176,134 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
||||
amount: number;
|
||||
recipient: string;
|
||||
}) =>
|
||||
await compositeClient?.validatorClient.post.send(
|
||||
subaccountClient.wallet,
|
||||
await compositeClient?.validatorClient.post.sendToken(
|
||||
subaccountClient,
|
||||
recipient,
|
||||
DYDX_DENOM,
|
||||
Long.fromNumber(amount * QUANTUM_MULTIPLIER),
|
||||
false,
|
||||
Method.BroadcastTxCommit
|
||||
),
|
||||
|
||||
simulateTransferNativeToken: async ({
|
||||
subaccountClient,
|
||||
amount,
|
||||
recipient,
|
||||
}: {
|
||||
subaccountClient: SubaccountClient;
|
||||
amount: number;
|
||||
recipient: string;
|
||||
}) =>
|
||||
await compositeClient?.simulate(
|
||||
subaccountClient?.wallet,
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
const msg = compositeClient?.sendTokenMessage(
|
||||
subaccountClient.wallet,
|
||||
amount.toString(),
|
||||
const msg = compositeClient?.validatorClient.post.composer.composeMsgSendToken(
|
||||
subaccountClient.address,
|
||||
recipient,
|
||||
DYDX_DENOM,
|
||||
Long.fromNumber(amount * QUANTUM_MULTIPLIER)
|
||||
);
|
||||
|
||||
resolve([msg]);
|
||||
}),
|
||||
false,
|
||||
compositeClient?.validatorClient?.post.defaultDydxGasPrice,
|
||||
undefined,
|
||||
Method.BroadcastTxCommit
|
||||
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 ({
|
||||
subaccountClient,
|
||||
amount,
|
||||
@@ -161,7 +317,7 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
||||
|
||||
const transaction = JSON.parse(payload);
|
||||
|
||||
const msg = compositeClient.withdrawFromSubaccountMessage(subaccountClient, amount.toString());
|
||||
const msg = compositeClient.withdrawFromSubaccountMessage(subaccountClient, amount);
|
||||
const ibcMsg: EncodeObject = {
|
||||
typeUrl: transaction.msgTypeUrl,
|
||||
value: transaction.msg,
|
||||
@@ -197,12 +353,16 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
||||
|
||||
// ------ Deposit/Withdraw Methods ------ //
|
||||
const depositFunds = useCallback(
|
||||
async (balance: AccountBalance) => {
|
||||
async (balance?: Coin) => {
|
||||
if (!localDydxWallet) return;
|
||||
|
||||
const amount = parseFloat(balance.amount) - AMOUNT_RESERVED_FOR_GAS_USDC;
|
||||
const amountAfterDust = MustBigNumber(balance?.amount)
|
||||
.minus(AMOUNT_RESERVED_FOR_GAS_USDC) // keep 0.1 USDC in user's wallet for gas
|
||||
.toString();
|
||||
|
||||
if (amount > 0) {
|
||||
const amount = Long.fromString(amountAfterDust || '0');
|
||||
|
||||
if (amount.greaterThan(Long.ZERO)) {
|
||||
const subaccountClient = new SubaccountClient(localDydxWallet, 0);
|
||||
await depositToSubaccount({ amount, subaccountClient });
|
||||
}
|
||||
@@ -210,17 +370,14 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
||||
[localDydxWallet, depositToSubaccount]
|
||||
);
|
||||
|
||||
const balances = useSelector(getBalances, shallowEqual);
|
||||
const usdcCoinBalance = balances?.[usdcDenom];
|
||||
const balance = usePollUSDCBalance({ dydxAddress });
|
||||
|
||||
useEffect(() => {
|
||||
if (usdcCoinBalance) {
|
||||
depositFunds(usdcCoinBalance);
|
||||
}
|
||||
}, [usdcCoinBalance]);
|
||||
depositFunds(balance);
|
||||
}, [balance]);
|
||||
|
||||
const deposit = useCallback(
|
||||
async (amount: number) => {
|
||||
async (amount: Long) => {
|
||||
if (!subaccountClient) {
|
||||
return;
|
||||
}
|
||||
@@ -248,13 +405,38 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
||||
if (!subaccountClient) {
|
||||
return;
|
||||
}
|
||||
return (await (coinDenom === usdcDenom
|
||||
|
||||
return (await (coinDenom === USDC_DENOM
|
||||
? transferFromSubaccountToAddress
|
||||
: transferNativeToken)({ subaccountClient, amount, recipient })) as IndexedTx;
|
||||
},
|
||||
[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(
|
||||
async (amount: number, payload: string) => {
|
||||
if (!subaccountClient) {
|
||||
@@ -274,7 +456,6 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
||||
await getFaucetFunds({ dydxAddress, subaccountNumber });
|
||||
} catch (error) {
|
||||
log('useSubaccount/getFaucetFunds', error);
|
||||
throw error;
|
||||
}
|
||||
}, [dydxAddress, getFaucetFunds, subaccountNumber]);
|
||||
|
||||
@@ -286,41 +467,101 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
||||
onSuccess,
|
||||
}: {
|
||||
isClosePosition?: boolean;
|
||||
onError?: (onErrorParams?: { errorStringKey?: Nullable<string> }) => void;
|
||||
onSuccess?: (placeOrderPayload: Nullable<HumanReadablePlaceOrderPayload>) => void;
|
||||
onError?: (onErrorParams?: { errorStringKey?: string }) => void;
|
||||
onSuccess?: () => void;
|
||||
}) => {
|
||||
const callback = (
|
||||
success: boolean,
|
||||
parsingError?: Nullable<ParsingError>,
|
||||
data?: Nullable<HumanReadablePlaceOrderPayload>
|
||||
) => {
|
||||
if (success) {
|
||||
onSuccess?.(data);
|
||||
} else {
|
||||
onError?.({ errorStringKey: parsingError?.stringKey });
|
||||
let orderParams: Nullable<HumanReadablePlaceOrderPayload>;
|
||||
|
||||
if (data?.clientId !== undefined) {
|
||||
dispatch(removeUncommittedOrderClientId(data.clientId));
|
||||
}
|
||||
if (!subaccountClient) return;
|
||||
|
||||
try {
|
||||
orderParams = isClosePosition
|
||||
? abacusStateManager.closePositionPayload()
|
||||
: abacusStateManager.placeOrderPayload();
|
||||
|
||||
if (!orderParams) {
|
||||
throw new Error('Missing order params');
|
||||
}
|
||||
};
|
||||
|
||||
let placeOrderParams;
|
||||
const {
|
||||
marketId,
|
||||
type,
|
||||
side,
|
||||
price,
|
||||
triggerPrice,
|
||||
size,
|
||||
clientId,
|
||||
timeInForce,
|
||||
goodTilTimeInSeconds,
|
||||
execution,
|
||||
postOnly,
|
||||
reduceOnly,
|
||||
} = orderParams;
|
||||
|
||||
if (isClosePosition) {
|
||||
placeOrderParams = abacusStateManager.closePosition(callback);
|
||||
} else {
|
||||
placeOrderParams = abacusStateManager.placeOrder(callback);
|
||||
dispatch(addUncommittedOrderClientId(clientId));
|
||||
|
||||
// Remove uncommitted order after timeout if it hasn't already been removed
|
||||
setTimeout(() => {
|
||||
dispatch(removeUncommittedOrderClientId(clientId));
|
||||
}, UNCOMMITTED_ORDER_TIMEOUT);
|
||||
|
||||
console.log('useSubaccount/placeOrder', {
|
||||
...orderParams,
|
||||
});
|
||||
|
||||
const response = await placeOrderForSubaccount({
|
||||
subaccount: subaccountClient,
|
||||
marketId,
|
||||
type: type as OrderType,
|
||||
side: side as OrderSide,
|
||||
price,
|
||||
triggerPrice,
|
||||
size,
|
||||
clientId,
|
||||
timeInForce: timeInForce as OrderTimeInForce,
|
||||
goodTilTimeInSeconds: goodTilTimeInSeconds ?? 0,
|
||||
execution: execution as OrderExecution,
|
||||
postOnly,
|
||||
reduceOnly,
|
||||
});
|
||||
|
||||
// Handle Stateful orders
|
||||
if ((response as IndexedTx)?.code !== 0) {
|
||||
throw new StatefulOrderError('Stateful order has failed to commit.', response);
|
||||
}
|
||||
|
||||
if (orderParams?.clientId) {
|
||||
dispatch(removeUncommittedOrderClientId(orderParams.clientId));
|
||||
}
|
||||
|
||||
if (response?.hash) {
|
||||
console.log(
|
||||
isClosePosition
|
||||
? 'useSubaccount/closePosition'
|
||||
: 'useSubaccount/placeOrderForSubaccount',
|
||||
{
|
||||
txHash: Buffer.from(response.hash).toString('hex').toUpperCase(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
track(AnalyticsEvent.TradePlaceOrder, {
|
||||
...orderParams,
|
||||
isClosePosition,
|
||||
} as HumanReadablePlaceOrderPayload & { isClosePosition: boolean });
|
||||
onSuccess?.();
|
||||
} catch (error) {
|
||||
const errorCode: number | undefined = error?.code;
|
||||
const errorStringKey = errorCode ? ORDER_ERROR_CODE_MAP[errorCode] : undefined;
|
||||
onError?.({ errorStringKey });
|
||||
|
||||
log('useSubaccount/placeOrder', error, {
|
||||
orderParams,
|
||||
isClosePosition,
|
||||
});
|
||||
}
|
||||
|
||||
track(AnalyticsEvent.TradePlaceOrder, {
|
||||
...placeOrderParams,
|
||||
isClosePosition,
|
||||
} as HumanReadablePlaceOrderPayload & { isClosePosition: boolean });
|
||||
|
||||
return placeOrderParams;
|
||||
},
|
||||
[subaccountClient]
|
||||
[subaccountClient, placeOrderForSubaccount]
|
||||
);
|
||||
|
||||
const closePosition = useCallback(
|
||||
@@ -328,8 +569,8 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
||||
onError,
|
||||
onSuccess,
|
||||
}: {
|
||||
onError: (onErrorParams?: { errorStringKey?: Nullable<string> }) => void;
|
||||
onSuccess?: (placeOrderPayload: Nullable<HumanReadablePlaceOrderPayload>) => void;
|
||||
onError: (onErrorParams?: { errorStringKey?: string }) => void;
|
||||
onSuccess?: () => void;
|
||||
}) => await placeOrder({ isClosePosition: true, onError, onSuccess }),
|
||||
[placeOrder]
|
||||
);
|
||||
@@ -341,21 +582,53 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
||||
onSuccess,
|
||||
}: {
|
||||
orderId: string;
|
||||
onError?: ({ errorStringKey }?: { errorStringKey?: Nullable<string> }) => void;
|
||||
onError?: ({ errorMsg }?: { errorMsg?: string }) => void;
|
||||
onSuccess?: () => void;
|
||||
}) => {
|
||||
const callback = (success: boolean, parsingError?: Nullable<ParsingError>) => {
|
||||
if (success) {
|
||||
track(AnalyticsEvent.TradeCancelOrder);
|
||||
onSuccess?.();
|
||||
} else {
|
||||
onError?.({ errorStringKey: parsingError?.stringKey });
|
||||
}
|
||||
};
|
||||
let cancelOrderParams: Nullable<HumanReadableCancelOrderPayload>;
|
||||
|
||||
abacusStateManager.cancelOrder(orderId, callback);
|
||||
if (!subaccountClient) return;
|
||||
|
||||
try {
|
||||
cancelOrderParams = abacusStateManager.cancelOrderPayload(orderId);
|
||||
|
||||
if (!cancelOrderParams) {
|
||||
throw new Error('Missing cancel order params');
|
||||
}
|
||||
|
||||
const { clientId, clobPairId, goodTilBlock, goodTilBlockTime, orderFlags } =
|
||||
cancelOrderParams;
|
||||
|
||||
// Keep for debugging
|
||||
console.log('useSubaccount/cancelOrder', cancelOrderParams);
|
||||
|
||||
const response = await cancelOrderForSubaccount({
|
||||
subaccount: subaccountClient,
|
||||
clientId,
|
||||
orderFlags,
|
||||
clobPairId,
|
||||
goodTilBlock: goodTilBlock || undefined,
|
||||
goodTilBlockTime: goodTilBlockTime || undefined,
|
||||
});
|
||||
|
||||
// Handle Stateful orders
|
||||
if ((response as IndexedTx)?.code !== 0) {
|
||||
throw new StatefulOrderError('Stateful cancel has failed to commit.', response);
|
||||
}
|
||||
|
||||
if (response?.hash) {
|
||||
console.log('useSubaccount/cancelOrderForSubaccount', {
|
||||
txHash: Buffer.from(response.hash).toString('hex').toUpperCase(),
|
||||
});
|
||||
}
|
||||
|
||||
onSuccess?.();
|
||||
} catch (error) {
|
||||
onError?.();
|
||||
log('useSubaccount/cancelOrder', error, cancelOrderParams);
|
||||
}
|
||||
},
|
||||
[subaccountClient]
|
||||
[subaccountClient, cancelOrderForSubaccount]
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -365,7 +638,9 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
|
||||
requestFaucetFunds,
|
||||
|
||||
// Transfer Methods
|
||||
simulateTransfer,
|
||||
transfer,
|
||||
simulateWithdraw,
|
||||
sendSquidWithdraw,
|
||||
|
||||
// Trading Methods
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
import { DydxChainAsset } from '@/constants/wallets';
|
||||
|
||||
import { useSelectedNetwork } from '@/hooks';
|
||||
|
||||
export const useTokenConfigs = (): {
|
||||
tokensConfigs: {
|
||||
[DydxChainAsset.USDC]: {
|
||||
denom: string;
|
||||
name: string;
|
||||
decimals: number;
|
||||
gasDenom?: string;
|
||||
},
|
||||
[DydxChainAsset.CHAINTOKEN]: {
|
||||
denom: string;
|
||||
name: string;
|
||||
decimals: number;
|
||||
gasDenom?: string;
|
||||
},
|
||||
};
|
||||
usdcDenom: string;
|
||||
usdcLabel: string;
|
||||
chainTokenDenom: string;
|
||||
chainTokenLabel: string;
|
||||
} => {
|
||||
const { selectedNetwork } = useSelectedNetwork();
|
||||
const tokensConfigs = ENVIRONMENT_CONFIG_MAP[selectedNetwork].tokens;
|
||||
|
||||
return {
|
||||
tokensConfigs,
|
||||
usdcDenom: tokensConfigs[DydxChainAsset.USDC].denom,
|
||||
usdcLabel: tokensConfigs[DydxChainAsset.USDC].name,
|
||||
chainTokenDenom: tokensConfigs[DydxChainAsset.CHAINTOKEN].denom,
|
||||
chainTokenLabel: tokensConfigs[DydxChainAsset.CHAINTOKEN].name,
|
||||
};
|
||||
};
|
||||
@@ -1,50 +0,0 @@
|
||||
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
|
||||
import { useSelectedNetwork } from '@/hooks';
|
||||
|
||||
const FALLBACK_URL = 'https://help.dydx.exchange/';
|
||||
|
||||
export interface LinksConfigs {
|
||||
tos: string,
|
||||
privacy: string,
|
||||
mintscan: string,
|
||||
mintscanBase: string,
|
||||
feedback?: string,
|
||||
help?: string,
|
||||
blogs?: string,
|
||||
foundation?: string,
|
||||
initialMarginFractionLearnMore?: string,
|
||||
reduceOnlyLearnMore?: string,
|
||||
documentation?: string,
|
||||
community?: string,
|
||||
governanceLearnMore?: string,
|
||||
stakingLearnMore?: string,
|
||||
keplrDashboard?: string,
|
||||
accountExportLearnMore?: string,
|
||||
walletLearnMore?: string
|
||||
}
|
||||
|
||||
export const useURLConfigs = (): LinksConfigs => {
|
||||
const { selectedNetwork } = useSelectedNetwork();
|
||||
const linksConfigs = ENVIRONMENT_CONFIG_MAP[selectedNetwork].links as LinksConfigs;
|
||||
|
||||
return {
|
||||
tos: linksConfigs.tos,
|
||||
privacy: linksConfigs.privacy,
|
||||
mintscan: linksConfigs.mintscan,
|
||||
mintscanBase: linksConfigs.mintscanBase,
|
||||
feedback: linksConfigs.feedback || FALLBACK_URL,
|
||||
help: linksConfigs.help || FALLBACK_URL,
|
||||
blogs: linksConfigs.blogs || FALLBACK_URL,
|
||||
foundation: linksConfigs.foundation || FALLBACK_URL,
|
||||
initialMarginFractionLearnMore: linksConfigs.initialMarginFractionLearnMore || FALLBACK_URL,
|
||||
reduceOnlyLearnMore: linksConfigs.reduceOnlyLearnMore || FALLBACK_URL,
|
||||
documentation: linksConfigs.documentation || FALLBACK_URL,
|
||||
community: linksConfigs.community || FALLBACK_URL,
|
||||
governanceLearnMore: linksConfigs.governanceLearnMore || FALLBACK_URL,
|
||||
stakingLearnMore: linksConfigs.stakingLearnMore || FALLBACK_URL,
|
||||
keplrDashboard: linksConfigs.keplrDashboard || FALLBACK_URL,
|
||||
accountExportLearnMore: linksConfigs.accountExportLearnMore || FALLBACK_URL,
|
||||
walletLearnMore: linksConfigs.walletLearnMore || FALLBACK_URL,
|
||||
};
|
||||
};
|
||||
@@ -1,8 +1,6 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { LocalStorageKey } from '@/constants/localStorage';
|
||||
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
|
||||
import {
|
||||
type DydxAddress,
|
||||
@@ -30,8 +28,6 @@ import {
|
||||
WalletType as CosmosWalletType,
|
||||
} from 'graz';
|
||||
|
||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||
|
||||
import { resolveWagmiConnector } from '@/lib/wagmi';
|
||||
import { getWalletConnection, parseWalletError } from '@/lib/wallet';
|
||||
import { log } from '@/lib/telemetry';
|
||||
@@ -88,9 +84,6 @@ export const useWalletConnection = () => {
|
||||
|
||||
// Wallet connection
|
||||
|
||||
const selectedNetwork = useSelector(getSelectedNetwork);
|
||||
const walletConnectConfig = ENVIRONMENT_CONFIG_MAP[selectedNetwork].wallets.walletconnect;
|
||||
|
||||
const { connectAsync: connectWagmi } =
|
||||
walletType && walletConnectionType
|
||||
? useConnectWagmi({
|
||||
@@ -99,7 +92,6 @@ export const useWalletConnection = () => {
|
||||
walletConnection: {
|
||||
type: walletConnectionType,
|
||||
},
|
||||
walletConnectConfig,
|
||||
}),
|
||||
})
|
||||
: useConnectWagmi();
|
||||
@@ -135,7 +127,6 @@ export const useWalletConnection = () => {
|
||||
connector: resolveWagmiConnector({
|
||||
walletType,
|
||||
walletConnection,
|
||||
walletConnectConfig,
|
||||
}),
|
||||
});
|
||||
}
|
||||
@@ -226,7 +217,7 @@ export const useWalletConnection = () => {
|
||||
evmAddressWagmi,
|
||||
signerWagmi,
|
||||
publicClientWagmi,
|
||||
|
||||
|
||||
// Wallet connection (Cosmos)
|
||||
dydxAddress,
|
||||
dydxAddressGraz,
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.3811 13.235C11.9338 13.0512 13.4593 12.6847 14.9261 12.1433C13.6839 10.7673 12.9975 8.97875 13.0003 7.125V6.54167V6.5C13.0003 5.17392 12.4735 3.90215 11.5358 2.96447C10.5981 2.02678 9.32636 1.5 8.00028 1.5C6.67419 1.5 5.40242 2.02678 4.46474 2.96447C3.52706 3.90215 3.00028 5.17392 3.00028 6.5V7.125C3.00279 8.97886 2.3161 10.7675 1.07361 12.1433C2.51778 12.6767 4.04027 13.0475 5.61944 13.235M10.3811 13.235C8.79943 13.4226 7.20112 13.4226 5.61944 13.235M10.3811 13.235C10.5012 13.6099 10.5311 14.0078 10.4683 14.3964C10.4055 14.785 10.2518 15.1533 10.0198 15.4713C9.78772 15.7893 9.48386 16.048 9.13293 16.2263C8.78201 16.4047 8.39392 16.4976 8.00028 16.4976C7.60663 16.4976 7.21854 16.4047 6.86762 16.2263C6.51669 16.048 6.21283 15.7893 5.98079 15.4713C5.74875 15.1533 5.59508 14.785 5.53229 14.3964C5.46949 14.0078 5.49936 13.6099 5.61944 13.235" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 24"><path fill="currentColor" d="M11 0C8.5 0 6.101.948 4.333 2.636 2.565 4.324 1.57 6.613 1.57 9v5.379l-1.11 1.06c-.22.21-.37.477-.43.768-.061.291-.03.593.089.867s.32.508.578.673c.259.165.562.253.873.253h18.86c.31 0 .614-.088.873-.253.258-.165.46-.399.578-.673.12-.274.15-.576.09-.867a1.48 1.48 0 0 0-.43-.768l-1.111-1.06V9c0-2.387-.993-4.676-2.762-6.364C15.9.948 13.501 0 11 0Zm0 24a4.83 4.83 0 0 1-3.334-1.318 4.399 4.399 0 0 1-1.38-3.182h9.428a4.399 4.399 0 0 1-1.38 3.182A4.83 4.83 0 0 1 11 24Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 24"><path fill="currentColor" d="M11 0C8.5 0 6.101.948 4.333 2.636 2.565 4.324 1.57 6.613 1.57 9v5.379l-1.11 1.06c-.22.21-.37.477-.43.768-.061.291-.03.593.089.867s.32.508.578.673c.259.165.562.253.873.253h18.86c.31 0 .614-.088.873-.253.258-.165.46-.399.578-.673.12-.274.15-.576.09-.867a1.48 1.48 0 0 0-.43-.768l-1.111-1.06V9c0-2.387-.993-4.676-2.762-6.364C15.9.948 13.501 0 11 0Zm0 24a4.83 4.83 0 0 1-3.334-1.318 4.399 4.399 0 0 1-1.38-3.182h9.428a4.399 4.399 0 0 1-1.38 3.182A4.83 4.83 0 0 1 11 24Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 564 B After Width: | Height: | Size: 563 B |
@@ -1 +0,0 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5.625 6.1875h6.75m-6.75 2.25H9M1.6875 9.57c0 1.2.84225 2.2455 2.03025 2.4203.84675.1245 1.7025.2197 2.56725.2842.2625.0195.5025.1575.64875.3758L9 15.75l2.0663-3.0997c.0724-.1078.1685-.1977.2809-.2628.1125-.0651.2382-.1038.3678-.113.8589-.0641 1.7152-.1589 2.5672-.2842 1.188-.1748 2.0303-1.2195 2.0303-2.42105v-4.5135c0-1.2015-.8423-2.24625-2.0303-2.421A36.294434 36.294434 0 0 0 9 2.25c-1.794 0-3.558.13125-5.28225.38475-1.188.17475-2.03025 1.22025-2.03025 2.421V9.57Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
|
Before Width: | Height: | Size: 672 B |
@@ -1 +1 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M15 8C15 9.53527 14.6867 10.8758 14.2269 11.7954C13.77 12.7092 13.3116 12.9664 13.0527 12.9968L12.6636 12.8953C12.4116 12.7511 12.092 12.4331 11.7731 11.7954C11.3133 10.8758 11 9.53527 11 8C11 6.46473 11.3133 5.12424 11.7731 4.20457C12.0924 3.56607 12.4124 3.24815 12.6645 3.10417L13.0519 3.00311C13.3107 3.0331 13.7696 3.28991 14.2269 4.20457C14.6867 5.12424 15 6.46473 15 8ZM12.2928 2.16768L1.41963 5.00415C0.628571 5.08768 0 6.39699 0 7.99992C0 9.60346 0.629045 10.9131 1.42053 10.9958L2.50023 11.2774V12.9998C2.50023 13.8283 3.17181 14.4998 4.00023 14.4998C4.82866 14.4998 5.50023 13.8283 5.50023 12.9998V12.0601L12.2914 13.8317C12.5186 13.9417 12.7559 14 13 14C14.6569 14 16 11.3137 16 8C16 4.68629 14.6569 2 13 2C12.7564 2 12.5196 2.05808 12.2928 2.16768ZM10.9931 3.5402L2.49607 5.75681C2.80523 6.30634 3 7.10764 3 7.99992C3 8.89214 2.80525 9.69339 2.49614 10.2429L10.9929 12.4595C10.3833 11.3612 10 9.77013 10 8C10 6.22968 10.3834 4.63843 10.9931 3.5402ZM2 7.99992C2 8.70677 1.85462 9.29725 1.66623 9.67403C1.59961 9.80728 1.54246 9.88955 1.5 9.93955C1.45754 9.88955 1.40039 9.80728 1.33377 9.67403C1.14538 9.29725 1 8.70677 1 7.99992C1 7.29308 1.14538 6.70259 1.33377 6.32582C1.40039 6.19256 1.45754 6.11029 1.5 6.06029C1.54246 6.11029 1.59961 6.19256 1.66623 6.32582C1.85462 6.70259 2 7.29308 2 7.99992ZM1.41964 10.0124C1.41962 10.0124 1.42071 10.0118 1.42296 10.0109C1.42079 10.0121 1.41966 10.0125 1.41964 10.0124ZM1.57704 10.0109C1.57929 10.0118 1.58038 10.0124 1.58036 10.0124C1.58033 10.0125 1.57921 10.0121 1.57704 10.0109ZM1.58036 5.9874C1.58038 5.98747 1.57929 5.98808 1.57704 5.98899C1.57921 5.98778 1.58034 5.98732 1.58036 5.9874ZM1.42296 5.98899C1.42071 5.98808 1.41962 5.98747 1.41964 5.9874C1.41966 5.98732 1.42079 5.98778 1.42296 5.98899ZM3.50023 12.9998V11.5383L4.50023 11.7992V12.9998C4.50023 13.276 4.27638 13.4998 4.00023 13.4998C3.72409 13.4998 3.50023 13.276 3.50023 12.9998Z" fill="currentColor"/></svg>
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M15 8C15 9.53527 14.6867 10.8758 14.2269 11.7954C13.77 12.7092 13.3116 12.9664 13.0527 12.9968L12.6636 12.8953C12.4116 12.7511 12.092 12.4331 11.7731 11.7954C11.3133 10.8758 11 9.53527 11 8C11 6.46473 11.3133 5.12424 11.7731 4.20457C12.0924 3.56607 12.4124 3.24815 12.6645 3.10417L13.0519 3.00311C13.3107 3.0331 13.7696 3.28991 14.2269 4.20457C14.6867 5.12424 15 6.46473 15 8ZM12.2928 2.16768L1.41963 5.00415C0.628571 5.08768 0 6.39699 0 7.99992C0 9.60346 0.629045 10.9131 1.42053 10.9958L2.50023 11.2774V12.9998C2.50023 13.8283 3.17181 14.4998 4.00023 14.4998C4.82866 14.4998 5.50023 13.8283 5.50023 12.9998V12.0601L12.2914 13.8317C12.5186 13.9417 12.7559 14 13 14C14.6569 14 16 11.3137 16 8C16 4.68629 14.6569 2 13 2C12.7564 2 12.5196 2.05808 12.2928 2.16768ZM10.9931 3.5402L2.49607 5.75681C2.80523 6.30634 3 7.10764 3 7.99992C3 8.89214 2.80525 9.69339 2.49614 10.2429L10.9929 12.4595C10.3833 11.3612 10 9.77013 10 8C10 6.22968 10.3834 4.63843 10.9931 3.5402ZM2 7.99992C2 8.70677 1.85462 9.29725 1.66623 9.67403C1.59961 9.80728 1.54246 9.88955 1.5 9.93955C1.45754 9.88955 1.40039 9.80728 1.33377 9.67403C1.14538 9.29725 1 8.70677 1 7.99992C1 7.29308 1.14538 6.70259 1.33377 6.32582C1.40039 6.19256 1.45754 6.11029 1.5 6.06029C1.54246 6.11029 1.59961 6.19256 1.66623 6.32582C1.85462 6.70259 2 7.29308 2 7.99992ZM1.41964 10.0124C1.41962 10.0124 1.42071 10.0118 1.42296 10.0109C1.42079 10.0121 1.41966 10.0125 1.41964 10.0124ZM1.57704 10.0109C1.57929 10.0118 1.58038 10.0124 1.58036 10.0124C1.58033 10.0125 1.57921 10.0121 1.57704 10.0109ZM1.58036 5.9874C1.58038 5.98747 1.57929 5.98808 1.57704 5.98899C1.57921 5.98778 1.58034 5.98732 1.58036 5.9874ZM1.42296 5.98899C1.42071 5.98808 1.41962 5.98747 1.41964 5.9874C1.41966 5.98732 1.42079 5.98778 1.42296 5.98899ZM3.50023 12.9998V11.5383L4.50023 11.7992V12.9998C4.50023 13.276 4.27638 13.4998 4.00023 13.4998C3.72409 13.4998 3.50023 13.276 3.50023 12.9998Z" fill="currentColor"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
@@ -2,13 +2,11 @@ export { default as AddressConnectorIcon } from './address-connector.svg';
|
||||
export { default as ArrowIcon } from './arrow.svg';
|
||||
export { default as Bar3Icon } from './bar3.svg';
|
||||
export { default as BellIcon } from './bell.svg';
|
||||
export { default as BellStrokeIcon } from './bell-stroke.svg';
|
||||
export { default as BoxCloseIcon } from './box-close.svg';
|
||||
export { default as CalculatorIcon } from './calculator.svg';
|
||||
export { default as CaretIcon } from './caret-down.svg';
|
||||
export { default as CautionCircleIcon } from './caution-circle.svg';
|
||||
export { default as CautionCircleStrokeIcon } from './caution-circle-stroke.svg';
|
||||
export { default as ChatIcon } from './chat-bubble.svg';
|
||||
export { default as CheckIcon } from './check.svg';
|
||||
export { default as ChevronLeftIcon } from './chevron-left.svg';
|
||||
export { default as ChevronRightIcon } from './chevron-right.svg';
|
||||
@@ -35,7 +33,6 @@ export { default as LockIcon } from './lock.svg';
|
||||
export { default as LogoShortIcon } from './logo-short';
|
||||
export { default as MarketsIcon } from './markets.svg';
|
||||
export { default as MenuIcon } from './menu.svg';
|
||||
export { default as MigrateIcon } from './migrate.svg';
|
||||
export { default as MintscanIcon } from './logos/mintscan.svg';
|
||||
export { default as OrderbookIcon } from './orderbook.svg';
|
||||
export { default as OverviewIcon } from './overview.svg';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const LogoShortIcon: React.FC<{ id?: string }> = ({ id }: { id?: string }) => (
|
||||
const LogoShortIcon: React.FC<{ id?: string }> = ({ id }) => (
|
||||
<svg
|
||||
width="135"
|
||||
height="145"
|
||||
|
||||
|
Before Width: | Height: | Size: 694 KiB |
@@ -11,16 +11,12 @@ import { DepositDialog } from '@/views/dialogs/DepositDialog';
|
||||
import { DisconnectDialog } from '@/views/dialogs/DisconnectDialog';
|
||||
import { ExchangeOfflineDialog } from '@/views/dialogs/ExchangeOfflineDialog';
|
||||
import { HelpDialog } from '@/views/dialogs/HelpDialog';
|
||||
import { ExternalNavKeplrDialog } from '@/views/dialogs/ExternalNavKeplrDialog';
|
||||
import { MnemonicExportDialog } from '@/views/dialogs/MnemonicExportDialog';
|
||||
import { MobileSignInDialog } from '@/views/dialogs/MobileSignInDialog';
|
||||
import { OnboardingDialog } from '@/views/dialogs/OnboardingDialog';
|
||||
import { RateLimitDialog } from '@/views/dialogs/RateLimitDialog';
|
||||
import { ReceiveDialog } from '@/views/dialogs/ReceiveDialog';
|
||||
import { RestrictedGeoDialog } from '@/views/dialogs/RestrictedGeoDialog';
|
||||
import { TradeDialog } from '@/views/dialogs/TradeDialog';
|
||||
import { TransferDialog } from '@/views/dialogs/TransferDialog';
|
||||
import { RestrictedWalletDialog } from '@/views/dialogs/RestrictedWalletDialog';
|
||||
import { WithdrawDialog } from '@/views/dialogs/WithdrawDialog';
|
||||
|
||||
import { OrderDetailsDialog } from '@/views/dialogs/DetailsDialog/OrderDetailsDialog';
|
||||
@@ -51,15 +47,11 @@ export const DialogManager = () => {
|
||||
[DialogTypes.ExchangeOffline]: <ExchangeOfflineDialog {...modalProps} />,
|
||||
[DialogTypes.FillDetails]: <FillDetailsDialog {...modalProps} />,
|
||||
[DialogTypes.Help]: <HelpDialog {...modalProps} />,
|
||||
[DialogTypes.ExternalNavKeplr]: <ExternalNavKeplrDialog {...modalProps} />,
|
||||
[DialogTypes.MnemonicExport]: <MnemonicExportDialog {...modalProps} />,
|
||||
[DialogTypes.MobileSignIn]: <MobileSignInDialog {...modalProps} />,
|
||||
[DialogTypes.Onboarding]: <OnboardingDialog {...modalProps} />,
|
||||
[DialogTypes.OrderDetails]: <OrderDetailsDialog {...modalProps} />,
|
||||
[DialogTypes.RateLimit]: <RateLimitDialog {...modalProps} />,
|
||||
[DialogTypes.Receive]: <ReceiveDialog {...modalProps} />,
|
||||
[DialogTypes.RestrictedGeo]: <RestrictedGeoDialog {...modalProps} />,
|
||||
[DialogTypes.RestrictedWallet]: <RestrictedWalletDialog {...modalProps} />,
|
||||
[DialogTypes.Trade]: <TradeDialog {...modalProps} />,
|
||||
[DialogTypes.Transfer]: <TransferDialog {...modalProps} />,
|
||||
[DialogTypes.Withdraw]: <WithdrawDialog {...modalProps} />,
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import styled, { type AnyStyledComponent, css } from 'styled-components';
|
||||
|
||||
import { AbacusApiStatus } from '@/constants/abacus';
|
||||
import { ButtonSize } from '@/constants/buttons';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
|
||||
import { useApiState, useSelectedNetwork, useStringGetter } from '@/hooks';
|
||||
import { ChatIcon, LinkOutIcon } from '@/icons';
|
||||
import { useApiState, useStringGetter } from '@/hooks';
|
||||
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { Button } from '@/components/Button';
|
||||
import { Details } from '@/components/Details';
|
||||
import { Output, OutputType } from '@/components/Output';
|
||||
import { WithTooltip } from '@/components/WithTooltip';
|
||||
@@ -28,8 +24,6 @@ enum ExchangeStatus {
|
||||
export const FooterDesktop = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
const { height, indexerHeight, status, statusErrorMessage } = useApiState();
|
||||
const { selectedNetwork } = useSelectedNetwork();
|
||||
const { statusPage } = ENVIRONMENT_CONFIG_MAP[selectedNetwork].links;
|
||||
|
||||
const { exchangeStatus, label } =
|
||||
!status || status === AbacusApiStatus.NORMAL
|
||||
@@ -44,37 +38,20 @@ export const FooterDesktop = () => {
|
||||
|
||||
return (
|
||||
<Styled.Footer>
|
||||
<Styled.Row>
|
||||
<WithTooltip
|
||||
slotTooltip={
|
||||
statusErrorMessage && (
|
||||
<dl>
|
||||
<dd>{statusErrorMessage}</dd>
|
||||
</dl>
|
||||
)
|
||||
}
|
||||
>
|
||||
<Styled.FooterButton
|
||||
slotLeft={<Styled.StatusDot exchangeStatus={exchangeStatus} />}
|
||||
slotRight={statusPage && <LinkOutIcon />}
|
||||
size={ButtonSize.XSmall}
|
||||
state={{ isDisabled: !statusPage }}
|
||||
>
|
||||
{label}
|
||||
</Styled.FooterButton>
|
||||
</WithTooltip>
|
||||
|
||||
{globalThis?.Intercom && (
|
||||
<Styled.FooterButton
|
||||
slotLeft={<ChatIcon />}
|
||||
size={ButtonSize.XSmall}
|
||||
onClick={() => globalThis.Intercom('show')}
|
||||
>
|
||||
{stringGetter({ key: STRING_KEYS.HELP_AND_SUPPORT })}
|
||||
</Styled.FooterButton>
|
||||
)}
|
||||
</Styled.Row>
|
||||
|
||||
<WithTooltip
|
||||
slotTooltip={
|
||||
statusErrorMessage && (
|
||||
<dl>
|
||||
<dd>{statusErrorMessage}</dd>
|
||||
</dl>
|
||||
)
|
||||
}
|
||||
>
|
||||
<Styled.Row>
|
||||
<Styled.StatusDot exchangeStatus={exchangeStatus} />
|
||||
<span>{label}</span>
|
||||
</Styled.Row>
|
||||
</WithTooltip>
|
||||
{import.meta.env.MODE !== 'production' && (
|
||||
<Styled.Details
|
||||
withSeparators
|
||||
@@ -105,22 +82,20 @@ Styled.Footer = styled.footer`
|
||||
${layoutMixins.stickyFooter}
|
||||
${layoutMixins.spacedRow}
|
||||
grid-area: Footer;
|
||||
|
||||
font-size: 0.66em;
|
||||
`;
|
||||
|
||||
Styled.Row = styled.div`
|
||||
${layoutMixins.row}
|
||||
${layoutMixins.spacedRow}
|
||||
width: var(--sidebar-width);
|
||||
|
||||
padding: 0 0.5rem;
|
||||
border-right: 1px solid var(--color-border);
|
||||
padding: 0 1rem;
|
||||
gap: 0.5rem;
|
||||
`;
|
||||
|
||||
Styled.StatusDot = styled.div<{ exchangeStatus: ExchangeStatus }>`
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border-radius: 50%;
|
||||
margin-right: 0.25rem;
|
||||
|
||||
background-color: ${({ exchangeStatus }) =>
|
||||
({
|
||||
@@ -129,28 +104,12 @@ Styled.StatusDot = styled.div<{ exchangeStatus: ExchangeStatus }>`
|
||||
}[exchangeStatus])};
|
||||
`;
|
||||
|
||||
Styled.FooterButton = styled(Button)`
|
||||
--button-height: 1.5rem;
|
||||
--button-radius: 0.25rem;
|
||||
--button-backgroundColor: var(--color-layer-2);
|
||||
--button-border: none;
|
||||
--button-textColor: var(--color-text-0);
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
--button-backgroundColor: var(--color-layer-3);
|
||||
--button-textColor: var(--color-text-1);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.WarningOutput = styled(Output)`
|
||||
color: var(--color-warning);
|
||||
`;
|
||||
|
||||
Styled.Details = styled(Details)`
|
||||
${layoutMixins.scrollArea}
|
||||
font: var(--font-tiny-book);
|
||||
|
||||
padding: 0 0.5em;
|
||||
`;
|
||||
|
||||
@@ -6,11 +6,11 @@ import { ButtonShape } from '@/constants/buttons';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { AppRoute } from '@/constants/routes';
|
||||
import { LogoShortIcon, BellStrokeIcon } from '@/icons';
|
||||
|
||||
import { useTokenConfigs, useStringGetter, useURLConfigs } from '@/hooks';
|
||||
import { useStringGetter } from '@/hooks';
|
||||
import { LogoShortIcon } from '@/icons';
|
||||
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
import { BellIcon } from '@/icons';
|
||||
import { IconButton } from '@/components/IconButton';
|
||||
import { NavigationMenu } from '@/components/NavigationMenu';
|
||||
import { VerticalSeparator } from '@/components/Separator';
|
||||
@@ -24,18 +24,20 @@ import { openDialog } from '@/state/dialogs';
|
||||
|
||||
import { headerMixins } from '@/styles/headerMixins';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import breakpoints from '@/styles/breakpoints';
|
||||
|
||||
export const HeaderDesktop = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
const { documentation, community, mintscanBase } = useURLConfigs();
|
||||
const dispatch = useDispatch();
|
||||
const { chainTokenLabel } = useTokenConfigs();
|
||||
|
||||
const navItems = [
|
||||
{
|
||||
group: 'navigation',
|
||||
items: [
|
||||
{
|
||||
value: 'MARKET',
|
||||
label: stringGetter({ key: STRING_KEYS.MARKET }),
|
||||
href: AppRoute.Markets,
|
||||
},
|
||||
{
|
||||
value: 'TRADE',
|
||||
label: stringGetter({ key: STRING_KEYS.TRADE }),
|
||||
@@ -46,16 +48,6 @@ export const HeaderDesktop = () => {
|
||||
label: stringGetter({ key: STRING_KEYS.PORTFOLIO }),
|
||||
href: AppRoute.Portfolio,
|
||||
},
|
||||
{
|
||||
value: 'MARKETS',
|
||||
label: stringGetter({ key: STRING_KEYS.MARKETS }),
|
||||
href: AppRoute.Markets,
|
||||
},
|
||||
{
|
||||
value: chainTokenLabel,
|
||||
label: chainTokenLabel,
|
||||
href: `/${chainTokenLabel}`,
|
||||
},
|
||||
{
|
||||
value: 'MORE',
|
||||
label: stringGetter({ key: STRING_KEYS.MORE }),
|
||||
@@ -64,31 +56,31 @@ export const HeaderDesktop = () => {
|
||||
value: 'DOCUMENTATION',
|
||||
slotBefore: <Icon iconName={IconName.Terminal} />,
|
||||
label: stringGetter({ key: STRING_KEYS.DOCUMENTATION }),
|
||||
href: documentation,
|
||||
href: 'https://v4-teacher.vercel.app/',
|
||||
},
|
||||
{
|
||||
value: 'MINTSCAN',
|
||||
slotBefore: <Icon iconName={IconName.Mintscan} />,
|
||||
label: stringGetter({ key: STRING_KEYS.MINTSCAN }),
|
||||
href: mintscanBase,
|
||||
href: 'https://testnet.mintscan.io/dydx-testnet',
|
||||
},
|
||||
{
|
||||
value: 'COMMUNITY',
|
||||
slotBefore: <Icon iconName={IconName.Discord} />,
|
||||
label: stringGetter({ key: STRING_KEYS.COMMUNITY }),
|
||||
href: community,
|
||||
href: 'https://discord.gg/dydx',
|
||||
},
|
||||
{
|
||||
value: 'TERMS_OF_USE',
|
||||
slotBefore: <Icon iconName={IconName.File} />,
|
||||
label: stringGetter({ key: STRING_KEYS.TERMS_OF_USE }),
|
||||
href: AppRoute.Terms,
|
||||
href: 'https://dydx.exchange/terms',
|
||||
},
|
||||
{
|
||||
value: 'PRIVACY_POLICY',
|
||||
slotBefore: <Icon iconName={IconName.Privacy} />,
|
||||
label: stringGetter({ key: STRING_KEYS.PRIVACY_POLICY }),
|
||||
href: AppRoute.Privacy,
|
||||
href: 'https://dydx.exchange/privacy',
|
||||
},
|
||||
{
|
||||
value: 'HELP',
|
||||
@@ -135,9 +127,7 @@ export const HeaderDesktop = () => {
|
||||
<VerticalSeparator />
|
||||
|
||||
<NotificationsMenu
|
||||
slotTrigger={
|
||||
<Styled.IconButton shape={ButtonShape.Rectangle} iconComponent={BellStrokeIcon} />
|
||||
}
|
||||
slotTrigger={<Styled.IconButton shape={ButtonShape.Rectangle} iconComponent={BellIcon} />}
|
||||
/>
|
||||
|
||||
<VerticalSeparator />
|
||||
@@ -152,7 +142,6 @@ const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Header = styled.header`
|
||||
--header-horizontal-padding-mobile: 0.5rem;
|
||||
--trigger-height: 2.25rem;
|
||||
--logo-width: 3.5rem;
|
||||
|
||||
${layoutMixins.container}
|
||||
@@ -160,8 +149,6 @@ Styled.Header = styled.header`
|
||||
${layoutMixins.scrollSnapItem}
|
||||
backdrop-filter: none;
|
||||
|
||||
height: var(--page-currentHeaderHeight);
|
||||
|
||||
grid-area: Header;
|
||||
|
||||
display: grid;
|
||||
@@ -174,15 +161,7 @@ Styled.Header = styled.header`
|
||||
)
|
||||
var(--border-width) 1fr var(--border-width) auto;
|
||||
|
||||
@media ${breakpoints.tablet} {
|
||||
--trigger-height: 3rem;
|
||||
}
|
||||
|
||||
@media ${breakpoints.mobile} {
|
||||
--navBefore-width: 7rem;
|
||||
}
|
||||
|
||||
font-size: 0.9375rem;
|
||||
font-size: 0.9375em;
|
||||
|
||||
:before {
|
||||
backdrop-filter: blur(10px);
|
||||
@@ -192,7 +171,6 @@ Styled.Header = styled.header`
|
||||
Styled.NavigationMenu = styled(NavigationMenu)`
|
||||
& {
|
||||
--navigationMenu-height: var(--stickyArea-topHeight);
|
||||
--navigationMenu-item-height: var(--trigger-height);
|
||||
}
|
||||
|
||||
${layoutMixins.scrollArea}
|
||||
@@ -232,8 +210,7 @@ Styled.NavAfter = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.IconButton = styled(IconButton)<{ size?: string }>`
|
||||
Styled.IconButton = styled(IconButton)`
|
||||
${headerMixins.button}
|
||||
--button-border: none;
|
||||
--button-icon-size: 1rem;
|
||||
`;
|
||||
|
||||
@@ -15,20 +15,12 @@ type 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();
|
||||
|
||||
return (
|
||||
<StyledToastArea swipeDirection={isMobile ? 'up' : 'right'} className={className}>
|
||||
<$ToastArea swipeDirection={isMobile ? 'up' : 'right'} className={className}>
|
||||
{Object.values(notifications)
|
||||
// Sort by time of first trigger
|
||||
.sort(
|
||||
@@ -61,20 +53,19 @@ export const NotificationsToastArea = ({ className }: StyleProps) => {
|
||||
sensitivity={displayData.toastSensitivity}
|
||||
setIsOpen={(isOpen, isClosedFromTimeout) => {
|
||||
if (!isOpen)
|
||||
if (isClosedFromTimeout)
|
||||
// Toast timer expired without user interaction
|
||||
markUnseen(notification);
|
||||
// Toast timer expired without user interaction
|
||||
if (isClosedFromTimeout) markUnseen(notification);
|
||||
// Toast interacted with or dismissed
|
||||
else markSeen(notification);
|
||||
}}
|
||||
lastUpdated={notification.timestamps[notification.status]}
|
||||
/>
|
||||
))}
|
||||
</StyledToastArea>
|
||||
</$ToastArea>
|
||||
);
|
||||
};
|
||||
|
||||
const StyledToastArea = styled(ToastArea)`
|
||||
const $ToastArea = styled(ToastArea)`
|
||||
position: absolute;
|
||||
width: min(16rem, 100%);
|
||||
inset: 0 0 0 auto;
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import type { AbacusTrackingProtocol, Nullable } from '@/constants/abacus';
|
||||
import type { AnalyticsEvent } from '@/constants/analytics';
|
||||
|
||||
import { track } from '../analytics';
|
||||
import { log as telemetryLog } from '../telemetry';
|
||||
|
||||
class AbacusAnalytics implements AbacusTrackingProtocol {
|
||||
log(event: string, data: Nullable<string>) {
|
||||
try {
|
||||
const parsedData = data ? JSON.parse(data) : {};
|
||||
track(event as AnalyticsEvent, parsedData);
|
||||
} catch (error) {
|
||||
telemetryLog('AbacusAnalytics/log', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default AbacusAnalytics;
|
||||
@@ -1,118 +1,62 @@
|
||||
import Abacus, { type Nullable } from '@dydxprotocol/v4-abacus';
|
||||
import Abacus, { Nullable } from '@dydxprotocol/abacus';
|
||||
import Long from 'long';
|
||||
import type { IndexedTx } from '@cosmjs/stargate';
|
||||
|
||||
import {
|
||||
CompositeClient,
|
||||
IndexerConfig,
|
||||
type LocalWallet,
|
||||
Network,
|
||||
NetworkOptimizer,
|
||||
SubaccountClient,
|
||||
ValidatorConfig,
|
||||
OrderType,
|
||||
OrderSide,
|
||||
OrderTimeInForce,
|
||||
OrderExecution,
|
||||
} from '@dydxprotocol/v4-client-js';
|
||||
|
||||
import {
|
||||
type AbacusDYDXChainTransactionsProtocol,
|
||||
QueryType,
|
||||
type QueryTypes,
|
||||
TransactionType,
|
||||
type TransactionTypes,
|
||||
type HumanReadablePlaceOrderPayload,
|
||||
type HumanReadableCancelOrderPayload,
|
||||
type HumanReadableWithdrawPayload,
|
||||
type HumanReadableTransferPayload,
|
||||
} 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';
|
||||
|
||||
class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
||||
private compositeClient: CompositeClient | undefined;
|
||||
private store: RootStore | undefined;
|
||||
private localWallet: LocalWallet | undefined;
|
||||
|
||||
constructor() {
|
||||
this.compositeClient = undefined;
|
||||
this.store = undefined;
|
||||
}
|
||||
|
||||
setStore(store: RootStore): void {
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
setLocalWallet(localWallet: LocalWallet) {
|
||||
this.localWallet = localWallet;
|
||||
}
|
||||
|
||||
async connectNetwork(
|
||||
paramsInJson: Nullable<string>,
|
||||
indexerUrl: string,
|
||||
indexerSocketUrl: string,
|
||||
validatorUrl: string,
|
||||
chainId: string,
|
||||
faucetUrl: Nullable<string> | undefined,
|
||||
callback: (p0: Nullable<string>) => void
|
||||
): Promise<void> {
|
||||
try {
|
||||
const parsedParams = paramsInJson ? JSON.parse(paramsInJson) : {};
|
||||
const {
|
||||
this.compositeClient = await CompositeClient.connect(
|
||||
new Network(
|
||||
chainId,
|
||||
new IndexerConfig(indexerUrl, indexerSocketUrl),
|
||||
new ValidatorConfig(validatorUrl, chainId, {
|
||||
broadcastPollIntervalMs: 3_000,
|
||||
broadcastTimeoutMs: 60_000,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
// Dispatch custom event to notify other parts of the app that the network has been connected
|
||||
const customEvent = new CustomEvent('abacus:connectNetwork', {
|
||||
detail: {
|
||||
indexerUrl,
|
||||
websocketUrl,
|
||||
indexerSocketUrl,
|
||||
validatorUrl,
|
||||
chainId,
|
||||
USDC_DENOM,
|
||||
USDC_DECIMALS,
|
||||
USDC_GAS_DENOM,
|
||||
CHAINTOKEN_DENOM,
|
||||
CHAINTOKEN_DECIMALS,
|
||||
} = parsedParams;
|
||||
faucetUrl,
|
||||
},
|
||||
});
|
||||
|
||||
const compositeClient = await CompositeClient.connect(
|
||||
new Network(
|
||||
chainId,
|
||||
new IndexerConfig(indexerUrl, websocketUrl),
|
||||
new ValidatorConfig(
|
||||
validatorUrl,
|
||||
chainId,
|
||||
{
|
||||
USDC_DENOM,
|
||||
USDC_DECIMALS,
|
||||
USDC_GAS_DENOM,
|
||||
CHAINTOKEN_DENOM,
|
||||
CHAINTOKEN_DECIMALS,
|
||||
},
|
||||
{
|
||||
broadcastPollIntervalMs: 3_000,
|
||||
broadcastTimeoutMs: 60_000,
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
this.compositeClient = compositeClient;
|
||||
|
||||
// Dispatch custom event to notify other parts of the app that the network has been connected
|
||||
const customEvent = new CustomEvent('abacus:connectNetwork', {
|
||||
detail: parsedParams,
|
||||
});
|
||||
|
||||
globalThis.dispatchEvent(customEvent);
|
||||
callback(JSON.stringify({ success: true }));
|
||||
} catch (error) {
|
||||
this.store?.dispatch(
|
||||
openDialog({ type: DialogTypes.ExchangeOffline, dialogProps: { preventClose: true } })
|
||||
);
|
||||
log('DydxChainTransactions/connectNetwork', error);
|
||||
}
|
||||
globalThis.dispatchEvent(customEvent);
|
||||
callback(JSON.stringify({ success: true }));
|
||||
}
|
||||
|
||||
parseToPrimitives<T>(x: T): T {
|
||||
@@ -128,10 +72,6 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
||||
return x.toString() as T;
|
||||
}
|
||||
|
||||
if (x instanceof Uint8Array) {
|
||||
return bytesToBigInt(x).toString() as T;
|
||||
}
|
||||
|
||||
if (typeof x === 'object') {
|
||||
const parsedObj: { [key: string]: any } = {};
|
||||
for (const key in x) {
|
||||
@@ -145,222 +85,14 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
||||
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?.cancelRawOrder(
|
||||
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) => {
|
||||
if (!this.localWallet) {
|
||||
throw new Error('Missing compositeClient or localWallet');
|
||||
}
|
||||
const msg = compositeClient?.sendTokenMessage(
|
||||
this.localWallet,
|
||||
amount,
|
||||
recipient,
|
||||
);
|
||||
|
||||
resolve([msg]);
|
||||
}),
|
||||
this.compositeClient?.validatorClient?.post.defaultDydxGasPrice,
|
||||
);
|
||||
|
||||
const parsedTx = this.parseToPrimitives(tx);
|
||||
|
||||
return JSON.stringify(parsedTx);
|
||||
} catch (error) {
|
||||
log('DydxChainTransactions/simulateTransferNativeTokenTransaction', error);
|
||||
|
||||
return JSON.stringify({
|
||||
error,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async transaction(
|
||||
type: TransactionTypes,
|
||||
paramsInJson: Abacus.Nullable<string>,
|
||||
callback: (p0: Abacus.Nullable<string>) => void
|
||||
): Promise<void> {
|
||||
try {
|
||||
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);
|
||||
}
|
||||
// To be implemented
|
||||
return;
|
||||
}
|
||||
|
||||
async get(
|
||||
@@ -384,12 +116,6 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
||||
);
|
||||
callback(JSON.stringify({ url: optimalNode }));
|
||||
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:
|
||||
const feeTiers = await this.compositeClient?.validatorClient.get.getFeeTiers();
|
||||
const parsedFeeTiers = this.parseToPrimitives(feeTiers);
|
||||
@@ -409,31 +135,6 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
||||
const parsedUserStats = this.parseToPrimitives(userStats);
|
||||
callback(JSON.stringify(parsedUserStats));
|
||||
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)
|
||||
case QueryType.Transfers:
|
||||
default:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { AbacusFileSystemProtocol, FileLocation, Nullable } from '@/constants/abacus';
|
||||
import { NETWORK_ENDPOINTS } from '@/constants/networks';
|
||||
|
||||
export const ENDPOINTS_PATH =
|
||||
import.meta.env.MODE === 'production'
|
||||
@@ -7,6 +8,10 @@ export const ENDPOINTS_PATH =
|
||||
|
||||
class AbacusFileSystem implements AbacusFileSystemProtocol {
|
||||
readTextFile(location: FileLocation, path: string): Nullable<string> {
|
||||
if (path === ENDPOINTS_PATH) {
|
||||
return JSON.stringify(NETWORK_ENDPOINTS);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import type { LocalWallet } from '@dydxprotocol/v4-client-js';
|
||||
|
||||
import type {
|
||||
ClosePositionInputFields,
|
||||
Nullable,
|
||||
@@ -8,7 +6,6 @@ import type {
|
||||
TradeInputFields,
|
||||
TransferInputFields,
|
||||
HistoricalPnlPeriods,
|
||||
ParsingError,
|
||||
} from '@/constants/abacus';
|
||||
|
||||
import {
|
||||
@@ -22,11 +19,10 @@ import {
|
||||
UIImplementations,
|
||||
CoroutineTimer,
|
||||
TransferType,
|
||||
AbacusAppConfig,
|
||||
} from '@/constants/abacus';
|
||||
|
||||
import { DEFAULT_MARKETID } from '@/constants/markets';
|
||||
import { CURRENT_ABACUS_DEPLOYMENT, type DydxNetwork } from '@/constants/networks';
|
||||
import { type DydxNetwork } from '@/constants/networks';
|
||||
|
||||
import type { RootStore } from '@/state/_store';
|
||||
|
||||
@@ -34,13 +30,13 @@ import { getInputTradeOptions } from '@/state/inputsSelectors';
|
||||
import { getTransferInputs } from '@/state/inputsSelectors';
|
||||
|
||||
import AbacusRest from './rest';
|
||||
import AbacusAnalytics from './analytics';
|
||||
import AbacusWebsocket from './websocket';
|
||||
import AbacusChainTransaction from './dydxChainTransactions';
|
||||
import AbacusStateNotifier from './stateNotification';
|
||||
import AbacusLocalizer from './localizer';
|
||||
import AbacusFormatter from './formatter';
|
||||
import AbacusThreading from './threading';
|
||||
import AbacusTracking from './tracking';
|
||||
import AbacusFileSystem, { ENDPOINTS_PATH } from './filesystem';
|
||||
import { LocaleSeparators } from '../numbers';
|
||||
class AbacusStateManager {
|
||||
@@ -50,25 +46,21 @@ class AbacusStateManager {
|
||||
stateManager: InstanceType<typeof AsyncAbacusStateManager>;
|
||||
websocket: AbacusWebsocket;
|
||||
stateNotifier: AbacusStateNotifier;
|
||||
analytics: AbacusAnalytics;
|
||||
abacusFormatter: AbacusFormatter;
|
||||
chainTransactions: AbacusChainTransaction;
|
||||
|
||||
constructor() {
|
||||
this.store = undefined;
|
||||
this.currentMarket = undefined;
|
||||
this.stateNotifier = new AbacusStateNotifier();
|
||||
this.analytics = new AbacusAnalytics();
|
||||
this.websocket = new AbacusWebsocket();
|
||||
this.abacusFormatter = new AbacusFormatter();
|
||||
this.chainTransactions = new AbacusChainTransaction();
|
||||
|
||||
const ioImplementations = new IOImplementations(
|
||||
// @ts-ignore
|
||||
new AbacusRest(),
|
||||
this.websocket,
|
||||
this.chainTransactions,
|
||||
this.analytics,
|
||||
new AbacusChainTransaction(),
|
||||
new AbacusTracking(),
|
||||
new AbacusThreading(),
|
||||
new CoroutineTimer(),
|
||||
new AbacusFileSystem()
|
||||
@@ -81,9 +73,8 @@ class AbacusStateManager {
|
||||
);
|
||||
|
||||
this.stateManager = new AsyncAbacusStateManager(
|
||||
'',
|
||||
CURRENT_ABACUS_DEPLOYMENT,
|
||||
AbacusAppConfig.Companion.forWeb,
|
||||
import.meta.env.SHARED_RESOURCES_URI,
|
||||
ENDPOINTS_PATH,
|
||||
ioImplementations,
|
||||
uiImplementations,
|
||||
// @ts-ignore
|
||||
@@ -165,14 +156,10 @@ class AbacusStateManager {
|
||||
setStore = (store: RootStore) => {
|
||||
this.store = store;
|
||||
this.stateNotifier.setStore(store);
|
||||
this.chainTransactions.setStore(store);
|
||||
};
|
||||
|
||||
setAccount = (localWallet?: LocalWallet) => {
|
||||
if (localWallet) {
|
||||
this.stateManager.accountAddress = localWallet.address;
|
||||
this.chainTransactions.setLocalWallet(localWallet);
|
||||
}
|
||||
setAccount = (walletAddress: string) => {
|
||||
this.stateManager.accountAddress = walletAddress;
|
||||
};
|
||||
|
||||
setTransfersSourceAddress = (evmAddress: string) => {
|
||||
@@ -230,34 +217,16 @@ class AbacusStateManager {
|
||||
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 ------ //
|
||||
placeOrderPayload = (): Nullable<HumanReadablePlaceOrderPayload> =>
|
||||
this.stateManager.placeOrderPayload();
|
||||
|
||||
closePositionPayload = (): Nullable<HumanReadablePlaceOrderPayload> =>
|
||||
this.stateManager.closePositionPayload();
|
||||
|
||||
cancelOrderPayload = (orderId: string): Nullable<HumanReadableCancelOrderPayload> =>
|
||||
this.stateManager.cancelOrderPayload(orderId);
|
||||
|
||||
getHistoricalPnlPeriod = (): Nullable<HistoricalPnlPeriods> =>
|
||||
this.stateManager.historicalPnlPeriod;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Nullable, kollections } from '@dydxprotocol/v4-abacus';
|
||||
import type { Nullable, kollections } from '@dydxprotocol/abacus';
|
||||
|
||||
import type { AbacusRestProtocol } from '@/constants/abacus';
|
||||
|
||||
@@ -47,11 +47,6 @@ class AbacusRest implements AbacusRestProtocol {
|
||||
body,
|
||||
};
|
||||
|
||||
if (!url) {
|
||||
callback(null, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(url, options)
|
||||
.then(async (response) => {
|
||||
const data = await response.text();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { kollections } from '@dydxprotocol/v4-abacus';
|
||||
import { kollections } from '@dydxprotocol/abacus';
|
||||
|
||||
import type {
|
||||
AccountBalance,
|
||||
AbacusApiState,
|
||||
AbacusNotification,
|
||||
AbacusStateNotificationProtocol,
|
||||
@@ -19,13 +18,9 @@ import { Changes } from '@/constants/abacus';
|
||||
import type { RootStore } from '@/state/_store';
|
||||
|
||||
import {
|
||||
setBalances,
|
||||
setStakingBalances,
|
||||
setFills,
|
||||
setFundingPayments,
|
||||
setHistoricalPnl,
|
||||
setLatestOrder,
|
||||
setRestrictionType,
|
||||
setSubaccount,
|
||||
setTransfers,
|
||||
setWallet,
|
||||
@@ -36,7 +31,6 @@ import { setAssets } from '@/state/assets';
|
||||
import { setConfigs } from '@/state/configs';
|
||||
import { setInputs } from '@/state/inputs';
|
||||
import { setHistoricalFundings, setLiveTrades, setMarkets, setOrderbook } from '@/state/perpetuals';
|
||||
import { isTruthy } from '../isTruthy';
|
||||
|
||||
class AbacusStateNotifier implements AbacusStateNotificationProtocol {
|
||||
private store: RootStore | undefined;
|
||||
@@ -77,23 +71,6 @@ class AbacusStateNotifier implements AbacusStateNotificationProtocol {
|
||||
);
|
||||
}
|
||||
|
||||
if (changes.has(Changes.accountBalances)) {
|
||||
if (updatedState.account?.balances) {
|
||||
const balances: Record<string, AccountBalance> = {};
|
||||
for (const { k, v } of updatedState.account.balances.toArray()) {
|
||||
balances[k] = v;
|
||||
}
|
||||
dispatch(setBalances(balances));
|
||||
}
|
||||
if (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)) {
|
||||
dispatch(setConfigs(updatedState.configs));
|
||||
}
|
||||
@@ -110,22 +87,16 @@ class AbacusStateNotifier implements AbacusStateNotificationProtocol {
|
||||
dispatch(
|
||||
setMarkets({
|
||||
markets: Object.fromEntries(
|
||||
(marketIds || updatedState.marketIds()?.toArray() || [])
|
||||
.map((marketId: string) => {
|
||||
const marketData = updatedState.market(marketId);
|
||||
return [marketId, marketData];
|
||||
})
|
||||
.filter(isTruthy)
|
||||
(marketIds || updatedState.marketIds()?.toArray() || []).map((marketId: string) => {
|
||||
const marketData = updatedState.market(marketId);
|
||||
return [marketId, marketData];
|
||||
})
|
||||
) as Record<string, PerpetualMarket>,
|
||||
update: !!marketIds,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (changes.has(Changes.restriction)) {
|
||||
dispatch(setRestrictionType(updatedState.restriction));
|
||||
}
|
||||
|
||||
subaccountNumbers?.forEach((subaccountId: number) => {
|
||||
if (subaccountId !== null) {
|
||||
if (changes.has(Changes.subaccount)) {
|
||||
@@ -184,9 +155,7 @@ class AbacusStateNotifier implements AbacusStateNotificationProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
lastOrderChanged(order: SubaccountOrder) {
|
||||
this.store?.dispatch(setLatestOrder(order));
|
||||
}
|
||||
lastOrderChanged(order: SubaccountOrder) {}
|
||||
|
||||
errorsEmitted(errors: ParsingErrors) {
|
||||
console.error('parse errors', errors.toArray());
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { AbacusTrackingProtocol, Nullable } from '@/constants/abacus';
|
||||
|
||||
class AbacusTracking implements AbacusTrackingProtocol {
|
||||
log(event: string, data: Nullable<string>) {
|
||||
console.log({ event, data });
|
||||
}
|
||||
}
|
||||
|
||||
export default AbacusTracking;
|
||||
@@ -5,15 +5,10 @@ import type {
|
||||
AnalyticsEventData,
|
||||
} from '@/constants/analytics';
|
||||
|
||||
const DEBUG_ANALYTICS = false;
|
||||
|
||||
export const identify = <T extends AnalyticsUserProperty>(
|
||||
property: T,
|
||||
propertyValue: AnalyticsUserPropertyValue<T>
|
||||
) => {
|
||||
if (DEBUG_ANALYTICS) {
|
||||
console.log(`[Analytics:Identify] ${property}`, propertyValue);
|
||||
}
|
||||
const customEvent = new CustomEvent('dydx:identify', {
|
||||
detail: { property, propertyValue },
|
||||
});
|
||||
@@ -25,9 +20,6 @@ export const track = <T extends AnalyticsEvent>(
|
||||
eventType: T,
|
||||
eventData?: AnalyticsEventData<T>
|
||||
) => {
|
||||
if (DEBUG_ANALYTICS) {
|
||||
console.log(`[Analytics] ${eventType}`, eventData);
|
||||
}
|
||||
const customEvent = new CustomEvent('dydx:track', {
|
||||
detail: { eventType, eventData },
|
||||
});
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
import { type DydxNetwork, ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
|
||||
export const validateAgainstAvailableEnvironments = (value: DydxNetwork) =>
|
||||
Object.keys(ENVIRONMENT_CONFIG_MAP).includes(value);
|
||||
@@ -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
|
||||
*/
|
||||
export const shortenNumberForDisplay = (num?: number) =>
|
||||
export const shorternNumberForDisplay = (num?: number) =>
|
||||
MustBigNumber(num).eq(0) ? null : MustBigNumber(num).gt(99) ? '99+' : num;
|
||||
|
||||
/**
|
||||
@@ -66,17 +66,3 @@ export const getSeparator = ({
|
||||
Intl.NumberFormat(browserLanguage)
|
||||
.formatToParts(1000.1)
|
||||
.find?.((part) => part.type === separatorType)?.value;
|
||||
|
||||
/**
|
||||
* Converts a byte array (representing an arbitrary-size signed integer) into a bigint.
|
||||
* @param u Array of bytes represented as a Uint8Array.
|
||||
*/
|
||||
export function bytesToBigInt(u: Uint8Array): bigint {
|
||||
if (u.length <= 1) {
|
||||
return BigInt(0);
|
||||
}
|
||||
const negated: boolean = (u[0] & 1) === 1;
|
||||
const hex: string = Buffer.from(u.slice(1)).toString('hex');
|
||||
const abs: bigint = BigInt(`0x${hex}`);
|
||||
return negated ? -abs : abs;
|
||||
}
|
||||
|
||||
@@ -58,12 +58,6 @@ export const getStatusIconInfo = ({
|
||||
statusIconColor: `var(--color-negative)`,
|
||||
};
|
||||
}
|
||||
case AbacusOrderStatus.untriggered: {
|
||||
return {
|
||||
statusIcon: IconName.OrderUntriggered,
|
||||
statusIconColor: `var(--color-text-2)`,
|
||||
};
|
||||
}
|
||||
case AbacusOrderStatus.pending:
|
||||
default: {
|
||||
return {
|
||||
@@ -107,8 +101,8 @@ export const getHydratedTradingData = ({
|
||||
perpetualMarkets?: Record<string, PerpetualMarket>;
|
||||
}) => ({
|
||||
...data,
|
||||
asset: assets && perpetualMarkets && assets[perpetualMarkets[data.marketId]?.assetId],
|
||||
stepSizeDecimals: perpetualMarkets?.[data.marketId]?.configs?.stepSizeDecimals,
|
||||
tickSizeDecimals: perpetualMarkets?.[data.marketId]?.configs?.tickSizeDecimals,
|
||||
asset: assets && perpetualMarkets && assets[perpetualMarkets[data.marketId].assetId],
|
||||
stepSizeDecimals: perpetualMarkets?.[data.marketId].configs?.stepSizeDecimals,
|
||||
tickSizeDecimals: perpetualMarkets?.[data.marketId].configs?.tickSizeDecimals,
|
||||
...('side' in data && { orderSide: convertAbacusOrderSide(data.side) }),
|
||||
});
|
||||
|
||||
@@ -21,8 +21,10 @@ const applyComputedStyles = (html: string) => {
|
||||
const toDataUrl = (bytes: string, type = 'image/svg+xml') =>
|
||||
new Promise<string | ArrayBuffer | null>((resolve, reject) => {
|
||||
Object.assign(new FileReader(), {
|
||||
onload: (e) => resolve(e.target.result),
|
||||
onerror: (e) => reject(e.target.error),
|
||||
onload: (e: {
|
||||
target: { result: string | ArrayBuffer | PromiseLike<string | ArrayBuffer | null> | null };
|
||||
}) => resolve(e.target.result),
|
||||
onerror: (e: { target: { error: Error } }) => reject(e.target.error),
|
||||
}).readAsDataURL(new File([bytes], '', { type }));
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
export const log = (location: string, error: Error, metadata?: any) => {
|
||||
if (import.meta.env.MODE !== 'production') {
|
||||
console.warn('telemetry/log:', { location, error, metadata });
|
||||
}
|
||||
// console.warn(error);
|
||||
|
||||
const customEvent = new CustomEvent('dydx:log', {
|
||||
detail: {
|
||||
@@ -18,10 +16,7 @@ export const log = (location: string, error: Error, metadata?: any) => {
|
||||
globalThis.addEventListener('unhandledrejection', (event) => {
|
||||
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)
|
||||
@@ -30,10 +25,7 @@ globalThis.addEventListener(
|
||||
(event) => {
|
||||
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
|
||||
);
|
||||
|
||||
@@ -10,23 +10,7 @@ import {
|
||||
bscTestnet,
|
||||
optimism,
|
||||
optimismGoerli,
|
||||
base,
|
||||
baseGoerli,
|
||||
polygon,
|
||||
polygonMumbai,
|
||||
linea,
|
||||
lineaTestnet,
|
||||
mantle,
|
||||
mantleTestnet,
|
||||
moonbeam,
|
||||
moonbaseAlpha,
|
||||
filecoin,
|
||||
filecoinHyperspace,
|
||||
fantom,
|
||||
fantomTestnet,
|
||||
celo,
|
||||
celoAlfajores,
|
||||
} from 'viem/chains';
|
||||
} from 'wagmi/chains';
|
||||
|
||||
import { alchemyProvider } from 'wagmi/providers/alchemy';
|
||||
import { jsonRpcProvider } from 'wagmi/providers/jsonRpc';
|
||||
@@ -36,17 +20,7 @@ import { CoinbaseWalletConnector } from 'wagmi/connectors/coinbaseWallet';
|
||||
import { InjectedConnector } from 'wagmi/connectors/injected';
|
||||
import { MetaMaskConnector } from 'wagmi/connectors/metaMask';
|
||||
import { WalletConnectConnector } from 'wagmi/connectors/walletConnect';
|
||||
|
||||
import {
|
||||
type WalletConnection,
|
||||
WalletConnectionType,
|
||||
type WalletType,
|
||||
walletConnectionTypes,
|
||||
wallets,
|
||||
WALLET_CONNECT_EXPLORER_RECOMMENDED_IDS,
|
||||
} from '@/constants/wallets';
|
||||
|
||||
import { isTruthy } from './isTruthy';
|
||||
import { WalletConnectLegacyConnector } from 'wagmi/connectors/walletConnectLegacy';
|
||||
|
||||
// Config
|
||||
|
||||
@@ -62,35 +36,15 @@ export const WAGMI_SUPPORTED_CHAINS: Chain[] = [
|
||||
bscTestnet,
|
||||
optimism,
|
||||
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,
|
||||
[
|
||||
import.meta.env.VITE_ALCHEMY_API_KEY &&
|
||||
alchemyProvider({ apiKey: import.meta.env.VITE_ALCHEMY_API_KEY }),
|
||||
jsonRpcProvider({
|
||||
rpc: (chain) => ({ http: chain.rpcUrls.default.http[0] }),
|
||||
}),
|
||||
publicProvider(),
|
||||
].filter(isTruthy)
|
||||
);
|
||||
const { chains, publicClient, webSocketPublicClient } = configureChains(WAGMI_SUPPORTED_CHAINS, [
|
||||
alchemyProvider({ apiKey: import.meta.env.VITE_ALCHEMY_API_KEY }),
|
||||
jsonRpcProvider({
|
||||
rpc: (chain) => ({ http: chain.rpcUrls.default.http[0] }),
|
||||
}),
|
||||
publicProvider(),
|
||||
]);
|
||||
|
||||
const injectedConnectorOptions = {
|
||||
chains,
|
||||
@@ -101,46 +55,53 @@ const injectedConnectorOptions = {
|
||||
},
|
||||
};
|
||||
|
||||
type WalletConnectConfig = {
|
||||
client: {
|
||||
name: string;
|
||||
description: string;
|
||||
iconUrl: string;
|
||||
};
|
||||
v2: {
|
||||
projectId: string;
|
||||
};
|
||||
};
|
||||
|
||||
const getWalletconnect2ConnectorOptions = (
|
||||
config: WalletConnectConfig
|
||||
): ConstructorParameters<typeof WalletConnectConnector>[0] => ({
|
||||
const walletconnect1ConnectorOptions = {
|
||||
chains,
|
||||
options: {
|
||||
projectId: config.v2.projectId,
|
||||
bridge: import.meta.env.VITE_WALLETCONNECT1_BRIDGE,
|
||||
qrcode: true,
|
||||
},
|
||||
};
|
||||
|
||||
const walletconnect2ConnectorOptions: ConstructorParameters<typeof WalletConnectConnector>[0] = {
|
||||
chains,
|
||||
options: {
|
||||
projectId: import.meta.env.VITE_WALLETCONNECT2_PROJECT_ID,
|
||||
metadata: {
|
||||
name: config.client.name,
|
||||
description: config.client.description,
|
||||
url: import.meta.env.VITE_BASE_URL,
|
||||
icons: [config.client.iconUrl],
|
||||
// TODO: update to local URLs/images
|
||||
name: 'dYdX',
|
||||
description: '',
|
||||
url: 'https://trade.dydx.exchange',
|
||||
icons: ['https://trade.dydx.exchange/cbw-image.png'],
|
||||
},
|
||||
showQrModal: true,
|
||||
qrModalOptions: {
|
||||
themeMode: 'dark' as const,
|
||||
themeVariables: {
|
||||
'--wcm-accent-color': '#5973fe',
|
||||
'--wcm-font-family': 'var(--fontFamily-base)',
|
||||
'--w3m-accent-color': '#5973fe',
|
||||
'--w3m-background-color': '#5973fe',
|
||||
'--w3m-color-bg-1': 'var(--color-layer-3)',
|
||||
'--w3m-color-bg-2': 'var(--color-layer-4)',
|
||||
'--w3m-color-bg-3': 'var(--color-layer-5)',
|
||||
'--w3m-font-family': 'var(--fontFamily-base)',
|
||||
'--w3m-font-feature-settings': 'none',
|
||||
'--w3m-overlay-backdrop-filter': 'blur(6px)',
|
||||
// '--w3m-logo-image-url': 'https://trade.dydx.exchange/cbw-image.png',
|
||||
},
|
||||
explorerRecommendedWalletIds: WALLET_CONNECT_EXPLORER_RECOMMENDED_IDS,
|
||||
enableExplorer: true,
|
||||
explorerAllowList: [],
|
||||
explorerDenyList: [],
|
||||
chainImages: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const getConnectors = (walletConnectConfig: WalletConnectConfig) => [
|
||||
const connectors = [
|
||||
new MetaMaskConnector({
|
||||
chains,
|
||||
options: {
|
||||
shimDisconnect: true,
|
||||
shimChainChangedDisconnect: false,
|
||||
},
|
||||
}),
|
||||
new CoinbaseWalletConnector({
|
||||
@@ -150,7 +111,8 @@ const getConnectors = (walletConnectConfig: WalletConnectConfig) => [
|
||||
reloadOnDisconnect: false,
|
||||
},
|
||||
}),
|
||||
new WalletConnectConnector(getWalletconnect2ConnectorOptions(walletConnectConfig)),
|
||||
new WalletConnectLegacyConnector(walletconnect1ConnectorOptions),
|
||||
new WalletConnectConnector(walletconnect2ConnectorOptions),
|
||||
new InjectedConnector(injectedConnectorOptions),
|
||||
];
|
||||
|
||||
@@ -172,43 +134,58 @@ const createInjectedConnectorWithProvider = (provider: ExternalProvider) =>
|
||||
provider as unknown as Awaited<ReturnType<InjectedConnector['getProvider']>>;
|
||||
})(injectedConnectorOptions) as InjectedConnector;
|
||||
|
||||
const createWalletConnect2ConnectorWithId = (
|
||||
walletconnectId: string,
|
||||
walletConnectConfig: WalletConnectConfig
|
||||
) => {
|
||||
const walletconnect2ConnectorOptions = getWalletconnect2ConnectorOptions(walletConnectConfig);
|
||||
return new WalletConnectConnector({
|
||||
// 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) =>
|
||||
new WalletConnectConnector({
|
||||
...walletconnect2ConnectorOptions,
|
||||
options: {
|
||||
...walletconnect2ConnectorOptions.options,
|
||||
qrModalOptions: {
|
||||
...walletconnect2ConnectorOptions.options.qrModalOptions,
|
||||
explorerRecommendedWalletIds: [walletconnectId],
|
||||
explorerRecommendedWalletIds: [walletconnect2Id],
|
||||
explorerExcludedWalletIds: 'ALL',
|
||||
chainImages: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Custom connector from wallet selection
|
||||
import {
|
||||
type WalletConnection,
|
||||
WalletConnectionType,
|
||||
type WalletType,
|
||||
walletConnectionTypes,
|
||||
wallets,
|
||||
} from '@/constants/wallets';
|
||||
|
||||
export const resolveWagmiConnector = ({
|
||||
walletType,
|
||||
walletConnection,
|
||||
walletConnectConfig,
|
||||
}: {
|
||||
walletType: WalletType;
|
||||
walletConnection: WalletConnection;
|
||||
walletConnectConfig: WalletConnectConfig;
|
||||
}) => {
|
||||
const walletConfig = wallets[walletType];
|
||||
const walletConnectionConfig = walletConnectionTypes[walletConnection.type];
|
||||
|
||||
return walletConnection.type === WalletConnectionType.InjectedEip1193 && walletConnection.provider
|
||||
? createInjectedConnectorWithProvider(walletConnection.provider)
|
||||
: walletConnection.type === WalletConnectionType.WalletConnect1 &&
|
||||
walletConfig.walletconnect1Name
|
||||
? createWalletConnect1ConnectorWithName(walletConfig.walletconnect1Name)
|
||||
: walletConnection.type === WalletConnectionType.WalletConnect2 && walletConfig.walletconnect2Id
|
||||
? createWalletConnect2ConnectorWithId(walletConfig.walletconnect2Id, walletConnectConfig)
|
||||
: getConnectors(walletConnectConfig).find(
|
||||
({ id }: { id: string }) => id === walletConnectionConfig.wagmiConnectorId
|
||||
);
|
||||
? createWalletConnect2ConnectorWithId(walletConfig.walletconnect2Id)
|
||||
: connectors.find(({ id }: { id: string }) => id === walletConnectionConfig.wagmiConnectorId);
|
||||
};
|
||||
|
||||
@@ -45,6 +45,12 @@ export const getWalletConnection = ({
|
||||
break;
|
||||
}
|
||||
|
||||
case WalletConnectionType.WalletConnect1: {
|
||||
return {
|
||||
type: WalletConnectionType.WalletConnect1,
|
||||
};
|
||||
}
|
||||
|
||||
case WalletConnectionType.WalletConnect2: {
|
||||
return {
|
||||
type: WalletConnectionType.WalletConnect2,
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import styled, { AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { articleMixins } from '@/styles/articleMixins';
|
||||
|
||||
export const PrivacyPolicyPage = () => (
|
||||
<Styled.Article>
|
||||
<header>
|
||||
<h1>Privacy Policy</h1>
|
||||
</header>
|
||||
</Styled.Article>
|
||||
);
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.Article = styled.article`
|
||||
${articleMixins.article}
|
||||
`;
|
||||
@@ -17,7 +17,7 @@ import { DialogTypes } from '@/constants/dialogs';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { AppRoute, PortfolioRoute, HistoryRoute } from '@/constants/routes';
|
||||
import { wallets, WalletType } from '@/constants/wallets';
|
||||
import { useAccounts, useStringGetter, useTokenConfigs } from '@/hooks';
|
||||
import { useAccounts, useStringGetter } from '@/hooks';
|
||||
|
||||
import { getOnboardingState } from '@/state/accountSelectors';
|
||||
import { openDialog } from '@/state/dialogs';
|
||||
@@ -37,7 +37,6 @@ const Profile = () => {
|
||||
const isConnected = onboardingState !== OnboardingState.Disconnected;
|
||||
|
||||
const { evmAddress, dydxAddress, walletType } = useAccounts();
|
||||
const { chainTokenLabel } = useTokenConfigs();
|
||||
|
||||
const { data: ensName } = useEnsName({
|
||||
address: evmAddress,
|
||||
@@ -138,11 +137,7 @@ const Profile = () => {
|
||||
layout="grid"
|
||||
/>
|
||||
</Panel>
|
||||
<Styled.RewardsPanel
|
||||
slotHeaderContent={stringGetter({ key: STRING_KEYS.REWARDS })}
|
||||
href={`/${chainTokenLabel}`}
|
||||
hasSeparator
|
||||
>
|
||||
<Styled.RewardsPanel header={stringGetter({ key: STRING_KEYS.REWARDS })} hasSeparator>
|
||||
<Styled.Details
|
||||
items={[
|
||||
{ key: 'maker', label: stringGetter({ key: STRING_KEYS.YOU_WILL_EARN }), value: '-' },
|
||||
@@ -154,8 +149,8 @@ const Profile = () => {
|
||||
</Styled.EqualGrid>
|
||||
|
||||
<Styled.TablePanel
|
||||
slotHeaderContent={stringGetter({ key: STRING_KEYS.HISTORY })}
|
||||
href={`${AppRoute.Portfolio}/${PortfolioRoute.History}/${HistoryRoute.Trades}`}
|
||||
header={stringGetter({ key: STRING_KEYS.HISTORY })}
|
||||
linkto={`${AppRoute.Portfolio}/${PortfolioRoute.History}/${HistoryRoute.Trades}`}
|
||||
hasSeparator
|
||||
>
|
||||
<FillsTable
|
||||
@@ -278,21 +273,22 @@ Styled.RewardsPanel = styled(Panel)`
|
||||
`;
|
||||
|
||||
Styled.TablePanel = styled(Panel)`
|
||||
padding: 0;
|
||||
max-width: calc(100vw - 2rem);
|
||||
max-height: 10rem;
|
||||
margin-top: 0.5rem;
|
||||
padding: 0;
|
||||
border-radius: 0.875rem;
|
||||
|
||||
table {
|
||||
--tableCell-padding: 0.25rem 1rem;
|
||||
--tableRow-backgroundColor: var(--color-layer-3);
|
||||
background-color: var(--color-layer-3);
|
||||
|
||||
background-color: transparent;
|
||||
|
||||
thead {
|
||||
color: var(--color-text-0);
|
||||
}
|
||||
|
||||
tbody {
|
||||
background-color: transparent;
|
||||
|
||||
tr {
|
||||
td:nth-child(2),
|
||||
td:nth-child(3) {
|
||||
|
||||