Compare commits
852 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
ae27e245b4 | ||
|
|
897f354202 | ||
|
|
4745052f0e | ||
|
|
5bc5d6504e | ||
|
|
7a0b85bf41 | ||
|
|
534afb3278 | ||
|
|
7d71af353e | ||
|
|
af6098bfe5 | ||
|
|
fce29bb36f | ||
|
|
4517af39ba | ||
|
|
b40163ce05 | ||
|
|
809902efec | ||
|
|
70be9894fa | ||
|
|
698ad5bac9 | ||
|
|
cd37b7533d | ||
|
|
c0cd6a1c8d | ||
|
|
f6245d1093 | ||
|
|
83c784f7c0 | ||
|
|
3d95b1a33b | ||
|
|
5c96942922 | ||
|
|
d27c83b382 | ||
|
|
b2f88965a9 | ||
|
|
95db44e0fc | ||
|
|
7d45ff5ca8 | ||
|
|
8964cf86aa | ||
|
|
0e9f23ed56 | ||
|
|
e08e597655 | ||
|
|
744db49803 | ||
|
|
54f59e02fd | ||
|
|
7950e5c083 | ||
|
|
dd33205bf6 | ||
|
|
397a20b9ec | ||
|
|
0aac0b43aa | ||
|
|
702500794d | ||
|
|
8bea95fab2 | ||
|
|
27094c87f2 | ||
|
|
bede9171c3 | ||
|
|
251d7c0bc2 | ||
|
|
285e4f28f5 | ||
|
|
3aa045303a | ||
|
|
e0a7c3794e | ||
|
|
f5fc5da341 | ||
|
|
bea5c0484b | ||
|
|
27960532ca | ||
|
|
37a4e2f6e3 | ||
|
|
19a3b12ca8 | ||
|
|
22c1ddf393 | ||
|
|
b44ae1a267 | ||
|
|
418dcf0cb2 | ||
|
|
58a508c9d6 | ||
|
|
3198129af2 | ||
|
|
89d484d882 | ||
|
|
fa4688d96c | ||
|
|
7ee761a59e | ||
|
|
78e95f6073 | ||
|
|
c67e57505a | ||
|
|
30f7385db7 | ||
|
|
c0f58ae810 | ||
|
|
54dd5476ca | ||
|
|
57786335df | ||
|
|
948e01a196 | ||
|
|
abf127c596 | ||
|
|
4d3f870b93 | ||
|
|
452f2dc3c0 | ||
|
|
b6bd59f2b1 | ||
|
|
0190b5a408 | ||
|
|
d6030dcd45 | ||
|
|
f0e2a491dc | ||
|
|
021aab6547 | ||
|
|
81af31eec1 | ||
|
|
d3898cf900 | ||
|
|
b8f61d5f90 | ||
|
|
6c880d29a6 | ||
|
|
a8baa6d6a5 | ||
|
|
00a1dee073 | ||
|
|
a6de7d7846 | ||
|
|
e1a81a9996 | ||
|
|
0770bab032 | ||
|
|
e5404dbf97 | ||
|
|
78e41848f2 | ||
|
|
77c090534b | ||
|
|
05acbfee88 | ||
|
|
64dd2f9ed1 | ||
|
|
09f30ce0f7 | ||
|
|
a49d5382db | ||
|
|
3affcb8d32 | ||
|
|
56e759ff78 | ||
|
|
f73a166d92 | ||
|
|
e1e52c06db | ||
|
|
ca4d915903 | ||
|
|
bdb6327c13 | ||
|
|
ce81dd5a79 | ||
|
|
73f29eea2c | ||
|
|
92193076c5 | ||
|
|
1aab086693 | ||
|
|
8057cb9fbe | ||
|
|
1b798889af | ||
|
|
1e54b97693 | ||
|
|
02c21ef720 | ||
|
|
b39aeeb805 | ||
|
|
8401a4b9b4 | ||
|
|
3b27ee94d7 | ||
|
|
660c355273 | ||
|
|
cd3c48462d | ||
|
|
acfd5c2720 | ||
|
|
a5ed12bfc7 | ||
|
|
40f0e619cc | ||
|
|
bd817083c9 | ||
|
|
699bcc25b6 | ||
|
|
e88a8effef | ||
|
|
2339817170 | ||
|
|
4220cafbd3 | ||
|
|
a61df58599 | ||
|
|
1b35128035 | ||
|
|
ab5114c5f5 | ||
|
|
28c7cfa1f1 | ||
|
|
509b307b67 | ||
|
|
1e5519de3f | ||
|
|
e8587396d3 | ||
|
|
d69b194ffb | ||
|
|
1325443025 | ||
|
|
1607f8919a | ||
|
|
873cf9760e | ||
|
|
b39f2fe055 | ||
|
|
516e783be6 | ||
|
|
9a326fa023 | ||
|
|
06d6c711dd | ||
|
|
7178746023 | ||
|
|
2efe8250ae | ||
|
|
24d8b4abc9 | ||
|
|
aec18b7eb1 | ||
|
|
0c37e81d97 | ||
|
|
624c3678c7 | ||
|
|
cdae20f2ed | ||
|
|
042967502e | ||
|
|
eedba9795e | ||
|
|
eaec4a33fc | ||
|
|
300dd70804 | ||
|
|
f4e994867e | ||
|
|
7ca79ff12b | ||
|
|
4903258b7c | ||
|
|
fe35ca9db8 | ||
|
|
4bc1b8eb5c | ||
|
|
7801695180 | ||
|
|
ee69357305 | ||
|
|
ce7f94f16a | ||
|
|
124b83ae56 | ||
|
|
0dcd5743c8 | ||
|
|
9fec8dbe93 | ||
|
|
549d4e38c6 | ||
|
|
3af6781821 | ||
|
|
fbccb83edb | ||
|
|
ff12b7be10 | ||
|
|
4c2cb5b0c1 | ||
|
|
d7785942b1 | ||
|
|
ed801deb15 | ||
|
|
c34727641d | ||
|
|
807860aac6 | ||
|
|
5a9034fe95 | ||
|
|
ee0db4f2aa | ||
|
|
6d5625a1f8 | ||
|
|
1619386ab4 | ||
|
|
93d33947da | ||
|
|
91f3e21bd4 | ||
|
|
e9a432b58e | ||
|
|
1f41587ba9 | ||
|
|
9b639bee65 | ||
|
|
eaddc9e6f8 | ||
|
|
e83a1ec923 | ||
|
|
8021315f87 | ||
|
|
0540012bb9 | ||
|
|
3f6bf607dd | ||
|
|
828f7ee446 | ||
|
|
432d17bdfd | ||
|
|
9743b211e7 | ||
|
|
dce187e433 | ||
|
|
8859ff6979 | ||
|
|
f566a72b06 | ||
|
|
e39e5908e7 | ||
|
|
c5424d2dcc | ||
|
|
8b4bc167c5 | ||
|
|
43bceb232e | ||
|
|
c3909bc1d0 | ||
|
|
b25287923c | ||
|
|
65c51ea4aa | ||
|
|
b0fa08e9b0 | ||
|
|
b09eb8fb52 | ||
|
|
5b49cedebb | ||
|
|
ae76f26501 | ||
|
|
1cb1ffe2f6 | ||
|
|
d83bc3097d | ||
|
|
323edc0fcd | ||
|
|
fc258fdf5c | ||
|
|
2e599dc00e | ||
|
|
464a682fcc | ||
|
|
6ace3c211c | ||
|
|
2d0b0c70bf | ||
|
|
3c3be3c61a | ||
|
|
7660943e97 | ||
|
|
d342ccdb78 | ||
|
|
0ea9dc046b | ||
|
|
3a34c2ec25 | ||
|
|
83b0ef94f9 | ||
|
|
3680b93fb3 | ||
|
|
8e86ded09b | ||
|
|
37a50372f6 | ||
|
|
ab99cc612b | ||
|
|
11a4fa23c1 | ||
|
|
5358b4dc15 | ||
|
|
ccbd5dfcf7 | ||
|
|
bb17c57a84 | ||
|
|
3b6213f411 | ||
|
|
a5251f55de | ||
|
|
d846c83afa | ||
|
|
00438bea12 | ||
|
|
0dd5e6b33f | ||
|
|
2d7642ed9b | ||
|
|
740e18454f | ||
|
|
7c17cfc642 | ||
|
|
7f61b67947 | ||
|
|
466c0b0142 | ||
|
|
1c6d9d810e | ||
|
|
c3ad129658 | ||
|
|
a25a72d0d9 | ||
|
|
dbc9e85b90 | ||
|
|
7dafb0cfba | ||
|
|
da33ec9d2f | ||
|
|
605368629f | ||
|
|
ca2b84ec0b | ||
|
|
ac7cf35bb7 | ||
|
|
bd346030f0 | ||
|
|
aa742f415d | ||
|
|
77fa61495f | ||
|
|
bff3811faf | ||
|
|
60d1f8743f | ||
|
|
2d118a904a | ||
|
|
b69f08cbe1 | ||
|
|
644ecdbd32 | ||
|
|
4b47ae6da3 | ||
|
|
fe6a46fc0f | ||
|
|
539a6e05e5 | ||
|
|
3693c83b6e | ||
|
|
18408c9c75 | ||
|
|
39f018bae0 | ||
|
|
c00eb2451f | ||
|
|
6b99309fab | ||
|
|
976b15986f | ||
|
|
df4c18c8d4 | ||
|
|
62d3aa4b76 | ||
|
|
361d17c925 | ||
|
|
9269f15ffc | ||
|
|
925668c7a7 | ||
|
|
a64d526206 | ||
|
|
69234eb708 | ||
|
|
f833133689 | ||
|
|
c73a2655cb | ||
|
|
c55061873c | ||
|
|
d425ff64b4 | ||
|
|
2f47fdf71d | ||
|
|
d4562a2373 | ||
|
|
e13369154d | ||
|
|
9f8719f2a5 | ||
|
|
77b640c41b | ||
|
|
d2f98bc9b4 | ||
|
|
6be54afd12 | ||
|
|
d1fad3c4ea | ||
|
|
a1c4b97a18 | ||
|
|
f00adc755a | ||
|
|
e97939c545 | ||
|
|
63907b7bc5 | ||
|
|
48ad8e529f | ||
|
|
b669ec6976 | ||
|
|
2d4b60b9dd | ||
|
|
e4dd4f9283 | ||
|
|
7798443919 | ||
|
|
9fc096d091 | ||
|
|
9f5584c37d | ||
|
|
392d78b9da | ||
|
|
231289732c | ||
|
|
0c0305a53d | ||
|
|
140ff7a674 | ||
|
|
c3f65e3abd | ||
|
|
306aaa3a20 | ||
|
|
d08cae175a | ||
|
|
f0f4110b4b | ||
|
|
3fbc4e34f4 | ||
|
|
24521f0c92 | ||
|
|
a037595e6e | ||
|
|
b964953daf | ||
|
|
649fd9c845 | ||
|
|
edf4c47451 | ||
|
|
c0ce6a55c4 | ||
|
|
6347e63a15 | ||
|
|
bdcb9a8a0a | ||
|
|
8d90bb7a39 | ||
|
|
d70b456855 | ||
|
|
fbb797fa54 | ||
|
|
878fc9cf4d | ||
|
|
32e679c62e | ||
|
|
8ace518311 | ||
|
|
59164f876f | ||
|
|
5d952661d7 | ||
|
|
67c776c995 | ||
|
|
719754c46c | ||
|
|
9170af888e | ||
|
|
9509737811 | ||
|
|
e42a26c3dc | ||
|
|
eeb258ebd5 | ||
|
|
ab1538b196 | ||
|
|
b258f557d1 | ||
|
|
7e5a230a33 | ||
|
|
20adf82c79 | ||
|
|
0ec6cad6d1 | ||
|
|
eb850cff0b | ||
|
|
9d9b57dd4c | ||
|
|
37b0e2fa28 | ||
|
|
b6d8512316 | ||
|
|
4e17107ac5 | ||
|
|
64c97f7e30 | ||
|
|
f3c513573d | ||
|
|
6965707d45 | ||
|
|
d762836eb9 | ||
|
|
88f8f804d9 | ||
|
|
c1042c6b7a | ||
|
|
3cb382376a | ||
|
|
b44c7e7d7e | ||
|
|
333c907e63 | ||
|
|
b630d59437 | ||
|
|
ba647d9d6c | ||
|
|
449ed0185e | ||
|
|
52e3ad0703 | ||
|
|
bf9dad2550 | ||
|
|
adcecdeefc | ||
|
|
5fabe438e5 | ||
|
|
98e77cb01b | ||
|
|
b05c4c111c | ||
|
|
4d612dc2a2 | ||
|
|
619c7c2d95 | ||
|
|
db886930a0 |
4
.env
@@ -1,2 +1,2 @@
|
||||
REACT_APP_CHAIN_ID="1"
|
||||
REACT_APP_NETWORK_URL="https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
|
||||
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
|
||||
REACT_APP_WALLETCONNECT_BRIDGE_URL="https://uniswap.bridge.walletconnect.org"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
REACT_APP_CHAIN_ID="1"
|
||||
REACT_APP_NETWORK_URL="https://mainnet.infura.io/v3/099fc58e0de9451d80b18d7c74caa7c1"
|
||||
REACT_APP_INFURA_KEY="099fc58e0de9451d80b18d7c74caa7c1"
|
||||
REACT_APP_PORTIS_ID="c0e2bf01-4b08-4fd5-ac7b-8e26b58cd236"
|
||||
REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
|
||||
REACT_APP_GOOGLE_ANALYTICS_ID="UA-128182339-4"
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"node_modules/**/*"
|
||||
],
|
||||
"ignorePatterns": ["node_modules/**/*"],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
@@ -26,6 +24,22 @@
|
||||
"rules": {
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"prettier/prettier": "error",
|
||||
"@typescript-eslint/no-explicit-any": "off"
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/ban-ts-ignore": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@@ -10,9 +10,10 @@ assignees: ''
|
||||
A clear and concise description of the bug.
|
||||
|
||||
**Steps to Reproduce**
|
||||
|
||||
1. Go to ...
|
||||
2. Click on ...
|
||||
...
|
||||
...
|
||||
|
||||
**Expected Behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,4 +1,4 @@
|
||||
blank_issues_enabled: false
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Support
|
||||
url: https://discord.gg/FCfyBSbCU5
|
||||
|
||||
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 }}
|
||||
51
.github/workflows/integration-tests.yaml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Integration Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
integration-tests:
|
||||
name: Cypress
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v2
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- run: yarn cypress install
|
||||
- run: yarn build
|
||||
env:
|
||||
CI: false
|
||||
REACT_APP_NETWORK_URL: "https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
|
||||
|
||||
- run: yarn integration-test
|
||||
env:
|
||||
CYPRESS_INTEGRATION_TEST_PRIVATE_KEY: ${{ secrets.CYPRESS_INTEGRATION_TEST_PRIVATE_KEY }}
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
|
||||
|
||||
23
.github/workflows/lint.yml
vendored
@@ -14,33 +14,32 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up node
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12
|
||||
always-auth: true
|
||||
node-version: 14
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Set output of cache
|
||||
id: yarn-cache
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- name: Node dependency cache
|
||||
uses: actions/cache@v1
|
||||
- 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.outputs.dir }}
|
||||
key: yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
yarn-
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Run linters
|
||||
uses: wearerequired/lint-action@77d70b9a07ecb93bc98dc46dc27d96c4f004d035
|
||||
uses: wearerequired/lint-action@b98b0918aa71490373d2eca9e8e39a9bc1cc2517
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
eslint: true
|
||||
|
||||
34
.github/workflows/release.yaml
vendored
@@ -15,11 +15,11 @@ jobs:
|
||||
changelog: ${{ steps.github_tag_action.outputs.changelog }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Bump version and push tag
|
||||
id: github_tag_action
|
||||
uses: mathieudutour/github-tag-action@v4.5
|
||||
uses: mathieudutour/github-tag-action@331898d5052eedac9b15fec867b5ba66ebf9b692
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
release_branches: .*
|
||||
@@ -31,12 +31,12 @@ jobs:
|
||||
if: ${{ needs.bump_version.outputs.new_tag != null }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v1
|
||||
- name: Set up node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
always-auth: true
|
||||
node-version: 14
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Install dependencies
|
||||
@@ -54,6 +54,14 @@ jobs:
|
||||
pinata-api-key: ${{ secrets.PINATA_API_KEY }}
|
||||
pinata-secret-api-key: ${{ secrets.PINATA_API_SECRET_KEY }}
|
||||
|
||||
- name: Pin to Crust
|
||||
uses: crustio/ipfs-crust-action@v1.0.8
|
||||
continue-on-error: true
|
||||
timeout-minutes: 2
|
||||
with:
|
||||
cid: ${{ steps.upload.outputs.hash }}
|
||||
seeds: ${{ secrets.CRUST_SEEDS }}
|
||||
|
||||
- name: Convert CIDv0 to CIDv1
|
||||
id: convert_cidv0
|
||||
uses: uniswap/convert-cidv0-cidv1@v1.0.0
|
||||
@@ -86,19 +94,13 @@ jobs:
|
||||
The latest release is always accessible via our alias to the Cloudflare IPFS gateway at [app.uniswap.org](https://app.uniswap.org).
|
||||
|
||||
You can also access the Uniswap Interface directly from an IPFS gateway.
|
||||
The Uniswap interface uses [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to store your settings.
|
||||
**Beware** that other sites you access via the _same_ IPFS gateway can read and modify your settings on the Uniswap interface without your permission.
|
||||
You can avoid this issue by using a subdomain IPFS gateway, or our alias to the latest release at [app.uniswap.org](https://app.uniswap.org).
|
||||
The preferred URLs below are safe to use to access this specific release.
|
||||
**BEWARE**: The Uniswap interface uses [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to remember your settings, such as which tokens you have imported.
|
||||
**You should always use an IPFS gateway that enforces origin separation**, or our alias to the latest release at [app.uniswap.org](https://app.uniswap.org).
|
||||
Your Uniswap settings are never remembered across different URLs.
|
||||
|
||||
Preferred URLs:
|
||||
IPFS gateways:
|
||||
- https://${{ steps.convert_cidv0.outputs.cidv1 }}.ipfs.dweb.link/
|
||||
- https://${{ steps.convert_cidv0.outputs.cidv1 }}.ipfs.cf-ipfs.com/
|
||||
- [ipfs://${{ steps.upload.outputs.hash }}/](ipfs://${{ steps.upload.outputs.hash }}/)
|
||||
|
||||
Other IPFS gateways:
|
||||
- https://cloudflare-ipfs.com/ipfs/${{ steps.upload.outputs.hash }}/
|
||||
- https://ipfs.infura.io/ipfs/${{ steps.upload.outputs.hash }}/
|
||||
- https://ipfs.io/ipfs/${{ steps.upload.outputs.hash }}/
|
||||
|
||||
${{ needs.bump_version.outputs.changelog }}
|
||||
|
||||
65
.github/workflows/tests.yaml
vendored
@@ -1,65 +0,0 @@
|
||||
name: Tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
integration-tests:
|
||||
name: Integration tests
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12'
|
||||
always-auth: true
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- uses: actions/cache@v1
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn cypress install
|
||||
- run: yarn build
|
||||
env:
|
||||
CI: false
|
||||
REACT_APP_NETWORK_URL: "https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
|
||||
- run: yarn integration-test
|
||||
|
||||
unit-tests:
|
||||
name: Unit tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12'
|
||||
always-auth: true
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- uses: actions/cache@v1
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn test
|
||||
|
||||
40
.github/workflows/unit-tests.yaml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: Unit Tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
unit-tests:
|
||||
name: Unit tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v2
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Run unit tests
|
||||
run: yarn test
|
||||
11
.gitignore
vendored
@@ -1,5 +1,14 @@
|
||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# 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
|
||||
|
||||
@@ -31,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
|
||||
62
CONTRIBUTING.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# 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
|
||||
- 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!)
|
||||
|
||||
## 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
|
||||
|
||||
Help Uniswap reach a global audience!
|
||||
|
||||
Uniswap uses [Crowdin](https://crowdin.com/project/uniswap-interface)
|
||||
for managing translations. Whenever a new string is added to the project,
|
||||
it gets uploaded to Crowdin for translation by [this workflow](./.github/workflows/crowdin.yaml).
|
||||
|
||||
Every hour, translations are synced from Crowdin to the repository in [this other workflow](./.github/workflows/crowdin-sync.yaml).
|
||||
|
||||
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!
|
||||
68
README.md
@@ -1,66 +1,46 @@
|
||||
# Uniswap Interface
|
||||
|
||||
[](https://github.com/Uniswap/uniswap-interface/actions?query=workflow%3ALint)
|
||||
[](https://github.com/Uniswap/uniswap-interface/actions?query=workflow%3ATests)
|
||||
[](https://prettier.io/)
|
||||
[](https://github.com/Uniswap/uniswap-interface/actions/workflows/unit-tests.yaml)
|
||||
[](https://github.com/Uniswap/uniswap-interface/actions/workflows/integration-tests.yaml)
|
||||
[](https://github.com/Uniswap/uniswap-interface/actions/workflows/lint.yml)
|
||||
[](https://github.com/Uniswap/uniswap-interface/actions/workflows/release.yaml)
|
||||
[](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
|
||||
|
||||
To access the Uniswap Interface, use an IPFS gateway link from the
|
||||
[latest release](https://github.com/Uniswap/uniswap-interface/releases/latest),
|
||||
[latest release](https://github.com/Uniswap/uniswap-interface/releases/latest),
|
||||
or visit [app.uniswap.org](https://app.uniswap.org).
|
||||
|
||||
## Listing a token
|
||||
|
||||
Please see the
|
||||
[@uniswap/default-token-list](https://github.com/uniswap/default-token-list)
|
||||
repository.
|
||||
|
||||
## Development
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
```bash
|
||||
yarn
|
||||
```
|
||||
|
||||
### Run
|
||||
|
||||
```bash
|
||||
yarn start
|
||||
```
|
||||
|
||||
### Configuring the environment (optional)
|
||||
|
||||
To have the interface default to a different network when a wallet is not connected:
|
||||
|
||||
1. Make a copy of `.env` named `.env.local`
|
||||
2. Change `REACT_APP_NETWORK_ID` to `"{YOUR_NETWORK_ID}"`
|
||||
3. Change `REACT_APP_NETWORK_URL` to e.g. `"https://{YOUR_NETWORK_ID}.infura.io/v3/{YOUR_INFURA_KEY}"`
|
||||
|
||||
Note that the interface only works on testnets where both
|
||||
[Uniswap V2](https://uniswap.org/docs/v2/smart-contracts/factory/) and
|
||||
[multicall](https://github.com/makerdao/multicall) are deployed.
|
||||
The interface will not work on other networks.
|
||||
|
||||
## Contributions
|
||||
|
||||
**Please open all pull requests against the `master` branch.**
|
||||
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).
|
||||
|
||||
8
babel-plugin-macros.config.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const isDev = process.env.NODE_ENV !== 'production'
|
||||
|
||||
module.exports = {
|
||||
styledComponents: {
|
||||
fileName: isDev,
|
||||
displayName: isDev,
|
||||
},
|
||||
}
|
||||
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
|
||||
@@ -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,18 +1,27 @@
|
||||
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')
|
||||
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab/500')
|
||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
|
||||
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'ETH')
|
||||
})
|
||||
|
||||
it('does not crash if ETH is duplicated', () => {
|
||||
cy.visit('/add/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xc778417E063141139Fce010982780140Aa0cD5Ab')
|
||||
cy.visit('/add/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xc778417E063141139Fce010982780140Aa0cD5Ab')
|
||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'ETH')
|
||||
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('not.contain.text', 'ETH')
|
||||
})
|
||||
|
||||
it('token not in storage is loaded', () => {
|
||||
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'SKL')
|
||||
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'MKR')
|
||||
})
|
||||
@@ -24,27 +33,31 @@ describe('Add Liquidity', () => {
|
||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
|
||||
})
|
||||
|
||||
it('redirects /add/token-token to add/token/token', () => {
|
||||
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.url().should(
|
||||
'contain',
|
||||
'/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'
|
||||
)
|
||||
})
|
||||
it('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'
|
||||
|
||||
it('redirects /add/WETH-token to /add/WETH-address/token', () => {
|
||||
cy.visit('/add/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.url().should(
|
||||
'contain',
|
||||
'/add/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'
|
||||
)
|
||||
})
|
||||
req.reply({
|
||||
body: {
|
||||
data: {
|
||||
...feeTierDistribution,
|
||||
},
|
||||
},
|
||||
headers: {
|
||||
'access-control-allow-origin': '*',
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
it('redirects /add/token-WETH to /add/token/WETH-address', () => {
|
||||
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85-0xc778417E063141139Fce010982780140Aa0cD5Ab')
|
||||
cy.url().should(
|
||||
'contain',
|
||||
'/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab'
|
||||
)
|
||||
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', () => {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
describe('Migrate V1 Liquidity', () => {
|
||||
describe('Remove V1 liquidity', () => {
|
||||
it('renders the correct page', () => {
|
||||
cy.visit('/remove/v1/0x93bB63aFe1E0180d0eF100D774B473034fd60C36')
|
||||
cy.get('#remove-v1-exchange').should('contain', 'MKR/ETH')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -4,9 +4,4 @@ describe('Pool', () => {
|
||||
cy.get('#join-pool-button').click()
|
||||
cy.url().should('contain', '/add/ETH')
|
||||
})
|
||||
|
||||
it('import pool links to /import', () => {
|
||||
cy.get('#import-pool-link').click()
|
||||
cy.url().should('contain', '/find')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,38 +1,30 @@
|
||||
describe('Remove Liquidity', () => {
|
||||
it('redirects', () => {
|
||||
cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.url().should(
|
||||
'contain',
|
||||
'/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'
|
||||
)
|
||||
})
|
||||
|
||||
it('eth remove', () => {
|
||||
cy.visit('/remove/ETH/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.visit('/remove/v2/ETH/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'ETH')
|
||||
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
|
||||
})
|
||||
|
||||
it('eth remove swap order', () => {
|
||||
cy.visit('/remove/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/ETH')
|
||||
cy.visit('/remove/v2/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/ETH')
|
||||
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'MKR')
|
||||
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'ETH')
|
||||
})
|
||||
|
||||
it('loads the two correct tokens', () => {
|
||||
cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.visit('/remove/v2/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'WETH')
|
||||
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
|
||||
})
|
||||
|
||||
it('does not crash if ETH is duplicated', () => {
|
||||
cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xc778417E063141139Fce010982780140Aa0cD5Ab')
|
||||
cy.visit('/remove/v2/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xc778417E063141139Fce010982780140Aa0cD5Ab')
|
||||
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'WETH')
|
||||
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'WETH')
|
||||
})
|
||||
|
||||
it('token not in storage is loaded', () => {
|
||||
cy.visit('/remove/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.visit('/remove/v2/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'SKL')
|
||||
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
|
||||
})
|
||||
|
||||
@@ -3,36 +3,26 @@ describe('Swap', () => {
|
||||
cy.visit('/swap')
|
||||
})
|
||||
it('can enter an amount into input', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input')
|
||||
.type('0.001', { delay: 200 })
|
||||
.should('have.value', '0.001')
|
||||
cy.get('#swap-currency-input .token-amount-input').type('0.001', { delay: 200 }).should('have.value', '0.001')
|
||||
})
|
||||
|
||||
it('zero swap amount', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input')
|
||||
.type('0.0', { delay: 200 })
|
||||
.should('have.value', '0.0')
|
||||
cy.get('#swap-currency-input .token-amount-input').type('0.0', { delay: 200 }).should('have.value', '0.0')
|
||||
})
|
||||
|
||||
it('invalid swap amount', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input')
|
||||
.type('\\', { delay: 200 })
|
||||
.should('have.value', '')
|
||||
cy.get('#swap-currency-input .token-amount-input').type('\\', { delay: 200 }).should('have.value', '')
|
||||
})
|
||||
|
||||
it('can enter an amount into output', () => {
|
||||
cy.get('#swap-currency-output .token-amount-input')
|
||||
.type('0.001', { delay: 200 })
|
||||
.should('have.value', '0.001')
|
||||
cy.get('#swap-currency-output .token-amount-input').type('0.001', { delay: 200 }).should('have.value', '0.001')
|
||||
})
|
||||
|
||||
it('zero output amount', () => {
|
||||
cy.get('#swap-currency-output .token-amount-input')
|
||||
.type('0.0', { delay: 200 })
|
||||
.should('have.value', '0.0')
|
||||
cy.get('#swap-currency-output .token-amount-input').type('0.0', { delay: 200 }).should('have.value', '0.0')
|
||||
})
|
||||
|
||||
it('can swap ETH for DAI', () => {
|
||||
it.skip('can swap ETH for DAI', () => {
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
cy.get('.token-item-0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735').should('be.visible')
|
||||
cy.get('.token-item-0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735').click({ force: true })
|
||||
@@ -43,13 +33,13 @@ describe('Swap', () => {
|
||||
cy.get('#confirm-swap-or-send').should('contain', 'Confirm Swap')
|
||||
})
|
||||
|
||||
it('add a recipient does not exist unless in expert mode', () => {
|
||||
it.skip('add a recipient does not exist unless in expert mode', () => {
|
||||
cy.get('#add-recipient-button').should('not.exist')
|
||||
})
|
||||
|
||||
describe('expert mode', () => {
|
||||
beforeEach(() => {
|
||||
cy.window().then(win => {
|
||||
cy.window().then((win) => {
|
||||
cy.stub(win, 'prompt').returns('confirm')
|
||||
})
|
||||
cy.get('#open-settings-dialog-button').click()
|
||||
@@ -57,16 +47,16 @@ describe('Swap', () => {
|
||||
cy.get('#confirm-expert-mode').click()
|
||||
})
|
||||
|
||||
it('add a recipient is visible', () => {
|
||||
it.skip('add a recipient is visible', () => {
|
||||
cy.get('#add-recipient-button').should('be.visible')
|
||||
})
|
||||
|
||||
it('add a recipient', () => {
|
||||
it.skip('add a recipient', () => {
|
||||
cy.get('#add-recipient-button').click()
|
||||
cy.get('#recipient').should('exist')
|
||||
})
|
||||
|
||||
it('remove recipient', () => {
|
||||
it.skip('remove recipient', () => {
|
||||
cy.get('#add-recipient-button').click()
|
||||
cy.get('#remove-recipient-button').click()
|
||||
cy.get('#recipient').should('not.exist')
|
||||
|
||||
@@ -6,17 +6,23 @@
|
||||
|
||||
import { JsonRpcProvider } from '@ethersproject/providers'
|
||||
import { Wallet } from '@ethersproject/wallet'
|
||||
import { _Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
|
||||
import { Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
|
||||
|
||||
// never send real ether to this, obviously
|
||||
const PRIVATE_KEY_TEST_NEVER_USE = '0xad20c82497421e9784f18460ad2fe84f73569068e98e270b3e63743268af5763'
|
||||
// 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 = '0x0fF2D1eFd7A57B7562b2bf27F3f37899dB27F4a5'
|
||||
export const TEST_ADDRESS_NEVER_USE = new Wallet(TEST_PRIVATE_KEY).address
|
||||
|
||||
export const TEST_ADDRESS_NEVER_USE_SHORTENED = '0x0fF2...F4a5'
|
||||
export const TEST_ADDRESS_NEVER_USE_SHORTENED = `${TEST_ADDRESS_NEVER_USE.substr(
|
||||
0,
|
||||
6
|
||||
)}...${TEST_ADDRESS_NEVER_USE.substr(-4, 4)}`
|
||||
|
||||
class CustomizedBridge extends Eip1193Bridge {
|
||||
chainId = 4
|
||||
|
||||
class CustomizedBridge extends _Eip1193Bridge {
|
||||
async sendAsync(...args) {
|
||||
console.debug('sendAsync called', ...args)
|
||||
return this.send(...args)
|
||||
@@ -75,8 +81,8 @@ Cypress.Commands.overwrite('visit', (original, url, options) => {
|
||||
options && options.onBeforeLoad && options.onBeforeLoad(win)
|
||||
win.localStorage.clear()
|
||||
const provider = new JsonRpcProvider('https://rinkeby.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847', 4)
|
||||
const signer = new Wallet(PRIVATE_KEY_TEST_NEVER_USE, provider)
|
||||
const signer = new Wallet(TEST_PRIVATE_KEY, provider)
|
||||
win.ethereum = new CustomizedBridge(signer, provider)
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
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',
|
||||
}
|
||||
105
package.json
@@ -4,88 +4,113 @@
|
||||
"homepage": ".",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@ethersproject/experimental": "^5.0.1",
|
||||
"@ethersproject/experimental": "^5.2.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",
|
||||
"@reduxjs/toolkit": "^1.3.5",
|
||||
"@react-hook/window-scroll": "^1.3.0",
|
||||
"@reduxjs/toolkit": "^1.6.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",
|
||||
"@types/react": "^16.9.34",
|
||||
"@types/react-dom": "^16.9.7",
|
||||
"@types/react-redux": "^7.1.8",
|
||||
"@types/react": "^17.0.2",
|
||||
"@types/react-dom": "^17.0.1",
|
||||
"@types/react-redux": "^7.1.16",
|
||||
"@types/react-router-dom": "^5.0.0",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.0",
|
||||
"@types/react-window": "^1.8.2",
|
||||
"@types/rebass": "^4.0.5",
|
||||
"@types/rebass": "^4.0.7",
|
||||
"@types/styled-components": "^5.1.0",
|
||||
"@types/testing-library__cypress": "^5.0.5",
|
||||
"@types/ua-parser-js": "^0.7.35",
|
||||
"@types/wcag-contrast": "^3.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^2.31.0",
|
||||
"@typescript-eslint/parser": "^2.31.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.1.0",
|
||||
"@typescript-eslint/parser": "^4.1.0",
|
||||
"@uniswap/default-token-list": "^2.0.0",
|
||||
"@uniswap/governance": "^1.0.2",
|
||||
"@uniswap/liquidity-staker": "^1.0.2",
|
||||
"@uniswap/merkle-distributor": "1.0.1",
|
||||
"@uniswap/sdk": "3.0.3",
|
||||
"@uniswap/token-lists": "^1.0.0-beta.19",
|
||||
"@uniswap/token-lists": "^1.0.0-beta.25",
|
||||
"@uniswap/v2-core": "1.0.0",
|
||||
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
||||
"@uniswap/v2-sdk": "^3.0.0-alpha.2",
|
||||
"@uniswap/v3-core": "1.0.0",
|
||||
"@uniswap/v3-periphery": "^1.1.1",
|
||||
"@uniswap/v3-sdk": "^3.2.1",
|
||||
"@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.1.1",
|
||||
"@web3-react/walletlink-connector": "^6.0.9",
|
||||
"@web3-react/walletconnect-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",
|
||||
"eslint": "^6.8.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.2.0",
|
||||
"graphql": "^15.5.0",
|
||||
"graphql-request": "^3.4.0",
|
||||
"inter-ui": "^3.13.1",
|
||||
"jazzicon": "^1.5.0",
|
||||
"lodash.flatmap": "^4.5.0",
|
||||
"luxon": "^1.25.0",
|
||||
"multicodec": "^2.0.0",
|
||||
"multihashes": "^3.0.1",
|
||||
"ms.macro": "^2.0.0",
|
||||
"multicodec": "^3.0.1",
|
||||
"multihashes": "^4.0.2",
|
||||
"node-vibrant": "^3.1.5",
|
||||
"polished": "^3.3.2",
|
||||
"prettier": "^1.17.0",
|
||||
"prettier": "^2.2.1",
|
||||
"qs": "^6.9.4",
|
||||
"react": "^16.13.1",
|
||||
"react": "^17.0.1",
|
||||
"react-confetti": "^6.0.0",
|
||||
"react-device-detect": "^1.6.2",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-feather": "^2.0.8",
|
||||
"react-ga": "^2.5.7",
|
||||
"react-i18next": "^10.7.0",
|
||||
"react-is": "^17.0.2",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-popper": "^2.2.3",
|
||||
"react-redux": "^7.2.0",
|
||||
"react-redux": "^7.2.2",
|
||||
"react-router-dom": "^5.0.0",
|
||||
"react-scripts": "^3.4.1",
|
||||
"react-scripts": "^4.0.3",
|
||||
"react-spring": "^8.0.27",
|
||||
"react-use-gesture": "^6.0.14",
|
||||
"react-virtualized-auto-sizer": "^1.0.2",
|
||||
"react-window": "^1.8.5",
|
||||
"rebass": "^4.0.7",
|
||||
"redux-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",
|
||||
"typescript": "^3.8.3",
|
||||
"styled-components": "^5.3.0",
|
||||
"styled-system": "^5.1.5",
|
||||
"typechain": "^5.0.0",
|
||||
"typescript": "^4.2.3",
|
||||
"ua-parser-js": "^0.7.28",
|
||||
"use-count-up": "^2.2.5",
|
||||
"wcag-contrast": "^3.0.0",
|
||||
"workbox-core": "^6.1.0",
|
||||
@@ -95,15 +120,21 @@
|
||||
"workbox-strategies": "^6.1.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"@walletconnect/web3-provider": "1.1.1-alpha.0"
|
||||
"@walletconnect/web3-provider": "1.5.0-rc.5"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"start:service-worker": "yarn build && yarn serve -s build",
|
||||
"build": "react-scripts build",
|
||||
"compile-contract-types": "yarn compile-external-abi-types && yarn compile-v3-contract-types",
|
||||
"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",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
"eject": "react-scripts eject",
|
||||
"integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'"
|
||||
"prestart": "yarn graphql:generate && touch src/locales/en-US.po"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app",
|
||||
@@ -124,7 +155,5 @@
|
||||
]
|
||||
},
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@uniswap/default-token-list": "^2.0.0"
|
||||
}
|
||||
"dependencies": {}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.6 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,91 +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"
|
||||
}
|
||||
@@ -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": "輸入代幣地址"
|
||||
}
|
||||
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"
|
||||
}
|
||||
]
|
||||
20
src/abis/eip_2612.json
Normal file
@@ -0,0 +1,20 @@
|
||||
[
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [{ "name": "owner", "type": "address" }],
|
||||
"name": "nonces",
|
||||
"outputs": [{ "name": "", "type": "uint256" }],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [],
|
||||
"name": "DOMAIN_SEPARATOR",
|
||||
"outputs": [{ "name": "", "type": "bytes32" }],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
|
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 |
BIN
src/assets/images/sandtexture.webp
Normal file
|
After Width: | Height: | Size: 235 KiB |
BIN
src/assets/images/squiggle.png
Normal file
|
After Width: | Height: | Size: 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 |
@@ -1,11 +1,11 @@
|
||||
<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.15217 1.55141C3.96412 1.52242 3.95619 1.51902 4.04468 1.5055C4.21427 1.47958 4.61472 1.51491 4.89067 1.58012C5.53489 1.73232 6.12109 2.12221 6.74683 2.81466L6.91307 2.99862L7.15088 2.96062C8.15274 2.8006 9.17194 2.92778 10.0244 3.31918C10.2589 3.42686 10.6287 3.64121 10.6749 3.69629C10.6896 3.71384 10.7166 3.82684 10.7349 3.94742C10.7982 4.36458 10.7665 4.68434 10.6382 4.92317C10.5683 5.05313 10.5644 5.09432 10.6114 5.20554C10.6489 5.2943 10.7534 5.35999 10.8569 5.35985C11.0687 5.35956 11.2967 5.0192 11.4024 4.54561L11.4444 4.3575L11.5275 4.45109C11.9835 4.96459 12.3417 5.66488 12.4032 6.16335L12.4192 6.29332L12.3426 6.17517C12.2107 5.97186 12.0781 5.83346 11.9084 5.72183C11.6024 5.52062 11.2789 5.45215 10.4222 5.40727C9.64839 5.36675 9.21045 5.30106 8.77621 5.16032C8.03738 4.9209 7.66493 4.60204 6.78729 3.4576C6.39748 2.94928 6.15654 2.66804 5.91687 2.44155C5.37228 1.92691 4.83716 1.65701 4.15217 1.55141Z" fill="white"/>
|
||||
<path d="M4.15217 1.55141C3.96412 1.52242 3.95619 1.51902 4.04468 1.5055C4.21427 1.47958 4.61472 1.51491 4.89067 1.58012C5.53489 1.73232 6.12109 2.12221 6.74683 2.81466L6.91307 2.99862L7.15088 2.96062C8.15274 2.8006 9.17194 2.92778 10.0244 3.31918C10.2589 3.42686 10.6287 3.64121 10.6749 3.69629C10.6896 3.71384 10.7166 3.82684 10.7349 3.94742C10.7982 4.36458 10.7665 4.68434 10.6382 4.92317C10.5683 5.05313 10.5644 5.09432 10.6114 5.20554C10.6489 5.2943 10.7534 5.35999 10.8569 5.35985C11.0687 5.35956 11.2968 5.0192 11.4024 4.54561L11.4444 4.3575L11.5275 4.45109C11.9835 4.96459 12.3417 5.66488 12.4032 6.16335L12.4192 6.29332L12.3426 6.17517C12.2107 5.97186 12.0781 5.83346 11.9084 5.72183C11.6024 5.52062 11.2789 5.45215 10.4222 5.40727C9.64839 5.36675 9.21045 5.30106 8.77621 5.16032C8.03738 4.9209 7.66493 4.60204 6.78729 3.4576C6.39748 2.94928 6.15654 2.66804 5.91687 2.44155C5.37228 1.92691 4.83716 1.65701 4.15217 1.55141Z" fill="white"/>
|
||||
<path d="M10.8494 2.68637C10.8689 2.34575 10.9153 2.12108 11.0088 1.9159C11.0458 1.83469 11.0804 1.76822 11.0858 1.76822C11.0911 1.76822 11.075 1.82816 11.05 1.90142C10.9821 2.10054 10.9709 2.3729 11.0177 2.68978C11.0771 3.09184 11.1109 3.14985 11.5385 3.58416C11.739 3.78788 11.9723 4.0448 12.0568 4.15511L12.2106 4.35568L12.0568 4.21234C11.8688 4.03705 11.4364 3.6952 11.3409 3.64633C11.2768 3.61356 11.2673 3.61413 11.2278 3.65321C11.1914 3.68922 11.1837 3.74333 11.1787 3.99915C11.1708 4.39786 11.1161 4.65377 10.9842 4.90965C10.9128 5.04805 10.9015 5.01851 10.9661 4.8623C11.0143 4.74566 11.0192 4.69439 11.0189 4.30842C11.0181 3.53291 10.9255 3.34647 10.3823 3.02709C10.2447 2.94618 10.0179 2.8295 9.87839 2.76778C9.73887 2.70606 9.62805 2.6523 9.63208 2.64828C9.64746 2.63307 10.1772 2.78675 10.3905 2.86828C10.7077 2.98954 10.76 3.00526 10.7985 2.99063C10.8244 2.98082 10.8369 2.90608 10.8494 2.68637Z" fill="white"/>
|
||||
<path d="M4.51745 4.01304C4.13569 3.49066 3.89948 2.68973 3.95062 2.091L3.96643 1.90572L4.05333 1.92148C4.21652 1.95106 4.49789 2.05515 4.62964 2.13469C4.9912 2.35293 5.14773 2.64027 5.30697 3.37811C5.35362 3.59423 5.41482 3.8388 5.44298 3.9216C5.48831 4.05487 5.65962 4.36617 5.7989 4.56834C5.89922 4.71395 5.83258 4.78295 5.61082 4.76305C5.27215 4.73267 4.8134 4.41799 4.51745 4.01304Z" fill="white"/>
|
||||
<path d="M10.3863 7.90088C8.60224 7.18693 7.97389 6.56721 7.97389 5.52157C7.97389 5.36769 7.97922 5.24179 7.98571 5.24179C7.99221 5.24179 8.06124 5.29257 8.1391 5.35465C8.50088 5.64305 8.906 5.76623 10.0275 5.92885C10.6875 6.02455 11.0589 6.10185 11.4015 6.21477C12.4904 6.57371 13.1641 7.30212 13.3248 8.29426C13.3715 8.58255 13.3441 9.12317 13.2684 9.4081C13.2087 9.63315 13.0263 10.0388 12.9779 10.0544C12.9645 10.0587 12.9514 10.0076 12.9479 9.93809C12.9296 9.56554 12.7402 9.20285 12.4221 8.93116C12.0604 8.62227 11.5745 8.37633 10.3863 7.90088Z" fill="white"/>
|
||||
<path d="M9.13385 8.19748C9.11149 8.06527 9.07272 7.89643 9.04769 7.82228L9.00217 7.68748L9.08672 7.7818C9.20374 7.91234 9.2962 8.07937 9.37457 8.30185C9.43438 8.47165 9.44111 8.52215 9.44066 8.79807C9.4402 9.06896 9.43273 9.12575 9.3775 9.27859C9.29042 9.51959 9.18233 9.69048 9.00097 9.87391C8.67507 10.2036 8.25607 10.3861 7.65143 10.4618C7.54633 10.4749 7.24 10.4971 6.97069 10.511C6.292 10.5461 5.84531 10.6186 5.44393 10.7587C5.38623 10.7788 5.3347 10.7911 5.32947 10.7859C5.31323 10.7698 5.58651 10.6079 5.81223 10.4998C6.1305 10.3474 6.44733 10.2643 7.15719 10.1468C7.50785 10.0887 7.86998 10.0183 7.96194 9.99029C8.83033 9.72566 9.27671 9.04276 9.13385 8.19748Z" fill="white"/>
|
||||
<path d="M9.95169 9.64109C9.71465 9.13463 9.66022 8.64564 9.79008 8.18961C9.80399 8.14088 9.82632 8.101 9.83976 8.101C9.85319 8.101 9.90913 8.13105 9.96404 8.16777C10.0733 8.24086 10.2924 8.36395 10.876 8.68023C11.6043 9.0749 12.0196 9.3805 12.302 9.72965C12.5493 10.0354 12.7023 10.3837 12.776 10.8084C12.8177 11.0489 12.7932 11.6277 12.7311 11.8699C12.5353 12.6337 12.0802 13.2336 11.4311 13.5837C11.336 13.635 11.2506 13.6771 11.2414 13.6773C11.2321 13.6775 11.2668 13.5899 11.3184 13.4827C11.5367 13.029 11.5616 12.5877 11.3965 12.0965C11.2954 11.7957 11.0893 11.4287 10.6732 10.8084C10.1893 10.0873 10.0707 9.89539 9.95169 9.64109Z" fill="white"/>
|
||||
<path d="M3.25046 12.3737C3.91252 11.8181 4.73629 11.4234 5.48666 11.3022C5.81005 11.25 6.34877 11.2707 6.64823 11.3469C7.12824 11.469 7.55763 11.7425 7.78094 12.0683C7.99918 12.3867 8.09281 12.6642 8.19029 13.2816C8.22875 13.5252 8.27057 13.7697 8.28323 13.8251C8.35644 14.1451 8.4989 14.4008 8.67544 14.5293C8.95583 14.7333 9.43865 14.7459 9.91362 14.5618C9.99423 14.5305 10.0642 14.5089 10.0691 14.5138C10.0864 14.5308 9.84719 14.6899 9.67847 14.7737C9.45143 14.8864 9.2709 14.93 9.03102 14.93C8.59601 14.93 8.23486 14.7101 7.9335 14.2616C7.87419 14.1733 7.7409 13.909 7.63729 13.6741C7.3191 12.9528 7.16199 12.7331 6.79255 12.4926C6.47104 12.2834 6.05641 12.2459 5.74449 12.3979C5.33475 12.5976 5.22043 13.118 5.51389 13.4478C5.63053 13.5789 5.84803 13.6919 6.02588 13.7139C6.35861 13.7551 6.64455 13.5035 6.64455 13.1696C6.64455 12.9528 6.56071 12.8291 6.34966 12.7344C6.0614 12.6051 5.75156 12.7563 5.75304 13.0254C5.75368 13.1402 5.80396 13.2122 5.91971 13.2643C5.99397 13.2977 5.99569 13.3003 5.93514 13.2878C5.67066 13.2333 5.6087 12.9164 5.82135 12.706C6.07667 12.4535 6.60461 12.5649 6.78591 12.9097C6.86208 13.0545 6.87091 13.3429 6.80451 13.517C6.6559 13.9068 6.22256 14.1117 5.78297 14.0002C5.48368 13.9242 5.36181 13.842 5.00097 13.4726C4.37395 12.8306 4.13053 12.7062 3.22657 12.566L3.05335 12.5391L3.25046 12.3737Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.308383 0.883984C2.40235 3.40996 3.84457 4.45213 4.00484 4.67231C4.13717 4.85412 4.08737 5.01757 3.86067 5.14567C3.7346 5.21689 3.47541 5.28905 3.34564 5.28905C3.19887 5.28905 3.14847 5.23278 3.14847 5.23278C3.06337 5.15255 3.01544 5.16658 2.5784 4.39555C1.97166 3.45981 1.46389 2.68357 1.45004 2.67057C1.41801 2.64052 1.41856 2.64153 2.51654 4.59413C2.69394 5.0011 2.55182 5.15049 2.55182 5.20845C2.55182 5.32636 2.51946 5.38834 2.37311 5.55059C2.12914 5.8211 2.02008 6.12505 1.94135 6.7541C1.8531 7.45926 1.60492 7.95737 0.917156 8.80989C0.514562 9.30893 0.448686 9.4004 0.3471 9.60153C0.219144 9.85482 0.183961 9.99669 0.169701 10.3165C0.154629 10.6547 0.183983 10.8732 0.287934 11.1965C0.378939 11.4796 0.473932 11.6665 0.716778 12.0403C0.926352 12.3629 1.04702 12.6027 1.04702 12.6965C1.04702 12.7711 1.06136 12.7712 1.38611 12.6983C2.16328 12.5239 2.79434 12.2171 3.14925 11.8411C3.36891 11.6084 3.42048 11.4799 3.42215 11.1611C3.42325 10.9525 3.41587 10.9088 3.35914 10.7888C3.2668 10.5935 3.09869 10.4311 2.72817 10.1794C2.2427 9.84953 2.03534 9.58398 1.97807 9.21878C1.93108 8.91913 1.98559 8.70771 2.25416 8.14825C2.53214 7.56916 2.60103 7.32239 2.64763 6.73869C2.67773 6.36158 2.71941 6.21286 2.82842 6.09348C2.94212 5.969 3.04447 5.92684 3.32584 5.88863C3.78457 5.82635 4.07667 5.70839 4.31677 5.48849C4.52505 5.29772 4.61221 5.11391 4.62558 4.8372L4.63574 4.62747L4.51934 4.49259C4.09783 4.00411 0.0261003 0.5 0.000160437 0.5C-0.00538105 0.5 0.133325 0.672804 0.308383 0.883984ZM1.28364 10.6992C1.37894 10.5314 1.3283 10.3158 1.16889 10.2104C1.01827 10.1109 0.78428 10.1578 0.78428 10.2875C0.78428 10.3271 0.806303 10.3559 0.855937 10.3813C0.939514 10.424 0.945581 10.4721 0.879823 10.5703C0.81323 10.6698 0.818604 10.7573 0.894991 10.8167C1.0181 10.9125 1.19237 10.8598 1.28364 10.6992Z" fill="white"/>
|
||||
<path d="M9.13385 8.19748C9.11149 8.06527 9.07272 7.89643 9.04769 7.82228L9.00217 7.68748L9.08672 7.7818C9.20374 7.91233 9.2962 8.07937 9.37457 8.30185C9.43438 8.47165 9.44111 8.52215 9.44066 8.79807C9.4402 9.06896 9.43273 9.12575 9.3775 9.27858C9.29042 9.51959 9.18233 9.69048 9.00097 9.87391C8.67507 10.2036 8.25607 10.3861 7.65143 10.4618C7.54633 10.4749 7.24 10.4971 6.97069 10.511C6.292 10.5461 5.84531 10.6186 5.44393 10.7587C5.38623 10.7788 5.3347 10.7911 5.32947 10.7859C5.31323 10.7698 5.58651 10.6079 5.81223 10.4998C6.1305 10.3474 6.44733 10.2643 7.15719 10.1468C7.50785 10.0887 7.86998 10.0183 7.96194 9.99029C8.83033 9.72566 9.27671 9.04276 9.13385 8.19748Z" fill="white"/>
|
||||
<path d="M9.95169 9.64109C9.71465 9.13463 9.66022 8.64564 9.79009 8.18961C9.80399 8.14088 9.82632 8.101 9.83976 8.101C9.85319 8.101 9.90913 8.13105 9.96404 8.16777C10.0733 8.24086 10.2924 8.36395 10.876 8.68023C11.6043 9.0749 12.0196 9.3805 12.302 9.72965C12.5493 10.0354 12.7023 10.3837 12.776 10.8084C12.8177 11.0489 12.7932 11.6277 12.7311 11.8699C12.5353 12.6337 12.0802 13.2336 11.4311 13.5837C11.336 13.635 11.2506 13.6771 11.2414 13.6773C11.2321 13.6775 11.2668 13.5899 11.3184 13.4827C11.5367 13.029 11.5616 12.5877 11.3965 12.0965C11.2954 11.7957 11.0893 11.4287 10.6732 10.8084C10.1893 10.0873 10.0707 9.89539 9.95169 9.64109Z" fill="white"/>
|
||||
<path d="M3.25046 12.3737C3.91252 11.8181 4.73629 11.4234 5.48666 11.3022C5.81005 11.25 6.34877 11.2707 6.64823 11.3469C7.12824 11.469 7.55763 11.7425 7.78094 12.0683C7.99918 12.3867 8.09281 12.6642 8.19029 13.2816C8.22875 13.5252 8.27057 13.7697 8.28323 13.8251C8.35644 14.1451 8.4989 14.4008 8.67544 14.5293C8.95583 14.7333 9.43865 14.7459 9.91362 14.5618C9.99423 14.5305 10.0642 14.5089 10.0691 14.5138C10.0864 14.5308 9.84719 14.6899 9.67847 14.7737C9.45143 14.8864 9.2709 14.93 9.03102 14.93C8.59601 14.93 8.23486 14.7101 7.9335 14.2616C7.87419 14.1733 7.7409 13.909 7.63729 13.6741C7.3191 12.9528 7.16199 12.7331 6.79255 12.4926C6.47104 12.2834 6.05641 12.2459 5.74449 12.3979C5.33475 12.5976 5.22043 13.118 5.51389 13.4478C5.63053 13.5789 5.84803 13.6919 6.02588 13.7139C6.35861 13.7551 6.64455 13.5035 6.64455 13.1696C6.64455 12.9528 6.56071 12.8291 6.34966 12.7344C6.0614 12.6051 5.75156 12.7562 5.75304 13.0254C5.75368 13.1402 5.80396 13.2122 5.91971 13.2643C5.99397 13.2977 5.99569 13.3003 5.93514 13.2878C5.67066 13.2333 5.6087 12.9164 5.82135 12.706C6.07667 12.4535 6.60461 12.5649 6.78591 12.9097C6.86208 13.0545 6.87092 13.3429 6.80451 13.517C6.6559 13.9068 6.22256 14.1117 5.78297 14.0002C5.48368 13.9242 5.36181 13.842 5.00097 13.4726C4.37395 12.8306 4.13053 12.7062 3.22657 12.566L3.05335 12.5391L3.25046 12.3737Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.308383 0.883984C2.40235 3.40996 3.84457 4.45213 4.00484 4.67231C4.13717 4.85412 4.08737 5.01757 3.86067 5.14567C3.7346 5.21689 3.47541 5.28905 3.34564 5.28905C3.19887 5.28905 3.14847 5.23278 3.14847 5.23278C3.06337 5.15255 3.01544 5.16658 2.5784 4.39555C1.97166 3.45981 1.46389 2.68357 1.45004 2.67057C1.41801 2.64052 1.41856 2.64153 2.51654 4.59413C2.69394 5.0011 2.55182 5.15049 2.55182 5.20845C2.55182 5.32636 2.51946 5.38834 2.37311 5.55059C2.12914 5.8211 2.02008 6.12505 1.94135 6.7541C1.8531 7.45926 1.60492 7.95737 0.917156 8.80989C0.514562 9.30893 0.448686 9.4004 0.3471 9.60153C0.219144 9.85482 0.183961 9.99669 0.169701 10.3165C0.154629 10.6547 0.183983 10.8732 0.287934 11.1965C0.378939 11.4796 0.473932 11.6665 0.716778 12.0403C0.926351 12.3629 1.04702 12.6027 1.04702 12.6965C1.04702 12.7711 1.06136 12.7712 1.38611 12.6983C2.16328 12.5239 2.79434 12.2171 3.14925 11.8411C3.36891 11.6084 3.42048 11.4799 3.42215 11.1611C3.42325 10.9525 3.41587 10.9088 3.35914 10.7888C3.2668 10.5935 3.09869 10.4311 2.72817 10.1794C2.2427 9.84953 2.03534 9.58398 1.97807 9.21878C1.93108 8.91913 1.98559 8.70771 2.25416 8.14825C2.53214 7.56916 2.60103 7.32239 2.64763 6.73869C2.67773 6.36158 2.71941 6.21286 2.82842 6.09348C2.94212 5.969 3.04447 5.92684 3.32584 5.88863C3.78457 5.82635 4.07667 5.70839 4.31677 5.48849C4.52505 5.29772 4.61221 5.11391 4.62558 4.8372L4.63574 4.62747L4.51934 4.49259C4.09783 4.00411 0.0261003 0.5 0.000160437 0.5C-0.00538105 0.5 0.133325 0.672804 0.308383 0.883984ZM1.28364 10.6992C1.37894 10.5314 1.3283 10.3158 1.16889 10.2104C1.01827 10.1109 0.78428 10.1578 0.78428 10.2875C0.78428 10.3271 0.806303 10.3559 0.855937 10.3813C0.939514 10.424 0.945581 10.4721 0.879823 10.5703C0.81323 10.6698 0.818604 10.7573 0.894991 10.8167C1.0181 10.9125 1.19237 10.8598 1.28364 10.6992Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.92523 5.99865C4.70988 6.06439 4.50054 6.29124 4.43574 6.5291C4.39621 6.67421 4.41864 6.92875 4.47785 7.00736C4.57351 7.13433 4.66602 7.16778 4.91651 7.16603C5.40693 7.16263 5.83327 6.95358 5.88284 6.69224C5.92347 6.47801 5.73622 6.18112 5.4783 6.05078C5.34521 5.98355 5.06217 5.95688 4.92523 5.99865ZM5.49853 6.44422C5.57416 6.33741 5.54107 6.22198 5.41245 6.14391C5.1675 5.99525 4.79708 6.11827 4.79708 6.34826C4.79708 6.46274 4.99025 6.58765 5.16731 6.58765C5.28516 6.58765 5.44644 6.5178 5.49853 6.44422Z" fill="white"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
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 |
6
src/assets/svg/switch.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 2.5L12.5 5L10 7.5" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3 5L12.3333 5" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M5.5 13.5L3 11L5.5 8.5" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12.3333 11L3 11" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 476 B |
@@ -1,9 +1,10 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import styled from 'styled-components/macro'
|
||||
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'
|
||||
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,12 +19,14 @@ 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};
|
||||
padding: 1rem 1rem;
|
||||
font-weight: 500;
|
||||
color: ${props => (props.color === 'blue' ? ({ theme }) => theme.primary1 : 'inherit')};
|
||||
color: ${(props) => (props.color === 'blue' ? ({ theme }) => theme.primary1 : 'inherit')};
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
padding: 1rem;
|
||||
`};
|
||||
@@ -76,7 +76,6 @@ const AccountGroupingRow = styled.div`
|
||||
`
|
||||
|
||||
const AccountSection = styled.div`
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
padding: 0rem 1rem;
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`padding: 0rem 1rem 1.5rem 1rem;`};
|
||||
`
|
||||
@@ -223,22 +222,26 @@ export default function AccountDetails({
|
||||
pendingTransactions,
|
||||
confirmedTransactions,
|
||||
ENSName,
|
||||
openOptions
|
||||
openOptions,
|
||||
}: AccountDetailsProps) {
|
||||
const { chainId, account, connector } = useActiveWeb3React()
|
||||
const theme = useContext(ThemeContext)
|
||||
const dispatch = useDispatch<AppDispatch>()
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
function formatConnectorName() {
|
||||
const { ethereum } = window
|
||||
const isMetaMask = !!(ethereum && ethereum.isMetaMask)
|
||||
const name = Object.keys(SUPPORTED_WALLETS)
|
||||
.filter(
|
||||
k =>
|
||||
(k) =>
|
||||
SUPPORTED_WALLETS[k].connector === connector && (connector !== injected || isMetaMask === (k === 'METAMASK'))
|
||||
)
|
||||
.map(k => SUPPORTED_WALLETS[k].name)[0]
|
||||
return <WalletName>Connected with {name}</WalletName>
|
||||
.map((k) => SUPPORTED_WALLETS[k].name)[0]
|
||||
return (
|
||||
<WalletName>
|
||||
<Trans>Connected with {name}</Trans>
|
||||
</WalletName>
|
||||
)
|
||||
}
|
||||
|
||||
function getStatusIcon() {
|
||||
@@ -251,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>
|
||||
</>
|
||||
@@ -295,7 +298,9 @@ export default function AccountDetails({
|
||||
<CloseIcon onClick={toggleWalletModal}>
|
||||
<CloseColor />
|
||||
</CloseIcon>
|
||||
<HeaderRow>Account</HeaderRow>
|
||||
<HeaderRow>
|
||||
<Trans>Account</Trans>
|
||||
</HeaderRow>
|
||||
<AccountSection>
|
||||
<YourAccount>
|
||||
<InfoCard>
|
||||
@@ -309,7 +314,7 @@ export default function AccountDetails({
|
||||
;(connector as any).close()
|
||||
}}
|
||||
>
|
||||
Disconnect
|
||||
<Trans>Disconnect</Trans>
|
||||
</WalletAction>
|
||||
)}
|
||||
<WalletAction
|
||||
@@ -318,7 +323,7 @@ export default function AccountDetails({
|
||||
openOptions()
|
||||
}}
|
||||
>
|
||||
Change
|
||||
<Trans>Change</Trans>
|
||||
</WalletAction>
|
||||
</div>
|
||||
</AccountGroupingRow>
|
||||
@@ -348,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>
|
||||
@@ -370,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>
|
||||
@@ -395,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
|
||||
onChange,
|
||||
}: {
|
||||
id?: string
|
||||
className?: string
|
||||
label?: ReactNode
|
||||
placeholder?: string
|
||||
// the typed string value
|
||||
value: string
|
||||
// triggers whenever the typed value changes
|
||||
@@ -82,7 +89,7 @@ export default function AddressInputPanel({
|
||||
const { address, loading, name } = useENS(value)
|
||||
|
||||
const handleInput = useCallback(
|
||||
event => {
|
||||
(event) => {
|
||||
const input = event.target.value
|
||||
const withoutSpaces = input.replace(/\s+/g, '')
|
||||
onChange(withoutSpaces)
|
||||
@@ -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}
|
||||
|
||||
80
src/components/Badge/RangeBadge.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import Badge, { BadgeVariant } from 'components/Badge'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { MouseoverTooltip } from '../../components/Tooltip'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { AlertCircle } from 'react-feather'
|
||||
|
||||
const BadgeWrapper = styled.div`
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
`
|
||||
|
||||
const BadgeText = styled.div`
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
`
|
||||
|
||||
const ActiveDot = styled.span`
|
||||
background-color: ${({ theme }) => theme.success};
|
||||
border-radius: 50%;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
margin-right: 4px;
|
||||
`
|
||||
|
||||
export default function RangeBadge({
|
||||
removed,
|
||||
inRange,
|
||||
}: {
|
||||
removed: boolean | undefined
|
||||
inRange: boolean | undefined
|
||||
}) {
|
||||
return (
|
||||
<BadgeWrapper>
|
||||
{removed ? (
|
||||
<MouseoverTooltip text={<Trans>Your position has 0 liquidity, and is not earning fees.</Trans>}>
|
||||
<Badge variant={BadgeVariant.DEFAULT}>
|
||||
<AlertCircle width={14} height={14} />
|
||||
|
||||
<BadgeText>
|
||||
<Trans>Closed</Trans>
|
||||
</BadgeText>
|
||||
</Badge>
|
||||
</MouseoverTooltip>
|
||||
) : inRange ? (
|
||||
<MouseoverTooltip
|
||||
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>
|
||||
<Trans>In range</Trans>
|
||||
</BadgeText>
|
||||
</Badge>
|
||||
</MouseoverTooltip>
|
||||
) : (
|
||||
<MouseoverTooltip
|
||||
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>
|
||||
<Trans>Out of range</Trans>
|
||||
</BadgeText>
|
||||
</Badge>
|
||||
</MouseoverTooltip>
|
||||
)}
|
||||
</BadgeWrapper>
|
||||
)
|
||||
}
|
||||
73
src/components/Badge/index.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import { readableColor } from 'polished'
|
||||
import { PropsWithChildren } from 'react'
|
||||
import styled, { DefaultTheme } from 'styled-components/macro'
|
||||
import { Color } from 'theme/styled'
|
||||
|
||||
export enum BadgeVariant {
|
||||
DEFAULT = 'DEFAULT',
|
||||
NEGATIVE = 'NEGATIVE',
|
||||
POSITIVE = 'POSITIVE',
|
||||
PRIMARY = 'PRIMARY',
|
||||
WARNING = 'WARNING',
|
||||
|
||||
WARNING_OUTLINE = 'WARNING_OUTLINE',
|
||||
}
|
||||
|
||||
interface BadgeProps {
|
||||
variant?: BadgeVariant
|
||||
}
|
||||
|
||||
function pickBackgroundColor(variant: BadgeVariant | undefined, theme: DefaultTheme): Color {
|
||||
switch (variant) {
|
||||
case BadgeVariant.NEGATIVE:
|
||||
return theme.error
|
||||
case BadgeVariant.POSITIVE:
|
||||
return theme.success
|
||||
case BadgeVariant.PRIMARY:
|
||||
return theme.primary1
|
||||
case BadgeVariant.WARNING:
|
||||
return theme.warning
|
||||
case BadgeVariant.WARNING_OUTLINE:
|
||||
return 'transparent'
|
||||
default:
|
||||
return theme.bg2
|
||||
}
|
||||
}
|
||||
|
||||
function pickBorder(variant: BadgeVariant | undefined, theme: DefaultTheme): string {
|
||||
switch (variant) {
|
||||
case BadgeVariant.WARNING_OUTLINE:
|
||||
return `1px solid ${theme.warning}`
|
||||
default:
|
||||
return 'unset'
|
||||
}
|
||||
}
|
||||
|
||||
function pickFontColor(variant: BadgeVariant | undefined, theme: DefaultTheme): string {
|
||||
switch (variant) {
|
||||
case BadgeVariant.NEGATIVE:
|
||||
return readableColor(theme.error)
|
||||
case BadgeVariant.POSITIVE:
|
||||
return readableColor(theme.success)
|
||||
case BadgeVariant.WARNING:
|
||||
return readableColor(theme.warning)
|
||||
case BadgeVariant.WARNING_OUTLINE:
|
||||
return theme.warning
|
||||
default:
|
||||
return readableColor(theme.bg2)
|
||||
}
|
||||
}
|
||||
|
||||
const Badge = styled.div<PropsWithChildren<BadgeProps>>`
|
||||
align-items: center;
|
||||
background-color: ${({ theme, variant }) => pickBackgroundColor(variant, theme)};
|
||||
border: ${({ theme, variant }) => pickBorder(variant, theme)};
|
||||
border-radius: 0.5rem;
|
||||
color: ${({ theme, variant }) => pickFontColor(variant, theme)};
|
||||
display: inline-flex;
|
||||
padding: 4px 6px;
|
||||
justify-content: center;
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
export default Badge
|
||||
@@ -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,23 +1,26 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { darken, lighten } from 'polished'
|
||||
import styled from 'styled-components/macro'
|
||||
import { darken } from 'polished'
|
||||
|
||||
import { RowBetween } from '../Row'
|
||||
import { ChevronDown } from 'react-feather'
|
||||
import { Button as RebassButton, ButtonProps } from 'rebass/styled-components'
|
||||
import { ChevronDown, Check } from 'react-feather'
|
||||
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 : '18px')};
|
||||
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;
|
||||
@@ -31,11 +34,20 @@ const Base = styled(RebassButton)<{
|
||||
z-index: 1;
|
||||
&:disabled {
|
||||
cursor: auto;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
will-change: transform;
|
||||
transition: transform 450ms ease;
|
||||
transform: perspective(1px) translateZ(0);
|
||||
|
||||
> * {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
> a {
|
||||
text-decoration: none;
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonPrimary = styled(Base)`
|
||||
@@ -54,14 +66,12 @@ export const ButtonPrimary = styled(Base)`
|
||||
}
|
||||
&:disabled {
|
||||
background-color: ${({ theme, altDisabledStyle, disabled }) =>
|
||||
altDisabledStyle ? (disabled ? theme.bg3 : theme.primary1) : theme.bg3};
|
||||
color: ${({ theme, altDisabledStyle, disabled }) =>
|
||||
altDisabledStyle ? (disabled ? theme.text3 : 'white') : theme.text3};
|
||||
altDisabledStyle ? (disabled ? theme.primary1 : theme.bg2) : theme.bg2};
|
||||
color: ${({ theme }) => theme.text2};
|
||||
cursor: auto;
|
||||
box-shadow: none;
|
||||
border: 1px solid transparent;
|
||||
outline: none;
|
||||
opacity: ${({ altDisabledStyle }) => (altDisabledStyle ? '0.5' : '1')};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -94,18 +104,16 @@ export const ButtonLight = styled(Base)`
|
||||
`
|
||||
|
||||
export const ButtonGray = styled(Base)`
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
color: ${({ theme }) => theme.text2};
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
&:focus {
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg4)};
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg4)};
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
|
||||
}
|
||||
&:active {
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.1, theme.bg4)};
|
||||
background-color: ${({ theme, disabled }) => !disabled && darken(0.1, theme.bg2)};
|
||||
}
|
||||
`
|
||||
|
||||
@@ -137,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;
|
||||
}
|
||||
@@ -221,20 +207,21 @@ export const ButtonEmpty = styled(Base)`
|
||||
}
|
||||
`
|
||||
|
||||
export const ButtonWhite = styled(Base)`
|
||||
border: 1px solid #edeef2;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
color: black;
|
||||
|
||||
export const ButtonText = styled(Base)`
|
||||
padding: 0;
|
||||
width: fit-content;
|
||||
background: none;
|
||||
text-decoration: none;
|
||||
&:focus {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
box-shadow: 0 0 0 1pt ${darken(0.05, '#edeef2')};
|
||||
text-decoration: underline;
|
||||
}
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 1pt ${darken(0.1, '#edeef2')};
|
||||
// text-decoration: underline;
|
||||
opacity: 0.9;
|
||||
}
|
||||
&:active {
|
||||
box-shadow: 0 0 0 1pt ${darken(0.1, '#edeef2')};
|
||||
text-decoration: underline;
|
||||
}
|
||||
&:disabled {
|
||||
opacity: 50%;
|
||||
@@ -243,12 +230,14 @@ export const ButtonWhite = styled(Base)`
|
||||
`
|
||||
|
||||
const ButtonConfirmedStyle = styled(Base)`
|
||||
background-color: ${({ theme }) => lighten(0.5, theme.green1)};
|
||||
color: ${({ theme }) => theme.green1};
|
||||
border: 1px solid ${({ theme }) => theme.green1};
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
color: ${({ theme }) => theme.text1};
|
||||
/* border: 1px solid ${({ theme }) => theme.green1}; */
|
||||
|
||||
&:disabled {
|
||||
opacity: 50%;
|
||||
/* opacity: 50%; */
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
color: ${({ theme }) => theme.text2};
|
||||
cursor: auto;
|
||||
}
|
||||
`
|
||||
@@ -308,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}>
|
||||
@@ -330,10 +308,56 @@ export function ButtonDropdownLight({ disabled = false, children, ...rest }: { d
|
||||
)
|
||||
}
|
||||
|
||||
export function ButtonRadio({ active, ...rest }: { active?: boolean } & ButtonProps) {
|
||||
const ActiveOutlined = styled(ButtonOutlined)`
|
||||
border: 1px solid;
|
||||
border-color: ${({ theme }) => theme.primary1};
|
||||
`
|
||||
|
||||
const Circle = styled.div`
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: ${({ theme }) => theme.primary1};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`
|
||||
|
||||
const CheckboxWrapper = styled.div`
|
||||
width: 30px;
|
||||
padding: 0 10px;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
`
|
||||
|
||||
const ResponsiveCheck = styled(Check)`
|
||||
size: 13px;
|
||||
`
|
||||
|
||||
export function ButtonRadioChecked({ active = false, children, ...rest }: { active?: boolean } & ButtonProps) {
|
||||
const theme = useTheme()
|
||||
|
||||
if (!active) {
|
||||
return <ButtonWhite {...rest} />
|
||||
return (
|
||||
<ButtonOutlined $borderRadius="12px" padding="12px 8px" {...rest}>
|
||||
{<RowBetween>{children}</RowBetween>}
|
||||
</ButtonOutlined>
|
||||
)
|
||||
} else {
|
||||
return <ButtonPrimary {...rest} />
|
||||
return (
|
||||
<ActiveOutlined {...rest} padding="12px 8px" $borderRadius="12px">
|
||||
{
|
||||
<RowBetween>
|
||||
{children}
|
||||
<CheckboxWrapper>
|
||||
<Circle>
|
||||
<ResponsiveCheck size={13} stroke={theme.white} />
|
||||
</Circle>
|
||||
</CheckboxWrapper>
|
||||
</RowBetween>
|
||||
}
|
||||
</ActiveOutlined>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { CardProps, Text } from 'rebass'
|
||||
import styled from 'styled-components/macro'
|
||||
import { Box } from 'rebass/styled-components'
|
||||
|
||||
const Card = styled(Box)<{ 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: 1.25rem;
|
||||
padding: ${({ padding }) => padding};
|
||||
padding: ${({ padding }) => padding ?? '1rem'};
|
||||
border-radius: ${({ $borderRadius }) => $borderRadius ?? '16px'};
|
||||
border: ${({ border }) => border};
|
||||
border-radius: ${({ borderRadius }) => borderRadius};
|
||||
`
|
||||
export default Card
|
||||
|
||||
@@ -26,35 +22,26 @@ export const GreyCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
`
|
||||
|
||||
export const DarkGreyCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
`
|
||||
|
||||
export const DarkCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
`
|
||||
|
||||
export const OutlineCard = styled(Card)`
|
||||
border: 1px solid ${({ theme }) => theme.bg3};
|
||||
`
|
||||
|
||||
export const YellowCard = styled(Card)`
|
||||
background-color: rgba(243, 132, 30, 0.05);
|
||||
color: ${({ theme }) => theme.yellow2};
|
||||
color: ${({ theme }) => theme.yellow3};
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
export const PinkCard = styled(Card)`
|
||||
background-color: rgba(255, 0, 122, 0.03);
|
||||
color: ${({ theme }) => theme.primary1};
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
const BlueCardStyled = styled(Card)`
|
||||
export const BlueCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.primary5};
|
||||
color: ${({ theme }) => theme.primary1};
|
||||
color: ${({ theme }) => theme.blue2};
|
||||
border-radius: 12px;
|
||||
width: fit-content;
|
||||
`
|
||||
|
||||
export const BlueCard = ({ children, ...rest }: CardProps) => {
|
||||
return (
|
||||
<BlueCardStyled {...rest}>
|
||||
<Text fontWeight={500} color="#2172E5">
|
||||
{children}
|
||||
</Text>
|
||||
</BlueCardStyled>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import styled from 'styled-components'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
const Column = styled.div`
|
||||
display: flex;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import React from 'react'
|
||||
import ReactConfetti from 'react-confetti'
|
||||
import { useWindowSize } from '../../hooks/useWindowSize'
|
||||
|
||||
@@ -20,7 +19,7 @@ export default function Confetti({ start, variant }: { start: boolean; variant?:
|
||||
h: height,
|
||||
w: width,
|
||||
x: 0,
|
||||
y: _variant === 'top' ? height * 0.25 : _variant === 'bottom' ? height * 0.75 : height * 0.5
|
||||
y: _variant === 'top' ? height * 0.25 : _variant === 'bottom' ? height * 0.75 : height * 0.5,
|
||||
}}
|
||||
initialVelocityX={15}
|
||||
initialVelocityY={30}
|
||||
|
||||
43
src/components/CurrencyInputPanel/FiatValue.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
|
||||
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,
|
||||
priceImpact,
|
||||
}: {
|
||||
fiatValue: CurrencyAmount<Currency> | null | undefined
|
||||
priceImpact?: Percent
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
const priceImpactColor = useMemo(() => {
|
||||
if (!priceImpact) return undefined
|
||||
if (priceImpact.lessThan('0')) return theme.green1
|
||||
const severity = warningSeverity(priceImpact)
|
||||
if (severity < 1) return theme.text3
|
||||
if (severity < 3) return theme.yellow1
|
||||
return theme.red1
|
||||
}, [priceImpact, theme.green1, theme.red1, theme.text3, theme.yellow1])
|
||||
|
||||
return (
|
||||
<TYPE.body fontSize={14} color={fiatValue ? theme.text2 : theme.text4}>
|
||||
{fiatValue ? (
|
||||
<Trans>
|
||||
~$ <HoverInlineText text={fiatValue?.toSignificant(6, { groupSeparator: ',' })} />
|
||||
</Trans>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{priceImpact ? (
|
||||
<span style={{ color: priceImpactColor }}>
|
||||
{' '}
|
||||
(<Trans>{priceImpact.multiply(-1).toSignificant(3)}%</Trans>)
|
||||
</span>
|
||||
) : null}
|
||||
</TYPE.body>
|
||||
)
|
||||
}
|
||||
@@ -1,68 +1,114 @@
|
||||
import { Currency, Pair } from '@uniswap/sdk'
|
||||
import React, { useState, useCallback } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { Pair } from '@uniswap/v2-sdk'
|
||||
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
|
||||
import { useState, useCallback, ReactNode } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import { darken } from 'polished'
|
||||
import { useCurrencyBalance } from '../../state/wallet/hooks'
|
||||
import CurrencySearchModal from '../SearchModal/CurrencySearchModal'
|
||||
import CurrencyLogo from '../CurrencyLogo'
|
||||
import DoubleCurrencyLogo from '../DoubleLogo'
|
||||
import { RowBetween } from '../Row'
|
||||
import { ButtonGray } from '../Button'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import { TYPE } from '../../theme'
|
||||
import { Input as NumericalInput } from '../NumericalInput'
|
||||
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { 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 { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
|
||||
|
||||
const InputRow = styled.div<{ selected: boolean }>`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
padding: ${({ selected }) => (selected ? '0.75rem 0.5rem 0.75rem 1rem' : '0.75rem 0.75rem 0.75rem 1rem')};
|
||||
const InputPanel = styled.div<{ hideInput?: boolean }>`
|
||||
${({ theme }) => theme.flexColumnNoWrap}
|
||||
position: relative;
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
|
||||
background-color: ${({ theme, hideInput }) => (hideInput ? 'transparent' : theme.bg2)};
|
||||
z-index: 1;
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
`
|
||||
|
||||
const CurrencySelect = styled.button<{ selected: boolean }>`
|
||||
const FixedContainer = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
border-radius: 20px;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
opacity: 0.95;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 2.2rem;
|
||||
font-size: 20px;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
`
|
||||
|
||||
const Container = styled.div<{ hideInput: boolean }>`
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
|
||||
border: 1px solid ${({ theme, hideInput }) => (hideInput ? ' transparent' : theme.bg2)};
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
:focus,
|
||||
:hover {
|
||||
border: 1px solid ${({ theme, hideInput }) => (hideInput ? ' transparent' : theme.bg3)};
|
||||
}
|
||||
`
|
||||
|
||||
const CurrencySelect = styled(ButtonGray)<{ selected: boolean; hideInput?: boolean }>`
|
||||
align-items: center;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.bg1 : theme.primary1)};
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.bg0 : theme.primary1)};
|
||||
color: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)};
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
box-shadow: ${({ selected }) => (selected ? 'none' : '0px 6px 10px rgba(0, 0, 0, 0.075)')};
|
||||
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
border: none;
|
||||
padding: 0 0.5rem;
|
||||
|
||||
height: ${({ hideInput }) => (hideInput ? '2.8rem' : '2.4rem')};
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
padding: 0 8px;
|
||||
justify-content: space-between;
|
||||
margin-right: ${({ hideInput }) => (hideInput ? '0' : '12px')};
|
||||
:focus,
|
||||
:hover {
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.bg2 : darken(0.05, theme.primary1))};
|
||||
}
|
||||
`
|
||||
|
||||
const InputRow = styled.div<{ selected: boolean }>`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
padding: ${({ selected }) => (selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 0.75rem 1rem')};
|
||||
`
|
||||
|
||||
const LabelRow = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
padding: 0.75rem 1rem 0 1rem;
|
||||
padding: 0 1rem 1rem;
|
||||
span:hover {
|
||||
cursor: pointer;
|
||||
color: ${({ theme }) => darken(0.2, theme.text2)};
|
||||
}
|
||||
`
|
||||
|
||||
const FiatRow = styled(LabelRow)`
|
||||
justify-content: flex-end;
|
||||
`
|
||||
|
||||
const Aligner = styled.span`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const StyledDropDown = styled(DropDown)<{ selected: boolean }>`
|
||||
margin: 0 0.25rem 0 0.5rem;
|
||||
margin: 0 0.25rem 0 0.35rem;
|
||||
height: 35%;
|
||||
|
||||
path {
|
||||
@@ -71,42 +117,25 @@ const StyledDropDown = styled(DropDown)<{ selected: boolean }>`
|
||||
}
|
||||
`
|
||||
|
||||
const InputPanel = styled.div<{ hideInput?: boolean }>`
|
||||
${({ theme }) => theme.flexColumnNoWrap}
|
||||
position: relative;
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')};
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
z-index: 1;
|
||||
`
|
||||
|
||||
const Container = styled.div<{ hideInput: boolean }>`
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')};
|
||||
border: 1px solid ${({ theme }) => theme.bg2};
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
`
|
||||
|
||||
const StyledTokenName = styled.span<{ active?: boolean }>`
|
||||
${({ active }) => (active ? ' margin: 0 0.25rem 0 0.75rem;' : ' margin: 0 0.25rem 0 0.25rem;')}
|
||||
font-size: ${({ active }) => (active ? '20px' : '16px')};
|
||||
|
||||
${({ active }) => (active ? ' margin: 0 0.25rem 0 0.25rem;' : ' margin: 0 0.25rem 0 0.25rem;')}
|
||||
font-size: ${({ active }) => (active ? '18px' : '18px')};
|
||||
`
|
||||
|
||||
const StyledBalanceMax = styled.button`
|
||||
height: 28px;
|
||||
background-color: ${({ theme }) => theme.primary5};
|
||||
border: 1px solid ${({ theme }) => theme.primary5};
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
|
||||
const StyledBalanceMax = styled.button<{ disabled?: boolean }>`
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
margin-right: 0.5rem;
|
||||
padding: 0;
|
||||
color: ${({ theme }) => theme.primaryText1};
|
||||
:hover {
|
||||
border: 1px solid ${({ theme }) => theme.primary1};
|
||||
}
|
||||
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
|
||||
pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')};
|
||||
margin-left: 0.25rem;
|
||||
|
||||
:focus {
|
||||
border: 1px solid ${({ theme }) => theme.primary1};
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@@ -120,17 +149,21 @@ interface CurrencyInputPanelProps {
|
||||
onUserInput: (value: string) => void
|
||||
onMax?: () => void
|
||||
showMaxButton: boolean
|
||||
label?: string
|
||||
label?: ReactNode
|
||||
onCurrencySelect?: (currency: Currency) => void
|
||||
currency?: Currency | null
|
||||
disableCurrencySelect?: boolean
|
||||
hideBalance?: boolean
|
||||
pair?: Pair | null
|
||||
hideInput?: boolean
|
||||
otherCurrency?: Currency | null
|
||||
fiatValue?: CurrencyAmount<Token> | null
|
||||
priceImpact?: Percent
|
||||
id: string
|
||||
showCommonBases?: boolean
|
||||
customBalanceText?: string
|
||||
showCurrencyAmount?: boolean
|
||||
disableNonToken?: boolean
|
||||
renderBalance?: (amount: CurrencyAmount<Currency>) => ReactNode
|
||||
locked?: boolean
|
||||
}
|
||||
|
||||
export default function CurrencyInputPanel({
|
||||
@@ -138,20 +171,22 @@ export default function CurrencyInputPanel({
|
||||
onUserInput,
|
||||
onMax,
|
||||
showMaxButton,
|
||||
label = 'Input',
|
||||
onCurrencySelect,
|
||||
currency,
|
||||
disableCurrencySelect = false,
|
||||
hideBalance = false,
|
||||
pair = null, // used for double token logo
|
||||
hideInput = false,
|
||||
otherCurrency,
|
||||
id,
|
||||
showCommonBases,
|
||||
customBalanceText
|
||||
showCurrencyAmount,
|
||||
disableNonToken,
|
||||
renderBalance,
|
||||
fiatValue,
|
||||
priceImpact,
|
||||
hideBalance = false,
|
||||
pair = null, // used for double token logo
|
||||
hideInput = false,
|
||||
locked = false,
|
||||
...rest
|
||||
}: CurrencyInputPanelProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [modalOpen, setModalOpen] = useState(false)
|
||||
const { account } = useActiveWeb3React()
|
||||
const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined)
|
||||
@@ -162,79 +197,104 @@ export default function CurrencyInputPanel({
|
||||
}, [setModalOpen])
|
||||
|
||||
return (
|
||||
<InputPanel id={id}>
|
||||
<InputPanel id={id} hideInput={hideInput} {...rest}>
|
||||
{locked && (
|
||||
<FixedContainer>
|
||||
<AutoColumn gap="sm" justify="center">
|
||||
<Lock />
|
||||
<TYPE.label fontSize="12px" textAlign="center" padding="0 12px">
|
||||
<Trans>The market price is outside your specified price range. Single-asset deposit only.</Trans>
|
||||
</TYPE.label>
|
||||
</AutoColumn>
|
||||
</FixedContainer>
|
||||
)}
|
||||
<Container hideInput={hideInput}>
|
||||
{!hideInput && (
|
||||
<LabelRow>
|
||||
<RowBetween>
|
||||
<TYPE.body color={theme.text2} fontWeight={500} fontSize={14}>
|
||||
{label}
|
||||
</TYPE.body>
|
||||
{account && (
|
||||
<TYPE.body
|
||||
onClick={onMax}
|
||||
color={theme.text2}
|
||||
fontWeight={500}
|
||||
fontSize={14}
|
||||
style={{ display: 'inline', cursor: 'pointer' }}
|
||||
>
|
||||
{!hideBalance && !!currency && selectedCurrencyBalance
|
||||
? (customBalanceText ?? 'Balance: ') + selectedCurrencyBalance?.toSignificant(6)
|
||||
: ' -'}
|
||||
</TYPE.body>
|
||||
)}
|
||||
</RowBetween>
|
||||
</LabelRow>
|
||||
)}
|
||||
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={disableCurrencySelect}>
|
||||
{!hideInput && (
|
||||
<>
|
||||
<NumericalInput
|
||||
className="token-amount-input"
|
||||
value={value}
|
||||
onUserInput={val => {
|
||||
onUserInput(val)
|
||||
}}
|
||||
/>
|
||||
{account && currency && showMaxButton && label !== 'To' && (
|
||||
<StyledBalanceMax onClick={onMax}>MAX</StyledBalanceMax>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={!onCurrencySelect}>
|
||||
<CurrencySelect
|
||||
selected={!!currency}
|
||||
hideInput={hideInput}
|
||||
className="open-currency-select-button"
|
||||
onClick={() => {
|
||||
if (!disableCurrencySelect) {
|
||||
if (onCurrencySelect) {
|
||||
setModalOpen(true)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Aligner>
|
||||
{pair ? (
|
||||
<DoubleCurrencyLogo currency0={pair.token0} currency1={pair.token1} size={24} margin={true} />
|
||||
) : currency ? (
|
||||
<CurrencyLogo currency={currency} size={'24px'} />
|
||||
) : null}
|
||||
{pair ? (
|
||||
<StyledTokenName className="pair-name-container">
|
||||
{pair?.token0.symbol}:{pair?.token1.symbol}
|
||||
</StyledTokenName>
|
||||
) : (
|
||||
<StyledTokenName className="token-symbol-container" active={Boolean(currency && currency.symbol)}>
|
||||
{(currency && currency.symbol && currency.symbol.length > 20
|
||||
? currency.symbol.slice(0, 4) +
|
||||
'...' +
|
||||
currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length)
|
||||
: currency?.symbol) || t('selectToken')}
|
||||
</StyledTokenName>
|
||||
)}
|
||||
{!disableCurrencySelect && <StyledDropDown selected={!!currency} />}
|
||||
<RowFixed>
|
||||
{pair ? (
|
||||
<span style={{ marginRight: '0.5rem' }}>
|
||||
<DoubleCurrencyLogo currency0={pair.token0} currency1={pair.token1} size={24} margin={true} />
|
||||
</span>
|
||||
) : currency ? (
|
||||
<CurrencyLogo style={{ marginRight: '0.5rem' }} currency={currency} size={'24px'} />
|
||||
) : null}
|
||||
{pair ? (
|
||||
<StyledTokenName className="pair-name-container">
|
||||
{pair?.token0.symbol}:{pair?.token1.symbol}
|
||||
</StyledTokenName>
|
||||
) : (
|
||||
<StyledTokenName className="token-symbol-container" active={Boolean(currency && currency.symbol)}>
|
||||
{(currency && currency.symbol && currency.symbol.length > 20
|
||||
? currency.symbol.slice(0, 4) +
|
||||
'...' +
|
||||
currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length)
|
||||
: currency?.symbol) || <Trans>Select a token</Trans>}
|
||||
</StyledTokenName>
|
||||
)}
|
||||
</RowFixed>
|
||||
{onCurrencySelect && <StyledDropDown selected={!!currency} />}
|
||||
</Aligner>
|
||||
</CurrencySelect>
|
||||
{!hideInput && (
|
||||
<>
|
||||
<NumericalInput
|
||||
className="token-amount-input"
|
||||
value={value}
|
||||
onUserInput={(val) => {
|
||||
onUserInput(val)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</InputRow>
|
||||
{!hideInput && !hideBalance && (
|
||||
<FiatRow>
|
||||
<RowBetween>
|
||||
{account ? (
|
||||
<RowFixed style={{ height: '17px' }}>
|
||||
<TYPE.body
|
||||
onClick={onMax}
|
||||
color={theme.text2}
|
||||
fontWeight={400}
|
||||
fontSize={14}
|
||||
style={{ display: 'inline', cursor: 'pointer' }}
|
||||
>
|
||||
{!hideBalance && currency && selectedCurrencyBalance ? (
|
||||
renderBalance ? (
|
||||
renderBalance(selectedCurrencyBalance)
|
||||
) : (
|
||||
<Trans>
|
||||
Balance: {formatCurrencyAmount(selectedCurrencyBalance, 4)} {currency.symbol}
|
||||
</Trans>
|
||||
)
|
||||
) : null}
|
||||
</TYPE.body>
|
||||
{showMaxButton && selectedCurrencyBalance ? (
|
||||
<StyledBalanceMax onClick={onMax}>
|
||||
<Trans>(Max)</Trans>
|
||||
</StyledBalanceMax>
|
||||
) : null}
|
||||
</RowFixed>
|
||||
) : (
|
||||
<span />
|
||||
)}
|
||||
<FiatValue fiatValue={fiatValue} priceImpact={priceImpact} />
|
||||
</RowBetween>
|
||||
</FiatRow>
|
||||
)}
|
||||
</Container>
|
||||
{!disableCurrencySelect && onCurrencySelect && (
|
||||
{onCurrencySelect && (
|
||||
<CurrencySearchModal
|
||||
isOpen={modalOpen}
|
||||
onDismiss={handleDismissSearch}
|
||||
@@ -242,6 +302,8 @@ export default function CurrencyInputPanel({
|
||||
selectedCurrency={currency}
|
||||
otherSelectedCurrency={otherCurrency}
|
||||
showCommonBases={showCommonBases}
|
||||
showCurrencyAmount={showCurrencyAmount}
|
||||
disableNonToken={disableNonToken}
|
||||
/>
|
||||
)}
|
||||
</InputPanel>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { Currency, ETHER, Token } from '@uniswap/sdk'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import React, { useMemo } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import styled from 'styled-components/macro'
|
||||
import EthereumLogo from '../../assets/images/ethereum-logo.png'
|
||||
import useHttpLocations from '../../hooks/useHttpLocations'
|
||||
import { WrappedTokenInfo } from '../../state/lists/hooks'
|
||||
import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
|
||||
import Logo from '../Logo'
|
||||
|
||||
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};
|
||||
@@ -28,7 +27,8 @@ const StyledLogo = styled(Logo)<{ size: string }>`
|
||||
export default function CurrencyLogo({
|
||||
currency,
|
||||
size = '24px',
|
||||
style
|
||||
style,
|
||||
...rest
|
||||
}: {
|
||||
currency?: Currency
|
||||
size?: string
|
||||
@@ -37,20 +37,21 @@ export default function CurrencyLogo({
|
||||
const uriLocations = useHttpLocations(currency instanceof WrappedTokenInfo ? currency.logoURI : undefined)
|
||||
|
||||
const srcs: string[] = useMemo(() => {
|
||||
if (currency === ETHER) return []
|
||||
if (!currency || currency.isNative) return []
|
||||
|
||||
if (currency instanceof Token) {
|
||||
if (currency.isToken) {
|
||||
const defaultUrls = currency.chainId === 1 ? [getTokenLogoURL(currency.address)] : []
|
||||
if (currency instanceof WrappedTokenInfo) {
|
||||
return [...uriLocations, getTokenLogoURL(currency.address)]
|
||||
return [...uriLocations, ...defaultUrls]
|
||||
}
|
||||
return [getTokenLogoURL(currency.address)]
|
||||
return defaultUrls
|
||||
}
|
||||
return []
|
||||
}, [currency, uriLocations])
|
||||
|
||||
if (currency === ETHER) {
|
||||
return <StyledEthereumLogo src={EthereumLogo} size={size} style={style} />
|
||||
if (currency?.isNative) {
|
||||
return <StyledEthereumLogo src={EthereumLogo} size={size} style={style} {...rest} />
|
||||
}
|
||||
|
||||
return <StyledLogo size={size} srcs={srcs} alt={`${currency?.symbol ?? 'token'} logo`} style={style} />
|
||||
return <StyledLogo size={size} srcs={srcs} alt={`${currency?.symbol ?? 'token'} logo`} style={style} {...rest} />
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { Currency } from '@uniswap/sdk'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import styled from 'styled-components/macro'
|
||||
import CurrencyLogo from '../CurrencyLogo'
|
||||
|
||||
const Wrapper = styled.div<{ margin: boolean; sizeraw: number }>`
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-right: ${({ sizeraw, margin }) => margin && (sizeraw / 3 + 8).toString() + 'px'};
|
||||
margin-left: ${({ sizeraw, margin }) => margin && (sizeraw / 3 + 8).toString() + 'px'};
|
||||
`
|
||||
|
||||
interface DoubleCurrencyLogoProps {
|
||||
@@ -29,7 +28,7 @@ export default function DoubleCurrencyLogo({
|
||||
currency0,
|
||||
currency1,
|
||||
size = 16,
|
||||
margin = false
|
||||
margin = false,
|
||||
}: DoubleCurrencyLogoProps) {
|
||||
return (
|
||||
<Wrapper sizeraw={size} margin={margin}>
|
||||
|
||||
182
src/components/ErrorBoundary/index.tsx
Normal file
@@ -0,0 +1,182 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import React, { ErrorInfo } from 'react'
|
||||
import store, { AppState } from '../../state'
|
||||
import { ExternalLink, TYPE } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
import styled from 'styled-components/macro'
|
||||
import ReactGA from 'react-ga'
|
||||
import { getUserAgent } from '../../utils/getUserAgent'
|
||||
import { AutoRow } from '../Row'
|
||||
|
||||
const FallbackWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
z-index: 1;
|
||||
`
|
||||
|
||||
const BodyWrapper = styled.div<{ margin?: string }>`
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
white-space: ;
|
||||
`
|
||||
|
||||
const CodeBlockWrapper = styled.div`
|
||||
background: ${({ theme }) => theme.bg0};
|
||||
overflow: auto;
|
||||
white-space: pre;
|
||||
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
|
||||
0px 24px 32px rgba(0, 0, 0, 0.01);
|
||||
border-radius: 24px;
|
||||
padding: 18px 24px;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
`
|
||||
|
||||
const LinkWrapper = styled.div`
|
||||
color: ${({ theme }) => theme.blue1};
|
||||
padding: 6px 24px;
|
||||
`
|
||||
|
||||
const SomethingWentWrongWrapper = styled.div`
|
||||
padding: 6px 24px;
|
||||
`
|
||||
|
||||
type ErrorBoundaryState = {
|
||||
error: Error | null
|
||||
}
|
||||
|
||||
export default class ErrorBoundary extends React.Component<unknown, ErrorBoundaryState> {
|
||||
constructor(props: unknown) {
|
||||
super(props)
|
||||
this.state = { error: null }
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
||||
return { error }
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
||||
ReactGA.exception({
|
||||
...error,
|
||||
...errorInfo,
|
||||
fatal: true,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { error } = this.state
|
||||
if (error !== null) {
|
||||
const encodedBody = encodeURIComponent(issueBody(error))
|
||||
return (
|
||||
<FallbackWrapper>
|
||||
<BodyWrapper>
|
||||
<AutoColumn gap={'md'}>
|
||||
<SomethingWentWrongWrapper>
|
||||
<TYPE.label fontSize={24} fontWeight={600}>
|
||||
<Trans>Something went wrong</Trans>
|
||||
</TYPE.label>
|
||||
</SomethingWentWrongWrapper>
|
||||
<CodeBlockWrapper>
|
||||
<code>
|
||||
<TYPE.main fontSize={10}>{error.stack}</TYPE.main>
|
||||
</code>
|
||||
</CodeBlockWrapper>
|
||||
<AutoRow>
|
||||
<LinkWrapper>
|
||||
<ExternalLink
|
||||
id="create-github-issue-link"
|
||||
href={`https://github.com/Uniswap/uniswap-interface/issues/new?assignees=&labels=bug&body=${encodedBody}&title=${encodeURIComponent(
|
||||
`Crash report: \`${error.name}${error.message && `: ${error.message}`}\``
|
||||
)}`}
|
||||
target="_blank"
|
||||
>
|
||||
<TYPE.link fontSize={16}>
|
||||
<Trans>Create an issue on GitHub</Trans>
|
||||
<span>↗</span>
|
||||
</TYPE.link>
|
||||
</ExternalLink>
|
||||
</LinkWrapper>
|
||||
<LinkWrapper>
|
||||
<ExternalLink id="get-support-on-discord" href="https://discord.gg/FCfyBSbCU5" target="_blank">
|
||||
<TYPE.link fontSize={16}>
|
||||
<Trans>Get support on Discord</Trans>
|
||||
<span>↗</span>
|
||||
</TYPE.link>
|
||||
</ExternalLink>
|
||||
</LinkWrapper>
|
||||
</AutoRow>
|
||||
</AutoColumn>
|
||||
</BodyWrapper>
|
||||
</FallbackWrapper>
|
||||
)
|
||||
}
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
function getRelevantState(): null | keyof AppState {
|
||||
const path = window.location.hash
|
||||
if (!path.startsWith('#/')) {
|
||||
return null
|
||||
}
|
||||
const pieces = path.substring(2).split(/[\/\\?]/)
|
||||
switch (pieces[0]) {
|
||||
case 'swap':
|
||||
return 'swap'
|
||||
case 'add':
|
||||
if (pieces[1] === 'v2') return 'mint'
|
||||
else return 'mintV3'
|
||||
case 'remove':
|
||||
if (pieces[1] === 'v2') return 'burn'
|
||||
else return 'burnV3'
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function issueBody(error: Error): string {
|
||||
const relevantState = getRelevantState()
|
||||
const deviceData = getUserAgent()
|
||||
return `## URL
|
||||
|
||||
${window.location.href}
|
||||
|
||||
${
|
||||
relevantState
|
||||
? `## \`${relevantState}\` state
|
||||
|
||||
\`\`\`json
|
||||
${JSON.stringify(store.getState()[relevantState], null, 2)}
|
||||
\`\`\`
|
||||
`
|
||||
: ''
|
||||
}
|
||||
${
|
||||
error.name &&
|
||||
`## Error
|
||||
|
||||
\`\`\`
|
||||
${error.name}${error.message && `: ${error.message}`}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
${
|
||||
error.stack &&
|
||||
`## Stacktrace
|
||||
|
||||
\`\`\`
|
||||
${error.stack}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
${
|
||||
deviceData &&
|
||||
`## Device data
|
||||
|
||||
\`\`\`json
|
||||
${JSON.stringify(deviceData, null, 2)}
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
`
|
||||
}
|
||||
229
src/components/FeeSelector/index.tsx
Normal file
@@ -0,0 +1,229 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
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 { 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 { 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}>
|
||||
<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>
|
||||
|
||||
<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,13 +1,13 @@
|
||||
import React from 'react'
|
||||
import { CurrencyAmount, Fraction, JSBI } from '@uniswap/sdk'
|
||||
import JSBI from 'jsbi'
|
||||
import { Currency, CurrencyAmount, Fraction } from '@uniswap/sdk-core'
|
||||
|
||||
const CURRENCY_AMOUNT_MIN = new Fraction(JSBI.BigInt(1), JSBI.BigInt(1000000))
|
||||
|
||||
export default function FormattedCurrencyAmount({
|
||||
currencyAmount,
|
||||
significantDigits = 4
|
||||
significantDigits = 4,
|
||||
}: {
|
||||
currencyAmount: CurrencyAmount
|
||||
currencyAmount: CurrencyAmount<Currency>
|
||||
significantDigits?: number
|
||||
}) {
|
||||
return (
|
||||
|
||||
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,35 +1,37 @@
|
||||
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;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 1rem;
|
||||
color: white;
|
||||
transition: opacity 0.25s ease;
|
||||
color: ${({ theme }) => theme.green1};
|
||||
:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
display: none;
|
||||
`}
|
||||
`
|
||||
const StyledPollingNumber = styled(TYPE.small)<{ breathe: boolean; hovering: boolean }>`
|
||||
transition: opacity 0.25s ease;
|
||||
opacity: ${({ breathe, hovering }) => (hovering ? 0.7 : breathe ? 1 : 0.5)};
|
||||
:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
`
|
||||
const StyledPollingDot = styled.div`
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
min-height: 8px;
|
||||
min-width: 8px;
|
||||
margin-left: 0.5rem;
|
||||
margin-top: 3px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
background-color: ${({ theme }) => theme.green1};
|
||||
@@ -67,16 +69,21 @@ export default function Polling() {
|
||||
|
||||
const blockNumber = useBlockNumber()
|
||||
|
||||
const [isMounted, setIsMounted] = useState(true)
|
||||
const [isMounting, setIsMounting] = useState(false)
|
||||
const [isHover, setIsHover] = useState(false)
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
const timer1 = setTimeout(() => setIsMounted(true), 1000)
|
||||
if (!blockNumber) {
|
||||
return
|
||||
}
|
||||
|
||||
setIsMounting(true)
|
||||
const mountingTimer = setTimeout(() => setIsMounting(false), 1000)
|
||||
|
||||
// this will clear Timeout when component unmount like in willComponentUnmount
|
||||
return () => {
|
||||
setIsMounted(false)
|
||||
clearTimeout(timer1)
|
||||
clearTimeout(mountingTimer)
|
||||
}
|
||||
},
|
||||
[blockNumber] //useEffect will run only one time
|
||||
@@ -84,10 +91,14 @@ export default function Polling() {
|
||||
)
|
||||
|
||||
return (
|
||||
<ExternalLink href={chainId && blockNumber ? getEtherscanLink(chainId, blockNumber.toString(), 'block') : ''}>
|
||||
<StyledPolling>
|
||||
<TYPE.small style={{ opacity: isMounted ? '0.2' : '0.6' }}>{blockNumber}</TYPE.small>
|
||||
<StyledPollingDot>{!isMounted && <Spinner />}</StyledPollingDot>
|
||||
<ExternalLink
|
||||
href={chainId && blockNumber ? getExplorerLink(chainId, blockNumber.toString(), ExplorerDataType.BLOCK) : ''}
|
||||
>
|
||||
<StyledPolling onMouseEnter={() => setIsHover(true)} onMouseLeave={() => setIsHover(false)}>
|
||||
<StyledPollingNumber breathe={isMounting} hovering={isHover}>
|
||||
{blockNumber}
|
||||
</StyledPollingNumber>
|
||||
<StyledPollingDot>{isMounting && <Spinner />}</StyledPollingDot>
|
||||
</StyledPolling>
|
||||
</ExternalLink>
|
||||
)
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
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, TokenAmount } from '@uniswap/sdk'
|
||||
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'
|
||||
import styled from 'styled-components/macro'
|
||||
import tokenLogo from '../../assets/images/token-logo.png'
|
||||
import { UNI } from '../../constants'
|
||||
import { useTotalSupply } from '../../data/TotalSupply'
|
||||
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 '../../utils/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%;
|
||||
@@ -45,21 +47,21 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
|
||||
const uni = chainId ? UNI[chainId] : undefined
|
||||
|
||||
const total = useAggregateUniBalance()
|
||||
const uniBalance: TokenAmount | undefined = useTokenBalance(account ?? undefined, uni)
|
||||
const uniToClaim: TokenAmount | undefined = useTotalUniEarned()
|
||||
const uniBalance: CurrencyAmount<Token> | undefined = useTokenBalance(account ?? undefined, uni)
|
||||
const uniToClaim: CurrencyAmount<Token> | undefined = useTotalUniEarned()
|
||||
|
||||
const totalSupply: TokenAmount | undefined = useTotalSupply(uni)
|
||||
const totalSupply: CurrencyAmount<Token> | undefined = useTotalSupply(uni)
|
||||
const uniPrice = useUSDCPrice(uni)
|
||||
const blockTimestamp = useCurrentBlockTimestamp()
|
||||
const unclaimedUni = useTokenBalance(useMerkleDistributorContract()?.address, uni)
|
||||
const circulation: TokenAmount | undefined = useMemo(
|
||||
const circulation: CurrencyAmount<Token> | undefined = useMemo(
|
||||
() =>
|
||||
blockTimestamp && uni && chainId === ChainId.MAINNET
|
||||
? computeUniCirculation(uni, blockTimestamp, unclaimedUni)
|
||||
: 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://uniswap.info/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,39 +1,33 @@
|
||||
import { ChainId, TokenAmount } from '@uniswap/sdk'
|
||||
import React, { useState } from 'react'
|
||||
import { Text } from 'rebass'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import useScrollPosition from '@react-hook/window-scroll'
|
||||
import { CHAIN_INFO, SupportedChainId } from 'constants/chains'
|
||||
import { darken } from 'polished'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import styled from 'styled-components'
|
||||
|
||||
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, useAggregateUniBalance } from '../../state/wallet/hooks'
|
||||
import { CardNoise } from '../earn/styled'
|
||||
import { CountUp } from 'use-count-up'
|
||||
import { TYPE, ExternalLink } from '../../theme'
|
||||
|
||||
import { YellowCard } from '../Card'
|
||||
import { Moon, Sun } from 'react-feather'
|
||||
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'
|
||||
import usePrevious from '../../hooks/usePrevious'
|
||||
|
||||
const HeaderFrame = styled.div`
|
||||
const HeaderFrame = styled.div<{ showBackground: boolean }>`
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 120px;
|
||||
grid-template-columns: 120px 1fr 120px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
@@ -41,19 +35,30 @@ const HeaderFrame = styled.div`
|
||||
width: 100%;
|
||||
top: 0;
|
||||
position: relative;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
padding: 1rem;
|
||||
z-index: 2;
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
grid-template-columns: 1fr;
|
||||
padding: 0 1rem;
|
||||
width: calc(100%);
|
||||
position: relative;
|
||||
z-index: 21;
|
||||
position: relative;
|
||||
/* Background slide effect on scroll. */
|
||||
background-image: ${({ theme }) => `linear-gradient(to bottom, transparent 50%, ${theme.bg0} 50% )}}`};
|
||||
background-position: ${({ showBackground }) => (showBackground ? '0 -100%' : '0 0')};
|
||||
background-size: 100% 200%;
|
||||
box-shadow: 0px 0px 0px 1px ${({ theme, showBackground }) => (showBackground ? theme.bg2 : 'transparent;')};
|
||||
transition: background-position 0.1s, box-shadow 0.1s;
|
||||
background-blend-mode: hard-light;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToLarge`
|
||||
grid-template-columns: 48px 1fr 1fr;
|
||||
`};
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToExtraSmall`
|
||||
padding: 0.5rem 1rem;
|
||||
`}
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
padding: 1rem;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
`};
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
padding: 1rem;
|
||||
grid-template-columns: 36px 1fr;
|
||||
`};
|
||||
`
|
||||
|
||||
const HeaderControls = styled.div`
|
||||
@@ -61,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`
|
||||
@@ -90,35 +78,47 @@ 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-content: center;
|
||||
justify-self: center;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
width: fit-content;
|
||||
padding: 4px;
|
||||
border-radius: 16px;
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-gap: 10px;
|
||||
overflow: auto;
|
||||
align-items: center;
|
||||
${({ theme }) => theme.mediaWidth.upToLarge`
|
||||
justify-self: start;
|
||||
`};
|
||||
${({ theme }) => theme.mediaWidth.upToMedium`
|
||||
padding: 1rem 0 1rem 1rem;
|
||||
justify-content: 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%);
|
||||
`};
|
||||
`
|
||||
|
||||
const AccountElement = styled.div<{ active: boolean }>`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg3)};
|
||||
background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg1)};
|
||||
border-radius: 12px;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
@@ -152,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;
|
||||
@@ -201,7 +182,7 @@ const UniIcon = styled.div`
|
||||
const activeClassName = 'ACTIVE'
|
||||
|
||||
const StyledNavLink = styled(NavLink).attrs({
|
||||
activeClassName
|
||||
activeClassName,
|
||||
})`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: left;
|
||||
@@ -212,13 +193,15 @@ const StyledNavLink = styled(NavLink).attrs({
|
||||
color: ${({ theme }) => theme.text2};
|
||||
font-size: 1rem;
|
||||
width: fit-content;
|
||||
margin: 0 12px;
|
||||
font-weight: 500;
|
||||
padding: 8px 12px;
|
||||
word-break: break-word;
|
||||
|
||||
&.${activeClassName} {
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
}
|
||||
|
||||
:hover,
|
||||
@@ -228,7 +211,7 @@ const StyledNavLink = styled(NavLink).attrs({
|
||||
`
|
||||
|
||||
const StyledExternalLink = styled(ExternalLink).attrs({
|
||||
activeClassName
|
||||
activeClassName,
|
||||
})<{ isActive?: boolean }>`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: left;
|
||||
@@ -251,56 +234,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.bg3};
|
||||
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()
|
||||
|
||||
@@ -308,112 +250,80 @@ export default function Header() {
|
||||
|
||||
const { claimTxn } = useUserHasSubmittedClaim(account ?? undefined)
|
||||
|
||||
const aggregateBalance: TokenAmount | undefined = useAggregateUniBalance()
|
||||
|
||||
const [showUniBalanceModal, setShowUniBalanceModal] = useState(false)
|
||||
const showClaimPopup = useShowClaimPopup()
|
||||
|
||||
const countUpValue = aggregateBalance?.toFixed(0) ?? '0'
|
||||
const countUpValuePrevious = usePrevious(countUpValue) ?? '0'
|
||||
const scrollY = useScrollPosition()
|
||||
|
||||
const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET]
|
||||
return (
|
||||
<HeaderFrame>
|
||||
<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>
|
||||
<HeaderLinks>
|
||||
<StyledNavLink id={`swap-nav-link`} to={'/swap'}>
|
||||
{t('swap')}
|
||||
</StyledNavLink>
|
||||
<StyledNavLink
|
||||
id={`pool-nav-link`}
|
||||
to={'/pool'}
|
||||
isActive={(match, { pathname }) =>
|
||||
Boolean(match) ||
|
||||
pathname.startsWith('/add') ||
|
||||
pathname.startsWith('/remove') ||
|
||||
pathname.startsWith('/create') ||
|
||||
pathname.startsWith('/find')
|
||||
}
|
||||
>
|
||||
{t('pool')}
|
||||
</StyledNavLink>
|
||||
<StyledNavLink id={`stake-nav-link`} to={'/uni'}>
|
||||
UNI
|
||||
</StyledNavLink>
|
||||
<Title href=".">
|
||||
<UniIcon>
|
||||
<img width={'24px'} src={darkMode ? LogoDark : Logo} alt="logo" />
|
||||
</UniIcon>
|
||||
</Title>
|
||||
<HeaderLinks>
|
||||
<StyledNavLink id={`swap-nav-link`} to={'/swap'}>
|
||||
<Trans>Swap</Trans>
|
||||
</StyledNavLink>
|
||||
<StyledNavLink
|
||||
id={`pool-nav-link`}
|
||||
to={'/pool'}
|
||||
isActive={(match, { pathname }) =>
|
||||
Boolean(match) ||
|
||||
pathname.startsWith('/add') ||
|
||||
pathname.startsWith('/remove') ||
|
||||
pathname.startsWith('/increase') ||
|
||||
pathname.startsWith('/find')
|
||||
}
|
||||
>
|
||||
<Trans>Pool</Trans>
|
||||
</StyledNavLink>
|
||||
{chainId && chainId === SupportedChainId.MAINNET && (
|
||||
<StyledNavLink id={`stake-nav-link`} to={'/vote'}>
|
||||
Vote
|
||||
<Trans>Vote</Trans>
|
||||
</StyledNavLink>
|
||||
<StyledExternalLink id={`stake-nav-link`} href={'https://uniswap.info'}>
|
||||
Charts <span style={{ fontSize: '11px' }}>↗</span>
|
||||
</StyledExternalLink>
|
||||
</HeaderLinks>
|
||||
</HeaderRow>
|
||||
)}
|
||||
<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 />
|
||||
</UNIWrapper>
|
||||
)}
|
||||
{!availableClaim && aggregateBalance && (
|
||||
<UNIWrapper onClick={() => setShowUniBalanceModal(true)}>
|
||||
<UNIAmount active={!!account && !availableClaim} style={{ pointerEvents: 'auto' }}>
|
||||
{account && (
|
||||
<HideSmall>
|
||||
<TYPE.white
|
||||
style={{
|
||||
paddingRight: '.4rem'
|
||||
}}
|
||||
>
|
||||
<CountUp
|
||||
key={countUpValue}
|
||||
isCounting
|
||||
start={parseFloat(countUpValuePrevious)}
|
||||
end={parseFloat(countUpValue)}
|
||||
thousandsSeparator={','}
|
||||
duration={1}
|
||||
/>
|
||||
</TYPE.white>
|
||||
</HideSmall>
|
||||
)}
|
||||
UNI
|
||||
</UNIAmount>
|
||||
<CardNoise />
|
||||
</UNIWrapper>
|
||||
)}
|
||||
<AccountElement active={!!account} style={{ pointerEvents: 'auto' }}>
|
||||
{account && userEthBalance ? (
|
||||
<BalanceText style={{ flexShrink: 0 }} pl="0.75rem" pr="0.5rem" fontWeight={500}>
|
||||
{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>
|
||||
)
|
||||
|
||||
62
src/components/HoverInlineText/index.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import Tooltip from 'components/Tooltip'
|
||||
import { useState } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
const TextWrapper = styled.span<{ margin: boolean; link?: boolean; fontSize?: string; adjustSize?: boolean }>`
|
||||
margin-left: ${({ margin }) => margin && '4px'};
|
||||
color: ${({ theme, link }) => (link ? theme.blue1 : theme.text1)};
|
||||
font-size: ${({ fontSize }) => fontSize ?? 'inherit'};
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
font-size: ${({ adjustSize }) => adjustSize && '12px'};
|
||||
}
|
||||
`
|
||||
|
||||
const HoverInlineText = ({
|
||||
text,
|
||||
maxCharacters = 20,
|
||||
margin = false,
|
||||
adjustSize = false,
|
||||
fontSize,
|
||||
link,
|
||||
...rest
|
||||
}: {
|
||||
text?: string
|
||||
maxCharacters?: number
|
||||
margin?: boolean
|
||||
adjustSize?: boolean
|
||||
fontSize?: string
|
||||
link?: boolean
|
||||
}) => {
|
||||
const [showHover, setShowHover] = useState(false)
|
||||
|
||||
if (!text) {
|
||||
return <span />
|
||||
}
|
||||
|
||||
if (text.length > maxCharacters) {
|
||||
return (
|
||||
<Tooltip text={text} show={showHover}>
|
||||
<TextWrapper
|
||||
onMouseEnter={() => setShowHover(true)}
|
||||
onMouseLeave={() => setShowHover(false)}
|
||||
margin={margin}
|
||||
adjustSize={adjustSize}
|
||||
link={link}
|
||||
fontSize={fontSize}
|
||||
{...rest}
|
||||
>
|
||||
{' ' + text.slice(0, maxCharacters - 1) + '...'}
|
||||
</TextWrapper>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<TextWrapper margin={margin} adjustSize={adjustSize} link={link} fontSize={fontSize} {...rest}>
|
||||
{text}
|
||||
</TextWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export default HoverInlineText
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { useEffect, useRef } from 'react'
|
||||
|
||||
import styled from 'styled-components'
|
||||
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;
|
||||
|
||||
187
src/components/InputStepCounter/InputStepCounter.tsx
Normal file
@@ -0,0 +1,187 @@
|
||||
import { useState, useCallback, useEffect, ReactNode } from 'react'
|
||||
import { OutlineCard } from 'components/Card'
|
||||
import { Input as NumericalInput } from '../NumericalInput'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { ButtonGray } from 'components/Button'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Plus, Minus } from 'react-feather'
|
||||
|
||||
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 InputRow = styled.div`
|
||||
display: grid;
|
||||
|
||||
grid-template-columns: 30px 1fr 30px;
|
||||
`
|
||||
|
||||
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: transparent;
|
||||
text-align: center;
|
||||
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)`
|
||||
color: ${({ theme }) => theme.text2};
|
||||
font-size: 12px;
|
||||
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: ReactNode
|
||||
tokenA: string | undefined
|
||||
tokenB: string | undefined
|
||||
}
|
||||
|
||||
const StepCounter = ({
|
||||
value,
|
||||
decrement,
|
||||
increment,
|
||||
decrementDisabled = false,
|
||||
incrementDisabled = false,
|
||||
width,
|
||||
locked,
|
||||
onUserInput,
|
||||
title,
|
||||
tokenA,
|
||||
tokenB,
|
||||
}: StepCounterProps) => {
|
||||
// for focus state, styled components doesnt let you select input parent container
|
||||
const [active, setActive] = useState(false)
|
||||
|
||||
// let user type value and only update parent value on blur
|
||||
const [localValue, setLocalValue] = useState('')
|
||||
const [useLocalValue, setUseLocalValue] = useState(false)
|
||||
|
||||
// animation if parent value updates local value
|
||||
const [pulsing, setPulsing] = useState<boolean>(false)
|
||||
|
||||
const handleOnFocus = () => {
|
||||
setUseLocalValue(true)
|
||||
setActive(true)
|
||||
}
|
||||
|
||||
const handleOnBlur = useCallback(() => {
|
||||
setUseLocalValue(false)
|
||||
setActive(false)
|
||||
onUserInput(localValue) // trigger update on parent value
|
||||
}, [localValue, onUserInput])
|
||||
|
||||
// for button clicks
|
||||
const handleDecrement = useCallback(() => {
|
||||
setUseLocalValue(false)
|
||||
onUserInput(decrement())
|
||||
}, [decrement, onUserInput])
|
||||
|
||||
const handleIncrement = useCallback(() => {
|
||||
setUseLocalValue(false)
|
||||
onUserInput(increment())
|
||||
}, [increment, onUserInput])
|
||||
|
||||
useEffect(() => {
|
||||
if (localValue !== value && !useLocalValue) {
|
||||
setTimeout(() => {
|
||||
setLocalValue(value) // reset local value to match parent
|
||||
setPulsing(true) // trigger animation
|
||||
setTimeout(function () {
|
||||
setPulsing(false)
|
||||
}, 1800)
|
||||
}, 0)
|
||||
}
|
||||
}, [localValue, useLocalValue, value])
|
||||
|
||||
return (
|
||||
<FocusedOutlineCard pulsing={pulsing} active={active} onFocus={handleOnFocus} onBlur={handleOnBlur} width={width}>
|
||||
<AutoColumn gap="6px">
|
||||
<InputTitle fontSize={12} textAlign="center">
|
||||
{title}
|
||||
</InputTitle>
|
||||
|
||||
<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">
|
||||
<Trans>
|
||||
{tokenB} per {tokenA}
|
||||
</Trans>
|
||||
</InputTitle>
|
||||
</AutoColumn>
|
||||
</FocusedOutlineCard>
|
||||
)
|
||||
}
|
||||
|
||||
export default StepCounter
|
||||
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]
|
||||
)
|
||||
262
src/components/LiquidityChartRangeInput/Brush.tsx
Normal file
@@ -0,0 +1,262 @@
|
||||
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]): boolean => a1[0] !== a2[0] || a1[1] !== 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]) => 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(
|
||||
({ type, selection }: D3BrushEvent<unknown>) => {
|
||||
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)) {
|
||||
setBrushExtent(scaled)
|
||||
}
|
||||
|
||||
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)) {
|
||||
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,
|
||||
]
|
||||
)
|
||||
}
|
||||
138
src/components/LiquidityChartRangeInput/Chart.tsx
Normal file
@@ -0,0 +1,138 @@
|
||||
import { max, scaleLinear, ZoomTransform } from 'd3'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
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 },
|
||||
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])
|
||||
}
|
||||
}, [brushDomain, onBrushDomainChange, xScale])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Zoom
|
||||
svg={zoomRef.current}
|
||||
xScale={xScale}
|
||||
setZoom={setZoom}
|
||||
width={innerWidth}
|
||||
height={
|
||||
// allow zooming inside the x-axis
|
||||
height
|
||||
}
|
||||
showClear={false}
|
||||
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]
|
||||
)
|
||||
121
src/components/LiquidityChartRangeInput/Zoom.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import React, { useEffect, useMemo, useRef } from 'react'
|
||||
import { ButtonGray } from 'components/Button'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ScaleLinear, select, ZoomBehavior, zoom, ZoomTransform } 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,
|
||||
showClear,
|
||||
zoomLevels,
|
||||
}: {
|
||||
svg: SVGElement | null
|
||||
xScale: ScaleLinear<number, number>
|
||||
setZoom: (transform: ZoomTransform) => void
|
||||
width: number
|
||||
height: number
|
||||
showClear: boolean
|
||||
zoomLevels: ZoomLevels
|
||||
}) {
|
||||
const zoomBehavior = useRef<ZoomBehavior<Element, unknown>>()
|
||||
|
||||
const [zoomIn, zoomOut, reset, initial] = 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, 1),
|
||||
() =>
|
||||
svg &&
|
||||
zoomBehavior.current &&
|
||||
select(svg as Element)
|
||||
.transition()
|
||||
.call(zoomBehavior.current.scaleTo, 0.5),
|
||||
],
|
||||
[svg, zoomBehavior]
|
||||
)
|
||||
|
||||
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
|
||||
initial()
|
||||
}, [initial, zoomLevels])
|
||||
|
||||
return (
|
||||
<Wrapper count={showClear ? 3 : 2}>
|
||||
{showClear && (
|
||||
<Button onClick={reset} 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])
|
||||
}
|
||||
197
src/components/LiquidityChartRangeInput/index.tsx
Normal file
@@ -0,0 +1,197 @@
|
||||
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 { isLoading, isUninitialized, isError, error, formattedData } = useDensityChartData({
|
||||
currencyA,
|
||||
currencyB,
|
||||
feeAmount,
|
||||
})
|
||||
|
||||
const onBrushDomainChangeEnded = useCallback(
|
||||
(domain) => {
|
||||
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
|
||||
leftRangeValue > 0 && onLeftRangeInput(leftRangeValue.toFixed(6))
|
||||
rightRangeValue > 0 && onRightRangeInput(rightRangeValue.toFixed(6))
|
||||
})
|
||||
},
|
||||
[onLeftRangeInput, onRightRangeInput]
|
||||
)
|
||||
|
||||
interactive = interactive && Boolean(formattedData?.length)
|
||||
|
||||
const brushDomain: [number, number] | undefined = useMemo(() => {
|
||||
const isSorted = currencyA && currencyB && currencyA?.wrapped.sortsBefore(currencyB?.wrapped)
|
||||
|
||||
const leftPrice = isSorted ? priceLower : priceUpper?.invert()
|
||||
const rightPrice = isSorted ? priceUpper : priceLower?.invert()
|
||||
|
||||
return leftPrice && rightPrice
|
||||
? [parseFloat(leftPrice?.toSignificant(5)), parseFloat(rightPrice?.toSignificant(5))]
|
||||
: undefined
|
||||
}, [currencyA, currencyB, priceLower, priceUpper])
|
||||
|
||||
const brushLabelValue = useCallback(
|
||||
(d: 'w' | 'e', x: number) => {
|
||||
if (!price) return ''
|
||||
|
||||
if (d === 'w' && ticksAtLimit[Bound.LOWER]) return '0'
|
||||
if (d === 'e' && ticksAtLimit[Bound.UPPER]) return '∞'
|
||||
|
||||
//const percent = (((x < price ? -1 : 1) * (Math.max(x, price) - Math.min(x, price))) / Math.min(x, price)) * 100
|
||||
|
||||
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)}%` : ''
|
||||
},
|
||||
[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]}
|
||||
/>
|
||||
</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"
|
||||
/>
|
||||
)
|
||||
58
src/components/LiquidityChartRangeInput/types.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
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
|
||||
}
|
||||
|
||||
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]) => void
|
||||
|
||||
zoomLevels: ZoomLevels
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import styled from 'styled-components/macro'
|
||||
import useHttpLocations from '../../hooks/useHttpLocations'
|
||||
|
||||
import Logo from '../Logo'
|
||||
@@ -13,7 +13,7 @@ export default function ListLogo({
|
||||
logoURI,
|
||||
style,
|
||||
size = '24px',
|
||||
alt
|
||||
alt,
|
||||
}: {
|
||||
logoURI: string
|
||||
size?: string
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import React from 'react'
|
||||
|
||||
import styled, { keyframes } from 'styled-components'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
|
||||
const rotate = keyframes`
|
||||
from {
|
||||
|
||||