Compare commits
563 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a48b7ce702 | ||
|
|
fc08d1fc6a | ||
|
|
b416603ff4 | ||
|
|
c4dc26e1b2 | ||
|
|
adfe7225a8 | ||
|
|
13e347ee02 | ||
|
|
b527bfbeea | ||
|
|
a25df00ef6 | ||
|
|
2446a6e88b | ||
|
|
3b3bf14e8a | ||
|
|
017e79f7ae | ||
|
|
804fe8f5ee | ||
|
|
bf01b0d342 | ||
|
|
f46f73f35f | ||
|
|
cad3575247 | ||
|
|
17aa9fcdb0 | ||
|
|
087745d7c6 | ||
|
|
20e6ca6fd5 | ||
|
|
d51b7e779b | ||
|
|
cfd0412d78 | ||
|
|
50afef03cb | ||
|
|
43b6e7abf4 | ||
|
|
64b9df8710 | ||
|
|
d75271484a | ||
|
|
952cc98df3 | ||
|
|
9b7637e012 | ||
|
|
a7f599127b | ||
|
|
676890d89c | ||
|
|
beb1bf3bdc | ||
|
|
631c202c49 | ||
|
|
491c9b4fd3 | ||
|
|
dcaed9094e | ||
|
|
8996ba6e38 | ||
|
|
c8570e5427 | ||
|
|
2806f6513a | ||
|
|
422c703e71 | ||
|
|
506493e8ab | ||
|
|
8dfd143208 | ||
|
|
718003b6f2 | ||
|
|
80b3aa9e61 | ||
|
|
4078390a48 | ||
|
|
e07599ef0f | ||
|
|
311bdc19b3 | ||
|
|
a6342d40f1 | ||
|
|
3686506f2a | ||
|
|
634d010d92 | ||
|
|
979f29ad62 | ||
|
|
4d3ed5d6ba | ||
|
|
ea79fbc2e0 | ||
|
|
4d19122bd2 | ||
|
|
13d7d2c992 | ||
|
|
02cf33e115 | ||
|
|
1f62cdf7a2 | ||
|
|
701e8fe116 | ||
|
|
3207e6026e | ||
|
|
b0d93dbc0e | ||
|
|
975a5e3434 | ||
|
|
07f52f02ff | ||
|
|
5f112611c8 | ||
|
|
1d82a4c71e | ||
|
|
6004c4be3e | ||
|
|
a00432c1c3 | ||
|
|
a184afa41e | ||
|
|
49fd256e79 | ||
|
|
c006edf696 | ||
|
|
b16dd2a930 | ||
|
|
08a8f669e8 | ||
|
|
95d91c8528 | ||
|
|
03902fe5c1 | ||
|
|
631052e6a5 | ||
|
|
c63bb84f09 | ||
|
|
f0d10ba0df | ||
|
|
88600d3a78 | ||
|
|
a1060d5f75 | ||
|
|
83762b3c6a | ||
|
|
7784f647e1 | ||
|
|
65ba56c5f0 | ||
|
|
963514cd51 | ||
|
|
6d0c8037a8 | ||
|
|
d5ef7f9b24 | ||
|
|
f86db00464 | ||
|
|
8884020fab | ||
|
|
db145658db | ||
|
|
55bbaffe15 | ||
|
|
e1c3ad8f54 | ||
|
|
730af2eed4 | ||
|
|
c625d15feb | ||
|
|
f9de85251c | ||
|
|
a186833b7a | ||
|
|
aa06db7684 | ||
|
|
f5b601ee99 | ||
|
|
bf30013b6c | ||
|
|
2bc2a2c76e | ||
|
|
28bf95123e | ||
|
|
b1d88317b9 | ||
|
|
f332315de9 | ||
|
|
2fbb86ac44 | ||
|
|
8d567e4d4a | ||
|
|
77fbccd3f1 | ||
|
|
41330ab080 | ||
|
|
4ba9d98e67 | ||
|
|
52790ac92e | ||
|
|
380ca11dfd | ||
|
|
e4261cc01b | ||
|
|
99148dfd3f | ||
|
|
8bf9a9ce8d | ||
|
|
b6c0c1e607 | ||
|
|
0739613abe | ||
|
|
3401dfdebe | ||
|
|
ab9c27edad | ||
|
|
f2dfc57002 | ||
|
|
318252b6a9 | ||
|
|
440c50dd3a | ||
|
|
eacf4e6049 | ||
|
|
ffbd2d107a | ||
|
|
2db29f205b | ||
|
|
3ae9e2af2a | ||
|
|
5ff2cff841 | ||
|
|
0c0968864b | ||
|
|
9230473c36 | ||
|
|
b791afac59 | ||
|
|
2899c8af45 | ||
|
|
5f13f7824a | ||
|
|
6690bc513d | ||
|
|
aea3c1f6e6 | ||
|
|
e0c625671b | ||
|
|
aa3c867222 | ||
|
|
2182e676d4 | ||
|
|
f0fc11a3b5 | ||
|
|
53d93afd78 | ||
|
|
b612b15e1e | ||
|
|
9f5764aab3 | ||
|
|
f6dea47907 | ||
|
|
8e9981e186 | ||
|
|
1509c430fd | ||
|
|
93073321db | ||
|
|
618760ff1b | ||
|
|
2d20fc939d | ||
|
|
8c1e9b394b | ||
|
|
9b9f431e6f | ||
|
|
cfe590d810 | ||
|
|
2ee9b16c49 | ||
|
|
c52cd2c38e | ||
|
|
43cefbdc6a | ||
|
|
ae86fefe8b | ||
|
|
8901605c32 | ||
|
|
568829d3b5 | ||
|
|
b0f4b4c735 | ||
|
|
a73d3af4b2 | ||
|
|
2c7b431e60 | ||
|
|
0805b2d9b8 | ||
|
|
6c8fc5a7f6 | ||
|
|
702258574d | ||
|
|
17bc229a36 | ||
|
|
458a7d90a0 | ||
|
|
72877fb0b6 | ||
|
|
27f756107e | ||
|
|
abfd87c517 | ||
|
|
b3d772bdb5 | ||
|
|
d9c82ebf49 | ||
|
|
5298a5ce29 | ||
|
|
4d3073d2d9 | ||
|
|
c732f407c6 | ||
|
|
361e0ca8d7 | ||
|
|
cc0a757ddd | ||
|
|
31632d3c5b | ||
|
|
9cfbec4b8b | ||
|
|
b5ac5d882f | ||
|
|
130606cab4 | ||
|
|
fa56919a06 | ||
|
|
633f65676e | ||
|
|
9bccb7ae3a | ||
|
|
8a2e4123c6 | ||
|
|
e93ddbedfa | ||
|
|
1667b56a04 | ||
|
|
6e995d6c09 | ||
|
|
f096112716 | ||
|
|
50c7d36164 | ||
|
|
8a7f1d51ce | ||
|
|
8f59f501cc | ||
|
|
197947835b | ||
|
|
94081b29a3 | ||
|
|
69183ed8c2 | ||
|
|
05e741b6c9 | ||
|
|
dfe550c43b | ||
|
|
65dfb40e44 | ||
|
|
25f5ed9983 | ||
|
|
9b1ef415c6 | ||
|
|
70f481430a | ||
|
|
942211eb00 | ||
|
|
6589c41800 | ||
|
|
14bac770b6 | ||
|
|
c9dbe2f0ac | ||
|
|
775cf57c30 | ||
|
|
f4935e9264 | ||
|
|
6b3914b7b9 | ||
|
|
41efb6f617 | ||
|
|
52df5f14cf | ||
|
|
b48a7c529f | ||
|
|
2eca50be93 | ||
|
|
be2ce18bfa | ||
|
|
fe13b29e5e | ||
|
|
02aac08489 | ||
|
|
8f29980b94 | ||
|
|
bb542ef0fb | ||
|
|
c2fe17615f | ||
|
|
a4cab75d09 | ||
|
|
882457cfef | ||
|
|
9a1bb5dbfb | ||
|
|
767cc85b3e | ||
|
|
47d726e544 | ||
|
|
32118520cd | ||
|
|
ada8a3af92 | ||
|
|
63694b32c0 | ||
|
|
c9ab94d799 | ||
|
|
a990c4af70 | ||
|
|
b5cc33c1f5 | ||
|
|
31da6cdb9d | ||
|
|
4079d8a517 | ||
|
|
e93fbfd31b | ||
|
|
7817846368 | ||
|
|
fc6a69a9af | ||
|
|
93a774092f | ||
|
|
55563e9bb2 | ||
|
|
f44aae2f53 | ||
|
|
7d674c33e7 | ||
|
|
79e6337629 | ||
|
|
3c8e8604b8 | ||
|
|
b763659788 | ||
|
|
1ac2581419 | ||
|
|
9f556680ed | ||
|
|
3f242f1b44 | ||
|
|
126688c753 | ||
|
|
374cc361a6 | ||
|
|
0198d0baa1 | ||
|
|
da2d7ba648 | ||
|
|
37cf492dd5 | ||
|
|
9e93f809a0 | ||
|
|
d9bd392e6d | ||
|
|
c48adc519f | ||
|
|
9f8983a92e | ||
|
|
c9d3d81768 | ||
|
|
c6f73c35ee | ||
|
|
7a2777b814 | ||
|
|
014595cdfb | ||
|
|
1b27d8dab0 | ||
|
|
c4d12c86a8 | ||
|
|
8d2feeb89e | ||
|
|
576b399768 | ||
|
|
eb8ce85872 | ||
|
|
1644e4f9c6 | ||
|
|
574fab54d1 | ||
|
|
8e581c19ff | ||
|
|
8c2fa89e9e | ||
|
|
0ae7452eca | ||
|
|
4589beb789 | ||
|
|
55aee86971 | ||
|
|
a457af91b6 | ||
|
|
e61b5da3c5 | ||
|
|
b4f44744c1 | ||
|
|
27f4e2e750 | ||
|
|
01159c53d5 | ||
|
|
8d8d63b873 | ||
|
|
da9db4b5f1 | ||
|
|
1724bee0f5 | ||
|
|
48e526554b | ||
|
|
53add345de | ||
|
|
e28faf0da2 | ||
|
|
f79dc5fe82 | ||
|
|
a6f29cbff8 | ||
|
|
68f0d4b725 | ||
|
|
111e7dc929 | ||
|
|
39d61e4913 | ||
|
|
3e6d3ec1b5 | ||
|
|
3f65530bf4 | ||
|
|
dba0d20781 | ||
|
|
a0f3bd0dc6 | ||
|
|
bef85abfd4 | ||
|
|
9934dba805 | ||
|
|
72d94b2831 | ||
|
|
138697a30a | ||
|
|
0add8ae177 | ||
|
|
13f7859aaa | ||
|
|
3337b5de22 | ||
|
|
55ea6616a4 | ||
|
|
5dd1249ddd | ||
|
|
4ab2bd3474 | ||
|
|
9eb20624cb | ||
|
|
25c5cf04e6 | ||
|
|
1f70e63cd0 | ||
|
|
df88b0ddd1 | ||
|
|
679759c3a0 | ||
|
|
9b6003bdc5 | ||
|
|
2b32824c26 | ||
|
|
a4e4cf8ab9 | ||
|
|
f08cea1820 | ||
|
|
e2d35c56fc | ||
|
|
5f20040107 | ||
|
|
9344abd1cf | ||
|
|
426e31230d | ||
|
|
06c48559c5 | ||
|
|
0a33ed1a50 | ||
|
|
d5c490b871 | ||
|
|
099c2adea3 | ||
|
|
d387538576 | ||
|
|
a3a7d01ce9 | ||
|
|
549e67bcea | ||
|
|
932992b57f | ||
|
|
76a24376d4 | ||
|
|
f873b2249f | ||
|
|
22452c03b0 | ||
|
|
14e045b999 | ||
|
|
948781f927 | ||
|
|
2f72d6b77e | ||
|
|
a78209690d | ||
|
|
98a2f51125 | ||
|
|
a1043573af | ||
|
|
63760faa9d | ||
|
|
0566ec28be | ||
|
|
d4c1baba57 | ||
|
|
7e41b27cd3 | ||
|
|
1d18a5159f | ||
|
|
bb87d2dd03 | ||
|
|
0b27528a6a | ||
|
|
5788a273ba | ||
|
|
4d322b17da | ||
|
|
cb5b80b558 | ||
|
|
6964b519bc | ||
|
|
ee4ea2a1f0 | ||
|
|
3763ad15e0 | ||
|
|
557042a88f | ||
|
|
860413dd52 | ||
|
|
18d4cdcce4 | ||
|
|
55767de484 | ||
|
|
60d5cef69b | ||
|
|
ad2833a3ed | ||
|
|
e4a105714a | ||
|
|
3e842d3259 | ||
|
|
e9d18acd41 | ||
|
|
abeba3d6b6 | ||
|
|
84c4219c02 | ||
|
|
50b81c8334 | ||
|
|
8986b63335 | ||
|
|
631b4c08b6 | ||
|
|
b92f0b8d90 | ||
|
|
ce926e91b7 | ||
|
|
4857281a1e | ||
|
|
d2a872e7d2 | ||
|
|
73042f20fd | ||
|
|
29b5e583c5 | ||
|
|
d67f3158bd | ||
|
|
817732d62e | ||
|
|
562a54a175 | ||
|
|
cd1e0cf378 | ||
|
|
0c81b08d66 | ||
|
|
2662f8afbc | ||
|
|
355012eec9 | ||
|
|
68518d7ce0 | ||
|
|
2c9c99b868 | ||
|
|
7b78dfa2c4 | ||
|
|
e6cbab3c72 | ||
|
|
144f86bdfd | ||
|
|
6703743aef | ||
|
|
0320083909 | ||
|
|
b96f7d27ca | ||
|
|
45e85bc857 | ||
|
|
24e1a01b1a | ||
|
|
0e7a94313c | ||
|
|
35e80fa3ed | ||
|
|
4a70d8eb28 | ||
|
|
706d8724c1 | ||
|
|
6e363c1ff1 | ||
|
|
27007708ec | ||
|
|
c4739ea963 | ||
|
|
50954eff0f | ||
|
|
af112765e4 | ||
|
|
52b71004d0 | ||
|
|
2ba7c857cd | ||
|
|
67f3b40671 | ||
|
|
c2acf5c1a8 | ||
|
|
51cf8aa0da | ||
|
|
bc0e578610 | ||
|
|
1b13cac0fb | ||
|
|
14ed4dbb2e | ||
|
|
dc140901b3 | ||
|
|
07a92ba63b | ||
|
|
3f2f46df43 | ||
|
|
2dc5c0d386 | ||
|
|
38de86f4c9 | ||
|
|
b2196e7e48 | ||
|
|
30f66f7062 | ||
|
|
b87430277a | ||
|
|
b4c59cfc6e | ||
|
|
2326dfa108 | ||
|
|
97bf36efbf | ||
|
|
264acaae48 | ||
|
|
63e9f6e4d2 | ||
|
|
02d80e07dc | ||
|
|
ad53da5efe | ||
|
|
29223ce34f | ||
|
|
b88919ed69 | ||
|
|
ab80910553 | ||
|
|
1cdb8aa6a4 | ||
|
|
88976d0294 | ||
|
|
b908c81e2b | ||
|
|
5c20d0adb7 | ||
|
|
ca6295fcb7 | ||
|
|
e8af59198d | ||
|
|
cd4c43b482 | ||
|
|
c1e07f3713 | ||
|
|
bccda20473 | ||
|
|
bed8dd5110 | ||
|
|
6bcbabec23 | ||
|
|
71a490f2aa | ||
|
|
abcf1f5a5c | ||
|
|
65e5e9c98e | ||
|
|
fdabb90f0e | ||
|
|
049054f5bb | ||
|
|
91d0a002c9 | ||
|
|
305d2e1df9 | ||
|
|
6bb7d9d9b2 | ||
|
|
55d5762e39 | ||
|
|
979686100e | ||
|
|
a7a1607faa | ||
|
|
5125cbb1a3 | ||
|
|
e89387bb26 | ||
|
|
f7896db5aa | ||
|
|
0a0053c6f1 | ||
|
|
9e10abbf97 | ||
|
|
c38771a6aa | ||
|
|
8e364680b6 | ||
|
|
04c25685f7 | ||
|
|
be1e184d88 | ||
|
|
b485b14395 | ||
|
|
44ef0138de | ||
|
|
a1142cc7af | ||
|
|
089280ddf0 | ||
|
|
f3c2a6eaea | ||
|
|
54dc5b1f19 | ||
|
|
0af7651c0b | ||
|
|
61d9dfe127 | ||
|
|
d990dfdec7 | ||
|
|
4336e08233 | ||
|
|
3840daf365 | ||
|
|
1104bdcefc | ||
|
|
499ef27722 | ||
|
|
1ea0769ba3 | ||
|
|
637d43621b | ||
|
|
9831cda290 | ||
|
|
62d4bb8b51 | ||
|
|
58a1be8421 | ||
|
|
40b939df58 | ||
|
|
3ccecc5ca1 | ||
|
|
9df5b536c9 | ||
|
|
4b72d757c5 | ||
|
|
63affd9828 | ||
|
|
135f9dc219 | ||
|
|
08b74b2524 | ||
|
|
ffcea6388e | ||
|
|
408a07ebd4 | ||
|
|
7f837810e0 | ||
|
|
57a7a4995a | ||
|
|
cc66783278 | ||
|
|
f97dcaef1d | ||
|
|
cfea3723a2 | ||
|
|
7d0fb9cc10 | ||
|
|
8633dc7a50 | ||
|
|
f340537544 | ||
|
|
3e9289dc35 | ||
|
|
4eb5c3cac8 | ||
|
|
cfadd3b165 | ||
|
|
d6db79b8f5 | ||
|
|
6007527ce0 | ||
|
|
06c8db917f | ||
|
|
3394bc738f | ||
|
|
32b56ba0fd | ||
|
|
f97ce35cee | ||
|
|
0dafd3e7ba | ||
|
|
d719942931 | ||
|
|
62b9b5d527 | ||
|
|
ab34645073 | ||
|
|
dab3671cfe | ||
|
|
478cb3afa3 | ||
|
|
d56015d750 | ||
|
|
a639e65cee | ||
|
|
243e14b74e | ||
|
|
70174140bc | ||
|
|
4fede9df36 | ||
|
|
fbd5dab53e | ||
|
|
ecdce21b16 | ||
|
|
1a38309500 | ||
|
|
46f539c604 | ||
|
|
ad65b63868 | ||
|
|
f9a5dbf6ce | ||
|
|
7991b0b0d8 | ||
|
|
d85dccd01a | ||
|
|
37799caa03 | ||
|
|
04878e095c | ||
|
|
14a6953b90 | ||
|
|
d866d79578 | ||
|
|
7f1def300d | ||
|
|
8930627064 | ||
|
|
efb939297f | ||
|
|
69cc728d43 | ||
|
|
a6bc7775bc | ||
|
|
3452be9a45 | ||
|
|
be6e2fe74a | ||
|
|
83b1297ff6 | ||
|
|
a20ed15b00 | ||
|
|
1413ab91bf | ||
|
|
c4846c8832 | ||
|
|
dbbffd17a6 | ||
|
|
85d8b1e514 | ||
|
|
739986ba66 | ||
|
|
e9148b30c9 | ||
|
|
6e46f25bd4 | ||
|
|
02269e9376 | ||
|
|
9822e68d5a | ||
|
|
9d1556b777 | ||
|
|
858b6afb2f | ||
|
|
da14d8fd5b | ||
|
|
3aaa3fef89 | ||
|
|
bc243e1c07 | ||
|
|
679a58daf4 | ||
|
|
eff512deb8 | ||
|
|
562b402293 | ||
|
|
99ad4ae44c | ||
|
|
6e52a43584 | ||
|
|
d4c5d3e8c8 | ||
|
|
a6e46623ad | ||
|
|
9353c7838c | ||
|
|
816249b4b1 | ||
|
|
70292a5512 | ||
|
|
ecdbb4a49f | ||
|
|
536a5d99de | ||
|
|
ffc2015595 | ||
|
|
5e30a4b4ad | ||
|
|
b14da2844d | ||
|
|
d3c04b7246 | ||
|
|
307a995a50 | ||
|
|
46911593e5 | ||
|
|
1ac36c7b6b | ||
|
|
8e055df447 | ||
|
|
02ecd727eb | ||
|
|
adc8429bdc | ||
|
|
458d34d96c | ||
|
|
6a4fa0c9bf | ||
|
|
7fc9a655fc | ||
|
|
8d3babd015 | ||
|
|
75ed4c5dae | ||
|
|
58dfadb13c | ||
|
|
3d5f5f783c | ||
|
|
ecb7132843 | ||
|
|
980eb8a518 | ||
|
|
a11e89c0e1 | ||
|
|
3efe5268ae | ||
|
|
3f43a8fbaf | ||
|
|
cccf149568 | ||
|
|
604ea49567 | ||
|
|
4c0c6dfde6 | ||
|
|
a160bb8f02 | ||
|
|
e86946b104 | ||
|
|
03108b981e |
2
.env
@@ -1,4 +1,2 @@
|
||||
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
|
||||
REACT_APP_WALLETCONNECT_BRIDGE_URL="https://uniswap.bridge.walletconnect.org"
|
||||
# Because we use storybook which has its own babel-loader dependency @ 8.2.2, where react-scripts uses 8.1.0
|
||||
SKIP_PREFLIGHT_CHECK=true
|
||||
@@ -27,6 +27,19 @@
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/ban-ts-ignore": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off"
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"paths": [
|
||||
{
|
||||
"name": "styled-components",
|
||||
"message": "Please import from styled-components/macro."
|
||||
}
|
||||
],
|
||||
"patterns": ["!styled-components/macro"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
17
.github/workflows/check-pr-title.yaml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: "Check PR Title"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- synchronize
|
||||
|
||||
jobs:
|
||||
check-pr-title:
|
||||
name: Check PR Title
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: amannn/action-semantic-pull-request@v3.4.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
57
.github/workflows/crowdin-sync.yaml
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
name: Crowdin Download
|
||||
|
||||
# hourly we sync translations from Crowdin
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 * * * *' # every hour we download translations and update the pr from crowdin
|
||||
|
||||
# manual trigger
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
download-translations:
|
||||
name: Download translations
|
||||
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: Extract translations
|
||||
run: "yarn i18n:extract"
|
||||
|
||||
- name: Synchronize
|
||||
uses: crowdin/github-action@1.1.0
|
||||
with:
|
||||
upload_sources: false
|
||||
download_translations: true
|
||||
project_id: 458284
|
||||
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]"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
52
.github/workflows/crowdin.yaml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
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
|
||||
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: Extract translations
|
||||
run: "yarn i18n:extract"
|
||||
|
||||
- name: Synchronize
|
||||
uses: crowdin/github-action@1.1.0
|
||||
with:
|
||||
upload_sources: true
|
||||
download_translations: false
|
||||
project_id: 458284
|
||||
token: ${{ secrets.CROWDIN_PERSONAL_TOKEN_SECRET }}
|
||||
source: 'src/locales/en-US.po'
|
||||
translation: 'src/locales/%locale%.po'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
13
.github/workflows/integration-tests.yaml
vendored
@@ -22,6 +22,18 @@ jobs:
|
||||
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
|
||||
|
||||
@@ -34,5 +46,6 @@ jobs:
|
||||
- run: yarn integration-test
|
||||
env:
|
||||
CYPRESS_INTEGRATION_TEST_PRIVATE_KEY: ${{ secrets.CYPRESS_INTEGRATION_TEST_PRIVATE_KEY }}
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
|
||||
|
||||
|
||||
12
.github/workflows/lint.yml
vendored
@@ -23,6 +23,18 @@ jobs:
|
||||
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
|
||||
|
||||
|
||||
12
.github/workflows/unit-tests.yaml
vendored
@@ -21,6 +21,18 @@ jobs:
|
||||
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
|
||||
|
||||
|
||||
7
.gitignore
vendored
@@ -3,6 +3,11 @@
|
||||
# generated contract types
|
||||
/src/types/v3
|
||||
/src/abis/types
|
||||
/src/locales/**/*.js
|
||||
/src/locales/**/*.ts
|
||||
/src/locales/**/*.json
|
||||
/src/locales/**/en-US.po
|
||||
/src/state/data/generated.ts
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
@@ -35,4 +40,4 @@ package-lock.json
|
||||
|
||||
cypress/videos
|
||||
cypress/screenshots
|
||||
cypress/fixtures/example.json
|
||||
cypress/fixtures/example.json
|
||||
|
||||
1
.prettierignore
Normal file
@@ -0,0 +1 @@
|
||||
/src/state/data/generated.ts
|
||||
@@ -1,16 +0,0 @@
|
||||
const { dirname, join, parse, resolve } = require('path')
|
||||
const { existsSync } = require('fs')
|
||||
|
||||
module.exports = {
|
||||
stories: ['../src/**/*.stories.@(ts|tsx)'],
|
||||
addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/preset-create-react-app'],
|
||||
typescript: {
|
||||
check: true,
|
||||
checkOptions: {},
|
||||
reactDocgen: 'react-docgen-typescript',
|
||||
reactDocgenTypescriptOptions: {
|
||||
shouldExtractLiteralValuesFromEnum: true,
|
||||
propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
import { addons } from '@storybook/addons'
|
||||
import { light } from './theme'
|
||||
|
||||
addons.setConfig({ theme: light })
|
||||
@@ -1,91 +0,0 @@
|
||||
import 'inter-ui'
|
||||
import { Story } from '@storybook/react/types-6-0'
|
||||
import { createWeb3ReactRoot, Web3ReactProvider } from '@web3-react/core'
|
||||
import React from 'react'
|
||||
import { Provider as StoreProvider } from 'react-redux'
|
||||
import { ThemeProvider as SCThemeProvider } from 'styled-components'
|
||||
import { NetworkContextName } from '../src/constants'
|
||||
import store from '../src/state'
|
||||
import { FixedGlobalStyle, theme, ThemedGlobalStyle } from '../src/theme'
|
||||
import getLibrary from '../src/utils/getLibrary'
|
||||
import * as storybookThemes from './theme'
|
||||
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: '^on[A-Z].*' },
|
||||
dependencies: {
|
||||
withStoriesOnly: true,
|
||||
hideEmpty: true,
|
||||
},
|
||||
docs: {
|
||||
theme: storybookThemes.light,
|
||||
},
|
||||
viewport: {
|
||||
viewports: {
|
||||
mobile: {
|
||||
name: 'iPhone X',
|
||||
styles: {
|
||||
width: '375px',
|
||||
height: '812px',
|
||||
},
|
||||
},
|
||||
tablet: {
|
||||
name: 'iPad',
|
||||
styles: {
|
||||
width: '768px',
|
||||
height: '1024px',
|
||||
},
|
||||
},
|
||||
laptop: {
|
||||
name: 'Laptop',
|
||||
styles: {
|
||||
width: '1024px',
|
||||
height: '768px',
|
||||
},
|
||||
},
|
||||
desktop: {
|
||||
name: 'Desktop',
|
||||
styles: {
|
||||
width: '1440px',
|
||||
height: '1024px',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const globalTypes = {
|
||||
theme: {
|
||||
name: 'Theme',
|
||||
description: 'Global theme for components',
|
||||
defaultValue: 'light',
|
||||
toolbar: {
|
||||
icon: 'circlehollow',
|
||||
items: ['light', 'dark'],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const Web3ProviderNetwork = createWeb3ReactRoot(NetworkContextName)
|
||||
|
||||
const withProviders = (Component: Story, context: Record<string, any>) => {
|
||||
const THEME = theme(context.globals.theme === 'dark')
|
||||
return (
|
||||
<>
|
||||
<Web3ReactProvider getLibrary={getLibrary}>
|
||||
<Web3ProviderNetwork getLibrary={getLibrary}>
|
||||
<StoreProvider store={store}>
|
||||
<SCThemeProvider theme={THEME}>
|
||||
<FixedGlobalStyle />
|
||||
<ThemedGlobalStyle />
|
||||
<main>
|
||||
<Component />
|
||||
</main>
|
||||
</SCThemeProvider>
|
||||
</StoreProvider>
|
||||
</Web3ProviderNetwork>
|
||||
</Web3ReactProvider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const decorators = [withProviders]
|
||||
@@ -1,17 +0,0 @@
|
||||
import { create } from '@storybook/theming'
|
||||
|
||||
// this themes the storybook UI
|
||||
const uniswapBaseTheme = {
|
||||
brandTitle: 'Uniswap Design',
|
||||
brandUrl: 'https://uniswap.org',
|
||||
brandImage: 'https://ipfs.io/ipfs/QmNa8mQkrNKp1WEEeGjFezDmDeodkWRevGFN8JCV7b4Xir',
|
||||
}
|
||||
export const light = create({
|
||||
base: 'light',
|
||||
...uniswapBaseTheme,
|
||||
})
|
||||
|
||||
// export const dark = create({
|
||||
// base: 'dark',
|
||||
// ...uniswapBaseTheme,
|
||||
// })
|
||||
76
CONTRIBUTING.md
Normal file
@@ -0,0 +1,76 @@
|
||||
|
||||
# Contributing
|
||||
|
||||
Thank you for your interest in contributing to the Uniswap interface! 🦄
|
||||
|
||||
# Development
|
||||
|
||||
## Running the interface locally
|
||||
|
||||
1. `yarn install`
|
||||
1. `yarn start`
|
||||
|
||||
## Creating a production build
|
||||
|
||||
1. `yarn install`
|
||||
1. `yarn build`
|
||||
|
||||
## 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
|
||||
makes large architectural changes, consider following all the standards.
|
||||
|
||||
- Have at least one engineer approve of large code refactorings
|
||||
- At least manually test small code changes, prefer automated tests
|
||||
- Thoroughly unit test when code is not obviously correct
|
||||
- If something breaks, add automated tests so it doesn't break again
|
||||
- Add integration tests for new pages or flows
|
||||
- Verify that all CI checks pass before merging
|
||||
- Have at least one product manager or designer approve of significant UX changes
|
||||
|
||||
## Guidelines
|
||||
|
||||
The following points should help guide your development:
|
||||
|
||||
- Security: the interface is safe to use
|
||||
- Avoid adding unnecessary dependencies due to [supply chain risk](https://github.com/LavaMoat/lavamoat#further-reading-on-software-supplychain-security)
|
||||
- Reproducibility: anyone can build the interface
|
||||
- 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
|
||||
- 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 run well on low performance devices (majority of swaps on mobile!)
|
||||
|
||||
## Release process
|
||||
|
||||
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.
|
||||
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
|
||||
the appropriate UX reviewers (PMs or designers).
|
||||
|
||||
## Finding a first issue
|
||||
|
||||
Start with issues with the label
|
||||
[`good first issue`](https://github.com/Uniswap/uniswap-interface/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
|
||||
|
||||
# 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).
|
||||
We sync to the repository on a schedule, rather than download translations at build time, so that builds are always reproducible.
|
||||
|
||||
You can contribute by joining Crowdin to proofread existing translations [here](https://crowdin.com/project/uniswap-interface/invite?d=93i5n413q403t4g473p443o4c3t2g3s21343u2c3n403l4b3v2735353i4g4k4l4g453j4g4o4j4e4k4b323l4a3h463s4g453q443m4e3t2b303s2a35353l403o443v293e303k4g4n4r4g483i4g4r4j4e4o473i5n4a3t463t4o4)
|
||||
|
||||
Or, ask to join us as a translator in the Discord!
|
||||
40
README.md
@@ -4,17 +4,21 @@
|
||||
[](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://crowdin.com/project/uniswap-interface)
|
||||
|
||||
An open source interface for Uniswap -- a protocol for decentralized exchange of Ethereum tokens.
|
||||
|
||||
- Website: [uniswap.org](https://uniswap.org/)
|
||||
- Interface: [app.uniswap.org](https://app.uniswap.org)
|
||||
- Docs: [uniswap.org/docs/](https://uniswap.org/docs/)
|
||||
- Twitter: [@UniswapProtocol](https://twitter.com/UniswapProtocol)
|
||||
- Twitter: [@Uniswap](https://twitter.com/Uniswap)
|
||||
- Reddit: [/r/Uniswap](https://www.reddit.com/r/Uniswap/)
|
||||
- Email: [contact@uniswap.org](mailto:contact@uniswap.org)
|
||||
- Discord: [Uniswap](https://discord.gg/FCfyBSbCU5)
|
||||
- Whitepaper: [Link](https://hackmd.io/C-DvwDSfSxuh-Gd4WKE_ig)
|
||||
- Whitepapers:
|
||||
- [V1](https://hackmd.io/C-DvwDSfSxuh-Gd4WKE_ig)
|
||||
- [V2](https://uniswap.org/whitepaper.pdf)
|
||||
- [V3](https://uniswap.org/whitepaper-v3.pdf)
|
||||
|
||||
## Accessing the Uniswap Interface
|
||||
|
||||
@@ -22,27 +26,21 @@ To access the Uniswap Interface, use an IPFS gateway link from the
|
||||
[latest release](https://github.com/Uniswap/uniswap-interface/releases/latest),
|
||||
or visit [app.uniswap.org](https://app.uniswap.org).
|
||||
|
||||
## Development
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Run
|
||||
|
||||
```bash
|
||||
yarn start
|
||||
```
|
||||
|
||||
## Contributions
|
||||
|
||||
**Please open all pull requests against the `main` branch.**
|
||||
CI checks will run against all PRs.
|
||||
For steps on local deployment, development, and code contribution, please see [CONTRIBUTING](./CONTRIBUTING.md).
|
||||
|
||||
## Accessing Uniswap Interface V1
|
||||
## Accessing Uniswap V2
|
||||
|
||||
The Uniswap Interface supports swapping against, and migrating or removing liquidity from Uniswap V1. However,
|
||||
if you would like to use Uniswap V1, the Uniswap V1 interface for mainnet and testnets is accessible via IPFS gateways
|
||||
The Uniswap Interface supports swapping, adding liquidity, removing liquidity and migrating liquidity for
|
||||
Uniswap protocol V2.
|
||||
|
||||
- Swap on Uniswap V2: https://app.uniswap.org/#/swap?use=v2
|
||||
- View V2 liquidity: https://app.uniswap.org/#/pool/v2
|
||||
- Add V2 liquidity: https://app.uniswap.org/#/add/v2
|
||||
- Migrate V2 liquidity to V3: https://app.uniswap.org/#/migrate/v2
|
||||
|
||||
## Accessing Uniswap V1
|
||||
|
||||
The Uniswap V1 interface for mainnet and testnets is accessible via IPFS gateways
|
||||
linked from the [v1.0.0 release](https://github.com/Uniswap/uniswap-interface/releases/tag/v1.0.0).
|
||||
|
||||
11
codegen.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
overrideExisting: true
|
||||
schema: 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3'
|
||||
documents: 'src/**/!(*.d).{ts,tsx}'
|
||||
generates:
|
||||
./src/state/data/generated.ts:
|
||||
plugins:
|
||||
- typescript
|
||||
- typescript-operations
|
||||
- typescript-rtk-query:
|
||||
importBaseApiFrom: './slice'
|
||||
exportHooks: true
|
||||
16
custom-test-env.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"projectId": "yp82ef",
|
||||
"baseUrl": "http://localhost:3000",
|
||||
"pluginsFile": false,
|
||||
"fixturesFolder": false,
|
||||
"supportFile": "cypress/support/index.js",
|
||||
"video": false,
|
||||
"defaultCommandTimeout": 10000
|
||||
|
||||
25
cypress/fixtures/feeTierDistribution.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"_meta": {
|
||||
"block": {
|
||||
"number": 99999999
|
||||
}
|
||||
},
|
||||
"asToken0": [
|
||||
{
|
||||
"feeTier": "500",
|
||||
"totalValueLockedToken0": "0",
|
||||
"totalValueLockedToken1": "1"
|
||||
},
|
||||
{
|
||||
"feeTier": "3000",
|
||||
"totalValueLockedToken0": "0",
|
||||
"totalValueLockedToken1": "7"
|
||||
},
|
||||
{
|
||||
"feeTier": "10000",
|
||||
"totalValueLockedToken0": "0",
|
||||
"totalValueLockedToken1": "2"
|
||||
}
|
||||
],
|
||||
"asToken1": []
|
||||
}
|
||||
@@ -1,4 +1,13 @@
|
||||
import { CyHttpMessages } from 'cypress/types/net-stubbing'
|
||||
import { aliasQuery, hasQuery } from '../utils/graphql-test-utils'
|
||||
|
||||
describe('Add Liquidity', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3', (req) => {
|
||||
aliasQuery(req, 'feeTierDistribution')
|
||||
})
|
||||
})
|
||||
|
||||
it('loads the two correct tokens', () => {
|
||||
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab/500')
|
||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
|
||||
@@ -23,4 +32,32 @@ describe('Add Liquidity', () => {
|
||||
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
|
||||
})
|
||||
|
||||
it('loads fee tier distribution', () => {
|
||||
cy.fixture('feeTierDistribution.json').then((feeTierDistribution) => {
|
||||
cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3', (req: CyHttpMessages.IncomingHttpRequest) => {
|
||||
if (hasQuery(req, 'feeTierDistribution')) {
|
||||
req.alias = 'feeTierDistributionQuery'
|
||||
|
||||
req.reply({
|
||||
body: {
|
||||
data: {
|
||||
...feeTierDistribution,
|
||||
},
|
||||
},
|
||||
headers: {
|
||||
'access-control-allow-origin': '*',
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab')
|
||||
|
||||
cy.wait('@feeTierDistributionQuery')
|
||||
|
||||
cy.get('#add-liquidity-selected-fee .selected-fee-label').should('contain.text', '0.3% fee tier')
|
||||
cy.get('#add-liquidity-selected-fee .selected-fee-percentage').should('contain.text', '70%')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,6 +4,7 @@ describe('Landing Page', () => {
|
||||
beforeEach(() => cy.visit('/'))
|
||||
it('loads swap page', () => {
|
||||
cy.get('#swap-page')
|
||||
cy.screenshot()
|
||||
})
|
||||
|
||||
it('redirects to url /swap', () => {
|
||||
|
||||
@@ -8,7 +8,9 @@ import { JsonRpcProvider } from '@ethersproject/providers'
|
||||
import { Wallet } from '@ethersproject/wallet'
|
||||
import { Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
|
||||
|
||||
const TEST_PRIVATE_KEY = Cypress.env('INTEGRATION_TEST_PRIVATE_KEY')
|
||||
// todo: figure out how env vars actually work in CI
|
||||
// const TEST_PRIVATE_KEY = Cypress.env('INTEGRATION_TEST_PRIVATE_KEY')
|
||||
const TEST_PRIVATE_KEY = '0xe580410d7c37d26c6ad1a837bbae46bc27f9066a466fb3a66e770523b4666d19'
|
||||
|
||||
// address of the above key
|
||||
export const TEST_ADDRESS_NEVER_USE = new Wallet(TEST_PRIVATE_KEY).address
|
||||
|
||||
12
cypress/utils/graphql-test-utils.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// Utility to match GraphQL mutation based on the query name
|
||||
export const hasQuery = (req: any, queryName: string) => {
|
||||
const { body } = req
|
||||
return body.hasOwnProperty('query') && body.query.includes(queryName)
|
||||
}
|
||||
|
||||
// Alias query if queryName matches
|
||||
export const aliasQuery = (req: any, queryName: string) => {
|
||||
if (hasQuery(req, queryName)) {
|
||||
req.alias = `${queryName}Query`
|
||||
}
|
||||
}
|
||||
53
lingui.config.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
export default {
|
||||
catalogs: [
|
||||
{
|
||||
path: '<rootDir>/src/locales/{locale}',
|
||||
include: ['<rootDir>/src'],
|
||||
},
|
||||
],
|
||||
compileNamespace: 'cjs',
|
||||
fallbackLocales: {
|
||||
default: 'en-US',
|
||||
},
|
||||
format: 'po',
|
||||
formatOptions: {
|
||||
lineNumbers: false,
|
||||
},
|
||||
locales: [
|
||||
'af-ZA',
|
||||
'ar-SA',
|
||||
'ca-ES',
|
||||
'cs-CZ',
|
||||
'da-DK',
|
||||
'de-DE',
|
||||
'el-GR',
|
||||
'en-US',
|
||||
'es-ES',
|
||||
'fi-FI',
|
||||
'fr-FR',
|
||||
'he-IL',
|
||||
'hu-HU',
|
||||
'id-ID',
|
||||
'it-IT',
|
||||
'ja-JP',
|
||||
'ko-KR',
|
||||
'nl-NL',
|
||||
'no-NO',
|
||||
'pl-PL',
|
||||
'pt-BR',
|
||||
'pt-PT',
|
||||
'ro-RO',
|
||||
'ru-RU',
|
||||
'sr-SP',
|
||||
'sv-SE',
|
||||
'tr-TR',
|
||||
'uk-UA',
|
||||
'vi-VN',
|
||||
'zh-CN',
|
||||
'zh-TW',
|
||||
],
|
||||
orderBy: 'messageId',
|
||||
rootDir: '.',
|
||||
runtimeConfigModule: ['@lingui/core', 'i18n'],
|
||||
sourceLocale: 'en-US',
|
||||
}
|
||||
80
package.json
@@ -4,27 +4,33 @@
|
||||
"homepage": ".",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@emotion/core": "^11.0.0",
|
||||
"@ethersproject/experimental": "^5.0.1",
|
||||
"@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",
|
||||
"@lingui/loader": "^3.9.0",
|
||||
"@lingui/macro": "^3.9.0",
|
||||
"@lingui/react": "^3.9.0",
|
||||
"@metamask/jazzicon": "^2.0.0",
|
||||
"@popperjs/core": "^2.4.4",
|
||||
"@reach/dialog": "^0.10.3",
|
||||
"@reach/portal": "^0.10.3",
|
||||
"@react-hook/window-scroll": "^1.3.0",
|
||||
"@reduxjs/toolkit": "^1.3.5",
|
||||
"@storybook/addon-actions": "^6.1.17",
|
||||
"@storybook/addon-essentials": "^6.1.17",
|
||||
"@storybook/addon-links": "^6.1.17",
|
||||
"@storybook/addons": "^6.1.17",
|
||||
"@storybook/components": "^6.1.17",
|
||||
"@storybook/preset-create-react-app": "^3.1.5",
|
||||
"@storybook/preset-typescript": "^3.0.0",
|
||||
"@storybook/react": "^6.1.17",
|
||||
"@storybook/theming": "^6.1.17",
|
||||
"@styled-system/css": "^5.1.5",
|
||||
"@reduxjs/toolkit": "^1.6.1",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^12.0.0",
|
||||
"@typechain/ethers-v5": "^7.0.0",
|
||||
"@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/lodash.flatmap": "^4.5.6",
|
||||
"@types/luxon": "^1.24.4",
|
||||
"@types/ms.macro": "^2.0.0",
|
||||
"@types/multicodec": "^1.0.0",
|
||||
"@types/node": "^13.13.5",
|
||||
"@types/qs": "^6.9.2",
|
||||
@@ -45,38 +51,39 @@
|
||||
"@uniswap/governance": "^1.0.2",
|
||||
"@uniswap/liquidity-staker": "^1.0.2",
|
||||
"@uniswap/merkle-distributor": "1.0.1",
|
||||
"@uniswap/token-lists": "^1.0.0-beta.19",
|
||||
"@uniswap/token-lists": "^1.0.0-beta.25",
|
||||
"@uniswap/sdk-core": "^3.0.1",
|
||||
"@uniswap/v2-core": "1.0.0",
|
||||
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
||||
"@uniswap/v2-sdk": "^3.0.0-alpha.0",
|
||||
"@uniswap/v2-sdk": "^3.0.0-alpha.2",
|
||||
"@uniswap/v3-core": "1.0.0",
|
||||
"@uniswap/v3-periphery": "1.0.0",
|
||||
"@uniswap/v3-sdk": "^3.0.0-alpha.4",
|
||||
"@uniswap/v3-periphery": "^1.1.1",
|
||||
"@uniswap/v3-sdk": "^3.3.0",
|
||||
"@web3-react/core": "^6.0.9",
|
||||
"@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": "^6.2.0",
|
||||
"@web3-react/walletlink-connector": "^6.2.0",
|
||||
"@web3-react/walletlink-connector": "^6.2.3",
|
||||
"ajv": "^6.12.3",
|
||||
"cids": "^1.0.0",
|
||||
"copy-to-clipboard": "^3.2.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"cypress": "^4.11.0",
|
||||
"cypress": "^7.7.0",
|
||||
"d3": "^7.0.0",
|
||||
"eslint": "^7.11.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"eslint-plugin-react": "^7.19.0",
|
||||
"eslint-plugin-react-hooks": "^4.0.0",
|
||||
"ethers": "^5.0.7",
|
||||
"i18next": "^15.0.9",
|
||||
"i18next-browser-languagedetector": "^3.0.1",
|
||||
"i18next-xhr-backend": "^2.0.1",
|
||||
"ethers": "^5.4.0",
|
||||
"graphql": "^15.5.0",
|
||||
"graphql-request": "^3.4.0",
|
||||
"inter-ui": "^3.13.1",
|
||||
"jazzicon": "^1.5.0",
|
||||
"lightweight-charts": "^3.3.0",
|
||||
"jest-styled-components": "^7.0.5",
|
||||
"lodash.flatmap": "^4.5.0",
|
||||
"luxon": "^1.25.0",
|
||||
"ms.macro": "^2.0.0",
|
||||
"multicodec": "^3.0.1",
|
||||
"multihashes": "^4.0.2",
|
||||
"node-vibrant": "^3.1.5",
|
||||
@@ -89,7 +96,7 @@
|
||||
"react-dom": "^17.0.1",
|
||||
"react-feather": "^2.0.8",
|
||||
"react-ga": "^2.5.7",
|
||||
"react-i18next": "^10.7.0",
|
||||
"react-is": "^17.0.2",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-popper": "^2.2.3",
|
||||
"react-redux": "^7.2.2",
|
||||
@@ -100,10 +107,11 @@
|
||||
"react-virtualized-auto-sizer": "^1.0.2",
|
||||
"react-window": "^1.8.5",
|
||||
"rebass": "^4.0.7",
|
||||
"redux-devtools-extension": "^2.13.9",
|
||||
"redux-localstorage-simple": "^2.3.1",
|
||||
"serve": "^11.3.2",
|
||||
"start-server-and-test": "^1.11.0",
|
||||
"styled-components": "^4.2.0",
|
||||
"styled-components": "^5.3.0",
|
||||
"styled-system": "^5.1.5",
|
||||
"typechain": "^5.0.0",
|
||||
"typescript": "^4.2.3",
|
||||
@@ -116,16 +124,22 @@
|
||||
"workbox-routing": "^6.1.0",
|
||||
"workbox-strategies": "^6.1.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"@walletconnect/web3-provider": "1.5.2"
|
||||
},
|
||||
"scripts": {
|
||||
"compile-contract-types": "yarn compile-external-abi-types && yarn compile-v3-contract-types",
|
||||
"compile-external-abi-types": "npx typechain --target ethers-v5 --out-dir src/abis/types './src/abis/**/*.json'",
|
||||
"compile-v3-contract-types": "npx typechain --target ethers-v5 --out-dir src/types/v3 './node_modules/@uniswap/?(v3-core|v3-periphery)/artifacts/contracts/**/*.json'",
|
||||
"build": "yarn compile-contract-types && react-scripts build",
|
||||
"integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'",
|
||||
"compile-external-abi-types": "typechain --target ethers-v5 --out-dir src/abis/types './src/abis/**/*.json'",
|
||||
"compile-v3-contract-types": "typechain --target ethers-v5 --out-dir src/types/v3 './node_modules/@uniswap/?(v3-core|v3-periphery)/artifacts/contracts/**/*.json'",
|
||||
"build": "yarn compile-contract-types && yarn graphql:generate && yarn i18n:extract && yarn i18n:compile && react-scripts build",
|
||||
"i18n:extract": "lingui extract --locale en-US",
|
||||
"i18n:compile": "lingui compile",
|
||||
"integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run --record'",
|
||||
"graphql:generate": "graphql-codegen --config codegen.yml",
|
||||
"postinstall": "yarn compile-contract-types",
|
||||
"start": "yarn compile-contract-types && react-scripts start",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"test": "react-scripts test --env=jsdom"
|
||||
"test": "react-scripts test --env=./custom-test-env.js",
|
||||
"prestart": "yarn graphql:generate && touch src/locales/en-US.po"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app",
|
||||
|
||||
11
public/images/256x256_App_Icon_Pink.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="257" height="256" viewBox="0 0 257 256" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M96.4347 60.2768C94.3434 59.9524 94.2552 59.9143 95.2394 59.7631C97.1254 59.473 101.579 59.8683 104.648 60.5981C111.812 62.3011 118.331 66.6637 125.29 74.4117L127.139 76.4701L129.784 76.0449C140.926 74.2544 152.26 75.6774 161.741 80.057C164.349 81.2619 168.461 83.6603 168.975 84.2766C169.138 84.473 169.439 85.7374 169.642 87.0866C170.347 91.7544 169.994 95.3323 168.567 98.0046C167.79 99.4588 167.746 99.9197 168.268 101.164C168.686 102.157 169.848 102.892 170.999 102.891C173.355 102.888 175.891 99.0791 177.066 93.78L177.532 91.6751L178.457 92.7224C183.528 98.4681 187.512 106.304 188.196 111.881L188.374 113.336L187.522 112.014C186.054 109.739 184.58 108.19 182.693 106.941C179.29 104.69 175.693 103.923 166.164 103.421C157.559 102.968 152.689 102.233 147.859 100.658C139.643 97.9792 135.501 94.4114 125.74 81.6059C121.405 75.918 118.726 72.7711 116.06 70.2368C110.004 64.4784 104.053 61.4584 96.4347 60.2768Z" fill="#FF007A"/>
|
||||
<path d="M170.916 72.9763C171.132 69.1649 171.649 66.651 172.688 64.3552C173.099 63.4465 173.485 62.7027 173.544 62.7027C173.604 62.7027 173.425 63.3735 173.147 64.1931C172.391 66.4212 172.267 69.4687 172.788 73.0144C173.448 77.5132 173.824 78.1623 178.579 83.022C180.809 85.3014 183.403 88.1762 184.344 89.4105L186.054 91.6547L184.344 90.0508C182.253 88.0895 177.444 84.2644 176.381 83.7176C175.669 83.3509 175.563 83.3573 175.124 83.7946C174.719 84.1975 174.634 84.803 174.577 87.6654C174.49 92.1267 173.882 94.9901 172.414 97.8533C171.621 99.4019 171.495 99.0714 172.214 97.3235C172.75 96.0184 172.805 95.4446 172.801 91.1259C172.792 82.4485 171.762 80.3624 165.721 76.7887C164.19 75.8834 161.668 74.5778 160.117 73.8872C158.565 73.1965 157.333 72.595 157.378 72.5501C157.549 72.3798 163.441 74.0995 165.812 75.0117C169.339 76.3686 169.922 76.5444 170.35 76.3807C170.637 76.271 170.776 75.4347 170.916 72.9763Z" fill="#FF007A"/>
|
||||
<path d="M100.497 87.8209C96.2514 81.9758 93.6246 73.0138 94.1933 66.3144L94.3691 64.2413L95.3355 64.4176C97.1504 64.7486 100.28 65.9133 101.745 66.8033C105.766 69.2453 107.506 72.4605 109.277 80.7164C109.796 83.1346 110.477 85.8712 110.79 86.7976C111.294 88.2889 113.199 91.7721 114.748 94.0343C115.864 95.6636 115.123 96.4356 112.657 96.213C108.89 95.873 103.788 92.3519 100.497 87.8209Z" fill="#FF007A"/>
|
||||
<path d="M165.766 131.323C145.925 123.335 138.937 116.4 138.937 104.7C138.937 102.979 138.996 101.57 139.068 101.57C139.14 101.57 139.908 102.138 140.774 102.833C144.797 106.06 149.303 107.438 161.776 109.258C169.115 110.328 173.245 111.193 177.056 112.457C189.166 116.473 196.658 124.624 198.445 135.725C198.964 138.951 198.66 145 197.818 148.188C197.153 150.706 195.125 155.245 194.588 155.419C194.439 155.468 194.292 154.896 194.254 154.118C194.05 149.95 191.943 145.891 188.406 142.851C184.383 139.395 178.979 136.643 165.766 131.323Z" fill="#FF007A"/>
|
||||
<path d="M151.837 134.642C151.588 133.163 151.157 131.273 150.879 130.444L150.372 128.935L151.313 129.991C152.614 131.451 153.642 133.32 154.514 135.81C155.179 137.71 155.254 138.275 155.249 141.362C155.244 144.393 155.161 145.029 154.546 146.739C153.578 149.436 152.376 151.348 150.359 153.4C146.735 157.089 142.075 159.131 135.351 159.978C134.182 160.125 130.775 160.373 127.78 160.529C120.232 160.922 115.264 161.733 110.801 163.3C110.159 163.525 109.586 163.662 109.528 163.604C109.347 163.425 112.386 161.613 114.897 160.404C118.436 158.699 121.96 157.768 129.854 156.454C133.754 155.804 137.781 155.016 138.804 154.702C148.461 151.741 153.426 144.1 151.837 134.642Z" fill="#FF007A"/>
|
||||
<path d="M160.932 150.795C158.296 145.128 157.691 139.657 159.135 134.554C159.289 134.009 159.538 133.562 159.687 133.562C159.837 133.562 160.459 133.899 161.07 134.31C162.284 135.127 164.721 136.505 171.212 140.044C179.311 144.46 183.929 147.879 187.07 151.786C189.82 155.208 191.522 159.104 192.341 163.856C192.805 166.548 192.533 173.024 191.843 175.735C189.665 184.281 184.604 190.993 177.385 194.911C176.327 195.484 175.377 195.955 175.275 195.958C175.172 195.96 175.557 194.98 176.131 193.78C178.56 188.703 178.836 183.765 177 178.269C175.876 174.904 173.584 170.797 168.956 163.857C163.575 155.788 162.256 153.641 160.932 150.795Z" fill="#FF007A"/>
|
||||
<path d="M86.4067 181.371C93.7696 175.154 102.931 170.738 111.276 169.382C114.872 168.798 120.864 169.03 124.194 169.882C129.532 171.248 134.307 174.308 136.791 177.954C139.218 181.517 140.259 184.622 141.343 191.53C141.771 194.255 142.236 196.992 142.377 197.611C143.191 201.192 144.775 204.054 146.739 205.491C149.857 207.773 155.227 207.915 160.509 205.855C161.405 205.505 162.184 205.263 162.238 205.318C162.43 205.508 159.77 207.288 157.894 208.226C155.369 209.487 153.361 209.975 150.693 209.975C145.855 209.975 141.839 207.514 138.487 202.495C137.828 201.508 136.346 198.55 135.193 195.922C131.655 187.851 129.907 185.393 125.799 182.702C122.223 180.361 117.612 179.941 114.143 181.642C109.586 183.876 108.315 189.699 111.579 193.39C112.876 194.856 115.295 196.121 117.273 196.367C120.973 196.828 124.153 194.013 124.153 190.277C124.153 187.851 123.221 186.467 120.873 185.407C117.668 183.961 114.222 185.652 114.238 188.663C114.245 189.947 114.805 190.754 116.092 191.336C116.918 191.71 116.937 191.74 116.263 191.6C113.322 190.99 112.633 187.444 114.998 185.09C117.837 182.264 123.709 183.511 125.725 187.368C126.572 188.988 126.67 192.215 125.932 194.164C124.279 198.525 119.46 200.819 114.571 199.571C111.243 198.721 109.887 197.801 105.874 193.667C98.9012 186.484 96.1941 185.092 86.141 183.523L84.2146 183.222L86.4067 181.371Z" fill="#FF007A"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.8241 20.4645C57.1114 48.7285 93.0139 92.734 94.7963 95.1977C96.2679 97.232 95.7141 99.0609 93.1929 100.494C91.7909 101.291 88.9084 102.099 87.4652 102.099C85.8329 102.099 83.9939 101.313 82.6548 100.042C81.7084 99.1447 77.8882 93.4402 69.0694 79.7566C62.3216 69.2863 56.6747 60.6007 56.5206 60.4553C56.1644 60.119 56.1705 60.1303 68.3813 81.9787C76.0487 95.6979 78.6371 100.548 78.6371 101.197C78.6371 102.516 78.2771 103.21 76.6495 105.025C73.9363 108.052 72.7235 111.453 71.8479 118.492C70.8664 126.382 68.1064 131.955 60.4577 141.494C55.9803 147.078 55.2477 148.102 54.118 150.352C52.695 153.187 52.3037 154.774 52.1451 158.353C51.9775 162.137 52.3039 164.581 53.46 168.199C54.4721 171.366 55.5285 173.458 58.2292 177.641C60.5599 181.251 61.9019 183.933 61.9019 184.983C61.9019 185.818 62.0613 185.819 65.6729 185.003C74.316 183.052 81.3341 179.619 85.2812 175.412C87.724 172.808 88.2975 171.371 88.3161 167.802C88.3283 165.469 88.2462 164.98 87.6153 163.637C86.5884 161.452 84.7188 159.635 80.5983 156.818C75.1992 153.127 72.8932 150.156 72.2562 146.07C71.7337 142.717 72.3399 140.351 75.3267 134.091C78.4182 127.612 79.1843 124.85 79.7025 118.319C80.0372 114.1 80.5008 112.435 81.7131 111.1C82.9776 109.707 84.1158 109.235 87.245 108.808C92.3466 108.111 95.5951 106.791 98.2652 104.33C100.582 102.196 101.551 100.139 101.7 97.0427L101.813 94.6959L100.518 93.1867C95.8304 87.7209 30.6848 16.168 30.3963 16.168C30.3347 16.168 31.8773 18.1015 33.8241 20.4645ZM64.5333 162.634C65.5932 160.757 65.0301 158.344 63.2572 157.166C61.5821 156.052 58.9799 156.576 58.9799 158.028C58.9799 158.471 59.2248 158.793 59.7768 159.077C60.7063 159.555 60.7738 160.093 60.0425 161.192C59.3019 162.306 59.3616 163.284 60.2111 163.949C61.5802 165.021 63.5183 164.432 64.5333 162.634Z" fill="#FF007A"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M105.032 110.039C102.637 110.774 100.309 113.312 99.5884 115.974C99.1487 117.598 99.3982 120.446 100.057 121.325C101.121 122.746 102.149 123.12 104.935 123.101C110.389 123.063 115.131 120.724 115.682 117.799C116.134 115.402 114.051 112.08 111.183 110.622C109.703 109.87 106.555 109.571 105.032 110.039ZM111.408 115.024C112.249 113.829 111.881 112.537 110.451 111.664C107.726 110 103.607 111.377 103.607 113.95C103.607 115.231 105.755 116.629 107.724 116.629C109.035 116.629 110.829 115.847 111.408 115.024Z" fill="#FF007A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.9 KiB |
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html translate="no">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" type="image/png" href="%PUBLIC_URL%/favicon.png" />
|
||||
@@ -24,11 +24,79 @@
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
|
||||
<style>
|
||||
* {
|
||||
font-family: 'Inter', sans-serif;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@supports (font-variation-settings: normal) {
|
||||
* {
|
||||
font-family: 'Inter var', sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
font-variant: none;
|
||||
-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 {
|
||||
background-color: #212429;
|
||||
}
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
html {
|
||||
background-color: #F7F8FA;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<title>Uniswap Interface</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
|
||||
<!-- The root is the container of the app -->
|
||||
<div id="root">
|
||||
<!-- Triggers the font to load immediately and then is replaced by the app -->
|
||||
<div style="visibility: hidden">X</div>
|
||||
</div>
|
||||
|
||||
<div id="background-radial-gradient"></div>
|
||||
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
{
|
||||
"noWallet": "Keine Ethereum-Wallet gefunden",
|
||||
"wrongNetwork": "Du bist auf dem falschen Netzwerk.",
|
||||
"switchNetwork": "Bitte wechsle zum {{ correctNetwork }}",
|
||||
"installWeb3MobileBrowser": "Bitte besuche uns mit einem web3-fähigen mobilen Browser wie z.B. Trust Wallet oder Coinbase Wallet.",
|
||||
"installMetamask": "Bitte besuch uns erneut, nachdem du Metamask oder Brave installiert hast.",
|
||||
"disconnected": "Nicht verbunden",
|
||||
"swap": "Tauschen",
|
||||
"swapAnyway": "Trotzdem tauschen",
|
||||
"send": "Senden",
|
||||
"sendAnyway": "Trotzdem senden",
|
||||
"pool": "Pool",
|
||||
"betaWarning": "Dieses Projekt ist in beta. Nutzung auf eigenes Risiko.",
|
||||
"input": "Input",
|
||||
"output": "Output",
|
||||
"estimated": "geschätzt",
|
||||
"balance": "Guthaben: {{ balanceInput }}",
|
||||
"unlock": "Freischalten",
|
||||
"pending": "hängige",
|
||||
"selectToken": "Token auswählen",
|
||||
"searchOrPaste": "Token Name, Symbol oder Adresse suchen",
|
||||
"searchOrPasteMobile": "Name, Symbol oder Adresse",
|
||||
"noExchange": "Exchange nicht gefunden",
|
||||
"exchangeRate": "Wechselkurs",
|
||||
"invertedRate": "Invertierter Wechselkurs",
|
||||
"unknownError": "Oops! Ein unbekannter Fehler ist aufgetreten. Bitte Seite neu laden oder uns von einem anderen Browser oder Gerät erneut besuchen.",
|
||||
"enterValueCont": "Wert {{ missingCurrencyValue }} eingeben um fortzufahren.",
|
||||
"selectTokenCont": "Token auswählen um fortzufahren.",
|
||||
"noLiquidity": "Keine Liquidität.",
|
||||
"insufficientLiquidity": "Liquidität ungenügend.",
|
||||
"unlockTokenCont": "Token freischalten um fortzufahren.",
|
||||
"transactionDetails": "Details der Transaktion",
|
||||
"hideDetails": "Details ausblenden",
|
||||
"slippageWarning": "Wechselkursrutsch",
|
||||
"highSlippageWarning": "Hoher Wechselkursrutsch",
|
||||
"youAreSelling": "Du verkaufst",
|
||||
"orTransFail": "oder die Transaktion wird fehlschlagen.",
|
||||
"youWillReceive": "Du erhältst mindestens",
|
||||
"youAreBuying": "Du kaufst",
|
||||
"itWillCost": "Es kostet höchstens",
|
||||
"forAtMost": "für maximal",
|
||||
"insufficientBalance": "Guthaben ungenügend",
|
||||
"inputNotValid": "Eingabewert ungültig",
|
||||
"differentToken": "Es müssen unterschiedliche Token sein.",
|
||||
"noRecipient": "Empfängeradresse angeben.",
|
||||
"invalidRecipient": "Bitte gib eine gültige Empfängeradresse an.",
|
||||
"recipientAddress": "Adresse des Empfängers",
|
||||
"youAreSending": "Du schickst",
|
||||
"willReceive": "erhält mindestens",
|
||||
"to": "zu",
|
||||
"addLiquidity": "Liquidität hinzufügen",
|
||||
"deposit": "Depot",
|
||||
"currentPoolSize": "Aktuelle Größe des Pools",
|
||||
"yourPoolShare": "Dein Anteil am Pool",
|
||||
"noZero": "Wert darf nicht Null sein.",
|
||||
"mustBeETH": "Einer der Inputs muß ETH sein.",
|
||||
"enterCurrencyOrLabelCont": "{{ inputCurrency }} oder {{ label }} Wert eingeben um fortzufahren.",
|
||||
"youAreAdding": "Du fügst zwischen",
|
||||
"and": "und",
|
||||
"intoPool": "in den Liquiditätspool.",
|
||||
"outPool": "vom Liquiditätspool.",
|
||||
"youWillMint": "Du prägst",
|
||||
"liquidityTokens": "Liquiditätstokens.",
|
||||
"totalSupplyIs": "Die gesamte Anzahl Liquiditätstokens ist aktuell",
|
||||
"youAreSettingExRate": "Du setzt den anfänglichen Wechselkurs auf",
|
||||
"totalSupplyIs0": "Die gesamte Anzahl Liquiditätstokens ist aktuell 0.",
|
||||
"tokenWorth": "Zum gegenwärtigen Wechselkurs ist jeder Pool Token so viel Wert",
|
||||
"firstLiquidity": "Du bist die erste Person die Liquidität bereitstellt!",
|
||||
"initialExchangeRate": "Der initiale Wechselkurs wird auf deiner Überweisung basieren. Stelle sicher, dass deine ETH und {{ label }} denselben Fiatwert haben.",
|
||||
"removeLiquidity": "Liquidität entfernen",
|
||||
"poolTokens": "Pool Tokens",
|
||||
"enterLabelCont": "{{ label }} Wert eingeben um fortzufahren.",
|
||||
"youAreRemoving": "Du entfernst zwischen",
|
||||
"youWillRemove": "Du entfernst",
|
||||
"createExchange": "Exchange erstellen",
|
||||
"invalidTokenAddress": "Ungültige Tokenadresse",
|
||||
"exchangeExists": "{{ label }} Exchange existiert bereits!",
|
||||
"invalidSymbol": "Symbol ungültig",
|
||||
"invalidDecimals": "Dezimalstellen ungültig",
|
||||
"tokenAddress": "Tokenadresse",
|
||||
"label": "Label",
|
||||
"name": "Name",
|
||||
"symbol": "Symbol",
|
||||
"decimals": "Dezimalstellen",
|
||||
"enterTokenCont": "Tokenadresse eingeben um fortzufahren",
|
||||
"priceChange": "Geschätzter Wechselkursrutsch",
|
||||
"forAtLeast": "für mindestens ",
|
||||
"brokenToken": "Der ausgewählte Token ist nicht kompatibel mit Uniswap V1. Liquidität hinzufügen wird zu nicht mehr zugänglichen Token führen!"
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
{
|
||||
"noWallet": "No Ethereum wallet found",
|
||||
"wrongNetwork": "You are on the wrong network",
|
||||
"switchNetwork": "Please switch to {{ correctNetwork }}",
|
||||
"installWeb3MobileBrowser": "Please visit us from a web3-enabled mobile browser such as Trust Wallet or Coinbase Wallet.",
|
||||
"installMetamask": "Please visit us after installing Metamask on Chrome or Brave.",
|
||||
"disconnected": "Disconnected",
|
||||
"swap": "Swap",
|
||||
"swapAnyway": "Swap Anyway",
|
||||
"send": "Send",
|
||||
"sendAnyway": "Send Anyway",
|
||||
"pool": "Pool",
|
||||
"betaWarning": "This project is in beta. Use at your own risk.",
|
||||
"input": "Input",
|
||||
"output": "Output",
|
||||
"estimated": "estimated",
|
||||
"balance": "Balance: {{ balanceInput }}",
|
||||
"unlock": "Unlock",
|
||||
"pending": "Pending",
|
||||
"selectToken": "Select a token",
|
||||
"searchOrPaste": "Search Token Name, Symbol, or Address",
|
||||
"searchOrPasteMobile": "Name, Symbol, or Address",
|
||||
"noExchange": "No Exchange Found",
|
||||
"noToken": "No Token Found",
|
||||
"exchangeRate": "Exchange Rate",
|
||||
"unknownError": "Oops! An unknown error occurred. Please refresh the page, or visit from another browser or device.",
|
||||
"enterValueCont": "Enter a {{ missingCurrencyValue }} value to continue.",
|
||||
"selectTokenCont": "Select a token to continue.",
|
||||
"noLiquidity": "No liquidity.",
|
||||
"insufficientLiquidity": "Insufficient liquidity.",
|
||||
"unlockTokenCont": "Please unlock token to continue.",
|
||||
"transactionDetails": "Advanced Details",
|
||||
"hideDetails": "Hide Details",
|
||||
"slippageWarning": "Slippage Warning",
|
||||
"highSlippageWarning": "High Slippage Warning",
|
||||
"youAreSelling": "You are selling",
|
||||
"orTransFail": "or the transaction will fail.",
|
||||
"youWillReceive": "You will receive at least",
|
||||
"youAreBuying": "You are buying",
|
||||
"itWillCost": "It will cost at most",
|
||||
"forAtMost": "for at most",
|
||||
"insufficientBalance": "Insufficient Balance",
|
||||
"inputNotValid": "Not a valid input value",
|
||||
"differentToken": "Must be different token.",
|
||||
"noRecipient": "Enter a wallet address to send to.",
|
||||
"invalidRecipient": "Please enter a valid wallet address recipient.",
|
||||
"recipientAddress": "Recipient Address",
|
||||
"youAreSending": "You are sending",
|
||||
"willReceive": "will receive at least",
|
||||
"to": "to",
|
||||
"addLiquidity": "Add Liquidity",
|
||||
"deposit": "Deposit",
|
||||
"currentPoolSize": "Current Pool Size",
|
||||
"yourPoolShare": "Your Pool Share",
|
||||
"noZero": "Amount cannot be zero.",
|
||||
"mustBeETH": "One of the input must be ETH.",
|
||||
"enterCurrencyOrLabelCont": "Enter a {{ inputCurrency }} or {{ label }} value to continue.",
|
||||
"youAreAdding": "You are adding",
|
||||
"and": "and",
|
||||
"intoPool": "into the liquidity pool.",
|
||||
"outPool": "from the liquidity pool.",
|
||||
"youWillMint": "You will mint",
|
||||
"liquidityTokens": "liquidity tokens.",
|
||||
"totalSupplyIs": "Current total supply of liquidity tokens is",
|
||||
"youAreSettingExRate": "You are setting the initial exchange rate to",
|
||||
"totalSupplyIs0": "Current total supply of liquidity tokens is 0.",
|
||||
"tokenWorth": "At current exchange rate, each pool token is worth",
|
||||
"firstLiquidity": "You are the first person to add liquidity!",
|
||||
"initialExchangeRate": "The initial exchange rate will be set based on your deposits. Please make sure that your ETH and {{ label }} deposits have the same fiat value.",
|
||||
"removeLiquidity": "Remove Liquidity",
|
||||
"poolTokens": "Pool Tokens",
|
||||
"enterLabelCont": "Enter a {{ label }} value to continue.",
|
||||
"youAreRemoving": "You are removing between",
|
||||
"youWillRemove": "You will remove",
|
||||
"createExchange": "Create Exchange",
|
||||
"invalidTokenAddress": "Not a valid token address",
|
||||
"exchangeExists": "{{ label }} Exchange already exists!",
|
||||
"invalidSymbol": "Invalid symbol",
|
||||
"invalidDecimals": "Invalid decimals",
|
||||
"tokenAddress": "Token Address",
|
||||
"label": "Label",
|
||||
"name": "Name",
|
||||
"symbol": "Symbol",
|
||||
"decimals": "Decimals",
|
||||
"enterTokenCont": "Enter a token address to continue",
|
||||
"priceChange": "Expected price slippage",
|
||||
"forAtLeast": "for at least ",
|
||||
"brokenToken": "The selected token is not compatible with Uniswap V1. Adding liquidity will result in locked funds.",
|
||||
"toleranceExplanation": "Lowering this limit decreases your risk of frontrunning. However, this makes more likely that your transaction will fail due to normal price movements.",
|
||||
"tokenSearchPlaceholder": "Search name or paste address",
|
||||
"selectFee": "Select Fee",
|
||||
"selectLiquidityRange": "Set Price Range",
|
||||
"selectPool": "Select Fee Tier",
|
||||
"depositAmounts": "Deposit Amounts",
|
||||
"fee": "fee",
|
||||
"setLimits": "Set Limits",
|
||||
"percent": "Percent",
|
||||
"rate": "Rate",
|
||||
"currentRate": "Current {{label}} Price:",
|
||||
"inactiveRangeWarning": "Your position will not earn fees or be used in trades until the market price moves into your range.",
|
||||
"invalidRangeWarning": "Invalid range selected. The min price must be lower than the max price.",
|
||||
"connectWallet": "Connect Wallet",
|
||||
"unsupportedAsset": "Unsupported Asset",
|
||||
"feePool": "Fee Pool",
|
||||
"feeTier": "Fee Tier",
|
||||
"rebalanceMessage": "Your underlying tokens will be automatically rebalanced when the rate of the pool changes and may be different when you withdraw the position.",
|
||||
"addEarnHelper": "You will earn fees from trades proportional to your share of the pool.",
|
||||
"learnMoreAboutFess": " Learn more about earning fees.",
|
||||
"selectAPool": "Choose the best fee tier for your selected pair.",
|
||||
"poolType": "Select a fee tier based on your preferred liquidity provider fee.",
|
||||
"rangeWarning": "Your liquidity will only be active and earning fees when the rate of the pool is within this price range.",
|
||||
"chooseLiquidityAmount": "Choose an amount of tokens to open this liquidity position. If you don’t have enough tokens you can trade for them with a Swap.",
|
||||
"inputTokenDynamic": "Input {{label}}",
|
||||
"selectStartingPrice": "Set Starting Price",
|
||||
"newPoolPrice": "Select the market rate for the tokens being added."
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
{
|
||||
"noWallet": "No se encontró billetera de Ethereum",
|
||||
"wrongNetwork": "Te encontrás en la red equivocada",
|
||||
"switchNetwork": "Por favor cambia a {{ correctNetwork }}",
|
||||
"installWeb3MobileBrowser": "Por favor ingresá desde un navegador móvil con web3 habilitado como Trust Wallet o Coinbase Wallet.",
|
||||
"installMetamask": "Por favor visítanos nuevamente luego de instalar Metamask en Chrome o Brave.",
|
||||
"disconnected": "Desconectado",
|
||||
"swap": "Intercambiar",
|
||||
"send": "Enviar",
|
||||
"pool": "Pool",
|
||||
"betaWarning": "Este proyecto se encuentra en beta. Usalo bajo tu propio riesgo.",
|
||||
"input": "Entrada",
|
||||
"output": "Salida",
|
||||
"estimated": "estimado",
|
||||
"balance": "Saldo: {{ balanceInput }}",
|
||||
"unlock": "Desbloquear",
|
||||
"pending": "Pendiente",
|
||||
"selectToken": "Seleccioná un token",
|
||||
"searchOrPaste": "Buscar Token o Pegar Dirección",
|
||||
"noExchange": "No se encontró la divisa",
|
||||
"exchangeRate": "Tasa de Cambio",
|
||||
"enterValueCont": "Ingresá un valor en {{ missingCurrencyValue }} para continuar.",
|
||||
"selectTokenCont": "Seleccioná un token para continuar.",
|
||||
"noLiquidity": "Sin liquidez.",
|
||||
"unlockTokenCont": "Por favor desbloqueá un token para continuar.",
|
||||
"transactionDetails": "Detalles de la transacción",
|
||||
"hideDetails": "Ocultar detalles",
|
||||
"youAreSelling": "Estás vendiendo",
|
||||
"orTransFail": "o la transacción fallará.",
|
||||
"youWillReceive": "Vas a recibir al menos",
|
||||
"youAreBuying": "Estás comprando",
|
||||
"itWillCost": "Costará a lo sumo",
|
||||
"insufficientBalance": "Saldo insuficiente",
|
||||
"inputNotValid": "No es un valor de entrada válido",
|
||||
"differentToken": "Debe ser un token distinto.",
|
||||
"noRecipient": "Ingresá una dirección de billetera para enviar.",
|
||||
"invalidRecipient": "Por favor ingrese una billetera de destino válida.",
|
||||
"recipientAddress": "Dirección del recipiente",
|
||||
"youAreSending": "Estás enviando",
|
||||
"willReceive": "recibirá al menos",
|
||||
"to": "a",
|
||||
"addLiquidity": "Agregar liquidez",
|
||||
"deposit": "Depositar",
|
||||
"currentPoolSize": "Tamaño del Pool Actual",
|
||||
"yourPoolShare": "Tu parte del Pool",
|
||||
"noZero": "El monto no puede ser cero.",
|
||||
"mustBeETH": "Una de las entradas debe ser ETH.",
|
||||
"enterCurrencyOrLabelCont": "Ingresá un valor de {{ inputCurrency }} o de {{ label }} para continuar.",
|
||||
"youAreAdding": "Estás agregando entre",
|
||||
"and": "y",
|
||||
"intoPool": "en el pool de liquidez.",
|
||||
"outPool": "en el pool de liquidez.",
|
||||
"youWillMint": "Vas a acuñar",
|
||||
"liquidityTokens": "tokens de liquidez.",
|
||||
"totalSupplyIs": "El actual suministro total de tokens de liquidez es",
|
||||
"youAreSettingExRate": "Está configurando el tipo de cambio inicial a",
|
||||
"totalSupplyIs0": "El actual suministro total de tokens de liquidez es 0.",
|
||||
"tokenWorth": "Al tipo de cambio actual, cada token del pool vale",
|
||||
"firstLiquidity": "Sos la primer persona en agregar liquidez!",
|
||||
"initialExchangeRate": "El tipo de cambio inicial se establecerá en función de tus depósitos. Por favor, asegúrate de que tus depósitos en ETH y {{ label }} tengan el mismo valor fíat.",
|
||||
"removeLiquidity": "Remover Liquidez",
|
||||
"poolTokens": "Pool de Tokens",
|
||||
"enterLabelCont": "Ingresá un valor de {{ label }} para continuar.",
|
||||
"youAreRemoving": "Estás quitando entre",
|
||||
"youWillRemove": "Vas a remover",
|
||||
"createExchange": "Crear divisa",
|
||||
"invalidTokenAddress": "No es una dirección de token válida",
|
||||
"exchangeExists": "La divisa {{ label }} ya existe!",
|
||||
"invalidSymbol": "Símbolo inválido",
|
||||
"invalidDecimals": "Decimales inválidos",
|
||||
"tokenAddress": "Dirección de Token",
|
||||
"label": "Etiqueta",
|
||||
"decimals": "Decimales",
|
||||
"enterTokenCont": "Ingresá una dirección de token para continuar"
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
{
|
||||
"noWallet": "No se ha encontrado billetera de Ethereum",
|
||||
"wrongNetwork": "Se encuentra en la red equivocada",
|
||||
"switchNetwork": "Por favor cambie a {{ correctNetwork }}",
|
||||
"installWeb3MobileBrowser": "Por favor ingrese desde un navegador móvil con web3 habilitado como Trust Wallet o Coinbase Wallet.",
|
||||
"installMetamask": "Por favor visítenos nuevamente luego de instalar Metamask en Chrome o Brave.",
|
||||
"disconnected": "Desconectado",
|
||||
"swap": "Intercambiar",
|
||||
"send": "Enviar",
|
||||
"pool": "Pool",
|
||||
"betaWarning": "Este proyecto se encuentra en beta. Úselo bajo tu propio riesgo.",
|
||||
"input": "Entrada",
|
||||
"output": "Salida",
|
||||
"estimated": "estimado",
|
||||
"balance": "Saldo: {{ balanceInput }}",
|
||||
"unlock": "Desbloquear",
|
||||
"pending": "Pendiente",
|
||||
"selectToken": "Seleccione un token",
|
||||
"searchOrPaste": "Buscar Token o Pegar Dirección",
|
||||
"noExchange": "No se ha encontrado la divisa",
|
||||
"exchangeRate": "Tasa de Cambio",
|
||||
"enterValueCont": "Ingrese un valor en {{ missingCurrencyValue }} para continuar.",
|
||||
"selectTokenCont": "Seleccione un token para continuar.",
|
||||
"noLiquidity": "Sin liquidez.",
|
||||
"unlockTokenCont": "Por favor desbloquea un token para continuar.",
|
||||
"transactionDetails": "Detalles de la transacción",
|
||||
"hideDetails": "Ocultar detalles",
|
||||
"youAreSelling": "Está vendiendo",
|
||||
"orTransFail": "o la transacción fallará.",
|
||||
"youWillReceive": "Va a recibir al menos",
|
||||
"youAreBuying": "Está comprando",
|
||||
"itWillCost": "Costará a lo sumo",
|
||||
"insufficientBalance": "Saldo insuficiente",
|
||||
"inputNotValid": "No es un valor de entrada válido",
|
||||
"differentToken": "Debe ser un token distinto.",
|
||||
"noRecipient": "Ingrese una dirección de billetera para enviar.",
|
||||
"invalidRecipient": "Por favor ingrese una billetera de destino válida.",
|
||||
"recipientAddress": "Dirección del recipiente",
|
||||
"youAreSending": "Está enviando",
|
||||
"willReceive": "recibirá al menos",
|
||||
"to": "a",
|
||||
"addLiquidity": "Agregar liquidez",
|
||||
"deposit": "Depositar",
|
||||
"currentPoolSize": "Tamaño del Pool Actual",
|
||||
"yourPoolShare": "Su parte del Pool",
|
||||
"noZero": "El monto no puede ser cero.",
|
||||
"mustBeETH": "Una de las entradas debe ser ETH.",
|
||||
"enterCurrencyOrLabelCont": "Ingrese un valor de {{ inputCurrency }} o de {{ label }} para continuar.",
|
||||
"youAreAdding": "Está agregando entre",
|
||||
"and": "y",
|
||||
"intoPool": "en el pool de liquidez.",
|
||||
"outPool": "en el pool de liquidez.",
|
||||
"youWillMint": "Va a acuñar",
|
||||
"liquidityTokens": "tokens de liquidez.",
|
||||
"totalSupplyIs": "El actual suministro total de tokens de liquidez es",
|
||||
"youAreSettingExRate": "Está configurando el tipo de cambio inicial a",
|
||||
"totalSupplyIs0": "El actual suministro total de tokens de liquidez es 0.",
|
||||
"tokenWorth": "Al tipo de cambio actual, cada token del pool vale",
|
||||
"firstLiquidity": "Es la primer persona en agregar liquidez!",
|
||||
"initialExchangeRate": "El tipo de cambio inicial se establecerá en función de sus depósitos. Por favor, asegúrese de que sus depósitos en ETH y {{ label }} tengan el mismo valor fíat.",
|
||||
"removeLiquidity": "Remover Liquidez",
|
||||
"poolTokens": "Pool de Tokens",
|
||||
"enterLabelCont": "Ingresa un valor de {{ label }} para continuar.",
|
||||
"youAreRemoving": "Está quitando entre",
|
||||
"youWillRemove": "Va a remover",
|
||||
"createExchange": "Crear tipo de cambio",
|
||||
"invalidTokenAddress": "No es una dirección de token válida",
|
||||
"exchangeExists": "El tipo de cambio {{ label }} ya existe!",
|
||||
"invalidSymbol": "Símbolo inválido",
|
||||
"invalidDecimals": "Decimales inválidos",
|
||||
"tokenAddress": "Dirección de Token",
|
||||
"label": "Etiqueta",
|
||||
"decimals": "Decimales",
|
||||
"enterTokenCont": "Ingrese una dirección de token para continuar"
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
{
|
||||
"noWallet": "Wallet Ethereum non trovato",
|
||||
"wrongNetwork": "Sei connesso alla rete sbagliata",
|
||||
"switchNetwork": "Perfavore connettiti su {{ correctNetwork }}",
|
||||
"installWeb3MobileBrowser": "Perfavore visita il sito da un browser abilitato web3 o da un app mobile come Trust Wallet o Coinbase Wallet.",
|
||||
"installMetamask": "Perfavore ritorna dopo aver installato Metamask su Chrome o Brave.",
|
||||
"disconnected": "Disconnesso",
|
||||
"swap": "Scambia",
|
||||
"swapAnyway": "Scambia comunque",
|
||||
"send": "Invia",
|
||||
"sendAnyway": "Invia comunque",
|
||||
"pool": "Riserva",
|
||||
"betaWarning": "Questo progetto è in beta. Usalo a tuo rischio.",
|
||||
"input": "Input",
|
||||
"output": "Output",
|
||||
"estimated": "stimato",
|
||||
"balance": "Saldo: {{ balanceInput }}",
|
||||
"unlock": "Sblocca",
|
||||
"pending": "In attesa",
|
||||
"selectToken": "Seleziona un token",
|
||||
"searchOrPaste": "Cerca Nome, Simbolo o Indirizzo Token",
|
||||
"searchOrPasteMobile": "Nome, Simbolo, o Indirizzo",
|
||||
"noExchange": "Nessun Exchange Trovato",
|
||||
"exchangeRate": "Tasso di cambio",
|
||||
"unknownError": "Oops! Si è verificato un Errore imprevisto. Aggiorna la pagina o visita da un altro browser o dispositivo.",
|
||||
"enterValueCont": "Inserisci un valore {{ missingCurrencyValue }} per continuare.",
|
||||
"selectTokenCont": "Seleziona un token per continuare.",
|
||||
"noLiquidity": "Nessuna liquidità.",
|
||||
"insufficientLiquidity": "Liquidità insufficiente.",
|
||||
"unlockTokenCont": "Si prega di sbloccare il token per continuare.",
|
||||
"transactionDetails": "Dettagli avanzati",
|
||||
"hideDetails": "Nascondi dettagli",
|
||||
"slippageWarning": "Avviso di scostamento",
|
||||
"highSlippageWarning": "Avviso di elevato scostamento",
|
||||
"youAreSelling": "Stai vendendo",
|
||||
"orTransFail": "o la transazione fallità.",
|
||||
"youWillReceive": "Riceverai almeno",
|
||||
"youAreBuying": "Stai comprando",
|
||||
"itWillCost": "Costerà al massimo",
|
||||
"forAtMost": "per al massimo",
|
||||
"insufficientBalance": "Saldo insufficente",
|
||||
"inputNotValid": "Non è un valore di input valido",
|
||||
"differentToken": "Deve essere un token diverso.",
|
||||
"noRecipient": "Inserisci un indirizzo di wallet a cui inviare.",
|
||||
"invalidRecipient": "Inserisci un destinatario valido per l'indirizzo del wallet.",
|
||||
"recipientAddress": "Indirizzo del destinatario",
|
||||
"youAreSending": "Stai inviando",
|
||||
"willReceive": "riceverà almeno",
|
||||
"to": "a",
|
||||
"addLiquidity": "Aggiungi liquidità",
|
||||
"deposit": "Depositare",
|
||||
"currentPoolSize": "Dimensione attuale del pool",
|
||||
"yourPoolShare": "La tua parte di pool condivisa",
|
||||
"noZero": "L'importo non può essere zero.",
|
||||
"mustBeETH": "Uno degli input deve essere ETH.",
|
||||
"enterCurrencyOrLabelCont": "Inserisci un valore {{ inputCurrency }} o {{ label }} per continuare.",
|
||||
"youAreAdding": "Stai agginugendo",
|
||||
"and": "e",
|
||||
"intoPool": "nella riserva di liquidità.",
|
||||
"outPool": "dalla riserva di liquidità.",
|
||||
"youWillMint": "Tu conierai",
|
||||
"liquidityTokens": "token di liquidità.",
|
||||
"totalSupplyIs": "L'attuale disponibilità totale di token di liquidità è",
|
||||
"youAreSettingExRate": "Stai impostando il tasso di cambio iniziale su",
|
||||
"totalSupplyIs0": "L'attuale disponibilità totale di token di liquidità è 0.",
|
||||
"tokenWorth": "Al tasso di cambio corrente, ogni token del pool vale",
|
||||
"firstLiquidity": "Sei la prima persona ad aggiungere liquidità!",
|
||||
"initialExchangeRate": "Il tasso di cambio iniziale verrà impostato in base ai tuoi depositi. Assicurati che i tuoi depositi ETH e {{ label }} abbiano lo stesso valore fiat.",
|
||||
"removeLiquidity": "Rimuovi Liquidità",
|
||||
"poolTokens": "Token Pool",
|
||||
"enterLabelCont": "Inserisci un valore {{ label }} per continuare.",
|
||||
"youAreRemoving": "Stai rimuovendo tra",
|
||||
"youWillRemove": "Rimuoverai",
|
||||
"createExchange": "Crea scambio",
|
||||
"invalidTokenAddress": "Indirizzo token non valido",
|
||||
"exchangeExists": "{{ label }} Exchange già esistente!",
|
||||
"invalidSymbol": "Simbolo non valido",
|
||||
"invalidDecimals": "Decimali non validi",
|
||||
"tokenAddress": "Indirizzo Token",
|
||||
"label": "Etichetta",
|
||||
"name": "Nome",
|
||||
"symbol": "Simbolo",
|
||||
"decimals": "Decimali",
|
||||
"enterTokenCont": "Inserire un indirizzo token per continuare",
|
||||
"priceChange": "Scostamento del prezzo previsto",
|
||||
"forAtLeast": "per almeno "
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
{
|
||||
"noWallet": "לא נמצא ארנק",
|
||||
"wrongNetwork": "נבחרה רשת לא נכונה",
|
||||
"switchNetwork": "{{ correctNetwork }} יש צורך לשנות את הרשת ל",
|
||||
"installWeb3MobileBrowser": "יש צורך בארנק ווב3.0, תתקין מטאמאסק או ארנק דומה",
|
||||
"installMetamask": " Metamask יש צורך להתקין תוסף מטאמאסק לדפדפן, חפשו בגוגל ",
|
||||
"disconnected": "מנותק",
|
||||
"swap": "המרה",
|
||||
"send": "שליחה",
|
||||
"pool": "להפקיד",
|
||||
"betaWarning": "הפרויקט נמצא בשלב בטא, השתמשו באחריות",
|
||||
"input": "מוכר",
|
||||
"output": "אקבל",
|
||||
"estimated": "הערכה",
|
||||
"balance": "בארנק שלי {{ balanceInput }}",
|
||||
"unlock": "שחרור נעילת ארנק",
|
||||
"pending": "ממתין לאישור",
|
||||
"selectToken": "בחרו את הטוקן להמרה",
|
||||
"searchOrPaste": "הכניסו שם או כתובת של טוקן לחיפוש",
|
||||
"noExchange": "לא מתאפשרת המרה",
|
||||
"exchangeRate": "שער המרה",
|
||||
"enterValueCont": "כדי להמשיך {{ missingCurrencyValue }} הזינו ",
|
||||
"selectTokenCont": "בחרו טוקן כדי להמשיך",
|
||||
"noLiquidity": "אין נזילות",
|
||||
"unlockTokenCont": "יש צורך לאשר את הטוקן למסחר",
|
||||
"transactionDetails": "פרטי הטרנזקציה",
|
||||
"hideDetails": "הסתר פרטים נוספים",
|
||||
"youAreSelling": "למכירה",
|
||||
"orTransFail": "או שהטרנזקציה תיכשל",
|
||||
"youWillReceive": "תוצר המרה מינימלי",
|
||||
"youAreBuying": "קונה",
|
||||
"itWillCost": "זה יעלה",
|
||||
"insufficientBalance": "אין בחשבון מספיק מטבעות",
|
||||
"inputNotValid": "קלט לא תקין",
|
||||
"differentToken": "יש צורך בטוקנים שונים",
|
||||
"noRecipient": "לא הוכנסה כתובת ארנק יעד",
|
||||
"invalidRecipient": "לא הוכנסה כתובת תקינה",
|
||||
"recipientAddress": "כתובת יעד",
|
||||
"youAreSending": "כמות לשליחה",
|
||||
"willReceive": "יתקבל לכל הפחות",
|
||||
"to": "אל",
|
||||
"addLiquidity": "להוספת נזילות למאגר",
|
||||
"deposit": "הפקדה",
|
||||
"currentPoolSize": "גודל מאגר הנזילות הכולל",
|
||||
"yourPoolShare": "חלקך במאגר הנזילות",
|
||||
"noZero": "אפס אינו ערך תקין",
|
||||
"mustBeETH": "ETH חייב להופיע באחד מהצדדים",
|
||||
"enterCurrencyOrLabelCont": "כדי להמשיך {{ inputCurrency }} או {{ label }} הכנס",
|
||||
"youAreAdding": "מתווספים למאגר",
|
||||
"and": "וגם",
|
||||
"intoPool": "לתוך הנזילות",
|
||||
"outPool": "מתוך",
|
||||
"youWillMint": "יונפקו לכם",
|
||||
"liquidityTokens": "טוקנים של נזילות",
|
||||
"totalSupplyIs": "חלקך במאגר הנזילות",
|
||||
"youAreSettingExRate": "שער ההמרה יקבע על ידך",
|
||||
"totalSupplyIs0": "אין לך טוקנים של נזילות",
|
||||
"tokenWorth": "שווי כל טוקן נזילות הינו",
|
||||
"firstLiquidity": "אתה הראשוןה שמזרים נזילות למאגר",
|
||||
"initialExchangeRate": "ושל האית'ר הינן בערך שווה {{ label }} תוודאו שההפקדה של הטוקן",
|
||||
"removeLiquidity": "הוצאה של נזילות",
|
||||
"poolTokens": "טוקנים של מאגר הנזילות",
|
||||
"enterLabelCont": "כדי להמשיך {{ label }} הכנס ",
|
||||
"youAreRemoving": "יוסרו",
|
||||
"youWillRemove": "יוסרו",
|
||||
"createExchange": "ליצירת זוג מסחר",
|
||||
"invalidTokenAddress": "כתובת טוקן לא נכונה",
|
||||
"exchangeExists": "{{ label }} כבר קיים זוג המרה עבור",
|
||||
"invalidSymbol": "תו שגוי",
|
||||
"invalidDecimals": "ספרות עשרוניות שגויות",
|
||||
"tokenAddress": "כתובת הטוקן",
|
||||
"label": "שם",
|
||||
"decimals": "ספרות עשרויות",
|
||||
"enterTokenCont": "הכניסו כתובת טוקן כדי להמשיך"
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
{
|
||||
"noWallet": "Niciun portofel Ethereum găsit",
|
||||
"wrongNetwork": "Nu ești conectat la rețeaua corectă",
|
||||
"switchNetwork": "Conectează-te te rog la {{ correctNetwork }}",
|
||||
"installWeb3MobileBrowser": "Incearcă să vizitezi această pagina folosind un browser precum Trust Wallet sau Coinbase Wallet.",
|
||||
"installMetamask": "Vizitează această pagină din nou după ce instalezi MetaMask în Chrome sau Brave",
|
||||
"disconnected": "Deconectat",
|
||||
"swap": "Schimbă",
|
||||
"swapAnyway": "Schimbă Oricum",
|
||||
"send": "Trimite",
|
||||
"sendAnyway": "Trimite Oricum",
|
||||
"pool": "Depune Lichiditate",
|
||||
"betaWarning": "Proiectul este încă în versiunea beta. Folosește-l cu grijă, riscul este al tău.",
|
||||
"input": "Input",
|
||||
"output": "Output",
|
||||
"estimated": "estimat",
|
||||
"balance": "Balanță: {{ balanceInput }}",
|
||||
"unlock": "Deblochează",
|
||||
"pending": "În Așteptare",
|
||||
"selectToken": "Selectează un jeton",
|
||||
"searchOrPaste": "Caută după Numele, Simbolul sau Adresa Jetonului",
|
||||
"searchOrPasteMobile": "Nume, Simbol sau Adresă",
|
||||
"noExchange": "Nicio Piață de Schimb Găsită",
|
||||
"noToken": "Nicin Jeton Găsit",
|
||||
"exchangeRate": "Curs de Schimb",
|
||||
"unknownError": "Ups! A intervenit o eroare tehnică. Te rog reîncarcă pagina, sau acceseaz-o de pe alt navigator sau dispozitiv.",
|
||||
"enterValueCont": "Setează o valoare pentru {{ missingCurrencyValue }} pentru a continua.",
|
||||
"selectTokenCont": "Selectează un jeton pentru a continua.",
|
||||
"noLiquidity": "Nu Există Lichiditate.",
|
||||
"insufficientLiquidity": "Lichiditate Insuficientă.",
|
||||
"unlockTokenCont": "Te rog deblochează jetonul pentru a continua.",
|
||||
"transactionDetails": "Detalii Avansate",
|
||||
"hideDetails": "Ascunde Detaili",
|
||||
"slippageWarning": "Alertă Deviație de Preț",
|
||||
"highSlippageWarning": "Alertă Deviație de Preț Mare",
|
||||
"youAreSelling": "Tu vinzi",
|
||||
"orTransFail": "sau tranzacția va eșua.",
|
||||
"youWillReceive": "Vei primi cel puțin",
|
||||
"youAreBuying": "Tu cumperi",
|
||||
"itWillCost": "Va costa cel mult",
|
||||
"forAtMost": "pentru maximum",
|
||||
"insufficientBalance": "Balanță Insuficientă",
|
||||
"inputNotValid": "Valoarea setată nu este validă",
|
||||
"differentToken": "Trebuie să fie un jeton diferit.",
|
||||
"noRecipient": "Setează o adresă de portofel pentru destinatar.",
|
||||
"invalidRecipient": "Te rog setează o adresă de portofel validă pentru destinatar.",
|
||||
"recipientAddress": "Adresa Destinatarului",
|
||||
"youAreSending": "Tu trimiți",
|
||||
"willReceive": "vei primi cel puțin",
|
||||
"to": "spre",
|
||||
"addLiquidity": "Adaugă Lichiditate",
|
||||
"deposit": "Depozitează",
|
||||
"currentPoolSize": "Volumul Actual al Fondului",
|
||||
"yourPoolShare": "Partea Ta din Fond",
|
||||
"noZero": "Cantitatea nu poate fi zero.",
|
||||
"mustBeETH": "Una dintre valori trebuie să fie ETH.",
|
||||
"enterCurrencyOrLabelCont": "Setează o valoare pentru {{ inputCurrency }} sau {{ label }} pentru a continua. ",
|
||||
"youAreAdding": "Tu adaugi",
|
||||
"and": "și",
|
||||
"intoPool": "în fondul de lichidatate.",
|
||||
"outPool": "din fondul de lichiditate.",
|
||||
"youWillMint": "Tu vei tipări",
|
||||
"liquidityTokens": "jetoane de lichiditate.",
|
||||
"totalSupplyIs": "Cantitatea totală de jetoane de lichiditate este",
|
||||
"youAreSettingExRate": "Setezi cursul de schimb inițial la",
|
||||
"totalSupplyIs0": "Cantitatea totală de jetoane de lichiditate este 0.",
|
||||
"tokenWorth": "La cursul de schimb actual, fiecare jeton de lichiditate este valorat la",
|
||||
"firstLiquidity": "Ești prima persoană care depune lichiditate!",
|
||||
"initialExchangeRate": "Cursul de schimb inițial va fi setat în funcție de depozitele tale. Te rog asigură-te că ETH-ul și {{ label }}-ul pe care le-ai depozitat au aceeași valoare în bani fiat.",
|
||||
"removeLiquidity": "Retrage Lichiditat",
|
||||
"poolTokens": "Depune Jetoane",
|
||||
"enterLabelCont": "Setează o valoare pentru {{ label }} pentru a continua.",
|
||||
"youAreRemoving": "Retragi între",
|
||||
"youWillRemove": "Vei retrage",
|
||||
"createExchange": "Creează Piață de Schimb",
|
||||
"invalidTokenAddress": "Adresa de jeton nu este validă",
|
||||
"exchangeExists": "{{ label }} Piața de schimb există deja!",
|
||||
"invalidSymbol": "Simbol invalid",
|
||||
"invalidDecimals": "Zecimale invalide",
|
||||
"tokenAddress": "Adresă Jeton",
|
||||
"label": "Denumire",
|
||||
"name": "Nume",
|
||||
"symbol": "Simbol",
|
||||
"decimals": "Zecimale",
|
||||
"enterTokenCont": "Setează o adresă de jeton pentru a continua",
|
||||
"priceChange": "Deviație de preț așteptată",
|
||||
"forAtLeast": "pentru cel puțin ",
|
||||
"brokenToken": "Jetonul selectat nu este compatibil cu Uniswap V1. Depunerea de lichiditate îți va bloca fondurile pe vecie.",
|
||||
"toleranceExplanation": "Micșorând această limită, vei reduce riscul de frontrunning. În același timp, devine mai probabil că tranzacția va eșua din cauza variațiilor normale de preț.",
|
||||
"tokenSearchPlaceholder": "Caută nume sau lipește adresă"
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
{
|
||||
"noWallet": "Кошелек эфира не найден",
|
||||
"wrongNetwork": "Некорректная сеть",
|
||||
"switchNetwork": "Пожалуйста, перейдите в {{ correctNetwork }}",
|
||||
"installWeb3MobileBrowser": "Пожалуйста, откройте в браузере с поддержкой web3, таком, как Trust Wallet или Coinbase Wallet.",
|
||||
"installMetamask": "Пожалуйста, откройте в Chrome или Brave с установленным расширением Metamask.",
|
||||
"disconnected": "Нет подключения",
|
||||
"swap": "Обменять",
|
||||
"send": "Отправить",
|
||||
"pool": "Вложить",
|
||||
"betaWarning": "Проект находится на стадии бета тестирования.",
|
||||
"input": "Ввести",
|
||||
"output": "Вывести",
|
||||
"estimated": "по оценке",
|
||||
"balance": "Баланс: {{ balanceInput }}",
|
||||
"unlock": "Разблокировать",
|
||||
"pending": "Ожидание",
|
||||
"selectToken": "Выберите токен",
|
||||
"searchOrPaste": "Поиск токена или вставить адрес токена",
|
||||
"noExchange": "Обмен не найден",
|
||||
"exchangeRate": "Курс обмена",
|
||||
"enterValueCont": "Введите {{ missingCurrencyValue }}, чтобы продолжить.",
|
||||
"selectTokenCont": "Введите токен, чтобы продолжить.",
|
||||
"noLiquidity": "Нет ликвидности.",
|
||||
"unlockTokenCont": "Пожалуйста, разблокируйте токен, чтобы продолжить.",
|
||||
"transactionDetails": "Детали транзакции",
|
||||
"hideDetails": "Скрыть подробности",
|
||||
"youAreSelling": "Вы продаете",
|
||||
"orTransFail": "или транзакция будет отклонена.",
|
||||
"youWillReceive": "Вы получите как минимум",
|
||||
"youAreBuying": "Вы покупаете",
|
||||
"itWillCost": "В крайнем случае это будет стоить",
|
||||
"insufficientBalance": "Недостаточно средств",
|
||||
"inputNotValid": "Некорректное значение",
|
||||
"differentToken": "Должны быть разные токены.",
|
||||
"noRecipient": "Введите адрес кошелька эфира, куда перечислить.",
|
||||
"invalidRecipient": "Пожалуйста, введите корректный адрес кошелька получателя.",
|
||||
"recipientAddress": "Адрес получателя",
|
||||
"youAreSending": "Вы отправляете",
|
||||
"willReceive": "получит как минимум",
|
||||
"to": "",
|
||||
"addLiquidity": "Добавить ликвидность",
|
||||
"deposit": "Вложить",
|
||||
"currentPoolSize": "Текущий размер пула",
|
||||
"yourPoolShare": "Ваша доля в пуле",
|
||||
"noZero": "Значение не может быть нулевым.",
|
||||
"mustBeETH": "Одно из значений должно быть ETH.",
|
||||
"enterCurrencyOrLabelCont": "Введите {{ inputCurrency }} или {{ label }}, чтобы продолжить.",
|
||||
"youAreAdding": "Вы добавляете от",
|
||||
"and": "и",
|
||||
"intoPool": "в пул ликвидности.",
|
||||
"outPool": "из пула ликвидности.",
|
||||
"youWillMint": "Вы произведёте",
|
||||
"liquidityTokens": "токенов ликвидности.",
|
||||
"totalSupplyIs": "Ваш объем токенов ликвидности",
|
||||
"youAreSettingExRate": "Вы устанавливаете начальный курс обмена",
|
||||
"totalSupplyIs0": "Ваш объем токенов ликвидности 0.",
|
||||
"tokenWorth": "При текущем курсе, каждый токен пула оценивается в",
|
||||
"firstLiquidity": "Вы первый, кто создаст ликвидность!",
|
||||
"initialExchangeRate": "Начальный курс обмена будет установлен согласно вашим депозитам. Убедитесь, что ваши депозиты ETH и {{ label }} имеют одинаковое значение в валюте.",
|
||||
"removeLiquidity": "Убрать ликвидность",
|
||||
"poolTokens": "Токены пула",
|
||||
"enterLabelCont": "Введите {{ label }}, чтобы продолжить.",
|
||||
"youAreRemoving": "Вы убираете в от",
|
||||
"youWillRemove": "Вы уберёте",
|
||||
"createExchange": "Создать обмен",
|
||||
"invalidTokenAddress": "Некорректный адрес токена",
|
||||
"exchangeExists": "{{ label }} Обмен уже существует!",
|
||||
"invalidSymbol": "Некорректный символ",
|
||||
"invalidDecimals": "Некорректное десятичное значение",
|
||||
"tokenAddress": "Адрес токена",
|
||||
"label": "Название",
|
||||
"decimals": "Десятичное значение",
|
||||
"enterTokenCont": "Чтобы продолжить, введите адрес токена"
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
{
|
||||
"noWallet": "Không tìm thấy ví tiền Ethereum",
|
||||
"wrongNetwork": "Kết nối mạng không đúng",
|
||||
"switchNetwork": "Vui lòng chuyển sang {{ correctNetwork }}",
|
||||
"installWeb3MobileBrowser": "Vui lòng truy cập từ trình duyệt di động hỗ trợ web3 như là Ví Trust hoặc Ví Coinbase",
|
||||
"installMetamask": "Vui lòng truy cập sau khi cài đặt Metamask trên Chrome hoặc Brave.",
|
||||
"disconnected": "Ngắt kết nối rồi",
|
||||
"swap": "Hoán đổi",
|
||||
"swapAnyway": "Tiếp tục hoán đổi?",
|
||||
"send": "Gửi",
|
||||
"sendAnyway": "Tiếp tục gửi?",
|
||||
"pool": "Chung vốn",
|
||||
"betaWarning": "Dự án này đang trong giai đoạn thử nghiệm. Sử dụng có rủi ro của riêng bạn",
|
||||
"input": "Đầu vào",
|
||||
"output": "Đầu ra",
|
||||
"estimated": "ước lượng",
|
||||
"balance": "Số dư: {{ balanceInput }}",
|
||||
"unlock": "Mở khóa",
|
||||
"pending": "Đang chờ xử lý",
|
||||
"selectToken": "Chọn một đồng tiền ảo",
|
||||
"searchOrPaste": "Tìm kiếm tên, biểu tượng, hoặc địa chỉ của đồng tiền ảo",
|
||||
"searchOrPasteMobile": "Tên, Biểu tượng, hoặc Địa chỉ",
|
||||
"noExchange": "Không tìm thấy giao dịch",
|
||||
"exchangeRate": "Tỷ giá",
|
||||
"unknownError": "Rất tiếc! Xảy ra lỗi không xác định. Vui lòng làm mới trang, hoặc truy cập từ trình duyệt hay thiết bị khác.",
|
||||
"enterValueCont": "Nhập một giá trị {{ missingCurrencyValue }} để tiếp tục.",
|
||||
"selectTokenCont": "Chọn một đồng tiền ảo để tiếp tục.",
|
||||
"noLiquidity": "Không có tính thanh khoản.",
|
||||
"insufficientLiquidity": "Không đủ tính thanh khoản.",
|
||||
"unlockTokenCont": "Vui lòng mở khoá đồng tiền ảo để tiếp tục",
|
||||
"transactionDetails": "Chi tiết nâng cao",
|
||||
"hideDetails": "Ẩn chi tiết",
|
||||
"slippageWarning": "Cảnh báo trượt giá",
|
||||
"highSlippageWarning": "Cảnh báo trượt giá cao",
|
||||
"youAreSelling": "Bạn đang bán",
|
||||
"orTransFail": "hoặc giao dịch sẽ thất bại.",
|
||||
"youWillReceive": "Bạn sẽ nhận dược ít nhất là",
|
||||
"youAreBuying": "Bạn đang mua",
|
||||
"itWillCost": "Nó sẽ có giá cao nhất",
|
||||
"forAtMost": "nhiều nhất",
|
||||
"insufficientBalance": "Số dư không đủ",
|
||||
"inputNotValid": "Giá trị nhập vào không hợp lệ",
|
||||
"differentToken": "Đồng tiền ảo phải khác nhau.",
|
||||
"noRecipient": "Nhập địa chỉ ví để gửi đến.",
|
||||
"invalidRecipient": "Vui lòng nhập một người nhận địa chỉ ví hợp lệ.",
|
||||
"recipientAddress": "Địa chỉ người nhận",
|
||||
"youAreSending": "Bạn đang gửi",
|
||||
"willReceive": "sẽ nhận dược ít nhất là",
|
||||
"to": "đến",
|
||||
"addLiquidity": "Thêm tiền thanh khoản",
|
||||
"deposit": "Gửi tiền",
|
||||
"currentPoolSize": "Quy mô hiện tại của quỹ",
|
||||
"yourPoolShare": "Phần hùn của bạn trong quỹ",
|
||||
"noZero": "Số tiền không thể bằng không.",
|
||||
"mustBeETH": "Một trong những đầu vào phải là ETH.",
|
||||
"enterCurrencyOrLabelCont": "Nhập giá trị {{ inputCurrency }} hoặc {{ label }} để tiếp tục.",
|
||||
"youAreAdding": "Bạn đang thêm",
|
||||
"and": "và",
|
||||
"intoPool": "vào nhóm thanh khoản.",
|
||||
"outPool": "từ nhóm thanh khoản.",
|
||||
"youWillMint": "Bạn sẽ đúc tiền",
|
||||
"liquidityTokens": "đồng thanh khoản.",
|
||||
"totalSupplyIs": "Tổng cung hiện tại của đồng thanh khoản là",
|
||||
"youAreSettingExRate": "Bạn đang đặt tỷ giá hối đoái ban đầu thành",
|
||||
"totalSupplyIs0": "Tổng cung hiện tại của đồng thanh khoản là 0.",
|
||||
"tokenWorth": "Tại tỷ giá hối đoái hiện tại, giá trị đồng token của quỹ là",
|
||||
"firstLiquidity": "Bạn là người đầu tiên thêm thanh khoản!",
|
||||
"initialExchangeRate": "Tỷ giá hối đoái ban đầu sẽ được thiết lập dựa trên tiền gửi của bạn. Vui lòng đảm bảo rằng tiền gửi ETH và {{ label }} của bạn có cùng giá trị tiền định danh.",
|
||||
"removeLiquidity": "Loại bỏ thanh khoản",
|
||||
"poolTokens": "Đồng tiền ảo của quỹ",
|
||||
"enterLabelCont": "Nhập giá trị {{ label }} để tiếp tục",
|
||||
"youAreRemoving": "Bạn đang loại bỏ giữa",
|
||||
"youWillRemove": "Bạn sẽ loại bỏ",
|
||||
"createExchange": "Tạo giao dịch",
|
||||
"invalidTokenAddress": "Địa chỉ đồng tiền điện tử không hợp lệ",
|
||||
"exchangeExists": "{{ label }} Giao dịch đã tồn tại!",
|
||||
"invalidSymbol": "Biểu tượng không hợp lệ",
|
||||
"invalidDecimals": "Số thập phân không hợp lệ",
|
||||
"tokenAddress": "Địa chỉ đồng tiền điện tử",
|
||||
"label": "Nhãn",
|
||||
"name": "Tên",
|
||||
"symbol": "Biểu tượng",
|
||||
"decimals": "Số thập phân",
|
||||
"enterTokenCont": "Nhập địa chỉ đồng tiền ảo để tiếp tục",
|
||||
"priceChange": "Trượt giá dự kiến",
|
||||
"forAtLeast": "cho ít nhất "
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
{
|
||||
"noWallet": "未发现以太钱包",
|
||||
"wrongNetwork": "网络错误",
|
||||
"switchNetwork": "请切换到 {{ correctNetwork }}",
|
||||
"installWeb3MobileBrowser": "请从支持web3的移动端浏览器,如 Trust Wallet 或 Coinbase Wallet 访问。",
|
||||
"installMetamask": "请从安装了 Metamask 插件的 Chrome 或 Brave 访问。",
|
||||
"disconnected": "未连接",
|
||||
"swap": "兑换",
|
||||
"send": "发送",
|
||||
"pool": "资金池",
|
||||
"betaWarning": "项目尚处于beta阶段。使用需自行承担风险。",
|
||||
"input": "输入",
|
||||
"output": "输出",
|
||||
"estimated": "估计",
|
||||
"balance": "余额: {{ balanceInput }}",
|
||||
"unlock": "解锁",
|
||||
"pending": "处理中",
|
||||
"selectToken": "选择通证",
|
||||
"searchOrPaste": "搜索通证或粘贴地址",
|
||||
"noExchange": "未找到交易所",
|
||||
"exchangeRate": "兑换率",
|
||||
"enterValueCont": "输入{{ missingCurrencyValue }}值并继续。",
|
||||
"selectTokenCont": "选取通证继续。",
|
||||
"noLiquidity": "没有流动金。",
|
||||
"unlockTokenCont": "请解锁通证并继续。",
|
||||
"transactionDetails": "交易明细",
|
||||
"hideDetails": "隐藏明细",
|
||||
"youAreSelling": "你正在出售",
|
||||
"orTransFail": "或交易失败。",
|
||||
"youWillReceive": "你将至少收到",
|
||||
"youAreBuying": "你正在购买",
|
||||
"itWillCost": "它将至少花费",
|
||||
"insufficientBalance": "余额不足",
|
||||
"inputNotValid": "无效的输入值",
|
||||
"differentToken": "必须是不同的通证。",
|
||||
"noRecipient": "输入接收钱包地址。",
|
||||
"invalidRecipient": "请输入有效的收钱地址。",
|
||||
"recipientAddress": "接收地址",
|
||||
"youAreSending": "你正在发送",
|
||||
"willReceive": "将至少收到",
|
||||
"to": "至",
|
||||
"addLiquidity": "添加流动金",
|
||||
"deposit": "存入",
|
||||
"currentPoolSize": "当前资金池大小",
|
||||
"yourPoolShare": "你的资金池份额",
|
||||
"noZero": "金额不能为零。",
|
||||
"mustBeETH": "输入中必须有一个是 ETH。",
|
||||
"enterCurrencyOrLabelCont": "输入 {{ inputCurrency }} 或 {{ label }} 值并继续。",
|
||||
"youAreAdding": "你将添加",
|
||||
"and": "和",
|
||||
"intoPool": "入流动资金池。",
|
||||
"outPool": "出流动资金池。",
|
||||
"youWillMint": "你将铸造",
|
||||
"liquidityTokens": "流动通证。",
|
||||
"totalSupplyIs": "当前流动通证的总量是",
|
||||
"youAreSettingExRate": "你将初始兑换率设置为",
|
||||
"totalSupplyIs0": "当前流动通证的总量是0。",
|
||||
"tokenWorth": "当前兑换率下,每个资金池通证价值",
|
||||
"firstLiquidity": "你是第一个添加流动金的人!",
|
||||
"initialExchangeRate": "初始兑换率将由你的存入情况决定。请确保你存入的 ETH 和 {{ label }} 具有相同的总市值。",
|
||||
"removeLiquidity": "删除流动金",
|
||||
"poolTokens": "资金池通证",
|
||||
"enterLabelCont": "输入 {{ label }} 值并继续。",
|
||||
"youAreRemoving": "你正在移除",
|
||||
"youWillRemove": "你将移除",
|
||||
"createExchange": "创建交易所",
|
||||
"invalidTokenAddress": "通证地址无效",
|
||||
"exchangeExists": "{{ label }} 交易所已存在!",
|
||||
"invalidSymbol": "通证符号无效",
|
||||
"invalidDecimals": "小数位数无效",
|
||||
"tokenAddress": "通证地址",
|
||||
"label": "通证符号",
|
||||
"decimals": "小数位数",
|
||||
"enterTokenCont": "输入通证地址并继续"
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
{
|
||||
"noWallet": "未偵測到以太坊錢包",
|
||||
"wrongNetwork": "你位在錯誤的網路",
|
||||
"switchNetwork": "請切換到 {{ correctNetwork }}",
|
||||
"installWeb3MobileBrowser": "請安裝含有 web3 瀏覽器的手機錢包,如 Trust Wallet 或 Coinbase Wallet。",
|
||||
"installMetamask": "請使用 Chrome 或 Brave 瀏覽器安裝 Metamask。",
|
||||
"disconnected": "未連接",
|
||||
"swap": "兌換",
|
||||
"send": "發送",
|
||||
"pool": "資金池",
|
||||
"betaWarning": "本產品仍在測試階段。使用者需自負風險。",
|
||||
"input": "輸入",
|
||||
"output": "輸出",
|
||||
"estimated": "估計",
|
||||
"balance": "餘額: {{ balanceInput }}",
|
||||
"unlock": "解鎖",
|
||||
"pending": "處理中",
|
||||
"selectToken": "選擇代幣",
|
||||
"searchOrPaste": "選擇代幣或輸入地址",
|
||||
"noExchange": "找不到交易所",
|
||||
"exchangeRate": "匯率",
|
||||
"enterValueCont": "輸入 {{ missingCurrencyValue }} 以繼續。",
|
||||
"selectTokenCont": "選擇代幣以繼續。",
|
||||
"noLiquidity": "沒有流動性資金。",
|
||||
"unlockTokenCont": "解鎖代幣以繼續。",
|
||||
"transactionDetails": "交易明細",
|
||||
"hideDetails": "隱藏明細",
|
||||
"youAreSelling": "你正在出售",
|
||||
"orTransFail": "或交易失敗。",
|
||||
"youWillReceive": "你將至少收到",
|
||||
"youAreBuying": "你正在購買",
|
||||
"itWillCost": "這將花費至多",
|
||||
"insufficientBalance": "餘額不足",
|
||||
"inputNotValid": "無效的輸入值",
|
||||
"differentToken": "必須是不同的代幣。",
|
||||
"noRecipient": "請輸入收款人錢包地址。",
|
||||
"invalidRecipient": "請輸入有效的錢包地址。",
|
||||
"recipientAddress": "收款人錢包地址",
|
||||
"youAreSending": "你正在發送",
|
||||
"willReceive": "將至少收到",
|
||||
"to": "至",
|
||||
"addLiquidity": "增加流動性資金",
|
||||
"deposit": "存入",
|
||||
"currentPoolSize": "目前的資金池總量",
|
||||
"yourPoolShare": "你在資金池中的佔比",
|
||||
"noZero": "金額不能為零。",
|
||||
"mustBeETH": "輸入中必須包含 ETH。",
|
||||
"enterCurrencyOrLabelCont": "輸入 {{ inputCurrency }} 或 {{ label }} 以繼續。",
|
||||
"youAreAdding": "你將把",
|
||||
"and": "和",
|
||||
"intoPool": "加入資金池。",
|
||||
"outPool": "領出資金池。",
|
||||
"youWillMint": "你將產生",
|
||||
"liquidityTokens": "流動性代幣。",
|
||||
"totalSupplyIs": "目前流動性代幣供給總量為",
|
||||
"youAreSettingExRate": "初始的匯率將被設定為",
|
||||
"totalSupplyIs0": "目前流動性代幣供給為零。",
|
||||
"tokenWorth": "依據目前的匯率,每個流動性代幣價值",
|
||||
"firstLiquidity": "您是第一個提供流動性資金的人!",
|
||||
"initialExchangeRate": "初始的匯率將取決於你存入的資金。請確保存入的 ETH 和 {{ label }} 的價值相等。",
|
||||
"removeLiquidity": "領出流動性資金",
|
||||
"poolTokens": "資金池代幣",
|
||||
"enterLabelCont": "輸入 {{ label }} 以繼續。",
|
||||
"youAreRemoving": "您正在移除",
|
||||
"youWillRemove": "您即將移除",
|
||||
"createExchange": "創建交易所",
|
||||
"invalidTokenAddress": "無效的代幣地址",
|
||||
"exchangeExists": "{{ label }} 的交易所已經存在!",
|
||||
"invalidSymbol": "代幣符號錯誤",
|
||||
"invalidDecimals": "小數位數錯誤",
|
||||
"tokenAddress": "代幣地址",
|
||||
"label": "代幣符號",
|
||||
"decimals": "小數位數",
|
||||
"enterTokenCont": "輸入代幣地址"
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
"background_color": "#fff",
|
||||
"display": "standalone",
|
||||
"homepage_url": "https://app.uniswap.org",
|
||||
"providedBy": { "name": "Uniswap", "url": "https://uniswap.org" },
|
||||
"icons": [
|
||||
{
|
||||
"src": "./images/192x192_App_Icon.png",
|
||||
@@ -18,6 +19,8 @@
|
||||
],
|
||||
"orientation": "portrait",
|
||||
"name": "Uniswap",
|
||||
"description": "Swap or provide liquidity on the Uniswap Protocol",
|
||||
"iconPath": "./images/256x256_App_Icon_Pink.svg",
|
||||
"short_name": "Uniswap",
|
||||
"start_url": ".",
|
||||
"theme_color": "#ff007a"
|
||||
|
||||
61
src/abis/argent-wallet-contract.json
Normal file
@@ -0,0 +1,61 @@
|
||||
[
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "data",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "_transactions",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "wc_multiCall",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bytes[]",
|
||||
"name": "",
|
||||
"type": "bytes[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "_msgHash",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "_signature",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "isValidSignature",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bytes4",
|
||||
"name": "",
|
||||
"type": "bytes4"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
@@ -1,55 +0,0 @@
|
||||
[
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "_factoryV1",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "_router",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "token",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amountTokenMin",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amountETHMin",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "deadline",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "migrate",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"stateMutability": "payable",
|
||||
"type": "receive"
|
||||
}
|
||||
]
|
||||
@@ -1,330 +0,0 @@
|
||||
[
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "target",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "callData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Multicall2.Call[]",
|
||||
"name": "calls",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "aggregate",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "blockNumber",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes[]",
|
||||
"name": "returnData",
|
||||
"type": "bytes[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "target",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "callData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Multicall2.Call[]",
|
||||
"name": "calls",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "blockAndAggregate",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "blockNumber",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "blockHash",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "success",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "returnData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Multicall2.Result[]",
|
||||
"name": "returnData",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "blockNumber",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "getBlockHash",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "blockHash",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "success",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "returnData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Multicall2.Result[]",
|
||||
"name": "returnData",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getBlockNumber",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "blockNumber",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getCurrentBlockCoinbase",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "coinbase",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getCurrentBlockDifficulty",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "difficulty",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getCurrentBlockGasLimit",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "gaslimit",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getCurrentBlockTimestamp",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "timestamp",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "addr",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "getEthBalance",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "balance",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getLastBlockHash",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "blockHash",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "requireSuccess",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "target",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "callData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Multicall2.Call[]",
|
||||
"name": "calls",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "tryAggregate",
|
||||
"outputs": [
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "success",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "returnData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Multicall2.Result[]",
|
||||
"name": "returnData",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "requireSuccess",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "target",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "callData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Multicall2.Call[]",
|
||||
"name": "calls",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "tryBlockAndAggregate",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "blockNumber",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "blockHash",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "success",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "returnData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct Multicall2.Result[]",
|
||||
"name": "returnData",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
@@ -1,165 +0,0 @@
|
||||
[
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"components": [
|
||||
{ "internalType": "address", "name": "target", "type": "address" },
|
||||
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
|
||||
],
|
||||
"internalType": "struct Multicall2.Call[]",
|
||||
"name": "calls",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "aggregate",
|
||||
"outputs": [
|
||||
{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" },
|
||||
{ "internalType": "bytes[]", "name": "returnData", "type": "bytes[]" }
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"components": [
|
||||
{ "internalType": "address", "name": "target", "type": "address" },
|
||||
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
|
||||
],
|
||||
"internalType": "struct Multicall2.Call[]",
|
||||
"name": "calls",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "blockAndAggregate",
|
||||
"outputs": [
|
||||
{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" },
|
||||
{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" },
|
||||
{
|
||||
"components": [
|
||||
{ "internalType": "bool", "name": "success", "type": "bool" },
|
||||
{ "internalType": "bytes", "name": "returnData", "type": "bytes" }
|
||||
],
|
||||
"internalType": "struct Multicall2.Result[]",
|
||||
"name": "returnData",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" }],
|
||||
"name": "getBlockHash",
|
||||
"outputs": [{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getBlockNumber",
|
||||
"outputs": [{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getCurrentBlockCoinbase",
|
||||
"outputs": [{ "internalType": "address", "name": "coinbase", "type": "address" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getCurrentBlockDifficulty",
|
||||
"outputs": [{ "internalType": "uint256", "name": "difficulty", "type": "uint256" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getCurrentBlockGasLimit",
|
||||
"outputs": [{ "internalType": "uint256", "name": "gaslimit", "type": "uint256" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getCurrentBlockTimestamp",
|
||||
"outputs": [{ "internalType": "uint256", "name": "timestamp", "type": "uint256" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [{ "internalType": "address", "name": "addr", "type": "address" }],
|
||||
"name": "getEthBalance",
|
||||
"outputs": [{ "internalType": "uint256", "name": "balance", "type": "uint256" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "getLastBlockHash",
|
||||
"outputs": [{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{ "internalType": "bool", "name": "requireSuccess", "type": "bool" },
|
||||
{
|
||||
"components": [
|
||||
{ "internalType": "address", "name": "target", "type": "address" },
|
||||
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
|
||||
],
|
||||
"internalType": "struct Multicall2.Call[]",
|
||||
"name": "calls",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "tryAggregate",
|
||||
"outputs": [
|
||||
{
|
||||
"components": [
|
||||
{ "internalType": "bool", "name": "success", "type": "bool" },
|
||||
{ "internalType": "bytes", "name": "returnData", "type": "bytes" }
|
||||
],
|
||||
"internalType": "struct Multicall2.Result[]",
|
||||
"name": "returnData",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{ "internalType": "bool", "name": "requireSuccess", "type": "bool" },
|
||||
{
|
||||
"components": [
|
||||
{ "internalType": "address", "name": "target", "type": "address" },
|
||||
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
|
||||
],
|
||||
"internalType": "struct Multicall2.Call[]",
|
||||
"name": "calls",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"name": "tryBlockAndAggregate",
|
||||
"outputs": [
|
||||
{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" },
|
||||
{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" },
|
||||
{
|
||||
"components": [
|
||||
{ "internalType": "bool", "name": "success", "type": "bool" },
|
||||
{ "internalType": "bytes", "name": "returnData", "type": "bytes" }
|
||||
],
|
||||
"internalType": "struct Multicall2.Result[]",
|
||||
"name": "returnData",
|
||||
"type": "tuple[]"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
@@ -1,6 +0,0 @@
|
||||
import { Interface } from '@ethersproject/abi'
|
||||
import { abi as STAKING_REWARDS_ABI } from '@uniswap/liquidity-staker/build/StakingRewards.json'
|
||||
|
||||
const STAKING_REWARDS_INTERFACE = new Interface(STAKING_REWARDS_ABI)
|
||||
|
||||
export { STAKING_REWARDS_INTERFACE }
|
||||
@@ -1,471 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "Transfer",
|
||||
"inputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_from",
|
||||
"indexed": true
|
||||
},
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_to",
|
||||
"indexed": true
|
||||
},
|
||||
{
|
||||
"type": "uint256",
|
||||
"name": "_tokenId",
|
||||
"indexed": true
|
||||
}
|
||||
],
|
||||
"anonymous": false,
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"name": "Approval",
|
||||
"inputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_owner",
|
||||
"indexed": true
|
||||
},
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_approved",
|
||||
"indexed": true
|
||||
},
|
||||
{
|
||||
"type": "uint256",
|
||||
"name": "_tokenId",
|
||||
"indexed": true
|
||||
}
|
||||
],
|
||||
"anonymous": false,
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"name": "ApprovalForAll",
|
||||
"inputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_owner",
|
||||
"indexed": true
|
||||
},
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_operator",
|
||||
"indexed": true
|
||||
},
|
||||
{
|
||||
"type": "bool",
|
||||
"name": "_approved",
|
||||
"indexed": false
|
||||
}
|
||||
],
|
||||
"anonymous": false,
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"outputs": [],
|
||||
"inputs": [],
|
||||
"constant": false,
|
||||
"payable": false,
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"name": "tokenURI",
|
||||
"outputs": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "out"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"type": "uint256",
|
||||
"name": "_tokenId"
|
||||
}
|
||||
],
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "22405"
|
||||
},
|
||||
{
|
||||
"name": "tokenByIndex",
|
||||
"outputs": [
|
||||
{
|
||||
"type": "uint256",
|
||||
"name": "out"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"type": "uint256",
|
||||
"name": "_index"
|
||||
}
|
||||
],
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "631"
|
||||
},
|
||||
{
|
||||
"name": "tokenOfOwnerByIndex",
|
||||
"outputs": [
|
||||
{
|
||||
"type": "uint256",
|
||||
"name": "out"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_owner"
|
||||
},
|
||||
{
|
||||
"type": "uint256",
|
||||
"name": "_index"
|
||||
}
|
||||
],
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "1248"
|
||||
},
|
||||
{
|
||||
"name": "transferFrom",
|
||||
"outputs": [],
|
||||
"inputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_from"
|
||||
},
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_to"
|
||||
},
|
||||
{
|
||||
"type": "uint256",
|
||||
"name": "_tokenId"
|
||||
}
|
||||
],
|
||||
"constant": false,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "259486"
|
||||
},
|
||||
{
|
||||
"name": "safeTransferFrom",
|
||||
"outputs": [],
|
||||
"inputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_from"
|
||||
},
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_to"
|
||||
},
|
||||
{
|
||||
"type": "uint256",
|
||||
"name": "_tokenId"
|
||||
}
|
||||
],
|
||||
"constant": false,
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"name": "safeTransferFrom",
|
||||
"outputs": [],
|
||||
"inputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_from"
|
||||
},
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_to"
|
||||
},
|
||||
{
|
||||
"type": "uint256",
|
||||
"name": "_tokenId"
|
||||
},
|
||||
{
|
||||
"type": "bytes",
|
||||
"name": "_data"
|
||||
}
|
||||
],
|
||||
"constant": false,
|
||||
"payable": false,
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"name": "approve",
|
||||
"outputs": [],
|
||||
"inputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_approved"
|
||||
},
|
||||
{
|
||||
"type": "uint256",
|
||||
"name": "_tokenId"
|
||||
}
|
||||
],
|
||||
"constant": false,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "38422"
|
||||
},
|
||||
{
|
||||
"name": "setApprovalForAll",
|
||||
"outputs": [],
|
||||
"inputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_operator"
|
||||
},
|
||||
{
|
||||
"type": "bool",
|
||||
"name": "_approved"
|
||||
}
|
||||
],
|
||||
"constant": false,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "38016"
|
||||
},
|
||||
{
|
||||
"name": "mint",
|
||||
"outputs": [
|
||||
{
|
||||
"type": "bool",
|
||||
"name": "out"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_to"
|
||||
}
|
||||
],
|
||||
"constant": false,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "182636"
|
||||
},
|
||||
{
|
||||
"name": "changeMinter",
|
||||
"outputs": [],
|
||||
"inputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_minter"
|
||||
}
|
||||
],
|
||||
"constant": false,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "35897"
|
||||
},
|
||||
{
|
||||
"name": "changeURI",
|
||||
"outputs": [],
|
||||
"inputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "_newURI"
|
||||
}
|
||||
],
|
||||
"constant": false,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "35927"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"outputs": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "out"
|
||||
}
|
||||
],
|
||||
"inputs": [],
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "6612"
|
||||
},
|
||||
{
|
||||
"name": "symbol",
|
||||
"outputs": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "out"
|
||||
}
|
||||
],
|
||||
"inputs": [],
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "6642"
|
||||
},
|
||||
{
|
||||
"name": "totalSupply",
|
||||
"outputs": [
|
||||
{
|
||||
"type": "uint256",
|
||||
"name": "out"
|
||||
}
|
||||
],
|
||||
"inputs": [],
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "873"
|
||||
},
|
||||
{
|
||||
"name": "minter",
|
||||
"outputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "out"
|
||||
}
|
||||
],
|
||||
"inputs": [],
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "903"
|
||||
},
|
||||
{
|
||||
"name": "socks",
|
||||
"outputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "out",
|
||||
"unit": "Socks"
|
||||
}
|
||||
],
|
||||
"inputs": [],
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "933"
|
||||
},
|
||||
{
|
||||
"name": "newURI",
|
||||
"outputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "out"
|
||||
}
|
||||
],
|
||||
"inputs": [],
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "963"
|
||||
},
|
||||
{
|
||||
"name": "ownerOf",
|
||||
"outputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "out"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"type": "uint256",
|
||||
"name": "arg0"
|
||||
}
|
||||
],
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "1126"
|
||||
},
|
||||
{
|
||||
"name": "balanceOf",
|
||||
"outputs": [
|
||||
{
|
||||
"type": "uint256",
|
||||
"name": "out"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "arg0"
|
||||
}
|
||||
],
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "1195"
|
||||
},
|
||||
{
|
||||
"name": "getApproved",
|
||||
"outputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "out"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"type": "uint256",
|
||||
"name": "arg0"
|
||||
}
|
||||
],
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "1186"
|
||||
},
|
||||
{
|
||||
"name": "isApprovedForAll",
|
||||
"outputs": [
|
||||
{
|
||||
"type": "bool",
|
||||
"name": "out"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"type": "address",
|
||||
"name": "arg0"
|
||||
},
|
||||
{
|
||||
"type": "address",
|
||||
"name": "arg1"
|
||||
}
|
||||
],
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "1415"
|
||||
},
|
||||
{
|
||||
"name": "supportsInterface",
|
||||
"outputs": [
|
||||
{
|
||||
"type": "bool",
|
||||
"name": "out"
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"type": "bytes32",
|
||||
"name": "arg0"
|
||||
}
|
||||
],
|
||||
"constant": true,
|
||||
"payable": false,
|
||||
"type": "function",
|
||||
"gas": "1246"
|
||||
}
|
||||
]
|
||||
|
Before Width: | Height: | Size: 883 KiB After Width: | Height: | Size: 221 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 670 KiB |
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 413 KiB |
181
src/assets/svg/arbitrum_logo.svg
Normal file
@@ -0,0 +1,181 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 470.287 514.251" enable-background="new 0 0 470.287 514.251" xml:space="preserve">
|
||||
<g id="Background">
|
||||
</g>
|
||||
<g id="Logos_and_symbols">
|
||||
<g id="SYMBOL_VER_3">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_3_3_">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_4">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_4_1_">
|
||||
<g id="SYMBOL_VER_4_3_">
|
||||
</g>
|
||||
</g>
|
||||
<g id="SYMBOL_VER_5_1_">
|
||||
</g>
|
||||
<g id="off_2_1_">
|
||||
</g>
|
||||
<g id="VER_3_1_">
|
||||
<g id="SYMBOL_VER_2_1_">
|
||||
</g>
|
||||
</g>
|
||||
<g id="VER_3">
|
||||
<g id="SYMBOL_VER_2">
|
||||
</g>
|
||||
</g>
|
||||
<g id="off_2">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_5">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_1">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_1_1_">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_1-1_3_">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_1-1_2_">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_1-1">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_1-1_1_">
|
||||
<g id="_x31_-3">
|
||||
</g>
|
||||
<g id="Symbol_-_Original_14_">
|
||||
<path fill="#2D374B" d="M291.134,237.469l35.654-60.5l96.103,149.684l0.046,28.727l-0.313-197.672
|
||||
c-0.228-4.832-2.794-9.252-6.887-11.859L242.715,46.324c-4.045-1.99-9.18-1.967-13.22,0.063c-0.546,0.272-1.06,0.57-1.548,0.895
|
||||
l-0.604,0.379L59.399,144.983l-0.651,0.296c-0.838,0.385-1.686,0.875-2.48,1.444c-3.185,2.283-5.299,5.66-5.983,9.448
|
||||
c-0.103,0.574-0.179,1.158-0.214,1.749l0.264,161.083l89.515-138.745c11.271-18.397,35.825-24.323,58.62-24.001l26.753,0.706
|
||||
L67.588,409.765l18.582,10.697L245.692,157.22l70.51-0.256L157.091,426.849l66.306,38.138l7.922,4.556
|
||||
c3.351,1.362,7.302,1.431,10.681,0.21l175.453-101.678l-33.544,19.438L291.134,237.469z M304.736,433.395l-66.969-105.108
|
||||
l40.881-69.371l87.952,138.628L304.736,433.395z"/>
|
||||
<polygon fill="#28A0F0" points="237.768,328.286 304.736,433.395 366.601,397.543 278.648,258.915 "/>
|
||||
<path fill="#28A0F0" d="M422.937,355.379l-0.046-28.727l-96.103-149.684l-35.654,60.5l92.774,150.043l33.544-19.438
|
||||
c3.29-2.673,5.281-6.594,5.49-10.825L422.937,355.379z"/>
|
||||
<path fill="#FFFFFF" d="M20.219,382.469l47.369,27.296l157.634-252.801l-26.753-0.706c-22.795-0.322-47.35,5.604-58.62,24.001
|
||||
L50.334,319.004l-30.115,46.271V382.469z"/>
|
||||
<polygon fill="#FFFFFF" points="316.202,156.964 245.692,157.22 86.17,420.462 141.928,452.565 157.091,426.849 "/>
|
||||
<path fill="#96BEDC" d="M452.65,156.601c-0.59-14.746-8.574-28.245-21.08-36.104L256.28,19.692
|
||||
c-12.371-6.229-27.825-6.237-40.218-0.004c-1.465,0.739-170.465,98.752-170.465,98.752c-2.339,1.122-4.592,2.458-6.711,3.975
|
||||
c-11.164,8.001-17.969,20.435-18.668,34.095v208.765l30.115-46.271L50.07,157.921c0.035-0.589,0.109-1.169,0.214-1.741
|
||||
c0.681-3.79,2.797-7.171,5.983-9.456c0.795-0.569,172.682-100.064,173.228-100.337c4.04-2.029,9.175-2.053,13.22-0.063
|
||||
l173.022,99.523c4.093,2.607,6.659,7.027,6.887,11.859v199.542c-0.209,4.231-1.882,8.152-5.172,10.825l-33.544,19.438
|
||||
l-17.308,10.031l-61.864,35.852l-62.737,36.357c-3.379,1.221-7.33,1.152-10.681-0.21l-74.228-42.693l-15.163,25.717
|
||||
l66.706,38.406c2.206,1.255,4.171,2.367,5.784,3.272c2.497,1.4,4.199,2.337,4.8,2.629c4.741,2.303,11.563,3.643,17.71,3.643
|
||||
c5.636,0,11.132-1.035,16.332-3.072l182.225-105.531c10.459-8.104,16.612-20.325,17.166-33.564V156.601z"/>
|
||||
</g>
|
||||
<g id="Symbol_-_Original_13_">
|
||||
</g>
|
||||
<g id="Symbol_-_Original_6_">
|
||||
</g>
|
||||
<g id="Symbol_-_Original_4_">
|
||||
</g>
|
||||
<g id="One_color_version_-_White_3_">
|
||||
<g id="Symbol_-_Original_15_">
|
||||
</g>
|
||||
</g>
|
||||
<g id="One_color_version_-_White">
|
||||
<g id="Symbol_-_Original">
|
||||
</g>
|
||||
</g>
|
||||
<g id="Symbol_-_Monochromatic_3_">
|
||||
<g id="_x33__7_">
|
||||
</g>
|
||||
</g>
|
||||
<g id="Symbol_-_Monochromatic">
|
||||
<g id="_x33__3_">
|
||||
</g>
|
||||
</g>
|
||||
<g id="_x33__2_">
|
||||
</g>
|
||||
<g id="_x33__1_">
|
||||
</g>
|
||||
<g id="_x33_">
|
||||
</g>
|
||||
<g id="Symbol_-_Original_10_">
|
||||
</g>
|
||||
<g id="Symbol_-_Original_1_">
|
||||
</g>
|
||||
<g id="Symbol_-_Original_2_">
|
||||
</g>
|
||||
<g id="_x34__1_">
|
||||
</g>
|
||||
<g id="Symbol_-_Monochromatic_2_">
|
||||
<g id="_x33__6_">
|
||||
</g>
|
||||
</g>
|
||||
<g id="One_color_version_-_White_2_">
|
||||
<g id="Symbol_-_Original_11_">
|
||||
</g>
|
||||
</g>
|
||||
<g id="Symbol_-_Original_5_">
|
||||
<g id="Symbol_-_Original_12_">
|
||||
</g>
|
||||
</g>
|
||||
<g id="One_color_version_-_White_1_">
|
||||
<g id="Symbol_-_Original_9_">
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="SYMBOL_VER_1_2_">
|
||||
<g id="SYMBOL_VER_2_4_">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_2-1-1_1_">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_2-2-1_1_">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_2-3-1_4_">
|
||||
</g>
|
||||
<g id="New_Symbol_1_">
|
||||
<g id="SYMBOL_VER_2-3-1_3_">
|
||||
</g>
|
||||
</g>
|
||||
<g id="New_Symbol">
|
||||
<g id="SYMBOL_VER_2-3-1_1_">
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="SYMBOL_VER_2_2_">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_4_2_">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_3_2_">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_3_1_">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_1-1-1_1_">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_1-1-1">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_1-1-1_2_2_">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_1-1-1_2">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_1-1-1_2_1_">
|
||||
</g>
|
||||
<g id="Symbol_-_Original_7_">
|
||||
</g>
|
||||
<g id="Symbol_-_Original_8_">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_2-1-1">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_2-2-1">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_2-3-1">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_5-1_1_">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_5-1">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_5-2_1_">
|
||||
</g>
|
||||
<g id="SYMBOL_VER_5-2">
|
||||
</g>
|
||||
<g id="Symbol_-_Monochromatic_1_">
|
||||
<g id="_x33__4_">
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
5
src/assets/svg/optimism_logo.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -4,6 +4,7 @@ import useCopyClipboard from '../../hooks/useCopyClipboard'
|
||||
|
||||
import { LinkStyledButton } from '../../theme'
|
||||
import { CheckCircle, Copy } from 'react-feather'
|
||||
import { Trans } from '@lingui/macro'
|
||||
|
||||
const CopyIcon = styled(LinkStyledButton)`
|
||||
color: ${({ theme }) => theme.text3};
|
||||
@@ -33,7 +34,9 @@ export default function CopyHelper(props: { toCopy: string; children?: React.Rea
|
||||
{isCopied ? (
|
||||
<TransactionStatusText>
|
||||
<CheckCircle size={'16'} />
|
||||
<TransactionStatusText>Copied</TransactionStatusText>
|
||||
<TransactionStatusText>
|
||||
<Trans>Copied</Trans>
|
||||
</TransactionStatusText>
|
||||
</TransactionStatusText>
|
||||
) : (
|
||||
<TransactionStatusText>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import { CheckCircle, Triangle } from 'react-feather'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { getEtherscanLink } from '../../utils'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { ExternalLink } from '../../theme'
|
||||
import { useAllTransactions } from '../../state/transactions/hooks'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { RowFixed } from '../Row'
|
||||
import Loader from '../Loader'
|
||||
|
||||
@@ -49,7 +48,11 @@ export default function Transaction({ hash }: { hash: string }) {
|
||||
|
||||
return (
|
||||
<TransactionWrapper>
|
||||
<TransactionState href={getEtherscanLink(chainId, hash, 'transaction')} pending={pending} success={success}>
|
||||
<TransactionState
|
||||
href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}
|
||||
pending={pending}
|
||||
success={success}
|
||||
>
|
||||
<RowFixed>
|
||||
<TransactionStatusText>{summary ?? hash} ↗</TransactionStatusText>
|
||||
</RowFixed>
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import React, { useCallback, useContext } from 'react'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import styled, { ThemeContext } from 'styled-components'
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { AppDispatch } from '../../state'
|
||||
import { useCallback, useContext } from 'react'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
import { SUPPORTED_WALLETS } from '../../constants/wallet'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { clearAllTransactions } from '../../state/transactions/actions'
|
||||
import { shortenAddress } from '../../utils'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { AutoRow } from '../Row'
|
||||
import Copy from './Copy'
|
||||
import Transaction from './Transaction'
|
||||
|
||||
import { SUPPORTED_WALLETS } from '../../constants'
|
||||
import { ReactComponent as Close } from '../../assets/images/x.svg'
|
||||
import { getEtherscanLink } from '../../utils'
|
||||
import { injected, walletconnect, walletlink, fortmatic, portis } from '../../connectors'
|
||||
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
|
||||
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
|
||||
@@ -21,6 +19,8 @@ import Identicon from '../Identicon'
|
||||
import { ButtonSecondary } from '../Button'
|
||||
import { ExternalLink as LinkIcon } from 'react-feather'
|
||||
import { ExternalLink, LinkStyledButton, TYPE } from '../../theme'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { useAppDispatch } from 'state/hooks'
|
||||
|
||||
const HeaderRow = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap};
|
||||
@@ -226,7 +226,7 @@ export default function AccountDetails({
|
||||
}: AccountDetailsProps) {
|
||||
const { chainId, account, connector } = useActiveWeb3React()
|
||||
const theme = useContext(ThemeContext)
|
||||
const dispatch = useDispatch<AppDispatch>()
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
function formatConnectorName() {
|
||||
const { ethereum } = window
|
||||
@@ -237,7 +237,11 @@ export default function AccountDetails({
|
||||
SUPPORTED_WALLETS[k].connector === connector && (connector !== injected || isMetaMask === (k === 'METAMASK'))
|
||||
)
|
||||
.map((k) => SUPPORTED_WALLETS[k].name)[0]
|
||||
return <WalletName>Connected with {name}</WalletName>
|
||||
return (
|
||||
<WalletName>
|
||||
<Trans>Connected with {name}</Trans>
|
||||
</WalletName>
|
||||
)
|
||||
}
|
||||
|
||||
function getStatusIcon() {
|
||||
@@ -250,32 +254,32 @@ export default function AccountDetails({
|
||||
} else if (connector === walletconnect) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<img src={WalletConnectIcon} alt={'wallet connect logo'} />
|
||||
<img src={WalletConnectIcon} alt={'WalletConnect logo'} />
|
||||
</IconWrapper>
|
||||
)
|
||||
} else if (connector === walletlink) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<img src={CoinbaseWalletIcon} alt={'coinbase wallet logo'} />
|
||||
<img src={CoinbaseWalletIcon} alt={'Coinbase Wallet logo'} />
|
||||
</IconWrapper>
|
||||
)
|
||||
} else if (connector === fortmatic) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<img src={FortmaticIcon} alt={'fortmatic logo'} />
|
||||
<img src={FortmaticIcon} alt={'Fortmatic logo'} />
|
||||
</IconWrapper>
|
||||
)
|
||||
} else if (connector === portis) {
|
||||
return (
|
||||
<>
|
||||
<IconWrapper size={16}>
|
||||
<img src={PortisIcon} alt={'portis logo'} />
|
||||
<img src={PortisIcon} alt={'Portis logo'} />
|
||||
<MainWalletAction
|
||||
onClick={() => {
|
||||
portis.portis.showPortis()
|
||||
}}
|
||||
>
|
||||
Show Portis
|
||||
<Trans>Show Portis</Trans>
|
||||
</MainWalletAction>
|
||||
</IconWrapper>
|
||||
</>
|
||||
@@ -294,7 +298,9 @@ export default function AccountDetails({
|
||||
<CloseIcon onClick={toggleWalletModal}>
|
||||
<CloseColor />
|
||||
</CloseIcon>
|
||||
<HeaderRow>Account</HeaderRow>
|
||||
<HeaderRow>
|
||||
<Trans>Account</Trans>
|
||||
</HeaderRow>
|
||||
<AccountSection>
|
||||
<YourAccount>
|
||||
<InfoCard>
|
||||
@@ -308,7 +314,7 @@ export default function AccountDetails({
|
||||
;(connector as any).close()
|
||||
}}
|
||||
>
|
||||
Disconnect
|
||||
<Trans>Disconnect</Trans>
|
||||
</WalletAction>
|
||||
)}
|
||||
<WalletAction
|
||||
@@ -317,7 +323,7 @@ export default function AccountDetails({
|
||||
openOptions()
|
||||
}}
|
||||
>
|
||||
Change
|
||||
<Trans>Change</Trans>
|
||||
</WalletAction>
|
||||
</div>
|
||||
</AccountGroupingRow>
|
||||
@@ -347,17 +353,21 @@ export default function AccountDetails({
|
||||
<div>
|
||||
{account && (
|
||||
<Copy toCopy={account}>
|
||||
<span style={{ marginLeft: '4px' }}>Copy Address</span>
|
||||
<span style={{ marginLeft: '4px' }}>
|
||||
<Trans>Copy Address</Trans>
|
||||
</span>
|
||||
</Copy>
|
||||
)}
|
||||
{chainId && account && (
|
||||
<AddressLink
|
||||
hasENS={!!ENSName}
|
||||
isENS={true}
|
||||
href={chainId && getEtherscanLink(chainId, ENSName, 'address')}
|
||||
href={getExplorerLink(chainId, ENSName, ExplorerDataType.ADDRESS)}
|
||||
>
|
||||
<LinkIcon size={16} />
|
||||
<span style={{ marginLeft: '4px' }}>View on Etherscan</span>
|
||||
<span style={{ marginLeft: '4px' }}>
|
||||
<Trans>View on Explorer</Trans>
|
||||
</span>
|
||||
</AddressLink>
|
||||
)}
|
||||
</div>
|
||||
@@ -369,17 +379,21 @@ export default function AccountDetails({
|
||||
<div>
|
||||
{account && (
|
||||
<Copy toCopy={account}>
|
||||
<span style={{ marginLeft: '4px' }}>Copy Address</span>
|
||||
<span style={{ marginLeft: '4px' }}>
|
||||
<Trans>Copy Address</Trans>
|
||||
</span>
|
||||
</Copy>
|
||||
)}
|
||||
{chainId && account && (
|
||||
<AddressLink
|
||||
hasENS={!!ENSName}
|
||||
isENS={false}
|
||||
href={getEtherscanLink(chainId, account, 'address')}
|
||||
href={getExplorerLink(chainId, account, ExplorerDataType.ADDRESS)}
|
||||
>
|
||||
<LinkIcon size={16} />
|
||||
<span style={{ marginLeft: '4px' }}>View on Etherscan</span>
|
||||
<span style={{ marginLeft: '4px' }}>
|
||||
<Trans>View on Explorer</Trans>
|
||||
</span>
|
||||
</AddressLink>
|
||||
)}
|
||||
</div>
|
||||
@@ -394,15 +408,21 @@ export default function AccountDetails({
|
||||
{!!pendingTransactions.length || !!confirmedTransactions.length ? (
|
||||
<LowerSection>
|
||||
<AutoRow mb={'1rem'} style={{ justifyContent: 'space-between' }}>
|
||||
<TYPE.body>Recent Transactions</TYPE.body>
|
||||
<LinkStyledButton onClick={clearAllTransactionsCallback}>(clear all)</LinkStyledButton>
|
||||
<TYPE.body>
|
||||
<Trans>Recent Transactions</Trans>
|
||||
</TYPE.body>
|
||||
<LinkStyledButton onClick={clearAllTransactionsCallback}>
|
||||
<Trans>(clear all)</Trans>
|
||||
</LinkStyledButton>
|
||||
</AutoRow>
|
||||
{renderTransactions(pendingTransactions)}
|
||||
{renderTransactions(confirmedTransactions)}
|
||||
</LowerSection>
|
||||
) : (
|
||||
<LowerSection>
|
||||
<TYPE.body color={theme.text1}>Your transactions will appear here...</TYPE.body>
|
||||
<TYPE.body color={theme.text1}>
|
||||
<Trans>Your transactions will appear here...</Trans>
|
||||
</TYPE.body>
|
||||
</LowerSection>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import React, { useContext, useCallback } from 'react'
|
||||
import styled, { ThemeContext } from 'styled-components'
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
import { useContext, useCallback, ReactNode } from 'react'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
import useENS from '../../hooks/useENS'
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { ExternalLink, TYPE } from '../../theme'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { RowBetween } from '../Row'
|
||||
import { getEtherscanLink } from '../../utils'
|
||||
|
||||
const InputPanel = styled.div`
|
||||
${({ theme }) => theme.flexColumnNoWrap}
|
||||
@@ -40,7 +41,7 @@ const Input = styled.input<{ error?: boolean }>`
|
||||
width: 0;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
transition: color 300ms ${({ error }) => (error ? 'step-end' : 'step-start')};
|
||||
color: ${({ error, theme }) => (error ? theme.red1 : theme.primary1)};
|
||||
color: ${({ error, theme }) => (error ? theme.red1 : theme.text1)};
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: 500;
|
||||
@@ -67,10 +68,16 @@ const Input = styled.input<{ error?: boolean }>`
|
||||
|
||||
export default function AddressInputPanel({
|
||||
id,
|
||||
className = 'recipient-address-input',
|
||||
label,
|
||||
placeholder,
|
||||
value,
|
||||
onChange,
|
||||
}: {
|
||||
id?: string
|
||||
className?: string
|
||||
label?: ReactNode
|
||||
placeholder?: string
|
||||
// the typed string value
|
||||
value: string
|
||||
// triggers whenever the typed value changes
|
||||
@@ -99,22 +106,25 @@ export default function AddressInputPanel({
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<TYPE.black color={theme.text2} fontWeight={500} fontSize={14}>
|
||||
Recipient
|
||||
{label ?? <Trans>Recipient</Trans>}
|
||||
</TYPE.black>
|
||||
{address && chainId && (
|
||||
<ExternalLink href={getEtherscanLink(chainId, name ?? address, 'address')} style={{ fontSize: '14px' }}>
|
||||
(View on Etherscan)
|
||||
<ExternalLink
|
||||
href={getExplorerLink(chainId, name ?? address, ExplorerDataType.ADDRESS)}
|
||||
style={{ fontSize: '14px' }}
|
||||
>
|
||||
<Trans>(View on Explorer)</Trans>
|
||||
</ExternalLink>
|
||||
)}
|
||||
</RowBetween>
|
||||
<Input
|
||||
className="recipient-address-input"
|
||||
className={className}
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
placeholder="Wallet Address or ENS name"
|
||||
placeholder={placeholder ?? t`Wallet Address or ENS name`}
|
||||
error={error}
|
||||
pattern="^(0x[a-fA-F0-9]{40})$"
|
||||
onChange={handleInput}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import { Story } from '@storybook/react/types-6-0'
|
||||
import React, { PropsWithChildren } from 'react'
|
||||
import Component, { BadgeProps, BadgeVariant } from './index'
|
||||
|
||||
export default {
|
||||
title: 'Badge',
|
||||
argTypes: {
|
||||
variant: {
|
||||
name: 'variant',
|
||||
type: { name: 'string', require: false },
|
||||
defaultValue: BadgeVariant.DEFAULT,
|
||||
description: 'badge variant',
|
||||
control: {
|
||||
type: 'select',
|
||||
options: Object.values(BadgeVariant),
|
||||
},
|
||||
},
|
||||
},
|
||||
args: {
|
||||
children: '🦄 UNISWAP 🦄',
|
||||
},
|
||||
}
|
||||
|
||||
const Template: Story<PropsWithChildren<BadgeProps>> = (args) => <Component {...args}>{args.children}</Component>
|
||||
|
||||
export const DefaultBadge = Template.bind({})
|
||||
DefaultBadge.args = {
|
||||
variant: BadgeVariant.DEFAULT,
|
||||
}
|
||||
|
||||
export const WarningBadge = Template.bind({})
|
||||
WarningBadge.args = {
|
||||
variant: BadgeVariant.WARNING,
|
||||
}
|
||||
|
||||
export const NegativeBadge = Template.bind({})
|
||||
NegativeBadge.args = {
|
||||
variant: BadgeVariant.NEGATIVE,
|
||||
}
|
||||
|
||||
export const PositiveBadge = Template.bind({})
|
||||
PositiveBadge.args = {
|
||||
variant: BadgeVariant.POSITIVE,
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
import React from 'react'
|
||||
|
||||
import Badge, { BadgeVariant } from 'components/Badge'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { MouseoverTooltip } from '../../components/Tooltip'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { AlertCircle } from 'react-feather'
|
||||
|
||||
const BadgeWrapper = styled.div`
|
||||
@@ -26,13 +24,6 @@ const ActiveDot = styled.span`
|
||||
margin-right: 4px;
|
||||
`
|
||||
|
||||
export const DarkBadge = styled.div`
|
||||
width: fit-content;
|
||||
border-radius: 8px;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
padding: 4px 6px;
|
||||
`
|
||||
|
||||
export default function RangeBadge({
|
||||
removed,
|
||||
inRange,
|
||||
@@ -40,35 +31,47 @@ export default function RangeBadge({
|
||||
removed: boolean | undefined
|
||||
inRange: boolean | undefined
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<BadgeWrapper>
|
||||
{removed ? (
|
||||
<MouseoverTooltip text={`Your position has 0 liquidity, and is not earning fees.`}>
|
||||
<MouseoverTooltip text={<Trans>Your position has 0 liquidity, and is not earning fees.</Trans>}>
|
||||
<Badge variant={BadgeVariant.DEFAULT}>
|
||||
<AlertCircle width={14} height={14} />
|
||||
|
||||
<BadgeText>{t('Inactive')}</BadgeText>
|
||||
<BadgeText>
|
||||
<Trans>Closed</Trans>
|
||||
</BadgeText>
|
||||
</Badge>
|
||||
</MouseoverTooltip>
|
||||
) : inRange ? (
|
||||
<MouseoverTooltip
|
||||
text={`The price of this pool is within your selected range. Your position is currently earning fees.`}
|
||||
text={
|
||||
<Trans>
|
||||
The price of this pool is within your selected range. Your position is currently earning fees.
|
||||
</Trans>
|
||||
}
|
||||
>
|
||||
<Badge variant={BadgeVariant.DEFAULT}>
|
||||
<ActiveDot />
|
||||
<BadgeText>{t('In range')}</BadgeText>
|
||||
<BadgeText>
|
||||
<Trans>In range</Trans>
|
||||
</BadgeText>
|
||||
</Badge>
|
||||
</MouseoverTooltip>
|
||||
) : (
|
||||
<MouseoverTooltip
|
||||
text={`The price of this pool is outside of your selected range. Your position is not currently earning fees.`}
|
||||
text={
|
||||
<Trans>
|
||||
The price of this pool is outside of your selected range. Your position is not currently earning fees.
|
||||
</Trans>
|
||||
}
|
||||
>
|
||||
<Badge variant={BadgeVariant.WARNING}>
|
||||
<AlertCircle width={14} height={14} />
|
||||
|
||||
<BadgeText>{t('Out of range')}</BadgeText>
|
||||
<BadgeText>
|
||||
<Trans>Out of range</Trans>
|
||||
</BadgeText>
|
||||
</Badge>
|
||||
</MouseoverTooltip>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { readableColor } from 'polished'
|
||||
import { PropsWithChildren } from 'react'
|
||||
import styled, { DefaultTheme } from 'styled-components'
|
||||
import styled, { DefaultTheme } from 'styled-components/macro'
|
||||
import { Color } from 'theme/styled'
|
||||
|
||||
export enum BadgeVariant {
|
||||
@@ -13,7 +13,7 @@ export enum BadgeVariant {
|
||||
WARNING_OUTLINE = 'WARNING_OUTLINE',
|
||||
}
|
||||
|
||||
export interface BadgeProps {
|
||||
interface BadgeProps {
|
||||
variant?: BadgeVariant
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,25 @@
|
||||
import React, { ReactNode, useMemo } from 'react'
|
||||
import { BLOCKED_ADDRESSES } from '../../constants'
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { ReactNode, useMemo } from 'react'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { Trans } from '@lingui/macro'
|
||||
|
||||
// SDN OFAC addresses
|
||||
const BLOCKED_ADDRESSES: string[] = [
|
||||
'0x7F367cC41522cE07553e823bf3be79A889DEbe1B',
|
||||
'0xd882cFc20F52f2599D84b8e8D58C7FB62cfE344b',
|
||||
'0x901bb9583b24D97e995513C6778dc6888AB6870e',
|
||||
'0xA7e5d5A720f06526557c513402f2e6B5fA20b008',
|
||||
'0x8576aCC5C05D6Ce88f4e49bf65BdF0C62F91353C',
|
||||
]
|
||||
|
||||
export default function Blocklist({ children }: { children: ReactNode }) {
|
||||
const { account } = useActiveWeb3React()
|
||||
const blocked: boolean = useMemo(() => Boolean(account && BLOCKED_ADDRESSES.indexOf(account) !== -1), [account])
|
||||
if (blocked) {
|
||||
return <div>Blocked address</div>
|
||||
return (
|
||||
<div>
|
||||
<Trans>Blocked address</Trans>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return <>{children}</>
|
||||
}
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
import { Story } from '@storybook/react/types-6-0'
|
||||
import styled from 'styled-components/macro'
|
||||
import React from 'react'
|
||||
import {
|
||||
ButtonConfirmed,
|
||||
ButtonDropdown,
|
||||
ButtonDropdownGrey,
|
||||
ButtonDropdownLight,
|
||||
ButtonEmpty,
|
||||
ButtonError,
|
||||
ButtonGray,
|
||||
ButtonLight,
|
||||
ButtonOutlined,
|
||||
ButtonPink,
|
||||
ButtonPrimary,
|
||||
ButtonRadio,
|
||||
ButtonSecondary,
|
||||
ButtonUNIGradient,
|
||||
ButtonWhite,
|
||||
} from './index'
|
||||
|
||||
const wrapperCss = styled.main`
|
||||
font-size: 2em;
|
||||
margin: 3em;
|
||||
max-width: 300px;
|
||||
`
|
||||
|
||||
export default {
|
||||
title: 'Buttons',
|
||||
argTypes: {
|
||||
disabled: { control: { type: 'boolean' } },
|
||||
onClick: { action: 'clicked' },
|
||||
},
|
||||
decorators: [
|
||||
(Component: Story) => (
|
||||
<div css={wrapperCss}>
|
||||
<Component />
|
||||
</div>
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
const Unicorn = () => (
|
||||
<span role="img" aria-label="unicorn">
|
||||
🦄
|
||||
</span>
|
||||
)
|
||||
|
||||
export const Radio = () => (
|
||||
<ButtonRadio>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonRadio>
|
||||
)
|
||||
export const DropdownLight = () => (
|
||||
<ButtonDropdownLight>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonDropdownLight>
|
||||
)
|
||||
export const DropdownGrey = () => (
|
||||
<ButtonDropdownGrey>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonDropdownGrey>
|
||||
)
|
||||
export const Dropdown = () => (
|
||||
<ButtonDropdown>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonDropdown>
|
||||
)
|
||||
export const Error = () => (
|
||||
<ButtonError>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonError>
|
||||
)
|
||||
export const Confirmed = () => (
|
||||
<ButtonConfirmed>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonConfirmed>
|
||||
)
|
||||
export const White = () => (
|
||||
<ButtonWhite>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonWhite>
|
||||
)
|
||||
export const Empty = () => (
|
||||
<ButtonEmpty>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonEmpty>
|
||||
)
|
||||
export const Outlined = () => (
|
||||
<ButtonOutlined>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonOutlined>
|
||||
)
|
||||
export const UNIGradient = () => (
|
||||
<ButtonUNIGradient>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonUNIGradient>
|
||||
)
|
||||
export const Pink = () => (
|
||||
<ButtonPink>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonPink>
|
||||
)
|
||||
export const Secondary = () => (
|
||||
<ButtonSecondary>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonSecondary>
|
||||
)
|
||||
export const Gray = () => (
|
||||
<ButtonGray>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonGray>
|
||||
)
|
||||
export const Light = () => (
|
||||
<ButtonLight>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonLight>
|
||||
)
|
||||
export const Primary = () => (
|
||||
<ButtonPrimary>
|
||||
<Unicorn />
|
||||
UNISWAP
|
||||
<Unicorn />
|
||||
</ButtonPrimary>
|
||||
)
|
||||
@@ -1,24 +1,26 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import { darken } from 'polished'
|
||||
|
||||
import { RowBetween } from '../Row'
|
||||
import { ChevronDown, Check } from 'react-feather'
|
||||
import { Button as RebassButton, ButtonProps } from 'rebass/styled-components'
|
||||
import { Button as RebassButton, ButtonProps as ButtonPropsOriginal } from 'rebass/styled-components'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
|
||||
const Base = styled(RebassButton)<{
|
||||
padding?: string
|
||||
width?: string
|
||||
borderRadius?: string
|
||||
altDisabledStyle?: boolean
|
||||
}>`
|
||||
padding: ${({ padding }) => (padding ? padding : '16px')};
|
||||
width: ${({ width }) => (width ? width : '100%')};
|
||||
type ButtonProps = Omit<ButtonPropsOriginal, 'css'>
|
||||
|
||||
const Base = styled(RebassButton)<
|
||||
{
|
||||
padding?: string
|
||||
width?: string
|
||||
$borderRadius?: string
|
||||
altDisabledStyle?: boolean
|
||||
} & ButtonProps
|
||||
>`
|
||||
padding: ${({ padding }) => padding ?? '16px'};
|
||||
width: ${({ width }) => width ?? '100%'};
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
border-radius: 20px;
|
||||
border-radius: ${({ borderRadius }) => borderRadius && borderRadius};
|
||||
border-radius: ${({ $borderRadius }) => $borderRadius ?? '20px'};
|
||||
outline: none;
|
||||
border: 1px solid transparent;
|
||||
color: white;
|
||||
@@ -39,10 +41,6 @@ const Base = styled(RebassButton)<{
|
||||
transition: transform 450ms ease;
|
||||
transform: perspective(1px) translateZ(0);
|
||||
|
||||
&:hover {
|
||||
transform: scale(0.99);
|
||||
}
|
||||
|
||||
> * {
|
||||
user-select: none;
|
||||
}
|
||||
@@ -68,14 +66,12 @@ export const ButtonPrimary = styled(Base)`
|
||||
}
|
||||
&:disabled {
|
||||
background-color: ${({ theme, altDisabledStyle, disabled }) =>
|
||||
altDisabledStyle ? (disabled ? theme.primary1 : theme.primary1) : theme.primary1};
|
||||
color: white;
|
||||
altDisabledStyle ? (disabled ? theme.primary1 : theme.bg2) : theme.bg2};
|
||||
color: ${({ theme }) => theme.text2};
|
||||
cursor: auto;
|
||||
box-shadow: none;
|
||||
border: 1px solid transparent;
|
||||
outline: none;
|
||||
opacity: 0.4;
|
||||
opacity: ${({ altDisabledStyle }) => (altDisabledStyle ? '0.5' : '0.4')};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -112,9 +108,7 @@ export const ButtonGray = styled(Base)`
|
||||
color: ${({ theme }) => theme.text2};
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
&:focus {
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
|
||||
}
|
||||
@@ -151,63 +145,41 @@ export const ButtonSecondary = styled(Base)`
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonPink = styled(Base)`
|
||||
background-color: ${({ theme }) => theme.primary1};
|
||||
color: white;
|
||||
|
||||
export const ButtonOutlined = styled(Base)`
|
||||
border: 1px solid ${({ theme }) => theme.bg2};
|
||||
background-color: transparent;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
&: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 1px ${({ theme }) => theme.bg4};
|
||||
}
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => darken(0.05, theme.primary1)};
|
||||
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4};
|
||||
}
|
||||
&: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 1px ${({ theme }) => theme.bg4};
|
||||
}
|
||||
&:disabled {
|
||||
background-color: ${({ theme }) => theme.primary1};
|
||||
opacity: 50%;
|
||||
cursor: auto;
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonUNIGradient = styled(ButtonPrimary)`
|
||||
export const ButtonYellow = styled(Base)`
|
||||
background-color: ${({ theme }) => theme.yellow3};
|
||||
color: white;
|
||||
padding: 4px 8px;
|
||||
height: 36px;
|
||||
font-weight: 500;
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
background: radial-gradient(174.47% 188.91% at 1.84% 0%, #ff007a 0%, #2172e5 100%), #edeef2;
|
||||
width: fit-content;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
white-space: no-wrap;
|
||||
:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
:active {
|
||||
opacity: 0.9;
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonOutlined = styled(Base)`
|
||||
border: 1px solid ${({ theme }) => theme.bg2};
|
||||
background-color: transparent;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4};
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.yellow3)};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.yellow3)};
|
||||
}
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4};
|
||||
background-color: ${({ theme }) => darken(0.05, theme.yellow3)};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4};
|
||||
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.yellow3)};
|
||||
background-color: ${({ theme }) => darken(0.1, theme.yellow3)};
|
||||
}
|
||||
&:disabled {
|
||||
background-color: ${({ theme }) => theme.yellow3};
|
||||
opacity: 50%;
|
||||
cursor: auto;
|
||||
}
|
||||
@@ -257,27 +229,6 @@ export const ButtonText = styled(Base)`
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonWhite = styled(Base)`
|
||||
border: 1px solid #edeef2;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
color: black;
|
||||
|
||||
&:focus {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
box-shadow: 0 0 0 1pt ${darken(0.05, '#edeef2')};
|
||||
}
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 1pt ${darken(0.1, '#edeef2')};
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1pt ${darken(0.1, '#edeef2')};
|
||||
}
|
||||
&:disabled {
|
||||
opacity: 50%;
|
||||
cursor: auto;
|
||||
}
|
||||
`
|
||||
|
||||
const ButtonConfirmedStyle = styled(Base)`
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
color: ${({ theme }) => theme.text1};
|
||||
@@ -346,17 +297,6 @@ export function ButtonDropdown({ disabled = false, children, ...rest }: { disabl
|
||||
)
|
||||
}
|
||||
|
||||
export function ButtonDropdownGrey({ disabled = false, children, ...rest }: { disabled?: boolean } & ButtonProps) {
|
||||
return (
|
||||
<ButtonGray {...rest} disabled={disabled} style={{ borderRadius: '20px' }}>
|
||||
<RowBetween>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>{children}</div>
|
||||
<ChevronDown size={24} />
|
||||
</RowBetween>
|
||||
</ButtonGray>
|
||||
)
|
||||
}
|
||||
|
||||
export function ButtonDropdownLight({ disabled = false, children, ...rest }: { disabled?: boolean } & ButtonProps) {
|
||||
return (
|
||||
<ButtonOutlined {...rest} disabled={disabled}>
|
||||
@@ -368,14 +308,6 @@ export function ButtonDropdownLight({ disabled = false, children, ...rest }: { d
|
||||
)
|
||||
}
|
||||
|
||||
export function ButtonRadio({ active, ...rest }: { active?: boolean } & ButtonProps) {
|
||||
if (!active) {
|
||||
return <ButtonWhite {...rest} />
|
||||
} else {
|
||||
return <ButtonPrimary {...rest} />
|
||||
}
|
||||
}
|
||||
|
||||
const ActiveOutlined = styled(ButtonOutlined)`
|
||||
border: 1px solid;
|
||||
border-color: ${({ theme }) => theme.primary1};
|
||||
@@ -408,13 +340,13 @@ export function ButtonRadioChecked({ active = false, children, ...rest }: { acti
|
||||
|
||||
if (!active) {
|
||||
return (
|
||||
<ButtonOutlined borderRadius="12px" padding="12px 8px" {...rest}>
|
||||
<ButtonOutlined $borderRadius="12px" padding="12px 8px" {...rest}>
|
||||
{<RowBetween>{children}</RowBetween>}
|
||||
</ButtonOutlined>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<ActiveOutlined {...rest} padding="12px 8px" borderRadius="12px">
|
||||
<ActiveOutlined {...rest} padding="12px 8px" $borderRadius="12px">
|
||||
{
|
||||
<RowBetween>
|
||||
{children}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import styled from 'styled-components/macro'
|
||||
import { Box } from 'rebass/styled-components'
|
||||
|
||||
const Card = styled(Box)<{ width?: string; padding?: string; border?: string; borderRadius?: string }>`
|
||||
const Card = styled(Box)<{ width?: string; padding?: string; border?: string; $borderRadius?: string }>`
|
||||
width: ${({ width }) => width ?? '100%'};
|
||||
border-radius: 16px;
|
||||
padding: 1rem;
|
||||
padding: ${({ padding }) => padding};
|
||||
padding: ${({ padding }) => padding ?? '1rem'};
|
||||
border-radius: ${({ $borderRadius }) => $borderRadius ?? '16px'};
|
||||
border: ${({ border }) => border};
|
||||
border-radius: ${({ borderRadius }) => borderRadius};
|
||||
`
|
||||
export default Card
|
||||
|
||||
@@ -42,12 +40,6 @@ export const YellowCard = styled(Card)`
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
export const PinkCard = styled(Card)`
|
||||
background-color: rgba(255, 0, 122, 0.03);
|
||||
color: ${({ theme }) => theme.primary1};
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
export const BlueCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.primary5};
|
||||
color: ${({ theme }) => theme.blue2};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import React from 'react'
|
||||
import ReactConfetti from 'react-confetti'
|
||||
import { useWindowSize } from '../../hooks/useWindowSize'
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
|
||||
import React, { useMemo } from 'react'
|
||||
import { useMemo } from 'react'
|
||||
import useTheme from '../../hooks/useTheme'
|
||||
import { TYPE } from '../../theme'
|
||||
import { warningSeverity } from '../../utils/prices'
|
||||
import HoverInlineText from 'components/HoverInlineText'
|
||||
import { Trans } from '@lingui/macro'
|
||||
|
||||
export function FiatValue({
|
||||
fiatValue,
|
||||
@@ -17,17 +18,25 @@ export function FiatValue({
|
||||
if (!priceImpact) return undefined
|
||||
if (priceImpact.lessThan('0')) return theme.green1
|
||||
const severity = warningSeverity(priceImpact)
|
||||
if (severity < 1) return theme.text4
|
||||
if (severity < 1) return theme.text3
|
||||
if (severity < 3) return theme.yellow1
|
||||
return theme.red1
|
||||
}, [priceImpact, theme.green1, theme.red1, theme.text4, theme.yellow1])
|
||||
}, [priceImpact, theme.green1, theme.red1, theme.text3, theme.yellow1])
|
||||
|
||||
return (
|
||||
<TYPE.body fontSize={14} color={fiatValue ? theme.text2 : theme.text4}>
|
||||
{fiatValue ? '~' : ''}$
|
||||
<HoverInlineText text={fiatValue ? fiatValue?.toSignificant(6, { groupSeparator: ',' }) : '-'} />{' '}
|
||||
{fiatValue ? (
|
||||
<Trans>
|
||||
~$ <HoverInlineText text={fiatValue?.toSignificant(6, { groupSeparator: ',' })} />
|
||||
</Trans>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{priceImpact ? (
|
||||
<span style={{ color: priceImpactColor }}> ({priceImpact.multiply(-1).toSignificant(3)}%)</span>
|
||||
<span style={{ color: priceImpactColor }}>
|
||||
{' '}
|
||||
(<Trans>{priceImpact.multiply(-1).toSignificant(3)}%</Trans>)
|
||||
</span>
|
||||
) : null}
|
||||
</TYPE.body>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Pair } from '@uniswap/v2-sdk'
|
||||
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
|
||||
import React, { useState, useCallback } from 'react'
|
||||
import { useState, useCallback, ReactNode } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import { darken } from 'polished'
|
||||
import { useCurrencyBalance } from '../../state/wallet/hooks'
|
||||
@@ -12,13 +12,13 @@ import { RowBetween, RowFixed } from '../Row'
|
||||
import { TYPE } from '../../theme'
|
||||
import { Input as NumericalInput } from '../NumericalInput'
|
||||
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import useTheme from '../../hooks/useTheme'
|
||||
import { Lock } from 'react-feather'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { FiatValue } from './FiatValue'
|
||||
import { formatTokenAmount } from 'utils/formatTokenAmount'
|
||||
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
|
||||
|
||||
const InputPanel = styled.div<{ hideInput?: boolean }>`
|
||||
${({ theme }) => theme.flexColumnNoWrap}
|
||||
@@ -130,7 +130,7 @@ const StyledBalanceMax = styled.button<{ disabled?: boolean }>`
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
color: ${({ theme }) => theme.primary1};
|
||||
color: ${({ theme }) => theme.primaryText1};
|
||||
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
|
||||
pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')};
|
||||
margin-left: 0.25rem;
|
||||
@@ -149,7 +149,7 @@ interface CurrencyInputPanelProps {
|
||||
onUserInput: (value: string) => void
|
||||
onMax?: () => void
|
||||
showMaxButton: boolean
|
||||
label?: string
|
||||
label?: ReactNode
|
||||
onCurrencySelect?: (currency: Currency) => void
|
||||
currency?: Currency | null
|
||||
hideBalance?: boolean
|
||||
@@ -160,7 +160,9 @@ interface CurrencyInputPanelProps {
|
||||
priceImpact?: Percent
|
||||
id: string
|
||||
showCommonBases?: boolean
|
||||
customBalanceText?: string
|
||||
showCurrencyAmount?: boolean
|
||||
disableNonToken?: boolean
|
||||
renderBalance?: (amount: CurrencyAmount<Currency>) => ReactNode
|
||||
locked?: boolean
|
||||
}
|
||||
|
||||
@@ -174,7 +176,9 @@ export default function CurrencyInputPanel({
|
||||
otherCurrency,
|
||||
id,
|
||||
showCommonBases,
|
||||
customBalanceText,
|
||||
showCurrencyAmount,
|
||||
disableNonToken,
|
||||
renderBalance,
|
||||
fiatValue,
|
||||
priceImpact,
|
||||
hideBalance = false,
|
||||
@@ -183,8 +187,6 @@ export default function CurrencyInputPanel({
|
||||
locked = false,
|
||||
...rest
|
||||
}: CurrencyInputPanelProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [modalOpen, setModalOpen] = useState(false)
|
||||
const { account } = useActiveWeb3React()
|
||||
const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined)
|
||||
@@ -200,8 +202,8 @@ export default function CurrencyInputPanel({
|
||||
<FixedContainer>
|
||||
<AutoColumn gap="sm" justify="center">
|
||||
<Lock />
|
||||
<TYPE.label fontSize="12px" textAlign="center">
|
||||
The market price is outside your specified price range. Single-asset deposit only.
|
||||
<TYPE.label fontSize="12px" textAlign="center" padding="0 12px">
|
||||
<Trans>The market price is outside your specified price range. Single-asset deposit only.</Trans>
|
||||
</TYPE.label>
|
||||
</AutoColumn>
|
||||
</FixedContainer>
|
||||
@@ -237,7 +239,7 @@ export default function CurrencyInputPanel({
|
||||
? currency.symbol.slice(0, 4) +
|
||||
'...' +
|
||||
currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length)
|
||||
: currency?.symbol) || t('selectToken')}
|
||||
: currency?.symbol) || <Trans>Select a token</Trans>}
|
||||
</StyledTokenName>
|
||||
)}
|
||||
</RowFixed>
|
||||
@@ -268,19 +270,24 @@ export default function CurrencyInputPanel({
|
||||
fontSize={14}
|
||||
style={{ display: 'inline', cursor: 'pointer' }}
|
||||
>
|
||||
{!hideBalance && !!currency && selectedCurrencyBalance
|
||||
? (customBalanceText ?? 'Balance: ') +
|
||||
formatTokenAmount(selectedCurrencyBalance, 4) +
|
||||
' ' +
|
||||
currency.symbol
|
||||
: '-'}
|
||||
{!hideBalance && currency && selectedCurrencyBalance ? (
|
||||
renderBalance ? (
|
||||
renderBalance(selectedCurrencyBalance)
|
||||
) : (
|
||||
<Trans>
|
||||
Balance: {formatCurrencyAmount(selectedCurrencyBalance, 4)} {currency.symbol}
|
||||
</Trans>
|
||||
)
|
||||
) : null}
|
||||
</TYPE.body>
|
||||
{showMaxButton && selectedCurrencyBalance ? (
|
||||
<StyledBalanceMax onClick={onMax}>(Max)</StyledBalanceMax>
|
||||
<StyledBalanceMax onClick={onMax}>
|
||||
<Trans>(Max)</Trans>
|
||||
</StyledBalanceMax>
|
||||
) : null}
|
||||
</RowFixed>
|
||||
) : (
|
||||
'-'
|
||||
<span />
|
||||
)}
|
||||
<FiatValue fiatValue={fiatValue} priceImpact={priceImpact} />
|
||||
</RowBetween>
|
||||
@@ -295,6 +302,8 @@ export default function CurrencyInputPanel({
|
||||
selectedCurrency={currency}
|
||||
otherSelectedCurrency={otherCurrency}
|
||||
showCommonBases={showCommonBases}
|
||||
showCurrencyAmount={showCurrencyAmount}
|
||||
disableNonToken={disableNonToken}
|
||||
/>
|
||||
)}
|
||||
</InputPanel>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChainId, Currency } from '@uniswap/sdk-core'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import React, { useMemo } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import EthereumLogo from '../../assets/images/ethereum-logo.png'
|
||||
@@ -7,7 +7,7 @@ import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
|
||||
import Logo from '../Logo'
|
||||
|
||||
export const getTokenLogoURL = (address: string) =>
|
||||
`https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${address}/logo.png`
|
||||
`https://raw.githubusercontent.com/uniswap/assets/master/blockchains/ethereum/assets/${address}/logo.png`
|
||||
|
||||
const StyledEthereumLogo = styled.img<{ size: string }>`
|
||||
width: ${({ size }) => size};
|
||||
@@ -37,10 +37,10 @@ export default function CurrencyLogo({
|
||||
const uriLocations = useHttpLocations(currency instanceof WrappedTokenInfo ? currency.logoURI : undefined)
|
||||
|
||||
const srcs: string[] = useMemo(() => {
|
||||
if (!currency || currency.isEther) return []
|
||||
if (!currency || currency.isNative) return []
|
||||
|
||||
if (currency.isToken) {
|
||||
const defaultUrls = currency.chainId === ChainId.MAINNET ? [getTokenLogoURL(currency.address)] : []
|
||||
const defaultUrls = currency.chainId === 1 ? [getTokenLogoURL(currency.address)] : []
|
||||
if (currency instanceof WrappedTokenInfo) {
|
||||
return [...uriLocations, ...defaultUrls]
|
||||
}
|
||||
@@ -49,7 +49,7 @@ export default function CurrencyLogo({
|
||||
return []
|
||||
}, [currency, uriLocations])
|
||||
|
||||
if (currency?.isEther) {
|
||||
if (currency?.isNative) {
|
||||
return <StyledEthereumLogo src={EthereumLogo} size={size} style={style} {...rest} />
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Story } from '@storybook/react/types-6-0'
|
||||
import React from 'react'
|
||||
import { DAI, WBTC } from '../../constants'
|
||||
import Component, { DoubleCurrencyLogoProps } from './index'
|
||||
|
||||
export default {
|
||||
title: 'DoubleCurrencyLogo',
|
||||
decorators: [],
|
||||
}
|
||||
|
||||
const Template: Story<DoubleCurrencyLogoProps> = (args) => <Component {...args} />
|
||||
|
||||
export const DoubleCurrencyLogo = Template.bind({})
|
||||
DoubleCurrencyLogo.args = {
|
||||
currency0: DAI,
|
||||
currency1: WBTC,
|
||||
size: 220,
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import CurrencyLogo from '../CurrencyLogo'
|
||||
|
||||
@@ -10,7 +9,7 @@ const Wrapper = styled.div<{ margin: boolean; sizeraw: number }>`
|
||||
margin-left: ${({ sizeraw, margin }) => margin && (sizeraw / 3 + 8).toString() + 'px'};
|
||||
`
|
||||
|
||||
export interface DoubleCurrencyLogoProps {
|
||||
interface DoubleCurrencyLogoProps {
|
||||
margin?: boolean
|
||||
size?: number
|
||||
currency0?: Currency
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import React, { ErrorInfo } from 'react'
|
||||
import store, { AppState } from '../../state'
|
||||
import { ExternalLink, ThemedBackground, TYPE } from '../../theme'
|
||||
import { ExternalLink, TYPE } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
import styled from 'styled-components/macro'
|
||||
import ReactGA from 'react-ga'
|
||||
@@ -69,12 +70,11 @@ export default class ErrorBoundary extends React.Component<unknown, ErrorBoundar
|
||||
const encodedBody = encodeURIComponent(issueBody(error))
|
||||
return (
|
||||
<FallbackWrapper>
|
||||
<ThemedBackground />
|
||||
<BodyWrapper>
|
||||
<AutoColumn gap={'md'}>
|
||||
<SomethingWentWrongWrapper>
|
||||
<TYPE.label fontSize={24} fontWeight={600}>
|
||||
Something went wrong
|
||||
<Trans>Something went wrong</Trans>
|
||||
</TYPE.label>
|
||||
</SomethingWentWrongWrapper>
|
||||
<CodeBlockWrapper>
|
||||
@@ -92,7 +92,7 @@ export default class ErrorBoundary extends React.Component<unknown, ErrorBoundar
|
||||
target="_blank"
|
||||
>
|
||||
<TYPE.link fontSize={16}>
|
||||
Create an issue on GitHub
|
||||
<Trans>Create an issue on GitHub</Trans>
|
||||
<span>↗</span>
|
||||
</TYPE.link>
|
||||
</ExternalLink>
|
||||
@@ -100,7 +100,7 @@ export default class ErrorBoundary extends React.Component<unknown, ErrorBoundar
|
||||
<LinkWrapper>
|
||||
<ExternalLink id="get-support-on-discord" href="https://discord.gg/FCfyBSbCU5" target="_blank">
|
||||
<TYPE.link fontSize={16}>
|
||||
Get support on Discord
|
||||
<Trans>Get support on Discord</Trans>
|
||||
<span>↗</span>
|
||||
</TYPE.link>
|
||||
</ExternalLink>
|
||||
|
||||
@@ -1,75 +1,228 @@
|
||||
import React from 'react'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { DynamicSection } from 'pages/AddLiquidity/styled'
|
||||
import { TYPE } from 'theme'
|
||||
import { RowBetween } from 'components/Row'
|
||||
import { ButtonRadioChecked } from 'components/Button'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ButtonGray, ButtonRadioChecked } from 'components/Button'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
import Badge from 'components/Badge'
|
||||
import Card from 'components/Card'
|
||||
import usePrevious from 'hooks/usePrevious'
|
||||
import { useFeeTierDistribution } from 'hooks/useFeeTierDistribution'
|
||||
import ReactGA from 'react-ga'
|
||||
import { Box } from 'rebass'
|
||||
|
||||
const pulse = (color: string) => keyframes`
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 ${color};
|
||||
}
|
||||
|
||||
70% {
|
||||
box-shadow: 0 0 0 2px ${color};
|
||||
}
|
||||
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 ${color};
|
||||
}
|
||||
`
|
||||
|
||||
const ResponsiveText = styled(TYPE.label)`
|
||||
line-height: 16px;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
`};
|
||||
`
|
||||
|
||||
const FocusedOutlineCard = styled(Card)<{ pulsing: boolean }>`
|
||||
border: 1px solid ${({ theme }) => theme.bg2};
|
||||
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.primary1)} 0.6s linear;
|
||||
`
|
||||
|
||||
const FeeAmountLabel = {
|
||||
[FeeAmount.LOW]: {
|
||||
label: '0.05',
|
||||
description: <Trans>Best for stable pairs.</Trans>,
|
||||
},
|
||||
[FeeAmount.MEDIUM]: {
|
||||
label: '0.3',
|
||||
description: <Trans>Best for most pairs.</Trans>,
|
||||
},
|
||||
[FeeAmount.HIGH]: {
|
||||
label: '1',
|
||||
description: <Trans>Best for exotic pairs.</Trans>,
|
||||
},
|
||||
}
|
||||
|
||||
const FeeTierPercentageBadge = ({ percentage }: { percentage: number | undefined }) => {
|
||||
return (
|
||||
<Badge>
|
||||
<TYPE.label fontSize={12}>
|
||||
{percentage !== undefined ? <Trans>{percentage?.toFixed(0)}% select</Trans> : <Trans>Not created</Trans>}
|
||||
</TYPE.label>
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
|
||||
export default function FeeSelector({
|
||||
disabled = false,
|
||||
feeAmount,
|
||||
handleFeePoolSelect,
|
||||
currencyA,
|
||||
currencyB,
|
||||
}: {
|
||||
disabled?: boolean
|
||||
feeAmount?: FeeAmount
|
||||
handleFeePoolSelect: (feeAmount: FeeAmount) => void
|
||||
currencyA?: Currency | undefined
|
||||
currencyB?: Currency | undefined
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const { isLoading, isError, largestUsageFeeTier, distributions } = useFeeTierDistribution(currencyA, currencyB)
|
||||
|
||||
const [showOptions, setShowOptions] = useState(false)
|
||||
const [pulsing, setPulsing] = useState(false)
|
||||
|
||||
const previousFeeAmount = usePrevious(feeAmount)
|
||||
|
||||
const recommended = useRef(false)
|
||||
|
||||
const handleFeePoolSelectWithEvent = useCallback(
|
||||
(fee) => {
|
||||
ReactGA.event({
|
||||
category: 'FeePoolSelect',
|
||||
action: 'Manual',
|
||||
})
|
||||
handleFeePoolSelect(fee)
|
||||
},
|
||||
[handleFeePoolSelect]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (feeAmount || isLoading || isError) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!largestUsageFeeTier) {
|
||||
// cannot recommend, open options
|
||||
setShowOptions(true)
|
||||
} else {
|
||||
setShowOptions(false)
|
||||
|
||||
recommended.current = true
|
||||
ReactGA.event({
|
||||
category: 'FeePoolSelect',
|
||||
action: ' Recommended',
|
||||
})
|
||||
|
||||
handleFeePoolSelect(largestUsageFeeTier)
|
||||
}
|
||||
}, [feeAmount, isLoading, isError, largestUsageFeeTier, handleFeePoolSelect])
|
||||
|
||||
useEffect(() => {
|
||||
setShowOptions(isError)
|
||||
}, [isError])
|
||||
|
||||
useEffect(() => {
|
||||
if (feeAmount && previousFeeAmount !== feeAmount) {
|
||||
setPulsing(true)
|
||||
}
|
||||
}, [previousFeeAmount, feeAmount])
|
||||
|
||||
return (
|
||||
<AutoColumn gap="16px">
|
||||
<DynamicSection gap="md" disabled={disabled}>
|
||||
<TYPE.label>{t('selectPool')}</TYPE.label>
|
||||
<TYPE.main fontSize={14} fontWeight={400} style={{ marginBottom: '.5rem', lineHeight: '125%' }}>
|
||||
Select a pool type based on your preferred liquidity provider fee.
|
||||
</TYPE.main>
|
||||
<RowBetween>
|
||||
<ButtonRadioChecked
|
||||
width="32%"
|
||||
active={feeAmount === FeeAmount.LOW}
|
||||
onClick={() => handleFeePoolSelect(FeeAmount.LOW)}
|
||||
>
|
||||
<AutoColumn gap="sm" justify="flex-start">
|
||||
<ResponsiveText>0.05% {t('fee')}</ResponsiveText>
|
||||
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
Best for stable pairs.
|
||||
</TYPE.main>
|
||||
<FocusedOutlineCard pulsing={pulsing} onAnimationEnd={() => setPulsing(false)}>
|
||||
<RowBetween>
|
||||
<AutoColumn id="add-liquidity-selected-fee">
|
||||
{!feeAmount ? (
|
||||
<>
|
||||
<TYPE.label>
|
||||
<Trans>Fee tier</Trans>
|
||||
</TYPE.label>
|
||||
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
<Trans>The % you will earn in fees.</Trans>
|
||||
</TYPE.main>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<TYPE.label className="selected-fee-label">
|
||||
<Trans>{FeeAmountLabel[feeAmount].label}% fee tier</Trans>
|
||||
</TYPE.label>
|
||||
<Box style={{ width: 'fit-content', marginTop: '8px' }} className="selected-fee-percentage">
|
||||
{distributions && feeAmount && <FeeTierPercentageBadge percentage={distributions[feeAmount]} />}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</AutoColumn>
|
||||
</ButtonRadioChecked>
|
||||
<ButtonRadioChecked
|
||||
width="32%"
|
||||
active={feeAmount === FeeAmount.MEDIUM}
|
||||
onClick={() => handleFeePoolSelect(FeeAmount.MEDIUM)}
|
||||
>
|
||||
<AutoColumn gap="sm" justify="flex-start">
|
||||
<ResponsiveText>0.3% {t('fee')}</ResponsiveText>
|
||||
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
Best for most pairs.
|
||||
</TYPE.main>
|
||||
</AutoColumn>
|
||||
</ButtonRadioChecked>
|
||||
<ButtonRadioChecked
|
||||
width="32%"
|
||||
active={feeAmount === FeeAmount.HIGH}
|
||||
onClick={() => handleFeePoolSelect(FeeAmount.HIGH)}
|
||||
>
|
||||
<AutoColumn gap="sm" justify="flex-start">
|
||||
<ResponsiveText>1% {t('fee')}</ResponsiveText>
|
||||
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
Best for exotic pairs.
|
||||
</TYPE.main>
|
||||
</AutoColumn>
|
||||
</ButtonRadioChecked>
|
||||
</RowBetween>
|
||||
|
||||
<ButtonGray onClick={() => setShowOptions(!showOptions)} width="auto" padding="4px" $borderRadius="6px">
|
||||
{showOptions ? <Trans>Hide</Trans> : <Trans>Edit</Trans>}
|
||||
</ButtonGray>
|
||||
</RowBetween>
|
||||
</FocusedOutlineCard>
|
||||
|
||||
{showOptions && (
|
||||
<RowBetween>
|
||||
<ButtonRadioChecked
|
||||
width="32%"
|
||||
active={feeAmount === FeeAmount.LOW}
|
||||
onClick={() => handleFeePoolSelectWithEvent(FeeAmount.LOW)}
|
||||
>
|
||||
<AutoColumn gap="sm" justify="flex-start">
|
||||
<AutoColumn justify="flex-start" gap="6px">
|
||||
<ResponsiveText>
|
||||
<Trans>0.05% fee</Trans>
|
||||
</ResponsiveText>
|
||||
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
<Trans>Best for stable pairs.</Trans>
|
||||
</TYPE.main>
|
||||
</AutoColumn>
|
||||
|
||||
{distributions && <FeeTierPercentageBadge percentage={distributions[FeeAmount.LOW]} />}
|
||||
</AutoColumn>
|
||||
</ButtonRadioChecked>
|
||||
<ButtonRadioChecked
|
||||
width="32%"
|
||||
active={feeAmount === FeeAmount.MEDIUM}
|
||||
onClick={() => handleFeePoolSelectWithEvent(FeeAmount.MEDIUM)}
|
||||
>
|
||||
<AutoColumn gap="sm" justify="flex-start">
|
||||
<AutoColumn justify="flex-start" gap="4px">
|
||||
<ResponsiveText>
|
||||
<Trans>0.3% fee</Trans>
|
||||
</ResponsiveText>
|
||||
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
<Trans>Best for most pairs.</Trans>
|
||||
</TYPE.main>
|
||||
</AutoColumn>
|
||||
|
||||
{distributions && <FeeTierPercentageBadge percentage={distributions[FeeAmount.MEDIUM]} />}
|
||||
</AutoColumn>
|
||||
</ButtonRadioChecked>
|
||||
<ButtonRadioChecked
|
||||
width="32%"
|
||||
active={feeAmount === FeeAmount.HIGH}
|
||||
onClick={() => handleFeePoolSelectWithEvent(FeeAmount.HIGH)}
|
||||
>
|
||||
<AutoColumn gap="sm" justify="flex-start">
|
||||
<AutoColumn justify="flex-start" gap="4px">
|
||||
<ResponsiveText>
|
||||
<Trans>1% fee</Trans>
|
||||
</ResponsiveText>
|
||||
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
<Trans>Best for exotic pairs.</Trans>
|
||||
</TYPE.main>
|
||||
</AutoColumn>
|
||||
|
||||
{distributions && <FeeTierPercentageBadge percentage={distributions[FeeAmount.HIGH]} />}
|
||||
</AutoColumn>
|
||||
</ButtonRadioChecked>
|
||||
</RowBetween>
|
||||
)}
|
||||
</DynamicSection>
|
||||
</AutoColumn>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import JSBI from 'jsbi'
|
||||
import React from 'react'
|
||||
import { Currency, CurrencyAmount, Fraction } from '@uniswap/sdk-core'
|
||||
|
||||
const CURRENCY_AMOUNT_MIN = new Fraction(JSBI.BigInt(1), JSBI.BigInt(1000000))
|
||||
|
||||
234
src/components/Header/NetworkCard.tsx
Normal file
@@ -0,0 +1,234 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { YellowCard } from 'components/Card'
|
||||
import { useOnClickOutside } from 'hooks/useOnClickOutside'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { ArrowDownCircle, ChevronDown, ToggleLeft } from 'react-feather'
|
||||
import { ApplicationModal } from 'state/application/actions'
|
||||
import { useModalOpen, useToggleModal } from 'state/application/hooks'
|
||||
import styled, { css } from 'styled-components/macro'
|
||||
import { ExternalLink } from 'theme'
|
||||
import { switchToNetwork } from 'utils/switchToNetwork'
|
||||
import { CHAIN_INFO, L2_CHAIN_IDS, SupportedChainId, SupportedL2ChainId } from '../../constants/chains'
|
||||
|
||||
const BaseWrapper = css`
|
||||
position: relative;
|
||||
margin-right: 8px;
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
justify-self: end;
|
||||
`};
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
margin: 0 0.5rem 0 0;
|
||||
width: initial;
|
||||
text-overflow: ellipsis;
|
||||
flex-shrink: 1;
|
||||
`};
|
||||
`
|
||||
const L2Wrapper = styled.div`
|
||||
${BaseWrapper}
|
||||
`
|
||||
const BaseMenuItem = css`
|
||||
align-items: center;
|
||||
background-color: transparent;
|
||||
border-radius: 12px;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
justify-content: space-between;
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.text1};
|
||||
text-decoration: none;
|
||||
}
|
||||
`
|
||||
const DisabledMenuItem = styled.div`
|
||||
${BaseMenuItem}
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
cursor: auto;
|
||||
display: flex;
|
||||
font-size: 10px;
|
||||
font-style: italic;
|
||||
justify-content: center;
|
||||
:hover,
|
||||
:active,
|
||||
:focus {
|
||||
color: ${({ theme }) => theme.text2};
|
||||
}
|
||||
`
|
||||
const FallbackWrapper = styled(YellowCard)`
|
||||
${BaseWrapper}
|
||||
width: auto;
|
||||
border-radius: 12px;
|
||||
padding: 8px 12px;
|
||||
width: 100%;
|
||||
`
|
||||
const Icon = styled.img`
|
||||
width: 16px;
|
||||
margin-right: 2px;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
margin-right: 4px;
|
||||
|
||||
`};
|
||||
`
|
||||
|
||||
const MenuFlyout = styled.span`
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
border: 1px solid ${({ theme }) => theme.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: 12px;
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 1rem;
|
||||
position: absolute;
|
||||
left: 0rem;
|
||||
top: 3rem;
|
||||
z-index: 100;
|
||||
width: 237px;
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
|
||||
bottom: unset;
|
||||
top: 4.5em
|
||||
right: 0;
|
||||
|
||||
`};
|
||||
> {
|
||||
padding: 12px;
|
||||
}
|
||||
> :not(:first-child) {
|
||||
margin-top: 8px;
|
||||
}
|
||||
> :not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
`
|
||||
const LinkOutCircle = styled(ArrowDownCircle)`
|
||||
transform: rotate(230deg);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
opacity: 0.6;
|
||||
`
|
||||
const MenuItem = styled(ExternalLink)`
|
||||
${BaseMenuItem}
|
||||
`
|
||||
const ButtonMenuItem = styled.button`
|
||||
${BaseMenuItem}
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
outline: none;
|
||||
padding: 0;
|
||||
`
|
||||
const NetworkInfo = styled.button<{ chainId: SupportedChainId }>`
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
border-radius: 12px;
|
||||
border: 1px solid ${({ theme }) => theme.bg0};
|
||||
color: ${({ theme }) => theme.text1};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
height: 38px;
|
||||
padding: 0.7rem;
|
||||
|
||||
:hover,
|
||||
:focus {
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
border: 1px solid ${({ theme }) => theme.bg3};
|
||||
}
|
||||
`
|
||||
const NetworkName = styled.div<{ chainId: SupportedChainId }>`
|
||||
border-radius: 6px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
padding: 0 2px 0.5px 4px;
|
||||
margin: 0 2px;
|
||||
white-space: pre;
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
display: none;
|
||||
`};
|
||||
`
|
||||
|
||||
export default function NetworkCard() {
|
||||
const { chainId, library } = useActiveWeb3React()
|
||||
const node = useRef<HTMLDivElement>(null)
|
||||
const open = useModalOpen(ApplicationModal.ARBITRUM_OPTIONS)
|
||||
const toggle = useToggleModal(ApplicationModal.ARBITRUM_OPTIONS)
|
||||
useOnClickOutside(node, open ? toggle : undefined)
|
||||
|
||||
const [implements3085, setImplements3085] = useState(false)
|
||||
useEffect(() => {
|
||||
// metamask is currently the only known implementer of this EIP
|
||||
// here we proceed w/ a noop feature check to ensure the user's version of metamask supports network switching
|
||||
// if not, we hide the UI
|
||||
if (!library?.provider?.request || !chainId || !library?.provider?.isMetaMask) {
|
||||
return
|
||||
}
|
||||
switchToNetwork({ library, chainId })
|
||||
.then((x) => x ?? setImplements3085(true))
|
||||
.catch(() => setImplements3085(false))
|
||||
}, [library, chainId])
|
||||
|
||||
const info = chainId ? CHAIN_INFO[chainId] : undefined
|
||||
if (!chainId || chainId === SupportedChainId.MAINNET || !info || !library) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (L2_CHAIN_IDS.includes(chainId)) {
|
||||
const info = CHAIN_INFO[chainId as SupportedL2ChainId]
|
||||
const isArbitrum = [SupportedChainId.ARBITRUM_ONE, SupportedChainId.ARBITRUM_RINKEBY].includes(chainId)
|
||||
return (
|
||||
<L2Wrapper ref={node}>
|
||||
<NetworkInfo onClick={toggle} chainId={chainId}>
|
||||
<Icon src={info.logoUrl} />
|
||||
<NetworkName chainId={chainId}>{info.label}</NetworkName>
|
||||
<ChevronDown size={16} style={{ marginTop: '2px' }} strokeWidth={2.5} />
|
||||
</NetworkInfo>
|
||||
{open && (
|
||||
<MenuFlyout>
|
||||
<MenuItem href={info.bridge}>
|
||||
<div>{isArbitrum ? <Trans>{info.label} Bridge</Trans> : <Trans>Optimistic L2 Gateway</Trans>}</div>
|
||||
<LinkOutCircle />
|
||||
</MenuItem>
|
||||
<MenuItem href={info.explorer}>
|
||||
{isArbitrum ? <Trans>{info.label} Explorer</Trans> : <Trans>Optimistic Etherscan</Trans>}
|
||||
<LinkOutCircle />
|
||||
</MenuItem>
|
||||
<MenuItem href={info.docs}>
|
||||
<div>
|
||||
<Trans>Learn more</Trans>
|
||||
</div>
|
||||
<LinkOutCircle />
|
||||
</MenuItem>
|
||||
{implements3085 ? (
|
||||
<ButtonMenuItem onClick={() => switchToNetwork({ library, chainId: SupportedChainId.MAINNET })}>
|
||||
<div>
|
||||
<Trans>Switch to L1 (Mainnet)</Trans>
|
||||
</div>
|
||||
<ToggleLeft opacity={0.6} size={16} />
|
||||
</ButtonMenuItem>
|
||||
) : (
|
||||
<DisabledMenuItem>
|
||||
<Trans>Change your network to go back to L1</Trans>
|
||||
</DisabledMenuItem>
|
||||
)}
|
||||
</MenuFlyout>
|
||||
)}
|
||||
</L2Wrapper>
|
||||
)
|
||||
}
|
||||
|
||||
return <FallbackWrapper title={info.label}>{info.label}</FallbackWrapper>
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import styled, { keyframes } from 'styled-components'
|
||||
import { TYPE, ExternalLink } from '../../theme'
|
||||
import { useEffect, useState } from 'react'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
|
||||
import { useBlockNumber } from '../../state/application/hooks'
|
||||
import { getEtherscanLink } from '../../utils'
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { ExternalLink, TYPE } from '../../theme'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
|
||||
const StyledPolling = styled.div`
|
||||
position: fixed;
|
||||
@@ -21,7 +21,7 @@ const StyledPolling = styled.div`
|
||||
`
|
||||
const StyledPollingNumber = styled(TYPE.small)<{ breathe: boolean; hovering: boolean }>`
|
||||
transition: opacity 0.25s ease;
|
||||
opacity: ${({ breathe, hovering }) => (hovering ? 0.7 : breathe ? 1 : 0.2)};
|
||||
opacity: ${({ breathe, hovering }) => (hovering ? 0.7 : breathe ? 1 : 0.5)};
|
||||
:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -91,7 +91,9 @@ export default function Polling() {
|
||||
)
|
||||
|
||||
return (
|
||||
<ExternalLink href={chainId && blockNumber ? getEtherscanLink(chainId, blockNumber.toString(), 'block') : ''}>
|
||||
<ExternalLink
|
||||
href={chainId && blockNumber ? getExplorerLink(chainId, blockNumber.toString(), ExplorerDataType.BLOCK) : ''}
|
||||
>
|
||||
<StyledPolling onMouseEnter={() => setIsHover(true)} onMouseLeave={() => setIsHover(false)}>
|
||||
<StyledPollingNumber breathe={isMounting} hovering={isHover}>
|
||||
{blockNumber}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { AlertTriangle, X } from 'react-feather'
|
||||
import { useURLWarningToggle, useURLWarningVisible } from '../../state/user/hooks'
|
||||
import { isMobile } from 'react-device-detect'
|
||||
|
||||
const PhishAlert = styled.div<{ isActive: any }>`
|
||||
width: 100%;
|
||||
padding: 6px 6px;
|
||||
background-color: ${({ theme }) => theme.blue1};
|
||||
color: white;
|
||||
font-size: 11px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
display: ${({ isActive }) => (isActive ? 'flex' : 'none')};
|
||||
`
|
||||
|
||||
export const StyledClose = styled(X)`
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`
|
||||
|
||||
export default function URLWarning() {
|
||||
const toggleURLWarning = useURLWarningToggle()
|
||||
const showURLWarning = useURLWarningVisible()
|
||||
|
||||
return isMobile ? (
|
||||
<PhishAlert isActive={showURLWarning}>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<AlertTriangle style={{ marginRight: 6 }} size={12} /> Make sure the URL is
|
||||
<code style={{ padding: '0 4px', display: 'inline', fontWeight: 'bold' }}>app.uniswap.org</code>
|
||||
</div>
|
||||
<StyledClose size={12} onClick={toggleURLWarning} />
|
||||
</PhishAlert>
|
||||
) : window.location.hostname === 'app.uniswap.org' ? (
|
||||
<PhishAlert isActive={showURLWarning}>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<AlertTriangle style={{ marginRight: 6 }} size={12} /> Always make sure the URL is
|
||||
<code style={{ padding: '0 4px', display: 'inline', fontWeight: 'bold' }}>app.uniswap.org</code> - bookmark it
|
||||
to be safe.
|
||||
</div>
|
||||
<StyledClose size={12} onClick={toggleURLWarning} />
|
||||
</PhishAlert>
|
||||
) : null
|
||||
}
|
||||
@@ -1,21 +1,23 @@
|
||||
import { ChainId, CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import React, { useMemo } from 'react'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { CHAIN_INFO, SupportedChainId } from 'constants/chains'
|
||||
import { useMemo } from 'react'
|
||||
import { X } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import tokenLogo from '../../assets/images/token-logo.png'
|
||||
import { UNI } from '../../constants'
|
||||
import { useTotalSupply } from '../../hooks/useTotalSupply'
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { UNI } from '../../constants/tokens'
|
||||
import { useMerkleDistributorContract } from '../../hooks/useContract'
|
||||
import useCurrentBlockTimestamp from '../../hooks/useCurrentBlockTimestamp'
|
||||
import { useTotalSupply } from '../../hooks/useTotalSupply'
|
||||
import useUSDCPrice from '../../hooks/useUSDCPrice'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useTotalUniEarned } from '../../state/stake/hooks'
|
||||
import { useAggregateUniBalance, useTokenBalance } from '../../state/wallet/hooks'
|
||||
import { ExternalLink, StyledInternalLink, TYPE, UniTokenAnimated } from '../../theme'
|
||||
import { computeUniCirculation } from '../../utils/computeUniCirculation'
|
||||
import useUSDCPrice from '../../hooks/useUSDCPrice'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { RowBetween } from '../Row'
|
||||
import { Break, CardBGImage, CardNoise, CardSection, DataCard } from '../earn/styled'
|
||||
import { RowBetween } from '../Row'
|
||||
|
||||
const ContentWrapper = styled(AutoColumn)`
|
||||
width: 100%;
|
||||
@@ -54,12 +56,12 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
|
||||
const unclaimedUni = useTokenBalance(useMerkleDistributorContract()?.address, uni)
|
||||
const circulation: CurrencyAmount<Token> | undefined = useMemo(
|
||||
() =>
|
||||
blockTimestamp && uni && chainId === ChainId.MAINNET
|
||||
? computeUniCirculation(uni, blockTimestamp, unclaimedUni)
|
||||
: totalSupply,
|
||||
blockTimestamp && uni && chainId === 1 ? computeUniCirculation(uni, blockTimestamp, unclaimedUni) : totalSupply,
|
||||
[blockTimestamp, chainId, totalSupply, unclaimedUni, uni]
|
||||
)
|
||||
|
||||
const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET]
|
||||
|
||||
return (
|
||||
<ContentWrapper gap="lg">
|
||||
<ModalUpper>
|
||||
@@ -67,7 +69,9 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
|
||||
<CardNoise />
|
||||
<CardSection gap="md">
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">Your UNI Breakdown</TYPE.white>
|
||||
<TYPE.white color="white">
|
||||
<Trans>Your UNI Breakdown</Trans>
|
||||
</TYPE.white>
|
||||
<StyledClose stroke="white" onClick={() => setShowUniBalanceModal(false)} />
|
||||
</RowBetween>
|
||||
</CardSection>
|
||||
@@ -83,16 +87,20 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
|
||||
</AutoColumn>
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">Balance:</TYPE.white>
|
||||
<TYPE.white color="white">
|
||||
<Trans>Balance:</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white color="white">{uniBalance?.toFixed(2, { groupSeparator: ',' })}</TYPE.white>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">Unclaimed:</TYPE.white>
|
||||
<TYPE.white color="white">
|
||||
<Trans>Unclaimed:</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white color="white">
|
||||
{uniToClaim?.toFixed(4, { groupSeparator: ',' })}{' '}
|
||||
{uniToClaim && uniToClaim.greaterThan('0') && (
|
||||
<StyledInternalLink onClick={() => setShowUniBalanceModal(false)} to="/uni">
|
||||
(claim)
|
||||
<Trans>(claim)</Trans>
|
||||
</StyledInternalLink>
|
||||
)}
|
||||
</TYPE.white>
|
||||
@@ -105,19 +113,27 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
|
||||
<CardSection gap="sm">
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">UNI price:</TYPE.white>
|
||||
<TYPE.white color="white">
|
||||
<Trans>UNI price:</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white color="white">${uniPrice?.toFixed(2) ?? '-'}</TYPE.white>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">UNI in circulation:</TYPE.white>
|
||||
<TYPE.white color="white">
|
||||
<Trans>UNI in circulation:</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white color="white">{circulation?.toFixed(0, { groupSeparator: ',' })}</TYPE.white>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">Total Supply</TYPE.white>
|
||||
<TYPE.white color="white">
|
||||
<Trans>Total Supply</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white color="white">{totalSupply?.toFixed(0, { groupSeparator: ',' })}</TYPE.white>
|
||||
</RowBetween>
|
||||
{uni && uni.chainId === ChainId.MAINNET ? (
|
||||
<ExternalLink href={`https://info.uniswap.org/token/${uni.address}`}>View UNI Analytics</ExternalLink>
|
||||
{uni && uni.chainId === 1 ? (
|
||||
<ExternalLink href={`${infoLink}/token/${uni.address}`}>
|
||||
<Trans>View UNI Analytics</Trans>
|
||||
</ExternalLink>
|
||||
) : null}
|
||||
</AutoColumn>
|
||||
</CardSection>
|
||||
|
||||
@@ -1,33 +1,28 @@
|
||||
import { ChainId } from '@uniswap/sdk-core'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import useScrollPosition from '@react-hook/window-scroll'
|
||||
import React, { useState } from 'react'
|
||||
import { Text } from 'rebass'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { CHAIN_INFO, SupportedChainId } from 'constants/chains'
|
||||
import { darken } from 'polished'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Moon, Sun } from 'react-feather'
|
||||
import { useState } from 'react'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { Text } from 'rebass'
|
||||
import { useShowClaimPopup, useToggleSelfClaimModal } from 'state/application/hooks'
|
||||
import { useUserHasAvailableClaim } from 'state/claim/hooks'
|
||||
import { useUserHasSubmittedClaim } from 'state/transactions/hooks'
|
||||
import { useDarkModeManager } from 'state/user/hooks'
|
||||
import { useETHBalances } from 'state/wallet/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import Logo from '../../assets/svg/logo.svg'
|
||||
import LogoDark from '../../assets/svg/logo_white.svg'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { useDarkModeManager } from '../../state/user/hooks'
|
||||
import { useETHBalances } from '../../state/wallet/hooks'
|
||||
import { CardNoise } from '../earn/styled'
|
||||
import { TYPE, ExternalLink } from '../../theme'
|
||||
|
||||
import { YellowCard } from '../Card'
|
||||
import Menu from '../Menu'
|
||||
|
||||
import Row, { RowFixed } from '../Row'
|
||||
import Web3Status from '../Web3Status'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { ExternalLink, TYPE } from '../../theme'
|
||||
import ClaimModal from '../claim/ClaimModal'
|
||||
import { useToggleSelfClaimModal, useShowClaimPopup } from '../../state/application/hooks'
|
||||
import { useUserHasAvailableClaim } from '../../state/claim/hooks'
|
||||
import { useUserHasSubmittedClaim } from '../../state/transactions/hooks'
|
||||
import { Dots } from '../swap/styleds'
|
||||
import { CardNoise } from '../earn/styled'
|
||||
import Menu from '../Menu'
|
||||
import Modal from '../Modal'
|
||||
import Row from '../Row'
|
||||
import { Dots } from '../swap/styleds'
|
||||
import Web3Status from '../Web3Status'
|
||||
import NetworkCard from './NetworkCard'
|
||||
import UniBalanceContent from './UniBalanceContent'
|
||||
|
||||
const HeaderFrame = styled.div<{ showBackground: boolean }>`
|
||||
@@ -43,23 +38,27 @@ const HeaderFrame = styled.div<{ showBackground: boolean }>`
|
||||
padding: 1rem;
|
||||
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.bg0} 50% )}}`};
|
||||
background-position: ${({ showBackground }) => (showBackground ? '0 -100%' : '0 0')};
|
||||
background-size: 100% 200%;
|
||||
box-shadow: 0px 0px 0px 1px ${({ theme, showBackground }) => (showBackground ? theme.bg2 : 'transparent;')};
|
||||
transition: background-position .1s, box-shadow .1s;
|
||||
transition: background-position 0.1s, box-shadow 0.1s;
|
||||
background-blend-mode: hard-light;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToLarge`
|
||||
grid-template-columns: 48px 1fr 1fr;
|
||||
`};
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
padding: 1rem;
|
||||
grid-template-columns: 120px 1fr;
|
||||
|
||||
grid-template-columns: 1fr 1fr;
|
||||
`};
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToExtraSmall`
|
||||
padding: 1rem;
|
||||
`}
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
padding: 1rem;
|
||||
grid-template-columns: 36px 1fr;
|
||||
`};
|
||||
`
|
||||
|
||||
const HeaderControls = styled.div`
|
||||
@@ -67,23 +66,6 @@ const HeaderControls = styled.div`
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-self: flex-end;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
justify-self: center;
|
||||
width: 100%;
|
||||
max-width: 960px;
|
||||
padding: 1rem;
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
z-index: 99;
|
||||
height: 72px;
|
||||
border-radius: 12px 12px 0 0;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
`};
|
||||
`
|
||||
|
||||
const HeaderElement = styled.div`
|
||||
@@ -96,22 +78,10 @@ const HeaderElement = styled.div`
|
||||
}
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
flex-direction: row-reverse;
|
||||
align-items: center;
|
||||
`};
|
||||
`
|
||||
|
||||
const HeaderElementWrap = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const HeaderRow = styled(RowFixed)`
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
width: 100%;
|
||||
`};
|
||||
`
|
||||
|
||||
const HeaderLinks = styled(Row)`
|
||||
justify-self: center;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
@@ -122,8 +92,25 @@ const HeaderLinks = styled(Row)`
|
||||
grid-auto-flow: column;
|
||||
grid-gap: 10px;
|
||||
overflow: auto;
|
||||
align-items: center;
|
||||
${({ theme }) => theme.mediaWidth.upToLarge`
|
||||
justify-self: start;
|
||||
`};
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
justify-self: flex-end;
|
||||
justify-self: center;
|
||||
`};
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
justify-self: center;
|
||||
z-index: 99;
|
||||
position: fixed;
|
||||
bottom: 0; right: 50%;
|
||||
transform: translate(50%,-50%);
|
||||
margin: 0 auto;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
border: 1px solid ${({ theme }) => theme.bg2};
|
||||
box-shadow: 0px 6px 10px rgb(0 0 0 / 2%);
|
||||
`};
|
||||
`
|
||||
|
||||
@@ -131,7 +118,7 @@ const AccountElement = styled.div<{ active: boolean }>`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg2)};
|
||||
background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg1)};
|
||||
border-radius: 12px;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
@@ -165,25 +152,6 @@ const UNIWrapper = styled.span`
|
||||
}
|
||||
`
|
||||
|
||||
const HideSmall = styled.span`
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
display: none;
|
||||
`};
|
||||
`
|
||||
|
||||
const NetworkCard = styled(YellowCard)`
|
||||
border-radius: 12px;
|
||||
padding: 8px 12px;
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
margin: 0;
|
||||
margin-right: 0.5rem;
|
||||
width: initial;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex-shrink: 1;
|
||||
`};
|
||||
`
|
||||
|
||||
const BalanceText = styled(Text)`
|
||||
${({ theme }) => theme.mediaWidth.upToExtraSmall`
|
||||
display: none;
|
||||
@@ -224,13 +192,15 @@ const StyledNavLink = styled(NavLink).attrs({
|
||||
text-decoration: none;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
font-size: 1rem;
|
||||
width: fit-content;
|
||||
font-weight: 500;
|
||||
padding: 8px 12px;
|
||||
|
||||
word-break: break-word;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
&.${activeClassName} {
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
justify-content: center;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
}
|
||||
@@ -265,56 +235,15 @@ const StyledExternalLink = styled(ExternalLink).attrs({
|
||||
:hover,
|
||||
:focus {
|
||||
color: ${({ theme }) => darken(0.1, theme.text1)};
|
||||
}
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToExtraSmall`
|
||||
display: none;
|
||||
`}
|
||||
`
|
||||
|
||||
export const StyledMenuButton = styled.button`
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 35px;
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
margin-left: 8px;
|
||||
padding: 0.15rem 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
:hover,
|
||||
:focus {
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
background-color: ${({ theme }) => theme.bg4};
|
||||
}
|
||||
|
||||
svg {
|
||||
margin-top: 2px;
|
||||
}
|
||||
> * {
|
||||
stroke: ${({ theme }) => theme.text1};
|
||||
text-decoration: none;
|
||||
}
|
||||
`
|
||||
|
||||
const NETWORK_LABELS: { [chainId in ChainId]?: string } = {
|
||||
[ChainId.RINKEBY]: 'Rinkeby',
|
||||
[ChainId.ROPSTEN]: 'Ropsten',
|
||||
[ChainId.GÖRLI]: 'Görli',
|
||||
[ChainId.KOVAN]: 'Kovan',
|
||||
}
|
||||
|
||||
export default function Header() {
|
||||
const { account, chainId } = useActiveWeb3React()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? '']
|
||||
// const [isDark] = useDarkModeManager()
|
||||
const [darkMode, toggleDarkMode] = useDarkModeManager()
|
||||
const [darkMode] = useDarkModeManager()
|
||||
|
||||
const toggleClaimModal = useToggleSelfClaimModal()
|
||||
|
||||
@@ -327,22 +256,21 @@ export default function Header() {
|
||||
|
||||
const scrollY = useScrollPosition()
|
||||
|
||||
const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET]
|
||||
return (
|
||||
<HeaderFrame showBackground={scrollY > 45}>
|
||||
<ClaimModal />
|
||||
<Modal isOpen={showUniBalanceModal} onDismiss={() => setShowUniBalanceModal(false)}>
|
||||
<UniBalanceContent setShowUniBalanceModal={setShowUniBalanceModal} />
|
||||
</Modal>
|
||||
<HeaderRow>
|
||||
<Title href=".">
|
||||
<UniIcon>
|
||||
<img width={'24px'} src={darkMode ? LogoDark : Logo} alt="logo" />
|
||||
</UniIcon>
|
||||
</Title>
|
||||
</HeaderRow>
|
||||
<Title href=".">
|
||||
<UniIcon>
|
||||
<img width={'24px'} src={darkMode ? LogoDark : Logo} alt="logo" />
|
||||
</UniIcon>
|
||||
</Title>
|
||||
<HeaderLinks>
|
||||
<StyledNavLink id={`swap-nav-link`} to={'/swap'}>
|
||||
{t('swap')}
|
||||
<Trans>Swap</Trans>
|
||||
</StyledNavLink>
|
||||
<StyledNavLink
|
||||
id={`pool-nav-link`}
|
||||
@@ -355,27 +283,33 @@ export default function Header() {
|
||||
pathname.startsWith('/find')
|
||||
}
|
||||
>
|
||||
{t('pool')}
|
||||
<Trans>Pool</Trans>
|
||||
</StyledNavLink>
|
||||
<StyledNavLink id={`stake-nav-link`} to={'/vote'}>
|
||||
Vote
|
||||
</StyledNavLink>
|
||||
<StyledExternalLink id={`stake-nav-link`} href={'https://info.uniswap.org'}>
|
||||
Charts <span style={{ fontSize: '11px', textDecoration: 'none !important' }}>↗</span>
|
||||
{chainId && chainId === SupportedChainId.MAINNET && (
|
||||
<StyledNavLink id={`stake-nav-link`} to={'/vote'}>
|
||||
<Trans>Vote</Trans>
|
||||
</StyledNavLink>
|
||||
)}
|
||||
<StyledExternalLink id={`stake-nav-link`} href={infoLink}>
|
||||
<Trans>Charts</Trans>
|
||||
<sup>↗</sup>
|
||||
</StyledExternalLink>
|
||||
</HeaderLinks>
|
||||
|
||||
<HeaderControls>
|
||||
<NetworkCard />
|
||||
<HeaderElement>
|
||||
<HideSmall>
|
||||
{chainId && NETWORK_LABELS[chainId] && (
|
||||
<NetworkCard title={NETWORK_LABELS[chainId]}>{NETWORK_LABELS[chainId]}</NetworkCard>
|
||||
)}
|
||||
</HideSmall>
|
||||
{availableClaim && !showClaimPopup && (
|
||||
<UNIWrapper onClick={toggleClaimModal}>
|
||||
<UNIAmount active={!!account && !availableClaim} style={{ pointerEvents: 'auto' }}>
|
||||
<TYPE.white padding="0 2px">
|
||||
{claimTxn && !claimTxn?.receipt ? <Dots>Claiming UNI</Dots> : 'Claim UNI'}
|
||||
{claimTxn && !claimTxn?.receipt ? (
|
||||
<Dots>
|
||||
<Trans>Claiming UNI</Trans>
|
||||
</Dots>
|
||||
) : (
|
||||
<Trans>Claim UNI</Trans>
|
||||
)}
|
||||
</TYPE.white>
|
||||
</UNIAmount>
|
||||
<CardNoise />
|
||||
@@ -384,18 +318,13 @@ export default function Header() {
|
||||
<AccountElement active={!!account} style={{ pointerEvents: 'auto' }}>
|
||||
{account && userEthBalance ? (
|
||||
<BalanceText style={{ flexShrink: 0 }} pl="0.75rem" pr="0.5rem" fontWeight={500}>
|
||||
{userEthBalance?.toSignificant(4)} ETH
|
||||
<Trans>{userEthBalance?.toSignificant(3)} ETH</Trans>
|
||||
</BalanceText>
|
||||
) : null}
|
||||
<Web3Status />
|
||||
</AccountElement>
|
||||
</HeaderElement>
|
||||
<HeaderElementWrap>
|
||||
<StyledMenuButton onClick={() => toggleDarkMode()}>
|
||||
{darkMode ? <Moon size={20} /> : <Sun size={20} />}
|
||||
</StyledMenuButton>
|
||||
<Menu />
|
||||
</HeaderElementWrap>
|
||||
</HeaderElement>
|
||||
</HeaderControls>
|
||||
</HeaderFrame>
|
||||
)
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import Tooltip from 'components/Tooltip'
|
||||
import React, { useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
const TextWrapper = styled.span<{ margin: boolean; link?: boolean; fontSize?: string; adjustSize?: boolean }>`
|
||||
cursor: auto;
|
||||
margin-left: ${({ margin }) => margin && '4px'};
|
||||
color: ${({ theme, link }) => (link ? theme.blue1 : theme.text1)};
|
||||
font-size: ${({ fontSize }) => fontSize ?? 'inherit'};
|
||||
@@ -22,7 +21,7 @@ const HoverInlineText = ({
|
||||
link,
|
||||
...rest
|
||||
}: {
|
||||
text: string
|
||||
text?: string
|
||||
maxCharacters?: number
|
||||
margin?: boolean
|
||||
adjustSize?: boolean
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import Jazzicon from 'jazzicon'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import Jazzicon from '@metamask/jazzicon'
|
||||
|
||||
const StyledIdenticonContainer = styled.div`
|
||||
height: 1rem;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React, { useState, useCallback, useEffect } from 'react'
|
||||
import { LightCard } from 'components/Card'
|
||||
import { RowBetween } from 'components/Row'
|
||||
import { useState, useCallback, useEffect, ReactNode } from 'react'
|
||||
import { OutlineCard } from 'components/Card'
|
||||
import { Input as NumericalInput } from '../NumericalInput'
|
||||
import styled, { keyframes } from 'styled-components'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { ButtonPrimary } from 'components/Button'
|
||||
import { ButtonGray } from 'components/Button'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import { formattedFeeAmount } from 'utils'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Plus, Minus } from 'react-feather'
|
||||
|
||||
const pulse = (color: string) => keyframes`
|
||||
0% {
|
||||
@@ -23,25 +23,37 @@ const pulse = (color: string) => keyframes`
|
||||
}
|
||||
`
|
||||
|
||||
const SmallButton = styled(ButtonPrimary)`
|
||||
/* background-color: ${({ theme }) => theme.bg2}; */
|
||||
border-radius: 8px;
|
||||
padding: 4px 6px;
|
||||
width: 48%;
|
||||
const InputRow = styled.div`
|
||||
display: grid;
|
||||
|
||||
grid-template-columns: 30px 1fr 30px;
|
||||
`
|
||||
|
||||
const FocusedOutlineCard = styled(LightCard)<{ active?: boolean; pulsing?: boolean }>`
|
||||
const SmallButton = styled(ButtonGray)`
|
||||
border-radius: 8px;
|
||||
padding: 4px;
|
||||
`
|
||||
|
||||
const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boolean }>`
|
||||
border-color: ${({ active, theme }) => active && theme.blue1};
|
||||
padding: 12px;
|
||||
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.blue1)} 0.8s linear;
|
||||
`
|
||||
|
||||
const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
|
||||
/* background-color: ${({ theme }) => theme.bg0}; */
|
||||
background-color: transparent;
|
||||
text-align: center;
|
||||
margin-right: 12px;
|
||||
width: 100%;
|
||||
font-weight: 500;
|
||||
padding: 0 10px;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
font-size: 16px;
|
||||
`};
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToExtraSmall`
|
||||
font-size: 12px;
|
||||
`};
|
||||
`
|
||||
|
||||
const InputTitle = styled(TYPE.small)`
|
||||
@@ -50,16 +62,22 @@ const InputTitle = styled(TYPE.small)`
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
const ButtonLabel = styled(TYPE.white)<{ disabled: boolean }>`
|
||||
color: ${({ theme, disabled }) => (disabled ? theme.text2 : theme.text1)} !important;
|
||||
`
|
||||
|
||||
interface StepCounterProps {
|
||||
value: string
|
||||
onUserInput: (value: string) => void
|
||||
decrement: () => string
|
||||
increment: () => string
|
||||
decrementDisabled?: boolean
|
||||
incrementDisabled?: boolean
|
||||
feeAmount?: FeeAmount
|
||||
label?: string
|
||||
width?: string
|
||||
locked?: boolean // disable input
|
||||
title: string
|
||||
title: ReactNode
|
||||
tokenA: string | undefined
|
||||
tokenB: string | undefined
|
||||
}
|
||||
@@ -68,7 +86,8 @@ const StepCounter = ({
|
||||
value,
|
||||
decrement,
|
||||
increment,
|
||||
feeAmount,
|
||||
decrementDisabled = false,
|
||||
incrementDisabled = false,
|
||||
width,
|
||||
locked,
|
||||
onUserInput,
|
||||
@@ -86,9 +105,6 @@ const StepCounter = ({
|
||||
// animation if parent value updates local value
|
||||
const [pulsing, setPulsing] = useState<boolean>(false)
|
||||
|
||||
// format fee amount
|
||||
const feeAmountFormatted = feeAmount ? formattedFeeAmount(feeAmount * 2) : ''
|
||||
|
||||
const handleOnFocus = () => {
|
||||
setUseLocalValue(true)
|
||||
setActive(true)
|
||||
@@ -125,33 +141,45 @@ const StepCounter = ({
|
||||
|
||||
return (
|
||||
<FocusedOutlineCard pulsing={pulsing} active={active} onFocus={handleOnFocus} onBlur={handleOnBlur} width={width}>
|
||||
<AutoColumn gap="6px" style={{ marginBottom: '12px' }}>
|
||||
<AutoColumn gap="6px">
|
||||
<InputTitle fontSize={12} textAlign="center">
|
||||
{title}
|
||||
</InputTitle>
|
||||
<StyledInput
|
||||
className="rate-input-0"
|
||||
value={localValue}
|
||||
fontSize="20px"
|
||||
disabled={locked}
|
||||
onUserInput={(val) => {
|
||||
setLocalValue(val)
|
||||
}}
|
||||
/>
|
||||
|
||||
<InputRow>
|
||||
{!locked && (
|
||||
<SmallButton onClick={handleDecrement} disabled={decrementDisabled}>
|
||||
<ButtonLabel disabled={decrementDisabled} fontSize="12px">
|
||||
<Minus size={18} />
|
||||
</ButtonLabel>
|
||||
</SmallButton>
|
||||
)}
|
||||
|
||||
<StyledInput
|
||||
className="rate-input-0"
|
||||
value={localValue}
|
||||
fontSize="20px"
|
||||
disabled={locked}
|
||||
onUserInput={(val) => {
|
||||
setLocalValue(val)
|
||||
}}
|
||||
/>
|
||||
|
||||
{!locked && (
|
||||
<SmallButton onClick={handleIncrement} disabled={incrementDisabled}>
|
||||
<ButtonLabel disabled={incrementDisabled} fontSize="12px">
|
||||
<Plus size={18} />
|
||||
</ButtonLabel>
|
||||
</SmallButton>
|
||||
)}
|
||||
</InputRow>
|
||||
|
||||
<InputTitle fontSize={12} textAlign="center">
|
||||
{tokenB + ' per ' + tokenA}
|
||||
<Trans>
|
||||
{tokenB} per {tokenA}
|
||||
</Trans>
|
||||
</InputTitle>
|
||||
</AutoColumn>
|
||||
{!locked ? (
|
||||
<RowBetween>
|
||||
<SmallButton onClick={handleDecrement}>
|
||||
<TYPE.white fontSize="12px">-{feeAmountFormatted}%</TYPE.white>
|
||||
</SmallButton>
|
||||
<SmallButton onClick={handleIncrement}>
|
||||
<TYPE.white fontSize="12px">+{feeAmountFormatted}%</TYPE.white>
|
||||
</SmallButton>
|
||||
</RowBetween>
|
||||
) : null}
|
||||
</FocusedOutlineCard>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
import { Story } from '@storybook/react/types-6-0'
|
||||
import React from 'react'
|
||||
// import Row, { RowFixed } from 'components/Row'
|
||||
import styled from 'styled-components/macro'
|
||||
import Component, { LineChartProps } from './'
|
||||
import { dummyData } from './data'
|
||||
// import { AutoColumn } from 'components/Column'
|
||||
// import { TYPE } from 'theme'
|
||||
// import DoubleCurrencyLogo from 'components/DoubleLogo'
|
||||
// import { MKR } from 'constants'
|
||||
// import { ETHER } from '@uniswap/sdk'
|
||||
// import LineChart from '.'
|
||||
|
||||
const Wrapper = styled.div`
|
||||
margin: 1em 2em;
|
||||
max-width: 500px;
|
||||
& > * {
|
||||
font-size: 1em;
|
||||
}
|
||||
`
|
||||
|
||||
export default {
|
||||
title: 'Charts',
|
||||
argTypes: {
|
||||
disabled: { control: { type: 'boolean' } },
|
||||
onClick: { action: 'clicked' },
|
||||
},
|
||||
decorators: [
|
||||
(Component: Story) => (
|
||||
<Wrapper>
|
||||
<Component />
|
||||
</Wrapper>
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
const Template: Story<LineChartProps> = (args) => <Component {...args}>{args.children}</Component>
|
||||
|
||||
export const Basic = Template.bind({})
|
||||
Basic.args = { data: dummyData }
|
||||
|
||||
// const Full = () => {
|
||||
// const [value, setValue] = useState<number | undefined>(dummyData[dummyData.length - 1].value)
|
||||
|
||||
// const dummyUSDPrice = 410 // used for conversion
|
||||
|
||||
// const TopLeftContent = () => (
|
||||
// <AutoColumn gap="md">
|
||||
// <RowFixed align="center">
|
||||
// <DoubleCurrencyLogo currency0={MKR} currency1={ETHER} size={20} />{' '}
|
||||
// <TYPE.main fontSize="20px" color="white" ml="10px">
|
||||
// ETH / MKR
|
||||
// </TYPE.main>
|
||||
// </RowFixed>
|
||||
// <Row>
|
||||
// <TYPE.main fontSize="20px" color="white">
|
||||
// {value} MKR
|
||||
// </TYPE.main>
|
||||
// <TYPE.main color="#565A69" fontSize="20px" ml="10px">
|
||||
// ($
|
||||
// {value
|
||||
// ? (value * dummyUSDPrice).toLocaleString('USD', {
|
||||
// currency: 'USD',
|
||||
// minimumFractionDigits: 2
|
||||
// })
|
||||
// : null}{' '}
|
||||
// )
|
||||
// </TYPE.main>
|
||||
// </Row>
|
||||
// </AutoColumn>
|
||||
// )
|
||||
|
||||
// return (
|
||||
// <Wrapper>
|
||||
// <LineChart data={dummyData} setValue={setValue} topLeft={<TopLeftContent />} />
|
||||
// </Wrapper>
|
||||
// )
|
||||
// }
|
||||
|
||||
// export const FullVersion = Template.bind(() => <LineChart data={dummyData} />)
|
||||
// Full.args = { data: dummyData }
|
||||
// Full.decorators = [
|
||||
// (Story: any) => {
|
||||
// return (
|
||||
// <Wrapper>
|
||||
// <LineChart data={dummyData} />
|
||||
// </Wrapper>
|
||||
// )
|
||||
// }
|
||||
// ]
|
||||
@@ -1,152 +0,0 @@
|
||||
export const dummyData = [
|
||||
{ time: '2018-10-19', value: 35.98 },
|
||||
{ time: '2018-10-22', value: 35.75 },
|
||||
{ time: '2018-10-23', value: 35.65 },
|
||||
{ time: '2018-10-24', value: 34.12 },
|
||||
{ time: '2018-10-25', value: 35.84 },
|
||||
{ time: '2018-10-26', value: 35.24 },
|
||||
{ time: '2018-10-29', value: 35.99 },
|
||||
{ time: '2018-10-30', value: 37.71 },
|
||||
{ time: '2018-10-31', value: 38.14 },
|
||||
{ time: '2018-11-01', value: 37.95 },
|
||||
{ time: '2018-11-02', value: 37.66 },
|
||||
{ time: '2018-11-05', value: 38.02 },
|
||||
{ time: '2018-11-06', value: 37.73 },
|
||||
{ time: '2018-11-07', value: 38.3 },
|
||||
{ time: '2018-11-08', value: 38.3 },
|
||||
{ time: '2018-11-09', value: 38.34 },
|
||||
{ time: '2018-11-12', value: 38.0 },
|
||||
{ time: '2018-11-13', value: 37.72 },
|
||||
{ time: '2018-11-14', value: 38.29 },
|
||||
{ time: '2018-11-15', value: 38.49 },
|
||||
{ time: '2018-11-16', value: 38.59 },
|
||||
{ time: '2018-11-19', value: 38.18 },
|
||||
{ time: '2018-11-20', value: 36.76 },
|
||||
{ time: '2018-11-21', value: 37.51 },
|
||||
{ time: '2018-11-23', value: 37.39 },
|
||||
{ time: '2018-11-26', value: 37.77 },
|
||||
{ time: '2018-11-27', value: 38.36 },
|
||||
{ time: '2018-11-28', value: 39.06 },
|
||||
{ time: '2018-11-29', value: 39.42 },
|
||||
{ time: '2018-11-30', value: 39.01 },
|
||||
{ time: '2018-12-03', value: 39.15 },
|
||||
{ time: '2018-12-04', value: 37.69 },
|
||||
{ time: '2018-12-06', value: 37.88 },
|
||||
{ time: '2018-12-07', value: 37.41 },
|
||||
{ time: '2018-12-10', value: 37.35 },
|
||||
{ time: '2018-12-11', value: 36.84 },
|
||||
{ time: '2018-12-12', value: 36.98 },
|
||||
{ time: '2018-12-13', value: 36.76 },
|
||||
{ time: '2018-12-14', value: 36.34 },
|
||||
{ time: '2018-12-17', value: 36.21 },
|
||||
{ time: '2018-12-18', value: 35.65 },
|
||||
{ time: '2018-12-19', value: 35.19 },
|
||||
{ time: '2018-12-20', value: 34.62 },
|
||||
{ time: '2018-12-21', value: 33.75 },
|
||||
{ time: '2018-12-24', value: 33.07 },
|
||||
{ time: '2018-12-26', value: 34.14 },
|
||||
{ time: '2018-12-27', value: 34.47 },
|
||||
{ time: '2018-12-28', value: 34.35 },
|
||||
{ time: '2018-12-31', value: 34.05 },
|
||||
{ time: '2019-01-02', value: 34.37 },
|
||||
{ time: '2019-01-03', value: 34.64 },
|
||||
{ time: '2019-01-04', value: 35.81 },
|
||||
{ time: '2019-01-07', value: 35.43 },
|
||||
{ time: '2019-01-08', value: 35.72 },
|
||||
{ time: '2019-01-09', value: 36.06 },
|
||||
{ time: '2019-01-10', value: 35.82 },
|
||||
{ time: '2019-01-11', value: 35.63 },
|
||||
{ time: '2019-01-14', value: 35.77 },
|
||||
{ time: '2019-01-15', value: 35.83 },
|
||||
{ time: '2019-01-16', value: 35.9 },
|
||||
{ time: '2019-01-17', value: 35.91 },
|
||||
{ time: '2019-01-18', value: 36.21 },
|
||||
{ time: '2019-01-22', value: 34.97 },
|
||||
{ time: '2019-01-23', value: 36.89 },
|
||||
{ time: '2019-01-24', value: 36.24 },
|
||||
{ time: '2019-01-25', value: 35.78 },
|
||||
{ time: '2019-01-28', value: 35.37 },
|
||||
{ time: '2019-01-29', value: 36.08 },
|
||||
{ time: '2019-01-30', value: 35.43 },
|
||||
{ time: '2019-01-31', value: 36.57 },
|
||||
{ time: '2019-02-01', value: 36.79 },
|
||||
{ time: '2019-02-04', value: 36.77 },
|
||||
{ time: '2019-02-05', value: 37.15 },
|
||||
{ time: '2019-02-06', value: 37.17 },
|
||||
{ time: '2019-02-07', value: 37.68 },
|
||||
{ time: '2019-02-08', value: 37.6 },
|
||||
{ time: '2019-02-11', value: 37.0 },
|
||||
{ time: '2019-02-12', value: 37.24 },
|
||||
{ time: '2019-02-13', value: 37.03 },
|
||||
{ time: '2019-02-14', value: 37.26 },
|
||||
{ time: '2019-02-15', value: 37.77 },
|
||||
{ time: '2019-02-19', value: 37.55 },
|
||||
{ time: '2019-02-20', value: 37.79 },
|
||||
{ time: '2019-02-21', value: 38.47 },
|
||||
{ time: '2019-02-22', value: 38.61 },
|
||||
{ time: '2019-02-25', value: 38.57 },
|
||||
{ time: '2019-02-26', value: 38.8 },
|
||||
{ time: '2019-02-27', value: 38.53 },
|
||||
{ time: '2019-02-28', value: 38.67 },
|
||||
{ time: '2019-03-01', value: 39.1 },
|
||||
{ time: '2019-03-04', value: 38.73 },
|
||||
{ time: '2019-03-05', value: 38.72 },
|
||||
{ time: '2019-03-06', value: 38.61 },
|
||||
{ time: '2019-03-07', value: 38.38 },
|
||||
{ time: '2019-03-08', value: 38.19 },
|
||||
{ time: '2019-03-11', value: 39.17 },
|
||||
{ time: '2019-03-12', value: 39.49 },
|
||||
{ time: '2019-03-13', value: 39.56 },
|
||||
{ time: '2019-03-14', value: 39.87 },
|
||||
{ time: '2019-03-15', value: 40.47 },
|
||||
{ time: '2019-03-18', value: 39.92 },
|
||||
{ time: '2019-03-19', value: 39.78 },
|
||||
{ time: '2019-03-20', value: 39.47 },
|
||||
{ time: '2019-03-21', value: 40.05 },
|
||||
{ time: '2019-03-22', value: 39.46 },
|
||||
{ time: '2019-03-25', value: 39.18 },
|
||||
{ time: '2019-03-26', value: 39.63 },
|
||||
{ time: '2019-03-27', value: 40.21 },
|
||||
{ time: '2019-03-28', value: 40.42 },
|
||||
{ time: '2019-03-29', value: 39.98 },
|
||||
{ time: '2019-04-01', value: 40.31 },
|
||||
{ time: '2019-04-02', value: 40.02 },
|
||||
{ time: '2019-04-03', value: 40.27 },
|
||||
{ time: '2019-04-04', value: 40.41 },
|
||||
{ time: '2019-04-05', value: 40.42 },
|
||||
{ time: '2019-04-08', value: 40.71 },
|
||||
{ time: '2019-04-09', value: 41.04 },
|
||||
{ time: '2019-04-10', value: 41.08 },
|
||||
{ time: '2019-04-11', value: 41.04 },
|
||||
{ time: '2019-04-12', value: 41.3 },
|
||||
{ time: '2019-04-15', value: 41.78 },
|
||||
{ time: '2019-04-16', value: 41.97 },
|
||||
{ time: '2019-04-17', value: 42.57 },
|
||||
{ time: '2019-04-18', value: 42.43 },
|
||||
{ time: '2019-04-22', value: 42.0 },
|
||||
{ time: '2019-04-23', value: 41.99 },
|
||||
{ time: '2019-04-24', value: 41.85 },
|
||||
{ time: '2019-04-25', value: 42.93 },
|
||||
{ time: '2019-04-26', value: 43.08 },
|
||||
{ time: '2019-04-29', value: 43.45 },
|
||||
{ time: '2019-04-30', value: 43.53 },
|
||||
{ time: '2019-05-01', value: 43.42 },
|
||||
{ time: '2019-05-02', value: 42.65 },
|
||||
{ time: '2019-05-03', value: 43.29 },
|
||||
{ time: '2019-05-06', value: 43.3 },
|
||||
{ time: '2019-05-07', value: 42.76 },
|
||||
{ time: '2019-05-08', value: 42.55 },
|
||||
{ time: '2019-05-09', value: 42.92 },
|
||||
{ time: '2019-05-10', value: 43.15 },
|
||||
{ time: '2019-05-13', value: 42.28 },
|
||||
{ time: '2019-05-14', value: 42.91 },
|
||||
{ time: '2019-05-15', value: 42.49 },
|
||||
{ time: '2019-05-16', value: 43.19 },
|
||||
{ time: '2019-05-17', value: 43.54 },
|
||||
{ time: '2019-05-20', value: 42.78 },
|
||||
{ time: '2019-05-21', value: 43.29 },
|
||||
{ time: '2019-05-22', value: 43.3 },
|
||||
{ time: '2019-05-23', value: 42.73 },
|
||||
{ time: '2019-05-24', value: 42.67 },
|
||||
{ time: '2019-05-28', value: 42.75 },
|
||||
]
|
||||
@@ -1,169 +0,0 @@
|
||||
import React, { useRef, useState, useEffect, useCallback, Dispatch, SetStateAction, ReactNode } from 'react'
|
||||
import { createChart, IChartApi } from 'lightweight-charts'
|
||||
import { darken } from 'polished'
|
||||
import { RowBetween } from 'components/Row'
|
||||
import Card from '../Card'
|
||||
import styled from 'styled-components/macro'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
|
||||
const Wrapper = styled(Card)`
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
background-color: ${({ theme }) => theme.bg0}
|
||||
flex-direction: column;
|
||||
> * {
|
||||
font-size: 1rem;
|
||||
}
|
||||
`
|
||||
|
||||
const DEFAULT_HEIGHT = 300
|
||||
|
||||
export type LineChartProps = {
|
||||
data: any[]
|
||||
color?: string | undefined
|
||||
height?: number | undefined
|
||||
setValue?: Dispatch<SetStateAction<number | undefined>> // used for value on hover
|
||||
topLeft?: ReactNode | undefined
|
||||
topRight?: ReactNode | undefined
|
||||
bottomLeft?: ReactNode | undefined
|
||||
bottomRight?: ReactNode | undefined
|
||||
} & React.HTMLAttributes<HTMLDivElement>
|
||||
|
||||
const LineChart = ({
|
||||
data,
|
||||
color = '#56B2A4',
|
||||
setValue,
|
||||
topLeft,
|
||||
topRight,
|
||||
bottomLeft,
|
||||
bottomRight,
|
||||
height = DEFAULT_HEIGHT,
|
||||
...rest
|
||||
}: LineChartProps) => {
|
||||
const theme = useTheme()
|
||||
|
||||
const chartRef = useRef<HTMLDivElement>(null)
|
||||
const [chartCreated, setChart] = useState<IChartApi | undefined>()
|
||||
|
||||
// for reseting value on hover exit
|
||||
const currenValue = data[data.length - 1].value
|
||||
|
||||
const handleResize = useCallback(() => {
|
||||
if (chartCreated && chartRef?.current?.parentElement) {
|
||||
chartCreated.resize(chartRef.current.parentElement.clientWidth - 32, height)
|
||||
chartCreated.timeScale().fitContent()
|
||||
chartCreated.timeScale().scrollToPosition(0, false)
|
||||
}
|
||||
}, [chartCreated, chartRef, height])
|
||||
|
||||
// add event listener for resize
|
||||
const isClient = typeof window === 'object'
|
||||
useEffect(() => {
|
||||
if (!isClient) {
|
||||
return
|
||||
}
|
||||
window.addEventListener('resize', handleResize)
|
||||
return () => window.removeEventListener('resize', handleResize)
|
||||
}, [isClient, chartRef, handleResize]) // Empty array ensures that effect is only run on mount and unmount
|
||||
|
||||
const textColor = theme.text2
|
||||
|
||||
// if chart not instantiated in canvas, create it
|
||||
useEffect(() => {
|
||||
if (!chartCreated && data && !!chartRef?.current?.parentElement) {
|
||||
const chart = createChart(chartRef.current, {
|
||||
height: height,
|
||||
width: chartRef.current.parentElement.clientWidth - 32,
|
||||
layout: {
|
||||
backgroundColor: 'transparent',
|
||||
textColor: textColor,
|
||||
fontFamily: 'Inter',
|
||||
},
|
||||
rightPriceScale: {
|
||||
scaleMargins: {
|
||||
top: 0.1,
|
||||
bottom: 0.1,
|
||||
},
|
||||
borderVisible: false,
|
||||
},
|
||||
timeScale: {
|
||||
borderVisible: false,
|
||||
},
|
||||
watermark: {
|
||||
color: 'rgba(0, 0, 0, 0)',
|
||||
},
|
||||
grid: {
|
||||
horzLines: {
|
||||
visible: false,
|
||||
},
|
||||
vertLines: {
|
||||
visible: false,
|
||||
},
|
||||
},
|
||||
crosshair: {
|
||||
horzLine: {
|
||||
visible: true,
|
||||
style: 3,
|
||||
width: 1,
|
||||
color: '#505050',
|
||||
labelBackgroundColor: color,
|
||||
},
|
||||
vertLine: {
|
||||
visible: true,
|
||||
style: 3,
|
||||
width: 1,
|
||||
color: '#505050',
|
||||
labelBackgroundColor: color,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const series = chart.addAreaSeries({
|
||||
lineColor: color,
|
||||
topColor: darken(0.4, color),
|
||||
bottomColor: theme.bg0,
|
||||
lineWidth: 2,
|
||||
priceLineVisible: false,
|
||||
})
|
||||
|
||||
series.setData(data)
|
||||
|
||||
// update the title when hovering on the chart
|
||||
chart.subscribeCrosshairMove(function (param) {
|
||||
if (
|
||||
chartRef?.current &&
|
||||
(param === undefined ||
|
||||
param.time === undefined ||
|
||||
(param && param.point && param.point.x < 0) ||
|
||||
(param && param.point && param.point.x > chartRef.current.clientWidth) ||
|
||||
(param && param.point && param.point.y < 0) ||
|
||||
(param && param.point && param.point.y > height))
|
||||
) {
|
||||
setValue && setValue(currenValue)
|
||||
} else {
|
||||
const price = parseFloat(param.seriesPrices.get(series)?.toString() ?? currenValue)
|
||||
setValue && setValue(price)
|
||||
}
|
||||
})
|
||||
chart.timeScale().fitContent()
|
||||
setChart(chart)
|
||||
}
|
||||
}, [color, chartCreated, currenValue, data, height, setValue, textColor, theme])
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<RowBetween>
|
||||
{topLeft ?? null}
|
||||
{topRight ?? null}
|
||||
</RowBetween>
|
||||
<div ref={chartRef} id={'line-chart'} {...rest} />
|
||||
<RowBetween>
|
||||
{bottomLeft ?? null}
|
||||
{bottomRight ?? null}
|
||||
</RowBetween>
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export default LineChart
|
||||
44
src/components/LiquidityChartRangeInput/Area.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import { area, curveStepAfter, ScaleLinear } from 'd3'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ChartEntry } from './types'
|
||||
import inRange from 'lodash/inRange'
|
||||
|
||||
const Path = styled.path<{ fill: string | undefined }>`
|
||||
opacity: 0.5;
|
||||
stroke: ${({ fill, theme }) => fill ?? theme.blue2};
|
||||
fill: ${({ fill, theme }) => fill ?? theme.blue2};
|
||||
`
|
||||
|
||||
export const Area = ({
|
||||
series,
|
||||
xScale,
|
||||
yScale,
|
||||
xValue,
|
||||
yValue,
|
||||
fill,
|
||||
}: {
|
||||
series: ChartEntry[]
|
||||
xScale: ScaleLinear<number, number>
|
||||
yScale: ScaleLinear<number, number>
|
||||
xValue: (d: ChartEntry) => number
|
||||
yValue: (d: ChartEntry) => number
|
||||
fill?: string | undefined
|
||||
}) =>
|
||||
useMemo(
|
||||
() => (
|
||||
<Path
|
||||
fill={fill}
|
||||
d={
|
||||
area()
|
||||
.curve(curveStepAfter)
|
||||
.x((d: unknown) => xScale(xValue(d as ChartEntry)))
|
||||
.y1((d: unknown) => yScale(yValue(d as ChartEntry)))
|
||||
.y0(yScale(0))(
|
||||
series.filter((d) => inRange(xScale(xValue(d)), 0, innerWidth)) as Iterable<[number, number]>
|
||||
) ?? undefined
|
||||
}
|
||||
/>
|
||||
),
|
||||
[fill, series, xScale, xValue, yScale, yValue]
|
||||
)
|
||||
43
src/components/LiquidityChartRangeInput/AxisBottom.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import { Axis as d3Axis, axisBottom, NumberValue, ScaleLinear, select } from 'd3'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
const StyledGroup = styled.g`
|
||||
line {
|
||||
display: none;
|
||||
}
|
||||
|
||||
text {
|
||||
color: ${({ theme }) => theme.text2};
|
||||
transform: translateY(5px);
|
||||
}
|
||||
`
|
||||
|
||||
const Axis = ({ axisGenerator }: { axisGenerator: d3Axis<NumberValue> }) => {
|
||||
const axisRef = (axis: SVGGElement) => {
|
||||
axis &&
|
||||
select(axis)
|
||||
.call(axisGenerator)
|
||||
.call((g) => g.select('.domain').remove())
|
||||
}
|
||||
|
||||
return <g ref={axisRef} />
|
||||
}
|
||||
|
||||
export const AxisBottom = ({
|
||||
xScale,
|
||||
innerHeight,
|
||||
offset = 0,
|
||||
}: {
|
||||
xScale: ScaleLinear<number, number>
|
||||
innerHeight: number
|
||||
offset?: number
|
||||
}) =>
|
||||
useMemo(
|
||||
() => (
|
||||
<StyledGroup transform={`translate(0, ${innerHeight + offset})`}>
|
||||
<Axis axisGenerator={axisBottom(xScale).ticks(6)} />
|
||||
</StyledGroup>
|
||||
),
|
||||
[innerHeight, offset, xScale]
|
||||
)
|
||||
265
src/components/LiquidityChartRangeInput/Brush.tsx
Normal file
@@ -0,0 +1,265 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { BrushBehavior, brushX, D3BrushEvent, ScaleLinear, select } from 'd3'
|
||||
import styled from 'styled-components/macro'
|
||||
import { brushHandleAccentPath, brushHandlePath, OffScreenHandle } from 'components/LiquidityChartRangeInput/svg'
|
||||
import usePrevious from 'hooks/usePrevious'
|
||||
|
||||
const Handle = styled.path<{ color: string }>`
|
||||
cursor: ew-resize;
|
||||
pointer-events: none;
|
||||
|
||||
stroke-width: 3;
|
||||
stroke: ${({ color }) => color};
|
||||
fill: ${({ color }) => color};
|
||||
`
|
||||
|
||||
const HandleAccent = styled.path`
|
||||
cursor: ew-resize;
|
||||
pointer-events: none;
|
||||
|
||||
stroke-width: 1.5;
|
||||
stroke: ${({ theme }) => theme.white};
|
||||
opacity: 0.6;
|
||||
`
|
||||
|
||||
const LabelGroup = styled.g<{ visible: boolean }>`
|
||||
opacity: ${({ visible }) => (visible ? '1' : '0')};
|
||||
transition: opacity 300ms;
|
||||
`
|
||||
|
||||
const TooltipBackground = styled.rect`
|
||||
fill: ${({ theme }) => theme.bg2};
|
||||
`
|
||||
|
||||
const Tooltip = styled.text`
|
||||
text-anchor: middle;
|
||||
font-size: 13px;
|
||||
fill: ${({ theme }) => theme.text1};
|
||||
`
|
||||
|
||||
// flips the handles draggers when close to the container edges
|
||||
const FLIP_HANDLE_THRESHOLD_PX = 20
|
||||
|
||||
// margin to prevent tick snapping from putting the brush off screen
|
||||
const BRUSH_EXTENT_MARGIN_PX = 2
|
||||
|
||||
const compare = (a1: [number, number], a2: [number, number], xScale: ScaleLinear<number, number>): boolean =>
|
||||
xScale(a1[0]) !== xScale(a2[0]) || xScale(a1[1]) !== xScale(a2[1])
|
||||
|
||||
export const Brush = ({
|
||||
id,
|
||||
xScale,
|
||||
interactive,
|
||||
brushLabelValue,
|
||||
brushExtent,
|
||||
setBrushExtent,
|
||||
innerWidth,
|
||||
innerHeight,
|
||||
westHandleColor,
|
||||
eastHandleColor,
|
||||
}: {
|
||||
id: string
|
||||
xScale: ScaleLinear<number, number>
|
||||
interactive: boolean
|
||||
brushLabelValue: (d: 'w' | 'e', x: number) => string
|
||||
brushExtent: [number, number]
|
||||
setBrushExtent: (extent: [number, number], mode: string | undefined) => void
|
||||
innerWidth: number
|
||||
innerHeight: number
|
||||
westHandleColor: string
|
||||
eastHandleColor: string
|
||||
}) => {
|
||||
const brushRef = useRef<SVGGElement | null>(null)
|
||||
const brushBehavior = useRef<BrushBehavior<SVGGElement> | null>(null)
|
||||
|
||||
// only used to drag the handles on brush for performance
|
||||
const [localBrushExtent, setLocalBrushExtent] = useState<[number, number] | null>(brushExtent)
|
||||
const [showLabels, setShowLabels] = useState(false)
|
||||
const [hovering, setHovering] = useState(false)
|
||||
|
||||
const previousBrushExtent = usePrevious(brushExtent)
|
||||
|
||||
const brushed = useCallback(
|
||||
(event: D3BrushEvent<unknown>) => {
|
||||
const { type, selection, mode } = event
|
||||
|
||||
if (!selection) {
|
||||
setLocalBrushExtent(null)
|
||||
return
|
||||
}
|
||||
|
||||
const scaled = (selection as [number, number]).map(xScale.invert) as [number, number]
|
||||
|
||||
// avoid infinite render loop by checking for change
|
||||
if (type === 'end' && compare(brushExtent, scaled, xScale)) {
|
||||
setBrushExtent(scaled, mode)
|
||||
}
|
||||
|
||||
setLocalBrushExtent(scaled)
|
||||
},
|
||||
[xScale, brushExtent, setBrushExtent]
|
||||
)
|
||||
|
||||
// keep local and external brush extent in sync
|
||||
// i.e. snap to ticks on bruhs end
|
||||
useEffect(() => {
|
||||
setLocalBrushExtent(brushExtent)
|
||||
}, [brushExtent])
|
||||
|
||||
// initialize the brush
|
||||
useEffect(() => {
|
||||
if (!brushRef.current) return
|
||||
|
||||
brushBehavior.current = brushX<SVGGElement>()
|
||||
.extent([
|
||||
[Math.max(0 + BRUSH_EXTENT_MARGIN_PX, xScale(0)), 0],
|
||||
[innerWidth - BRUSH_EXTENT_MARGIN_PX, innerHeight],
|
||||
])
|
||||
.handleSize(30)
|
||||
.filter(() => interactive)
|
||||
.on('brush end', brushed)
|
||||
|
||||
brushBehavior.current(select(brushRef.current))
|
||||
|
||||
if (previousBrushExtent && compare(brushExtent, previousBrushExtent, xScale)) {
|
||||
select(brushRef.current)
|
||||
.transition()
|
||||
.call(brushBehavior.current.move as any, brushExtent.map(xScale))
|
||||
}
|
||||
|
||||
// brush linear gradient
|
||||
select(brushRef.current)
|
||||
.selectAll('.selection')
|
||||
.attr('stroke', 'none')
|
||||
.attr('fill-opacity', '0.1')
|
||||
.attr('fill', `url(#${id}-gradient-selection)`)
|
||||
}, [brushExtent, brushed, id, innerHeight, innerWidth, interactive, previousBrushExtent, xScale])
|
||||
|
||||
// respond to xScale changes only
|
||||
useEffect(() => {
|
||||
if (!brushRef.current || !brushBehavior.current) return
|
||||
|
||||
brushBehavior.current.move(select(brushRef.current) as any, brushExtent.map(xScale) as any)
|
||||
}, [brushExtent, xScale])
|
||||
|
||||
// show labels when local brush changes
|
||||
useEffect(() => {
|
||||
setShowLabels(true)
|
||||
const timeout = setTimeout(() => setShowLabels(false), 1500)
|
||||
return () => clearTimeout(timeout)
|
||||
}, [localBrushExtent])
|
||||
|
||||
// variables to help render the SVGs
|
||||
const flipWestHandle = localBrushExtent && xScale(localBrushExtent[0]) > FLIP_HANDLE_THRESHOLD_PX
|
||||
const flipEastHandle = localBrushExtent && xScale(localBrushExtent[1]) > innerWidth - FLIP_HANDLE_THRESHOLD_PX
|
||||
|
||||
const showWestArrow = localBrushExtent && (xScale(localBrushExtent[0]) < 0 || xScale(localBrushExtent[1]) < 0)
|
||||
const showEastArrow =
|
||||
localBrushExtent && (xScale(localBrushExtent[0]) > innerWidth || xScale(localBrushExtent[1]) > innerWidth)
|
||||
|
||||
const westHandleInView =
|
||||
localBrushExtent && xScale(localBrushExtent[0]) >= 0 && xScale(localBrushExtent[0]) <= innerWidth
|
||||
const eastHandleInView =
|
||||
localBrushExtent && xScale(localBrushExtent[1]) >= 0 && xScale(localBrushExtent[1]) <= innerWidth
|
||||
|
||||
return useMemo(
|
||||
() => (
|
||||
<>
|
||||
<defs>
|
||||
<linearGradient id={`${id}-gradient-selection`} x1="0%" y1="100%" x2="100%" y2="100%">
|
||||
<stop stopColor={westHandleColor} />
|
||||
<stop stopColor={eastHandleColor} offset="1" />
|
||||
</linearGradient>
|
||||
|
||||
{/* clips at exactly the svg area */}
|
||||
<clipPath id={`${id}-brush-clip`}>
|
||||
<rect x="0" y="0" width={innerWidth} height={innerHeight} />
|
||||
</clipPath>
|
||||
</defs>
|
||||
|
||||
{/* will host the d3 brush */}
|
||||
<g
|
||||
ref={brushRef}
|
||||
clipPath={`url(#${id}-brush-clip)`}
|
||||
onMouseEnter={() => setHovering(true)}
|
||||
onMouseLeave={() => setHovering(false)}
|
||||
/>
|
||||
|
||||
{/* custom brush handles */}
|
||||
{localBrushExtent && (
|
||||
<>
|
||||
{/* west handle */}
|
||||
{westHandleInView ? (
|
||||
<g
|
||||
transform={`translate(${Math.max(0, xScale(localBrushExtent[0]))}, 0), scale(${
|
||||
flipWestHandle ? '-1' : '1'
|
||||
}, 1)`}
|
||||
>
|
||||
<g>
|
||||
<Handle color={westHandleColor} d={brushHandlePath(innerHeight)} />
|
||||
<HandleAccent d={brushHandleAccentPath()} />
|
||||
</g>
|
||||
|
||||
<LabelGroup
|
||||
transform={`translate(50,0), scale(${flipWestHandle ? '1' : '-1'}, 1)`}
|
||||
visible={showLabels || hovering}
|
||||
>
|
||||
<TooltipBackground y="0" x="-30" height="30" width="60" rx="8" />
|
||||
<Tooltip transform={`scale(-1, 1)`} y="15" dominantBaseline="middle">
|
||||
{brushLabelValue('w', localBrushExtent[0])}
|
||||
</Tooltip>
|
||||
</LabelGroup>
|
||||
</g>
|
||||
) : null}
|
||||
|
||||
{/* east handle */}
|
||||
{eastHandleInView ? (
|
||||
<g transform={`translate(${xScale(localBrushExtent[1])}, 0), scale(${flipEastHandle ? '-1' : '1'}, 1)`}>
|
||||
<g>
|
||||
<Handle color={eastHandleColor} d={brushHandlePath(innerHeight)} />
|
||||
<HandleAccent d={brushHandleAccentPath()} />
|
||||
</g>
|
||||
|
||||
<LabelGroup
|
||||
transform={`translate(50,0), scale(${flipEastHandle ? '-1' : '1'}, 1)`}
|
||||
visible={showLabels || hovering}
|
||||
>
|
||||
<TooltipBackground y="0" x="-30" height="30" width="60" rx="8" />
|
||||
<Tooltip y="15" dominantBaseline="middle">
|
||||
{brushLabelValue('e', localBrushExtent[1])}
|
||||
</Tooltip>
|
||||
</LabelGroup>
|
||||
</g>
|
||||
) : null}
|
||||
|
||||
{showWestArrow && <OffScreenHandle color={westHandleColor} />}
|
||||
|
||||
{showEastArrow && (
|
||||
<g transform={`translate(${innerWidth}, 0) scale(-1, 1)`}>
|
||||
<OffScreenHandle color={eastHandleColor} />
|
||||
</g>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
[
|
||||
brushLabelValue,
|
||||
eastHandleColor,
|
||||
eastHandleInView,
|
||||
flipEastHandle,
|
||||
flipWestHandle,
|
||||
hovering,
|
||||
id,
|
||||
innerHeight,
|
||||
innerWidth,
|
||||
localBrushExtent,
|
||||
showEastArrow,
|
||||
showLabels,
|
||||
showWestArrow,
|
||||
westHandleColor,
|
||||
westHandleInView,
|
||||
xScale,
|
||||
]
|
||||
)
|
||||
}
|
||||
146
src/components/LiquidityChartRangeInput/Chart.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
import { max, scaleLinear, ZoomTransform } from 'd3'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Bound } from 'state/mint/v3/actions'
|
||||
import { Area } from './Area'
|
||||
import { AxisBottom } from './AxisBottom'
|
||||
import { Brush } from './Brush'
|
||||
import { Line } from './Line'
|
||||
import { ChartEntry, LiquidityChartRangeInputProps } from './types'
|
||||
import Zoom, { ZoomOverlay } from './Zoom'
|
||||
|
||||
export const xAccessor = (d: ChartEntry) => d.price0
|
||||
export const yAccessor = (d: ChartEntry) => d.activeLiquidity
|
||||
|
||||
export function Chart({
|
||||
id = 'liquidityChartRangeInput',
|
||||
data: { series, current },
|
||||
ticksAtLimit,
|
||||
styles,
|
||||
dimensions: { width, height },
|
||||
margins,
|
||||
interactive = true,
|
||||
brushDomain,
|
||||
brushLabels,
|
||||
onBrushDomainChange,
|
||||
zoomLevels,
|
||||
}: LiquidityChartRangeInputProps) {
|
||||
const zoomRef = useRef<SVGRectElement | null>(null)
|
||||
|
||||
const [zoom, setZoom] = useState<ZoomTransform | null>(null)
|
||||
|
||||
const [innerHeight, innerWidth] = useMemo(
|
||||
() => [height - margins.top - margins.bottom, width - margins.left - margins.right],
|
||||
[width, height, margins]
|
||||
)
|
||||
|
||||
const { xScale, yScale } = useMemo(() => {
|
||||
const scales = {
|
||||
xScale: scaleLinear()
|
||||
.domain([current * zoomLevels.initialMin, current * zoomLevels.initialMax] as number[])
|
||||
.range([0, innerWidth]),
|
||||
yScale: scaleLinear()
|
||||
.domain([0, max(series, yAccessor)] as number[])
|
||||
.range([innerHeight, 0]),
|
||||
}
|
||||
|
||||
if (zoom) {
|
||||
const newXscale = zoom.rescaleX(scales.xScale)
|
||||
scales.xScale.domain(newXscale.domain())
|
||||
}
|
||||
|
||||
return scales
|
||||
}, [current, zoomLevels.initialMin, zoomLevels.initialMax, innerWidth, series, innerHeight, zoom])
|
||||
|
||||
useEffect(() => {
|
||||
// reset zoom as necessary
|
||||
setZoom(null)
|
||||
}, [zoomLevels])
|
||||
|
||||
useEffect(() => {
|
||||
if (!brushDomain) {
|
||||
onBrushDomainChange(xScale.domain() as [number, number], undefined)
|
||||
}
|
||||
}, [brushDomain, onBrushDomainChange, xScale])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Zoom
|
||||
svg={zoomRef.current}
|
||||
xScale={xScale}
|
||||
setZoom={setZoom}
|
||||
width={innerWidth}
|
||||
height={
|
||||
// allow zooming inside the x-axis
|
||||
height
|
||||
}
|
||||
resetBrush={() => {
|
||||
onBrushDomainChange(
|
||||
[current * zoomLevels.initialMin, current * zoomLevels.initialMax] as [number, number],
|
||||
'reset'
|
||||
)
|
||||
}}
|
||||
showResetButton={Boolean(ticksAtLimit[Bound.LOWER] || ticksAtLimit[Bound.UPPER])}
|
||||
zoomLevels={zoomLevels}
|
||||
/>
|
||||
<svg width="100%" height="100%" viewBox={`0 0 ${width} ${height}`} style={{ overflow: 'visible' }}>
|
||||
<defs>
|
||||
<clipPath id={`${id}-chart-clip`}>
|
||||
<rect x="0" y="0" width={innerWidth} height={height} />
|
||||
</clipPath>
|
||||
|
||||
{brushDomain && (
|
||||
// mask to highlight selected area
|
||||
<mask id={`${id}-chart-area-mask`}>
|
||||
<rect
|
||||
fill="white"
|
||||
x={xScale(brushDomain[0])}
|
||||
y="0"
|
||||
width={xScale(brushDomain[1]) - xScale(brushDomain[0])}
|
||||
height={innerHeight}
|
||||
/>
|
||||
</mask>
|
||||
)}
|
||||
</defs>
|
||||
|
||||
<g transform={`translate(${margins.left},${margins.top})`}>
|
||||
<g clipPath={`url(#${id}-chart-clip)`}>
|
||||
<Area series={series} xScale={xScale} yScale={yScale} xValue={xAccessor} yValue={yAccessor} />
|
||||
|
||||
{brushDomain && (
|
||||
// duplicate area chart with mask for selected area
|
||||
<g mask={`url(#${id}-chart-area-mask)`}>
|
||||
<Area
|
||||
series={series}
|
||||
xScale={xScale}
|
||||
yScale={yScale}
|
||||
xValue={xAccessor}
|
||||
yValue={yAccessor}
|
||||
fill={styles.area.selection}
|
||||
/>
|
||||
</g>
|
||||
)}
|
||||
|
||||
<Line value={current} xScale={xScale} innerHeight={innerHeight} />
|
||||
|
||||
<AxisBottom xScale={xScale} innerHeight={innerHeight} />
|
||||
</g>
|
||||
|
||||
<ZoomOverlay width={innerWidth} height={height} ref={zoomRef} />
|
||||
|
||||
<Brush
|
||||
id={id}
|
||||
xScale={xScale}
|
||||
interactive={interactive}
|
||||
brushLabelValue={brushLabels}
|
||||
brushExtent={brushDomain ?? (xScale.domain() as [number, number])}
|
||||
innerWidth={innerWidth}
|
||||
innerHeight={innerHeight}
|
||||
setBrushExtent={onBrushDomainChange}
|
||||
westHandleColor={styles.brush.handle.west}
|
||||
eastHandleColor={styles.brush.handle.east}
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</>
|
||||
)
|
||||
}
|
||||
24
src/components/LiquidityChartRangeInput/Line.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import { ScaleLinear } from 'd3'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
const StyledLine = styled.line`
|
||||
opacity: 0.5;
|
||||
stroke-width: 2;
|
||||
stroke: ${({ theme }) => theme.text1};
|
||||
fill: none;
|
||||
`
|
||||
|
||||
export const Line = ({
|
||||
value,
|
||||
xScale,
|
||||
innerHeight,
|
||||
}: {
|
||||
value: number
|
||||
xScale: ScaleLinear<number, number>
|
||||
innerHeight: number
|
||||
}) =>
|
||||
useMemo(
|
||||
() => <StyledLine x1={xScale(value)} y1="0" x2={xScale(value)} y2={innerHeight} />,
|
||||
[value, xScale, innerHeight]
|
||||
)
|
||||
130
src/components/LiquidityChartRangeInput/Zoom.tsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import React, { useEffect, useMemo, useRef } from 'react'
|
||||
import { ButtonGray } from 'components/Button'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ScaleLinear, select, ZoomBehavior, zoom, ZoomTransform, zoomIdentity } from 'd3'
|
||||
import { RefreshCcw, ZoomIn, ZoomOut } from 'react-feather'
|
||||
import { ZoomLevels } from './types'
|
||||
|
||||
const Wrapper = styled.div<{ count: number }>`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(${({ count }) => count.toString()}, 1fr);
|
||||
grid-gap: 6px;
|
||||
|
||||
position: absolute;
|
||||
top: -75px;
|
||||
right: 0;
|
||||
`
|
||||
|
||||
const Button = styled(ButtonGray)`
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
color: ${({ theme }) => theme.text1};
|
||||
}
|
||||
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 4px;
|
||||
`
|
||||
|
||||
export const ZoomOverlay = styled.rect`
|
||||
fill: transparent;
|
||||
cursor: grab;
|
||||
|
||||
&:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
`
|
||||
|
||||
export default function Zoom({
|
||||
svg,
|
||||
xScale,
|
||||
setZoom,
|
||||
width,
|
||||
height,
|
||||
resetBrush,
|
||||
showResetButton,
|
||||
zoomLevels,
|
||||
}: {
|
||||
svg: SVGElement | null
|
||||
xScale: ScaleLinear<number, number>
|
||||
setZoom: (transform: ZoomTransform) => void
|
||||
width: number
|
||||
height: number
|
||||
resetBrush: () => void
|
||||
showResetButton: boolean
|
||||
zoomLevels: ZoomLevels
|
||||
}) {
|
||||
const zoomBehavior = useRef<ZoomBehavior<Element, unknown>>()
|
||||
|
||||
const [zoomIn, zoomOut, zoomInitial, zoomReset] = useMemo(
|
||||
() => [
|
||||
() =>
|
||||
svg &&
|
||||
zoomBehavior.current &&
|
||||
select(svg as Element)
|
||||
.transition()
|
||||
.call(zoomBehavior.current.scaleBy, 2),
|
||||
() =>
|
||||
svg &&
|
||||
zoomBehavior.current &&
|
||||
select(svg as Element)
|
||||
.transition()
|
||||
.call(zoomBehavior.current.scaleBy, 0.5),
|
||||
() =>
|
||||
svg &&
|
||||
zoomBehavior.current &&
|
||||
select(svg as Element)
|
||||
.transition()
|
||||
.call(zoomBehavior.current.scaleTo, 0.5),
|
||||
() =>
|
||||
svg &&
|
||||
zoomBehavior.current &&
|
||||
select(svg as Element)
|
||||
.call(zoomBehavior.current.transform, zoomIdentity.translate(0, 0).scale(1))
|
||||
.transition()
|
||||
.call(zoomBehavior.current.scaleTo, 0.5),
|
||||
],
|
||||
[svg]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!svg) return
|
||||
|
||||
zoomBehavior.current = zoom()
|
||||
.scaleExtent([zoomLevels.min, zoomLevels.max])
|
||||
.extent([
|
||||
[0, 0],
|
||||
[width, height],
|
||||
])
|
||||
.on('zoom', ({ transform }: { transform: ZoomTransform }) => setZoom(transform))
|
||||
|
||||
select(svg as Element).call(zoomBehavior.current)
|
||||
}, [height, width, setZoom, svg, xScale, zoomBehavior, zoomLevels, zoomLevels.max, zoomLevels.min])
|
||||
|
||||
useEffect(() => {
|
||||
// reset zoom to initial on zoomLevel change
|
||||
zoomInitial()
|
||||
}, [zoomInitial, zoomLevels])
|
||||
|
||||
return (
|
||||
<Wrapper count={showResetButton ? 3 : 2}>
|
||||
{showResetButton && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
resetBrush()
|
||||
zoomReset()
|
||||
}}
|
||||
disabled={false}
|
||||
>
|
||||
<RefreshCcw size={16} />
|
||||
</Button>
|
||||
)}
|
||||
<Button onClick={zoomIn} disabled={false}>
|
||||
<ZoomIn size={16} />
|
||||
</Button>
|
||||
<Button onClick={zoomOut} disabled={false}>
|
||||
<ZoomOut size={16} />
|
||||
</Button>
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
56
src/components/LiquidityChartRangeInput/hooks.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import { usePoolActiveLiquidity } from 'hooks/usePoolTickData'
|
||||
import { ChartEntry } from './types'
|
||||
import JSBI from 'jsbi'
|
||||
|
||||
export interface TickProcessed {
|
||||
liquidityActive: JSBI
|
||||
price0: string
|
||||
}
|
||||
|
||||
export function useDensityChartData({
|
||||
currencyA,
|
||||
currencyB,
|
||||
feeAmount,
|
||||
}: {
|
||||
currencyA: Currency | undefined
|
||||
currencyB: Currency | undefined
|
||||
feeAmount: FeeAmount | undefined
|
||||
}) {
|
||||
const { isLoading, isUninitialized, isError, error, data } = usePoolActiveLiquidity(currencyA, currencyB, feeAmount)
|
||||
|
||||
const formatData = useCallback(() => {
|
||||
if (!data?.length) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const newData: ChartEntry[] = []
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const t: TickProcessed = data[i]
|
||||
|
||||
const chartEntry = {
|
||||
activeLiquidity: parseFloat(t.liquidityActive.toString()),
|
||||
price0: parseFloat(t.price0),
|
||||
}
|
||||
|
||||
if (chartEntry.activeLiquidity > 0) {
|
||||
newData.push(chartEntry)
|
||||
}
|
||||
}
|
||||
|
||||
return newData
|
||||
}, [data])
|
||||
|
||||
return useMemo(() => {
|
||||
return {
|
||||
isLoading,
|
||||
isUninitialized,
|
||||
isError,
|
||||
error,
|
||||
formattedData: !isLoading && !isUninitialized ? formatData() : undefined,
|
||||
}
|
||||
}, [isLoading, isUninitialized, isError, error, formatData])
|
||||
}
|
||||
208
src/components/LiquidityChartRangeInput/index.tsx
Normal file
@@ -0,0 +1,208 @@
|
||||
import React, { ReactNode, useCallback, useMemo } from 'react'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Currency, Price, Token } from '@uniswap/sdk-core'
|
||||
import { AutoColumn, ColumnCenter } from 'components/Column'
|
||||
import Loader from 'components/Loader'
|
||||
import { useColor } from 'hooks/useColor'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import { saturate } from 'polished'
|
||||
import { BarChart2, Inbox, CloudOff } from 'react-feather'
|
||||
import { batch } from 'react-redux'
|
||||
import styled from 'styled-components/macro'
|
||||
import { TYPE } from '../../theme'
|
||||
import { Chart } from './Chart'
|
||||
import { useDensityChartData } from './hooks'
|
||||
import { format } from 'd3'
|
||||
import { Bound } from 'state/mint/v3/actions'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import ReactGA from 'react-ga'
|
||||
import { ZoomLevels } from './types'
|
||||
|
||||
const ZOOM_LEVELS: Record<FeeAmount, ZoomLevels> = {
|
||||
[FeeAmount.LOW]: {
|
||||
initialMin: 0.999,
|
||||
initialMax: 1.001,
|
||||
min: 0.00001,
|
||||
max: 1.5,
|
||||
},
|
||||
[FeeAmount.MEDIUM]: {
|
||||
initialMin: 0.5,
|
||||
initialMax: 2,
|
||||
min: 0.00001,
|
||||
max: 20,
|
||||
},
|
||||
[FeeAmount.HIGH]: {
|
||||
initialMin: 0.5,
|
||||
initialMax: 2,
|
||||
min: 0.00001,
|
||||
max: 20,
|
||||
},
|
||||
}
|
||||
|
||||
const ChartWrapper = styled.div`
|
||||
position: relative;
|
||||
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
`
|
||||
|
||||
function InfoBox({ message, icon }: { message?: ReactNode; icon: ReactNode }) {
|
||||
return (
|
||||
<ColumnCenter style={{ height: '100%', justifyContent: 'center' }}>
|
||||
{icon}
|
||||
{message && (
|
||||
<TYPE.mediumHeader padding={10} marginTop="20px" textAlign="center">
|
||||
{message}
|
||||
</TYPE.mediumHeader>
|
||||
)}
|
||||
</ColumnCenter>
|
||||
)
|
||||
}
|
||||
|
||||
export default function LiquidityChartRangeInput({
|
||||
currencyA,
|
||||
currencyB,
|
||||
feeAmount,
|
||||
ticksAtLimit,
|
||||
price,
|
||||
priceLower,
|
||||
priceUpper,
|
||||
onLeftRangeInput,
|
||||
onRightRangeInput,
|
||||
interactive,
|
||||
}: {
|
||||
currencyA: Currency | undefined
|
||||
currencyB: Currency | undefined
|
||||
feeAmount?: FeeAmount
|
||||
ticksAtLimit: { [bound in Bound]?: boolean | undefined }
|
||||
price: number | undefined
|
||||
priceLower?: Price<Token, Token>
|
||||
priceUpper?: Price<Token, Token>
|
||||
onLeftRangeInput: (typedValue: string) => void
|
||||
onRightRangeInput: (typedValue: string) => void
|
||||
interactive: boolean
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
|
||||
const tokenAColor = useColor(currencyA?.wrapped)
|
||||
const tokenBColor = useColor(currencyB?.wrapped)
|
||||
|
||||
const isSorted = currencyA && currencyB && currencyA?.wrapped.sortsBefore(currencyB?.wrapped)
|
||||
|
||||
const { isLoading, isUninitialized, isError, error, formattedData } = useDensityChartData({
|
||||
currencyA,
|
||||
currencyB,
|
||||
feeAmount,
|
||||
})
|
||||
|
||||
const onBrushDomainChangeEnded = useCallback(
|
||||
(domain, mode) => {
|
||||
let leftRangeValue = Number(domain[0])
|
||||
const rightRangeValue = Number(domain[1])
|
||||
|
||||
if (leftRangeValue <= 0) {
|
||||
leftRangeValue = 1 / 10 ** 6
|
||||
}
|
||||
|
||||
batch(() => {
|
||||
// simulate user input for auto-formatting and other validations
|
||||
if (
|
||||
(!ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER] || mode === 'handle' || mode === 'reset') &&
|
||||
leftRangeValue > 0
|
||||
) {
|
||||
onLeftRangeInput(leftRangeValue.toFixed(6))
|
||||
}
|
||||
|
||||
if ((!ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER] || mode === 'reset') && rightRangeValue > 0) {
|
||||
// todo: remove this check. Upper bound for large numbers
|
||||
// sometimes fails to parse to tick.
|
||||
if (rightRangeValue < 1e35) {
|
||||
onRightRangeInput(rightRangeValue.toFixed(6))
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
[isSorted, onLeftRangeInput, onRightRangeInput, ticksAtLimit]
|
||||
)
|
||||
|
||||
interactive = interactive && Boolean(formattedData?.length)
|
||||
|
||||
const brushDomain: [number, number] | undefined = useMemo(() => {
|
||||
const leftPrice = isSorted ? priceLower : priceUpper?.invert()
|
||||
const rightPrice = isSorted ? priceUpper : priceLower?.invert()
|
||||
|
||||
return leftPrice && rightPrice
|
||||
? [parseFloat(leftPrice?.toSignificant(6)), parseFloat(rightPrice?.toSignificant(6))]
|
||||
: undefined
|
||||
}, [isSorted, priceLower, priceUpper])
|
||||
|
||||
const brushLabelValue = useCallback(
|
||||
(d: 'w' | 'e', x: number) => {
|
||||
if (!price) return ''
|
||||
|
||||
if (d === 'w' && ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER]) return '0'
|
||||
if (d === 'e' && ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER]) return '∞'
|
||||
|
||||
const percent = (x < price ? -1 : 1) * ((Math.max(x, price) - Math.min(x, price)) / price) * 100
|
||||
|
||||
return price ? `${format(Math.abs(percent) > 1 ? '.2~s' : '.2~f')(percent)}%` : ''
|
||||
},
|
||||
[isSorted, price, ticksAtLimit]
|
||||
)
|
||||
|
||||
if (isError) {
|
||||
ReactGA.exception({
|
||||
...error,
|
||||
category: 'Liquidity',
|
||||
fatal: false,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<AutoColumn gap="md" style={{ minHeight: '200px' }}>
|
||||
{isUninitialized ? (
|
||||
<InfoBox
|
||||
message={<Trans>Your position will appear here.</Trans>}
|
||||
icon={<Inbox size={56} stroke={theme.text1} />}
|
||||
/>
|
||||
) : isLoading ? (
|
||||
<InfoBox icon={<Loader size="40px" stroke={theme.text4} />} />
|
||||
) : isError ? (
|
||||
<InfoBox
|
||||
message={<Trans>Liquidity data not available.</Trans>}
|
||||
icon={<CloudOff size={56} stroke={theme.text4} />}
|
||||
/>
|
||||
) : !formattedData || formattedData === [] || !price ? (
|
||||
<InfoBox
|
||||
message={<Trans>There is no liquidity data.</Trans>}
|
||||
icon={<BarChart2 size={56} stroke={theme.text4} />}
|
||||
/>
|
||||
) : (
|
||||
<ChartWrapper>
|
||||
<Chart
|
||||
data={{ series: formattedData, current: price }}
|
||||
dimensions={{ width: 400, height: 200 }}
|
||||
margins={{ top: 10, right: 2, bottom: 20, left: 0 }}
|
||||
styles={{
|
||||
area: {
|
||||
selection: theme.blue1,
|
||||
},
|
||||
brush: {
|
||||
handle: {
|
||||
west: saturate(0.1, tokenAColor) ?? theme.red1,
|
||||
east: saturate(0.1, tokenBColor) ?? theme.blue1,
|
||||
},
|
||||
},
|
||||
}}
|
||||
interactive={interactive}
|
||||
brushLabels={brushLabelValue}
|
||||
brushDomain={brushDomain}
|
||||
onBrushDomainChange={onBrushDomainChangeEnded}
|
||||
zoomLevels={ZOOM_LEVELS[feeAmount ?? FeeAmount.MEDIUM]}
|
||||
ticksAtLimit={ticksAtLimit}
|
||||
/>
|
||||
</ChartWrapper>
|
||||
)}
|
||||
</AutoColumn>
|
||||
)
|
||||
}
|
||||
61
src/components/LiquidityChartRangeInput/svg.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Generates an SVG path for the east brush handle.
|
||||
* Apply `scale(-1, 1)` to generate west brush handle.
|
||||
*
|
||||
* |```````\
|
||||
* | | | |
|
||||
* |______/
|
||||
* |
|
||||
* |
|
||||
* |
|
||||
* |
|
||||
* |
|
||||
*
|
||||
* https://medium.com/@dennismphil/one-side-rounded-rectangle-using-svg-fb31cf318d90
|
||||
*/
|
||||
export const brushHandlePath = (height: number) =>
|
||||
[
|
||||
// handle
|
||||
`M 0 0`, // move to origin
|
||||
`v ${height}`, // vertical line
|
||||
'm 1 0', // move 1px to the right
|
||||
`V 0`, // second vertical line
|
||||
`M 0 1`, // move to origin
|
||||
|
||||
// head
|
||||
'h 12', // horizontal line
|
||||
'q 2 0, 2 2', // rounded corner
|
||||
'v 22', // vertical line
|
||||
'q 0 2 -2 2', // rounded corner
|
||||
'h -12', // horizontal line
|
||||
`z`, // close path
|
||||
].join(' ')
|
||||
|
||||
export const brushHandleAccentPath = () =>
|
||||
[
|
||||
'm 5 7', // move to first accent
|
||||
'v 14', // vertical line
|
||||
'M 0 0', // move to origin
|
||||
'm 9 7', // move to second accent
|
||||
'v 14', // vertical line
|
||||
'z',
|
||||
].join(' ')
|
||||
|
||||
export const OffScreenHandle = ({
|
||||
color,
|
||||
size = 10,
|
||||
margin = 10,
|
||||
}: {
|
||||
color: string
|
||||
size?: number
|
||||
margin?: number
|
||||
}) => (
|
||||
<polygon
|
||||
points={`0 0, ${size} ${size}, 0 ${size}`}
|
||||
transform={` translate(${size + margin}, ${margin}) rotate(45) `}
|
||||
fill={color}
|
||||
stroke={color}
|
||||
strokeWidth="4"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
)
|
||||
61
src/components/LiquidityChartRangeInput/types.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Bound } from 'state/mint/v3/actions'
|
||||
|
||||
export interface ChartEntry {
|
||||
activeLiquidity: number
|
||||
price0: number
|
||||
}
|
||||
|
||||
export interface Dimensions {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
export interface Margins {
|
||||
top: number
|
||||
right: number
|
||||
bottom: number
|
||||
left: number
|
||||
}
|
||||
|
||||
export interface ZoomLevels {
|
||||
initialMin: number
|
||||
initialMax: number
|
||||
min: number
|
||||
max: number
|
||||
}
|
||||
|
||||
export interface LiquidityChartRangeInputProps {
|
||||
// to distringuish between multiple charts in the DOM
|
||||
id?: string
|
||||
|
||||
data: {
|
||||
series: ChartEntry[]
|
||||
current: number
|
||||
}
|
||||
ticksAtLimit: { [bound in Bound]?: boolean | undefined }
|
||||
|
||||
styles: {
|
||||
area: {
|
||||
// color of the ticks in range
|
||||
selection: string
|
||||
}
|
||||
|
||||
brush: {
|
||||
handle: {
|
||||
west: string
|
||||
east: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dimensions: Dimensions
|
||||
margins: Margins
|
||||
|
||||
interactive?: boolean
|
||||
|
||||
brushLabels: (d: 'w' | 'e', x: number) => string
|
||||
brushDomain: [number, number] | undefined
|
||||
onBrushDomainChange: (domain: [number, number], mode: string | undefined) => void
|
||||
|
||||
zoomLevels: ZoomLevels
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
import React from 'react'
|
||||
|
||||
import styled, { keyframes } from 'styled-components'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
|
||||
const rotate = keyframes`
|
||||
from {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { Slash } from 'react-feather'
|
||||
import { ImageProps } from 'rebass'
|
||||
import useTheme from '../../hooks/useTheme'
|
||||
|
||||
const BAD_SRCS: { [tokenAddress: string]: true } = {}
|
||||
|
||||
export interface LogoProps extends Pick<ImageProps, 'style' | 'alt' | 'className'> {
|
||||
interface LogoProps extends Pick<ImageProps, 'style' | 'alt' | 'className'> {
|
||||
srcs: string[]
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { Story } from '@storybook/react/types-6-0'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import Component from './index'
|
||||
|
||||
const Wrapper = styled.div`
|
||||
max-width: 150px;
|
||||
`
|
||||
export default {
|
||||
title: 'Menu',
|
||||
decorators: [
|
||||
() => (
|
||||
<Wrapper>
|
||||
<Component />
|
||||
</Wrapper>
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
const Template: Story<any> = (args) => <Component {...args} />
|
||||
|
||||
export const Menu = Template.bind({})
|
||||
Menu.args = {}
|
||||
@@ -1,15 +1,32 @@
|
||||
import React, { useRef } from 'react'
|
||||
import { BookOpen, Code, Info, MessageCircle, PieChart } from 'react-feather'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import {
|
||||
BookOpen,
|
||||
Code,
|
||||
Info,
|
||||
MessageCircle,
|
||||
PieChart,
|
||||
Moon,
|
||||
Sun,
|
||||
ChevronRight,
|
||||
ChevronLeft,
|
||||
Check,
|
||||
} from 'react-feather'
|
||||
import { Link } from 'react-router-dom'
|
||||
import styled, { css } from 'styled-components'
|
||||
import styled, { css } from 'styled-components/macro'
|
||||
import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg'
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
|
||||
import { ApplicationModal } from '../../state/application/actions'
|
||||
import { useModalOpen, useToggleModal } from '../../state/application/hooks'
|
||||
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { ExternalLink } from '../../theme'
|
||||
import { ButtonPrimary } from '../Button'
|
||||
import { useDarkModeManager } from 'state/user/hooks'
|
||||
|
||||
import { L2_CHAIN_IDS, CHAIN_INFO, SupportedChainId } from 'constants/chains'
|
||||
import { LOCALE_LABEL, SupportedLocale, SUPPORTED_LOCALES } from 'constants/locales'
|
||||
import { useLocationLinkProps } from 'hooks/useLocationLinkProps'
|
||||
import { useActiveLocale } from 'hooks/useActiveLocale'
|
||||
|
||||
export enum FlyoutAlignment {
|
||||
LEFT = 'LEFT',
|
||||
@@ -29,17 +46,18 @@ const StyledMenuButton = styled.button`
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 35px;
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
height: 38px;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
border: 1px solid ${({ theme }) => theme.bg0};
|
||||
|
||||
padding: 0.15rem 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
border-radius: 12px;
|
||||
|
||||
:hover,
|
||||
:focus {
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
border: 1px solid ${({ theme }) => theme.bg3};
|
||||
}
|
||||
|
||||
svg {
|
||||
@@ -64,18 +82,22 @@ const StyledMenu = styled.div`
|
||||
`
|
||||
|
||||
const MenuFlyout = styled.span<{ flyoutAlignment?: FlyoutAlignment }>`
|
||||
min-width: 8.125rem;
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
min-width: 196px;
|
||||
max-height: 350px;
|
||||
overflow: auto;
|
||||
background-color: ${({ theme }) => theme.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-radius: 12px;
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 1rem;
|
||||
font-size: 16px;
|
||||
position: absolute;
|
||||
top: 3rem;
|
||||
z-index: 100;
|
||||
|
||||
${({ flyoutAlignment = FlyoutAlignment.RIGHT }) =>
|
||||
flyoutAlignment === FlyoutAlignment.RIGHT
|
||||
? css`
|
||||
@@ -85,7 +107,9 @@ const MenuFlyout = styled.span<{ flyoutAlignment?: FlyoutAlignment }>`
|
||||
left: 0rem;
|
||||
`};
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
top: -17.25rem;
|
||||
bottom: unset;
|
||||
right: 0;
|
||||
left: unset;
|
||||
`};
|
||||
`
|
||||
|
||||
@@ -95,15 +119,13 @@ const MenuItem = styled(ExternalLink)`
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.5rem;
|
||||
justify-content: space-between;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.text1};
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
> svg {
|
||||
margin-right: 8px;
|
||||
}
|
||||
`
|
||||
|
||||
const InternalMenuItem = styled(Link)`
|
||||
@@ -120,16 +142,89 @@ const InternalMenuItem = styled(Link)`
|
||||
}
|
||||
`
|
||||
|
||||
const InternalLinkMenuItem = styled(InternalMenuItem)`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.5rem;
|
||||
justify-content: space-between;
|
||||
text-decoration: none;
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.text1};
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
`
|
||||
|
||||
const ToggleMenuItem = styled.button`
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.5rem;
|
||||
justify-content: space-between;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.text1};
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
`
|
||||
|
||||
const CODE_LINK = 'https://github.com/Uniswap/uniswap-interface'
|
||||
|
||||
function LanguageMenuItem({ locale, active, key }: { locale: SupportedLocale; active: boolean; key: string }) {
|
||||
const { to, onClick } = useLocationLinkProps(locale)
|
||||
|
||||
if (!to) return null
|
||||
|
||||
return (
|
||||
<InternalLinkMenuItem onClick={onClick} key={key} to={to}>
|
||||
<div>{LOCALE_LABEL[locale]}</div>
|
||||
{active && <Check opacity={0.6} size={16} />}
|
||||
</InternalLinkMenuItem>
|
||||
)
|
||||
}
|
||||
|
||||
function LanguageMenu({ close }: { close: () => void }) {
|
||||
const activeLocale = useActiveLocale()
|
||||
|
||||
return (
|
||||
<MenuFlyout>
|
||||
<ToggleMenuItem onClick={close}>
|
||||
<ChevronLeft size={16} />
|
||||
</ToggleMenuItem>
|
||||
{SUPPORTED_LOCALES.map((locale) => (
|
||||
<LanguageMenuItem locale={locale} active={activeLocale === locale} key={locale} />
|
||||
))}
|
||||
</MenuFlyout>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Menu() {
|
||||
const { account } = useActiveWeb3React()
|
||||
const { account, chainId } = useActiveWeb3React()
|
||||
|
||||
const node = useRef<HTMLDivElement>()
|
||||
const open = useModalOpen(ApplicationModal.MENU)
|
||||
const toggle = useToggleModal(ApplicationModal.MENU)
|
||||
useOnClickOutside(node, open ? toggle : undefined)
|
||||
const openClaimModal = useToggleModal(ApplicationModal.ADDRESS_CLAIM)
|
||||
const showUNIClaimOption = Boolean(!!account && !!chainId && !L2_CHAIN_IDS.includes(chainId))
|
||||
const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET]
|
||||
|
||||
const [darkMode, toggleDarkMode] = useDarkModeManager()
|
||||
|
||||
const [menu, setMenu] = useState<'main' | 'lang'>('main')
|
||||
|
||||
useEffect(() => {
|
||||
setMenu('main')
|
||||
}, [open])
|
||||
|
||||
return (
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451
|
||||
@@ -138,35 +233,70 @@ export default function Menu() {
|
||||
<StyledMenuIcon />
|
||||
</StyledMenuButton>
|
||||
|
||||
{open && (
|
||||
<MenuFlyout>
|
||||
<MenuItem href="https://uniswap.org/">
|
||||
<Info size={14} />
|
||||
<div>About</div>
|
||||
</MenuItem>
|
||||
<MenuItem href="https://docs.uniswap.org/">
|
||||
<BookOpen size={14} />
|
||||
<div>Docs</div>
|
||||
</MenuItem>
|
||||
<MenuItem href={CODE_LINK}>
|
||||
<Code size={14} />
|
||||
<div>Code</div>
|
||||
</MenuItem>
|
||||
<MenuItem href="https://discord.gg/FCfyBSbCU5">
|
||||
<MessageCircle size={14} />
|
||||
<div>Discord</div>
|
||||
</MenuItem>
|
||||
<MenuItem href="https://info.uniswap.org/">
|
||||
<PieChart size={14} />
|
||||
<div>Analytics</div>
|
||||
</MenuItem>
|
||||
{account && (
|
||||
<UNIbutton onClick={openClaimModal} padding="8px 16px" width="100%" borderRadius="12px" mt="0.5rem">
|
||||
Claim UNI
|
||||
</UNIbutton>
|
||||
)}
|
||||
</MenuFlyout>
|
||||
)}
|
||||
{open &&
|
||||
(() => {
|
||||
switch (menu) {
|
||||
case 'lang':
|
||||
return <LanguageMenu close={() => setMenu('main')} />
|
||||
case 'main':
|
||||
default:
|
||||
return (
|
||||
<MenuFlyout>
|
||||
<MenuItem href="https://uniswap.org/">
|
||||
<div>
|
||||
<Trans>About</Trans>
|
||||
</div>
|
||||
<Info opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<MenuItem href="https://docs.uniswap.org/">
|
||||
<div>
|
||||
<Trans>Docs</Trans>
|
||||
</div>
|
||||
<BookOpen opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<MenuItem href={CODE_LINK}>
|
||||
<div>
|
||||
<Trans>Code</Trans>
|
||||
</div>
|
||||
<Code opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<MenuItem href="https://discord.gg/FCfyBSbCU5">
|
||||
<div>
|
||||
<Trans>Discord</Trans>
|
||||
</div>
|
||||
<MessageCircle opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<MenuItem href={infoLink}>
|
||||
<div>
|
||||
<Trans>Analytics</Trans>
|
||||
</div>
|
||||
<PieChart opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<ToggleMenuItem onClick={() => setMenu('lang')}>
|
||||
<div>
|
||||
<Trans>Language</Trans>
|
||||
</div>
|
||||
<ChevronRight size={16} opacity={0.6} />
|
||||
</ToggleMenuItem>
|
||||
<ToggleMenuItem onClick={() => toggleDarkMode()}>
|
||||
<div>{darkMode ? <Trans>Light Theme</Trans> : <Trans>Dark Theme</Trans>}</div>
|
||||
{darkMode ? <Moon opacity={0.6} size={16} /> : <Sun opacity={0.6} size={16} />}
|
||||
</ToggleMenuItem>
|
||||
{showUNIClaimOption && (
|
||||
<UNIbutton
|
||||
onClick={openClaimModal}
|
||||
padding="8px 16px"
|
||||
width="100%"
|
||||
$borderRadius="12px"
|
||||
mt="0.5rem"
|
||||
>
|
||||
<Trans>Claim UNI</Trans>
|
||||
</UNIbutton>
|
||||
)}
|
||||
</MenuFlyout>
|
||||
)
|
||||
}
|
||||
})()}
|
||||
</StyledMenu>
|
||||
)
|
||||
}
|
||||
@@ -207,11 +337,11 @@ export const NewMenu = ({ flyoutAlignment = FlyoutAlignment.RIGHT, ToggleUI, men
|
||||
<NewMenuFlyout flyoutAlignment={flyoutAlignment}>
|
||||
{menuItems.map(({ content, link, external }, i) =>
|
||||
external ? (
|
||||
<ExternalMenuItem id="link" href={link} key={link + i}>
|
||||
<ExternalMenuItem href={link} key={i}>
|
||||
{content}
|
||||
</ExternalMenuItem>
|
||||
) : (
|
||||
<NewMenuItem id="link" to={link} key={link + i}>
|
||||
<NewMenuItem to={link} key={i}>
|
||||
{content}
|
||||
</NewMenuItem>
|
||||
)
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React from 'react'
|
||||
import styled, { css } from 'styled-components'
|
||||
import styled, { css } from 'styled-components/macro'
|
||||
import { animated, useTransition, useSpring } from 'react-spring'
|
||||
import { DialogOverlay, DialogContent } from '@reach/dialog'
|
||||
import { isMobile } from 'react-device-detect'
|
||||
import '@reach/dialog/styles.css'
|
||||
import { transparentize } from 'polished'
|
||||
import { useGesture } from 'react-use-gesture'
|
||||
|
||||
@@ -31,7 +30,7 @@ const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...r
|
||||
)).attrs({
|
||||
'aria-label': 'dialog',
|
||||
})`
|
||||
overflow-y: ${({ mobile }) => (mobile ? 'scroll' : 'hidden')};
|
||||
overflow-y: auto;
|
||||
|
||||
&[data-reach-dialog-content] {
|
||||
margin: 0 0 2rem 0;
|
||||
@@ -40,7 +39,7 @@ const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...r
|
||||
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.shadow1)};
|
||||
padding: 0px;
|
||||
width: 50vw;
|
||||
overflow-y: ${({ mobile }) => (mobile ? 'scroll' : 'hidden')};
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
align-self: ${({ mobile }) => (mobile ? 'flex-end' : 'center')};
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import React, { useContext } from 'react'
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { useContext } from 'react'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
|
||||
import { AutoColumn, ColumnCenter } from '../Column'
|
||||
import styled, { ThemeContext } from 'styled-components'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
import { RowBetween } from '../Row'
|
||||
import { TYPE, CloseIcon, CustomLightSpinner } from '../../theme'
|
||||
import { ArrowUpCircle } from 'react-feather'
|
||||
import { Trans } from '@lingui/macro'
|
||||
|
||||
import Circle from '../../assets/images/blue-loader.svg'
|
||||
import { getEtherscanLink } from '../../utils'
|
||||
import { ExternalLink } from '../../theme/components'
|
||||
|
||||
const ConfirmOrLoadingWrapper = styled.div`
|
||||
@@ -32,7 +33,9 @@ export function LoadingView({ children, onDismiss }: { children: any; onDismiss:
|
||||
</ConfirmedIcon>
|
||||
<AutoColumn gap="100px" justify={'center'}>
|
||||
{children}
|
||||
<TYPE.subHeader>Confirm this transaction in your wallet</TYPE.subHeader>
|
||||
<TYPE.subHeader>
|
||||
<Trans>Confirm this transaction in your wallet</Trans>
|
||||
</TYPE.subHeader>
|
||||
</AutoColumn>
|
||||
</ConfirmOrLoadingWrapper>
|
||||
)
|
||||
@@ -62,8 +65,13 @@ export function SubmittedView({
|
||||
<AutoColumn gap="100px" justify={'center'}>
|
||||
{children}
|
||||
{chainId && hash && (
|
||||
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')} style={{ marginLeft: '4px' }}>
|
||||
<TYPE.subHeader>View transaction on Etherscan</TYPE.subHeader>
|
||||
<ExternalLink
|
||||
href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}
|
||||
style={{ marginLeft: '4px' }}
|
||||
>
|
||||
<TYPE.subHeader>
|
||||
<Trans>View transaction on Explorer</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ExternalLink>
|
||||
)}
|
||||
</AutoColumn>
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import { darken } from 'polished'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { NavLink, Link as HistoryLink } from 'react-router-dom'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { NavLink, Link as HistoryLink, useLocation } from 'react-router-dom'
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
|
||||
import { ArrowLeft } from 'react-feather'
|
||||
import { RowBetween } from '../Row'
|
||||
import Row, { RowBetween } from '../Row'
|
||||
import SettingsTab from '../Settings'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { AppDispatch } from 'state'
|
||||
|
||||
import { useAppDispatch } from 'state/hooks'
|
||||
import { resetMintState } from 'state/mint/actions'
|
||||
import { resetMintState as resetMintV3State } from 'state/mint/v3/actions'
|
||||
import { TYPE } from 'theme'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import { ReactNode } from 'react'
|
||||
import { Box } from 'rebass'
|
||||
|
||||
const Tabs = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
@@ -50,6 +51,15 @@ const StyledNavLink = styled(NavLink).attrs({
|
||||
}
|
||||
`
|
||||
|
||||
const StyledHistoryLink = styled(HistoryLink)<{ flex: string | undefined }>`
|
||||
flex: ${({ flex }) => flex ?? 'none'};
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
flex: none;
|
||||
margin-right: 10px;
|
||||
`};
|
||||
`
|
||||
|
||||
const ActiveText = styled.div`
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
@@ -60,14 +70,13 @@ const StyledArrowLeft = styled(ArrowLeft)`
|
||||
`
|
||||
|
||||
export function SwapPoolTabs({ active }: { active: 'swap' | 'pool' }) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<Tabs style={{ marginBottom: '20px', display: 'none', padding: '1rem 1rem 0 1rem' }}>
|
||||
<StyledNavLink id={`swap-nav-link`} to={'/swap'} isActive={() => active === 'swap'}>
|
||||
{t('swap')}
|
||||
<Trans>Swap</Trans>
|
||||
</StyledNavLink>
|
||||
<StyledNavLink id={`pool-nav-link`} to={'/pool'} isActive={() => active === 'pool'}>
|
||||
{t('pool')}
|
||||
<Trans>Pool</Trans>
|
||||
</StyledNavLink>
|
||||
</Tabs>
|
||||
)
|
||||
@@ -76,11 +85,13 @@ export function SwapPoolTabs({ active }: { active: 'swap' | 'pool' }) {
|
||||
export function FindPoolTabs({ origin }: { origin: string }) {
|
||||
return (
|
||||
<Tabs>
|
||||
<RowBetween style={{ padding: '1rem 1rem 0 1rem' }}>
|
||||
<RowBetween style={{ padding: '1rem 1rem 0 1rem', position: 'relative' }}>
|
||||
<HistoryLink to={origin}>
|
||||
<StyledArrowLeft />
|
||||
</HistoryLink>
|
||||
<ActiveText>Import Pool</ActiveText>
|
||||
<ActiveText style={{ position: 'absolute', left: '50%', transform: 'translateX(-50%)' }}>
|
||||
<Trans>Import V2 Pool</Trans>
|
||||
</ActiveText>
|
||||
</RowBetween>
|
||||
</Tabs>
|
||||
)
|
||||
@@ -89,24 +100,32 @@ export function FindPoolTabs({ origin }: { origin: string }) {
|
||||
export function AddRemoveTabs({
|
||||
adding,
|
||||
creating,
|
||||
positionID,
|
||||
defaultSlippage,
|
||||
positionID,
|
||||
children,
|
||||
}: {
|
||||
adding: boolean
|
||||
creating: boolean
|
||||
positionID?: string | undefined
|
||||
defaultSlippage: Percent
|
||||
positionID?: string | undefined
|
||||
showBackLink?: boolean
|
||||
children?: ReactNode | undefined
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
|
||||
// reset states on back
|
||||
const dispatch = useDispatch<AppDispatch>()
|
||||
const dispatch = useAppDispatch()
|
||||
const location = useLocation()
|
||||
|
||||
// detect if back should redirect to v3 or v2 pool page
|
||||
const poolLink = location.pathname.includes('add/v2')
|
||||
? '/pool/v2'
|
||||
: '/pool' + (!!positionID ? `/${positionID.toString()}` : '')
|
||||
|
||||
return (
|
||||
<Tabs>
|
||||
<RowBetween style={{ padding: '1rem 1rem 0 1rem' }}>
|
||||
<HistoryLink
|
||||
to={'/pool' + (!!positionID ? `/${positionID.toString()}` : '')}
|
||||
<StyledHistoryLink
|
||||
to={poolLink}
|
||||
onClick={() => {
|
||||
if (adding) {
|
||||
// not 100% sure both of these are needed
|
||||
@@ -114,14 +133,39 @@ export function AddRemoveTabs({
|
||||
dispatch(resetMintV3State())
|
||||
}
|
||||
}}
|
||||
flex={children ? '1' : undefined}
|
||||
>
|
||||
<StyledArrowLeft stroke={theme.text2} />
|
||||
</HistoryLink>
|
||||
<TYPE.mediumHeader fontWeight={500} fontSize={20}>
|
||||
{creating ? 'Create a pair' : adding ? 'Add Liquidity' : 'Remove Liquidity'}
|
||||
</StyledHistoryLink>
|
||||
<TYPE.mediumHeader
|
||||
fontWeight={500}
|
||||
fontSize={20}
|
||||
style={{ flex: '1', margin: 'auto', textAlign: children ? 'start' : 'center' }}
|
||||
>
|
||||
{creating ? (
|
||||
<Trans>Create a pair</Trans>
|
||||
) : adding ? (
|
||||
<Trans>Add Liquidity</Trans>
|
||||
) : (
|
||||
<Trans>Remove Liquidity</Trans>
|
||||
)}
|
||||
</TYPE.mediumHeader>
|
||||
<Box style={{ marginRight: '.5rem' }}>{children}</Box>
|
||||
<SettingsTab placeholderSlippage={defaultSlippage} />
|
||||
</RowBetween>
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
|
||||
export function CreateProposalTabs() {
|
||||
return (
|
||||
<Tabs>
|
||||
<Row style={{ padding: '1rem 1rem 0 1rem' }}>
|
||||
<HistoryLink to="/vote">
|
||||
<StyledArrowLeft />
|
||||
</HistoryLink>
|
||||
<ActiveText style={{ marginLeft: 'auto', marginRight: 'auto' }}>Create Proposal</ActiveText>
|
||||
</Row>
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
|
||||