Compare commits

...

449 Commits

Author SHA1 Message Date
Crowdin Bot
2899c8af45 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-08 06:04:59 +00:00
Moody Salem
5f13f7824a chore: update the contribution guidelines (#1987)
* chore: update the contribution guidelines

* move

* note about non-deterministic steps

* little bit of separation

* language nits, add accessibility

* add reasoning
2021-07-07 19:48:03 -05:00
Crowdin Bot
6690bc513d chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-08 00:06:45 +00:00
Justin Domingue
aea3c1f6e6 feat(pools): add liquidity chart range input primitives (#1990)
* first iteration of liquidity chart primitives

* add tickprocessed type

* clean up
2021-07-07 16:39:44 -07:00
Justin Domingue
e0c625671b improve liquidity depth hooks and data fetching (#1989) 2021-07-07 14:37:29 -07:00
Crowdin Bot
aa3c867222 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-07 16:06:58 +00:00
Justin Domingue
2182e676d4 fix: enforce styled-components/macro usage and upgrade react-scripts (#1981)
* upgrade react-scripts and enforce macro rule

* Fix code style issues with ESLint

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-07-07 08:45:47 -07:00
Justin Domingue
f0fc11a3b5 stabilize fee distribution in hook (#1979) 2021-07-07 08:27:18 -07:00
Crowdin Bot
53d93afd78 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-07 15:05:04 +00:00
Jordan Frankfurt
b612b15e1e fix(L2): routing fixes (#1980)
* Revert "fix last commit"

This reverts commit 9f5764aab3.

* Revert "fix l2 routing bug"

This reverts commit f6dea47907.

* drops routing changes for L2

* pool v2 switch networks message
2021-07-07 10:59:31 -04:00
Noah Zinsmeister
9f5764aab3 fix last commit 2021-07-07 09:44:28 -04:00
Noah Zinsmeister
f6dea47907 fix l2 routing bug 2021-07-07 09:38:29 -04:00
Crowdin Bot
8e9981e186 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-07 08:05:21 +00:00
Crowdin Bot
1509c430fd chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-07 07:04:46 +00:00
Crowdin Bot
93073321db chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-07 06:05:06 +00:00
Crowdin Bot
618760ff1b chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-07 03:04:52 +00:00
Jordan Frankfurt
2d20fc939d feat(L2): network alert cards (optimism and arbitrum) (#1957)
* design review for arbitrum UI

* add optimism L2 UI

* better gradients, remove v2 links/routes, some minor padding and margin fixes
2021-07-06 22:38:47 -04:00
Crowdin Bot
8c1e9b394b chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-06 22:04:39 +00:00
Moody Salem
9b9f431e6f chore: update yarn.lock 2021-07-06 16:28:55 -05:00
Moody Salem
cfe590d810 refactor(devx): show a warning when a call fails due to insufficient gas allowed 2021-07-06 16:28:01 -05:00
Noah Zinsmeister
2ee9b16c49 remove a lot of dead code (#1970) 2021-07-06 17:08:40 -04:00
Moody Salem
c52cd2c38e fix(position image): increase the gas required constraint for the token URI 2021-07-06 16:08:20 -05:00
Noah Zinsmeister
43cefbdc6a no more borderRadius errors! (#1968) 2021-07-06 17:05:19 -04:00
Moody Salem
ae86fefe8b fix: do not reverse the proposals list in place 2021-07-06 10:17:08 -05:00
Crowdin Bot
8901605c32 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-05 12:05:15 +00:00
Justin Domingue
568829d3b5 fix: invalidate subgraph cache with provideTags (#1962)
* invalidate queries using tags

* enhance generated api with provide tags

* clean up
2021-07-03 12:56:38 -07:00
Justin Domingue
b0f4b4c735 remove loading indicator from fee tier selection (#1954) 2021-07-02 11:00:48 -07:00
Moody Salem
a73d3af4b2 fix: use an arbitrum multicall that returns a better block number 2021-07-02 12:56:12 -05:00
Moody Salem
2c7b431e60 fix: use a multicall that allows limiting gas of the individual calls (#1953)
* fix: use a multicall that allows limiting gas of the individual calls

* use the latest multicall addresses
2021-07-02 12:25:37 -05:00
Ian Lapham
0805b2d9b8 update defaults (#1955) 2021-07-02 13:24:04 -04:00
Moody Salem
6c8fc5a7f6 fix: reduce call retries by specifying a block tag in multicall 2021-07-02 09:25:11 -05:00
Crowdin Bot
702258574d chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-02 06:09:45 +00:00
Crowdin Bot
17bc229a36 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-01 23:07:38 +00:00
Justin Domingue
458a7d90a0 various edits 2021-07-01 15:13:36 -07:00
Crowdin Bot
72877fb0b6 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-01 20:09:52 +00:00
Moody Salem
27f756107e refactor: logs hook (#1941)
* feat(logs): add an infrastructure for fetching logs in a declarative way

* use the logs hook in the vote page, first pass

* fix comment

* bit of cleanup

* unused imports

* improve loading indicator on vote page

* some testnet behavior improvements

* fix loader state

* loading state nit

* show correct indexes

* remove the unnecessary retry code

* first pass at the slice

* no throws

* loading indicator should go away if not connected

* use the logs slice for the logs hook state

* style changes per cal's request
2021-07-01 14:44:02 -05:00
Justin Domingue
abfd87c517 feat(pools): fetch pool tick data from uniswap v3 subgraph (#1932)
* add hooks to fecth ticks data and active liquidity

* cleanup

* add polling interval

* moved ms.macro types to dev deps

* generate graphql schema on build

* added @types/ms.macro

* use clone deep
2021-07-01 11:08:53 -07:00
Crowdin Bot
b3d772bdb5 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-01 18:04:48 +00:00
Justin Domingue
d9c82ebf49 feat: add fee tier distribution badge (#1862)
* integrate with The Graph and auto-select fee tier

* restored

* addressed some design feedback

* add pulsing animation on feeAmount change

* simplify fee tier title

* adjust button radios

* addressed some design feedback

* log ReactGA events

* ignore data while fetching

* update to use new generated queries

* remove deleted file

* add usefeetierdistribution hook

* invalidate cache and standardize the outlined card

* added react ga

* fix show options logic

* address design feedback

* show % select in minified view

* updated merge error
2021-07-01 10:56:27 -07:00
Justin Domingue
5298a5ce29 feat: handle chain id in subgraph api (#1938)
* build dynamic subgraph url based on chain id

* reset api state (query cache) on chain id change

* removed dependency on rtk-query/graphql

* add error message
2021-07-01 09:50:03 -07:00
Moody Salem
4d3073d2d9 fix(position page): link to the token page of the explorer instead of the address page from the position page 2021-07-01 10:55:28 -05:00
Moody Salem
c732f407c6 fix: error messages from walletconnect provider (#1943) 2021-07-01 09:22:28 -05:00
Crowdin Bot
361e0ca8d7 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-01 10:04:56 +00:00
Justin Domingue
cc0a757ddd fix: run graphql:codegen on yarn build (#1933)
* run graphql codegen on build

* yarn.lock

* remove ms.macro
2021-06-29 17:22:41 -07:00
Justin Domingue
31632d3c5b ignore state/data/generated.ts 2021-06-29 16:27:12 -07:00
Justin Domingue
9cfbec4b8b generated graphql schema and en-US.po on start (#1930) 2021-06-29 12:03:47 -07:00
Crowdin Bot
b5ac5d882f chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-29 09:04:42 +00:00
Crowdin Bot
130606cab4 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-29 08:04:53 +00:00
Moody Salem
fa56919a06 fix(theme): always show the gradient background color 2021-06-28 19:47:22 -05:00
Moody Salem
633f65676e fix(theme): fix the missing background radial gradient
fixes https://github.com/Uniswap/uniswap-interface/issues/1925
2021-06-28 19:35:41 -05:00
Moody Salem
9bccb7ae3a fix(multicall): better use the block number returned from calls, and cancel all calls when a new block comes in 2021-06-28 19:26:47 -05:00
Moody Salem
8a2e4123c6 chore(yarn): update yarn.lock 2021-06-28 18:57:10 -05:00
Moody Salem
e93ddbedfa chore(ci): take a screenshot in the cypress test for an example 2021-06-28 18:55:42 -05:00
Justin Domingue
1667b56a04 feat(thegraph): auto-generate graphql types (#1926)
* auto-generate graphql types

* remove introspection

* generated .ts and add to prettierignore

* updated graph url
2021-06-28 16:43:24 -07:00
Justin Domingue
6e995d6c09 feat(pools): integrate with The Graph using RTK Query (#1924)
* integrate with the graph

* Update src/state/data/slice.ts

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2021-06-28 15:12:58 -07:00
Moody Salem
f096112716 fix: improve how we use blocks per fetch in the memoization of the listeners (#1920)
fixes https://github.com/Uniswap/uniswap-interface/issues/1877
2021-06-28 16:39:19 -05:00
Jordan Frankfurt
50c7d36164 replace trustwallet assets repo w/ our fork (#1922) 2021-06-28 16:35:31 -04:00
Moody Salem
8a7f1d51ce chore(ci): record integration tests 2021-06-28 15:12:24 -05:00
Crowdin Bot
8f59f501cc chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-28 16:04:43 +00:00
Jordan Frankfurt
197947835b remove unneeded log (#1917) 2021-06-27 22:23:46 -04:00
Crowdin Bot
94081b29a3 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-27 05:04:39 +00:00
Moody Salem
69183ed8c2 fix: translate claim modal inputs 2021-06-26 23:58:25 -05:00
Crowdin Bot
05e741b6c9 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-27 00:08:49 +00:00
Jordan Frankfurt
dfe550c43b add arbitrum-specific L2 designs (#1912) 2021-06-26 19:54:30 -04:00
Crowdin Bot
65dfb40e44 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-25 15:04:42 +00:00
Moody Salem
25f5ed9983 fix: scrollbars showing up in modal with mouse connected, use auto instead of scroll 2021-06-25 10:00:25 -05:00
Yuta Sugimura
9b1ef415c6 refactor: remove unneeded react import (#1801)
* Add rules

* Remove React import

* remove React import
2021-06-25 09:52:49 -05:00
Crowdin Bot
70f481430a chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-25 07:04:27 +00:00
Justin Domingue
942211eb00 fix: typos in modal css (#1911)
* allow modal scrolling on dekstop

* removed quote and fixed typo
2021-06-24 13:33:11 -07:00
Justin Domingue
6589c41800 allow modal scrolling on dekstop (#1910) 2021-06-24 10:04:07 -07:00
Moody Salem
14bac770b6 fix(arbitrum): explorer links and better anonymization of GA events 2021-06-24 11:38:08 -05:00
Crowdin Bot
c9dbe2f0ac chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-24 10:04:51 +00:00
Crowdin Bot
775cf57c30 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-24 09:04:32 +00:00
Moody Salem
f4935e9264 fix(arbitrum): miscellaneous arbitrum network changes
- adds the arbitrum rinkeby testnet deployment
- compute usdc prices on arbitrum one
2021-06-23 20:58:03 -05:00
Crowdin Bot
6b3914b7b9 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-23 08:05:03 +00:00
Crowdin Bot
41efb6f617 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-23 06:05:18 +00:00
Crowdin Bot
52df5f14cf chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-22 23:04:56 +00:00
Noah Zinsmeister
b48a7c529f fix: proposal sorting 2021-06-22 18:47:12 -04:00
Moody Salem
2eca50be93 chore: remove defunct arbitrum kovan testnet 2021-06-22 17:23:40 -05:00
Moody Salem
be2ce18bfa fix: change polling interval per network (#1898) 2021-06-22 17:21:01 -05:00
yj
fe13b29e5e Update README.md (#1897)
Simple signposting, for issue #1896
2021-06-22 15:03:45 -07:00
Crowdin Bot
02aac08489 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-22 21:05:04 +00:00
Crowdin Bot
8f29980b94 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-22 20:05:13 +00:00
Scott Moses Sunarto
bb542ef0fb feat: Add proposal creation interface (#1799)
* Make address input panel more generalized

* Add TextInput component

* Allow adjusting AppBody maxwidth

* Add create proposal layout page

* Fix style consistency

* create proposal ui goes brrr

* various fixes

* minor tweaks and bug fixes

* merge conflict clean up + bug fix

* i18n

* always show create proposal button

* adjust proposal title top margin and show ButtonError styling

* use button disable instead of button error when form is not filled

* revert mistaken css change

* default params for address input panel

* Refactor & fixes

- Add missing i18n
- Refactor how some styled-components is done
- ButtonError is now disabled when proposal is true in create proposal interface
- Edit copywriting
- Minor styling adjustments

* Fixed create proposal padding on medium screen

* refactor

* refactor pt. 2

* single column styling

* change AddressInputPanel font color

* Design adjustments

* change route for create proposal

* Add autonomous proposal CTA

* cta text edit

* Add link to docs for custom action

* small cleanup

* work with latest governor

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2021-06-22 15:51:32 -04:00
Crowdin Bot
c2fe17615f chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-22 13:12:10 +00:00
Crowdin Bot
a4cab75d09 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-22 08:04:47 +00:00
Crowdin Bot
882457cfef chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-21 16:05:32 +00:00
Justin Domingue
9a1bb5dbfb Add missing semicolon in English source 2021-06-21 08:10:00 -07:00
Crowdin Bot
767cc85b3e chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-21 07:07:47 +00:00
Crowdin Bot
47d726e544 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-21 06:04:54 +00:00
Crowdin Bot
32118520cd chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-20 19:05:02 +00:00
Crowdin Bot
ada8a3af92 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-19 07:06:22 +00:00
Crowdin Bot
63694b32c0 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-19 06:05:14 +00:00
Crowdin Bot
c9ab94d799 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-19 04:05:38 +00:00
Crowdin Bot
a990c4af70 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-18 21:04:57 +00:00
Crowdin Bot
b5cc33c1f5 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-18 20:05:12 +00:00
Justin Domingue
31da6cdb9d fix: new toggle theme (#1782)
* new toggle theme

* moved trans to default

* update radius

* fianlize styles
2021-06-18 12:44:14 -07:00
Crowdin Bot
4079d8a517 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-18 16:05:52 +00:00
Crowdin Bot
e93fbfd31b chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-18 15:05:19 +00:00
Crowdin Bot
7817846368 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-17 23:04:59 +00:00
Crowdin Bot
fc6a69a9af chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-17 09:04:49 +00:00
Crowdin Bot
93a774092f chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-17 08:05:12 +00:00
Crowdin Bot
55563e9bb2 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-17 07:07:40 +00:00
Crowdin Bot
f44aae2f53 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-17 06:04:53 +00:00
Crowdin Bot
7d674c33e7 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-17 04:05:17 +00:00
Noah Zinsmeister
79e6337629 fix governorIndex 2021-06-16 17:31:24 -04:00
Noah Zinsmeister
3c8e8604b8 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-06-16 17:22:46 -04:00
Noah Zinsmeister
b763659788 restrict governance UI to mainnet only
fix governor name bug

revert useContract change

add governorIndex to vote page

only fetch latest useLatestProposalCount

fix useDataFromEventLogs

hardcode proposalIndexes for old governors
2021-06-16 17:22:37 -04:00
Crowdin Bot
1ac2581419 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-16 20:04:59 +00:00
Crowdin Bot
9f556680ed chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-16 19:06:17 +00:00
Justin Domingue
3f242f1b44 fix: memoize extendedEther to unnecessary rerenders (#1869) 2021-06-16 11:07:16 -07:00
Crowdin Bot
126688c753 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-16 17:16:00 +00:00
Ian Lapham
374cc361a6 small bg fixes (#1868) 2021-06-16 13:14:31 -04:00
Crowdin Bot
0198d0baa1 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-16 16:05:39 +00:00
Crowdin Bot
da2d7ba648 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-16 15:05:16 +00:00
Yuta Sugimura
37cf492dd5 fix: Add SwitchLocaleLink to a page where it does not exist (#1867) 2021-06-16 07:24:25 -07:00
Crowdin Bot
9e93f809a0 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-16 14:05:04 +00:00
Crowdin Bot
d9bd392e6d chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-16 12:05:13 +00:00
Crowdin Bot
c48adc519f chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-16 11:05:10 +00:00
Crowdin Bot
9f8983a92e chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-16 10:05:07 +00:00
Crowdin Bot
c9d3d81768 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-16 09:05:06 +00:00
Crowdin Bot
c6f73c35ee chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-16 08:05:22 +00:00
Crowdin Bot
7a2777b814 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-16 03:05:54 +00:00
Jordan Frankfurt
014595cdfb feat: add multi-contract governance support (#1860)
* Revert "feat: quick fix for new governor"
This reverts commit 5dd1249ddd.

* support multiple governance contracts
2021-06-15 22:36:27 -04:00
Jordan Frankfurt
1b27d8dab0 test integration key change (#1864) 2021-06-15 21:04:54 -04:00
Crowdin Bot
c4d12c86a8 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-15 23:04:42 +00:00
Crowdin Bot
8d2feeb89e chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-15 19:05:52 +00:00
Crowdin Bot
576b399768 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-15 18:06:23 +00:00
Crowdin Bot
eb8ce85872 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-15 15:04:56 +00:00
Justin Domingue
1644e4f9c6 fix(a11y): color contrast changes (#1803)
* fix: a11y changes

* more a11y fixes

* updated theme colors

* addressed rev4 comments

* address rev5 comments

* addressed rev6
2021-06-15 07:55:38 -07:00
Yuta Sugimura
574fab54d1 fix: props for ResponsiveButtonPrimary (#1839)
* fix: props for ResponsiveButtonPrimary

* fix ResponsiveButtonPrimary style
2021-06-15 07:55:11 -07:00
Crowdin Bot
8e581c19ff chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-15 09:05:09 +00:00
Crowdin Bot
8c2fa89e9e chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-15 08:05:05 +00:00
Crowdin Bot
0ae7452eca chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-15 07:07:02 +00:00
Crowdin Bot
4589beb789 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-15 06:04:49 +00:00
Crowdin Bot
55aee86971 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-15 04:04:55 +00:00
Crowdin Bot
a457af91b6 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-15 03:07:51 +00:00
Crowdin Bot
e61b5da3c5 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-15 00:08:26 +00:00
Crowdin Bot
b4f44744c1 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 23:05:29 +00:00
Crowdin Bot
27f4e2e750 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 22:04:51 +00:00
Crowdin Bot
01159c53d5 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 21:04:56 +00:00
Crowdin Bot
8d8d63b873 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 20:05:10 +00:00
Crowdin Bot
da9db4b5f1 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 18:04:48 +00:00
Justin Domingue
1724bee0f5 Update useSwapCallback.ts 2021-06-14 10:24:33 -07:00
Crowdin Bot
48e526554b chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 17:14:33 +00:00
Crowdin Bot
53add345de chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 16:05:45 +00:00
Crowdin Bot
e28faf0da2 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 13:12:25 +00:00
Crowdin Bot
f79dc5fe82 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 12:04:53 +00:00
Crowdin Bot
a6f29cbff8 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 11:04:51 +00:00
Crowdin Bot
68f0d4b725 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 10:04:57 +00:00
Crowdin Bot
111e7dc929 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 09:04:55 +00:00
Crowdin Bot
39d61e4913 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 08:07:59 +00:00
Crowdin Bot
3e6d3ec1b5 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 07:07:26 +00:00
Crowdin Bot
3f65530bf4 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 06:04:48 +00:00
Crowdin Bot
dba0d20781 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 05:07:28 +00:00
Crowdin Bot
a0f3bd0dc6 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 04:04:52 +00:00
Crowdin Bot
bef85abfd4 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 03:06:19 +00:00
Crowdin Bot
9934dba805 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 02:21:00 +00:00
Crowdin Bot
72d94b2831 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 01:28:24 +00:00
Crowdin Bot
138697a30a chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-14 00:10:25 +00:00
Crowdin Bot
0add8ae177 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 22:04:40 +00:00
Crowdin Bot
13f7859aaa chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 21:04:46 +00:00
Crowdin Bot
3337b5de22 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 20:04:51 +00:00
Noah Zinsmeister
55ea6616a4 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-06-13 15:15:07 -04:00
Noah Zinsmeister
5dd1249ddd feat: quick fix for new governor 2021-06-13 15:15:00 -04:00
Crowdin Bot
4ab2bd3474 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 19:06:13 +00:00
Crowdin Bot
9eb20624cb chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 18:05:07 +00:00
Crowdin Bot
25c5cf04e6 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 17:13:27 +00:00
Crowdin Bot
1f70e63cd0 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 16:08:49 +00:00
Crowdin Bot
df88b0ddd1 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 15:05:26 +00:00
Crowdin Bot
679759c3a0 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 14:05:00 +00:00
Crowdin Bot
9b6003bdc5 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 13:12:31 +00:00
Crowdin Bot
2b32824c26 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 12:09:45 +00:00
Crowdin Bot
a4e4cf8ab9 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 11:04:48 +00:00
Crowdin Bot
f08cea1820 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 10:04:50 +00:00
Crowdin Bot
e2d35c56fc chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 09:05:04 +00:00
Crowdin Bot
5f20040107 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 07:06:32 +00:00
Crowdin Bot
9344abd1cf chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 06:05:04 +00:00
Crowdin Bot
426e31230d chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 05:06:53 +00:00
Crowdin Bot
06c48559c5 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 04:05:34 +00:00
Crowdin Bot
0a33ed1a50 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 03:05:54 +00:00
Crowdin Bot
d5c490b871 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 02:19:46 +00:00
Crowdin Bot
099c2adea3 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 01:28:37 +00:00
Crowdin Bot
d387538576 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-13 00:10:49 +00:00
Crowdin Bot
a3a7d01ce9 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 23:05:25 +00:00
Crowdin Bot
549e67bcea chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 22:09:06 +00:00
Crowdin Bot
932992b57f chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 21:04:46 +00:00
Crowdin Bot
76a24376d4 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 20:08:09 +00:00
Crowdin Bot
f873b2249f chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 19:08:33 +00:00
Crowdin Bot
22452c03b0 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 18:05:07 +00:00
Crowdin Bot
14e045b999 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 17:13:55 +00:00
Crowdin Bot
948781f927 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 16:05:42 +00:00
Crowdin Bot
2f72d6b77e chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 15:06:47 +00:00
Crowdin Bot
a78209690d chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 14:04:39 +00:00
Crowdin Bot
98a2f51125 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 13:10:38 +00:00
Crowdin Bot
a1043573af chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 12:07:02 +00:00
Crowdin Bot
63760faa9d chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 11:04:39 +00:00
Crowdin Bot
0566ec28be chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 10:04:55 +00:00
Crowdin Bot
d4c1baba57 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 09:04:49 +00:00
Crowdin Bot
7e41b27cd3 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 08:05:55 +00:00
Crowdin Bot
1d18a5159f chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 07:06:40 +00:00
Crowdin Bot
bb87d2dd03 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 06:04:58 +00:00
Crowdin Bot
0b27528a6a chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 05:06:33 +00:00
Crowdin Bot
5788a273ba chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 04:09:51 +00:00
Crowdin Bot
4d322b17da chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-12 03:09:09 +00:00
Crowdin Bot
cb5b80b558 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-11 16:05:43 +00:00
Crowdin Bot
6964b519bc chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-11 11:04:45 +00:00
Crowdin Bot
ee4ea2a1f0 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-11 10:05:12 +00:00
Crowdin Bot
3763ad15e0 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-10 18:14:56 +00:00
Justin Domingue
557042a88f add missing translations (#1842) 2021-06-10 10:42:07 -07:00
Crowdin Bot
860413dd52 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-10 17:21:32 +00:00
Crowdin Bot
18d4cdcce4 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-10 14:05:54 +00:00
Crowdin Bot
55767de484 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-10 13:15:24 +00:00
Yuta Sugimura
60d5cef69b fix: untranslated parts (#1837) 2021-06-10 07:49:03 -05:00
yj
ad2833a3ed Translate both text in PositionPage.tsx (#1838) 2021-06-10 07:47:59 -05:00
Crowdin Bot
e4a105714a chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-10 12:05:11 +00:00
Crowdin Bot
3e842d3259 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-10 10:05:04 +00:00
Crowdin Bot
e9d18acd41 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-10 00:10:42 +00:00
Moody Salem
abeba3d6b6 fix: translate both prices in the rate toggle
fixes https://github.com/Uniswap/uniswap-interface/issues/1834
2021-06-09 18:06:42 -05:00
Moody Salem
84c4219c02 fix: proposals not showing up on first load of vote page 2021-06-09 18:04:59 -05:00
Crowdin Bot
50b81c8334 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-09 19:14:47 +00:00
Crowdin Bot
8986b63335 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-09 18:15:03 +00:00
Crowdin Bot
631b4c08b6 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-09 17:20:45 +00:00
Crowdin Bot
b92f0b8d90 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-09 08:05:19 +00:00
Moody Salem
ce926e91b7 chore(ci): skip ci on crowdin syncs 2021-06-08 19:29:57 -05:00
Justin Domingue
4857281a1e undo testing artifact 2021-06-08 11:49:38 -07:00
Crowdin Bot
d2a872e7d2 chore(i18n): synchronize translations from crowdin 2021-06-08 14:09:11 +00:00
Crowdin Bot
73042f20fd chore(i18n): synchronize translations from crowdin 2021-06-08 13:16:53 +00:00
Crowdin Bot
29b5e583c5 chore(i18n): synchronize translations from crowdin 2021-06-08 12:05:11 +00:00
Crowdin Bot
d67f3158bd chore(i18n): synchronize translations from crowdin 2021-06-08 11:07:08 +00:00
Crowdin Bot
817732d62e chore(i18n): synchronize translations from crowdin 2021-06-08 09:05:31 +00:00
Crowdin Bot
562a54a175 chore(i18n): synchronize translations from crowdin 2021-06-08 08:06:38 +00:00
Crowdin Bot
cd1e0cf378 chore(i18n): synchronize translations from crowdin 2021-06-08 06:12:39 +00:00
Crowdin Bot
0c81b08d66 chore(i18n): synchronize translations from crowdin 2021-06-08 04:21:20 +00:00
Crowdin Bot
2662f8afbc chore(i18n): synchronize translations from crowdin 2021-06-08 02:38:00 +00:00
Moody Salem
355012eec9 fix: set polling interval back to 15 seconds 2021-06-07 17:52:49 -05:00
Noah Zinsmeister
68518d7ce0 fix[migrate]: use proper slippage check (#1821) 2021-06-07 15:01:30 -04:00
Crowdin Bot
2c9c99b868 chore(i18n): synchronize translations from crowdin 2021-06-07 18:48:52 +00:00
ChristophSiegenthaler
7b78dfa2c4 Fix typo (#1815)
Co-authored-by: ChristophSiegenthaler <csi@gmx.ch>
2021-06-07 11:07:16 -07:00
Crowdin Bot
e6cbab3c72 chore(i18n): synchronize translations from crowdin 2021-06-07 16:21:50 +00:00
Crowdin Bot
144f86bdfd chore(i18n): synchronize translations from crowdin 2021-06-06 01:33:53 +00:00
Crowdin Bot
6703743aef chore(i18n): synchronize translations from crowdin 2021-06-06 00:16:18 +00:00
Crowdin Bot
0320083909 chore(i18n): synchronize translations from crowdin 2021-06-05 22:08:28 +00:00
Crowdin Bot
b96f7d27ca chore(i18n): synchronize translations from crowdin 2021-06-05 21:08:45 +00:00
Crowdin Bot
45e85bc857 chore(i18n): synchronize translations from crowdin 2021-06-05 20:11:50 +00:00
Crowdin Bot
24e1a01b1a chore(i18n): synchronize translations from crowdin 2021-06-05 19:24:25 +00:00
Crowdin Bot
0e7a94313c chore(i18n): synchronize translations from crowdin 2021-06-05 18:31:11 +00:00
Crowdin Bot
35e80fa3ed chore(i18n): synchronize translations from crowdin 2021-06-05 17:29:29 +00:00
Crowdin Bot
4a70d8eb28 chore(i18n): synchronize translations from crowdin 2021-06-05 16:14:07 +00:00
Moody Salem
706d8724c1 README.md already at the top! 2021-06-05 09:47:18 -05:00
Moody Salem
6e363c1ff1 make _README.md into a symlink 2021-06-05 09:46:49 -05:00
Crowdin Bot
27007708ec chore(i18n): synchronize translations from crowdin 2021-06-04 20:28:49 +00:00
Crowdin Bot
c4739ea963 chore(i18n): synchronize translations from crowdin 2021-06-04 18:41:01 +00:00
Justin Domingue
50954eff0f Create _README.md 2021-06-04 11:14:15 -07:00
Crowdin Bot
af112765e4 chore(i18n): synchronize translations from crowdin 2021-06-04 14:32:59 +00:00
Crowdin Bot
52b71004d0 chore(i18n): synchronize translations from crowdin 2021-06-04 13:29:48 +00:00
Crowdin Bot
2ba7c857cd chore(i18n): synchronize translations from crowdin 2021-06-04 12:26:58 +00:00
Crowdin Bot
67f3b40671 chore(i18n): synchronize translations from crowdin 2021-06-04 05:34:23 +00:00
Crowdin Bot
c2acf5c1a8 chore(i18n): synchronize translations from crowdin 2021-06-04 02:49:43 +00:00
Crowdin Bot
51cf8aa0da chore(i18n): synchronize translations from crowdin 2021-06-04 01:34:22 +00:00
Crowdin Bot
bc0e578610 chore(i18n): synchronize translations from crowdin 2021-06-03 21:28:59 +00:00
Crowdin Bot
1b13cac0fb chore(i18n): synchronize translations from crowdin 2021-06-03 20:33:14 +00:00
Crowdin Bot
14ed4dbb2e chore(i18n): synchronize translations from crowdin 2021-06-03 11:25:20 +00:00
Crowdin Bot
dc140901b3 chore(i18n): synchronize translations from crowdin 2021-06-03 07:33:57 +00:00
Crowdin Bot
07a92ba63b chore(i18n): synchronize translations from crowdin 2021-06-03 06:36:08 +00:00
Crowdin Bot
3f2f46df43 chore(i18n): synchronize translations from crowdin 2021-06-03 05:39:54 +00:00
Crowdin Bot
2dc5c0d386 chore(i18n): synchronize translations from crowdin 2021-06-03 04:58:53 +00:00
Crowdin Bot
38de86f4c9 chore(i18n): synchronize translations from crowdin 2021-06-03 03:01:20 +00:00
Crowdin Bot
b2196e7e48 chore(i18n): synchronize translations from crowdin 2021-06-03 00:24:19 +00:00
Crowdin Bot
30f66f7062 chore(i18n): synchronize translations from crowdin 2021-06-02 23:21:59 +00:00
Justin Domingue
b87430277a support plurals (#1798) 2021-06-02 16:01:16 -07:00
Moody Salem
b4c59cfc6e chore: fix the double translation of 'For'
fixes https://github.com/Uniswap/uniswap-interface/issues/1800
2021-06-02 17:51:54 -05:00
Moody Salem
2326dfa108 chore: remove the lang attribute as well 2021-06-02 17:25:41 -05:00
Crowdin Bot
97bf36efbf chore(i18n): synchronize translations from crowdin 2021-06-02 21:32:30 +00:00
Justin Domingue
264acaae48 Update CONTRIBUTING.md 2021-06-02 14:16:11 -07:00
Crowdin Bot
63e9f6e4d2 chore(i18n): synchronize translations from crowdin 2021-06-02 20:50:26 +00:00
Moody Salem
02d80e07dc refactor(L2): Arbitrum Kovan testnet and Arbitrum One support (#1716)
* experimental! point at a kovan arbitrum deployment

* remove the unwrapped token from mint hook

* fix explorer links

* Etherscan -> Explorer

* move chains to constant file

* use NETWORK_URLS instead

* temporary fix to the syncing issue

* fix tests

* fix unknown chain id crash

* use a multicall that returns arbitrum block numbers

* lower polling interval for layer 2

* use a better multicall

* remove unused import

* fixed multicall2

* make some v2 code chain specific

* fix build

* line number changes

* update keys

* fix the locale parsing of full locale string

* extract

* fix lint

* add arbitrum one

* add arbitrum one to supported chains

* add missing arbitrum one label

* refactor: remove storybook

* point to the arbitrum explorer address

* fix arbitrum mainnet links

* fix how weth shows up in the interface

* make the usdc price feature more cross chain compatible

* missing translation

* clean up some governance code so it doesn't crash on unsupported networks

* improve how we check for transaction receipts for sequencer networks

* improve it a bit more
2021-06-02 15:14:34 -05:00
Crowdin Bot
ad53da5efe chore(i18n): synchronize translations from crowdin 2021-06-02 19:58:27 +00:00
Crowdin Bot
29223ce34f chore(i18n): synchronize translations from crowdin 2021-06-02 18:58:14 +00:00
Crowdin Bot
b88919ed69 chore(i18n): synchronize translations from crowdin 2021-06-02 17:34:57 +00:00
Ian Lapham
ab80910553 fix: add pool find button to v2 pool page (#1700)
* add pool find button to v2 pool page

* update copy to be v2 specific

* fixed migrate v2 liquidity back arrow

* add missing <Trans>

Co-authored-by: Justin Domingue <judo@uniswap.org>
2021-06-02 10:08:45 -07:00
Crowdin Bot
1cdb8aa6a4 chore(i18n): synchronize translations from crowdin 2021-06-02 16:27:43 +00:00
Crowdin Bot
88976d0294 chore(i18n): synchronize translations from crowdin 2021-06-02 15:21:20 +00:00
Crowdin Bot
b908c81e2b chore(i18n): synchronize translations from crowdin 2021-06-02 14:34:38 +00:00
Crowdin Bot
5c20d0adb7 chore(i18n): synchronize translations from crowdin 2021-06-02 09:35:14 +00:00
Crowdin Bot
ca6295fcb7 chore(i18n): synchronize translations from crowdin 2021-06-02 07:35:26 +00:00
Crowdin Bot
e8af59198d chore(i18n): synchronize translations from crowdin 2021-06-02 06:49:31 +00:00
Crowdin Bot
cd4c43b482 chore(i18n): synchronize translations from crowdin 2021-06-02 05:07:18 +00:00
jab416171
c1e07f3713 Fixing some instances of the removeChild issue (#1793)
specifically when the google translate extension triggers it
2021-06-01 20:39:53 -07:00
Crowdin Bot
bccda20473 chore(i18n): synchronize translations from crowdin 2021-06-02 03:03:17 +00:00
Moody Salem
bed8dd5110 fix: do not show price data on non-mainnet 2021-06-01 17:33:50 -05:00
Moody Salem
6bcbabec23 refactor: remove storybook 2021-06-01 17:04:26 -05:00
Justin Domingue
71a490f2aa fix(#1694): fix popup item header overlap (#1783) 2021-06-01 14:30:08 -07:00
Moody Salem
abcf1f5a5c fix: background should show up while loading the js 2021-06-01 16:20:31 -05:00
Crowdin Bot
65e5e9c98e chore(i18n): synchronize translations from crowdin 2021-06-01 20:55:07 +00:00
Moody Salem
fdabb90f0e fix: fire a GA event when the locale is switched 2021-06-01 15:31:15 -05:00
Crowdin Bot
049054f5bb chore(i18n): synchronize translations from crowdin 2021-06-01 20:00:20 +00:00
Crowdin Bot
91d0a002c9 chore(i18n): synchronize translations from crowdin 2021-06-01 16:25:05 +00:00
Justin Domingue
305d2e1df9 chore: change inactive to closed (#1780) 2021-06-01 08:23:23 -07:00
Crowdin Bot
6bb7d9d9b2 chore(i18n): synchronize translations from crowdin 2021-06-01 15:03:26 +00:00
Moody Salem
55d5762e39 chore(ci): sync translations from crowdin straight into main 2021-06-01 09:59:27 -05:00
Justin Domingue
979686100e chore: simplify useSelector and useDispatch types using inference (#1760)
* chore: simplify app dispatch and selector types with ts inference

* update SearchModal to use new types inference

* chore: simplify useSelector and useDispatch types using inference
2021-06-01 07:58:21 -07:00
Moody Salem
a7a1607faa fix: add a link to switch the locale conditionally (#1776)
* add a link to switch the locale conditionally

* make it smaller

* add the locale labels

* language labels from the internet

* fix the heart in the claim popup

* undo the conditional change

* remove todo

* updated Chinese locale name to office-365 language ids

* added missing <Trans>

Co-authored-by: Justin Domingue <judo@uniswap.org>
2021-06-01 09:54:43 -05:00
Hart Lambur
5125cbb1a3 fix(uma): UMA/UNI June expiry call option routing (#1777)
This would allow for direct routing from UMA tokens to June expiry UMA and UNI covered call option tokens (specifically [UMA 25 Call [30 June 2021]](https://etherscan.io/address/0xb46f57e7ce3a284d74b70447ef9352b5e5df8963) and  [UNI 50 Call [30 June 2021]](https://etherscan.io/address/0xe0360a9e2cdd7d03b9408c7d3001e017bac2ecd5)), in the same manner as the May expiry call tokens allowed.
2021-06-01 07:28:21 -05:00
github-actions[bot]
e89387bb26 chore(i18n): synchronize translations from crowdin (#1775) 2021-06-01 05:57:18 -05:00
Moody Salem
f7896db5aa fix linting error, fix header responsiveness in non-english languages, try to fix the background color issue on safari 2021-05-31 22:08:48 -05:00
Moody Salem
0a0053c6f1 fix: prevent the interface from flashing the wrong locale temporarily on load 2021-05-31 21:36:56 -05:00
github-actions[bot]
9e10abbf97 chore(i18n): synchronize translations from crowdin (#1773)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2021-05-31 21:11:08 -05:00
Moody Salem
c38771a6aa refactor: remove the fixed global style component (#1772)
* chore: remove the fixed global style component

* move it to a more appropriate place

* move some css around

* add an element that triggers the font to immediately load

* some more nit refactoring

* style

* move the radial gradient into the index.css file

* try overriding the font display attribute of the inter family

* remove font-display rule
2021-05-31 21:10:36 -05:00
Moody Salem
8e364680b6 chore(deps): remove some unused code and dependencies 2021-05-31 20:15:59 -05:00
Henry Zhu
04c25685f7 fix: add space to backToV3 swap button (#1769) 2021-05-31 19:35:53 -05:00
github-actions[bot]
be1e184d88 chore(i18n): synchronize translations from crowdin (#1766)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2021-05-31 16:55:57 -05:00
Moody Salem
b485b14395 chore(i18n): add id-ID 2021-05-31 09:55:01 -05:00
github-actions[bot]
44ef0138de chore(i18n): synchronize translations from crowdin (#1762)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2021-05-31 09:54:06 -05:00
github-actions[bot]
a1142cc7af chore(i18n): synchronize translations from crowdin (#1761) 2021-05-30 22:00:29 -05:00
Moody Salem
089280ddf0 fix: clean up the charts navigation link 2021-05-30 18:44:34 -05:00
Moody Salem
f3c2a6eaea more translations in the swap page 2021-05-30 18:40:36 -05:00
github-actions[bot]
54dc5b1f19 chore(i18n): synchronize translations from crowdin (#1757)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2021-05-30 18:28:19 -05:00
Moody Salem
0af7651c0b info about doing translations 2021-05-30 17:51:26 -05:00
Justin Domingue
61d9dfe127 chore: simplify redux types with ts inference (#1758)
* chore: simplify app dispatch and selector types with ts inference

* update SearchModal to use new types inference
2021-05-30 14:32:57 -07:00
Henry Zhu
d990dfdec7 fix: add padding to price outside of range (#1752) 2021-05-30 14:51:15 -05:00
github-actions[bot]
4336e08233 chore(i18n): synchronize translations from crowdin (#1755)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2021-05-30 14:49:57 -05:00
Moody Salem
3840daf365 few more missed spots 2021-05-30 14:15:55 -05:00
Moody Salem
1104bdcefc use trans macro instead of t wherever possible 2021-05-30 14:05:37 -05:00
Moody Salem
499ef27722 fix the english translation in build 2021-05-30 13:37:33 -05:00
github-actions[bot]
1ea0769ba3 chore(i18n): synchronize translations from crowdin (#1751)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2021-05-29 20:49:44 -05:00
Moody Salem
637d43621b chore(ci): undo the if check on yarn install 2021-05-29 20:41:16 -05:00
Moody Salem
9831cda290 more i18n for errors 2021-05-29 20:36:43 -05:00
Moody Salem
62d4bb8b51 fix linting error, one more missing key 2021-05-29 20:32:31 -05:00
Moody Salem
58a1be8421 chore(i18n): add a few more translations 2021-05-29 20:29:49 -05:00
Moody Salem
40b939df58 chore(ci): skip install step if node modules is cached and remove cache from release 2021-05-29 20:19:49 -05:00
github-actions[bot]
3ccecc5ca1 chore(i18n): synchronize translations from crowdin (#1749)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2021-05-29 20:13:31 -05:00
Moody Salem
9df5b536c9 chore(ci): set up the shared cache for all jobs and extract before doing the sync 2021-05-29 20:06:49 -05:00
Moody Salem
4b72d757c5 chore: add a bunch of missing translation keys 2021-05-29 16:02:43 -05:00
Moody Salem
63affd9828 add required translation key 2021-05-29 15:48:20 -05:00
github-actions[bot]
135f9dc219 chore(i18n): synchronize translations from crowdin (#1748)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2021-05-29 15:47:37 -05:00
Moody Salem
08b74b2524 add the source config key 2021-05-29 15:45:49 -05:00
Moody Salem
ffcea6388e chore(ci): separate sources upload and translations download workflows 2021-05-29 15:44:26 -05:00
Moody Salem
408a07ebd4 try extracting en-US.po only in the github workflow 2021-05-29 15:32:51 -05:00
Moody Salem
7f837810e0 chore: reduce png image sizes 2021-05-29 15:22:00 -05:00
github-actions[bot]
57a7a4995a chore(i18n): synchronize translations from crowdin (#1747)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2021-05-29 12:27:29 -07:00
Justin Domingue
cc66783278 Update CONTRIBUTING.md 2021-05-29 12:07:46 -07:00
Justin Domingue
f97dcaef1d Add localized pill 2021-05-29 11:34:10 -07:00
Moody Salem
cfea3723a2 remove setting the locale from using the active locale 2021-05-29 07:14:26 -05:00
Moody Salem
7d0fb9cc10 workaround for the url locale not sticking 2021-05-29 00:29:17 -05:00
github-actions[bot]
8633dc7a50 chore(i18n): synchronize translations from crowdin (#1746)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2021-05-29 00:21:49 -05:00
Moody Salem
f340537544 add a bunch of missing translations to the swap page 2021-05-29 00:16:57 -05:00
Moody Salem
3e9289dc35 couple of missing translations in the currency list 2021-05-29 00:11:10 -05:00
Moody Salem
4eb5c3cac8 fix the forced re-render on locale change 2021-05-29 00:07:42 -05:00
github-actions[bot]
cfadd3b165 chore(i18n): synchronize translations from crowdin (#1745)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2021-05-29 00:03:15 -05:00
Moody Salem
d6db79b8f5 update the commit message since it is used in the pr merge commit 2021-05-29 00:01:51 -05:00
Moody Salem
6007527ce0 couple more missing keys 2021-05-28 23:58:23 -05:00
Moody Salem
06c8db917f chore: handle a couple missing translation keys 2021-05-28 23:46:55 -05:00
Moody Salem
3394bc738f fix the locale parsing of full locale string 2021-05-28 23:43:15 -05:00
Moody Salem
32b56ba0fd just pick the first occurrence of an abbreviated locale in the locale parsing 2021-05-28 23:30:29 -05:00
github-actions[bot]
f97ce35cee chore(i18n): synchronize translations from Crowdin
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2021-05-28 23:27:19 -05:00
Moody Salem
0dafd3e7ba chore: update to the top 30 locales 2021-05-28 23:25:32 -05:00
Moody Salem
d719942931 chore: en -> en-US 2021-05-28 23:15:07 -05:00
github-actions[bot]
62b9b5d527 New Crowdin translations by Github Action (#1743)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2021-05-28 23:10:02 -05:00
Moody Salem
ab34645073 chore: pare down the locales directory to let the crowdin sync do the work 2021-05-28 23:06:54 -05:00
Moody Salem
dab3671cfe fix environment variable 2021-05-28 22:07:15 -05:00
Moody Salem
478cb3afa3 another config change to try 2021-05-28 22:06:15 -05:00
Moody Salem
d56015d750 one more config attempt 2021-05-28 21:57:25 -05:00
Moody Salem
a639e65cee chore(ci): try inlining the config 2021-05-28 21:55:29 -05:00
Moody Salem
243e14b74e chore(ci): add a workflow for crowdin.yaml 2021-05-28 21:48:15 -05:00
Justin Domingue
70174140bc chore: refactored locale management hook (#1737)
* chore: refactored locale management hook

* use useMemo instead of useState
2021-05-28 17:36:37 -07:00
Justin Domingue
4fede9df36 removed custom keys, part 2 (#1740) 2021-05-28 17:10:56 -07:00
Justin Domingue
fbd5dab53e Update CONTRIBUTING.md 2021-05-28 16:32:55 -07:00
Justin Domingue
ecdce21b16 Update README.md 2021-05-28 16:30:40 -07:00
Justin Domingue
1a38309500 Update CONTRIBUTING.md 2021-05-28 16:29:38 -07:00
Justin Domingue
46f539c604 chore(i18n): wrapped English text around <Trans> component (#1714)
* feat(i18n): wrapped English text around <Trans> component

* cleanup

* synced

* regenerated locales
2021-05-28 16:25:50 -07:00
Justin Domingue
ad65b63868 remove custom keys (#1739) 2021-05-28 15:33:40 -07:00
Justin Domingue
f9a5dbf6ce fix: add sourceLocale: en (#1738)
* fix: add sourceLocale: en

* Fix code style issues with ESLint

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-05-28 14:52:54 -07:00
Justin Domingue
7991b0b0d8 Update CONTRIBUTING.md 2021-05-28 10:17:41 -07:00
Moody Salem
d85dccd01a chore: remove list update popup code 2021-05-28 11:08:04 -05:00
dependabot[bot]
37799caa03 chore(deps): bump dns-packet from 1.3.1 to 1.3.4 (#1721)
Bumps [dns-packet](https://github.com/mafintosh/dns-packet) from 1.3.1 to 1.3.4.
- [Release notes](https://github.com/mafintosh/dns-packet/releases)
- [Changelog](https://github.com/mafintosh/dns-packet/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mafintosh/dns-packet/compare/v1.3.1...v1.3.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-28 11:04:22 -05:00
Justin Domingue
04878e095c feat(i18n): save and load locale from redux store (#1723)
* wrap sock around function component to get re-renders

* removed sourcelocale since we are using custom ids

* load locale data dynamically

* undo dev change

* ran yarn i18n:extract

* store locale in redux

* added support for redux

* restored header

* refactor

* addressed pr feedback

* specify type

* added navigator locale fallback to generic dialect

* make locale array const and typed

* addressed pr feedback

* fixed various

* addressed pr feedback

* make supported locales constant uppercase

* add back toUpperCase removed during refactoring

* removed lingui/detect-locale

* run yarn
2021-05-28 09:03:53 -07:00
Moody Salem
14a6953b90 fix: useCallback is not necessary 2021-05-28 09:44:05 -05:00
Moody Salem
d866d79578 fix: anonymize outbound link events sent to google analytics
fixes https://github.com/Uniswap/uniswap-interface/issues/1729
2021-05-28 09:08:53 -05:00
Moody Salem
7f1def300d fix: usdc prices shouldn't reload when v3 prices reload 2021-05-27 15:28:45 -05:00
Justin Domingue
8930627064 delete old locales/ folders (#1725) 2021-05-27 12:10:42 -07:00
Justin Domingue
efb939297f update CONTRIBUTING.md with new folder structure 2021-05-27 09:42:38 -07:00
Justin Domingue
69cc728d43 fix(i18n): various fixes and improvements (#1718)
* wrap sock around function component to get re-renders

* removed sourcelocale since we are using custom ids

* load locale data dynamically

* undo dev change

* ran yarn i18n:extract

* make SOCK component lower case

* fixed unmerged conflicts

* regenerated po for wallet.socks

* added loadLocaleData
2021-05-27 09:38:37 -07:00
Moody Salem
a6bc7775bc chore(i18n): single directory for locales 2021-05-27 10:52:17 -05:00
Moody Salem
3452be9a45 chore(i18n): remove line numbers from extracted po 2021-05-27 10:37:15 -05:00
Justin Domingue
be6e2fe74a chore(i18n): convert old locale file to new format (#1715)
* added supported locales

* added vi locale

* deleted old locale files

* add catalogues to i18n.tsx

* remove fallbackLocales

* add fallback
2021-05-26 18:58:00 -07:00
Justin Domingue
83b1297ff6 Update CONTRIBUTING.md 2021-05-26 17:18:57 -07:00
Justin Domingue
a20ed15b00 Update CONTRIBUTING.md 2021-05-26 17:15:04 -07:00
Justin Domingue
1413ab91bf Create CONTRIBUTING.md 2021-05-26 17:14:02 -07:00
Justin Domingue
c4846c8832 feat(i18n): improve i18n (#1692)
* replaced i18next with lingui

* integrate lingui in i18n and update dev setup

* updated components to @lingui

* fix compile error after rebase

* detect locale

* add all previous languages to linguirc

* address pr feedback

* remove it for now

* ignore generate *js files, various fixes

* added more translations

* fixed yarn build command

* wrapped more hardcoded english around <Trans>

* finished second round of translations

* added support for pseudo-en locale

* improvements

* moved copy.tsx to different branch

* moved extra files to different branch

* regenerated po

* clean up

* more fixes

* regenerate po

* remove messages.js

* clean up

* addressed pr feedback

* regenerated po
2021-05-26 13:34:52 -07:00
Moody Salem
dbbffd17a6 fix: improve cancel/retry detection 2021-05-25 13:48:23 -05:00
Moody Salem
85d8b1e514 fix: improve privacy of the claim popup (#1698)
* fix: improve privacy of the claim popup

fixes https://github.com/Uniswap/uniswap-interface/issues/1337

* fix unhandled rejection

* clean up the claim code a bit more

* working claim fetch

* working claim fetch

* trigger some events on the claim popup
2021-05-24 18:15:12 -05:00
Moody Salem
739986ba66 chore: update README.md twitter link 2021-05-24 12:11:59 -05:00
Moody Salem
e9148b30c9 chore: bump v3-sdk 2021-05-21 20:21:41 -05:00
Moody Salem
6e46f25bd4 improvement: use the block number in receipts to update the app state (#1681) 2021-05-21 19:55:44 -05:00
Justin Domingue
02269e9376 fix: handle usdc directly in useUSDCPrice (#1683) 2021-05-21 16:45:39 -07:00
Moody Salem
9822e68d5a remove console.log accidentally committed to swap 2021-05-21 14:35:48 -05:00
Justin Domingue
9d1556b777 feat(pool): add toggle to hide inactive positions (#1676)
* feat(pool): add toggle to hide inactive positions in pools page

* cleanup code

* keep positions sorted, but move zero liquidity positions to end

* simplified sorting logic
2021-05-21 12:22:40 -07:00
Justin Domingue
858b6afb2f improvement(swap): hide swap placeholders to reduce visual complexity (#1678)
* resolve #1646: hide swap placeholders to reduce visual complexity

* use empty string over space
2021-05-21 10:44:41 -07:00
Moody Salem
da14d8fd5b chore: update links in README.md 2021-05-21 10:28:39 -05:00
Moody Salem
3aaa3fef89 fix: wrap/unwrap should not show computed trade details 2021-05-21 10:09:11 -05:00
Moody Salem
bc243e1c07 refactor: some clean up of the common bases code, add WETH 2021-05-21 10:01:58 -05:00
Moody Salem
679a58daf4 unwrapped token is not needed 2021-05-21 09:39:32 -05:00
Moody Salem
eff512deb8 bump sdk-core 2021-05-21 09:34:46 -05:00
Moody Salem
562b402293 feat: use multicall for argent wallets in swap and v3 add liquidity (#1387)
* use argent wallet contract in swap callback

* maybe working swap callback

* chore(v3): trigger a breaking release

BREAKING CHANGE: trigger a major release for the uniswap interface to indicate it now supports swapping and liquidity provision against uniswap protocol v3

* fix the value

* improve the error coverage

* retry more frequently, couple more error nits

* the is argent call was being sketchy

* get it working for add liquidity

* `0x0` for v2 swaps too

* small nits in position page

* fix import

* fix compiler error
2021-05-21 09:30:07 -05:00
Moody Salem
99ad4ae44c feat: ETH2x-FLI to ETH2X pair routing (#1644) 2021-05-20 23:53:28 -05:00
Moody Salem
6e52a43584 fix: remove the click to flip price on the position list item
fixes https://github.com/Uniswap/uniswap-interface/issues/1436
2021-05-20 23:09:14 -05:00
Moody Salem
d4c5d3e8c8 chore: run eslint 2021-05-20 20:14:55 -05:00
Moody Salem
a6e46623ad chore: bump ethers and regenerate lockfile (#1666)
* bump ethers

* regenerate lockfile

* update code style

* Fix code style issues with ESLint

* remove unused useRef

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-05-20 20:11:32 -05:00
Justin Domingue
9353c7838c fix: catch errors from Pair.getLiquidityMinted (#1668) 2021-05-20 16:14:23 -07:00
TeaTwo
816249b4b1 fix: replace jazzicon to @metamask/jazzicon (#1659) 2021-05-20 15:10:39 -07:00
Moody Salem
70292a5512 do not ask for approve for argent wallet migrate 2021-05-20 16:36:51 -05:00
Justin Domingue
ecdbb4a49f revert: "fix: handle insufficient input amount error in v2 add liquidity (#1652)" (#1665)
This reverts commit 307a995a50.
2021-05-20 11:08:07 -07:00
Justin Domingue
536a5d99de fix: fix typo is getLiquidityMinted catch (#1663) 2021-05-20 10:11:42 -07:00
Noah Zinsmeister
ffc2015595 invalidate stale requests in useV3PositionFees 2021-05-20 12:31:46 -04:00
Moody Salem
5e30a4b4ad refactor: bump to latest sdk (#1657) 2021-05-20 11:21:40 -05:00
Moody Salem
b14da2844d chore: remove all tick lens stuff that is not used 2021-05-19 17:35:59 -05:00
Justin Domingue
d3c04b7246 fix: stabilize fiat value by fixing max hop and increasing USDC out to 100k (#1651)
* stablizie fiat value by fixing max hop and increasing USDC out to 100k

* move maxHops and singleHopOnly to parameter of useV2TradeExactOut

* remove single hop param
2021-05-19 13:21:27 -07:00
Justin Domingue
307a995a50 fix: handle insufficient input amount error in v2 add liquidity (#1652)
* handle insufficient input amount error in mint/hooks

* use error.isInsufficientInputAmountError

* fixed typo
2021-05-19 13:18:32 -07:00
Moody Salem
46911593e5 fix: removing and adding liquidity when the price of the pool is at the min/max price
fixes https://github.com/Uniswap/uniswap-interface/issues/1641
2021-05-18 19:17:05 -05:00
Justin Domingue
1ac36c7b6b fix: display pool shares < 10% (#1639)
* fix: display pool shares < 10%

* set bips to 1/10_000
2021-05-18 17:07:06 -05:00
Moody Salem
8e055df447 refactor: make the price computation in v2 add liquidity a little safer 2021-05-18 16:06:54 -05:00
Moody Salem
02ecd727eb fix: do not crash when user switches eth -> weth in add (#1637)
* fix: do not crash when user switches eth -> weth in add

* linting error
2021-05-18 16:52:34 -04:00
Moody Salem
adc8429bdc fix: working copy to clipboard button in walletconnect qr code modal (#1636)
fixes https://github.com/Uniswap/uniswap-interface/issues/1488
2021-05-18 15:22:53 -05:00
Moody Salem
458d34d96c give the check a name 2021-05-18 15:14:33 -05:00
Justin Domingue
6a4fa0c9bf fix: division by 0 error when both reserves are 0 (#1632)
* set pair to invalid if both reserves are 0

* consider reserve0/1=0 as no liquidity
2021-05-18 12:50:28 -07:00
Moody Salem
7fc9a655fc fix: improve rendering of fee dollar values 2021-05-18 14:35:02 -05:00
Moody Salem
8d3babd015 refactor: more separation by chain of addresses 2021-05-18 14:33:03 -05:00
Moody Salem
75ed4c5dae fix: missed a memo for fetching v2 pairs (#1635) 2021-05-18 14:21:06 -05:00
Justin Domingue
58dfadb13c handle custom token overflow (#1634) 2021-05-18 11:32:09 -07:00
Moody Salem
3d5f5f783c chore: check pr titles 2021-05-18 13:26:23 -05:00
jab416171
ecb7132843 Add etherscan links for the two tokens in the pool on the NFT page (#1593)
* Add etherscan links for the two tokens in the pool on the NFT page

* moving linked tokens to a function

added arrow to show that it's a link
added check for eth

* clean up impl

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2021-05-18 13:34:20 -04:00
Justin Domingue
980eb8a518 Leverage Trade api for USDC price with v2 & v3 support (#1610)
* calculate usdc price using trade exact out

* fix typo

* add support for non-mainnet

* use fromRawAmount instead of tryParseAmount
2021-05-18 10:07:37 -07:00
Noah Zinsmeister
a11e89c0e1 hide WETH toggle for non-WETH pairs (#1633)
* only show weth toggle for eth pairs

* revert unwrappedToken change
2021-05-18 12:58:42 -04:00
Moody Salem
3efe5268ae refactor: break down some constants files and move some stuff around 2021-05-18 10:31:22 -05:00
Moody Salem
3f43a8fbaf npx not needed 2021-05-18 09:28:30 -05:00
Ian Lapham
cccf149568 fix: update tick parsing to handle min/max prices (#1613)
* update tick parsing to handle min/max prices

* remove fee tier step
2021-05-17 20:19:15 -04:00
Justin Domingue
604ea49567 Revert "lazy load uncommon routes to reduce initial bundle size (#1597)" (#1611)
This reverts commit 4c0c6dfde6.
2021-05-17 11:24:31 -07:00
Justin Domingue
4c0c6dfde6 lazy load uncommon routes to reduce initial bundle size (#1597) 2021-05-17 11:03:49 -07:00
Noah Zinsmeister
a160bb8f02 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-05-17 11:54:17 -04:00
Noah Zinsmeister
e86946b104 bump to sdk version that fixes mint slippage bug
closes #1573
2021-05-17 11:54:06 -04:00
Moody Salem
03108b981e update README.md 2021-05-17 10:14:35 -05:00
Moody Salem
ae27e245b4 replace the message when we fail to parse an error 2021-05-14 14:13:13 -05:00
Moody Salem
897f354202 chore: bump mathieudutour/github-tag-action to latest release version to fix generated changelogs
https://github.com/Uniswap/uniswap-interface/issues/1572
2021-05-14 10:56:57 -05:00
Moody Salem
4745052f0e fix: break words in swap error messages (fixes https://github.com/Uniswap/uniswap-interface/issues/1462) 2021-05-14 10:50:55 -05:00
Moody Salem
5bc5d6504e improve the text slightly 2021-05-14 08:45:17 -05:00
Moody Salem
7a0b85bf41 improvement: estimated amounts instead of minimum/maximums (#1570)
* estimated amounts instead of minimum/maximums

* missed a spot

* fix linting error
2021-05-14 07:15:53 -05:00
OMGspidertanx
534afb3278 fix: tweak element sizing/wraping in approve button (#1569) 2021-05-14 07:15:41 -05:00
Moody Salem
7d71af353e perf: debounce localStorage save 2021-05-14 07:10:25 -05:00
372 changed files with 81646 additions and 13786 deletions

2
.env
View File

@@ -1,4 +1,2 @@
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
REACT_APP_WALLETCONNECT_BRIDGE_URL="https://uniswap.bridge.walletconnect.org"
# Because we use storybook which has its own babel-loader dependency @ 8.2.2, where react-scripts uses 8.1.0
SKIP_PREFLIGHT_CHECK=true

View File

@@ -27,6 +27,19 @@
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/explicit-module-boundary-types": "off"
"@typescript-eslint/explicit-module-boundary-types": "off",
"react/react-in-jsx-scope": "off",
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "styled-components",
"message": "Please import from styled-components/macro."
}
],
"patterns": ["!styled-components/macro"]
}
]
}
}

17
.github/workflows/check-pr-title.yaml vendored Normal file
View 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
View 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
View 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 }}

View File

@@ -22,6 +22,18 @@ jobs:
node-version: 14
registry-url: https://registry.npmjs.org
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies
run: yarn install --frozen-lockfile
@@ -34,5 +46,6 @@ jobs:
- run: yarn integration-test
env:
CYPRESS_INTEGRATION_TEST_PRIVATE_KEY: ${{ secrets.CYPRESS_INTEGRATION_TEST_PRIVATE_KEY }}
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}

View File

@@ -23,6 +23,18 @@ jobs:
node-version: 14
registry-url: https://registry.npmjs.org
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies
run: yarn install --frozen-lockfile

View File

@@ -19,7 +19,7 @@ jobs:
- 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: .*

View File

@@ -21,6 +21,18 @@ jobs:
node-version: 14
registry-url: https://registry.npmjs.org
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies
run: yarn install --frozen-lockfile

7
.gitignore vendored
View File

@@ -3,6 +3,11 @@
# generated contract types
/src/types/v3
/src/abis/types
/src/locales/**/*.js
/src/locales/**/*.ts
/src/locales/**/*.json
/src/locales/**/en-US.po
/src/state/data/generated.ts
# dependencies
/node_modules
@@ -35,4 +40,4 @@ package-lock.json
cypress/videos
cypress/screenshots
cypress/fixtures/example.json
cypress/fixtures/example.json

1
.prettierignore Normal file
View File

@@ -0,0 +1 @@
/src/state/data/generated.ts

View File

@@ -1,16 +0,0 @@
const { dirname, join, parse, resolve } = require('path')
const { existsSync } = require('fs')
module.exports = {
stories: ['../src/**/*.stories.@(ts|tsx)'],
addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/preset-create-react-app'],
typescript: {
check: true,
checkOptions: {},
reactDocgen: 'react-docgen-typescript',
reactDocgenTypescriptOptions: {
shouldExtractLiteralValuesFromEnum: true,
propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true),
},
},
}

View File

@@ -1,4 +0,0 @@
import { addons } from '@storybook/addons'
import { light } from './theme'
addons.setConfig({ theme: light })

View File

@@ -1,91 +0,0 @@
import 'inter-ui'
import { Story } from '@storybook/react/types-6-0'
import { createWeb3ReactRoot, Web3ReactProvider } from '@web3-react/core'
import React from 'react'
import { Provider as StoreProvider } from 'react-redux'
import { ThemeProvider as SCThemeProvider } from 'styled-components'
import { NetworkContextName } from '../src/constants'
import store from '../src/state'
import { FixedGlobalStyle, theme, ThemedGlobalStyle } from '../src/theme'
import getLibrary from '../src/utils/getLibrary'
import * as storybookThemes from './theme'
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
dependencies: {
withStoriesOnly: true,
hideEmpty: true,
},
docs: {
theme: storybookThemes.light,
},
viewport: {
viewports: {
mobile: {
name: 'iPhone X',
styles: {
width: '375px',
height: '812px',
},
},
tablet: {
name: 'iPad',
styles: {
width: '768px',
height: '1024px',
},
},
laptop: {
name: 'Laptop',
styles: {
width: '1024px',
height: '768px',
},
},
desktop: {
name: 'Desktop',
styles: {
width: '1440px',
height: '1024px',
},
},
},
},
}
export const globalTypes = {
theme: {
name: 'Theme',
description: 'Global theme for components',
defaultValue: 'light',
toolbar: {
icon: 'circlehollow',
items: ['light', 'dark'],
},
},
}
const Web3ProviderNetwork = createWeb3ReactRoot(NetworkContextName)
const withProviders = (Component: Story, context: Record<string, any>) => {
const THEME = theme(context.globals.theme === 'dark')
return (
<>
<Web3ReactProvider getLibrary={getLibrary}>
<Web3ProviderNetwork getLibrary={getLibrary}>
<StoreProvider store={store}>
<SCThemeProvider theme={THEME}>
<FixedGlobalStyle />
<ThemedGlobalStyle />
<main>
<Component />
</main>
</SCThemeProvider>
</StoreProvider>
</Web3ProviderNetwork>
</Web3ReactProvider>
</>
)
}
export const decorators = [withProviders]

View File

@@ -1,17 +0,0 @@
import { create } from '@storybook/theming'
// this themes the storybook UI
const uniswapBaseTheme = {
brandTitle: 'Uniswap Design',
brandUrl: 'https://uniswap.org',
brandImage: 'https://ipfs.io/ipfs/QmNa8mQkrNKp1WEEeGjFezDmDeodkWRevGFN8JCV7b4Xir',
}
export const light = create({
base: 'light',
...uniswapBaseTheme,
})
// export const dark = create({
// base: 'dark',
// ...uniswapBaseTheme,
// })

62
CONTRIBUTING.md Normal file
View 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!

View File

@@ -4,17 +4,21 @@
[![Integration Tests](https://github.com/Uniswap/uniswap-interface/actions/workflows/integration-tests.yaml/badge.svg)](https://github.com/Uniswap/uniswap-interface/actions/workflows/integration-tests.yaml)
[![Lint](https://github.com/Uniswap/uniswap-interface/actions/workflows/lint.yml/badge.svg)](https://github.com/Uniswap/uniswap-interface/actions/workflows/lint.yml)
[![Release](https://github.com/Uniswap/uniswap-interface/actions/workflows/release.yaml/badge.svg)](https://github.com/Uniswap/uniswap-interface/actions/workflows/release.yaml)
[![Crowdin](https://badges.crowdin.net/uniswap-interface/localized.svg)](https://crowdin.com/project/uniswap-interface)
An open source interface for Uniswap -- a protocol for decentralized exchange of Ethereum tokens.
- Website: [uniswap.org](https://uniswap.org/)
- Interface: [app.uniswap.org](https://app.uniswap.org)
- Docs: [uniswap.org/docs/](https://uniswap.org/docs/)
- Twitter: [@UniswapProtocol](https://twitter.com/UniswapProtocol)
- Twitter: [@Uniswap](https://twitter.com/Uniswap)
- Reddit: [/r/Uniswap](https://www.reddit.com/r/Uniswap/)
- Email: [contact@uniswap.org](mailto:contact@uniswap.org)
- Discord: [Uniswap](https://discord.gg/FCfyBSbCU5)
- Whitepaper: [Link](https://hackmd.io/C-DvwDSfSxuh-Gd4WKE_ig)
- Whitepapers:
- [V1](https://hackmd.io/C-DvwDSfSxuh-Gd4WKE_ig)
- [V2](https://uniswap.org/whitepaper.pdf)
- [V3](https://uniswap.org/whitepaper-v3.pdf)
## Accessing the Uniswap Interface
@@ -22,27 +26,21 @@ To access the Uniswap Interface, use an IPFS gateway link from the
[latest release](https://github.com/Uniswap/uniswap-interface/releases/latest),
or visit [app.uniswap.org](https://app.uniswap.org).
## Development
### Install Dependencies
```bash
yarn install
```
### Run
```bash
yarn start
```
## Contributions
**Please open all pull requests against the `main` branch.**
CI checks will run against all PRs.
For steps on local deployment, development, and code contribution, please see [CONTRIBUTING](./CONTRIBUTING.md).
## Accessing Uniswap Interface V1
## Accessing Uniswap V2
The Uniswap Interface supports swapping against, and migrating or removing liquidity from Uniswap V1. However,
if you would like to use Uniswap V1, the Uniswap V1 interface for mainnet and testnets is accessible via IPFS gateways
The Uniswap Interface supports swapping, adding liquidity, removing liquidity and migrating liquidity for
Uniswap protocol V2.
- Swap on Uniswap V2: https://app.uniswap.org/#/swap?use=v2
- View V2 liquidity: https://app.uniswap.org/#/pool/v2
- Add V2 liquidity: https://app.uniswap.org/#/add/v2
- Migrate V2 liquidity to V3: https://app.uniswap.org/#/migrate/v2
## Accessing Uniswap V1
The Uniswap V1 interface for mainnet and testnets is accessible via IPFS gateways
linked from the [v1.0.0 release](https://github.com/Uniswap/uniswap-interface/releases/tag/v1.0.0).

11
codegen.yml Normal file
View 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

View File

@@ -1,4 +1,5 @@
{
"projectId": "yp82ef",
"baseUrl": "http://localhost:3000",
"pluginsFile": false,
"fixturesFolder": false,

View File

@@ -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', () => {

View File

@@ -8,7 +8,9 @@ import { JsonRpcProvider } from '@ethersproject/providers'
import { Wallet } from '@ethersproject/wallet'
import { Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
const TEST_PRIVATE_KEY = Cypress.env('INTEGRATION_TEST_PRIVATE_KEY')
// todo: figure out how env vars actually work in CI
// const TEST_PRIVATE_KEY = Cypress.env('INTEGRATION_TEST_PRIVATE_KEY')
const TEST_PRIVATE_KEY = '0xe580410d7c37d26c6ad1a837bbae46bc27f9066a466fb3a66e770523b4666d19'
// address of the above key
export const TEST_ADDRESS_NEVER_USE = new Wallet(TEST_PRIVATE_KEY).address

53
lingui.config.ts Normal file
View 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',
}

View File

@@ -4,27 +4,30 @@
"homepage": ".",
"private": true,
"devDependencies": {
"@emotion/core": "^11.0.0",
"@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",
"@react-hook/window-scroll": "^1.3.0",
"@reduxjs/toolkit": "^1.3.5",
"@storybook/addon-actions": "^6.1.17",
"@storybook/addon-essentials": "^6.1.17",
"@storybook/addon-links": "^6.1.17",
"@storybook/addons": "^6.1.17",
"@storybook/components": "^6.1.17",
"@storybook/preset-create-react-app": "^3.1.5",
"@storybook/preset-typescript": "^3.0.0",
"@storybook/react": "^6.1.17",
"@storybook/theming": "^6.1.17",
"@styled-system/css": "^5.1.5",
"@reduxjs/toolkit": "^1.6.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",
@@ -48,10 +51,10 @@
"@uniswap/token-lists": "^1.0.0-beta.19",
"@uniswap/v2-core": "1.0.0",
"@uniswap/v2-periphery": "^1.1.0-beta.0",
"@uniswap/v2-sdk": "^3.0.0-alpha.0",
"@uniswap/v2-sdk": "^3.0.0-alpha.2",
"@uniswap/v3-core": "1.0.0",
"@uniswap/v3-periphery": "1.0.0",
"@uniswap/v3-sdk": "^3.0.0-alpha.2",
"@uniswap/v3-periphery": "^1.1.1",
"@uniswap/v3-sdk": "^3.0.0-alpha.9",
"@web3-react/core": "^6.0.9",
"@web3-react/fortmatic-connector": "^6.0.9",
"@web3-react/injected-connector": "^6.0.7",
@@ -63,20 +66,20 @@
"copy-to-clipboard": "^3.2.0",
"cross-env": "^7.0.2",
"cypress": "^4.11.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",
"lightweight-charts": "^3.3.0",
"lodash.flatmap": "^4.5.0",
"luxon": "^1.25.0",
"ms.macro": "^2.0.0",
"multicodec": "^3.0.1",
"multihashes": "^4.0.2",
"node-vibrant": "^3.1.5",
@@ -89,7 +92,7 @@
"react-dom": "^17.0.1",
"react-feather": "^2.0.8",
"react-ga": "^2.5.7",
"react-i18next": "^10.7.0",
"react-is": "^17.0.2",
"react-markdown": "^4.3.1",
"react-popper": "^2.2.3",
"react-redux": "^7.2.2",
@@ -100,10 +103,11 @@
"react-virtualized-auto-sizer": "^1.0.2",
"react-window": "^1.8.5",
"rebass": "^4.0.7",
"redux-devtools-extension": "^2.13.9",
"redux-localstorage-simple": "^2.3.1",
"serve": "^11.3.2",
"start-server-and-test": "^1.11.0",
"styled-components": "^4.2.0",
"styled-components": "^5.3.0",
"styled-system": "^5.1.5",
"typechain": "^5.0.0",
"typescript": "^4.2.3",
@@ -116,16 +120,22 @@
"workbox-routing": "^6.1.0",
"workbox-strategies": "^6.1.0"
},
"resolutions": {
"@walletconnect/web3-provider": "1.5.0-rc.5"
},
"scripts": {
"compile-contract-types": "yarn compile-external-abi-types && yarn compile-v3-contract-types",
"compile-external-abi-types": "npx typechain --target ethers-v5 --out-dir src/abis/types './src/abis/**/*.json'",
"compile-v3-contract-types": "npx typechain --target ethers-v5 --out-dir src/types/v3 './node_modules/@uniswap/?(v3-core|v3-periphery)/artifacts/contracts/**/*.json'",
"build": "yarn compile-contract-types && react-scripts build",
"integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'",
"compile-external-abi-types": "typechain --target ethers-v5 --out-dir src/abis/types './src/abis/**/*.json'",
"compile-v3-contract-types": "typechain --target ethers-v5 --out-dir src/types/v3 './node_modules/@uniswap/?(v3-core|v3-periphery)/artifacts/contracts/**/*.json'",
"build": "yarn compile-contract-types && yarn graphql:generate && yarn i18n:extract && yarn i18n:compile && react-scripts build",
"i18n:extract": "lingui extract --locale en-US",
"i18n:compile": "lingui compile",
"integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run --record'",
"graphql:generate": "graphql-codegen --config codegen.yml",
"postinstall": "yarn compile-contract-types",
"start": "yarn compile-contract-types && react-scripts start",
"storybook": "start-storybook -p 6006",
"test": "react-scripts test --env=jsdom"
"test": "react-scripts test --env=jsdom",
"prestart": "yarn graphql:generate && touch src/locales/en-US.po"
},
"eslintConfig": {
"extends": "react-app",

View File

@@ -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.

View File

@@ -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!"
}

View File

@@ -1,116 +0,0 @@
{
"noWallet": "No Ethereum wallet found",
"wrongNetwork": "You are on the wrong network",
"switchNetwork": "Please switch to {{ correctNetwork }}",
"installWeb3MobileBrowser": "Please visit us from a web3-enabled mobile browser such as Trust Wallet or Coinbase Wallet.",
"installMetamask": "Please visit us after installing Metamask on Chrome or Brave.",
"disconnected": "Disconnected",
"swap": "Swap",
"swapAnyway": "Swap Anyway",
"send": "Send",
"sendAnyway": "Send Anyway",
"pool": "Pool",
"betaWarning": "This project is in beta. Use at your own risk.",
"input": "Input",
"output": "Output",
"estimated": "estimated",
"balance": "Balance: {{ balanceInput }}",
"unlock": "Unlock",
"pending": "Pending",
"selectToken": "Select a token",
"searchOrPaste": "Search Token Name, Symbol, or Address",
"searchOrPasteMobile": "Name, Symbol, or Address",
"noExchange": "No Exchange Found",
"noToken": "No Token Found",
"exchangeRate": "Exchange Rate",
"unknownError": "Oops! An unknown error occurred. Please refresh the page, or visit from another browser or device.",
"enterValueCont": "Enter a {{ missingCurrencyValue }} value to continue.",
"selectTokenCont": "Select a token to continue.",
"noLiquidity": "No liquidity.",
"insufficientLiquidity": "Insufficient liquidity.",
"unlockTokenCont": "Please unlock token to continue.",
"transactionDetails": "Advanced Details",
"hideDetails": "Hide Details",
"slippageWarning": "Slippage Warning",
"highSlippageWarning": "High Slippage Warning",
"youAreSelling": "You are selling",
"orTransFail": "or the transaction will fail.",
"youWillReceive": "You will receive at least",
"youAreBuying": "You are buying",
"itWillCost": "It will cost at most",
"forAtMost": "for at most",
"insufficientBalance": "Insufficient Balance",
"inputNotValid": "Not a valid input value",
"differentToken": "Must be different token.",
"noRecipient": "Enter a wallet address to send to.",
"invalidRecipient": "Please enter a valid wallet address recipient.",
"recipientAddress": "Recipient Address",
"youAreSending": "You are sending",
"willReceive": "will receive at least",
"to": "to",
"addLiquidity": "Add Liquidity",
"deposit": "Deposit",
"currentPoolSize": "Current Pool Size",
"yourPoolShare": "Your Pool Share",
"noZero": "Amount cannot be zero.",
"mustBeETH": "One of the input must be ETH.",
"enterCurrencyOrLabelCont": "Enter a {{ inputCurrency }} or {{ label }} value to continue.",
"youAreAdding": "You are adding",
"and": "and",
"intoPool": "into the liquidity pool.",
"outPool": "from the liquidity pool.",
"youWillMint": "You will mint",
"liquidityTokens": "liquidity tokens.",
"totalSupplyIs": "Current total supply of liquidity tokens is",
"youAreSettingExRate": "You are setting the initial exchange rate to",
"totalSupplyIs0": "Current total supply of liquidity tokens is 0.",
"tokenWorth": "At current exchange rate, each pool token is worth",
"firstLiquidity": "You are the first person to add liquidity!",
"initialExchangeRate": "The initial exchange rate will be set based on your deposits. Please make sure that your ETH and {{ label }} deposits have the same fiat value.",
"removeLiquidity": "Remove Liquidity",
"poolTokens": "Pool Tokens",
"enterLabelCont": "Enter a {{ label }} value to continue.",
"youAreRemoving": "You are removing between",
"youWillRemove": "You will remove",
"createExchange": "Create Exchange",
"invalidTokenAddress": "Not a valid token address",
"exchangeExists": "{{ label }} Exchange already exists!",
"invalidSymbol": "Invalid symbol",
"invalidDecimals": "Invalid decimals",
"tokenAddress": "Token Address",
"label": "Label",
"name": "Name",
"symbol": "Symbol",
"decimals": "Decimals",
"enterTokenCont": "Enter a token address to continue",
"priceChange": "Expected price slippage",
"forAtLeast": "for at least ",
"brokenToken": "The selected token is not compatible with Uniswap V1. Adding liquidity will result in locked funds.",
"toleranceExplanation": "Lowering this limit decreases your risk of frontrunning. However, this makes more likely that your transaction will fail due to normal price movements.",
"tokenSearchPlaceholder": "Search name or paste address",
"selectFee": "Select Fee",
"selectLiquidityRange": "Set Price Range",
"selectPool": "Select Fee Tier",
"depositAmounts": "Deposit Amounts",
"fee": "fee",
"setLimits": "Set Limits",
"percent": "Percent",
"rate": "Rate",
"currentRate": "Current {{label}} Price:",
"inactiveRangeWarning": "Your position will not earn fees or be used in trades until the market price moves into your range.",
"invalidRangeWarning": "Invalid range selected. The min price must be lower than the max price.",
"connectWallet": "Connect Wallet",
"unsupportedAsset": "Unsupported Asset",
"feePool": "Fee Pool",
"feeTier": "Fee Tier",
"rebalanceMessage": "Your underlying tokens will be automatically rebalanced when the rate of the pool changes and may be different when you withdraw the position.",
"addEarnHelper": "You will earn fees from trades proportional to your share of the pool.",
"learnMoreAboutFess": " Learn more about earning fees.",
"selectAPool": "Choose the best fee tier for your selected pair.",
"poolType": "Select a fee tier based on your preferred liquidity provider fee.",
"rangeWarning": "Your liquidity will only be active and earning fees when the rate of the pool is within this price range.",
"chooseLiquidityAmount": "Choose an amount of tokens to open this liquidity position. If you dont have enough tokens you can trade for them with a Swap.",
"inputTokenDynamic": "Input {{label}}",
"selectStartingPrice": "Set Starting Price",
"newPoolPrice": "Select the market rate for the tokens being added."
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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 "
}

View File

@@ -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": "הכניסו כתובת טוקן כדי להמשיך"
}

View File

@@ -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ă"
}

View File

@@ -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": "Чтобы продолжить, введите адрес токена"
}

View File

@@ -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 "
}

View File

@@ -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": "输入通证地址并继续"
}

View File

@@ -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": "輸入代幣地址"
}

View 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"
}
]

View File

@@ -1,55 +0,0 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "_factoryV1",
"type": "address"
},
{
"internalType": "address",
"name": "_router",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amountTokenMin",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amountETHMin",
"type": "uint256"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
}
],
"name": "migrate",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]

View File

@@ -1,330 +0,0 @@
[
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall2.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "aggregate",
"outputs": [
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
},
{
"internalType": "bytes[]",
"name": "returnData",
"type": "bytes[]"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall2.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "blockAndAggregate",
"outputs": [
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "blockHash",
"type": "bytes32"
},
{
"components": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "bytes",
"name": "returnData",
"type": "bytes"
}
],
"internalType": "struct Multicall2.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
}
],
"name": "getBlockHash",
"outputs": [
{
"internalType": "bytes32",
"name": "blockHash",
"type": "bytes32"
},
{
"components": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "bytes",
"name": "returnData",
"type": "bytes"
}
],
"internalType": "struct Multicall2.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getBlockNumber",
"outputs": [
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCurrentBlockCoinbase",
"outputs": [
{
"internalType": "address",
"name": "coinbase",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCurrentBlockDifficulty",
"outputs": [
{
"internalType": "uint256",
"name": "difficulty",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCurrentBlockGasLimit",
"outputs": [
{
"internalType": "uint256",
"name": "gaslimit",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCurrentBlockTimestamp",
"outputs": [
{
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "addr",
"type": "address"
}
],
"name": "getEthBalance",
"outputs": [
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getLastBlockHash",
"outputs": [
{
"internalType": "bytes32",
"name": "blockHash",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "requireSuccess",
"type": "bool"
},
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall2.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "tryAggregate",
"outputs": [
{
"components": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "bytes",
"name": "returnData",
"type": "bytes"
}
],
"internalType": "struct Multicall2.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "requireSuccess",
"type": "bool"
},
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall2.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "tryBlockAndAggregate",
"outputs": [
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "blockHash",
"type": "bytes32"
},
{
"components": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "bytes",
"name": "returnData",
"type": "bytes"
}
],
"internalType": "struct Multicall2.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
]

View File

@@ -1,165 +0,0 @@
[
{
"inputs": [
{
"components": [
{ "internalType": "address", "name": "target", "type": "address" },
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
],
"internalType": "struct Multicall2.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "aggregate",
"outputs": [
{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" },
{ "internalType": "bytes[]", "name": "returnData", "type": "bytes[]" }
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{ "internalType": "address", "name": "target", "type": "address" },
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
],
"internalType": "struct Multicall2.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "blockAndAggregate",
"outputs": [
{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" },
{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" },
{
"components": [
{ "internalType": "bool", "name": "success", "type": "bool" },
{ "internalType": "bytes", "name": "returnData", "type": "bytes" }
],
"internalType": "struct Multicall2.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" }],
"name": "getBlockHash",
"outputs": [{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getBlockNumber",
"outputs": [{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCurrentBlockCoinbase",
"outputs": [{ "internalType": "address", "name": "coinbase", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCurrentBlockDifficulty",
"outputs": [{ "internalType": "uint256", "name": "difficulty", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCurrentBlockGasLimit",
"outputs": [{ "internalType": "uint256", "name": "gaslimit", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCurrentBlockTimestamp",
"outputs": [{ "internalType": "uint256", "name": "timestamp", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "addr", "type": "address" }],
"name": "getEthBalance",
"outputs": [{ "internalType": "uint256", "name": "balance", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getLastBlockHash",
"outputs": [{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "bool", "name": "requireSuccess", "type": "bool" },
{
"components": [
{ "internalType": "address", "name": "target", "type": "address" },
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
],
"internalType": "struct Multicall2.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "tryAggregate",
"outputs": [
{
"components": [
{ "internalType": "bool", "name": "success", "type": "bool" },
{ "internalType": "bytes", "name": "returnData", "type": "bytes" }
],
"internalType": "struct Multicall2.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "bool", "name": "requireSuccess", "type": "bool" },
{
"components": [
{ "internalType": "address", "name": "target", "type": "address" },
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
],
"internalType": "struct Multicall2.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "tryBlockAndAggregate",
"outputs": [
{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" },
{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" },
{
"components": [
{ "internalType": "bool", "name": "success", "type": "bool" },
{ "internalType": "bytes", "name": "returnData", "type": "bytes" }
],
"internalType": "struct Multicall2.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
]

View File

@@ -1,6 +0,0 @@
import { Interface } from '@ethersproject/abi'
import { abi as STAKING_REWARDS_ABI } from '@uniswap/liquidity-staker/build/StakingRewards.json'
const STAKING_REWARDS_INTERFACE = new Interface(STAKING_REWARDS_ABI)
export { STAKING_REWARDS_INTERFACE }

View File

@@ -1,471 +0,0 @@
[
{
"name": "Transfer",
"inputs": [
{
"type": "address",
"name": "_from",
"indexed": true
},
{
"type": "address",
"name": "_to",
"indexed": true
},
{
"type": "uint256",
"name": "_tokenId",
"indexed": true
}
],
"anonymous": false,
"type": "event"
},
{
"name": "Approval",
"inputs": [
{
"type": "address",
"name": "_owner",
"indexed": true
},
{
"type": "address",
"name": "_approved",
"indexed": true
},
{
"type": "uint256",
"name": "_tokenId",
"indexed": true
}
],
"anonymous": false,
"type": "event"
},
{
"name": "ApprovalForAll",
"inputs": [
{
"type": "address",
"name": "_owner",
"indexed": true
},
{
"type": "address",
"name": "_operator",
"indexed": true
},
{
"type": "bool",
"name": "_approved",
"indexed": false
}
],
"anonymous": false,
"type": "event"
},
{
"outputs": [],
"inputs": [],
"constant": false,
"payable": false,
"type": "constructor"
},
{
"name": "tokenURI",
"outputs": [
{
"type": "string",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "_tokenId"
}
],
"constant": true,
"payable": false,
"type": "function",
"gas": "22405"
},
{
"name": "tokenByIndex",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "_index"
}
],
"constant": true,
"payable": false,
"type": "function",
"gas": "631"
},
{
"name": "tokenOfOwnerByIndex",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "address",
"name": "_owner"
},
{
"type": "uint256",
"name": "_index"
}
],
"constant": true,
"payable": false,
"type": "function",
"gas": "1248"
},
{
"name": "transferFrom",
"outputs": [],
"inputs": [
{
"type": "address",
"name": "_from"
},
{
"type": "address",
"name": "_to"
},
{
"type": "uint256",
"name": "_tokenId"
}
],
"constant": false,
"payable": false,
"type": "function",
"gas": "259486"
},
{
"name": "safeTransferFrom",
"outputs": [],
"inputs": [
{
"type": "address",
"name": "_from"
},
{
"type": "address",
"name": "_to"
},
{
"type": "uint256",
"name": "_tokenId"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "safeTransferFrom",
"outputs": [],
"inputs": [
{
"type": "address",
"name": "_from"
},
{
"type": "address",
"name": "_to"
},
{
"type": "uint256",
"name": "_tokenId"
},
{
"type": "bytes",
"name": "_data"
}
],
"constant": false,
"payable": false,
"type": "function"
},
{
"name": "approve",
"outputs": [],
"inputs": [
{
"type": "address",
"name": "_approved"
},
{
"type": "uint256",
"name": "_tokenId"
}
],
"constant": false,
"payable": false,
"type": "function",
"gas": "38422"
},
{
"name": "setApprovalForAll",
"outputs": [],
"inputs": [
{
"type": "address",
"name": "_operator"
},
{
"type": "bool",
"name": "_approved"
}
],
"constant": false,
"payable": false,
"type": "function",
"gas": "38016"
},
{
"name": "mint",
"outputs": [
{
"type": "bool",
"name": "out"
}
],
"inputs": [
{
"type": "address",
"name": "_to"
}
],
"constant": false,
"payable": false,
"type": "function",
"gas": "182636"
},
{
"name": "changeMinter",
"outputs": [],
"inputs": [
{
"type": "address",
"name": "_minter"
}
],
"constant": false,
"payable": false,
"type": "function",
"gas": "35897"
},
{
"name": "changeURI",
"outputs": [],
"inputs": [
{
"type": "address",
"name": "_newURI"
}
],
"constant": false,
"payable": false,
"type": "function",
"gas": "35927"
},
{
"name": "name",
"outputs": [
{
"type": "string",
"name": "out"
}
],
"inputs": [],
"constant": true,
"payable": false,
"type": "function",
"gas": "6612"
},
{
"name": "symbol",
"outputs": [
{
"type": "string",
"name": "out"
}
],
"inputs": [],
"constant": true,
"payable": false,
"type": "function",
"gas": "6642"
},
{
"name": "totalSupply",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [],
"constant": true,
"payable": false,
"type": "function",
"gas": "873"
},
{
"name": "minter",
"outputs": [
{
"type": "address",
"name": "out"
}
],
"inputs": [],
"constant": true,
"payable": false,
"type": "function",
"gas": "903"
},
{
"name": "socks",
"outputs": [
{
"type": "address",
"name": "out",
"unit": "Socks"
}
],
"inputs": [],
"constant": true,
"payable": false,
"type": "function",
"gas": "933"
},
{
"name": "newURI",
"outputs": [
{
"type": "address",
"name": "out"
}
],
"inputs": [],
"constant": true,
"payable": false,
"type": "function",
"gas": "963"
},
{
"name": "ownerOf",
"outputs": [
{
"type": "address",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "arg0"
}
],
"constant": true,
"payable": false,
"type": "function",
"gas": "1126"
},
{
"name": "balanceOf",
"outputs": [
{
"type": "uint256",
"name": "out"
}
],
"inputs": [
{
"type": "address",
"name": "arg0"
}
],
"constant": true,
"payable": false,
"type": "function",
"gas": "1195"
},
{
"name": "getApproved",
"outputs": [
{
"type": "address",
"name": "out"
}
],
"inputs": [
{
"type": "uint256",
"name": "arg0"
}
],
"constant": true,
"payable": false,
"type": "function",
"gas": "1186"
},
{
"name": "isApprovedForAll",
"outputs": [
{
"type": "bool",
"name": "out"
}
],
"inputs": [
{
"type": "address",
"name": "arg0"
},
{
"type": "address",
"name": "arg1"
}
],
"constant": true,
"payable": false,
"type": "function",
"gas": "1415"
},
{
"name": "supportsInterface",
"outputs": [
{
"type": "bool",
"name": "out"
}
],
"inputs": [
{
"type": "bytes32",
"name": "arg0"
}
],
"constant": true,
"payable": false,
"type": "function",
"gas": "1246"
}
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 883 KiB

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 670 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 413 KiB

View 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

View 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

View File

@@ -4,6 +4,7 @@ import useCopyClipboard from '../../hooks/useCopyClipboard'
import { LinkStyledButton } from '../../theme'
import { CheckCircle, Copy } from 'react-feather'
import { Trans } from '@lingui/macro'
const CopyIcon = styled(LinkStyledButton)`
color: ${({ theme }) => theme.text3};
@@ -33,7 +34,9 @@ export default function CopyHelper(props: { toCopy: string; children?: React.Rea
{isCopied ? (
<TransactionStatusText>
<CheckCircle size={'16'} />
<TransactionStatusText>Copied</TransactionStatusText>
<TransactionStatusText>
<Trans>Copied</Trans>
</TransactionStatusText>
</TransactionStatusText>
) : (
<TransactionStatusText>

View File

@@ -1,11 +1,10 @@
import React from 'react'
import styled from 'styled-components/macro'
import { CheckCircle, Triangle } from 'react-feather'
import { useActiveWeb3React } from '../../hooks'
import { getEtherscanLink } from '../../utils'
import { useActiveWeb3React } from '../../hooks/web3'
import { ExternalLink } from '../../theme'
import { useAllTransactions } from '../../state/transactions/hooks'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { RowFixed } from '../Row'
import Loader from '../Loader'
@@ -49,7 +48,11 @@ export default function Transaction({ hash }: { hash: string }) {
return (
<TransactionWrapper>
<TransactionState href={getEtherscanLink(chainId, hash, 'transaction')} pending={pending} success={success}>
<TransactionState
href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}
pending={pending}
success={success}
>
<RowFixed>
<TransactionStatusText>{summary ?? hash} </TransactionStatusText>
</RowFixed>

View File

@@ -1,17 +1,15 @@
import React, { useCallback, useContext } from 'react'
import { useDispatch } from 'react-redux'
import styled, { ThemeContext } from 'styled-components'
import { useActiveWeb3React } from '../../hooks'
import { AppDispatch } from '../../state'
import { useCallback, useContext } from 'react'
import styled, { ThemeContext } from 'styled-components/macro'
import { SUPPORTED_WALLETS } from '../../constants/wallet'
import { useActiveWeb3React } from '../../hooks/web3'
import { clearAllTransactions } from '../../state/transactions/actions'
import { shortenAddress } from '../../utils'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { AutoRow } from '../Row'
import Copy from './Copy'
import Transaction from './Transaction'
import { SUPPORTED_WALLETS } from '../../constants'
import { ReactComponent as Close } from '../../assets/images/x.svg'
import { getEtherscanLink } from '../../utils'
import { injected, walletconnect, walletlink, fortmatic, portis } from '../../connectors'
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
@@ -21,6 +19,8 @@ import Identicon from '../Identicon'
import { ButtonSecondary } from '../Button'
import { ExternalLink as LinkIcon } from 'react-feather'
import { ExternalLink, LinkStyledButton, TYPE } from '../../theme'
import { Trans } from '@lingui/macro'
import { useAppDispatch } from 'state/hooks'
const HeaderRow = styled.div`
${({ theme }) => theme.flexRowNoWrap};
@@ -226,7 +226,7 @@ export default function AccountDetails({
}: AccountDetailsProps) {
const { chainId, account, connector } = useActiveWeb3React()
const theme = useContext(ThemeContext)
const dispatch = useDispatch<AppDispatch>()
const dispatch = useAppDispatch()
function formatConnectorName() {
const { ethereum } = window
@@ -237,7 +237,11 @@ export default function AccountDetails({
SUPPORTED_WALLETS[k].connector === connector && (connector !== injected || isMetaMask === (k === 'METAMASK'))
)
.map((k) => SUPPORTED_WALLETS[k].name)[0]
return <WalletName>Connected with {name}</WalletName>
return (
<WalletName>
<Trans>Connected with {name}</Trans>
</WalletName>
)
}
function getStatusIcon() {
@@ -250,32 +254,32 @@ export default function AccountDetails({
} else if (connector === walletconnect) {
return (
<IconWrapper size={16}>
<img src={WalletConnectIcon} alt={'wallet connect logo'} />
<img src={WalletConnectIcon} alt={'WalletConnect logo'} />
</IconWrapper>
)
} else if (connector === walletlink) {
return (
<IconWrapper size={16}>
<img src={CoinbaseWalletIcon} alt={'coinbase wallet logo'} />
<img src={CoinbaseWalletIcon} alt={'Coinbase Wallet logo'} />
</IconWrapper>
)
} else if (connector === fortmatic) {
return (
<IconWrapper size={16}>
<img src={FortmaticIcon} alt={'fortmatic logo'} />
<img src={FortmaticIcon} alt={'Fortmatic logo'} />
</IconWrapper>
)
} else if (connector === portis) {
return (
<>
<IconWrapper size={16}>
<img src={PortisIcon} alt={'portis logo'} />
<img src={PortisIcon} alt={'Portis logo'} />
<MainWalletAction
onClick={() => {
portis.portis.showPortis()
}}
>
Show Portis
<Trans>Show Portis</Trans>
</MainWalletAction>
</IconWrapper>
</>
@@ -294,7 +298,9 @@ export default function AccountDetails({
<CloseIcon onClick={toggleWalletModal}>
<CloseColor />
</CloseIcon>
<HeaderRow>Account</HeaderRow>
<HeaderRow>
<Trans>Account</Trans>
</HeaderRow>
<AccountSection>
<YourAccount>
<InfoCard>
@@ -308,7 +314,7 @@ export default function AccountDetails({
;(connector as any).close()
}}
>
Disconnect
<Trans>Disconnect</Trans>
</WalletAction>
)}
<WalletAction
@@ -317,7 +323,7 @@ export default function AccountDetails({
openOptions()
}}
>
Change
<Trans>Change</Trans>
</WalletAction>
</div>
</AccountGroupingRow>
@@ -347,17 +353,21 @@ export default function AccountDetails({
<div>
{account && (
<Copy toCopy={account}>
<span style={{ marginLeft: '4px' }}>Copy Address</span>
<span style={{ marginLeft: '4px' }}>
<Trans>Copy Address</Trans>
</span>
</Copy>
)}
{chainId && account && (
<AddressLink
hasENS={!!ENSName}
isENS={true}
href={chainId && getEtherscanLink(chainId, ENSName, 'address')}
href={getExplorerLink(chainId, ENSName, ExplorerDataType.ADDRESS)}
>
<LinkIcon size={16} />
<span style={{ marginLeft: '4px' }}>View on Etherscan</span>
<span style={{ marginLeft: '4px' }}>
<Trans>View on Explorer</Trans>
</span>
</AddressLink>
)}
</div>
@@ -369,17 +379,21 @@ export default function AccountDetails({
<div>
{account && (
<Copy toCopy={account}>
<span style={{ marginLeft: '4px' }}>Copy Address</span>
<span style={{ marginLeft: '4px' }}>
<Trans>Copy Address</Trans>
</span>
</Copy>
)}
{chainId && account && (
<AddressLink
hasENS={!!ENSName}
isENS={false}
href={getEtherscanLink(chainId, account, 'address')}
href={getExplorerLink(chainId, account, ExplorerDataType.ADDRESS)}
>
<LinkIcon size={16} />
<span style={{ marginLeft: '4px' }}>View on Etherscan</span>
<span style={{ marginLeft: '4px' }}>
<Trans>View on Explorer</Trans>
</span>
</AddressLink>
)}
</div>
@@ -394,15 +408,21 @@ export default function AccountDetails({
{!!pendingTransactions.length || !!confirmedTransactions.length ? (
<LowerSection>
<AutoRow mb={'1rem'} style={{ justifyContent: 'space-between' }}>
<TYPE.body>Recent Transactions</TYPE.body>
<LinkStyledButton onClick={clearAllTransactionsCallback}>(clear all)</LinkStyledButton>
<TYPE.body>
<Trans>Recent Transactions</Trans>
</TYPE.body>
<LinkStyledButton onClick={clearAllTransactionsCallback}>
<Trans>(clear all)</Trans>
</LinkStyledButton>
</AutoRow>
{renderTransactions(pendingTransactions)}
{renderTransactions(confirmedTransactions)}
</LowerSection>
) : (
<LowerSection>
<TYPE.body color={theme.text1}>Your transactions will appear here...</TYPE.body>
<TYPE.body color={theme.text1}>
<Trans>Your transactions will appear here...</Trans>
</TYPE.body>
</LowerSection>
)}
</>

View File

@@ -1,11 +1,12 @@
import React, { useContext, useCallback } from 'react'
import styled, { ThemeContext } from 'styled-components'
import { t, Trans } from '@lingui/macro'
import { useContext, useCallback, ReactNode } from 'react'
import styled, { ThemeContext } from 'styled-components/macro'
import useENS from '../../hooks/useENS'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { ExternalLink, TYPE } from '../../theme'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { AutoColumn } from '../Column'
import { RowBetween } from '../Row'
import { getEtherscanLink } from '../../utils'
const InputPanel = styled.div`
${({ theme }) => theme.flexColumnNoWrap}
@@ -40,7 +41,7 @@ const Input = styled.input<{ error?: boolean }>`
width: 0;
background-color: ${({ theme }) => theme.bg1};
transition: color 300ms ${({ error }) => (error ? 'step-end' : 'step-start')};
color: ${({ error, theme }) => (error ? theme.red1 : theme.primary1)};
color: ${({ error, theme }) => (error ? theme.red1 : theme.text1)};
overflow: hidden;
text-overflow: ellipsis;
font-weight: 500;
@@ -67,10 +68,16 @@ const Input = styled.input<{ error?: boolean }>`
export default function AddressInputPanel({
id,
className = 'recipient-address-input',
label,
placeholder,
value,
onChange,
}: {
id?: string
className?: string
label?: ReactNode
placeholder?: string
// the typed string value
value: string
// triggers whenever the typed value changes
@@ -99,22 +106,25 @@ export default function AddressInputPanel({
<AutoColumn gap="md">
<RowBetween>
<TYPE.black color={theme.text2} fontWeight={500} fontSize={14}>
Recipient
{label ?? <Trans>Recipient</Trans>}
</TYPE.black>
{address && chainId && (
<ExternalLink href={getEtherscanLink(chainId, name ?? address, 'address')} style={{ fontSize: '14px' }}>
(View on Etherscan)
<ExternalLink
href={getExplorerLink(chainId, name ?? address, ExplorerDataType.ADDRESS)}
style={{ fontSize: '14px' }}
>
<Trans>(View on Explorer)</Trans>
</ExternalLink>
)}
</RowBetween>
<Input
className="recipient-address-input"
className={className}
type="text"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
placeholder="Wallet Address or ENS name"
placeholder={placeholder ?? t`Wallet Address or ENS name`}
error={error}
pattern="^(0x[a-fA-F0-9]{40})$"
onChange={handleInput}

View File

@@ -1,44 +0,0 @@
import { Story } from '@storybook/react/types-6-0'
import React, { PropsWithChildren } from 'react'
import Component, { BadgeProps, BadgeVariant } from './index'
export default {
title: 'Badge',
argTypes: {
variant: {
name: 'variant',
type: { name: 'string', require: false },
defaultValue: BadgeVariant.DEFAULT,
description: 'badge variant',
control: {
type: 'select',
options: Object.values(BadgeVariant),
},
},
},
args: {
children: '🦄 UNISWAP 🦄',
},
}
const Template: Story<PropsWithChildren<BadgeProps>> = (args) => <Component {...args}>{args.children}</Component>
export const DefaultBadge = Template.bind({})
DefaultBadge.args = {
variant: BadgeVariant.DEFAULT,
}
export const WarningBadge = Template.bind({})
WarningBadge.args = {
variant: BadgeVariant.WARNING,
}
export const NegativeBadge = Template.bind({})
NegativeBadge.args = {
variant: BadgeVariant.NEGATIVE,
}
export const PositiveBadge = Template.bind({})
PositiveBadge.args = {
variant: BadgeVariant.POSITIVE,
}

View File

@@ -1,10 +1,8 @@
import React from 'react'
import Badge, { BadgeVariant } from 'components/Badge'
import styled from 'styled-components/macro'
import { MouseoverTooltip } from '../../components/Tooltip'
import { useTranslation } from 'react-i18next'
import { Trans } from '@lingui/macro'
import { AlertCircle } from 'react-feather'
const BadgeWrapper = styled.div`
@@ -26,13 +24,6 @@ const ActiveDot = styled.span`
margin-right: 4px;
`
export const DarkBadge = styled.div`
width: fit-content;
border-radius: 8px;
background-color: ${({ theme }) => theme.bg0};
padding: 4px 6px;
`
export default function RangeBadge({
removed,
inRange,
@@ -40,35 +31,47 @@ export default function RangeBadge({
removed: boolean | undefined
inRange: boolean | undefined
}) {
const { t } = useTranslation()
return (
<BadgeWrapper>
{removed ? (
<MouseoverTooltip text={`Your position has 0 liquidity, and is not earning fees.`}>
<MouseoverTooltip text={<Trans>Your position has 0 liquidity, and is not earning fees.</Trans>}>
<Badge variant={BadgeVariant.DEFAULT}>
<AlertCircle width={14} height={14} />
&nbsp;
<BadgeText>{t('Inactive')}</BadgeText>
<BadgeText>
<Trans>Closed</Trans>
</BadgeText>
</Badge>
</MouseoverTooltip>
) : inRange ? (
<MouseoverTooltip
text={`The price of this pool is within your selected range. Your position is currently earning fees.`}
text={
<Trans>
The price of this pool is within your selected range. Your position is currently earning fees.
</Trans>
}
>
<Badge variant={BadgeVariant.DEFAULT}>
<ActiveDot /> &nbsp;
<BadgeText>{t('In range')}</BadgeText>
<BadgeText>
<Trans>In range</Trans>
</BadgeText>
</Badge>
</MouseoverTooltip>
) : (
<MouseoverTooltip
text={`The price of this pool is outside of your selected range. Your position is not currently earning fees.`}
text={
<Trans>
The price of this pool is outside of your selected range. Your position is not currently earning fees.
</Trans>
}
>
<Badge variant={BadgeVariant.WARNING}>
<AlertCircle width={14} height={14} />
&nbsp;
<BadgeText>{t('Out of range')}</BadgeText>
<BadgeText>
<Trans>Out of range</Trans>
</BadgeText>
</Badge>
</MouseoverTooltip>
)}

View File

@@ -1,6 +1,6 @@
import { readableColor } from 'polished'
import { PropsWithChildren } from 'react'
import styled, { DefaultTheme } from 'styled-components'
import styled, { DefaultTheme } from 'styled-components/macro'
import { Color } from 'theme/styled'
export enum BadgeVariant {
@@ -13,7 +13,7 @@ export enum BadgeVariant {
WARNING_OUTLINE = 'WARNING_OUTLINE',
}
export interface BadgeProps {
interface BadgeProps {
variant?: BadgeVariant
}

View File

@@ -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}</>
}

View File

@@ -1,153 +0,0 @@
import { Story } from '@storybook/react/types-6-0'
import styled from 'styled-components/macro'
import React from 'react'
import {
ButtonConfirmed,
ButtonDropdown,
ButtonDropdownGrey,
ButtonDropdownLight,
ButtonEmpty,
ButtonError,
ButtonGray,
ButtonLight,
ButtonOutlined,
ButtonPink,
ButtonPrimary,
ButtonRadio,
ButtonSecondary,
ButtonUNIGradient,
ButtonWhite,
} from './index'
const wrapperCss = styled.main`
font-size: 2em;
margin: 3em;
max-width: 300px;
`
export default {
title: 'Buttons',
argTypes: {
disabled: { control: { type: 'boolean' } },
onClick: { action: 'clicked' },
},
decorators: [
(Component: Story) => (
<div css={wrapperCss}>
<Component />
</div>
),
],
}
const Unicorn = () => (
<span role="img" aria-label="unicorn">
🦄
</span>
)
export const Radio = () => (
<ButtonRadio>
<Unicorn />
&nbsp;UNISWAP&nbsp;
<Unicorn />
</ButtonRadio>
)
export const DropdownLight = () => (
<ButtonDropdownLight>
<Unicorn />
&nbsp;UNISWAP&nbsp;
<Unicorn />
</ButtonDropdownLight>
)
export const DropdownGrey = () => (
<ButtonDropdownGrey>
<Unicorn />
&nbsp;UNISWAP&nbsp;
<Unicorn />
</ButtonDropdownGrey>
)
export const Dropdown = () => (
<ButtonDropdown>
<Unicorn />
&nbsp;UNISWAP&nbsp;
<Unicorn />
</ButtonDropdown>
)
export const Error = () => (
<ButtonError>
<Unicorn />
&nbsp;UNISWAP&nbsp;
<Unicorn />
</ButtonError>
)
export const Confirmed = () => (
<ButtonConfirmed>
<Unicorn />
&nbsp;UNISWAP&nbsp;
<Unicorn />
</ButtonConfirmed>
)
export const White = () => (
<ButtonWhite>
<Unicorn />
&nbsp;UNISWAP&nbsp;
<Unicorn />
</ButtonWhite>
)
export const Empty = () => (
<ButtonEmpty>
<Unicorn />
&nbsp;UNISWAP&nbsp;
<Unicorn />
</ButtonEmpty>
)
export const Outlined = () => (
<ButtonOutlined>
<Unicorn />
&nbsp;UNISWAP&nbsp;
<Unicorn />
</ButtonOutlined>
)
export const UNIGradient = () => (
<ButtonUNIGradient>
<Unicorn />
&nbsp;UNISWAP&nbsp;
<Unicorn />
</ButtonUNIGradient>
)
export const Pink = () => (
<ButtonPink>
<Unicorn />
&nbsp;UNISWAP&nbsp;
<Unicorn />
</ButtonPink>
)
export const Secondary = () => (
<ButtonSecondary>
<Unicorn />
&nbsp;UNISWAP&nbsp;
<Unicorn />
</ButtonSecondary>
)
export const Gray = () => (
<ButtonGray>
<Unicorn />
&nbsp;UNISWAP&nbsp;
<Unicorn />
</ButtonGray>
)
export const Light = () => (
<ButtonLight>
<Unicorn />
&nbsp;UNISWAP&nbsp;
<Unicorn />
</ButtonLight>
)
export const Primary = () => (
<ButtonPrimary>
<Unicorn />
&nbsp;UNISWAP&nbsp;
<Unicorn />
</ButtonPrimary>
)

View File

@@ -1,24 +1,26 @@
import React from 'react'
import styled from 'styled-components/macro'
import { darken } from 'polished'
import { RowBetween } from '../Row'
import { ChevronDown, Check } from 'react-feather'
import { Button as RebassButton, ButtonProps } from 'rebass/styled-components'
import { Button as RebassButton, ButtonProps as ButtonPropsOriginal } from 'rebass/styled-components'
import useTheme from 'hooks/useTheme'
const Base = styled(RebassButton)<{
padding?: string
width?: string
borderRadius?: string
altDisabledStyle?: boolean
}>`
padding: ${({ padding }) => (padding ? padding : '16px')};
width: ${({ width }) => (width ? width : '100%')};
type ButtonProps = Omit<ButtonPropsOriginal, 'css'>
const Base = styled(RebassButton)<
{
padding?: string
width?: string
$borderRadius?: string
altDisabledStyle?: boolean
} & ButtonProps
>`
padding: ${({ padding }) => padding ?? '16px'};
width: ${({ width }) => width ?? '100%'};
font-weight: 500;
text-align: center;
border-radius: 20px;
border-radius: ${({ borderRadius }) => borderRadius && borderRadius};
border-radius: ${({ $borderRadius }) => $borderRadius ?? '20px'};
outline: none;
border: 1px solid transparent;
color: white;
@@ -68,14 +70,12 @@ export const ButtonPrimary = styled(Base)`
}
&:disabled {
background-color: ${({ theme, altDisabledStyle, disabled }) =>
altDisabledStyle ? (disabled ? theme.primary1 : theme.primary1) : theme.primary1};
color: white;
altDisabledStyle ? (disabled ? theme.primary1 : theme.bg2) : theme.bg2};
color: ${({ theme }) => theme.text2};
cursor: auto;
box-shadow: none;
border: 1px solid transparent;
outline: none;
opacity: 0.4;
opacity: ${({ altDisabledStyle }) => (altDisabledStyle ? '0.5' : '0.4')};
}
`
@@ -112,9 +112,7 @@ export const ButtonGray = styled(Base)`
color: ${({ theme }) => theme.text2};
font-size: 16px;
font-weight: 500;
&:focus {
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
}
&:hover {
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
}
@@ -151,48 +149,6 @@ export const ButtonSecondary = styled(Base)`
}
`
export const ButtonPink = styled(Base)`
background-color: ${({ theme }) => theme.primary1};
color: white;
&:focus {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.primary1)};
background-color: ${({ theme }) => darken(0.05, theme.primary1)};
}
&:hover {
background-color: ${({ theme }) => darken(0.05, theme.primary1)};
}
&:active {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.primary1)};
background-color: ${({ theme }) => darken(0.1, theme.primary1)};
}
&:disabled {
background-color: ${({ theme }) => theme.primary1};
opacity: 50%;
cursor: auto;
}
`
export const ButtonUNIGradient = styled(ButtonPrimary)`
color: white;
padding: 4px 8px;
height: 36px;
font-weight: 500;
background-color: ${({ theme }) => theme.bg3};
background: radial-gradient(174.47% 188.91% at 1.84% 0%, #ff007a 0%, #2172e5 100%), #edeef2;
width: fit-content;
position: relative;
cursor: pointer;
border: none;
white-space: no-wrap;
:hover {
opacity: 0.8;
}
:active {
opacity: 0.9;
}
`
export const ButtonOutlined = styled(Base)`
border: 1px solid ${({ theme }) => theme.bg2};
background-color: transparent;
@@ -257,27 +213,6 @@ export const ButtonText = styled(Base)`
}
`
export const ButtonWhite = styled(Base)`
border: 1px solid #edeef2;
background-color: ${({ theme }) => theme.bg1};
color: black;
&:focus {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
box-shadow: 0 0 0 1pt ${darken(0.05, '#edeef2')};
}
&:hover {
box-shadow: 0 0 0 1pt ${darken(0.1, '#edeef2')};
}
&:active {
box-shadow: 0 0 0 1pt ${darken(0.1, '#edeef2')};
}
&:disabled {
opacity: 50%;
cursor: auto;
}
`
const ButtonConfirmedStyle = styled(Base)`
background-color: ${({ theme }) => theme.bg3};
color: ${({ theme }) => theme.text1};
@@ -346,17 +281,6 @@ export function ButtonDropdown({ disabled = false, children, ...rest }: { disabl
)
}
export function ButtonDropdownGrey({ disabled = false, children, ...rest }: { disabled?: boolean } & ButtonProps) {
return (
<ButtonGray {...rest} disabled={disabled} style={{ borderRadius: '20px' }}>
<RowBetween>
<div style={{ display: 'flex', alignItems: 'center' }}>{children}</div>
<ChevronDown size={24} />
</RowBetween>
</ButtonGray>
)
}
export function ButtonDropdownLight({ disabled = false, children, ...rest }: { disabled?: boolean } & ButtonProps) {
return (
<ButtonOutlined {...rest} disabled={disabled}>
@@ -368,14 +292,6 @@ export function ButtonDropdownLight({ disabled = false, children, ...rest }: { d
)
}
export function ButtonRadio({ active, ...rest }: { active?: boolean } & ButtonProps) {
if (!active) {
return <ButtonWhite {...rest} />
} else {
return <ButtonPrimary {...rest} />
}
}
const ActiveOutlined = styled(ButtonOutlined)`
border: 1px solid;
border-color: ${({ theme }) => theme.primary1};
@@ -408,13 +324,13 @@ export function ButtonRadioChecked({ active = false, children, ...rest }: { acti
if (!active) {
return (
<ButtonOutlined borderRadius="12px" padding="12px 8px" {...rest}>
<ButtonOutlined $borderRadius="12px" padding="12px 8px" {...rest}>
{<RowBetween>{children}</RowBetween>}
</ButtonOutlined>
)
} else {
return (
<ActiveOutlined {...rest} padding="12px 8px" borderRadius="12px">
<ActiveOutlined {...rest} padding="12px 8px" $borderRadius="12px">
{
<RowBetween>
{children}

View File

@@ -1,13 +1,11 @@
import styled from 'styled-components/macro'
import { Box } from 'rebass/styled-components'
const Card = styled(Box)<{ width?: string; padding?: string; border?: string; borderRadius?: string }>`
const Card = styled(Box)<{ width?: string; padding?: string; border?: string; $borderRadius?: string }>`
width: ${({ width }) => width ?? '100%'};
border-radius: 16px;
padding: 1rem;
padding: ${({ padding }) => padding};
padding: ${({ padding }) => padding ?? '1rem'};
border-radius: ${({ $borderRadius }) => $borderRadius ?? '16px'};
border: ${({ border }) => border};
border-radius: ${({ borderRadius }) => borderRadius};
`
export default Card
@@ -42,12 +40,6 @@ export const YellowCard = styled(Card)`
font-weight: 500;
`
export const PinkCard = styled(Card)`
background-color: rgba(255, 0, 122, 0.03);
color: ${({ theme }) => theme.primary1};
font-weight: 500;
`
export const BlueCard = styled(Card)`
background-color: ${({ theme }) => theme.primary5};
color: ${({ theme }) => theme.blue2};

View File

@@ -1,4 +1,3 @@
import React from 'react'
import ReactConfetti from 'react-confetti'
import { useWindowSize } from '../../hooks/useWindowSize'

View File

@@ -1,9 +1,10 @@
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
import React, { useMemo } from 'react'
import { useMemo } from 'react'
import useTheme from '../../hooks/useTheme'
import { TYPE } from '../../theme'
import { warningSeverity } from '../../utils/prices'
import HoverInlineText from 'components/HoverInlineText'
import { Trans } from '@lingui/macro'
export function FiatValue({
fiatValue,
@@ -17,17 +18,25 @@ export function FiatValue({
if (!priceImpact) return undefined
if (priceImpact.lessThan('0')) return theme.green1
const severity = warningSeverity(priceImpact)
if (severity < 1) return theme.text4
if (severity < 1) return theme.text3
if (severity < 3) return theme.yellow1
return theme.red1
}, [priceImpact, theme.green1, theme.red1, theme.text4, theme.yellow1])
}, [priceImpact, theme.green1, theme.red1, theme.text3, theme.yellow1])
return (
<TYPE.body fontSize={14} color={fiatValue ? theme.text2 : theme.text4}>
{fiatValue ? '~' : ''}$
<HoverInlineText text={fiatValue ? fiatValue?.toSignificant(6, { groupSeparator: ',' }) : '-'} />{' '}
{fiatValue ? (
<Trans>
~$ <HoverInlineText text={fiatValue?.toSignificant(6, { groupSeparator: ',' })} />
</Trans>
) : (
''
)}
{priceImpact ? (
<span style={{ color: priceImpactColor }}> ({priceImpact.multiply(-1).toSignificant(3)}%)</span>
<span style={{ color: priceImpactColor }}>
{' '}
(<Trans>{priceImpact.multiply(-1).toSignificant(3)}%</Trans>)
</span>
) : null}
</TYPE.body>
)

View File

@@ -1,6 +1,6 @@
import { Pair } from '@uniswap/v2-sdk'
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
import React, { useState, useCallback } from 'react'
import { useState, useCallback, ReactNode } from 'react'
import styled from 'styled-components/macro'
import { darken } from 'polished'
import { useCurrencyBalance } from '../../state/wallet/hooks'
@@ -12,13 +12,13 @@ import { RowBetween, RowFixed } from '../Row'
import { TYPE } from '../../theme'
import { Input as NumericalInput } from '../NumericalInput'
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
import { useActiveWeb3React } from '../../hooks'
import { useTranslation } from 'react-i18next'
import { useActiveWeb3React } from '../../hooks/web3'
import { Trans } from '@lingui/macro'
import useTheme from '../../hooks/useTheme'
import { Lock } from 'react-feather'
import { AutoColumn } from 'components/Column'
import { FiatValue } from './FiatValue'
import { formatTokenAmount } from 'utils/formatTokenAmount'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
const InputPanel = styled.div<{ hideInput?: boolean }>`
${({ theme }) => theme.flexColumnNoWrap}
@@ -130,7 +130,7 @@ const StyledBalanceMax = styled.button<{ disabled?: boolean }>`
font-weight: 500;
cursor: pointer;
padding: 0;
color: ${({ theme }) => theme.primary1};
color: ${({ theme }) => theme.primaryText1};
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')};
margin-left: 0.25rem;
@@ -149,7 +149,7 @@ interface CurrencyInputPanelProps {
onUserInput: (value: string) => void
onMax?: () => void
showMaxButton: boolean
label?: string
label?: ReactNode
onCurrencySelect?: (currency: Currency) => void
currency?: Currency | null
hideBalance?: boolean
@@ -160,7 +160,9 @@ interface CurrencyInputPanelProps {
priceImpact?: Percent
id: string
showCommonBases?: boolean
customBalanceText?: string
showCurrencyAmount?: boolean
disableNonToken?: boolean
renderBalance?: (amount: CurrencyAmount<Currency>) => ReactNode
locked?: boolean
}
@@ -174,7 +176,9 @@ export default function CurrencyInputPanel({
otherCurrency,
id,
showCommonBases,
customBalanceText,
showCurrencyAmount,
disableNonToken,
renderBalance,
fiatValue,
priceImpact,
hideBalance = false,
@@ -183,8 +187,6 @@ export default function CurrencyInputPanel({
locked = false,
...rest
}: CurrencyInputPanelProps) {
const { t } = useTranslation()
const [modalOpen, setModalOpen] = useState(false)
const { account } = useActiveWeb3React()
const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined)
@@ -200,8 +202,8 @@ export default function CurrencyInputPanel({
<FixedContainer>
<AutoColumn gap="sm" justify="center">
<Lock />
<TYPE.label fontSize="12px" textAlign="center">
The market price is outside your specified price range. Single-asset deposit only.
<TYPE.label fontSize="12px" textAlign="center" padding="0 12px">
<Trans>The market price is outside your specified price range. Single-asset deposit only.</Trans>
</TYPE.label>
</AutoColumn>
</FixedContainer>
@@ -237,7 +239,7 @@ export default function CurrencyInputPanel({
? currency.symbol.slice(0, 4) +
'...' +
currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length)
: currency?.symbol) || t('selectToken')}
: currency?.symbol) || <Trans>Select a token</Trans>}
</StyledTokenName>
)}
</RowFixed>
@@ -268,19 +270,24 @@ export default function CurrencyInputPanel({
fontSize={14}
style={{ display: 'inline', cursor: 'pointer' }}
>
{!hideBalance && !!currency && selectedCurrencyBalance
? (customBalanceText ?? 'Balance: ') +
formatTokenAmount(selectedCurrencyBalance, 4) +
' ' +
currency.symbol
: '-'}
{!hideBalance && currency && selectedCurrencyBalance ? (
renderBalance ? (
renderBalance(selectedCurrencyBalance)
) : (
<Trans>
Balance: {formatCurrencyAmount(selectedCurrencyBalance, 4)} {currency.symbol}
</Trans>
)
) : null}
</TYPE.body>
{showMaxButton && selectedCurrencyBalance ? (
<StyledBalanceMax onClick={onMax}>(Max)</StyledBalanceMax>
<StyledBalanceMax onClick={onMax}>
<Trans>(Max)</Trans>
</StyledBalanceMax>
) : null}
</RowFixed>
) : (
'-'
<span />
)}
<FiatValue fiatValue={fiatValue} priceImpact={priceImpact} />
</RowBetween>
@@ -295,6 +302,8 @@ export default function CurrencyInputPanel({
selectedCurrency={currency}
otherSelectedCurrency={otherCurrency}
showCommonBases={showCommonBases}
showCurrencyAmount={showCurrencyAmount}
disableNonToken={disableNonToken}
/>
)}
</InputPanel>

View File

@@ -1,4 +1,4 @@
import { ChainId, Currency } from '@uniswap/sdk-core'
import { Currency } from '@uniswap/sdk-core'
import React, { useMemo } from 'react'
import styled from 'styled-components/macro'
import EthereumLogo from '../../assets/images/ethereum-logo.png'
@@ -7,7 +7,7 @@ import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
import Logo from '../Logo'
export const getTokenLogoURL = (address: string) =>
`https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${address}/logo.png`
`https://raw.githubusercontent.com/uniswap/assets/master/blockchains/ethereum/assets/${address}/logo.png`
const StyledEthereumLogo = styled.img<{ size: string }>`
width: ${({ size }) => size};
@@ -37,10 +37,10 @@ export default function CurrencyLogo({
const uriLocations = useHttpLocations(currency instanceof WrappedTokenInfo ? currency.logoURI : undefined)
const srcs: string[] = useMemo(() => {
if (!currency || currency.isEther) return []
if (!currency || currency.isNative) return []
if (currency.isToken) {
const defaultUrls = currency.chainId === ChainId.MAINNET ? [getTokenLogoURL(currency.address)] : []
const defaultUrls = currency.chainId === 1 ? [getTokenLogoURL(currency.address)] : []
if (currency instanceof WrappedTokenInfo) {
return [...uriLocations, ...defaultUrls]
}
@@ -49,7 +49,7 @@ export default function CurrencyLogo({
return []
}, [currency, uriLocations])
if (currency?.isEther) {
if (currency?.isNative) {
return <StyledEthereumLogo src={EthereumLogo} size={size} style={style} {...rest} />
}

View File

@@ -1,18 +0,0 @@
import { Story } from '@storybook/react/types-6-0'
import React from 'react'
import { DAI, WBTC } from '../../constants'
import Component, { DoubleCurrencyLogoProps } from './index'
export default {
title: 'DoubleCurrencyLogo',
decorators: [],
}
const Template: Story<DoubleCurrencyLogoProps> = (args) => <Component {...args} />
export const DoubleCurrencyLogo = Template.bind({})
DoubleCurrencyLogo.args = {
currency0: DAI,
currency1: WBTC,
size: 220,
}

View File

@@ -1,5 +1,4 @@
import { Currency } from '@uniswap/sdk-core'
import React from 'react'
import styled from 'styled-components/macro'
import CurrencyLogo from '../CurrencyLogo'
@@ -10,7 +9,7 @@ const Wrapper = styled.div<{ margin: boolean; sizeraw: number }>`
margin-left: ${({ sizeraw, margin }) => margin && (sizeraw / 3 + 8).toString() + 'px'};
`
export interface DoubleCurrencyLogoProps {
interface DoubleCurrencyLogoProps {
margin?: boolean
size?: number
currency0?: Currency

View File

@@ -1,6 +1,7 @@
import { Trans } from '@lingui/macro'
import React, { ErrorInfo } from 'react'
import store, { AppState } from '../../state'
import { ExternalLink, ThemedBackground, TYPE } from '../../theme'
import { ExternalLink, TYPE } from '../../theme'
import { AutoColumn } from '../Column'
import styled from 'styled-components/macro'
import ReactGA from 'react-ga'
@@ -69,12 +70,11 @@ export default class ErrorBoundary extends React.Component<unknown, ErrorBoundar
const encodedBody = encodeURIComponent(issueBody(error))
return (
<FallbackWrapper>
<ThemedBackground />
<BodyWrapper>
<AutoColumn gap={'md'}>
<SomethingWentWrongWrapper>
<TYPE.label fontSize={24} fontWeight={600}>
Something went wrong
<Trans>Something went wrong</Trans>
</TYPE.label>
</SomethingWentWrongWrapper>
<CodeBlockWrapper>
@@ -92,7 +92,7 @@ export default class ErrorBoundary extends React.Component<unknown, ErrorBoundar
target="_blank"
>
<TYPE.link fontSize={16}>
Create an issue on GitHub
<Trans>Create an issue on GitHub</Trans>
<span></span>
</TYPE.link>
</ExternalLink>
@@ -100,7 +100,7 @@ export default class ErrorBoundary extends React.Component<unknown, ErrorBoundar
<LinkWrapper>
<ExternalLink id="get-support-on-discord" href="https://discord.gg/FCfyBSbCU5" target="_blank">
<TYPE.link fontSize={16}>
Get support on Discord
<Trans>Get support on Discord</Trans>
<span></span>
</TYPE.link>
</ExternalLink>

View File

@@ -1,75 +1,228 @@
import React from 'react'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { FeeAmount } from '@uniswap/v3-sdk'
import { useTranslation } from 'react-i18next'
import { Token } from '@uniswap/sdk-core'
import { Trans } from '@lingui/macro'
import { AutoColumn } from 'components/Column'
import { DynamicSection } from 'pages/AddLiquidity/styled'
import { TYPE } from 'theme'
import { RowBetween } from 'components/Row'
import { ButtonRadioChecked } from 'components/Button'
import styled from 'styled-components/macro'
import { ButtonGray, ButtonRadioChecked } from 'components/Button'
import styled, { keyframes } from 'styled-components/macro'
import Badge from 'components/Badge'
import Card from 'components/Card'
import usePrevious from 'hooks/usePrevious'
import { useFeeTierDistribution } from 'hooks/useFeeTierDistribution'
import ReactGA from 'react-ga'
import { Box } from 'rebass'
const pulse = (color: string) => keyframes`
0% {
box-shadow: 0 0 0 0 ${color};
}
70% {
box-shadow: 0 0 0 2px ${color};
}
100% {
box-shadow: 0 0 0 0 ${color};
}
`
const ResponsiveText = styled(TYPE.label)`
line-height: 16px;
${({ theme }) => theme.mediaWidth.upToSmall`
font-size: 12px;
line-height: 12px;
`};
`
const FocusedOutlineCard = styled(Card)<{ pulsing: boolean }>`
border: 1px solid ${({ theme }) => theme.bg2};
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.primary1)} 0.6s linear;
`
const FeeAmountLabel = {
[FeeAmount.LOW]: {
label: '0.05',
description: <Trans>Best for stable pairs.</Trans>,
},
[FeeAmount.MEDIUM]: {
label: '0.3',
description: <Trans>Best for most pairs.</Trans>,
},
[FeeAmount.HIGH]: {
label: '1',
description: <Trans>Best for exotic pairs.</Trans>,
},
}
const FeeTierPercentageBadge = ({ percentage }: { percentage: number | undefined }) => {
return (
<Badge>
<TYPE.label fontSize={12}>
{Boolean(percentage) ? <Trans>{percentage?.toFixed(0)}% select</Trans> : <Trans>Not created</Trans>}
</TYPE.label>
</Badge>
)
}
export default function FeeSelector({
disabled = false,
feeAmount,
handleFeePoolSelect,
token0,
token1,
}: {
disabled?: boolean
feeAmount?: FeeAmount
handleFeePoolSelect: (feeAmount: FeeAmount) => void
token0?: Token | undefined
token1?: Token | undefined
}) {
const { t } = useTranslation()
const { isLoading, isError, largestUsageFeeTier, distributions } = useFeeTierDistribution(token0, token1)
const [showOptions, setShowOptions] = useState(false)
const [pulsing, setPulsing] = useState(false)
const previousFeeAmount = usePrevious(feeAmount)
const recommended = useRef(false)
const handleFeePoolSelectWithEvent = useCallback(
(fee) => {
ReactGA.event({
category: 'FeePoolSelect',
action: 'Manual',
})
handleFeePoolSelect(fee)
},
[handleFeePoolSelect]
)
useEffect(() => {
if (feeAmount || isLoading || isError) {
return
}
if (!largestUsageFeeTier) {
// cannot recommend, open options
setShowOptions(true)
} else {
setShowOptions(false)
recommended.current = true
ReactGA.event({
category: 'FeePoolSelect',
action: ' Recommended',
})
handleFeePoolSelect(largestUsageFeeTier)
}
}, [feeAmount, isLoading, isError, largestUsageFeeTier, handleFeePoolSelect])
useEffect(() => {
setShowOptions(isError)
}, [isError])
useEffect(() => {
if (feeAmount && previousFeeAmount !== feeAmount) {
setPulsing(true)
}
}, [previousFeeAmount, feeAmount])
return (
<AutoColumn gap="16px">
<DynamicSection gap="md" disabled={disabled}>
<TYPE.label>{t('selectPool')}</TYPE.label>
<TYPE.main fontSize={14} fontWeight={400} style={{ marginBottom: '.5rem', lineHeight: '125%' }}>
Select a pool type based on your preferred liquidity provider fee.
</TYPE.main>
<RowBetween>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.LOW}
onClick={() => handleFeePoolSelect(FeeAmount.LOW)}
>
<AutoColumn gap="sm" justify="flex-start">
<ResponsiveText>0.05% {t('fee')}</ResponsiveText>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
Best for stable pairs.
</TYPE.main>
<FocusedOutlineCard pulsing={pulsing} onAnimationEnd={() => setPulsing(false)}>
<RowBetween>
<AutoColumn>
{!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>
<Trans>{FeeAmountLabel[feeAmount].label}% fee tier</Trans>
</TYPE.label>
<Box style={{ width: 'fit-content', marginTop: '8px' }}>
{distributions && feeAmount && <FeeTierPercentageBadge percentage={distributions[feeAmount]} />}
</Box>
</>
)}
</AutoColumn>
</ButtonRadioChecked>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.MEDIUM}
onClick={() => handleFeePoolSelect(FeeAmount.MEDIUM)}
>
<AutoColumn gap="sm" justify="flex-start">
<ResponsiveText>0.3% {t('fee')}</ResponsiveText>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
Best for most pairs.
</TYPE.main>
</AutoColumn>
</ButtonRadioChecked>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.HIGH}
onClick={() => handleFeePoolSelect(FeeAmount.HIGH)}
>
<AutoColumn gap="sm" justify="flex-start">
<ResponsiveText>1% {t('fee')}</ResponsiveText>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
Best for exotic pairs.
</TYPE.main>
</AutoColumn>
</ButtonRadioChecked>
</RowBetween>
<ButtonGray onClick={() => setShowOptions(!showOptions)} width="auto" padding="4px" $borderRadius="6px">
{showOptions ? <Trans>Hide</Trans> : <Trans>Edit</Trans>}
</ButtonGray>
</RowBetween>
</FocusedOutlineCard>
{showOptions && (
<RowBetween>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.LOW}
onClick={() => handleFeePoolSelectWithEvent(FeeAmount.LOW)}
>
<AutoColumn gap="sm" justify="flex-start">
<AutoColumn justify="flex-start" gap="4px">
<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>
)

View File

@@ -1,5 +1,4 @@
import JSBI from 'jsbi'
import React from 'react'
import { Currency, CurrencyAmount, Fraction } from '@uniswap/sdk-core'
const CURRENCY_AMOUNT_MIN = new Fraction(JSBI.BigInt(1), JSBI.BigInt(1000000))

View File

@@ -0,0 +1,232 @@
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 } 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, MEDIA_WIDTHS } from 'theme'
import { switchToNetwork } from 'utils/switchToNetwork'
import { L2_CHAIN_IDS, L2_INFO, NETWORK_LABELS, SupportedChainId } from '../../constants/chains'
const StopOverflowQuery = `@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) and (max-width: ${
MEDIA_WIDTHS.upToMedium + 400
}px)`
const BaseWrapper = css`
position: relative;
${StopOverflowQuery} {
position: absolute;
top: 80px;
right: 20px;
}
${({ theme }) => theme.mediaWidth.upToMedium`
margin-left: 12px;
`};
${({ theme }) => theme.mediaWidth.upToSmall`
margin: 0 0.5rem 0 0;
width: initial;
overflow: hidden;
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: 14px;
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;
`
const Icon = styled.img`
width: 17px;
`
const L1Tag = styled.div`
color: #c4d9f8;
opacity: 40%;
`
const L2Tag = styled.div<{ chainId: SupportedChainId }>`
background-color: ${({ chainId }) => (chainId === SupportedChainId.ARBITRUM_ONE ? '#28A0F0' : '#FF0420')};
border-radius: 6px;
color: white;
font-size: 12px;
font-weight: 600;
padding: 2px 6px;
`
const MenuFlyout = styled.span`
background-color: ${({ theme }) => theme.bg2};
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
0px 24px 32px rgba(0, 0, 0, 0.01);
border-radius: 20px;
padding: 12px;
display: flex;
flex-direction: column;
font-size: 1rem;
position: absolute;
left: 0rem;
top: 3rem;
z-index: 100;
width: 237px;
${({ theme }) => theme.mediaWidth.upToMedium`
top: -10.25rem;
`};
> {
padding: 12px;
}
> :not(:first-child) {
margin-top: 8px;
}
> :not(:last-child) {
margin-bottom: 8px;
}
${StopOverflowQuery} {
left: unset;
right: 0rem;
}
`
const LinkOutCircle = styled(ArrowDownCircle)`
transform: rotate(230deg);
width: 20px;
height: 20px;
`
const MenuItem = styled(ExternalLink)`
${BaseMenuItem}
`
const ButtonMenuItem = styled.button`
${BaseMenuItem}
border: none;
outline: none;
box-shadow: none;
`
const NetworkInfo = styled.button`
align-items: center;
background-color: ${({ theme }) => theme.bg2};
border-radius: 8px;
border: none;
color: ${({ theme }) => theme.text1};
display: flex;
flex-direction: row;
font-weight: 500;
height: 100%;
justify-content: space-between;
margin: 0;
padding: 8px;
width: 172px;
:hover,
:focus {
cursor: pointer;
outline: none;
background-color: ${({ theme }) => theme.bg3};
}
`
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])
if (!chainId || chainId === SupportedChainId.MAINNET || !NETWORK_LABELS[chainId] || !library) {
return null
}
if (L2_CHAIN_IDS.includes(chainId)) {
const info = L2_INFO[chainId]
return (
<L2Wrapper ref={node}>
<NetworkInfo onClick={toggle}>
<Icon src={info.logoUrl} />
<span>{NETWORK_LABELS[chainId]}</span>
<L2Tag chainId={chainId}>L2 Alpha</L2Tag>
</NetworkInfo>
{open && (
<MenuFlyout>
<MenuItem href={info.bridge}>
<div>
<Trans>{NETWORK_LABELS[chainId]} Bridge</Trans>
</div>
<LinkOutCircle />
</MenuItem>
<MenuItem href={info.explorer}>
<div>
<Trans>{NETWORK_LABELS[chainId]} Explorer</Trans>
</div>
<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 Ethereum</Trans>
</div>
<L1Tag>L1</L1Tag>
</ButtonMenuItem>
) : (
<DisabledMenuItem>
<Trans>Change your network to go back to L1</Trans>
</DisabledMenuItem>
)}
</MenuFlyout>
)}
</L2Wrapper>
)
}
return <FallbackWrapper title={NETWORK_LABELS[chainId]}>{NETWORK_LABELS[chainId]}</FallbackWrapper>
}

View File

@@ -1,10 +1,10 @@
import React, { useState, useEffect } from 'react'
import styled, { keyframes } from 'styled-components'
import { TYPE, ExternalLink } from '../../theme'
import { useEffect, useState } from 'react'
import styled, { keyframes } from 'styled-components/macro'
import { useActiveWeb3React } from '../../hooks/web3'
import { useBlockNumber } from '../../state/application/hooks'
import { getEtherscanLink } from '../../utils'
import { useActiveWeb3React } from '../../hooks'
import { ExternalLink, TYPE } from '../../theme'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
const StyledPolling = styled.div`
position: fixed;
@@ -21,7 +21,7 @@ const StyledPolling = styled.div`
`
const StyledPollingNumber = styled(TYPE.small)<{ breathe: boolean; hovering: boolean }>`
transition: opacity 0.25s ease;
opacity: ${({ breathe, hovering }) => (hovering ? 0.7 : breathe ? 1 : 0.2)};
opacity: ${({ breathe, hovering }) => (hovering ? 0.7 : breathe ? 1 : 0.5)};
:hover {
opacity: 1;
}
@@ -91,7 +91,9 @@ export default function Polling() {
)
return (
<ExternalLink href={chainId && blockNumber ? getEtherscanLink(chainId, blockNumber.toString(), 'block') : ''}>
<ExternalLink
href={chainId && blockNumber ? getExplorerLink(chainId, blockNumber.toString(), ExplorerDataType.BLOCK) : ''}
>
<StyledPolling onMouseEnter={() => setIsHover(true)} onMouseLeave={() => setIsHover(false)}>
<StyledPollingNumber breathe={isMounting} hovering={isHover}>
{blockNumber}

View File

@@ -1,47 +0,0 @@
import React from 'react'
import styled from 'styled-components/macro'
import { AlertTriangle, X } from 'react-feather'
import { useURLWarningToggle, useURLWarningVisible } from '../../state/user/hooks'
import { isMobile } from 'react-device-detect'
const PhishAlert = styled.div<{ isActive: any }>`
width: 100%;
padding: 6px 6px;
background-color: ${({ theme }) => theme.blue1};
color: white;
font-size: 11px;
justify-content: space-between;
align-items: center;
display: ${({ isActive }) => (isActive ? 'flex' : 'none')};
`
export const StyledClose = styled(X)`
:hover {
cursor: pointer;
}
`
export default function URLWarning() {
const toggleURLWarning = useURLWarningToggle()
const showURLWarning = useURLWarningVisible()
return isMobile ? (
<PhishAlert isActive={showURLWarning}>
<div style={{ display: 'flex' }}>
<AlertTriangle style={{ marginRight: 6 }} size={12} /> Make sure the URL is
<code style={{ padding: '0 4px', display: 'inline', fontWeight: 'bold' }}>app.uniswap.org</code>
</div>
<StyledClose size={12} onClick={toggleURLWarning} />
</PhishAlert>
) : window.location.hostname === 'app.uniswap.org' ? (
<PhishAlert isActive={showURLWarning}>
<div style={{ display: 'flex' }}>
<AlertTriangle style={{ marginRight: 6 }} size={12} /> Always make sure the URL is
<code style={{ padding: '0 4px', display: 'inline', fontWeight: 'bold' }}>app.uniswap.org</code> - bookmark it
to be safe.
</div>
<StyledClose size={12} onClick={toggleURLWarning} />
</PhishAlert>
) : null
}

View File

@@ -1,11 +1,11 @@
import { ChainId, CurrencyAmount, Token } from '@uniswap/sdk-core'
import React, { useMemo } from 'react'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { useMemo } from 'react'
import { X } from 'react-feather'
import styled from 'styled-components/macro'
import tokenLogo from '../../assets/images/token-logo.png'
import { UNI } from '../../constants'
import { UNI } from '../../constants/tokens'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { useMerkleDistributorContract } from '../../hooks/useContract'
import useCurrentBlockTimestamp from '../../hooks/useCurrentBlockTimestamp'
import { useTotalUniEarned } from '../../state/stake/hooks'
@@ -16,6 +16,7 @@ import useUSDCPrice from '../../hooks/useUSDCPrice'
import { AutoColumn } from '../Column'
import { RowBetween } from '../Row'
import { Break, CardBGImage, CardNoise, CardSection, DataCard } from '../earn/styled'
import { Trans } from '@lingui/macro'
const ContentWrapper = styled(AutoColumn)`
width: 100%;
@@ -54,9 +55,7 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
const unclaimedUni = useTokenBalance(useMerkleDistributorContract()?.address, uni)
const circulation: CurrencyAmount<Token> | undefined = useMemo(
() =>
blockTimestamp && uni && chainId === ChainId.MAINNET
? computeUniCirculation(uni, blockTimestamp, unclaimedUni)
: totalSupply,
blockTimestamp && uni && chainId === 1 ? computeUniCirculation(uni, blockTimestamp, unclaimedUni) : totalSupply,
[blockTimestamp, chainId, totalSupply, unclaimedUni, uni]
)
@@ -67,7 +66,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 +84,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 +110,27 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
<CardSection gap="sm">
<AutoColumn gap="md">
<RowBetween>
<TYPE.white color="white">UNI price:</TYPE.white>
<TYPE.white color="white">
<Trans>UNI price:</Trans>
</TYPE.white>
<TYPE.white color="white">${uniPrice?.toFixed(2) ?? '-'}</TYPE.white>
</RowBetween>
<RowBetween>
<TYPE.white color="white">UNI in circulation:</TYPE.white>
<TYPE.white color="white">
<Trans>UNI in circulation:</Trans>
</TYPE.white>
<TYPE.white color="white">{circulation?.toFixed(0, { groupSeparator: ',' })}</TYPE.white>
</RowBetween>
<RowBetween>
<TYPE.white color="white">Total Supply</TYPE.white>
<TYPE.white color="white">
<Trans>Total Supply</Trans>
</TYPE.white>
<TYPE.white color="white">{totalSupply?.toFixed(0, { groupSeparator: ',' })}</TYPE.white>
</RowBetween>
{uni && uni.chainId === ChainId.MAINNET ? (
<ExternalLink href={`https://info.uniswap.org/token/${uni.address}`}>View UNI Analytics</ExternalLink>
{uni && uni.chainId === 1 ? (
<ExternalLink href={`https://info.uniswap.org/token/${uni.address}`}>
<Trans>View UNI Analytics</Trans>
</ExternalLink>
) : null}
</AutoColumn>
</CardSection>

View File

@@ -1,33 +1,28 @@
import { ChainId } from '@uniswap/sdk-core'
import { Trans } from '@lingui/macro'
import useScrollPosition from '@react-hook/window-scroll'
import React, { useState } from 'react'
import { Text } from 'rebass'
import { NavLink } from 'react-router-dom'
import { darken } from 'polished'
import { useTranslation } from 'react-i18next'
import { useState } from 'react'
import { Moon, Sun } from 'react-feather'
import { NavLink } from 'react-router-dom'
import { Text } from 'rebass'
import { useShowClaimPopup, useToggleSelfClaimModal } from 'state/application/hooks'
import { useUserHasAvailableClaim } from 'state/claim/hooks'
import { useUserHasSubmittedClaim } from 'state/transactions/hooks'
import { useDarkModeManager } from 'state/user/hooks'
import { useETHBalances } from 'state/wallet/hooks'
import styled from 'styled-components/macro'
import Logo from '../../assets/svg/logo.svg'
import LogoDark from '../../assets/svg/logo_white.svg'
import { useActiveWeb3React } from '../../hooks'
import { useDarkModeManager } from '../../state/user/hooks'
import { useETHBalances } from '../../state/wallet/hooks'
import { CardNoise } from '../earn/styled'
import { TYPE, ExternalLink } from '../../theme'
import { YellowCard } from '../Card'
import Menu from '../Menu'
import Row, { RowFixed } from '../Row'
import Web3Status from '../Web3Status'
import { useActiveWeb3React } from '../../hooks/web3'
import { ExternalLink, TYPE } from '../../theme'
import ClaimModal from '../claim/ClaimModal'
import { useToggleSelfClaimModal, useShowClaimPopup } from '../../state/application/hooks'
import { useUserHasAvailableClaim } from '../../state/claim/hooks'
import { useUserHasSubmittedClaim } from '../../state/transactions/hooks'
import { Dots } from '../swap/styleds'
import { CardNoise } from '../earn/styled'
import Menu from '../Menu'
import Modal from '../Modal'
import Row, { RowFixed } from '../Row'
import { Dots } from '../swap/styleds'
import Web3Status from '../Web3Status'
import NetworkCard from './NetworkCard'
import UniBalanceContent from './UniBalanceContent'
const HeaderFrame = styled.div<{ showBackground: boolean }>`
@@ -53,8 +48,7 @@ const HeaderFrame = styled.div<{ showBackground: boolean }>`
${({ theme }) => theme.mediaWidth.upToMedium`
padding: 1rem;
grid-template-columns: 120px 1fr;
grid-template-columns: auto 1fr;
`};
${({ theme }) => theme.mediaWidth.upToExtraSmall`
@@ -171,19 +165,6 @@ const HideSmall = styled.span`
`};
`
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;
@@ -227,6 +208,7 @@ const StyledNavLink = styled(NavLink).attrs({
width: fit-content;
font-weight: 500;
padding: 8px 12px;
word-break: break-word;
&.${activeClassName} {
border-radius: 12px;
@@ -265,6 +247,7 @@ const StyledExternalLink = styled(ExternalLink).attrs({
:hover,
:focus {
color: ${({ theme }) => darken(0.1, theme.text1)};
text-decoration: none;
}
${({ theme }) => theme.mediaWidth.upToExtraSmall`
@@ -272,7 +255,7 @@ const StyledExternalLink = styled(ExternalLink).attrs({
`}
`
export const StyledMenuButton = styled.button`
const StyledMenuButton = styled.button`
position: relative;
width: 100%;
height: 100%;
@@ -301,16 +284,8 @@ export const StyledMenuButton = styled.button`
}
`
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 { account } = useActiveWeb3React()
const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? '']
// const [isDark] = useDarkModeManager()
@@ -342,7 +317,7 @@ export default function Header() {
</HeaderRow>
<HeaderLinks>
<StyledNavLink id={`swap-nav-link`} to={'/swap'}>
{t('swap')}
<Trans>Swap</Trans>
</StyledNavLink>
<StyledNavLink
id={`pool-nav-link`}
@@ -355,27 +330,32 @@ export default function Header() {
pathname.startsWith('/find')
}
>
{t('pool')}
<Trans>Pool</Trans>
</StyledNavLink>
<StyledNavLink id={`stake-nav-link`} to={'/vote'}>
Vote
<Trans>Vote</Trans>
</StyledNavLink>
<StyledExternalLink id={`stake-nav-link`} href={'https://info.uniswap.org'}>
Charts <span style={{ fontSize: '11px', textDecoration: 'none !important' }}></span>
<Trans>Charts</Trans>
<sup></sup>
</StyledExternalLink>
</HeaderLinks>
<HeaderControls>
<HeaderElement>
<HideSmall>
{chainId && NETWORK_LABELS[chainId] && (
<NetworkCard title={NETWORK_LABELS[chainId]}>{NETWORK_LABELS[chainId]}</NetworkCard>
)}
<NetworkCard />
</HideSmall>
{availableClaim && !showClaimPopup && (
<UNIWrapper onClick={toggleClaimModal}>
<UNIAmount active={!!account && !availableClaim} style={{ pointerEvents: 'auto' }}>
<TYPE.white padding="0 2px">
{claimTxn && !claimTxn?.receipt ? <Dots>Claiming UNI</Dots> : 'Claim UNI'}
{claimTxn && !claimTxn?.receipt ? (
<Dots>
<Trans>Claiming UNI</Trans>
</Dots>
) : (
<Trans>Claim UNI</Trans>
)}
</TYPE.white>
</UNIAmount>
<CardNoise />
@@ -384,7 +364,7 @@ export default function Header() {
<AccountElement active={!!account} style={{ pointerEvents: 'auto' }}>
{account && userEthBalance ? (
<BalanceText style={{ flexShrink: 0 }} pl="0.75rem" pr="0.5rem" fontWeight={500}>
{userEthBalance?.toSignificant(4)} ETH
<Trans>{userEthBalance?.toSignificant(4)} ETH</Trans>
</BalanceText>
) : null}
<Web3Status />

View File

@@ -1,9 +1,8 @@
import Tooltip from 'components/Tooltip'
import React, { useState } from 'react'
import { useState } from 'react'
import styled from 'styled-components/macro'
const TextWrapper = styled.span<{ margin: boolean; link?: boolean; fontSize?: string; adjustSize?: boolean }>`
cursor: auto;
margin-left: ${({ margin }) => margin && '4px'};
color: ${({ theme, link }) => (link ? theme.blue1 : theme.text1)};
font-size: ${({ fontSize }) => fontSize ?? 'inherit'};
@@ -22,7 +21,7 @@ const HoverInlineText = ({
link,
...rest
}: {
text: string
text?: string
maxCharacters?: number
margin?: boolean
adjustSize?: boolean

View File

@@ -1,9 +1,9 @@
import React, { useEffect, useRef } from 'react'
import { useEffect, useRef } from 'react'
import styled from 'styled-components/macro'
import { useActiveWeb3React } from '../../hooks'
import Jazzicon from 'jazzicon'
import { useActiveWeb3React } from '../../hooks/web3'
import Jazzicon from '@metamask/jazzicon'
const StyledIdenticonContainer = styled.div`
height: 1rem;

View File

@@ -1,13 +1,14 @@
import React, { useState, useCallback, useEffect } from 'react'
import { useState, useCallback, useEffect, ReactNode } from 'react'
import { LightCard } from 'components/Card'
import { RowBetween } from 'components/Row'
import { Input as NumericalInput } from '../NumericalInput'
import styled, { keyframes } from 'styled-components'
import styled, { keyframes } from 'styled-components/macro'
import { TYPE } from 'theme'
import { AutoColumn } from 'components/Column'
import { ButtonPrimary } from 'components/Button'
import { FeeAmount } from '@uniswap/v3-sdk'
import { formattedFeeAmount } from 'utils'
import { Trans } from '@lingui/macro'
const pulse = (color: string) => keyframes`
0% {
@@ -59,7 +60,7 @@ interface StepCounterProps {
label?: string
width?: string
locked?: boolean // disable input
title: string
title: ReactNode
tokenA: string | undefined
tokenB: string | undefined
}
@@ -139,16 +140,22 @@ const StepCounter = ({
}}
/>
<InputTitle fontSize={12} textAlign="center">
{tokenB + ' per ' + tokenA}
<Trans>
{tokenB} per {tokenA}
</Trans>
</InputTitle>
</AutoColumn>
{!locked ? (
<RowBetween>
<SmallButton onClick={handleDecrement}>
<TYPE.white fontSize="12px">-{feeAmountFormatted}%</TYPE.white>
<TYPE.white fontSize="12px">
<Trans>-{feeAmountFormatted}%</Trans>
</TYPE.white>
</SmallButton>
<SmallButton onClick={handleIncrement}>
<TYPE.white fontSize="12px">+{feeAmountFormatted}%</TYPE.white>
<TYPE.white fontSize="12px">
<Trans>+{feeAmountFormatted}%</Trans>
</TYPE.white>
</SmallButton>
</RowBetween>
) : null}

View File

@@ -1,90 +0,0 @@
import { Story } from '@storybook/react/types-6-0'
import React from 'react'
// import Row, { RowFixed } from 'components/Row'
import styled from 'styled-components/macro'
import Component, { LineChartProps } from './'
import { dummyData } from './data'
// import { AutoColumn } from 'components/Column'
// import { TYPE } from 'theme'
// import DoubleCurrencyLogo from 'components/DoubleLogo'
// import { MKR } from 'constants'
// import { ETHER } from '@uniswap/sdk'
// import LineChart from '.'
const Wrapper = styled.div`
margin: 1em 2em;
max-width: 500px;
& > * {
font-size: 1em;
}
`
export default {
title: 'Charts',
argTypes: {
disabled: { control: { type: 'boolean' } },
onClick: { action: 'clicked' },
},
decorators: [
(Component: Story) => (
<Wrapper>
<Component />
</Wrapper>
),
],
}
const Template: Story<LineChartProps> = (args) => <Component {...args}>{args.children}</Component>
export const Basic = Template.bind({})
Basic.args = { data: dummyData }
// const Full = () => {
// const [value, setValue] = useState<number | undefined>(dummyData[dummyData.length - 1].value)
// const dummyUSDPrice = 410 // used for conversion
// const TopLeftContent = () => (
// <AutoColumn gap="md">
// <RowFixed align="center">
// <DoubleCurrencyLogo currency0={MKR} currency1={ETHER} size={20} />{' '}
// <TYPE.main fontSize="20px" color="white" ml="10px">
// ETH / MKR
// </TYPE.main>
// </RowFixed>
// <Row>
// <TYPE.main fontSize="20px" color="white">
// {value} MKR
// </TYPE.main>
// <TYPE.main color="#565A69" fontSize="20px" ml="10px">
// ($
// {value
// ? (value * dummyUSDPrice).toLocaleString('USD', {
// currency: 'USD',
// minimumFractionDigits: 2
// })
// : null}{' '}
// )
// </TYPE.main>
// </Row>
// </AutoColumn>
// )
// return (
// <Wrapper>
// <LineChart data={dummyData} setValue={setValue} topLeft={<TopLeftContent />} />
// </Wrapper>
// )
// }
// export const FullVersion = Template.bind(() => <LineChart data={dummyData} />)
// Full.args = { data: dummyData }
// Full.decorators = [
// (Story: any) => {
// return (
// <Wrapper>
// <LineChart data={dummyData} />
// </Wrapper>
// )
// }
// ]

View File

@@ -1,152 +0,0 @@
export const dummyData = [
{ time: '2018-10-19', value: 35.98 },
{ time: '2018-10-22', value: 35.75 },
{ time: '2018-10-23', value: 35.65 },
{ time: '2018-10-24', value: 34.12 },
{ time: '2018-10-25', value: 35.84 },
{ time: '2018-10-26', value: 35.24 },
{ time: '2018-10-29', value: 35.99 },
{ time: '2018-10-30', value: 37.71 },
{ time: '2018-10-31', value: 38.14 },
{ time: '2018-11-01', value: 37.95 },
{ time: '2018-11-02', value: 37.66 },
{ time: '2018-11-05', value: 38.02 },
{ time: '2018-11-06', value: 37.73 },
{ time: '2018-11-07', value: 38.3 },
{ time: '2018-11-08', value: 38.3 },
{ time: '2018-11-09', value: 38.34 },
{ time: '2018-11-12', value: 38.0 },
{ time: '2018-11-13', value: 37.72 },
{ time: '2018-11-14', value: 38.29 },
{ time: '2018-11-15', value: 38.49 },
{ time: '2018-11-16', value: 38.59 },
{ time: '2018-11-19', value: 38.18 },
{ time: '2018-11-20', value: 36.76 },
{ time: '2018-11-21', value: 37.51 },
{ time: '2018-11-23', value: 37.39 },
{ time: '2018-11-26', value: 37.77 },
{ time: '2018-11-27', value: 38.36 },
{ time: '2018-11-28', value: 39.06 },
{ time: '2018-11-29', value: 39.42 },
{ time: '2018-11-30', value: 39.01 },
{ time: '2018-12-03', value: 39.15 },
{ time: '2018-12-04', value: 37.69 },
{ time: '2018-12-06', value: 37.88 },
{ time: '2018-12-07', value: 37.41 },
{ time: '2018-12-10', value: 37.35 },
{ time: '2018-12-11', value: 36.84 },
{ time: '2018-12-12', value: 36.98 },
{ time: '2018-12-13', value: 36.76 },
{ time: '2018-12-14', value: 36.34 },
{ time: '2018-12-17', value: 36.21 },
{ time: '2018-12-18', value: 35.65 },
{ time: '2018-12-19', value: 35.19 },
{ time: '2018-12-20', value: 34.62 },
{ time: '2018-12-21', value: 33.75 },
{ time: '2018-12-24', value: 33.07 },
{ time: '2018-12-26', value: 34.14 },
{ time: '2018-12-27', value: 34.47 },
{ time: '2018-12-28', value: 34.35 },
{ time: '2018-12-31', value: 34.05 },
{ time: '2019-01-02', value: 34.37 },
{ time: '2019-01-03', value: 34.64 },
{ time: '2019-01-04', value: 35.81 },
{ time: '2019-01-07', value: 35.43 },
{ time: '2019-01-08', value: 35.72 },
{ time: '2019-01-09', value: 36.06 },
{ time: '2019-01-10', value: 35.82 },
{ time: '2019-01-11', value: 35.63 },
{ time: '2019-01-14', value: 35.77 },
{ time: '2019-01-15', value: 35.83 },
{ time: '2019-01-16', value: 35.9 },
{ time: '2019-01-17', value: 35.91 },
{ time: '2019-01-18', value: 36.21 },
{ time: '2019-01-22', value: 34.97 },
{ time: '2019-01-23', value: 36.89 },
{ time: '2019-01-24', value: 36.24 },
{ time: '2019-01-25', value: 35.78 },
{ time: '2019-01-28', value: 35.37 },
{ time: '2019-01-29', value: 36.08 },
{ time: '2019-01-30', value: 35.43 },
{ time: '2019-01-31', value: 36.57 },
{ time: '2019-02-01', value: 36.79 },
{ time: '2019-02-04', value: 36.77 },
{ time: '2019-02-05', value: 37.15 },
{ time: '2019-02-06', value: 37.17 },
{ time: '2019-02-07', value: 37.68 },
{ time: '2019-02-08', value: 37.6 },
{ time: '2019-02-11', value: 37.0 },
{ time: '2019-02-12', value: 37.24 },
{ time: '2019-02-13', value: 37.03 },
{ time: '2019-02-14', value: 37.26 },
{ time: '2019-02-15', value: 37.77 },
{ time: '2019-02-19', value: 37.55 },
{ time: '2019-02-20', value: 37.79 },
{ time: '2019-02-21', value: 38.47 },
{ time: '2019-02-22', value: 38.61 },
{ time: '2019-02-25', value: 38.57 },
{ time: '2019-02-26', value: 38.8 },
{ time: '2019-02-27', value: 38.53 },
{ time: '2019-02-28', value: 38.67 },
{ time: '2019-03-01', value: 39.1 },
{ time: '2019-03-04', value: 38.73 },
{ time: '2019-03-05', value: 38.72 },
{ time: '2019-03-06', value: 38.61 },
{ time: '2019-03-07', value: 38.38 },
{ time: '2019-03-08', value: 38.19 },
{ time: '2019-03-11', value: 39.17 },
{ time: '2019-03-12', value: 39.49 },
{ time: '2019-03-13', value: 39.56 },
{ time: '2019-03-14', value: 39.87 },
{ time: '2019-03-15', value: 40.47 },
{ time: '2019-03-18', value: 39.92 },
{ time: '2019-03-19', value: 39.78 },
{ time: '2019-03-20', value: 39.47 },
{ time: '2019-03-21', value: 40.05 },
{ time: '2019-03-22', value: 39.46 },
{ time: '2019-03-25', value: 39.18 },
{ time: '2019-03-26', value: 39.63 },
{ time: '2019-03-27', value: 40.21 },
{ time: '2019-03-28', value: 40.42 },
{ time: '2019-03-29', value: 39.98 },
{ time: '2019-04-01', value: 40.31 },
{ time: '2019-04-02', value: 40.02 },
{ time: '2019-04-03', value: 40.27 },
{ time: '2019-04-04', value: 40.41 },
{ time: '2019-04-05', value: 40.42 },
{ time: '2019-04-08', value: 40.71 },
{ time: '2019-04-09', value: 41.04 },
{ time: '2019-04-10', value: 41.08 },
{ time: '2019-04-11', value: 41.04 },
{ time: '2019-04-12', value: 41.3 },
{ time: '2019-04-15', value: 41.78 },
{ time: '2019-04-16', value: 41.97 },
{ time: '2019-04-17', value: 42.57 },
{ time: '2019-04-18', value: 42.43 },
{ time: '2019-04-22', value: 42.0 },
{ time: '2019-04-23', value: 41.99 },
{ time: '2019-04-24', value: 41.85 },
{ time: '2019-04-25', value: 42.93 },
{ time: '2019-04-26', value: 43.08 },
{ time: '2019-04-29', value: 43.45 },
{ time: '2019-04-30', value: 43.53 },
{ time: '2019-05-01', value: 43.42 },
{ time: '2019-05-02', value: 42.65 },
{ time: '2019-05-03', value: 43.29 },
{ time: '2019-05-06', value: 43.3 },
{ time: '2019-05-07', value: 42.76 },
{ time: '2019-05-08', value: 42.55 },
{ time: '2019-05-09', value: 42.92 },
{ time: '2019-05-10', value: 43.15 },
{ time: '2019-05-13', value: 42.28 },
{ time: '2019-05-14', value: 42.91 },
{ time: '2019-05-15', value: 42.49 },
{ time: '2019-05-16', value: 43.19 },
{ time: '2019-05-17', value: 43.54 },
{ time: '2019-05-20', value: 42.78 },
{ time: '2019-05-21', value: 43.29 },
{ time: '2019-05-22', value: 43.3 },
{ time: '2019-05-23', value: 42.73 },
{ time: '2019-05-24', value: 42.67 },
{ time: '2019-05-28', value: 42.75 },
]

View File

@@ -10,7 +10,7 @@ const Wrapper = styled(Card)`
width: 100%;
padding: 1rem;
display: flex;
background-color: ${({ theme }) => theme.bg0}
background-color: ${({ theme }) => theme.bg0};
flex-direction: column;
> * {
font-size: 1rem;
@@ -19,7 +19,7 @@ const Wrapper = styled(Card)`
const DEFAULT_HEIGHT = 300
export type LineChartProps = {
type LineChartProps = {
data: any[]
color?: string | undefined
height?: number | undefined

View 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]
)

View 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 = 5,
}: {
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]
)

View File

@@ -0,0 +1,242 @@
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 } from 'components/LiquidityChartRangeInput/svg'
import usePrevious from 'hooks/usePrevious'
const Handle = styled.path<{ color: string }>`
cursor: ew-resize;
pointer-events: none;
stroke-width: 4;
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};
`
export const Brush = ({
id,
xScale,
interactive,
brushLabelValue,
brushExtent,
setBrushExtent,
innerWidth,
innerHeight,
colors,
}: {
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
colors: {
west: string
east: 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(
({ mode, type, selection }: D3BrushEvent<unknown>) => {
if (!selection) {
setLocalBrushExtent(null)
return
}
const scaled = (selection as [number, number]).map(xScale.invert) as [number, number]
// undefined `mode` means brush was programatically moved
// skip calling the handler to avoid a loop
if (type === 'end' && mode !== undefined) {
setBrushExtent(scaled)
}
setLocalBrushExtent(scaled)
},
[xScale.invert, 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, xScale(0)), 0],
[innerWidth, innerHeight],
])
.handleSize(30)
.filter(() => interactive)
.on('brush end', brushed)
brushBehavior.current(select(brushRef.current))
if (
previousBrushExtent &&
(brushExtent[0] !== previousBrushExtent[0] || brushExtent[1] !== previousBrushExtent[1])
) {
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)
// dependency on brushExtent would start an update loop
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [xScale])
useEffect(() => {
setShowLabels(true)
const timeout = setTimeout(() => setShowLabels(false), 1500)
return () => clearTimeout(timeout)
}, [localBrushExtent])
const flipWestHandle = localBrushExtent && xScale(localBrushExtent[0]) > 15
const flipEastHandle = localBrushExtent && xScale(localBrushExtent[1]) > innerWidth - 15
return useMemo(
() => (
<>
<defs>
<linearGradient id={`${id}-gradient-selection`} x1="0%" y1="100%" x2="100%" y2="100%">
<stop stopColor={colors.west} />
<stop stopColor={colors.east} offset="1" />
</linearGradient>
{/* clips at exactly the svg area */}
<clipPath id={`${id}-brush-clip`}>
<rect x="0" y="0" width={innerWidth} height="100%" />
</clipPath>
<clipPath id={`${id}-handles-clip`}>
<rect x="0" y="0" width="100%" height="100%" />
</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 */}
<g
transform={`translate(${Math.max(0, xScale(localBrushExtent[0]))}, 0), scale(${
flipWestHandle ? '-1' : '1'
}, 1)`}
>
<g clipPath={`url(#${id}-handles-clip)`}>
<Handle color={colors.west} 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>
{/* east handle */}
<g
transform={`translate(${Math.min(xScale(localBrushExtent[1]), innerWidth)}, 0), scale(${
flipEastHandle ? '-1' : '1'
}, 1)`}
>
<g clipPath={`url(#${id}-handles-clip)`}>
<Handle color={colors.east} 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>
</>
)}
</>
),
[
brushLabelValue,
colors.east,
colors.west,
flipEastHandle,
flipWestHandle,
hovering,
id,
innerHeight,
innerWidth,
localBrushExtent,
showLabels,
xScale,
]
)
}

View File

@@ -0,0 +1,132 @@
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 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,
initialZoom,
}: LiquidityChartRangeInputProps) {
const svgRef = useRef<SVGSVGElement | null>(null)
const [zoom, setZoom] = useState<ZoomTransform>()
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([(1 - initialZoom) * current, (1 + initialZoom) * current] 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
}, [initialZoom, current, innerWidth, series, innerHeight, zoom])
useEffect(() => {
if (!brushDomain) {
onBrushDomainChange(xScale.domain() as [number, number])
}
}, [brushDomain, onBrushDomainChange, xScale])
// ensures the brush remains in view and adapts to zooms
xScale.clamp(true)
return (
<>
<Zoom
svg={svgRef.current}
xScale={xScale}
setZoom={setZoom}
innerWidth={innerWidth}
innerHeight={innerHeight}
showClear={Boolean(zoom && zoom.k !== 1)}
/>
<svg ref={svgRef} style={{ overflow: 'visible' }} width={width} height={height}>
<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>
<Brush
id={id}
xScale={xScale}
interactive={interactive}
brushLabelValue={brushLabels}
brushExtent={brushDomain ?? (xScale.domain() as [number, number])}
innerWidth={innerWidth}
innerHeight={innerHeight}
setBrushExtent={onBrushDomainChange}
colors={{
west: styles.brush.handle.west,
east: styles.brush.handle.east,
}}
/>
</g>
</svg>
</>
)
}

View 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]
)

View File

@@ -0,0 +1,105 @@
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'
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: 28px;
height: 28px;
padding: 4px;
`
export default function Zoom({
svg,
xScale,
setZoom,
innerWidth,
innerHeight,
showClear,
}: {
svg: SVGSVGElement | null
xScale: ScaleLinear<number, number>
setZoom: (transform: ZoomTransform) => void
innerWidth: number
innerHeight: number
showClear: boolean
}) {
const zoomBehavior = useRef<ZoomBehavior<Element, unknown>>()
const [zoomIn, zoomOut, reset] = 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]
)
useEffect(() => {
if (!svg) return
// zoom
zoomBehavior.current = zoom()
.scaleExtent([0.3, 10])
.translateExtent([
[0, 0],
[innerWidth, innerHeight],
])
.extent([
[0, 0],
[innerWidth, innerHeight],
])
.on('zoom', ({ transform }: { transform: ZoomTransform }) => setZoom(transform))
select(svg as Element)
.call(zoomBehavior.current)
.on('mousedown.zoom', null)
}, [innerHeight, innerWidth, setZoom, svg, xScale, zoomBehavior])
return (
<Wrapper count={showClear ? 3 : 2}>
{showClear && (
<Button onClick={reset} disabled={false}>
<RefreshCcw size={14} />
</Button>
)}
<Button onClick={zoomIn} disabled={false}>
<ZoomIn size={14} />
</Button>
<Button onClick={zoomOut} disabled={false}>
<ZoomOut size={14} />
</Button>
</Wrapper>
)
}

View File

@@ -0,0 +1,74 @@
import { useEffect, useState } 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'
// Tick with fields parsed to JSBIs, and active liquidity computed.
export interface TickProcessed {
tickIdx: number
liquidityActive: JSBI
liquidityNet: JSBI
price0: string
}
export function useDensityChartData({
currencyA,
currencyB,
feeAmount,
}: {
currencyA: Currency | undefined
currencyB: Currency | undefined
feeAmount: FeeAmount | undefined
}) {
const [formattedData, setFormattedData] = useState<ChartEntry[] | undefined>()
const { isLoading, isUninitialized, isError, error, activeTick, data } = usePoolActiveLiquidity(
currencyA,
currencyB,
feeAmount
)
useEffect(() => {
// clear data when inputs are cleared
setFormattedData(undefined)
}, [currencyA, currencyB, feeAmount])
useEffect(() => {
function formatData() {
if (!data?.length) {
return
}
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),
}
newData.push(chartEntry)
}
if (newData) {
setFormattedData(newData)
}
}
if (!isLoading) {
formatData()
}
}, [isLoading, activeTick, data])
return {
isLoading,
isUninitialized,
isError,
error,
formattedData,
}
}

View File

@@ -0,0 +1,180 @@
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'
const ChartWrapper = styled.div`
display: grid;
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">
{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?: number
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) => {
const leftRangeValue = Number(domain[0])
const rightRangeValue = Number(domain[1])
ReactGA.event({
category: 'Liquidity',
action: 'Chart brushed',
})
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
return price ? `${format(Math.abs(percent) > 1 ? '.2~s' : '.2~f')(percent)}%` : ''
},
[price, ticksAtLimit]
)
if (isError) {
ReactGA.exception({
...error,
category: 'Liquidity',
fatal: false,
})
if (error?.name === 'UnsupportedChainId') {
// do not show the chart container when the chain is not supported
return null
}
}
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>Subgraph 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: 30, 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}
initialZoom={feeAmount === FeeAmount.LOW ? 0.02 : 0.3}
/>
</ChartWrapper>
)}
</AutoColumn>
)
}

View File

@@ -0,0 +1,42 @@
/*
* 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 2`, // 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 6 8', // move to first accent
'v 14', // vertical line
'M 0 0', // move to origin
'm 10 8', // move to second accent
'v 14', // vertical line
'z',
].join(' ')

View File

@@ -0,0 +1,51 @@
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 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
initialZoom: number
}

View File

@@ -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 {

View File

@@ -1,11 +1,11 @@
import React, { useState } from 'react'
import { useState } from 'react'
import { Slash } from 'react-feather'
import { ImageProps } from 'rebass'
import useTheme from '../../hooks/useTheme'
const BAD_SRCS: { [tokenAddress: string]: true } = {}
export interface LogoProps extends Pick<ImageProps, 'style' | 'alt' | 'className'> {
interface LogoProps extends Pick<ImageProps, 'style' | 'alt' | 'className'> {
srcs: string[]
}

View File

@@ -1,23 +0,0 @@
import { Story } from '@storybook/react/types-6-0'
import React from 'react'
import styled from 'styled-components/macro'
import Component from './index'
const Wrapper = styled.div`
max-width: 150px;
`
export default {
title: 'Menu',
decorators: [
() => (
<Wrapper>
<Component />
</Wrapper>
),
],
}
const Template: Story<any> = (args) => <Component {...args} />
export const Menu = Template.bind({})
Menu.args = {}

View File

@@ -1,15 +1,16 @@
import React, { useRef } from 'react'
import { BookOpen, Code, Info, MessageCircle, PieChart } from 'react-feather'
import { Link } from 'react-router-dom'
import styled, { css } from 'styled-components'
import styled, { css } from 'styled-components/macro'
import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
import { ApplicationModal } from '../../state/application/actions'
import { useModalOpen, useToggleModal } from '../../state/application/hooks'
import { Trans } from '@lingui/macro'
import { ExternalLink } from '../../theme'
import { ButtonPrimary } from '../Button'
import { L2_CHAIN_IDS } from 'constants/chains'
export enum FlyoutAlignment {
LEFT = 'LEFT',
@@ -85,7 +86,8 @@ const MenuFlyout = styled.span<{ flyoutAlignment?: FlyoutAlignment }>`
left: 0rem;
`};
${({ theme }) => theme.mediaWidth.upToMedium`
top: -17.25rem;
top: unset;
bottom: 3em
`};
`
@@ -123,13 +125,14 @@ const InternalMenuItem = styled(Link)`
const CODE_LINK = 'https://github.com/Uniswap/uniswap-interface'
export default function Menu() {
const { account } = useActiveWeb3React()
const { account, chainId } = useActiveWeb3React()
const node = useRef<HTMLDivElement>()
const open = useModalOpen(ApplicationModal.MENU)
const toggle = useToggleModal(ApplicationModal.MENU)
useOnClickOutside(node, open ? toggle : undefined)
const openClaimModal = useToggleModal(ApplicationModal.ADDRESS_CLAIM)
const showUNIClaimOption = Boolean(!!account && !!chainId && !L2_CHAIN_IDS.includes(chainId))
return (
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451
@@ -142,27 +145,37 @@ export default function Menu() {
<MenuFlyout>
<MenuItem href="https://uniswap.org/">
<Info size={14} />
<div>About</div>
<div>
<Trans>About</Trans>
</div>
</MenuItem>
<MenuItem href="https://docs.uniswap.org/">
<BookOpen size={14} />
<div>Docs</div>
<div>
<Trans>Docs</Trans>
</div>
</MenuItem>
<MenuItem href={CODE_LINK}>
<Code size={14} />
<div>Code</div>
<div>
<Trans>Code</Trans>
</div>
</MenuItem>
<MenuItem href="https://discord.gg/FCfyBSbCU5">
<MessageCircle size={14} />
<div>Discord</div>
<div>
<Trans>Discord</Trans>
</div>
</MenuItem>
<MenuItem href="https://info.uniswap.org/">
<PieChart size={14} />
<div>Analytics</div>
<div>
<Trans>Analytics</Trans>
</div>
</MenuItem>
{account && (
<UNIbutton onClick={openClaimModal} padding="8px 16px" width="100%" borderRadius="12px" mt="0.5rem">
Claim UNI
{showUNIClaimOption && (
<UNIbutton onClick={openClaimModal} padding="8px 16px" width="100%" $borderRadius="12px" mt="0.5rem">
<Trans>Claim UNI</Trans>
</UNIbutton>
)}
</MenuFlyout>

View File

@@ -1,9 +1,8 @@
import React from 'react'
import styled, { css } from 'styled-components'
import styled, { css } from 'styled-components/macro'
import { animated, useTransition, useSpring } from 'react-spring'
import { DialogOverlay, DialogContent } from '@reach/dialog'
import { isMobile } from 'react-device-detect'
import '@reach/dialog/styles.css'
import { transparentize } from 'polished'
import { useGesture } from 'react-use-gesture'
@@ -31,7 +30,7 @@ const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...r
)).attrs({
'aria-label': 'dialog',
})`
overflow-y: ${({ mobile }) => (mobile ? 'scroll' : 'hidden')};
overflow-y: auto;
&[data-reach-dialog-content] {
margin: 0 0 2rem 0;
@@ -40,7 +39,7 @@ const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...r
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.shadow1)};
padding: 0px;
width: 50vw;
overflow-y: ${({ mobile }) => (mobile ? 'scroll' : 'hidden')};
overflow-y: auto;
overflow-x: hidden;
align-self: ${({ mobile }) => (mobile ? 'flex-end' : 'center')};

View File

@@ -1,14 +1,15 @@
import React, { useContext } from 'react'
import { useActiveWeb3React } from '../../hooks'
import { useContext } from 'react'
import { useActiveWeb3React } from '../../hooks/web3'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { AutoColumn, ColumnCenter } from '../Column'
import styled, { ThemeContext } from 'styled-components'
import styled, { ThemeContext } from 'styled-components/macro'
import { RowBetween } from '../Row'
import { TYPE, CloseIcon, CustomLightSpinner } from '../../theme'
import { ArrowUpCircle } from 'react-feather'
import { Trans } from '@lingui/macro'
import Circle from '../../assets/images/blue-loader.svg'
import { getEtherscanLink } from '../../utils'
import { ExternalLink } from '../../theme/components'
const ConfirmOrLoadingWrapper = styled.div`
@@ -32,7 +33,9 @@ export function LoadingView({ children, onDismiss }: { children: any; onDismiss:
</ConfirmedIcon>
<AutoColumn gap="100px" justify={'center'}>
{children}
<TYPE.subHeader>Confirm this transaction in your wallet</TYPE.subHeader>
<TYPE.subHeader>
<Trans>Confirm this transaction in your wallet</Trans>
</TYPE.subHeader>
</AutoColumn>
</ConfirmOrLoadingWrapper>
)
@@ -62,8 +65,13 @@ export function SubmittedView({
<AutoColumn gap="100px" justify={'center'}>
{children}
{chainId && hash && (
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')} style={{ marginLeft: '4px' }}>
<TYPE.subHeader>View transaction on Etherscan</TYPE.subHeader>
<ExternalLink
href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}
style={{ marginLeft: '4px' }}
>
<TYPE.subHeader>
<Trans>View transaction on Explorer</Trans>
</TYPE.subHeader>
</ExternalLink>
)}
</AutoColumn>

View File

@@ -1,15 +1,14 @@
import React from 'react'
import styled from 'styled-components/macro'
import { darken } from 'polished'
import { useTranslation } from 'react-i18next'
import { NavLink, Link as HistoryLink } from 'react-router-dom'
import { Trans } from '@lingui/macro'
import { NavLink, Link as HistoryLink, useLocation } from 'react-router-dom'
import { Percent } from '@uniswap/sdk-core'
import { ArrowLeft } from 'react-feather'
import { RowBetween } from '../Row'
import Row, { RowBetween } from '../Row'
import SettingsTab from '../Settings'
import { useDispatch } from 'react-redux'
import { AppDispatch } from 'state'
import { useAppDispatch } from 'state/hooks'
import { resetMintState } from 'state/mint/actions'
import { resetMintState as resetMintV3State } from 'state/mint/v3/actions'
import { TYPE } from 'theme'
@@ -60,14 +59,13 @@ const StyledArrowLeft = styled(ArrowLeft)`
`
export function SwapPoolTabs({ active }: { active: 'swap' | 'pool' }) {
const { t } = useTranslation()
return (
<Tabs style={{ marginBottom: '20px', display: 'none', padding: '1rem 1rem 0 1rem' }}>
<StyledNavLink id={`swap-nav-link`} to={'/swap'} isActive={() => active === 'swap'}>
{t('swap')}
<Trans>Swap</Trans>
</StyledNavLink>
<StyledNavLink id={`pool-nav-link`} to={'/pool'} isActive={() => active === 'pool'}>
{t('pool')}
<Trans>Pool</Trans>
</StyledNavLink>
</Tabs>
)
@@ -80,7 +78,9 @@ export function FindPoolTabs({ origin }: { origin: string }) {
<HistoryLink to={origin}>
<StyledArrowLeft />
</HistoryLink>
<ActiveText>Import Pool</ActiveText>
<ActiveText>
<Trans>Import V2 Pool</Trans>
</ActiveText>
</RowBetween>
</Tabs>
)
@@ -89,24 +89,29 @@ export function FindPoolTabs({ origin }: { origin: string }) {
export function AddRemoveTabs({
adding,
creating,
positionID,
defaultSlippage,
positionID,
}: {
adding: boolean
creating: boolean
positionID?: string | undefined
defaultSlippage: Percent
positionID?: string | undefined
}) {
const theme = useTheme()
// reset states on back
const dispatch = useDispatch<AppDispatch>()
const dispatch = useAppDispatch()
const location = useLocation()
// detect if back should redirect to v3 or v2 pool page
const poolLink = location.pathname.includes('add/v2')
? '/pool/v2'
: '/pool' + (!!positionID ? `/${positionID.toString()}` : '')
return (
<Tabs>
<RowBetween style={{ padding: '1rem 1rem 0 1rem' }}>
<HistoryLink
to={'/pool' + (!!positionID ? `/${positionID.toString()}` : '')}
to={poolLink}
onClick={() => {
if (adding) {
// not 100% sure both of these are needed
@@ -118,10 +123,29 @@ export function AddRemoveTabs({
<StyledArrowLeft stroke={theme.text2} />
</HistoryLink>
<TYPE.mediumHeader fontWeight={500} fontSize={20}>
{creating ? 'Create a pair' : adding ? 'Add Liquidity' : 'Remove Liquidity'}
{creating ? (
<Trans>Create a pair</Trans>
) : adding ? (
<Trans>Add Liquidity</Trans>
) : (
<Trans>Remove Liquidity</Trans>
)}
</TYPE.mediumHeader>
<SettingsTab placeholderSlippage={defaultSlippage} />
</RowBetween>
</Tabs>
)
}
export function CreateProposalTabs() {
return (
<Tabs>
<Row style={{ padding: '1rem 1rem 0 1rem' }}>
<HistoryLink to="/vote">
<StyledArrowLeft />
</HistoryLink>
<ActiveText style={{ marginLeft: 'auto', marginRight: 'auto' }}>Create Proposal</ActiveText>
</Row>
</Tabs>
)
}

View File

@@ -0,0 +1,123 @@
import { Trans } from '@lingui/macro'
import {
ArbitrumWrapperBackgroundDarkMode,
ArbitrumWrapperBackgroundLightMode,
OptimismWrapperBackgroundDarkMode,
OptimismWrapperBackgroundLightMode,
} from 'components/NetworkAlert/NetworkAlert'
import { L2_CHAIN_IDS, L2_INFO, NETWORK_LABELS, SupportedChainId } from 'constants/chains'
import { useActiveWeb3React } from 'hooks/web3'
import { ArrowDownCircle } from 'react-feather'
import { useArbitrumAlphaAlert, useDarkModeManager } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { MEDIA_WIDTHS } from 'theme'
const L2Icon = styled.img`
display: none;
height: 40px;
margin: auto 20px auto 4px;
width: 40px;
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
display: block;
}
`
const DesktopTextBreak = styled.div`
display: none;
@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) {
display: block;
}
`
const Wrapper = styled.div<{ chainId: SupportedChainId; darkMode: boolean; logoUrl: string }>`
${({ chainId, darkMode }) =>
chainId === SupportedChainId.OPTIMISM
? darkMode
? OptimismWrapperBackgroundDarkMode
: OptimismWrapperBackgroundLightMode
: darkMode
? ArbitrumWrapperBackgroundDarkMode
: ArbitrumWrapperBackgroundLightMode};
border-radius: 20px;
display: flex;
flex-direction: column;
overflow: hidden;
padding: 12px;
position: relative;
width: 100%;
:before {
background-image: url(${({ logoUrl }) => logoUrl});
background-repeat: no-repeat;
background-size: 300px;
content: '';
height: 300px;
opacity: 0.1;
position: absolute;
transform: rotate(25deg) translate(-90px, -40px);
width: 300px;
z-index: -1;
}
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
flex-direction: row;
padding: 16px 20px;
}
`
const Body = styled.div`
line-height: 143%;
margin: 12px;
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
margin: 16px 20px 31px;
flex: 1 1 auto;
margin: 0;
}
`
const LinkOutCircle = styled(ArrowDownCircle)`
transform: rotate(230deg);
width: 20px;
height: 20px;
`
const LinkOutToBridge = styled.a`
align-items: center;
background-color: black;
border-radius: 16px;
color: white;
display: flex;
justify-content: space-between;
margin: 0;
max-height: 47px;
padding: 14px;
text-decoration: none;
width: auto;
:hover,
:focus,
:active {
background-color: black;
}
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
margin: auto 0 auto auto;
padding: 14px 17px;
min-width: 226px;
}
`
export function MinimalNetworkAlert() {
const { chainId } = useActiveWeb3React()
const [darkMode] = useDarkModeManager()
const [arbitrumAlphaAcknowledged] = useArbitrumAlphaAlert()
if (!chainId || !L2_CHAIN_IDS.includes(chainId) || arbitrumAlphaAcknowledged) {
return null
}
const info = L2_INFO[chainId]
return (
<Wrapper darkMode={darkMode} chainId={chainId} logoUrl={info.logoUrl}>
<L2Icon src={info.logoUrl} />
<Body>
<Trans>This is an alpha release of Uniswap on the {NETWORK_LABELS[chainId]} network.</Trans>
<DesktopTextBreak /> <Trans>You must bridge L1 assets to the network to swap them.</Trans>
</Body>
<LinkOutToBridge href={info.bridge} target="_blank" rel="noopener noreferrer">
<Trans>Deposit to {NETWORK_LABELS[chainId]}</Trans>
<LinkOutCircle />
</LinkOutToBridge>
</Wrapper>
)
}

View File

@@ -0,0 +1,155 @@
import { Trans } from '@lingui/macro'
import { L2_CHAIN_IDS, NETWORK_LABELS, SupportedChainId } from 'constants/chains'
import { useActiveWeb3React } from 'hooks/web3'
import { useCallback, useState } from 'react'
import { ArrowDownCircle, X } from 'react-feather'
import { useArbitrumAlphaAlert, useDarkModeManager } from 'state/user/hooks'
import { useETHBalances } from 'state/wallet/hooks'
import styled, { css } from 'styled-components/macro'
import { MEDIA_WIDTHS, TYPE } from 'theme'
import { L2_INFO } from '../../constants/chains'
const L2Icon = styled.img`
width: 40px;
height: 40px;
justify-self: center;
`
const CloseIcon = styled(X)`
cursor: pointer;
position: absolute;
top: 16px;
right: 16px;
`
const ContentWrapper = styled.div`
align-items: center;
display: grid;
grid-gap: 4px;
grid-template-columns: 40px 4fr;
grid-template-rows: auto auto;
margin: 20px 16px;
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
grid-template-columns: 42px 4fr;
grid-gap: 8px;
}
`
export const ArbitrumWrapperBackgroundDarkMode = css`
background: radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),
radial-gradient(75% 75% at 0% 0%, rgba(150, 190, 220, 0.3) 0%, rgba(33, 114, 229, 0.3) 100%), hsla(0, 0%, 100%, 0.1);
`
export const ArbitrumWrapperBackgroundLightMode = css`
background: radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),
radial-gradient(circle at top left, hsla(206, 50%, 75%, 0.01), hsla(215, 79%, 51%, 0.12)), hsla(0, 0%, 100%, 0.1);
`
export const OptimismWrapperBackgroundDarkMode = css`
background: radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.2) 0%, rgba(255, 255, 255, 0.1) 100%),
radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.5) 0%, rgba(235, 0, 255, 0.345) 96%);
`
export const OptimismWrapperBackgroundLightMode = css`
background: radial-gradient(92% 105% at 50% 7%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.03) 100%),
radial-gradient(100% 97% at 0% 12%, rgba(235, 0, 255, 0.1) 0%, rgba(243, 19, 19, 0.1) 100%), hsla(0, 0%, 100%, 0.5);
`
const RootWrapper = styled.div<{ chainId: SupportedChainId; darkMode: boolean; logoUrl: string }>`
${({ chainId, darkMode }) =>
chainId === SupportedChainId.OPTIMISM
? darkMode
? OptimismWrapperBackgroundDarkMode
: OptimismWrapperBackgroundLightMode
: darkMode
? ArbitrumWrapperBackgroundDarkMode
: ArbitrumWrapperBackgroundLightMode};
border-radius: 20px;
display: flex;
flex-direction: column;
max-width: 480px;
min-height: 218px;
overflow: hidden;
position: relative;
width: 100%;
:before {
background-image: url(${({ logoUrl }) => logoUrl});
background-repeat: no-repeat;
background-size: 300px;
content: '';
height: 300px;
opacity: 0.1;
position: absolute;
transform: rotate(25deg) translate(-90px, -40px);
width: 300px;
z-index: -1;
}
`
const Header = styled(TYPE.largeHeader)`
margin: 0;
padding-right: 30px;
`
const Body = styled.p`
grid-column: 1 / 3;
line-height: 143%;
margin: 0;
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
grid-column: 2 / 3;
}
`
const LinkOutCircle = styled(ArrowDownCircle)`
transform: rotate(230deg);
width: 20px;
height: 20px;
`
const LinkOutToBridge = styled.a`
align-items: center;
background-color: black;
border-radius: 16px;
color: white;
display: flex;
justify-content: space-between;
margin: 0 18px 18px 18px;
padding: 14px 24px;
text-decoration: none;
width: auto;
:hover,
:focus,
:active {
background-color: black;
}
`
export function NetworkAlert() {
const { account, chainId } = useActiveWeb3React()
const [darkMode] = useDarkModeManager()
const [arbitrumAlphaAcknowledged, setArbitrumAlphaAcknowledged] = useArbitrumAlphaAlert()
const [locallyDismissed, setLocallyDimissed] = useState(false)
const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? '']
const dismiss = useCallback(() => {
if (userEthBalance?.greaterThan(0)) {
setArbitrumAlphaAcknowledged(true)
} else {
setLocallyDimissed(true)
}
}, [setArbitrumAlphaAcknowledged, userEthBalance])
if (!chainId || !L2_CHAIN_IDS.includes(chainId) || arbitrumAlphaAcknowledged || locallyDismissed) {
return null
}
const info = L2_INFO[chainId]
return (
<RootWrapper chainId={chainId} darkMode={darkMode} logoUrl={info.logoUrl}>
<CloseIcon onClick={dismiss} />
<ContentWrapper>
<L2Icon src={info.logoUrl} />
<Header>
<Trans>Uniswap on {NETWORK_LABELS[chainId]}</Trans>
</Header>
<Body>
<Trans>
This is an alpha release of Uniswap on the {NETWORK_LABELS[chainId]} network. You must bridge L1 assets to
the network to swap them.
</Trans>
</Body>
</ContentWrapper>
<LinkOutToBridge href={info.bridge} target="_blank" rel="noopener noreferrer">
<Trans>Deposit to {NETWORK_LABELS[chainId]}</Trans>
<LinkOutCircle />
</LinkOutToBridge>
</RootWrapper>
)
}

View File

@@ -1,10 +1,12 @@
import { Trans } from '@lingui/macro'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import React, { useEffect } from 'react'
import { X } from 'react-feather'
import styled, { keyframes } from 'styled-components'
import { useCallback, useEffect } from 'react'
import ReactGA from 'react-ga'
import { Heart, X } from 'react-feather'
import styled, { keyframes } from 'styled-components/macro'
import tokenLogo from '../../assets/images/token-logo.png'
import { ButtonPrimary } from '../../components/Button'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { ApplicationModal } from '../../state/application/actions'
import {
useModalOpen,
@@ -62,6 +64,13 @@ export default function ClaimPopup() {
// toggle for showing this modal
const showClaimModal = useModalOpen(ApplicationModal.SELF_CLAIM)
const toggleSelfClaimModal = useToggleSelfClaimModal()
const handleToggleSelfClaimModal = useCallback(() => {
ReactGA.event({
category: 'MerkleDrop',
action: 'Toggle self claim modal',
})
toggleSelfClaimModal()
}, [toggleSelfClaimModal])
// const userHasAvailableclaim = useUserHasAvailableClaim()
const userHasAvailableclaim: boolean = useUserHasAvailableClaim(account)
@@ -70,6 +79,10 @@ export default function ClaimPopup() {
// listen for available claim and show popup if needed
useEffect(() => {
if (userHasAvailableclaim) {
ReactGA.event({
category: 'MerkleDrop',
action: 'Show claim popup',
})
toggleShowClaimPopup()
}
// the toggleShowClaimPopup function changes every time the popup changes, so this will cause an infinite loop.
@@ -92,18 +105,20 @@ export default function ClaimPopup() {
<span role="img" aria-label="party">
🎉
</span>{' '}
UNI has arrived{' '}
<Trans>UNI has arrived</Trans>{' '}
<span role="img" aria-label="party">
🎉
</span>
</TYPE.white>
<TYPE.subHeader style={{ paddingTop: '0.5rem', textAlign: 'center' }} color="white">
{`Thanks for being part of the Uniswap community <3`}
<Trans>
Thanks for being part of the Uniswap community <Heart size={12} />
</Trans>
</TYPE.subHeader>
</AutoColumn>
<AutoColumn style={{ zIndex: 10 }} justify="center">
<ButtonPrimary padding="8px" borderRadius="8px" width={'fit-content'} onClick={toggleSelfClaimModal}>
Claim your UNI tokens
<ButtonPrimary padding="8px" $borderRadius="8px" width={'fit-content'} onClick={handleToggleSelfClaimModal}>
<Trans>Claim your UNI tokens</Trans>
</ButtonPrimary>
</AutoColumn>
</StyledClaimPopup>

View File

@@ -1,112 +0,0 @@
import { diffTokenLists, TokenList } from '@uniswap/token-lists'
import React, { useCallback, useMemo } from 'react'
import ReactGA from 'react-ga'
import { useDispatch } from 'react-redux'
import { Text } from 'rebass'
import styled from 'styled-components/macro'
import { AppDispatch } from '../../state'
import { useRemovePopup } from '../../state/application/hooks'
import { acceptListUpdate } from '../../state/lists/actions'
import { TYPE } from '../../theme'
import listVersionLabel from '../../utils/listVersionLabel'
import { ButtonSecondary } from '../Button'
import { AutoColumn } from '../Column'
import { AutoRow } from '../Row'
export const ChangesList = styled.ul`
max-height: 400px;
overflow: auto;
`
export default function ListUpdatePopup({
popKey,
listUrl,
oldList,
newList,
auto,
}: {
popKey: string
listUrl: string
oldList: TokenList
newList: TokenList
auto: boolean
}) {
const removePopup = useRemovePopup()
const removeThisPopup = useCallback(() => removePopup(popKey), [popKey, removePopup])
const dispatch = useDispatch<AppDispatch>()
const handleAcceptUpdate = useCallback(() => {
if (auto) return
ReactGA.event({
category: 'Lists',
action: 'Update List from Popup',
label: listUrl,
})
dispatch(acceptListUpdate(listUrl))
removeThisPopup()
}, [auto, dispatch, listUrl, removeThisPopup])
const { added: tokensAdded, changed: tokensChanged, removed: tokensRemoved } = useMemo(() => {
return diffTokenLists(oldList.tokens, newList.tokens)
}, [newList.tokens, oldList.tokens])
const numTokensChanged = useMemo(
() =>
Object.keys(tokensChanged).reduce((memo, chainId: any) => memo + Object.keys(tokensChanged[chainId]).length, 0),
[tokensChanged]
)
return (
<AutoRow>
<AutoColumn style={{ flex: '1' }} gap="8px">
{auto ? (
<TYPE.body fontWeight={500}>
The token list &quot;{oldList.name}&quot; has been updated to{' '}
<strong>{listVersionLabel(newList.version)}</strong>.
</TYPE.body>
) : (
<>
<div>
<Text>
An update is available for the token list &quot;{oldList.name}&quot; (
{listVersionLabel(oldList.version)} to {listVersionLabel(newList.version)}).
</Text>
<ChangesList>
{tokensAdded.length > 0 ? (
<li>
{tokensAdded.map((token, i) => (
<React.Fragment key={`${token.chainId}-${token.address}`}>
<strong title={token.address}>{token.symbol}</strong>
{i === tokensAdded.length - 1 ? null : ', '}
</React.Fragment>
))}{' '}
added
</li>
) : null}
{tokensRemoved.length > 0 ? (
<li>
{tokensRemoved.map((token, i) => (
<React.Fragment key={`${token.chainId}-${token.address}`}>
<strong title={token.address}>{token.symbol}</strong>
{i === tokensRemoved.length - 1 ? null : ', '}
</React.Fragment>
))}{' '}
removed
</li>
) : null}
{numTokensChanged > 0 ? <li>{numTokensChanged} tokens updated</li> : null}
</ChangesList>
</div>
<AutoRow>
<div style={{ flexGrow: 1, marginRight: 12 }}>
<ButtonSecondary onClick={handleAcceptUpdate}>Accept update</ButtonSecondary>
</div>
<div style={{ flexGrow: 1 }}>
<ButtonSecondary onClick={removeThisPopup}>Dismiss</ButtonSecondary>
</div>
</AutoRow>
</>
)}
</AutoColumn>
</AutoRow>
)
}

View File

@@ -1,14 +1,13 @@
import React, { useCallback, useContext, useEffect } from 'react'
import { useCallback, useContext, useEffect } from 'react'
import { X } from 'react-feather'
import { useSpring } from 'react-spring/web'
import styled, { ThemeContext } from 'styled-components'
import styled, { ThemeContext } from 'styled-components/macro'
import { animated } from 'react-spring'
import { PopupContent } from '../../state/application/actions'
import { useRemovePopup } from '../../state/application/hooks'
import ListUpdatePopup from './ListUpdatePopup'
import TransactionPopup from './TransactionPopup'
export const StyledClose = styled(X)`
const StyledClose = styled(X)`
position: absolute;
right: 10px;
top: 10px;
@@ -17,7 +16,7 @@ export const StyledClose = styled(X)`
cursor: pointer;
}
`
export const Popup = styled.div`
const Popup = styled.div`
display: inline-block;
width: 100%;
padding: 1em;
@@ -77,11 +76,6 @@ export default function PopupItem({
txn: { hash, success, summary },
} = content
popupContent = <TransactionPopup hash={hash} success={success} summary={summary} />
} else if ('listUpdate' in content) {
const {
listUpdate: { listUrl, oldList, newList, auto },
} = content
popupContent = <ListUpdatePopup popKey={popKey} listUrl={listUrl} oldList={oldList} newList={newList} auto={auto} />
}
const faderStyle = useSpring({

Some files were not shown because too many files have changed in this diff Show More