Compare commits
356 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c96942922 | ||
|
|
d27c83b382 | ||
|
|
b2f88965a9 | ||
|
|
95db44e0fc | ||
|
|
7d45ff5ca8 | ||
|
|
8964cf86aa | ||
|
|
0e9f23ed56 | ||
|
|
e08e597655 | ||
|
|
744db49803 | ||
|
|
54f59e02fd | ||
|
|
7950e5c083 | ||
|
|
dd33205bf6 | ||
|
|
397a20b9ec | ||
|
|
0aac0b43aa | ||
|
|
702500794d | ||
|
|
8bea95fab2 | ||
|
|
27094c87f2 | ||
|
|
bede9171c3 | ||
|
|
251d7c0bc2 | ||
|
|
285e4f28f5 | ||
|
|
3aa045303a | ||
|
|
e0a7c3794e | ||
|
|
f5fc5da341 | ||
|
|
bea5c0484b | ||
|
|
27960532ca | ||
|
|
37a4e2f6e3 | ||
|
|
19a3b12ca8 | ||
|
|
22c1ddf393 | ||
|
|
b44ae1a267 | ||
|
|
418dcf0cb2 | ||
|
|
58a508c9d6 | ||
|
|
3198129af2 | ||
|
|
89d484d882 | ||
|
|
fa4688d96c | ||
|
|
7ee761a59e | ||
|
|
78e95f6073 | ||
|
|
c67e57505a | ||
|
|
30f7385db7 | ||
|
|
c0f58ae810 | ||
|
|
54dd5476ca | ||
|
|
57786335df | ||
|
|
948e01a196 | ||
|
|
abf127c596 | ||
|
|
4d3f870b93 | ||
|
|
452f2dc3c0 | ||
|
|
b6bd59f2b1 | ||
|
|
0190b5a408 | ||
|
|
d6030dcd45 | ||
|
|
f0e2a491dc | ||
|
|
021aab6547 | ||
|
|
81af31eec1 | ||
|
|
d3898cf900 | ||
|
|
b8f61d5f90 | ||
|
|
6c880d29a6 | ||
|
|
a8baa6d6a5 | ||
|
|
00a1dee073 | ||
|
|
a6de7d7846 | ||
|
|
e1a81a9996 | ||
|
|
0770bab032 | ||
|
|
e5404dbf97 | ||
|
|
78e41848f2 | ||
|
|
77c090534b | ||
|
|
05acbfee88 | ||
|
|
64dd2f9ed1 | ||
|
|
09f30ce0f7 | ||
|
|
a49d5382db | ||
|
|
3affcb8d32 | ||
|
|
56e759ff78 | ||
|
|
f73a166d92 | ||
|
|
e1e52c06db | ||
|
|
ca4d915903 | ||
|
|
bdb6327c13 | ||
|
|
ce81dd5a79 | ||
|
|
73f29eea2c | ||
|
|
92193076c5 | ||
|
|
1aab086693 | ||
|
|
8057cb9fbe | ||
|
|
1b798889af | ||
|
|
1e54b97693 | ||
|
|
02c21ef720 | ||
|
|
b39aeeb805 | ||
|
|
8401a4b9b4 | ||
|
|
3b27ee94d7 | ||
|
|
660c355273 | ||
|
|
cd3c48462d | ||
|
|
acfd5c2720 | ||
|
|
a5ed12bfc7 | ||
|
|
40f0e619cc | ||
|
|
bd817083c9 | ||
|
|
699bcc25b6 | ||
|
|
e88a8effef | ||
|
|
2339817170 | ||
|
|
4220cafbd3 | ||
|
|
a61df58599 | ||
|
|
1b35128035 | ||
|
|
ab5114c5f5 | ||
|
|
28c7cfa1f1 | ||
|
|
509b307b67 | ||
|
|
1e5519de3f | ||
|
|
e8587396d3 | ||
|
|
d69b194ffb | ||
|
|
1325443025 | ||
|
|
1607f8919a | ||
|
|
873cf9760e | ||
|
|
b39f2fe055 | ||
|
|
516e783be6 | ||
|
|
9a326fa023 | ||
|
|
06d6c711dd | ||
|
|
7178746023 | ||
|
|
2efe8250ae | ||
|
|
24d8b4abc9 | ||
|
|
aec18b7eb1 | ||
|
|
0c37e81d97 | ||
|
|
624c3678c7 | ||
|
|
cdae20f2ed | ||
|
|
042967502e | ||
|
|
eedba9795e | ||
|
|
eaec4a33fc | ||
|
|
300dd70804 | ||
|
|
f4e994867e | ||
|
|
7ca79ff12b | ||
|
|
4903258b7c | ||
|
|
fe35ca9db8 | ||
|
|
4bc1b8eb5c | ||
|
|
7801695180 | ||
|
|
ee69357305 | ||
|
|
ce7f94f16a | ||
|
|
124b83ae56 | ||
|
|
0dcd5743c8 | ||
|
|
9fec8dbe93 | ||
|
|
549d4e38c6 | ||
|
|
3af6781821 | ||
|
|
fbccb83edb | ||
|
|
ff12b7be10 | ||
|
|
4c2cb5b0c1 | ||
|
|
d7785942b1 | ||
|
|
ed801deb15 | ||
|
|
c34727641d | ||
|
|
807860aac6 | ||
|
|
5a9034fe95 | ||
|
|
ee0db4f2aa | ||
|
|
6d5625a1f8 | ||
|
|
1619386ab4 | ||
|
|
93d33947da | ||
|
|
91f3e21bd4 | ||
|
|
e9a432b58e | ||
|
|
1f41587ba9 | ||
|
|
9b639bee65 | ||
|
|
eaddc9e6f8 | ||
|
|
e83a1ec923 | ||
|
|
8021315f87 | ||
|
|
0540012bb9 | ||
|
|
3f6bf607dd | ||
|
|
828f7ee446 | ||
|
|
432d17bdfd | ||
|
|
9743b211e7 | ||
|
|
dce187e433 | ||
|
|
8859ff6979 | ||
|
|
f566a72b06 | ||
|
|
e39e5908e7 | ||
|
|
c5424d2dcc | ||
|
|
8b4bc167c5 | ||
|
|
43bceb232e | ||
|
|
c3909bc1d0 | ||
|
|
b25287923c | ||
|
|
65c51ea4aa | ||
|
|
b0fa08e9b0 | ||
|
|
b09eb8fb52 | ||
|
|
5b49cedebb | ||
|
|
ae76f26501 | ||
|
|
1cb1ffe2f6 | ||
|
|
d83bc3097d | ||
|
|
323edc0fcd | ||
|
|
fc258fdf5c | ||
|
|
2e599dc00e | ||
|
|
464a682fcc | ||
|
|
6ace3c211c | ||
|
|
2d0b0c70bf | ||
|
|
3c3be3c61a | ||
|
|
7660943e97 | ||
|
|
d342ccdb78 | ||
|
|
0ea9dc046b | ||
|
|
3a34c2ec25 | ||
|
|
83b0ef94f9 | ||
|
|
3680b93fb3 | ||
|
|
8e86ded09b | ||
|
|
37a50372f6 | ||
|
|
ab99cc612b | ||
|
|
11a4fa23c1 | ||
|
|
5358b4dc15 | ||
|
|
ccbd5dfcf7 | ||
|
|
bb17c57a84 | ||
|
|
3b6213f411 | ||
|
|
a5251f55de | ||
|
|
d846c83afa | ||
|
|
00438bea12 | ||
|
|
0dd5e6b33f | ||
|
|
2d7642ed9b | ||
|
|
740e18454f | ||
|
|
7c17cfc642 | ||
|
|
7f61b67947 | ||
|
|
466c0b0142 | ||
|
|
1c6d9d810e | ||
|
|
c3ad129658 | ||
|
|
a25a72d0d9 | ||
|
|
dbc9e85b90 | ||
|
|
7dafb0cfba | ||
|
|
da33ec9d2f | ||
|
|
605368629f | ||
|
|
ca2b84ec0b | ||
|
|
ac7cf35bb7 | ||
|
|
bd346030f0 | ||
|
|
aa742f415d | ||
|
|
77fa61495f | ||
|
|
bff3811faf | ||
|
|
60d1f8743f | ||
|
|
2d118a904a | ||
|
|
b69f08cbe1 | ||
|
|
644ecdbd32 | ||
|
|
4b47ae6da3 | ||
|
|
fe6a46fc0f | ||
|
|
539a6e05e5 | ||
|
|
3693c83b6e | ||
|
|
18408c9c75 | ||
|
|
39f018bae0 | ||
|
|
c00eb2451f | ||
|
|
6b99309fab | ||
|
|
976b15986f | ||
|
|
df4c18c8d4 | ||
|
|
62d3aa4b76 | ||
|
|
361d17c925 | ||
|
|
9269f15ffc | ||
|
|
925668c7a7 | ||
|
|
a64d526206 | ||
|
|
69234eb708 | ||
|
|
f833133689 | ||
|
|
c73a2655cb | ||
|
|
c55061873c | ||
|
|
d425ff64b4 | ||
|
|
2f47fdf71d | ||
|
|
d4562a2373 | ||
|
|
e13369154d | ||
|
|
9f8719f2a5 | ||
|
|
77b640c41b | ||
|
|
d2f98bc9b4 | ||
|
|
6be54afd12 | ||
|
|
d1fad3c4ea | ||
|
|
a1c4b97a18 | ||
|
|
f00adc755a | ||
|
|
e97939c545 | ||
|
|
63907b7bc5 | ||
|
|
48ad8e529f | ||
|
|
b669ec6976 | ||
|
|
2d4b60b9dd | ||
|
|
e4dd4f9283 | ||
|
|
7798443919 | ||
|
|
9fc096d091 | ||
|
|
9f5584c37d | ||
|
|
392d78b9da | ||
|
|
231289732c | ||
|
|
0c0305a53d | ||
|
|
140ff7a674 | ||
|
|
c3f65e3abd | ||
|
|
306aaa3a20 | ||
|
|
d08cae175a | ||
|
|
f0f4110b4b | ||
|
|
3fbc4e34f4 | ||
|
|
24521f0c92 | ||
|
|
a037595e6e | ||
|
|
b964953daf | ||
|
|
649fd9c845 | ||
|
|
edf4c47451 | ||
|
|
c0ce6a55c4 | ||
|
|
6347e63a15 | ||
|
|
bdcb9a8a0a | ||
|
|
8d90bb7a39 | ||
|
|
d70b456855 | ||
|
|
fbb797fa54 | ||
|
|
878fc9cf4d | ||
|
|
32e679c62e | ||
|
|
8ace518311 | ||
|
|
59164f876f | ||
|
|
5d952661d7 | ||
|
|
67c776c995 | ||
|
|
719754c46c | ||
|
|
9170af888e | ||
|
|
9509737811 | ||
|
|
e42a26c3dc | ||
|
|
eeb258ebd5 | ||
|
|
ab1538b196 | ||
|
|
b258f557d1 | ||
|
|
9d8c7f8e12 | ||
|
|
9c44e61e23 | ||
|
|
71db11b6ac | ||
|
|
db3328c8d9 | ||
|
|
34dfb41a1e | ||
|
|
7e5a230a33 | ||
|
|
20adf82c79 | ||
|
|
0ec6cad6d1 | ||
|
|
eb850cff0b | ||
|
|
9d9b57dd4c | ||
|
|
37b0e2fa28 | ||
|
|
b6d8512316 | ||
|
|
4e17107ac5 | ||
|
|
64c97f7e30 | ||
|
|
f3c513573d | ||
|
|
6965707d45 | ||
|
|
d762836eb9 | ||
|
|
88f8f804d9 | ||
|
|
c1042c6b7a | ||
|
|
3cb382376a | ||
|
|
b44c7e7d7e | ||
|
|
333c907e63 | ||
|
|
b630d59437 | ||
|
|
ba647d9d6c | ||
|
|
449ed0185e | ||
|
|
52e3ad0703 | ||
|
|
bf9dad2550 | ||
|
|
adcecdeefc | ||
|
|
5fabe438e5 | ||
|
|
e77fcd21dc | ||
|
|
98e77cb01b | ||
|
|
b05c4c111c | ||
|
|
4d612dc2a2 | ||
|
|
619c7c2d95 | ||
|
|
db886930a0 | ||
|
|
0b7846ee1d | ||
|
|
f450d34d69 | ||
|
|
76ab349b9e | ||
|
|
5c3c1c67f5 | ||
|
|
9efd5da1f7 | ||
|
|
8fd894f2d1 | ||
|
|
cc22183388 | ||
|
|
9175dd10cc | ||
|
|
bbd50f066d | ||
|
|
2291e3ec20 | ||
|
|
28d8f0b0bb | ||
|
|
8bed3900ba | ||
|
|
a1000c6576 | ||
|
|
267204d98e | ||
|
|
74f50f1b7e | ||
|
|
a70aa41df2 | ||
|
|
587b659816 | ||
|
|
5388cab779 | ||
|
|
cadd68fb37 | ||
|
|
ab8ce37ceb | ||
|
|
5a9a71ae2d | ||
|
|
b93fd230bd | ||
|
|
e9a11bb604 | ||
|
|
c5afbedb3e | ||
|
|
5b2c44522e | ||
|
|
7fd4005154 | ||
|
|
48eab0d0ac | ||
|
|
c9ee1b3b32 | ||
|
|
eb4c305eff | ||
|
|
d9825622f1 |
5
.env
@@ -1,2 +1,5 @@
|
||||
REACT_APP_CHAIN_ID="1"
|
||||
REACT_APP_NETWORK_URL="https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
|
||||
REACT_APP_NETWORK_URL="https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
|
||||
REACT_APP_WALLETCONNECT_BRIDGE_URL="https://uniswap.bridge.walletconnect.org"
|
||||
# Because we use storybook which has its own babel-loader dependency @ 8.2.2, where react-scripts uses 8.1.0
|
||||
SKIP_PREFLIGHT_CHECK=true
|
||||
@@ -8,9 +8,7 @@
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"node_modules/**/*"
|
||||
],
|
||||
"ignorePatterns": ["node_modules/**/*"],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
@@ -26,6 +24,9 @@
|
||||
"rules": {
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"prettier/prettier": "error",
|
||||
"@typescript-eslint/no-explicit-any": "off"
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/ban-ts-ignore": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@@ -10,9 +10,10 @@ assignees: ''
|
||||
A clear and concise description of the bug.
|
||||
|
||||
**Steps to Reproduce**
|
||||
|
||||
1. Go to ...
|
||||
2. Click on ...
|
||||
...
|
||||
...
|
||||
|
||||
**Expected Behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,7 +1,7 @@
|
||||
blank_issues_enabled: false
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Support
|
||||
url: https://discord.gg/EwFs3Pp
|
||||
url: https://discord.gg/FCfyBSbCU5
|
||||
about: Please ask and answer questions here
|
||||
- name: List a token
|
||||
url: https://github.com/Uniswap/default-token-list#adding-a-token
|
||||
|
||||
38
.github/workflows/integration-tests.yaml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: Integration Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
integration-tests:
|
||||
name: Cypress
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- run: yarn cypress install
|
||||
- run: yarn build
|
||||
env:
|
||||
CI: false
|
||||
REACT_APP_NETWORK_URL: "https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
|
||||
|
||||
- run: yarn integration-test
|
||||
env:
|
||||
CYPRESS_INTEGRATION_TEST_PRIVATE_KEY: ${{ secrets.CYPRESS_INTEGRATION_TEST_PRIVATE_KEY }}
|
||||
|
||||
|
||||
25
.github/workflows/lint.yml
vendored
@@ -3,10 +3,10 @@ name: Lint
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
pull_request_target:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
|
||||
jobs:
|
||||
run-linters:
|
||||
@@ -14,33 +14,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up node
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12
|
||||
always-auth: true
|
||||
node-version: 14
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Set output of cache
|
||||
id: yarn-cache
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- name: Node dependency cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache.outputs.dir }}
|
||||
key: yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Run linters
|
||||
uses: wearerequired/lint-action@77d70b9a07ecb93bc98dc46dc27d96c4f004d035
|
||||
uses: wearerequired/lint-action@b98b0918aa71490373d2eca9e8e39a9bc1cc2517
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
eslint: true
|
||||
|
||||
18
.github/workflows/release.yaml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
changelog: ${{ steps.github_tag_action.outputs.changelog }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Bump version and push tag
|
||||
id: github_tag_action
|
||||
@@ -31,12 +31,12 @@ jobs:
|
||||
if: ${{ needs.bump_version.outputs.new_tag != null }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v1
|
||||
- name: Set up node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
always-auth: true
|
||||
node-version: 14
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Install dependencies
|
||||
@@ -54,6 +54,14 @@ jobs:
|
||||
pinata-api-key: ${{ secrets.PINATA_API_KEY }}
|
||||
pinata-secret-api-key: ${{ secrets.PINATA_API_SECRET_KEY }}
|
||||
|
||||
- name: Pin to Crust
|
||||
uses: crustio/ipfs-crust-action@v1.0.8
|
||||
continue-on-error: true
|
||||
timeout-minutes: 2
|
||||
with:
|
||||
cid: ${{ steps.upload.outputs.hash }}
|
||||
seeds: ${{ secrets.CRUST_SEEDS }}
|
||||
|
||||
- name: Convert CIDv0 to CIDv1
|
||||
id: convert_cidv0
|
||||
uses: uniswap/convert-cidv0-cidv1@v1.0.0
|
||||
|
||||
65
.github/workflows/tests.yaml
vendored
@@ -1,65 +0,0 @@
|
||||
name: Tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
integration-tests:
|
||||
name: Integration tests
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12'
|
||||
always-auth: true
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- uses: actions/cache@v1
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn cypress install
|
||||
- run: yarn build
|
||||
env:
|
||||
CI: false
|
||||
REACT_APP_NETWORK_URL: "https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
|
||||
- run: yarn integration-test
|
||||
|
||||
unit-tests:
|
||||
name: Unit tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12'
|
||||
always-auth: true
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- uses: actions/cache@v1
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn test
|
||||
|
||||
28
.github/workflows/unit-tests.yaml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Unit Tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
unit-tests:
|
||||
name: Unit tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Run unit tests
|
||||
run: yarn test
|
||||
4
.gitignore
vendored
@@ -1,5 +1,9 @@
|
||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# generated contract types
|
||||
/src/types/v3
|
||||
/src/abis/types
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
|
||||
16
.storybook/main.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
const { dirname, join, parse, resolve } = require('path')
|
||||
const { existsSync } = require('fs')
|
||||
|
||||
module.exports = {
|
||||
stories: ['../src/**/*.stories.@(ts|tsx)'],
|
||||
addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/preset-create-react-app'],
|
||||
typescript: {
|
||||
check: true,
|
||||
checkOptions: {},
|
||||
reactDocgen: 'react-docgen-typescript',
|
||||
reactDocgenTypescriptOptions: {
|
||||
shouldExtractLiteralValuesFromEnum: true,
|
||||
propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true),
|
||||
},
|
||||
},
|
||||
}
|
||||
4
.storybook/manager.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { addons } from '@storybook/addons'
|
||||
import { light } from './theme'
|
||||
|
||||
addons.setConfig({ theme: light })
|
||||
91
.storybook/preview.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import 'inter-ui'
|
||||
import { Story } from '@storybook/react/types-6-0'
|
||||
import { createWeb3ReactRoot, Web3ReactProvider } from '@web3-react/core'
|
||||
import React from 'react'
|
||||
import { Provider as StoreProvider } from 'react-redux'
|
||||
import { ThemeProvider as SCThemeProvider } from 'styled-components'
|
||||
import { NetworkContextName } from '../src/constants'
|
||||
import store from '../src/state'
|
||||
import { FixedGlobalStyle, theme, ThemedGlobalStyle } from '../src/theme'
|
||||
import getLibrary from '../src/utils/getLibrary'
|
||||
import * as storybookThemes from './theme'
|
||||
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: '^on[A-Z].*' },
|
||||
dependencies: {
|
||||
withStoriesOnly: true,
|
||||
hideEmpty: true,
|
||||
},
|
||||
docs: {
|
||||
theme: storybookThemes.light,
|
||||
},
|
||||
viewport: {
|
||||
viewports: {
|
||||
mobile: {
|
||||
name: 'iPhone X',
|
||||
styles: {
|
||||
width: '375px',
|
||||
height: '812px',
|
||||
},
|
||||
},
|
||||
tablet: {
|
||||
name: 'iPad',
|
||||
styles: {
|
||||
width: '768px',
|
||||
height: '1024px',
|
||||
},
|
||||
},
|
||||
laptop: {
|
||||
name: 'Laptop',
|
||||
styles: {
|
||||
width: '1024px',
|
||||
height: '768px',
|
||||
},
|
||||
},
|
||||
desktop: {
|
||||
name: 'Desktop',
|
||||
styles: {
|
||||
width: '1440px',
|
||||
height: '1024px',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const globalTypes = {
|
||||
theme: {
|
||||
name: 'Theme',
|
||||
description: 'Global theme for components',
|
||||
defaultValue: 'light',
|
||||
toolbar: {
|
||||
icon: 'circlehollow',
|
||||
items: ['light', 'dark'],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const Web3ProviderNetwork = createWeb3ReactRoot(NetworkContextName)
|
||||
|
||||
const withProviders = (Component: Story, context: Record<string, any>) => {
|
||||
const THEME = theme(context.globals.theme === 'dark')
|
||||
return (
|
||||
<>
|
||||
<Web3ReactProvider getLibrary={getLibrary}>
|
||||
<Web3ProviderNetwork getLibrary={getLibrary}>
|
||||
<StoreProvider store={store}>
|
||||
<SCThemeProvider theme={THEME}>
|
||||
<FixedGlobalStyle />
|
||||
<ThemedGlobalStyle />
|
||||
<main>
|
||||
<Component />
|
||||
</main>
|
||||
</SCThemeProvider>
|
||||
</StoreProvider>
|
||||
</Web3ProviderNetwork>
|
||||
</Web3ReactProvider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const decorators = [withProviders]
|
||||
17
.storybook/theme.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { create } from '@storybook/theming'
|
||||
|
||||
// this themes the storybook UI
|
||||
const uniswapBaseTheme = {
|
||||
brandTitle: 'Uniswap Design',
|
||||
brandUrl: 'https://uniswap.org',
|
||||
brandImage: 'https://ipfs.io/ipfs/QmNa8mQkrNKp1WEEeGjFezDmDeodkWRevGFN8JCV7b4Xir',
|
||||
}
|
||||
export const light = create({
|
||||
base: 'light',
|
||||
...uniswapBaseTheme,
|
||||
})
|
||||
|
||||
// export const dark = create({
|
||||
// base: 'dark',
|
||||
// ...uniswapBaseTheme,
|
||||
// })
|
||||
27
README.md
@@ -1,8 +1,9 @@
|
||||
# Uniswap Interface
|
||||
|
||||
[](https://github.com/Uniswap/uniswap-interface/actions?query=workflow%3ALint)
|
||||
[](https://github.com/Uniswap/uniswap-interface/actions?query=workflow%3ATests)
|
||||
[](https://prettier.io/)
|
||||
[](https://github.com/Uniswap/uniswap-interface/actions/workflows/unit-tests.yaml)
|
||||
[](https://github.com/Uniswap/uniswap-interface/actions/workflows/integration-tests.yaml)
|
||||
[](https://github.com/Uniswap/uniswap-interface/actions/workflows/lint.yml)
|
||||
[](https://github.com/Uniswap/uniswap-interface/actions/workflows/release.yaml)
|
||||
|
||||
An open source interface for Uniswap -- a protocol for decentralized exchange of Ethereum tokens.
|
||||
|
||||
@@ -12,21 +13,15 @@ An open source interface for Uniswap -- a protocol for decentralized exchange of
|
||||
- Twitter: [@UniswapProtocol](https://twitter.com/UniswapProtocol)
|
||||
- Reddit: [/r/Uniswap](https://www.reddit.com/r/Uniswap/)
|
||||
- Email: [contact@uniswap.org](mailto:contact@uniswap.org)
|
||||
- Discord: [Uniswap](https://discord.gg/Y7TF6QA)
|
||||
- Discord: [Uniswap](https://discord.gg/FCfyBSbCU5)
|
||||
- Whitepaper: [Link](https://hackmd.io/C-DvwDSfSxuh-Gd4WKE_ig)
|
||||
|
||||
## Accessing the Uniswap Interface
|
||||
|
||||
To access the Uniswap Interface, use an IPFS gateway link from the
|
||||
[latest release](https://github.com/Uniswap/uniswap-interface/releases/latest),
|
||||
[latest release](https://github.com/Uniswap/uniswap-interface/releases/latest),
|
||||
or visit [app.uniswap.org](https://app.uniswap.org).
|
||||
|
||||
## Listing a token
|
||||
|
||||
Please see the
|
||||
[@uniswap/default-token-list](https://github.com/uniswap/default-token-list)
|
||||
repository.
|
||||
|
||||
## Development
|
||||
|
||||
### Install Dependencies
|
||||
@@ -47,20 +42,20 @@ To have the interface default to a different network when a wallet is not connec
|
||||
|
||||
1. Make a copy of `.env` named `.env.local`
|
||||
2. Change `REACT_APP_NETWORK_ID` to `"{YOUR_NETWORK_ID}"`
|
||||
3. Change `REACT_APP_NETWORK_URL` to e.g. `"https://{YOUR_NETWORK_ID}.infura.io/v3/{YOUR_INFURA_KEY}"`
|
||||
3. Change `REACT_APP_NETWORK_URL` to e.g. `"https://{YOUR_NETWORK_ID}.infura.io/v3/{YOUR_INFURA_KEY}"`
|
||||
|
||||
Note that the interface only works on testnets where both
|
||||
[Uniswap V2](https://uniswap.org/docs/v2/smart-contracts/factory/) and
|
||||
Note that the interface only works on testnets where both
|
||||
[Uniswap V2](https://uniswap.org/docs/v2/smart-contracts/factory/) and
|
||||
[multicall](https://github.com/makerdao/multicall) are deployed.
|
||||
The interface will not work on other networks.
|
||||
|
||||
## Contributions
|
||||
|
||||
**Please open all pull requests against the `master` branch.**
|
||||
**Please open all pull requests against the `main` branch.**
|
||||
CI checks will run against all PRs.
|
||||
|
||||
## Accessing Uniswap Interface V1
|
||||
|
||||
The Uniswap Interface supports swapping against, and migrating or removing liquidity from Uniswap V1. However,
|
||||
if you would like to use Uniswap V1, the Uniswap V1 interface for mainnet and testnets is accessible via IPFS gateways
|
||||
if you would like to use Uniswap V1, the Uniswap V1 interface for mainnet and testnets is accessible via IPFS gateways
|
||||
linked from the [v1.0.0 release](https://github.com/Uniswap/uniswap-interface/releases/tag/v1.0.0).
|
||||
|
||||
8
babel-plugin-macros.config.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const isDev = process.env.NODE_ENV !== 'production'
|
||||
|
||||
module.exports = {
|
||||
styledComponents: {
|
||||
fileName: isDev,
|
||||
displayName: isDev,
|
||||
},
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
describe('Add Liquidity', () => {
|
||||
it('loads the two correct tokens', () => {
|
||||
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85-0xc778417E063141139Fce010982780140Aa0cD5Ab')
|
||||
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab/500')
|
||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
|
||||
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'ETH')
|
||||
})
|
||||
|
||||
it('does not crash if ETH is duplicated', () => {
|
||||
cy.visit('/add/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xc778417E063141139Fce010982780140Aa0cD5Ab')
|
||||
cy.visit('/add/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xc778417E063141139Fce010982780140Aa0cD5Ab')
|
||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'ETH')
|
||||
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('not.contain.text', 'ETH')
|
||||
})
|
||||
|
||||
it('token not in storage is loaded', () => {
|
||||
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'SKL')
|
||||
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'MKR')
|
||||
})
|
||||
@@ -23,28 +23,4 @@ describe('Add Liquidity', () => {
|
||||
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
|
||||
})
|
||||
|
||||
it('redirects /add/token-token to add/token/token', () => {
|
||||
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.url().should(
|
||||
'contain',
|
||||
'/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'
|
||||
)
|
||||
})
|
||||
|
||||
it('redirects /add/WETH-token to /add/WETH-address/token', () => {
|
||||
cy.visit('/add/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.url().should(
|
||||
'contain',
|
||||
'/add/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'
|
||||
)
|
||||
})
|
||||
|
||||
it('redirects /add/token-WETH to /add/token/WETH-address', () => {
|
||||
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85-0xc778417E063141139Fce010982780140Aa0cD5Ab')
|
||||
cy.url().should(
|
||||
'contain',
|
||||
'/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,18 +3,9 @@ describe('Lists', () => {
|
||||
cy.visit('/swap')
|
||||
})
|
||||
|
||||
it('defaults to uniswap list', () => {
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
cy.get('#currency-search-selected-list-name').should('contain', 'Uniswap')
|
||||
})
|
||||
|
||||
// @TODO check if default lists are active when we have them
|
||||
it('change list', () => {
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
cy.get('#currency-search-change-list-button').click()
|
||||
cy.get('#list-row-tokens-1inch-eth .select-button').click()
|
||||
cy.get('#currency-search-selected-list-name').should('contain', '1inch')
|
||||
cy.get('#currency-search-change-list-button').click()
|
||||
cy.get('#list-row-tokens-uniswap-eth .select-button').click()
|
||||
cy.get('#currency-search-selected-list-name').should('contain', 'Uniswap')
|
||||
cy.get('.list-token-manage-button').click()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
describe('Migrate V1 Liquidity', () => {
|
||||
describe('Remove V1 liquidity', () => {
|
||||
it('renders the correct page', () => {
|
||||
cy.visit('/remove/v1/0x93bB63aFe1E0180d0eF100D774B473034fd60C36')
|
||||
cy.get('#remove-v1-exchange').should('contain', 'MKR/ETH')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -4,9 +4,4 @@ describe('Pool', () => {
|
||||
cy.get('#join-pool-button').click()
|
||||
cy.url().should('contain', '/add/ETH')
|
||||
})
|
||||
|
||||
it('import pool links to /import', () => {
|
||||
cy.get('#import-pool-link').click()
|
||||
cy.url().should('contain', '/find')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,38 +1,30 @@
|
||||
describe('Remove Liquidity', () => {
|
||||
it('redirects', () => {
|
||||
cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.url().should(
|
||||
'contain',
|
||||
'/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'
|
||||
)
|
||||
})
|
||||
|
||||
it('eth remove', () => {
|
||||
cy.visit('/remove/ETH/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.visit('/remove/v2/ETH/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'ETH')
|
||||
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
|
||||
})
|
||||
|
||||
it('eth remove swap order', () => {
|
||||
cy.visit('/remove/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/ETH')
|
||||
cy.visit('/remove/v2/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/ETH')
|
||||
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'MKR')
|
||||
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'ETH')
|
||||
})
|
||||
|
||||
it('loads the two correct tokens', () => {
|
||||
cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.visit('/remove/v2/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'WETH')
|
||||
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
|
||||
})
|
||||
|
||||
it('does not crash if ETH is duplicated', () => {
|
||||
cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xc778417E063141139Fce010982780140Aa0cD5Ab')
|
||||
cy.visit('/remove/v2/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xc778417E063141139Fce010982780140Aa0cD5Ab')
|
||||
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'WETH')
|
||||
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'WETH')
|
||||
})
|
||||
|
||||
it('token not in storage is loaded', () => {
|
||||
cy.visit('/remove/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.visit('/remove/v2/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'SKL')
|
||||
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
|
||||
})
|
||||
|
||||
@@ -3,36 +3,26 @@ describe('Swap', () => {
|
||||
cy.visit('/swap')
|
||||
})
|
||||
it('can enter an amount into input', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input')
|
||||
.type('0.001', { delay: 200 })
|
||||
.should('have.value', '0.001')
|
||||
cy.get('#swap-currency-input .token-amount-input').type('0.001', { delay: 200 }).should('have.value', '0.001')
|
||||
})
|
||||
|
||||
it('zero swap amount', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input')
|
||||
.type('0.0', { delay: 200 })
|
||||
.should('have.value', '0.0')
|
||||
cy.get('#swap-currency-input .token-amount-input').type('0.0', { delay: 200 }).should('have.value', '0.0')
|
||||
})
|
||||
|
||||
it('invalid swap amount', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input')
|
||||
.type('\\', { delay: 200 })
|
||||
.should('have.value', '')
|
||||
cy.get('#swap-currency-input .token-amount-input').type('\\', { delay: 200 }).should('have.value', '')
|
||||
})
|
||||
|
||||
it('can enter an amount into output', () => {
|
||||
cy.get('#swap-currency-output .token-amount-input')
|
||||
.type('0.001', { delay: 200 })
|
||||
.should('have.value', '0.001')
|
||||
cy.get('#swap-currency-output .token-amount-input').type('0.001', { delay: 200 }).should('have.value', '0.001')
|
||||
})
|
||||
|
||||
it('zero output amount', () => {
|
||||
cy.get('#swap-currency-output .token-amount-input')
|
||||
.type('0.0', { delay: 200 })
|
||||
.should('have.value', '0.0')
|
||||
cy.get('#swap-currency-output .token-amount-input').type('0.0', { delay: 200 }).should('have.value', '0.0')
|
||||
})
|
||||
|
||||
it('can swap ETH for DAI', () => {
|
||||
it.skip('can swap ETH for DAI', () => {
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
cy.get('.token-item-0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735').should('be.visible')
|
||||
cy.get('.token-item-0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735').click({ force: true })
|
||||
@@ -43,13 +33,13 @@ describe('Swap', () => {
|
||||
cy.get('#confirm-swap-or-send').should('contain', 'Confirm Swap')
|
||||
})
|
||||
|
||||
it('add a recipient does not exist unless in expert mode', () => {
|
||||
it.skip('add a recipient does not exist unless in expert mode', () => {
|
||||
cy.get('#add-recipient-button').should('not.exist')
|
||||
})
|
||||
|
||||
describe('expert mode', () => {
|
||||
beforeEach(() => {
|
||||
cy.window().then(win => {
|
||||
cy.window().then((win) => {
|
||||
cy.stub(win, 'prompt').returns('confirm')
|
||||
})
|
||||
cy.get('#open-settings-dialog-button').click()
|
||||
@@ -57,16 +47,16 @@ describe('Swap', () => {
|
||||
cy.get('#confirm-expert-mode').click()
|
||||
})
|
||||
|
||||
it('add a recipient is visible', () => {
|
||||
it.skip('add a recipient is visible', () => {
|
||||
cy.get('#add-recipient-button').should('be.visible')
|
||||
})
|
||||
|
||||
it('add a recipient', () => {
|
||||
it.skip('add a recipient', () => {
|
||||
cy.get('#add-recipient-button').click()
|
||||
cy.get('#recipient').should('exist')
|
||||
})
|
||||
|
||||
it('remove recipient', () => {
|
||||
it.skip('remove recipient', () => {
|
||||
cy.get('#add-recipient-button').click()
|
||||
cy.get('#remove-recipient-button').click()
|
||||
cy.get('#recipient').should('not.exist')
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
describe('Warning', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/swap?outputCurrency=0x0a40f26d74274b7f22b28556a27b35d97ce08e0a')
|
||||
})
|
||||
|
||||
it('Check that warning is displayed', () => {
|
||||
cy.get('.token-warning-container').should('be.visible')
|
||||
})
|
||||
|
||||
it('Check that warning hides after button dismissal', () => {
|
||||
cy.get('.token-dismiss-button').should('be.disabled')
|
||||
cy.get('.understand-checkbox').click()
|
||||
cy.get('.token-dismiss-button').should('not.be.disabled')
|
||||
cy.get('.token-dismiss-button').click()
|
||||
cy.get('.token-warning-container').should('not.be.visible')
|
||||
})
|
||||
})
|
||||
@@ -6,17 +6,21 @@
|
||||
|
||||
import { JsonRpcProvider } from '@ethersproject/providers'
|
||||
import { Wallet } from '@ethersproject/wallet'
|
||||
import { _Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
|
||||
import { Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
|
||||
|
||||
// never send real ether to this, obviously
|
||||
const PRIVATE_KEY_TEST_NEVER_USE = '0xad20c82497421e9784f18460ad2fe84f73569068e98e270b3e63743268af5763'
|
||||
const TEST_PRIVATE_KEY = Cypress.env('INTEGRATION_TEST_PRIVATE_KEY')
|
||||
|
||||
// address of the above key
|
||||
export const TEST_ADDRESS_NEVER_USE = '0x0fF2D1eFd7A57B7562b2bf27F3f37899dB27F4a5'
|
||||
export const TEST_ADDRESS_NEVER_USE = new Wallet(TEST_PRIVATE_KEY).address
|
||||
|
||||
export const TEST_ADDRESS_NEVER_USE_SHORTENED = '0x0fF2...F4a5'
|
||||
export const TEST_ADDRESS_NEVER_USE_SHORTENED = `${TEST_ADDRESS_NEVER_USE.substr(
|
||||
0,
|
||||
6
|
||||
)}...${TEST_ADDRESS_NEVER_USE.substr(-4, 4)}`
|
||||
|
||||
class CustomizedBridge extends Eip1193Bridge {
|
||||
chainId = 4
|
||||
|
||||
class CustomizedBridge extends _Eip1193Bridge {
|
||||
async sendAsync(...args) {
|
||||
console.debug('sendAsync called', ...args)
|
||||
return this.send(...args)
|
||||
@@ -75,8 +79,8 @@ Cypress.Commands.overwrite('visit', (original, url, options) => {
|
||||
options && options.onBeforeLoad && options.onBeforeLoad(win)
|
||||
win.localStorage.clear()
|
||||
const provider = new JsonRpcProvider('https://rinkeby.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847', 4)
|
||||
const signer = new Wallet(PRIVATE_KEY_TEST_NEVER_USE, provider)
|
||||
const signer = new Wallet(TEST_PRIVATE_KEY, provider)
|
||||
win.ethereum = new CustomizedBridge(signer, provider)
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
85
package.json
@@ -4,36 +4,54 @@
|
||||
"homepage": ".",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@emotion/core": "^11.0.0",
|
||||
"@ethersproject/experimental": "^5.0.1",
|
||||
"@popperjs/core": "^2.4.4",
|
||||
"@reach/dialog": "^0.10.3",
|
||||
"@reach/portal": "^0.10.3",
|
||||
"@react-hook/window-scroll": "^1.3.0",
|
||||
"@reduxjs/toolkit": "^1.3.5",
|
||||
"@storybook/addon-actions": "^6.1.17",
|
||||
"@storybook/addon-essentials": "^6.1.17",
|
||||
"@storybook/addon-links": "^6.1.17",
|
||||
"@storybook/addons": "^6.1.17",
|
||||
"@storybook/components": "^6.1.17",
|
||||
"@storybook/preset-create-react-app": "^3.1.5",
|
||||
"@storybook/preset-typescript": "^3.0.0",
|
||||
"@storybook/react": "^6.1.17",
|
||||
"@storybook/theming": "^6.1.17",
|
||||
"@styled-system/css": "^5.1.5",
|
||||
"@typechain/ethers-v5": "^7.0.0",
|
||||
"@types/jest": "^25.2.1",
|
||||
"@types/lodash.flatmap": "^4.5.6",
|
||||
"@types/luxon": "^1.24.4",
|
||||
"@types/multicodec": "^1.0.0",
|
||||
"@types/node": "^13.13.5",
|
||||
"@types/qs": "^6.9.2",
|
||||
"@types/react": "^16.9.34",
|
||||
"@types/react-dom": "^16.9.7",
|
||||
"@types/react-redux": "^7.1.8",
|
||||
"@types/react": "^17.0.2",
|
||||
"@types/react-dom": "^17.0.1",
|
||||
"@types/react-redux": "^7.1.16",
|
||||
"@types/react-router-dom": "^5.0.0",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.0",
|
||||
"@types/react-window": "^1.8.2",
|
||||
"@types/rebass": "^4.0.5",
|
||||
"@types/rebass": "^4.0.7",
|
||||
"@types/styled-components": "^5.1.0",
|
||||
"@types/testing-library__cypress": "^5.0.5",
|
||||
"@types/ua-parser-js": "^0.7.35",
|
||||
"@types/wcag-contrast": "^3.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^2.31.0",
|
||||
"@typescript-eslint/parser": "^2.31.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.1.0",
|
||||
"@typescript-eslint/parser": "^4.1.0",
|
||||
"@uniswap/default-token-list": "^2.0.0",
|
||||
"@uniswap/governance": "^1.0.2",
|
||||
"@uniswap/liquidity-staker": "^1.0.2",
|
||||
"@uniswap/merkle-distributor": "1.0.1",
|
||||
"@uniswap/sdk": "3.0.3",
|
||||
"@uniswap/token-lists": "^1.0.0-beta.17",
|
||||
"@uniswap/token-lists": "^1.0.0-beta.19",
|
||||
"@uniswap/v2-core": "1.0.0",
|
||||
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
||||
"@uniswap/v2-sdk": "^3.0.0-alpha.0",
|
||||
"@uniswap/v3-core": "1.0.0",
|
||||
"@uniswap/v3-periphery": "1.0.0",
|
||||
"@uniswap/v3-sdk": "^3.0.0-alpha.1",
|
||||
"@web3-react/core": "^6.0.9",
|
||||
"@web3-react/fortmatic-connector": "^6.0.9",
|
||||
"@web3-react/injected-connector": "^6.0.7",
|
||||
@@ -45,7 +63,7 @@
|
||||
"copy-to-clipboard": "^3.2.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"cypress": "^4.11.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint": "^7.11.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"eslint-plugin-react": "^7.19.0",
|
||||
@@ -56,48 +74,58 @@
|
||||
"i18next-xhr-backend": "^2.0.1",
|
||||
"inter-ui": "^3.13.1",
|
||||
"jazzicon": "^1.5.0",
|
||||
"lightweight-charts": "^3.3.0",
|
||||
"lodash.flatmap": "^4.5.0",
|
||||
"luxon": "^1.25.0",
|
||||
"multicodec": "^2.0.0",
|
||||
"multihashes": "^3.0.1",
|
||||
"multicodec": "^3.0.1",
|
||||
"multihashes": "^4.0.2",
|
||||
"node-vibrant": "^3.1.5",
|
||||
"polished": "^3.3.2",
|
||||
"prettier": "^1.17.0",
|
||||
"prettier": "^2.2.1",
|
||||
"qs": "^6.9.4",
|
||||
"react": "^16.13.1",
|
||||
"react": "^17.0.1",
|
||||
"react-confetti": "^6.0.0",
|
||||
"react-device-detect": "^1.6.2",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-feather": "^2.0.8",
|
||||
"react-ga": "^2.5.7",
|
||||
"react-i18next": "^10.7.0",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-popper": "^2.2.3",
|
||||
"react-redux": "^7.2.0",
|
||||
"react-redux": "^7.2.2",
|
||||
"react-router-dom": "^5.0.0",
|
||||
"react-scripts": "^3.4.1",
|
||||
"react-scripts": "^4.0.3",
|
||||
"react-spring": "^8.0.27",
|
||||
"react-use-gesture": "^6.0.14",
|
||||
"react-virtualized-auto-sizer": "^1.0.2",
|
||||
"react-window": "^1.8.5",
|
||||
"rebass": "^4.0.7",
|
||||
"redux-localstorage-simple": "^2.3.1",
|
||||
"serve": "^11.3.0",
|
||||
"serve": "^11.3.2",
|
||||
"start-server-and-test": "^1.11.0",
|
||||
"styled-components": "^4.2.0",
|
||||
"typescript": "^3.8.3",
|
||||
"styled-system": "^5.1.5",
|
||||
"typechain": "^5.0.0",
|
||||
"typescript": "^4.2.3",
|
||||
"ua-parser-js": "^0.7.28",
|
||||
"use-count-up": "^2.2.5",
|
||||
"wcag-contrast": "^3.0.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"@walletconnect/web3-provider": "1.1.1-alpha.0"
|
||||
"wcag-contrast": "^3.0.0",
|
||||
"workbox-core": "^6.1.0",
|
||||
"workbox-expiration": "^6.1.0",
|
||||
"workbox-precaching": "^6.1.0",
|
||||
"workbox-routing": "^6.1.0",
|
||||
"workbox-strategies": "^6.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
"eject": "react-scripts eject",
|
||||
"integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'"
|
||||
"compile-contract-types": "yarn compile-external-abi-types && yarn compile-v3-contract-types",
|
||||
"compile-external-abi-types": "npx typechain --target ethers-v5 --out-dir src/abis/types './src/abis/**/*.json'",
|
||||
"compile-v3-contract-types": "npx typechain --target ethers-v5 --out-dir src/types/v3 './node_modules/@uniswap/?(v3-core|v3-periphery)/artifacts/contracts/**/*.json'",
|
||||
"build": "yarn compile-contract-types && react-scripts build",
|
||||
"integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'",
|
||||
"postinstall": "yarn compile-contract-types",
|
||||
"start": "yarn compile-contract-types && react-scripts start",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"test": "react-scripts test --env=jsdom"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app",
|
||||
@@ -117,5 +145,6 @@
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"license": "GPL-3.0-or-later"
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.6 KiB |
@@ -87,5 +87,30 @@
|
||||
"forAtLeast": "for at least ",
|
||||
"brokenToken": "The selected token is not compatible with Uniswap V1. Adding liquidity will result in locked funds.",
|
||||
"toleranceExplanation": "Lowering this limit decreases your risk of frontrunning. However, this makes more likely that your transaction will fail due to normal price movements.",
|
||||
"tokenSearchPlaceholder": "Search name or paste address"
|
||||
"tokenSearchPlaceholder": "Search name or paste address",
|
||||
"selectFee": "Select Fee",
|
||||
"selectLiquidityRange": "Set Price Range",
|
||||
"selectPool": "Select Fee Tier",
|
||||
"depositAmounts": "Deposit Amounts",
|
||||
"fee": "fee",
|
||||
"setLimits": "Set Limits",
|
||||
"percent": "Percent",
|
||||
"rate": "Rate",
|
||||
"currentRate": "Current {{label}} Price:",
|
||||
"inactiveRangeWarning": "Your position will not earn fees or be used in trades until the market price moves into your range.",
|
||||
"invalidRangeWarning": "Invalid range selected. The min price must be lower than the max price.",
|
||||
"connectWallet": "Connect Wallet",
|
||||
"unsupportedAsset": "Unsupported Asset",
|
||||
"feePool": "Fee Pool",
|
||||
"feeTier": "Fee Tier",
|
||||
"rebalanceMessage": "Your underlying tokens will be automatically rebalanced when the rate of the pool changes and may be different when you withdraw the position.",
|
||||
"addEarnHelper": "You will earn fees from trades proportional to your share of the pool.",
|
||||
"learnMoreAboutFess": " Learn more about earning fees.",
|
||||
"selectAPool": "Choose the best fee tier for your selected pair.",
|
||||
"poolType": "Select a fee tier based on your preferred liquidity provider fee.",
|
||||
"rangeWarning": "Your liquidity will only be active and earning fees when the rate of the pool is within this price range.",
|
||||
"chooseLiquidityAmount": "Choose an amount of tokens to open this liquidity position. If you don’t have enough tokens you can trade for them with a Swap.",
|
||||
"inputTokenDynamic": "Input {{label}}",
|
||||
"selectStartingPrice": "Set Starting Price",
|
||||
"newPoolPrice": "Select the market rate for the tokens being added."
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"short_name": "Uniswap",
|
||||
"name": "Uniswap",
|
||||
"background_color": "#fff",
|
||||
"display": "standalone",
|
||||
"homepage_url": "https://app.uniswap.org",
|
||||
"icons": [
|
||||
{
|
||||
"src": "./images/192x192_App_Icon.png",
|
||||
@@ -16,7 +17,8 @@
|
||||
}
|
||||
],
|
||||
"orientation": "portrait",
|
||||
"display": "standalone",
|
||||
"theme_color": "#ff007a",
|
||||
"background_color": "#fff"
|
||||
"name": "Uniswap",
|
||||
"short_name": "Uniswap",
|
||||
"start_url": ".",
|
||||
"theme_color": "#ff007a"
|
||||
}
|
||||
|
||||
20
src/abis/eip_2612.json
Normal file
@@ -0,0 +1,20 @@
|
||||
[
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [{ "name": "owner", "type": "address" }],
|
||||
"name": "nonces",
|
||||
"outputs": [{ "name": "", "type": "uint256" }],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "DOMAIN_SEPARATOR",
|
||||
"outputs": [{ "name": "", "type": "bytes32" }],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
330
src/abis/multicall.json
Normal file
@@ -0,0 +1,330 @@
|
||||
[
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "target",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "callData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Multicall2.Call[]",
|
||||
"name": "calls",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "aggregate",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "blockNumber",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes[]",
|
||||
"name": "returnData",
|
||||
"type": "bytes[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "target",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "callData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Multicall2.Call[]",
|
||||
"name": "calls",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "blockAndAggregate",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "blockNumber",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "blockHash",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "success",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "returnData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Multicall2.Result[]",
|
||||
"name": "returnData",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "blockNumber",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getBlockHash",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "blockHash",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "success",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "returnData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Multicall2.Result[]",
|
||||
"name": "returnData",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getBlockNumber",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "blockNumber",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getCurrentBlockCoinbase",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "coinbase",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getCurrentBlockDifficulty",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "difficulty",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getCurrentBlockGasLimit",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "gaslimit",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getCurrentBlockTimestamp",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "timestamp",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "addr",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "getEthBalance",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "balance",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getLastBlockHash",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "blockHash",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "requireSuccess",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "target",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "callData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Multicall2.Call[]",
|
||||
"name": "calls",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "tryAggregate",
|
||||
"outputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "success",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "returnData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Multicall2.Result[]",
|
||||
"name": "returnData",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "requireSuccess",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "target",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "callData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Multicall2.Call[]",
|
||||
"name": "calls",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "tryBlockAndAggregate",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "blockNumber",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "blockHash",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "success",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "returnData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Multicall2.Result[]",
|
||||
"name": "returnData",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
165
src/abis/multicall2.json
Normal file
@@ -0,0 +1,165 @@
|
||||
[
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"components": [
|
||||
{ "internalType": "address", "name": "target", "type": "address" },
|
||||
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
|
||||
],
|
||||
"internalType": "struct Multicall2.Call[]",
|
||||
"name": "calls",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "aggregate",
|
||||
"outputs": [
|
||||
{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" },
|
||||
{ "internalType": "bytes[]", "name": "returnData", "type": "bytes[]" }
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"components": [
|
||||
{ "internalType": "address", "name": "target", "type": "address" },
|
||||
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
|
||||
],
|
||||
"internalType": "struct Multicall2.Call[]",
|
||||
"name": "calls",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "blockAndAggregate",
|
||||
"outputs": [
|
||||
{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" },
|
||||
{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" },
|
||||
{
|
||||
"components": [
|
||||
{ "internalType": "bool", "name": "success", "type": "bool" },
|
||||
{ "internalType": "bytes", "name": "returnData", "type": "bytes" }
|
||||
],
|
||||
"internalType": "struct Multicall2.Result[]",
|
||||
"name": "returnData",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" }],
|
||||
"name": "getBlockHash",
|
||||
"outputs": [{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getBlockNumber",
|
||||
"outputs": [{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getCurrentBlockCoinbase",
|
||||
"outputs": [{ "internalType": "address", "name": "coinbase", "type": "address" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getCurrentBlockDifficulty",
|
||||
"outputs": [{ "internalType": "uint256", "name": "difficulty", "type": "uint256" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getCurrentBlockGasLimit",
|
||||
"outputs": [{ "internalType": "uint256", "name": "gaslimit", "type": "uint256" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getCurrentBlockTimestamp",
|
||||
"outputs": [{ "internalType": "uint256", "name": "timestamp", "type": "uint256" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [{ "internalType": "address", "name": "addr", "type": "address" }],
|
||||
"name": "getEthBalance",
|
||||
"outputs": [{ "internalType": "uint256", "name": "balance", "type": "uint256" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getLastBlockHash",
|
||||
"outputs": [{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{ "internalType": "bool", "name": "requireSuccess", "type": "bool" },
|
||||
{
|
||||
"components": [
|
||||
{ "internalType": "address", "name": "target", "type": "address" },
|
||||
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
|
||||
],
|
||||
"internalType": "struct Multicall2.Call[]",
|
||||
"name": "calls",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "tryAggregate",
|
||||
"outputs": [
|
||||
{
|
||||
"components": [
|
||||
{ "internalType": "bool", "name": "success", "type": "bool" },
|
||||
{ "internalType": "bytes", "name": "returnData", "type": "bytes" }
|
||||
],
|
||||
"internalType": "struct Multicall2.Result[]",
|
||||
"name": "returnData",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{ "internalType": "bool", "name": "requireSuccess", "type": "bool" },
|
||||
{
|
||||
"components": [
|
||||
{ "internalType": "address", "name": "target", "type": "address" },
|
||||
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
|
||||
],
|
||||
"internalType": "struct Multicall2.Call[]",
|
||||
"name": "calls",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "tryBlockAndAggregate",
|
||||
"outputs": [
|
||||
{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" },
|
||||
{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" },
|
||||
{
|
||||
"components": [
|
||||
{ "internalType": "bool", "name": "success", "type": "bool" },
|
||||
{ "internalType": "bytes", "name": "returnData", "type": "bytes" }
|
||||
],
|
||||
"internalType": "struct Multicall2.Result[]",
|
||||
"name": "returnData",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
6
src/abis/staking-rewards.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { Interface } from '@ethersproject/abi'
|
||||
import { abi as STAKING_REWARDS_ABI } from '@uniswap/liquidity-staker/build/StakingRewards.json'
|
||||
|
||||
const STAKING_REWARDS_INTERFACE = new Interface(STAKING_REWARDS_ABI)
|
||||
|
||||
export { STAKING_REWARDS_INTERFACE }
|
||||
@@ -89,7 +89,7 @@
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 22405
|
||||
"gas": "22405"
|
||||
},
|
||||
{
|
||||
"name": "tokenByIndex",
|
||||
@@ -108,7 +108,7 @@
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 631
|
||||
"gas": "631"
|
||||
},
|
||||
{
|
||||
"name": "tokenOfOwnerByIndex",
|
||||
@@ -131,7 +131,7 @@
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 1248
|
||||
"gas": "1248"
|
||||
},
|
||||
{
|
||||
"name": "transferFrom",
|
||||
@@ -153,7 +153,7 @@
|
||||
"constant": false,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 259486
|
||||
"gas": "259486"
|
||||
},
|
||||
{
|
||||
"name": "safeTransferFrom",
|
||||
@@ -217,7 +217,7 @@
|
||||
"constant": false,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 38422
|
||||
"gas": "38422"
|
||||
},
|
||||
{
|
||||
"name": "setApprovalForAll",
|
||||
@@ -235,7 +235,7 @@
|
||||
"constant": false,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 38016
|
||||
"gas": "38016"
|
||||
},
|
||||
{
|
||||
"name": "mint",
|
||||
@@ -254,7 +254,7 @@
|
||||
"constant": false,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 182636
|
||||
"gas": "182636"
|
||||
},
|
||||
{
|
||||
"name": "changeMinter",
|
||||
@@ -268,7 +268,7 @@
|
||||
"constant": false,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 35897
|
||||
"gas": "35897"
|
||||
},
|
||||
{
|
||||
"name": "changeURI",
|
||||
@@ -282,7 +282,7 @@
|
||||
"constant": false,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 35927
|
||||
"gas": "35927"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
@@ -296,7 +296,7 @@
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 6612
|
||||
"gas": "6612"
|
||||
},
|
||||
{
|
||||
"name": "symbol",
|
||||
@@ -310,7 +310,7 @@
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 6642
|
||||
"gas": "6642"
|
||||
},
|
||||
{
|
||||
"name": "totalSupply",
|
||||
@@ -324,7 +324,7 @@
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 873
|
||||
"gas": "873"
|
||||
},
|
||||
{
|
||||
"name": "minter",
|
||||
@@ -338,7 +338,7 @@
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 903
|
||||
"gas": "903"
|
||||
},
|
||||
{
|
||||
"name": "socks",
|
||||
@@ -353,7 +353,7 @@
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 933
|
||||
"gas": "933"
|
||||
},
|
||||
{
|
||||
"name": "newURI",
|
||||
@@ -367,7 +367,7 @@
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 963
|
||||
"gas": "963"
|
||||
},
|
||||
{
|
||||
"name": "ownerOf",
|
||||
@@ -386,7 +386,7 @@
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 1126
|
||||
"gas": "1126"
|
||||
},
|
||||
{
|
||||
"name": "balanceOf",
|
||||
@@ -405,7 +405,7 @@
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 1195
|
||||
"gas": "1195"
|
||||
},
|
||||
{
|
||||
"name": "getApproved",
|
||||
@@ -424,7 +424,7 @@
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 1186
|
||||
"gas": "1186"
|
||||
},
|
||||
{
|
||||
"name": "isApprovedForAll",
|
||||
@@ -447,7 +447,7 @@
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 1415
|
||||
"gas": "1415"
|
||||
},
|
||||
{
|
||||
"name": "supportsInterface",
|
||||
@@ -466,6 +466,6 @@
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": 1246
|
||||
"gas": "1246"
|
||||
}
|
||||
]
|
||||
BIN
src/assets/images/sandtexture.webp
Normal file
|
After Width: | Height: | Size: 235 KiB |
BIN
src/assets/images/squiggle.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
src/assets/images/token-list-logo.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
src/assets/images/tokenlistsgrouped.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
@@ -1,11 +1,11 @@
|
||||
<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.15217 1.55141C3.96412 1.52242 3.95619 1.51902 4.04468 1.5055C4.21427 1.47958 4.61472 1.51491 4.89067 1.58012C5.53489 1.73232 6.12109 2.12221 6.74683 2.81466L6.91307 2.99862L7.15088 2.96062C8.15274 2.8006 9.17194 2.92778 10.0244 3.31918C10.2589 3.42686 10.6287 3.64121 10.6749 3.69629C10.6896 3.71384 10.7166 3.82684 10.7349 3.94742C10.7982 4.36458 10.7665 4.68434 10.6382 4.92317C10.5683 5.05313 10.5644 5.09432 10.6114 5.20554C10.6489 5.2943 10.7534 5.35999 10.8569 5.35985C11.0687 5.35956 11.2967 5.0192 11.4024 4.54561L11.4444 4.3575L11.5275 4.45109C11.9835 4.96459 12.3417 5.66488 12.4032 6.16335L12.4192 6.29332L12.3426 6.17517C12.2107 5.97186 12.0781 5.83346 11.9084 5.72183C11.6024 5.52062 11.2789 5.45215 10.4222 5.40727C9.64839 5.36675 9.21045 5.30106 8.77621 5.16032C8.03738 4.9209 7.66493 4.60204 6.78729 3.4576C6.39748 2.94928 6.15654 2.66804 5.91687 2.44155C5.37228 1.92691 4.83716 1.65701 4.15217 1.55141Z" fill="white"/>
|
||||
<path d="M4.15217 1.55141C3.96412 1.52242 3.95619 1.51902 4.04468 1.5055C4.21427 1.47958 4.61472 1.51491 4.89067 1.58012C5.53489 1.73232 6.12109 2.12221 6.74683 2.81466L6.91307 2.99862L7.15088 2.96062C8.15274 2.8006 9.17194 2.92778 10.0244 3.31918C10.2589 3.42686 10.6287 3.64121 10.6749 3.69629C10.6896 3.71384 10.7166 3.82684 10.7349 3.94742C10.7982 4.36458 10.7665 4.68434 10.6382 4.92317C10.5683 5.05313 10.5644 5.09432 10.6114 5.20554C10.6489 5.2943 10.7534 5.35999 10.8569 5.35985C11.0687 5.35956 11.2968 5.0192 11.4024 4.54561L11.4444 4.3575L11.5275 4.45109C11.9835 4.96459 12.3417 5.66488 12.4032 6.16335L12.4192 6.29332L12.3426 6.17517C12.2107 5.97186 12.0781 5.83346 11.9084 5.72183C11.6024 5.52062 11.2789 5.45215 10.4222 5.40727C9.64839 5.36675 9.21045 5.30106 8.77621 5.16032C8.03738 4.9209 7.66493 4.60204 6.78729 3.4576C6.39748 2.94928 6.15654 2.66804 5.91687 2.44155C5.37228 1.92691 4.83716 1.65701 4.15217 1.55141Z" fill="white"/>
|
||||
<path d="M10.8494 2.68637C10.8689 2.34575 10.9153 2.12108 11.0088 1.9159C11.0458 1.83469 11.0804 1.76822 11.0858 1.76822C11.0911 1.76822 11.075 1.82816 11.05 1.90142C10.9821 2.10054 10.9709 2.3729 11.0177 2.68978C11.0771 3.09184 11.1109 3.14985 11.5385 3.58416C11.739 3.78788 11.9723 4.0448 12.0568 4.15511L12.2106 4.35568L12.0568 4.21234C11.8688 4.03705 11.4364 3.6952 11.3409 3.64633C11.2768 3.61356 11.2673 3.61413 11.2278 3.65321C11.1914 3.68922 11.1837 3.74333 11.1787 3.99915C11.1708 4.39786 11.1161 4.65377 10.9842 4.90965C10.9128 5.04805 10.9015 5.01851 10.9661 4.8623C11.0143 4.74566 11.0192 4.69439 11.0189 4.30842C11.0181 3.53291 10.9255 3.34647 10.3823 3.02709C10.2447 2.94618 10.0179 2.8295 9.87839 2.76778C9.73887 2.70606 9.62805 2.6523 9.63208 2.64828C9.64746 2.63307 10.1772 2.78675 10.3905 2.86828C10.7077 2.98954 10.76 3.00526 10.7985 2.99063C10.8244 2.98082 10.8369 2.90608 10.8494 2.68637Z" fill="white"/>
|
||||
<path d="M4.51745 4.01304C4.13569 3.49066 3.89948 2.68973 3.95062 2.091L3.96643 1.90572L4.05333 1.92148C4.21652 1.95106 4.49789 2.05515 4.62964 2.13469C4.9912 2.35293 5.14773 2.64027 5.30697 3.37811C5.35362 3.59423 5.41482 3.8388 5.44298 3.9216C5.48831 4.05487 5.65962 4.36617 5.7989 4.56834C5.89922 4.71395 5.83258 4.78295 5.61082 4.76305C5.27215 4.73267 4.8134 4.41799 4.51745 4.01304Z" fill="white"/>
|
||||
<path d="M10.3863 7.90088C8.60224 7.18693 7.97389 6.56721 7.97389 5.52157C7.97389 5.36769 7.97922 5.24179 7.98571 5.24179C7.99221 5.24179 8.06124 5.29257 8.1391 5.35465C8.50088 5.64305 8.906 5.76623 10.0275 5.92885C10.6875 6.02455 11.0589 6.10185 11.4015 6.21477C12.4904 6.57371 13.1641 7.30212 13.3248 8.29426C13.3715 8.58255 13.3441 9.12317 13.2684 9.4081C13.2087 9.63315 13.0263 10.0388 12.9779 10.0544C12.9645 10.0587 12.9514 10.0076 12.9479 9.93809C12.9296 9.56554 12.7402 9.20285 12.4221 8.93116C12.0604 8.62227 11.5745 8.37633 10.3863 7.90088Z" fill="white"/>
|
||||
<path d="M9.13385 8.19748C9.11149 8.06527 9.07272 7.89643 9.04769 7.82228L9.00217 7.68748L9.08672 7.7818C9.20374 7.91234 9.2962 8.07937 9.37457 8.30185C9.43438 8.47165 9.44111 8.52215 9.44066 8.79807C9.4402 9.06896 9.43273 9.12575 9.3775 9.27859C9.29042 9.51959 9.18233 9.69048 9.00097 9.87391C8.67507 10.2036 8.25607 10.3861 7.65143 10.4618C7.54633 10.4749 7.24 10.4971 6.97069 10.511C6.292 10.5461 5.84531 10.6186 5.44393 10.7587C5.38623 10.7788 5.3347 10.7911 5.32947 10.7859C5.31323 10.7698 5.58651 10.6079 5.81223 10.4998C6.1305 10.3474 6.44733 10.2643 7.15719 10.1468C7.50785 10.0887 7.86998 10.0183 7.96194 9.99029C8.83033 9.72566 9.27671 9.04276 9.13385 8.19748Z" fill="white"/>
|
||||
<path d="M9.95169 9.64109C9.71465 9.13463 9.66022 8.64564 9.79008 8.18961C9.80399 8.14088 9.82632 8.101 9.83976 8.101C9.85319 8.101 9.90913 8.13105 9.96404 8.16777C10.0733 8.24086 10.2924 8.36395 10.876 8.68023C11.6043 9.0749 12.0196 9.3805 12.302 9.72965C12.5493 10.0354 12.7023 10.3837 12.776 10.8084C12.8177 11.0489 12.7932 11.6277 12.7311 11.8699C12.5353 12.6337 12.0802 13.2336 11.4311 13.5837C11.336 13.635 11.2506 13.6771 11.2414 13.6773C11.2321 13.6775 11.2668 13.5899 11.3184 13.4827C11.5367 13.029 11.5616 12.5877 11.3965 12.0965C11.2954 11.7957 11.0893 11.4287 10.6732 10.8084C10.1893 10.0873 10.0707 9.89539 9.95169 9.64109Z" fill="white"/>
|
||||
<path d="M3.25046 12.3737C3.91252 11.8181 4.73629 11.4234 5.48666 11.3022C5.81005 11.25 6.34877 11.2707 6.64823 11.3469C7.12824 11.469 7.55763 11.7425 7.78094 12.0683C7.99918 12.3867 8.09281 12.6642 8.19029 13.2816C8.22875 13.5252 8.27057 13.7697 8.28323 13.8251C8.35644 14.1451 8.4989 14.4008 8.67544 14.5293C8.95583 14.7333 9.43865 14.7459 9.91362 14.5618C9.99423 14.5305 10.0642 14.5089 10.0691 14.5138C10.0864 14.5308 9.84719 14.6899 9.67847 14.7737C9.45143 14.8864 9.2709 14.93 9.03102 14.93C8.59601 14.93 8.23486 14.7101 7.9335 14.2616C7.87419 14.1733 7.7409 13.909 7.63729 13.6741C7.3191 12.9528 7.16199 12.7331 6.79255 12.4926C6.47104 12.2834 6.05641 12.2459 5.74449 12.3979C5.33475 12.5976 5.22043 13.118 5.51389 13.4478C5.63053 13.5789 5.84803 13.6919 6.02588 13.7139C6.35861 13.7551 6.64455 13.5035 6.64455 13.1696C6.64455 12.9528 6.56071 12.8291 6.34966 12.7344C6.0614 12.6051 5.75156 12.7563 5.75304 13.0254C5.75368 13.1402 5.80396 13.2122 5.91971 13.2643C5.99397 13.2977 5.99569 13.3003 5.93514 13.2878C5.67066 13.2333 5.6087 12.9164 5.82135 12.706C6.07667 12.4535 6.60461 12.5649 6.78591 12.9097C6.86208 13.0545 6.87091 13.3429 6.80451 13.517C6.6559 13.9068 6.22256 14.1117 5.78297 14.0002C5.48368 13.9242 5.36181 13.842 5.00097 13.4726C4.37395 12.8306 4.13053 12.7062 3.22657 12.566L3.05335 12.5391L3.25046 12.3737Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.308383 0.883984C2.40235 3.40996 3.84457 4.45213 4.00484 4.67231C4.13717 4.85412 4.08737 5.01757 3.86067 5.14567C3.7346 5.21689 3.47541 5.28905 3.34564 5.28905C3.19887 5.28905 3.14847 5.23278 3.14847 5.23278C3.06337 5.15255 3.01544 5.16658 2.5784 4.39555C1.97166 3.45981 1.46389 2.68357 1.45004 2.67057C1.41801 2.64052 1.41856 2.64153 2.51654 4.59413C2.69394 5.0011 2.55182 5.15049 2.55182 5.20845C2.55182 5.32636 2.51946 5.38834 2.37311 5.55059C2.12914 5.8211 2.02008 6.12505 1.94135 6.7541C1.8531 7.45926 1.60492 7.95737 0.917156 8.80989C0.514562 9.30893 0.448686 9.4004 0.3471 9.60153C0.219144 9.85482 0.183961 9.99669 0.169701 10.3165C0.154629 10.6547 0.183983 10.8732 0.287934 11.1965C0.378939 11.4796 0.473932 11.6665 0.716778 12.0403C0.926352 12.3629 1.04702 12.6027 1.04702 12.6965C1.04702 12.7711 1.06136 12.7712 1.38611 12.6983C2.16328 12.5239 2.79434 12.2171 3.14925 11.8411C3.36891 11.6084 3.42048 11.4799 3.42215 11.1611C3.42325 10.9525 3.41587 10.9088 3.35914 10.7888C3.2668 10.5935 3.09869 10.4311 2.72817 10.1794C2.2427 9.84953 2.03534 9.58398 1.97807 9.21878C1.93108 8.91913 1.98559 8.70771 2.25416 8.14825C2.53214 7.56916 2.60103 7.32239 2.64763 6.73869C2.67773 6.36158 2.71941 6.21286 2.82842 6.09348C2.94212 5.969 3.04447 5.92684 3.32584 5.88863C3.78457 5.82635 4.07667 5.70839 4.31677 5.48849C4.52505 5.29772 4.61221 5.11391 4.62558 4.8372L4.63574 4.62747L4.51934 4.49259C4.09783 4.00411 0.0261003 0.5 0.000160437 0.5C-0.00538105 0.5 0.133325 0.672804 0.308383 0.883984ZM1.28364 10.6992C1.37894 10.5314 1.3283 10.3158 1.16889 10.2104C1.01827 10.1109 0.78428 10.1578 0.78428 10.2875C0.78428 10.3271 0.806303 10.3559 0.855937 10.3813C0.939514 10.424 0.945581 10.4721 0.879823 10.5703C0.81323 10.6698 0.818604 10.7573 0.894991 10.8167C1.0181 10.9125 1.19237 10.8598 1.28364 10.6992Z" fill="white"/>
|
||||
<path d="M9.13385 8.19748C9.11149 8.06527 9.07272 7.89643 9.04769 7.82228L9.00217 7.68748L9.08672 7.7818C9.20374 7.91233 9.2962 8.07937 9.37457 8.30185C9.43438 8.47165 9.44111 8.52215 9.44066 8.79807C9.4402 9.06896 9.43273 9.12575 9.3775 9.27858C9.29042 9.51959 9.18233 9.69048 9.00097 9.87391C8.67507 10.2036 8.25607 10.3861 7.65143 10.4618C7.54633 10.4749 7.24 10.4971 6.97069 10.511C6.292 10.5461 5.84531 10.6186 5.44393 10.7587C5.38623 10.7788 5.3347 10.7911 5.32947 10.7859C5.31323 10.7698 5.58651 10.6079 5.81223 10.4998C6.1305 10.3474 6.44733 10.2643 7.15719 10.1468C7.50785 10.0887 7.86998 10.0183 7.96194 9.99029C8.83033 9.72566 9.27671 9.04276 9.13385 8.19748Z" fill="white"/>
|
||||
<path d="M9.95169 9.64109C9.71465 9.13463 9.66022 8.64564 9.79009 8.18961C9.80399 8.14088 9.82632 8.101 9.83976 8.101C9.85319 8.101 9.90913 8.13105 9.96404 8.16777C10.0733 8.24086 10.2924 8.36395 10.876 8.68023C11.6043 9.0749 12.0196 9.3805 12.302 9.72965C12.5493 10.0354 12.7023 10.3837 12.776 10.8084C12.8177 11.0489 12.7932 11.6277 12.7311 11.8699C12.5353 12.6337 12.0802 13.2336 11.4311 13.5837C11.336 13.635 11.2506 13.6771 11.2414 13.6773C11.2321 13.6775 11.2668 13.5899 11.3184 13.4827C11.5367 13.029 11.5616 12.5877 11.3965 12.0965C11.2954 11.7957 11.0893 11.4287 10.6732 10.8084C10.1893 10.0873 10.0707 9.89539 9.95169 9.64109Z" fill="white"/>
|
||||
<path d="M3.25046 12.3737C3.91252 11.8181 4.73629 11.4234 5.48666 11.3022C5.81005 11.25 6.34877 11.2707 6.64823 11.3469C7.12824 11.469 7.55763 11.7425 7.78094 12.0683C7.99918 12.3867 8.09281 12.6642 8.19029 13.2816C8.22875 13.5252 8.27057 13.7697 8.28323 13.8251C8.35644 14.1451 8.4989 14.4008 8.67544 14.5293C8.95583 14.7333 9.43865 14.7459 9.91362 14.5618C9.99423 14.5305 10.0642 14.5089 10.0691 14.5138C10.0864 14.5308 9.84719 14.6899 9.67847 14.7737C9.45143 14.8864 9.2709 14.93 9.03102 14.93C8.59601 14.93 8.23486 14.7101 7.9335 14.2616C7.87419 14.1733 7.7409 13.909 7.63729 13.6741C7.3191 12.9528 7.16199 12.7331 6.79255 12.4926C6.47104 12.2834 6.05641 12.2459 5.74449 12.3979C5.33475 12.5976 5.22043 13.118 5.51389 13.4478C5.63053 13.5789 5.84803 13.6919 6.02588 13.7139C6.35861 13.7551 6.64455 13.5035 6.64455 13.1696C6.64455 12.9528 6.56071 12.8291 6.34966 12.7344C6.0614 12.6051 5.75156 12.7562 5.75304 13.0254C5.75368 13.1402 5.80396 13.2122 5.91971 13.2643C5.99397 13.2977 5.99569 13.3003 5.93514 13.2878C5.67066 13.2333 5.6087 12.9164 5.82135 12.706C6.07667 12.4535 6.60461 12.5649 6.78591 12.9097C6.86208 13.0545 6.87092 13.3429 6.80451 13.517C6.6559 13.9068 6.22256 14.1117 5.78297 14.0002C5.48368 13.9242 5.36181 13.842 5.00097 13.4726C4.37395 12.8306 4.13053 12.7062 3.22657 12.566L3.05335 12.5391L3.25046 12.3737Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.308383 0.883984C2.40235 3.40996 3.84457 4.45213 4.00484 4.67231C4.13717 4.85412 4.08737 5.01757 3.86067 5.14567C3.7346 5.21689 3.47541 5.28905 3.34564 5.28905C3.19887 5.28905 3.14847 5.23278 3.14847 5.23278C3.06337 5.15255 3.01544 5.16658 2.5784 4.39555C1.97166 3.45981 1.46389 2.68357 1.45004 2.67057C1.41801 2.64052 1.41856 2.64153 2.51654 4.59413C2.69394 5.0011 2.55182 5.15049 2.55182 5.20845C2.55182 5.32636 2.51946 5.38834 2.37311 5.55059C2.12914 5.8211 2.02008 6.12505 1.94135 6.7541C1.8531 7.45926 1.60492 7.95737 0.917156 8.80989C0.514562 9.30893 0.448686 9.4004 0.3471 9.60153C0.219144 9.85482 0.183961 9.99669 0.169701 10.3165C0.154629 10.6547 0.183983 10.8732 0.287934 11.1965C0.378939 11.4796 0.473932 11.6665 0.716778 12.0403C0.926351 12.3629 1.04702 12.6027 1.04702 12.6965C1.04702 12.7711 1.06136 12.7712 1.38611 12.6983C2.16328 12.5239 2.79434 12.2171 3.14925 11.8411C3.36891 11.6084 3.42048 11.4799 3.42215 11.1611C3.42325 10.9525 3.41587 10.9088 3.35914 10.7888C3.2668 10.5935 3.09869 10.4311 2.72817 10.1794C2.2427 9.84953 2.03534 9.58398 1.97807 9.21878C1.93108 8.91913 1.98559 8.70771 2.25416 8.14825C2.53214 7.56916 2.60103 7.32239 2.64763 6.73869C2.67773 6.36158 2.71941 6.21286 2.82842 6.09348C2.94212 5.969 3.04447 5.92684 3.32584 5.88863C3.78457 5.82635 4.07667 5.70839 4.31677 5.48849C4.52505 5.29772 4.61221 5.11391 4.62558 4.8372L4.63574 4.62747L4.51934 4.49259C4.09783 4.00411 0.0261003 0.5 0.000160437 0.5C-0.00538105 0.5 0.133325 0.672804 0.308383 0.883984ZM1.28364 10.6992C1.37894 10.5314 1.3283 10.3158 1.16889 10.2104C1.01827 10.1109 0.78428 10.1578 0.78428 10.2875C0.78428 10.3271 0.806303 10.3559 0.855937 10.3813C0.939514 10.424 0.945581 10.4721 0.879823 10.5703C0.81323 10.6698 0.818604 10.7573 0.894991 10.8167C1.0181 10.9125 1.19237 10.8598 1.28364 10.6992Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.92523 5.99865C4.70988 6.06439 4.50054 6.29124 4.43574 6.5291C4.39621 6.67421 4.41864 6.92875 4.47785 7.00736C4.57351 7.13433 4.66602 7.16778 4.91651 7.16603C5.40693 7.16263 5.83327 6.95358 5.88284 6.69224C5.92347 6.47801 5.73622 6.18112 5.4783 6.05078C5.34521 5.98355 5.06217 5.95688 4.92523 5.99865ZM5.49853 6.44422C5.57416 6.33741 5.54107 6.22198 5.41245 6.14391C5.1675 5.99525 4.79708 6.11827 4.79708 6.34826C4.79708 6.46274 4.99025 6.58765 5.16731 6.58765C5.28516 6.58765 5.44644 6.5178 5.49853 6.44422Z" fill="white"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
6
src/assets/svg/switch.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 2.5L12.5 5L10 7.5" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3 5L12.3333 5" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M5.5 13.5L3 11L5.5 8.5" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12.3333 11L3 11" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 476 B |
30
src/assets/svg/tokenlist.svg
Normal file
@@ -0,0 +1,30 @@
|
||||
<svg width="225" height="225" viewBox="0 0 225 225" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M74.8125 190.529C65.7561 190.513 55.5298 183.748 51.9715 175.42L19.9417 100.456C16.3834 92.1277 20.8404 85.39 29.8968 85.4068L111.417 85.5579C120.473 85.5747 130.699 92.3395 134.258 100.668L166.288 175.632C169.846 183.96 165.389 190.697 156.332 190.681L74.8125 190.529Z" fill="#131313"/>
|
||||
<path d="M92.1541 164.065C83.0977 164.049 72.8715 157.284 69.3132 148.956L28.3003 52.9672C24.7419 44.6391 29.199 37.9015 38.2554 37.9182L142.638 38.1117C151.695 38.1285 161.921 44.8933 165.479 53.2214L206.492 149.21C210.051 157.538 205.594 164.276 196.537 164.259L92.1541 164.065Z" fill="white"/>
|
||||
<path d="M92.1541 164.065C83.0977 164.049 72.8715 157.284 69.3132 148.956L28.3003 52.9672C24.7419 44.6391 29.199 37.9015 38.2554 37.9182L142.638 38.1117C151.695 38.1285 161.921 44.8933 165.479 53.2214L206.492 149.21C210.051 157.538 205.594 164.276 196.537 164.259L92.1541 164.065Z" fill="url(#paint0_radial)"/>
|
||||
<path d="M92.1541 164.065C83.0977 164.049 72.8715 157.284 69.3132 148.956L28.3003 52.9672C24.7419 44.6391 29.199 37.9015 38.2554 37.9182L142.638 38.1117C151.695 38.1285 161.921 44.8933 165.479 53.2214L206.492 149.21C210.051 157.538 205.594 164.276 196.537 164.259L92.1541 164.065Z" fill="url(#paint1_radial)"/>
|
||||
<path d="M92.1541 164.065C83.0977 164.049 72.8715 157.284 69.3132 148.956L28.3003 52.9672C24.7419 44.6391 29.199 37.9015 38.2554 37.9182L142.638 38.1117C151.695 38.1285 161.921 44.8933 165.479 53.2214L206.492 149.21C210.051 157.538 205.594 164.276 196.537 164.259L92.1541 164.065Z" fill="url(#paint2_radial)"/>
|
||||
<path d="M92.1541 164.065C83.0977 164.049 72.8715 157.284 69.3132 148.956L28.3003 52.9672C24.7419 44.6391 29.199 37.9015 38.2554 37.9182L142.638 38.1117C151.695 38.1285 161.921 44.8933 165.479 53.2214L206.492 149.21C210.051 157.538 205.594 164.276 196.537 164.259L92.1541 164.065Z" fill="url(#paint3_radial)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M92.958 165.95C82.7695 165.931 71.265 158.321 67.2619 148.952L26.2489 52.9632C22.2458 43.5941 27.26 36.0143 37.4485 36.0332L141.832 36.2266C152.02 36.2455 163.525 43.8559 167.528 53.225L208.541 149.214C212.544 158.583 207.53 166.163 197.341 166.144L92.958 165.95ZM71.3614 148.959C74.475 156.246 83.4229 162.166 91.3473 162.18L195.73 162.374C203.655 162.388 207.555 156.493 204.441 149.206L163.428 53.2174C160.315 45.9304 151.367 40.0111 143.442 39.9964L39.0592 39.803C31.1349 39.7883 27.2349 45.6837 30.3485 52.9708L71.3614 148.959Z" fill="#131313"/>
|
||||
<path d="M68.565 53.3425C81.1781 53.3659 95.4205 62.7875 100.376 74.3862C105.332 85.985 99.1246 95.3687 86.5115 95.3454C73.8984 95.322 59.6559 85.9004 54.7001 74.3016C49.7443 62.7028 55.9518 53.3191 68.565 53.3425Z" fill="#131313"/>
|
||||
<path d="M90.6891 104.981C103.302 105.004 117.545 114.425 122.5 126.024C127.456 137.623 121.249 147.007 108.636 146.983C96.0225 146.96 81.7801 137.538 76.8243 125.94C71.8685 114.341 78.076 104.957 90.6891 104.981Z" fill="#131313"/>
|
||||
<path d="M147.538 105.142C160.151 105.166 174.394 114.587 179.349 126.186C184.305 137.785 178.098 147.168 165.485 147.145C152.871 147.122 138.629 137.7 133.673 126.101C128.717 114.503 134.925 105.119 147.538 105.142Z" fill="#131313"/>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(134.41 68.3006) rotate(-33.9533) scale(90.6795 83.3208)">
|
||||
<stop offset="0.661458" stop-color="#C4FCF8"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint1_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(42.7873 129.218) rotate(-24.1606) scale(213.359 196.045)">
|
||||
<stop stop-color="#FF0099" stop-opacity="0.9"/>
|
||||
<stop offset="0.770833" stop-color="white" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint2_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(176.854 148.655) rotate(-53.4908) scale(107.342 98.6309)">
|
||||
<stop stop-color="#FFEC43"/>
|
||||
<stop offset="0.805707" stop-color="#FFF6A8" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint3_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(57.5443 53.4752) rotate(20.3896) scale(137.027 125.907)">
|
||||
<stop offset="0.125" stop-color="#5886FE" stop-opacity="0.46"/>
|
||||
<stop offset="0.673044" stop-color="white" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import styled from 'styled-components/macro'
|
||||
import useCopyClipboard from '../../hooks/useCopyClipboard'
|
||||
|
||||
import { LinkStyledButton } from '../../theme'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import styled from 'styled-components/macro'
|
||||
import { CheckCircle, Triangle } from 'react-feather'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
|
||||
@@ -26,7 +26,7 @@ const HeaderRow = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap};
|
||||
padding: 1rem 1rem;
|
||||
font-weight: 500;
|
||||
color: ${props => (props.color === 'blue' ? ({ theme }) => theme.primary1 : 'inherit')};
|
||||
color: ${(props) => (props.color === 'blue' ? ({ theme }) => theme.primary1 : 'inherit')};
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
padding: 1rem;
|
||||
`};
|
||||
@@ -76,7 +76,6 @@ const AccountGroupingRow = styled.div`
|
||||
`
|
||||
|
||||
const AccountSection = styled.div`
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
padding: 0rem 1rem;
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`padding: 0rem 1rem 1.5rem 1rem;`};
|
||||
`
|
||||
@@ -223,7 +222,7 @@ export default function AccountDetails({
|
||||
pendingTransactions,
|
||||
confirmedTransactions,
|
||||
ENSName,
|
||||
openOptions
|
||||
openOptions,
|
||||
}: AccountDetailsProps) {
|
||||
const { chainId, account, connector } = useActiveWeb3React()
|
||||
const theme = useContext(ThemeContext)
|
||||
@@ -234,10 +233,10 @@ export default function AccountDetails({
|
||||
const isMetaMask = !!(ethereum && ethereum.isMetaMask)
|
||||
const name = Object.keys(SUPPORTED_WALLETS)
|
||||
.filter(
|
||||
k =>
|
||||
(k) =>
|
||||
SUPPORTED_WALLETS[k].connector === connector && (connector !== injected || isMetaMask === (k === 'METAMASK'))
|
||||
)
|
||||
.map(k => SUPPORTED_WALLETS[k].name)[0]
|
||||
.map((k) => SUPPORTED_WALLETS[k].name)[0]
|
||||
return <WalletName>Connected with {name}</WalletName>
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ const Input = styled.input<{ error?: boolean }>`
|
||||
export default function AddressInputPanel({
|
||||
id,
|
||||
value,
|
||||
onChange
|
||||
onChange,
|
||||
}: {
|
||||
id?: string
|
||||
// the typed string value
|
||||
@@ -82,7 +82,7 @@ export default function AddressInputPanel({
|
||||
const { address, loading, name } = useENS(value)
|
||||
|
||||
const handleInput = useCallback(
|
||||
event => {
|
||||
(event) => {
|
||||
const input = event.target.value
|
||||
const withoutSpaces = input.replace(/\s+/g, '')
|
||||
onChange(withoutSpaces)
|
||||
|
||||
44
src/components/Badge/Badge.stories.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Story } from '@storybook/react/types-6-0'
|
||||
import React, { PropsWithChildren } from 'react'
|
||||
import Component, { BadgeProps, BadgeVariant } from './index'
|
||||
|
||||
export default {
|
||||
title: 'Badge',
|
||||
argTypes: {
|
||||
variant: {
|
||||
name: 'variant',
|
||||
type: { name: 'string', require: false },
|
||||
defaultValue: BadgeVariant.DEFAULT,
|
||||
description: 'badge variant',
|
||||
control: {
|
||||
type: 'select',
|
||||
options: Object.values(BadgeVariant),
|
||||
},
|
||||
},
|
||||
},
|
||||
args: {
|
||||
children: '🦄 UNISWAP 🦄',
|
||||
},
|
||||
}
|
||||
|
||||
const Template: Story<PropsWithChildren<BadgeProps>> = (args) => <Component {...args}>{args.children}</Component>
|
||||
|
||||
export const DefaultBadge = Template.bind({})
|
||||
DefaultBadge.args = {
|
||||
variant: BadgeVariant.DEFAULT,
|
||||
}
|
||||
|
||||
export const WarningBadge = Template.bind({})
|
||||
WarningBadge.args = {
|
||||
variant: BadgeVariant.WARNING,
|
||||
}
|
||||
|
||||
export const NegativeBadge = Template.bind({})
|
||||
NegativeBadge.args = {
|
||||
variant: BadgeVariant.NEGATIVE,
|
||||
}
|
||||
|
||||
export const PositiveBadge = Template.bind({})
|
||||
PositiveBadge.args = {
|
||||
variant: BadgeVariant.POSITIVE,
|
||||
}
|
||||
77
src/components/Badge/RangeBadge.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import React from 'react'
|
||||
|
||||
import Badge, { BadgeVariant } from 'components/Badge'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { MouseoverTooltip } from '../../components/Tooltip'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AlertCircle } from 'react-feather'
|
||||
|
||||
const BadgeWrapper = styled.div`
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
`
|
||||
|
||||
const BadgeText = styled.div`
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
`
|
||||
|
||||
const ActiveDot = styled.span`
|
||||
background-color: ${({ theme }) => theme.success};
|
||||
border-radius: 50%;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
margin-right: 4px;
|
||||
`
|
||||
|
||||
export const DarkBadge = styled.div`
|
||||
width: fit-content;
|
||||
border-radius: 8px;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
padding: 4px 6px;
|
||||
`
|
||||
|
||||
export default function RangeBadge({
|
||||
removed,
|
||||
inRange,
|
||||
}: {
|
||||
removed: boolean | undefined
|
||||
inRange: boolean | undefined
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<BadgeWrapper>
|
||||
{removed ? (
|
||||
<MouseoverTooltip text={`Your position has 0 liquidity, and is not earning fees.`}>
|
||||
<Badge variant={BadgeVariant.DEFAULT}>
|
||||
<AlertCircle width={14} height={14} />
|
||||
|
||||
<BadgeText>{t('Inactive')}</BadgeText>
|
||||
</Badge>
|
||||
</MouseoverTooltip>
|
||||
) : inRange ? (
|
||||
<MouseoverTooltip
|
||||
text={`The price of this pool is within your selected range. Your position is currently earning fees.`}
|
||||
>
|
||||
<Badge variant={BadgeVariant.DEFAULT}>
|
||||
<ActiveDot />
|
||||
<BadgeText>{t('In range')}</BadgeText>
|
||||
</Badge>
|
||||
</MouseoverTooltip>
|
||||
) : (
|
||||
<MouseoverTooltip
|
||||
text={`The price of this pool is outside of your selected range. Your position is not currently earning fees.`}
|
||||
>
|
||||
<Badge variant={BadgeVariant.WARNING}>
|
||||
<AlertCircle width={14} height={14} />
|
||||
|
||||
<BadgeText>{t('Out of range')}</BadgeText>
|
||||
</Badge>
|
||||
</MouseoverTooltip>
|
||||
)}
|
||||
</BadgeWrapper>
|
||||
)
|
||||
}
|
||||
73
src/components/Badge/index.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import { readableColor } from 'polished'
|
||||
import { PropsWithChildren } from 'react'
|
||||
import styled, { DefaultTheme } from 'styled-components'
|
||||
import { Color } from 'theme/styled'
|
||||
|
||||
export enum BadgeVariant {
|
||||
DEFAULT = 'DEFAULT',
|
||||
NEGATIVE = 'NEGATIVE',
|
||||
POSITIVE = 'POSITIVE',
|
||||
PRIMARY = 'PRIMARY',
|
||||
WARNING = 'WARNING',
|
||||
|
||||
WARNING_OUTLINE = 'WARNING_OUTLINE',
|
||||
}
|
||||
|
||||
export interface BadgeProps {
|
||||
variant?: BadgeVariant
|
||||
}
|
||||
|
||||
function pickBackgroundColor(variant: BadgeVariant | undefined, theme: DefaultTheme): Color {
|
||||
switch (variant) {
|
||||
case BadgeVariant.NEGATIVE:
|
||||
return theme.error
|
||||
case BadgeVariant.POSITIVE:
|
||||
return theme.success
|
||||
case BadgeVariant.PRIMARY:
|
||||
return theme.primary1
|
||||
case BadgeVariant.WARNING:
|
||||
return theme.warning
|
||||
case BadgeVariant.WARNING_OUTLINE:
|
||||
return 'transparent'
|
||||
default:
|
||||
return theme.bg2
|
||||
}
|
||||
}
|
||||
|
||||
function pickBorder(variant: BadgeVariant | undefined, theme: DefaultTheme): string {
|
||||
switch (variant) {
|
||||
case BadgeVariant.WARNING_OUTLINE:
|
||||
return `1px solid ${theme.warning}`
|
||||
default:
|
||||
return 'unset'
|
||||
}
|
||||
}
|
||||
|
||||
function pickFontColor(variant: BadgeVariant | undefined, theme: DefaultTheme): string {
|
||||
switch (variant) {
|
||||
case BadgeVariant.NEGATIVE:
|
||||
return readableColor(theme.error)
|
||||
case BadgeVariant.POSITIVE:
|
||||
return readableColor(theme.success)
|
||||
case BadgeVariant.WARNING:
|
||||
return readableColor(theme.warning)
|
||||
case BadgeVariant.WARNING_OUTLINE:
|
||||
return theme.warning
|
||||
default:
|
||||
return readableColor(theme.bg2)
|
||||
}
|
||||
}
|
||||
|
||||
const Badge = styled.div<PropsWithChildren<BadgeProps>>`
|
||||
align-items: center;
|
||||
background-color: ${({ theme, variant }) => pickBackgroundColor(variant, theme)};
|
||||
border: ${({ theme, variant }) => pickBorder(variant, theme)};
|
||||
border-radius: 0.5rem;
|
||||
color: ${({ theme, variant }) => pickFontColor(variant, theme)};
|
||||
display: inline-flex;
|
||||
padding: 4px 6px;
|
||||
justify-content: center;
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
export default Badge
|
||||
12
src/components/Blocklist/index.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import React, { ReactNode, useMemo } from 'react'
|
||||
import { BLOCKED_ADDRESSES } from '../../constants'
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
|
||||
export default function Blocklist({ children }: { children: ReactNode }) {
|
||||
const { account } = useActiveWeb3React()
|
||||
const blocked: boolean = useMemo(() => Boolean(account && BLOCKED_ADDRESSES.indexOf(account) !== -1), [account])
|
||||
if (blocked) {
|
||||
return <div>Blocked address</div>
|
||||
}
|
||||
return <>{children}</>
|
||||
}
|
||||
153
src/components/Button/Button.stories.tsx
Normal file
@@ -0,0 +1,153 @@
|
||||
import { Story } from '@storybook/react/types-6-0'
|
||||
import styled from 'styled-components/macro'
|
||||
import React from 'react'
|
||||
import {
|
||||
ButtonConfirmed,
|
||||
ButtonDropdown,
|
||||
ButtonDropdownGrey,
|
||||
ButtonDropdownLight,
|
||||
ButtonEmpty,
|
||||
ButtonError,
|
||||
ButtonGray,
|
||||
ButtonLight,
|
||||
ButtonOutlined,
|
||||
ButtonPink,
|
||||
ButtonPrimary,
|
||||
ButtonRadio,
|
||||
ButtonSecondary,
|
||||
ButtonUNIGradient,
|
||||
ButtonWhite,
|
||||
} from './index'
|
||||
|
||||
const wrapperCss = styled.main`
|
||||
font-size: 2em;
|
||||
margin: 3em;
|
||||
max-width: 300px;
|
||||
`
|
||||
|
||||
export default {
|
||||
title: 'Buttons',
|
||||
argTypes: {
|
||||
disabled: { control: { type: 'boolean' } },
|
||||
onClick: { action: 'clicked' },
|
||||
},
|
||||
decorators: [
|
||||
(Component: Story) => (
|
||||
<div css={wrapperCss}>
|
||||
<Component />
|
||||
</div>
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
const Unicorn = () => (
|
||||
<span role="img" aria-label="unicorn">
|
||||
🦄
|
||||
</span>
|
||||
)
|
||||
|
||||
export const Radio = () => (
|
||||
<ButtonRadio>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonRadio>
|
||||
)
|
||||
export const DropdownLight = () => (
|
||||
<ButtonDropdownLight>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonDropdownLight>
|
||||
)
|
||||
export const DropdownGrey = () => (
|
||||
<ButtonDropdownGrey>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonDropdownGrey>
|
||||
)
|
||||
export const Dropdown = () => (
|
||||
<ButtonDropdown>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonDropdown>
|
||||
)
|
||||
export const Error = () => (
|
||||
<ButtonError>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonError>
|
||||
)
|
||||
export const Confirmed = () => (
|
||||
<ButtonConfirmed>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonConfirmed>
|
||||
)
|
||||
export const White = () => (
|
||||
<ButtonWhite>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonWhite>
|
||||
)
|
||||
export const Empty = () => (
|
||||
<ButtonEmpty>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonEmpty>
|
||||
)
|
||||
export const Outlined = () => (
|
||||
<ButtonOutlined>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonOutlined>
|
||||
)
|
||||
export const UNIGradient = () => (
|
||||
<ButtonUNIGradient>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonUNIGradient>
|
||||
)
|
||||
export const Pink = () => (
|
||||
<ButtonPink>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonPink>
|
||||
)
|
||||
export const Secondary = () => (
|
||||
<ButtonSecondary>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonSecondary>
|
||||
)
|
||||
export const Gray = () => (
|
||||
<ButtonGray>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonGray>
|
||||
)
|
||||
export const Light = () => (
|
||||
<ButtonLight>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonLight>
|
||||
)
|
||||
export const Primary = () => (
|
||||
<ButtonPrimary>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonPrimary>
|
||||
)
|
||||
@@ -1,10 +1,11 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { darken, lighten } from 'polished'
|
||||
import styled from 'styled-components/macro'
|
||||
import { darken } from 'polished'
|
||||
|
||||
import { RowBetween } from '../Row'
|
||||
import { ChevronDown } from 'react-feather'
|
||||
import { ChevronDown, Check } from 'react-feather'
|
||||
import { Button as RebassButton, ButtonProps } from 'rebass/styled-components'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
|
||||
const Base = styled(RebassButton)<{
|
||||
padding?: string
|
||||
@@ -12,11 +13,11 @@ const Base = styled(RebassButton)<{
|
||||
borderRadius?: string
|
||||
altDisabledStyle?: boolean
|
||||
}>`
|
||||
padding: ${({ padding }) => (padding ? padding : '18px')};
|
||||
padding: ${({ padding }) => (padding ? padding : '16px')};
|
||||
width: ${({ width }) => (width ? width : '100%')};
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
border-radius: 12px;
|
||||
border-radius: 20px;
|
||||
border-radius: ${({ borderRadius }) => borderRadius && borderRadius};
|
||||
outline: none;
|
||||
border: 1px solid transparent;
|
||||
@@ -31,11 +32,24 @@ const Base = styled(RebassButton)<{
|
||||
z-index: 1;
|
||||
&:disabled {
|
||||
cursor: auto;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
will-change: transform;
|
||||
transition: transform 450ms ease;
|
||||
transform: perspective(1px) translateZ(0);
|
||||
|
||||
&:hover {
|
||||
transform: scale(0.99);
|
||||
}
|
||||
|
||||
> * {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
> a {
|
||||
text-decoration: none;
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonPrimary = styled(Base)`
|
||||
@@ -53,13 +67,15 @@ export const ButtonPrimary = styled(Base)`
|
||||
background-color: ${({ theme }) => darken(0.1, theme.primary1)};
|
||||
}
|
||||
&:disabled {
|
||||
background-color: ${({ theme, altDisabledStyle }) => (altDisabledStyle ? theme.primary1 : theme.bg3)};
|
||||
color: ${({ theme, altDisabledStyle }) => (altDisabledStyle ? 'white' : theme.text3)};
|
||||
background-color: ${({ theme, altDisabledStyle, disabled }) =>
|
||||
altDisabledStyle ? (disabled ? theme.primary1 : theme.primary1) : theme.primary1};
|
||||
color: white;
|
||||
cursor: auto;
|
||||
box-shadow: none;
|
||||
border: 1px solid transparent;
|
||||
outline: none;
|
||||
opacity: ${({ altDisabledStyle }) => (altDisabledStyle ? '0.7' : '1')};
|
||||
opacity: 0.4;
|
||||
opacity: ${({ altDisabledStyle }) => (altDisabledStyle ? '0.5' : '0.4')};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -92,19 +108,17 @@ export const ButtonLight = styled(Base)`
|
||||
`
|
||||
|
||||
export const ButtonGray = styled(Base)`
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
color: ${({ theme }) => theme.text2};
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1pt ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
|
||||
}
|
||||
&:hover {
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1pt ${({ theme, disabled }) => !disabled && darken(0.1, theme.bg2)};
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.1, theme.bg2)};
|
||||
}
|
||||
`
|
||||
@@ -159,6 +173,26 @@ export const ButtonPink = styled(Base)`
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonUNIGradient = styled(ButtonPrimary)`
|
||||
color: white;
|
||||
padding: 4px 8px;
|
||||
height: 36px;
|
||||
font-weight: 500;
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
background: radial-gradient(174.47% 188.91% at 1.84% 0%, #ff007a 0%, #2172e5 100%), #edeef2;
|
||||
width: fit-content;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
white-space: no-wrap;
|
||||
:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
:active {
|
||||
opacity: 0.9;
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonOutlined = styled(Base)`
|
||||
border: 1px solid ${({ theme }) => theme.bg2};
|
||||
background-color: transparent;
|
||||
@@ -190,8 +224,30 @@ export const ButtonEmpty = styled(Base)`
|
||||
text-decoration: underline;
|
||||
}
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
&:disabled {
|
||||
opacity: 50%;
|
||||
cursor: auto;
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonText = styled(Base)`
|
||||
padding: 0;
|
||||
width: fit-content;
|
||||
background: none;
|
||||
text-decoration: none;
|
||||
&:focus {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
text-decoration: underline;
|
||||
}
|
||||
&:hover {
|
||||
// text-decoration: underline;
|
||||
opacity: 0.9;
|
||||
}
|
||||
&:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
@@ -223,12 +279,14 @@ export const ButtonWhite = styled(Base)`
|
||||
`
|
||||
|
||||
const ButtonConfirmedStyle = styled(Base)`
|
||||
background-color: ${({ theme }) => lighten(0.5, theme.green1)};
|
||||
color: ${({ theme }) => theme.green1};
|
||||
border: 1px solid ${({ theme }) => theme.green1};
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
color: ${({ theme }) => theme.text1};
|
||||
/* border: 1px solid ${({ theme }) => theme.green1}; */
|
||||
|
||||
&:disabled {
|
||||
opacity: 50%;
|
||||
/* opacity: 50%; */
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
color: ${({ theme }) => theme.text2};
|
||||
cursor: auto;
|
||||
}
|
||||
`
|
||||
@@ -288,6 +346,17 @@ export function ButtonDropdown({ disabled = false, children, ...rest }: { disabl
|
||||
)
|
||||
}
|
||||
|
||||
export function ButtonDropdownGrey({ disabled = false, children, ...rest }: { disabled?: boolean } & ButtonProps) {
|
||||
return (
|
||||
<ButtonGray {...rest} disabled={disabled} style={{ borderRadius: '20px' }}>
|
||||
<RowBetween>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>{children}</div>
|
||||
<ChevronDown size={24} />
|
||||
</RowBetween>
|
||||
</ButtonGray>
|
||||
)
|
||||
}
|
||||
|
||||
export function ButtonDropdownLight({ disabled = false, children, ...rest }: { disabled?: boolean } & ButtonProps) {
|
||||
return (
|
||||
<ButtonOutlined {...rest} disabled={disabled}>
|
||||
@@ -306,3 +375,57 @@ export function ButtonRadio({ active, ...rest }: { active?: boolean } & ButtonPr
|
||||
return <ButtonPrimary {...rest} />
|
||||
}
|
||||
}
|
||||
|
||||
const ActiveOutlined = styled(ButtonOutlined)`
|
||||
border: 1px solid;
|
||||
border-color: ${({ theme }) => theme.primary1};
|
||||
`
|
||||
|
||||
const Circle = styled.div`
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: ${({ theme }) => theme.primary1};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`
|
||||
|
||||
const CheckboxWrapper = styled.div`
|
||||
width: 30px;
|
||||
padding: 0 10px;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
`
|
||||
|
||||
const ResponsiveCheck = styled(Check)`
|
||||
size: 13px;
|
||||
`
|
||||
|
||||
export function ButtonRadioChecked({ active = false, children, ...rest }: { active?: boolean } & ButtonProps) {
|
||||
const theme = useTheme()
|
||||
|
||||
if (!active) {
|
||||
return (
|
||||
<ButtonOutlined borderRadius="12px" padding="12px 8px" {...rest}>
|
||||
{<RowBetween>{children}</RowBetween>}
|
||||
</ButtonOutlined>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<ActiveOutlined {...rest} padding="12px 8px" borderRadius="12px">
|
||||
{
|
||||
<RowBetween>
|
||||
{children}
|
||||
<CheckboxWrapper>
|
||||
<Circle>
|
||||
<ResponsiveCheck size={13} stroke={theme.white} />
|
||||
</Circle>
|
||||
</CheckboxWrapper>
|
||||
</RowBetween>
|
||||
}
|
||||
</ActiveOutlined>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { CardProps, Text } from 'rebass'
|
||||
import styled from 'styled-components/macro'
|
||||
import { Box } from 'rebass/styled-components'
|
||||
|
||||
const Card = styled(Box)<{ padding?: string; border?: string; borderRadius?: string }>`
|
||||
width: 100%;
|
||||
const Card = styled(Box)<{ width?: string; padding?: string; border?: string; borderRadius?: string }>`
|
||||
width: ${({ width }) => width ?? '100%'};
|
||||
border-radius: 16px;
|
||||
padding: 1.25rem;
|
||||
padding: 1rem;
|
||||
padding: ${({ padding }) => padding};
|
||||
border: ${({ border }) => border};
|
||||
border-radius: ${({ borderRadius }) => borderRadius};
|
||||
@@ -18,17 +16,29 @@ export const LightCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
`
|
||||
|
||||
export const LightGreyCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
`
|
||||
|
||||
export const GreyCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
`
|
||||
|
||||
export const DarkGreyCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
`
|
||||
|
||||
export const DarkCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
`
|
||||
|
||||
export const OutlineCard = styled(Card)`
|
||||
border: 1px solid ${({ theme }) => theme.bg3};
|
||||
`
|
||||
|
||||
export const YellowCard = styled(Card)`
|
||||
background-color: rgba(243, 132, 30, 0.05);
|
||||
color: ${({ theme }) => theme.yellow2};
|
||||
color: ${({ theme }) => theme.yellow3};
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
@@ -38,19 +48,8 @@ export const PinkCard = styled(Card)`
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
const BlueCardStyled = styled(Card)`
|
||||
export const BlueCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.primary5};
|
||||
color: ${({ theme }) => theme.primary1};
|
||||
color: ${({ theme }) => theme.blue2};
|
||||
border-radius: 12px;
|
||||
width: fit-content;
|
||||
`
|
||||
|
||||
export const BlueCard = ({ children, ...rest }: CardProps) => {
|
||||
return (
|
||||
<BlueCardStyled {...rest}>
|
||||
<Text fontWeight={500} color="#2172E5">
|
||||
{children}
|
||||
</Text>
|
||||
</BlueCardStyled>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import styled from 'styled-components'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
const Column = styled.div`
|
||||
display: flex;
|
||||
|
||||
@@ -20,7 +20,7 @@ export default function Confetti({ start, variant }: { start: boolean; variant?:
|
||||
h: height,
|
||||
w: width,
|
||||
x: 0,
|
||||
y: _variant === 'top' ? height * 0.25 : _variant === 'bottom' ? height * 0.75 : height * 0.5
|
||||
y: _variant === 'top' ? height * 0.25 : _variant === 'bottom' ? height * 0.75 : height * 0.5,
|
||||
}}
|
||||
initialVelocityX={15}
|
||||
initialVelocityY={30}
|
||||
|
||||
34
src/components/CurrencyInputPanel/FiatValue.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
|
||||
import React, { useMemo } from 'react'
|
||||
import useTheme from '../../hooks/useTheme'
|
||||
import { TYPE } from '../../theme'
|
||||
import { warningSeverity } from '../../utils/prices'
|
||||
import HoverInlineText from 'components/HoverInlineText'
|
||||
|
||||
export function FiatValue({
|
||||
fiatValue,
|
||||
priceImpact,
|
||||
}: {
|
||||
fiatValue: CurrencyAmount<Currency> | null | undefined
|
||||
priceImpact?: Percent
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
const priceImpactColor = useMemo(() => {
|
||||
if (!priceImpact) return undefined
|
||||
if (priceImpact.lessThan('0')) return theme.green1
|
||||
const severity = warningSeverity(priceImpact)
|
||||
if (severity < 1) return theme.text4
|
||||
if (severity < 3) return theme.yellow1
|
||||
return theme.red1
|
||||
}, [priceImpact, theme.green1, theme.red1, theme.text4, theme.yellow1])
|
||||
|
||||
return (
|
||||
<TYPE.body fontSize={14} color={fiatValue ? theme.text2 : theme.text4}>
|
||||
{fiatValue ? '~' : ''}$
|
||||
<HoverInlineText text={fiatValue ? fiatValue?.toSignificant(6, { groupSeparator: ',' }) : '-'} />{' '}
|
||||
{priceImpact ? (
|
||||
<span style={{ color: priceImpactColor }}> ({priceImpact.multiply(-1).toSignificant(3)}%)</span>
|
||||
) : null}
|
||||
</TYPE.body>
|
||||
)
|
||||
}
|
||||
@@ -1,67 +1,114 @@
|
||||
import { Currency, Pair } from '@uniswap/sdk'
|
||||
import React, { useState, useContext, useCallback } from 'react'
|
||||
import styled, { ThemeContext } from 'styled-components'
|
||||
import { Pair } from '@uniswap/v2-sdk'
|
||||
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
|
||||
import React, { useState, useCallback } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import { darken } from 'polished'
|
||||
import { useCurrencyBalance } from '../../state/wallet/hooks'
|
||||
import CurrencySearchModal from '../SearchModal/CurrencySearchModal'
|
||||
import CurrencyLogo from '../CurrencyLogo'
|
||||
import DoubleCurrencyLogo from '../DoubleLogo'
|
||||
import { RowBetween } from '../Row'
|
||||
import { ButtonGray } from '../Button'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import { TYPE } from '../../theme'
|
||||
import { Input as NumericalInput } from '../NumericalInput'
|
||||
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useTheme from '../../hooks/useTheme'
|
||||
import { Lock } from 'react-feather'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { FiatValue } from './FiatValue'
|
||||
import { formatTokenAmount } from 'utils/formatTokenAmount'
|
||||
|
||||
const InputRow = styled.div<{ selected: boolean }>`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
padding: ${({ selected }) => (selected ? '0.75rem 0.5rem 0.75rem 1rem' : '0.75rem 0.75rem 0.75rem 1rem')};
|
||||
const InputPanel = styled.div<{ hideInput?: boolean }>`
|
||||
${({ theme }) => theme.flexColumnNoWrap}
|
||||
position: relative;
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
|
||||
background-color: ${({ theme, hideInput }) => (hideInput ? 'transparent' : theme.bg2)};
|
||||
z-index: 1;
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
`
|
||||
|
||||
const CurrencySelect = styled.button<{ selected: boolean }>`
|
||||
const FixedContainer = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
border-radius: 20px;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
opacity: 0.95;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 2.2rem;
|
||||
font-size: 20px;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
`
|
||||
|
||||
const Container = styled.div<{ hideInput: boolean }>`
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
|
||||
border: 1px solid ${({ theme, hideInput }) => (hideInput ? ' transparent' : theme.bg2)};
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
:focus,
|
||||
:hover {
|
||||
border: 1px solid ${({ theme, hideInput }) => (hideInput ? ' transparent' : theme.bg3)};
|
||||
}
|
||||
`
|
||||
|
||||
const CurrencySelect = styled(ButtonGray)<{ selected: boolean; hideInput?: boolean }>`
|
||||
align-items: center;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.bg1 : theme.primary1)};
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.bg0 : theme.primary1)};
|
||||
color: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)};
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
box-shadow: ${({ selected }) => (selected ? 'none' : '0px 6px 10px rgba(0, 0, 0, 0.075)')};
|
||||
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
border: none;
|
||||
padding: 0 0.5rem;
|
||||
|
||||
height: ${({ hideInput }) => (hideInput ? '2.8rem' : '2.4rem')};
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
padding: 0 8px;
|
||||
justify-content: space-between;
|
||||
margin-right: ${({ hideInput }) => (hideInput ? '0' : '12px')};
|
||||
:focus,
|
||||
:hover {
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.bg2 : darken(0.05, theme.primary1))};
|
||||
}
|
||||
`
|
||||
|
||||
const InputRow = styled.div<{ selected: boolean }>`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
padding: ${({ selected }) => (selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 0.75rem 1rem')};
|
||||
`
|
||||
|
||||
const LabelRow = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
padding: 0.75rem 1rem 0 1rem;
|
||||
padding: 0 1rem 1rem;
|
||||
span:hover {
|
||||
cursor: pointer;
|
||||
color: ${({ theme }) => darken(0.2, theme.text2)};
|
||||
}
|
||||
`
|
||||
|
||||
const FiatRow = styled(LabelRow)`
|
||||
justify-content: flex-end;
|
||||
`
|
||||
|
||||
const Aligner = styled.span`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const StyledDropDown = styled(DropDown)<{ selected: boolean }>`
|
||||
margin: 0 0.25rem 0 0.5rem;
|
||||
margin: 0 0.25rem 0 0.35rem;
|
||||
height: 35%;
|
||||
|
||||
path {
|
||||
@@ -70,42 +117,25 @@ const StyledDropDown = styled(DropDown)<{ selected: boolean }>`
|
||||
}
|
||||
`
|
||||
|
||||
const InputPanel = styled.div<{ hideInput?: boolean }>`
|
||||
${({ theme }) => theme.flexColumnNoWrap}
|
||||
position: relative;
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')};
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
z-index: 1;
|
||||
`
|
||||
|
||||
const Container = styled.div<{ hideInput: boolean }>`
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')};
|
||||
border: 1px solid ${({ theme }) => theme.bg2};
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
`
|
||||
|
||||
const StyledTokenName = styled.span<{ active?: boolean }>`
|
||||
${({ active }) => (active ? ' margin: 0 0.25rem 0 0.75rem;' : ' margin: 0 0.25rem 0 0.25rem;')}
|
||||
font-size: ${({ active }) => (active ? '20px' : '16px')};
|
||||
|
||||
${({ active }) => (active ? ' margin: 0 0.25rem 0 0.25rem;' : ' margin: 0 0.25rem 0 0.25rem;')}
|
||||
font-size: ${({ active }) => (active ? '18px' : '18px')};
|
||||
`
|
||||
|
||||
const StyledBalanceMax = styled.button`
|
||||
height: 28px;
|
||||
background-color: ${({ theme }) => theme.primary5};
|
||||
border: 1px solid ${({ theme }) => theme.primary5};
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
|
||||
const StyledBalanceMax = styled.button<{ disabled?: boolean }>`
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
margin-right: 0.5rem;
|
||||
color: ${({ theme }) => theme.primaryText1};
|
||||
:hover {
|
||||
border: 1px solid ${({ theme }) => theme.primary1};
|
||||
}
|
||||
padding: 0;
|
||||
color: ${({ theme }) => theme.primary1};
|
||||
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
|
||||
pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')};
|
||||
margin-left: 0.25rem;
|
||||
|
||||
:focus {
|
||||
border: 1px solid ${({ theme }) => theme.primary1};
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@@ -122,14 +152,16 @@ interface CurrencyInputPanelProps {
|
||||
label?: string
|
||||
onCurrencySelect?: (currency: Currency) => void
|
||||
currency?: Currency | null
|
||||
disableCurrencySelect?: boolean
|
||||
hideBalance?: boolean
|
||||
pair?: Pair | null
|
||||
hideInput?: boolean
|
||||
otherCurrency?: Currency | null
|
||||
fiatValue?: CurrencyAmount<Token> | null
|
||||
priceImpact?: Percent
|
||||
id: string
|
||||
showCommonBases?: boolean
|
||||
customBalanceText?: string
|
||||
locked?: boolean
|
||||
}
|
||||
|
||||
export default function CurrencyInputPanel({
|
||||
@@ -137,103 +169,125 @@ export default function CurrencyInputPanel({
|
||||
onUserInput,
|
||||
onMax,
|
||||
showMaxButton,
|
||||
label = 'Input',
|
||||
onCurrencySelect,
|
||||
currency,
|
||||
disableCurrencySelect = false,
|
||||
hideBalance = false,
|
||||
pair = null, // used for double token logo
|
||||
hideInput = false,
|
||||
otherCurrency,
|
||||
id,
|
||||
showCommonBases,
|
||||
customBalanceText
|
||||
customBalanceText,
|
||||
fiatValue,
|
||||
priceImpact,
|
||||
hideBalance = false,
|
||||
pair = null, // used for double token logo
|
||||
hideInput = false,
|
||||
locked = false,
|
||||
...rest
|
||||
}: CurrencyInputPanelProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [modalOpen, setModalOpen] = useState(false)
|
||||
const { account } = useActiveWeb3React()
|
||||
const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined)
|
||||
const theme = useContext(ThemeContext)
|
||||
const theme = useTheme()
|
||||
|
||||
const handleDismissSearch = useCallback(() => {
|
||||
setModalOpen(false)
|
||||
}, [setModalOpen])
|
||||
|
||||
return (
|
||||
<InputPanel id={id}>
|
||||
<InputPanel id={id} hideInput={hideInput} {...rest}>
|
||||
{locked && (
|
||||
<FixedContainer>
|
||||
<AutoColumn gap="sm" justify="center">
|
||||
<Lock />
|
||||
<TYPE.label fontSize="12px" textAlign="center">
|
||||
The market price is outside your specified price range. Single-asset deposit only.
|
||||
</TYPE.label>
|
||||
</AutoColumn>
|
||||
</FixedContainer>
|
||||
)}
|
||||
<Container hideInput={hideInput}>
|
||||
{!hideInput && (
|
||||
<LabelRow>
|
||||
<RowBetween>
|
||||
<TYPE.body color={theme.text2} fontWeight={500} fontSize={14}>
|
||||
{label}
|
||||
</TYPE.body>
|
||||
{account && (
|
||||
<TYPE.body
|
||||
onClick={onMax}
|
||||
color={theme.text2}
|
||||
fontWeight={500}
|
||||
fontSize={14}
|
||||
style={{ display: 'inline', cursor: 'pointer' }}
|
||||
>
|
||||
{!hideBalance && !!currency && selectedCurrencyBalance
|
||||
? (customBalanceText ?? 'Balance: ') + selectedCurrencyBalance?.toSignificant(6)
|
||||
: ' -'}
|
||||
</TYPE.body>
|
||||
)}
|
||||
</RowBetween>
|
||||
</LabelRow>
|
||||
)}
|
||||
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={disableCurrencySelect}>
|
||||
{!hideInput && (
|
||||
<>
|
||||
<NumericalInput
|
||||
className="token-amount-input"
|
||||
value={value}
|
||||
onUserInput={val => {
|
||||
onUserInput(val)
|
||||
}}
|
||||
/>
|
||||
{account && currency && showMaxButton && label !== 'To' && (
|
||||
<StyledBalanceMax onClick={onMax}>MAX</StyledBalanceMax>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={!onCurrencySelect}>
|
||||
<CurrencySelect
|
||||
selected={!!currency}
|
||||
hideInput={hideInput}
|
||||
className="open-currency-select-button"
|
||||
onClick={() => {
|
||||
if (!disableCurrencySelect) {
|
||||
if (onCurrencySelect) {
|
||||
setModalOpen(true)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Aligner>
|
||||
{pair ? (
|
||||
<DoubleCurrencyLogo currency0={pair.token0} currency1={pair.token1} size={24} margin={true} />
|
||||
) : currency ? (
|
||||
<CurrencyLogo currency={currency} size={'24px'} />
|
||||
) : null}
|
||||
{pair ? (
|
||||
<StyledTokenName className="pair-name-container">
|
||||
{pair?.token0.symbol}:{pair?.token1.symbol}
|
||||
</StyledTokenName>
|
||||
) : (
|
||||
<StyledTokenName className="token-symbol-container" active={Boolean(currency && currency.symbol)}>
|
||||
{(currency && currency.symbol && currency.symbol.length > 20
|
||||
? currency.symbol.slice(0, 4) +
|
||||
'...' +
|
||||
currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length)
|
||||
: currency?.symbol) || t('selectToken')}
|
||||
</StyledTokenName>
|
||||
)}
|
||||
{!disableCurrencySelect && <StyledDropDown selected={!!currency} />}
|
||||
<RowFixed>
|
||||
{pair ? (
|
||||
<span style={{ marginRight: '0.5rem' }}>
|
||||
<DoubleCurrencyLogo currency0={pair.token0} currency1={pair.token1} size={24} margin={true} />
|
||||
</span>
|
||||
) : currency ? (
|
||||
<CurrencyLogo style={{ marginRight: '0.5rem' }} currency={currency} size={'24px'} />
|
||||
) : null}
|
||||
{pair ? (
|
||||
<StyledTokenName className="pair-name-container">
|
||||
{pair?.token0.symbol}:{pair?.token1.symbol}
|
||||
</StyledTokenName>
|
||||
) : (
|
||||
<StyledTokenName className="token-symbol-container" active={Boolean(currency && currency.symbol)}>
|
||||
{(currency && currency.symbol && currency.symbol.length > 20
|
||||
? currency.symbol.slice(0, 4) +
|
||||
'...' +
|
||||
currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length)
|
||||
: currency?.symbol) || t('selectToken')}
|
||||
</StyledTokenName>
|
||||
)}
|
||||
</RowFixed>
|
||||
{onCurrencySelect && <StyledDropDown selected={!!currency} />}
|
||||
</Aligner>
|
||||
</CurrencySelect>
|
||||
{!hideInput && (
|
||||
<>
|
||||
<NumericalInput
|
||||
className="token-amount-input"
|
||||
value={value}
|
||||
onUserInput={(val) => {
|
||||
onUserInput(val)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</InputRow>
|
||||
{!hideInput && !hideBalance && (
|
||||
<FiatRow>
|
||||
<RowBetween>
|
||||
{account ? (
|
||||
<RowFixed style={{ height: '17px' }}>
|
||||
<TYPE.body
|
||||
onClick={onMax}
|
||||
color={theme.text2}
|
||||
fontWeight={400}
|
||||
fontSize={14}
|
||||
style={{ display: 'inline', cursor: 'pointer' }}
|
||||
>
|
||||
{!hideBalance && !!currency && selectedCurrencyBalance
|
||||
? (customBalanceText ?? 'Balance: ') +
|
||||
formatTokenAmount(selectedCurrencyBalance, 4) +
|
||||
' ' +
|
||||
currency.symbol
|
||||
: '-'}
|
||||
</TYPE.body>
|
||||
{showMaxButton && selectedCurrencyBalance ? (
|
||||
<StyledBalanceMax onClick={onMax}>(Max)</StyledBalanceMax>
|
||||
) : null}
|
||||
</RowFixed>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
<FiatValue fiatValue={fiatValue} priceImpact={priceImpact} />
|
||||
</RowBetween>
|
||||
</FiatRow>
|
||||
)}
|
||||
</Container>
|
||||
{!disableCurrencySelect && onCurrencySelect && (
|
||||
{onCurrencySelect && (
|
||||
<CurrencySearchModal
|
||||
isOpen={modalOpen}
|
||||
onDismiss={handleDismissSearch}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { Currency, ETHER, Token } from '@uniswap/sdk'
|
||||
import { ChainId, Currency } from '@uniswap/sdk-core'
|
||||
import React, { useMemo } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import styled from 'styled-components/macro'
|
||||
import EthereumLogo from '../../assets/images/ethereum-logo.png'
|
||||
import useHttpLocations from '../../hooks/useHttpLocations'
|
||||
import { WrappedTokenInfo } from '../../state/lists/hooks'
|
||||
import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
|
||||
import Logo from '../Logo'
|
||||
|
||||
const getTokenLogoURL = (address: string) =>
|
||||
export const getTokenLogoURL = (address: string) =>
|
||||
`https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${address}/logo.png`
|
||||
|
||||
const StyledEthereumLogo = styled.img<{ size: string }>`
|
||||
@@ -22,12 +21,14 @@ const StyledLogo = styled(Logo)<{ size: string }>`
|
||||
height: ${({ size }) => size};
|
||||
border-radius: ${({ size }) => size};
|
||||
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
|
||||
background-color: ${({ theme }) => theme.white};
|
||||
`
|
||||
|
||||
export default function CurrencyLogo({
|
||||
currency,
|
||||
size = '24px',
|
||||
style
|
||||
style,
|
||||
...rest
|
||||
}: {
|
||||
currency?: Currency
|
||||
size?: string
|
||||
@@ -36,21 +37,21 @@ export default function CurrencyLogo({
|
||||
const uriLocations = useHttpLocations(currency instanceof WrappedTokenInfo ? currency.logoURI : undefined)
|
||||
|
||||
const srcs: string[] = useMemo(() => {
|
||||
if (currency === ETHER) return []
|
||||
if (!currency || currency.isEther) return []
|
||||
|
||||
if (currency instanceof Token) {
|
||||
if (currency.isToken) {
|
||||
const defaultUrls = currency.chainId === ChainId.MAINNET ? [getTokenLogoURL(currency.address)] : []
|
||||
if (currency instanceof WrappedTokenInfo) {
|
||||
return [...uriLocations, getTokenLogoURL(currency.address)]
|
||||
return [...uriLocations, ...defaultUrls]
|
||||
}
|
||||
|
||||
return [getTokenLogoURL(currency.address)]
|
||||
return defaultUrls
|
||||
}
|
||||
return []
|
||||
}, [currency, uriLocations])
|
||||
|
||||
if (currency === ETHER) {
|
||||
return <StyledEthereumLogo src={EthereumLogo} size={size} style={style} />
|
||||
if (currency?.isEther) {
|
||||
return <StyledEthereumLogo src={EthereumLogo} size={size} style={style} {...rest} />
|
||||
}
|
||||
|
||||
return <StyledLogo size={size} srcs={srcs} alt={`${currency?.symbol ?? 'token'} logo`} style={style} />
|
||||
return <StyledLogo size={size} srcs={srcs} alt={`${currency?.symbol ?? 'token'} logo`} style={style} {...rest} />
|
||||
}
|
||||
|
||||
18
src/components/DoubleLogo/DoubleCurrencyLogo.stories.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Story } from '@storybook/react/types-6-0'
|
||||
import React from 'react'
|
||||
import { DAI, WBTC } from '../../constants'
|
||||
import Component, { DoubleCurrencyLogoProps } from './index'
|
||||
|
||||
export default {
|
||||
title: 'DoubleCurrencyLogo',
|
||||
decorators: [],
|
||||
}
|
||||
|
||||
const Template: Story<DoubleCurrencyLogoProps> = (args) => <Component {...args} />
|
||||
|
||||
export const DoubleCurrencyLogo = Template.bind({})
|
||||
DoubleCurrencyLogo.args = {
|
||||
currency0: DAI,
|
||||
currency1: WBTC,
|
||||
size: 220,
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Currency } from '@uniswap/sdk'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import styled from 'styled-components/macro'
|
||||
import CurrencyLogo from '../CurrencyLogo'
|
||||
|
||||
const Wrapper = styled.div<{ margin: boolean; sizeraw: number }>`
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-right: ${({ sizeraw, margin }) => margin && (sizeraw / 3 + 8).toString() + 'px'};
|
||||
margin-left: ${({ sizeraw, margin }) => margin && (sizeraw / 3 + 8).toString() + 'px'};
|
||||
`
|
||||
|
||||
interface DoubleCurrencyLogoProps {
|
||||
export interface DoubleCurrencyLogoProps {
|
||||
margin?: boolean
|
||||
size?: number
|
||||
currency0?: Currency
|
||||
@@ -29,7 +29,7 @@ export default function DoubleCurrencyLogo({
|
||||
currency0,
|
||||
currency1,
|
||||
size = 16,
|
||||
margin = false
|
||||
margin = false,
|
||||
}: DoubleCurrencyLogoProps) {
|
||||
return (
|
||||
<Wrapper sizeraw={size} margin={margin}>
|
||||
|
||||
182
src/components/ErrorBoundary/index.tsx
Normal file
@@ -0,0 +1,182 @@
|
||||
import React, { ErrorInfo } from 'react'
|
||||
import store, { AppState } from '../../state'
|
||||
import { ExternalLink, ThemedBackground, TYPE } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
import styled from 'styled-components/macro'
|
||||
import ReactGA from 'react-ga'
|
||||
import { getUserAgent } from '../../utils/getUserAgent'
|
||||
import { AutoRow } from '../Row'
|
||||
|
||||
const FallbackWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
`
|
||||
|
||||
const BodyWrapper = styled.div<{ margin?: string }>`
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
white-space: ;
|
||||
`
|
||||
|
||||
const CodeBlockWrapper = styled.div`
|
||||
background: ${({ theme }) => theme.bg0};
|
||||
overflow: auto;
|
||||
white-space: pre;
|
||||
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
|
||||
0px 24px 32px rgba(0, 0, 0, 0.01);
|
||||
border-radius: 24px;
|
||||
padding: 18px 24px;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
`
|
||||
|
||||
const LinkWrapper = styled.div`
|
||||
color: ${({ theme }) => theme.blue1};
|
||||
padding: 6px 24px;
|
||||
`
|
||||
|
||||
const SomethingWentWrongWrapper = styled.div`
|
||||
padding: 6px 24px;
|
||||
`
|
||||
|
||||
type ErrorBoundaryState = {
|
||||
error: Error | null
|
||||
}
|
||||
|
||||
export default class ErrorBoundary extends React.Component<unknown, ErrorBoundaryState> {
|
||||
constructor(props: unknown) {
|
||||
super(props)
|
||||
this.state = { error: null }
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
||||
return { error }
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||
ReactGA.exception({
|
||||
...error,
|
||||
...errorInfo,
|
||||
fatal: true,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { error } = this.state
|
||||
if (error !== null) {
|
||||
const encodedBody = encodeURIComponent(issueBody(error))
|
||||
return (
|
||||
<FallbackWrapper>
|
||||
<ThemedBackground />
|
||||
<BodyWrapper>
|
||||
<AutoColumn gap={'md'}>
|
||||
<SomethingWentWrongWrapper>
|
||||
<TYPE.label fontSize={24} fontWeight={600}>
|
||||
Something went wrong
|
||||
</TYPE.label>
|
||||
</SomethingWentWrongWrapper>
|
||||
<CodeBlockWrapper>
|
||||
<code>
|
||||
<TYPE.main fontSize={10}>{error.stack}</TYPE.main>
|
||||
</code>
|
||||
</CodeBlockWrapper>
|
||||
<AutoRow>
|
||||
<LinkWrapper>
|
||||
<ExternalLink
|
||||
id="create-github-issue-link"
|
||||
href={`https://github.com/Uniswap/uniswap-interface/issues/new?assignees=&labels=bug&body=${encodedBody}&title=${encodeURIComponent(
|
||||
`Crash report: \`${error.name}${error.message && `: ${error.message}`}\``
|
||||
)}`}
|
||||
target="_blank"
|
||||
>
|
||||
<TYPE.link fontSize={16}>
|
||||
Create an issue on GitHub
|
||||
<span>↗</span>
|
||||
</TYPE.link>
|
||||
</ExternalLink>
|
||||
</LinkWrapper>
|
||||
<LinkWrapper>
|
||||
<ExternalLink id="get-support-on-discord" href="https://discord.gg/FCfyBSbCU5" target="_blank">
|
||||
<TYPE.link fontSize={16}>
|
||||
Get support on Discord
|
||||
<span>↗</span>
|
||||
</TYPE.link>
|
||||
</ExternalLink>
|
||||
</LinkWrapper>
|
||||
</AutoRow>
|
||||
</AutoColumn>
|
||||
</BodyWrapper>
|
||||
</FallbackWrapper>
|
||||
)
|
||||
}
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
function getRelevantState(): null | keyof AppState {
|
||||
const path = window.location.hash
|
||||
if (!path.startsWith('#/')) {
|
||||
return null
|
||||
}
|
||||
const pieces = path.substring(2).split(/[\/\\?]/)
|
||||
switch (pieces[0]) {
|
||||
case 'swap':
|
||||
return 'swap'
|
||||
case 'add':
|
||||
if (pieces[1] === 'v2') return 'mint'
|
||||
else return 'mintV3'
|
||||
case 'remove':
|
||||
if (pieces[1] === 'v2') return 'burn'
|
||||
else return 'burnV3'
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function issueBody(error: Error): string {
|
||||
const relevantState = getRelevantState()
|
||||
const deviceData = getUserAgent()
|
||||
return `## URL
|
||||
|
||||
${window.location.href}
|
||||
|
||||
${
|
||||
relevantState
|
||||
? `## \`${relevantState}\` state
|
||||
|
||||
\`\`\`json
|
||||
${JSON.stringify(store.getState()[relevantState], null, 2)}
|
||||
\`\`\`
|
||||
`
|
||||
: ''
|
||||
}
|
||||
${
|
||||
error.name &&
|
||||
`## Error
|
||||
|
||||
\`\`\`
|
||||
${error.name}${error.message && `: ${error.message}`}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
${
|
||||
error.stack &&
|
||||
`## Stacktrace
|
||||
|
||||
\`\`\`
|
||||
${error.stack}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
${
|
||||
deviceData &&
|
||||
`## Device data
|
||||
|
||||
\`\`\`json
|
||||
${JSON.stringify(deviceData, null, 2)}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
`
|
||||
}
|
||||
76
src/components/FeeSelector/index.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import React from 'react'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { DynamicSection } from 'pages/AddLiquidity/styled'
|
||||
import { TYPE } from 'theme'
|
||||
import { RowBetween } from 'components/Row'
|
||||
import { ButtonRadioChecked } from 'components/Button'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
const ResponsiveText = styled(TYPE.label)`
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
font-size: 12px;
|
||||
`};
|
||||
`
|
||||
|
||||
export default function FeeSelector({
|
||||
disabled = false,
|
||||
feeAmount,
|
||||
handleFeePoolSelect,
|
||||
}: {
|
||||
disabled?: boolean
|
||||
feeAmount?: FeeAmount
|
||||
handleFeePoolSelect: (feeAmount: FeeAmount) => void
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<AutoColumn gap="16px">
|
||||
<DynamicSection gap="md" disabled={disabled}>
|
||||
<TYPE.label>{t('selectPool')}</TYPE.label>
|
||||
<TYPE.main fontSize={14} fontWeight={400} style={{ marginBottom: '.5rem', lineHeight: '125%' }}>
|
||||
Select a pool type based on your preferred liquidity provider fee.
|
||||
</TYPE.main>
|
||||
<RowBetween>
|
||||
<ButtonRadioChecked
|
||||
width="32%"
|
||||
active={feeAmount === FeeAmount.LOW}
|
||||
onClick={() => handleFeePoolSelect(FeeAmount.LOW)}
|
||||
>
|
||||
<AutoColumn gap="sm" justify="flex-start">
|
||||
<ResponsiveText>0.05% {t('fee')}</ResponsiveText>
|
||||
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
Best for stable pairs.
|
||||
</TYPE.main>
|
||||
</AutoColumn>
|
||||
</ButtonRadioChecked>
|
||||
<ButtonRadioChecked
|
||||
width="32%"
|
||||
active={feeAmount === FeeAmount.MEDIUM}
|
||||
onClick={() => handleFeePoolSelect(FeeAmount.MEDIUM)}
|
||||
>
|
||||
<AutoColumn gap="sm" justify="flex-start">
|
||||
<ResponsiveText>0.3% {t('fee')}</ResponsiveText>
|
||||
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
Best for most pairs.
|
||||
</TYPE.main>
|
||||
</AutoColumn>
|
||||
</ButtonRadioChecked>
|
||||
<ButtonRadioChecked
|
||||
width="32%"
|
||||
active={feeAmount === FeeAmount.HIGH}
|
||||
onClick={() => handleFeePoolSelect(FeeAmount.HIGH)}
|
||||
>
|
||||
<AutoColumn gap="sm" justify="flex-start">
|
||||
<ResponsiveText>1% {t('fee')}</ResponsiveText>
|
||||
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
Best for exotic pairs.
|
||||
</TYPE.main>
|
||||
</AutoColumn>
|
||||
</ButtonRadioChecked>
|
||||
</RowBetween>
|
||||
</DynamicSection>
|
||||
</AutoColumn>
|
||||
)
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
import JSBI from 'jsbi'
|
||||
import React from 'react'
|
||||
import { CurrencyAmount, Fraction, JSBI } from '@uniswap/sdk'
|
||||
import { Currency, CurrencyAmount, Fraction } from '@uniswap/sdk-core'
|
||||
|
||||
const CURRENCY_AMOUNT_MIN = new Fraction(JSBI.BigInt(1), JSBI.BigInt(1000000))
|
||||
|
||||
export default function FormattedCurrencyAmount({
|
||||
currencyAmount,
|
||||
significantDigits = 4
|
||||
significantDigits = 4,
|
||||
}: {
|
||||
currencyAmount: CurrencyAmount
|
||||
currencyAmount: CurrencyAmount<Currency>
|
||||
significantDigits?: number
|
||||
}) {
|
||||
return (
|
||||
|
||||
@@ -9,27 +9,29 @@ import { useActiveWeb3React } from '../../hooks'
|
||||
const StyledPolling = styled.div`
|
||||
position: fixed;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 1rem;
|
||||
color: white;
|
||||
transition: opacity 0.25s ease;
|
||||
color: ${({ theme }) => theme.green1};
|
||||
:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
display: none;
|
||||
`}
|
||||
`
|
||||
const StyledPollingNumber = styled(TYPE.small)<{ breathe: boolean; hovering: boolean }>`
|
||||
transition: opacity 0.25s ease;
|
||||
opacity: ${({ breathe, hovering }) => (hovering ? 0.7 : breathe ? 1 : 0.2)};
|
||||
:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
`
|
||||
const StyledPollingDot = styled.div`
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
min-height: 8px;
|
||||
min-width: 8px;
|
||||
margin-left: 0.5rem;
|
||||
margin-top: 3px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
background-color: ${({ theme }) => theme.green1};
|
||||
@@ -67,16 +69,21 @@ export default function Polling() {
|
||||
|
||||
const blockNumber = useBlockNumber()
|
||||
|
||||
const [isMounted, setIsMounted] = useState(true)
|
||||
const [isMounting, setIsMounting] = useState(false)
|
||||
const [isHover, setIsHover] = useState(false)
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
const timer1 = setTimeout(() => setIsMounted(true), 1000)
|
||||
if (!blockNumber) {
|
||||
return
|
||||
}
|
||||
|
||||
setIsMounting(true)
|
||||
const mountingTimer = setTimeout(() => setIsMounting(false), 1000)
|
||||
|
||||
// this will clear Timeout when component unmount like in willComponentUnmount
|
||||
return () => {
|
||||
setIsMounted(false)
|
||||
clearTimeout(timer1)
|
||||
clearTimeout(mountingTimer)
|
||||
}
|
||||
},
|
||||
[blockNumber] //useEffect will run only one time
|
||||
@@ -85,9 +92,11 @@ export default function Polling() {
|
||||
|
||||
return (
|
||||
<ExternalLink href={chainId && blockNumber ? getEtherscanLink(chainId, blockNumber.toString(), 'block') : ''}>
|
||||
<StyledPolling>
|
||||
<TYPE.small style={{ opacity: isMounted ? '0.2' : '0.6' }}>{blockNumber}</TYPE.small>
|
||||
<StyledPollingDot>{!isMounted && <Spinner />}</StyledPollingDot>
|
||||
<StyledPolling onMouseEnter={() => setIsHover(true)} onMouseLeave={() => setIsHover(false)}>
|
||||
<StyledPollingNumber breathe={isMounting} hovering={isHover}>
|
||||
{blockNumber}
|
||||
</StyledPollingNumber>
|
||||
<StyledPollingDot>{isMounting && <Spinner />}</StyledPollingDot>
|
||||
</StyledPolling>
|
||||
</ExternalLink>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { AlertTriangle, X } from 'react-feather'
|
||||
import { useURLWarningToggle, useURLWarningVisible } from '../../state/user/hooks'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { ChainId, TokenAmount } from '@uniswap/sdk'
|
||||
import { ChainId, CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import React, { useMemo } from 'react'
|
||||
import { X } from 'react-feather'
|
||||
import styled from 'styled-components'
|
||||
import styled from 'styled-components/macro'
|
||||
import tokenLogo from '../../assets/images/token-logo.png'
|
||||
import { UNI } from '../../constants'
|
||||
import { useTotalSupply } from '../../data/TotalSupply'
|
||||
import { useTotalSupply } from '../../hooks/useTotalSupply'
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { useMerkleDistributorContract } from '../../hooks/useContract'
|
||||
import useCurrentBlockTimestamp from '../../hooks/useCurrentBlockTimestamp'
|
||||
@@ -12,7 +12,7 @@ import { useTotalUniEarned } from '../../state/stake/hooks'
|
||||
import { useAggregateUniBalance, useTokenBalance } from '../../state/wallet/hooks'
|
||||
import { ExternalLink, StyledInternalLink, TYPE, UniTokenAnimated } from '../../theme'
|
||||
import { computeUniCirculation } from '../../utils/computeUniCirculation'
|
||||
import useUSDCPrice from '../../utils/useUSDCPrice'
|
||||
import useUSDCPrice from '../../hooks/useUSDCPrice'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { RowBetween } from '../Row'
|
||||
import { Break, CardBGImage, CardNoise, CardSection, DataCard } from '../earn/styled'
|
||||
@@ -45,14 +45,14 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
|
||||
const uni = chainId ? UNI[chainId] : undefined
|
||||
|
||||
const total = useAggregateUniBalance()
|
||||
const uniBalance: TokenAmount | undefined = useTokenBalance(account ?? undefined, uni)
|
||||
const uniToClaim: TokenAmount | undefined = useTotalUniEarned()
|
||||
const uniBalance: CurrencyAmount<Token> | undefined = useTokenBalance(account ?? undefined, uni)
|
||||
const uniToClaim: CurrencyAmount<Token> | undefined = useTotalUniEarned()
|
||||
|
||||
const totalSupply: TokenAmount | undefined = useTotalSupply(uni)
|
||||
const totalSupply: CurrencyAmount<Token> | undefined = useTotalSupply(uni)
|
||||
const uniPrice = useUSDCPrice(uni)
|
||||
const blockTimestamp = useCurrentBlockTimestamp()
|
||||
const unclaimedUni = useTokenBalance(useMerkleDistributorContract()?.address, uni)
|
||||
const circulation: TokenAmount | undefined = useMemo(
|
||||
const circulation: CurrencyAmount<Token> | undefined = useMemo(
|
||||
() =>
|
||||
blockTimestamp && uni && chainId === ChainId.MAINNET
|
||||
? computeUniCirculation(uni, blockTimestamp, unclaimedUni)
|
||||
@@ -117,7 +117,7 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
|
||||
<TYPE.white color="white">{totalSupply?.toFixed(0, { groupSeparator: ',' })}</TYPE.white>
|
||||
</RowBetween>
|
||||
{uni && uni.chainId === ChainId.MAINNET ? (
|
||||
<ExternalLink href={`https://uniswap.info/token/${uni.address}`}>View UNI Analytics</ExternalLink>
|
||||
<ExternalLink href={`https://info.uniswap.org/token/${uni.address}`}>View UNI Analytics</ExternalLink>
|
||||
) : null}
|
||||
</AutoColumn>
|
||||
</CardSection>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { ChainId, TokenAmount } from '@uniswap/sdk'
|
||||
import { ChainId } from '@uniswap/sdk-core'
|
||||
import useScrollPosition from '@react-hook/window-scroll'
|
||||
import React, { useState } from 'react'
|
||||
import { Text } from 'rebass'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { darken } from 'polished'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import styled from 'styled-components'
|
||||
import { Moon, Sun } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import Logo from '../../assets/svg/logo.svg'
|
||||
import LogoDark from '../../assets/svg/logo_white.svg'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { useDarkModeManager } from '../../state/user/hooks'
|
||||
import { useETHBalances, useAggregateUniBalance } from '../../state/wallet/hooks'
|
||||
import { useETHBalances } from '../../state/wallet/hooks'
|
||||
import { CardNoise } from '../earn/styled'
|
||||
import { CountUp } from 'use-count-up'
|
||||
import { TYPE, ExternalLink } from '../../theme'
|
||||
|
||||
import { YellowCard } from '../Card'
|
||||
import Settings from '../Settings'
|
||||
import Menu from '../Menu'
|
||||
|
||||
import Row, { RowFixed } from '../Row'
|
||||
@@ -29,11 +29,10 @@ import { useUserHasSubmittedClaim } from '../../state/transactions/hooks'
|
||||
import { Dots } from '../swap/styleds'
|
||||
import Modal from '../Modal'
|
||||
import UniBalanceContent from './UniBalanceContent'
|
||||
import usePrevious from '../../hooks/usePrevious'
|
||||
|
||||
const HeaderFrame = styled.div`
|
||||
const HeaderFrame = styled.div<{ showBackground: boolean }>`
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 120px;
|
||||
grid-template-columns: 120px 1fr 120px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
@@ -41,18 +40,25 @@ const HeaderFrame = styled.div`
|
||||
width: 100%;
|
||||
top: 0;
|
||||
position: relative;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
padding: 1rem;
|
||||
z-index: 2;
|
||||
z-index: 21;
|
||||
position: relative;
|
||||
|
||||
/* Background slide effect on scroll. */
|
||||
background-image: ${({ theme }) => `linear-gradient(to bottom, transparent 50%, ${theme.bg0} 50% )}}`}
|
||||
background-position: ${({ showBackground }) => (showBackground ? '0 -100%' : '0 0')};
|
||||
background-size: 100% 200%;
|
||||
box-shadow: 0px 0px 0px 1px ${({ theme, showBackground }) => (showBackground ? theme.bg2 : 'transparent;')};
|
||||
transition: background-position .1s, box-shadow .1s;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
grid-template-columns: 1fr;
|
||||
padding: 0 1rem;
|
||||
width: calc(100%);
|
||||
position: relative;
|
||||
padding: 1rem;
|
||||
grid-template-columns: 120px 1fr;
|
||||
|
||||
`};
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToExtraSmall`
|
||||
padding: 0.5rem 1rem;
|
||||
padding: 1rem;
|
||||
`}
|
||||
`
|
||||
|
||||
@@ -83,10 +89,14 @@ const HeaderControls = styled.div`
|
||||
const HeaderElement = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
/* addresses safari's lack of support for "gap" */
|
||||
& > *:not(:first-child) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
flex-direction: row-reverse;
|
||||
flex-direction: row-reverse;
|
||||
align-items: center;
|
||||
`};
|
||||
`
|
||||
@@ -103,18 +113,25 @@ const HeaderRow = styled(RowFixed)`
|
||||
`
|
||||
|
||||
const HeaderLinks = styled(Row)`
|
||||
justify-content: center;
|
||||
justify-self: center;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
width: fit-content;
|
||||
padding: 4px;
|
||||
border-radius: 16px;
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-gap: 10px;
|
||||
overflow: auto;
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
padding: 1rem 0 1rem 1rem;
|
||||
justify-content: flex-end;
|
||||
`};
|
||||
justify-self: flex-end;
|
||||
`};
|
||||
`
|
||||
|
||||
const AccountElement = styled.div<{ active: boolean }>`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg3)};
|
||||
background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg2)};
|
||||
border-radius: 12px;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
@@ -123,9 +140,6 @@ const AccountElement = styled.div<{ active: boolean }>`
|
||||
:focus {
|
||||
border: 1px solid blue;
|
||||
}
|
||||
/* :hover {
|
||||
background-color: ${({ theme, active }) => (!active ? theme.bg2 : theme.bg4)};
|
||||
} */
|
||||
`
|
||||
|
||||
const UNIAmount = styled(AccountElement)`
|
||||
@@ -200,7 +214,7 @@ const UniIcon = styled.div`
|
||||
const activeClassName = 'ACTIVE'
|
||||
|
||||
const StyledNavLink = styled(NavLink).attrs({
|
||||
activeClassName
|
||||
activeClassName,
|
||||
})`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: left;
|
||||
@@ -211,13 +225,14 @@ const StyledNavLink = styled(NavLink).attrs({
|
||||
color: ${({ theme }) => theme.text2};
|
||||
font-size: 1rem;
|
||||
width: fit-content;
|
||||
margin: 0 12px;
|
||||
font-weight: 500;
|
||||
padding: 8px 12px;
|
||||
|
||||
&.${activeClassName} {
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
}
|
||||
|
||||
:hover,
|
||||
@@ -227,7 +242,7 @@ const StyledNavLink = styled(NavLink).attrs({
|
||||
`
|
||||
|
||||
const StyledExternalLink = styled(ExternalLink).attrs({
|
||||
activeClassName
|
||||
activeClassName,
|
||||
})<{ isActive?: boolean }>`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: left;
|
||||
@@ -257,11 +272,40 @@ const StyledExternalLink = styled(ExternalLink).attrs({
|
||||
`}
|
||||
`
|
||||
|
||||
export const StyledMenuButton = styled.button`
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 35px;
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
margin-left: 8px;
|
||||
padding: 0.15rem 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
:hover,
|
||||
:focus {
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
background-color: ${({ theme }) => theme.bg4};
|
||||
}
|
||||
|
||||
svg {
|
||||
margin-top: 2px;
|
||||
}
|
||||
> * {
|
||||
stroke: ${({ theme }) => theme.text1};
|
||||
}
|
||||
`
|
||||
|
||||
const NETWORK_LABELS: { [chainId in ChainId]?: string } = {
|
||||
[ChainId.RINKEBY]: 'Rinkeby',
|
||||
[ChainId.ROPSTEN]: 'Ropsten',
|
||||
[ChainId.GÖRLI]: 'Görli',
|
||||
[ChainId.KOVAN]: 'Kovan'
|
||||
[ChainId.KOVAN]: 'Kovan',
|
||||
}
|
||||
|
||||
export default function Header() {
|
||||
@@ -269,7 +313,8 @@ export default function Header() {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? '']
|
||||
const [isDark] = useDarkModeManager()
|
||||
// const [isDark] = useDarkModeManager()
|
||||
const [darkMode, toggleDarkMode] = useDarkModeManager()
|
||||
|
||||
const toggleClaimModal = useToggleSelfClaimModal()
|
||||
|
||||
@@ -277,16 +322,13 @@ export default function Header() {
|
||||
|
||||
const { claimTxn } = useUserHasSubmittedClaim(account ?? undefined)
|
||||
|
||||
const aggregateBalance: TokenAmount | undefined = useAggregateUniBalance()
|
||||
|
||||
const [showUniBalanceModal, setShowUniBalanceModal] = useState(false)
|
||||
const showClaimPopup = useShowClaimPopup()
|
||||
|
||||
const countUpValue = aggregateBalance?.toFixed(0) ?? '0'
|
||||
const countUpValuePrevious = usePrevious(countUpValue) ?? '0'
|
||||
const scrollY = useScrollPosition()
|
||||
|
||||
return (
|
||||
<HeaderFrame>
|
||||
<HeaderFrame showBackground={scrollY > 45}>
|
||||
<ClaimModal />
|
||||
<Modal isOpen={showUniBalanceModal} onDismiss={() => setShowUniBalanceModal(false)}>
|
||||
<UniBalanceContent setShowUniBalanceModal={setShowUniBalanceModal} />
|
||||
@@ -294,37 +336,34 @@ export default function Header() {
|
||||
<HeaderRow>
|
||||
<Title href=".">
|
||||
<UniIcon>
|
||||
<img width={'24px'} src={isDark ? LogoDark : Logo} alt="logo" />
|
||||
<img width={'24px'} src={darkMode ? LogoDark : Logo} alt="logo" />
|
||||
</UniIcon>
|
||||
</Title>
|
||||
<HeaderLinks>
|
||||
<StyledNavLink id={`swap-nav-link`} to={'/swap'}>
|
||||
{t('swap')}
|
||||
</StyledNavLink>
|
||||
<StyledNavLink
|
||||
id={`pool-nav-link`}
|
||||
to={'/pool'}
|
||||
isActive={(match, { pathname }) =>
|
||||
Boolean(match) ||
|
||||
pathname.startsWith('/add') ||
|
||||
pathname.startsWith('/remove') ||
|
||||
pathname.startsWith('/create') ||
|
||||
pathname.startsWith('/find')
|
||||
}
|
||||
>
|
||||
{t('pool')}
|
||||
</StyledNavLink>
|
||||
<StyledNavLink id={`stake-nav-link`} to={'/uni'}>
|
||||
UNI
|
||||
</StyledNavLink>
|
||||
<StyledNavLink id={`stake-nav-link`} to={'/vote'}>
|
||||
Vote
|
||||
</StyledNavLink>
|
||||
<StyledExternalLink id={`stake-nav-link`} href={'https://uniswap.info'}>
|
||||
Charts <span style={{ fontSize: '11px' }}>↗</span>
|
||||
</StyledExternalLink>
|
||||
</HeaderLinks>
|
||||
</HeaderRow>
|
||||
<HeaderLinks>
|
||||
<StyledNavLink id={`swap-nav-link`} to={'/swap'}>
|
||||
{t('swap')}
|
||||
</StyledNavLink>
|
||||
<StyledNavLink
|
||||
id={`pool-nav-link`}
|
||||
to={'/pool'}
|
||||
isActive={(match, { pathname }) =>
|
||||
Boolean(match) ||
|
||||
pathname.startsWith('/add') ||
|
||||
pathname.startsWith('/remove') ||
|
||||
pathname.startsWith('/increase') ||
|
||||
pathname.startsWith('/find')
|
||||
}
|
||||
>
|
||||
{t('pool')}
|
||||
</StyledNavLink>
|
||||
<StyledNavLink id={`stake-nav-link`} to={'/vote'}>
|
||||
Vote
|
||||
</StyledNavLink>
|
||||
<StyledExternalLink id={`stake-nav-link`} href={'https://info.uniswap.org'}>
|
||||
Charts <span style={{ fontSize: '11px', textDecoration: 'none !important' }}>↗</span>
|
||||
</StyledExternalLink>
|
||||
</HeaderLinks>
|
||||
<HeaderControls>
|
||||
<HeaderElement>
|
||||
<HideSmall>
|
||||
@@ -342,32 +381,6 @@ export default function Header() {
|
||||
<CardNoise />
|
||||
</UNIWrapper>
|
||||
)}
|
||||
{!availableClaim && aggregateBalance && (
|
||||
<UNIWrapper onClick={() => setShowUniBalanceModal(true)}>
|
||||
<UNIAmount active={!!account && !availableClaim} style={{ pointerEvents: 'auto' }}>
|
||||
{account && (
|
||||
<HideSmall>
|
||||
<TYPE.white
|
||||
style={{
|
||||
paddingRight: '.4rem'
|
||||
}}
|
||||
>
|
||||
<CountUp
|
||||
key={countUpValue}
|
||||
isCounting
|
||||
start={parseFloat(countUpValuePrevious)}
|
||||
end={parseFloat(countUpValue)}
|
||||
thousandsSeparator={','}
|
||||
duration={1}
|
||||
/>
|
||||
</TYPE.white>
|
||||
</HideSmall>
|
||||
)}
|
||||
UNI
|
||||
</UNIAmount>
|
||||
<CardNoise />
|
||||
</UNIWrapper>
|
||||
)}
|
||||
<AccountElement active={!!account} style={{ pointerEvents: 'auto' }}>
|
||||
{account && userEthBalance ? (
|
||||
<BalanceText style={{ flexShrink: 0 }} pl="0.75rem" pr="0.5rem" fontWeight={500}>
|
||||
@@ -378,7 +391,9 @@ export default function Header() {
|
||||
</AccountElement>
|
||||
</HeaderElement>
|
||||
<HeaderElementWrap>
|
||||
<Settings />
|
||||
<StyledMenuButton onClick={() => toggleDarkMode()}>
|
||||
{darkMode ? <Moon size={20} /> : <Sun size={20} />}
|
||||
</StyledMenuButton>
|
||||
<Menu />
|
||||
</HeaderElementWrap>
|
||||
</HeaderControls>
|
||||
|
||||
63
src/components/HoverInlineText/index.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import Tooltip from 'components/Tooltip'
|
||||
import React, { useState } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
const TextWrapper = styled.span<{ margin: boolean; link?: boolean; fontSize?: string; adjustSize?: boolean }>`
|
||||
cursor: auto;
|
||||
margin-left: ${({ margin }) => margin && '4px'};
|
||||
color: ${({ theme, link }) => (link ? theme.blue1 : theme.text1)};
|
||||
font-size: ${({ fontSize }) => fontSize ?? 'inherit'};
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
font-size: ${({ adjustSize }) => adjustSize && '12px'};
|
||||
}
|
||||
`
|
||||
|
||||
const HoverInlineText = ({
|
||||
text,
|
||||
maxCharacters = 20,
|
||||
margin = false,
|
||||
adjustSize = false,
|
||||
fontSize,
|
||||
link,
|
||||
...rest
|
||||
}: {
|
||||
text: string
|
||||
maxCharacters?: number
|
||||
margin?: boolean
|
||||
adjustSize?: boolean
|
||||
fontSize?: string
|
||||
link?: boolean
|
||||
}) => {
|
||||
const [showHover, setShowHover] = useState(false)
|
||||
|
||||
if (!text) {
|
||||
return <span />
|
||||
}
|
||||
|
||||
if (text.length > maxCharacters) {
|
||||
return (
|
||||
<Tooltip text={text} show={showHover}>
|
||||
<TextWrapper
|
||||
onMouseEnter={() => setShowHover(true)}
|
||||
onMouseLeave={() => setShowHover(false)}
|
||||
margin={margin}
|
||||
adjustSize={adjustSize}
|
||||
link={link}
|
||||
fontSize={fontSize}
|
||||
{...rest}
|
||||
>
|
||||
{' ' + text.slice(0, maxCharacters - 1) + '...'}
|
||||
</TextWrapper>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<TextWrapper margin={margin} adjustSize={adjustSize} link={link} fontSize={fontSize} {...rest}>
|
||||
{text}
|
||||
</TextWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export default HoverInlineText
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
|
||||
import styled from 'styled-components'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import Jazzicon from 'jazzicon'
|
||||
|
||||
159
src/components/InputStepCounter/InputStepCounter.tsx
Normal file
@@ -0,0 +1,159 @@
|
||||
import React, { useState, useCallback, useEffect } from 'react'
|
||||
import { LightCard } from 'components/Card'
|
||||
import { RowBetween } from 'components/Row'
|
||||
import { Input as NumericalInput } from '../NumericalInput'
|
||||
import styled, { keyframes } from 'styled-components'
|
||||
import { TYPE } from 'theme'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { ButtonPrimary } from 'components/Button'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import { formattedFeeAmount } from 'utils'
|
||||
|
||||
const pulse = (color: string) => keyframes`
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 ${color};
|
||||
}
|
||||
|
||||
70% {
|
||||
box-shadow: 0 0 0 2px ${color};
|
||||
}
|
||||
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 ${color};
|
||||
}
|
||||
`
|
||||
|
||||
const SmallButton = styled(ButtonPrimary)`
|
||||
/* background-color: ${({ theme }) => theme.bg2}; */
|
||||
border-radius: 8px;
|
||||
padding: 4px 6px;
|
||||
width: 48%;
|
||||
`
|
||||
|
||||
const FocusedOutlineCard = styled(LightCard)<{ active?: boolean; pulsing?: boolean }>`
|
||||
border-color: ${({ active, theme }) => active && theme.blue1};
|
||||
padding: 12px;
|
||||
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.blue1)} 0.8s linear;
|
||||
`
|
||||
|
||||
const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
|
||||
/* background-color: ${({ theme }) => theme.bg0}; */
|
||||
text-align: center;
|
||||
margin-right: 12px;
|
||||
width: 100%;
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
const InputTitle = styled(TYPE.small)`
|
||||
color: ${({ theme }) => theme.text2};
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
interface StepCounterProps {
|
||||
value: string
|
||||
onUserInput: (value: string) => void
|
||||
decrement: () => string
|
||||
increment: () => string
|
||||
feeAmount?: FeeAmount
|
||||
label?: string
|
||||
width?: string
|
||||
locked?: boolean // disable input
|
||||
title: string
|
||||
tokenA: string | undefined
|
||||
tokenB: string | undefined
|
||||
}
|
||||
|
||||
const StepCounter = ({
|
||||
value,
|
||||
decrement,
|
||||
increment,
|
||||
feeAmount,
|
||||
width,
|
||||
locked,
|
||||
onUserInput,
|
||||
title,
|
||||
tokenA,
|
||||
tokenB,
|
||||
}: StepCounterProps) => {
|
||||
// for focus state, styled components doesnt let you select input parent container
|
||||
const [active, setActive] = useState(false)
|
||||
|
||||
// let user type value and only update parent value on blur
|
||||
const [localValue, setLocalValue] = useState('')
|
||||
const [useLocalValue, setUseLocalValue] = useState(false)
|
||||
|
||||
// animation if parent value updates local value
|
||||
const [pulsing, setPulsing] = useState<boolean>(false)
|
||||
|
||||
// format fee amount
|
||||
const feeAmountFormatted = feeAmount ? formattedFeeAmount(feeAmount * 2) : ''
|
||||
|
||||
const handleOnFocus = () => {
|
||||
setUseLocalValue(true)
|
||||
setActive(true)
|
||||
}
|
||||
|
||||
const handleOnBlur = useCallback(() => {
|
||||
setUseLocalValue(false)
|
||||
setActive(false)
|
||||
onUserInput(localValue) // trigger update on parent value
|
||||
}, [localValue, onUserInput])
|
||||
|
||||
// for button clicks
|
||||
const handleDecrement = useCallback(() => {
|
||||
setUseLocalValue(false)
|
||||
onUserInput(decrement())
|
||||
}, [decrement, onUserInput])
|
||||
|
||||
const handleIncrement = useCallback(() => {
|
||||
setUseLocalValue(false)
|
||||
onUserInput(increment())
|
||||
}, [increment, onUserInput])
|
||||
|
||||
useEffect(() => {
|
||||
if (localValue !== value && !useLocalValue) {
|
||||
setTimeout(() => {
|
||||
setLocalValue(value) // reset local value to match parent
|
||||
setPulsing(true) // trigger animation
|
||||
setTimeout(function () {
|
||||
setPulsing(false)
|
||||
}, 1800)
|
||||
}, 0)
|
||||
}
|
||||
}, [localValue, useLocalValue, value])
|
||||
|
||||
return (
|
||||
<FocusedOutlineCard pulsing={pulsing} active={active} onFocus={handleOnFocus} onBlur={handleOnBlur} width={width}>
|
||||
<AutoColumn gap="6px" style={{ marginBottom: '12px' }}>
|
||||
<InputTitle fontSize={12} textAlign="center">
|
||||
{title}
|
||||
</InputTitle>
|
||||
<StyledInput
|
||||
className="rate-input-0"
|
||||
value={localValue}
|
||||
fontSize="20px"
|
||||
disabled={locked}
|
||||
onUserInput={(val) => {
|
||||
setLocalValue(val)
|
||||
}}
|
||||
/>
|
||||
<InputTitle fontSize={12} textAlign="center">
|
||||
{tokenB + ' per ' + tokenA}
|
||||
</InputTitle>
|
||||
</AutoColumn>
|
||||
{!locked ? (
|
||||
<RowBetween>
|
||||
<SmallButton onClick={handleDecrement}>
|
||||
<TYPE.white fontSize="12px">-{feeAmountFormatted}%</TYPE.white>
|
||||
</SmallButton>
|
||||
<SmallButton onClick={handleIncrement}>
|
||||
<TYPE.white fontSize="12px">+{feeAmountFormatted}%</TYPE.white>
|
||||
</SmallButton>
|
||||
</RowBetween>
|
||||
) : null}
|
||||
</FocusedOutlineCard>
|
||||
)
|
||||
}
|
||||
|
||||
export default StepCounter
|
||||
90
src/components/LineChart/LineChart.stories.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import { Story } from '@storybook/react/types-6-0'
|
||||
import React from 'react'
|
||||
// import Row, { RowFixed } from 'components/Row'
|
||||
import styled from 'styled-components/macro'
|
||||
import Component, { LineChartProps } from './'
|
||||
import { dummyData } from './data'
|
||||
// import { AutoColumn } from 'components/Column'
|
||||
// import { TYPE } from 'theme'
|
||||
// import DoubleCurrencyLogo from 'components/DoubleLogo'
|
||||
// import { MKR } from 'constants'
|
||||
// import { ETHER } from '@uniswap/sdk'
|
||||
// import LineChart from '.'
|
||||
|
||||
const Wrapper = styled.div`
|
||||
margin: 1em 2em;
|
||||
max-width: 500px;
|
||||
& > * {
|
||||
font-size: 1em;
|
||||
}
|
||||
`
|
||||
|
||||
export default {
|
||||
title: 'Charts',
|
||||
argTypes: {
|
||||
disabled: { control: { type: 'boolean' } },
|
||||
onClick: { action: 'clicked' },
|
||||
},
|
||||
decorators: [
|
||||
(Component: Story) => (
|
||||
<Wrapper>
|
||||
<Component />
|
||||
</Wrapper>
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
const Template: Story<LineChartProps> = (args) => <Component {...args}>{args.children}</Component>
|
||||
|
||||
export const Basic = Template.bind({})
|
||||
Basic.args = { data: dummyData }
|
||||
|
||||
// const Full = () => {
|
||||
// const [value, setValue] = useState<number | undefined>(dummyData[dummyData.length - 1].value)
|
||||
|
||||
// const dummyUSDPrice = 410 // used for conversion
|
||||
|
||||
// const TopLeftContent = () => (
|
||||
// <AutoColumn gap="md">
|
||||
// <RowFixed align="center">
|
||||
// <DoubleCurrencyLogo currency0={MKR} currency1={ETHER} size={20} />{' '}
|
||||
// <TYPE.main fontSize="20px" color="white" ml="10px">
|
||||
// ETH / MKR
|
||||
// </TYPE.main>
|
||||
// </RowFixed>
|
||||
// <Row>
|
||||
// <TYPE.main fontSize="20px" color="white">
|
||||
// {value} MKR
|
||||
// </TYPE.main>
|
||||
// <TYPE.main color="#565A69" fontSize="20px" ml="10px">
|
||||
// ($
|
||||
// {value
|
||||
// ? (value * dummyUSDPrice).toLocaleString('USD', {
|
||||
// currency: 'USD',
|
||||
// minimumFractionDigits: 2
|
||||
// })
|
||||
// : null}{' '}
|
||||
// )
|
||||
// </TYPE.main>
|
||||
// </Row>
|
||||
// </AutoColumn>
|
||||
// )
|
||||
|
||||
// return (
|
||||
// <Wrapper>
|
||||
// <LineChart data={dummyData} setValue={setValue} topLeft={<TopLeftContent />} />
|
||||
// </Wrapper>
|
||||
// )
|
||||
// }
|
||||
|
||||
// export const FullVersion = Template.bind(() => <LineChart data={dummyData} />)
|
||||
// Full.args = { data: dummyData }
|
||||
// Full.decorators = [
|
||||
// (Story: any) => {
|
||||
// return (
|
||||
// <Wrapper>
|
||||
// <LineChart data={dummyData} />
|
||||
// </Wrapper>
|
||||
// )
|
||||
// }
|
||||
// ]
|
||||
152
src/components/LineChart/data.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
export const dummyData = [
|
||||
{ time: '2018-10-19', value: 35.98 },
|
||||
{ time: '2018-10-22', value: 35.75 },
|
||||
{ time: '2018-10-23', value: 35.65 },
|
||||
{ time: '2018-10-24', value: 34.12 },
|
||||
{ time: '2018-10-25', value: 35.84 },
|
||||
{ time: '2018-10-26', value: 35.24 },
|
||||
{ time: '2018-10-29', value: 35.99 },
|
||||
{ time: '2018-10-30', value: 37.71 },
|
||||
{ time: '2018-10-31', value: 38.14 },
|
||||
{ time: '2018-11-01', value: 37.95 },
|
||||
{ time: '2018-11-02', value: 37.66 },
|
||||
{ time: '2018-11-05', value: 38.02 },
|
||||
{ time: '2018-11-06', value: 37.73 },
|
||||
{ time: '2018-11-07', value: 38.3 },
|
||||
{ time: '2018-11-08', value: 38.3 },
|
||||
{ time: '2018-11-09', value: 38.34 },
|
||||
{ time: '2018-11-12', value: 38.0 },
|
||||
{ time: '2018-11-13', value: 37.72 },
|
||||
{ time: '2018-11-14', value: 38.29 },
|
||||
{ time: '2018-11-15', value: 38.49 },
|
||||
{ time: '2018-11-16', value: 38.59 },
|
||||
{ time: '2018-11-19', value: 38.18 },
|
||||
{ time: '2018-11-20', value: 36.76 },
|
||||
{ time: '2018-11-21', value: 37.51 },
|
||||
{ time: '2018-11-23', value: 37.39 },
|
||||
{ time: '2018-11-26', value: 37.77 },
|
||||
{ time: '2018-11-27', value: 38.36 },
|
||||
{ time: '2018-11-28', value: 39.06 },
|
||||
{ time: '2018-11-29', value: 39.42 },
|
||||
{ time: '2018-11-30', value: 39.01 },
|
||||
{ time: '2018-12-03', value: 39.15 },
|
||||
{ time: '2018-12-04', value: 37.69 },
|
||||
{ time: '2018-12-06', value: 37.88 },
|
||||
{ time: '2018-12-07', value: 37.41 },
|
||||
{ time: '2018-12-10', value: 37.35 },
|
||||
{ time: '2018-12-11', value: 36.84 },
|
||||
{ time: '2018-12-12', value: 36.98 },
|
||||
{ time: '2018-12-13', value: 36.76 },
|
||||
{ time: '2018-12-14', value: 36.34 },
|
||||
{ time: '2018-12-17', value: 36.21 },
|
||||
{ time: '2018-12-18', value: 35.65 },
|
||||
{ time: '2018-12-19', value: 35.19 },
|
||||
{ time: '2018-12-20', value: 34.62 },
|
||||
{ time: '2018-12-21', value: 33.75 },
|
||||
{ time: '2018-12-24', value: 33.07 },
|
||||
{ time: '2018-12-26', value: 34.14 },
|
||||
{ time: '2018-12-27', value: 34.47 },
|
||||
{ time: '2018-12-28', value: 34.35 },
|
||||
{ time: '2018-12-31', value: 34.05 },
|
||||
{ time: '2019-01-02', value: 34.37 },
|
||||
{ time: '2019-01-03', value: 34.64 },
|
||||
{ time: '2019-01-04', value: 35.81 },
|
||||
{ time: '2019-01-07', value: 35.43 },
|
||||
{ time: '2019-01-08', value: 35.72 },
|
||||
{ time: '2019-01-09', value: 36.06 },
|
||||
{ time: '2019-01-10', value: 35.82 },
|
||||
{ time: '2019-01-11', value: 35.63 },
|
||||
{ time: '2019-01-14', value: 35.77 },
|
||||
{ time: '2019-01-15', value: 35.83 },
|
||||
{ time: '2019-01-16', value: 35.9 },
|
||||
{ time: '2019-01-17', value: 35.91 },
|
||||
{ time: '2019-01-18', value: 36.21 },
|
||||
{ time: '2019-01-22', value: 34.97 },
|
||||
{ time: '2019-01-23', value: 36.89 },
|
||||
{ time: '2019-01-24', value: 36.24 },
|
||||
{ time: '2019-01-25', value: 35.78 },
|
||||
{ time: '2019-01-28', value: 35.37 },
|
||||
{ time: '2019-01-29', value: 36.08 },
|
||||
{ time: '2019-01-30', value: 35.43 },
|
||||
{ time: '2019-01-31', value: 36.57 },
|
||||
{ time: '2019-02-01', value: 36.79 },
|
||||
{ time: '2019-02-04', value: 36.77 },
|
||||
{ time: '2019-02-05', value: 37.15 },
|
||||
{ time: '2019-02-06', value: 37.17 },
|
||||
{ time: '2019-02-07', value: 37.68 },
|
||||
{ time: '2019-02-08', value: 37.6 },
|
||||
{ time: '2019-02-11', value: 37.0 },
|
||||
{ time: '2019-02-12', value: 37.24 },
|
||||
{ time: '2019-02-13', value: 37.03 },
|
||||
{ time: '2019-02-14', value: 37.26 },
|
||||
{ time: '2019-02-15', value: 37.77 },
|
||||
{ time: '2019-02-19', value: 37.55 },
|
||||
{ time: '2019-02-20', value: 37.79 },
|
||||
{ time: '2019-02-21', value: 38.47 },
|
||||
{ time: '2019-02-22', value: 38.61 },
|
||||
{ time: '2019-02-25', value: 38.57 },
|
||||
{ time: '2019-02-26', value: 38.8 },
|
||||
{ time: '2019-02-27', value: 38.53 },
|
||||
{ time: '2019-02-28', value: 38.67 },
|
||||
{ time: '2019-03-01', value: 39.1 },
|
||||
{ time: '2019-03-04', value: 38.73 },
|
||||
{ time: '2019-03-05', value: 38.72 },
|
||||
{ time: '2019-03-06', value: 38.61 },
|
||||
{ time: '2019-03-07', value: 38.38 },
|
||||
{ time: '2019-03-08', value: 38.19 },
|
||||
{ time: '2019-03-11', value: 39.17 },
|
||||
{ time: '2019-03-12', value: 39.49 },
|
||||
{ time: '2019-03-13', value: 39.56 },
|
||||
{ time: '2019-03-14', value: 39.87 },
|
||||
{ time: '2019-03-15', value: 40.47 },
|
||||
{ time: '2019-03-18', value: 39.92 },
|
||||
{ time: '2019-03-19', value: 39.78 },
|
||||
{ time: '2019-03-20', value: 39.47 },
|
||||
{ time: '2019-03-21', value: 40.05 },
|
||||
{ time: '2019-03-22', value: 39.46 },
|
||||
{ time: '2019-03-25', value: 39.18 },
|
||||
{ time: '2019-03-26', value: 39.63 },
|
||||
{ time: '2019-03-27', value: 40.21 },
|
||||
{ time: '2019-03-28', value: 40.42 },
|
||||
{ time: '2019-03-29', value: 39.98 },
|
||||
{ time: '2019-04-01', value: 40.31 },
|
||||
{ time: '2019-04-02', value: 40.02 },
|
||||
{ time: '2019-04-03', value: 40.27 },
|
||||
{ time: '2019-04-04', value: 40.41 },
|
||||
{ time: '2019-04-05', value: 40.42 },
|
||||
{ time: '2019-04-08', value: 40.71 },
|
||||
{ time: '2019-04-09', value: 41.04 },
|
||||
{ time: '2019-04-10', value: 41.08 },
|
||||
{ time: '2019-04-11', value: 41.04 },
|
||||
{ time: '2019-04-12', value: 41.3 },
|
||||
{ time: '2019-04-15', value: 41.78 },
|
||||
{ time: '2019-04-16', value: 41.97 },
|
||||
{ time: '2019-04-17', value: 42.57 },
|
||||
{ time: '2019-04-18', value: 42.43 },
|
||||
{ time: '2019-04-22', value: 42.0 },
|
||||
{ time: '2019-04-23', value: 41.99 },
|
||||
{ time: '2019-04-24', value: 41.85 },
|
||||
{ time: '2019-04-25', value: 42.93 },
|
||||
{ time: '2019-04-26', value: 43.08 },
|
||||
{ time: '2019-04-29', value: 43.45 },
|
||||
{ time: '2019-04-30', value: 43.53 },
|
||||
{ time: '2019-05-01', value: 43.42 },
|
||||
{ time: '2019-05-02', value: 42.65 },
|
||||
{ time: '2019-05-03', value: 43.29 },
|
||||
{ time: '2019-05-06', value: 43.3 },
|
||||
{ time: '2019-05-07', value: 42.76 },
|
||||
{ time: '2019-05-08', value: 42.55 },
|
||||
{ time: '2019-05-09', value: 42.92 },
|
||||
{ time: '2019-05-10', value: 43.15 },
|
||||
{ time: '2019-05-13', value: 42.28 },
|
||||
{ time: '2019-05-14', value: 42.91 },
|
||||
{ time: '2019-05-15', value: 42.49 },
|
||||
{ time: '2019-05-16', value: 43.19 },
|
||||
{ time: '2019-05-17', value: 43.54 },
|
||||
{ time: '2019-05-20', value: 42.78 },
|
||||
{ time: '2019-05-21', value: 43.29 },
|
||||
{ time: '2019-05-22', value: 43.3 },
|
||||
{ time: '2019-05-23', value: 42.73 },
|
||||
{ time: '2019-05-24', value: 42.67 },
|
||||
{ time: '2019-05-28', value: 42.75 },
|
||||
]
|
||||
169
src/components/LineChart/index.tsx
Normal file
@@ -0,0 +1,169 @@
|
||||
import React, { useRef, useState, useEffect, useCallback, Dispatch, SetStateAction, ReactNode } from 'react'
|
||||
import { createChart, IChartApi } from 'lightweight-charts'
|
||||
import { darken } from 'polished'
|
||||
import { RowBetween } from 'components/Row'
|
||||
import Card from '../Card'
|
||||
import styled from 'styled-components/macro'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
|
||||
const Wrapper = styled(Card)`
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
background-color: ${({ theme }) => theme.bg0}
|
||||
flex-direction: column;
|
||||
> * {
|
||||
font-size: 1rem;
|
||||
}
|
||||
`
|
||||
|
||||
const DEFAULT_HEIGHT = 300
|
||||
|
||||
export type LineChartProps = {
|
||||
data: any[]
|
||||
color?: string | undefined
|
||||
height?: number | undefined
|
||||
setValue?: Dispatch<SetStateAction<number | undefined>> // used for value on hover
|
||||
topLeft?: ReactNode | undefined
|
||||
topRight?: ReactNode | undefined
|
||||
bottomLeft?: ReactNode | undefined
|
||||
bottomRight?: ReactNode | undefined
|
||||
} & React.HTMLAttributes<HTMLDivElement>
|
||||
|
||||
const LineChart = ({
|
||||
data,
|
||||
color = '#56B2A4',
|
||||
setValue,
|
||||
topLeft,
|
||||
topRight,
|
||||
bottomLeft,
|
||||
bottomRight,
|
||||
height = DEFAULT_HEIGHT,
|
||||
...rest
|
||||
}: LineChartProps) => {
|
||||
const theme = useTheme()
|
||||
|
||||
const chartRef = useRef<HTMLDivElement>(null)
|
||||
const [chartCreated, setChart] = useState<IChartApi | undefined>()
|
||||
|
||||
// for reseting value on hover exit
|
||||
const currenValue = data[data.length - 1].value
|
||||
|
||||
const handleResize = useCallback(() => {
|
||||
if (chartCreated && chartRef?.current?.parentElement) {
|
||||
chartCreated.resize(chartRef.current.parentElement.clientWidth - 32, height)
|
||||
chartCreated.timeScale().fitContent()
|
||||
chartCreated.timeScale().scrollToPosition(0, false)
|
||||
}
|
||||
}, [chartCreated, chartRef, height])
|
||||
|
||||
// add event listener for resize
|
||||
const isClient = typeof window === 'object'
|
||||
useEffect(() => {
|
||||
if (!isClient) {
|
||||
return
|
||||
}
|
||||
window.addEventListener('resize', handleResize)
|
||||
return () => window.removeEventListener('resize', handleResize)
|
||||
}, [isClient, chartRef, handleResize]) // Empty array ensures that effect is only run on mount and unmount
|
||||
|
||||
const textColor = theme.text2
|
||||
|
||||
// if chart not instantiated in canvas, create it
|
||||
useEffect(() => {
|
||||
if (!chartCreated && data && !!chartRef?.current?.parentElement) {
|
||||
const chart = createChart(chartRef.current, {
|
||||
height: height,
|
||||
width: chartRef.current.parentElement.clientWidth - 32,
|
||||
layout: {
|
||||
backgroundColor: 'transparent',
|
||||
textColor: textColor,
|
||||
fontFamily: 'Inter',
|
||||
},
|
||||
rightPriceScale: {
|
||||
scaleMargins: {
|
||||
top: 0.1,
|
||||
bottom: 0.1,
|
||||
},
|
||||
borderVisible: false,
|
||||
},
|
||||
timeScale: {
|
||||
borderVisible: false,
|
||||
},
|
||||
watermark: {
|
||||
color: 'rgba(0, 0, 0, 0)',
|
||||
},
|
||||
grid: {
|
||||
horzLines: {
|
||||
visible: false,
|
||||
},
|
||||
vertLines: {
|
||||
visible: false,
|
||||
},
|
||||
},
|
||||
crosshair: {
|
||||
horzLine: {
|
||||
visible: true,
|
||||
style: 3,
|
||||
width: 1,
|
||||
color: '#505050',
|
||||
labelBackgroundColor: color,
|
||||
},
|
||||
vertLine: {
|
||||
visible: true,
|
||||
style: 3,
|
||||
width: 1,
|
||||
color: '#505050',
|
||||
labelBackgroundColor: color,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const series = chart.addAreaSeries({
|
||||
lineColor: color,
|
||||
topColor: darken(0.4, color),
|
||||
bottomColor: theme.bg0,
|
||||
lineWidth: 2,
|
||||
priceLineVisible: false,
|
||||
})
|
||||
|
||||
series.setData(data)
|
||||
|
||||
// update the title when hovering on the chart
|
||||
chart.subscribeCrosshairMove(function (param) {
|
||||
if (
|
||||
chartRef?.current &&
|
||||
(param === undefined ||
|
||||
param.time === undefined ||
|
||||
(param && param.point && param.point.x < 0) ||
|
||||
(param && param.point && param.point.x > chartRef.current.clientWidth) ||
|
||||
(param && param.point && param.point.y < 0) ||
|
||||
(param && param.point && param.point.y > height))
|
||||
) {
|
||||
setValue && setValue(currenValue)
|
||||
} else {
|
||||
const price = parseFloat(param.seriesPrices.get(series)?.toString() ?? currenValue)
|
||||
setValue && setValue(price)
|
||||
}
|
||||
})
|
||||
chart.timeScale().fitContent()
|
||||
setChart(chart)
|
||||
}
|
||||
}, [color, chartCreated, currenValue, data, height, setValue, textColor, theme])
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<RowBetween>
|
||||
{topLeft ?? null}
|
||||
{topRight ?? null}
|
||||
</RowBetween>
|
||||
<div ref={chartRef} id={'line-chart'} {...rest} />
|
||||
<RowBetween>
|
||||
{bottomLeft ?? null}
|
||||
{bottomRight ?? null}
|
||||
</RowBetween>
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export default LineChart
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import styled from 'styled-components/macro'
|
||||
import useHttpLocations from '../../hooks/useHttpLocations'
|
||||
|
||||
import Logo from '../Logo'
|
||||
@@ -13,7 +13,7 @@ export default function ListLogo({
|
||||
logoURI,
|
||||
style,
|
||||
size = '24px',
|
||||
alt
|
||||
alt,
|
||||
}: {
|
||||
logoURI: string
|
||||
size?: string
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState } from 'react'
|
||||
import { HelpCircle } from 'react-feather'
|
||||
import { Slash } from 'react-feather'
|
||||
import { ImageProps } from 'rebass'
|
||||
import useTheme from '../../hooks/useTheme'
|
||||
|
||||
const BAD_SRCS: { [tokenAddress: string]: true } = {}
|
||||
|
||||
@@ -11,10 +12,12 @@ export interface LogoProps extends Pick<ImageProps, 'style' | 'alt' | 'className
|
||||
/**
|
||||
* Renders an image by sequentially trying a list of URIs, and then eventually a fallback triangle alert
|
||||
*/
|
||||
export default function Logo({ srcs, alt, ...rest }: LogoProps) {
|
||||
export default function Logo({ srcs, alt, style, ...rest }: LogoProps) {
|
||||
const [, refresh] = useState<number>(0)
|
||||
|
||||
const src: string | undefined = srcs.find(src => !BAD_SRCS[src])
|
||||
const theme = useTheme()
|
||||
|
||||
const src: string | undefined = srcs.find((src) => !BAD_SRCS[src])
|
||||
|
||||
if (src) {
|
||||
return (
|
||||
@@ -22,13 +25,14 @@ export default function Logo({ srcs, alt, ...rest }: LogoProps) {
|
||||
{...rest}
|
||||
alt={alt}
|
||||
src={src}
|
||||
style={style}
|
||||
onError={() => {
|
||||
if (src) BAD_SRCS[src] = true
|
||||
refresh(i => i + 1)
|
||||
refresh((i) => i + 1)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return <HelpCircle {...rest} />
|
||||
return <Slash {...rest} style={{ ...style, color: theme.bg4 }} />
|
||||
}
|
||||
|
||||
23
src/components/Menu/Menu.stories.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Story } from '@storybook/react/types-6-0'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import Component from './index'
|
||||
|
||||
const Wrapper = styled.div`
|
||||
max-width: 150px;
|
||||
`
|
||||
export default {
|
||||
title: 'Menu',
|
||||
decorators: [
|
||||
() => (
|
||||
<Wrapper>
|
||||
<Component />
|
||||
</Wrapper>
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
const Template: Story<any> = (args) => <Component {...args} />
|
||||
|
||||
export const Menu = Template.bind({})
|
||||
Menu.args = {}
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useRef } from 'react'
|
||||
import { BookOpen, Code, Info, MessageCircle, PieChart } from 'react-feather'
|
||||
import styled from 'styled-components'
|
||||
import { Link } from 'react-router-dom'
|
||||
import styled, { css } from 'styled-components'
|
||||
import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg'
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
|
||||
@@ -10,6 +11,11 @@ import { useModalOpen, useToggleModal } from '../../state/application/hooks'
|
||||
import { ExternalLink } from '../../theme'
|
||||
import { ButtonPrimary } from '../Button'
|
||||
|
||||
export enum FlyoutAlignment {
|
||||
LEFT = 'LEFT',
|
||||
RIGHT = 'RIGHT',
|
||||
}
|
||||
|
||||
const StyledMenuIcon = styled(MenuIcon)`
|
||||
path {
|
||||
stroke: ${({ theme }) => theme.text1};
|
||||
@@ -24,7 +30,7 @@ const StyledMenuButton = styled.button`
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 35px;
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
|
||||
padding: 0.15rem 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
@@ -33,7 +39,7 @@ const StyledMenuButton = styled.button`
|
||||
:focus {
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
background-color: ${({ theme }) => theme.bg4};
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
}
|
||||
|
||||
svg {
|
||||
@@ -41,6 +47,12 @@ const StyledMenuButton = styled.button`
|
||||
}
|
||||
`
|
||||
|
||||
const UNIbutton = styled(ButtonPrimary)`
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
background: radial-gradient(174.47% 188.91% at 1.84% 0%, #ff007a 0%, #2172e5 100%), #edeef2;
|
||||
border: none;
|
||||
`
|
||||
|
||||
const StyledMenu = styled.div`
|
||||
margin-left: 0.5rem;
|
||||
display: flex;
|
||||
@@ -51,9 +63,9 @@ const StyledMenu = styled.div`
|
||||
text-align: left;
|
||||
`
|
||||
|
||||
const MenuFlyout = styled.span`
|
||||
const MenuFlyout = styled.span<{ flyoutAlignment?: FlyoutAlignment }>`
|
||||
min-width: 8.125rem;
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
|
||||
0px 24px 32px rgba(0, 0, 0, 0.01);
|
||||
border-radius: 12px;
|
||||
@@ -62,16 +74,39 @@ const MenuFlyout = styled.span`
|
||||
flex-direction: column;
|
||||
font-size: 1rem;
|
||||
position: absolute;
|
||||
top: 4rem;
|
||||
right: 0rem;
|
||||
top: 3rem;
|
||||
z-index: 100;
|
||||
|
||||
${({ flyoutAlignment = FlyoutAlignment.RIGHT }) =>
|
||||
flyoutAlignment === FlyoutAlignment.RIGHT
|
||||
? css`
|
||||
right: 0rem;
|
||||
`
|
||||
: css`
|
||||
left: 0rem;
|
||||
`};
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
top: -17.25rem;
|
||||
`};
|
||||
`
|
||||
|
||||
const MenuItem = styled(ExternalLink)`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.5rem;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.text1};
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
> svg {
|
||||
margin-right: 8px;
|
||||
}
|
||||
`
|
||||
|
||||
const InternalMenuItem = styled(Link)`
|
||||
flex: 1;
|
||||
padding: 0.5rem 0.5rem;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
@@ -105,33 +140,84 @@ export default function Menu() {
|
||||
|
||||
{open && (
|
||||
<MenuFlyout>
|
||||
<MenuItem id="link" href="https://uniswap.org/">
|
||||
<MenuItem href="https://uniswap.org/">
|
||||
<Info size={14} />
|
||||
About
|
||||
<div>About</div>
|
||||
</MenuItem>
|
||||
<MenuItem id="link" href="https://uniswap.org/docs/v2">
|
||||
<MenuItem href="https://docs.uniswap.org/">
|
||||
<BookOpen size={14} />
|
||||
Docs
|
||||
<div>Docs</div>
|
||||
</MenuItem>
|
||||
<MenuItem id="link" href={CODE_LINK}>
|
||||
<MenuItem href={CODE_LINK}>
|
||||
<Code size={14} />
|
||||
Code
|
||||
<div>Code</div>
|
||||
</MenuItem>
|
||||
<MenuItem id="link" href="https://discord.gg/EwFs3Pp">
|
||||
<MenuItem href="https://discord.gg/FCfyBSbCU5">
|
||||
<MessageCircle size={14} />
|
||||
Discord
|
||||
<div>Discord</div>
|
||||
</MenuItem>
|
||||
<MenuItem id="link" href="https://uniswap.info/">
|
||||
<MenuItem href="https://info.uniswap.org/">
|
||||
<PieChart size={14} />
|
||||
Analytics
|
||||
<div>Analytics</div>
|
||||
</MenuItem>
|
||||
{account && (
|
||||
<ButtonPrimary onClick={openClaimModal} padding="8px 16px" width="100%" borderRadius="12px" mt="0.5rem">
|
||||
<UNIbutton onClick={openClaimModal} padding="8px 16px" width="100%" borderRadius="12px" mt="0.5rem">
|
||||
Claim UNI
|
||||
</ButtonPrimary>
|
||||
</UNIbutton>
|
||||
)}
|
||||
</MenuFlyout>
|
||||
)}
|
||||
</StyledMenu>
|
||||
)
|
||||
}
|
||||
|
||||
interface NewMenuProps {
|
||||
flyoutAlignment?: FlyoutAlignment
|
||||
ToggleUI?: React.FunctionComponent
|
||||
menuItems: {
|
||||
content: any
|
||||
link: string
|
||||
external: boolean
|
||||
}[]
|
||||
}
|
||||
|
||||
const NewMenuFlyout = styled(MenuFlyout)`
|
||||
top: 3rem !important;
|
||||
`
|
||||
const NewMenuItem = styled(InternalMenuItem)`
|
||||
width: max-content;
|
||||
text-decoration: none;
|
||||
`
|
||||
|
||||
const ExternalMenuItem = styled(MenuItem)`
|
||||
width: max-content;
|
||||
text-decoration: none;
|
||||
`
|
||||
|
||||
export const NewMenu = ({ flyoutAlignment = FlyoutAlignment.RIGHT, ToggleUI, menuItems, ...rest }: NewMenuProps) => {
|
||||
const node = useRef<HTMLDivElement>()
|
||||
const open = useModalOpen(ApplicationModal.POOL_OVERVIEW_OPTIONS)
|
||||
const toggle = useToggleModal(ApplicationModal.POOL_OVERVIEW_OPTIONS)
|
||||
useOnClickOutside(node, open ? toggle : undefined)
|
||||
const ToggleElement = ToggleUI || StyledMenuIcon
|
||||
return (
|
||||
<StyledMenu ref={node as any} {...rest}>
|
||||
<ToggleElement onClick={toggle} />
|
||||
{open && (
|
||||
<NewMenuFlyout flyoutAlignment={flyoutAlignment}>
|
||||
{menuItems.map(({ content, link, external }, i) =>
|
||||
external ? (
|
||||
<ExternalMenuItem id="link" href={link} key={link + i}>
|
||||
{content}
|
||||
</ExternalMenuItem>
|
||||
) : (
|
||||
<NewMenuItem id="link" to={link} key={link + i}>
|
||||
{content}
|
||||
</NewMenuItem>
|
||||
)
|
||||
)}
|
||||
</NewMenuFlyout>
|
||||
)}
|
||||
</StyledMenu>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -29,13 +29,14 @@ const AnimatedDialogContent = animated(DialogContent)
|
||||
const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...rest }) => (
|
||||
<AnimatedDialogContent {...rest} />
|
||||
)).attrs({
|
||||
'aria-label': 'dialog'
|
||||
'aria-label': 'dialog',
|
||||
})`
|
||||
overflow-y: ${({ mobile }) => (mobile ? 'scroll' : 'hidden')};
|
||||
|
||||
&[data-reach-dialog-content] {
|
||||
margin: 0 0 2rem 0;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
border: 1px solid ${({ theme }) => theme.bg1};
|
||||
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.shadow1)};
|
||||
padding: 0px;
|
||||
width: 50vw;
|
||||
@@ -63,13 +64,15 @@ const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...r
|
||||
`}
|
||||
${({ theme, mobile }) => theme.mediaWidth.upToSmall`
|
||||
width: 85vw;
|
||||
${mobile &&
|
||||
${
|
||||
mobile &&
|
||||
css`
|
||||
width: 100vw;
|
||||
border-radius: 20px;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
`}
|
||||
`
|
||||
}
|
||||
`}
|
||||
}
|
||||
`
|
||||
@@ -89,25 +92,25 @@ export default function Modal({
|
||||
minHeight = false,
|
||||
maxHeight = 90,
|
||||
initialFocusRef,
|
||||
children
|
||||
children,
|
||||
}: ModalProps) {
|
||||
const fadeTransition = useTransition(isOpen, null, {
|
||||
config: { duration: 200 },
|
||||
from: { opacity: 0 },
|
||||
enter: { opacity: 1 },
|
||||
leave: { opacity: 0 }
|
||||
leave: { opacity: 0 },
|
||||
})
|
||||
|
||||
const [{ y }, set] = useSpring(() => ({ y: 0, config: { mass: 1, tension: 210, friction: 20 } }))
|
||||
const bind = useGesture({
|
||||
onDrag: state => {
|
||||
onDrag: (state) => {
|
||||
set({
|
||||
y: state.down ? state.movement[1] : 0
|
||||
y: state.down ? state.movement[1] : 0,
|
||||
})
|
||||
if (state.movement[1] > 300 || (state.velocity > 3 && state.direction[1] > 0)) {
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -115,12 +118,18 @@ export default function Modal({
|
||||
{fadeTransition.map(
|
||||
({ item, key, props }) =>
|
||||
item && (
|
||||
<StyledDialogOverlay key={key} style={props} onDismiss={onDismiss} initialFocusRef={initialFocusRef}>
|
||||
<StyledDialogOverlay
|
||||
key={key}
|
||||
style={props}
|
||||
onDismiss={onDismiss}
|
||||
initialFocusRef={initialFocusRef}
|
||||
unstable_lockFocusAcrossFrames={false}
|
||||
>
|
||||
<StyledDialogContent
|
||||
{...(isMobile
|
||||
? {
|
||||
...bind(),
|
||||
style: { transform: y.interpolate(y => `translateY(${y > 0 ? y : 0}px)`) }
|
||||
style: { transform: y.interpolate((y) => `translateY(${(y as number) > 0 ? y : 0}px)`) },
|
||||
}
|
||||
: {})}
|
||||
aria-label="dialog content"
|
||||
|
||||
@@ -41,7 +41,7 @@ export function LoadingView({ children, onDismiss }: { children: any; onDismiss:
|
||||
export function SubmittedView({
|
||||
children,
|
||||
onDismiss,
|
||||
hash
|
||||
hash,
|
||||
}: {
|
||||
children: any
|
||||
onDismiss: () => void
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import styled from 'styled-components/macro'
|
||||
import { darken } from 'polished'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { NavLink, Link as HistoryLink } from 'react-router-dom'
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
|
||||
import { ArrowLeft } from 'react-feather'
|
||||
import { RowBetween } from '../Row'
|
||||
import QuestionHelper from '../QuestionHelper'
|
||||
import SettingsTab from '../Settings'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { AppDispatch } from 'state'
|
||||
import { resetMintState } from 'state/mint/actions'
|
||||
import { resetMintState as resetMintV3State } from 'state/mint/v3/actions'
|
||||
import { TYPE } from 'theme'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
|
||||
const Tabs = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
@@ -18,7 +25,7 @@ const Tabs = styled.div`
|
||||
const activeClassName = 'ACTIVE'
|
||||
|
||||
const StyledNavLink = styled(NavLink).attrs({
|
||||
activeClassName
|
||||
activeClassName,
|
||||
})`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
@@ -55,7 +62,7 @@ const StyledArrowLeft = styled(ArrowLeft)`
|
||||
export function SwapPoolTabs({ active }: { active: 'swap' | 'pool' }) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<Tabs style={{ marginBottom: '20px', display: 'none' }}>
|
||||
<Tabs style={{ marginBottom: '20px', display: 'none', padding: '1rem 1rem 0 1rem' }}>
|
||||
<StyledNavLink id={`swap-nav-link`} to={'/swap'} isActive={() => active === 'swap'}>
|
||||
{t('swap')}
|
||||
</StyledNavLink>
|
||||
@@ -66,35 +73,54 @@ export function SwapPoolTabs({ active }: { active: 'swap' | 'pool' }) {
|
||||
)
|
||||
}
|
||||
|
||||
export function FindPoolTabs() {
|
||||
export function FindPoolTabs({ origin }: { origin: string }) {
|
||||
return (
|
||||
<Tabs>
|
||||
<RowBetween style={{ padding: '1rem' }}>
|
||||
<HistoryLink to="/pool">
|
||||
<RowBetween style={{ padding: '1rem 1rem 0 1rem' }}>
|
||||
<HistoryLink to={origin}>
|
||||
<StyledArrowLeft />
|
||||
</HistoryLink>
|
||||
<ActiveText>Import Pool</ActiveText>
|
||||
<QuestionHelper text={"Use this tool to find pairs that don't automatically appear in the interface."} />
|
||||
</RowBetween>
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
|
||||
export function AddRemoveTabs({ adding, creating }: { adding: boolean; creating: boolean }) {
|
||||
export function AddRemoveTabs({
|
||||
adding,
|
||||
creating,
|
||||
positionID,
|
||||
defaultSlippage,
|
||||
}: {
|
||||
adding: boolean
|
||||
creating: boolean
|
||||
positionID?: string | undefined
|
||||
defaultSlippage: Percent
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
|
||||
// reset states on back
|
||||
const dispatch = useDispatch<AppDispatch>()
|
||||
|
||||
return (
|
||||
<Tabs>
|
||||
<RowBetween style={{ padding: '1rem' }}>
|
||||
<HistoryLink to="/pool">
|
||||
<StyledArrowLeft />
|
||||
<RowBetween style={{ padding: '1rem 1rem 0 1rem' }}>
|
||||
<HistoryLink
|
||||
to={'/pool' + (!!positionID ? `/${positionID.toString()}` : '')}
|
||||
onClick={() => {
|
||||
if (adding) {
|
||||
// not 100% sure both of these are needed
|
||||
dispatch(resetMintState())
|
||||
dispatch(resetMintV3State())
|
||||
}
|
||||
}}
|
||||
>
|
||||
<StyledArrowLeft stroke={theme.text2} />
|
||||
</HistoryLink>
|
||||
<ActiveText>{creating ? 'Create a pair' : adding ? 'Add Liquidity' : 'Remove Liquidity'}</ActiveText>
|
||||
<QuestionHelper
|
||||
text={
|
||||
adding
|
||||
? 'When you add liquidity, you are given pool tokens representing your position. These tokens automatically earn fees proportional to your share of the pool, and can be redeemed at any time.'
|
||||
: 'Removing pool tokens converts your position back into underlying tokens at the current rate, proportional to your share of the pool. Accrued fees are included in the amounts you receive.'
|
||||
}
|
||||
/>
|
||||
<TYPE.mediumHeader fontWeight={500} fontSize={20}>
|
||||
{creating ? 'Create a pair' : adding ? 'Add Liquidity' : 'Remove Liquidity'}
|
||||
</TYPE.mediumHeader>
|
||||
<SettingsTab placeholderSlippage={defaultSlippage} />
|
||||
</RowBetween>
|
||||
</Tabs>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import styled from 'styled-components/macro'
|
||||
import { escapeRegExp } from '../../utils'
|
||||
|
||||
const StyledInput = styled.input<{ error?: boolean; fontSize?: string; align?: string }>`
|
||||
@@ -18,6 +18,7 @@ const StyledInput = styled.input<{ error?: boolean; fontSize?: string; align?: s
|
||||
text-overflow: ellipsis;
|
||||
padding: 0px;
|
||||
-webkit-appearance: textfield;
|
||||
text-align: right;
|
||||
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
@@ -43,6 +44,7 @@ export const Input = React.memo(function InnerInput({
|
||||
value,
|
||||
onUserInput,
|
||||
placeholder,
|
||||
prependSymbol,
|
||||
...rest
|
||||
}: {
|
||||
value: string | number
|
||||
@@ -50,6 +52,7 @@ export const Input = React.memo(function InnerInput({
|
||||
error?: boolean
|
||||
fontSize?: string
|
||||
align?: 'right' | 'left'
|
||||
prependSymbol?: string | undefined
|
||||
} & Omit<React.HTMLProps<HTMLInputElement>, 'ref' | 'onChange' | 'as'>) {
|
||||
const enforcer = (nextUserInput: string) => {
|
||||
if (nextUserInput === '' || inputRegex.test(escapeRegExp(nextUserInput))) {
|
||||
@@ -60,14 +63,24 @@ export const Input = React.memo(function InnerInput({
|
||||
return (
|
||||
<StyledInput
|
||||
{...rest}
|
||||
value={value}
|
||||
onChange={event => {
|
||||
// replace commas with periods, because uniswap exclusively uses period as the decimal separator
|
||||
enforcer(event.target.value.replace(/,/g, '.'))
|
||||
value={prependSymbol && value ? prependSymbol + value : value}
|
||||
onChange={(event) => {
|
||||
if (prependSymbol) {
|
||||
const value = event.target.value
|
||||
|
||||
// cut off prepended symbol
|
||||
const formattedValue = value.toString().includes(prependSymbol)
|
||||
? value.toString().slice(1, value.toString().length + 1)
|
||||
: value
|
||||
|
||||
// replace commas with periods, because uniswap exclusively uses period as the decimal separator
|
||||
enforcer(formattedValue.replace(/,/g, '.'))
|
||||
} else {
|
||||
enforcer(event.target.value.replace(/,/g, '.'))
|
||||
}
|
||||
}}
|
||||
// universal input options
|
||||
inputMode="decimal"
|
||||
title="Token Amount"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
// text-specific options
|
||||
|
||||
@@ -2,17 +2,15 @@ import { Placement } from '@popperjs/core'
|
||||
import { transparentize } from 'polished'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { usePopper } from 'react-popper'
|
||||
import styled from 'styled-components'
|
||||
import styled from 'styled-components/macro'
|
||||
import useInterval from '../../hooks/useInterval'
|
||||
import Portal from '@reach/portal'
|
||||
|
||||
const PopoverContainer = styled.div<{ show: boolean }>`
|
||||
z-index: 9999;
|
||||
|
||||
visibility: ${props => (props.show ? 'visible' : 'hidden')};
|
||||
opacity: ${props => (props.show ? 1 : 0)};
|
||||
visibility: ${(props) => (props.show ? 'visible' : 'hidden')};
|
||||
opacity: ${(props) => (props.show ? 1 : 0)};
|
||||
transition: visibility 150ms linear, opacity 150ms linear;
|
||||
|
||||
background: ${({ theme }) => theme.bg2};
|
||||
border: 1px solid ${({ theme }) => theme.bg3};
|
||||
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.9, theme.shadow1)};
|
||||
@@ -91,8 +89,8 @@ export default function Popover({ content, show, children, placement = 'auto' }:
|
||||
strategy: 'fixed',
|
||||
modifiers: [
|
||||
{ name: 'offset', options: { offset: [8, 8] } },
|
||||
{ name: 'arrow', options: { element: arrowElement } }
|
||||
]
|
||||
{ name: 'arrow', options: { element: arrowElement } },
|
||||
],
|
||||
})
|
||||
const updateCallback = useCallback(() => {
|
||||
update && update()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TokenAmount } from '@uniswap/sdk'
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import React, { useEffect } from 'react'
|
||||
import { X } from 'react-feather'
|
||||
import styled, { keyframes } from 'styled-components'
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
useModalOpen,
|
||||
useShowClaimPopup,
|
||||
useToggleSelfClaimModal,
|
||||
useToggleShowClaimPopup
|
||||
useToggleShowClaimPopup,
|
||||
} from '../../state/application/hooks'
|
||||
|
||||
import { useUserHasAvailableClaim, useUserUnclaimedAmount } from '../../state/claim/hooks'
|
||||
@@ -65,7 +65,7 @@ export default function ClaimPopup() {
|
||||
|
||||
// const userHasAvailableclaim = useUserHasAvailableClaim()
|
||||
const userHasAvailableclaim: boolean = useUserHasAvailableClaim(account)
|
||||
const unclaimedAmount: TokenAmount | undefined = useUserUnclaimedAmount(account)
|
||||
const unclaimedAmount: CurrencyAmount<Token> | undefined = useUserUnclaimedAmount(account)
|
||||
|
||||
// listen for available claim and show popup if needed
|
||||
useEffect(() => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import React, { useCallback, useMemo } from 'react'
|
||||
import ReactGA from 'react-ga'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { Text } from 'rebass'
|
||||
import styled from 'styled-components/macro'
|
||||
import { AppDispatch } from '../../state'
|
||||
import { useRemovePopup } from '../../state/application/hooks'
|
||||
import { acceptListUpdate } from '../../state/lists/actions'
|
||||
@@ -12,12 +13,17 @@ import { ButtonSecondary } from '../Button'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { AutoRow } from '../Row'
|
||||
|
||||
export const ChangesList = styled.ul`
|
||||
max-height: 400px;
|
||||
overflow: auto;
|
||||
`
|
||||
|
||||
export default function ListUpdatePopup({
|
||||
popKey,
|
||||
listUrl,
|
||||
oldList,
|
||||
newList,
|
||||
auto
|
||||
auto,
|
||||
}: {
|
||||
popKey: string
|
||||
listUrl: string
|
||||
@@ -34,7 +40,7 @@ export default function ListUpdatePopup({
|
||||
ReactGA.event({
|
||||
category: 'Lists',
|
||||
action: 'Update List from Popup',
|
||||
label: listUrl
|
||||
label: listUrl,
|
||||
})
|
||||
dispatch(acceptListUpdate(listUrl))
|
||||
removeThisPopup()
|
||||
@@ -64,7 +70,7 @@ export default function ListUpdatePopup({
|
||||
An update is available for the token list "{oldList.name}" (
|
||||
{listVersionLabel(oldList.version)} to {listVersionLabel(newList.version)}).
|
||||
</Text>
|
||||
<ul>
|
||||
<ChangesList>
|
||||
{tokensAdded.length > 0 ? (
|
||||
<li>
|
||||
{tokensAdded.map((token, i) => (
|
||||
@@ -88,7 +94,7 @@ export default function ListUpdatePopup({
|
||||
</li>
|
||||
) : null}
|
||||
{numTokensChanged > 0 ? <li>{numTokensChanged} tokens updated</li> : null}
|
||||
</ul>
|
||||
</ChangesList>
|
||||
</div>
|
||||
<AutoRow>
|
||||
<div style={{ flexGrow: 1, marginRight: 12 }}>
|
||||
|
||||
@@ -21,7 +21,7 @@ export const Popup = styled.div`
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
padding: 1em;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
@@ -49,7 +49,7 @@ const AnimatedFader = animated(Fader)
|
||||
export default function PopupItem({
|
||||
removeAfterMs,
|
||||
content,
|
||||
popKey
|
||||
popKey,
|
||||
}: {
|
||||
removeAfterMs: number | null
|
||||
content: PopupContent
|
||||
@@ -74,12 +74,12 @@ export default function PopupItem({
|
||||
let popupContent
|
||||
if ('txn' in content) {
|
||||
const {
|
||||
txn: { hash, success, summary }
|
||||
txn: { hash, success, summary },
|
||||
} = content
|
||||
popupContent = <TransactionPopup hash={hash} success={success} summary={summary} />
|
||||
} else if ('listUpdate' in content) {
|
||||
const {
|
||||
listUpdate: { listUrl, oldList, newList, auto }
|
||||
listUpdate: { listUrl, oldList, newList, auto },
|
||||
} = content
|
||||
popupContent = <ListUpdatePopup popKey={popKey} listUrl={listUrl} oldList={oldList} newList={newList} auto={auto} />
|
||||
}
|
||||
@@ -87,7 +87,7 @@ export default function PopupItem({
|
||||
const faderStyle = useSpring({
|
||||
from: { width: '100%' },
|
||||
to: { width: '0%' },
|
||||
config: { duration: removeAfterMs ?? undefined }
|
||||
config: { duration: removeAfterMs ?? undefined },
|
||||
})
|
||||
|
||||
return (
|
||||
|
||||
@@ -15,7 +15,7 @@ const RowNoFlex = styled(AutoRow)`
|
||||
export default function TransactionPopup({
|
||||
hash,
|
||||
success,
|
||||
summary
|
||||
summary,
|
||||
}: {
|
||||
hash: string
|
||||
success?: boolean
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import styled from 'styled-components/macro'
|
||||
import { useActivePopups } from '../../state/application/hooks'
|
||||
import { AutoColumn } from '../Column'
|
||||
import PopupItem from './PopupItem'
|
||||
@@ -33,7 +33,7 @@ const MobilePopupInner = styled.div`
|
||||
|
||||
const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean }>`
|
||||
position: fixed;
|
||||
top: ${({ extraPadding }) => (extraPadding ? '108px' : '88px')};
|
||||
top: ${({ extraPadding }) => (extraPadding ? '72px' : '88px')};
|
||||
right: 1rem;
|
||||
max-width: 355px !important;
|
||||
width: 100%;
|
||||
@@ -54,7 +54,7 @@ export default function Popups() {
|
||||
<>
|
||||
<FixedPopupColumn gap="20px" extraPadding={urlWarningActive}>
|
||||
<ClaimPopup />
|
||||
{activePopups.map(item => (
|
||||
{activePopups.map((item) => (
|
||||
<PopupItem key={item.key} content={item.content} popKey={item.key} removeAfterMs={item.removeAfterMs} />
|
||||
))}
|
||||
</FixedPopupColumn>
|
||||
@@ -63,7 +63,7 @@ export default function Popups() {
|
||||
{activePopups // reverse so new items up front
|
||||
.slice(0)
|
||||
.reverse()
|
||||
.map(item => (
|
||||
.map((item) => (
|
||||
<PopupItem key={item.key} content={item.content} popKey={item.key} removeAfterMs={item.removeAfterMs} />
|
||||
))}
|
||||
</MobilePopupInner>
|
||||
|
||||
71
src/components/PositionCard/Sushi.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import React from 'react'
|
||||
import { Token } from '@uniswap/sdk-core'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Text } from 'rebass'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { unwrappedToken } from '../../utils/wrappedCurrency'
|
||||
import { ButtonEmpty } from '../Button'
|
||||
import { transparentize } from 'polished'
|
||||
import { CardNoise } from '../earn/styled'
|
||||
|
||||
import { useColor } from '../../hooks/useColor'
|
||||
|
||||
import { LightCard } from '../Card'
|
||||
import { AutoColumn } from '../Column'
|
||||
import DoubleCurrencyLogo from '../DoubleLogo'
|
||||
import { RowFixed, AutoRow } from '../Row'
|
||||
import { Dots } from '../swap/styleds'
|
||||
import { FixedHeightRow } from '.'
|
||||
import Badge, { BadgeVariant } from 'components/Badge'
|
||||
|
||||
const StyledPositionCard = styled(LightCard)<{ bgColor: any }>`
|
||||
border: none;
|
||||
background: ${({ theme, bgColor }) =>
|
||||
`radial-gradient(91.85% 100% at 1.84% 0%, ${transparentize(0.8, bgColor)} 0%, ${theme.bg3} 100%) `};
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
`
|
||||
|
||||
interface PositionCardProps {
|
||||
tokenA: Token
|
||||
tokenB: Token
|
||||
liquidityToken: Token
|
||||
border?: string
|
||||
}
|
||||
|
||||
export default function SushiPositionCard({ tokenA, tokenB, liquidityToken, border }: PositionCardProps) {
|
||||
const currency0 = unwrappedToken(tokenA)
|
||||
const currency1 = unwrappedToken(tokenB)
|
||||
|
||||
const backgroundColor = useColor(tokenA)
|
||||
|
||||
return (
|
||||
<StyledPositionCard border={border} bgColor={backgroundColor}>
|
||||
<CardNoise />
|
||||
<AutoColumn gap="12px">
|
||||
<FixedHeightRow>
|
||||
<AutoRow gap="8px">
|
||||
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} size={20} />
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
{!currency0 || !currency1 ? <Dots>Loading</Dots> : `${currency0.symbol}/${currency1.symbol}`}
|
||||
</Text>
|
||||
|
||||
<Badge variant={BadgeVariant.WARNING}>Sushi</Badge>
|
||||
</AutoRow>
|
||||
<RowFixed gap="8px">
|
||||
<ButtonEmpty
|
||||
padding="0px 35px 0px 0px"
|
||||
borderRadius="12px"
|
||||
width="fit-content"
|
||||
as={Link}
|
||||
to={`/migrate/v2/${liquidityToken.address}`}
|
||||
>
|
||||
Migrate
|
||||
</ButtonEmpty>
|
||||
</RowFixed>
|
||||
</FixedHeightRow>
|
||||
</AutoColumn>
|
||||
</StyledPositionCard>
|
||||
)
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import React, { useContext } from 'react'
|
||||
import { Link, RouteComponentProps, withRouter } from 'react-router-dom'
|
||||
import { Token, TokenAmount, WETH } from '@uniswap/sdk'
|
||||
|
||||
import { Text } from 'rebass'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { ButtonSecondary } from '../Button'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import { FixedHeightRow, HoverCard } from './index'
|
||||
import DoubleCurrencyLogo from '../DoubleLogo'
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { ThemeContext } from 'styled-components'
|
||||
|
||||
interface PositionCardProps extends RouteComponentProps<{}> {
|
||||
token: Token
|
||||
V1LiquidityBalance: TokenAmount
|
||||
}
|
||||
|
||||
function V1PositionCard({ token, V1LiquidityBalance }: PositionCardProps) {
|
||||
const theme = useContext(ThemeContext)
|
||||
|
||||
const { chainId } = useActiveWeb3React()
|
||||
|
||||
return (
|
||||
<HoverCard>
|
||||
<AutoColumn gap="12px">
|
||||
<FixedHeightRow>
|
||||
<RowFixed>
|
||||
<DoubleCurrencyLogo currency0={token} margin={true} size={20} />
|
||||
<Text fontWeight={500} fontSize={20} style={{ marginLeft: '' }}>
|
||||
{`${chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol}/ETH`}
|
||||
</Text>
|
||||
<Text
|
||||
fontSize={12}
|
||||
fontWeight={500}
|
||||
ml="0.5rem"
|
||||
px="0.75rem"
|
||||
py="0.25rem"
|
||||
style={{ borderRadius: '1rem' }}
|
||||
backgroundColor={theme.yellow1}
|
||||
color={'black'}
|
||||
>
|
||||
V1
|
||||
</Text>
|
||||
</RowFixed>
|
||||
</FixedHeightRow>
|
||||
|
||||
<AutoColumn gap="8px">
|
||||
<RowBetween marginTop="10px">
|
||||
<ButtonSecondary width="68%" as={Link} to={`/migrate/v1/${V1LiquidityBalance.token.address}`}>
|
||||
Migrate
|
||||
</ButtonSecondary>
|
||||
|
||||
<ButtonSecondary
|
||||
style={{ backgroundColor: 'transparent' }}
|
||||
width="28%"
|
||||
as={Link}
|
||||
to={`/remove/v1/${V1LiquidityBalance.token.address}`}
|
||||
>
|
||||
Remove
|
||||
</ButtonSecondary>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
</AutoColumn>
|
||||
</HoverCard>
|
||||
)
|
||||
}
|
||||
|
||||
export default withRouter(V1PositionCard)
|
||||
207
src/components/PositionCard/V2.tsx
Normal file
@@ -0,0 +1,207 @@
|
||||
import JSBI from 'jsbi'
|
||||
import React, { useState } from 'react'
|
||||
import { Percent, CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { Pair } from '@uniswap/v2-sdk'
|
||||
import { ChevronDown, ChevronUp } from 'react-feather'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Text } from 'rebass'
|
||||
import styled from 'styled-components/macro'
|
||||
import { useTotalSupply } from '../../hooks/useTotalSupply'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { useTokenBalance } from '../../state/wallet/hooks'
|
||||
import { currencyId } from '../../utils/currencyId'
|
||||
import { unwrappedToken } from '../../utils/wrappedCurrency'
|
||||
import { ButtonPrimary, ButtonSecondary, ButtonEmpty } from '../Button'
|
||||
import { transparentize } from 'polished'
|
||||
import { CardNoise } from '../earn/styled'
|
||||
|
||||
import { useColor } from '../../hooks/useColor'
|
||||
|
||||
import { LightCard } from '../Card'
|
||||
import { AutoColumn } from '../Column'
|
||||
import CurrencyLogo from '../CurrencyLogo'
|
||||
import DoubleCurrencyLogo from '../DoubleLogo'
|
||||
import { RowBetween, RowFixed, AutoRow } from '../Row'
|
||||
import { Dots } from '../swap/styleds'
|
||||
import { BIG_INT_ZERO } from '../../constants'
|
||||
import { FixedHeightRow } from '.'
|
||||
|
||||
const StyledPositionCard = styled(LightCard)<{ bgColor: any }>`
|
||||
border: none;
|
||||
background: ${({ theme, bgColor }) =>
|
||||
`radial-gradient(91.85% 100% at 1.84% 0%, ${transparentize(0.8, bgColor)} 0%, ${theme.bg3} 100%) `};
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
`
|
||||
|
||||
interface PositionCardProps {
|
||||
pair: Pair
|
||||
showUnwrapped?: boolean
|
||||
border?: string
|
||||
stakedBalance?: CurrencyAmount<Token> // optional balance to indicate that liquidity is deposited in mining pool
|
||||
}
|
||||
|
||||
export default function V2PositionCard({ pair, border, stakedBalance }: PositionCardProps) {
|
||||
const { account } = useActiveWeb3React()
|
||||
|
||||
const currency0 = unwrappedToken(pair.token0)
|
||||
const currency1 = unwrappedToken(pair.token1)
|
||||
|
||||
const [showMore, setShowMore] = useState(false)
|
||||
|
||||
const userDefaultPoolBalance = useTokenBalance(account ?? undefined, pair.liquidityToken)
|
||||
const totalPoolTokens = useTotalSupply(pair.liquidityToken)
|
||||
|
||||
// if staked balance balance provided, add to standard liquidity amount
|
||||
const userPoolBalance = stakedBalance ? userDefaultPoolBalance?.add(stakedBalance) : userDefaultPoolBalance
|
||||
|
||||
const poolTokenPercentage =
|
||||
!!userPoolBalance &&
|
||||
!!totalPoolTokens &&
|
||||
JSBI.greaterThanOrEqual(totalPoolTokens.quotient, userPoolBalance.quotient)
|
||||
? new Percent(userPoolBalance.quotient, totalPoolTokens.quotient)
|
||||
: undefined
|
||||
|
||||
const [token0Deposited, token1Deposited] =
|
||||
!!pair &&
|
||||
!!totalPoolTokens &&
|
||||
!!userPoolBalance &&
|
||||
// this condition is a short-circuit in the case where useTokenBalance updates sooner than useTotalSupply
|
||||
JSBI.greaterThanOrEqual(totalPoolTokens.quotient, userPoolBalance.quotient)
|
||||
? [
|
||||
pair.getLiquidityValue(pair.token0, totalPoolTokens, userPoolBalance, false),
|
||||
pair.getLiquidityValue(pair.token1, totalPoolTokens, userPoolBalance, false),
|
||||
]
|
||||
: [undefined, undefined]
|
||||
|
||||
const backgroundColor = useColor(pair?.token0)
|
||||
|
||||
return (
|
||||
<StyledPositionCard border={border} bgColor={backgroundColor}>
|
||||
<CardNoise />
|
||||
<AutoColumn gap="12px">
|
||||
<FixedHeightRow>
|
||||
<AutoRow gap="8px">
|
||||
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} size={20} />
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
{!currency0 || !currency1 ? <Dots>Loading</Dots> : `${currency0.symbol}/${currency1.symbol}`}
|
||||
</Text>
|
||||
</AutoRow>
|
||||
<RowFixed gap="8px">
|
||||
<ButtonEmpty
|
||||
padding="6px 8px"
|
||||
borderRadius="12px"
|
||||
width="fit-content"
|
||||
onClick={() => setShowMore(!showMore)}
|
||||
>
|
||||
{showMore ? (
|
||||
<>
|
||||
Manage
|
||||
<ChevronUp size="20" style={{ marginLeft: '10px' }} />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Manage
|
||||
<ChevronDown size="20" style={{ marginLeft: '10px' }} />
|
||||
</>
|
||||
)}
|
||||
</ButtonEmpty>
|
||||
</RowFixed>
|
||||
</FixedHeightRow>
|
||||
|
||||
{showMore && (
|
||||
<AutoColumn gap="8px">
|
||||
<FixedHeightRow>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
Your total pool tokens:
|
||||
</Text>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
{userPoolBalance ? userPoolBalance.toSignificant(4) : '-'}
|
||||
</Text>
|
||||
</FixedHeightRow>
|
||||
{stakedBalance && (
|
||||
<FixedHeightRow>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
Pool tokens in rewards pool:
|
||||
</Text>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
{stakedBalance.toSignificant(4)}
|
||||
</Text>
|
||||
</FixedHeightRow>
|
||||
)}
|
||||
<FixedHeightRow>
|
||||
<RowFixed>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
Pooled {currency0.symbol}:
|
||||
</Text>
|
||||
</RowFixed>
|
||||
{token0Deposited ? (
|
||||
<RowFixed>
|
||||
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
{token0Deposited?.toSignificant(6)}
|
||||
</Text>
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={currency0} />
|
||||
</RowFixed>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</FixedHeightRow>
|
||||
|
||||
<FixedHeightRow>
|
||||
<RowFixed>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
Pooled {currency1.symbol}:
|
||||
</Text>
|
||||
</RowFixed>
|
||||
{token1Deposited ? (
|
||||
<RowFixed>
|
||||
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
|
||||
{token1Deposited?.toSignificant(6)}
|
||||
</Text>
|
||||
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={currency1} />
|
||||
</RowFixed>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</FixedHeightRow>
|
||||
|
||||
<FixedHeightRow>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
Your pool share:
|
||||
</Text>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
{poolTokenPercentage
|
||||
? (poolTokenPercentage.toFixed(2) === '0.00' ? '<0.01' : poolTokenPercentage.toFixed(2)) + '%'
|
||||
: '-'}
|
||||
</Text>
|
||||
</FixedHeightRow>
|
||||
|
||||
{userDefaultPoolBalance && JSBI.greaterThan(userDefaultPoolBalance.quotient, BIG_INT_ZERO) && (
|
||||
<RowBetween marginTop="10px">
|
||||
<ButtonPrimary
|
||||
padding="8px"
|
||||
borderRadius="8px"
|
||||
as={Link}
|
||||
to={`/migrate/v2/${pair.liquidityToken.address}`}
|
||||
width="64%"
|
||||
>
|
||||
Migrate
|
||||
</ButtonPrimary>
|
||||
<ButtonSecondary
|
||||
padding="8px"
|
||||
borderRadius="8px"
|
||||
as={Link}
|
||||
width="32%"
|
||||
to={`/remove/v2/${currencyId(currency0)}/${currencyId(currency1)}`}
|
||||
>
|
||||
Remove
|
||||
</ButtonSecondary>
|
||||
</RowBetween>
|
||||
)}
|
||||
</AutoColumn>
|
||||
)}
|
||||
</AutoColumn>
|
||||
</StyledPositionCard>
|
||||
)
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
import { JSBI, Pair, Percent } from '@uniswap/sdk'
|
||||
import JSBI from 'jsbi'
|
||||
import { Percent, CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { Pair } from '@uniswap/v2-sdk'
|
||||
import { darken } from 'polished'
|
||||
import React, { useState } from 'react'
|
||||
import { ChevronDown, ChevronUp } from 'react-feather'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Text } from 'rebass'
|
||||
import styled from 'styled-components'
|
||||
import { useTotalSupply } from '../../data/TotalSupply'
|
||||
import styled from 'styled-components/macro'
|
||||
import { useTotalSupply } from '../../hooks/useTotalSupply'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { useTokenBalance } from '../../state/wallet/hooks'
|
||||
@@ -22,8 +24,9 @@ import Card, { GreyCard, LightCard } from '../Card'
|
||||
import { AutoColumn } from '../Column'
|
||||
import CurrencyLogo from '../CurrencyLogo'
|
||||
import DoubleCurrencyLogo from '../DoubleLogo'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import { RowBetween, RowFixed, AutoRow } from '../Row'
|
||||
import { Dots } from '../swap/styleds'
|
||||
import { BIG_INT_ZERO } from '../../constants'
|
||||
|
||||
export const FixedHeightRow = styled(RowBetween)`
|
||||
height: 24px;
|
||||
@@ -47,6 +50,7 @@ interface PositionCardProps {
|
||||
pair: Pair
|
||||
showUnwrapped?: boolean
|
||||
border?: string
|
||||
stakedBalance?: CurrencyAmount<Token> // optional balance to indicate that liquidity is deposited in mining pool
|
||||
}
|
||||
|
||||
export function MinimalPositionCard({ pair, showUnwrapped = false, border }: PositionCardProps) {
|
||||
@@ -61,8 +65,10 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
|
||||
const totalPoolTokens = useTotalSupply(pair.liquidityToken)
|
||||
|
||||
const poolTokenPercentage =
|
||||
!!userPoolBalance && !!totalPoolTokens && JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
|
||||
? new Percent(userPoolBalance.raw, totalPoolTokens.raw)
|
||||
!!userPoolBalance &&
|
||||
!!totalPoolTokens &&
|
||||
JSBI.greaterThanOrEqual(totalPoolTokens.quotient, userPoolBalance.quotient)
|
||||
? new Percent(userPoolBalance.quotient, totalPoolTokens.quotient)
|
||||
: undefined
|
||||
|
||||
const [token0Deposited, token1Deposited] =
|
||||
@@ -70,16 +76,16 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
|
||||
!!totalPoolTokens &&
|
||||
!!userPoolBalance &&
|
||||
// this condition is a short-circuit in the case where useTokenBalance updates sooner than useTotalSupply
|
||||
JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
|
||||
JSBI.greaterThanOrEqual(totalPoolTokens.quotient, userPoolBalance.quotient)
|
||||
? [
|
||||
pair.getLiquidityValue(pair.token0, totalPoolTokens, userPoolBalance, false),
|
||||
pair.getLiquidityValue(pair.token1, totalPoolTokens, userPoolBalance, false)
|
||||
pair.getLiquidityValue(pair.token1, totalPoolTokens, userPoolBalance, false),
|
||||
]
|
||||
: [undefined, undefined]
|
||||
|
||||
return (
|
||||
<>
|
||||
{userPoolBalance && JSBI.greaterThan(userPoolBalance.raw, JSBI.BigInt(0)) ? (
|
||||
{userPoolBalance && JSBI.greaterThan(userPoolBalance.quotient, JSBI.BigInt(0)) ? (
|
||||
<GreyCard border={border}>
|
||||
<AutoColumn gap="12px">
|
||||
<FixedHeightRow>
|
||||
@@ -157,7 +163,7 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
|
||||
)
|
||||
}
|
||||
|
||||
export default function FullPositionCard({ pair, border }: PositionCardProps) {
|
||||
export default function FullPositionCard({ pair, border, stakedBalance }: PositionCardProps) {
|
||||
const { account } = useActiveWeb3React()
|
||||
|
||||
const currency0 = unwrappedToken(pair.token0)
|
||||
@@ -165,12 +171,17 @@ export default function FullPositionCard({ pair, border }: PositionCardProps) {
|
||||
|
||||
const [showMore, setShowMore] = useState(false)
|
||||
|
||||
const userPoolBalance = useTokenBalance(account ?? undefined, pair.liquidityToken)
|
||||
const userDefaultPoolBalance = useTokenBalance(account ?? undefined, pair.liquidityToken)
|
||||
const totalPoolTokens = useTotalSupply(pair.liquidityToken)
|
||||
|
||||
// if staked balance balance provided, add to standard liquidity amount
|
||||
const userPoolBalance = stakedBalance ? userDefaultPoolBalance?.add(stakedBalance) : userDefaultPoolBalance
|
||||
|
||||
const poolTokenPercentage =
|
||||
!!userPoolBalance && !!totalPoolTokens && JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
|
||||
? new Percent(userPoolBalance.raw, totalPoolTokens.raw)
|
||||
!!userPoolBalance &&
|
||||
!!totalPoolTokens &&
|
||||
JSBI.greaterThanOrEqual(totalPoolTokens.quotient, userPoolBalance.quotient)
|
||||
? new Percent(userPoolBalance.quotient, totalPoolTokens.quotient)
|
||||
: undefined
|
||||
|
||||
const [token0Deposited, token1Deposited] =
|
||||
@@ -178,10 +189,10 @@ export default function FullPositionCard({ pair, border }: PositionCardProps) {
|
||||
!!totalPoolTokens &&
|
||||
!!userPoolBalance &&
|
||||
// this condition is a short-circuit in the case where useTokenBalance updates sooner than useTotalSupply
|
||||
JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
|
||||
JSBI.greaterThanOrEqual(totalPoolTokens.quotient, userPoolBalance.quotient)
|
||||
? [
|
||||
pair.getLiquidityValue(pair.token0, totalPoolTokens, userPoolBalance, false),
|
||||
pair.getLiquidityValue(pair.token1, totalPoolTokens, userPoolBalance, false)
|
||||
pair.getLiquidityValue(pair.token1, totalPoolTokens, userPoolBalance, false),
|
||||
]
|
||||
: [undefined, undefined]
|
||||
|
||||
@@ -192,30 +203,23 @@ export default function FullPositionCard({ pair, border }: PositionCardProps) {
|
||||
<CardNoise />
|
||||
<AutoColumn gap="12px">
|
||||
<FixedHeightRow>
|
||||
<RowFixed>
|
||||
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} margin={true} size={20} />
|
||||
<AutoRow gap="8px" style={{ marginLeft: '8px' }}>
|
||||
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} size={20} />
|
||||
<Text fontWeight={500} fontSize={20}>
|
||||
{!currency0 || !currency1 ? <Dots>Loading</Dots> : `${currency0.symbol}/${currency1.symbol}`}
|
||||
</Text>
|
||||
</RowFixed>
|
||||
|
||||
<RowFixed gap="8px">
|
||||
<ButtonEmpty
|
||||
padding="6px 8px"
|
||||
borderRadius="12px"
|
||||
width="fit-content"
|
||||
onClick={() => setShowMore(!showMore)}
|
||||
>
|
||||
</AutoRow>
|
||||
<RowFixed gap="8px" style={{ marginRight: '4px' }}>
|
||||
<ButtonEmpty padding="6px 8px" borderRadius="12px" width="100%" onClick={() => setShowMore(!showMore)}>
|
||||
{showMore ? (
|
||||
<>
|
||||
{' '}
|
||||
Manage
|
||||
<ChevronUp size="20" style={{ marginLeft: '10px' }} />
|
||||
<ChevronUp size="20" style={{ marginLeft: '8px', height: '20px', minWidth: '20px' }} />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Manage
|
||||
<ChevronDown size="20" style={{ marginLeft: '10px' }} />
|
||||
<ChevronDown size="20" style={{ marginLeft: '8px', height: '20px', minWidth: '20px' }} />
|
||||
</>
|
||||
)}
|
||||
</ButtonEmpty>
|
||||
@@ -226,12 +230,22 @@ export default function FullPositionCard({ pair, border }: PositionCardProps) {
|
||||
<AutoColumn gap="8px">
|
||||
<FixedHeightRow>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
Your pool tokens:
|
||||
Your total pool tokens:
|
||||
</Text>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
{userPoolBalance ? userPoolBalance.toSignificant(4) : '-'}
|
||||
</Text>
|
||||
</FixedHeightRow>
|
||||
{stakedBalance && (
|
||||
<FixedHeightRow>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
Pool tokens in rewards pool:
|
||||
</Text>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
{stakedBalance.toSignificant(4)}
|
||||
</Text>
|
||||
</FixedHeightRow>
|
||||
)}
|
||||
<FixedHeightRow>
|
||||
<RowFixed>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
@@ -273,38 +287,62 @@ export default function FullPositionCard({ pair, border }: PositionCardProps) {
|
||||
Your pool share:
|
||||
</Text>
|
||||
<Text fontSize={16} fontWeight={500}>
|
||||
{poolTokenPercentage ? poolTokenPercentage.toFixed(2) + '%' : '-'}
|
||||
{poolTokenPercentage
|
||||
? (poolTokenPercentage.toFixed(2) === '0.00' ? '<0.01' : poolTokenPercentage.toFixed(2)) + '%'
|
||||
: '-'}
|
||||
</Text>
|
||||
</FixedHeightRow>
|
||||
|
||||
<ButtonSecondary padding="8px" borderRadius="8px">
|
||||
<ExternalLink
|
||||
style={{ width: '100%', textAlign: 'center' }}
|
||||
href={`https://uniswap.info/account/${account}`}
|
||||
href={`https://v2.info.uniswap.org/account/${account}`}
|
||||
>
|
||||
View accrued fees and analytics<span style={{ fontSize: '11px' }}>↗</span>
|
||||
</ExternalLink>
|
||||
</ButtonSecondary>
|
||||
<RowBetween marginTop="10px">
|
||||
{userDefaultPoolBalance && JSBI.greaterThan(userDefaultPoolBalance.quotient, BIG_INT_ZERO) && (
|
||||
<RowBetween marginTop="10px">
|
||||
<ButtonPrimary
|
||||
padding="8px"
|
||||
borderRadius="8px"
|
||||
as={Link}
|
||||
to={`/migrate/v2/${pair.liquidityToken.address}`}
|
||||
width="32%"
|
||||
>
|
||||
Migrate
|
||||
</ButtonPrimary>
|
||||
<ButtonPrimary
|
||||
padding="8px"
|
||||
borderRadius="8px"
|
||||
as={Link}
|
||||
to={`/add/v2/${currencyId(currency0)}/${currencyId(currency1)}`}
|
||||
width="32%"
|
||||
>
|
||||
Add
|
||||
</ButtonPrimary>
|
||||
<ButtonPrimary
|
||||
padding="8px"
|
||||
borderRadius="8px"
|
||||
as={Link}
|
||||
width="32%"
|
||||
to={`/remove/v2/${currencyId(currency0)}/${currencyId(currency1)}`}
|
||||
>
|
||||
Remove
|
||||
</ButtonPrimary>
|
||||
</RowBetween>
|
||||
)}
|
||||
{stakedBalance && JSBI.greaterThan(stakedBalance.quotient, BIG_INT_ZERO) && (
|
||||
<ButtonPrimary
|
||||
padding="8px"
|
||||
borderRadius="8px"
|
||||
as={Link}
|
||||
to={`/add/${currencyId(currency0)}/${currencyId(currency1)}`}
|
||||
width="48%"
|
||||
to={`/uni/${currencyId(currency0)}/${currencyId(currency1)}`}
|
||||
width="100%"
|
||||
>
|
||||
Add
|
||||
Manage Liquidity in Rewards Pool
|
||||
</ButtonPrimary>
|
||||
<ButtonPrimary
|
||||
padding="8px"
|
||||
borderRadius="8px"
|
||||
as={Link}
|
||||
width="48%"
|
||||
to={`/remove/${currencyId(currency0)}/${currencyId(currency1)}`}
|
||||
>
|
||||
Remove
|
||||
</ButtonPrimary>
|
||||
</RowBetween>
|
||||
)}
|
||||
</AutoColumn>
|
||||
)}
|
||||
</AutoColumn>
|
||||
|
||||
53
src/components/PositionList/PositionList.stories.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
// import { Story } from '@storybook/react/types-6-0'
|
||||
// import React from 'react'
|
||||
// import { Position } from 'types/position'
|
||||
// import { basisPointsToPercent } from 'utils'
|
||||
// import { DAI, WBTC } from '../../constants'
|
||||
// import Component, { PositionListProps } from './index'
|
||||
// import { CurrencyAmount } from '@uniswap/sdk-core'
|
||||
// import JSBI from 'jsbi'
|
||||
|
||||
// const FEE_BIPS = {
|
||||
// FIVE: basisPointsToPercent(5),
|
||||
// THIRTY: basisPointsToPercent(30),
|
||||
// ONE_HUNDRED: basisPointsToPercent(100),
|
||||
// }
|
||||
// const daiAmount = CurrencyAmount.fromRawAmount(DAI, JSBI.BigInt(500 * 10 ** 18))
|
||||
// const wbtcAmount = CurrencyAmount.fromRawAmount(WBTC, JSBI.BigInt(10 ** 7))
|
||||
// const positions = [
|
||||
// {
|
||||
// feesEarned: {
|
||||
// DAI: 1000,
|
||||
// WBTC: 0.005,
|
||||
// },
|
||||
// feeLevel: FEE_BIPS.FIVE,
|
||||
// tokenAmount0: daiAmount,
|
||||
// tokenAmount1: wbtcAmount,
|
||||
// tickLower: 40000,
|
||||
// tickUpper: 60000,
|
||||
// },
|
||||
// {
|
||||
// feesEarned: {
|
||||
// DAI: 1000,
|
||||
// WBTC: 0.005,
|
||||
// },
|
||||
// feeLevel: FEE_BIPS.THIRTY,
|
||||
// tokenAmount0: daiAmount,
|
||||
// tokenAmount1: wbtcAmount,
|
||||
// tickLower: 45000,
|
||||
// tickUpper: 55000,
|
||||
// },
|
||||
// ]
|
||||
// const positions: Position[] = []
|
||||
|
||||
export default {
|
||||
title: 'PositionList',
|
||||
}
|
||||
|
||||
// const Template: Story<PositionListProps> = (args) => <Component {...args} />
|
||||
|
||||
// export const PositionList = Template.bind({})
|
||||
// PositionList.args = {
|
||||
// positions,
|
||||
// showUnwrapped: true,
|
||||
// }
|
||||
59
src/components/PositionList/index.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import PositionListItem from 'components/PositionListItem'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components/macro'
|
||||
import { MEDIA_WIDTHS } from 'theme'
|
||||
import { PositionDetails } from 'types/position'
|
||||
|
||||
const DesktopHeader = styled.div`
|
||||
display: none;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
padding: 8px;
|
||||
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
& > div:last-child {
|
||||
text-align: right;
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const MobileHeader = styled.div`
|
||||
font-weight: medium;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
padding: 8px;
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
|
||||
export type PositionListProps = React.PropsWithChildren<{
|
||||
positions: PositionDetails[]
|
||||
}>
|
||||
|
||||
export default function PositionList({ positions }: PositionListProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<>
|
||||
<DesktopHeader>
|
||||
<div>
|
||||
{t('Your positions')}
|
||||
{positions && ' (' + positions.length + ')'}
|
||||
</div>
|
||||
<div>{t('Price range')}</div>
|
||||
</DesktopHeader>
|
||||
<MobileHeader>Your positions</MobileHeader>
|
||||
{positions.map((p) => {
|
||||
return <PositionListItem key={p.tokenId.toString()} positionDetails={p} />
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
||||
31
src/components/PositionListItem/PositionListItem.stories.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
// import { Story } from '@storybook/react/types-6-0'
|
||||
// import { FeeAmount, MAX_TICK, MIN_TICK, TICK_SPACINGS } from '@uniswap/v3-sdk'
|
||||
// import { BigNumber } from 'ethers'
|
||||
// import React from 'react'
|
||||
// import { Position } from 'types/position'
|
||||
// import Component, { PositionListItemProps } from './index'
|
||||
|
||||
// const position: Position = {
|
||||
// nonce: BigNumber.from(0),
|
||||
// operator: '',
|
||||
// token0: '',
|
||||
// token1: '',
|
||||
// fee: FeeAmount.LOW,
|
||||
// tickLower: MIN_TICK(TICK_SPACINGS[FeeAmount.LOW]),
|
||||
// tickUpper: MAX_TICK(TICK_SPACINGS[FeeAmount.LOW]),
|
||||
// liquidity,
|
||||
// feeGrowthInside0LastX128fee
|
||||
// feeGrowthInside0LastX128
|
||||
// feeGrowthInside1LastX128
|
||||
// tokensOwed0
|
||||
// tokensOwed1
|
||||
// }
|
||||
|
||||
export default {
|
||||
title: 'PositionListItem',
|
||||
}
|
||||
|
||||
// const Template: Story<PositionListItemProps> = (args) => <Component {...args} />
|
||||
|
||||
// export const PositionListItem = Template.bind({})
|
||||
// PositionListItem.args = {position}
|
||||