Compare commits

...

358 Commits

Author SHA1 Message Date
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
Moody Salem
af6098bfe5 feat: allow walletconnect on testnets (#1389)
* try walletconnect multinetwork

* clean up environment variables for multinetwork

* remove irrelevant config

* move some stuff around

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2021-05-13 17:46:30 -05:00
Moody Salem
fce29bb36f fix: division by 0 when computing the USDC value of tokens that do not have a v2 pair (fixes https://github.com/Uniswap/uniswap-interface/issues/1566) 2021-05-13 17:27:34 -05:00
Noah Zinsmeister
4517af39ba don't jump on mouse exit (#1565) 2021-05-13 17:06:25 -04:00
Noah Zinsmeister
b40163ce05 allow fee collection/liquidity removal in weth (#1553)
* add dummy flags for burn/collect as weth

* add toggles

* clean up toggle position

* only show weth toggle if collection is possible
2021-05-13 14:49:54 -04:00
Moody Salem
809902efec fixes https://github.com/Uniswap/uniswap-interface/issues/1386 2021-05-13 12:17:34 -05:00
Noah Zinsmeister
70be9894fa bump walletlink connector (#1563) 2021-05-13 12:35:17 -04:00
Moody Salem
698ad5bac9 bump v3 sdk 2021-05-13 11:21:07 -05:00
Moody Salem
cd37b7533d put the error reason in the message so we can differentiate between unrecognized errors 2021-05-13 11:16:37 -05:00
Justin Domingue
c0cd6a1c8d handle ape= query parameter (#1555)
* handle expert_mode= and ape= query parameters

* removed console log

* use useParsedQueryString

* only handle setting ape mode
2021-05-13 12:01:58 -04:00
Moody Salem
f6245d1093 retry more frequently, couple more error nits 2021-05-13 10:51:06 -05:00
Moody Salem
83c784f7c0 improve the error coverage 2021-05-13 10:51:06 -05:00
Moody Salem
3d95b1a33b fix access of undefined property 2021-05-13 09:37:04 -05:00
Moody Salem
5c96942922 fix: show better errors 2021-05-13 09:02:48 -05:00
Justin Domingue
d27c83b382 use styled-components/macro to display className in dev builds (#1541) 2021-05-13 09:15:13 -04:00
Moody Salem
b2f88965a9 handle errors better 2021-05-12 22:05:00 -05:00
Moody Salem
95db44e0fc add a little state to the automatic issue body 2021-05-12 18:20:16 -05:00
Moody Salem
7d45ff5ca8 fix 0 decimal tokens error 2021-05-12 17:29:02 -05:00
Moody Salem
8964cf86aa nit with how we convert percent to negative value 2021-05-12 17:13:40 -05:00
Moody Salem
0e9f23ed56 hover text nit 2021-05-12 17:12:16 -05:00
Moody Salem
e08e597655 show list in import token dialog 2021-05-12 17:01:20 -05:00
Moody Salem
744db49803 do not show duplicate token results, and stop searching as soon as possible 2021-05-12 16:54:05 -05:00
Moody Salem
54f59e02fd add page url to the issue template 2021-05-12 16:17:46 -05:00
Moody Salem
7950e5c083 fixes https://github.com/Uniswap/uniswap-interface/issues/1548 2021-05-12 16:16:00 -05:00
Ian Lapham
dd33205bf6 update string in token amount formatting (#1539) 2021-05-12 14:19:01 -04:00
Moody Salem
397a20b9ec fix the issue title 2021-05-12 13:14:02 -05:00
Moody Salem
0aac0b43aa clean up the error boundary 2021-05-12 13:11:10 -05:00
Ian Lapham
702500794d truncate and format large numbers (#1518)
* truncate and format large numbers

* update truncation shorthand

* update logic for detecting small amount
2021-05-12 14:08:05 -04:00
Moody Salem
8bea95fab2 fix the transaction deadline errors 2021-05-12 13:01:31 -05:00
Moody Salem
27094c87f2 clean up price impact formatting 2021-05-12 12:39:21 -05:00
Justin Domingue
bede9171c3 add header background on scroll body overlap (#1531)
* add header background-color on scroll overlap

* slide background in/out on scroll

* remove unused import

Co-authored-by: Justin Domingue <domingue.justin@gmail.com>
2021-05-12 13:19:43 -04:00
Justin Domingue
251d7c0bc2 Fix NFT SVG performance issue in browser (#1509)
* only animate NFT SVG on hover by using a canvas

* handle high dpis

* animation transition between canvas and img

* set start state to not animated

* removed animations that were causing issues on Firefox

* simplify code

* remove debugger statement

* remove useEffect in favor of an event handler

* hide canvas without unmounting to avoid blinking

* fix lint error

* fix flicker on hover by leaving canvas always visible

* add comment about z-index

Co-authored-by: Justin Domingue <domingue.justin@gmail.com>
2021-05-12 13:02:52 -04:00
Moody Salem
285e4f28f5 fix bug in fiat value price impact display 2021-05-12 11:56:18 -05:00
Moody Salem
3aa045303a remove a few unused isToken calls 2021-05-12 11:43:30 -05:00
Noah Zinsmeister
e0a7c3794e bump gas margin to 20% 2021-05-12 12:35:46 -04:00
Noah Zinsmeister
f5fc5da341 fix erroneous addition of tokensOwned{0,1} (#1533)
only pass tokenId to useV3PositionFees
2021-05-12 12:20:20 -04:00
Moody Salem
bea5c0484b fix lint errors, show source list in extra tokens 2021-05-12 09:32:24 -05:00
Moody Salem
27960532ca refactor: use new sdk version (#1472)
* do not construct tokens for wrapped token info

* some cleanup of the wrapped token info

* back to extends, bump sdk core version via v2/v3 sdk updates

* Revert "back to extends, bump sdk core version via v2/v3 sdk updates"

This reverts commit 92cc5073

* update the sdk version

* fix some more uses of instanceof

* finish the refactor

* mess with the currency list performance

* start replacing with the latest v3/v2 sdks

* raw -> quotient

* more cleanup

* finish the refactor

* clean up currency list refactor

* fix list rendering

* perf(token lists): improve app performance when there are large inactive token lists (#1510)

* improve inactive token lists performance

* cleanup before larger changes to combine inactive lists

* only do the search if the query does not match any active lists, limit the number of results

* more performance improvements

* search inactive lists more aggressively
2021-05-12 08:52:17 -05:00
Ian Lapham
37a4e2f6e3 More UI bug fixes (#1515)
* fix for error token map parsings

* update varios UI styles

* update padding on & amounts
2021-05-11 22:29:25 -04:00
Moody Salem
19a3b12ca8 bump typechain for faster/less noisy type generation 2021-05-11 16:43:08 -05:00
Noah Zinsmeister
22c1ddf393 swaps to .5% slippage 2021-05-11 15:00:51 -04:00
Noah Zinsmeister
b44ae1a267 clean up parseCurrencyFromURLParameter 2021-05-11 14:19:56 -04:00
Ian Lapham
418dcf0cb2 Various bug fixes (#1501)
* fix for error token map parsings

* fix error on formatting sig figs

* fix various bugs

* no hover cursor

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2021-05-11 14:03:02 -04:00
Noah Zinsmeister
58a508c9d6 .25% -> .30% slippage for v3 swaps 2021-05-11 13:53:15 -04:00
Jorropo
3198129af2 feat(routing): support mirror protocol routing as additional bases (#1375)
* feat(routing): support mirror protocol routing as additional bases

* Fix code style issues with ESLint

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-05-11 13:28:50 -04:00
John Shutt
89d484d882 feat(uma): uma call option routing (#1385)
* feat(uma): uma call option routing

Signed-off-by: John Shutt <john.d.shutt@gmail.com>

* Fix code style issues with ESLint

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
Co-authored-by: Moody Salem <moodysalem@users.noreply.github.com>
2021-05-11 13:25:51 -04:00
Callil Capuozzo
fa4688d96c UI improvements (#1505)
* Change price ratio using slash to "per"

* Fix header, toggle copy and increase copy

* Add clearer V2 and migrate buttons

* Fix link

* fix account modal background color

* tweak sig figs

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2021-05-11 13:25:04 -04:00
Moody Salem
7ee761a59e feat: automatic slippage tolerance (#1463)
* automatic slippage tolerance start

* get it compiling

* out of range/in range behavior of slippage tolerance in add

* small useDerivedSwapInfo refactor

* improve useSwapSlippageTolerance

* fix unit test

* thread placeholder slippage through

* small improvement to slippage input behavior

* fix the display bug

* fix tx settings modal ux

* don't pass props unnecessarily

* switch back to static swap slippage for now

bump migrate slippage to .75%

* fix font size

* add flag for auto slippage migration

validate version updates even more

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2021-05-11 13:00:42 -04:00
jochenboesmans
78e95f6073 Add App-level error boundary, referring users to GitHub issue creation (#1464)
* Add App-level error boundary, referring users to GitHub issue creation on page crashes. (#1452)

* Class component is used as boundary since catching errors is apparently not yet possible with hooks.

* EventListener in window was removed and replaced by error boundary's error catch, which now fires a GA exception. The fields it passes are slightly different because React uses slightly different error types.

* Pre-filling issues with dynamic data is possible with POST requests to GitHub's API, but the GH web client seems to only support pre-fill based on templates. Therefore users still need to copy error info themselves.

* Prefill GitHub issues with crash data.

* Added package 'react-device-detect' to include device data such as OS, browser etc. in crash report.
* Included error stack in issue body.
* Used <code> html tag for displaying stack to user.

* Slightly reduce vertical padding on code block.

* Add ua-parser-js for parsing user agent.

* Revert react-device-detect to ^1.6.2 (which is used for mobile detection etc. in components)
2021-05-11 12:09:01 -04:00
Noah Zinsmeister
c67e57505a make price sig figs more consistent 2021-05-11 10:17:06 -04:00
Justin Domingue
30f7385db7 optimize sandtexture.png with .webp (#1502)
Co-authored-by: Justin Domingue <domingue.justin@gmail.com>
2021-05-10 21:32:53 -04:00
Noah Zinsmeister
c0f58ae810 don't use a signer for callStatic contract 2021-05-10 16:59:11 -04:00
Noah Zinsmeister
54dd5476ca fetch fees directly from collect via callStatic (#1500)
* fetch fees directly from collect via callStatic

* don't clear state
2021-05-10 16:30:02 -04:00
Moody Salem
57786335df fix calculateSlippageAmount (#1497) 2021-05-10 14:22:26 -05:00
Joe Butler
948e01a196 Fix typo (#1454) 2021-05-10 14:01:35 -04:00
Noah Zinsmeister
abf127c596 sdk bugfix bump (#1492) 2021-05-10 13:04:07 -04:00
Moody Salem
4d3f870b93 add a test for calculating slippage amounts 2021-05-09 12:31:45 -05:00
Moody Salem
452f2dc3c0 fix slippage amount bug https://github.com/Uniswap/uniswap-interface/issues/1473 2021-05-09 11:30:56 -05:00
Ian Lapham
b6bd59f2b1 Fix bug on formatted token amounts when decimals < sig fig (#1479)
* fix for error token map parsings

* fix error on formatting sig figs
2021-05-07 16:52:00 -04:00
Noah Zinsmeister
0190b5a408 bump sdk to fix add/remove slippage 2021-05-06 18:20:36 -04:00
Noah Zinsmeister
d6030dcd45 add settings tab to migrate 2021-05-06 17:44:05 -04:00
Moody Salem
f0e2a491dc fix(slippage settings): improve slippage tolerance warnings 2021-05-06 11:19:36 -04:00
Moody Salem
021aab6547 fix(wallet): workaround the ethers bug to fix other wallets 2021-05-06 11:10:45 -04:00
Noah Zinsmeister
81af31eec1 estimate gas in migrate v2 2021-05-06 10:40:19 -04:00
Moody Salem
d3898cf900 fix(wallet): workaround for coinbase wallet / fortmatic 2021-05-06 10:09:41 -04:00
Moody Salem
b8f61d5f90 fix(positions list): base/currency ordering 2021-05-06 09:57:12 -04:00
Moody Salem
6c880d29a6 fix(position fee computation): incorrect sub underflow 2021-05-05 22:54:30 -04:00
Lint Action
a8baa6d6a5 Fix code style issues with ESLint 2021-05-05 21:56:49 +00:00
Callil Capuozzo
00a1dee073 Merge branch 'main' of https://github.com/Uniswap/uniswap-interface into main 2021-05-05 17:54:47 -04:00
Callil Capuozzo
a6de7d7846 add links 2021-05-05 17:54:33 -04:00
Moody Salem
e1a81a9996 uniswap protocol disclaimer 2021-05-05 17:40:22 -04:00
Noah Zinsmeister
0770bab032 try manual approve if erc712 doesn't work (#1397)
* initial impl

* fix async logic
2021-05-05 17:36:07 -04:00
Callil Capuozzo
e5404dbf97 Fix some links 2021-05-05 16:04:40 -04:00
Noah Zinsmeister
78e41848f2 another copy fix 2021-05-05 14:26:53 -04:00
Noah Zinsmeister
77c090534b copy hotfix 2021-05-05 14:26:02 -04:00
Moody Salem
05acbfee88 fix(swap): do not throw an error in the routing if we cannot construct a pool 2021-05-05 14:22:57 -04:00
Callil Capuozzo
64dd2f9ed1 update button styles 2021-05-05 13:58:37 -04:00
Callil Capuozzo
09f30ce0f7 Merge branch 'main' of https://github.com/Uniswap/v3-interface into main 2021-05-05 13:41:47 -04:00
Callil Capuozzo
a49d5382db UI tweaks 2021-05-05 13:41:46 -04:00
Ian Lapham
3affcb8d32 small ux style updates (#160) 2021-05-05 13:18:47 -04:00
Moody Salem
56e759ff78 fix unit test 2021-05-05 13:10:19 -04:00
Callil Capuozzo
f73a166d92 Merge branch 'main' of https://github.com/Uniswap/v3-interface into main 2021-05-05 13:07:26 -04:00
Callil Capuozzo
e1e52c06db update copy 2021-05-05 13:06:26 -04:00
Moody Salem
ca4d915903 fix it everywhere 2021-05-05 13:04:50 -04:00
Moody Salem
bdb6327c13 set initial allowed slippage to 10 bips instead of 50 bips 2021-05-05 13:03:39 -04:00
Noah Zinsmeister
ce81dd5a79 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-05-05 12:47:47 -04:00
Noah Zinsmeister
73f29eea2c don't show approve for both assets...
...in the single-sided liquidity provision case
2021-05-05 12:47:41 -04:00
Moody Salem
92193076c5 fixes https://github.com/Uniswap/v3-interface/issues/161 2021-05-05 12:45:29 -04:00
Moody Salem
1aab086693 Swap interface tweaks (#159)
* fix some nits in the swap interface

* remove some unused css

* tweak the info icon a bit
2021-05-05 12:29:33 -04:00
Noah Zinsmeister
8057cb9fbe fix rate toggle clearing add liquidity state 2021-05-05 12:19:21 -04:00
Ian Lapham
1b798889af add default increment behavior (#157)
Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2021-05-05 12:12:23 -04:00
Connor Martin
1e54b97693 changed to v3 docs links (#138) 2021-05-05 12:05:13 -04:00
Noah Zinsmeister
02c21ef720 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-05-05 12:03:29 -04:00
Noah Zinsmeister
b39aeeb805 fix for inactive positions 2021-05-05 12:03:20 -04:00
Callil Capuozzo
8401a4b9b4 Update styles, change warning modals 2021-05-05 11:43:47 -04:00
Callil Capuozzo
3b27ee94d7 Merge branch 'main' of https://github.com/Uniswap/v3-interface into main 2021-05-05 11:42:04 -04:00
Noah Zinsmeister
660c355273 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-05-05 11:19:02 -04:00
Noah Zinsmeister
cd3c48462d separate v2 + v3 mint reducers
reset v3 mint state on migrate mount/unmount
2021-05-05 11:18:54 -04:00
Moody Salem
acfd5c2720 fixes https://github.com/Uniswap/v3-interface/issues/123 2021-05-05 10:23:45 -04:00
Callil Capuozzo
a5ed12bfc7 Merge branch 'main' of https://github.com/Uniswap/v3-interface into main 2021-05-05 00:08:15 -04:00
Noah Zinsmeister
40f0e619cc fix additional base currency toggle bugs 2021-05-04 22:43:36 -04:00
Noah Zinsmeister
bd817083c9 actually fix isNotUniswap logic 2021-05-04 22:32:01 -04:00
Callil Capuozzo
699bcc25b6 Merge branch 'main' of https://github.com/Uniswap/v3-interface into main 2021-05-04 20:52:27 -04:00
Callil Capuozzo
e88a8effef style tweaks
- bring back gradient background
- improve header
- improve light mode
2021-05-04 20:52:25 -04:00
Ian Lapham
2339817170 multiple bug fixes (#141)
* multiple bug fixes

* remove unused local

* disable currency select on bottom liquidity
2021-05-04 20:17:19 -04:00
Moody Salem
4220cafbd3 fix the usdc permit 2021-05-04 18:22:45 -05:00
Noah Zinsmeister
a61df58599 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-05-04 19:15:46 -04:00
Noah Zinsmeister
1b35128035 clarify migrate ui 2021-05-04 19:15:29 -04:00
Moody Salem
ab5114c5f5 move the compute price impact function out to a utility file 2021-05-04 17:55:51 -05:00
Moody Salem
28c7cfa1f1 formatted price impact 2021-05-04 17:52:23 -05:00
Moody Salem
509b307b67 show some price impact in the interface 2021-05-04 17:51:01 -05:00
Ian Lapham
1e5519de3f add dark mdoe toggle, update some styles (#134) 2021-05-04 18:25:20 -04:00
Noah Zinsmeister
e8587396d3 hide collect fees if not owner
link to owner
2021-05-04 17:48:54 -04:00
Noah Zinsmeister
d69b194ffb close #127 2021-05-04 17:32:24 -04:00
Noah Zinsmeister
1325443025 fix ratio bugs 2021-05-04 17:03:17 -04:00
Moody Salem
1607f8919a code in the mainnet addresses 2021-05-04 14:44:55 -05:00
Moody Salem
873cf9760e fix addresses 2021-05-04 14:39:02 -05:00
Noah Zinsmeister
b39f2fe055 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-05-04 14:58:53 -04:00
Noah Zinsmeister
516e783be6 hot fix for asset composition formula 2021-05-04 14:58:29 -04:00
Callil Capuozzo
9a326fa023 Add liquidity styles (#98)
* Start styles

* Add liquidity styles & consistency

* clean up styles

* remove un-used element

* New styles

* fix errors
2021-05-04 14:57:57 -04:00
Noah Zinsmeister
06d6c711dd closes #94 2021-05-04 14:17:28 -04:00
Noah Zinsmeister
7178746023 highlight 100% position composition 2021-05-04 14:04:30 -04:00
Moody Salem
2efe8250ae rename gorli to goerli 2021-05-04 12:31:51 -05:00
Moody Salem
24d8b4abc9 latest deploy 2021-05-04 12:23:07 -05:00
Noah Zinsmeister
aec18b7eb1 closes #107 2021-05-04 13:22:26 -04:00
Noah Zinsmeister
0c37e81d97 closes #108 2021-05-04 13:20:52 -04:00
Noah Zinsmeister
624c3678c7 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-05-04 13:18:27 -04:00
Noah Zinsmeister
cdae20f2ed fix weth/eth bug
closes #47
2021-05-04 13:18:21 -04:00
Ian Lapham
042967502e recreate bug fixes (#109) 2021-05-04 12:43:43 -04:00
Ian Lapham
eedba9795e Mobile styles (#100)
* mobile styles

* remove unused local

* update react imports

* fix imports

Co-authored-by: Callil Capuozzo <callil.capuozzo@gmail.com>
2021-05-04 11:05:26 -04:00
Noah Zinsmeister
eaec4a33fc bump v3 sdk 2021-05-04 10:31:23 -04:00
Moody Salem
300dd70804 show currency values and price impact in the confirmation modal (fixes https://github.com/Uniswap/v3-interface/issues/92) 2021-05-04 08:38:47 -05:00
Lint Action
f4e994867e Fix code style issues with ESLint 2021-05-04 01:40:43 +00:00
Moody Salem
7ca79ff12b fix unit tests 2021-05-03 20:39:03 -05:00
Moody Salem
4903258b7c Merge branch 'main' into v3-main
# Conflicts:
#	src/pages/Vote/VotePage.tsx
#	src/pages/Vote/index.tsx
2021-05-03 20:37:44 -05:00
jochenboesmans
fe35ca9db8 use enum for enumerateProposalState #1166 (#1381)
* Use ts enum for ProposalState in state/governance/hooks.ts (#1166)

* Use ProposalState enum in Vote/styled.tsx for determining colors of state text etc. (#1166)
2021-05-03 20:33:34 -05:00
Moody Salem
4bc1b8eb5c Merge branch 'main' into v3-main 2021-05-03 20:30:46 -05:00
AmagiDDmxh
7801695180 fix(block number): Update polling block number breathing behavior (#1379) 2021-05-03 20:30:32 -05:00
Moody Salem
ee69357305 some minor refactoring with price impact functions 2021-05-03 20:25:23 -05:00
Moody Salem
ce7f94f16a hide max button appropriately 2021-05-03 20:17:15 -05:00
Moody Salem
124b83ae56 some clean up in showing the route 2021-05-03 20:13:52 -05:00
Moody Salem
0dcd5743c8 fix price impact handling/coloring 2021-05-03 20:05:20 -05:00
Moody Salem
9fec8dbe93 show percentage difference in price on swap 2021-05-03 17:13:05 -05:00
Noah Zinsmeister
549d4e38c6 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-05-03 17:43:49 -04:00
Noah Zinsmeister
3af6781821 add % breakdown for assets in range
closes #64
2021-05-03 17:43:39 -04:00
Moody Salem
fbccb83edb Merge branch 'main' into v3-main 2021-05-03 16:30:04 -05:00
Moody Salem
ff12b7be10 refactor the fiat values 2021-05-03 16:29:45 -05:00
Callil Capuozzo
4c2cb5b0c1 Swap polish 02 (#93)
* rework swap UI

* Add v2/v3 route toggle UX

* improve button and progress styles

* put optional route in swap header

* Further refinements

* Alt swap layout

* clean up

* Tweak route ui

* merge main

* update swap header

* adjust currency select ui
2021-05-03 16:32:40 -04:00
Callil Capuozzo
d7785942b1 Update migration styles (#78)
* re-work migrate page

* Layout Tweaks

* cleanup

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2021-05-03 15:55:30 -04:00
Noah Zinsmeister
ed801deb15 add fiat value of liquidity + fees 2021-05-03 15:19:59 -04:00
Noah Zinsmeister
c34727641d add basic sushi support (#87) 2021-05-03 14:48:30 -04:00
Ian Lapham
807860aac6 refactor review modals (#60) 2021-05-03 13:37:41 -04:00
Ian Lapham
5a9034fe95 fix for error token map parsings (#1384) 2021-05-03 13:34:01 -04:00
Ian Lapham
ee0db4f2aa fix list parsing on broken tokens (#85)
* fix list parsing on broken tokens

* revert to most recent changes
2021-05-03 13:28:55 -04:00
Ian Lapham
6d5625a1f8 Hot fix for token error parsing (#1382)
* hot fix for token list parsing

* revert tsconfig
2021-05-03 12:11:27 -04:00
Callil Capuozzo
1619386ab4 Position styles (#55)
* Better position list layout WIP

* Position list updates

* add badge data and current price hover

* merge cleanup

* fix missing library

* position page improvements

* Clean up position page and overview

* layout and color updates

* Clean up page

* Clean up position page

* clean up errors

* Add icons

* Merge main

* Position styles tweaks

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2021-05-03 11:50:54 -04:00
Moody Salem
93d33947da fix bug in remove 2021-04-30 22:57:55 -05:00
Moody Salem
91f3e21bd4 some list code cleanup 2021-04-30 22:56:19 -05:00
Moody Salem
e9a432b58e fix a bug in v3 trade routing returning the wrong trade type for output trade 2021-04-30 22:43:29 -05:00
Moody Salem
1f41587ba9 fix the swap notification 2021-04-30 22:38:24 -05:00
Moody Salem
9b639bee65 invalidate signature data for old nonce 2021-04-30 21:23:16 -05:00
Moody Salem
eaddc9e6f8 dai style permit 2021-04-30 21:20:39 -05:00
Noah Zinsmeister
e83a1ec923 undupe migrate menu 2021-04-30 15:55:53 -04:00
Noah Zinsmeister
8021315f87 fix rates in add liquidity 2021-04-30 15:40:39 -04:00
Noah Zinsmeister
0540012bb9 WETH -> ETH in migrate
fix rates in migrate

fix migrate link
2021-04-30 15:12:00 -04:00
Moody Salem
3f6bf607dd replace uniswap.info links 2021-04-30 13:14:31 -05:00
Moody Salem
828f7ee446 Use permit in swap (#65)
* add a usePermit hook

* semi working

* Fix code style issues with ESLint

* don't override gas, some cleanup in the permit function

* fix permit validity

* some more permit fixes

* nits

* another nit

* use the erc20 permit hook everywhere

* unused exports

* handle missing version

* replace everywhere

* add DAI and todos

* woopsie bug

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-04-30 12:50:57 -05:00
Noah Zinsmeister
432d17bdfd only show buttons if nft is owned 2021-04-29 15:55:39 -04:00
Noah Zinsmeister
9743b211e7 change migration threshold to 2
fix starting price bug
2021-04-29 15:39:44 -04:00
Noah Zinsmeister
dce187e433 fix price bug 2021-04-29 14:18:15 -04:00
Moody Salem
8859ff6979 fix gas estimate bug 2021-04-29 11:56:02 -05:00
Moody Salem
f566a72b06 fix a bug in the logo not passing style down 2021-04-29 11:31:34 -05:00
Moody Salem
e39e5908e7 drop the card in the position page 2021-04-29 11:18:56 -05:00
Moody Salem
c5424d2dcc some tweaks to the token logo 2021-04-29 10:26:12 -05:00
Noah Zinsmeister
8b4bc167c5 add multicall mainnet address 2021-04-29 11:01:53 -04:00
Moody Salem
43bceb232e label nit 2021-04-28 21:23:22 -05:00
Moody Salem
c3909bc1d0 improve the better trade link logic 2021-04-28 20:03:38 -05:00
Moody Salem
b25287923c return fake price data for non mainnet and fill it into the currency input panel 2021-04-28 19:44:10 -05:00
Noah Zinsmeister
65c51ea4aa update to latest sdk 2021-04-28 20:17:22 -04:00
Moody Salem
b0fa08e9b0 show a loading indicator 2021-04-28 19:11:27 -05:00
Moody Salem
b09eb8fb52 mess with the gas estimation code a bit to allow swapping 2021-04-28 18:58:53 -05:00
Moody Salem
5b49cedebb bump addresses 2021-04-28 18:16:49 -05:00
Noah Zinsmeister
ae76f26501 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-04-28 17:59:10 -04:00
Noah Zinsmeister
1cb1ffe2f6 update to latest formatting logic
nits
2021-04-28 17:58:59 -04:00
Moody Salem
d83bc3097d fix some linting errors and some better trade link cleanup 2021-04-28 16:47:03 -05:00
Moody Salem
323edc0fcd consistently use the worst price and amounts in the swap view 2021-04-28 16:42:56 -05:00
Moody Salem
fc258fdf5c don't throw if the trades are not comparable 2021-04-28 13:34:22 -05:00
Moody Salem
2e599dc00e handle loading v3 trade state better 2021-04-28 13:31:30 -05:00
Moody Salem
464a682fcc show the better trade link for v2/v3 2021-04-28 13:27:14 -05:00
Moody Salem
6ace3c211c fix linting errors 2021-04-28 12:55:56 -05:00
Moody Salem
2d0b0c70bf drop the show details stuff 2021-04-28 12:55:28 -05:00
Moody Salem
3c3be3c61a fix some unit tests 2021-04-28 12:17:40 -05:00
Noah Zinsmeister
7660943e97 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-04-27 22:25:48 -04:00
Noah Zinsmeister
d342ccdb78 use sdk for more calldata 2021-04-27 22:24:29 -04:00
Moody Salem
0ea9dc046b bumped v3 sdk version 2021-04-27 19:42:39 -05:00
Moody Salem
3a34c2ec25 eth swaps kinda working 2021-04-27 19:20:05 -05:00
Moody Salem
83b0ef94f9 show the worst amounts/prices instead of current price 2021-04-27 13:07:07 -05:00
Moody Salem
3680b93fb3 make the way we get the v2 router consistent, start working through changes in the swap callback 2021-04-27 11:00:34 -05:00
Moody Salem
8e86ded09b temporarily hude the usdc value, use the disable multihop setting in the v3 routing 2021-04-27 10:35:09 -05:00
Moody Salem
37a50372f6 improve responsiveness of header 2021-04-27 10:16:02 -05:00
Noah Zinsmeister
ab99cc612b add current price 2021-04-27 10:51:12 -04:00
Moody Salem
11a4fa23c1 fix the approve callback from trade bug 2021-04-26 21:26:57 -05:00
Callil Capuozzo
5358b4dc15 New swap layout (#53)
* re-work header

* swap tweaks

* re-work swap in progress

- inline slippage
- hidey details
- better invert UI
- better flip rate ui

* Swap improvements

- new layout order

* New swap layout

* merge main

* get all the tests running

* skip the swap test as well

Co-authored-by: Moody Salem <moody.salem@gmail.com>
2021-04-26 20:59:56 -05:00
Moody Salem
ccbd5dfcf7 show v3 quotes 2021-04-26 19:35:32 -05:00
Noah Zinsmeister
bb17c57a84 nits 2021-04-26 15:51:31 -04:00
Noah Zinsmeister
3b6213f411 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-04-26 14:28:45 -04:00
Noah Zinsmeister
a5251f55de improve stepping behavior 2021-04-26 14:28:36 -04:00
Moody Salem
d846c83afa handle errored calls better 2021-04-26 13:23:53 -05:00
Noah Zinsmeister
00438bea12 fix tick bug 2021-04-26 14:21:42 -04:00
Noah Zinsmeister
0dd5e6b33f Merge remote-tracking branch 'refs/remotes/origin/main' 2021-04-26 14:15:40 -04:00
Noah Zinsmeister
2d7642ed9b simplify range ui 2021-04-26 14:15:32 -04:00
Moody Salem
740e18454f fix linting errors for the preview 2021-04-26 13:01:13 -05:00
Moody Salem
7c17cfc642 bump and use the unchecked trade creation method 2021-04-26 12:29:20 -05:00
Moody Salem
7f61b67947 first pass v3 routing code 2021-04-26 12:17:37 -05:00
Moody Salem
466c0b0142 remove the anonymous credentials from the image (it causes a CORS preflight) 2021-04-26 10:53:55 -05:00
Moody Salem
1c6d9d810e keep list asset requests anonymous 2021-04-26 10:50:34 -05:00
Noah Zinsmeister
c3ad129658 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-04-26 11:20:49 -04:00
Moody Salem
a25a72d0d9 use path encoding from v3-sdk 2021-04-26 10:14:21 -05:00
Noah Zinsmeister
dbc9e85b90 bug fixes 2021-04-26 11:13:02 -04:00
Ian Lapham
7dafb0cfba Remove UI (#52)
* use token0 as base in all calculations

* refactor

* fix price order

* fix existing position ticks

* remove empty space

* basic remove page

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2021-04-25 18:46:53 -04:00
Ian Lapham
da33ec9d2f use token0 as base in all calculations (#51)
* use token0 as base in all calculations

* refactor

* fix price order

* fix existing position ticks

* remove empty space

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2021-04-25 15:45:37 -04:00
Moody Salem
605368629f surface the best v3 route to the swap page 2021-04-24 20:27:56 -05:00
Moody Salem
ca2b84ec0b get the best route in v3 for an exact in swap 2021-04-24 18:52:13 -05:00
Moody Salem
ac7cf35bb7 missed a spot 2021-04-24 17:43:57 -05:00
Moody Salem
bd346030f0 useAllV3Routes, some cleanup 2021-04-24 17:43:14 -05:00
Moody Salem
aa742f415d create some hooks for using in v3 swap routing 2021-04-23 21:32:31 -05:00
Moody Salem
77fa61495f fix loading state flashing in pool page 2021-04-23 21:12:34 -05:00
Moody Salem
bff3811faf use multicall2 2021-04-23 18:26:24 -05:00
Moody Salem
60d1f8743f just a bit more refactoring to remove the data directory 2021-04-23 17:57:48 -05:00
Moody Salem
2d118a904a refactor some hooks into the hooks directory 2021-04-23 17:54:24 -05:00
Moody Salem
b69f08cbe1 allow fetching multiple pools via usePools.ts 2021-04-23 17:31:36 -05:00
Noah Zinsmeister
644ecdbd32 extra / 2021-04-23 13:04:12 -04:00
Noah Zinsmeister
4b47ae6da3 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-04-23 13:03:25 -04:00
Noah Zinsmeister
fe6a46fc0f css bug fix 2021-04-23 13:03:06 -04:00
Moody Salem
539a6e05e5 don't fetch pool tick data 2021-04-23 11:58:07 -05:00
Noah Zinsmeister
3693c83b6e remove liquidity sends ETH now
can collect fees from position page now
2021-04-23 12:08:21 -04:00
Ian Lapham
18408c9c75 Increase Liquidity, component cleanup, preview components (#50)
* basic +/- buttons

* start increase liquidity

* fix v3 remove
2021-04-22 22:58:03 -04:00
Noah Zinsmeister
39f018bae0 small formatting fix 2021-04-22 18:45:36 -04:00
Noah Zinsmeister
c00eb2451f fix small merge bug 2021-04-22 18:43:07 -04:00
Noah Zinsmeister
6b99309fab Merge remote-tracking branch 'refs/remotes/origin/main' 2021-04-22 18:36:20 -04:00
Noah Zinsmeister
976b15986f add fee calculations 2021-04-22 18:36:11 -04:00
Moody Salem
df4c18c8d4 remove v1 swaps and migrate (#46)
* remove v1 swaps and migrate

* simplify

* more delete

* revert route changes

* fix v2 add

* fix integration tests

* try to fix https://github.com/Uniswap/v3-interface/issues/41
2021-04-22 17:10:18 -05:00
Moody Salem
62d3aa4b76 update sdk and addresses 2021-04-22 15:54:34 -05:00
Noah Zinsmeister
361d17c925 add permit support to migration 2021-04-22 15:23:13 -04:00
Noah Zinsmeister
9269f15ffc Merge remote-tracking branch 'refs/remotes/origin/main' 2021-04-21 17:46:33 -04:00
Noah Zinsmeister
925668c7a7 get skeleton remove working 2021-04-21 17:46:25 -04:00
Moody Salem
a64d526206 bump v3-sdk version again (fixing core/periphery dependencies) 2021-04-21 14:19:43 -05:00
Moody Salem
69234eb708 bump v3-sdk version 2021-04-21 14:14:18 -05:00
Moody Salem
f833133689 add margin bottom to text in confirm transaction modal 2021-04-21 14:11:04 -05:00
Moody Salem
c73a2655cb Merge branch 'main' into v3-main 2021-04-21 13:07:12 -05:00
Moody Salem
c55061873c update the v2-sdk version 2021-04-21 13:06:35 -05:00
Graeme Blackwood
d425ff64b4 [UPDATE] Correctly inverts Uniswap logo for dark mode. (#1263) 2021-04-21 12:59:12 -05:00
Graeme Blackwood
2f47fdf71d [UPDATE] Makes favicon visible in dark mode. (#1264) 2021-04-21 12:58:59 -05:00
Moody Salem
d4562a2373 uncomment release workflow lines (will be disabled manually) 2021-04-21 12:48:43 -05:00
Moody Salem
e13369154d Merge branch 'main' into v3-main
# Conflicts:
#	.github/workflows/tests.yaml
2021-04-21 12:37:20 -05:00
Moody Salem
9f8719f2a5 remove styled badge 2021-04-21 12:37:07 -05:00
Moody Salem
77b640c41b chore(tests): improve the CI (#1370)
* improve the CI

* change triggers

* add pull request target back
2021-04-21 12:35:49 -05:00
dependabot[bot]
d2f98bc9b4 chore(deps): bump ssri from 6.0.1 to 6.0.2 (#1368)
Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/npm/ssri/releases)
- [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
- [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-21 12:33:51 -05:00
Moody Salem
6be54afd12 fix the integration test errors from upgrading ethers 2021-04-21 11:19:34 -05:00
Noah Zinsmeister
d1fad3c4ea update to latest deploy
refactor useV3Positions to work with remove

axe old redirects
2021-04-20 15:31:31 -04:00
Noah Zinsmeister
a1c4b97a18 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-04-19 17:56:00 -04:00
Noah Zinsmeister
f00adc755a refactor range selection and use in migrate
improve migrate
2021-04-19 17:55:52 -04:00
Ian Lapham
e97939c545 update position details page (#43) 2021-04-19 15:49:43 -04:00
Moody Salem
63907b7bc5 fix icon urls 2021-04-19 13:02:04 -05:00
Moody Salem
48ad8e529f more flexible detection of network 2021-04-19 12:55:54 -05:00
Moody Salem
b669ec6976 workaround for detectNetwork on our custom provider 2021-04-19 12:51:16 -05:00
Moody Salem
2d4b60b9dd Merge branch 'main' into v3-main
# Conflicts:
#	.env
#	README.md
#	package.json
#	src/components/TokenWarningModal/index.tsx
#	src/connectors/index.ts
#	src/constants/index.ts
#	src/hooks/Trades.ts
#	src/index.tsx
#	src/state/claim/hooks.ts
#	src/state/user/hooks.tsx
#	yarn.lock
2021-04-19 11:37:33 -05:00
dependabot[bot]
e4dd4f9283 chore(deps): bump ini from 1.3.5 to 1.3.8 (#1277)
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-19 10:55:13 -05:00
Moody Salem
7798443919 improvement(walletconnect): upgrade the walletconnect connector to 6.1.9 2021-04-19 09:52:41 -05:00
Noah Zinsmeister
9fc096d091 Finish migration (#42)
* start migration (wip)

abstract some add liquidity components

bump deploy version

* add slippage params
2021-04-16 17:35:50 -04:00
Jordan Frankfurt
9f5584c37d Start Position management, bug fix on add amounts (#40)
* very rough positions/pools data fetching and position list rendering

* fix formatting

* fix loading

* position page routing, bug on add page

Co-authored-by: ianlapham <ianlapham@gmail.com>
2021-04-16 15:33:26 -04:00
Noah Zinsmeister
392d78b9da add to ADDITIONAL_BASES 2021-04-16 13:54:31 -04:00
Noah Zinsmeister
231289732c add additional bases (#1363)
* add routing token

* add logic for additional bases

* update address
2021-04-16 13:47:38 -04:00
Ian Lapham
0c0305a53d Add page MVP UI + v3 pool hooks and state (#35)
* WIP start usePool and useDerivedMint hooks

* naming updates

* Use real tick and pool math

* rate updates

* WIP start usePool and useDerivedMint hooks

* naming updates

* Use real tick and pool math

* rate updates

* fix warnings

* fix incorrect import

* clean up state, fix preview

* same token check

* amoutn parse update

* update hard coded chain id

* fix price creation in util

* update 1 amount in price calculation

* update comments

* update tick spacing input

* fix label on counter

* update rate label on range select

* update labels

* fixing pool hook

* clean pool hook

* preserve working rate switching

* reset values on rate switch

* clean up derived hook - setup for testnet

* format slippage amounts and support ETH

* fix import error

* fix package.json dependencies

* silence warnings

* silence more warnings

* bump multicodec and multihashes

* update migrator constants

* update txn to use sdk calldata

* fix txn formatting, update summary

* Squashed commit of the following:

commit b81ff7ca9e57bb8c3823be4c54827e5955fb3d15
Author: ianlapham <ianlapham@gmail.com>
Date:   Mon Apr 12 23:46:09 2021 -0400

    fix txn formatting, update summary

commit b9f91b0746c546602d336c8fd6f614ec9b4f3f19
Author: ianlapham <ianlapham@gmail.com>
Date:   Mon Apr 12 19:29:12 2021 -0400

    update txn to use sdk calldata

commit 20acf704c67cfd4f597494c8cb9c672c6270ae02
Merge: 4431914 2462901
Author: ianlapham <ianlapham@gmail.com>
Date:   Sun Apr 11 20:33:39 2021 -0400

    Merge branch 'minting' of https://github.com/Uniswap/v3-interface into minting

commit 44319146372e1c373b025741ae896fa2476e5765
Author: ianlapham <ianlapham@gmail.com>
Date:   Sun Apr 11 20:32:35 2021 -0400

    update migrator constants

commit 35e0618de06ba316d3a3f327075625760414ab83
Merge: 8927882 c3f65e3
Author: ianlapham <ianlapham@gmail.com>
Date:   Sun Apr 11 20:13:36 2021 -0400

    Merge branch 'main' of https://github.com/Uniswap/v3-interface into minting

commit 24629019e80c368c337a2679a51d4acb1097171c
Author: Noah Zinsmeister <noahwz@gmail.com>
Date:   Fri Apr 9 15:56:25 2021 -0400

    bump multicodec and multihashes

commit 9b5dd1876a64acbf6694d208b608bb0b429e317f
Author: Noah Zinsmeister <noahwz@gmail.com>
Date:   Fri Apr 9 14:59:09 2021 -0400

    silence more warnings

commit 140ddc1b54c7fbdd7ead2fa64bcc302f201d69f5
Author: Noah Zinsmeister <noahwz@gmail.com>
Date:   Fri Apr 9 14:57:58 2021 -0400

    silence warnings

commit 5a2726ebdd4ffaacfb3d8ec7903a944042c1bd9d
Author: Noah Zinsmeister <noahwz@gmail.com>
Date:   Fri Apr 9 14:35:01 2021 -0400

    fix package.json dependencies

commit 7c4d0a40931338de9a6197652b82fdab773483e3
Author: Noah Zinsmeister <noahwz@gmail.com>
Date:   Fri Apr 9 14:21:46 2021 -0400

    fix import error

commit e49ef19cbef7fbdf1737787a439e7cb78ba295b4
Merge: 8927882 c3f65e3
Author: Noah Zinsmeister <noahwz@gmail.com>
Date:   Fri Apr 9 14:08:34 2021 -0400

    Merge branch 'main' into minting

commit 89278825bd798a87d6010a74f8fc1d2b34a8ece1
Author: ianlapham <ianlapham@gmail.com>
Date:   Thu Apr 8 15:18:40 2021 -0400

    format slippage amounts and support ETH

commit 9a90b19e9a759cbc0c3e903a983660730c8833ad
Author: ianlapham <ianlapham@gmail.com>
Date:   Wed Apr 7 19:43:43 2021 -0400

    clean up derived hook - setup for testnet

commit dc034bc78a147f95f47b077d28a7d6e3165cedd7
Author: ianlapham <ianlapham@gmail.com>
Date:   Wed Apr 7 00:48:24 2021 -0400

    reset values on rate switch

commit bb5ccb2c853f7b2c27ec8d2f34f42a1b06f845b9
Author: ianlapham <ianlapham@gmail.com>
Date:   Wed Apr 7 00:38:39 2021 -0400

    preserve working rate switching

commit 5312d0ae7015150da48ba304de8c7a02b7d8925c
Author: ianlapham <ianlapham@gmail.com>
Date:   Mon Apr 5 13:52:46 2021 -0400

    clean pool hook

commit 5222de14834e76c37755225be17214a6e798d872
Merge: b2ba466 24521f0
Author: ianlapham <ianlapham@gmail.com>
Date:   Mon Apr 5 12:20:34 2021 -0400

    Merge branch 'main' of https://github.com/Uniswap/v3-interface into minting

commit b2ba46684a7b0bd8a8362f5990f4a208bfeff2dd
Author: ianlapham <ianlapham@gmail.com>
Date:   Mon Apr 5 12:19:20 2021 -0400

    fixing pool hook

commit b10742af99a725e04c1b756aa20f99e995f8cfeb
Author: ianlapham <ianlapham@gmail.com>
Date:   Thu Apr 1 16:53:52 2021 -0400

    update labels

commit 05abd395949245596c95090a9d5d77c7c272dbd3
Author: ianlapham <ianlapham@gmail.com>
Date:   Thu Apr 1 15:34:17 2021 -0400

    update rate label on range select

commit f098d01b6f4dc1dcb99e0fa314dde93647a19bb6
Author: ianlapham <ianlapham@gmail.com>
Date:   Thu Apr 1 15:26:30 2021 -0400

    fix label on counter

commit 16ffe61e8ee2b677adf5d468efa9d7aa8d7e092e
Author: ianlapham <ianlapham@gmail.com>
Date:   Thu Apr 1 15:06:50 2021 -0400

    update tick spacing input

commit 0fa2c8a15821dd32ec978750991a962ecb8f7344
Author: ianlapham <ianlapham@gmail.com>
Date:   Thu Apr 1 14:53:18 2021 -0400

    update comments

commit 1fccf57a1ef081ef6ba9790dc20e0ed604ac2b09
Author: ianlapham <ianlapham@gmail.com>
Date:   Thu Apr 1 14:52:37 2021 -0400

    update 1 amount in price calculation

commit b0e5d22bf8c57b3eacd75f077f68aaca4a9f975a
Author: ianlapham <ianlapham@gmail.com>
Date:   Thu Apr 1 14:46:41 2021 -0400

    fix price creation in util

commit 1ce246e85372e4f120f983ca18a1eb3d16e8647e
Author: ianlapham <ianlapham@gmail.com>
Date:   Thu Apr 1 13:55:14 2021 -0400

    update hard coded chain id

commit 2360b2d0a3233b604956e89de4bd7b09c0506875
Author: ianlapham <ianlapham@gmail.com>
Date:   Thu Apr 1 13:09:21 2021 -0400

    amoutn parse update

commit 6a99a7b71fe446fe77cb2741adce4c067862ca4a
Author: ianlapham <ianlapham@gmail.com>
Date:   Thu Apr 1 13:05:41 2021 -0400

    same token check

commit 83a1fd5a9ff02c6a49532cb54a57770b52fc052e
Author: ianlapham <ianlapham@gmail.com>
Date:   Thu Apr 1 12:31:21 2021 -0400

    clean up state, fix preview

commit 8592383b8386d7adbbaeaa2c6f9c36bb121d1c65
Author: ianlapham <ianlapham@gmail.com>
Date:   Wed Mar 31 22:47:56 2021 -0400

    fix incorrect import

commit ce526fd545e52142f847dbf3caec1ca37bb0650b
Author: ianlapham <ianlapham@gmail.com>
Date:   Wed Mar 31 22:36:10 2021 -0400

    fix warnings

commit 572770fd3e000ce31cd3a6c5c5c91eac92cc8c5c
Merge: a9e5b6c 2677491
Author: ianlapham <ianlapham@gmail.com>
Date:   Wed Mar 31 22:16:30 2021 -0400

    Merge branch 'minting' of https://github.com/Uniswap/v3-interface into minting

commit a9e5b6c5e5983e279a640886783f97c33b713125
Author: ianlapham <ianlapham@gmail.com>
Date:   Wed Mar 31 22:12:43 2021 -0400

    rate updates

commit b88cab6c06176eefe5cf71f7cc3e3664d9f514ab
Author: ianlapham <ianlapham@gmail.com>
Date:   Wed Mar 31 16:15:08 2021 -0400

    Use real tick and pool math

commit ed933cfd17141174c03b0bcac5f41cf75ff9b258
Author: ianlapham <ianlapham@gmail.com>
Date:   Sun Mar 28 22:42:05 2021 -0400

    naming updates

commit 50c0a0ece5c6c66a603508529c5e7a28f45db632
Author: ianlapham <ianlapham@gmail.com>
Date:   Sun Mar 28 22:36:08 2021 -0400

    WIP start usePool and useDerivedMint hooks

commit 2677491e2128e1318a0dd4307e63069e0f8e1dfe
Author: ianlapham <ianlapham@gmail.com>
Date:   Wed Mar 31 22:12:43 2021 -0400

    rate updates

commit c2f59d6c61068a2bf4d34d102d5d28c9863ce982
Author: ianlapham <ianlapham@gmail.com>
Date:   Wed Mar 31 16:15:08 2021 -0400

    Use real tick and pool math

commit 7d53e5c7e979be19fc5c63eb52307f302328c4eb
Author: ianlapham <ianlapham@gmail.com>
Date:   Sun Mar 28 22:42:05 2021 -0400

    naming updates

commit 9022650d391682f97e71d336021c2db2e5ea5455
Author: ianlapham <ianlapham@gmail.com>
Date:   Sun Mar 28 22:36:08 2021 -0400

    WIP start usePool and useDerivedMint hooks

* remove 1337 references

* clean up multicall

* clean up redirects/router

* cleanup

* improve useAllV3Ticks

* fix multicall

* typo

* Fix code style issues with ESLint

* preserve sticky

* reset to non fixed scroll

* fix inputs at 1

* update tests

* fix routes

* sticky sidebar

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
Co-authored-by: Callil Capuozzo <callil.capuozzo@gmail.com>
2021-04-14 10:12:35 -04:00
Brandon Curtis
140ff7a674 update BASES_TO_CHECK_TRADES_AGAINST (#1333) 2021-04-12 11:32:45 -05:00
Noah Zinsmeister
c3f65e3abd migration v2 (#38)
* wip

* add some v2 paths

* start to refactor migrate page

update deploy addresses

* fix tests

* stub out v3 remove

* silence some lint warnings
2021-04-09 14:01:56 -04:00
Lint Action
306aaa3a20 Fix code style issues with ESLint 2021-04-08 15:28:38 +00:00
Noah Zinsmeister
d08cae175a update to latest deploy + multicall 2 2021-04-08 11:27:19 -04:00
Noah Zinsmeister
f0f4110b4b update some multicall constants 2021-04-06 10:34:28 -04:00
Gökhan Çoban
3fbc4e34f4 typo: use main branch for pull requests (#1355) 2021-04-03 19:17:38 -05:00
Noah Zinsmeister
24521f0c92 deploy new tick lens, fix hook 2021-04-02 21:21:47 -04:00
Noah Zinsmeister
a037595e6e chunk multicall by gas cost
add useAllV3Ticks
2021-04-02 20:00:28 -04:00
Moody Salem
b964953daf fix(walletconnect): use a dedicated walletconnect bridge 2021-03-31 12:36:56 -05:00
Moody Salem
649fd9c845 fixes https://github.com/Uniswap/uniswap-interface/issues/1351 (#1352) 2021-03-31 12:26:03 -05:00
Jordan Frankfurt
edf4c47451 add external abi types (#37) 2021-03-31 03:40:13 -04:00
Jordan Frankfurt
c0ce6a55c4 add typechain gen scripts (#36)
add typechain generation for core/periphery contracts
2021-03-30 03:51:37 -04:00
Ian Lapham
6347e63a15 update unsupported list (#1346) 2021-03-28 16:11:18 -04:00
Moody Salem
bdcb9a8a0a fix(google analytics): anonymize IP in hits sent to google analytics
https://developers.google.com/analytics/devguides/collection/analyticsjs/ip-anonymization
2021-03-26 16:40:16 -05:00
Moody Salem
8d90bb7a39 fix(google analytics): don't set user cookies 2021-03-26 16:32:13 -05:00
Kun
d70b456855 Add Pin to Crust workflow (#1342) 2021-03-26 10:03:25 -05:00
Luke Donato
fbb797fa54 Move single hop toggle GA event (#1344)
* Move single hop toggle GA event

Move GA event from setSingleHopOnly hook to toggle function

* Fix code style issues with ESLint

* refactor ternary operator out

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-03-26 10:02:07 -05:00
Ian Lapham
878fc9cf4d Refactor with core (#33)
* Update index.ts

* feature(service worker): add offline support (#1319)

* Revert "feature(service worker): add offline support (#1319)" (#1320)

This reverts commit 34dfb41a1e.

* Revert "Revert "feature(service worker): add offline support (#1319)" (#1320)" (#1321)

This reverts commit db3328c8d9.

* unregisters all installed service workers (#1322)

* fix: modals stealing focus across frames (#1326)

* improvement(lists): add BA SEC tokens to unsupported list  (#1327)

* show hidden search results by default

* update break styles

* optimize filter, use debounce on input

* increase debounce time

* add ba association list

* handle dismiss (#1328)

* split up sdk use between core and v2

* Fix code style issues with ESLint

* remove service worker

Co-authored-by: Moody Salem <moodysalem@users.noreply.github.com>
Co-authored-by: Jordan Frankfurt <jordanwfrankfurt@gmail.com>
Co-authored-by: Hyperion <72735936+moontools-hyperion@users.noreply.github.com>
Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-03-23 21:45:58 -04:00
Jordan Frankfurt
32e679c62e prototype position data hook (#32)
Co-authored-by: Jordan Frankfurt <layup-entropy@protonmail.com>
2021-03-23 20:18:52 -04:00
Jordan Frankfurt
8ace518311 stop destructuring merkle drop response object (#1338)
stop destructuring merkle drop response object

Co-authored-by: Jordan Frankfurt <layup-entropy@protonmail.com>
2021-03-18 11:57:13 -04:00
Jordan Frankfurt
59164f876f dont destructure response 2021-03-18 11:36:27 -04:00
Jordan Frankfurt
5d952661d7 fix(merkle drop): make claim a post request 2021-03-18 01:46:51 -04:00
Ian Lapham
9509737811 Improvement(add): Style tweaks on add page, remove timeout (#30)
* start add liquidity

* update add state, add input UI

* basic add skeleton with dummy state

* refactor with preview steps on add

* small style tweaks, timeout update

* remove merge conflict
2021-03-09 11:58:41 -05:00
Callil Capuozzo
e42a26c3dc Tweak position list styles 2021-03-09 11:01:26 -05:00
Ian Lapham
eeb258ebd5 improvement(Add): skeleton UI with dummy state (#29)
* start add liquidity

* update add state, add input UI

* basic add skeleton with dummy state

* refactor with preview steps on add
2021-03-08 20:52:36 -05:00
Jordan Frankfurt
ab1538b196 feature(position-list): add summary page for positions with mock data (#28)
* feature(position-list): add summary page for positions with mock data

* add loading states

* mobile layout
2021-03-03 17:21:14 -05:00
ianlapham
7e5a230a33 update chart styles 2021-02-16 21:54:51 -05:00
ianlapham
20adf82c79 add dynamic toggle 2021-02-16 21:51:46 -05:00
Jordan Frankfurt
0ec6cad6d1 Revert "fix(storybook): remove it for now" (#9)
fix rebass types to work with storybook
2021-02-16 17:33:55 -05:00
Jordan Frankfurt
eb850cff0b remove husky/pretty-quick (#8)
Co-authored-by: Jordan Frankfurt <jordanwfrankfurt@github.com>
2021-02-16 13:45:36 -05:00
Lint Action
9d9b57dd4c Fix code style issues with ESLint 2021-02-16 08:46:17 +00:00
Jordan Frankfurt
37b0e2fa28 Merge pull request #7 from Uniswap/prettier-upgrade
upgrade prettier
2021-02-16 03:44:49 -05:00
Lint Action
b6d8512316 Fix code style issues with ESLint 2021-02-16 08:36:54 +00:00
Jordan Frankfurt
4e17107ac5 upgrade prettier 2021-02-16 03:34:45 -05:00
Lint Action
64c97f7e30 Fix code style issues with ESLint 2021-02-16 08:28:56 +00:00
Jordan Frankfurt
f3c513573d Merge pull request #6 from Uniswap/prettier-upgrade
Revert "upgrade prettier"
2021-02-16 03:27:31 -05:00
Jordan Frankfurt
6965707d45 Revert "upgrade prettier"
This reverts commit 5fabe438e5.
2021-02-16 03:26:53 -05:00
Ian Lapham
d762836eb9 Merge pull request #2 from Uniswap/nuke-storybook
fix(storybook): remove it for now
2021-02-15 21:48:18 -05:00
ianlapham
88f8f804d9 remove chart on swap page 2021-02-15 21:43:29 -05:00
ianlapham
c1042c6b7a update chart font 2021-02-15 21:42:28 -05:00
ianlapham
3cb382376a format chart component 2021-02-15 21:27:42 -05:00
ianlapham
b44c7e7d7e nuke it 2021-02-15 17:20:37 -05:00
ianlapham
333c907e63 remove stories 2021-02-15 17:19:21 -05:00
Lint Action
b630d59437 Fix code style issues with ESLint 2021-02-15 17:01:46 +00:00
Ian Lapham
ba647d9d6c Merge pull request #4 from Uniswap/upgrade-react
bump react
2021-02-15 11:59:59 -05:00
Ian Lapham
449ed0185e Merge pull request #3 from Uniswap/prettier-upgrade
upgrade prettier
2021-02-15 11:59:42 -05:00
Lint Action
52e3ad0703 Fix code style issues with ESLint 2021-02-14 10:44:11 +00:00
Jordan Frankfurt
bf9dad2550 bump react 2021-02-14 05:41:46 -05:00
Lint Action
adcecdeefc Fix code style issues with ESLint 2021-02-14 08:11:05 +00:00
Jordan Frankfurt
5fabe438e5 upgrade prettier 2021-02-14 03:07:58 -05:00
Jordan Frankfurt
98e77cb01b fix(storybook): remove it for now 2021-02-13 03:28:56 -05:00
ianlapham
b05c4c111c add start of chart component 2021-02-12 16:15:02 -05:00
Ian Lapham
4d612dc2a2 Merge pull request #1 from Uniswap/add-storybook
feature(storybook): first pass at storybook integration
2021-02-12 15:41:27 -05:00
Jordan Frankfurt
619c7c2d95 feature(storybook): first pass at storybook integration 2021-02-12 15:32:25 -05:00
Moody Salem
db886930a0 Update release.yaml 2021-02-12 10:51:31 -06:00
362 changed files with 23481 additions and 12767 deletions

6
.env
View File

@@ -1,2 +1,4 @@
REACT_APP_CHAIN_ID="1"
REACT_APP_NETWORK_URL="https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
REACT_APP_WALLETCONNECT_BRIDGE_URL="https://uniswap.bridge.walletconnect.org"
# 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

@@ -1,5 +1,4 @@
REACT_APP_CHAIN_ID="1"
REACT_APP_NETWORK_URL="https://mainnet.infura.io/v3/099fc58e0de9451d80b18d7c74caa7c1"
REACT_APP_INFURA_KEY="099fc58e0de9451d80b18d7c74caa7c1"
REACT_APP_PORTIS_ID="c0e2bf01-4b08-4fd5-ac7b-8e26b58cd236"
REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
REACT_APP_GOOGLE_ANALYTICS_ID="UA-128182339-4"

View File

@@ -8,9 +8,7 @@
"jsx": true
}
},
"ignorePatterns": [
"node_modules/**/*"
],
"ignorePatterns": ["node_modules/**/*"],
"settings": {
"react": {
"version": "detect"
@@ -26,6 +24,9 @@
"rules": {
"@typescript-eslint/explicit-function-return-type": "off",
"prettier/prettier": "error",
"@typescript-eslint/no-explicit-any": "off"
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/explicit-module-boundary-types": "off"
}
}
}

View File

@@ -10,9 +10,10 @@ assignees: ''
A clear and concise description of the bug.
**Steps to Reproduce**
1. Go to ...
2. Click on ...
...
...
**Expected Behavior**
A clear and concise description of what you expected to happen.

View File

@@ -1,4 +1,4 @@
blank_issues_enabled: false
blank_issues_enabled: true
contact_links:
- name: Support
url: https://discord.gg/FCfyBSbCU5

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

View File

@@ -0,0 +1,38 @@
name: Integration Tests
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
integration-tests:
name: Cypress
runs-on: ubuntu-16.04
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up node
uses: actions/setup-node@v2
with:
node-version: 14
registry-url: https://registry.npmjs.org
- name: Install dependencies
run: yarn install --frozen-lockfile
- run: yarn cypress install
- run: yarn build
env:
CI: false
REACT_APP_NETWORK_URL: "https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
- run: yarn integration-test
env:
CYPRESS_INTEGRATION_TEST_PRIVATE_KEY: ${{ secrets.CYPRESS_INTEGRATION_TEST_PRIVATE_KEY }}

View File

@@ -14,33 +14,20 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
- name: Checkout
uses: actions/checkout@v2
- name: Set up node
uses: actions/setup-node@v1
uses: actions/setup-node@v2
with:
node-version: 12
always-auth: true
node-version: 14
registry-url: https://registry.npmjs.org
- name: Set output of cache
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Node dependency cache
uses: actions/cache@v1
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
yarn-
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Run linters
uses: wearerequired/lint-action@77d70b9a07ecb93bc98dc46dc27d96c4f004d035
uses: wearerequired/lint-action@b98b0918aa71490373d2eca9e8e39a9bc1cc2517
with:
github_token: ${{ secrets.github_token }}
eslint: true

View File

@@ -15,11 +15,11 @@ jobs:
changelog: ${{ steps.github_tag_action.outputs.changelog }}
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v2
- name: Bump version and push tag
id: github_tag_action
uses: mathieudutour/github-tag-action@v4.5
uses: mathieudutour/github-tag-action@331898d5052eedac9b15fec867b5ba66ebf9b692
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
release_branches: .*
@@ -31,12 +31,12 @@ jobs:
if: ${{ needs.bump_version.outputs.new_tag != null }}
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v2
- uses: actions/setup-node@v1
- name: Set up node
uses: actions/setup-node@v2
with:
node-version: '12'
always-auth: true
node-version: 14
registry-url: https://registry.npmjs.org
- name: Install dependencies
@@ -54,6 +54,14 @@ jobs:
pinata-api-key: ${{ secrets.PINATA_API_KEY }}
pinata-secret-api-key: ${{ secrets.PINATA_API_SECRET_KEY }}
- name: Pin to Crust
uses: crustio/ipfs-crust-action@v1.0.8
continue-on-error: true
timeout-minutes: 2
with:
cid: ${{ steps.upload.outputs.hash }}
seeds: ${{ secrets.CRUST_SEEDS }}
- name: Convert CIDv0 to CIDv1
id: convert_cidv0
uses: uniswap/convert-cidv0-cidv1@v1.0.0
@@ -86,19 +94,13 @@ jobs:
The latest release is always accessible via our alias to the Cloudflare IPFS gateway at [app.uniswap.org](https://app.uniswap.org).
You can also access the Uniswap Interface directly from an IPFS gateway.
The Uniswap interface uses [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to store your settings.
**Beware** that other sites you access via the _same_ IPFS gateway can read and modify your settings on the Uniswap interface without your permission.
You can avoid this issue by using a subdomain IPFS gateway, or our alias to the latest release at [app.uniswap.org](https://app.uniswap.org).
The preferred URLs below are safe to use to access this specific release.
**BEWARE**: The Uniswap interface uses [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to remember your settings, such as which tokens you have imported.
**You should always use an IPFS gateway that enforces origin separation**, or our alias to the latest release at [app.uniswap.org](https://app.uniswap.org).
Your Uniswap settings are never remembered across different URLs.
Preferred URLs:
IPFS gateways:
- https://${{ steps.convert_cidv0.outputs.cidv1 }}.ipfs.dweb.link/
- https://${{ steps.convert_cidv0.outputs.cidv1 }}.ipfs.cf-ipfs.com/
- [ipfs://${{ steps.upload.outputs.hash }}/](ipfs://${{ steps.upload.outputs.hash }}/)
Other IPFS gateways:
- https://cloudflare-ipfs.com/ipfs/${{ steps.upload.outputs.hash }}/
- https://ipfs.infura.io/ipfs/${{ steps.upload.outputs.hash }}/
- https://ipfs.io/ipfs/${{ steps.upload.outputs.hash }}/
${{ needs.bump_version.outputs.changelog }}

View File

@@ -1,65 +0,0 @@
name: Tests
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
integration-tests:
name: Integration tests
runs-on: ubuntu-16.04
steps:
- name: Checkout
uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: '12'
always-auth: true
registry-url: https://registry.npmjs.org
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v1
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- run: yarn install --frozen-lockfile
- run: yarn cypress install
- run: yarn build
env:
CI: false
REACT_APP_NETWORK_URL: "https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
- run: yarn integration-test
unit-tests:
name: Unit tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: '12'
always-auth: true
registry-url: https://registry.npmjs.org
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v1
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- run: yarn install --frozen-lockfile
- run: yarn test

28
.github/workflows/unit-tests.yaml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: Unit Tests
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
unit-tests:
name: Unit tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up node
uses: actions/setup-node@v2
with:
node-version: 14
registry-url: https://registry.npmjs.org
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Run unit tests
run: yarn test

4
.gitignore vendored
View File

@@ -1,5 +1,9 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# generated contract types
/src/types/v3
/src/abis/types
# dependencies
/node_modules

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
lts/*

16
.storybook/main.ts Normal file
View File

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

4
.storybook/manager.ts Normal file
View File

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

91
.storybook/preview.tsx Normal file
View File

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

17
.storybook/theme.ts Normal file
View File

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

View File

@@ -1,8 +1,9 @@
# Uniswap Interface
[![Lint](https://github.com/Uniswap/uniswap-interface/workflows/Lint/badge.svg)](https://github.com/Uniswap/uniswap-interface/actions?query=workflow%3ALint)
[![Tests](https://github.com/Uniswap/uniswap-interface/workflows/Tests/badge.svg)](https://github.com/Uniswap/uniswap-interface/actions?query=workflow%3ATests)
[![Styled With Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://prettier.io/)
[![Unit Tests](https://github.com/Uniswap/uniswap-interface/actions/workflows/unit-tests.yaml/badge.svg)](https://github.com/Uniswap/uniswap-interface/actions/workflows/unit-tests.yaml)
[![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)
An open source interface for Uniswap -- a protocol for decentralized exchange of Ethereum tokens.
@@ -18,21 +19,15 @@ An open source interface for Uniswap -- a protocol for decentralized exchange of
## Accessing the Uniswap Interface
To access the Uniswap Interface, use an IPFS gateway link from the
[latest release](https://github.com/Uniswap/uniswap-interface/releases/latest),
[latest release](https://github.com/Uniswap/uniswap-interface/releases/latest),
or visit [app.uniswap.org](https://app.uniswap.org).
## Listing a token
Please see the
[@uniswap/default-token-list](https://github.com/uniswap/default-token-list)
repository.
## Development
### Install Dependencies
```bash
yarn
yarn install
```
### Run
@@ -41,26 +36,22 @@ yarn
yarn start
```
### Configuring the environment (optional)
To have the interface default to a different network when a wallet is not connected:
1. Make a copy of `.env` named `.env.local`
2. Change `REACT_APP_NETWORK_ID` to `"{YOUR_NETWORK_ID}"`
3. Change `REACT_APP_NETWORK_URL` to e.g. `"https://{YOUR_NETWORK_ID}.infura.io/v3/{YOUR_INFURA_KEY}"`
Note that the interface only works on testnets where both
[Uniswap V2](https://uniswap.org/docs/v2/smart-contracts/factory/) and
[multicall](https://github.com/makerdao/multicall) are deployed.
The interface will not work on other networks.
## Contributions
**Please open all pull requests against the `master` branch.**
**Please open all pull requests against the `main` branch.**
CI checks will run against all PRs.
## 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).

View File

@@ -0,0 +1,8 @@
const isDev = process.env.NODE_ENV !== 'production'
module.exports = {
styledComponents: {
fileName: isDev,
displayName: isDev,
},
}

View File

@@ -1,18 +1,18 @@
describe('Add Liquidity', () => {
it('loads the two correct tokens', () => {
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85-0xc778417E063141139Fce010982780140Aa0cD5Ab')
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab/500')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'ETH')
})
it('does not crash if ETH is duplicated', () => {
cy.visit('/add/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xc778417E063141139Fce010982780140Aa0cD5Ab')
cy.visit('/add/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xc778417E063141139Fce010982780140Aa0cD5Ab')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'ETH')
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('not.contain.text', 'ETH')
})
it('token not in storage is loaded', () => {
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'SKL')
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'MKR')
})
@@ -23,28 +23,4 @@ describe('Add Liquidity', () => {
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
})
it('redirects /add/token-token to add/token/token', () => {
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.url().should(
'contain',
'/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'
)
})
it('redirects /add/WETH-token to /add/WETH-address/token', () => {
cy.visit('/add/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.url().should(
'contain',
'/add/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'
)
})
it('redirects /add/token-WETH to /add/token/WETH-address', () => {
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85-0xc778417E063141139Fce010982780140Aa0cD5Ab')
cy.url().should(
'contain',
'/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab'
)
})
})

View File

@@ -1,8 +0,0 @@
describe('Migrate V1 Liquidity', () => {
describe('Remove V1 liquidity', () => {
it('renders the correct page', () => {
cy.visit('/remove/v1/0x93bB63aFe1E0180d0eF100D774B473034fd60C36')
cy.get('#remove-v1-exchange').should('contain', 'MKR/ETH')
})
})
})

View File

@@ -4,9 +4,4 @@ describe('Pool', () => {
cy.get('#join-pool-button').click()
cy.url().should('contain', '/add/ETH')
})
it('import pool links to /import', () => {
cy.get('#import-pool-link').click()
cy.url().should('contain', '/find')
})
})

View File

@@ -1,38 +1,30 @@
describe('Remove Liquidity', () => {
it('redirects', () => {
cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.url().should(
'contain',
'/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'
)
})
it('eth remove', () => {
cy.visit('/remove/ETH/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.visit('/remove/v2/ETH/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'ETH')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
})
it('eth remove swap order', () => {
cy.visit('/remove/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/ETH')
cy.visit('/remove/v2/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/ETH')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'MKR')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'ETH')
})
it('loads the two correct tokens', () => {
cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.visit('/remove/v2/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'WETH')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
})
it('does not crash if ETH is duplicated', () => {
cy.visit('/remove/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xc778417E063141139Fce010982780140Aa0cD5Ab')
cy.visit('/remove/v2/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xc778417E063141139Fce010982780140Aa0cD5Ab')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'WETH')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'WETH')
})
it('token not in storage is loaded', () => {
cy.visit('/remove/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.visit('/remove/v2/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'SKL')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
})

View File

@@ -3,36 +3,26 @@ describe('Swap', () => {
cy.visit('/swap')
})
it('can enter an amount into input', () => {
cy.get('#swap-currency-input .token-amount-input')
.type('0.001', { delay: 200 })
.should('have.value', '0.001')
cy.get('#swap-currency-input .token-amount-input').type('0.001', { delay: 200 }).should('have.value', '0.001')
})
it('zero swap amount', () => {
cy.get('#swap-currency-input .token-amount-input')
.type('0.0', { delay: 200 })
.should('have.value', '0.0')
cy.get('#swap-currency-input .token-amount-input').type('0.0', { delay: 200 }).should('have.value', '0.0')
})
it('invalid swap amount', () => {
cy.get('#swap-currency-input .token-amount-input')
.type('\\', { delay: 200 })
.should('have.value', '')
cy.get('#swap-currency-input .token-amount-input').type('\\', { delay: 200 }).should('have.value', '')
})
it('can enter an amount into output', () => {
cy.get('#swap-currency-output .token-amount-input')
.type('0.001', { delay: 200 })
.should('have.value', '0.001')
cy.get('#swap-currency-output .token-amount-input').type('0.001', { delay: 200 }).should('have.value', '0.001')
})
it('zero output amount', () => {
cy.get('#swap-currency-output .token-amount-input')
.type('0.0', { delay: 200 })
.should('have.value', '0.0')
cy.get('#swap-currency-output .token-amount-input').type('0.0', { delay: 200 }).should('have.value', '0.0')
})
it('can swap ETH for DAI', () => {
it.skip('can swap ETH for DAI', () => {
cy.get('#swap-currency-output .open-currency-select-button').click()
cy.get('.token-item-0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735').should('be.visible')
cy.get('.token-item-0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735').click({ force: true })
@@ -43,13 +33,13 @@ describe('Swap', () => {
cy.get('#confirm-swap-or-send').should('contain', 'Confirm Swap')
})
it('add a recipient does not exist unless in expert mode', () => {
it.skip('add a recipient does not exist unless in expert mode', () => {
cy.get('#add-recipient-button').should('not.exist')
})
describe('expert mode', () => {
beforeEach(() => {
cy.window().then(win => {
cy.window().then((win) => {
cy.stub(win, 'prompt').returns('confirm')
})
cy.get('#open-settings-dialog-button').click()
@@ -57,16 +47,16 @@ describe('Swap', () => {
cy.get('#confirm-expert-mode').click()
})
it('add a recipient is visible', () => {
it.skip('add a recipient is visible', () => {
cy.get('#add-recipient-button').should('be.visible')
})
it('add a recipient', () => {
it.skip('add a recipient', () => {
cy.get('#add-recipient-button').click()
cy.get('#recipient').should('exist')
})
it('remove recipient', () => {
it.skip('remove recipient', () => {
cy.get('#add-recipient-button').click()
cy.get('#remove-recipient-button').click()
cy.get('#recipient').should('not.exist')

View File

@@ -6,17 +6,21 @@
import { JsonRpcProvider } from '@ethersproject/providers'
import { Wallet } from '@ethersproject/wallet'
import { _Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
import { Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
// never send real ether to this, obviously
const PRIVATE_KEY_TEST_NEVER_USE = '0xad20c82497421e9784f18460ad2fe84f73569068e98e270b3e63743268af5763'
const TEST_PRIVATE_KEY = Cypress.env('INTEGRATION_TEST_PRIVATE_KEY')
// address of the above key
export const TEST_ADDRESS_NEVER_USE = '0x0fF2D1eFd7A57B7562b2bf27F3f37899dB27F4a5'
export const TEST_ADDRESS_NEVER_USE = new Wallet(TEST_PRIVATE_KEY).address
export const TEST_ADDRESS_NEVER_USE_SHORTENED = '0x0fF2...F4a5'
export const TEST_ADDRESS_NEVER_USE_SHORTENED = `${TEST_ADDRESS_NEVER_USE.substr(
0,
6
)}...${TEST_ADDRESS_NEVER_USE.substr(-4, 4)}`
class CustomizedBridge extends Eip1193Bridge {
chainId = 4
class CustomizedBridge extends _Eip1193Bridge {
async sendAsync(...args) {
console.debug('sendAsync called', ...args)
return this.send(...args)
@@ -75,8 +79,8 @@ Cypress.Commands.overwrite('visit', (original, url, options) => {
options && options.onBeforeLoad && options.onBeforeLoad(win)
win.localStorage.clear()
const provider = new JsonRpcProvider('https://rinkeby.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847', 4)
const signer = new Wallet(PRIVATE_KEY_TEST_NEVER_USE, provider)
const signer = new Wallet(TEST_PRIVATE_KEY, provider)
win.ethereum = new CustomizedBridge(signer, provider)
}
},
})
})

View File

@@ -4,48 +4,66 @@
"homepage": ".",
"private": true,
"devDependencies": {
"@emotion/core": "^11.0.0",
"@ethersproject/experimental": "^5.0.1",
"@popperjs/core": "^2.4.4",
"@reach/dialog": "^0.10.3",
"@reach/portal": "^0.10.3",
"@react-hook/window-scroll": "^1.3.0",
"@reduxjs/toolkit": "^1.3.5",
"@storybook/addon-actions": "^6.1.17",
"@storybook/addon-essentials": "^6.1.17",
"@storybook/addon-links": "^6.1.17",
"@storybook/addons": "^6.1.17",
"@storybook/components": "^6.1.17",
"@storybook/preset-create-react-app": "^3.1.5",
"@storybook/preset-typescript": "^3.0.0",
"@storybook/react": "^6.1.17",
"@storybook/theming": "^6.1.17",
"@styled-system/css": "^5.1.5",
"@typechain/ethers-v5": "^7.0.0",
"@types/jest": "^25.2.1",
"@types/lodash.flatmap": "^4.5.6",
"@types/luxon": "^1.24.4",
"@types/multicodec": "^1.0.0",
"@types/node": "^13.13.5",
"@types/qs": "^6.9.2",
"@types/react": "^16.9.34",
"@types/react-dom": "^16.9.7",
"@types/react-redux": "^7.1.8",
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.1",
"@types/react-redux": "^7.1.16",
"@types/react-router-dom": "^5.0.0",
"@types/react-virtualized-auto-sizer": "^1.0.0",
"@types/react-window": "^1.8.2",
"@types/rebass": "^4.0.5",
"@types/rebass": "^4.0.7",
"@types/styled-components": "^5.1.0",
"@types/testing-library__cypress": "^5.0.5",
"@types/ua-parser-js": "^0.7.35",
"@types/wcag-contrast": "^3.0.0",
"@typescript-eslint/eslint-plugin": "^2.31.0",
"@typescript-eslint/parser": "^2.31.0",
"@typescript-eslint/eslint-plugin": "^4.1.0",
"@typescript-eslint/parser": "^4.1.0",
"@uniswap/default-token-list": "^2.0.0",
"@uniswap/governance": "^1.0.2",
"@uniswap/liquidity-staker": "^1.0.2",
"@uniswap/merkle-distributor": "1.0.1",
"@uniswap/sdk": "3.0.3",
"@uniswap/token-lists": "^1.0.0-beta.19",
"@uniswap/v2-core": "1.0.0",
"@uniswap/v2-periphery": "^1.1.0-beta.0",
"@uniswap/v2-sdk": "^3.0.0-alpha.0",
"@uniswap/v3-core": "1.0.0",
"@uniswap/v3-periphery": "1.0.0",
"@uniswap/v3-sdk": "^3.0.0-alpha.6",
"@web3-react/core": "^6.0.9",
"@web3-react/fortmatic-connector": "^6.0.9",
"@web3-react/injected-connector": "^6.0.7",
"@web3-react/portis-connector": "^6.0.9",
"@web3-react/walletconnect-connector": "^6.1.1",
"@web3-react/walletlink-connector": "^6.0.9",
"@web3-react/walletconnect-connector": "^6.2.0",
"@web3-react/walletlink-connector": "^6.2.0",
"ajv": "^6.12.3",
"cids": "^1.0.0",
"copy-to-clipboard": "^3.2.0",
"cross-env": "^7.0.2",
"cypress": "^4.11.0",
"eslint": "^6.8.0",
"eslint": "^7.11.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-react": "^7.19.0",
@@ -56,26 +74,27 @@
"i18next-xhr-backend": "^2.0.1",
"inter-ui": "^3.13.1",
"jazzicon": "^1.5.0",
"lightweight-charts": "^3.3.0",
"lodash.flatmap": "^4.5.0",
"luxon": "^1.25.0",
"multicodec": "^2.0.0",
"multihashes": "^3.0.1",
"multicodec": "^3.0.1",
"multihashes": "^4.0.2",
"node-vibrant": "^3.1.5",
"polished": "^3.3.2",
"prettier": "^1.17.0",
"prettier": "^2.2.1",
"qs": "^6.9.4",
"react": "^16.13.1",
"react": "^17.0.1",
"react-confetti": "^6.0.0",
"react-device-detect": "^1.6.2",
"react-dom": "^16.13.1",
"react-dom": "^17.0.1",
"react-feather": "^2.0.8",
"react-ga": "^2.5.7",
"react-i18next": "^10.7.0",
"react-markdown": "^4.3.1",
"react-popper": "^2.2.3",
"react-redux": "^7.2.0",
"react-redux": "^7.2.2",
"react-router-dom": "^5.0.0",
"react-scripts": "^3.4.1",
"react-scripts": "^4.0.3",
"react-spring": "^8.0.27",
"react-use-gesture": "^6.0.14",
"react-virtualized-auto-sizer": "^1.0.2",
@@ -85,7 +104,10 @@
"serve": "^11.3.2",
"start-server-and-test": "^1.11.0",
"styled-components": "^4.2.0",
"typescript": "^3.8.3",
"styled-system": "^5.1.5",
"typechain": "^5.0.0",
"typescript": "^4.2.3",
"ua-parser-js": "^0.7.28",
"use-count-up": "^2.2.5",
"wcag-contrast": "^3.0.0",
"workbox-core": "^6.1.0",
@@ -95,15 +117,18 @@
"workbox-strategies": "^6.1.0"
},
"resolutions": {
"@walletconnect/web3-provider": "1.1.1-alpha.0"
"@walletconnect/web3-provider": "1.4.2-rc.2"
},
"scripts": {
"start": "react-scripts start",
"start:service-worker": "yarn build && yarn serve -s build",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'"
"compile-contract-types": "yarn compile-external-abi-types && yarn compile-v3-contract-types",
"compile-external-abi-types": "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 && react-scripts build",
"integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'",
"postinstall": "yarn compile-contract-types",
"start": "yarn compile-contract-types && react-scripts start",
"storybook": "start-storybook -p 6006",
"test": "react-scripts test --env=jsdom"
},
"eslintConfig": {
"extends": "react-app",
@@ -124,7 +149,5 @@
]
},
"license": "GPL-3.0-or-later",
"dependencies": {
"@uniswap/default-token-list": "^2.0.0"
}
"dependencies": {}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -87,5 +87,30 @@
"forAtLeast": "for at least ",
"brokenToken": "The selected token is not compatible with Uniswap V1. Adding liquidity will result in locked funds.",
"toleranceExplanation": "Lowering this limit decreases your risk of frontrunning. However, this makes more likely that your transaction will fail due to normal price movements.",
"tokenSearchPlaceholder": "Search name or paste address"
"tokenSearchPlaceholder": "Search name or paste address",
"selectFee": "Select Fee",
"selectLiquidityRange": "Set Price Range",
"selectPool": "Select Fee Tier",
"depositAmounts": "Deposit Amounts",
"fee": "fee",
"setLimits": "Set Limits",
"percent": "Percent",
"rate": "Rate",
"currentRate": "Current {{label}} Price:",
"inactiveRangeWarning": "Your position will not earn fees or be used in trades until the market price moves into your range.",
"invalidRangeWarning": "Invalid range selected. The min price must be lower than the max price.",
"connectWallet": "Connect Wallet",
"unsupportedAsset": "Unsupported Asset",
"feePool": "Fee Pool",
"feeTier": "Fee Tier",
"rebalanceMessage": "Your underlying tokens will be automatically rebalanced when the rate of the pool changes and may be different when you withdraw the position.",
"addEarnHelper": "You will earn fees from trades proportional to your share of the pool.",
"learnMoreAboutFess": " Learn more about earning fees.",
"selectAPool": "Choose the best fee tier for your selected pair.",
"poolType": "Select a fee tier based on your preferred liquidity provider fee.",
"rangeWarning": "Your liquidity will only be active and earning fees when the rate of the pool is within this price range.",
"chooseLiquidityAmount": "Choose an amount of tokens to open this liquidity position. If you 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."
}

20
src/abis/eip_2612.json Normal file
View File

@@ -0,0 +1,20 @@
[
{
"constant": true,
"inputs": [{ "name": "owner", "type": "address" }],
"name": "nonces",
"outputs": [{ "name": "", "type": "uint256" }],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "DOMAIN_SEPARATOR",
"outputs": [{ "name": "", "type": "bytes32" }],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]

330
src/abis/multicall.json Normal file
View File

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

165
src/abis/multicall2.json Normal file
View File

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

View File

@@ -89,7 +89,7 @@
"constant": true,
"payable": false,
"type": "function",
"gas": 22405
"gas": "22405"
},
{
"name": "tokenByIndex",
@@ -108,7 +108,7 @@
"constant": true,
"payable": false,
"type": "function",
"gas": 631
"gas": "631"
},
{
"name": "tokenOfOwnerByIndex",
@@ -131,7 +131,7 @@
"constant": true,
"payable": false,
"type": "function",
"gas": 1248
"gas": "1248"
},
{
"name": "transferFrom",
@@ -153,7 +153,7 @@
"constant": false,
"payable": false,
"type": "function",
"gas": 259486
"gas": "259486"
},
{
"name": "safeTransferFrom",
@@ -217,7 +217,7 @@
"constant": false,
"payable": false,
"type": "function",
"gas": 38422
"gas": "38422"
},
{
"name": "setApprovalForAll",
@@ -235,7 +235,7 @@
"constant": false,
"payable": false,
"type": "function",
"gas": 38016
"gas": "38016"
},
{
"name": "mint",
@@ -254,7 +254,7 @@
"constant": false,
"payable": false,
"type": "function",
"gas": 182636
"gas": "182636"
},
{
"name": "changeMinter",
@@ -268,7 +268,7 @@
"constant": false,
"payable": false,
"type": "function",
"gas": 35897
"gas": "35897"
},
{
"name": "changeURI",
@@ -282,7 +282,7 @@
"constant": false,
"payable": false,
"type": "function",
"gas": 35927
"gas": "35927"
},
{
"name": "name",
@@ -296,7 +296,7 @@
"constant": true,
"payable": false,
"type": "function",
"gas": 6612
"gas": "6612"
},
{
"name": "symbol",
@@ -310,7 +310,7 @@
"constant": true,
"payable": false,
"type": "function",
"gas": 6642
"gas": "6642"
},
{
"name": "totalSupply",
@@ -324,7 +324,7 @@
"constant": true,
"payable": false,
"type": "function",
"gas": 873
"gas": "873"
},
{
"name": "minter",
@@ -338,7 +338,7 @@
"constant": true,
"payable": false,
"type": "function",
"gas": 903
"gas": "903"
},
{
"name": "socks",
@@ -353,7 +353,7 @@
"constant": true,
"payable": false,
"type": "function",
"gas": 933
"gas": "933"
},
{
"name": "newURI",
@@ -367,7 +367,7 @@
"constant": true,
"payable": false,
"type": "function",
"gas": 963
"gas": "963"
},
{
"name": "ownerOf",
@@ -386,7 +386,7 @@
"constant": true,
"payable": false,
"type": "function",
"gas": 1126
"gas": "1126"
},
{
"name": "balanceOf",
@@ -405,7 +405,7 @@
"constant": true,
"payable": false,
"type": "function",
"gas": 1195
"gas": "1195"
},
{
"name": "getApproved",
@@ -424,7 +424,7 @@
"constant": true,
"payable": false,
"type": "function",
"gas": 1186
"gas": "1186"
},
{
"name": "isApprovedForAll",
@@ -447,7 +447,7 @@
"constant": true,
"payable": false,
"type": "function",
"gas": 1415
"gas": "1415"
},
{
"name": "supportsInterface",
@@ -466,6 +466,6 @@
"constant": true,
"payable": false,
"type": "function",
"gas": 1246
"gas": "1246"
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

View File

@@ -1,11 +1,11 @@
<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.15217 1.55141C3.96412 1.52242 3.95619 1.51902 4.04468 1.5055C4.21427 1.47958 4.61472 1.51491 4.89067 1.58012C5.53489 1.73232 6.12109 2.12221 6.74683 2.81466L6.91307 2.99862L7.15088 2.96062C8.15274 2.8006 9.17194 2.92778 10.0244 3.31918C10.2589 3.42686 10.6287 3.64121 10.6749 3.69629C10.6896 3.71384 10.7166 3.82684 10.7349 3.94742C10.7982 4.36458 10.7665 4.68434 10.6382 4.92317C10.5683 5.05313 10.5644 5.09432 10.6114 5.20554C10.6489 5.2943 10.7534 5.35999 10.8569 5.35985C11.0687 5.35956 11.2967 5.0192 11.4024 4.54561L11.4444 4.3575L11.5275 4.45109C11.9835 4.96459 12.3417 5.66488 12.4032 6.16335L12.4192 6.29332L12.3426 6.17517C12.2107 5.97186 12.0781 5.83346 11.9084 5.72183C11.6024 5.52062 11.2789 5.45215 10.4222 5.40727C9.64839 5.36675 9.21045 5.30106 8.77621 5.16032C8.03738 4.9209 7.66493 4.60204 6.78729 3.4576C6.39748 2.94928 6.15654 2.66804 5.91687 2.44155C5.37228 1.92691 4.83716 1.65701 4.15217 1.55141Z" fill="white"/>
<path d="M4.15217 1.55141C3.96412 1.52242 3.95619 1.51902 4.04468 1.5055C4.21427 1.47958 4.61472 1.51491 4.89067 1.58012C5.53489 1.73232 6.12109 2.12221 6.74683 2.81466L6.91307 2.99862L7.15088 2.96062C8.15274 2.8006 9.17194 2.92778 10.0244 3.31918C10.2589 3.42686 10.6287 3.64121 10.6749 3.69629C10.6896 3.71384 10.7166 3.82684 10.7349 3.94742C10.7982 4.36458 10.7665 4.68434 10.6382 4.92317C10.5683 5.05313 10.5644 5.09432 10.6114 5.20554C10.6489 5.2943 10.7534 5.35999 10.8569 5.35985C11.0687 5.35956 11.2968 5.0192 11.4024 4.54561L11.4444 4.3575L11.5275 4.45109C11.9835 4.96459 12.3417 5.66488 12.4032 6.16335L12.4192 6.29332L12.3426 6.17517C12.2107 5.97186 12.0781 5.83346 11.9084 5.72183C11.6024 5.52062 11.2789 5.45215 10.4222 5.40727C9.64839 5.36675 9.21045 5.30106 8.77621 5.16032C8.03738 4.9209 7.66493 4.60204 6.78729 3.4576C6.39748 2.94928 6.15654 2.66804 5.91687 2.44155C5.37228 1.92691 4.83716 1.65701 4.15217 1.55141Z" fill="white"/>
<path d="M10.8494 2.68637C10.8689 2.34575 10.9153 2.12108 11.0088 1.9159C11.0458 1.83469 11.0804 1.76822 11.0858 1.76822C11.0911 1.76822 11.075 1.82816 11.05 1.90142C10.9821 2.10054 10.9709 2.3729 11.0177 2.68978C11.0771 3.09184 11.1109 3.14985 11.5385 3.58416C11.739 3.78788 11.9723 4.0448 12.0568 4.15511L12.2106 4.35568L12.0568 4.21234C11.8688 4.03705 11.4364 3.6952 11.3409 3.64633C11.2768 3.61356 11.2673 3.61413 11.2278 3.65321C11.1914 3.68922 11.1837 3.74333 11.1787 3.99915C11.1708 4.39786 11.1161 4.65377 10.9842 4.90965C10.9128 5.04805 10.9015 5.01851 10.9661 4.8623C11.0143 4.74566 11.0192 4.69439 11.0189 4.30842C11.0181 3.53291 10.9255 3.34647 10.3823 3.02709C10.2447 2.94618 10.0179 2.8295 9.87839 2.76778C9.73887 2.70606 9.62805 2.6523 9.63208 2.64828C9.64746 2.63307 10.1772 2.78675 10.3905 2.86828C10.7077 2.98954 10.76 3.00526 10.7985 2.99063C10.8244 2.98082 10.8369 2.90608 10.8494 2.68637Z" fill="white"/>
<path d="M4.51745 4.01304C4.13569 3.49066 3.89948 2.68973 3.95062 2.091L3.96643 1.90572L4.05333 1.92148C4.21652 1.95106 4.49789 2.05515 4.62964 2.13469C4.9912 2.35293 5.14773 2.64027 5.30697 3.37811C5.35362 3.59423 5.41482 3.8388 5.44298 3.9216C5.48831 4.05487 5.65962 4.36617 5.7989 4.56834C5.89922 4.71395 5.83258 4.78295 5.61082 4.76305C5.27215 4.73267 4.8134 4.41799 4.51745 4.01304Z" fill="white"/>
<path d="M10.3863 7.90088C8.60224 7.18693 7.97389 6.56721 7.97389 5.52157C7.97389 5.36769 7.97922 5.24179 7.98571 5.24179C7.99221 5.24179 8.06124 5.29257 8.1391 5.35465C8.50088 5.64305 8.906 5.76623 10.0275 5.92885C10.6875 6.02455 11.0589 6.10185 11.4015 6.21477C12.4904 6.57371 13.1641 7.30212 13.3248 8.29426C13.3715 8.58255 13.3441 9.12317 13.2684 9.4081C13.2087 9.63315 13.0263 10.0388 12.9779 10.0544C12.9645 10.0587 12.9514 10.0076 12.9479 9.93809C12.9296 9.56554 12.7402 9.20285 12.4221 8.93116C12.0604 8.62227 11.5745 8.37633 10.3863 7.90088Z" fill="white"/>
<path d="M9.13385 8.19748C9.11149 8.06527 9.07272 7.89643 9.04769 7.82228L9.00217 7.68748L9.08672 7.7818C9.20374 7.91234 9.2962 8.07937 9.37457 8.30185C9.43438 8.47165 9.44111 8.52215 9.44066 8.79807C9.4402 9.06896 9.43273 9.12575 9.3775 9.27859C9.29042 9.51959 9.18233 9.69048 9.00097 9.87391C8.67507 10.2036 8.25607 10.3861 7.65143 10.4618C7.54633 10.4749 7.24 10.4971 6.97069 10.511C6.292 10.5461 5.84531 10.6186 5.44393 10.7587C5.38623 10.7788 5.3347 10.7911 5.32947 10.7859C5.31323 10.7698 5.58651 10.6079 5.81223 10.4998C6.1305 10.3474 6.44733 10.2643 7.15719 10.1468C7.50785 10.0887 7.86998 10.0183 7.96194 9.99029C8.83033 9.72566 9.27671 9.04276 9.13385 8.19748Z" fill="white"/>
<path d="M9.95169 9.64109C9.71465 9.13463 9.66022 8.64564 9.79008 8.18961C9.80399 8.14088 9.82632 8.101 9.83976 8.101C9.85319 8.101 9.90913 8.13105 9.96404 8.16777C10.0733 8.24086 10.2924 8.36395 10.876 8.68023C11.6043 9.0749 12.0196 9.3805 12.302 9.72965C12.5493 10.0354 12.7023 10.3837 12.776 10.8084C12.8177 11.0489 12.7932 11.6277 12.7311 11.8699C12.5353 12.6337 12.0802 13.2336 11.4311 13.5837C11.336 13.635 11.2506 13.6771 11.2414 13.6773C11.2321 13.6775 11.2668 13.5899 11.3184 13.4827C11.5367 13.029 11.5616 12.5877 11.3965 12.0965C11.2954 11.7957 11.0893 11.4287 10.6732 10.8084C10.1893 10.0873 10.0707 9.89539 9.95169 9.64109Z" fill="white"/>
<path d="M3.25046 12.3737C3.91252 11.8181 4.73629 11.4234 5.48666 11.3022C5.81005 11.25 6.34877 11.2707 6.64823 11.3469C7.12824 11.469 7.55763 11.7425 7.78094 12.0683C7.99918 12.3867 8.09281 12.6642 8.19029 13.2816C8.22875 13.5252 8.27057 13.7697 8.28323 13.8251C8.35644 14.1451 8.4989 14.4008 8.67544 14.5293C8.95583 14.7333 9.43865 14.7459 9.91362 14.5618C9.99423 14.5305 10.0642 14.5089 10.0691 14.5138C10.0864 14.5308 9.84719 14.6899 9.67847 14.7737C9.45143 14.8864 9.2709 14.93 9.03102 14.93C8.59601 14.93 8.23486 14.7101 7.9335 14.2616C7.87419 14.1733 7.7409 13.909 7.63729 13.6741C7.3191 12.9528 7.16199 12.7331 6.79255 12.4926C6.47104 12.2834 6.05641 12.2459 5.74449 12.3979C5.33475 12.5976 5.22043 13.118 5.51389 13.4478C5.63053 13.5789 5.84803 13.6919 6.02588 13.7139C6.35861 13.7551 6.64455 13.5035 6.64455 13.1696C6.64455 12.9528 6.56071 12.8291 6.34966 12.7344C6.0614 12.6051 5.75156 12.7563 5.75304 13.0254C5.75368 13.1402 5.80396 13.2122 5.91971 13.2643C5.99397 13.2977 5.99569 13.3003 5.93514 13.2878C5.67066 13.2333 5.6087 12.9164 5.82135 12.706C6.07667 12.4535 6.60461 12.5649 6.78591 12.9097C6.86208 13.0545 6.87091 13.3429 6.80451 13.517C6.6559 13.9068 6.22256 14.1117 5.78297 14.0002C5.48368 13.9242 5.36181 13.842 5.00097 13.4726C4.37395 12.8306 4.13053 12.7062 3.22657 12.566L3.05335 12.5391L3.25046 12.3737Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.308383 0.883984C2.40235 3.40996 3.84457 4.45213 4.00484 4.67231C4.13717 4.85412 4.08737 5.01757 3.86067 5.14567C3.7346 5.21689 3.47541 5.28905 3.34564 5.28905C3.19887 5.28905 3.14847 5.23278 3.14847 5.23278C3.06337 5.15255 3.01544 5.16658 2.5784 4.39555C1.97166 3.45981 1.46389 2.68357 1.45004 2.67057C1.41801 2.64052 1.41856 2.64153 2.51654 4.59413C2.69394 5.0011 2.55182 5.15049 2.55182 5.20845C2.55182 5.32636 2.51946 5.38834 2.37311 5.55059C2.12914 5.8211 2.02008 6.12505 1.94135 6.7541C1.8531 7.45926 1.60492 7.95737 0.917156 8.80989C0.514562 9.30893 0.448686 9.4004 0.3471 9.60153C0.219144 9.85482 0.183961 9.99669 0.169701 10.3165C0.154629 10.6547 0.183983 10.8732 0.287934 11.1965C0.378939 11.4796 0.473932 11.6665 0.716778 12.0403C0.926352 12.3629 1.04702 12.6027 1.04702 12.6965C1.04702 12.7711 1.06136 12.7712 1.38611 12.6983C2.16328 12.5239 2.79434 12.2171 3.14925 11.8411C3.36891 11.6084 3.42048 11.4799 3.42215 11.1611C3.42325 10.9525 3.41587 10.9088 3.35914 10.7888C3.2668 10.5935 3.09869 10.4311 2.72817 10.1794C2.2427 9.84953 2.03534 9.58398 1.97807 9.21878C1.93108 8.91913 1.98559 8.70771 2.25416 8.14825C2.53214 7.56916 2.60103 7.32239 2.64763 6.73869C2.67773 6.36158 2.71941 6.21286 2.82842 6.09348C2.94212 5.969 3.04447 5.92684 3.32584 5.88863C3.78457 5.82635 4.07667 5.70839 4.31677 5.48849C4.52505 5.29772 4.61221 5.11391 4.62558 4.8372L4.63574 4.62747L4.51934 4.49259C4.09783 4.00411 0.0261003 0.5 0.000160437 0.5C-0.00538105 0.5 0.133325 0.672804 0.308383 0.883984ZM1.28364 10.6992C1.37894 10.5314 1.3283 10.3158 1.16889 10.2104C1.01827 10.1109 0.78428 10.1578 0.78428 10.2875C0.78428 10.3271 0.806303 10.3559 0.855937 10.3813C0.939514 10.424 0.945581 10.4721 0.879823 10.5703C0.81323 10.6698 0.818604 10.7573 0.894991 10.8167C1.0181 10.9125 1.19237 10.8598 1.28364 10.6992Z" fill="white"/>
<path d="M9.13385 8.19748C9.11149 8.06527 9.07272 7.89643 9.04769 7.82228L9.00217 7.68748L9.08672 7.7818C9.20374 7.91233 9.2962 8.07937 9.37457 8.30185C9.43438 8.47165 9.44111 8.52215 9.44066 8.79807C9.4402 9.06896 9.43273 9.12575 9.3775 9.27858C9.29042 9.51959 9.18233 9.69048 9.00097 9.87391C8.67507 10.2036 8.25607 10.3861 7.65143 10.4618C7.54633 10.4749 7.24 10.4971 6.97069 10.511C6.292 10.5461 5.84531 10.6186 5.44393 10.7587C5.38623 10.7788 5.3347 10.7911 5.32947 10.7859C5.31323 10.7698 5.58651 10.6079 5.81223 10.4998C6.1305 10.3474 6.44733 10.2643 7.15719 10.1468C7.50785 10.0887 7.86998 10.0183 7.96194 9.99029C8.83033 9.72566 9.27671 9.04276 9.13385 8.19748Z" fill="white"/>
<path d="M9.95169 9.64109C9.71465 9.13463 9.66022 8.64564 9.79009 8.18961C9.80399 8.14088 9.82632 8.101 9.83976 8.101C9.85319 8.101 9.90913 8.13105 9.96404 8.16777C10.0733 8.24086 10.2924 8.36395 10.876 8.68023C11.6043 9.0749 12.0196 9.3805 12.302 9.72965C12.5493 10.0354 12.7023 10.3837 12.776 10.8084C12.8177 11.0489 12.7932 11.6277 12.7311 11.8699C12.5353 12.6337 12.0802 13.2336 11.4311 13.5837C11.336 13.635 11.2506 13.6771 11.2414 13.6773C11.2321 13.6775 11.2668 13.5899 11.3184 13.4827C11.5367 13.029 11.5616 12.5877 11.3965 12.0965C11.2954 11.7957 11.0893 11.4287 10.6732 10.8084C10.1893 10.0873 10.0707 9.89539 9.95169 9.64109Z" fill="white"/>
<path d="M3.25046 12.3737C3.91252 11.8181 4.73629 11.4234 5.48666 11.3022C5.81005 11.25 6.34877 11.2707 6.64823 11.3469C7.12824 11.469 7.55763 11.7425 7.78094 12.0683C7.99918 12.3867 8.09281 12.6642 8.19029 13.2816C8.22875 13.5252 8.27057 13.7697 8.28323 13.8251C8.35644 14.1451 8.4989 14.4008 8.67544 14.5293C8.95583 14.7333 9.43865 14.7459 9.91362 14.5618C9.99423 14.5305 10.0642 14.5089 10.0691 14.5138C10.0864 14.5308 9.84719 14.6899 9.67847 14.7737C9.45143 14.8864 9.2709 14.93 9.03102 14.93C8.59601 14.93 8.23486 14.7101 7.9335 14.2616C7.87419 14.1733 7.7409 13.909 7.63729 13.6741C7.3191 12.9528 7.16199 12.7331 6.79255 12.4926C6.47104 12.2834 6.05641 12.2459 5.74449 12.3979C5.33475 12.5976 5.22043 13.118 5.51389 13.4478C5.63053 13.5789 5.84803 13.6919 6.02588 13.7139C6.35861 13.7551 6.64455 13.5035 6.64455 13.1696C6.64455 12.9528 6.56071 12.8291 6.34966 12.7344C6.0614 12.6051 5.75156 12.7562 5.75304 13.0254C5.75368 13.1402 5.80396 13.2122 5.91971 13.2643C5.99397 13.2977 5.99569 13.3003 5.93514 13.2878C5.67066 13.2333 5.6087 12.9164 5.82135 12.706C6.07667 12.4535 6.60461 12.5649 6.78591 12.9097C6.86208 13.0545 6.87092 13.3429 6.80451 13.517C6.6559 13.9068 6.22256 14.1117 5.78297 14.0002C5.48368 13.9242 5.36181 13.842 5.00097 13.4726C4.37395 12.8306 4.13053 12.7062 3.22657 12.566L3.05335 12.5391L3.25046 12.3737Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.308383 0.883984C2.40235 3.40996 3.84457 4.45213 4.00484 4.67231C4.13717 4.85412 4.08737 5.01757 3.86067 5.14567C3.7346 5.21689 3.47541 5.28905 3.34564 5.28905C3.19887 5.28905 3.14847 5.23278 3.14847 5.23278C3.06337 5.15255 3.01544 5.16658 2.5784 4.39555C1.97166 3.45981 1.46389 2.68357 1.45004 2.67057C1.41801 2.64052 1.41856 2.64153 2.51654 4.59413C2.69394 5.0011 2.55182 5.15049 2.55182 5.20845C2.55182 5.32636 2.51946 5.38834 2.37311 5.55059C2.12914 5.8211 2.02008 6.12505 1.94135 6.7541C1.8531 7.45926 1.60492 7.95737 0.917156 8.80989C0.514562 9.30893 0.448686 9.4004 0.3471 9.60153C0.219144 9.85482 0.183961 9.99669 0.169701 10.3165C0.154629 10.6547 0.183983 10.8732 0.287934 11.1965C0.378939 11.4796 0.473932 11.6665 0.716778 12.0403C0.926351 12.3629 1.04702 12.6027 1.04702 12.6965C1.04702 12.7711 1.06136 12.7712 1.38611 12.6983C2.16328 12.5239 2.79434 12.2171 3.14925 11.8411C3.36891 11.6084 3.42048 11.4799 3.42215 11.1611C3.42325 10.9525 3.41587 10.9088 3.35914 10.7888C3.2668 10.5935 3.09869 10.4311 2.72817 10.1794C2.2427 9.84953 2.03534 9.58398 1.97807 9.21878C1.93108 8.91913 1.98559 8.70771 2.25416 8.14825C2.53214 7.56916 2.60103 7.32239 2.64763 6.73869C2.67773 6.36158 2.71941 6.21286 2.82842 6.09348C2.94212 5.969 3.04447 5.92684 3.32584 5.88863C3.78457 5.82635 4.07667 5.70839 4.31677 5.48849C4.52505 5.29772 4.61221 5.11391 4.62558 4.8372L4.63574 4.62747L4.51934 4.49259C4.09783 4.00411 0.0261003 0.5 0.000160437 0.5C-0.00538105 0.5 0.133325 0.672804 0.308383 0.883984ZM1.28364 10.6992C1.37894 10.5314 1.3283 10.3158 1.16889 10.2104C1.01827 10.1109 0.78428 10.1578 0.78428 10.2875C0.78428 10.3271 0.806303 10.3559 0.855937 10.3813C0.939514 10.424 0.945581 10.4721 0.879823 10.5703C0.81323 10.6698 0.818604 10.7573 0.894991 10.8167C1.0181 10.9125 1.19237 10.8598 1.28364 10.6992Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.92523 5.99865C4.70988 6.06439 4.50054 6.29124 4.43574 6.5291C4.39621 6.67421 4.41864 6.92875 4.47785 7.00736C4.57351 7.13433 4.66602 7.16778 4.91651 7.16603C5.40693 7.16263 5.83327 6.95358 5.88284 6.69224C5.92347 6.47801 5.73622 6.18112 5.4783 6.05078C5.34521 5.98355 5.06217 5.95688 4.92523 5.99865ZM5.49853 6.44422C5.57416 6.33741 5.54107 6.22198 5.41245 6.14391C5.1675 5.99525 4.79708 6.11827 4.79708 6.34826C4.79708 6.46274 4.99025 6.58765 5.16731 6.58765C5.28516 6.58765 5.44644 6.5178 5.49853 6.44422Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@@ -0,0 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 2.5L12.5 5L10 7.5" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 5L12.3333 5" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.5 13.5L3 11L5.5 8.5" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.3333 11L3 11" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 476 B

View File

@@ -1,5 +1,5 @@
import React from 'react'
import styled from 'styled-components'
import styled from 'styled-components/macro'
import useCopyClipboard from '../../hooks/useCopyClipboard'
import { LinkStyledButton } from '../../theme'

View File

@@ -1,11 +1,11 @@
import React from 'react'
import styled from 'styled-components'
import styled from 'styled-components/macro'
import { CheckCircle, Triangle } from 'react-feather'
import { useActiveWeb3React } from '../../hooks'
import { getEtherscanLink } from '../../utils'
import { useActiveWeb3React } from '../../hooks/web3'
import { ExternalLink } from '../../theme'
import { useAllTransactions } from '../../state/transactions/hooks'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { RowFixed } from '../Row'
import Loader from '../Loader'
@@ -49,7 +49,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,17 @@
import React, { useCallback, useContext } from 'react'
import { useDispatch } from 'react-redux'
import styled, { ThemeContext } from 'styled-components'
import { useActiveWeb3React } from '../../hooks'
import { SUPPORTED_WALLETS } from '../../constants/wallet'
import { useActiveWeb3React } from '../../hooks/web3'
import { AppDispatch } from '../../state'
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'
@@ -26,7 +26,7 @@ const HeaderRow = styled.div`
${({ theme }) => theme.flexRowNoWrap};
padding: 1rem 1rem;
font-weight: 500;
color: ${props => (props.color === 'blue' ? ({ theme }) => theme.primary1 : 'inherit')};
color: ${(props) => (props.color === 'blue' ? ({ theme }) => theme.primary1 : 'inherit')};
${({ theme }) => theme.mediaWidth.upToMedium`
padding: 1rem;
`};
@@ -76,7 +76,6 @@ const AccountGroupingRow = styled.div`
`
const AccountSection = styled.div`
background-color: ${({ theme }) => theme.bg1};
padding: 0rem 1rem;
${({ theme }) => theme.mediaWidth.upToMedium`padding: 0rem 1rem 1.5rem 1rem;`};
`
@@ -223,7 +222,7 @@ export default function AccountDetails({
pendingTransactions,
confirmedTransactions,
ENSName,
openOptions
openOptions,
}: AccountDetailsProps) {
const { chainId, account, connector } = useActiveWeb3React()
const theme = useContext(ThemeContext)
@@ -234,10 +233,10 @@ export default function AccountDetails({
const isMetaMask = !!(ethereum && ethereum.isMetaMask)
const name = Object.keys(SUPPORTED_WALLETS)
.filter(
k =>
(k) =>
SUPPORTED_WALLETS[k].connector === connector && (connector !== injected || isMetaMask === (k === 'METAMASK'))
)
.map(k => SUPPORTED_WALLETS[k].name)[0]
.map((k) => SUPPORTED_WALLETS[k].name)[0]
return <WalletName>Connected with {name}</WalletName>
}
@@ -355,7 +354,7 @@ export default function AccountDetails({
<AddressLink
hasENS={!!ENSName}
isENS={true}
href={chainId && getEtherscanLink(chainId, ENSName, 'address')}
href={chainId && getExplorerLink(chainId, ENSName, ExplorerDataType.ADDRESS)}
>
<LinkIcon size={16} />
<span style={{ marginLeft: '4px' }}>View on Etherscan</span>
@@ -377,7 +376,7 @@ export default function AccountDetails({
<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>

View File

@@ -1,11 +1,11 @@
import React, { useContext, useCallback } from 'react'
import styled, { ThemeContext } from 'styled-components'
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}
@@ -68,7 +68,7 @@ const Input = styled.input<{ error?: boolean }>`
export default function AddressInputPanel({
id,
value,
onChange
onChange,
}: {
id?: string
// the typed string value
@@ -82,7 +82,7 @@ export default function AddressInputPanel({
const { address, loading, name } = useENS(value)
const handleInput = useCallback(
event => {
(event) => {
const input = event.target.value
const withoutSpaces = input.replace(/\s+/g, '')
onChange(withoutSpaces)
@@ -102,7 +102,10 @@ export default function AddressInputPanel({
Recipient
</TYPE.black>
{address && chainId && (
<ExternalLink href={getEtherscanLink(chainId, name ?? address, 'address')} style={{ fontSize: '14px' }}>
<ExternalLink
href={getExplorerLink(chainId, name ?? address, ExplorerDataType.ADDRESS)}
style={{ fontSize: '14px' }}
>
(View on Etherscan)
</ExternalLink>
)}

View File

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

View File

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

View File

@@ -0,0 +1,73 @@
import { readableColor } from 'polished'
import { PropsWithChildren } from 'react'
import styled, { DefaultTheme } from 'styled-components'
import { Color } from 'theme/styled'
export enum BadgeVariant {
DEFAULT = 'DEFAULT',
NEGATIVE = 'NEGATIVE',
POSITIVE = 'POSITIVE',
PRIMARY = 'PRIMARY',
WARNING = 'WARNING',
WARNING_OUTLINE = 'WARNING_OUTLINE',
}
export interface BadgeProps {
variant?: BadgeVariant
}
function pickBackgroundColor(variant: BadgeVariant | undefined, theme: DefaultTheme): Color {
switch (variant) {
case BadgeVariant.NEGATIVE:
return theme.error
case BadgeVariant.POSITIVE:
return theme.success
case BadgeVariant.PRIMARY:
return theme.primary1
case BadgeVariant.WARNING:
return theme.warning
case BadgeVariant.WARNING_OUTLINE:
return 'transparent'
default:
return theme.bg2
}
}
function pickBorder(variant: BadgeVariant | undefined, theme: DefaultTheme): string {
switch (variant) {
case BadgeVariant.WARNING_OUTLINE:
return `1px solid ${theme.warning}`
default:
return 'unset'
}
}
function pickFontColor(variant: BadgeVariant | undefined, theme: DefaultTheme): string {
switch (variant) {
case BadgeVariant.NEGATIVE:
return readableColor(theme.error)
case BadgeVariant.POSITIVE:
return readableColor(theme.success)
case BadgeVariant.WARNING:
return readableColor(theme.warning)
case BadgeVariant.WARNING_OUTLINE:
return theme.warning
default:
return readableColor(theme.bg2)
}
}
const Badge = styled.div<PropsWithChildren<BadgeProps>>`
align-items: center;
background-color: ${({ theme, variant }) => pickBackgroundColor(variant, theme)};
border: ${({ theme, variant }) => pickBorder(variant, theme)};
border-radius: 0.5rem;
color: ${({ theme, variant }) => pickFontColor(variant, theme)};
display: inline-flex;
padding: 4px 6px;
justify-content: center;
font-weight: 500;
`
export default Badge

View File

@@ -1,6 +1,14 @@
import React, { ReactNode, useMemo } from 'react'
import { BLOCKED_ADDRESSES } from '../../constants'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
// SDN OFAC addresses
const BLOCKED_ADDRESSES: string[] = [
'0x7F367cC41522cE07553e823bf3be79A889DEbe1B',
'0xd882cFc20F52f2599D84b8e8D58C7FB62cfE344b',
'0x901bb9583b24D97e995513C6778dc6888AB6870e',
'0xA7e5d5A720f06526557c513402f2e6B5fA20b008',
'0x8576aCC5C05D6Ce88f4e49bf65BdF0C62F91353C',
]
export default function Blocklist({ children }: { children: ReactNode }) {
const { account } = useActiveWeb3React()

View File

@@ -0,0 +1,153 @@
import { Story } from '@storybook/react/types-6-0'
import styled from 'styled-components/macro'
import React from 'react'
import {
ButtonConfirmed,
ButtonDropdown,
ButtonDropdownGrey,
ButtonDropdownLight,
ButtonEmpty,
ButtonError,
ButtonGray,
ButtonLight,
ButtonOutlined,
ButtonPink,
ButtonPrimary,
ButtonRadio,
ButtonSecondary,
ButtonUNIGradient,
ButtonWhite,
} from './index'
const wrapperCss = styled.main`
font-size: 2em;
margin: 3em;
max-width: 300px;
`
export default {
title: 'Buttons',
argTypes: {
disabled: { control: { type: 'boolean' } },
onClick: { action: 'clicked' },
},
decorators: [
(Component: Story) => (
<div css={wrapperCss}>
<Component />
</div>
),
],
}
const Unicorn = () => (
<span role="img" aria-label="unicorn">
🦄
</span>
)
export const Radio = () => (
<ButtonRadio>
<Unicorn />
&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,10 +1,11 @@
import React from 'react'
import styled from 'styled-components'
import { darken, lighten } from 'polished'
import styled from 'styled-components/macro'
import { darken } from 'polished'
import { RowBetween } from '../Row'
import { ChevronDown } from 'react-feather'
import { ChevronDown, Check } from 'react-feather'
import { Button as RebassButton, ButtonProps } from 'rebass/styled-components'
import useTheme from 'hooks/useTheme'
const Base = styled(RebassButton)<{
padding?: string
@@ -12,7 +13,7 @@ const Base = styled(RebassButton)<{
borderRadius?: string
altDisabledStyle?: boolean
}>`
padding: ${({ padding }) => (padding ? padding : '18px')};
padding: ${({ padding }) => (padding ? padding : '16px')};
width: ${({ width }) => (width ? width : '100%')};
font-weight: 500;
text-align: center;
@@ -31,11 +32,24 @@ const Base = styled(RebassButton)<{
z-index: 1;
&:disabled {
cursor: auto;
pointer-events: none;
}
will-change: transform;
transition: transform 450ms ease;
transform: perspective(1px) translateZ(0);
&:hover {
transform: scale(0.99);
}
> * {
user-select: none;
}
> a {
text-decoration: none;
}
`
export const ButtonPrimary = styled(Base)`
@@ -54,14 +68,14 @@ export const ButtonPrimary = styled(Base)`
}
&:disabled {
background-color: ${({ theme, altDisabledStyle, disabled }) =>
altDisabledStyle ? (disabled ? theme.bg3 : theme.primary1) : theme.bg3};
color: ${({ theme, altDisabledStyle, disabled }) =>
altDisabledStyle ? (disabled ? theme.text3 : 'white') : theme.text3};
altDisabledStyle ? (disabled ? theme.primary1 : theme.primary1) : theme.primary1};
color: white;
cursor: auto;
box-shadow: none;
border: 1px solid transparent;
outline: none;
opacity: ${({ altDisabledStyle }) => (altDisabledStyle ? '0.5' : '1')};
opacity: 0.4;
opacity: ${({ altDisabledStyle }) => (altDisabledStyle ? '0.5' : '0.4')};
}
`
@@ -94,18 +108,18 @@ export const ButtonLight = styled(Base)`
`
export const ButtonGray = styled(Base)`
background-color: ${({ theme }) => theme.bg3};
background-color: ${({ theme }) => theme.bg1};
color: ${({ theme }) => theme.text2};
font-size: 16px;
font-weight: 500;
&:focus {
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg4)};
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
}
&:hover {
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg4)};
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
}
&:active {
background-color: ${({ theme, disabled }) => !disabled && darken(0.1, theme.bg4)};
background-color: ${({ theme, disabled }) => !disabled && darken(0.1, theme.bg2)};
}
`
@@ -221,6 +235,28 @@ export const ButtonEmpty = styled(Base)`
}
`
export const ButtonText = styled(Base)`
padding: 0;
width: fit-content;
background: none;
text-decoration: none;
&:focus {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
text-decoration: underline;
}
&:hover {
// text-decoration: underline;
opacity: 0.9;
}
&:active {
text-decoration: underline;
}
&:disabled {
opacity: 50%;
cursor: auto;
}
`
export const ButtonWhite = styled(Base)`
border: 1px solid #edeef2;
background-color: ${({ theme }) => theme.bg1};
@@ -243,12 +279,14 @@ export const ButtonWhite = styled(Base)`
`
const ButtonConfirmedStyle = styled(Base)`
background-color: ${({ theme }) => lighten(0.5, theme.green1)};
color: ${({ theme }) => theme.green1};
border: 1px solid ${({ theme }) => theme.green1};
background-color: ${({ theme }) => theme.bg3};
color: ${({ theme }) => theme.text1};
/* border: 1px solid ${({ theme }) => theme.green1}; */
&:disabled {
opacity: 50%;
/* opacity: 50%; */
background-color: ${({ theme }) => theme.bg2};
color: ${({ theme }) => theme.text2};
cursor: auto;
}
`
@@ -337,3 +375,57 @@ export function ButtonRadio({ active, ...rest }: { active?: boolean } & ButtonPr
return <ButtonPrimary {...rest} />
}
}
const ActiveOutlined = styled(ButtonOutlined)`
border: 1px solid;
border-color: ${({ theme }) => theme.primary1};
`
const Circle = styled.div`
height: 20px;
width: 20px;
border-radius: 50%;
background-color: ${({ theme }) => theme.primary1};
display: flex;
align-items: center;
justify-content: center;
`
const CheckboxWrapper = styled.div`
width: 30px;
padding: 0 10px;
position: absolute;
top: 10px;
right: 10px;
`
const ResponsiveCheck = styled(Check)`
size: 13px;
`
export function ButtonRadioChecked({ active = false, children, ...rest }: { active?: boolean } & ButtonProps) {
const theme = useTheme()
if (!active) {
return (
<ButtonOutlined borderRadius="12px" padding="12px 8px" {...rest}>
{<RowBetween>{children}</RowBetween>}
</ButtonOutlined>
)
} else {
return (
<ActiveOutlined {...rest} padding="12px 8px" borderRadius="12px">
{
<RowBetween>
{children}
<CheckboxWrapper>
<Circle>
<ResponsiveCheck size={13} stroke={theme.white} />
</Circle>
</CheckboxWrapper>
</RowBetween>
}
</ActiveOutlined>
)
}
}

View File

@@ -1,12 +1,10 @@
import React from 'react'
import styled from 'styled-components'
import { CardProps, Text } from 'rebass'
import styled from 'styled-components/macro'
import { Box } from 'rebass/styled-components'
const Card = styled(Box)<{ width?: string; padding?: string; border?: string; borderRadius?: string }>`
width: ${({ width }) => width ?? '100%'};
border-radius: 16px;
padding: 1.25rem;
padding: 1rem;
padding: ${({ padding }) => padding};
border: ${({ border }) => border};
border-radius: ${({ borderRadius }) => borderRadius};
@@ -26,13 +24,21 @@ export const GreyCard = styled(Card)`
background-color: ${({ theme }) => theme.bg3};
`
export const DarkGreyCard = styled(Card)`
background-color: ${({ theme }) => theme.bg2};
`
export const DarkCard = styled(Card)`
background-color: ${({ theme }) => theme.bg0};
`
export const OutlineCard = styled(Card)`
border: 1px solid ${({ theme }) => theme.bg3};
`
export const YellowCard = styled(Card)`
background-color: rgba(243, 132, 30, 0.05);
color: ${({ theme }) => theme.yellow2};
color: ${({ theme }) => theme.yellow3};
font-weight: 500;
`
@@ -42,19 +48,8 @@ export const PinkCard = styled(Card)`
font-weight: 500;
`
const BlueCardStyled = styled(Card)`
export const BlueCard = styled(Card)`
background-color: ${({ theme }) => theme.primary5};
color: ${({ theme }) => theme.primary1};
color: ${({ theme }) => theme.blue2};
border-radius: 12px;
width: fit-content;
`
export const BlueCard = ({ children, ...rest }: CardProps) => {
return (
<BlueCardStyled {...rest}>
<Text fontWeight={500} color="#2172E5">
{children}
</Text>
</BlueCardStyled>
)
}

View File

@@ -1,4 +1,4 @@
import styled from 'styled-components'
import styled from 'styled-components/macro'
const Column = styled.div`
display: flex;

View File

@@ -20,7 +20,7 @@ export default function Confetti({ start, variant }: { start: boolean; variant?:
h: height,
w: width,
x: 0,
y: _variant === 'top' ? height * 0.25 : _variant === 'bottom' ? height * 0.75 : height * 0.5
y: _variant === 'top' ? height * 0.25 : _variant === 'bottom' ? height * 0.75 : height * 0.5,
}}
initialVelocityX={15}
initialVelocityY={30}

View File

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

View File

@@ -1,68 +1,114 @@
import { Currency, Pair } from '@uniswap/sdk'
import { Pair } from '@uniswap/v2-sdk'
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
import React, { useState, useCallback } from 'react'
import styled from 'styled-components'
import styled from 'styled-components/macro'
import { darken } from 'polished'
import { useCurrencyBalance } from '../../state/wallet/hooks'
import CurrencySearchModal from '../SearchModal/CurrencySearchModal'
import CurrencyLogo from '../CurrencyLogo'
import DoubleCurrencyLogo from '../DoubleLogo'
import { RowBetween } from '../Row'
import { ButtonGray } from '../Button'
import { RowBetween, RowFixed } from '../Row'
import { TYPE } from '../../theme'
import { Input as NumericalInput } from '../NumericalInput'
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { useTranslation } from 'react-i18next'
import useTheme from '../../hooks/useTheme'
import { Lock } from 'react-feather'
import { AutoColumn } from 'components/Column'
import { FiatValue } from './FiatValue'
import { formatTokenAmount } from 'utils/formatTokenAmount'
const InputRow = styled.div<{ selected: boolean }>`
${({ theme }) => theme.flexRowNoWrap}
align-items: center;
padding: ${({ selected }) => (selected ? '0.75rem 0.5rem 0.75rem 1rem' : '0.75rem 0.75rem 0.75rem 1rem')};
const InputPanel = styled.div<{ hideInput?: boolean }>`
${({ theme }) => theme.flexColumnNoWrap}
position: relative;
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
background-color: ${({ theme, hideInput }) => (hideInput ? 'transparent' : theme.bg2)};
z-index: 1;
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
`
const CurrencySelect = styled.button<{ selected: boolean }>`
const FixedContainer = styled.div`
width: 100%;
height: 100%;
position: absolute;
border-radius: 20px;
background-color: ${({ theme }) => theme.bg1};
opacity: 0.95;
display: flex;
align-items: center;
height: 2.2rem;
font-size: 20px;
justify-content: center;
z-index: 2;
`
const Container = styled.div<{ hideInput: boolean }>`
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
border: 1px solid ${({ theme, hideInput }) => (hideInput ? ' transparent' : theme.bg2)};
background-color: ${({ theme }) => theme.bg1};
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
:focus,
:hover {
border: 1px solid ${({ theme, hideInput }) => (hideInput ? ' transparent' : theme.bg3)};
}
`
const CurrencySelect = styled(ButtonGray)<{ selected: boolean; hideInput?: boolean }>`
align-items: center;
font-size: 24px;
font-weight: 500;
background-color: ${({ selected, theme }) => (selected ? theme.bg1 : theme.primary1)};
background-color: ${({ selected, theme }) => (selected ? theme.bg0 : theme.primary1)};
color: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)};
border-radius: 12px;
border-radius: 16px;
box-shadow: ${({ selected }) => (selected ? 'none' : '0px 6px 10px rgba(0, 0, 0, 0.075)')};
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
outline: none;
cursor: pointer;
user-select: none;
border: none;
padding: 0 0.5rem;
height: ${({ hideInput }) => (hideInput ? '2.8rem' : '2.4rem')};
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
padding: 0 8px;
justify-content: space-between;
margin-right: ${({ hideInput }) => (hideInput ? '0' : '12px')};
:focus,
:hover {
background-color: ${({ selected, theme }) => (selected ? theme.bg2 : darken(0.05, theme.primary1))};
}
`
const InputRow = styled.div<{ selected: boolean }>`
${({ theme }) => theme.flexRowNoWrap}
align-items: center;
padding: ${({ selected }) => (selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 0.75rem 1rem')};
`
const LabelRow = styled.div`
${({ theme }) => theme.flexRowNoWrap}
align-items: center;
color: ${({ theme }) => theme.text1};
font-size: 0.75rem;
line-height: 1rem;
padding: 0.75rem 1rem 0 1rem;
padding: 0 1rem 1rem;
span:hover {
cursor: pointer;
color: ${({ theme }) => darken(0.2, theme.text2)};
}
`
const FiatRow = styled(LabelRow)`
justify-content: flex-end;
`
const Aligner = styled.span`
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
`
const StyledDropDown = styled(DropDown)<{ selected: boolean }>`
margin: 0 0.25rem 0 0.5rem;
margin: 0 0.25rem 0 0.35rem;
height: 35%;
path {
@@ -71,42 +117,25 @@ const StyledDropDown = styled(DropDown)<{ selected: boolean }>`
}
`
const InputPanel = styled.div<{ hideInput?: boolean }>`
${({ theme }) => theme.flexColumnNoWrap}
position: relative;
border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')};
background-color: ${({ theme }) => theme.bg2};
z-index: 1;
`
const Container = styled.div<{ hideInput: boolean }>`
border-radius: ${({ hideInput }) => (hideInput ? '8px' : '20px')};
border: 1px solid ${({ theme }) => theme.bg2};
background-color: ${({ theme }) => theme.bg1};
`
const StyledTokenName = styled.span<{ active?: boolean }>`
${({ active }) => (active ? ' margin: 0 0.25rem 0 0.75rem;' : ' margin: 0 0.25rem 0 0.25rem;')}
font-size: ${({ active }) => (active ? '20px' : '16px')};
${({ active }) => (active ? ' margin: 0 0.25rem 0 0.25rem;' : ' margin: 0 0.25rem 0 0.25rem;')}
font-size: ${({ active }) => (active ? '18px' : '18px')};
`
const StyledBalanceMax = styled.button`
height: 28px;
background-color: ${({ theme }) => theme.primary5};
border: 1px solid ${({ theme }) => theme.primary5};
border-radius: 0.5rem;
font-size: 0.875rem;
const StyledBalanceMax = styled.button<{ disabled?: boolean }>`
background-color: transparent;
border: none;
border-radius: 12px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
margin-right: 0.5rem;
color: ${({ theme }) => theme.primaryText1};
:hover {
border: 1px solid ${({ theme }) => theme.primary1};
}
padding: 0;
color: ${({ theme }) => theme.primary1};
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')};
margin-left: 0.25rem;
:focus {
border: 1px solid ${({ theme }) => theme.primary1};
outline: none;
}
@@ -123,14 +152,16 @@ interface CurrencyInputPanelProps {
label?: string
onCurrencySelect?: (currency: Currency) => void
currency?: Currency | null
disableCurrencySelect?: boolean
hideBalance?: boolean
pair?: Pair | null
hideInput?: boolean
otherCurrency?: Currency | null
fiatValue?: CurrencyAmount<Token> | null
priceImpact?: Percent
id: string
showCommonBases?: boolean
customBalanceText?: string
locked?: boolean
}
export default function CurrencyInputPanel({
@@ -138,17 +169,19 @@ export default function CurrencyInputPanel({
onUserInput,
onMax,
showMaxButton,
label = 'Input',
onCurrencySelect,
currency,
disableCurrencySelect = false,
hideBalance = false,
pair = null, // used for double token logo
hideInput = false,
otherCurrency,
id,
showCommonBases,
customBalanceText
customBalanceText,
fiatValue,
priceImpact,
hideBalance = false,
pair = null, // used for double token logo
hideInput = false,
locked = false,
...rest
}: CurrencyInputPanelProps) {
const { t } = useTranslation()
@@ -162,79 +195,99 @@ export default function CurrencyInputPanel({
}, [setModalOpen])
return (
<InputPanel id={id}>
<InputPanel id={id} hideInput={hideInput} {...rest}>
{locked && (
<FixedContainer>
<AutoColumn gap="sm" justify="center">
<Lock />
<TYPE.label fontSize="12px" textAlign="center">
The market price is outside your specified price range. Single-asset deposit only.
</TYPE.label>
</AutoColumn>
</FixedContainer>
)}
<Container hideInput={hideInput}>
{!hideInput && (
<LabelRow>
<RowBetween>
<TYPE.body color={theme.text2} fontWeight={500} fontSize={14}>
{label}
</TYPE.body>
{account && (
<TYPE.body
onClick={onMax}
color={theme.text2}
fontWeight={500}
fontSize={14}
style={{ display: 'inline', cursor: 'pointer' }}
>
{!hideBalance && !!currency && selectedCurrencyBalance
? (customBalanceText ?? 'Balance: ') + selectedCurrencyBalance?.toSignificant(6)
: ' -'}
</TYPE.body>
)}
</RowBetween>
</LabelRow>
)}
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={disableCurrencySelect}>
{!hideInput && (
<>
<NumericalInput
className="token-amount-input"
value={value}
onUserInput={val => {
onUserInput(val)
}}
/>
{account && currency && showMaxButton && label !== 'To' && (
<StyledBalanceMax onClick={onMax}>MAX</StyledBalanceMax>
)}
</>
)}
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={!onCurrencySelect}>
<CurrencySelect
selected={!!currency}
hideInput={hideInput}
className="open-currency-select-button"
onClick={() => {
if (!disableCurrencySelect) {
if (onCurrencySelect) {
setModalOpen(true)
}
}}
>
<Aligner>
{pair ? (
<DoubleCurrencyLogo currency0={pair.token0} currency1={pair.token1} size={24} margin={true} />
) : currency ? (
<CurrencyLogo currency={currency} size={'24px'} />
) : null}
{pair ? (
<StyledTokenName className="pair-name-container">
{pair?.token0.symbol}:{pair?.token1.symbol}
</StyledTokenName>
) : (
<StyledTokenName className="token-symbol-container" active={Boolean(currency && currency.symbol)}>
{(currency && currency.symbol && currency.symbol.length > 20
? currency.symbol.slice(0, 4) +
'...' +
currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length)
: currency?.symbol) || t('selectToken')}
</StyledTokenName>
)}
{!disableCurrencySelect && <StyledDropDown selected={!!currency} />}
<RowFixed>
{pair ? (
<span style={{ marginRight: '0.5rem' }}>
<DoubleCurrencyLogo currency0={pair.token0} currency1={pair.token1} size={24} margin={true} />
</span>
) : currency ? (
<CurrencyLogo style={{ marginRight: '0.5rem' }} currency={currency} size={'24px'} />
) : null}
{pair ? (
<StyledTokenName className="pair-name-container">
{pair?.token0.symbol}:{pair?.token1.symbol}
</StyledTokenName>
) : (
<StyledTokenName className="token-symbol-container" active={Boolean(currency && currency.symbol)}>
{(currency && currency.symbol && currency.symbol.length > 20
? currency.symbol.slice(0, 4) +
'...' +
currency.symbol.slice(currency.symbol.length - 5, currency.symbol.length)
: currency?.symbol) || t('selectToken')}
</StyledTokenName>
)}
</RowFixed>
{onCurrencySelect && <StyledDropDown selected={!!currency} />}
</Aligner>
</CurrencySelect>
{!hideInput && (
<>
<NumericalInput
className="token-amount-input"
value={value}
onUserInput={(val) => {
onUserInput(val)
}}
/>
</>
)}
</InputRow>
{!hideInput && !hideBalance && (
<FiatRow>
<RowBetween>
{account ? (
<RowFixed style={{ height: '17px' }}>
<TYPE.body
onClick={onMax}
color={theme.text2}
fontWeight={400}
fontSize={14}
style={{ display: 'inline', cursor: 'pointer' }}
>
{!hideBalance && !!currency && selectedCurrencyBalance
? (customBalanceText ?? 'Balance: ') +
formatTokenAmount(selectedCurrencyBalance, 4) +
' ' +
currency.symbol
: '-'}
</TYPE.body>
{showMaxButton && selectedCurrencyBalance ? (
<StyledBalanceMax onClick={onMax}>(Max)</StyledBalanceMax>
) : null}
</RowFixed>
) : (
'-'
)}
<FiatValue fiatValue={fiatValue} priceImpact={priceImpact} />
</RowBetween>
</FiatRow>
)}
</Container>
{!disableCurrencySelect && onCurrencySelect && (
{onCurrencySelect && (
<CurrencySearchModal
isOpen={modalOpen}
onDismiss={handleDismissSearch}

View File

@@ -1,10 +1,9 @@
import { Currency, ETHER, Token } from '@uniswap/sdk'
import { ChainId, Currency } from '@uniswap/sdk-core'
import React, { useMemo } from 'react'
import styled from 'styled-components'
import styled from 'styled-components/macro'
import EthereumLogo from '../../assets/images/ethereum-logo.png'
import useHttpLocations from '../../hooks/useHttpLocations'
import { WrappedTokenInfo } from '../../state/lists/hooks'
import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
import Logo from '../Logo'
export const getTokenLogoURL = (address: string) =>
@@ -28,7 +27,8 @@ const StyledLogo = styled(Logo)<{ size: string }>`
export default function CurrencyLogo({
currency,
size = '24px',
style
style,
...rest
}: {
currency?: Currency
size?: string
@@ -37,20 +37,21 @@ export default function CurrencyLogo({
const uriLocations = useHttpLocations(currency instanceof WrappedTokenInfo ? currency.logoURI : undefined)
const srcs: string[] = useMemo(() => {
if (currency === ETHER) return []
if (!currency || currency.isEther) return []
if (currency instanceof Token) {
if (currency.isToken) {
const defaultUrls = currency.chainId === ChainId.MAINNET ? [getTokenLogoURL(currency.address)] : []
if (currency instanceof WrappedTokenInfo) {
return [...uriLocations, getTokenLogoURL(currency.address)]
return [...uriLocations, ...defaultUrls]
}
return [getTokenLogoURL(currency.address)]
return defaultUrls
}
return []
}, [currency, uriLocations])
if (currency === ETHER) {
return <StyledEthereumLogo src={EthereumLogo} size={size} style={style} />
if (currency?.isEther) {
return <StyledEthereumLogo src={EthereumLogo} size={size} style={style} {...rest} />
}
return <StyledLogo size={size} srcs={srcs} alt={`${currency?.symbol ?? 'token'} logo`} style={style} />
return <StyledLogo size={size} srcs={srcs} alt={`${currency?.symbol ?? 'token'} logo`} style={style} {...rest} />
}

View File

@@ -0,0 +1,18 @@
import { Story } from '@storybook/react/types-6-0'
import React from 'react'
import { DAI, WBTC } from '../../constants/tokens'
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,16 +1,16 @@
import { Currency } from '@uniswap/sdk'
import { Currency } from '@uniswap/sdk-core'
import React from 'react'
import styled from 'styled-components'
import styled from 'styled-components/macro'
import CurrencyLogo from '../CurrencyLogo'
const Wrapper = styled.div<{ margin: boolean; sizeraw: number }>`
position: relative;
display: flex;
flex-direction: row;
margin-right: ${({ sizeraw, margin }) => margin && (sizeraw / 3 + 8).toString() + 'px'};
margin-left: ${({ sizeraw, margin }) => margin && (sizeraw / 3 + 8).toString() + 'px'};
`
interface DoubleCurrencyLogoProps {
export interface DoubleCurrencyLogoProps {
margin?: boolean
size?: number
currency0?: Currency
@@ -29,7 +29,7 @@ export default function DoubleCurrencyLogo({
currency0,
currency1,
size = 16,
margin = false
margin = false,
}: DoubleCurrencyLogoProps) {
return (
<Wrapper sizeraw={size} margin={margin}>

View File

@@ -0,0 +1,182 @@
import React, { ErrorInfo } from 'react'
import store, { AppState } from '../../state'
import { ExternalLink, ThemedBackground, TYPE } from '../../theme'
import { AutoColumn } from '../Column'
import styled from 'styled-components/macro'
import ReactGA from 'react-ga'
import { getUserAgent } from '../../utils/getUserAgent'
import { AutoRow } from '../Row'
const FallbackWrapper = styled.div`
display: flex;
flex-direction: column;
width: 100%;
align-items: center;
z-index: 1;
`
const BodyWrapper = styled.div<{ margin?: string }>`
padding: 1rem;
width: 100%;
white-space: ;
`
const CodeBlockWrapper = styled.div`
background: ${({ theme }) => theme.bg0};
overflow: auto;
white-space: pre;
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
0px 24px 32px rgba(0, 0, 0, 0.01);
border-radius: 24px;
padding: 18px 24px;
color: ${({ theme }) => theme.text1};
`
const LinkWrapper = styled.div`
color: ${({ theme }) => theme.blue1};
padding: 6px 24px;
`
const SomethingWentWrongWrapper = styled.div`
padding: 6px 24px;
`
type ErrorBoundaryState = {
error: Error | null
}
export default class ErrorBoundary extends React.Component<unknown, ErrorBoundaryState> {
constructor(props: unknown) {
super(props)
this.state = { error: null }
}
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return { error }
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
ReactGA.exception({
...error,
...errorInfo,
fatal: true,
})
}
render() {
const { error } = this.state
if (error !== null) {
const encodedBody = encodeURIComponent(issueBody(error))
return (
<FallbackWrapper>
<ThemedBackground />
<BodyWrapper>
<AutoColumn gap={'md'}>
<SomethingWentWrongWrapper>
<TYPE.label fontSize={24} fontWeight={600}>
Something went wrong
</TYPE.label>
</SomethingWentWrongWrapper>
<CodeBlockWrapper>
<code>
<TYPE.main fontSize={10}>{error.stack}</TYPE.main>
</code>
</CodeBlockWrapper>
<AutoRow>
<LinkWrapper>
<ExternalLink
id="create-github-issue-link"
href={`https://github.com/Uniswap/uniswap-interface/issues/new?assignees=&labels=bug&body=${encodedBody}&title=${encodeURIComponent(
`Crash report: \`${error.name}${error.message && `: ${error.message}`}\``
)}`}
target="_blank"
>
<TYPE.link fontSize={16}>
Create an issue on GitHub
<span></span>
</TYPE.link>
</ExternalLink>
</LinkWrapper>
<LinkWrapper>
<ExternalLink id="get-support-on-discord" href="https://discord.gg/FCfyBSbCU5" target="_blank">
<TYPE.link fontSize={16}>
Get support on Discord
<span></span>
</TYPE.link>
</ExternalLink>
</LinkWrapper>
</AutoRow>
</AutoColumn>
</BodyWrapper>
</FallbackWrapper>
)
}
return this.props.children
}
}
function getRelevantState(): null | keyof AppState {
const path = window.location.hash
if (!path.startsWith('#/')) {
return null
}
const pieces = path.substring(2).split(/[\/\\?]/)
switch (pieces[0]) {
case 'swap':
return 'swap'
case 'add':
if (pieces[1] === 'v2') return 'mint'
else return 'mintV3'
case 'remove':
if (pieces[1] === 'v2') return 'burn'
else return 'burnV3'
}
return null
}
function issueBody(error: Error): string {
const relevantState = getRelevantState()
const deviceData = getUserAgent()
return `## URL
${window.location.href}
${
relevantState
? `## \`${relevantState}\` state
\`\`\`json
${JSON.stringify(store.getState()[relevantState], null, 2)}
\`\`\`
`
: ''
}
${
error.name &&
`## Error
\`\`\`
${error.name}${error.message && `: ${error.message}`}
\`\`\`
`
}
${
error.stack &&
`## Stacktrace
\`\`\`
${error.stack}
\`\`\`
`
}
${
deviceData &&
`## Device data
\`\`\`json
${JSON.stringify(deviceData, null, 2)}
\`\`\`
`
}
`
}

View File

@@ -0,0 +1,76 @@
import React from 'react'
import { FeeAmount } from '@uniswap/v3-sdk'
import { useTranslation } from 'react-i18next'
import { AutoColumn } from 'components/Column'
import { DynamicSection } from 'pages/AddLiquidity/styled'
import { TYPE } from 'theme'
import { RowBetween } from 'components/Row'
import { ButtonRadioChecked } from 'components/Button'
import styled from 'styled-components/macro'
const ResponsiveText = styled(TYPE.label)`
${({ theme }) => theme.mediaWidth.upToSmall`
font-size: 12px;
`};
`
export default function FeeSelector({
disabled = false,
feeAmount,
handleFeePoolSelect,
}: {
disabled?: boolean
feeAmount?: FeeAmount
handleFeePoolSelect: (feeAmount: FeeAmount) => void
}) {
const { t } = useTranslation()
return (
<AutoColumn gap="16px">
<DynamicSection gap="md" disabled={disabled}>
<TYPE.label>{t('selectPool')}</TYPE.label>
<TYPE.main fontSize={14} fontWeight={400} style={{ marginBottom: '.5rem', lineHeight: '125%' }}>
Select a pool type based on your preferred liquidity provider fee.
</TYPE.main>
<RowBetween>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.LOW}
onClick={() => handleFeePoolSelect(FeeAmount.LOW)}
>
<AutoColumn gap="sm" justify="flex-start">
<ResponsiveText>0.05% {t('fee')}</ResponsiveText>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
Best for stable pairs.
</TYPE.main>
</AutoColumn>
</ButtonRadioChecked>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.MEDIUM}
onClick={() => handleFeePoolSelect(FeeAmount.MEDIUM)}
>
<AutoColumn gap="sm" justify="flex-start">
<ResponsiveText>0.3% {t('fee')}</ResponsiveText>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
Best for most pairs.
</TYPE.main>
</AutoColumn>
</ButtonRadioChecked>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.HIGH}
onClick={() => handleFeePoolSelect(FeeAmount.HIGH)}
>
<AutoColumn gap="sm" justify="flex-start">
<ResponsiveText>1% {t('fee')}</ResponsiveText>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
Best for exotic pairs.
</TYPE.main>
</AutoColumn>
</ButtonRadioChecked>
</RowBetween>
</DynamicSection>
</AutoColumn>
)
}

View File

@@ -1,13 +1,14 @@
import JSBI from 'jsbi'
import React from 'react'
import { CurrencyAmount, Fraction, JSBI } from '@uniswap/sdk'
import { Currency, CurrencyAmount, Fraction } from '@uniswap/sdk-core'
const CURRENCY_AMOUNT_MIN = new Fraction(JSBI.BigInt(1), JSBI.BigInt(1000000))
export default function FormattedCurrencyAmount({
currencyAmount,
significantDigits = 4
significantDigits = 4,
}: {
currencyAmount: CurrencyAmount
currencyAmount: CurrencyAmount<Currency>
significantDigits?: number
}) {
return (

View File

@@ -1,35 +1,37 @@
import React, { useState, useEffect } from 'react'
import React, { useEffect, useState } from 'react'
import styled, { keyframes } from 'styled-components'
import { TYPE, ExternalLink } from '../../theme'
import { useActiveWeb3React } from '../../hooks/web3'
import { useBlockNumber } from '../../state/application/hooks'
import { getEtherscanLink } from '../../utils'
import { useActiveWeb3React } from '../../hooks'
import { ExternalLink, TYPE } from '../../theme'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
const StyledPolling = styled.div`
position: fixed;
display: flex;
align-items: center;
right: 0;
bottom: 0;
padding: 1rem;
color: white;
transition: opacity 0.25s ease;
color: ${({ theme }) => theme.green1};
:hover {
opacity: 1;
}
${({ theme }) => theme.mediaWidth.upToMedium`
display: none;
`}
`
const StyledPollingNumber = styled(TYPE.small)<{ breathe: boolean; hovering: boolean }>`
transition: opacity 0.25s ease;
opacity: ${({ breathe, hovering }) => (hovering ? 0.7 : breathe ? 1 : 0.2)};
:hover {
opacity: 1;
}
`
const StyledPollingDot = styled.div`
width: 8px;
height: 8px;
min-height: 8px;
min-width: 8px;
margin-left: 0.5rem;
margin-top: 3px;
border-radius: 50%;
position: relative;
background-color: ${({ theme }) => theme.green1};
@@ -67,16 +69,21 @@ export default function Polling() {
const blockNumber = useBlockNumber()
const [isMounted, setIsMounted] = useState(true)
const [isMounting, setIsMounting] = useState(false)
const [isHover, setIsHover] = useState(false)
useEffect(
() => {
const timer1 = setTimeout(() => setIsMounted(true), 1000)
if (!blockNumber) {
return
}
setIsMounting(true)
const mountingTimer = setTimeout(() => setIsMounting(false), 1000)
// this will clear Timeout when component unmount like in willComponentUnmount
return () => {
setIsMounted(false)
clearTimeout(timer1)
clearTimeout(mountingTimer)
}
},
[blockNumber] //useEffect will run only one time
@@ -84,10 +91,14 @@ export default function Polling() {
)
return (
<ExternalLink href={chainId && blockNumber ? getEtherscanLink(chainId, blockNumber.toString(), 'block') : ''}>
<StyledPolling>
<TYPE.small style={{ opacity: isMounted ? '0.2' : '0.6' }}>{blockNumber}</TYPE.small>
<StyledPollingDot>{!isMounted && <Spinner />}</StyledPollingDot>
<ExternalLink
href={chainId && blockNumber ? getExplorerLink(chainId, blockNumber.toString(), ExplorerDataType.BLOCK) : ''}
>
<StyledPolling onMouseEnter={() => setIsHover(true)} onMouseLeave={() => setIsHover(false)}>
<StyledPollingNumber breathe={isMounting} hovering={isHover}>
{blockNumber}
</StyledPollingNumber>
<StyledPollingDot>{isMounting && <Spinner />}</StyledPollingDot>
</StyledPolling>
</ExternalLink>
)

View File

@@ -1,5 +1,5 @@
import React from 'react'
import styled from 'styled-components'
import styled from 'styled-components/macro'
import { AlertTriangle, X } from 'react-feather'
import { useURLWarningToggle, useURLWarningVisible } from '../../state/user/hooks'

View File

@@ -1,18 +1,18 @@
import { ChainId, TokenAmount } from '@uniswap/sdk'
import { ChainId, CurrencyAmount, Token } from '@uniswap/sdk-core'
import React, { useMemo } from 'react'
import { X } from 'react-feather'
import styled from 'styled-components'
import styled from 'styled-components/macro'
import tokenLogo from '../../assets/images/token-logo.png'
import { UNI } from '../../constants'
import { useTotalSupply } from '../../data/TotalSupply'
import { useActiveWeb3React } from '../../hooks'
import { UNI } from '../../constants/tokens'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useActiveWeb3React } from '../../hooks/web3'
import { useMerkleDistributorContract } from '../../hooks/useContract'
import useCurrentBlockTimestamp from '../../hooks/useCurrentBlockTimestamp'
import { useTotalUniEarned } from '../../state/stake/hooks'
import { useAggregateUniBalance, useTokenBalance } from '../../state/wallet/hooks'
import { ExternalLink, StyledInternalLink, TYPE, UniTokenAnimated } from '../../theme'
import { computeUniCirculation } from '../../utils/computeUniCirculation'
import useUSDCPrice from '../../utils/useUSDCPrice'
import useUSDCPrice from '../../hooks/useUSDCPrice'
import { AutoColumn } from '../Column'
import { RowBetween } from '../Row'
import { Break, CardBGImage, CardNoise, CardSection, DataCard } from '../earn/styled'
@@ -45,14 +45,14 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
const uni = chainId ? UNI[chainId] : undefined
const total = useAggregateUniBalance()
const uniBalance: TokenAmount | undefined = useTokenBalance(account ?? undefined, uni)
const uniToClaim: TokenAmount | undefined = useTotalUniEarned()
const uniBalance: CurrencyAmount<Token> | undefined = useTokenBalance(account ?? undefined, uni)
const uniToClaim: CurrencyAmount<Token> | undefined = useTotalUniEarned()
const totalSupply: TokenAmount | undefined = useTotalSupply(uni)
const totalSupply: CurrencyAmount<Token> | undefined = useTotalSupply(uni)
const uniPrice = useUSDCPrice(uni)
const blockTimestamp = useCurrentBlockTimestamp()
const unclaimedUni = useTokenBalance(useMerkleDistributorContract()?.address, uni)
const circulation: TokenAmount | undefined = useMemo(
const circulation: CurrencyAmount<Token> | undefined = useMemo(
() =>
blockTimestamp && uni && chainId === ChainId.MAINNET
? computeUniCirculation(uni, blockTimestamp, unclaimedUni)
@@ -117,7 +117,7 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
<TYPE.white color="white">{totalSupply?.toFixed(0, { groupSeparator: ',' })}</TYPE.white>
</RowBetween>
{uni && uni.chainId === ChainId.MAINNET ? (
<ExternalLink href={`https://uniswap.info/token/${uni.address}`}>View UNI Analytics</ExternalLink>
<ExternalLink href={`https://info.uniswap.org/token/${uni.address}`}>View UNI Analytics</ExternalLink>
) : null}
</AutoColumn>
</CardSection>

View File

@@ -1,23 +1,23 @@
import { ChainId, TokenAmount } from '@uniswap/sdk'
import { ChainId } from '@uniswap/sdk-core'
import useScrollPosition from '@react-hook/window-scroll'
import React, { useState } from 'react'
import { Text } from 'rebass'
import { NavLink } from 'react-router-dom'
import { darken } from 'polished'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import { Moon, Sun } from 'react-feather'
import styled from 'styled-components/macro'
import Logo from '../../assets/svg/logo.svg'
import LogoDark from '../../assets/svg/logo_white.svg'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { useDarkModeManager } from '../../state/user/hooks'
import { useETHBalances, useAggregateUniBalance } from '../../state/wallet/hooks'
import { useETHBalances } from '../../state/wallet/hooks'
import { CardNoise } from '../earn/styled'
import { CountUp } from 'use-count-up'
import { TYPE, ExternalLink } from '../../theme'
import { YellowCard } from '../Card'
import { Moon, Sun } from 'react-feather'
import Menu from '../Menu'
import Row, { RowFixed } from '../Row'
@@ -29,11 +29,10 @@ import { useUserHasSubmittedClaim } from '../../state/transactions/hooks'
import { Dots } from '../swap/styleds'
import Modal from '../Modal'
import UniBalanceContent from './UniBalanceContent'
import usePrevious from '../../hooks/usePrevious'
const HeaderFrame = styled.div`
const HeaderFrame = styled.div<{ showBackground: boolean }>`
display: grid;
grid-template-columns: 1fr 120px;
grid-template-columns: 120px 1fr 120px;
align-items: center;
justify-content: space-between;
align-items: center;
@@ -41,18 +40,25 @@ const HeaderFrame = styled.div`
width: 100%;
top: 0;
position: relative;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
padding: 1rem;
z-index: 2;
z-index: 21;
position: relative;
/* Background slide effect on scroll. */
background-image: ${({ theme }) => `linear-gradient(to bottom, transparent 50%, ${theme.bg0} 50% )}}`}
background-position: ${({ showBackground }) => (showBackground ? '0 -100%' : '0 0')};
background-size: 100% 200%;
box-shadow: 0px 0px 0px 1px ${({ theme, showBackground }) => (showBackground ? theme.bg2 : 'transparent;')};
transition: background-position .1s, box-shadow .1s;
${({ theme }) => theme.mediaWidth.upToMedium`
grid-template-columns: 1fr;
padding: 0 1rem;
width: calc(100%);
position: relative;
padding: 1rem;
grid-template-columns: 120px 1fr;
`};
${({ theme }) => theme.mediaWidth.upToExtraSmall`
padding: 0.5rem 1rem;
padding: 1rem;
`}
`
@@ -90,7 +96,7 @@ const HeaderElement = styled.div`
}
${({ theme }) => theme.mediaWidth.upToMedium`
flex-direction: row-reverse;
flex-direction: row-reverse;
align-items: center;
`};
`
@@ -107,18 +113,25 @@ const HeaderRow = styled(RowFixed)`
`
const HeaderLinks = styled(Row)`
justify-content: center;
justify-self: center;
background-color: ${({ theme }) => theme.bg0};
width: fit-content;
padding: 4px;
border-radius: 16px;
display: grid;
grid-auto-flow: column;
grid-gap: 10px;
overflow: auto;
${({ theme }) => theme.mediaWidth.upToMedium`
padding: 1rem 0 1rem 1rem;
justify-content: flex-end;
`};
justify-self: flex-end;
`};
`
const AccountElement = styled.div<{ active: boolean }>`
display: flex;
flex-direction: row;
align-items: center;
background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg3)};
background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg2)};
border-radius: 12px;
white-space: nowrap;
width: 100%;
@@ -201,7 +214,7 @@ const UniIcon = styled.div`
const activeClassName = 'ACTIVE'
const StyledNavLink = styled(NavLink).attrs({
activeClassName
activeClassName,
})`
${({ theme }) => theme.flexRowNoWrap}
align-items: left;
@@ -212,13 +225,14 @@ const StyledNavLink = styled(NavLink).attrs({
color: ${({ theme }) => theme.text2};
font-size: 1rem;
width: fit-content;
margin: 0 12px;
font-weight: 500;
padding: 8px 12px;
&.${activeClassName} {
border-radius: 12px;
font-weight: 600;
color: ${({ theme }) => theme.text1};
background-color: ${({ theme }) => theme.bg2};
}
:hover,
@@ -228,7 +242,7 @@ const StyledNavLink = styled(NavLink).attrs({
`
const StyledExternalLink = styled(ExternalLink).attrs({
activeClassName
activeClassName,
})<{ isActive?: boolean }>`
${({ theme }) => theme.flexRowNoWrap}
align-items: left;
@@ -267,7 +281,7 @@ export const StyledMenuButton = styled.button`
margin: 0;
padding: 0;
height: 35px;
background-color: ${({ theme }) => theme.bg3};
background-color: ${({ theme }) => theme.bg2};
margin-left: 8px;
padding: 0.15rem 0.5rem;
border-radius: 0.5rem;
@@ -291,7 +305,7 @@ const NETWORK_LABELS: { [chainId in ChainId]?: string } = {
[ChainId.RINKEBY]: 'Rinkeby',
[ChainId.ROPSTEN]: 'Ropsten',
[ChainId.GÖRLI]: 'Görli',
[ChainId.KOVAN]: 'Kovan'
[ChainId.KOVAN]: 'Kovan',
}
export default function Header() {
@@ -308,16 +322,13 @@ export default function Header() {
const { claimTxn } = useUserHasSubmittedClaim(account ?? undefined)
const aggregateBalance: TokenAmount | undefined = useAggregateUniBalance()
const [showUniBalanceModal, setShowUniBalanceModal] = useState(false)
const showClaimPopup = useShowClaimPopup()
const countUpValue = aggregateBalance?.toFixed(0) ?? '0'
const countUpValuePrevious = usePrevious(countUpValue) ?? '0'
const scrollY = useScrollPosition()
return (
<HeaderFrame>
<HeaderFrame showBackground={scrollY > 45}>
<ClaimModal />
<Modal isOpen={showUniBalanceModal} onDismiss={() => setShowUniBalanceModal(false)}>
<UniBalanceContent setShowUniBalanceModal={setShowUniBalanceModal} />
@@ -328,34 +339,31 @@ export default function Header() {
<img width={'24px'} src={darkMode ? LogoDark : Logo} alt="logo" />
</UniIcon>
</Title>
<HeaderLinks>
<StyledNavLink id={`swap-nav-link`} to={'/swap'}>
{t('swap')}
</StyledNavLink>
<StyledNavLink
id={`pool-nav-link`}
to={'/pool'}
isActive={(match, { pathname }) =>
Boolean(match) ||
pathname.startsWith('/add') ||
pathname.startsWith('/remove') ||
pathname.startsWith('/create') ||
pathname.startsWith('/find')
}
>
{t('pool')}
</StyledNavLink>
<StyledNavLink id={`stake-nav-link`} to={'/uni'}>
UNI
</StyledNavLink>
<StyledNavLink id={`stake-nav-link`} to={'/vote'}>
Vote
</StyledNavLink>
<StyledExternalLink id={`stake-nav-link`} href={'https://uniswap.info'}>
Charts <span style={{ fontSize: '11px' }}></span>
</StyledExternalLink>
</HeaderLinks>
</HeaderRow>
<HeaderLinks>
<StyledNavLink id={`swap-nav-link`} to={'/swap'}>
{t('swap')}
</StyledNavLink>
<StyledNavLink
id={`pool-nav-link`}
to={'/pool'}
isActive={(match, { pathname }) =>
Boolean(match) ||
pathname.startsWith('/add') ||
pathname.startsWith('/remove') ||
pathname.startsWith('/increase') ||
pathname.startsWith('/find')
}
>
{t('pool')}
</StyledNavLink>
<StyledNavLink id={`stake-nav-link`} to={'/vote'}>
Vote
</StyledNavLink>
<StyledExternalLink id={`stake-nav-link`} href={'https://info.uniswap.org'}>
Charts <span style={{ fontSize: '11px', textDecoration: 'none !important' }}></span>
</StyledExternalLink>
</HeaderLinks>
<HeaderControls>
<HeaderElement>
<HideSmall>
@@ -373,32 +381,6 @@ export default function Header() {
<CardNoise />
</UNIWrapper>
)}
{!availableClaim && aggregateBalance && (
<UNIWrapper onClick={() => setShowUniBalanceModal(true)}>
<UNIAmount active={!!account && !availableClaim} style={{ pointerEvents: 'auto' }}>
{account && (
<HideSmall>
<TYPE.white
style={{
paddingRight: '.4rem'
}}
>
<CountUp
key={countUpValue}
isCounting
start={parseFloat(countUpValuePrevious)}
end={parseFloat(countUpValue)}
thousandsSeparator={','}
duration={1}
/>
</TYPE.white>
</HideSmall>
)}
UNI
</UNIAmount>
<CardNoise />
</UNIWrapper>
)}
<AccountElement active={!!account} style={{ pointerEvents: 'auto' }}>
{account && userEthBalance ? (
<BalanceText style={{ flexShrink: 0 }} pl="0.75rem" pr="0.5rem" fontWeight={500}>

View File

@@ -0,0 +1,63 @@
import Tooltip from 'components/Tooltip'
import React, { useState } from 'react'
import styled from 'styled-components/macro'
const TextWrapper = styled.span<{ margin: boolean; link?: boolean; fontSize?: string; adjustSize?: boolean }>`
cursor: auto;
margin-left: ${({ margin }) => margin && '4px'};
color: ${({ theme, link }) => (link ? theme.blue1 : theme.text1)};
font-size: ${({ fontSize }) => fontSize ?? 'inherit'};
@media screen and (max-width: 600px) {
font-size: ${({ adjustSize }) => adjustSize && '12px'};
}
`
const HoverInlineText = ({
text,
maxCharacters = 20,
margin = false,
adjustSize = false,
fontSize,
link,
...rest
}: {
text: string
maxCharacters?: number
margin?: boolean
adjustSize?: boolean
fontSize?: string
link?: boolean
}) => {
const [showHover, setShowHover] = useState(false)
if (!text) {
return <span />
}
if (text.length > maxCharacters) {
return (
<Tooltip text={text} show={showHover}>
<TextWrapper
onMouseEnter={() => setShowHover(true)}
onMouseLeave={() => setShowHover(false)}
margin={margin}
adjustSize={adjustSize}
link={link}
fontSize={fontSize}
{...rest}
>
{' ' + text.slice(0, maxCharacters - 1) + '...'}
</TextWrapper>
</Tooltip>
)
}
return (
<TextWrapper margin={margin} adjustSize={adjustSize} link={link} fontSize={fontSize} {...rest}>
{text}
</TextWrapper>
)
}
export default HoverInlineText

View File

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

View File

@@ -0,0 +1,159 @@
import React, { useState, useCallback, useEffect } from 'react'
import { LightCard } from 'components/Card'
import { RowBetween } from 'components/Row'
import { Input as NumericalInput } from '../NumericalInput'
import styled, { keyframes } from 'styled-components'
import { TYPE } from 'theme'
import { AutoColumn } from 'components/Column'
import { ButtonPrimary } from 'components/Button'
import { FeeAmount } from '@uniswap/v3-sdk'
import { formattedFeeAmount } from 'utils'
const pulse = (color: string) => keyframes`
0% {
box-shadow: 0 0 0 0 ${color};
}
70% {
box-shadow: 0 0 0 2px ${color};
}
100% {
box-shadow: 0 0 0 0 ${color};
}
`
const SmallButton = styled(ButtonPrimary)`
/* background-color: ${({ theme }) => theme.bg2}; */
border-radius: 8px;
padding: 4px 6px;
width: 48%;
`
const FocusedOutlineCard = styled(LightCard)<{ active?: boolean; pulsing?: boolean }>`
border-color: ${({ active, theme }) => active && theme.blue1};
padding: 12px;
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.blue1)} 0.8s linear;
`
const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
/* background-color: ${({ theme }) => theme.bg0}; */
text-align: center;
margin-right: 12px;
width: 100%;
font-weight: 500;
`
const InputTitle = styled(TYPE.small)`
color: ${({ theme }) => theme.text2};
font-size: 12px;
font-weight: 500;
`
interface StepCounterProps {
value: string
onUserInput: (value: string) => void
decrement: () => string
increment: () => string
feeAmount?: FeeAmount
label?: string
width?: string
locked?: boolean // disable input
title: string
tokenA: string | undefined
tokenB: string | undefined
}
const StepCounter = ({
value,
decrement,
increment,
feeAmount,
width,
locked,
onUserInput,
title,
tokenA,
tokenB,
}: StepCounterProps) => {
// for focus state, styled components doesnt let you select input parent container
const [active, setActive] = useState(false)
// let user type value and only update parent value on blur
const [localValue, setLocalValue] = useState('')
const [useLocalValue, setUseLocalValue] = useState(false)
// animation if parent value updates local value
const [pulsing, setPulsing] = useState<boolean>(false)
// format fee amount
const feeAmountFormatted = feeAmount ? formattedFeeAmount(feeAmount * 2) : ''
const handleOnFocus = () => {
setUseLocalValue(true)
setActive(true)
}
const handleOnBlur = useCallback(() => {
setUseLocalValue(false)
setActive(false)
onUserInput(localValue) // trigger update on parent value
}, [localValue, onUserInput])
// for button clicks
const handleDecrement = useCallback(() => {
setUseLocalValue(false)
onUserInput(decrement())
}, [decrement, onUserInput])
const handleIncrement = useCallback(() => {
setUseLocalValue(false)
onUserInput(increment())
}, [increment, onUserInput])
useEffect(() => {
if (localValue !== value && !useLocalValue) {
setTimeout(() => {
setLocalValue(value) // reset local value to match parent
setPulsing(true) // trigger animation
setTimeout(function () {
setPulsing(false)
}, 1800)
}, 0)
}
}, [localValue, useLocalValue, value])
return (
<FocusedOutlineCard pulsing={pulsing} active={active} onFocus={handleOnFocus} onBlur={handleOnBlur} width={width}>
<AutoColumn gap="6px" style={{ marginBottom: '12px' }}>
<InputTitle fontSize={12} textAlign="center">
{title}
</InputTitle>
<StyledInput
className="rate-input-0"
value={localValue}
fontSize="20px"
disabled={locked}
onUserInput={(val) => {
setLocalValue(val)
}}
/>
<InputTitle fontSize={12} textAlign="center">
{tokenB + ' per ' + tokenA}
</InputTitle>
</AutoColumn>
{!locked ? (
<RowBetween>
<SmallButton onClick={handleDecrement}>
<TYPE.white fontSize="12px">-{feeAmountFormatted}%</TYPE.white>
</SmallButton>
<SmallButton onClick={handleIncrement}>
<TYPE.white fontSize="12px">+{feeAmountFormatted}%</TYPE.white>
</SmallButton>
</RowBetween>
) : null}
</FocusedOutlineCard>
)
}
export default StepCounter

View File

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

View File

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

View File

@@ -0,0 +1,169 @@
import React, { useRef, useState, useEffect, useCallback, Dispatch, SetStateAction, ReactNode } from 'react'
import { createChart, IChartApi } from 'lightweight-charts'
import { darken } from 'polished'
import { RowBetween } from 'components/Row'
import Card from '../Card'
import styled from 'styled-components/macro'
import useTheme from 'hooks/useTheme'
const Wrapper = styled(Card)`
width: 100%;
padding: 1rem;
display: flex;
background-color: ${({ theme }) => theme.bg0}
flex-direction: column;
> * {
font-size: 1rem;
}
`
const DEFAULT_HEIGHT = 300
export type LineChartProps = {
data: any[]
color?: string | undefined
height?: number | undefined
setValue?: Dispatch<SetStateAction<number | undefined>> // used for value on hover
topLeft?: ReactNode | undefined
topRight?: ReactNode | undefined
bottomLeft?: ReactNode | undefined
bottomRight?: ReactNode | undefined
} & React.HTMLAttributes<HTMLDivElement>
const LineChart = ({
data,
color = '#56B2A4',
setValue,
topLeft,
topRight,
bottomLeft,
bottomRight,
height = DEFAULT_HEIGHT,
...rest
}: LineChartProps) => {
const theme = useTheme()
const chartRef = useRef<HTMLDivElement>(null)
const [chartCreated, setChart] = useState<IChartApi | undefined>()
// for reseting value on hover exit
const currenValue = data[data.length - 1].value
const handleResize = useCallback(() => {
if (chartCreated && chartRef?.current?.parentElement) {
chartCreated.resize(chartRef.current.parentElement.clientWidth - 32, height)
chartCreated.timeScale().fitContent()
chartCreated.timeScale().scrollToPosition(0, false)
}
}, [chartCreated, chartRef, height])
// add event listener for resize
const isClient = typeof window === 'object'
useEffect(() => {
if (!isClient) {
return
}
window.addEventListener('resize', handleResize)
return () => window.removeEventListener('resize', handleResize)
}, [isClient, chartRef, handleResize]) // Empty array ensures that effect is only run on mount and unmount
const textColor = theme.text2
// if chart not instantiated in canvas, create it
useEffect(() => {
if (!chartCreated && data && !!chartRef?.current?.parentElement) {
const chart = createChart(chartRef.current, {
height: height,
width: chartRef.current.parentElement.clientWidth - 32,
layout: {
backgroundColor: 'transparent',
textColor: textColor,
fontFamily: 'Inter',
},
rightPriceScale: {
scaleMargins: {
top: 0.1,
bottom: 0.1,
},
borderVisible: false,
},
timeScale: {
borderVisible: false,
},
watermark: {
color: 'rgba(0, 0, 0, 0)',
},
grid: {
horzLines: {
visible: false,
},
vertLines: {
visible: false,
},
},
crosshair: {
horzLine: {
visible: true,
style: 3,
width: 1,
color: '#505050',
labelBackgroundColor: color,
},
vertLine: {
visible: true,
style: 3,
width: 1,
color: '#505050',
labelBackgroundColor: color,
},
},
})
const series = chart.addAreaSeries({
lineColor: color,
topColor: darken(0.4, color),
bottomColor: theme.bg0,
lineWidth: 2,
priceLineVisible: false,
})
series.setData(data)
// update the title when hovering on the chart
chart.subscribeCrosshairMove(function (param) {
if (
chartRef?.current &&
(param === undefined ||
param.time === undefined ||
(param && param.point && param.point.x < 0) ||
(param && param.point && param.point.x > chartRef.current.clientWidth) ||
(param && param.point && param.point.y < 0) ||
(param && param.point && param.point.y > height))
) {
setValue && setValue(currenValue)
} else {
const price = parseFloat(param.seriesPrices.get(series)?.toString() ?? currenValue)
setValue && setValue(price)
}
})
chart.timeScale().fitContent()
setChart(chart)
}
}, [color, chartCreated, currenValue, data, height, setValue, textColor, theme])
return (
<Wrapper>
<RowBetween>
{topLeft ?? null}
{topRight ?? null}
</RowBetween>
<div ref={chartRef} id={'line-chart'} {...rest} />
<RowBetween>
{bottomLeft ?? null}
{bottomRight ?? null}
</RowBetween>
</Wrapper>
)
}
export default LineChart

View File

@@ -1,5 +1,5 @@
import React from 'react'
import styled from 'styled-components'
import styled from 'styled-components/macro'
import useHttpLocations from '../../hooks/useHttpLocations'
import Logo from '../Logo'
@@ -13,7 +13,7 @@ export default function ListLogo({
logoURI,
style,
size = '24px',
alt
alt,
}: {
logoURI: string
size?: string

View File

@@ -1,6 +1,7 @@
import React, { useState } from 'react'
import { HelpCircle } from 'react-feather'
import { Slash } from 'react-feather'
import { ImageProps } from 'rebass'
import useTheme from '../../hooks/useTheme'
const BAD_SRCS: { [tokenAddress: string]: true } = {}
@@ -11,10 +12,12 @@ export interface LogoProps extends Pick<ImageProps, 'style' | 'alt' | 'className
/**
* Renders an image by sequentially trying a list of URIs, and then eventually a fallback triangle alert
*/
export default function Logo({ srcs, alt, ...rest }: LogoProps) {
export default function Logo({ srcs, alt, style, ...rest }: LogoProps) {
const [, refresh] = useState<number>(0)
const src: string | undefined = srcs.find(src => !BAD_SRCS[src])
const theme = useTheme()
const src: string | undefined = srcs.find((src) => !BAD_SRCS[src])
if (src) {
return (
@@ -22,13 +25,14 @@ export default function Logo({ srcs, alt, ...rest }: LogoProps) {
{...rest}
alt={alt}
src={src}
style={style}
onError={() => {
if (src) BAD_SRCS[src] = true
refresh(i => i + 1)
refresh((i) => i + 1)
}}
/>
)
}
return <HelpCircle {...rest} />
return <Slash {...rest} style={{ ...style, color: theme.bg4 }} />
}

View File

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

View File

@@ -1,8 +1,9 @@
import React, { useRef } from 'react'
import { BookOpen, Code, Info, MessageCircle, PieChart } from 'react-feather'
import styled from 'styled-components'
import { Link } from 'react-router-dom'
import styled, { css } from 'styled-components'
import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
import { ApplicationModal } from '../../state/application/actions'
import { useModalOpen, useToggleModal } from '../../state/application/hooks'
@@ -10,6 +11,11 @@ import { useModalOpen, useToggleModal } from '../../state/application/hooks'
import { ExternalLink } from '../../theme'
import { ButtonPrimary } from '../Button'
export enum FlyoutAlignment {
LEFT = 'LEFT',
RIGHT = 'RIGHT',
}
const StyledMenuIcon = styled(MenuIcon)`
path {
stroke: ${({ theme }) => theme.text1};
@@ -24,7 +30,7 @@ const StyledMenuButton = styled.button`
margin: 0;
padding: 0;
height: 35px;
background-color: ${({ theme }) => theme.bg3};
background-color: ${({ theme }) => theme.bg2};
padding: 0.15rem 0.5rem;
border-radius: 0.5rem;
@@ -33,7 +39,7 @@ const StyledMenuButton = styled.button`
:focus {
cursor: pointer;
outline: none;
background-color: ${({ theme }) => theme.bg4};
background-color: ${({ theme }) => theme.bg3};
}
svg {
@@ -41,6 +47,12 @@ const StyledMenuButton = styled.button`
}
`
const UNIbutton = styled(ButtonPrimary)`
background-color: ${({ theme }) => theme.bg3};
background: radial-gradient(174.47% 188.91% at 1.84% 0%, #ff007a 0%, #2172e5 100%), #edeef2;
border: none;
`
const StyledMenu = styled.div`
margin-left: 0.5rem;
display: flex;
@@ -51,9 +63,9 @@ const StyledMenu = styled.div`
text-align: left;
`
const MenuFlyout = styled.span`
const MenuFlyout = styled.span<{ flyoutAlignment?: FlyoutAlignment }>`
min-width: 8.125rem;
background-color: ${({ theme }) => theme.bg3};
background-color: ${({ theme }) => theme.bg2};
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
0px 24px 32px rgba(0, 0, 0, 0.01);
border-radius: 12px;
@@ -62,16 +74,39 @@ const MenuFlyout = styled.span`
flex-direction: column;
font-size: 1rem;
position: absolute;
top: 4rem;
right: 0rem;
top: 3rem;
z-index: 100;
${({ flyoutAlignment = FlyoutAlignment.RIGHT }) =>
flyoutAlignment === FlyoutAlignment.RIGHT
? css`
right: 0rem;
`
: css`
left: 0rem;
`};
${({ theme }) => theme.mediaWidth.upToMedium`
top: -17.25rem;
`};
`
const MenuItem = styled(ExternalLink)`
display: flex;
flex: 1;
flex-direction: row;
align-items: center;
padding: 0.5rem 0.5rem;
color: ${({ theme }) => theme.text2};
:hover {
color: ${({ theme }) => theme.text1};
cursor: pointer;
text-decoration: none;
}
> svg {
margin-right: 8px;
}
`
const InternalMenuItem = styled(Link)`
flex: 1;
padding: 0.5rem 0.5rem;
color: ${({ theme }) => theme.text2};
@@ -105,33 +140,84 @@ export default function Menu() {
{open && (
<MenuFlyout>
<MenuItem id="link" href="https://uniswap.org/">
<MenuItem href="https://uniswap.org/">
<Info size={14} />
About
<div>About</div>
</MenuItem>
<MenuItem id="link" href="https://uniswap.org/docs/v2">
<MenuItem href="https://docs.uniswap.org/">
<BookOpen size={14} />
Docs
<div>Docs</div>
</MenuItem>
<MenuItem id="link" href={CODE_LINK}>
<MenuItem href={CODE_LINK}>
<Code size={14} />
Code
<div>Code</div>
</MenuItem>
<MenuItem id="link" href="https://discord.gg/FCfyBSbCU5">
<MenuItem href="https://discord.gg/FCfyBSbCU5">
<MessageCircle size={14} />
Discord
<div>Discord</div>
</MenuItem>
<MenuItem id="link" href="https://uniswap.info/">
<MenuItem href="https://info.uniswap.org/">
<PieChart size={14} />
Analytics
<div>Analytics</div>
</MenuItem>
{account && (
<ButtonPrimary onClick={openClaimModal} padding="8px 16px" width="100%" borderRadius="12px" mt="0.5rem">
<UNIbutton onClick={openClaimModal} padding="8px 16px" width="100%" borderRadius="12px" mt="0.5rem">
Claim UNI
</ButtonPrimary>
</UNIbutton>
)}
</MenuFlyout>
)}
</StyledMenu>
)
}
interface NewMenuProps {
flyoutAlignment?: FlyoutAlignment
ToggleUI?: React.FunctionComponent
menuItems: {
content: any
link: string
external: boolean
}[]
}
const NewMenuFlyout = styled(MenuFlyout)`
top: 3rem !important;
`
const NewMenuItem = styled(InternalMenuItem)`
width: max-content;
text-decoration: none;
`
const ExternalMenuItem = styled(MenuItem)`
width: max-content;
text-decoration: none;
`
export const NewMenu = ({ flyoutAlignment = FlyoutAlignment.RIGHT, ToggleUI, menuItems, ...rest }: NewMenuProps) => {
const node = useRef<HTMLDivElement>()
const open = useModalOpen(ApplicationModal.POOL_OVERVIEW_OPTIONS)
const toggle = useToggleModal(ApplicationModal.POOL_OVERVIEW_OPTIONS)
useOnClickOutside(node, open ? toggle : undefined)
const ToggleElement = ToggleUI || StyledMenuIcon
return (
<StyledMenu ref={node as any} {...rest}>
<ToggleElement onClick={toggle} />
{open && (
<NewMenuFlyout flyoutAlignment={flyoutAlignment}>
{menuItems.map(({ content, link, external }, i) =>
external ? (
<ExternalMenuItem id="link" href={link} key={link + i}>
{content}
</ExternalMenuItem>
) : (
<NewMenuItem id="link" to={link} key={link + i}>
{content}
</NewMenuItem>
)
)}
</NewMenuFlyout>
)}
</StyledMenu>
)
}

View File

@@ -29,13 +29,14 @@ const AnimatedDialogContent = animated(DialogContent)
const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...rest }) => (
<AnimatedDialogContent {...rest} />
)).attrs({
'aria-label': 'dialog'
'aria-label': 'dialog',
})`
overflow-y: ${({ mobile }) => (mobile ? 'scroll' : 'hidden')};
&[data-reach-dialog-content] {
margin: 0 0 2rem 0;
background-color: ${({ theme }) => theme.bg1};
background-color: ${({ theme }) => theme.bg0};
border: 1px solid ${({ theme }) => theme.bg1};
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.95, theme.shadow1)};
padding: 0px;
width: 50vw;
@@ -63,13 +64,15 @@ const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, ...r
`}
${({ theme, mobile }) => theme.mediaWidth.upToSmall`
width: 85vw;
${mobile &&
${
mobile &&
css`
width: 100vw;
border-radius: 20px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
`}
`
}
`}
}
`
@@ -89,25 +92,25 @@ export default function Modal({
minHeight = false,
maxHeight = 90,
initialFocusRef,
children
children,
}: ModalProps) {
const fadeTransition = useTransition(isOpen, null, {
config: { duration: 200 },
from: { opacity: 0 },
enter: { opacity: 1 },
leave: { opacity: 0 }
leave: { opacity: 0 },
})
const [{ y }, set] = useSpring(() => ({ y: 0, config: { mass: 1, tension: 210, friction: 20 } }))
const bind = useGesture({
onDrag: state => {
onDrag: (state) => {
set({
y: state.down ? state.movement[1] : 0
y: state.down ? state.movement[1] : 0,
})
if (state.movement[1] > 300 || (state.velocity > 3 && state.direction[1] > 0)) {
onDismiss()
}
}
},
})
return (
@@ -126,7 +129,7 @@ export default function Modal({
{...(isMobile
? {
...bind(),
style: { transform: y.interpolate(y => `translateY(${y > 0 ? y : 0}px)`) }
style: { transform: y.interpolate((y) => `translateY(${(y as number) > 0 ? y : 0}px)`) },
}
: {})}
aria-label="dialog content"

View File

@@ -1,5 +1,6 @@
import React, { useContext } from 'react'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { AutoColumn, ColumnCenter } from '../Column'
import styled, { ThemeContext } from 'styled-components'
@@ -8,7 +9,6 @@ import { TYPE, CloseIcon, CustomLightSpinner } from '../../theme'
import { ArrowUpCircle } from 'react-feather'
import Circle from '../../assets/images/blue-loader.svg'
import { getEtherscanLink } from '../../utils'
import { ExternalLink } from '../../theme/components'
const ConfirmOrLoadingWrapper = styled.div`
@@ -41,7 +41,7 @@ export function LoadingView({ children, onDismiss }: { children: any; onDismiss:
export function SubmittedView({
children,
onDismiss,
hash
hash,
}: {
children: any
onDismiss: () => void
@@ -62,7 +62,10 @@ export function SubmittedView({
<AutoColumn gap="100px" justify={'center'}>
{children}
{chainId && hash && (
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')} style={{ marginLeft: '4px' }}>
<ExternalLink
href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}
style={{ marginLeft: '4px' }}
>
<TYPE.subHeader>View transaction on Etherscan</TYPE.subHeader>
</ExternalLink>
)}

View File

@@ -1,16 +1,19 @@
import React from 'react'
import styled from 'styled-components'
import styled from 'styled-components/macro'
import { darken } from 'polished'
import { useTranslation } from 'react-i18next'
import { NavLink, Link as HistoryLink } from 'react-router-dom'
import { Percent } from '@uniswap/sdk-core'
import { ArrowLeft } from 'react-feather'
import { RowBetween } from '../Row'
// import QuestionHelper from '../QuestionHelper'
import Settings from '../Settings'
import SettingsTab from '../Settings'
import { useDispatch } from 'react-redux'
import { AppDispatch } from 'state'
import { resetMintState } from 'state/mint/actions'
import { resetMintState as resetMintV3State } from 'state/mint/v3/actions'
import { TYPE } from 'theme'
import useTheme from 'hooks/useTheme'
const Tabs = styled.div`
${({ theme }) => theme.flexRowNoWrap}
@@ -22,7 +25,7 @@ const Tabs = styled.div`
const activeClassName = 'ACTIVE'
const StyledNavLink = styled(NavLink).attrs({
activeClassName
activeClassName,
})`
${({ theme }) => theme.flexRowNoWrap}
align-items: center;
@@ -59,7 +62,7 @@ const StyledArrowLeft = styled(ArrowLeft)`
export function SwapPoolTabs({ active }: { active: 'swap' | 'pool' }) {
const { t } = useTranslation()
return (
<Tabs style={{ marginBottom: '20px', display: 'none' }}>
<Tabs style={{ marginBottom: '20px', display: 'none', padding: '1rem 1rem 0 1rem' }}>
<StyledNavLink id={`swap-nav-link`} to={'/swap'} isActive={() => active === 'swap'}>
{t('swap')}
</StyledNavLink>
@@ -70,21 +73,32 @@ export function SwapPoolTabs({ active }: { active: 'swap' | 'pool' }) {
)
}
export function FindPoolTabs() {
export function FindPoolTabs({ origin }: { origin: string }) {
return (
<Tabs>
<RowBetween style={{ padding: '1rem 1rem 0 1rem' }}>
<HistoryLink to="/pool">
<HistoryLink to={origin}>
<StyledArrowLeft />
</HistoryLink>
<ActiveText>Import Pool</ActiveText>
<Settings />
</RowBetween>
</Tabs>
)
}
export function AddRemoveTabs({ adding, creating }: { adding: boolean; creating: boolean }) {
export function AddRemoveTabs({
adding,
creating,
positionID,
defaultSlippage,
}: {
adding: boolean
creating: boolean
positionID?: string | undefined
defaultSlippage: Percent
}) {
const theme = useTheme()
// reset states on back
const dispatch = useDispatch<AppDispatch>()
@@ -92,15 +106,21 @@ export function AddRemoveTabs({ adding, creating }: { adding: boolean; creating:
<Tabs>
<RowBetween style={{ padding: '1rem 1rem 0 1rem' }}>
<HistoryLink
to="/pool"
to={'/pool' + (!!positionID ? `/${positionID.toString()}` : '')}
onClick={() => {
adding && dispatch(resetMintState())
if (adding) {
// not 100% sure both of these are needed
dispatch(resetMintState())
dispatch(resetMintV3State())
}
}}
>
<StyledArrowLeft />
<StyledArrowLeft stroke={theme.text2} />
</HistoryLink>
<ActiveText>{creating ? 'Create a pair' : adding ? 'Add Liquidity' : 'Remove Liquidity'}</ActiveText>
<Settings />
<TYPE.mediumHeader fontWeight={500} fontSize={20}>
{creating ? 'Create a pair' : adding ? 'Add Liquidity' : 'Remove Liquidity'}
</TYPE.mediumHeader>
<SettingsTab placeholderSlippage={defaultSlippage} />
</RowBetween>
</Tabs>
)

View File

@@ -1,5 +1,5 @@
import React from 'react'
import styled from 'styled-components'
import styled from 'styled-components/macro'
import { escapeRegExp } from '../../utils'
const StyledInput = styled.input<{ error?: boolean; fontSize?: string; align?: string }>`
@@ -18,6 +18,7 @@ const StyledInput = styled.input<{ error?: boolean; fontSize?: string; align?: s
text-overflow: ellipsis;
padding: 0px;
-webkit-appearance: textfield;
text-align: right;
::-webkit-search-decoration {
-webkit-appearance: none;
@@ -43,6 +44,7 @@ export const Input = React.memo(function InnerInput({
value,
onUserInput,
placeholder,
prependSymbol,
...rest
}: {
value: string | number
@@ -50,6 +52,7 @@ export const Input = React.memo(function InnerInput({
error?: boolean
fontSize?: string
align?: 'right' | 'left'
prependSymbol?: string | undefined
} & Omit<React.HTMLProps<HTMLInputElement>, 'ref' | 'onChange' | 'as'>) {
const enforcer = (nextUserInput: string) => {
if (nextUserInput === '' || inputRegex.test(escapeRegExp(nextUserInput))) {
@@ -60,14 +63,24 @@ export const Input = React.memo(function InnerInput({
return (
<StyledInput
{...rest}
value={value}
onChange={event => {
// replace commas with periods, because uniswap exclusively uses period as the decimal separator
enforcer(event.target.value.replace(/,/g, '.'))
value={prependSymbol && value ? prependSymbol + value : value}
onChange={(event) => {
if (prependSymbol) {
const value = event.target.value
// cut off prepended symbol
const formattedValue = value.toString().includes(prependSymbol)
? value.toString().slice(1, value.toString().length + 1)
: value
// replace commas with periods, because uniswap exclusively uses period as the decimal separator
enforcer(formattedValue.replace(/,/g, '.'))
} else {
enforcer(event.target.value.replace(/,/g, '.'))
}
}}
// universal input options
inputMode="decimal"
title="Token Amount"
autoComplete="off"
autoCorrect="off"
// text-specific options

View File

@@ -2,17 +2,15 @@ import { Placement } from '@popperjs/core'
import { transparentize } from 'polished'
import React, { useCallback, useState } from 'react'
import { usePopper } from 'react-popper'
import styled from 'styled-components'
import styled from 'styled-components/macro'
import useInterval from '../../hooks/useInterval'
import Portal from '@reach/portal'
const PopoverContainer = styled.div<{ show: boolean }>`
z-index: 9999;
visibility: ${props => (props.show ? 'visible' : 'hidden')};
opacity: ${props => (props.show ? 1 : 0)};
visibility: ${(props) => (props.show ? 'visible' : 'hidden')};
opacity: ${(props) => (props.show ? 1 : 0)};
transition: visibility 150ms linear, opacity 150ms linear;
background: ${({ theme }) => theme.bg2};
border: 1px solid ${({ theme }) => theme.bg3};
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.9, theme.shadow1)};
@@ -91,8 +89,8 @@ export default function Popover({ content, show, children, placement = 'auto' }:
strategy: 'fixed',
modifiers: [
{ name: 'offset', options: { offset: [8, 8] } },
{ name: 'arrow', options: { element: arrowElement } }
]
{ name: 'arrow', options: { element: arrowElement } },
],
})
const updateCallback = useCallback(() => {
update && update()

View File

@@ -1,16 +1,16 @@
import { TokenAmount } from '@uniswap/sdk'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import React, { useEffect } from 'react'
import { X } from 'react-feather'
import styled, { keyframes } from 'styled-components'
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,
useShowClaimPopup,
useToggleSelfClaimModal,
useToggleShowClaimPopup
useToggleShowClaimPopup,
} from '../../state/application/hooks'
import { useUserHasAvailableClaim, useUserUnclaimedAmount } from '../../state/claim/hooks'
@@ -65,7 +65,7 @@ export default function ClaimPopup() {
// const userHasAvailableclaim = useUserHasAvailableClaim()
const userHasAvailableclaim: boolean = useUserHasAvailableClaim(account)
const unclaimedAmount: TokenAmount | undefined = useUserUnclaimedAmount(account)
const unclaimedAmount: CurrencyAmount<Token> | undefined = useUserUnclaimedAmount(account)
// listen for available claim and show popup if needed
useEffect(() => {

View File

@@ -3,7 +3,7 @@ import React, { useCallback, useMemo } from 'react'
import ReactGA from 'react-ga'
import { useDispatch } from 'react-redux'
import { Text } from 'rebass'
import styled from 'styled-components'
import styled from 'styled-components/macro'
import { AppDispatch } from '../../state'
import { useRemovePopup } from '../../state/application/hooks'
import { acceptListUpdate } from '../../state/lists/actions'
@@ -23,7 +23,7 @@ export default function ListUpdatePopup({
listUrl,
oldList,
newList,
auto
auto,
}: {
popKey: string
listUrl: string
@@ -40,7 +40,7 @@ export default function ListUpdatePopup({
ReactGA.event({
category: 'Lists',
action: 'Update List from Popup',
label: listUrl
label: listUrl,
})
dispatch(acceptListUpdate(listUrl))
removeThisPopup()

View File

@@ -21,7 +21,7 @@ export const Popup = styled.div`
display: inline-block;
width: 100%;
padding: 1em;
background-color: ${({ theme }) => theme.bg1};
background-color: ${({ theme }) => theme.bg0};
position: relative;
border-radius: 10px;
padding: 20px;
@@ -49,7 +49,7 @@ const AnimatedFader = animated(Fader)
export default function PopupItem({
removeAfterMs,
content,
popKey
popKey,
}: {
removeAfterMs: number | null
content: PopupContent
@@ -74,12 +74,12 @@ export default function PopupItem({
let popupContent
if ('txn' in content) {
const {
txn: { hash, success, summary }
txn: { hash, success, summary },
} = content
popupContent = <TransactionPopup hash={hash} success={success} summary={summary} />
} else if ('listUpdate' in content) {
const {
listUpdate: { listUrl, oldList, newList, auto }
listUpdate: { listUrl, oldList, newList, auto },
} = content
popupContent = <ListUpdatePopup popKey={popKey} listUrl={listUrl} oldList={oldList} newList={newList} auto={auto} />
}
@@ -87,7 +87,7 @@ export default function PopupItem({
const faderStyle = useSpring({
from: { width: '100%' },
to: { width: '0%' },
config: { duration: removeAfterMs ?? undefined }
config: { duration: removeAfterMs ?? undefined },
})
return (

View File

@@ -1,10 +1,10 @@
import React, { useContext } from 'react'
import { AlertCircle, CheckCircle } from 'react-feather'
import styled, { ThemeContext } from 'styled-components'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { TYPE } from '../../theme'
import { ExternalLink } from '../../theme/components'
import { getEtherscanLink } from '../../utils'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { AutoColumn } from '../Column'
import { AutoRow } from '../Row'
@@ -15,7 +15,7 @@ const RowNoFlex = styled(AutoRow)`
export default function TransactionPopup({
hash,
success,
summary
summary,
}: {
hash: string
success?: boolean
@@ -33,7 +33,9 @@ export default function TransactionPopup({
<AutoColumn gap="8px">
<TYPE.body fontWeight={500}>{summary ?? 'Hash: ' + hash.slice(0, 8) + '...' + hash.slice(58, 65)}</TYPE.body>
{chainId && (
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')}>View on Etherscan</ExternalLink>
<ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}>
View on Etherscan
</ExternalLink>
)}
</AutoColumn>
</RowNoFlex>

View File

@@ -1,5 +1,5 @@
import React from 'react'
import styled from 'styled-components'
import styled from 'styled-components/macro'
import { useActivePopups } from '../../state/application/hooks'
import { AutoColumn } from '../Column'
import PopupItem from './PopupItem'
@@ -33,7 +33,7 @@ const MobilePopupInner = styled.div`
const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean }>`
position: fixed;
top: ${({ extraPadding }) => (extraPadding ? '108px' : '88px')};
top: ${({ extraPadding }) => (extraPadding ? '72px' : '88px')};
right: 1rem;
max-width: 355px !important;
width: 100%;
@@ -54,7 +54,7 @@ export default function Popups() {
<>
<FixedPopupColumn gap="20px" extraPadding={urlWarningActive}>
<ClaimPopup />
{activePopups.map(item => (
{activePopups.map((item) => (
<PopupItem key={item.key} content={item.content} popKey={item.key} removeAfterMs={item.removeAfterMs} />
))}
</FixedPopupColumn>
@@ -63,7 +63,7 @@ export default function Popups() {
{activePopups // reverse so new items up front
.slice(0)
.reverse()
.map(item => (
.map((item) => (
<PopupItem key={item.key} content={item.content} popKey={item.key} removeAfterMs={item.removeAfterMs} />
))}
</MobilePopupInner>

View File

@@ -0,0 +1,71 @@
import React from 'react'
import { Token } from '@uniswap/sdk-core'
import { Link } from 'react-router-dom'
import { Text } from 'rebass'
import styled from 'styled-components/macro'
import { unwrappedToken } from '../../utils/wrappedCurrency'
import { ButtonEmpty } from '../Button'
import { transparentize } from 'polished'
import { CardNoise } from '../earn/styled'
import { useColor } from '../../hooks/useColor'
import { LightCard } from '../Card'
import { AutoColumn } from '../Column'
import DoubleCurrencyLogo from '../DoubleLogo'
import { RowFixed, AutoRow } from '../Row'
import { Dots } from '../swap/styleds'
import { FixedHeightRow } from '.'
import Badge, { BadgeVariant } from 'components/Badge'
const StyledPositionCard = styled(LightCard)<{ bgColor: any }>`
border: none;
background: ${({ theme, bgColor }) =>
`radial-gradient(91.85% 100% at 1.84% 0%, ${transparentize(0.8, bgColor)} 0%, ${theme.bg3} 100%) `};
position: relative;
overflow: hidden;
`
interface PositionCardProps {
tokenA: Token
tokenB: Token
liquidityToken: Token
border?: string
}
export default function SushiPositionCard({ tokenA, tokenB, liquidityToken, border }: PositionCardProps) {
const currency0 = unwrappedToken(tokenA)
const currency1 = unwrappedToken(tokenB)
const backgroundColor = useColor(tokenA)
return (
<StyledPositionCard border={border} bgColor={backgroundColor}>
<CardNoise />
<AutoColumn gap="12px">
<FixedHeightRow>
<AutoRow gap="8px">
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} size={20} />
<Text fontWeight={500} fontSize={20}>
{!currency0 || !currency1 ? <Dots>Loading</Dots> : `${currency0.symbol}/${currency1.symbol}`}
</Text>
<Badge variant={BadgeVariant.WARNING}>Sushi</Badge>
</AutoRow>
<RowFixed gap="8px">
<ButtonEmpty
padding="0px 35px 0px 0px"
borderRadius="12px"
width="fit-content"
as={Link}
to={`/migrate/v2/${liquidityToken.address}`}
>
Migrate
</ButtonEmpty>
</RowFixed>
</FixedHeightRow>
</AutoColumn>
</StyledPositionCard>
)
}

View File

@@ -1,69 +0,0 @@
import React, { useContext } from 'react'
import { Link, RouteComponentProps, withRouter } from 'react-router-dom'
import { Token, TokenAmount, WETH } from '@uniswap/sdk'
import { Text } from 'rebass'
import { AutoColumn } from '../Column'
import { ButtonSecondary } from '../Button'
import { RowBetween, RowFixed } from '../Row'
import { FixedHeightRow, HoverCard } from './index'
import DoubleCurrencyLogo from '../DoubleLogo'
import { useActiveWeb3React } from '../../hooks'
import { ThemeContext } from 'styled-components'
interface PositionCardProps extends RouteComponentProps<{}> {
token: Token
V1LiquidityBalance: TokenAmount
}
function V1PositionCard({ token, V1LiquidityBalance }: PositionCardProps) {
const theme = useContext(ThemeContext)
const { chainId } = useActiveWeb3React()
return (
<HoverCard>
<AutoColumn gap="12px">
<FixedHeightRow>
<RowFixed>
<DoubleCurrencyLogo currency0={token} margin={true} size={20} />
<Text fontWeight={500} fontSize={20} style={{ marginLeft: '' }}>
{`${chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol}/ETH`}
</Text>
<Text
fontSize={12}
fontWeight={500}
ml="0.5rem"
px="0.75rem"
py="0.25rem"
style={{ borderRadius: '1rem' }}
backgroundColor={theme.yellow1}
color={'black'}
>
V1
</Text>
</RowFixed>
</FixedHeightRow>
<AutoColumn gap="8px">
<RowBetween marginTop="10px">
<ButtonSecondary width="68%" as={Link} to={`/migrate/v1/${V1LiquidityBalance.token.address}`}>
Migrate
</ButtonSecondary>
<ButtonSecondary
style={{ backgroundColor: 'transparent' }}
width="28%"
as={Link}
to={`/remove/v1/${V1LiquidityBalance.token.address}`}
>
Remove
</ButtonSecondary>
</RowBetween>
</AutoColumn>
</AutoColumn>
</HoverCard>
)
}
export default withRouter(V1PositionCard)

View File

@@ -0,0 +1,207 @@
import JSBI from 'jsbi'
import React, { useState } from 'react'
import { Percent, CurrencyAmount, Token } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import { ChevronDown, ChevronUp } from 'react-feather'
import { Link } from 'react-router-dom'
import { Text } from 'rebass'
import styled from 'styled-components/macro'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useActiveWeb3React } from '../../hooks/web3'
import { useTokenBalance } from '../../state/wallet/hooks'
import { currencyId } from '../../utils/currencyId'
import { unwrappedToken } from '../../utils/wrappedCurrency'
import { ButtonPrimary, ButtonSecondary, ButtonEmpty } from '../Button'
import { transparentize } from 'polished'
import { CardNoise } from '../earn/styled'
import { useColor } from '../../hooks/useColor'
import { LightCard } from '../Card'
import { AutoColumn } from '../Column'
import CurrencyLogo from '../CurrencyLogo'
import DoubleCurrencyLogo from '../DoubleLogo'
import { RowBetween, RowFixed, AutoRow } from '../Row'
import { Dots } from '../swap/styleds'
import { BIG_INT_ZERO } from '../../constants/misc'
import { FixedHeightRow } from '.'
const StyledPositionCard = styled(LightCard)<{ bgColor: any }>`
border: none;
background: ${({ theme, bgColor }) =>
`radial-gradient(91.85% 100% at 1.84% 0%, ${transparentize(0.8, bgColor)} 0%, ${theme.bg3} 100%) `};
position: relative;
overflow: hidden;
`
interface PositionCardProps {
pair: Pair
showUnwrapped?: boolean
border?: string
stakedBalance?: CurrencyAmount<Token> // optional balance to indicate that liquidity is deposited in mining pool
}
export default function V2PositionCard({ pair, border, stakedBalance }: PositionCardProps) {
const { account } = useActiveWeb3React()
const currency0 = unwrappedToken(pair.token0)
const currency1 = unwrappedToken(pair.token1)
const [showMore, setShowMore] = useState(false)
const userDefaultPoolBalance = useTokenBalance(account ?? undefined, pair.liquidityToken)
const totalPoolTokens = useTotalSupply(pair.liquidityToken)
// if staked balance balance provided, add to standard liquidity amount
const userPoolBalance = stakedBalance ? userDefaultPoolBalance?.add(stakedBalance) : userDefaultPoolBalance
const poolTokenPercentage =
!!userPoolBalance &&
!!totalPoolTokens &&
JSBI.greaterThanOrEqual(totalPoolTokens.quotient, userPoolBalance.quotient)
? new Percent(userPoolBalance.quotient, totalPoolTokens.quotient)
: undefined
const [token0Deposited, token1Deposited] =
!!pair &&
!!totalPoolTokens &&
!!userPoolBalance &&
// this condition is a short-circuit in the case where useTokenBalance updates sooner than useTotalSupply
JSBI.greaterThanOrEqual(totalPoolTokens.quotient, userPoolBalance.quotient)
? [
pair.getLiquidityValue(pair.token0, totalPoolTokens, userPoolBalance, false),
pair.getLiquidityValue(pair.token1, totalPoolTokens, userPoolBalance, false),
]
: [undefined, undefined]
const backgroundColor = useColor(pair?.token0)
return (
<StyledPositionCard border={border} bgColor={backgroundColor}>
<CardNoise />
<AutoColumn gap="12px">
<FixedHeightRow>
<AutoRow gap="8px">
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} size={20} />
<Text fontWeight={500} fontSize={20}>
{!currency0 || !currency1 ? <Dots>Loading</Dots> : `${currency0.symbol}/${currency1.symbol}`}
</Text>
</AutoRow>
<RowFixed gap="8px">
<ButtonEmpty
padding="6px 8px"
borderRadius="12px"
width="fit-content"
onClick={() => setShowMore(!showMore)}
>
{showMore ? (
<>
Manage
<ChevronUp size="20" style={{ marginLeft: '10px' }} />
</>
) : (
<>
Manage
<ChevronDown size="20" style={{ marginLeft: '10px' }} />
</>
)}
</ButtonEmpty>
</RowFixed>
</FixedHeightRow>
{showMore && (
<AutoColumn gap="8px">
<FixedHeightRow>
<Text fontSize={16} fontWeight={500}>
Your total pool tokens:
</Text>
<Text fontSize={16} fontWeight={500}>
{userPoolBalance ? userPoolBalance.toSignificant(4) : '-'}
</Text>
</FixedHeightRow>
{stakedBalance && (
<FixedHeightRow>
<Text fontSize={16} fontWeight={500}>
Pool tokens in rewards pool:
</Text>
<Text fontSize={16} fontWeight={500}>
{stakedBalance.toSignificant(4)}
</Text>
</FixedHeightRow>
)}
<FixedHeightRow>
<RowFixed>
<Text fontSize={16} fontWeight={500}>
Pooled {currency0.symbol}:
</Text>
</RowFixed>
{token0Deposited ? (
<RowFixed>
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
{token0Deposited?.toSignificant(6)}
</Text>
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={currency0} />
</RowFixed>
) : (
'-'
)}
</FixedHeightRow>
<FixedHeightRow>
<RowFixed>
<Text fontSize={16} fontWeight={500}>
Pooled {currency1.symbol}:
</Text>
</RowFixed>
{token1Deposited ? (
<RowFixed>
<Text fontSize={16} fontWeight={500} marginLeft={'6px'}>
{token1Deposited?.toSignificant(6)}
</Text>
<CurrencyLogo size="20px" style={{ marginLeft: '8px' }} currency={currency1} />
</RowFixed>
) : (
'-'
)}
</FixedHeightRow>
<FixedHeightRow>
<Text fontSize={16} fontWeight={500}>
Your pool share:
</Text>
<Text fontSize={16} fontWeight={500}>
{poolTokenPercentage
? (poolTokenPercentage.toFixed(2) === '0.00' ? '<0.01' : poolTokenPercentage.toFixed(2)) + '%'
: '-'}
</Text>
</FixedHeightRow>
{userDefaultPoolBalance && JSBI.greaterThan(userDefaultPoolBalance.quotient, BIG_INT_ZERO) && (
<RowBetween marginTop="10px">
<ButtonPrimary
padding="8px"
borderRadius="8px"
as={Link}
to={`/migrate/v2/${pair.liquidityToken.address}`}
width="64%"
>
Migrate
</ButtonPrimary>
<ButtonSecondary
padding="8px"
borderRadius="8px"
as={Link}
width="32%"
to={`/remove/v2/${currencyId(currency0)}/${currencyId(currency1)}`}
>
Remove
</ButtonSecondary>
</RowBetween>
)}
</AutoColumn>
)}
</AutoColumn>
</StyledPositionCard>
)
}

View File

@@ -1,13 +1,15 @@
import { JSBI, Pair, Percent, TokenAmount } from '@uniswap/sdk'
import JSBI from 'jsbi'
import { Percent, CurrencyAmount, Token } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import { darken } from 'polished'
import React, { useState } from 'react'
import { ChevronDown, ChevronUp } from 'react-feather'
import { Link } from 'react-router-dom'
import { Text } from 'rebass'
import styled from 'styled-components'
import { useTotalSupply } from '../../data/TotalSupply'
import styled from 'styled-components/macro'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { useTokenBalance } from '../../state/wallet/hooks'
import { ExternalLink, TYPE } from '../../theme'
import { currencyId } from '../../utils/currencyId'
@@ -24,7 +26,7 @@ import CurrencyLogo from '../CurrencyLogo'
import DoubleCurrencyLogo from '../DoubleLogo'
import { RowBetween, RowFixed, AutoRow } from '../Row'
import { Dots } from '../swap/styleds'
import { BIG_INT_ZERO } from '../../constants'
import { BIG_INT_ZERO } from '../../constants/misc'
export const FixedHeightRow = styled(RowBetween)`
height: 24px;
@@ -48,7 +50,7 @@ interface PositionCardProps {
pair: Pair
showUnwrapped?: boolean
border?: string
stakedBalance?: TokenAmount // optional balance to indicate that liquidity is deposited in mining pool
stakedBalance?: CurrencyAmount<Token> // optional balance to indicate that liquidity is deposited in mining pool
}
export function MinimalPositionCard({ pair, showUnwrapped = false, border }: PositionCardProps) {
@@ -63,8 +65,10 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
const totalPoolTokens = useTotalSupply(pair.liquidityToken)
const poolTokenPercentage =
!!userPoolBalance && !!totalPoolTokens && JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
? new Percent(userPoolBalance.raw, totalPoolTokens.raw)
!!userPoolBalance &&
!!totalPoolTokens &&
JSBI.greaterThanOrEqual(totalPoolTokens.quotient, userPoolBalance.quotient)
? new Percent(userPoolBalance.quotient, totalPoolTokens.quotient)
: undefined
const [token0Deposited, token1Deposited] =
@@ -72,16 +76,16 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
!!totalPoolTokens &&
!!userPoolBalance &&
// this condition is a short-circuit in the case where useTokenBalance updates sooner than useTotalSupply
JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
JSBI.greaterThanOrEqual(totalPoolTokens.quotient, userPoolBalance.quotient)
? [
pair.getLiquidityValue(pair.token0, totalPoolTokens, userPoolBalance, false),
pair.getLiquidityValue(pair.token1, totalPoolTokens, userPoolBalance, false)
pair.getLiquidityValue(pair.token1, totalPoolTokens, userPoolBalance, false),
]
: [undefined, undefined]
return (
<>
{userPoolBalance && JSBI.greaterThan(userPoolBalance.raw, JSBI.BigInt(0)) ? (
{userPoolBalance && JSBI.greaterThan(userPoolBalance.quotient, JSBI.BigInt(0)) ? (
<GreyCard border={border}>
<AutoColumn gap="12px">
<FixedHeightRow>
@@ -174,8 +178,10 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
const userPoolBalance = stakedBalance ? userDefaultPoolBalance?.add(stakedBalance) : userDefaultPoolBalance
const poolTokenPercentage =
!!userPoolBalance && !!totalPoolTokens && JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
? new Percent(userPoolBalance.raw, totalPoolTokens.raw)
!!userPoolBalance &&
!!totalPoolTokens &&
JSBI.greaterThanOrEqual(totalPoolTokens.quotient, userPoolBalance.quotient)
? new Percent(userPoolBalance.quotient, totalPoolTokens.quotient)
: undefined
const [token0Deposited, token1Deposited] =
@@ -183,10 +189,10 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
!!totalPoolTokens &&
!!userPoolBalance &&
// this condition is a short-circuit in the case where useTokenBalance updates sooner than useTotalSupply
JSBI.greaterThanOrEqual(totalPoolTokens.raw, userPoolBalance.raw)
JSBI.greaterThanOrEqual(totalPoolTokens.quotient, userPoolBalance.quotient)
? [
pair.getLiquidityValue(pair.token0, totalPoolTokens, userPoolBalance, false),
pair.getLiquidityValue(pair.token1, totalPoolTokens, userPoolBalance, false)
pair.getLiquidityValue(pair.token1, totalPoolTokens, userPoolBalance, false),
]
: [undefined, undefined]
@@ -197,28 +203,23 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
<CardNoise />
<AutoColumn gap="12px">
<FixedHeightRow>
<AutoRow gap="8px">
<AutoRow gap="8px" style={{ marginLeft: '8px' }}>
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} size={20} />
<Text fontWeight={500} fontSize={20}>
{!currency0 || !currency1 ? <Dots>Loading</Dots> : `${currency0.symbol}/${currency1.symbol}`}
</Text>
</AutoRow>
<RowFixed gap="8px">
<ButtonEmpty
padding="6px 8px"
borderRadius="12px"
width="fit-content"
onClick={() => setShowMore(!showMore)}
>
<RowFixed gap="8px" style={{ marginRight: '4px' }}>
<ButtonEmpty padding="6px 8px" borderRadius="12px" width="100%" onClick={() => setShowMore(!showMore)}>
{showMore ? (
<>
Manage
<ChevronUp size="20" style={{ marginLeft: '10px' }} />
<ChevronUp size="20" style={{ marginLeft: '8px', height: '20px', minWidth: '20px' }} />
</>
) : (
<>
Manage
<ChevronDown size="20" style={{ marginLeft: '10px' }} />
<ChevronDown size="20" style={{ marginLeft: '8px', height: '20px', minWidth: '20px' }} />
</>
)}
</ButtonEmpty>
@@ -295,19 +296,28 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
<ButtonSecondary padding="8px" borderRadius="8px">
<ExternalLink
style={{ width: '100%', textAlign: 'center' }}
href={`https://uniswap.info/account/${account}`}
href={`https://v2.info.uniswap.org/account/${account}`}
>
View accrued fees and analytics<span style={{ fontSize: '11px' }}></span>
</ExternalLink>
</ButtonSecondary>
{userDefaultPoolBalance && JSBI.greaterThan(userDefaultPoolBalance.raw, BIG_INT_ZERO) && (
{userDefaultPoolBalance && JSBI.greaterThan(userDefaultPoolBalance.quotient, BIG_INT_ZERO) && (
<RowBetween marginTop="10px">
<ButtonPrimary
padding="8px"
borderRadius="8px"
as={Link}
to={`/add/${currencyId(currency0)}/${currencyId(currency1)}`}
width="48%"
to={`/migrate/v2/${pair.liquidityToken.address}`}
width="32%"
>
Migrate
</ButtonPrimary>
<ButtonPrimary
padding="8px"
borderRadius="8px"
as={Link}
to={`/add/v2/${currencyId(currency0)}/${currencyId(currency1)}`}
width="32%"
>
Add
</ButtonPrimary>
@@ -315,14 +325,14 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
padding="8px"
borderRadius="8px"
as={Link}
width="48%"
to={`/remove/${currencyId(currency0)}/${currencyId(currency1)}`}
width="32%"
to={`/remove/v2/${currencyId(currency0)}/${currencyId(currency1)}`}
>
Remove
</ButtonPrimary>
</RowBetween>
)}
{stakedBalance && JSBI.greaterThan(stakedBalance.raw, BIG_INT_ZERO) && (
{stakedBalance && JSBI.greaterThan(stakedBalance.quotient, BIG_INT_ZERO) && (
<ButtonPrimary
padding="8px"
borderRadius="8px"

View File

@@ -0,0 +1,53 @@
// import { Story } from '@storybook/react/types-6-0'
// import React from 'react'
// import { Position } from 'types/position'
// import { basisPointsToPercent } from 'utils'
// import { DAI, WBTC } from '../../constants'
// import Component, { PositionListProps } from './index'
// import { CurrencyAmount } from '@uniswap/sdk-core'
// import JSBI from 'jsbi'
// const FEE_BIPS = {
// FIVE: basisPointsToPercent(5),
// THIRTY: basisPointsToPercent(30),
// ONE_HUNDRED: basisPointsToPercent(100),
// }
// const daiAmount = CurrencyAmount.fromRawAmount(DAI, JSBI.BigInt(500 * 10 ** 18))
// const wbtcAmount = CurrencyAmount.fromRawAmount(WBTC, JSBI.BigInt(10 ** 7))
// const positions = [
// {
// feesEarned: {
// DAI: 1000,
// WBTC: 0.005,
// },
// feeLevel: FEE_BIPS.FIVE,
// tokenAmount0: daiAmount,
// tokenAmount1: wbtcAmount,
// tickLower: 40000,
// tickUpper: 60000,
// },
// {
// feesEarned: {
// DAI: 1000,
// WBTC: 0.005,
// },
// feeLevel: FEE_BIPS.THIRTY,
// tokenAmount0: daiAmount,
// tokenAmount1: wbtcAmount,
// tickLower: 45000,
// tickUpper: 55000,
// },
// ]
// const positions: Position[] = []
export default {
title: 'PositionList',
}
// const Template: Story<PositionListProps> = (args) => <Component {...args} />
// export const PositionList = Template.bind({})
// PositionList.args = {
// positions,
// showUnwrapped: true,
// }

View File

@@ -0,0 +1,59 @@
import PositionListItem from 'components/PositionListItem'
import React from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components/macro'
import { MEDIA_WIDTHS } from 'theme'
import { PositionDetails } from 'types/position'
const DesktopHeader = styled.div`
display: none;
font-size: 14px;
font-weight: 500;
padding: 8px;
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
align-items: center;
display: flex;
display: grid;
grid-template-columns: 1fr 1fr;
& > div:last-child {
text-align: right;
margin-right: 12px;
}
}
`
const MobileHeader = styled.div`
font-weight: medium;
font-size: 16px;
font-weight: 500;
padding: 8px;
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
display: none;
}
`
export type PositionListProps = React.PropsWithChildren<{
positions: PositionDetails[]
}>
export default function PositionList({ positions }: PositionListProps) {
const { t } = useTranslation()
return (
<>
<DesktopHeader>
<div>
{t('Your positions')}
{positions && ' (' + positions.length + ')'}
</div>
<div>{t('Price range')}</div>
</DesktopHeader>
<MobileHeader>Your positions</MobileHeader>
{positions.map((p) => {
return <PositionListItem key={p.tokenId.toString()} positionDetails={p} />
})}
</>
)
}

View File

@@ -0,0 +1,31 @@
// import { Story } from '@storybook/react/types-6-0'
// import { FeeAmount, MAX_TICK, MIN_TICK, TICK_SPACINGS } from '@uniswap/v3-sdk'
// import { BigNumber } from 'ethers'
// import React from 'react'
// import { Position } from 'types/position'
// import Component, { PositionListItemProps } from './index'
// const position: Position = {
// nonce: BigNumber.from(0),
// operator: '',
// token0: '',
// token1: '',
// fee: FeeAmount.LOW,
// tickLower: MIN_TICK(TICK_SPACINGS[FeeAmount.LOW]),
// tickUpper: MAX_TICK(TICK_SPACINGS[FeeAmount.LOW]),
// liquidity,
// feeGrowthInside0LastX128fee
// feeGrowthInside0LastX128
// feeGrowthInside1LastX128
// tokensOwed0
// tokensOwed1
// }
export default {
title: 'PositionListItem',
}
// const Template: Story<PositionListItemProps> = (args) => <Component {...args} />
// export const PositionListItem = Template.bind({})
// PositionListItem.args = {position}

View File

@@ -0,0 +1,276 @@
import React, { useMemo, useState } from 'react'
import { Position } from '@uniswap/v3-sdk'
import Badge from 'components/Badge'
import DoubleCurrencyLogo from 'components/DoubleLogo'
import { usePool } from 'hooks/usePools'
import { useToken } from 'hooks/Tokens'
import { Link } from 'react-router-dom'
import styled from 'styled-components/macro'
import { HideSmall, MEDIA_WIDTHS, SmallOnly } from 'theme'
import { PositionDetails } from 'types/position'
import { WETH9, Price, Token, Percent } from '@uniswap/sdk-core'
import { formatPrice } from 'utils/formatTokenAmount'
import Loader from 'components/Loader'
import { unwrappedToken } from 'utils/wrappedCurrency'
import RangeBadge from 'components/Badge/RangeBadge'
import { RowFixed } from 'components/Row'
import HoverInlineText from 'components/HoverInlineText'
import { DAI, USDC, USDT, WBTC } from '../../constants/tokens'
const Row = styled(Link)`
align-items: center;
border-radius: 20px;
display: flex;
justify-content: space-between;
color: ${({ theme }) => theme.text1};
margin: 8px 0;
padding: 16px;
text-decoration: none;
font-weight: 500;
background-color: ${({ theme }) => theme.bg1};
&:first-of-type {
margin: 0 0 8px 0;
}
&:last-of-type {
margin: 8px 0 0 0;
}
& > div:not(:first-child) {
text-align: right;
}
:hover {
background-color: ${({ theme }) => theme.bg2};
}
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
flex-direction: row;
}
${({ theme }) => theme.mediaWidth.upToSmall`
flex-direction: column;
row-gap: 24px;
`};
`
const BadgeText = styled.div`
font-weight: 500;
font-size: 14px;
${({ theme }) => theme.mediaWidth.upToSmall`
font-size: 12px;
`};
`
const DataLineItem = styled.div`
font-size: 14px;
`
const RangeLineItem = styled(DataLineItem)`
display: flex;
flex-direction: row;
cursor: pointer;
align-items: center;
justify-self: flex-end;
${({ theme }) => theme.mediaWidth.upToSmall`
flex-direction: column;
row-gap: 4px;
`};
`
const DoubleArrow = styled.span`
margin: 0 2px;
color: ${({ theme }) => theme.text3};
${({ theme }) => theme.mediaWidth.upToSmall`
margin: 4px;
padding: 20px;
`};
`
const RangeText = styled.span`
/* background-color: ${({ theme }) => theme.bg2}; */
padding: 0.25rem 0.5rem;
border-radius: 8px;
`
const ExtentsText = styled.span`
color: ${({ theme }) => theme.text3};
font-size: 14px;
margin-right: 4px;
`
const PrimaryPositionIdData = styled.div`
display: flex;
flex-direction: row;
align-items: center;
> * {
margin-right: 8px;
}
`
const DataText = styled.div`
font-weight: 600;
font-size: 18px;
${({ theme }) => theme.mediaWidth.upToSmall`
font-size: 14px;
`};
`
export interface PositionListItemProps {
positionDetails: PositionDetails
}
export function getPriceOrderingFromPositionForUI(
position?: Position
): {
priceLower?: Price<Token, Token>
priceUpper?: Price<Token, Token>
quote?: Token
base?: Token
} {
if (!position) {
return {}
}
const token0 = position.amount0.currency
const token1 = position.amount1.currency
// if token0 is a dollar-stable asset, set it as the quote token
const stables = [DAI, USDC, USDT]
if (stables.some((stable) => stable.equals(token0))) {
return {
priceLower: position.token0PriceUpper.invert(),
priceUpper: position.token0PriceLower.invert(),
quote: token0,
base: token1,
}
}
// if token1 is an ETH-/BTC-stable asset, set it as the base token
const bases = [...Object.values(WETH9), WBTC]
if (bases.some((base) => base.equals(token1))) {
return {
priceLower: position.token0PriceUpper.invert(),
priceUpper: position.token0PriceLower.invert(),
quote: token0,
base: token1,
}
}
// if both prices are below 1, invert
if (position.token0PriceUpper.lessThan(1)) {
return {
priceLower: position.token0PriceUpper.invert(),
priceUpper: position.token0PriceLower.invert(),
quote: token0,
base: token1,
}
}
// otherwise, just return the default
return {
priceLower: position.token0PriceLower,
priceUpper: position.token0PriceUpper,
quote: token1,
base: token0,
}
}
export default function PositionListItem({ positionDetails }: PositionListItemProps) {
const {
token0: token0Address,
token1: token1Address,
fee: feeAmount,
liquidity,
tickLower,
tickUpper,
} = positionDetails
const token0 = useToken(token0Address)
const token1 = useToken(token1Address)
const currency0 = token0 ? unwrappedToken(token0) : undefined
const currency1 = token1 ? unwrappedToken(token1) : undefined
// construct Position from details returned
const [, pool] = usePool(currency0 ?? undefined, currency1 ?? undefined, feeAmount)
const position = useMemo(() => {
if (pool) {
return new Position({ pool, liquidity: liquidity.toString(), tickLower, tickUpper })
}
return undefined
}, [liquidity, pool, tickLower, tickUpper])
// prices
let { priceLower, priceUpper, base, quote } = getPriceOrderingFromPositionForUI(position)
const inverted = token1 ? base?.equals(token1) : undefined
const currencyQuote = inverted ? currency1 : currency0
const currencyBase = inverted ? currency0 : currency1
// check if price is within range
const outOfRange: boolean = pool ? pool.tickCurrent < tickLower || pool.tickCurrent >= tickUpper : false
const positionSummaryLink = '/pool/' + positionDetails.tokenId
const [manuallyInverted, setManuallyInverted] = useState(true)
if (manuallyInverted) {
;[priceLower, priceUpper, base, quote] = [priceUpper?.invert(), priceLower?.invert(), quote, base]
}
const removed = liquidity?.eq(0)
return (
<Row to={positionSummaryLink}>
<RowFixed>
<PrimaryPositionIdData>
<DoubleCurrencyLogo currency0={currencyBase} currency1={currencyQuote} size={18} margin />
<DataText>
&nbsp;{currencyQuote?.symbol}&nbsp;/&nbsp;{currencyBase?.symbol}
</DataText>
&nbsp;
<Badge>
<BadgeText>{new Percent(feeAmount, 1_000_000).toSignificant()}%</BadgeText>
</Badge>
</PrimaryPositionIdData>
<RangeBadge removed={removed} inRange={!outOfRange} />
</RowFixed>
{priceLower && priceUpper ? (
<>
<RangeLineItem
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
setManuallyInverted(!manuallyInverted)
}}
>
<RangeText>
<ExtentsText>Min: </ExtentsText>
{formatPrice(priceLower, 5)}{' '}
<HoverInlineText text={manuallyInverted ? currencyQuote?.symbol ?? '' : currencyBase?.symbol ?? ''} />{' '}
{' per '}{' '}
<HoverInlineText text={manuallyInverted ? currencyBase?.symbol ?? '' : currencyQuote?.symbol ?? ''} />
</RangeText>{' '}
<HideSmall>
<DoubleArrow></DoubleArrow>{' '}
</HideSmall>
<SmallOnly>
<DoubleArrow></DoubleArrow>{' '}
</SmallOnly>
<RangeText>
<ExtentsText>Max:</ExtentsText>
{formatPrice(priceUpper, 5)}{' '}
<HoverInlineText text={manuallyInverted ? currencyQuote?.symbol ?? '' : currencyBase?.symbol ?? ''} />{' '}
{' per '}{' '}
<HoverInlineText
maxCharacters={10}
text={manuallyInverted ? currencyBase?.symbol ?? '' : currencyQuote?.symbol ?? ''}
/>
</RangeText>{' '}
</RangeLineItem>
</>
) : (
<Loader />
)}
</Row>
)
}

View File

@@ -0,0 +1,158 @@
import React, { useState, useCallback, useContext } from 'react'
import { Position } from '@uniswap/v3-sdk'
import { LightCard } from 'components/Card'
import { AutoColumn } from 'components/Column'
import { TYPE } from 'theme'
import { RowBetween, RowFixed } from 'components/Row'
import CurrencyLogo from 'components/CurrencyLogo'
import { unwrappedToken } from 'utils/wrappedCurrency'
import { Break } from 'components/earn/styled'
import { useTranslation } from 'react-i18next'
import { Currency } from '@uniswap/sdk-core'
import RateToggle from 'components/RateToggle'
import DoubleCurrencyLogo from 'components/DoubleLogo'
import RangeBadge from 'components/Badge/RangeBadge'
import { ThemeContext } from 'styled-components'
import JSBI from 'jsbi'
export const PositionPreview = ({
position,
title,
inRange,
baseCurrencyDefault,
}: {
position: Position
title?: string
inRange: boolean
baseCurrencyDefault?: Currency | undefined
}) => {
const { t } = useTranslation()
const theme = useContext(ThemeContext)
const currency0 = unwrappedToken(position.pool.token0)
const currency1 = unwrappedToken(position.pool.token1)
// track which currency should be base
const [baseCurrency, setBaseCurrency] = useState(
baseCurrencyDefault
? baseCurrencyDefault === currency0
? currency0
: baseCurrencyDefault === currency1
? currency1
: currency0
: currency0
)
const sorted = baseCurrency === currency0
const quoteCurrency = sorted ? currency1 : currency0
const price = sorted ? position.pool.priceOf(position.pool.token0) : position.pool.priceOf(position.pool.token1)
const priceLower = sorted ? position.token0PriceLower : position.token0PriceUpper.invert()
const priceUpper = sorted ? position.token0PriceUpper : position.token0PriceLower.invert()
const handleRateChange = useCallback(() => {
setBaseCurrency(quoteCurrency)
}, [quoteCurrency])
const removed = position?.liquidity && JSBI.equal(position?.liquidity, JSBI.BigInt(0))
return (
<AutoColumn gap="md" style={{ marginTop: '0.5rem' }}>
<RowBetween style={{ marginBottom: '0.5rem' }}>
<RowFixed>
<DoubleCurrencyLogo
currency0={currency0 ?? undefined}
currency1={currency1 ?? undefined}
size={24}
margin={true}
/>
<TYPE.label ml="10px" fontSize="24px">
{currency0?.symbol} / {currency1?.symbol}
</TYPE.label>
</RowFixed>
<RangeBadge removed={removed} inRange={inRange} />
</RowBetween>
<LightCard>
<AutoColumn gap="md">
<RowBetween>
<RowFixed>
<CurrencyLogo currency={currency0} />
<TYPE.label ml="8px">{currency0?.symbol}</TYPE.label>
</RowFixed>
<RowFixed>
<TYPE.label mr="8px">{position.amount0.toSignificant(4)}</TYPE.label>
</RowFixed>
</RowBetween>
<RowBetween>
<RowFixed>
<CurrencyLogo currency={currency1} />
<TYPE.label ml="8px">{currency1?.symbol}</TYPE.label>
</RowFixed>
<RowFixed>
<TYPE.label mr="8px">{position.amount1.toSignificant(4)}</TYPE.label>
</RowFixed>
</RowBetween>
<Break />
<RowBetween>
<TYPE.label>{t('feeTier')}</TYPE.label>
<TYPE.label>{position?.pool?.fee / 10000}%</TYPE.label>
</RowBetween>
</AutoColumn>
</LightCard>
<AutoColumn gap="md">
<RowBetween>
{title ? <TYPE.main>{title}</TYPE.main> : <div />}
<RateToggle
currencyA={sorted ? currency0 : currency1}
currencyB={sorted ? currency1 : currency0}
handleRateToggle={handleRateChange}
/>
</RowBetween>
<RowBetween>
<LightCard width="48%" padding="8px">
<AutoColumn gap="4px" justify="center">
<TYPE.main fontSize="12px">Min Price</TYPE.main>
<TYPE.mediumHeader textAlign="center">{`${priceLower.toSignificant(5)}`}</TYPE.mediumHeader>
<TYPE.main
textAlign="center"
fontSize="12px"
>{` ${quoteCurrency.symbol}/${baseCurrency.symbol}`}</TYPE.main>
<TYPE.small textAlign="center" color={theme.text3} style={{ marginTop: '4px' }}>
Your position will be 100% composed of {baseCurrency?.symbol} at this price
</TYPE.small>
</AutoColumn>
</LightCard>
<LightCard width="48%" padding="8px">
<AutoColumn gap="4px" justify="center">
<TYPE.main fontSize="12px">Max Price</TYPE.main>
<TYPE.mediumHeader textAlign="center">{`${priceUpper.toSignificant(5)}`}</TYPE.mediumHeader>
<TYPE.main
textAlign="center"
fontSize="12px"
>{` ${quoteCurrency.symbol} per ${baseCurrency.symbol}`}</TYPE.main>
<TYPE.small textAlign="center" color={theme.text3} style={{ marginTop: '4px' }}>
Your position will be 100% composed of {quoteCurrency?.symbol} at this price
</TYPE.small>
</AutoColumn>
</LightCard>
</RowBetween>
<LightCard padding="12px ">
<AutoColumn gap="4px" justify="center">
<TYPE.main fontSize="12px">Current price</TYPE.main>
<TYPE.mediumHeader>{`${price.toSignificant(5)} `}</TYPE.mediumHeader>
<TYPE.main
textAlign="center"
fontSize="12px"
>{` ${quoteCurrency.symbol} per ${baseCurrency.symbol}`}</TYPE.main>
</AutoColumn>
</LightCard>
</AutoColumn>
</AutoColumn>
)
}

View File

@@ -1,49 +1,42 @@
import React from 'react'
import styled from 'styled-components'
import { RowBetween } from '../Row'
import React, { useContext } from 'react'
import styled from 'styled-components/macro'
import { AutoColumn } from '../Column'
import { transparentize } from 'polished'
import { ThemeContext } from 'styled-components'
import { TYPE } from '../../theme'
const Wrapper = styled(AutoColumn)``
const Wrapper = styled(AutoColumn)`
margin-right: 8px;
height: 100%;
`
const Grouping = styled(RowBetween)`
width: 50%;
const Grouping = styled(AutoColumn)`
width: fit-content;
padding: 4px;
/* background-color: ${({ theme }) => theme.bg2}; */
border-radius: 16px;
`
const Circle = styled.div<{ confirmed?: boolean; disabled?: boolean }>`
min-width: 20px;
min-height: 20px;
width: 48px;
height: 48px;
background-color: ${({ theme, confirmed, disabled }) =>
disabled ? theme.bg4 : confirmed ? theme.green1 : theme.primary1};
disabled ? theme.bg3 : confirmed ? theme.green1 : theme.primary1};
border-radius: 50%;
color: ${({ theme }) => theme.white};
color: ${({ theme, disabled }) => (disabled ? theme.text3 : theme.text1)};
display: flex;
align-items: center;
justify-content: center;
line-height: 8px;
font-size: 12px;
font-size: 16px;
padding: 1rem;
`
const CircleRow = styled.div`
width: calc(100% - 20px);
display: flex;
flex-direction: column;
align-items: center;
`
const Connector = styled.div<{ prevConfirmed?: boolean; disabled?: boolean }>`
width: 100%;
height: 2px;
background-color: ;
background: linear-gradient(
90deg,
${({ theme, prevConfirmed, disabled }) =>
disabled ? theme.bg4 : transparentize(0.5, prevConfirmed ? theme.green1 : theme.primary1)}
0%,
${({ theme, prevConfirmed, disabled }) => (disabled ? theme.bg4 : prevConfirmed ? theme.primary1 : theme.bg4)} 80%
);
opacity: 0.6;
`
interface ProgressCirclesProps {
steps: boolean[]
disabled?: boolean
@@ -60,6 +53,8 @@ interface ProgressCirclesProps {
* @param steps array of booleans where true means step is complete
*/
export default function ProgressCircles({ steps, disabled = false, ...rest }: ProgressCirclesProps) {
const theme = useContext(ThemeContext)
return (
<Wrapper justify={'center'} {...rest}>
<Grouping>
@@ -67,13 +62,13 @@ export default function ProgressCircles({ steps, disabled = false, ...rest }: Pr
return (
<CircleRow key={i}>
<Circle confirmed={step} disabled={disabled || (!steps[i - 1] && i !== 0)}>
{step ? '✓' : i + 1}
{step ? '✓' : i + 1 + '.'}
</Circle>
<Connector prevConfirmed={step} disabled={disabled} />
<TYPE.main color={theme.text4}>|</TYPE.main>
</CircleRow>
)
})}
<Circle disabled={disabled || !steps[steps.length - 1]}>{steps.length + 1}</Circle>
<Circle disabled={disabled || !steps[steps.length - 1]}>{steps.length + 1 + '.'}</Circle>
</Grouping>
</Wrapper>
)

View File

@@ -1,18 +1,20 @@
import React, { useCallback, useState } from 'react'
import { HelpCircle as Question } from 'react-feather'
import styled from 'styled-components'
import styled from 'styled-components/macro'
import Tooltip from '../Tooltip'
const QuestionWrapper = styled.div`
display: flex;
align-items: center;
justify-content: center;
padding: 0.2rem;
padding: 0px;
width: 18px;
height: 18px;
border: none;
background: none;
outline: none;
cursor: default;
border-radius: 36px;
font-size: 12px;
background-color: ${({ theme }) => theme.bg2};
color: ${({ theme }) => theme.text2};
@@ -44,20 +46,20 @@ const LightQuestionWrapper = styled.div`
`
const QuestionMark = styled.span`
font-size: 1rem;
font-size: 14px;
`
export default function QuestionHelper({ text }: { text: string }) {
export default function QuestionHelper({ text }: { text: string; size?: number }) {
const [show, setShow] = useState<boolean>(false)
const open = useCallback(() => setShow(true), [setShow])
const close = useCallback(() => setShow(false), [setShow])
return (
<span style={{ marginLeft: 4 }}>
<span style={{ marginLeft: 4, display: 'flex', alignItems: 'center' }}>
<Tooltip text={text} show={show}>
<QuestionWrapper onClick={open} onMouseEnter={open} onMouseLeave={close}>
<Question size={16} />
<QuestionMark>?</QuestionMark>
</QuestionWrapper>
</Tooltip>
</span>

View File

@@ -0,0 +1,70 @@
import React from 'react'
import { Currency, Price, Token } from '@uniswap/sdk-core'
import StepCounter from 'components/InputStepCounter/InputStepCounter'
import { RowBetween } from 'components/Row'
import { useActiveWeb3React } from 'hooks/web3'
import { wrappedCurrency } from 'utils/wrappedCurrency'
// currencyA is the base token
export default function RangeSelector({
priceLower,
priceUpper,
onLeftRangeInput,
onRightRangeInput,
getDecrementLower,
getIncrementLower,
getDecrementUpper,
getIncrementUpper,
currencyA,
currencyB,
feeAmount,
}: {
priceLower?: Price<Token, Token>
priceUpper?: Price<Token, Token>
getDecrementLower: () => string
getIncrementLower: () => string
getDecrementUpper: () => string
getIncrementUpper: () => string
onLeftRangeInput: (typedValue: string) => void
onRightRangeInput: (typedValue: string) => void
currencyA?: Currency | null
currencyB?: Currency | null
feeAmount?: number
}) {
const { chainId } = useActiveWeb3React()
const tokenA = wrappedCurrency(currencyA ?? undefined, chainId)
const tokenB = wrappedCurrency(currencyB ?? undefined, chainId)
const isSorted = tokenA && tokenB && tokenA.sortsBefore(tokenB)
const leftPrice = isSorted ? priceLower : priceUpper?.invert()
const rightPrice = isSorted ? priceUpper : priceLower?.invert()
return (
<RowBetween>
<StepCounter
value={leftPrice?.toSignificant(5) ?? ''}
onUserInput={onLeftRangeInput}
width="48%"
decrement={isSorted ? getDecrementLower : getIncrementUpper}
increment={isSorted ? getIncrementLower : getDecrementUpper}
feeAmount={feeAmount}
label={leftPrice ? `${currencyB?.symbol}` : '-'}
title={'Min Price'}
tokenA={currencyA?.symbol}
tokenB={currencyB?.symbol}
/>
<StepCounter
value={rightPrice?.toSignificant(5) ?? ''}
onUserInput={onRightRangeInput}
width="48%"
decrement={isSorted ? getDecrementUpper : getIncrementLower}
increment={isSorted ? getIncrementUpper : getDecrementLower}
feeAmount={feeAmount}
label={rightPrice ? `${currencyB?.symbol}` : '-'}
tokenA={currencyA?.symbol}
tokenB={currencyB?.symbol}
title={'Max Price'}
/>
</RowBetween>
)
}

View File

@@ -0,0 +1,37 @@
import React from 'react'
import { Currency } from '@uniswap/sdk-core'
import { ToggleElement, ToggleWrapper } from 'components/Toggle/MultiToggle'
import { useActiveWeb3React } from 'hooks/web3'
import { wrappedCurrency } from 'utils/wrappedCurrency'
// the order of displayed base currencies from left to right is always in sort order
// currencyA is treated as the preferred base currency
export default function RateToggle({
currencyA,
currencyB,
handleRateToggle,
}: {
currencyA: Currency
currencyB: Currency
handleRateToggle: () => void
}) {
const { chainId } = useActiveWeb3React()
const tokenA = wrappedCurrency(currencyA, chainId)
const tokenB = wrappedCurrency(currencyB, chainId)
const isSorted = tokenA && tokenB && tokenA.sortsBefore(tokenB)
return tokenA && tokenB ? (
<div style={{ width: 'fit-content', display: 'flex', alignItems: 'center' }}>
<ToggleWrapper width="fit-content">
<ToggleElement isActive={isSorted} fontSize="12px" onClick={handleRateToggle}>
{isSorted ? currencyA.symbol + ' price ' : currencyB.symbol + ' price '}
</ToggleElement>
<ToggleElement isActive={!isSorted} fontSize="12px" onClick={handleRateToggle}>
{isSorted ? currencyB.symbol + ' price ' : currencyA.symbol + ' price '}
</ToggleElement>
</ToggleWrapper>
</div>
) : null
}

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