Compare commits
536 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8829639e9 | ||
|
|
62550a4a1f | ||
|
|
e01f30c0b4 | ||
|
|
c60c028dbe | ||
|
|
439fe5e6d4 | ||
|
|
e06142e26b | ||
|
|
1f763a1c7a | ||
|
|
de6e3747cb | ||
|
|
475af6312f | ||
|
|
c4c6d7553d | ||
|
|
0e3c95706e | ||
|
|
d5e4e21a79 | ||
|
|
4649051843 | ||
|
|
4ba6275b71 | ||
|
|
17c7a9ee9d | ||
|
|
0e36944b23 | ||
|
|
5050fe7b06 | ||
|
|
80b965f2ca | ||
|
|
417e940c0a | ||
|
|
c42a5ccf26 | ||
|
|
7ba9b1faf6 | ||
|
|
5d43d08ff3 | ||
|
|
85625d09f0 | ||
|
|
d85f1f44b9 | ||
|
|
f41580e43c | ||
|
|
47fe9911fa | ||
|
|
e0f6d82d6c | ||
|
|
fb691cf17b | ||
|
|
f596293b6c | ||
|
|
8012789f69 | ||
|
|
4013743473 | ||
|
|
e9ef3193ab | ||
|
|
6a1506ade6 | ||
|
|
839d4ac8e2 | ||
|
|
29fdcb80f6 | ||
|
|
817ea44e8d | ||
|
|
3a1ea3df85 | ||
|
|
2667a897a1 | ||
|
|
65129604bd | ||
|
|
4e0c9b36a0 | ||
|
|
64cb9f3ff2 | ||
|
|
cb094a1f4b | ||
|
|
d05fefc231 | ||
|
|
3e1805a20f | ||
|
|
f58dfe1284 | ||
|
|
f67b7f8b66 | ||
|
|
869691d43f | ||
|
|
817d808ec5 | ||
|
|
aee1bce612 | ||
|
|
8b38a9c4e0 | ||
|
|
0a115fab17 | ||
|
|
882147b533 | ||
|
|
eb06aef199 | ||
|
|
1b91e7ce30 | ||
|
|
535e670c63 | ||
|
|
0b4c77155e | ||
|
|
e36722ccb4 | ||
|
|
ec0b94a920 | ||
|
|
d5eed8b15f | ||
|
|
2447afc43e | ||
|
|
8eef757f7f | ||
|
|
b1c29b3bf1 | ||
|
|
b211c9f150 | ||
|
|
66cae715f4 | ||
|
|
5a4a2782e8 | ||
|
|
4e462ddbef | ||
|
|
48a962a750 | ||
|
|
9a55402bdf | ||
|
|
663644553e | ||
|
|
ceec3f0e65 | ||
|
|
904d1835d2 | ||
|
|
77366bf81b | ||
|
|
cd8b048829 | ||
|
|
d62596ecfc | ||
|
|
2b496c62f1 | ||
|
|
a3c77708e2 | ||
|
|
b9e8139699 | ||
|
|
c82dcabd19 | ||
|
|
26b37d5274 | ||
|
|
d5c464b26b | ||
|
|
c48d4c5425 | ||
|
|
b28cd9c8b0 | ||
|
|
071017879c | ||
|
|
5535c83db5 | ||
|
|
8f9eccabaf | ||
|
|
322cdaf888 | ||
|
|
edcdbfd8f5 | ||
|
|
d36f13d7e2 | ||
|
|
8548b33bd9 | ||
|
|
0e148bb1b3 | ||
|
|
9ddb37a982 | ||
|
|
63227cd2c5 | ||
|
|
dc38dc1c10 | ||
|
|
2c6757ff62 | ||
|
|
0d03b09ae9 | ||
|
|
566da07448 | ||
|
|
31a3840b1f | ||
|
|
f89d7ccd5e | ||
|
|
628417f696 | ||
|
|
ea8c7326d6 | ||
|
|
dd5feaacb2 | ||
|
|
cc919ab3df | ||
|
|
53d6eb0922 | ||
|
|
db0d3cf3fa | ||
|
|
ace4276bcb | ||
|
|
50a2dc9560 | ||
|
|
8cdec6188c | ||
|
|
5325d0cbe5 | ||
|
|
c16e49e774 | ||
|
|
7e709e10db | ||
|
|
7389b178fd | ||
|
|
48f8c6a141 | ||
|
|
091876a374 | ||
|
|
d0e4aa832a | ||
|
|
b17a38d94b | ||
|
|
22136b2708 | ||
|
|
1897330ffc | ||
|
|
6131d0079f | ||
|
|
e6814994f6 | ||
|
|
fea7d3a867 | ||
|
|
f4f0f29409 | ||
|
|
fa25e3c3e5 | ||
|
|
51d2b3792f | ||
|
|
04ded04e74 | ||
|
|
cf188a86de | ||
|
|
f7e2435868 | ||
|
|
5817d3bbdb | ||
|
|
bdeb62ad64 | ||
|
|
e42effdbfd | ||
|
|
c0a5adf43d | ||
|
|
fd0489e654 | ||
|
|
caa0a2967d | ||
|
|
11ec2333dd | ||
|
|
251339a9ef | ||
|
|
148e415fe8 | ||
|
|
fd964c5b74 | ||
|
|
83b6eec271 | ||
|
|
cd76fffbbe | ||
|
|
2c0ac56296 | ||
|
|
f836e3ca32 | ||
|
|
1733fbb378 | ||
|
|
78142270a8 | ||
|
|
db4e2a9bee | ||
|
|
9cee94a473 | ||
|
|
db5a14387f | ||
|
|
5dc7d36669 | ||
|
|
a3cbe672c7 | ||
|
|
dc368ed7ac | ||
|
|
b109248b4c | ||
|
|
64cc6fb88c | ||
|
|
74f6a4ef3f | ||
|
|
f468001404 | ||
|
|
f26ec2ff1b | ||
|
|
61d1036d28 | ||
|
|
e11d2080a4 | ||
|
|
da33423719 | ||
|
|
bd4545538d | ||
|
|
4274db67d5 | ||
|
|
28498706cb | ||
|
|
68c71a67dd | ||
|
|
fe195b63f7 | ||
|
|
86b85e25a5 | ||
|
|
0ea635ce15 | ||
|
|
99ab581a87 | ||
|
|
fc571d0f63 | ||
|
|
2de43a8cdb | ||
|
|
5383436c88 | ||
|
|
521f3aae04 | ||
|
|
9318c1204b | ||
|
|
5055695b9b | ||
|
|
ae8c0377de | ||
|
|
8eaf1f4964 | ||
|
|
f717bf4a49 | ||
|
|
dcbd4e475d | ||
|
|
b704bdac94 | ||
|
|
00d3df95c0 | ||
|
|
251b8b703a | ||
|
|
ef8432437d | ||
|
|
71aebf33db | ||
|
|
5ff428b04b | ||
|
|
acb0c2056e | ||
|
|
0a4bcb62da | ||
|
|
f50bcbdb2d | ||
|
|
cbe421ee23 | ||
|
|
3439786c38 | ||
|
|
6294915be6 | ||
|
|
984c742d0e | ||
|
|
00b151d7fa | ||
|
|
5967cf5d9d | ||
|
|
e480f0ebe5 | ||
|
|
f6ceecbc5e | ||
|
|
a0348b45be | ||
|
|
e4b37cffcc | ||
|
|
dd69cccf91 | ||
|
|
8b228de88f | ||
|
|
f91fc3c6a6 | ||
|
|
e0e2b40f9f | ||
|
|
bc1c61b63a | ||
|
|
446ad3e0d4 | ||
|
|
65e58a08cf | ||
|
|
71b20b432c | ||
|
|
ecfa179b3f | ||
|
|
6c94a0f585 | ||
|
|
600aeaaff1 | ||
|
|
3bfbc74e47 | ||
|
|
84f76e34b2 | ||
|
|
b965bed865 | ||
|
|
a9039e8d0b | ||
|
|
60d35b46f3 | ||
|
|
3d422cf707 | ||
|
|
84c70ac84d | ||
|
|
de3a33dfcb | ||
|
|
99a084f230 | ||
|
|
e880955743 | ||
|
|
bbf43fcd27 | ||
|
|
a00ac56389 | ||
|
|
7201944bc2 | ||
|
|
b0ff0f83b0 | ||
|
|
5cf9e84db5 | ||
|
|
c0bdb8db12 | ||
|
|
2d8f767d74 | ||
|
|
1303416eca | ||
|
|
124f6420a5 | ||
|
|
91f5fc0881 | ||
|
|
ec831f8433 | ||
|
|
56bd9b68d7 | ||
|
|
865d21f039 | ||
|
|
dceadf8472 | ||
|
|
cd3a91bca8 | ||
|
|
de1f5d1adc | ||
|
|
b5d403768f | ||
|
|
c4c811aeb3 | ||
|
|
33c24a3f05 | ||
|
|
9ef2b3a116 | ||
|
|
afe38a2d10 | ||
|
|
d28607a1c8 | ||
|
|
7fb363ac46 | ||
|
|
16b0b1530d | ||
|
|
abb2696f40 | ||
|
|
772178fc86 | ||
|
|
9f1378f635 | ||
|
|
84275dcce1 | ||
|
|
a76ece6ce3 | ||
|
|
334e137fb3 | ||
|
|
eb6c4d464a | ||
|
|
24734e6a34 | ||
|
|
f1bcee3c08 | ||
|
|
7a215ccdb4 | ||
|
|
c5c4f48d96 | ||
|
|
bdcf761ddd | ||
|
|
e876267d83 | ||
|
|
76cbd82cb7 | ||
|
|
6c4f7ab9a1 | ||
|
|
da20315724 | ||
|
|
963b910552 | ||
|
|
9e2dc9a435 | ||
|
|
6567f18bf5 | ||
|
|
ee96973212 | ||
|
|
ce6c783174 | ||
|
|
64e8c3ced9 | ||
|
|
46e6c2295d | ||
|
|
3626dbdeec | ||
|
|
eb75e0dc2e | ||
|
|
c26ecdfc88 | ||
|
|
f508788026 | ||
|
|
377026bca8 | ||
|
|
9470c49d11 | ||
|
|
e1abd81a1d | ||
|
|
7d9657867d | ||
|
|
7cc52abb96 | ||
|
|
5ac41417b0 | ||
|
|
2c74c5f2df | ||
|
|
cbc2ff668e | ||
|
|
a73f59b4ff | ||
|
|
7a75626c31 | ||
|
|
a0e14bef10 | ||
|
|
9b5a53b2e8 | ||
|
|
50fdb36b6f | ||
|
|
b993902c73 | ||
|
|
828bf540ba | ||
|
|
7c88a5a008 | ||
|
|
360c5e2c96 | ||
|
|
72678ee667 | ||
|
|
4dd74f2144 | ||
|
|
e45c104135 | ||
|
|
98fcaacd9b | ||
|
|
93551579e4 | ||
|
|
8a9388ed81 | ||
|
|
884bf41da7 | ||
|
|
5b686aea97 | ||
|
|
c4a456a085 | ||
|
|
4b9098a7bf | ||
|
|
71a246f25c | ||
|
|
8de048bc84 | ||
|
|
163e2d5560 | ||
|
|
0edb0fe5e2 | ||
|
|
496408b3db | ||
|
|
78b6f5c72d | ||
|
|
f9fb71a803 | ||
|
|
59d0046411 | ||
|
|
b4e0234d07 | ||
|
|
4a8dbda0b8 | ||
|
|
0cbb24c614 | ||
|
|
a9dba258ff | ||
|
|
fa163cb938 | ||
|
|
b52273932a | ||
|
|
9ad8f80e4e | ||
|
|
69bc598dea | ||
|
|
7feba045fc | ||
|
|
04cee0a07d | ||
|
|
ea73260e56 | ||
|
|
b4bd2973a9 | ||
|
|
a071b8adb0 | ||
|
|
610acb0191 | ||
|
|
63bad8f890 | ||
|
|
32f955693f | ||
|
|
96c66a5846 | ||
|
|
8c269a6d39 | ||
|
|
36f111fa6f | ||
|
|
e569dc2152 | ||
|
|
1aa042c5ef | ||
|
|
1450315b98 | ||
|
|
aefbb3d812 | ||
|
|
c3f12398cd | ||
|
|
2272f2a01a | ||
|
|
fb71078ea2 | ||
|
|
1c7c93191e | ||
|
|
0713f730b3 | ||
|
|
5f7a18b411 | ||
|
|
020c8d181a | ||
|
|
ab3f024031 | ||
|
|
d989c61de5 | ||
|
|
5dd8059734 | ||
|
|
b50e5511ea | ||
|
|
1efe5e9cd5 | ||
|
|
2944dc4d0b | ||
|
|
29ae755f2a | ||
|
|
27b831b301 | ||
|
|
6d9d38819e | ||
|
|
2de29129ed | ||
|
|
52af0e506b | ||
|
|
4d69c946bf | ||
|
|
542bf0bf66 | ||
|
|
a4fbfae4ba | ||
|
|
b2288258f2 | ||
|
|
8703013b2d | ||
|
|
4f6173675d | ||
|
|
2469eb58b9 | ||
|
|
e0a8ac2408 | ||
|
|
0a736b5e62 | ||
|
|
b44eb8877c | ||
|
|
92e61fa34b | ||
|
|
ef62fd33b2 | ||
|
|
96a42f66d4 | ||
|
|
c446f20d2f | ||
|
|
5a1ef8fb7d | ||
|
|
2863971640 | ||
|
|
dcaf10ec29 | ||
|
|
bca5113569 | ||
|
|
6779c1a024 | ||
|
|
f79ef12494 | ||
|
|
7bcda46934 | ||
|
|
f4ba24cfd5 | ||
|
|
59c6ab16dd | ||
|
|
db17dcbf2c | ||
|
|
1835de7f5f | ||
|
|
00f158209c | ||
|
|
2108ceedd5 | ||
|
|
ad080470da | ||
|
|
fc34912b53 | ||
|
|
c25d2b894c | ||
|
|
83c99b8c04 | ||
|
|
ccdf1e7575 | ||
|
|
c9faafee5e | ||
|
|
26a44fb51b | ||
|
|
1e16ac8449 | ||
|
|
5b5e76573d | ||
|
|
27cdbd0d5f | ||
|
|
b2a30b9bf1 | ||
|
|
dfad7b89ab | ||
|
|
4fe35ea42e | ||
|
|
0d852b6165 | ||
|
|
8ac3b836bd | ||
|
|
12bc5957b4 | ||
|
|
a33187c33b | ||
|
|
248bc07cf1 | ||
|
|
369f8c94e3 | ||
|
|
de5f0541ee | ||
|
|
48b3efc612 | ||
|
|
90c59f31f3 | ||
|
|
0e709c257b | ||
|
|
7a3bb8de1d | ||
|
|
8018d1b9dc | ||
|
|
1297aa57d3 | ||
|
|
30e30189e1 | ||
|
|
6a602cf6d7 | ||
|
|
4c966caa2a | ||
|
|
a60ea703b0 | ||
|
|
ae664dc264 | ||
|
|
b152b11515 | ||
|
|
0f51991109 | ||
|
|
da8884d87d | ||
|
|
79bdc0c5ee | ||
|
|
82c30681ea | ||
|
|
41ef961679 | ||
|
|
7de63ab462 | ||
|
|
59c5989721 | ||
|
|
b042d2b3b4 | ||
|
|
897e7f4581 | ||
|
|
a7fb7dc906 | ||
|
|
5fe89b9d6c | ||
|
|
acbcd3763c | ||
|
|
01c467b48c | ||
|
|
636abe3b7b | ||
|
|
8404c6076c | ||
|
|
b4aac94c2c | ||
|
|
f47fcc9c17 | ||
|
|
b5d27e2063 | ||
|
|
26275ca580 | ||
|
|
92b7ca8f55 | ||
|
|
c5ea01ce19 | ||
|
|
88712b5065 | ||
|
|
1af34ae016 | ||
|
|
9cb19dd0ea | ||
|
|
02a77254c7 | ||
|
|
69ed7015ab | ||
|
|
ff16d3f18f | ||
|
|
5175cb6d1f | ||
|
|
b33686855d | ||
|
|
75ecc5810e | ||
|
|
c30eb89725 | ||
|
|
108feace02 | ||
|
|
e2c013a4d8 | ||
|
|
66308257d6 | ||
|
|
fd160531cc | ||
|
|
da36e638c2 | ||
|
|
fad55b8dbc | ||
|
|
c9c59698de | ||
|
|
828967031f | ||
|
|
440ac0cba0 | ||
|
|
b5a72cd63b | ||
|
|
37f273aab4 | ||
|
|
3acd993ec0 | ||
|
|
58778b5775 | ||
|
|
5bc21bebc3 | ||
|
|
c3d6727438 | ||
|
|
290f4bc1cb | ||
|
|
f95275d5ac | ||
|
|
0ec2dd4173 | ||
|
|
3b3db6f6d0 | ||
|
|
707abd0071 | ||
|
|
2efc1fb372 | ||
|
|
55b37825f3 | ||
|
|
bb27b7a2ef | ||
|
|
c595ba951b | ||
|
|
96a122d7b8 | ||
|
|
610f7d3581 | ||
|
|
781e774ce7 | ||
|
|
2aa1e40481 | ||
|
|
1c278d5012 | ||
|
|
a323a5c48b | ||
|
|
43931dd689 | ||
|
|
efa3d5529c | ||
|
|
5c0246cfc6 | ||
|
|
ee32418ff8 | ||
|
|
6e22389791 | ||
|
|
8064dd8ede | ||
|
|
921310ef52 | ||
|
|
7b90fe137e | ||
|
|
05b2711a8a | ||
|
|
d060782242 | ||
|
|
e19e8492c9 | ||
|
|
800b5e0bda | ||
|
|
fc637071f9 | ||
|
|
1b78ceec10 | ||
|
|
e5be3ebf8f | ||
|
|
1c73719766 | ||
|
|
14c91f9bba | ||
|
|
4b762ef5c9 | ||
|
|
c82b4fae64 | ||
|
|
ab8c1e3e90 | ||
|
|
7055d60406 | ||
|
|
c641cec651 | ||
|
|
b6a47c734f | ||
|
|
7aecf5d398 | ||
|
|
5bf2b81743 | ||
|
|
ed247065a7 | ||
|
|
0d0ad633fb | ||
|
|
4a8f1d9b96 | ||
|
|
043fb95d22 | ||
|
|
06536bc925 | ||
|
|
a598a15799 | ||
|
|
b0265c081e | ||
|
|
47aff6ff74 | ||
|
|
56717005e6 | ||
|
|
b50d10cbb2 | ||
|
|
ce96873a72 | ||
|
|
779625a04e | ||
|
|
d1e0812684 | ||
|
|
98e62b4f93 | ||
|
|
9fb0d424c2 | ||
|
|
8d145b908e | ||
|
|
c7633d910b | ||
|
|
1f89a46a3f | ||
|
|
8d54b01878 | ||
|
|
ffe334ccbf | ||
|
|
ffe2bd315e | ||
|
|
cee4b8c77a | ||
|
|
3153db9f73 | ||
|
|
bbdb5f3f56 | ||
|
|
7f9c56b68c | ||
|
|
2b69974fdc | ||
|
|
5236065769 | ||
|
|
52128a2dcd | ||
|
|
c9642c6cd0 | ||
|
|
b878d764e5 | ||
|
|
6a4f067ac0 | ||
|
|
e9407bb6bd | ||
|
|
8d822fd0e0 | ||
|
|
6404ee6e0b | ||
|
|
8ac3ed1128 | ||
|
|
b501974a76 | ||
|
|
567fb0181c | ||
|
|
8a37c427e6 | ||
|
|
034b3e3e58 | ||
|
|
053000e5fc | ||
|
|
b77e7deb49 | ||
|
|
c3321ae793 | ||
|
|
5dec0cf72b | ||
|
|
1efda07e7a | ||
|
|
fd819260f9 | ||
|
|
8e3b2cb4b8 | ||
|
|
d54783a324 | ||
|
|
850a20f6ad | ||
|
|
99f681818f | ||
|
|
1127e74357 |
4
.env
@@ -1 +1,3 @@
|
||||
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
|
||||
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
|
||||
REACT_APP_FORTMATIC_KEY="pk_live_357F77728B8EB880"
|
||||
REACT_APP_LOCALES="locales"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
REACT_APP_AMPLITUDE_KEY="1c694b28cd089acc2c386d518f93a775"
|
||||
REACT_APP_INFURA_KEY="099fc58e0de9451d80b18d7c74caa7c1"
|
||||
REACT_APP_PORTIS_ID="c0e2bf01-4b08-4fd5-ac7b-8e26b58cd236"
|
||||
REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
|
||||
REACT_APP_GOOGLE_ANALYTICS_ID="UA-128182339-4"
|
||||
REACT_APP_GOOGLE_ANALYTICS_ID="G-KDP9B6W4H8"
|
||||
REACT_APP_FIREBASE_KEY="AIzaSyBcZWwTcTJHj_R6ipZcrJkXdq05PuX0Rs0"
|
||||
|
||||
21
.github/actions/setup/action.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Setup
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 14
|
||||
registry-url: https://registry.npmjs.org
|
||||
cache: yarn
|
||||
|
||||
- uses: actions/cache@v3
|
||||
id: install-cache
|
||||
with:
|
||||
path: node_modules/
|
||||
key: ${{ runner.os }}-install-${{ hashFiles('**/yarn.lock') }}
|
||||
|
||||
- if: steps.install-cache.outputs.cache-hit != 'true'
|
||||
run: yarn install --frozen-lockfile --ignore-scripts
|
||||
shell: bash
|
||||
8
.github/dependabot.yml
vendored
@@ -2,9 +2,9 @@ version: 2
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
# Files stored in repository root
|
||||
directory: "/"
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: 'daily'
|
||||
allow:
|
||||
- dependency-name: "@uniswap/token-lists"
|
||||
- dependency-name: "@uniswap/default-token-list"
|
||||
- dependency-name: '@uniswap/token-lists'
|
||||
- dependency-name: '@uniswap/default-token-list'
|
||||
|
||||
24
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
Your PR title must follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary), and should start with one of the following [types](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type):
|
||||
|
||||
- build: Changes that affect the build system or external dependencies (example scopes: yarn, eslint, typescript)
|
||||
- ci: Changes to our CI configuration files and scripts (example scopes: vercel, github, cypress)
|
||||
- docs: Documentation only changes
|
||||
- feat: A new feature
|
||||
- fix: A bug fix
|
||||
- perf: A code change that improves performance
|
||||
- refactor: A code change that neither fixes a bug nor adds a feature
|
||||
- style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
|
||||
- test: Adding missing tests or correcting existing tests
|
||||
|
||||
Example commit messages:
|
||||
|
||||
- feat: adds support for gnosis safe wallet
|
||||
- fix: removes a polling memory leak
|
||||
- chore: bumps redux version
|
||||
|
||||
Other things to note:
|
||||
|
||||
- Please describe the change using verb statements (ex: Removes X from Y)
|
||||
- PRs with multiple changes should use a list of verb statements
|
||||
- Add any relevant unit / integration tests
|
||||
- Changes will be previewable via vercel. Non-obvious changes should include instructions for how to reproduce them
|
||||
40
.github/workflows/bundle.yaml
vendored
@@ -1,40 +0,0 @@
|
||||
name: Widgets
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
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: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v2
|
||||
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-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: yarn widgets:build
|
||||
6
.github/workflows/check-pr-title.yaml
vendored
@@ -1,4 +1,4 @@
|
||||
name: "Check PR Title"
|
||||
name: Check PR Title
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
@@ -8,8 +8,8 @@ on:
|
||||
- synchronize
|
||||
|
||||
jobs:
|
||||
check-pr-title:
|
||||
name: Check PR Title
|
||||
# Ensures that the PR title adheres to [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/).
|
||||
conventional-commit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: amannn/action-semantic-pull-request@v3.4.0
|
||||
|
||||
48
.github/workflows/crowdin-sync.yaml
vendored
@@ -1,48 +1,23 @@
|
||||
name: Crowdin Download
|
||||
|
||||
# hourly we sync translations from Crowdin
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 * * * *' # every hour we download translations and update the pr from crowdin
|
||||
|
||||
# Download translations every hour.
|
||||
# This is not done as part of the build so that builds remain reproducible.
|
||||
- cron: '0 * * * *'
|
||||
# manual trigger
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
download-translations:
|
||||
name: Download translations
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: yarn i18n:extract
|
||||
|
||||
- name: Set up node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
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@v2
|
||||
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-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Extract translations
|
||||
run: "yarn i18n:extract"
|
||||
|
||||
- name: Synchronize
|
||||
uses: crowdin/github-action@1.1.0
|
||||
- name: Download Crowdin translations
|
||||
uses: crowdin/github-action@1.4.9
|
||||
with:
|
||||
upload_sources: false
|
||||
download_translations: true
|
||||
@@ -50,8 +25,9 @@ jobs:
|
||||
token: ${{ secrets.CROWDIN_PERSONAL_TOKEN_SECRET }}
|
||||
source: 'src/locales/en-US.po'
|
||||
translation: 'src/locales/%locale%.po'
|
||||
create_pull_request: false
|
||||
localization_branch_name: main
|
||||
commit_message: "chore(i18n): synchronize translations from crowdin [skip ci]"
|
||||
create_pull_request: true
|
||||
pull_request_title: 'chore(i18n): new Crowdin translations'
|
||||
localization_branch_name: l10n_crowdin
|
||||
commit_message: 'chore(i18n): synchronize translations from crowdin [skip ci]'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
36
.github/workflows/crowdin.yaml
vendored
@@ -1,45 +1,19 @@
|
||||
name: Crowdin Upload
|
||||
|
||||
# on any push to main, we upload the translations to be translated
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
synchronize-with-crowdin:
|
||||
name: Upload sources to Crowdin
|
||||
upload-sources:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: yarn i18n:extract
|
||||
|
||||
- name: Set up node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
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@v2
|
||||
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-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Extract translations
|
||||
run: "yarn i18n:extract"
|
||||
|
||||
- name: Synchronize
|
||||
- name: Upload Crowdin sources
|
||||
uses: crowdin/github-action@1.1.0
|
||||
with:
|
||||
upload_sources: true
|
||||
|
||||
50
.github/workflows/integration-tests.yaml
vendored
@@ -1,50 +0,0 @@
|
||||
name: Integration Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
integration-tests:
|
||||
name: Cypress
|
||||
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: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v2
|
||||
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-
|
||||
|
||||
- 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'
|
||||
REACT_APP_SERVICE_WORKER: false
|
||||
|
||||
- run: yarn test:e2e
|
||||
env:
|
||||
CYPRESS_INTEGRATION_TEST_PRIVATE_KEY: ${{ secrets.CYPRESS_INTEGRATION_TEST_PRIVATE_KEY }}
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
52
.github/workflows/lint.yml
vendored
@@ -1,52 +0,0 @@
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
run-linters:
|
||||
name: Run linters
|
||||
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: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v2
|
||||
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-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Run eslint w/ autofix
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login == github.repository_owner }}
|
||||
uses: wearerequired/lint-action@36c7e6689e80d785d27a22f71d970f3a3b4fcb70
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
eslint: true
|
||||
eslint_args: "-c .eslintrc.json"
|
||||
auto_fix: true
|
||||
|
||||
- name: Run eslint
|
||||
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.owner.login != github.repository_owner }}
|
||||
run: yarn eslint .
|
||||
107
.github/workflows/release.yaml
vendored
@@ -1,56 +1,58 @@
|
||||
name: Release
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 12 * * 1-4' # every day 12:00 UTC Monday-Thursday
|
||||
|
||||
- cron: '0 12 * * 1-4' # every day 12:00 UTC Monday-Thursday
|
||||
# manual trigger
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
bump_version:
|
||||
name: Bump Version
|
||||
wait-on-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: unit-tests
|
||||
uses: fountainhead/action-wait-for-check@v1.0.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
checkName: unit-tests
|
||||
- id: cypress-tests
|
||||
uses: fountainhead/action-wait-for-check@v1.0.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
checkName: cypress-tests
|
||||
- if: steps.unit-tests.outputs.conclusion != 'success' || steps.cypress-tests.outputs.conclusion != 'success'
|
||||
run: exit 1
|
||||
|
||||
tag:
|
||||
needs: wait-on-tests
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
new_tag: ${{ steps.github_tag_action.outputs.new_tag }}
|
||||
changelog: ${{ steps.github_tag_action.outputs.changelog }}
|
||||
new_tag: ${{ steps.github-tag-action.outputs.new_tag }}
|
||||
changelog: ${{ steps.github-tag-action.outputs.changelog }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Bump version and push tag
|
||||
id: github_tag_action
|
||||
uses: mathieudutour/github-tag-action@331898d5052eedac9b15fec867b5ba66ebf9b692
|
||||
- uses: actions/checkout@v3
|
||||
- name: Bump and tag
|
||||
id: github-tag-action
|
||||
uses: mathieudutour/github-tag-action@v6.0
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
release_branches: .*
|
||||
default_bump: false
|
||||
default_bump: patch
|
||||
|
||||
create_release:
|
||||
name: Create Release
|
||||
release:
|
||||
needs: tag
|
||||
if: ${{ needs.tag.outputs.new_tag != null }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: bump_version
|
||||
if: ${{ needs.bump_version.outputs.new_tag != null }}
|
||||
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: Build the IPFS bundle
|
||||
run: yarn build
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: yarn prepare
|
||||
- run: yarn build
|
||||
|
||||
- name: Pin to IPFS
|
||||
id: upload
|
||||
id: pinata
|
||||
uses: anantaramdas/ipfs-pinata-deploy-action@39bbda1ce1fe24c69c6f57861b8038278d53688d
|
||||
with:
|
||||
pin-name: Uniswap ${{ needs.bump_version.outputs.new_tag }}
|
||||
pin-name: Uniswap ${{ needs.tag.outputs.new_tag }}
|
||||
path: './build'
|
||||
pinata-api-key: ${{ secrets.PINATA_API_KEY }}
|
||||
pinata-secret-api-key: ${{ secrets.PINATA_API_SECRET_KEY }}
|
||||
@@ -64,10 +66,24 @@ jobs:
|
||||
seeds: ${{ secrets.CRUST_SEEDS }}
|
||||
|
||||
- name: Convert CIDv0 to CIDv1
|
||||
id: convert_cidv0
|
||||
id: convert-cidv0
|
||||
uses: uniswap/convert-cidv0-cidv1@v1.0.0
|
||||
with:
|
||||
cidv0: ${{ steps.upload.outputs.hash }}
|
||||
cidv0: ${{ steps.pinata.outputs.hash }}
|
||||
|
||||
- uses: actions/cache@v3
|
||||
id: cypress-cache
|
||||
with:
|
||||
path: /home/runner/.cache/Cypress
|
||||
key: ${{ runner.os }}-cypress-${{ hashFiles('node_modules/cypress') }}
|
||||
- if: steps.cypress-cache.outputs.cache-hit != 'true'
|
||||
run: yarn cypress install
|
||||
- uses: cypress-io/github-action@v4
|
||||
with:
|
||||
install: false
|
||||
browser: chrome
|
||||
config-file: cypress.release.config.ts
|
||||
config: baseUrl=https://cloudflare-ipfs.com/ipfs/${{ steps.pinata.outputs.hash }}
|
||||
|
||||
- name: Update DNS with new IPFS hash
|
||||
env:
|
||||
@@ -77,20 +93,19 @@ jobs:
|
||||
CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
|
||||
uses: textileio/cloudflare-update-dnslink@0fe7b7a1ffc865db3a4da9773f0f987447ad5848
|
||||
with:
|
||||
cid: ${{ steps.upload.outputs.hash }}
|
||||
cid: ${{ steps.pinata.outputs.hash }}
|
||||
|
||||
- name: Create GitHub Release
|
||||
id: create_release
|
||||
- name: Release
|
||||
uses: actions/create-release@v1.1.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ needs.bump_version.outputs.new_tag }}
|
||||
release_name: Release ${{ needs.bump_version.outputs.new_tag }}
|
||||
tag_name: ${{ needs.tag.outputs.new_tag }}
|
||||
release_name: Release ${{ needs.tag.outputs.new_tag }}
|
||||
body: |
|
||||
IPFS hash of the deployment:
|
||||
- CIDv0: `${{ steps.upload.outputs.hash }}`
|
||||
- CIDv1: `${{ steps.convert_cidv0.outputs.cidv1 }}`
|
||||
- CIDv0: `${{ steps.pinata.outputs.hash }}`
|
||||
- CIDv1: `${{ steps.convert-cidv0.outputs.cidv1 }}`
|
||||
|
||||
The latest release is always accessible via our alias to the Cloudflare IPFS gateway at [app.uniswap.org](https://app.uniswap.org).
|
||||
|
||||
@@ -100,8 +115,8 @@ jobs:
|
||||
Your Uniswap settings are never remembered across different URLs.
|
||||
|
||||
IPFS gateways:
|
||||
- https://${{ steps.convert_cidv0.outputs.cidv1 }}.ipfs.dweb.link/
|
||||
- https://${{ steps.convert_cidv0.outputs.cidv1 }}.ipfs.cf-ipfs.com/
|
||||
- [ipfs://${{ steps.upload.outputs.hash }}/](ipfs://${{ steps.upload.outputs.hash }}/)
|
||||
- https://${{ steps.convert-cidv0.outputs.cidv1 }}.ipfs.dweb.link/
|
||||
- https://${{ steps.convert-cidv0.outputs.cidv1 }}.ipfs.cf-ipfs.com/
|
||||
- [ipfs://${{ steps.upload.outputs.hash }}/](ipfs://${{ steps.pinata.outputs.hash }}/)
|
||||
|
||||
${{ needs.bump_version.outputs.changelog }}
|
||||
${{ needs.tag.outputs.changelog }}
|
||||
|
||||
99
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
# manual trigger
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: yarn lint
|
||||
|
||||
deps-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: npx yarn-deduplicate --strategy=highest --list --fail
|
||||
|
||||
unit-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: yarn prepare
|
||||
- run: yarn test
|
||||
|
||||
cypress-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: yarn prepare
|
||||
|
||||
- run: yarn build
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: build
|
||||
path: build
|
||||
if-no-files-found: error
|
||||
|
||||
- uses: actions/cache@v3
|
||||
id: cypress-cache
|
||||
with:
|
||||
path: /home/runner/.cache/Cypress
|
||||
key: ${{ runner.os }}-cypress-${{ hashFiles('node_modules/cypress') }}
|
||||
- if: steps.cypress-cache.outputs.cache-hit != 'true'
|
||||
run: yarn cypress install
|
||||
|
||||
cypress-test-matrix:
|
||||
needs: cypress-build
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
containers: [1, 2, 3, 4]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: build
|
||||
path: build
|
||||
|
||||
- uses: actions/cache@v3
|
||||
id: cypress-cache
|
||||
with:
|
||||
path: /home/runner/.cache/Cypress
|
||||
key: ${{ runner.os }}-cypress-${{ hashFiles('node_modules/cypress') }}
|
||||
- if: steps.cypress-cache.outputs.cache-hit != 'true'
|
||||
run: yarn cypress install
|
||||
|
||||
- uses: cypress-io/github-action@v4
|
||||
with:
|
||||
install: false
|
||||
start: yarn serve
|
||||
wait-on: 'http://localhost:3000'
|
||||
browser: chrome
|
||||
record: true
|
||||
parallel: true
|
||||
env:
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Included as a single job to check against for cypress test success, as cypress runs in a matrix.
|
||||
cypress-tests:
|
||||
needs: cypress-test-matrix
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo 'Finished cypress tests https\://dashboard.cypress.io/projects/yp82ef'
|
||||
40
.github/workflows/unit-tests.yaml
vendored
@@ -1,40 +0,0 @@
|
||||
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: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v2
|
||||
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-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Run unit tests
|
||||
run: yarn test
|
||||
13
.gitignore
vendored
@@ -3,9 +3,6 @@
|
||||
# generated contract types
|
||||
/src/types/v3
|
||||
/src/abis/types
|
||||
/src/lib/locales/**/*.js
|
||||
/src/lib/locales/**/en-US.po
|
||||
/src/lib/locales/**/pseudo.po
|
||||
/src/locales/**/*.js
|
||||
/src/locales/**/en-US.po
|
||||
/src/locales/**/pseudo.po
|
||||
@@ -17,11 +14,9 @@
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
# builds
|
||||
/build
|
||||
|
||||
# widgets
|
||||
/dist
|
||||
/dts
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
@@ -39,10 +34,8 @@ yarn-error.log*
|
||||
notes.txt
|
||||
.idea/
|
||||
|
||||
.vscode/
|
||||
|
||||
package-lock.json
|
||||
|
||||
cypress/videos
|
||||
cypress/screenshots
|
||||
cypress/fixtures/example.json
|
||||
|
||||
|
||||
19
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"npm.packageManager": "yarn",
|
||||
"typescript.updateImportsOnFileMove.enabled": "always",
|
||||
"javascript.updateImportsOnFileMove.enabled": "always",
|
||||
"editor.formatOnSaveMode": "file",
|
||||
"editor.tabCompletion": "on",
|
||||
"editor.tabSize": 2,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.inlineSuggest.enabled": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": true
|
||||
},
|
||||
"files.eol": "\n",
|
||||
"eslint.enable": true,
|
||||
"eslint.debug": true,
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,73 @@
|
||||
|
||||
# Contributing
|
||||
|
||||
Thank you for your interest in contributing to the Uniswap interface! 🦄
|
||||
|
||||
# Development
|
||||
|
||||
Before running anything, you'll need to install the dependencies:
|
||||
|
||||
```
|
||||
yarn install
|
||||
```
|
||||
|
||||
## Running the interface locally
|
||||
|
||||
1. `yarn install`
|
||||
1. `yarn start`
|
||||
```
|
||||
yarn start
|
||||
```
|
||||
|
||||
The interface should automatically open. If it does not, navigate to [http://localhost:3000].
|
||||
|
||||
## Creating a production build
|
||||
|
||||
1. `yarn install`
|
||||
1. `yarn build`
|
||||
```
|
||||
yarn build
|
||||
```
|
||||
|
||||
To serve the production build:
|
||||
|
||||
```
|
||||
yarn serve
|
||||
```
|
||||
|
||||
Then, navigate to [http://localhost:3000] to see it.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
```
|
||||
yarn test
|
||||
```
|
||||
|
||||
By default, this runs only unit tests that have been affected since the last commit. To run _all_ unit tests:
|
||||
|
||||
```
|
||||
yarn test --watchAll
|
||||
```
|
||||
|
||||
## Running integration tests (cypress)
|
||||
|
||||
Integration tests require a server to be running. In order to see your changes quickly, run `start` in its own tab/window:
|
||||
|
||||
```
|
||||
yarn start
|
||||
```
|
||||
|
||||
Integration tests are run using `cypress`. When developing locally, use `cypress:open` for an interactive UI, and to inspect the rendered page:
|
||||
|
||||
```
|
||||
yarn cypress:open
|
||||
```
|
||||
|
||||
To run _all_ cypress integration tests _from the command line_:
|
||||
|
||||
```
|
||||
yarn cypress:run
|
||||
```
|
||||
|
||||
## Engineering standards
|
||||
|
||||
Code merged into the `main` branch of this repository should adhere to high standards of correctness and maintainability.
|
||||
Use your best judgment when applying these standards. If code is in the critical path, will be frequently visited, or
|
||||
Code merged into the `main` branch of this repository should adhere to high standards of correctness and maintainability.
|
||||
Use your best judgment when applying these standards. If code is in the critical path, will be frequently visited, or
|
||||
makes large architectural changes, consider following all the standards.
|
||||
|
||||
- Have at least one engineer approve of large code refactorings
|
||||
@@ -39,7 +88,7 @@ The following points should help guide your development:
|
||||
- Avoid adding steps to the development/build processes
|
||||
- The build must be deterministic, i.e. a particular commit hash always produces the same build
|
||||
- Decentralization: anyone can run the interface
|
||||
- An Ethereum node should be the only critical dependency
|
||||
- An Ethereum node should be the only critical dependency
|
||||
- All other external dependencies should only enhance the UX ([graceful degradation](https://developer.mozilla.org/en-US/docs/Glossary/Graceful_degradation))
|
||||
- Accessibility: anyone can use the interface
|
||||
- The interface should be responsive, small and also run well on low performance devices (majority of swaps on mobile!)
|
||||
@@ -48,14 +97,14 @@ The following points should help guide your development:
|
||||
|
||||
Releases are cut automatically from the `main` branch Monday-Thursday in the morning according to the [release workflow](./.github/workflows/release.yaml).
|
||||
|
||||
Fix pull requests should be merged whenever ready and tested.
|
||||
Fix pull requests should be merged whenever ready and tested.
|
||||
If a fix is urgently needed in production, releases can be manually triggered on [GitHub](https://github.com/Uniswap/uniswap-interface/actions/workflows/release.yaml)
|
||||
after the fix is merged into `main`.
|
||||
|
||||
Features should not be merged into `main` until they are ready for users.
|
||||
When building larger features or collaborating with other developers, create a new branch from `main` to track its development.
|
||||
Use the automatic Vercel preview for sharing the feature to collect feedback.
|
||||
When the feature is ready for review, create a new pull request from the feature branch into `main` and request reviews from
|
||||
When the feature is ready for review, create a new pull request from the feature branch into `main` and request reviews from
|
||||
the appropriate UX reviewers (PMs or designers).
|
||||
|
||||
## Finding a first issue
|
||||
@@ -65,7 +114,7 @@ Start with issues with the label
|
||||
|
||||
# Translations
|
||||
|
||||
Uniswap uses [Crowdin](https://crowdin.com/project/uniswap-interface) for managing translations.
|
||||
Uniswap uses [Crowdin](https://crowdin.com/project/uniswap-interface) for managing translations.
|
||||
[This workflow](./.github/workflows/crowdin.yaml) uploads new strings for translation to the Crowdin project whenever code using the [lingui translation macros](https://lingui.js.org/ref/macro.html) is merged into `main`.
|
||||
|
||||
Every hour, translations are synced back down from Crowdin to the repository in [this other workflow](./.github/workflows/crowdin-sync.yaml).
|
||||
|
||||
10
README.md
@@ -1,9 +1,9 @@
|
||||
# Uniswap Interface
|
||||
# Uniswap Labs Interface
|
||||
|
||||
[](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)
|
||||
[](https://github.com/Uniswap/interface/actions/workflows/unit-tests.yaml)
|
||||
[](https://github.com/Uniswap/interface/actions/workflows/integration-tests.yaml)
|
||||
[](https://github.com/Uniswap/interface/actions/workflows/lint.yml)
|
||||
[](https://github.com/Uniswap/interface/actions/workflows/release.yaml)
|
||||
[](https://crowdin.com/project/uniswap-interface)
|
||||
|
||||
An open source interface for Uniswap -- a protocol for decentralized exchange of Ethereum tokens.
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"watchDirs": [
|
||||
"src"
|
||||
],
|
||||
"webpack": {
|
||||
"configPath": "react-scripts/config/webpack.config",
|
||||
"overridePath": "cosmos.override.js"
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
|
||||
// Renders the cosmos fixtures in isolation, instead of using public/index.html.
|
||||
module.exports = (webpackConfig) => ({
|
||||
...webpackConfig,
|
||||
plugins: webpackConfig.plugins.map((plugin) =>
|
||||
plugin instanceof HtmlWebpackPlugin
|
||||
? new HtmlWebpackPlugin({
|
||||
templateContent: '<body></body>',
|
||||
})
|
||||
: plugin
|
||||
),
|
||||
})
|
||||
@@ -1,16 +0,0 @@
|
||||
// Custom test environment to provide `TextEncoder`/`TextDecoder`
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const Environment = require('jest-environment-jsdom')
|
||||
|
||||
module.exports = class CustomTestEnvironment extends Environment {
|
||||
async setup() {
|
||||
await super.setup()
|
||||
if (typeof this.global.TextEncoder === 'undefined') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { TextEncoder, TextDecoder } = require('util')
|
||||
this.global.TextEncoder = TextEncoder
|
||||
this.global.TextDecoder = TextDecoder
|
||||
}
|
||||
}
|
||||
}
|
||||
20
cypress.config.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { defineConfig } from 'cypress'
|
||||
|
||||
export default defineConfig({
|
||||
projectId: 'yp82ef',
|
||||
videoUploadOnPasses: false,
|
||||
defaultCommandTimeout: 10000,
|
||||
chromeWebSecurity: false,
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
return {
|
||||
...config,
|
||||
// Only enable Chrome.
|
||||
// Electron (the default) has issues injecting window.ethereum before pageload, so it is not viable.
|
||||
browsers: config.browsers.filter(({ name }) => name === 'chrome'),
|
||||
}
|
||||
},
|
||||
baseUrl: 'http://localhost:3000',
|
||||
specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
|
||||
},
|
||||
})
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"projectId": "yp82ef",
|
||||
"baseUrl": "http://localhost:3000",
|
||||
"pluginsFile": false,
|
||||
"supportFile": "cypress/support/index.js",
|
||||
"video": false,
|
||||
"defaultCommandTimeout": 10000
|
||||
}
|
||||
8
cypress.release.config.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { defineConfig } from 'cypress'
|
||||
|
||||
export default defineConfig({
|
||||
projectId: 'yp82ef',
|
||||
e2e: {
|
||||
specPattern: 'cypress/release.ts',
|
||||
},
|
||||
})
|
||||
@@ -1,5 +1,3 @@
|
||||
import { TEST_ADDRESS_NEVER_USE_SHORTENED } from '../support/commands'
|
||||
|
||||
describe('Landing Page', () => {
|
||||
beforeEach(() => cy.visit('/'))
|
||||
it('loads swap page', () => {
|
||||
@@ -15,9 +13,4 @@ describe('Landing Page', () => {
|
||||
cy.get('#pool-nav-link').click()
|
||||
cy.url().should('include', '/pool')
|
||||
})
|
||||
|
||||
it('is connected', () => {
|
||||
cy.get('#web3-status-connected').click()
|
||||
cy.get('#web3-account-identifier-row').contains(TEST_ADDRESS_NEVER_USE_SHORTENED)
|
||||
})
|
||||
})
|
||||
8
cypress/e2e/link.test.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// see https://github.com/Uniswap/interface/pull/4115
|
||||
describe('Link', () => {
|
||||
it('should update route', () => {
|
||||
cy.visit('/')
|
||||
cy.get('[data-cy="pool-nav-link"]').click()
|
||||
cy.get('[data-cy="join-pool-button"]').should('exist')
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,6 @@
|
||||
describe('Pool', () => {
|
||||
beforeEach(() => cy.visit('/pool'))
|
||||
|
||||
it('add liquidity links to /add/ETH', () => {
|
||||
cy.get('#join-pool-button').click()
|
||||
cy.url().should('contain', '/add/ETH')
|
||||
84
cypress/e2e/service-worker.test.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import assert = require('assert')
|
||||
|
||||
describe('Service Worker', () => {
|
||||
before(() => {
|
||||
// Fail fast if there is no Service Worker on this build.
|
||||
cy.request({ url: '/service-worker.js', headers: { 'Service-Worker': 'script' } }).then((response) => {
|
||||
const isValid = isValidServiceWorker(response)
|
||||
if (!isValid) {
|
||||
throw new Error(
|
||||
'\n' +
|
||||
'Service Worker tests must be run on a production-like build\n' +
|
||||
'To test, build with `yarn build:e2e` and serve with `yarn serve`'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
function isValidServiceWorker(response: Cypress.Response<any>) {
|
||||
const contentType = response.headers['content-type']
|
||||
return !(response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1))
|
||||
}
|
||||
})
|
||||
|
||||
function unregister() {
|
||||
return cy.log('unregister service worker').then(async () => {
|
||||
const cacheKeys = await window.caches.keys()
|
||||
const cacheKey = cacheKeys.find((key) => key.match(/precache/))
|
||||
if (cacheKey) {
|
||||
await window.caches.delete(cacheKey)
|
||||
}
|
||||
|
||||
const sw = await window.navigator.serviceWorker.getRegistration(Cypress.config().baseUrl ?? undefined)
|
||||
await sw?.unregister()
|
||||
})
|
||||
}
|
||||
before(unregister)
|
||||
after(unregister)
|
||||
|
||||
beforeEach(() => {
|
||||
cy.intercept({ hostname: 'www.google-analytics.com' }, (req) => {
|
||||
const body = req.body.toString()
|
||||
if (req.query['ep.event_category'] === 'Service Worker' || body.includes('Service%20Worker')) {
|
||||
if (req.query['en'] === 'Not Installed' || body.includes('Not%20Installed')) {
|
||||
req.alias = 'NotInstalled'
|
||||
} else if (req.query['en'] === 'Cache Hit' || body.includes('Cache%20Hit')) {
|
||||
req.alias = 'CacheHit'
|
||||
} else if (req.query['en'] === 'Cache Miss' || body.includes('Cache%20Miss')) {
|
||||
req.alias = 'CacheMiss'
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('installs a ServiceWorker', () => {
|
||||
cy.visit('/', { serviceWorker: true })
|
||||
.get('#swap-page')
|
||||
.wait('@NotInstalled', { timeout: 20000 })
|
||||
.window({ timeout: 20000 })
|
||||
.and((win) => {
|
||||
expect(win.navigator.serviceWorker.controller?.state).to.equal('activated')
|
||||
})
|
||||
})
|
||||
|
||||
it('records a cache hit', () => {
|
||||
cy.visit('/', { serviceWorker: true }).get('#swap-page').wait('@CacheHit', { timeout: 20000 })
|
||||
})
|
||||
|
||||
it('records a cache miss', () => {
|
||||
cy.then(async () => {
|
||||
const cacheKeys = await window.caches.keys()
|
||||
const cacheKey = cacheKeys.find((key) => key.match(/precache/))
|
||||
assert(cacheKey)
|
||||
|
||||
const cache = await window.caches.open(cacheKey)
|
||||
const keys = await cache.keys()
|
||||
const key = keys.find((key) => key.url.match(/index/))
|
||||
assert(key)
|
||||
|
||||
await cache.put(key, new Response())
|
||||
})
|
||||
.visit('/', { serviceWorker: true })
|
||||
.get('#swap-page')
|
||||
.wait('@CacheMiss', { timeout: 20000 })
|
||||
})
|
||||
})
|
||||
53
cypress/e2e/swap.test.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
describe('Swap', () => {
|
||||
before(() => {
|
||||
cy.visit('/swap')
|
||||
})
|
||||
|
||||
it('starts with ETH selected by default', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input').should('have.value', '')
|
||||
cy.get('#swap-currency-input .token-symbol-container').should('contain.text', 'ETH')
|
||||
cy.get('#swap-currency-output .token-amount-input').should('not.have.value')
|
||||
cy.get('#swap-currency-output .token-symbol-container').should('contain.text', 'Select a token')
|
||||
})
|
||||
|
||||
it('can enter an amount into input', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('0.001').should('have.value', '0.001')
|
||||
})
|
||||
|
||||
it('zero swap amount', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('0.0').should('have.value', '0.0')
|
||||
})
|
||||
|
||||
it('invalid swap amount', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('\\').should('have.value', '')
|
||||
})
|
||||
|
||||
it('can enter an amount into output', () => {
|
||||
cy.get('#swap-currency-output .token-amount-input').clear().type('0.001').should('have.value', '0.001')
|
||||
})
|
||||
|
||||
it('zero output amount', () => {
|
||||
cy.get('#swap-currency-output .token-amount-input').clear().type('0.0').should('have.value', '0.0')
|
||||
})
|
||||
|
||||
it.skip('can swap ETH for DAI', () => {
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
cy.get('.token-item-0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735').click()
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('0.0000001')
|
||||
cy.get('#swap-currency-output .token-amount-input').should('not.equal', '')
|
||||
cy.get('#swap-button').click()
|
||||
cy.get('#confirm-swap-or-send').should('contain', 'Confirm Swap')
|
||||
cy.get('[data-cy="confirmation-close-icon"]').click()
|
||||
})
|
||||
|
||||
it('add a recipient does not exist unless in expert mode', () => {
|
||||
cy.get('#add-recipient-button').should('not.exist')
|
||||
})
|
||||
|
||||
it.skip('ETH to wETH is same value (wrapped swaps have no price impact)', () => {
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
cy.get('.token-item-0xc778417E063141139Fce010982780140Aa0cD5Ab').click()
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('0.01')
|
||||
cy.get('#swap-currency-output .token-amount-input').should('have.value', '0.01')
|
||||
})
|
||||
})
|
||||
30
cypress/e2e/wallet.test.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { TEST_ADDRESS_NEVER_USE_SHORTENED } from '../support/ethereum'
|
||||
|
||||
describe('Wallet', () => {
|
||||
before(() => {
|
||||
cy.visit('/')
|
||||
})
|
||||
|
||||
it('displays account details', () => {
|
||||
cy.get('[data-testid=web3-status-connected]').contains(TEST_ADDRESS_NEVER_USE_SHORTENED).click()
|
||||
})
|
||||
|
||||
it('displays account view in wallet modal', () => {
|
||||
cy.get('[data-testid=web3-account-identifier-row]').contains(TEST_ADDRESS_NEVER_USE_SHORTENED)
|
||||
})
|
||||
|
||||
it('changes back to the options grid', () => {
|
||||
cy.contains('Change').click()
|
||||
cy.get('[data-testid=option-grid]').should('exist')
|
||||
})
|
||||
|
||||
it('selects injected wallet option', () => {
|
||||
cy.contains('Injected').click()
|
||||
cy.get('[data-testid=web3-account-identifier-row]').contains(TEST_ADDRESS_NEVER_USE_SHORTENED)
|
||||
})
|
||||
|
||||
it('shows connect buttons after disconnect', () => {
|
||||
cy.contains('Disconnect').click()
|
||||
cy.get('[data-testid=option-grid]').should('exist')
|
||||
})
|
||||
})
|
||||
@@ -1,65 +0,0 @@
|
||||
describe('Swap', () => {
|
||||
beforeEach(() => {
|
||||
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')
|
||||
})
|
||||
|
||||
it('zero swap amount', () => {
|
||||
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', '')
|
||||
})
|
||||
|
||||
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')
|
||||
})
|
||||
|
||||
it('zero output amount', () => {
|
||||
cy.get('#swap-currency-output .token-amount-input').type('0.0', { delay: 200 }).should('have.value', '0.0')
|
||||
})
|
||||
|
||||
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 })
|
||||
cy.get('#swap-currency-input .token-amount-input').should('be.visible')
|
||||
cy.get('#swap-currency-input .token-amount-input').type('0.001', { force: true, delay: 200 })
|
||||
cy.get('#swap-currency-output .token-amount-input').should('not.equal', '')
|
||||
cy.get('#swap-button').click()
|
||||
cy.get('#confirm-swap-or-send').should('contain', 'Confirm Swap')
|
||||
})
|
||||
|
||||
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.stub(win, 'prompt').returns('confirm')
|
||||
})
|
||||
cy.get('#open-settings-dialog-button').click()
|
||||
cy.get('#toggle-expert-mode-button').click()
|
||||
cy.get('#confirm-expert-mode').click()
|
||||
})
|
||||
|
||||
it.skip('add a recipient is visible', () => {
|
||||
cy.get('#add-recipient-button').should('be.visible')
|
||||
})
|
||||
|
||||
it.skip('add a recipient', () => {
|
||||
cy.get('#add-recipient-button').click()
|
||||
cy.get('#recipient').should('exist')
|
||||
})
|
||||
|
||||
it.skip('remove recipient', () => {
|
||||
cy.get('#add-recipient-button').click()
|
||||
cy.get('#remove-recipient-button').click()
|
||||
cy.get('#recipient').should('not.exist')
|
||||
})
|
||||
})
|
||||
})
|
||||
20
cypress/release.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
const ONE_MINUTE = 60_000
|
||||
|
||||
describe(
|
||||
'Release',
|
||||
{
|
||||
pageLoadTimeout: ONE_MINUTE,
|
||||
retries: 30,
|
||||
},
|
||||
() => {
|
||||
it('loads swap page', () => {
|
||||
// We *must* wait in order to space out the retry attempts.
|
||||
cy.wait(ONE_MINUTE)
|
||||
.visit('/', {
|
||||
retryOnStatusCodeFailure: true,
|
||||
retryOnNetworkFailure: true,
|
||||
})
|
||||
.get('#swap-page')
|
||||
})
|
||||
}
|
||||
)
|
||||
10
cypress/support/commands.d.ts
vendored
@@ -1,10 +0,0 @@
|
||||
export const TEST_ADDRESS_NEVER_USE: string
|
||||
|
||||
export const TEST_ADDRESS_NEVER_USE_SHORTENED: string
|
||||
|
||||
// declare namespace Cypress {
|
||||
// // eslint-disable-next-line @typescript-eslint/class-name-casing
|
||||
// interface cy {
|
||||
// additionalCommands(): void
|
||||
// }
|
||||
// }
|
||||
58
cypress/support/e2e.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
// ***********************************************************
|
||||
// This file is processed and loaded automatically before your test files.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.ts using ES2015 syntax:
|
||||
import { injected } from './ethereum'
|
||||
import assert = require('assert')
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface ApplicationWindow {
|
||||
ethereum: typeof injected
|
||||
}
|
||||
interface VisitOptions {
|
||||
serviceWorker?: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sets up the injected provider to be a mock ethereum provider with the given mnemonic/index
|
||||
// eslint-disable-next-line no-undef
|
||||
Cypress.Commands.overwrite(
|
||||
'visit',
|
||||
(original, url: string | Partial<Cypress.VisitOptions>, options?: Partial<Cypress.VisitOptions>) => {
|
||||
assert(typeof url === 'string')
|
||||
|
||||
cy.intercept('/service-worker.js', options?.serviceWorker ? undefined : { statusCode: 404 }).then(() => {
|
||||
original({
|
||||
...options,
|
||||
url: (url.startsWith('/') && url.length > 2 && !url.startsWith('/#') ? `/#${url}` : url) + '?chain=rinkeby',
|
||||
onBeforeLoad(win) {
|
||||
options?.onBeforeLoad?.(win)
|
||||
win.localStorage.clear()
|
||||
win.ethereum = injected
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
// Infura security policies are based on Origin headers.
|
||||
// These are stripped by cypress because chromeWebSecurity === false; this adds them back in.
|
||||
cy.intercept(/infura.io/, (res) => {
|
||||
res.headers['origin'] = 'http://localhost:3000'
|
||||
res.continue()
|
||||
})
|
||||
})
|
||||
|
||||
Cypress.on('uncaught:exception', (_err, _runnable) => {
|
||||
// returning false here prevents Cypress from
|
||||
// failing the test
|
||||
return false
|
||||
})
|
||||
@@ -1,8 +1,6 @@
|
||||
// ***********************************************
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
/**
|
||||
* Updates cy.visit() to include an injected window.ethereum provider.
|
||||
*/
|
||||
|
||||
import { Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
|
||||
import { JsonRpcProvider } from '@ethersproject/providers'
|
||||
@@ -20,14 +18,16 @@ export const TEST_ADDRESS_NEVER_USE_SHORTENED = `${TEST_ADDRESS_NEVER_USE.substr
|
||||
6
|
||||
)}...${TEST_ADDRESS_NEVER_USE.substr(-4, 4)}`
|
||||
|
||||
class CustomizedBridge extends Eip1193Bridge {
|
||||
const provider = new JsonRpcProvider('https://rinkeby.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847', 4)
|
||||
const signer = new Wallet(TEST_PRIVATE_KEY, provider)
|
||||
export const injected = new (class extends Eip1193Bridge {
|
||||
chainId = 4
|
||||
|
||||
async sendAsync(...args) {
|
||||
async sendAsync(...args: any[]) {
|
||||
console.debug('sendAsync called', ...args)
|
||||
return this.send(...args)
|
||||
}
|
||||
async send(...args) {
|
||||
async send(...args: any[]) {
|
||||
console.debug('send called', ...args)
|
||||
const isCallbackForm = typeof args[0] === 'object' && typeof args[1] === 'function'
|
||||
let callback
|
||||
@@ -71,19 +71,4 @@ class CustomizedBridge extends Eip1193Bridge {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sets up the injected provider to be a mock ethereum provider with the given mnemonic/index
|
||||
// eslint-disable-next-line no-undef
|
||||
Cypress.Commands.overwrite('visit', (original, url, options) => {
|
||||
return original(url.startsWith('/') && url.length > 2 && !url.startsWith('/#') ? `/#${url}` : url, {
|
||||
...options,
|
||||
onBeforeLoad(win) {
|
||||
options && options.onBeforeLoad && options.onBeforeLoad(win)
|
||||
win.localStorage.clear()
|
||||
const provider = new JsonRpcProvider('https://rinkeby.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847', 4)
|
||||
const signer = new Wallet(TEST_PRIVATE_KEY, provider)
|
||||
win.ethereum = new CustomizedBridge(signer, provider)
|
||||
},
|
||||
})
|
||||
})
|
||||
})(signer, provider)
|
||||
@@ -1,9 +0,0 @@
|
||||
// ***********************************************************
|
||||
// This file is processed and loaded automatically before your test files.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.ts using ES2015 syntax:
|
||||
import './commands'
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"baseUrl": "../node_modules",
|
||||
"target": "es5",
|
||||
"lib": ["es5", "dom"],
|
||||
"types": ["cypress"]
|
||||
|
||||
309
package.json
@@ -1,145 +1,46 @@
|
||||
{
|
||||
"name": "@uniswap/interface",
|
||||
"version": "1.0.7",
|
||||
"description": "Uniswap Interface",
|
||||
"homepage": ".",
|
||||
"main": "dist/interface.js",
|
||||
"module": "dist/interface.esm.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"lib",
|
||||
"dist"
|
||||
],
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@ethersproject/experimental": "^5.4.0",
|
||||
"@gnosis.pm/safe-apps-web3-react": "^0.6.0",
|
||||
"@graphql-codegen/cli": "1.21.5",
|
||||
"@graphql-codegen/typescript": "1.22.3",
|
||||
"@graphql-codegen/typescript-operations": "^1.18.2",
|
||||
"@graphql-codegen/typescript-rtk-query": "^1.1.1",
|
||||
"@lingui/cli": "^3.9.0",
|
||||
"@metamask/jazzicon": "^2.0.0",
|
||||
"@reach/dialog": "^0.10.3",
|
||||
"@reach/portal": "^0.10.3",
|
||||
"@react-hook/window-scroll": "^1.3.0",
|
||||
"@reduxjs/toolkit": "^1.6.1",
|
||||
"@rollup/plugin-eslint": "^8.0.1",
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@rollup/plugin-replace": "^3.0.1",
|
||||
"@rollup/plugin-url": "^6.1.0",
|
||||
"@svgr/rollup": "^6.2.0",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^12.0.0",
|
||||
"@testing-library/react-hooks": "^7.0.2",
|
||||
"@typechain/ethers-v5": "^7.0.0",
|
||||
"@types/array.prototype.flat": "^1.2.1",
|
||||
"@types/array.prototype.flatmap": "^1.2.2",
|
||||
"@types/d3": "^6.7.1",
|
||||
"@types/jest": "^25.2.1",
|
||||
"@types/lingui__core": "^2.7.1",
|
||||
"@types/lingui__macro": "^2.7.4",
|
||||
"@types/lingui__react": "^2.8.3",
|
||||
"@types/ms.macro": "^2.0.0",
|
||||
"@types/multicodec": "^1.0.0",
|
||||
"@types/node": "^13.13.5",
|
||||
"@types/qs": "^6.9.2",
|
||||
"@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.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": "^4.1.0",
|
||||
"@typescript-eslint/parser": "^4.1.0",
|
||||
"@uniswap/governance": "^1.0.2",
|
||||
"@uniswap/liquidity-staker": "^1.0.2",
|
||||
"@uniswap/merkle-distributor": "1.0.1",
|
||||
"@uniswap/router-sdk": "^1.0.3",
|
||||
"@uniswap/smart-order-router": "^2.5.10",
|
||||
"@uniswap/v2-core": "1.0.0",
|
||||
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
||||
"@uniswap/v3-core": "1.0.0",
|
||||
"@uniswap/v3-periphery": "^1.1.1",
|
||||
"@web3-react/fortmatic-connector": "^6.0.9",
|
||||
"@web3-react/injected-connector": "^6.0.7",
|
||||
"@web3-react/portis-connector": "^6.0.9",
|
||||
"@web3-react/walletconnect-connector": "^7.0.2-alpha.0",
|
||||
"@web3-react/walletlink-connector": "^6.2.8",
|
||||
"array.prototype.flat": "^1.2.4",
|
||||
"array.prototype.flatmap": "^1.2.4",
|
||||
"copy-to-clipboard": "^3.2.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"cypress": "^7.7.0",
|
||||
"d3": "^7.0.0",
|
||||
"eslint": "^7.11.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-better-styled-components": "^1.1.2",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"eslint-plugin-react": "^7.19.0",
|
||||
"eslint-plugin-react-hooks": "^4.0.0",
|
||||
"eslint-plugin-simple-import-sort": "^7.0.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"firebase": "^9.1.3",
|
||||
"graphql": "^15.5.0",
|
||||
"graphql-request": "^3.4.0",
|
||||
"inter-ui": "^3.13.1",
|
||||
"jest-styled-components": "^7.0.5",
|
||||
"ms.macro": "^2.0.0",
|
||||
"polyfill-object.fromentries": "^1.0.1",
|
||||
"prettier": "^2.2.1",
|
||||
"qs": "^6.9.4",
|
||||
"react-confetti": "^6.0.0",
|
||||
"react-cosmos": "^5.6.3",
|
||||
"react-ga": "^2.5.7",
|
||||
"react-is": "^17.0.2",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-router-dom": "^5.0.0",
|
||||
"react-scripts": "^4.0.3",
|
||||
"react-spring": "^8.0.27",
|
||||
"react-use-gesture": "^6.0.14",
|
||||
"redux-localstorage-simple": "^2.3.1",
|
||||
"rollup": "^2.63.0",
|
||||
"rollup-plugin-dts": "^4.1.0",
|
||||
"rollup-plugin-scss": "^3.0.0",
|
||||
"rollup-plugin-typescript2": "^0.31.1",
|
||||
"sass": "^1.45.1",
|
||||
"serve": "^11.3.2",
|
||||
"start-server-and-test": "^1.11.0",
|
||||
"typechain": "^5.0.0",
|
||||
"typescript": "^4.2.3",
|
||||
"ua-parser-js": "^0.7.28",
|
||||
"use-count-up": "^2.2.5",
|
||||
"use-resize-observer": "^8.0.0",
|
||||
"wcag-contrast": "^3.0.0",
|
||||
"web-vitals": "^2.1.0",
|
||||
"workbox-core": "^6.1.0",
|
||||
"workbox-precaching": "^6.1.0",
|
||||
"workbox-routing": "^6.1.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"@walletconnect/ethereum-provider": "1.7.1"
|
||||
},
|
||||
"license": "GPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"contracts:compile:abi": "typechain --target ethers-v5 --out-dir src/abis/types \"./src/abis/**/*.json\"",
|
||||
"contracts:compile:v3": "typechain --target ethers-v5 --out-dir src/types/v3 \"./node_modules/@uniswap/**/artifacts/contracts/**/*.json\"",
|
||||
"contracts:compile:v3": "typechain --target ethers-v5 --out-dir src/types/v3 \"./node_modules/@uniswap/**/artifacts/contracts/**/*[!dbg].json\"",
|
||||
"contracts:compile": "yarn contracts:compile:abi && yarn contracts:compile:v3",
|
||||
"graphql:generate": "graphql-codegen --config codegen.yml",
|
||||
"prei18n:extract": "touch src/locales/en-US.po",
|
||||
"prei18n:extract": "node prei18n-extract.js",
|
||||
"i18n:extract": "lingui extract --locale en-US",
|
||||
"i18n:compile": "yarn i18n:extract && lingui compile",
|
||||
"i18n:pseudo": "lingui extract --locale pseudo && lingui compile",
|
||||
"postinstall": "yarn contracts:compile && yarn graphql:generate && yarn i18n:compile",
|
||||
"prepare": "yarn contracts:compile && yarn graphql:generate && yarn i18n:compile",
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=./custom-test-env.js",
|
||||
"test:e2e": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run --record'",
|
||||
"widgets:start": "cross-env FAST_REFRESH=false REACT_APP_IS_WIDGET=true cosmos",
|
||||
"widgets:build": "rollup --config --failAfterWarnings --configPlugin typescript2"
|
||||
"serve": "serve build -l 3000",
|
||||
"lint": "yarn eslint .",
|
||||
"test": "react-scripts test --coverage",
|
||||
"cypress:open": "cypress open --browser chrome --e2e",
|
||||
"cypress:run": "cypress run --browser chrome --e2e"
|
||||
},
|
||||
"jest": {
|
||||
"collectCoverageFrom": [
|
||||
"src/components/**/*.ts*",
|
||||
"src/hooks/**/*.ts*",
|
||||
"src/lib/hooks/**/*.ts*",
|
||||
"src/lib/state/**/*.ts*",
|
||||
"src/lib/utils/**/*.ts*",
|
||||
"src/pages/**/*.ts*",
|
||||
"src/state/**/*.ts*",
|
||||
"src/utils/**/*.ts*"
|
||||
],
|
||||
"coverageThreshold": {
|
||||
"global": {
|
||||
"branches": 4,
|
||||
"functions": 6,
|
||||
"lines": 9,
|
||||
"statements": 9
|
||||
}
|
||||
}
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
@@ -153,36 +54,110 @@
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"license": "GPL-3.0-or-later",
|
||||
"devDependencies": {
|
||||
"@ethersproject/experimental": "^5.4.0",
|
||||
"@graphql-codegen/cli": "1.21.5",
|
||||
"@graphql-codegen/typescript": "1.22.3",
|
||||
"@graphql-codegen/typescript-operations": "^1.18.2",
|
||||
"@graphql-codegen/typescript-rtk-query": "^1.1.1",
|
||||
"@lingui/cli": "^3.9.0",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^13.1",
|
||||
"@typechain/ethers-v5": "^7.0.0",
|
||||
"@types/array.prototype.flat": "^1.2.1",
|
||||
"@types/array.prototype.flatmap": "^1.2.2",
|
||||
"@types/d3": "^6.7.1",
|
||||
"@types/jest": "^25.2.1",
|
||||
"@types/lingui__core": "^2.7.1",
|
||||
"@types/lingui__macro": "^2.7.4",
|
||||
"@types/lingui__react": "^2.8.3",
|
||||
"@types/ms.macro": "^2.0.0",
|
||||
"@types/multicodec": "^1.0.0",
|
||||
"@types/node": "^13.13.5",
|
||||
"@types/qs": "^6.9.2",
|
||||
"@types/react": "^18.0.15",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/react-redux": "^7.1.24",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.0",
|
||||
"@types/react-window": "^1.8.2",
|
||||
"@types/rebass": "^4.0.7",
|
||||
"@types/styled-components": "^5.1.25",
|
||||
"@types/testing-library__cypress": "^5.0.5",
|
||||
"@types/ua-parser-js": "^0.7.35",
|
||||
"@types/wcag-contrast": "^3.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4",
|
||||
"@typescript-eslint/parser": "^4",
|
||||
"cypress": "^10.3.1",
|
||||
"env-cmd": "^10.1.0",
|
||||
"eslint": "^7.11.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-better-styled-components": "^1.1.2",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"eslint-plugin-react": "^7.19.0",
|
||||
"eslint-plugin-react-hooks": "^4.0.0",
|
||||
"eslint-plugin-simple-import-sort": "^7.0.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"jest-styled-components": "^7.0.8",
|
||||
"ms.macro": "^2.0.0",
|
||||
"prettier": "^2.7.1",
|
||||
"react-scripts": "^4.0.3",
|
||||
"serve": "^11.3.2",
|
||||
"typechain": "^5.0.0",
|
||||
"typescript": "^4.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ethersproject/abi": "^5.4.1",
|
||||
"@ethersproject/abstract-provider": "^5.4.1",
|
||||
"@ethersproject/address": "^5.4.0",
|
||||
"@ethersproject/bignumber": "^5.4.2",
|
||||
"@ethersproject/bytes": "^5.4.0",
|
||||
"@ethersproject/constants": "^5.4.0",
|
||||
"@ethersproject/contracts": "^5.4.1",
|
||||
"@ethersproject/hash": "^5.4.0",
|
||||
"@ethersproject/providers": "^5.4.5",
|
||||
"@ethersproject/solidity": "^5.4.0",
|
||||
"@ethersproject/strings": "^5.4.0",
|
||||
"@ethersproject/units": "^5.4.0",
|
||||
"@ethersproject/wallet": "^5.4.0",
|
||||
"@amplitude/analytics-browser": "^0.5.1",
|
||||
"@coinbase/wallet-sdk": "^3.3.0",
|
||||
"@fontsource/ibm-plex-mono": "^4.5.1",
|
||||
"@fontsource/inter": "^4.5.1",
|
||||
"@lingui/core": "^3.9.0",
|
||||
"@lingui/macro": "^3.9.0",
|
||||
"@lingui/react": "^3.9.0",
|
||||
"@lingui/core": "^3.14.0",
|
||||
"@lingui/macro": "^3.14.0",
|
||||
"@lingui/react": "^3.14.0",
|
||||
"@metamask/jazzicon": "^2.0.0",
|
||||
"@popperjs/core": "^2.4.4",
|
||||
"@uniswap/redux-multicall": "^1.0.0",
|
||||
"@reach/dialog": "^0.10.3",
|
||||
"@reach/portal": "^0.10.3",
|
||||
"@react-hook/window-scroll": "^1.3.0",
|
||||
"@reduxjs/toolkit": "^1.6.1",
|
||||
"@uniswap/governance": "^1.0.2",
|
||||
"@uniswap/liquidity-staker": "^1.0.2",
|
||||
"@uniswap/merkle-distributor": "1.0.1",
|
||||
"@uniswap/redux-multicall": "^1.1.5",
|
||||
"@uniswap/router-sdk": "^1.0.6",
|
||||
"@uniswap/sdk-core": "^3.0.1",
|
||||
"@uniswap/token-lists": "^1.0.0-beta.27",
|
||||
"@uniswap/smart-order-router": "^2.5.26",
|
||||
"@uniswap/token-lists": "^1.0.0-beta.30",
|
||||
"@uniswap/v2-core": "1.0.0",
|
||||
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
||||
"@uniswap/v2-sdk": "^3.0.1",
|
||||
"@uniswap/v3-sdk": "^3.7.1",
|
||||
"@web3-react/core": "^6.0.9",
|
||||
"@uniswap/v3-core": "1.0.0",
|
||||
"@uniswap/v3-periphery": "^1.1.1",
|
||||
"@uniswap/v3-sdk": "^3.9.0",
|
||||
"@walletconnect/ethereum-provider": "1.7.1",
|
||||
"@web3-react/coinbase-wallet": "^8.0.34-beta.0",
|
||||
"@web3-react/core": "^8.0.35-beta.0",
|
||||
"@web3-react/eip1193": "^8.0.26-beta.0",
|
||||
"@web3-react/empty": "^8.0.20-beta.0",
|
||||
"@web3-react/gnosis-safe": "^8.0.6-beta.0",
|
||||
"@web3-react/metamask": "^8.0.28-beta.0",
|
||||
"@web3-react/network": "^8.0.27-beta.0",
|
||||
"@web3-react/types": "^8.0.20-beta.0",
|
||||
"@web3-react/url": "^8.0.25-beta.0",
|
||||
"@web3-react/walletconnect": "^8.0.35-beta.0",
|
||||
"ajv": "^6.12.3",
|
||||
"array.prototype.flat": "^1.2.4",
|
||||
"array.prototype.flatmap": "^1.2.4",
|
||||
"cids": "^1.0.0",
|
||||
"copy-to-clipboard": "^3.2.0",
|
||||
"d3": "^7.0.0",
|
||||
"ethers": "^5.1.4",
|
||||
"firebase": "^9.1.3",
|
||||
"fortmatic": "^2.4.0",
|
||||
"graphql": "^15.5.0",
|
||||
"graphql-request": "^3.4.0",
|
||||
"immer": "^9.0.6",
|
||||
"inter-ui": "^3.13.1",
|
||||
"jotai": "^1.3.7",
|
||||
"jsbi": "^3.1.4",
|
||||
"make-plural": "^7.0.0",
|
||||
@@ -190,25 +165,37 @@
|
||||
"multihashes": "^4.0.2",
|
||||
"node-vibrant": "^3.2.1-alpha.1",
|
||||
"polished": "^3.3.2",
|
||||
"polyfill-object.fromentries": "^1.0.1",
|
||||
"popper-max-size-modifier": "^0.2.0",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"qs": "^6.9.4",
|
||||
"react": "^18.2.0",
|
||||
"react-confetti": "^6.0.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-feather": "^2.0.8",
|
||||
"react-ga4": "^1.4.1",
|
||||
"react-is": "^17.0.2",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-popper": "^2.2.3",
|
||||
"react-redux": "^7.2.2",
|
||||
"react-redux": "^8.0.2",
|
||||
"react-router-dom": "^5.3.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": "^4.1.2",
|
||||
"styled-components": "^5.3.0",
|
||||
"redux-localstorage-simple": "^2.3.1",
|
||||
"setimmediate": "^1.0.5",
|
||||
"styled-components": "^5.3.5",
|
||||
"tiny-invariant": "^1.2.0",
|
||||
"ua-parser-js": "^0.7.28",
|
||||
"use-count-up": "^2.2.5",
|
||||
"use-resize-observer": "^9.0.2",
|
||||
"wcag-contrast": "^3.0.0",
|
||||
"wicg-inert": "^3.1.1",
|
||||
"widgets-web3-react/core": "npm:@web3-react/core@8.0.16-alpha.0",
|
||||
"widgets-web3-react/eip1193": "npm:@web3-react/eip1193@8.0.16-alpha.0",
|
||||
"widgets-web3-react/empty": "npm:@web3-react/empty@8.0.17-alpha.0",
|
||||
"widgets-web3-react/metamask": "npm:@web3-react/metamask@8.0.16-alpha.0",
|
||||
"widgets-web3-react/types": "npm:@web3-react/types@8.0.16-alpha.0",
|
||||
"widgets-web3-react/url": "npm:@web3-react/url@8.0.17-alpha.0"
|
||||
"web-vitals": "^2.1.0",
|
||||
"workbox-core": "^6.1.0",
|
||||
"workbox-navigation-preload": "^6.1.0",
|
||||
"workbox-precaching": "^6.1.0",
|
||||
"workbox-routing": "^6.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
9
prei18n-extract.js
Normal file
@@ -0,0 +1,9 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const exec = require('child_process').exec
|
||||
const isWindows = process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE)
|
||||
|
||||
if (isWindows) {
|
||||
exec(`type nul > src/locales/en-US.po`)
|
||||
} else {
|
||||
exec(`touch src/locales/en-US.po`)
|
||||
}
|
||||
@@ -24,83 +24,83 @@
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
|
||||
<link rel="preconnect" href="https://www.google-analytics.com/">
|
||||
<link rel="preconnect" href="https://www.google-analytics.com/" />
|
||||
|
||||
<link rel="preload" href="%PUBLIC_URL%/fonts/Inter-roman.var.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="preload" href="%PUBLIC_URL%/fonts/Inter-roman.var.woff2" as="font" type="font/woff2" crossorigin />
|
||||
|
||||
<style>
|
||||
* {
|
||||
font-family: 'Inter', sans-serif;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
* {
|
||||
font-family: 'Inter', sans-serif;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
Explicitly load Inter var from public/ so it does not block LCP's critical path.
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Inter custom';
|
||||
font-weight: 100 900;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
font-named-instance: 'Regular';
|
||||
src: url(%PUBLIC_URL%/fonts/Inter-roman.var.woff2) format("woff2 supports variations(gvar)"),
|
||||
url(%PUBLIC_URL%/fonts/Inter-roman.var.woff2) format("woff2-variations"),
|
||||
url(%PUBLIC_URL%/fonts/Inter-roman.var.woff2) format("woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Inter custom';
|
||||
font-weight: 100 900;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
font-named-instance: 'Regular';
|
||||
src: url(%PUBLIC_URL%/fonts/Inter-roman.var.woff2) format('woff2 supports variations(gvar)'),
|
||||
url(%PUBLIC_URL%/fonts/Inter-roman.var.woff2) format('woff2-variations'),
|
||||
url(%PUBLIC_URL%/fonts/Inter-roman.var.woff2) format('woff2');
|
||||
}
|
||||
|
||||
@supports (font-variation-settings: normal) {
|
||||
* {
|
||||
font-family: 'Inter custom', sans-serif;
|
||||
}
|
||||
@supports (font-variation-settings: normal) {
|
||||
* {
|
||||
font-family: 'Inter custom', sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
user-select: none;
|
||||
}
|
||||
button {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
font-variant: none;
|
||||
font-smooth: always;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
font-feature-settings: 'ss01' on, 'ss02' on, 'cv01' on, 'cv03' on;
|
||||
}
|
||||
|
||||
#background-radial-gradient {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
pointer-events: none;
|
||||
width: 200vw;
|
||||
height: 200vh;
|
||||
background: radial-gradient(50% 50% at 50% 50%, #fc077d10 0%, rgba(255, 255, 255, 0) 100%);
|
||||
transform: translate(-50vw, -100vh);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
html {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
font-size: 16px;
|
||||
font-variant: none;
|
||||
font-smooth: always;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
font-feature-settings: 'ss01' on, 'ss02' on, 'cv01' on, 'cv03' on;
|
||||
background-color: #212429;
|
||||
}
|
||||
|
||||
#background-radial-gradient {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
pointer-events: none;
|
||||
width: 200vw;
|
||||
height: 200vh;
|
||||
background: radial-gradient(50% 50% at 50% 50%, #fc077d10 0%, rgba(255, 255, 255, 0) 100%);
|
||||
transform: translate(-50vw, -100vh);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
html {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
background-color: #212429;
|
||||
}
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
html {
|
||||
background-color: #F7F8FA;
|
||||
}
|
||||
background-color: #f7f8fa;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/**
|
||||
* Bundles the widgets library, which is released independently of the interface application.
|
||||
* This library lives in src/lib, but shares code with the interface application.
|
||||
*/
|
||||
|
||||
import eslint from '@rollup/plugin-eslint'
|
||||
import json from '@rollup/plugin-json'
|
||||
import replace from '@rollup/plugin-replace'
|
||||
import url from '@rollup/plugin-url'
|
||||
import svgr from '@svgr/rollup'
|
||||
import dts from 'rollup-plugin-dts'
|
||||
import sass from 'rollup-plugin-scss'
|
||||
import typescript from 'rollup-plugin-typescript2'
|
||||
|
||||
import { dependencies } from './package.json'
|
||||
|
||||
const deps = Object.keys(dependencies)
|
||||
|
||||
const replacements = {
|
||||
'process.env.REACT_APP_IS_WIDGET': true,
|
||||
}
|
||||
|
||||
const library = {
|
||||
input: 'src/lib/index.tsx',
|
||||
output: [
|
||||
{
|
||||
file: 'dist/widgets.js',
|
||||
format: 'cjs',
|
||||
inlineDynamicImports: true,
|
||||
sourcemap: true,
|
||||
},
|
||||
{
|
||||
file: 'dist/widgets.esm.js',
|
||||
format: 'esm',
|
||||
inlineDynamicImports: true,
|
||||
sourcemap: true,
|
||||
},
|
||||
],
|
||||
// necessary because some nested imports (eg jotai/*) would otherwise not resolve.
|
||||
external: (source: string) => Boolean(deps.find((dep) => source === dep || source.startsWith(dep + '/'))),
|
||||
plugins: [
|
||||
eslint({ include: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'] }),
|
||||
json(), // imports json
|
||||
replace({ ...replacements, preventAssignment: true }),
|
||||
url(), // imports files (including svgs) as data URIs
|
||||
svgr({ exportType: 'named', svgo: false }), // imports svgs as React components
|
||||
sass(), // imports sass styles
|
||||
typescript({ tsconfig: './tsconfig.lib.json', useTsconfigDeclarationDir: true }),
|
||||
],
|
||||
}
|
||||
|
||||
const typings = {
|
||||
input: 'dist/dts/lib/index.d.ts',
|
||||
output: {
|
||||
file: 'dist/widgets.d.ts',
|
||||
format: 'es',
|
||||
},
|
||||
external: (source: string) => source.endsWith('.scss'),
|
||||
plugins: [dts({ compilerOptions: { baseUrl: 'dist/dts' } })],
|
||||
}
|
||||
|
||||
const config = [library, typings]
|
||||
export default config
|
||||
BIN
src/assets/images/ukraine.png
Normal file
|
After Width: | Height: | Size: 121 KiB |
BIN
src/assets/images/widget-screenshot.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
@@ -1,10 +0,0 @@
|
||||
<svg width="23" height="20" viewBox="0 0 23 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="gradient1" x1="0" y1="0" x2="1" y2="0" gradientTransform="rotate(95)">
|
||||
<stop id="stop1" offset="0" stop-color="#2274E2"/>
|
||||
<stop id="stop1" offset="0.5" stop-color="#2274E2"/>
|
||||
<stop id="stop2" offset="1" stop-color="#3FB672" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path d="M16 16C10 16 9 10 5 10M16 16C16 17.6569 17.3431 19 19 19C20.6569 19 22 17.6569 22 16C22 14.3431 20.6569 13 19 13C17.3431 13 16 14.3431 16 16ZM5 10C9 10 10 4 16 4M5 10H1.5M16 4C16 5.65685 17.3431 7 19 7C20.6569 7 22 5.65685 22 4C22 2.34315 20.6569 1 19 1C17.3431 1 16 2.34315 16 4Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke="url(#gradient1)" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 782 B |
1
src/assets/svg/celo_logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Celo_Rings" data-name="Celo Rings" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 950 950"><defs><style>.cls-1{fill:#fbcc5c;}.cls-2{fill:#35d07f;}.cls-3{fill:#5ea33b;}</style></defs><title>Artboard 1</title><path id="Bottom_Ring" data-name="Bottom Ring" class="cls-1" d="M375,850c151.88,0,275-123.12,275-275S526.88,300,375,300,100,423.12,100,575,223.12,850,375,850Zm0,100C167.9,950,0,782.1,0,575S167.9,200,375,200,750,367.9,750,575,582.1,950,375,950Z"/><path id="Top_Ring" data-name="Top Ring" class="cls-2" d="M575,650c151.88,0,275-123.12,275-275S726.88,100,575,100,300,223.12,300,375,423.12,650,575,650Zm0,100c-207.1,0-375-167.9-375-375S367.9,0,575,0,950,167.9,950,375,782.1,750,575,750Z"/><path id="Rings_Overlap" data-name="Rings Overlap" class="cls-3" d="M587.39,750a274.38,274.38,0,0,0,54.55-108.06A274.36,274.36,0,0,0,750,587.4a373.63,373.63,0,0,1-29.16,133.45A373.62,373.62,0,0,1,587.39,750ZM308.06,308.06A274.36,274.36,0,0,0,200,362.6a373.63,373.63,0,0,1,29.16-133.45A373.62,373.62,0,0,1,362.61,200,274.38,274.38,0,0,0,308.06,308.06Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -1,3 +1,3 @@
|
||||
<svg width="100%" height="35" viewBox="800 0 300 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<line x1="0" x2="2000" y1="100" y2="100" stroke="currentColor" stroke-width="20" stroke-linecap="round" stroke-dasharray="1, 45"/>
|
||||
<svg width="100%" height="35" viewBox="850 0 300 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<line x1="0" x2="3000" y1="100" y2="100" stroke="currentColor" stroke-width="20" stroke-linecap="round" stroke-dasharray="1, 45"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 233 B After Width: | Height: | Size: 233 B |
@@ -1,16 +1,5 @@
|
||||
<svg width="170" height="168" viewBox="0 0 170 168" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path opacity="0.6" d="M85.05 168C132.022 168 170.1 130.105 170.1 83.3593C170.1 36.6135 0 36.6135 0 83.3593C0 130.105 38.0782 168 85.05 168Z" fill="#FF505F"/>
|
||||
<path opacity="0.6" d="M85.05 168C132.022 168 170.1 130.105 170.1 83.3593C170.1 36.6135 0 36.6135 0 83.3593C0 130.105 38.0782 168 85.05 168Z" fill="#FF0320"/>
|
||||
<path d="M85.05 0C132.022 0 170.1 37.8949 170.1 84.6407C170.1 131.386 0 131.386 0 84.6407C0 37.8949 38.0782 0 85.05 0Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M144.665 64.0394L112.444 12.3742L89.0263 78.9477L144.665 64.0394Z" fill="#FF4E65"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M143.777 64.215L112.444 12.3742L165.349 58.4347L143.777 64.215Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M144.551 63.613L142.479 124.467L88.912 78.5213L144.551 63.613Z" fill="#D0001A"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M143.663 63.7886L142.479 124.467L165.235 58.0083L143.663 63.7886Z" fill="#FF697B"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="170" height="168" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="250" cy="250" r="250" fill="#FF0420"/>
|
||||
<path d="M177.133 316.446C162.247 316.446 150.051 312.943 140.544 305.938C131.162 298.808 126.471 288.676 126.471 275.541C126.471 272.789 126.784 269.411 127.409 265.408C129.036 256.402 131.35 245.581 134.352 232.947C142.858 198.547 164.812 181.347 200.213 181.347C209.845 181.347 218.476 182.973 226.107 186.225C233.738 189.352 239.742 194.106 244.12 200.486C248.498 206.74 250.688 214.246 250.688 223.002C250.688 225.629 250.375 228.944 249.749 232.947C247.873 244.08 245.621 254.901 242.994 265.408C238.616 282.546 231.048 295.368 220.29 303.874C209.532 312.255 195.147 316.446 177.133 316.446ZM179.76 289.426C186.766 289.426 192.707 287.362 197.586 283.234C202.59 279.106 206.155 272.789 208.281 264.283C211.158 252.524 213.348 242.266 214.849 233.51C215.349 230.883 215.599 228.194 215.599 225.441C215.599 214.058 209.657 208.366 197.774 208.366C190.768 208.366 184.764 210.43 179.76 214.558C174.882 218.687 171.379 225.004 169.253 233.51C167.001 241.891 164.749 252.149 162.498 264.283C161.997 266.784 161.747 269.411 161.747 272.163C161.747 283.672 167.752 289.426 179.76 289.426Z" fill="white"/>
|
||||
<path d="M259.303 314.57C257.927 314.57 256.863 314.132 256.113 313.256C255.487 312.255 255.3 311.13 255.55 309.879L281.444 187.914C281.694 186.538 282.382 185.412 283.508 184.536C284.634 183.661 285.822 183.223 287.073 183.223H336.985C350.87 183.223 362.003 186.1 370.384 191.854C378.891 197.609 383.144 205.927 383.144 216.81C383.144 219.937 382.769 223.19 382.018 226.567C378.891 240.953 372.574 251.586 363.067 258.466C353.685 265.346 340.8 268.786 324.413 268.786H299.082L290.451 309.879C290.2 311.255 289.512 312.38 288.387 313.256C287.261 314.132 286.072 314.57 284.822 314.57H259.303ZM325.727 242.892C330.98 242.892 335.546 241.453 339.424 238.576C343.427 235.699 346.054 231.571 347.305 226.192C347.68 224.065 347.868 222.189 347.868 220.563C347.868 216.935 346.805 214.183 344.678 212.307C342.551 210.305 338.924 209.305 333.795 209.305H311.278L304.148 242.892H325.727Z" fill="white"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.1 KiB |
@@ -1,49 +1,64 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import React from 'react'
|
||||
import useCopyClipboard from 'hooks/useCopyClipboard'
|
||||
import React, { useCallback } from 'react'
|
||||
import { CheckCircle, Copy } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import useCopyClipboard from '../../hooks/useCopyClipboard'
|
||||
import { LinkStyledButton } from '../../theme'
|
||||
import { LinkStyledButton } from 'theme'
|
||||
|
||||
const CopyIcon = styled(LinkStyledButton)`
|
||||
color: ${({ theme }) => theme.text3};
|
||||
color: ${({ color, theme }) => color || theme.deprecated_text3};
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
text-decoration: none;
|
||||
font-size: 0.825rem;
|
||||
:hover,
|
||||
:active,
|
||||
:focus {
|
||||
text-decoration: none;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
color: ${({ color, theme }) => color || theme.deprecated_text2};
|
||||
}
|
||||
`
|
||||
const TransactionStatusText = styled.span`
|
||||
const StyledText = styled.span`
|
||||
margin-left: 0.25rem;
|
||||
font-size: 0.825rem;
|
||||
${({ theme }) => theme.flexRowNoWrap};
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
export default function CopyHelper(props: { toCopy: string; children?: React.ReactNode }) {
|
||||
const Copied = ({ iconSize }: { iconSize?: number }) => (
|
||||
<StyledText>
|
||||
<CheckCircle size={iconSize ?? '16'} />
|
||||
<StyledText>
|
||||
<Trans>Copied</Trans>
|
||||
</StyledText>
|
||||
</StyledText>
|
||||
)
|
||||
|
||||
const Icon = ({ iconSize }: { iconSize?: number }) => (
|
||||
<StyledText>
|
||||
<Copy size={iconSize ?? '16'} />
|
||||
</StyledText>
|
||||
)
|
||||
|
||||
interface BaseProps {
|
||||
toCopy: string
|
||||
color?: string
|
||||
iconSize?: number
|
||||
iconPosition?: 'left' | 'right'
|
||||
}
|
||||
export type CopyHelperProps = BaseProps & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, keyof BaseProps>
|
||||
|
||||
export default function CopyHelper({ color, toCopy, children, iconSize, iconPosition }: CopyHelperProps) {
|
||||
const [isCopied, setCopied] = useCopyClipboard()
|
||||
const copy = useCallback(() => {
|
||||
setCopied(toCopy)
|
||||
}, [toCopy, setCopied])
|
||||
|
||||
return (
|
||||
<CopyIcon onClick={() => setCopied(props.toCopy)}>
|
||||
{isCopied ? (
|
||||
<TransactionStatusText>
|
||||
<CheckCircle size={'16'} />
|
||||
<TransactionStatusText>
|
||||
<Trans>Copied</Trans>
|
||||
</TransactionStatusText>
|
||||
</TransactionStatusText>
|
||||
) : (
|
||||
<TransactionStatusText>
|
||||
<Copy size={'16'} />
|
||||
</TransactionStatusText>
|
||||
)}
|
||||
{isCopied ? '' : props.children}
|
||||
<CopyIcon onClick={copy} color={color}>
|
||||
{iconPosition === 'left' ? isCopied ? <Copied iconSize={iconSize} /> : <Icon iconSize={iconSize} /> : null}
|
||||
{iconPosition === 'left' && <> </>}
|
||||
{isCopied ? '' : children}
|
||||
{iconPosition === 'right' && <> </>}
|
||||
{iconPosition === 'right' ? isCopied ? <Copied iconSize={iconSize} /> : <Icon iconSize={iconSize} /> : null}
|
||||
</CopyIcon>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { CheckCircle, Triangle } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
@@ -27,15 +27,16 @@ const TransactionState = styled(ExternalLink)<{ pending: boolean; success?: bool
|
||||
padding: 0.25rem 0rem;
|
||||
font-weight: 500;
|
||||
font-size: 0.825rem;
|
||||
color: ${({ theme }) => theme.primary1};
|
||||
color: ${({ theme }) => theme.deprecated_primary1};
|
||||
`
|
||||
|
||||
const IconWrapper = styled.div<{ pending: boolean; success?: boolean }>`
|
||||
color: ${({ pending, success, theme }) => (pending ? theme.primary1 : success ? theme.green1 : theme.red1)};
|
||||
color: ${({ pending, success, theme }) =>
|
||||
pending ? theme.deprecated_primary1 : success ? theme.deprecated_green1 : theme.deprecated_red1};
|
||||
`
|
||||
|
||||
export default function Transaction({ hash }: { hash: string }) {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const { chainId } = useWeb3React()
|
||||
const allTransactions = useAllTransactions()
|
||||
|
||||
const tx = allTransactions?.[hash]
|
||||
|
||||
@@ -17,7 +17,9 @@ import {
|
||||
DepositLiquidityStakingTransactionInfo,
|
||||
ExactInputSwapTransactionInfo,
|
||||
ExactOutputSwapTransactionInfo,
|
||||
ExecuteTransactionInfo,
|
||||
MigrateV2LiquidityToV3TransactionInfo,
|
||||
QueueTransactionInfo,
|
||||
RemoveLiquidityV3TransactionInfo,
|
||||
SubmitProposalTransactionInfo,
|
||||
TransactionInfo,
|
||||
@@ -25,7 +27,7 @@ import {
|
||||
VoteTransactionInfo,
|
||||
WithdrawLiquidityStakingTransactionInfo,
|
||||
WrapTransactionInfo,
|
||||
} from '../../state/transactions/actions'
|
||||
} from '../../state/transactions/types'
|
||||
|
||||
function formatAmount(amountRaw: string, decimals: number, sigFigs: number): string {
|
||||
return new Fraction(amountRaw, JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(decimals))).toSignificant(sigFigs)
|
||||
@@ -126,6 +128,16 @@ function VoteSummary({ info }: { info: VoteTransactionInfo }) {
|
||||
}
|
||||
}
|
||||
|
||||
function QueueSummary({ info }: { info: QueueTransactionInfo }) {
|
||||
const proposalKey = `${info.governorAddress}/${info.proposalId}`
|
||||
return <Trans>Queue proposal {proposalKey}.</Trans>
|
||||
}
|
||||
|
||||
function ExecuteSummary({ info }: { info: ExecuteTransactionInfo }) {
|
||||
const proposalKey = `${info.governorAddress}/${info.proposalId}`
|
||||
return <Trans>Execute proposal {proposalKey}.</Trans>
|
||||
}
|
||||
|
||||
function DelegateSummary({ info: { delegatee } }: { info: DelegateTransactionInfo }) {
|
||||
const { ENSName } = useENSName(delegatee)
|
||||
return <Trans>Delegate voting power to {ENSName ?? delegatee}</Trans>
|
||||
@@ -339,6 +351,12 @@ export function TransactionSummary({ info }: { info: TransactionInfo }) {
|
||||
case TransactionType.REMOVE_LIQUIDITY_V3:
|
||||
return <RemoveLiquidityV3Summary info={info} />
|
||||
|
||||
case TransactionType.QUEUE:
|
||||
return <QueueSummary info={info} />
|
||||
|
||||
case TransactionType.EXECUTE:
|
||||
return <ExecuteSummary info={info} />
|
||||
|
||||
case TransactionType.SUBMIT_PROPOSAL:
|
||||
return <SubmitProposalTransactionSummary info={info} />
|
||||
}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { AbstractConnector } from '@web3-react/abstract-connector'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useCallback, useContext } from 'react'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import CopyHelper from 'components/AccountDetails/Copy'
|
||||
import { getConnection, getConnectionName, getIsCoinbaseWallet, getIsMetaMask } from 'connection/utils'
|
||||
import { Context, useCallback, useContext } from 'react'
|
||||
import { ExternalLink as LinkIcon } from 'react-feather'
|
||||
import { useAppDispatch } from 'state/hooks'
|
||||
import { updateSelectedWallet } from 'state/user/reducer'
|
||||
import { DefaultTheme } from 'styled-components/macro'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
import { Connector } from 'widgets-web3-react/types'
|
||||
import { isMobile } from 'utils/userAgent'
|
||||
|
||||
import { ReactComponent as Close } from '../../assets/images/x.svg'
|
||||
import { injected, portis, walletlink } from '../../connectors'
|
||||
import { SUPPORTED_WALLETS } from '../../constants/wallet'
|
||||
import { clearAllTransactions } from '../../state/transactions/actions'
|
||||
import { clearAllTransactions } from '../../state/transactions/reducer'
|
||||
import { ExternalLink, LinkStyledButton, ThemedText } from '../../theme'
|
||||
import { shortenAddress } from '../../utils'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { ButtonSecondary } from '../Button'
|
||||
import StatusIcon from '../Identicon/StatusIcon'
|
||||
import { AutoRow } from '../Row'
|
||||
import Copy from './Copy'
|
||||
import Transaction from './Transaction'
|
||||
|
||||
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.deprecated_primary1 : 'inherit')};
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
padding: 1rem;
|
||||
`};
|
||||
@@ -52,7 +52,7 @@ const UpperSection = styled.div`
|
||||
|
||||
const InfoCard = styled.div`
|
||||
padding: 1rem;
|
||||
border: 1px solid ${({ theme }) => theme.bg3};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg3};
|
||||
border-radius: 20px;
|
||||
position: relative;
|
||||
display: grid;
|
||||
@@ -65,7 +65,7 @@ const AccountGroupingRow = styled.div`
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: 400;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
|
||||
div {
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
@@ -95,14 +95,14 @@ const LowerSection = styled.div`
|
||||
padding: 1.5rem;
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
border-bottom-left-radius: 20px;
|
||||
border-bottom-right-radius: 20px;
|
||||
|
||||
h5 {
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
color: ${({ theme }) => theme.text3};
|
||||
color: ${({ theme }) => theme.deprecated_text3};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -130,12 +130,12 @@ const AccountControl = styled.div`
|
||||
|
||||
const AddressLink = styled(ExternalLink)<{ hasENS: boolean; isENS: boolean }>`
|
||||
font-size: 0.825rem;
|
||||
color: ${({ theme }) => theme.text3};
|
||||
color: ${({ theme }) => theme.deprecated_text3};
|
||||
margin-left: 1rem;
|
||||
font-size: 0.825rem;
|
||||
display: flex;
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.text2};
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -151,7 +151,7 @@ const CloseIcon = styled.div`
|
||||
|
||||
const CloseColor = styled(Close)`
|
||||
path {
|
||||
stroke: ${({ theme }) => theme.text4};
|
||||
stroke: ${({ theme }) => theme.deprecated_text4};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -159,41 +159,9 @@ const WalletName = styled.div`
|
||||
width: initial;
|
||||
font-size: 0.825rem;
|
||||
font-weight: 500;
|
||||
color: ${({ theme }) => theme.text3};
|
||||
color: ${({ theme }) => theme.deprecated_text3};
|
||||
`
|
||||
|
||||
const IconWrapper = styled.div<{ size?: number }>`
|
||||
${({ theme }) => theme.flexColumnNoWrap};
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 8px;
|
||||
& > img,
|
||||
span {
|
||||
height: ${({ size }) => (size ? size + 'px' : '32px')};
|
||||
width: ${({ size }) => (size ? size + 'px' : '32px')};
|
||||
}
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
align-items: flex-end;
|
||||
`};
|
||||
`
|
||||
|
||||
function WrappedStatusIcon({ connector }: { connector: AbstractConnector | Connector }) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<StatusIcon connector={connector} />
|
||||
{connector === portis && (
|
||||
<MainWalletAction
|
||||
onClick={() => {
|
||||
portis.portis.showPortis()
|
||||
}}
|
||||
>
|
||||
<Trans>Show Portis</Trans>
|
||||
</MainWalletAction>
|
||||
)}
|
||||
</IconWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
const TransactionListWrapper = styled.div`
|
||||
${({ theme }) => theme.flexColumnNoWrap};
|
||||
`
|
||||
@@ -210,10 +178,6 @@ const WalletAction = styled(ButtonSecondary)`
|
||||
}
|
||||
`
|
||||
|
||||
const MainWalletAction = styled(WalletAction)`
|
||||
color: ${({ theme }) => theme.primary1};
|
||||
`
|
||||
|
||||
function renderTransactions(transactions: string[]) {
|
||||
return (
|
||||
<TransactionListWrapper>
|
||||
@@ -239,22 +203,20 @@ export default function AccountDetails({
|
||||
ENSName,
|
||||
openOptions,
|
||||
}: AccountDetailsProps) {
|
||||
const { chainId, account, connector } = useActiveWeb3React()
|
||||
const theme = useContext(ThemeContext)
|
||||
const { chainId, account, connector } = useWeb3React()
|
||||
const connectionType = getConnection(connector).type
|
||||
|
||||
const theme = useContext(ThemeContext as Context<DefaultTheme>)
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const isMetaMask = getIsMetaMask()
|
||||
const isCoinbaseWallet = getIsCoinbaseWallet()
|
||||
const isInjectedMobileBrowser = (isMetaMask || isCoinbaseWallet) && isMobile
|
||||
|
||||
function formatConnectorName() {
|
||||
const { ethereum } = window
|
||||
const isMetaMask = !!(ethereum && ethereum.isMetaMask)
|
||||
const name = Object.keys(SUPPORTED_WALLETS)
|
||||
.filter(
|
||||
(k) =>
|
||||
SUPPORTED_WALLETS[k].connector === connector && (connector !== injected || isMetaMask === (k === 'METAMASK'))
|
||||
)
|
||||
.map((k) => SUPPORTED_WALLETS[k].name)[0]
|
||||
return (
|
||||
<WalletName>
|
||||
<Trans>Connected with {name}</Trans>
|
||||
<Trans>Connected with</Trans> {getConnectionName(connectionType, isMetaMask)}
|
||||
</WalletName>
|
||||
)
|
||||
}
|
||||
@@ -278,43 +240,41 @@ export default function AccountDetails({
|
||||
<AccountGroupingRow>
|
||||
{formatConnectorName()}
|
||||
<div>
|
||||
{connector !== injected && connector !== walletlink && (
|
||||
<WalletAction
|
||||
style={{ fontSize: '.825rem', fontWeight: 400, marginRight: '8px' }}
|
||||
onClick={() => {
|
||||
;(connector as any).close()
|
||||
}}
|
||||
>
|
||||
<Trans>Disconnect</Trans>
|
||||
</WalletAction>
|
||||
{!isInjectedMobileBrowser && (
|
||||
<>
|
||||
<WalletAction
|
||||
style={{ fontSize: '.825rem', fontWeight: 400, marginRight: '8px' }}
|
||||
onClick={() => {
|
||||
if (connector.deactivate) {
|
||||
connector.deactivate()
|
||||
} else {
|
||||
connector.resetState()
|
||||
}
|
||||
|
||||
dispatch(updateSelectedWallet({ wallet: undefined }))
|
||||
openOptions()
|
||||
}}
|
||||
>
|
||||
<Trans>Disconnect</Trans>
|
||||
</WalletAction>
|
||||
<WalletAction
|
||||
style={{ fontSize: '.825rem', fontWeight: 400 }}
|
||||
onClick={() => {
|
||||
openOptions()
|
||||
}}
|
||||
>
|
||||
<Trans>Change</Trans>
|
||||
</WalletAction>
|
||||
</>
|
||||
)}
|
||||
<WalletAction
|
||||
style={{ fontSize: '.825rem', fontWeight: 400 }}
|
||||
onClick={() => {
|
||||
openOptions()
|
||||
}}
|
||||
>
|
||||
<Trans>Change</Trans>
|
||||
</WalletAction>
|
||||
</div>
|
||||
</AccountGroupingRow>
|
||||
<AccountGroupingRow id="web3-account-identifier-row">
|
||||
<AccountGroupingRow data-testid="web3-account-identifier-row">
|
||||
<AccountControl>
|
||||
{ENSName ? (
|
||||
<>
|
||||
<div>
|
||||
{connector && <WrappedStatusIcon connector={connector} />}
|
||||
<p> {ENSName}</p>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div>
|
||||
{connector && <WrappedStatusIcon connector={connector} />}
|
||||
<p> {account && shortenAddress(account)}</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div>
|
||||
<StatusIcon connectionType={connectionType} />
|
||||
<p>{ENSName ? ENSName : account && shortenAddress(account)}</p>
|
||||
</div>
|
||||
</AccountControl>
|
||||
</AccountGroupingRow>
|
||||
<AccountGroupingRow>
|
||||
@@ -323,11 +283,11 @@ export default function AccountDetails({
|
||||
<AccountControl>
|
||||
<div>
|
||||
{account && (
|
||||
<Copy toCopy={account}>
|
||||
<CopyHelper toCopy={account} iconPosition="left">
|
||||
<span style={{ marginLeft: '4px' }}>
|
||||
<Trans>Copy Address</Trans>
|
||||
</span>
|
||||
</Copy>
|
||||
</CopyHelper>
|
||||
)}
|
||||
{chainId && account && (
|
||||
<AddressLink
|
||||
@@ -349,11 +309,11 @@ export default function AccountDetails({
|
||||
<AccountControl>
|
||||
<div>
|
||||
{account && (
|
||||
<Copy toCopy={account}>
|
||||
<CopyHelper toCopy={account} iconPosition="left">
|
||||
<span style={{ marginLeft: '4px' }}>
|
||||
<Trans>Copy Address</Trans>
|
||||
</span>
|
||||
</Copy>
|
||||
</CopyHelper>
|
||||
)}
|
||||
{chainId && account && (
|
||||
<AddressLink
|
||||
@@ -391,7 +351,7 @@ export default function AccountDetails({
|
||||
</LowerSection>
|
||||
) : (
|
||||
<LowerSection>
|
||||
<ThemedText.Body color={theme.text1}>
|
||||
<ThemedText.Body color={theme.deprecated_text1}>
|
||||
<Trans>Your transactions will appear here...</Trans>
|
||||
</ThemedText.Body>
|
||||
</LowerSection>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t } from '@lingui/macro'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { ReactNode, useCallback, useContext } from 'react'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { ChangeEvent, Context, ReactNode, useCallback, useContext } from 'react'
|
||||
import styled, { DefaultTheme, ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import useENS from '../../hooks/useENS'
|
||||
import { ExternalLink, ThemedText } from '../../theme'
|
||||
@@ -15,7 +15,7 @@ const InputPanel = styled.div`
|
||||
${({ theme }) => theme.flexColumnNoWrap}
|
||||
position: relative;
|
||||
border-radius: 1.25rem;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
`
|
||||
@@ -25,10 +25,10 @@ const ContainerRow = styled.div<{ error: boolean }>`
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 1.25rem;
|
||||
border: 1px solid ${({ error, theme }) => (error ? theme.red1 : theme.bg2)};
|
||||
border: 1px solid ${({ error, theme }) => (error ? theme.deprecated_red1 : theme.deprecated_bg2)};
|
||||
transition: border-color 300ms ${({ error }) => (error ? 'step-end' : 'step-start')},
|
||||
color 500ms ${({ error }) => (error ? 'step-end' : 'step-start')};
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
`
|
||||
|
||||
const InputContainer = styled.div`
|
||||
@@ -42,15 +42,15 @@ const Input = styled.input<{ error?: boolean }>`
|
||||
border: none;
|
||||
flex: 1 1 auto;
|
||||
width: 0;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
transition: color 300ms ${({ error }) => (error ? 'step-end' : 'step-start')};
|
||||
color: ${({ error, theme }) => (error ? theme.red1 : theme.text1)};
|
||||
color: ${({ error, theme }) => (error ? theme.deprecated_red1 : theme.deprecated_text1)};
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: 500;
|
||||
width: 100%;
|
||||
::placeholder {
|
||||
color: ${({ theme }) => theme.text4};
|
||||
color: ${({ theme }) => theme.deprecated_text4};
|
||||
}
|
||||
padding: 0px;
|
||||
-webkit-appearance: textfield;
|
||||
@@ -65,7 +65,7 @@ const Input = styled.input<{ error?: boolean }>`
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: ${({ theme }) => theme.text4};
|
||||
color: ${({ theme }) => theme.deprecated_text4};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -86,13 +86,13 @@ export default function AddressInputPanel({
|
||||
// triggers whenever the typed value changes
|
||||
onChange: (value: string) => void
|
||||
}) {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const theme = useContext(ThemeContext)
|
||||
const { chainId } = useWeb3React()
|
||||
const theme = useContext(ThemeContext as Context<DefaultTheme>)
|
||||
|
||||
const { address, loading, name } = useENS(value)
|
||||
|
||||
const handleInput = useCallback(
|
||||
(event) => {
|
||||
(event: ChangeEvent<HTMLInputElement>) => {
|
||||
const input = event.target.value
|
||||
const withoutSpaces = input.replace(/\s+/g, '')
|
||||
onChange(withoutSpaces)
|
||||
@@ -108,7 +108,7 @@ export default function AddressInputPanel({
|
||||
<InputContainer>
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<ThemedText.Black color={theme.text2} fontWeight={500} fontSize={14}>
|
||||
<ThemedText.Black color={theme.deprecated_text2} fontWeight={500} fontSize={14}>
|
||||
{label ?? <Trans>Recipient</Trans>}
|
||||
</ThemedText.Black>
|
||||
{address && chainId && (
|
||||
|
||||
57
src/components/AmplitudeAnalytics/Trace.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import { createContext, memo, PropsWithChildren, useContext, useEffect, useMemo } from 'react'
|
||||
|
||||
import { sendAnalyticsEvent } from '.'
|
||||
import { ElementName, EventName, ModalName, PageName, SectionName } from './constants'
|
||||
|
||||
export interface ITraceContext {
|
||||
// Highest order context: eg Swap or Explore.
|
||||
page?: PageName
|
||||
|
||||
// Enclosed section name. For contexts with modals, refers to the
|
||||
// section of the page from which the user triggered the modal.
|
||||
section?: SectionName
|
||||
|
||||
modal?: ModalName
|
||||
|
||||
// Element name mostly used to identify events sources
|
||||
// Does not need to be unique given the higher order page and section.
|
||||
element?: ElementName
|
||||
}
|
||||
|
||||
export const TraceContext = createContext<ITraceContext>({})
|
||||
|
||||
type TraceProps = {
|
||||
shouldLogImpression?: boolean // whether to log impression on mount
|
||||
name?: EventName
|
||||
properties?: Record<string, unknown>
|
||||
} & ITraceContext
|
||||
|
||||
/**
|
||||
* Sends an analytics event on mount (if shouldLogImpression is set),
|
||||
* and propagates the context to child traces.
|
||||
*/
|
||||
export const Trace = memo(
|
||||
({ shouldLogImpression, name, children, page, section, element, properties }: PropsWithChildren<TraceProps>) => {
|
||||
const parentTrace = useContext(TraceContext)
|
||||
|
||||
const combinedProps = useMemo(
|
||||
() => ({
|
||||
...parentTrace,
|
||||
...Object.fromEntries(Object.entries({ page, section, element }).filter(([_, v]) => v !== undefined)),
|
||||
}),
|
||||
[element, parentTrace, page, section]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldLogImpression) {
|
||||
sendAnalyticsEvent(name ?? EventName.PAGE_VIEWED, { ...combinedProps, ...properties })
|
||||
}
|
||||
// Impressions should only be logged on mount.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
return <TraceContext.Provider value={combinedProps}>{children}</TraceContext.Provider>
|
||||
}
|
||||
)
|
||||
|
||||
Trace.displayName = 'Trace'
|
||||
75
src/components/AmplitudeAnalytics/TraceEvent.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import { Children, cloneElement, isValidElement, memo, PropsWithChildren, SyntheticEvent } from 'react'
|
||||
|
||||
import { sendAnalyticsEvent } from '.'
|
||||
import { Event, EventName } from './constants'
|
||||
import { ITraceContext, Trace, TraceContext } from './Trace'
|
||||
|
||||
type TraceEventProps = {
|
||||
events: Event[]
|
||||
name: EventName
|
||||
properties?: Record<string, unknown>
|
||||
shouldLogImpression?: boolean
|
||||
} & ITraceContext
|
||||
|
||||
/**
|
||||
* Analytics instrumentation component that wraps event callbacks with logging logic.
|
||||
*
|
||||
* @example
|
||||
* <TraceEvent events={[Event.onClick]} element={ElementName.SWAP_BUTTON}>
|
||||
* <Button onClick={() => console.log('clicked')}>Click me</Button>
|
||||
* </TraceEvent>
|
||||
*/
|
||||
export const TraceEvent = memo((props: PropsWithChildren<TraceEventProps>) => {
|
||||
const { shouldLogImpression, name, properties, events, children, ...traceProps } = props
|
||||
|
||||
return (
|
||||
<Trace {...traceProps}>
|
||||
<TraceContext.Consumer>
|
||||
{(traceContext) =>
|
||||
Children.map(children, (child) => {
|
||||
if (!isValidElement(child)) {
|
||||
return child
|
||||
}
|
||||
|
||||
// For each child, augment event handlers defined in `events` with event tracing.
|
||||
return cloneElement(
|
||||
child,
|
||||
getEventHandlers(child, traceContext, events, name, properties, shouldLogImpression)
|
||||
)
|
||||
})
|
||||
}
|
||||
</TraceContext.Consumer>
|
||||
</Trace>
|
||||
)
|
||||
})
|
||||
|
||||
TraceEvent.displayName = 'TraceEvent'
|
||||
|
||||
/**
|
||||
* Given a set of child element and event props, returns a spreadable
|
||||
* object of the event handlers augmented with analytics logging.
|
||||
*/
|
||||
function getEventHandlers(
|
||||
child: React.ReactElement,
|
||||
traceContext: ITraceContext,
|
||||
events: Event[],
|
||||
name: EventName,
|
||||
properties?: Record<string, unknown>,
|
||||
shouldLogImpression = true
|
||||
) {
|
||||
const eventHandlers: Partial<Record<Event, (e: SyntheticEvent<Element, Event>) => void>> = {}
|
||||
|
||||
for (const event of events) {
|
||||
eventHandlers[event] = (eventHandlerArgs: unknown) => {
|
||||
// call child event handler with original arguments, must be in array
|
||||
const args = Array.isArray(eventHandlerArgs) ? eventHandlerArgs : [eventHandlerArgs]
|
||||
child.props[event]?.apply(child, args)
|
||||
|
||||
// augment handler with analytics logging
|
||||
if (shouldLogImpression) sendAnalyticsEvent(name, { ...traceContext, ...properties, action: event })
|
||||
}
|
||||
}
|
||||
|
||||
// return a spreadable event handler object
|
||||
return eventHandlers
|
||||
}
|
||||
88
src/components/AmplitudeAnalytics/constants.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Event names that can occur in this application.
|
||||
*
|
||||
* Subject to change as new features are added and new events are defined
|
||||
* and logged.
|
||||
*/
|
||||
export enum EventName {
|
||||
CONNECT_WALLET_BUTTON_CLICKED = 'Connect Wallet Button Clicked',
|
||||
PAGE_VIEWED = 'Page Viewed',
|
||||
SWAP_AUTOROUTER_VISUALIZATION_EXPANDED = 'Swap Autorouter Visualization Expanded',
|
||||
SWAP_DETAILS_EXPANDED = 'Swap Details Expanded',
|
||||
SWAP_MAX_TOKEN_AMOUNT_SELECTED = 'Swap Max Token Amount Selected',
|
||||
SWAP_QUOTE_RECEIVED = 'Swap Quote Received',
|
||||
SWAP_SUBMITTED = 'Swap Submitted',
|
||||
SWAP_TOKENS_REVERSED = 'Swap Tokens Reversed',
|
||||
TOKEN_IMPORTED = 'Token Imported',
|
||||
TOKEN_SELECTED = 'Token Selected',
|
||||
TOKEN_SELECTOR_OPENED = 'Token Selector Opened',
|
||||
WALLET_CONNECT_TXN_COMPLETED = 'Wallet Connect Transaction Completed',
|
||||
WALLET_SELECTED = 'Wallet Selected',
|
||||
// alphabetize additional event names.
|
||||
}
|
||||
|
||||
export enum WALLET_CONNECTION_RESULT {
|
||||
SUCCEEDED = 'Succeeded',
|
||||
FAILED = 'Failed',
|
||||
}
|
||||
|
||||
/**
|
||||
* Known pages in the app. Highest order context.
|
||||
*/
|
||||
export const enum PageName {
|
||||
EXPLORE_PAGE = 'explore-page',
|
||||
POOL_PAGE = 'pool-page',
|
||||
SWAP_PAGE = 'swap-page',
|
||||
VOTE_PAGE = 'vote-page',
|
||||
// alphabetize additional page names.
|
||||
}
|
||||
|
||||
/**
|
||||
* Sections. Disambiguates low-level elements that may share a name.
|
||||
* eg a `back` button in a modal will have the same `element`,
|
||||
* but a different `section`.
|
||||
*/
|
||||
export const enum SectionName {
|
||||
CURRENCY_INPUT_PANEL = 'swap-currency-input',
|
||||
CURRENCY_OUTPUT_PANEL = 'swap-currency-output',
|
||||
// alphabetize additional section names.
|
||||
}
|
||||
|
||||
/** Known modals for analytics purposes. */
|
||||
export const enum ModalName {
|
||||
CONFIRM_SWAP = 'confirm-swap-modal',
|
||||
TOKEN_SELECTOR = 'token-selector-modal',
|
||||
// alphabetize additional modal names.
|
||||
}
|
||||
|
||||
/**
|
||||
* Known element names for analytics purposes.
|
||||
* Use to identify low-level components given a TraceContext
|
||||
*/
|
||||
export const enum ElementName {
|
||||
AUTOROUTER_VISUALIZATION_ROW = 'expandable-autorouter-visualization-row',
|
||||
COMMON_BASES_CURRENCY_BUTTON = 'common-bases-currency-button',
|
||||
CONFIRM_SWAP_BUTTON = 'confirm-swap-or-send',
|
||||
CONNECT_WALLET_BUTTON = 'connect-wallet-button',
|
||||
IMPORT_TOKEN_BUTTON = 'import-token-button',
|
||||
MAX_TOKEN_AMOUNT_BUTTON = 'max-token-amount-button',
|
||||
SWAP_BUTTON = 'swap-button',
|
||||
SWAP_DETAILS_DROPDOWN = 'swap-details-dropdown',
|
||||
SWAP_TOKENS_REVERSE_ARROW_BUTTON = 'swap-tokens-reverse-arrow-button',
|
||||
SWAP_TRADE_PRICE_ROW = 'swap-trade-price-row',
|
||||
TOKEN_SELECTOR_ROW = 'token-selector-row',
|
||||
WALLET_TYPE_OPTION = 'wallet-type-option',
|
||||
// alphabetize additional element names.
|
||||
}
|
||||
|
||||
/**
|
||||
* Known events that trigger callbacks.
|
||||
* @example
|
||||
* <TraceEvent events={[Event.onClick]} element={name}>
|
||||
*/
|
||||
export enum Event {
|
||||
onClick = 'onClick',
|
||||
onKeyPress = 'onKeyPress',
|
||||
onSelect = 'onSelect',
|
||||
// alphabetize additional events.
|
||||
}
|
||||
88
src/components/AmplitudeAnalytics/index.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { Identify, identify, init, track } from '@amplitude/analytics-browser'
|
||||
|
||||
/**
|
||||
* Initializes Amplitude with API key for project.
|
||||
*
|
||||
* Uniswap has two Amplitude projects: test and production. You must be a
|
||||
* member of the organization on Amplitude to view details.
|
||||
*/
|
||||
export function initializeAnalytics(isDevEnvironment = process.env.NODE_ENV === 'development') {
|
||||
if (isDevEnvironment) return
|
||||
|
||||
const API_KEY = process.env.REACT_APP_AMPLITUDE_KEY
|
||||
if (typeof API_KEY === 'undefined') {
|
||||
throw new Error(`REACT_APP_AMPLITUDE_KEY must be a defined environment variable`)
|
||||
}
|
||||
|
||||
init(
|
||||
API_KEY,
|
||||
/* userId= */ undefined, // User ID should be undefined to let Amplitude default to Device ID
|
||||
/* options= */ {
|
||||
// Disable tracking of private user information by Amplitude
|
||||
trackingOptions: {
|
||||
ipAddress: false,
|
||||
carrier: false,
|
||||
city: false,
|
||||
region: false,
|
||||
country: false,
|
||||
dma: false, // designated market area
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/** Sends an event to Amplitude. */
|
||||
export function sendAnalyticsEvent(eventName: string, eventProperties?: Record<string, unknown>) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
console.log(`[amplitude(${eventName})]: ${JSON.stringify(eventProperties)}`)
|
||||
return
|
||||
}
|
||||
|
||||
track(eventName, eventProperties)
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that exposes methods to mutate the User Model's properties in
|
||||
* Amplitude that represents the current session's user.
|
||||
*
|
||||
* See https://help.amplitude.com/hc/en-us/articles/115002380567-User-properties-and-event-properties
|
||||
* for details.
|
||||
*/
|
||||
class UserModel {
|
||||
constructor(private isDevEnvironment = process.env.NODE_ENV === 'development') {}
|
||||
|
||||
private log(method: string, ...parameters: unknown[]) {
|
||||
console.debug(`[amplitude(Identify)]: ${method}(${parameters})`)
|
||||
}
|
||||
|
||||
private call(mutate: (event: Identify) => Identify) {
|
||||
if (this.isDevEnvironment) {
|
||||
const log = (_: Identify, method: string) => this.log.bind(this, method)
|
||||
mutate(new Proxy(new Identify(), { get: log }))
|
||||
return
|
||||
}
|
||||
identify(mutate(new Identify()))
|
||||
}
|
||||
|
||||
set(key: string, value: string | number) {
|
||||
this.call((event) => event.set(key, value))
|
||||
}
|
||||
|
||||
setOnce(key: string, value: string | number) {
|
||||
this.call((event) => event.setOnce(key, value))
|
||||
}
|
||||
|
||||
add(key: string, value: string | number) {
|
||||
this.call((event) => event.add(key, typeof value === 'number' ? value : 0))
|
||||
}
|
||||
|
||||
postInsert(key: string, value: string | number) {
|
||||
this.call((event) => event.postInsert(key, value))
|
||||
}
|
||||
|
||||
remove(key: string, value: string | number) {
|
||||
this.call((event) => event.remove(key, value))
|
||||
}
|
||||
}
|
||||
|
||||
export const user = new UserModel()
|
||||
13
src/components/AmplitudeAnalytics/utils.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
|
||||
|
||||
export const getDurationTillTimestampSinceEpoch = (futureTimestampSinceEpoch?: number): number | undefined => {
|
||||
if (!futureTimestampSinceEpoch) return undefined
|
||||
return futureTimestampSinceEpoch - new Date().getTime() / 1000
|
||||
}
|
||||
|
||||
export const getNumberFormattedToDecimalPlace = (
|
||||
intialNumberObject: Percent | CurrencyAmount<Token | Currency>,
|
||||
decimalPlace: number
|
||||
): number => parseFloat(intialNumberObject.toFixed(decimalPlace))
|
||||
|
||||
export const formatPercentInBasisPointsNumber = (percent: Percent): number => parseFloat(percent.toFixed(2)) * 100
|
||||
@@ -17,7 +17,7 @@ const BadgeText = styled.div`
|
||||
`
|
||||
|
||||
const ActiveDot = styled.span`
|
||||
background-color: ${({ theme }) => theme.success};
|
||||
background-color: ${({ theme }) => theme.deprecated_success};
|
||||
border-radius: 50%;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
|
||||
@@ -20,24 +20,24 @@ interface BadgeProps {
|
||||
function pickBackgroundColor(variant: BadgeVariant | undefined, theme: DefaultTheme): Color {
|
||||
switch (variant) {
|
||||
case BadgeVariant.NEGATIVE:
|
||||
return theme.error
|
||||
return theme.deprecated_error
|
||||
case BadgeVariant.POSITIVE:
|
||||
return theme.success
|
||||
return theme.deprecated_success
|
||||
case BadgeVariant.PRIMARY:
|
||||
return theme.primary1
|
||||
return theme.deprecated_primary1
|
||||
case BadgeVariant.WARNING:
|
||||
return theme.warning
|
||||
return theme.deprecated_warning
|
||||
case BadgeVariant.WARNING_OUTLINE:
|
||||
return 'transparent'
|
||||
default:
|
||||
return theme.bg2
|
||||
return theme.deprecated_bg2
|
||||
}
|
||||
}
|
||||
|
||||
function pickBorder(variant: BadgeVariant | undefined, theme: DefaultTheme): string {
|
||||
switch (variant) {
|
||||
case BadgeVariant.WARNING_OUTLINE:
|
||||
return `1px solid ${theme.warning}`
|
||||
return `1px solid ${theme.deprecated_warning}`
|
||||
default:
|
||||
return 'unset'
|
||||
}
|
||||
@@ -46,15 +46,15 @@ function pickBorder(variant: BadgeVariant | undefined, theme: DefaultTheme): str
|
||||
function pickFontColor(variant: BadgeVariant | undefined, theme: DefaultTheme): string {
|
||||
switch (variant) {
|
||||
case BadgeVariant.NEGATIVE:
|
||||
return readableColor(theme.error)
|
||||
return readableColor(theme.deprecated_error)
|
||||
case BadgeVariant.POSITIVE:
|
||||
return readableColor(theme.success)
|
||||
return readableColor(theme.deprecated_success)
|
||||
case BadgeVariant.WARNING:
|
||||
return readableColor(theme.warning)
|
||||
return readableColor(theme.deprecated_warning)
|
||||
case BadgeVariant.WARNING_OUTLINE:
|
||||
return theme.warning
|
||||
return theme.deprecated_warning
|
||||
default:
|
||||
return readableColor(theme.bg2)
|
||||
return readableColor(theme.deprecated_bg2)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { ReactNode, useMemo } from 'react'
|
||||
|
||||
// SDN OFAC addresses
|
||||
const BLOCKED_ADDRESSES: string[] = [
|
||||
'0x7Db418b5D567A4e0E8c59Ad71BE1FcE48f3E6107',
|
||||
'0x72a5843cc08275C8171E582972Aa4fDa8C397B2A',
|
||||
@@ -24,17 +23,22 @@ const BLOCKED_ADDRESSES: string[] = [
|
||||
'0x6acdfba02d390b97ac2b2d42a63e85293bcc1',
|
||||
'0x48549a34ae37b12f6a30566245176994e17c6',
|
||||
'0x5512d943ed1f7c8a43f3435c85f7ab68b30121',
|
||||
'0xc455f7fd3e0e12afd51fba5c106909934d8a0e',
|
||||
'0x3cbded43efdaf0fc77b9c55f6fc9988fcc9b757d',
|
||||
'0xC455f7fd3e0e12afd51fba5c106909934D8A0e4a',
|
||||
'0x3CBdeD43EFdAf0FC77b9C55F6fC9988fCC9b757d',
|
||||
'0x67d40EE1A85bf4a4Bb7Ffae16De985e8427B6b45',
|
||||
'0x6f1ca141a28907f78ebaa64fb83a9088b02a8352',
|
||||
'0x6acdfba02d390b97ac2b2d42a63e85293bcc160e',
|
||||
'0x6F1cA141A28907F78Ebaa64fb83A9088b02A8352',
|
||||
'0x6aCDFBA02D390b97Ac2b2d42A63E85293BCc160e',
|
||||
'0x48549a34ae37b12f6a30566245176994e17c6b4a',
|
||||
'0x5512d943ed1f7c8a43f3435c85f7ab68b30121b0',
|
||||
'0xC455f7fd3e0e12afd51fba5c106909934D8A0e4a',
|
||||
'0x629e7Da20197a5429d30da36E77d06CdF796b71A',
|
||||
'0x7FF9cFad3877F21d41Da833E2F775dB0569eE3D9',
|
||||
'0x098B716B8Aaf21512996dC57EB0615e2383E2f96',
|
||||
'0xfEC8A60023265364D066a1212fDE3930F6Ae8da7',
|
||||
]
|
||||
|
||||
export default function Blocklist({ children }: { children: ReactNode }) {
|
||||
const { account } = useActiveWeb3React()
|
||||
const { account } = useWeb3React()
|
||||
const blocked: boolean = useMemo(() => Boolean(account && BLOCKED_ADDRESSES.indexOf(account) !== -1), [account])
|
||||
if (blocked) {
|
||||
return (
|
||||
|
||||
@@ -23,7 +23,7 @@ export const BaseButton = styled(RebassButton)<
|
||||
border-radius: ${({ $borderRadius }) => $borderRadius ?? '20px'};
|
||||
outline: none;
|
||||
border: 1px solid transparent;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -52,24 +52,24 @@ export const BaseButton = styled(RebassButton)<
|
||||
`
|
||||
|
||||
export const ButtonPrimary = styled(BaseButton)`
|
||||
background-color: ${({ theme }) => theme.primary1};
|
||||
background-color: ${({ theme }) => theme.deprecated_primary1};
|
||||
color: white;
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.primary1)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.primary1)};
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.deprecated_primary1)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.deprecated_primary1)};
|
||||
}
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => darken(0.05, theme.primary1)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.deprecated_primary1)};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.primary1)};
|
||||
background-color: ${({ theme }) => darken(0.1, theme.primary1)};
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.deprecated_primary1)};
|
||||
background-color: ${({ theme }) => darken(0.1, theme.deprecated_primary1)};
|
||||
}
|
||||
&:disabled {
|
||||
background-color: ${({ theme, altDisabledStyle, disabled }) =>
|
||||
altDisabledStyle ? (disabled ? theme.primary1 : theme.bg2) : theme.bg2};
|
||||
altDisabledStyle ? (disabled ? theme.deprecated_primary1 : theme.deprecated_bg2) : theme.deprecated_bg2};
|
||||
color: ${({ altDisabledStyle, disabled, theme }) =>
|
||||
altDisabledStyle ? (disabled ? theme.white : theme.text2) : theme.text2};
|
||||
altDisabledStyle ? (disabled ? theme.deprecated_white : theme.deprecated_text2) : theme.deprecated_text2};
|
||||
cursor: auto;
|
||||
box-shadow: none;
|
||||
border: 1px solid transparent;
|
||||
@@ -78,26 +78,26 @@ export const ButtonPrimary = styled(BaseButton)`
|
||||
`
|
||||
|
||||
export const ButtonLight = styled(BaseButton)`
|
||||
background-color: ${({ theme }) => theme.primary5};
|
||||
color: ${({ theme }) => theme.primaryText1};
|
||||
background-color: ${({ theme }) => theme.deprecated_primary5};
|
||||
color: ${({ theme }) => theme.deprecated_primaryText1};
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1pt ${({ theme, disabled }) => !disabled && darken(0.03, theme.primary5)};
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.03, theme.primary5)};
|
||||
box-shadow: 0 0 0 1pt ${({ theme, disabled }) => !disabled && darken(0.03, theme.deprecated_primary5)};
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.03, theme.deprecated_primary5)};
|
||||
}
|
||||
&:hover {
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.03, theme.primary5)};
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.03, theme.deprecated_primary5)};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1pt ${({ theme, disabled }) => !disabled && darken(0.05, theme.primary5)};
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.primary5)};
|
||||
box-shadow: 0 0 0 1pt ${({ theme, disabled }) => !disabled && darken(0.05, theme.deprecated_primary5)};
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.deprecated_primary5)};
|
||||
}
|
||||
:disabled {
|
||||
opacity: 0.4;
|
||||
:hover {
|
||||
cursor: auto;
|
||||
background-color: ${({ theme }) => theme.primary5};
|
||||
background-color: ${({ theme }) => theme.deprecated_primary5};
|
||||
box-shadow: none;
|
||||
border: 1px solid transparent;
|
||||
outline: none;
|
||||
@@ -106,37 +106,37 @@ export const ButtonLight = styled(BaseButton)`
|
||||
`
|
||||
|
||||
export const ButtonGray = styled(BaseButton)`
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
color: ${({ theme }) => theme.text2};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
|
||||
&:hover {
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.deprecated_bg2)};
|
||||
}
|
||||
&:active {
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.1, theme.bg2)};
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.1, theme.deprecated_bg2)};
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonSecondary = styled(BaseButton)`
|
||||
border: 1px solid ${({ theme }) => theme.primary4};
|
||||
color: ${({ theme }) => theme.primary1};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_primary4};
|
||||
color: ${({ theme }) => theme.deprecated_primary1};
|
||||
background-color: transparent;
|
||||
font-size: 16px;
|
||||
border-radius: 12px;
|
||||
padding: ${({ padding }) => (padding ? padding : '10px')};
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => theme.primary4};
|
||||
border: 1px solid ${({ theme }) => theme.primary3};
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => theme.deprecated_primary4};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_primary3};
|
||||
}
|
||||
&:hover {
|
||||
border: 1px solid ${({ theme }) => theme.primary3};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_primary3};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => theme.primary4};
|
||||
border: 1px solid ${({ theme }) => theme.primary3};
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => theme.deprecated_primary4};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_primary3};
|
||||
}
|
||||
&:disabled {
|
||||
opacity: 50%;
|
||||
@@ -148,17 +148,17 @@ export const ButtonSecondary = styled(BaseButton)`
|
||||
`
|
||||
|
||||
export const ButtonOutlined = styled(BaseButton)`
|
||||
border: 1px solid ${({ theme }) => theme.bg2};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg2};
|
||||
background-color: transparent;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4};
|
||||
box-shadow: 0 0 0 1px ${({ theme }) => theme.deprecated_bg4};
|
||||
}
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4};
|
||||
box-shadow: 0 0 0 1px ${({ theme }) => theme.deprecated_bg4};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4};
|
||||
box-shadow: 0 0 0 1px ${({ theme }) => theme.deprecated_bg4};
|
||||
}
|
||||
&:disabled {
|
||||
opacity: 50%;
|
||||
@@ -167,21 +167,21 @@ export const ButtonOutlined = styled(BaseButton)`
|
||||
`
|
||||
|
||||
export const ButtonYellow = styled(BaseButton)`
|
||||
background-color: ${({ theme }) => theme.yellow3};
|
||||
background-color: ${({ theme }) => theme.deprecated_yellow3};
|
||||
color: white;
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.yellow3)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.yellow3)};
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.deprecated_yellow3)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.deprecated_yellow3)};
|
||||
}
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => darken(0.05, theme.yellow3)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.deprecated_yellow3)};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.yellow3)};
|
||||
background-color: ${({ theme }) => darken(0.1, theme.yellow3)};
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.deprecated_yellow3)};
|
||||
background-color: ${({ theme }) => darken(0.1, theme.deprecated_yellow3)};
|
||||
}
|
||||
&:disabled {
|
||||
background-color: ${({ theme }) => theme.yellow3};
|
||||
background-color: ${({ theme }) => theme.deprecated_yellow3};
|
||||
opacity: 50%;
|
||||
cursor: auto;
|
||||
}
|
||||
@@ -189,7 +189,7 @@ export const ButtonYellow = styled(BaseButton)`
|
||||
|
||||
export const ButtonEmpty = styled(BaseButton)`
|
||||
background-color: transparent;
|
||||
color: ${({ theme }) => theme.primary1};
|
||||
color: ${({ theme }) => theme.deprecated_primary1};
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -232,39 +232,39 @@ export const ButtonText = styled(BaseButton)`
|
||||
`
|
||||
|
||||
const ButtonConfirmedStyle = styled(BaseButton)`
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
color: ${({ theme }) => theme.text1};
|
||||
/* border: 1px solid ${({ theme }) => theme.green1}; */
|
||||
background-color: ${({ theme }) => theme.deprecated_bg3};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
/* border: 1px solid ${({ theme }) => theme.deprecated_green1}; */
|
||||
|
||||
&:disabled {
|
||||
opacity: 50%;
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
color: ${({ theme }) => theme.text2};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
cursor: auto;
|
||||
}
|
||||
`
|
||||
|
||||
const ButtonErrorStyle = styled(BaseButton)`
|
||||
background-color: ${({ theme }) => theme.red1};
|
||||
border: 1px solid ${({ theme }) => theme.red1};
|
||||
background-color: ${({ theme }) => theme.deprecated_red1};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_red1};
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.red1)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.red1)};
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.deprecated_red1)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.deprecated_red1)};
|
||||
}
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => darken(0.05, theme.red1)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.deprecated_red1)};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.red1)};
|
||||
background-color: ${({ theme }) => darken(0.1, theme.red1)};
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.deprecated_red1)};
|
||||
background-color: ${({ theme }) => darken(0.1, theme.deprecated_red1)};
|
||||
}
|
||||
&:disabled {
|
||||
opacity: 50%;
|
||||
cursor: auto;
|
||||
box-shadow: none;
|
||||
background-color: ${({ theme }) => theme.red1};
|
||||
border: 1px solid ${({ theme }) => theme.red1};
|
||||
background-color: ${({ theme }) => theme.deprecated_red1};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_red1};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -312,14 +312,14 @@ export function ButtonDropdownLight({ disabled = false, children, ...rest }: { d
|
||||
|
||||
const ActiveOutlined = styled(ButtonOutlined)`
|
||||
border: 1px solid;
|
||||
border-color: ${({ theme }) => theme.primary1};
|
||||
border-color: ${({ theme }) => theme.deprecated_primary1};
|
||||
`
|
||||
|
||||
const Circle = styled.div`
|
||||
height: 17px;
|
||||
width: 17px;
|
||||
border-radius: 50%;
|
||||
background-color: ${({ theme }) => theme.primary1};
|
||||
background-color: ${({ theme }) => theme.deprecated_primary1};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -354,7 +354,7 @@ export function ButtonRadioChecked({ active = false, children, ...rest }: { acti
|
||||
{children}
|
||||
<CheckboxWrapper>
|
||||
<Circle>
|
||||
<ResponsiveCheck size={13} stroke={theme.white} />
|
||||
<ResponsiveCheck size={13} stroke={theme.deprecated_white} />
|
||||
</Circle>
|
||||
</CheckboxWrapper>
|
||||
</RowBetween>
|
||||
|
||||
@@ -10,38 +10,38 @@ const Card = styled(Box)<{ width?: string; padding?: string; border?: string; $b
|
||||
export default Card
|
||||
|
||||
export const LightCard = styled(Card)`
|
||||
border: 1px solid ${({ theme }) => theme.bg2};
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg2};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
`
|
||||
|
||||
export const LightGreyCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
`
|
||||
|
||||
export const GreyCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg3};
|
||||
`
|
||||
|
||||
export const DarkGreyCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
`
|
||||
|
||||
export const DarkCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg0};
|
||||
`
|
||||
|
||||
export const OutlineCard = styled(Card)`
|
||||
border: 1px solid ${({ theme }) => theme.bg3};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg3};
|
||||
`
|
||||
|
||||
export const YellowCard = styled(Card)`
|
||||
background-color: rgba(243, 132, 30, 0.05);
|
||||
color: ${({ theme }) => theme.yellow3};
|
||||
color: ${({ theme }) => theme.deprecated_yellow3};
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
export const BlueCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.primary5};
|
||||
color: ${({ theme }) => theme.blue2};
|
||||
background-color: ${({ theme }) => theme.deprecated_primary5};
|
||||
color: ${({ theme }) => theme.deprecated_blue2};
|
||||
border-radius: 12px;
|
||||
`
|
||||
|
||||
58
src/components/ConnectedAccountBlocked/index.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import CopyHelper from 'components/AccountDetails/Copy'
|
||||
import Column from 'components/Column'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import { AlertOctagon } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink, ThemedText } from 'theme'
|
||||
|
||||
import Modal from '../Modal'
|
||||
|
||||
const ContentWrapper = styled(Column)`
|
||||
align-items: center;
|
||||
margin: 32px;
|
||||
text-align: center;
|
||||
`
|
||||
const WarningIcon = styled(AlertOctagon)`
|
||||
min-height: 22px;
|
||||
min-width: 22px;
|
||||
color: ${({ theme }) => theme.deprecated_warning};
|
||||
`
|
||||
const Copy = styled(CopyHelper)`
|
||||
font-size: 12px;
|
||||
`
|
||||
|
||||
interface ConnectedAccountBlockedProps {
|
||||
account: string | null | undefined
|
||||
isOpen: boolean
|
||||
}
|
||||
|
||||
export default function ConnectedAccountBlocked(props: ConnectedAccountBlockedProps) {
|
||||
const theme = useTheme()
|
||||
return (
|
||||
<Modal isOpen={props.isOpen} onDismiss={Function.prototype()}>
|
||||
<ContentWrapper>
|
||||
<WarningIcon />
|
||||
<ThemedText.LargeHeader lineHeight={2} marginBottom={1} marginTop={1}>
|
||||
<Trans>Blocked Address</Trans>
|
||||
</ThemedText.LargeHeader>
|
||||
<ThemedText.DarkGray fontSize={12} marginBottom={12}>
|
||||
{props.account}
|
||||
</ThemedText.DarkGray>
|
||||
<ThemedText.Main fontSize={14} marginBottom={12}>
|
||||
<Trans>This address is blocked on the Uniswap Labs interface because it is associated with one or more</Trans>{' '}
|
||||
<ExternalLink href="https://help.uniswap.org/en/articles/6149816">
|
||||
<Trans>blocked activities</Trans>
|
||||
</ExternalLink>
|
||||
.
|
||||
</ThemedText.Main>
|
||||
<ThemedText.Main fontSize={12}>
|
||||
<Trans>If you believe this is an error, please send an email including your address to </Trans>{' '}
|
||||
</ThemedText.Main>
|
||||
<Copy iconSize={12} toCopy="compliance@uniswap.org" color={theme.deprecated_primary1} iconPosition="right">
|
||||
compliance@uniswap.org
|
||||
</Copy>
|
||||
</ContentWrapper>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
@@ -20,21 +20,24 @@ export function FiatValue({
|
||||
const theme = useTheme()
|
||||
const priceImpactColor = useMemo(() => {
|
||||
if (!priceImpact) return undefined
|
||||
if (priceImpact.lessThan('0')) return theme.green1
|
||||
if (priceImpact.lessThan('0')) return theme.deprecated_green1
|
||||
const severity = warningSeverity(priceImpact)
|
||||
if (severity < 1) return theme.text3
|
||||
if (severity < 3) return theme.yellow1
|
||||
return theme.red1
|
||||
}, [priceImpact, theme.green1, theme.red1, theme.text3, theme.yellow1])
|
||||
if (severity < 1) return theme.deprecated_text3
|
||||
if (severity < 3) return theme.deprecated_yellow1
|
||||
return theme.deprecated_red1
|
||||
}, [priceImpact, theme.deprecated_green1, theme.deprecated_red1, theme.deprecated_text3, theme.deprecated_yellow1])
|
||||
|
||||
const p = Number(fiatValue?.toFixed())
|
||||
const visibleDecimalPlaces = p < 1.05 ? 4 : 2
|
||||
|
||||
return (
|
||||
<ThemedText.Body fontSize={14} color={fiatValue ? theme.text3 : theme.text4}>
|
||||
<ThemedText.Body fontSize={14} color={fiatValue ? theme.deprecated_text3 : theme.deprecated_text4}>
|
||||
{fiatValue ? (
|
||||
<Trans>
|
||||
$
|
||||
<HoverInlineText
|
||||
text={fiatValue?.toSignificant(6, { groupSeparator: ',' })}
|
||||
textColor={fiatValue ? theme.text3 : theme.text4}
|
||||
text={fiatValue?.toFixed(visibleDecimalPlaces, { groupSeparator: ',' })}
|
||||
textColor={fiatValue ? theme.deprecated_text3 : theme.deprecated_text4}
|
||||
/>
|
||||
</Trans>
|
||||
) : (
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
|
||||
import { Pair } from '@uniswap/v2-sdk'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { ElementName, Event, EventName } from 'components/AmplitudeAnalytics/constants'
|
||||
import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { LoadingOpacityContainer, loadingOpacityMixin } from 'components/Loader/styled'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { isSupportedChain } from 'constants/chains'
|
||||
import { darken } from 'polished'
|
||||
import { ReactNode, useCallback, useState } from 'react'
|
||||
import { Lock } from 'react-feather'
|
||||
@@ -12,7 +15,7 @@ import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
|
||||
|
||||
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
|
||||
import useTheme from '../../hooks/useTheme'
|
||||
import { useCurrencyBalance } from '../../state/wallet/hooks'
|
||||
import { useCurrencyBalance } from '../../state/connection/hooks'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { ButtonGray } from '../Button'
|
||||
import CurrencyLogo from '../CurrencyLogo'
|
||||
@@ -26,7 +29,7 @@ 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)};
|
||||
background-color: ${({ theme, hideInput }) => (hideInput ? 'transparent' : theme.deprecated_bg2)};
|
||||
z-index: 1;
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
transition: height 1s ease;
|
||||
@@ -38,30 +41,40 @@ const FixedContainer = styled.div`
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
border-radius: 20px;
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
`
|
||||
|
||||
const Container = styled.div<{ hideInput: boolean }>`
|
||||
const Container = styled.div<{ hideInput: boolean; disabled: boolean }>`
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
|
||||
border: 1px solid ${({ theme }) => theme.bg0};
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg0};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
:focus,
|
||||
:hover {
|
||||
border: 1px solid ${({ theme, hideInput }) => (hideInput ? ' transparent' : theme.bg3)};
|
||||
}
|
||||
${({ theme, hideInput, disabled }) =>
|
||||
!disabled &&
|
||||
`
|
||||
:focus,
|
||||
:hover {
|
||||
border: 1px solid ${hideInput ? ' transparent' : theme.deprecated_bg3};
|
||||
}
|
||||
`}
|
||||
`
|
||||
|
||||
const CurrencySelect = styled(ButtonGray)<{ visible: boolean; selected: boolean; hideInput?: boolean }>`
|
||||
const CurrencySelect = styled(ButtonGray)<{
|
||||
visible: boolean
|
||||
selected: boolean
|
||||
hideInput?: boolean
|
||||
disabled?: boolean
|
||||
}>`
|
||||
align-items: center;
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.bg2 : theme.primary1)};
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.deprecated_bg2 : theme.deprecated_primary1)};
|
||||
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
|
||||
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);
|
||||
color: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)};
|
||||
color: ${({ selected, theme }) => (selected ? theme.deprecated_text1 : theme.deprecated_white)};
|
||||
cursor: pointer;
|
||||
border-radius: 16px;
|
||||
outline: none;
|
||||
@@ -76,7 +89,8 @@ const CurrencySelect = styled(ButtonGray)<{ visible: boolean; selected: boolean;
|
||||
margin-left: ${({ hideInput }) => (hideInput ? '0' : '12px')};
|
||||
:focus,
|
||||
:hover {
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.bg3 : darken(0.05, theme.primary1))};
|
||||
background-color: ${({ selected, theme }) =>
|
||||
selected ? theme.deprecated_bg3 : darken(0.05, theme.deprecated_primary1)};
|
||||
}
|
||||
visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')};
|
||||
`
|
||||
@@ -91,18 +105,19 @@ const InputRow = styled.div<{ selected: boolean }>`
|
||||
const LabelRow = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
padding: 0 1rem 1rem;
|
||||
span:hover {
|
||||
cursor: pointer;
|
||||
color: ${({ theme }) => darken(0.2, theme.text2)};
|
||||
color: ${({ theme }) => darken(0.2, theme.deprecated_text2)};
|
||||
}
|
||||
`
|
||||
|
||||
const FiatRow = styled(LabelRow)`
|
||||
justify-content: flex-end;
|
||||
height: 16px;
|
||||
`
|
||||
|
||||
const Aligner = styled.span`
|
||||
@@ -117,7 +132,7 @@ const StyledDropDown = styled(DropDown)<{ selected: boolean }>`
|
||||
height: 35%;
|
||||
|
||||
path {
|
||||
stroke: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)};
|
||||
stroke: ${({ selected, theme }) => (selected ? theme.deprecated_text1 : theme.deprecated_white)};
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
`
|
||||
@@ -129,10 +144,10 @@ const StyledTokenName = styled.span<{ active?: boolean }>`
|
||||
|
||||
const StyledBalanceMax = styled.button<{ disabled?: boolean }>`
|
||||
background-color: transparent;
|
||||
background-color: ${({ theme }) => theme.primary5};
|
||||
background-color: ${({ theme }) => theme.deprecated_primary5};
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
color: ${({ theme }) => theme.primary1};
|
||||
color: ${({ theme }) => theme.deprecated_primary1};
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
@@ -201,7 +216,7 @@ export default function CurrencyInputPanel({
|
||||
...rest
|
||||
}: CurrencyInputPanelProps) {
|
||||
const [modalOpen, setModalOpen] = useState(false)
|
||||
const { account } = useActiveWeb3React()
|
||||
const { account, chainId } = useWeb3React()
|
||||
const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined)
|
||||
const theme = useTheme()
|
||||
|
||||
@@ -209,6 +224,8 @@ export default function CurrencyInputPanel({
|
||||
setModalOpen(false)
|
||||
}, [setModalOpen])
|
||||
|
||||
const chainAllowed = isSupportedChain(chainId)
|
||||
|
||||
return (
|
||||
<InputPanel id={id} hideInput={hideInput} {...rest}>
|
||||
{locked && (
|
||||
@@ -221,18 +238,20 @@ export default function CurrencyInputPanel({
|
||||
</AutoColumn>
|
||||
</FixedContainer>
|
||||
)}
|
||||
<Container hideInput={hideInput}>
|
||||
<Container hideInput={hideInput} disabled={!chainAllowed}>
|
||||
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={!onCurrencySelect}>
|
||||
{!hideInput && (
|
||||
<StyledNumericalInput
|
||||
className="token-amount-input"
|
||||
value={value}
|
||||
onUserInput={onUserInput}
|
||||
disabled={!chainAllowed}
|
||||
$loading={loading}
|
||||
/>
|
||||
)}
|
||||
|
||||
<CurrencySelect
|
||||
disabled={!chainAllowed}
|
||||
visible={currency !== undefined}
|
||||
selected={!!currency}
|
||||
hideInput={hideInput}
|
||||
@@ -280,7 +299,7 @@ export default function CurrencyInputPanel({
|
||||
<RowFixed style={{ height: '17px' }}>
|
||||
<ThemedText.Body
|
||||
onClick={onMax}
|
||||
color={theme.text3}
|
||||
color={theme.deprecated_text3}
|
||||
fontWeight={500}
|
||||
fontSize={14}
|
||||
style={{ display: 'inline', cursor: 'pointer' }}
|
||||
@@ -294,9 +313,15 @@ export default function CurrencyInputPanel({
|
||||
) : null}
|
||||
</ThemedText.Body>
|
||||
{showMaxButton && selectedCurrencyBalance ? (
|
||||
<StyledBalanceMax onClick={onMax}>
|
||||
<Trans>MAX</Trans>
|
||||
</StyledBalanceMax>
|
||||
<TraceEvent
|
||||
events={[Event.onClick]}
|
||||
name={EventName.SWAP_MAX_TOKEN_AMOUNT_SELECTED}
|
||||
element={ElementName.MAX_TOKEN_AMOUNT_BUTTON}
|
||||
>
|
||||
<StyledBalanceMax onClick={onMax}>
|
||||
<Trans>MAX</Trans>
|
||||
</StyledBalanceMax>
|
||||
</TraceEvent>
|
||||
) : null}
|
||||
</RowFixed>
|
||||
) : (
|
||||
|
||||
@@ -1,52 +1,10 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import EthereumLogo from 'assets/images/ethereum-logo.png'
|
||||
import MaticLogo from 'assets/svg/matic-token-icon.svg'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useHttpLocations from 'hooks/useHttpLocations'
|
||||
import React, { useMemo } from 'react'
|
||||
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
|
||||
import useCurrencyLogoURIs from 'lib/hooks/useCurrencyLogoURIs'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import Logo from '../Logo'
|
||||
|
||||
type Network = 'ethereum' | 'arbitrum' | 'optimism'
|
||||
|
||||
function chainIdToNetworkName(networkId: SupportedChainId): Network {
|
||||
switch (networkId) {
|
||||
case SupportedChainId.MAINNET:
|
||||
return 'ethereum'
|
||||
case SupportedChainId.ARBITRUM_ONE:
|
||||
return 'arbitrum'
|
||||
case SupportedChainId.OPTIMISM:
|
||||
return 'optimism'
|
||||
default:
|
||||
return 'ethereum'
|
||||
}
|
||||
}
|
||||
|
||||
export const getTokenLogoURL = (
|
||||
address: string,
|
||||
chainId: SupportedChainId = SupportedChainId.MAINNET
|
||||
): string | void => {
|
||||
const networkName = chainIdToNetworkName(chainId)
|
||||
const networksWithUrls = [SupportedChainId.ARBITRUM_ONE, SupportedChainId.MAINNET, SupportedChainId.OPTIMISM]
|
||||
if (networksWithUrls.includes(chainId)) {
|
||||
return `https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/${networkName}/assets/${address}/logo.png`
|
||||
}
|
||||
}
|
||||
|
||||
const StyledNativeLogo = styled.img<{ size: string }>`
|
||||
width: ${({ size }) => size};
|
||||
height: ${({ size }) => size};
|
||||
background: radial-gradient(white 50%, #ffffff00 calc(75% + 1px), #ffffff00 100%);
|
||||
|
||||
border-radius: 50%;
|
||||
-mox-box-shadow: 0 0 1px white;
|
||||
-webkit-box-shadow: 0 0 1px white;
|
||||
box-shadow: 0 0 1px white;
|
||||
border: 0px solid rgba(255, 255, 255, 0);
|
||||
`
|
||||
|
||||
const StyledLogo = styled(Logo)<{ size: string }>`
|
||||
width: ${({ size }) => size};
|
||||
height: ${({ size }) => size};
|
||||
@@ -58,6 +16,12 @@ const StyledLogo = styled(Logo)<{ size: string }>`
|
||||
border: 0px solid rgba(255, 255, 255, 0);
|
||||
`
|
||||
|
||||
const StyledNativeLogo = styled(StyledLogo)`
|
||||
-mox-box-shadow: 0 0 1px white;
|
||||
-webkit-box-shadow: 0 0 1px white;
|
||||
box-shadow: 0 0 1px white;
|
||||
`
|
||||
|
||||
export default function CurrencyLogo({
|
||||
currency,
|
||||
size = '24px',
|
||||
@@ -68,38 +32,13 @@ export default function CurrencyLogo({
|
||||
size?: string
|
||||
style?: React.CSSProperties
|
||||
}) {
|
||||
const uriLocations = useHttpLocations(currency instanceof WrappedTokenInfo ? currency.logoURI : undefined)
|
||||
|
||||
const srcs: string[] = useMemo(() => {
|
||||
if (!currency || currency.isNative) return []
|
||||
|
||||
if (currency.isToken) {
|
||||
const defaultUrls = []
|
||||
const url = getTokenLogoURL(currency.address, currency.chainId)
|
||||
if (url) {
|
||||
defaultUrls.push(url)
|
||||
}
|
||||
if (currency instanceof WrappedTokenInfo) {
|
||||
return [...uriLocations, ...defaultUrls]
|
||||
}
|
||||
return defaultUrls
|
||||
}
|
||||
return []
|
||||
}, [currency, uriLocations])
|
||||
|
||||
if (currency?.isNative) {
|
||||
let nativeLogoUrl: string
|
||||
switch (currency.chainId) {
|
||||
case SupportedChainId.POLYGON_MUMBAI:
|
||||
case SupportedChainId.POLYGON:
|
||||
nativeLogoUrl = MaticLogo
|
||||
break
|
||||
default:
|
||||
nativeLogoUrl = EthereumLogo
|
||||
break
|
||||
}
|
||||
return <StyledNativeLogo src={nativeLogoUrl} alt="ethereum logo" size={size} style={style} {...rest} />
|
||||
const props = {
|
||||
alt: `${currency?.symbol ?? 'token'} logo`,
|
||||
size,
|
||||
srcs: useCurrencyLogoURIs(currency),
|
||||
style,
|
||||
...rest,
|
||||
}
|
||||
|
||||
return <StyledLogo size={size} srcs={srcs} alt={`${currency?.symbol ?? 'token'} logo`} style={style} {...rest} />
|
||||
return currency?.isNative ? <StyledNativeLogo {...props} /> : <StyledLogo {...props} />
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { AlertOctagon } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink } from 'theme'
|
||||
@@ -42,7 +42,7 @@ function Wrapper({ children }: { children: React.ReactNode }) {
|
||||
* Shows a downtime warning for the network if it's relevant
|
||||
*/
|
||||
export default function DowntimeWarning() {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const { chainId } = useWeb3React()
|
||||
if (!isL2ChainId(chainId)) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import React, { ErrorInfo } from 'react'
|
||||
import ReactGA from 'react-ga'
|
||||
import { sendEvent } from 'components/analytics'
|
||||
import React, { ErrorInfo, PropsWithChildren } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import store, { AppState } from '../../state'
|
||||
@@ -24,18 +24,18 @@ const BodyWrapper = styled.div<{ margin?: string }>`
|
||||
`
|
||||
|
||||
const CodeBlockWrapper = styled.div`
|
||||
background: ${({ theme }) => theme.bg0};
|
||||
background: ${({ theme }) => theme.deprecated_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};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
`
|
||||
|
||||
const LinkWrapper = styled.div`
|
||||
color: ${({ theme }) => theme.blue1};
|
||||
color: ${({ theme }) => theme.deprecated_blue1};
|
||||
padding: 6px 24px;
|
||||
`
|
||||
|
||||
@@ -49,20 +49,44 @@ type ErrorBoundaryState = {
|
||||
|
||||
const IS_UNISWAP = window.location.hostname === 'app.uniswap.org'
|
||||
|
||||
export default class ErrorBoundary extends React.Component<unknown, ErrorBoundaryState> {
|
||||
constructor(props: unknown) {
|
||||
async function updateServiceWorker(): Promise<ServiceWorkerRegistration> {
|
||||
const ready = await navigator.serviceWorker.ready
|
||||
// the return type of update is incorrectly typed as Promise<void>. See
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/update
|
||||
return ready.update() as unknown as Promise<ServiceWorkerRegistration>
|
||||
}
|
||||
|
||||
export default class ErrorBoundary extends React.Component<PropsWithChildren<unknown>, ErrorBoundaryState> {
|
||||
constructor(props: PropsWithChildren<unknown>) {
|
||||
super(props)
|
||||
this.state = { error: null }
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
||||
updateServiceWorker()
|
||||
.then(async (registration) => {
|
||||
// We want to refresh only if we detect a new service worker is waiting to be activated.
|
||||
// See details about it: https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle
|
||||
if (registration?.waiting) {
|
||||
await registration.unregister()
|
||||
|
||||
// Makes Workbox call skipWaiting(). For more info on skipWaiting see: https://developer.chrome.com/docs/workbox/handling-service-worker-updates/
|
||||
registration.waiting.postMessage({ type: 'SKIP_WAITING' })
|
||||
|
||||
// Once the service worker is unregistered, we can reload the page to let
|
||||
// the browser download a fresh copy of our app (invalidating the cache)
|
||||
window.location.reload()
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to update service worker', error)
|
||||
})
|
||||
return { error }
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||
ReactGA.exception({
|
||||
...error,
|
||||
...errorInfo,
|
||||
sendEvent('exception', {
|
||||
description: error.toString() + errorInfo.toString(),
|
||||
fatal: true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { sendEvent } from 'components/analytics'
|
||||
import { ButtonGray } from 'components/Button'
|
||||
import Card from 'components/Card'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { RowBetween } from 'components/Row'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useFeeTierDistribution } from 'hooks/useFeeTierDistribution'
|
||||
import { PoolState, usePools } from 'hooks/usePools'
|
||||
import usePrevious from 'hooks/usePrevious'
|
||||
import { DynamicSection } from 'pages/AddLiquidity/styled'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import ReactGA from 'react-ga'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Box } from 'rebass'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
import { ThemedText } from 'theme'
|
||||
@@ -34,8 +34,8 @@ const pulse = (color: string) => keyframes`
|
||||
}
|
||||
`
|
||||
const FocusedOutlineCard = styled(Card)<{ pulsing: boolean }>`
|
||||
border: 1px solid ${({ theme }) => theme.bg2};
|
||||
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.primary1)} 0.6s linear;
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg2};
|
||||
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.deprecated_primary1)} 0.6s linear;
|
||||
align-self: center;
|
||||
`
|
||||
|
||||
@@ -59,7 +59,7 @@ export default function FeeSelector({
|
||||
currencyA?: Currency | undefined
|
||||
currencyB?: Currency | undefined
|
||||
}) {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const { chainId } = useWeb3React()
|
||||
|
||||
const { isLoading, isError, largestUsageFeeTier, distributions } = useFeeTierDistribution(currencyA, currencyB)
|
||||
|
||||
@@ -101,7 +101,7 @@ export default function FeeSelector({
|
||||
|
||||
const handleFeePoolSelectWithEvent = useCallback(
|
||||
(fee: FeeAmount) => {
|
||||
ReactGA.event({
|
||||
sendEvent({
|
||||
category: 'FeePoolSelect',
|
||||
action: 'Manual',
|
||||
})
|
||||
@@ -122,7 +122,7 @@ export default function FeeSelector({
|
||||
setShowOptions(false)
|
||||
|
||||
recommended.current = true
|
||||
ReactGA.event({
|
||||
sendEvent({
|
||||
category: 'FeePoolSelect',
|
||||
action: ' Recommended',
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from 'constants/chains'
|
||||
import { ReactNode } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
export const FEE_AMOUNT_DETAIL: Record<
|
||||
FeeAmount,
|
||||
@@ -10,7 +10,14 @@ export const FEE_AMOUNT_DETAIL: Record<
|
||||
[FeeAmount.LOWEST]: {
|
||||
label: '0.01',
|
||||
description: <Trans>Best for very stable pairs.</Trans>,
|
||||
supportedChains: [SupportedChainId.MAINNET],
|
||||
supportedChains: [
|
||||
SupportedChainId.MAINNET,
|
||||
SupportedChainId.POLYGON,
|
||||
SupportedChainId.POLYGON_MUMBAI,
|
||||
SupportedChainId.CELO,
|
||||
SupportedChainId.CELO_ALFAJORES,
|
||||
SupportedChainId.OPTIMISM,
|
||||
],
|
||||
},
|
||||
[FeeAmount.LOW]: {
|
||||
label: '0.05',
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CHAIN_INFO, L2ChainInfo } from 'constants/chainInfo'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { getChainInfoOrDefault, L2ChainInfo } from 'constants/chainInfo'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { AlertOctagon } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
|
||||
|
||||
const BodyRow = styled.div`
|
||||
color: ${({ theme }) => theme.black};
|
||||
color: ${({ theme }) => theme.deprecated_black};
|
||||
font-size: 12px;
|
||||
`
|
||||
const CautionIcon = styled(AlertOctagon)`
|
||||
color: ${({ theme }) => theme.black};
|
||||
color: ${({ theme }) => theme.deprecated_black};
|
||||
`
|
||||
const Link = styled(ExternalLink)`
|
||||
color: ${({ theme }) => theme.black};
|
||||
color: ${({ theme }) => theme.deprecated_black};
|
||||
text-decoration: underline;
|
||||
`
|
||||
const TitleRow = styled.div`
|
||||
@@ -31,7 +31,7 @@ const TitleText = styled.div`
|
||||
margin: 0px 12px;
|
||||
`
|
||||
const Wrapper = styled.div`
|
||||
background-color: ${({ theme }) => theme.yellow3};
|
||||
background-color: ${({ theme }) => theme.deprecated_yellow3};
|
||||
border-radius: 12px;
|
||||
bottom: 60px;
|
||||
display: none;
|
||||
@@ -45,8 +45,8 @@ const Wrapper = styled.div`
|
||||
`
|
||||
|
||||
export function ChainConnectivityWarning() {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const info = CHAIN_INFO[chainId ?? SupportedChainId.MAINNET]
|
||||
const { chainId } = useWeb3React()
|
||||
const info = getChainInfoOrDefault(chainId)
|
||||
const label = info?.label
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CHAIN_INFO } from 'constants/chainInfo'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useOnClickOutside } from 'hooks/useOnClickOutside'
|
||||
import { useCallback, useRef } from 'react'
|
||||
import { ArrowDownCircle, ChevronDown } from 'react-feather'
|
||||
import { useModalOpen, useToggleModal } from 'state/application/hooks'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { getConnection } from 'connection/utils'
|
||||
import { getChainInfo } from 'constants/chainInfo'
|
||||
import { CHAIN_IDS_TO_NAMES, SupportedChainId } from 'constants/chains'
|
||||
import useParsedQueryString from 'hooks/useParsedQueryString'
|
||||
import usePrevious from 'hooks/usePrevious'
|
||||
import { darken } from 'polished'
|
||||
import { ParsedQs } from 'qs'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { AlertTriangle, ArrowDownCircle, ChevronDown } from 'react-feather'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { useCloseModal, useModalIsOpen, useOpenModal, useToggleModal } from 'state/application/hooks'
|
||||
import { addPopup, ApplicationModal } from 'state/application/reducer'
|
||||
import { updateConnectionError } from 'state/connection/reducer'
|
||||
import { useAppDispatch } from 'state/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
|
||||
|
||||
import { useAppDispatch } from '../../state/hooks'
|
||||
import { switchToNetwork } from '../../utils/switchToNetwork'
|
||||
import { replaceURLParam } from 'utils/routes'
|
||||
import { isChainAllowed, switchChain } from 'utils/switchChain'
|
||||
import { isMobile } from 'utils/userAgent'
|
||||
|
||||
const ActiveRowLinkList = styled.div`
|
||||
display: flex;
|
||||
@@ -19,7 +26,7 @@ const ActiveRowLinkList = styled.div`
|
||||
padding: 0 8px;
|
||||
& > a {
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 14px;
|
||||
@@ -35,19 +42,29 @@ const ActiveRowLinkList = styled.div`
|
||||
}
|
||||
`
|
||||
const ActiveRowWrapper = styled.div`
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
`
|
||||
const FlyoutHeader = styled.div`
|
||||
color: ${({ theme }) => theme.text2};
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
font-weight: 400;
|
||||
`
|
||||
const FlyoutMenu = styled.div`
|
||||
position: absolute;
|
||||
top: 54px;
|
||||
width: 272px;
|
||||
z-index: 99;
|
||||
padding-top: 10px;
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
top: 40px;
|
||||
}
|
||||
`
|
||||
const FlyoutMenuContents = styled.div`
|
||||
align-items: flex-start;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg0};
|
||||
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: 20px;
|
||||
@@ -56,20 +73,13 @@ const FlyoutMenu = styled.div`
|
||||
font-size: 16px;
|
||||
overflow: auto;
|
||||
padding: 16px;
|
||||
position: absolute;
|
||||
top: 64px;
|
||||
width: 272px;
|
||||
z-index: 99;
|
||||
& > *:not(:last-child) {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
top: 50px;
|
||||
}
|
||||
`
|
||||
const FlyoutRow = styled.div<{ active: boolean }>`
|
||||
align-items: center;
|
||||
background-color: ${({ active, theme }) => (active ? theme.bg1 : 'transparent')};
|
||||
background-color: ${({ active, theme }) => (active ? theme.deprecated_bg1 : 'transparent')};
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
@@ -80,11 +90,18 @@ const FlyoutRow = styled.div<{ active: boolean }>`
|
||||
width: 100%;
|
||||
`
|
||||
const FlyoutRowActiveIndicator = styled.div`
|
||||
background-color: ${({ theme }) => theme.green1};
|
||||
background-color: ${({ theme }) => theme.deprecated_green1};
|
||||
border-radius: 50%;
|
||||
height: 9px;
|
||||
width: 9px;
|
||||
`
|
||||
|
||||
const CircleContainer = styled.div`
|
||||
width: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
`
|
||||
|
||||
const LinkOutCircle = styled(ArrowDownCircle)`
|
||||
transform: rotate(230deg);
|
||||
width: 16px;
|
||||
@@ -105,20 +122,41 @@ const SelectorLabel = styled(NetworkLabel)`
|
||||
margin-right: 8px;
|
||||
}
|
||||
`
|
||||
const SelectorControls = styled.div<{ interactive: boolean }>`
|
||||
const NetworkAlertLabel = styled(NetworkLabel)`
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin: 0 0.5rem 0 0.4rem;
|
||||
font-size: 1rem;
|
||||
width: fit-content;
|
||||
font-weight: 500;
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
display: block;
|
||||
}
|
||||
`
|
||||
const SelectorControls = styled.div<{ supportedChain: boolean }>`
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
border: 2px solid ${({ theme }) => theme.bg0};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg0};
|
||||
border: 2px solid ${({ theme }) => theme.deprecated_bg0};
|
||||
border-radius: 16px;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
cursor: ${({ interactive }) => (interactive ? 'pointer' : 'auto')};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
display: flex;
|
||||
font-weight: 500;
|
||||
justify-content: space-between;
|
||||
padding: 6px 8px;
|
||||
${({ supportedChain, theme }) =>
|
||||
!supportedChain &&
|
||||
`
|
||||
color: ${theme.deprecated_white};
|
||||
background-color: ${theme.deprecated_red1};
|
||||
border: 2px solid ${theme.deprecated_red1};
|
||||
`}
|
||||
:focus {
|
||||
background-color: ${({ theme }) => darken(0.1, theme.deprecated_red1)};
|
||||
}
|
||||
`
|
||||
const SelectorLogo = styled(Logo)<{ interactive?: boolean }>`
|
||||
margin-right: ${({ interactive }) => (interactive ? 8 : 0)}px;
|
||||
const SelectorLogo = styled(Logo)`
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
@@ -131,6 +169,14 @@ const SelectorWrapper = styled.div`
|
||||
const StyledChevronDown = styled(ChevronDown)`
|
||||
width: 16px;
|
||||
`
|
||||
|
||||
const NetworkIcon = styled(AlertTriangle)`
|
||||
margin-left: 0.25rem;
|
||||
margin-right: 0.25rem;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
`
|
||||
|
||||
const BridgeLabel = ({ chainId }: { chainId: SupportedChainId }) => {
|
||||
switch (chainId) {
|
||||
case SupportedChainId.ARBITRUM_ONE:
|
||||
@@ -138,10 +184,13 @@ const BridgeLabel = ({ chainId }: { chainId: SupportedChainId }) => {
|
||||
return <Trans>Arbitrum Bridge</Trans>
|
||||
case SupportedChainId.OPTIMISM:
|
||||
case SupportedChainId.OPTIMISTIC_KOVAN:
|
||||
return <Trans>Optimism Gateway</Trans>
|
||||
return <Trans>Optimism Bridge</Trans>
|
||||
case SupportedChainId.POLYGON:
|
||||
case SupportedChainId.POLYGON_MUMBAI:
|
||||
return <Trans>Polygon Bridge</Trans>
|
||||
case SupportedChainId.CELO:
|
||||
case SupportedChainId.CELO_ALFAJORES:
|
||||
return <Trans>Portal Bridge</Trans>
|
||||
default:
|
||||
return <Trans>Bridge</Trans>
|
||||
}
|
||||
@@ -157,6 +206,9 @@ const ExplorerLabel = ({ chainId }: { chainId: SupportedChainId }) => {
|
||||
case SupportedChainId.POLYGON:
|
||||
case SupportedChainId.POLYGON_MUMBAI:
|
||||
return <Trans>Polygonscan</Trans>
|
||||
case SupportedChainId.CELO:
|
||||
case SupportedChainId.CELO_ALFAJORES:
|
||||
return <Trans>Blockscout</Trans>
|
||||
default:
|
||||
return <Trans>Etherscan</Trans>
|
||||
}
|
||||
@@ -169,18 +221,22 @@ function Row({
|
||||
targetChain: SupportedChainId
|
||||
onSelectChain: (targetChain: number) => void
|
||||
}) {
|
||||
const { library, chainId } = useActiveWeb3React()
|
||||
if (!library || !chainId) {
|
||||
const { provider, chainId } = useWeb3React()
|
||||
if (!provider || !chainId) {
|
||||
return null
|
||||
}
|
||||
const active = chainId === targetChain
|
||||
const { helpCenterUrl, explorer, bridge, label, logoUrl } = CHAIN_INFO[targetChain]
|
||||
const { helpCenterUrl, explorer, bridge, label, logoUrl } = getChainInfo(targetChain)
|
||||
|
||||
const rowContent = (
|
||||
<FlyoutRow onClick={() => onSelectChain(targetChain)} active={active}>
|
||||
<Logo src={logoUrl} />
|
||||
<NetworkLabel>{label}</NetworkLabel>
|
||||
{chainId === targetChain && <FlyoutRowActiveIndicator />}
|
||||
{chainId === targetChain && (
|
||||
<CircleContainer>
|
||||
<FlyoutRowActiveIndicator />
|
||||
</CircleContainer>
|
||||
)}
|
||||
</FlyoutRow>
|
||||
)
|
||||
|
||||
@@ -189,21 +245,30 @@ function Row({
|
||||
<ActiveRowWrapper>
|
||||
{rowContent}
|
||||
<ActiveRowLinkList>
|
||||
{bridge ? (
|
||||
{bridge && (
|
||||
<ExternalLink href={bridge}>
|
||||
<BridgeLabel chainId={chainId} /> <LinkOutCircle />
|
||||
<BridgeLabel chainId={chainId} />
|
||||
<CircleContainer>
|
||||
<LinkOutCircle />
|
||||
</CircleContainer>
|
||||
</ExternalLink>
|
||||
) : null}
|
||||
{explorer ? (
|
||||
)}
|
||||
{explorer && (
|
||||
<ExternalLink href={explorer}>
|
||||
<ExplorerLabel chainId={chainId} /> <LinkOutCircle />
|
||||
<ExplorerLabel chainId={chainId} />
|
||||
<CircleContainer>
|
||||
<LinkOutCircle />
|
||||
</CircleContainer>
|
||||
</ExternalLink>
|
||||
) : null}
|
||||
{helpCenterUrl ? (
|
||||
)}
|
||||
{helpCenterUrl && (
|
||||
<ExternalLink href={helpCenterUrl}>
|
||||
<Trans>Help Center</Trans> <LinkOutCircle />
|
||||
<Trans>Help Center</Trans>
|
||||
<CircleContainer>
|
||||
<LinkOutCircle />
|
||||
</CircleContainer>
|
||||
</ExternalLink>
|
||||
) : null}
|
||||
)}
|
||||
</ActiveRowLinkList>
|
||||
</ActiveRowWrapper>
|
||||
)
|
||||
@@ -211,51 +276,156 @@ function Row({
|
||||
return rowContent
|
||||
}
|
||||
|
||||
const getParsedChainId = (parsedQs?: ParsedQs) => {
|
||||
const chain = parsedQs?.chain
|
||||
if (!chain || typeof chain !== 'string') return
|
||||
|
||||
return getChainIdFromName(chain)
|
||||
}
|
||||
|
||||
const getChainIdFromName = (name: string) => {
|
||||
const entry = Object.entries(CHAIN_IDS_TO_NAMES).find(([_, n]) => n === name)
|
||||
const chainId = entry?.[0]
|
||||
return chainId ? parseInt(chainId) : undefined
|
||||
}
|
||||
|
||||
const getChainNameFromId = (id: string | number) => {
|
||||
// casting here may not be right but fine to return undefined if it's not a supported chain ID
|
||||
return CHAIN_IDS_TO_NAMES[id as SupportedChainId] || ''
|
||||
}
|
||||
|
||||
const NETWORK_SELECTOR_CHAINS = [
|
||||
SupportedChainId.MAINNET,
|
||||
SupportedChainId.POLYGON,
|
||||
SupportedChainId.OPTIMISM,
|
||||
SupportedChainId.ARBITRUM_ONE,
|
||||
SupportedChainId.CELO,
|
||||
]
|
||||
|
||||
export default function NetworkSelector() {
|
||||
const { chainId, library } = useActiveWeb3React()
|
||||
const node = useRef<HTMLDivElement>()
|
||||
const open = useModalOpen(ApplicationModal.NETWORK_SELECTOR)
|
||||
const toggle = useToggleModal(ApplicationModal.NETWORK_SELECTOR)
|
||||
useOnClickOutside(node, open ? toggle : undefined)
|
||||
|
||||
const info = chainId ? CHAIN_INFO[chainId] : undefined
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const handleRowClick = useCallback(
|
||||
(targetChain: number) => {
|
||||
if (!library) return
|
||||
switchToNetwork({ library, chainId: targetChain })
|
||||
.then(() => toggle())
|
||||
.catch((error) => {
|
||||
console.error('Failed to switch networks', error)
|
||||
toggle()
|
||||
dispatch(addPopup({ content: { failedSwitchNetwork: targetChain }, key: `failed-network-switch` }))
|
||||
})
|
||||
const { chainId, provider, connector, isActive } = useWeb3React()
|
||||
const [previousChainId, setPreviousChainId] = useState<number | undefined>(undefined)
|
||||
|
||||
// Can't use `usePrevious` because `chainId` can be undefined while activating.
|
||||
useEffect(() => {
|
||||
if (chainId && chainId !== previousChainId) {
|
||||
setPreviousChainId(chainId)
|
||||
}
|
||||
}, [chainId, previousChainId])
|
||||
|
||||
const parsedQs = useParsedQueryString()
|
||||
const urlChainId = getParsedChainId(parsedQs)
|
||||
const previousUrlChainId = usePrevious(urlChainId)
|
||||
|
||||
const history = useHistory()
|
||||
|
||||
const node = useRef<HTMLDivElement>(null)
|
||||
const isOpen = useModalIsOpen(ApplicationModal.NETWORK_SELECTOR)
|
||||
const openModal = useOpenModal(ApplicationModal.NETWORK_SELECTOR)
|
||||
const closeModal = useCloseModal(ApplicationModal.NETWORK_SELECTOR)
|
||||
const toggleModal = useToggleModal(ApplicationModal.NETWORK_SELECTOR)
|
||||
|
||||
const info = getChainInfo(chainId)
|
||||
|
||||
const replaceURLChainParam = useCallback(() => {
|
||||
if (chainId) {
|
||||
history.replace({ search: replaceURLParam(history.location.search, 'chain', getChainNameFromId(chainId)) })
|
||||
}
|
||||
}, [chainId, history])
|
||||
|
||||
const onSelectChain = useCallback(
|
||||
async (targetChain: SupportedChainId, skipClose?: boolean) => {
|
||||
if (!connector) return
|
||||
|
||||
const connectionType = getConnection(connector).type
|
||||
|
||||
try {
|
||||
dispatch(updateConnectionError({ connectionType, error: undefined }))
|
||||
await switchChain(connector, targetChain)
|
||||
} catch (error) {
|
||||
console.error('Failed to switch networks', error)
|
||||
|
||||
dispatch(updateConnectionError({ connectionType, error: error.message }))
|
||||
dispatch(addPopup({ content: { failedSwitchNetwork: targetChain }, key: `failed-network-switch` }))
|
||||
|
||||
// If we activate a chain and it fails, reset the query param to the current chainId
|
||||
replaceURLChainParam()
|
||||
}
|
||||
|
||||
if (!skipClose) {
|
||||
closeModal()
|
||||
}
|
||||
},
|
||||
[dispatch, library, toggle]
|
||||
[connector, closeModal, dispatch, replaceURLChainParam]
|
||||
)
|
||||
|
||||
if (!chainId || !info || !library) {
|
||||
// If there is no chain query param, set it to the current chain
|
||||
useEffect(() => {
|
||||
const chainQueryUnpopulated = !urlChainId
|
||||
if (chainQueryUnpopulated && chainId) {
|
||||
replaceURLChainParam()
|
||||
}
|
||||
}, [chainId, urlChainId, replaceURLChainParam])
|
||||
|
||||
// If the chain changed but the query param is stale, update to the current chain
|
||||
useEffect(() => {
|
||||
const chainChanged = chainId !== previousChainId
|
||||
const chainQueryStale = urlChainId !== chainId
|
||||
if (chainChanged && chainQueryStale) {
|
||||
replaceURLChainParam()
|
||||
}
|
||||
}, [chainId, previousChainId, replaceURLChainParam, urlChainId])
|
||||
|
||||
// If the query param changed, and the chain didn't change, then activate the new chain
|
||||
useEffect(() => {
|
||||
const chainQueryManuallyUpdated = urlChainId && urlChainId !== previousUrlChainId
|
||||
if (chainQueryManuallyUpdated && isActive) {
|
||||
onSelectChain(urlChainId, true)
|
||||
}
|
||||
}, [onSelectChain, urlChainId, previousUrlChainId, isActive])
|
||||
|
||||
if (!chainId || !provider) {
|
||||
return null
|
||||
}
|
||||
|
||||
const onSupportedChain = info !== undefined
|
||||
|
||||
return (
|
||||
<SelectorWrapper ref={node as any}>
|
||||
<SelectorControls onClick={toggle} interactive>
|
||||
<SelectorLogo interactive src={info.logoUrl} />
|
||||
<SelectorLabel>{info.label}</SelectorLabel>
|
||||
<StyledChevronDown />
|
||||
<SelectorWrapper
|
||||
ref={node}
|
||||
onMouseEnter={openModal}
|
||||
onMouseLeave={closeModal}
|
||||
onClick={isMobile ? toggleModal : undefined}
|
||||
>
|
||||
<SelectorControls supportedChain={onSupportedChain}>
|
||||
{onSupportedChain ? (
|
||||
<>
|
||||
<SelectorLogo src={info.logoUrl} />
|
||||
<SelectorLabel>{info.label}</SelectorLabel>
|
||||
<StyledChevronDown />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<NetworkIcon />
|
||||
<NetworkAlertLabel>Switch Network</NetworkAlertLabel>
|
||||
<StyledChevronDown />
|
||||
</>
|
||||
)}
|
||||
</SelectorControls>
|
||||
{open && (
|
||||
<FlyoutMenu onMouseLeave={toggle}>
|
||||
<FlyoutHeader>
|
||||
<Trans>Select a network</Trans>
|
||||
</FlyoutHeader>
|
||||
<Row onSelectChain={handleRowClick} targetChain={SupportedChainId.MAINNET} />
|
||||
<Row onSelectChain={handleRowClick} targetChain={SupportedChainId.POLYGON} />
|
||||
<Row onSelectChain={handleRowClick} targetChain={SupportedChainId.OPTIMISM} />
|
||||
<Row onSelectChain={handleRowClick} targetChain={SupportedChainId.ARBITRUM_ONE} />
|
||||
{isOpen && (
|
||||
<FlyoutMenu>
|
||||
<FlyoutMenuContents>
|
||||
<FlyoutHeader>
|
||||
<Trans>Select a {!onSupportedChain ? ' supported ' : ''}network</Trans>
|
||||
</FlyoutHeader>
|
||||
{NETWORK_SELECTOR_CHAINS.map((chainId: SupportedChainId) =>
|
||||
isChainAllowed(connector, chainId) ? (
|
||||
<Row onSelectChain={onSelectChain} targetChain={chainId} key={chainId} />
|
||||
) : null
|
||||
)}
|
||||
</FlyoutMenuContents>
|
||||
</FlyoutMenu>
|
||||
)}
|
||||
</SelectorWrapper>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { RowFixed } from 'components/Row'
|
||||
import { CHAIN_INFO } from 'constants/chainInfo'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { getChainInfo } from 'constants/chainInfo'
|
||||
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
|
||||
import useGasPrice from 'hooks/useGasPrice'
|
||||
import useMachineTimeMs from 'hooks/useMachineTime'
|
||||
@@ -24,7 +24,7 @@ const StyledPolling = styled.div<{ warning: boolean }>`
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 1rem;
|
||||
color: ${({ theme, warning }) => (warning ? theme.yellow3 : theme.green1)};
|
||||
color: ${({ theme, warning }) => (warning ? theme.deprecated_yellow3 : theme.deprecated_green1)};
|
||||
transition: 250ms ease color;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
@@ -53,12 +53,12 @@ const StyledPollingDot = styled.div<{ warning: boolean }>`
|
||||
min-width: 8px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
background-color: ${({ theme, warning }) => (warning ? theme.yellow3 : theme.green1)};
|
||||
background-color: ${({ theme, warning }) => (warning ? theme.deprecated_yellow3 : theme.deprecated_green1)};
|
||||
transition: 250ms ease background-color;
|
||||
`
|
||||
|
||||
const StyledGasDot = styled.div`
|
||||
background-color: ${({ theme }) => theme.text3};
|
||||
background-color: ${({ theme }) => theme.deprecated_text3};
|
||||
border-radius: 50%;
|
||||
height: 4px;
|
||||
min-height: 4px;
|
||||
@@ -84,7 +84,7 @@ const Spinner = styled.div<{ warning: boolean }>`
|
||||
border-top: 1px solid transparent;
|
||||
border-right: 1px solid transparent;
|
||||
border-bottom: 1px solid transparent;
|
||||
border-left: 2px solid ${({ theme, warning }) => (warning ? theme.yellow3 : theme.green1)};
|
||||
border-left: 2px solid ${({ theme, warning }) => (warning ? theme.deprecated_yellow3 : theme.deprecated_green1)};
|
||||
background: transparent;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
@@ -100,7 +100,7 @@ const DEFAULT_MS_BEFORE_WARNING = ms`10m`
|
||||
const NETWORK_HEALTH_CHECK_MS = ms`10s`
|
||||
|
||||
export default function Polling() {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const { chainId } = useWeb3React()
|
||||
const blockNumber = useBlockNumber()
|
||||
const [isMounting, setIsMounting] = useState(false)
|
||||
const [isHover, setIsHover] = useState(false)
|
||||
@@ -112,7 +112,7 @@ export default function Polling() {
|
||||
const priceGwei = ethGasPrice ? JSBI.divide(ethGasPrice, JSBI.BigInt(1000000000)) : undefined
|
||||
|
||||
const waitMsBeforeWarning =
|
||||
(chainId ? CHAIN_INFO[chainId]?.blockWaitMsBeforeWarning : DEFAULT_MS_BEFORE_WARNING) ?? DEFAULT_MS_BEFORE_WARNING
|
||||
(chainId ? getChainInfo(chainId)?.blockWaitMsBeforeWarning : DEFAULT_MS_BEFORE_WARNING) ?? DEFAULT_MS_BEFORE_WARNING
|
||||
|
||||
const warning = Boolean(!!blockTime && machineTime - blockTime.mul(1000).toNumber() > waitMsBeforeWarning)
|
||||
|
||||
@@ -143,7 +143,7 @@ export default function Polling() {
|
||||
<ExternalLink href={'https://etherscan.io/gastracker'}>
|
||||
{priceGwei ? (
|
||||
<RowFixed style={{ marginRight: '8px' }}>
|
||||
<ThemedText.Main fontSize="11px" mr="8px" color={theme.text3}>
|
||||
<ThemedText.Main fontSize="11px" mr="8px" color={theme.deprecated_text3}>
|
||||
<MouseoverTooltip
|
||||
text={
|
||||
<Trans>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import useScrollPosition from '@react-hook/window-scroll'
|
||||
import { CHAIN_INFO } from 'constants/chainInfo'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { getChainInfoOrDefault } from 'constants/chainInfo'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import { darken } from 'polished'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { Text } from 'rebass'
|
||||
import { useShowClaimPopup, useToggleSelfClaimModal } from 'state/application/hooks'
|
||||
import { useUserHasAvailableClaim } from 'state/claim/hooks'
|
||||
import { useNativeCurrencyBalances } from 'state/connection/hooks'
|
||||
import { useUserHasSubmittedClaim } from 'state/transactions/hooks'
|
||||
import { useDarkModeManager } from 'state/user/hooks'
|
||||
import { useNativeCurrencyBalances } from 'state/wallet/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { ReactComponent as Logo } from '../../assets/svg/logo.svg'
|
||||
@@ -39,10 +39,10 @@ const HeaderFrame = styled.div<{ showBackground: boolean }>`
|
||||
z-index: 21;
|
||||
position: relative;
|
||||
/* Background slide effect on scroll. */
|
||||
background-image: ${({ theme }) => `linear-gradient(to bottom, transparent 50%, ${theme.bg0} 50% )}}`};
|
||||
background-image: ${({ theme }) => `linear-gradient(to bottom, transparent 50%, ${theme.deprecated_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;')};
|
||||
box-shadow: 0px 0px 0px 1px ${({ theme, showBackground }) => (showBackground ? theme.deprecated_bg2 : 'transparent;')};
|
||||
transition: background-position 0.1s, box-shadow 0.1s;
|
||||
background-blend-mode: hard-light;
|
||||
|
||||
@@ -76,7 +76,7 @@ const HeaderElement = styled.div`
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
/* addresses safari's lack of support for "gap" */
|
||||
/* addresses safaris lack of support for "gap" */
|
||||
& > *:not(:first-child) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
@@ -88,7 +88,7 @@ const HeaderElement = styled.div`
|
||||
|
||||
const HeaderLinks = styled(Row)`
|
||||
justify-self: center;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg0};
|
||||
width: fit-content;
|
||||
padding: 2px;
|
||||
border-radius: 16px;
|
||||
@@ -98,7 +98,7 @@ const HeaderLinks = styled(Row)`
|
||||
overflow: auto;
|
||||
align-items: center;
|
||||
${({ theme }) => theme.mediaWidth.upToLarge`
|
||||
justify-self: start;
|
||||
justify-self: start;
|
||||
`};
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
justify-self: center;
|
||||
@@ -112,8 +112,8 @@ const HeaderLinks = styled(Row)`
|
||||
bottom: 0; right: 50%;
|
||||
transform: translate(50%,-50%);
|
||||
margin: 0 auto;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
border: 1px solid ${({ theme }) => theme.bg2};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg0};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg2};
|
||||
box-shadow: 0px 6px 10px rgb(0 0 0 / 2%);
|
||||
`};
|
||||
`
|
||||
@@ -122,7 +122,7 @@ const AccountElement = styled.div<{ active: boolean }>`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
background-color: ${({ theme, active }) => (!active ? theme.bg0 : theme.bg0)};
|
||||
background-color: ${({ theme, active }) => (!active ? theme.deprecated_bg0 : theme.deprecated_bg0)};
|
||||
border-radius: 16px;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
@@ -138,7 +138,7 @@ const UNIAmount = styled(AccountElement)`
|
||||
padding: 4px 8px;
|
||||
height: 36px;
|
||||
font-weight: 500;
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg3};
|
||||
background: radial-gradient(174.47% 188.91% at 1.84% 0%, #ff007a 0%, #2172e5 100%), #edeef2;
|
||||
`
|
||||
|
||||
@@ -196,7 +196,7 @@ const StyledNavLink = styled(NavLink).attrs({
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
padding: 8px 12px;
|
||||
@@ -207,13 +207,13 @@ const StyledNavLink = styled(NavLink).attrs({
|
||||
border-radius: 14px;
|
||||
font-weight: 600;
|
||||
justify-content: center;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
}
|
||||
|
||||
:hover,
|
||||
:focus {
|
||||
color: ${({ theme }) => darken(0.1, theme.text1)};
|
||||
color: ${({ theme }) => darken(0.1, theme.deprecated_text1)};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -226,7 +226,7 @@ const StyledExternalLink = styled(ExternalLink).attrs({
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
font-size: 1rem;
|
||||
width: fit-content;
|
||||
margin: 0 12px;
|
||||
@@ -235,22 +235,22 @@ const StyledExternalLink = styled(ExternalLink).attrs({
|
||||
&.${activeClassName} {
|
||||
border-radius: 14px;
|
||||
font-weight: 600;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
}
|
||||
|
||||
:hover,
|
||||
:focus {
|
||||
color: ${({ theme }) => darken(0.1, theme.text1)};
|
||||
color: ${({ theme }) => darken(0.1, theme.deprecated_text1)};
|
||||
text-decoration: none;
|
||||
}
|
||||
`
|
||||
|
||||
export default function Header() {
|
||||
const { account, chainId } = useActiveWeb3React()
|
||||
const { account, chainId } = useWeb3React()
|
||||
|
||||
const userEthBalance = useNativeCurrencyBalances(account ? [account] : [])?.[account ?? '']
|
||||
const [darkMode] = useDarkModeManager()
|
||||
const { white, black } = useTheme()
|
||||
const { deprecated_white, deprecated_black } = useTheme()
|
||||
|
||||
const toggleClaimModal = useToggleSelfClaimModal()
|
||||
|
||||
@@ -264,17 +264,15 @@ export default function Header() {
|
||||
|
||||
const {
|
||||
infoLink,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { symbol: nativeCurrencySymbol },
|
||||
},
|
||||
} = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET]
|
||||
nativeCurrency: { symbol: nativeCurrencySymbol },
|
||||
} = getChainInfoOrDefault(chainId)
|
||||
|
||||
return (
|
||||
<HeaderFrame showBackground={scrollY > 45}>
|
||||
<ClaimModal />
|
||||
<Title href=".">
|
||||
<UniIcon>
|
||||
<Logo fill={darkMode ? white : black} width="24px" height="100%" title="logo" />
|
||||
<Logo fill={darkMode ? deprecated_white : deprecated_black} width="24px" height="100%" title="logo" />
|
||||
<HolidayOrnament />
|
||||
</UniIcon>
|
||||
</Title>
|
||||
@@ -283,6 +281,7 @@ export default function Header() {
|
||||
<Trans>Swap</Trans>
|
||||
</StyledNavLink>
|
||||
<StyledNavLink
|
||||
data-cy="pool-nav-link"
|
||||
id={`pool-nav-link`}
|
||||
to={'/pool'}
|
||||
isActive={(match, { pathname }) =>
|
||||
@@ -329,7 +328,7 @@ export default function Header() {
|
||||
)}
|
||||
<AccountElement active={!!account}>
|
||||
{account && userEthBalance ? (
|
||||
<BalanceText style={{ flexShrink: 0, userSelect: 'none' }} pl="0.75rem" pr="0.5rem" fontWeight={500}>
|
||||
<BalanceText style={{ flexShrink: 0, userSelect: 'none' }} pl="0.75rem" pr=".4rem" fontWeight={500}>
|
||||
<Trans>
|
||||
{userEthBalance?.toSignificant(3)} {nativeCurrencySymbol}
|
||||
</Trans>
|
||||
|
||||
@@ -10,7 +10,7 @@ const TextWrapper = styled.span<{
|
||||
textColor?: string
|
||||
}>`
|
||||
margin-left: ${({ margin }) => margin && '4px'};
|
||||
color: ${({ theme, link, textColor }) => (link ? theme.blue1 : textColor ?? theme.text1)};
|
||||
color: ${({ theme, link, textColor }) => (link ? theme.deprecated_blue1 : textColor ?? theme.deprecated_text1)};
|
||||
font-size: ${({ fontSize }) => fontSize ?? 'inherit'};
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
|
||||
@@ -1,26 +1,42 @@
|
||||
import { AbstractConnector } from '@web3-react/abstract-connector'
|
||||
import { Connector } from 'widgets-web3-react/types'
|
||||
import { ConnectionType } from 'connection'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
|
||||
import FortmaticIcon from '../../assets/images/fortmaticIcon.png'
|
||||
import PortisIcon from '../../assets/images/portisIcon.png'
|
||||
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
|
||||
import { fortmatic, injected, portis, walletconnect, walletlink } from '../../connectors'
|
||||
import Identicon from '../Identicon'
|
||||
|
||||
export default function StatusIcon({ connector }: { connector: AbstractConnector | Connector }) {
|
||||
switch (connector) {
|
||||
case injected:
|
||||
return <Identicon />
|
||||
case walletconnect:
|
||||
return <img src={WalletConnectIcon} alt={'WalletConnect'} />
|
||||
case walletlink:
|
||||
return <img src={CoinbaseWalletIcon} alt={'Coinbase Wallet'} />
|
||||
case fortmatic:
|
||||
return <img src={FortmaticIcon} alt={'Fortmatic'} />
|
||||
case portis:
|
||||
return <img src={PortisIcon} alt={'Portis'} />
|
||||
default:
|
||||
return null
|
||||
const IconWrapper = styled.div<{ size?: number }>`
|
||||
${({ theme }) => theme.flexColumnNoWrap};
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 8px;
|
||||
& > img,
|
||||
span {
|
||||
height: ${({ size }) => (size ? size + 'px' : '32px')};
|
||||
width: ${({ size }) => (size ? size + 'px' : '32px')};
|
||||
}
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
align-items: flex-end;
|
||||
`};
|
||||
`
|
||||
|
||||
export default function StatusIcon({ connectionType }: { connectionType: ConnectionType }) {
|
||||
let image
|
||||
switch (connectionType) {
|
||||
case ConnectionType.INJECTED:
|
||||
image = <Identicon />
|
||||
break
|
||||
case ConnectionType.WALLET_CONNECT:
|
||||
image = <img src={WalletConnectIcon} alt="WalletConnect" />
|
||||
break
|
||||
case ConnectionType.COINBASE_WALLET:
|
||||
image = <img src={CoinbaseWalletIcon} alt="Coinbase Wallet" />
|
||||
break
|
||||
case ConnectionType.FORTMATIC:
|
||||
image = <img src={FortmaticIcon} alt="Fortmatic" />
|
||||
break
|
||||
}
|
||||
|
||||
return <IconWrapper size={16}>{image}</IconWrapper>
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import jazzicon from '@metamask/jazzicon'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import useENSAvatar from 'hooks/useENSAvatar'
|
||||
import { useLayoutEffect, useMemo, useRef, useState } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
@@ -8,7 +8,7 @@ const StyledIdenticon = styled.div`
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
border-radius: 1.125rem;
|
||||
background-color: ${({ theme }) => theme.bg4};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg4};
|
||||
font-size: initial;
|
||||
`
|
||||
|
||||
@@ -19,7 +19,7 @@ const StyledAvatar = styled.img`
|
||||
`
|
||||
|
||||
export default function Identicon() {
|
||||
const { account } = useActiveWeb3React()
|
||||
const { account } = useWeb3React()
|
||||
const { avatar } = useENSAvatar(account ?? undefined)
|
||||
const [fetchable, setFetchable] = useState(true)
|
||||
|
||||
|
||||
@@ -36,9 +36,9 @@ const SmallButton = styled(ButtonGray)`
|
||||
`
|
||||
|
||||
const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boolean }>`
|
||||
border-color: ${({ active, theme }) => active && theme.blue1};
|
||||
border-color: ${({ active, theme }) => active && theme.deprecated_blue1};
|
||||
padding: 12px;
|
||||
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.blue1)} 0.8s linear;
|
||||
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.deprecated_blue1)} 0.8s linear;
|
||||
`
|
||||
|
||||
const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
|
||||
@@ -58,13 +58,13 @@ const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
|
||||
`
|
||||
|
||||
const InputTitle = styled(ThemedText.Small)`
|
||||
color: ${({ theme }) => theme.text2};
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
const ButtonLabel = styled(ThemedText.White)<{ disabled: boolean }>`
|
||||
color: ${({ theme, disabled }) => (disabled ? theme.text2 : theme.text1)} !important;
|
||||
color: ${({ theme, disabled }) => (disabled ? theme.deprecated_text2 : theme.deprecated_text1)} !important;
|
||||
`
|
||||
|
||||
interface StepCounterProps {
|
||||
|
||||
@@ -6,8 +6,8 @@ import { ChartEntry } from './types'
|
||||
|
||||
const Path = styled.path<{ fill: string | undefined }>`
|
||||
opacity: 0.5;
|
||||
stroke: ${({ fill, theme }) => fill ?? theme.blue2};
|
||||
fill: ${({ fill, theme }) => fill ?? theme.blue2};
|
||||
stroke: ${({ fill, theme }) => fill ?? theme.deprecated_blue2};
|
||||
fill: ${({ fill, theme }) => fill ?? theme.deprecated_blue2};
|
||||
`
|
||||
|
||||
export const Area = ({
|
||||
|
||||
@@ -8,7 +8,7 @@ const StyledGroup = styled.g`
|
||||
}
|
||||
|
||||
text {
|
||||
color: ${({ theme }) => theme.text2};
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
transform: translateY(5px);
|
||||
}
|
||||
`
|
||||
|
||||
@@ -18,7 +18,7 @@ const HandleAccent = styled.path`
|
||||
pointer-events: none;
|
||||
|
||||
stroke-width: 1.5;
|
||||
stroke: ${({ theme }) => theme.white};
|
||||
stroke: ${({ theme }) => theme.deprecated_white};
|
||||
opacity: 0.6;
|
||||
`
|
||||
|
||||
@@ -28,13 +28,13 @@ const LabelGroup = styled.g<{ visible: boolean }>`
|
||||
`
|
||||
|
||||
const TooltipBackground = styled.rect`
|
||||
fill: ${({ theme }) => theme.bg2};
|
||||
fill: ${({ theme }) => theme.deprecated_bg2};
|
||||
`
|
||||
|
||||
const Tooltip = styled.text`
|
||||
text-anchor: middle;
|
||||
font-size: 13px;
|
||||
fill: ${({ theme }) => theme.text1};
|
||||
fill: ${({ theme }) => theme.deprecated_text1};
|
||||
`
|
||||
|
||||
// flips the handles draggers when close to the container edges
|
||||
|
||||
@@ -5,7 +5,7 @@ import styled from 'styled-components/macro'
|
||||
const StyledLine = styled.line`
|
||||
opacity: 0.5;
|
||||
stroke-width: 2;
|
||||
stroke: ${({ theme }) => theme.text1};
|
||||
stroke: ${({ theme }) => theme.deprecated_text1};
|
||||
fill: none;
|
||||
`
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ const Wrapper = styled.div<{ count: number }>`
|
||||
|
||||
const Button = styled(ButtonGray)`
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
color: ${({ theme }) => theme.text1};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg2};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
}
|
||||
|
||||
width: 32px;
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import { usePoolActiveLiquidity } from 'hooks/usePoolTickData'
|
||||
import JSBI from 'jsbi'
|
||||
import { TickProcessed, usePoolActiveLiquidity } from 'hooks/usePoolTickData'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
|
||||
import { ChartEntry } from './types'
|
||||
|
||||
export interface TickProcessed {
|
||||
liquidityActive: JSBI
|
||||
price0: string
|
||||
}
|
||||
|
||||
export function useDensityChartData({
|
||||
currencyA,
|
||||
currencyB,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Currency, Price, Token } from '@uniswap/sdk-core'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import { sendEvent } from 'components/analytics'
|
||||
import { AutoColumn, ColumnCenter } from 'components/Column'
|
||||
import Loader from 'components/Loader'
|
||||
import { format } from 'd3'
|
||||
@@ -9,7 +10,6 @@ import useTheme from 'hooks/useTheme'
|
||||
import { saturate } from 'polished'
|
||||
import React, { ReactNode, useCallback, useMemo } from 'react'
|
||||
import { BarChart2, CloudOff, Inbox } from 'react-feather'
|
||||
import ReactGA from 'react-ga'
|
||||
import { batch } from 'react-redux'
|
||||
import { Bound } from 'state/mint/v3/actions'
|
||||
import styled from 'styled-components/macro'
|
||||
@@ -103,7 +103,7 @@ export default function LiquidityChartRangeInput({
|
||||
})
|
||||
|
||||
const onBrushDomainChangeEnded = useCallback(
|
||||
(domain, mode) => {
|
||||
(domain: [number, number], mode: string | undefined) => {
|
||||
let leftRangeValue = Number(domain[0])
|
||||
const rightRangeValue = Number(domain[1])
|
||||
|
||||
@@ -158,11 +158,7 @@ export default function LiquidityChartRangeInput({
|
||||
)
|
||||
|
||||
if (isError) {
|
||||
ReactGA.exception({
|
||||
...error,
|
||||
category: 'Liquidity',
|
||||
fatal: false,
|
||||
})
|
||||
sendEvent('exception', { description: error.toString(), fatal: false })
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -170,19 +166,19 @@ export default function LiquidityChartRangeInput({
|
||||
{isUninitialized ? (
|
||||
<InfoBox
|
||||
message={<Trans>Your position will appear here.</Trans>}
|
||||
icon={<Inbox size={56} stroke={theme.text1} />}
|
||||
icon={<Inbox size={56} stroke={theme.deprecated_text1} />}
|
||||
/>
|
||||
) : isLoading ? (
|
||||
<InfoBox icon={<Loader size="40px" stroke={theme.text4} />} />
|
||||
<InfoBox icon={<Loader size="40px" stroke={theme.deprecated_text4} />} />
|
||||
) : isError ? (
|
||||
<InfoBox
|
||||
message={<Trans>Liquidity data not available.</Trans>}
|
||||
icon={<CloudOff size={56} stroke={theme.text4} />}
|
||||
icon={<CloudOff size={56} stroke={theme.deprecated_text4} />}
|
||||
/>
|
||||
) : !formattedData || formattedData === [] || !price ? (
|
||||
<InfoBox
|
||||
message={<Trans>There is no liquidity data.</Trans>}
|
||||
icon={<BarChart2 size={56} stroke={theme.text4} />}
|
||||
icon={<BarChart2 size={56} stroke={theme.deprecated_text4} />}
|
||||
/>
|
||||
) : (
|
||||
<ChartWrapper>
|
||||
@@ -192,12 +188,12 @@ export default function LiquidityChartRangeInput({
|
||||
margins={{ top: 10, right: 2, bottom: 20, left: 0 }}
|
||||
styles={{
|
||||
area: {
|
||||
selection: theme.blue1,
|
||||
selection: theme.deprecated_blue1,
|
||||
},
|
||||
brush: {
|
||||
handle: {
|
||||
west: saturate(0.1, tokenAColor) ?? theme.red1,
|
||||
east: saturate(0.1, tokenBColor) ?? theme.blue1,
|
||||
west: saturate(0.1, tokenAColor) ?? theme.deprecated_red1,
|
||||
east: saturate(0.1, tokenBColor) ?? theme.deprecated_blue1,
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
@@ -14,7 +14,7 @@ const StyledSVG = styled.svg<{ size: string; stroke?: string }>`
|
||||
height: ${({ size }) => size};
|
||||
width: ${({ size }) => size};
|
||||
path {
|
||||
stroke: ${({ stroke, theme }) => stroke ?? theme.primary1};
|
||||
stroke: ${({ stroke, theme }) => stroke ?? theme.deprecated_primary1};
|
||||
}
|
||||
`
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@ export const LoadingRows = styled.div`
|
||||
animation-fill-mode: both;
|
||||
background: linear-gradient(
|
||||
to left,
|
||||
${({ theme }) => theme.bg1} 25%,
|
||||
${({ theme }) => theme.bg2} 50%,
|
||||
${({ theme }) => theme.bg1} 75%
|
||||
${({ theme }) => theme.deprecated_bg1} 25%,
|
||||
${({ theme }) => theme.deprecated_bg2} 50%,
|
||||
${({ theme }) => theme.deprecated_bg1} 75%
|
||||
);
|
||||
background-size: 400%;
|
||||
border-radius: 12px;
|
||||
|
||||
@@ -35,5 +35,5 @@ export default function Logo({ srcs, alt, style, ...rest }: LogoProps) {
|
||||
)
|
||||
}
|
||||
|
||||
return <Slash {...rest} style={{ ...style, color: theme.bg4 }} />
|
||||
return <Slash {...rest} style={{ ...style, color: theme.deprecated_bg4 }} />
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { PrivacyPolicyModal } from 'components/PrivacyPolicy'
|
||||
import { L2_CHAIN_IDS } from 'constants/chains'
|
||||
import { LOCALE_LABEL, SUPPORTED_LOCALES, SupportedLocale } from 'constants/locales'
|
||||
import { useActiveLocale } from 'hooks/useActiveLocale'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useLocationLinkProps } from 'hooks/useLocationLinkProps'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { FunctionComponent, PropsWithChildren, useEffect, useRef, useState } from 'react'
|
||||
import {
|
||||
BookOpen,
|
||||
Check,
|
||||
@@ -26,7 +26,7 @@ import styled, { css } from 'styled-components/macro'
|
||||
|
||||
import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg'
|
||||
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
|
||||
import { useModalOpen, useToggleModal } from '../../state/application/hooks'
|
||||
import { useModalIsOpen, useToggleModal } from '../../state/application/hooks'
|
||||
import { ApplicationModal } from '../../state/application/reducer'
|
||||
import { ExternalLink } from '../../theme'
|
||||
import { ButtonPrimary } from '../Button'
|
||||
@@ -38,7 +38,7 @@ export enum FlyoutAlignment {
|
||||
|
||||
const StyledMenuIcon = styled(MenuIcon)`
|
||||
path {
|
||||
stroke: ${({ theme }) => theme.text1};
|
||||
stroke: ${({ theme }) => theme.deprecated_text1};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -50,8 +50,8 @@ const StyledMenuButton = styled.button`
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 40px;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
border: 1px solid ${({ theme }) => theme.bg0};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg0};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg0};
|
||||
padding: 0.15rem 0.5rem;
|
||||
border-radius: 16px;
|
||||
|
||||
@@ -59,7 +59,7 @@ const StyledMenuButton = styled.button`
|
||||
:focus {
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
border: 1px solid ${({ theme }) => theme.bg3};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg3};
|
||||
}
|
||||
|
||||
svg {
|
||||
@@ -68,7 +68,7 @@ const StyledMenuButton = styled.button`
|
||||
`
|
||||
|
||||
const UNIbutton = styled(ButtonPrimary)`
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg3};
|
||||
background: radial-gradient(174.47% 188.91% at 1.84% 0%, #ff007a 0%, #2172e5 100%), #edeef2;
|
||||
border: none;
|
||||
`
|
||||
@@ -86,10 +86,10 @@ const MenuFlyout = styled.span<{ flyoutAlignment?: FlyoutAlignment }>`
|
||||
min-width: 196px;
|
||||
max-height: 350px;
|
||||
overflow: auto;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
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: 1px solid ${({ theme }) => theme.bg0};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg0};
|
||||
border-radius: 12px;
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
@@ -121,9 +121,9 @@ const MenuItem = styled(ExternalLink)`
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.5rem;
|
||||
justify-content: space-between;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.text1};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -132,9 +132,9 @@ const MenuItem = styled(ExternalLink)`
|
||||
const InternalMenuItem = styled(Link)`
|
||||
flex: 1;
|
||||
padding: 0.5rem 0.5rem;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.text1};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -151,7 +151,7 @@ const InternalLinkMenuItem = styled(InternalMenuItem)`
|
||||
justify-content: space-between;
|
||||
text-decoration: none;
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.text1};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -170,9 +170,9 @@ const ToggleMenuItem = styled.button`
|
||||
justify-content: space-between;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.text1};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -207,10 +207,10 @@ function LanguageMenu({ close }: { close: () => void }) {
|
||||
}
|
||||
|
||||
export default function Menu() {
|
||||
const { account, chainId } = useActiveWeb3React()
|
||||
const { account, chainId } = useWeb3React()
|
||||
|
||||
const node = useRef<HTMLDivElement>()
|
||||
const open = useModalOpen(ApplicationModal.MENU)
|
||||
const open = useModalIsOpen(ApplicationModal.MENU)
|
||||
const toggleMenu = useToggleModal(ApplicationModal.MENU)
|
||||
useOnClickOutside(node, open ? toggleMenu : undefined)
|
||||
const togglePrivacyPolicy = useToggleModal(ApplicationModal.PRIVACY_POLICY)
|
||||
@@ -311,7 +311,7 @@ export default function Menu() {
|
||||
|
||||
interface NewMenuProps {
|
||||
flyoutAlignment?: FlyoutAlignment
|
||||
ToggleUI?: React.FunctionComponent
|
||||
ToggleUI?: FunctionComponent<PropsWithChildren<unknown>>
|
||||
menuItems: {
|
||||
content: any
|
||||
link: string
|
||||
@@ -334,7 +334,7 @@ const ExternalMenuItem = styled(MenuItem)`
|
||||
|
||||
export const NewMenu = ({ flyoutAlignment = FlyoutAlignment.RIGHT, ToggleUI, menuItems, ...rest }: NewMenuProps) => {
|
||||
const node = useRef<HTMLDivElement>()
|
||||
const open = useModalOpen(ApplicationModal.POOL_OVERVIEW_OPTIONS)
|
||||
const open = useModalIsOpen(ApplicationModal.POOL_OVERVIEW_OPTIONS)
|
||||
const toggle = useToggleModal(ApplicationModal.POOL_OVERVIEW_OPTIONS)
|
||||
useOnClickOutside(node, open ? toggle : undefined)
|
||||
const ToggleElement = ToggleUI || StyledMenuIcon
|
||||
|
||||
@@ -19,7 +19,7 @@ const StyledDialogOverlay = styled(AnimatedDialogOverlay)`
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
background-color: ${({ theme }) => theme.modalBG};
|
||||
background-color: ${({ theme }) => theme.deprecated_modalBG};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -35,8 +35,8 @@ const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...r
|
||||
|
||||
&[data-reach-dialog-content] {
|
||||
margin: 0 0 2rem 0;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
border: 1px solid ${({ theme }) => theme.bg1};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg0};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg1};
|
||||
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.shadow1)};
|
||||
padding: 0px;
|
||||
width: 50vw;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { useContext } from 'react'
|
||||
import { ArrowUpCircle } from 'react-feather'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
@@ -50,7 +50,7 @@ export function SubmittedView({
|
||||
hash: string | undefined
|
||||
}) {
|
||||
const theme = useContext(ThemeContext)
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const { chainId } = useWeb3React()
|
||||
|
||||
return (
|
||||
<ConfirmOrLoadingWrapper>
|
||||
@@ -59,7 +59,7 @@ export function SubmittedView({
|
||||
<CloseIcon onClick={onDismiss} />
|
||||
</RowBetween>
|
||||
<ConfirmedIcon>
|
||||
<ArrowUpCircle strokeWidth={0.5} size={90} color={theme.primary1} />
|
||||
<ArrowUpCircle strokeWidth={0.5} size={90} color={theme.deprecated_primary1} />
|
||||
</ConfirmedIcon>
|
||||
<AutoColumn gap="100px" justify={'center'}>
|
||||
{children}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import { darken } from 'polished'
|
||||
import { ReactNode } from 'react'
|
||||
import { ArrowLeft } from 'react-feather'
|
||||
import { Link as HistoryLink, NavLink, useLocation } from 'react-router-dom'
|
||||
import { Link as HistoryLink, useLocation } from 'react-router-dom'
|
||||
import { Box } from 'rebass'
|
||||
import { useAppDispatch } from 'state/hooks'
|
||||
import { resetMintState } from 'state/mint/actions'
|
||||
@@ -22,34 +21,6 @@ const Tabs = styled.div`
|
||||
justify-content: space-evenly;
|
||||
`
|
||||
|
||||
const activeClassName = 'ACTIVE'
|
||||
|
||||
const StyledNavLink = styled(NavLink).attrs({
|
||||
activeClassName,
|
||||
})`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 3rem;
|
||||
border-radius: 3rem;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: ${({ theme }) => theme.text3};
|
||||
font-size: 20px;
|
||||
|
||||
&.${activeClassName} {
|
||||
border-radius: 12px;
|
||||
font-weight: 500;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
}
|
||||
|
||||
:hover,
|
||||
:focus {
|
||||
color: ${({ theme }) => darken(0.1, theme.text1)};
|
||||
}
|
||||
`
|
||||
|
||||
const StyledHistoryLink = styled(HistoryLink)<{ flex: string | undefined }>`
|
||||
flex: ${({ flex }) => flex ?? 'none'};
|
||||
|
||||
@@ -65,22 +36,9 @@ const ActiveText = styled.div`
|
||||
`
|
||||
|
||||
const StyledArrowLeft = styled(ArrowLeft)`
|
||||
color: ${({ theme }) => theme.text1};
|
||||
color: ${({ theme }) => theme.deprecated_text1};
|
||||
`
|
||||
|
||||
export function SwapPoolTabs({ active }: { active: 'swap' | 'pool' }) {
|
||||
return (
|
||||
<Tabs style={{ marginBottom: '20px', display: 'none', padding: '1rem 1rem 0 1rem' }}>
|
||||
<StyledNavLink id={`swap-nav-link`} to={'/swap'} isActive={() => active === 'swap'}>
|
||||
<Trans>Swap</Trans>
|
||||
</StyledNavLink>
|
||||
<StyledNavLink id={`pool-nav-link`} to={'/pool'} isActive={() => active === 'pool'}>
|
||||
<Trans>Pool</Trans>
|
||||
</StyledNavLink>
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
|
||||
export function FindPoolTabs({ origin }: { origin: string }) {
|
||||
return (
|
||||
<Tabs>
|
||||
@@ -134,7 +92,7 @@ export function AddRemoveTabs({
|
||||
}}
|
||||
flex={children ? '1' : undefined}
|
||||
>
|
||||
<StyledArrowLeft stroke={theme.text2} />
|
||||
<StyledArrowLeft stroke={theme.deprecated_text2} />
|
||||
</StyledHistoryLink>
|
||||
<ThemedText.MediumHeader
|
||||
fontWeight={500}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CHAIN_INFO } from 'constants/chainInfo'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { getChainInfo } from 'constants/chainInfo'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { ArrowUpRight } from 'react-feather'
|
||||
import { useDarkModeManager } from 'state/user/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
@@ -42,6 +42,8 @@ const SHOULD_SHOW_ALERT = {
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: true,
|
||||
[SupportedChainId.POLYGON]: true,
|
||||
[SupportedChainId.POLYGON_MUMBAI]: true,
|
||||
[SupportedChainId.CELO]: true,
|
||||
[SupportedChainId.CELO_ALFAJORES]: true,
|
||||
}
|
||||
|
||||
type NetworkAlertChains = keyof typeof SHOULD_SHOW_ALERT
|
||||
@@ -54,6 +56,10 @@ const BG_COLORS_BY_DARK_MODE_AND_CHAIN_ID: {
|
||||
'radial-gradient(100% 93.36% at 0% 6.64%, rgba(160, 108, 247, 0.1) 0%, rgba(82, 32, 166, 0.1) 100%)',
|
||||
[SupportedChainId.POLYGON_MUMBAI]:
|
||||
'radial-gradient(100% 93.36% at 0% 6.64%, rgba(160, 108, 247, 0.1) 0%, rgba(82, 32, 166, 0.1) 100%)',
|
||||
[SupportedChainId.CELO]:
|
||||
'radial-gradient(182.71% 150.59% at 2.81% 7.69%, rgba(90, 190, 170, 0.15) 0%, rgba(80, 160, 40, 0.15) 100%)',
|
||||
[SupportedChainId.CELO_ALFAJORES]:
|
||||
'radial-gradient(182.71% 150.59% at 2.81% 7.69%, rgba(90, 190, 170, 0.15) 0%, rgba(80, 160, 40, 0.15) 100%)',
|
||||
[SupportedChainId.OPTIMISM]:
|
||||
'radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.01) 0%, rgba(255, 255, 255, 0.04) 100%),radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.01) 0%, rgba(235, 0, 255, 0.01) 96%)',
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]:
|
||||
@@ -68,6 +74,10 @@ const BG_COLORS_BY_DARK_MODE_AND_CHAIN_ID: {
|
||||
'radial-gradient(182.71% 205.59% at 2.81% 7.69%, rgba(130, 71, 229, 0.2) 0%, rgba(167, 202, 255, 0.2) 100%)',
|
||||
[SupportedChainId.POLYGON_MUMBAI]:
|
||||
'radial-gradient(182.71% 205.59% at 2.81% 7.69%, rgba(130, 71, 229, 0.2) 0%, rgba(167, 202, 255, 0.2) 100%)',
|
||||
[SupportedChainId.CELO]:
|
||||
'radial-gradient(182.71% 150.59% at 2.81% 7.69%, rgba(63, 208, 137, 0.15) 0%, rgba(49, 205, 50, 0.15) 100%)',
|
||||
[SupportedChainId.CELO_ALFAJORES]:
|
||||
'radial-gradient(182.71% 150.59% at 2.81% 7.69%, rgba(63, 208, 137, 0.15) 0%, rgba(49, 205, 50, 0.15) 100%)',
|
||||
[SupportedChainId.OPTIMISM]:
|
||||
'radial-gradient(92% 105% at 50% 7%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.03) 100%),radial-gradient(100% 97% at 0% 12%, rgba(235, 0, 255, 0.1) 0%, rgba(243, 19, 19, 0.1) 100%), hsla(0, 0%, 100%, 0.1)',
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]:
|
||||
@@ -129,6 +139,8 @@ const StyledArrowUpRight = styled(ArrowUpRight)`
|
||||
const TEXT_COLORS: { [chainId in NetworkAlertChains]: string } = {
|
||||
[SupportedChainId.POLYGON]: 'rgba(130, 71, 229)',
|
||||
[SupportedChainId.POLYGON_MUMBAI]: 'rgba(130, 71, 229)',
|
||||
[SupportedChainId.CELO]: 'rgba(53, 178, 97)',
|
||||
[SupportedChainId.CELO_ALFAJORES]: 'rgba(53, 178, 97)',
|
||||
[SupportedChainId.OPTIMISM]: '#ff3856',
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: '#ff3856',
|
||||
[SupportedChainId.ARBITRUM_ONE]: '#0490ed',
|
||||
@@ -140,14 +152,14 @@ function shouldShowAlert(chainId: number | undefined): chainId is NetworkAlertCh
|
||||
}
|
||||
|
||||
export function NetworkAlert() {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const { chainId } = useWeb3React()
|
||||
const [darkMode] = useDarkModeManager()
|
||||
|
||||
if (!shouldShowAlert(chainId)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { label, logoUrl, bridge } = CHAIN_INFO[chainId]
|
||||
const { label, logoUrl, bridge } = getChainInfo(chainId)
|
||||
const textColor = TEXT_COLORS[chainId]
|
||||
|
||||
return bridge ? (
|
||||
|
||||
@@ -4,14 +4,14 @@ import styled from 'styled-components/macro'
|
||||
import { escapeRegExp } from '../../utils'
|
||||
|
||||
const StyledInput = styled.input<{ error?: boolean; fontSize?: string; align?: string }>`
|
||||
color: ${({ error, theme }) => (error ? theme.red1 : theme.text1)};
|
||||
color: ${({ error, theme }) => (error ? theme.deprecated_red1 : theme.deprecated_text1)};
|
||||
width: 0;
|
||||
position: relative;
|
||||
font-weight: 500;
|
||||
outline: none;
|
||||
border: none;
|
||||
flex: 1 1 auto;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
background-color: ${({ theme }) => theme.deprecated_bg1};
|
||||
font-size: ${({ fontSize }) => fontSize ?? '28px'};
|
||||
text-align: ${({ align }) => align && align};
|
||||
white-space: nowrap;
|
||||
@@ -35,7 +35,7 @@ const StyledInput = styled.input<{ error?: boolean; fontSize?: string; align?: s
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: ${({ theme }) => theme.text4};
|
||||
color: ${({ theme }) => theme.deprecated_text4};
|
||||
}
|
||||
`
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ const PopoverContainer = styled.div<{ show: boolean }>`
|
||||
visibility: ${(props) => (props.show ? 'visible' : 'hidden')};
|
||||
opacity: ${(props) => (props.show ? 1 : 0)};
|
||||
transition: visibility 150ms linear, opacity 150ms linear;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
color: ${({ theme }) => theme.deprecated_text2};
|
||||
`
|
||||
|
||||
const ReferenceElement = styled.div`
|
||||
@@ -26,16 +26,17 @@ const Arrow = styled.div`
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
box-sizing: border-box;
|
||||
z-index: 9998;
|
||||
|
||||
content: '';
|
||||
border: 1px solid ${({ theme }) => theme.bg2};
|
||||
border: 1px solid ${({ theme }) => theme.deprecated_bg2};
|
||||
transform: rotate(45deg);
|
||||
background: ${({ theme }) => theme.bg0};
|
||||
background: ${({ theme }) => theme.deprecated_bg0};
|
||||
}
|
||||
|
||||
&.arrow-top {
|
||||
bottom: -5px;
|
||||
bottom: -4px;
|
||||
::before {
|
||||
border-top: none;
|
||||
border-left: none;
|
||||
@@ -43,7 +44,7 @@ const Arrow = styled.div`
|
||||
}
|
||||
|
||||
&.arrow-bottom {
|
||||
top: -5px;
|
||||
top: -4px;
|
||||
::before {
|
||||
border-bottom: none;
|
||||
border-right: none;
|
||||
@@ -51,7 +52,7 @@ const Arrow = styled.div`
|
||||
}
|
||||
|
||||
&.arrow-left {
|
||||
right: -5px;
|
||||
right: -4px;
|
||||
|
||||
::before {
|
||||
border-bottom: none;
|
||||
@@ -60,7 +61,7 @@ const Arrow = styled.div`
|
||||
}
|
||||
|
||||
&.arrow-right {
|
||||
left: -5px;
|
||||
left: -4px;
|
||||
::before {
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { sendEvent } from 'components/analytics'
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import { Heart, X } from 'react-feather'
|
||||
import ReactGA from 'react-ga'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
|
||||
import tokenLogo from '../../assets/images/token-logo.png'
|
||||
import {
|
||||
useModalOpen,
|
||||
useModalIsOpen,
|
||||
useShowClaimPopup,
|
||||
useToggleSelfClaimModal,
|
||||
useToggleShowClaimPopup,
|
||||
@@ -55,17 +55,17 @@ const UniToken = styled.img`
|
||||
`
|
||||
|
||||
export default function ClaimPopup() {
|
||||
const { account } = useActiveWeb3React()
|
||||
const { account } = useWeb3React()
|
||||
|
||||
// dont store these in persisted state yet
|
||||
const showClaimPopup: boolean = useShowClaimPopup()
|
||||
const toggleShowClaimPopup = useToggleShowClaimPopup()
|
||||
|
||||
// toggle for showing this modal
|
||||
const showClaimModal = useModalOpen(ApplicationModal.SELF_CLAIM)
|
||||
const showClaimModal = useModalIsOpen(ApplicationModal.SELF_CLAIM)
|
||||
const toggleSelfClaimModal = useToggleSelfClaimModal()
|
||||
const handleToggleSelfClaimModal = useCallback(() => {
|
||||
ReactGA.event({
|
||||
sendEvent({
|
||||
category: 'MerkleDrop',
|
||||
action: 'Toggle self claim modal',
|
||||
})
|
||||
@@ -79,7 +79,7 @@ export default function ClaimPopup() {
|
||||
// listen for available claim and show popup if needed
|
||||
useEffect(() => {
|
||||
if (userHasAvailableclaim) {
|
||||
ReactGA.event({
|
||||
sendEvent({
|
||||
category: 'MerkleDrop',
|
||||
action: 'Show claim popup',
|
||||
})
|
||||
|
||||