Compare commits

..

153 Commits

Author SHA1 Message Date
Justin Domingue
9e1a775c13 feat: integrate SwapRouter02 on L1/L2 + gas ui
* client-side smart order router support
* support auto router on L2s
* add swap router version in approval/swap callback GA events to save $ on approval txs
* add persistent UI view of gas estimate on L1s

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
Co-authored-by: Ian Lapham <ian@uniswap.org>
Co-authored-by: Callil Capuozzo <callil.capuozzo@gmail.com>
2021-12-16 14:44:03 -05:00
Barry G
642a4177d8 fix: extend transaction deadline to 3 days (#2982) 2021-12-15 16:52:41 -08:00
Ben Krochta
37e085763c fix: display Uniswap token list in UI (#2821)
* fix: display Uniswap token list in UI

* chore: remove default-token-list build dependency

* fix: use ENS name for Uniswap token list

* fix: change Uniswap token list url
2021-12-13 19:38:37 -05:00
Noah Zinsmeister
e2baa051c5 add fix for polygon proposal title (#2974) 2021-12-13 16:28:37 -05:00
Zach Pomerantz
a5b152da06 fix: memoize hooks from /swap (#2949)
* fix: memoize hooks from /swap

* chore: rm console
2021-12-09 13:06:55 -08:00
Noah Zinsmeister
38cf4f46d7 give a bit more gas to balanceOf (#2943) 2021-12-07 18:07:11 -05:00
Will Hennessy
744c313ecf feat: Add learn more link in TRM description (#2919)
* Add learn more link in TRM description

* Update src/components/PrivacyPolicy/index.tsx

Co-authored-by: Justin Domingue <judo@uniswap.org>
2021-12-06 12:26:33 -05:00
Noah Zinsmeister
b967b1b236 fix: introduce safeNamehash (#2925)
* namehash -> safeNamehash where necessary

* cleanup

* address comment
2021-12-03 16:45:33 -05:00
J M Rossy
596ea03043 refactor: Replace multicall implementation with library (#2768)
- Replace the local implementation of multicall with the new redux-multicall lib
- Create wrappers for redux-multicall hooks to inject block number and chainId
2021-12-03 06:29:39 -05:00
Zach Pomerantz
e81e8a8f71 fix: inadvertent merges/reverts (#2915)
* Revert "Revert "Merge branch 'main' of https://github.com/Uniswap/interface" (#2912)"

This reverts commit 7d343dcfbd.

* Revert "deleted files"

This reverts commit 097b8361d4.
2021-12-02 10:35:39 -08:00
Justin Domingue
7d343dcfbd Revert "Merge branch 'main' of https://github.com/Uniswap/interface" (#2912)
This reverts commit bf7a40be7a, reversing
changes made to 097b8361d4.
2021-12-02 13:12:22 -05:00
Justin Domingue
bf7a40be7a Merge branch 'main' of https://github.com/Uniswap/interface 2021-12-02 11:15:09 -05:00
Justin Domingue
097b8361d4 deleted files 2021-12-02 11:14:41 -05:00
Carlos Diaz-Padron
82843ff16a fix: typo in arweave URI recognition (#2901) 2021-12-01 13:26:46 -05:00
Moody Salem
890471f590 fix: fix layout of proposal list items on the vote page on mobile (#2898)
* fix: fixing layout from using grid to flexbox

* fix: setting WrapSmall to nowrap due to layout issue on mobile

* fix: using width auto instead of disabling flex wrap

Co-authored-by: Julian Anderson <juliancanderson@gmail.com>
2021-12-01 10:23:02 -05:00
Dmitri Tsumak
478ee7ba14 fix: Add routes for stakewise tokens (#2832)
* Add additional routes for stakewise tokens

* Reference StakeWise addresses with sdk tokens

* Sort token imports
2021-11-30 15:25:50 -05:00
Moody Salem
745be977ef fix(lint): clean up the eslint config (#2886)
* fix(lint): clean up the eslint config

* Fix code style issues with ESLint

* fix the linter errors that arose from using the proper config

* clean up the rebass text renames

* fix if statement, use the config

* use the same name prefix for both steps

* `TextPreset` -> `ThemedText`

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-11-30 15:20:59 -05:00
Justin Domingue
0e25c055fb add more tests for tryParseTick (#2110) 2021-11-30 11:44:40 -05:00
dependabot[bot]
82a079935e chore(deps): bump url-parse from 1.5.1 to 1.5.3 (#2504)
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.1 to 1.5.3.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.1...1.5.3)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-30 11:40:15 -05:00
dependabot[bot]
876c1539d4 chore(deps): bump ws from 5.2.2 to 5.2.3 (#2759)
Bumps [ws](https://github.com/websockets/ws) from 5.2.2 to 5.2.3.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/5.2.2...5.2.3)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-30 11:38:15 -05:00
Crowdin Bot
f43fd89884 chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-30 02:11:35 +00:00
Zach Pomerantz
efdfdc9083 refactor: use ref for jazzicon (#2874) 2021-11-29 15:49:01 -08:00
Zach Pomerantz
709f0299e2 fix: remove orphaned node (#2863)
* fix: remove orphaned node

* fix: react cleanup
2021-11-29 12:15:08 -08:00
Moody Salem
6b57ffe311 fix: do not show urls if issue is not occurring on app.uniswap.org (#2855)
* fix: do not show urls if issue is not occurring on app.uniswap.org

fixes https://github.com/Uniswap/interface/issues/2572

* address comment
2021-11-29 12:44:14 -05:00
Moody Salem
f7ecdc4332 chore: run linters with auto_fix = false for forks (#2852) 2021-11-28 12:30:58 -05:00
Moody Salem
b1009b0e03 chore: fix the build blocking linter error 2021-11-28 12:07:51 -05:00
Crowdin Bot
5a4c7890c6 chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-25 20:06:51 +00:00
Kimmo S
5a3c91f19f Update locales.ts (#2825)
update Finnish from person (Suomalainen) to language (suomi)
2021-11-23 10:04:14 -05:00
Zach Pomerantz
d5c4ee0342 feat: display an ENS avatar (#2806)
* feat: ens avatar resolution

* chore: uninstall @davatar/react

* fix: add avatar alt

* feat: support data uris

* feat: support arweave uris

* feat: support erc721 avatars

* feat: support erc1155 avatars

* fix: jazzicon integration

* fix: clean usage of status icon

* fix: fix jazzicon svg offset

* refactor: share status icon component

* fix: pass memoized args to multicall
2021-11-22 10:55:25 -08:00
Jordan Frankfurt
262d984f92 check for support on all connectors, and disable when the connector (or lack thereof) no longer supports 3085 (#2824) 2021-11-22 12:33:52 -05:00
Lint Action
7781f5112e Fix code style issues with ESLint 2021-11-22 16:08:21 +00:00
Jordan Frankfurt
204e44ac40 fix(L2): block L2 tokens explicitly linked to L1 tokens that are blocked (#2721)
* block L2 tokens explicitly linked to L1 tokens that are blocked
2021-11-22 11:04:36 -05:00
Will Hennessy
7c9d9bdb03 Add trailing slash to L2 info links (#2696)
Some links were broken. For example on /pools/ page click the 'Top Pools' CTA. It would mistakenly direct you to info.uniswap.org/optimismpools instead of optimism/pools
2021-11-22 09:32:34 -05:00
Justin Domingue
cb0ea3f14d log an event on max click (#2827) 2021-11-22 09:27:29 -05:00
Crowdin Bot
e54ffcc483 chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-19 16:06:45 +00:00
Justin Domingue
8a72b374a8 fix: fix #2818 2021-11-19 10:10:45 -05:00
Crowdin Bot
3f64415906 chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-18 19:06:37 +00:00
Lint Action
41a4500f41 Fix code style issues with ESLint 2021-11-18 18:27:24 +00:00
Ali Eray Kısabacak
093dc66cfe Fixing #2818 (#2820) 2021-11-18 13:23:13 -05:00
Moody Salem
b10729d217 feat: set the auto slippage tolerance by the dollar value of gas (#2815)
* feat: set the auto slippage tolerance by the dollar value of gas

* comments

* min/max at 0.5% to 25%

* oops on constant

* address review feedback
2021-11-18 13:05:29 -05:00
Brendan Weinstein
9b0fa8a2c4 fix: Enable 3085 requests for coinbase wallet (#2753)
enable 3085 requests for coinbase wallet
2021-11-18 12:29:56 -05:00
Crowdin Bot
82c026872f chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-18 06:11:55 +00:00
Sam Chen
222a6d53bc perf: lazy load vote related routes (#2468)
* perf: lazy load vote related routes

* wrap Switch in Suspense

* remove exact to match nested routes

* fix nested routes

* split Landing

* fix
2021-11-17 22:01:45 -08:00
Matthew Salamon
0f35f6ee93 chore: update typechain scripts for Windows (#2707)
There are two errors when deploying on Windows system:
1. Using single quotes in path argument doesn't seem to be accepted in typechain command
2. `?(v3-core|v3-periphery)` operator doesn't work

Here are fixes/workarounds.
2021-11-17 21:00:37 -08:00
Ikko Ashimine
7b83e3968f chore: fix typo in useAllCurrencyCombinations.ts (#2778)
occurence -> occurrence
2021-11-17 20:40:58 -08:00
Raj
7938273c0c fix: broken link to docs (#2816) 2021-11-17 20:30:54 -08:00
Crowdin Bot
51a4504c75 chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-18 03:06:35 +00:00
Zach Pomerantz
b2697f0077 fix: format date using Date.toLocaleString (#2459)
* fix: format date using Date.toLocaleString

Fixes #2458

* fix: date typings
2021-11-16 16:20:46 -08:00
Crowdin Bot
34a58851f7 chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-14 14:06:34 +00:00
Will Hennessy
5a20dc82cd change not created font size to 10 (#2785) 2021-11-12 15:23:15 -05:00
Jordan Frankfurt
d1627a6c36 fix(L2): remove redux from chain connectivity (#2781)
* remove redux from chain connectivity

* useMachineTimeMs instead of Date.now to force updates, useCurrentBlockTimestamp

* use useInterval
2021-11-12 13:24:45 -06:00
Crowdin Bot
55c971892c chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-12 17:09:58 +00:00
Moody Salem
68f8576499 fix: copy in network alert 2021-11-12 11:50:07 -05:00
Jordan Frankfurt
d0be3bf222 fix(L2): update block warning updater to check most recent block timestamp (#2777)
* update block warning updater to check most recent block timestamp

* stop doing dumb state manipulation
2021-11-12 10:18:25 -05:00
Crowdin Bot
b79fe4b833 chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-12 15:06:38 +00:00
Justin Domingue
408c907870 feat: add support for 0.01% tier (#2769)
* chore: add support for 0.01% tier

* only show 1bps on mainnet

* rename VERY_LOW to LOWEST

* upgrade to v3-sdk 3.7.0

* add snapshot testing for lowest tier

* fix integration test

* fix integration test

* use ALL_SUPPORTED_CHAIN_IDS over string all

* consider 0.01% tier in pool (#2770)

* merge main and only consider lowest tier for mainnet
2021-11-12 09:46:26 -05:00
Crowdin Bot
8a99bad736 chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-12 09:07:00 +00:00
Crowdin Bot
5ba3d2f679 chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-12 01:27:47 +00:00
Crowdin Bot
ec4cd57dc0 chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-11 21:07:22 +00:00
Moody Salem
ccad45d24e fix(optimism): Optimism regenesis support (#2703)
* feat(optimism): optimistic kovan local regenesis changes

* use the regenesis version of the sdk

* remove the override no longer necessary

* diff rpc url

* back to kovan url

* lint error

* Optimism mainnet regenesis test (#2695)

* remove the optimism mainnet specific code and point to the mainnet regenesis rpc url

* point at the old mainnet multicall address

* bump the sdk version

* copy the list

* multicall address regenesis change

* revert the gas limit special casing for optimism

* bump the sdk version

* remove a couple other temporary edits

* unused test case

* specific version of v3-sdk
2021-11-11 15:40:12 -05:00
Justin Domingue
1903a16097 fix: lint error (#2775) 2021-11-11 13:12:09 -05:00
Justin Domingue
0ea029db4f add protocols param to quote endpoint (#2774)
* add protocols param to quote endpoint

* Fix code style issues with ESLint

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-11-11 11:42:17 -05:00
Crowdin Bot
cb41df4cd5 chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-11 16:07:15 +00:00
Jordan Frankfurt
659a564db8 no unused imports eslint rule (#2773) 2021-11-11 10:53:14 -05:00
Will Hennessy
74c61c0213 feat: Menu update. Add help center & feature requests. Remove analytics & github. (#2709)
* Add help center, remove analytics from menu

* Add canny feature requests link, remove github link

* add coffee icon
2021-11-11 10:31:08 -05:00
Will Hennessy
dc55a21285 fix: remove deprecated optimism status url (#2771) 2021-11-10 16:41:00 -05:00
Moody Salem
48cc6811c9 fix: bump to latest token list including ENS token 2021-11-08 23:46:50 -05:00
Ben Krochta
ab93d512d3 fix: #2741 Increase liquidity form off center (#2746) 2021-11-05 10:29:55 -04:00
Crowdin Bot
cf4c26a77c chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-03 22:06:57 +00:00
Crowdin Bot
02296c686f chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-03 18:11:33 +00:00
Noah Zinsmeister
60bd0eb86c add proposal start time (#2738) 2021-11-03 13:57:56 -04:00
Noah Zinsmeister
188b321cc9 fix: Parse latest proposal description correctly 2021-11-03 13:12:39 -04:00
Brendan Weinstein
377331c44e fix: Update walletlink-connector to 6.2.8 (#2655)
* Update walletlink-connector to 6.2.5 which has a walletlink update to support addEthereumChain+switchEthereumChain requests

* Update walletlink-connector to 6.2.7

* Update walletlink-connector to 6.2.8
2021-11-03 10:56:54 -04:00
Jordan Frankfurt
dfd442cdac try cd1 for custom dimension (#2734) 2021-11-02 22:50:47 -04:00
Jordan Frankfurt
9561cf54e4 don't overwrite localstorage lists when fetch throws (#2723) 2021-11-02 16:31:25 -05:00
Ian Lapham
714953b50e add token to unsupported list (#2732) 2021-11-02 14:46:14 -04:00
Ian Lapham
b3844e38d1 feat: update cmc list link (#2710)
* update cmc lists

* update CMC url
2021-11-02 10:39:40 -04:00
Crowdin Bot
5dac6a03eb chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-01 21:06:15 +00:00
Moody Salem
d18974480a fix: memoize the list stuff so the tokens are consistently clickable (#2724) 2021-11-01 14:01:41 -04:00
Will Hennessy
6a90bf3b9d chore: update unsupported token list (#2689)
* chore: update unsupported token list

* Fix code style issues with ESLint

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-11-01 13:43:55 -04:00
M0kY
5026ebded8 fix: walletconnect modal re-open after user rejection (#2693)
Co-authored-by: M0kY <moky@example.com>
2021-11-01 13:42:16 -04:00
Crowdin Bot
02dbed7a75 chore(i18n): synchronize translations from crowdin [skip ci] 2021-11-01 15:09:04 +00:00
Crowdin Bot
20f462f5a4 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-27 19:06:32 +00:00
Jordan Frankfurt
cf9c6e4b4c try out 'dimension1' (#2704) 2021-10-27 12:49:42 -04:00
Crowdin Bot
c0201206cc chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-27 13:10:19 +00:00
dependabot[bot]
2d2508f681 chore(deps-dev): bump @uniswap/token-lists (#2699) 2021-10-27 07:15:50 -04:00
Crowdin Bot
609542c49b chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-27 06:07:13 +00:00
Crowdin Bot
9a0294f469 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-26 22:06:20 +00:00
Jordan Frankfurt
68e6bc1ba8 block import of unsupported tokens (#2673)
generalize custom import token block ui
2021-10-26 17:38:59 -04:00
Justin Domingue
bbc64f12bb fix: back arrow bug in wallet modal and fill tx for wallet (#2687)
* add tx to wallet connect

* remove id from env

* restore env
2021-10-26 12:46:10 -04:00
Crowdin Bot
06f5fdc6ad chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-26 09:07:10 +00:00
Crowdin Bot
b4e756ebba chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-26 08:07:05 +00:00
Crowdin Bot
17439d6fbb chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-25 18:06:54 +00:00
Crowdin Bot
79d582c72f chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-25 17:09:26 +00:00
Crowdin Bot
245f8d7279 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-25 16:07:10 +00:00
Justin Domingue
3bf36eade4 chore: set final privacy learn more link' (#2684)
* add learn more button

* add final link
2021-10-25 11:57:23 -04:00
Justin Domingue
8eb864426f refactor monitoring (#2682) 2021-10-25 10:39:16 -04:00
Justin Domingue
af83399606 log full signed tx (#2681) 2021-10-25 09:44:07 -04:00
Crowdin Bot
1d5be31621 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-25 13:12:59 +00:00
Justin Domingue
abe6bf500c feat: extend privacy and terms (#2623)
* initial iteration

* add logging

* added hook

* polish

* remove unused import

* add hash

* addressed pr feedback

* remove autorouter icon

* use firebase store

* style

* adjust recat ga

* log remove liquidity

* update copy

* addressed pr feedback

* addressed pr feedback

* prevent privacy content from dismissing modal

* make top-level key origin

* use hostname

* restore trm
2021-10-25 08:41:55 -04:00
Crowdin Bot
36cfe627f1 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-23 10:06:44 +00:00
Crowdin Bot
aef5d0513a chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-23 09:06:47 +00:00
Ian Lapham
5e09a0ced7 fix: update token list (#2671)
* update token list

* Fix code style issues with ESLint

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-10-22 16:18:37 -04:00
Ian Lapham
f768428b9d chore: update token list (#2670)
* update token list

* Fix code style issues with ESLint

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-10-22 15:47:42 -04:00
Zach Pomerantz
76c9bf84cd fix: use optional operator for chainId (#2666) 2021-10-22 10:33:18 -07:00
Jordan Frankfurt
f26a3306ec fix(L2): ensure chainIds match before fetching pool data (#2652)
* ensure chainIds match before fetching pool data

* debounce both input currencies, and only look for pairs on currencies that share a chainId

* pr feedback
2021-10-22 12:14:04 -04:00
Crowdin Bot
1846883e56 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-21 06:06:22 +00:00
Zach Pomerantz
9cacd577e7 fix: restrict @davatar usage to avoid 3p fetches (#2649) 2021-10-20 09:19:19 -07:00
Moody Salem
5e8d725e0e fix: split calls into more chunks if they fail due to out of gas errors (#2630)
* fix: split calls into more chunks if they fail due to out of gas errors

* set to 100m gas

* back to 25m so we batch fewer calls

* do not pass through gas limit, some simplification of the code

* unused import
2021-10-19 11:37:57 -05:00
Justin Domingue
c63482b6f7 use l2 logos in base pairs (#2634) 2021-10-19 10:55:59 -04:00
Justin Domingue
e15a8ddf59 remove WETH from optimism bases (#2640) 2021-10-19 10:08:28 -04:00
Justin Domingue
1e7dff060f fix: add usdc to arbitrum/optimism common bases (#2641) 2021-10-19 10:08:10 -04:00
Micael Rodrigues
244ed38b72 fix: center focused outline card (#2625) 2021-10-18 14:13:09 -04:00
Crowdin Bot
87d547ab2b chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-17 03:06:15 +00:00
Crowdin Bot
0c213be5d9 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-17 01:29:14 +00:00
Crowdin Bot
f62d301891 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-17 00:11:16 +00:00
Crowdin Bot
902cd343d2 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-16 07:06:35 +00:00
Crowdin Bot
a26db9a51d chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-15 07:06:39 +00:00
Crowdin Bot
b7cb2d24c7 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-15 01:28:43 +00:00
Justin Domingue
aea2bca775 fix: increase quote gas overrides on arbitrum (#2614) 2021-10-14 14:22:26 -04:00
Crowdin Bot
93b79be1b6 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-14 18:06:59 +00:00
Justin Domingue
54531b53c6 refactor: migrate state/application to slice (#2615) 2021-10-14 13:40:19 -04:00
Crowdin Bot
b286c1ba09 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-14 15:11:24 +00:00
Justin Domingue
8dd1be3b47 fix: remove extra $ from translations (#2611)
* remove extra $

* refactor

* refactor
2021-10-14 10:34:16 -04:00
Justin Domingue
0b8afcff41 only display TradePrice usdcPrice if available (#2613) 2021-10-14 10:15:17 -04:00
Crowdin Bot
e4b727f6f0 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-14 06:07:01 +00:00
Crowdin Bot
6e0b24cbb1 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-13 23:06:18 +00:00
Justin Domingue
442879ccd9 improve fee tier selector (#2609) 2021-10-13 18:22:45 -04:00
Carlos Diaz-Padron
50386f65e7 upgrade @davatar/react to 1.8.1 (#2485) 2021-10-13 14:03:06 -07:00
Zach Pomerantz
eba8170c46 chore: eslint nit (#2607) 2021-10-13 14:02:17 -07:00
Lint Action
095dbffbca Fix code style issues with ESLint 2021-10-13 18:02:05 +00:00
Justin Domingue
4df2824984 update arbitrum subgraph url
fee tier working, tick data not in this subgraph
2021-10-13 13:59:01 -04:00
Justin Domingue
15345690e3 add reducer path to routing api (#2541) 2021-10-12 16:56:06 -04:00
Connor McEwen
3e36281f77 fix: z-index bug which prevented modal click (#2264)
* Fix z-index bug which prevented modal click. Center button and remove underline

* Fix code style issues with ESLint

* fix: PR comments

* Fix code style issues with ESLint

* z-index comment

* Fix code style issues with ESLint

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-10-11 14:00:31 -04:00
nitipon.eth
27a868f0cb fix(SafariSquareDots): fixed by using border-top-with instead of bord… (#2474)
* fix(SafariSquareDots): fixed by using border-top-with instead of border-width

* fix(SafariSqaureDots): fixed by using a min width of 5px and then following the width of the screen if the screen is too wide

* fix(SafariSquareDots): fixed by using Dot SVG instead of border-style CSS

* fix(SafariSquareDots):  Fixed code follows suggestions from the Pull Request

* regenerate snapshots

Co-authored-by: NITIPON CHINGTHONGCHAI <nitipon.chingthongchai@scb.co.th>
Co-authored-by: Justin Domingue <judo@uniswap.org>
2021-10-11 13:56:58 -04:00
Connor McEwen
03c3fdef34 Change import to lib/bundle (#2524) 2021-10-11 13:06:29 -04:00
Connor McEwen
d37e963f26 fix: Show proper graph color for non-Ethereum L1 chains (#2049) (#2284)
* Switch to fetching token color from the logoURI included

* Fall back to github hosted images

* Move logger to changes to another PR
2021-10-11 12:07:28 -04:00
Crowdin Bot
2e40bef7f0 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-11 15:06:46 +00:00
Crowdin Bot
dac87e2299 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-11 14:06:32 +00:00
Justin Domingue
be15604244 fix: replace t with <Trans> where possible (#2516)
* replace t macro with <Trans>

* replace t macro with <Trans>

* add lint rule for t

* fixed more ts
2021-10-11 09:30:51 -04:00
Crowdin Bot
077570efb4 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-11 11:06:27 +00:00
Crowdin Bot
a9ea5c9a1d chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-11 08:06:19 +00:00
Crowdin Bot
f5b91c9cda chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-10 21:07:12 +00:00
Crowdin Bot
c0e6388bf8 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-09 11:07:06 +00:00
Crowdin Bot
d89d5598a0 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-09 09:06:21 +00:00
Crowdin Bot
6f66f5ee77 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-08 21:07:14 +00:00
Justin Domingue
4b8d942da6 fix: add usdc price in TradePrice (#2441)
* add usdc price in trade price

* adjust color

* adjust usdc price

* Fix code style issues with ESLint

* round to 4

* use same format as fiat value

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-10-08 16:49:54 -04:00
Crowdin Bot
ef2cb75f6f chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-08 10:06:46 +00:00
Crowdin Bot
5e0e9be955 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-08 09:05:59 +00:00
Crowdin Bot
72a914b0d1 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-08 08:06:18 +00:00
Crowdin Bot
c4ce5caf0c chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-08 07:06:21 +00:00
Crowdin Bot
b87a45b0ba chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-08 06:08:20 +00:00
Crowdin Bot
80eb394877 chore(i18n): synchronize translations from crowdin [skip ci] 2021-10-07 19:06:26 +00:00
255 changed files with 13101 additions and 8361 deletions

View File

@@ -2,3 +2,4 @@ REACT_APP_INFURA_KEY="099fc58e0de9451d80b18d7c74caa7c1"
REACT_APP_PORTIS_ID="c0e2bf01-4b08-4fd5-ac7b-8e26b58cd236"
REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
REACT_APP_GOOGLE_ANALYTICS_ID="UA-128182339-4"
REACT_APP_FIREBASE_KEY="AIzaSyBcZWwTcTJHj_R6ipZcrJkXdq05PuX0Rs0"

View File

@@ -8,21 +8,42 @@
"jsx": true
}
},
"ignorePatterns": ["node_modules/**/*"],
"settings": {
"react": {
"version": "detect"
}
},
"ignorePatterns": [
"src/types/v3",
"src/abis/types",
"src/locales/**/*.js",
"src/locales/**/en-US.po",
"src/state/data/generated.ts",
"node_modules",
"coverage",
"build",
"dist",
".DS_Store",
".env.local",
".env.development.local",
".env.test.local",
".env.production.local",
".idea/",
".vscode/",
"package-lock.json",
"yarn.lock"
],
"extends": [
"react-app",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
],
"plugins": ["simple-import-sort"],
"plugins": ["simple-import-sort", "unused-imports"],
"rules": {
"unused-imports/no-unused-imports": "error",
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error",
"@typescript-eslint/explicit-function-return-type": "off",
@@ -48,6 +69,11 @@
{
"name": "styled-components",
"message": "Please import from styled-components/macro."
},
{
"name": "@lingui/macro",
"importNames": ["t"],
"message": "Please use <Trans> instead of t."
}
],
"patterns": [

View File

@@ -7,3 +7,4 @@ updates:
interval: "daily"
allow:
- dependency-name: "@uniswap/token-lists"
- dependency-name: "@uniswap/default-token-list"

View File

@@ -11,7 +11,6 @@ on:
jobs:
run-linters:
name: Run linters
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login == github.repository_owner }}
runs-on: ubuntu-latest
steps:
@@ -39,10 +38,15 @@ jobs:
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Run linters
- name: Run eslint w/ autofix
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login == github.repository_owner }}
uses: wearerequired/lint-action@36c7e6689e80d785d27a22f71d970f3a3b4fcb70
with:
github_token: ${{ secrets.github_token }}
eslint: true
eslint_extensions: js,jsx,ts,tsx,json
eslint_args: "-c .eslintrc.json"
auto_fix: true
- name: Run eslint
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.owner.login != github.repository_owner }}
run: yarn eslint .

View File

@@ -10,7 +10,7 @@ An open source interface for Uniswap -- a protocol for decentralized exchange of
- Website: [uniswap.org](https://uniswap.org/)
- Interface: [app.uniswap.org](https://app.uniswap.org)
- Docs: [uniswap.org/docs/](https://uniswap.org/docs/)
- Docs: [uniswap.org/docs/](https://docs.uniswap.org/)
- Twitter: [@Uniswap](https://twitter.com/Uniswap)
- Reddit: [/r/Uniswap](https://www.reddit.com/r/Uniswap/)
- Email: [contact@uniswap.org](mailto:contact@uniswap.org)

View File

@@ -5,6 +5,11 @@
}
},
"asToken0": [
{
"feeTier": "100",
"totalValueLockedToken0": "0",
"totalValueLockedToken1": "3"
},
{
"feeTier": "500",
"totalValueLockedToken0": "0",
@@ -13,7 +18,7 @@
{
"feeTier": "3000",
"totalValueLockedToken0": "0",
"totalValueLockedToken1": "7"
"totalValueLockedToken1": "4"
},
{
"feeTier": "10000",

View File

@@ -58,7 +58,7 @@ describe('Add Liquidity', () => {
cy.wait('@feeTierDistributionQuery')
cy.get('#add-liquidity-selected-fee .selected-fee-label').should('contain.text', '0.3% fee tier')
cy.get('#add-liquidity-selected-fee .selected-fee-percentage').should('contain.text', '70%')
cy.get('#add-liquidity-selected-fee .selected-fee-percentage').should('contain.text', '40%')
})
})
})

View File

@@ -74,6 +74,7 @@ class CustomizedBridge extends Eip1193Bridge {
}
// sets up the injected provider to be a mock ethereum provider with the given mnemonic/index
// eslint-disable-next-line no-undef
Cypress.Commands.overwrite('visit', (original, url, options) => {
return original(url.startsWith('/') && url.length > 2 && !url.startsWith('/#') ? `/#${url}` : url, {
...options,

View File

@@ -1,4 +1,4 @@
export default {
const linguiConfig = {
catalogs: [
{
path: '<rootDir>/src/locales/{locale}',
@@ -52,3 +52,5 @@ export default {
runtimeConfigModule: ['@lingui/core', 'i18n'],
sourceLocale: 'en-US',
}
export default linguiConfig

View File

@@ -11,7 +11,6 @@
],
"private": true,
"devDependencies": {
"@davatar/react": "1.6.2",
"@ethersproject/experimental": "^5.4.0",
"@gnosis.pm/safe-apps-web3-react": "^0.6.0",
"@graphql-codegen/cli": "1.21.5",
@@ -21,6 +20,7 @@
"@lingui/cli": "^3.9.0",
"@lingui/macro": "^3.9.0",
"@lingui/react": "^3.9.0",
"@metamask/jazzicon": "^2.0.0",
"@popperjs/core": "^2.4.4",
"@reach/dialog": "^0.10.3",
"@reach/portal": "^0.10.3",
@@ -37,7 +37,6 @@
"@types/lingui__core": "^2.7.1",
"@types/lingui__macro": "^2.7.4",
"@types/lingui__react": "^2.8.3",
"@types/luxon": "^1.24.4",
"@types/ms.macro": "^2.0.0",
"@types/multicodec": "^1.0.0",
"@types/node": "^13.13.5",
@@ -55,24 +54,26 @@
"@types/wcag-contrast": "^3.0.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/redux-multicall": "^1.0.0",
"@uniswap/router-sdk": "^1.0.1",
"@uniswap/sdk-core": "^3.0.1",
"@uniswap/token-lists": "^1.0.0-beta.26",
"@uniswap/smart-order-router": "^2.5.4",
"@uniswap/token-lists": "^1.0.0-beta.27",
"@uniswap/v2-core": "1.0.0",
"@uniswap/v2-periphery": "^1.1.0-beta.0",
"@uniswap/v2-sdk": "^3.0.0-alpha.2",
"@uniswap/v2-sdk": "^3.0.1",
"@uniswap/v3-core": "1.0.0",
"@uniswap/v3-periphery": "^1.1.1",
"@uniswap/v3-sdk": "^3.4.1",
"@uniswap/v3-sdk": "^3.7.1",
"@web3-react/core": "^6.0.9",
"@web3-react/fortmatic-connector": "^6.0.9",
"@web3-react/injected-connector": "^6.0.7",
"@web3-react/portis-connector": "^6.0.9",
"@web3-react/walletconnect-connector": "^7.0.2-alpha.0",
"@web3-react/walletlink-connector": "^6.2.3",
"@web3-react/walletlink-connector": "^6.2.8",
"ajv": "^6.12.3",
"array.prototype.flat": "^1.2.4",
"array.prototype.flatmap": "^1.2.4",
@@ -87,12 +88,13 @@
"eslint-plugin-react": "^7.19.0",
"eslint-plugin-react-hooks": "^4.0.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"eslint-plugin-unused-imports": "^2.0.0",
"ethers": "^5.4.6",
"firebase": "^9.1.3",
"graphql": "^15.5.0",
"graphql-request": "^3.4.0",
"inter-ui": "^3.13.1",
"jest-styled-components": "^7.0.5",
"luxon": "^1.25.0",
"microbundle": "^0.13.3",
"ms.macro": "^2.0.0",
"multicodec": "^3.0.1",
@@ -127,6 +129,7 @@
"typescript": "^4.2.3",
"ua-parser-js": "^0.7.28",
"use-count-up": "^2.2.5",
"use-resize-observer": "^8.0.0",
"wcag-contrast": "^3.0.0",
"web-vitals": "^2.1.0",
"workbox-core": "^6.1.0",
@@ -137,8 +140,8 @@
"@walletconnect/ethereum-provider": "1.6.5"
},
"scripts": {
"contracts:compile:abi": "typechain --target ethers-v5 --out-dir src/abis/types './src/abis/**/*.json'",
"contracts:compile:v3": "typechain --target ethers-v5 --out-dir src/types/v3 './node_modules/@uniswap/?(v3-core|v3-periphery)/artifacts/contracts/**/*.json'",
"contracts:compile:abi": "typechain --target ethers-v5 --out-dir src/abis/types \"./src/abis/**/*.json\"",
"contracts:compile:v3": "typechain --target ethers-v5 --out-dir src/types/v3 \"./node_modules/@uniswap/**/artifacts/contracts/**/*.json\"",
"contracts:compile": "yarn contracts:compile:abi && yarn contracts:compile:v3",
"graphql:generate": "graphql-codegen --config codegen.yml",
"prei18n:extract": "touch src/locales/en-US.po",
@@ -152,12 +155,6 @@
"bundle": "microbundle --tsconfig tsconfig.lib.json src/lib/index.tsx --format esm,cjs",
"cosmos": "open http://localhost:5000 && cross-env FAST_REFRESH=false cosmos"
},
"eslintConfig": {
"extends": "react-app",
"ignorePatterns": [
"node_modules"
]
},
"browserslist": {
"production": [
">0.2%",

View File

@@ -67,6 +67,7 @@
html {
font-size: 16px;
font-variant: none;
font-smooth: always;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);

49
src/abis/erc1155.json Normal file
View File

@@ -0,0 +1,49 @@
[
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "_owner",
"type": "address"
},
{
"internalType": "uint256",
"name": "_id",
"type": "uint256"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "uint256",
"name": "_id",
"type": "uint256"
}
],
"name": "uri",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]

40
src/abis/erc721.json Normal file
View File

@@ -0,0 +1,40 @@
[
{
"inputs": [
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "ownerOf",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "tokenId",
"type": "uint256"
}
],
"name": "tokenURI",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
}
]

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.0047 9.26921H10.2714C11.0078 9.26921 11.6047 9.86617 11.6047 10.6025V12.1359C11.6047 12.7987 12.142 13.3359 12.8047 13.3359C13.4675 13.3359 14.0047 12.7995 14.0047 12.1367V5.22059C14.0047 4.86697 13.7758 4.56227 13.5258 4.31223L10.6714 1.33594M4.00472 2.00254H8.00472C8.7411 2.00254 9.33805 2.59949 9.33805 3.33587V14.0015H2.67139V3.33587C2.67139 2.59949 3.26834 2.00254 4.00472 2.00254ZM14.0047 5.33587C14.0047 6.07225 13.4078 6.66921 12.6714 6.66921C11.935 6.66921 11.3381 6.07225 11.3381 5.33587C11.3381 4.59949 11.935 4.00254 12.6714 4.00254C13.4078 4.00254 14.0047 4.59949 14.0047 5.33587Z" stroke="white"/>
<line x1="4" y1="9.99414" x2="8" y2="9.99414" stroke="white"/>
<line x1="4" y1="11.9941" x2="8" y2="11.9941" stroke="white"/>
<path d="M4 8.16113H8" stroke="white"/>
</svg>

After

Width:  |  Height:  |  Size: 895 B

View File

@@ -0,0 +1,12 @@
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_988_5781)">
<path d="M11.3333 12.5C7.33329 12.5 6.66663 8.5 3.99996 8.5M3.99996 8.5C6.66663 8.5 7.33329 4.5 11.3333 4.5M3.99996 8.5H1.66663" stroke="#888D9B" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="13.3334" cy="4.5" r="2" stroke="#888D9B" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="13.3334" cy="12.5" r="2" stroke="#888D9B" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_988_5781">
<rect width="16" height="16" fill="white" transform="translate(0 0.5)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 733 B

View File

@@ -0,0 +1,3 @@
<svg width="100%" height="35" viewBox="800 0 300 200" xmlns="http://www.w3.org/2000/svg">
<line x1="0" x2="2000" y1="100" y2="100" stroke="currentColor" stroke-width="20" stroke-linecap="round" stroke-dasharray="1, 45"/>
</svg>

After

Width:  |  Height:  |  Size: 233 B

View File

@@ -80,7 +80,7 @@ function ClaimSummary({ info: { recipient, uniAmountRaw } }: { info: ClaimTransa
)
}
function SubmitProposalTransactionSummary({}: { info: SubmitProposalTransactionInfo }) {
function SubmitProposalTransactionSummary(_: { info: SubmitProposalTransactionInfo }) {
return <Trans>Submit new proposal</Trans>
}
@@ -147,13 +147,13 @@ function WrapSummary({ info: { currencyAmountRaw, unwrapped } }: { info: WrapTra
}
}
function DepositLiquidityStakingSummary({}: { info: DepositLiquidityStakingTransactionInfo }) {
function DepositLiquidityStakingSummary(_: { info: DepositLiquidityStakingTransactionInfo }) {
// not worth rendering the tokens since you can should no longer deposit liquidity in the staking contracts
// todo: deprecate and delete the code paths that allow this, show user more information
return <Trans>Deposit liquidity</Trans>
}
function WithdrawLiquidityStakingSummary({}: { info: WithdrawLiquidityStakingTransactionInfo }) {
function WithdrawLiquidityStakingSummary(_: { info: WithdrawLiquidityStakingTransactionInfo }) {
return <Trans>Withdraw deposited liquidity</Trans>
}

View File

@@ -1,23 +1,20 @@
import { Trans } from '@lingui/macro'
import { AbstractConnector } from '@web3-react/abstract-connector'
import { useCallback, useContext } from 'react'
import { ExternalLink as LinkIcon } from 'react-feather'
import { useAppDispatch } from 'state/hooks'
import styled, { ThemeContext } from 'styled-components/macro'
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
import FortmaticIcon from '../../assets/images/fortmaticIcon.png'
import PortisIcon from '../../assets/images/portisIcon.png'
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
import { ReactComponent as Close } from '../../assets/images/x.svg'
import { fortmatic, injected, portis, walletconnect, walletlink } from '../../connectors'
import { injected, portis, walletlink } from '../../connectors'
import { SUPPORTED_WALLETS } from '../../constants/wallet'
import { useActiveWeb3React } from '../../hooks/web3'
import { clearAllTransactions } from '../../state/transactions/actions'
import { ExternalLink, LinkStyledButton, TYPE } from '../../theme'
import { ExternalLink, LinkStyledButton, ThemedText } from '../../theme'
import { shortenAddress } from '../../utils'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { ButtonSecondary } from '../Button'
import Identicon from '../Identicon'
import StatusIcon from '../Identicon/StatusIcon'
import { AutoRow } from '../Row'
import Copy from './Copy'
import Transaction from './Transaction'
@@ -179,6 +176,23 @@ const IconWrapper = styled.div<{ size?: number }>`
`};
`
function WrappedStatusIcon({ connector }: { connector: AbstractConnector }) {
return (
<IconWrapper size={16}>
<StatusIcon connector={connector} />
{connector === portis && (
<MainWalletAction
onClick={() => {
portis.portis.showPortis()
}}
>
<Trans>Show Portis</Trans>
</MainWalletAction>
)}
</IconWrapper>
)
}
const TransactionListWrapper = styled.div`
${({ theme }) => theme.flexColumnNoWrap};
`
@@ -244,50 +258,6 @@ export default function AccountDetails({
)
}
function getStatusIcon() {
if (connector === injected) {
return (
<IconWrapper size={16}>
<Identicon />
</IconWrapper>
)
} else if (connector === walletconnect) {
return (
<IconWrapper size={16}>
<img src={WalletConnectIcon} alt={'WalletConnect logo'} />
</IconWrapper>
)
} else if (connector === walletlink) {
return (
<IconWrapper size={16}>
<img src={CoinbaseWalletIcon} alt={'Coinbase Wallet logo'} />
</IconWrapper>
)
} else if (connector === fortmatic) {
return (
<IconWrapper size={16}>
<img src={FortmaticIcon} alt={'Fortmatic logo'} />
</IconWrapper>
)
} else if (connector === portis) {
return (
<>
<IconWrapper size={16}>
<img src={PortisIcon} alt={'Portis logo'} />
<MainWalletAction
onClick={() => {
portis.portis.showPortis()
}}
>
<Trans>Show Portis</Trans>
</MainWalletAction>
</IconWrapper>
</>
)
}
return null
}
const clearAllTransactionsCallback = useCallback(() => {
if (chainId) dispatch(clearAllTransactions({ chainId }))
}, [dispatch, chainId])
@@ -332,14 +302,14 @@ export default function AccountDetails({
{ENSName ? (
<>
<div>
{getStatusIcon()}
{connector && <WrappedStatusIcon connector={connector} />}
<p> {ENSName}</p>
</div>
</>
) : (
<>
<div>
{getStatusIcon()}
{connector && <WrappedStatusIcon connector={connector} />}
<p> {account && shortenAddress(account)}</p>
</div>
</>
@@ -408,9 +378,9 @@ export default function AccountDetails({
{!!pendingTransactions.length || !!confirmedTransactions.length ? (
<LowerSection>
<AutoRow mb={'1rem'} style={{ justifyContent: 'space-between' }}>
<TYPE.body>
<ThemedText.Body>
<Trans>Recent Transactions</Trans>
</TYPE.body>
</ThemedText.Body>
<LinkStyledButton onClick={clearAllTransactionsCallback}>
<Trans>(clear all)</Trans>
</LinkStyledButton>
@@ -420,9 +390,9 @@ export default function AccountDetails({
</LowerSection>
) : (
<LowerSection>
<TYPE.body color={theme.text1}>
<ThemedText.Body color={theme.text1}>
<Trans>Your transactions will appear here...</Trans>
</TYPE.body>
</ThemedText.Body>
</LowerSection>
)}
</>

View File

@@ -1,10 +1,12 @@
import { t, Trans } from '@lingui/macro'
import { Trans } from '@lingui/macro'
// eslint-disable-next-line no-restricted-imports
import { t } from '@lingui/macro'
import { ReactNode, useCallback, useContext } from 'react'
import styled, { ThemeContext } from 'styled-components/macro'
import useENS from '../../hooks/useENS'
import { useActiveWeb3React } from '../../hooks/web3'
import { ExternalLink, TYPE } from '../../theme'
import { ExternalLink, ThemedText } from '../../theme'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { AutoColumn } from '../Column'
import { RowBetween } from '../Row'
@@ -106,9 +108,9 @@ export default function AddressInputPanel({
<InputContainer>
<AutoColumn gap="md">
<RowBetween>
<TYPE.black color={theme.text2} fontWeight={500} fontSize={14}>
<ThemedText.Black color={theme.text2} fontWeight={500} fontSize={14}>
{label ?? <Trans>Recipient</Trans>}
</TYPE.black>
</ThemedText.Black>
{address && chainId && (
<ExternalLink
href={getExplorerLink(chainId, name ?? address, ExplorerDataType.ADDRESS)}

View File

@@ -0,0 +1,34 @@
import { animated, useSpring } from 'react-spring'
import useResizeObserver from 'use-resize-observer'
/**
* @param open conditional to show content or hide
* @returns Wrapper to smoothly hide and expand content
*/
export default function AnimatedDropdown({ open, children }: React.PropsWithChildren<{ open: boolean }>) {
const { ref, height } = useResizeObserver()
const props = useSpring({
height: open ? height ?? 0 : 0,
config: {
mass: 1.2,
tension: 300,
friction: 20,
clamp: true,
velocity: 0.01,
},
})
return (
<animated.div
style={{
...props,
overflow: 'hidden',
width: '100%',
willChange: 'height',
}}
>
<div ref={ref}>{children}</div>
</animated.div>
)
}

View File

@@ -33,6 +33,7 @@ export const BaseButton = styled(RebassButton)<
position: relative;
z-index: 1;
&:disabled {
opacity: 50%;
cursor: auto;
pointer-events: none;
}
@@ -236,7 +237,7 @@ const ButtonConfirmedStyle = styled(BaseButton)`
/* border: 1px solid ${({ theme }) => theme.green1}; */
&:disabled {
/* opacity: 50%; */
opacity: 50%;
background-color: ${({ theme }) => theme.bg2};
color: ${({ theme }) => theme.text2};
cursor: auto;
@@ -315,8 +316,8 @@ const ActiveOutlined = styled(ButtonOutlined)`
`
const Circle = styled.div`
height: 20px;
width: 20px;
height: 17px;
width: 17px;
border-radius: 50%;
background-color: ${({ theme }) => theme.primary1};
display: flex;
@@ -325,11 +326,11 @@ const Circle = styled.div`
`
const CheckboxWrapper = styled.div`
width: 30px;
width: 20px;
padding: 0 10px;
position: absolute;
top: 10px;
right: 10px;
top: 11px;
right: 15px;
`
const ResponsiveCheck = styled(Check)`

View File

@@ -1,11 +1,14 @@
import { Trans } from '@lingui/macro'
// eslint-disable-next-line no-restricted-imports
import { t } from '@lingui/macro'
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
import HoverInlineText from 'components/HoverInlineText'
import { useMemo } from 'react'
import useTheme from '../../hooks/useTheme'
import { TYPE } from '../../theme'
import { ThemedText } from '../../theme'
import { warningSeverity } from '../../utils/prices'
import { MouseoverTooltip } from '../Tooltip'
export function FiatValue({
fiatValue,
@@ -25,10 +28,14 @@ export function FiatValue({
}, [priceImpact, theme.green1, theme.red1, theme.text3, theme.yellow1])
return (
<TYPE.body fontSize={14} color={fiatValue ? theme.text2 : theme.text4}>
<ThemedText.Body fontSize={14} color={fiatValue ? theme.text3 : theme.text4}>
{fiatValue ? (
<Trans>
~$ <HoverInlineText text={fiatValue?.toSignificant(6, { groupSeparator: ',' })} />
$
<HoverInlineText
text={fiatValue?.toSignificant(6, { groupSeparator: ',' })}
textColor={fiatValue ? theme.text3 : theme.text4}
/>
</Trans>
) : (
''
@@ -36,9 +43,11 @@ export function FiatValue({
{priceImpact ? (
<span style={{ color: priceImpactColor }}>
{' '}
(<Trans>{priceImpact.multiply(-1).toSignificant(3)}%</Trans>)
<MouseoverTooltip text={t`The estimated difference between the USD values of input and output amounts.`}>
(<Trans>{priceImpact.multiply(-1).toSignificant(3)}%</Trans>)
</MouseoverTooltip>
</span>
) : null}
</TYPE.body>
</ThemedText.Body>
)
}

View File

@@ -13,7 +13,7 @@ import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
import useTheme from '../../hooks/useTheme'
import { useActiveWeb3React } from '../../hooks/web3'
import { useCurrencyBalance } from '../../state/wallet/hooks'
import { TYPE } from '../../theme'
import { ThemedText } from '../../theme'
import { ButtonGray } from '../Button'
import CurrencyLogo from '../CurrencyLogo'
import DoubleCurrencyLogo from '../DoubleLogo'
@@ -29,6 +29,8 @@ const InputPanel = styled.div<{ hideInput?: boolean }>`
background-color: ${({ theme, hideInput }) => (hideInput ? 'transparent' : theme.bg2)};
z-index: 1;
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
transition: height 1s ease;
will-change: height;
`
const FixedContainer = styled.div`
@@ -36,8 +38,7 @@ const FixedContainer = styled.div`
height: 100%;
position: absolute;
border-radius: 20px;
background-color: ${({ theme }) => theme.bg1};
opacity: 0.95;
background-color: ${({ theme }) => theme.bg2};
display: flex;
align-items: center;
justify-content: center;
@@ -46,7 +47,7 @@ const FixedContainer = styled.div`
const Container = styled.div<{ hideInput: boolean }>`
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
border: 1px solid ${({ theme, hideInput }) => (hideInput ? ' transparent' : theme.bg2)};
border: 1px solid ${({ theme }) => theme.bg0};
background-color: ${({ theme }) => theme.bg1};
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
:focus,
@@ -56,35 +57,35 @@ const Container = styled.div<{ hideInput: boolean }>`
`
const CurrencySelect = styled(ButtonGray)<{ visible: boolean; selected: boolean; hideInput?: boolean }>`
visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')};
align-items: center;
font-size: 24px;
font-weight: 500;
background-color: ${({ selected, theme }) => (selected ? theme.bg0 : theme.primary1)};
color: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)};
border-radius: 16px;
background-color: ${({ selected, theme }) => (selected ? theme.bg2 : theme.primary1)};
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;
color: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)};
cursor: pointer;
border-radius: 16px;
outline: none;
user-select: none;
border: none;
font-size: 24px;
font-weight: 500;
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')};
margin-left: ${({ hideInput }) => (hideInput ? '0' : '12px')};
:focus,
:hover {
background-color: ${({ selected, theme }) => (selected ? theme.bg2 : darken(0.05, theme.primary1))};
background-color: ${({ selected, theme }) => (selected ? theme.bg3 : darken(0.05, theme.primary1))};
}
visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')};
`
const InputRow = styled.div<{ selected: boolean }>`
${({ theme }) => theme.flexRowNoWrap}
align-items: center;
justify-content: space-between;
padding: ${({ selected }) => (selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 0.75rem 1rem')};
padding: ${({ selected }) => (selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 1rem 1rem')};
`
const LabelRow = styled.div`
@@ -128,28 +129,30 @@ const StyledTokenName = styled.span<{ active?: boolean }>`
const StyledBalanceMax = styled.button<{ disabled?: boolean }>`
background-color: transparent;
background-color: ${({ theme }) => theme.primary5};
border: none;
border-radius: 12px;
font-size: 14px;
font-weight: 500;
color: ${({ theme }) => theme.primary1};
cursor: pointer;
padding: 0;
color: ${({ theme }) => theme.primaryText1};
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')};
font-size: 11px;
font-weight: 500;
margin-left: 0.25rem;
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
padding: 4px 6px;
pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')};
:hover {
opacity: ${({ disabled }) => (!disabled ? 0.8 : 0.4)};
}
:focus {
outline: none;
}
${({ theme }) => theme.mediaWidth.upToExtraSmall`
margin-right: 0.5rem;
`};
`
const StyledNumericalInput = styled(NumericalInput)<{ $loading: boolean }>`
${loadingOpacityMixin}
${loadingOpacityMixin};
text-align: left;
`
interface CurrencyInputPanelProps {
@@ -212,14 +215,23 @@ export default function CurrencyInputPanel({
<FixedContainer>
<AutoColumn gap="sm" justify="center">
<Lock />
<TYPE.label fontSize="12px" textAlign="center" padding="0 12px">
<ThemedText.Label fontSize="12px" textAlign="center" padding="0 12px">
<Trans>The market price is outside your specified price range. Single-asset deposit only.</Trans>
</TYPE.label>
</ThemedText.Label>
</AutoColumn>
</FixedContainer>
)}
<Container hideInput={hideInput}>
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={!onCurrencySelect}>
{!hideInput && (
<StyledNumericalInput
className="token-amount-input"
value={value}
onUserInput={onUserInput}
$loading={loading}
/>
)}
<CurrencySelect
visible={currency !== undefined}
selected={!!currency}
@@ -257,24 +269,19 @@ export default function CurrencyInputPanel({
{onCurrencySelect && <StyledDropDown selected={!!currency} />}
</Aligner>
</CurrencySelect>
{!hideInput && (
<StyledNumericalInput
className="token-amount-input"
value={value}
onUserInput={onUserInput}
$loading={loading}
/>
)}
</InputRow>
{!hideInput && !hideBalance && (
{!hideInput && !hideBalance && currency && (
<FiatRow>
<RowBetween>
<LoadingOpacityContainer $loading={loading}>
<FiatValue fiatValue={fiatValue} priceImpact={priceImpact} />
</LoadingOpacityContainer>
{account ? (
<RowFixed style={{ height: '17px' }}>
<TYPE.body
<ThemedText.Body
onClick={onMax}
color={theme.text2}
fontWeight={400}
color={theme.text3}
fontWeight={500}
fontSize={14}
style={{ display: 'inline', cursor: 'pointer' }}
>
@@ -282,24 +289,19 @@ export default function CurrencyInputPanel({
renderBalance ? (
renderBalance(selectedCurrencyBalance)
) : (
<Trans>
Balance: {formatCurrencyAmount(selectedCurrencyBalance, 4)} {currency.symbol}
</Trans>
<Trans>Balance: {formatCurrencyAmount(selectedCurrencyBalance, 4)}</Trans>
)
) : null}
</TYPE.body>
</ThemedText.Body>
{showMaxButton && selectedCurrencyBalance ? (
<StyledBalanceMax onClick={onMax}>
<Trans>(Max)</Trans>
<Trans>MAX</Trans>
</StyledBalanceMax>
) : null}
</RowFixed>
) : (
<span />
)}
<LoadingOpacityContainer $loading={loading}>
<FiatValue fiatValue={fiatValue} priceImpact={priceImpact} />
</LoadingOpacityContainer>
</RowBetween>
</FiatRow>
)}

View File

@@ -37,16 +37,24 @@ export const getTokenLogoURL = (
const StyledEthereumLogo = styled.img<{ size: string }>`
width: ${({ size }) => size};
height: ${({ size }) => size};
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
border-radius: 24px;
background: radial-gradient(white 50%, #ffffff00 calc(75% + 1px), #ffffff00 100%);
border-radius: 50%;
-mox-box-shadow: 0 0 1px white;
-webkit-box-shadow: 0 0 1px white;
box-shadow: 0 0 1px white;
border: 0px solid rgba(255, 255, 255, 0);
`
const StyledLogo = styled(Logo)<{ size: string }>`
width: ${({ size }) => size};
height: ${({ size }) => size};
border-radius: ${({ size }) => size};
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
background-color: ${({ theme }) => theme.white};
background: radial-gradient(white 50%, #ffffff00 calc(75% + 1px), #ffffff00 100%);
border-radius: 50%;
-mox-box-shadow: 0 0 1px black;
-webkit-box-shadow: 0 0 1px black;
box-shadow: 0 0 1px black;
border: 0px solid rgba(255, 255, 255, 0);
`
export default function CurrencyLogo({

View File

@@ -4,7 +4,7 @@ import ReactGA from 'react-ga'
import styled from 'styled-components/macro'
import store, { AppState } from '../../state'
import { ExternalLink, TYPE } from '../../theme'
import { ExternalLink, ThemedText } from '../../theme'
import { userAgent } from '../../utils/userAgent'
import { AutoColumn } from '../Column'
import { AutoRow } from '../Row'
@@ -47,6 +47,8 @@ type ErrorBoundaryState = {
error: Error | null
}
const IS_UNISWAP = window.location.hostname === 'app.uniswap.org'
export default class ErrorBoundary extends React.Component<unknown, ErrorBoundaryState> {
constructor(props: unknown) {
super(props)
@@ -67,6 +69,7 @@ export default class ErrorBoundary extends React.Component<unknown, ErrorBoundar
render() {
const { error } = this.state
if (error !== null) {
const encodedBody = encodeURIComponent(issueBody(error))
return (
@@ -74,39 +77,41 @@ export default class ErrorBoundary extends React.Component<unknown, ErrorBoundar
<BodyWrapper>
<AutoColumn gap={'md'}>
<SomethingWentWrongWrapper>
<TYPE.label fontSize={24} fontWeight={600}>
<ThemedText.Label fontSize={24} fontWeight={600}>
<Trans>Something went wrong</Trans>
</TYPE.label>
</ThemedText.Label>
</SomethingWentWrongWrapper>
<CodeBlockWrapper>
<code>
<TYPE.main fontSize={10}>{error.stack}</TYPE.main>
<ThemedText.Main fontSize={10}>{error.stack}</ThemedText.Main>
</code>
</CodeBlockWrapper>
<AutoRow>
<LinkWrapper>
<ExternalLink
id="create-github-issue-link"
href={`https://github.com/Uniswap/uniswap-interface/issues/new?assignees=&labels=bug&body=${encodedBody}&title=${encodeURIComponent(
`Crash report: \`${error.name}${error.message && `: ${error.message}`}\``
)}`}
target="_blank"
>
<TYPE.link fontSize={16}>
<Trans>Create an issue on GitHub</Trans>
<span></span>
</TYPE.link>
</ExternalLink>
</LinkWrapper>
<LinkWrapper>
<ExternalLink id="get-support-on-discord" href="https://discord.gg/FCfyBSbCU5" target="_blank">
<TYPE.link fontSize={16}>
<Trans>Get support on Discord</Trans>
<span></span>
</TYPE.link>
</ExternalLink>
</LinkWrapper>
</AutoRow>
{IS_UNISWAP ? (
<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"
>
<ThemedText.Link fontSize={16}>
<Trans>Create an issue on GitHub</Trans>
<span></span>
</ThemedText.Link>
</ExternalLink>
</LinkWrapper>
<LinkWrapper>
<ExternalLink id="get-support-on-discord" href="https://discord.gg/FCfyBSbCU5" target="_blank">
<ThemedText.Link fontSize={16}>
<Trans>Get support on Discord</Trans>
<span></span>
</ThemedText.Link>
</ExternalLink>
</LinkWrapper>
</AutoRow>
) : null}
</AutoColumn>
</BodyWrapper>
</FallbackWrapper>
@@ -121,7 +126,7 @@ function getRelevantState(): null | keyof AppState {
if (!path.startsWith('#/')) {
return null
}
const pieces = path.substring(2).split(/[\/\\?]/)
const pieces = path.substring(2).split(/[/\\?]/)
switch (pieces[0]) {
case 'swap':
return 'swap'

View File

@@ -0,0 +1,51 @@
import { Trans } from '@lingui/macro'
import { FeeAmount } from '@uniswap/v3-sdk'
import { ButtonRadioChecked } from 'components/Button'
import { AutoColumn } from 'components/Column'
import { useFeeTierDistribution } from 'hooks/useFeeTierDistribution'
import { PoolState } from 'hooks/usePools'
import React from 'react'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { FeeTierPercentageBadge } from './FeeTierPercentageBadge'
import { FEE_AMOUNT_DETAIL } from './shared'
const ResponsiveText = styled(ThemedText.Label)`
line-height: 16px;
font-size: 14px;
${({ theme }) => theme.mediaWidth.upToSmall`
font-size: 12px;
line-height: 12px;
`};
`
interface FeeOptionProps {
feeAmount: FeeAmount
active: boolean
distributions: ReturnType<typeof useFeeTierDistribution>['distributions']
poolState: PoolState
onClick: () => void
}
export function FeeOption({ feeAmount, active, poolState, distributions, onClick }: FeeOptionProps) {
return (
<ButtonRadioChecked active={active} onClick={onClick}>
<AutoColumn gap="sm" justify="flex-start">
<AutoColumn justify="flex-start" gap="6px">
<ResponsiveText>
<Trans>{FEE_AMOUNT_DETAIL[feeAmount].label}%</Trans>
</ResponsiveText>
<ThemedText.Main fontWeight={400} fontSize="12px" textAlign="left">
{FEE_AMOUNT_DETAIL[feeAmount].description}
</ThemedText.Main>
</AutoColumn>
{distributions && (
<FeeTierPercentageBadge distributions={distributions} feeAmount={feeAmount} poolState={poolState} />
)}
</AutoColumn>
</ButtonRadioChecked>
)
}

View File

@@ -0,0 +1,31 @@
import { Trans } from '@lingui/macro'
import { FeeAmount } from '@uniswap/v3-sdk'
import Badge from 'components/Badge'
import { useFeeTierDistribution } from 'hooks/useFeeTierDistribution'
import { PoolState } from 'hooks/usePools'
import React from 'react'
import { ThemedText } from 'theme'
export function FeeTierPercentageBadge({
feeAmount,
distributions,
poolState,
}: {
feeAmount: FeeAmount
distributions: ReturnType<typeof useFeeTierDistribution>['distributions']
poolState: PoolState
}) {
return (
<Badge>
<ThemedText.Label fontSize={10}>
{!distributions || poolState === PoolState.NOT_EXISTS || poolState === PoolState.INVALID ? (
<Trans>Not created</Trans>
) : distributions[feeAmount] !== undefined ? (
<Trans>{distributions[feeAmount]?.toFixed(0)}% select</Trans>
) : (
<Trans>No data</Trans>
)}
</ThemedText.Label>
</Badge>
)
}

View File

@@ -1,19 +1,24 @@
import { Trans } from '@lingui/macro'
import { Currency } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk'
import Badge from 'components/Badge'
import { ButtonGray, ButtonRadioChecked } from 'components/Button'
import { ButtonGray } from 'components/Button'
import Card from 'components/Card'
import { AutoColumn } from 'components/Column'
import { RowBetween } from 'components/Row'
import { useFeeTierDistribution } from 'hooks/useFeeTierDistribution'
import { PoolState, usePools } from 'hooks/usePools'
import usePrevious from 'hooks/usePrevious'
import { useActiveWeb3React } from 'hooks/web3'
import { DynamicSection } from 'pages/AddLiquidity/styled'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import ReactGA from 'react-ga'
import { Box } from 'rebass'
import styled, { keyframes } from 'styled-components/macro'
import { TYPE } from 'theme'
import { ThemedText } from 'theme'
import { FeeOption } from './FeeOption'
import { FeeTierPercentageBadge } from './FeeTierPercentageBadge'
import { FEE_AMOUNT_DETAIL } from './shared'
const pulse = (color: string) => keyframes`
0% {
@@ -28,45 +33,18 @@ const pulse = (color: string) => keyframes`
box-shadow: 0 0 0 0 ${color};
}
`
const ResponsiveText = styled(TYPE.label)`
line-height: 16px;
${({ theme }) => theme.mediaWidth.upToSmall`
font-size: 12px;
line-height: 12px;
`};
`
const FocusedOutlineCard = styled(Card)<{ pulsing: boolean }>`
border: 1px solid ${({ theme }) => theme.bg2};
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.primary1)} 0.6s linear;
align-self: center;
`
const FeeAmountLabel = {
[FeeAmount.LOW]: {
label: '0.05',
description: <Trans>Best for stable pairs.</Trans>,
},
[FeeAmount.MEDIUM]: {
label: '0.3',
description: <Trans>Best for most pairs.</Trans>,
},
[FeeAmount.HIGH]: {
label: '1',
description: <Trans>Best for exotic pairs.</Trans>,
},
}
const FeeTierPercentageBadge = ({ percentage }: { percentage: number | undefined }) => {
return (
<Badge>
<TYPE.label fontSize={12}>
{percentage !== undefined ? <Trans>{percentage?.toFixed(0)}% select</Trans> : <Trans>Not created</Trans>}
</TYPE.label>
</Badge>
)
}
const Select = styled.div`
align-items: flex-start;
display: grid;
grid-auto-flow: column;
grid-gap: 8px;
`
export default function FeeSelector({
disabled = false,
@@ -81,8 +59,39 @@ export default function FeeSelector({
currencyA?: Currency | undefined
currencyB?: Currency | undefined
}) {
const { chainId } = useActiveWeb3React()
const { isLoading, isError, largestUsageFeeTier, distributions } = useFeeTierDistribution(currencyA, currencyB)
// get pool data on-chain for latest states
const pools = usePools([
[currencyA, currencyB, FeeAmount.LOWEST],
[currencyA, currencyB, FeeAmount.LOW],
[currencyA, currencyB, FeeAmount.MEDIUM],
[currencyA, currencyB, FeeAmount.HIGH],
])
const poolsByFeeTier: Record<FeeAmount, PoolState> = useMemo(
() =>
pools.reduce(
(acc, [curPoolState, curPool]) => {
acc = {
...acc,
...{ [curPool?.fee as FeeAmount]: curPoolState },
}
return acc
},
{
// default all states to NOT_EXISTS
[FeeAmount.LOWEST]: PoolState.NOT_EXISTS,
[FeeAmount.LOW]: PoolState.NOT_EXISTS,
[FeeAmount.MEDIUM]: PoolState.NOT_EXISTS,
[FeeAmount.HIGH]: PoolState.NOT_EXISTS,
}
),
[pools]
)
const [showOptions, setShowOptions] = useState(false)
const [pulsing, setPulsing] = useState(false)
@@ -91,7 +100,7 @@ export default function FeeSelector({
const recommended = useRef(false)
const handleFeePoolSelectWithEvent = useCallback(
(fee) => {
(fee: FeeAmount) => {
ReactGA.event({
category: 'FeePoolSelect',
action: 'Manual',
@@ -140,20 +149,26 @@ export default function FeeSelector({
<AutoColumn id="add-liquidity-selected-fee">
{!feeAmount ? (
<>
<TYPE.label>
<ThemedText.Label>
<Trans>Fee tier</Trans>
</TYPE.label>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
</ThemedText.Label>
<ThemedText.Main fontWeight={400} fontSize="12px" textAlign="left">
<Trans>The % you will earn in fees.</Trans>
</TYPE.main>
</ThemedText.Main>
</>
) : (
<>
<TYPE.label className="selected-fee-label">
<Trans>{FeeAmountLabel[feeAmount].label}% fee tier</Trans>
</TYPE.label>
<ThemedText.Label className="selected-fee-label">
<Trans>{FEE_AMOUNT_DETAIL[feeAmount].label}% fee tier</Trans>
</ThemedText.Label>
<Box style={{ width: 'fit-content', marginTop: '8px' }} className="selected-fee-percentage">
{distributions && feeAmount && <FeeTierPercentageBadge percentage={distributions[feeAmount]} />}
{distributions && (
<FeeTierPercentageBadge
distributions={distributions}
feeAmount={feeAmount}
poolState={poolsByFeeTier[feeAmount]}
/>
)}
</Box>
</>
)}
@@ -165,63 +180,25 @@ export default function FeeSelector({
</RowBetween>
</FocusedOutlineCard>
{showOptions && (
<RowBetween>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.LOW}
onClick={() => handleFeePoolSelectWithEvent(FeeAmount.LOW)}
>
<AutoColumn gap="sm" justify="flex-start">
<AutoColumn justify="flex-start" gap="6px">
<ResponsiveText>
<Trans>0.05% fee</Trans>
</ResponsiveText>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
<Trans>Best for stable pairs.</Trans>
</TYPE.main>
</AutoColumn>
{distributions && <FeeTierPercentageBadge percentage={distributions[FeeAmount.LOW]} />}
</AutoColumn>
</ButtonRadioChecked>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.MEDIUM}
onClick={() => handleFeePoolSelectWithEvent(FeeAmount.MEDIUM)}
>
<AutoColumn gap="sm" justify="flex-start">
<AutoColumn justify="flex-start" gap="4px">
<ResponsiveText>
<Trans>0.3% fee</Trans>
</ResponsiveText>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
<Trans>Best for most pairs.</Trans>
</TYPE.main>
</AutoColumn>
{distributions && <FeeTierPercentageBadge percentage={distributions[FeeAmount.MEDIUM]} />}
</AutoColumn>
</ButtonRadioChecked>
<ButtonRadioChecked
width="32%"
active={feeAmount === FeeAmount.HIGH}
onClick={() => handleFeePoolSelectWithEvent(FeeAmount.HIGH)}
>
<AutoColumn gap="sm" justify="flex-start">
<AutoColumn justify="flex-start" gap="4px">
<ResponsiveText>
<Trans>1% fee</Trans>
</ResponsiveText>
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
<Trans>Best for exotic pairs.</Trans>
</TYPE.main>
</AutoColumn>
{distributions && <FeeTierPercentageBadge percentage={distributions[FeeAmount.HIGH]} />}
</AutoColumn>
</ButtonRadioChecked>
</RowBetween>
{chainId && showOptions && (
<Select>
{[FeeAmount.LOWEST, FeeAmount.LOW, FeeAmount.MEDIUM, FeeAmount.HIGH].map((_feeAmount, i) => {
const { supportedChains } = FEE_AMOUNT_DETAIL[_feeAmount]
if (supportedChains.includes(chainId)) {
return (
<FeeOption
feeAmount={_feeAmount}
active={feeAmount === _feeAmount}
onClick={() => handleFeePoolSelectWithEvent(_feeAmount)}
distributions={distributions}
poolState={poolsByFeeTier[_feeAmount]}
key={i}
/>
)
}
return null
})}
</Select>
)}
</DynamicSection>
</AutoColumn>

View File

@@ -0,0 +1,30 @@
import { Trans } from '@lingui/macro'
import { FeeAmount } from '@uniswap/v3-sdk'
import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from 'constants/chains'
import { ReactNode } from 'react'
export const FEE_AMOUNT_DETAIL: Record<
FeeAmount,
{ label: string; description: ReactNode; supportedChains: SupportedChainId[] }
> = {
[FeeAmount.LOWEST]: {
label: '0.01',
description: <Trans>Best for very stable pairs.</Trans>,
supportedChains: [SupportedChainId.MAINNET],
},
[FeeAmount.LOW]: {
label: '0.05',
description: <Trans>Best for stable pairs.</Trans>,
supportedChains: ALL_SUPPORTED_CHAIN_IDS,
},
[FeeAmount.MEDIUM]: {
label: '0.3',
description: <Trans>Best for most pairs.</Trans>,
supportedChains: ALL_SUPPORTED_CHAIN_IDS,
},
[FeeAmount.HIGH]: {
label: '1',
description: <Trans>Best for exotic pairs.</Trans>,
supportedChains: ALL_SUPPORTED_CHAIN_IDS,
},
}

View File

@@ -11,8 +11,8 @@ import { useOnClickOutside } from 'hooks/useOnClickOutside'
import { useActiveWeb3React } from 'hooks/web3'
import { useCallback, useRef } from 'react'
import { ArrowDownCircle, ChevronDown } from 'react-feather'
import { ApplicationModal } from 'state/application/actions'
import { useModalOpen, useToggleModal } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import { useAppSelector } from 'state/hooks'
import styled from 'styled-components/macro'
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
@@ -34,17 +34,16 @@ const ActiveRowLinkList = styled.div`
text-decoration: none;
}
& > a:first-child {
border-top: 1px solid ${({ theme }) => theme.text2};
margin: 0;
margin-top: 6px;
margin-top: 0px;
padding-top: 10px;
}
`
const ActiveRowWrapper = styled.div`
background-color: ${({ theme }) => theme.bg2};
background-color: ${({ theme }) => theme.bg1};
border-radius: 8px;
cursor: pointer;
padding: 8px 0 8px 0;
padding: 8px;
width: 100%;
`
const FlyoutHeader = styled.div`
@@ -53,7 +52,7 @@ const FlyoutHeader = styled.div`
`
const FlyoutMenu = styled.div`
align-items: flex-start;
background-color: ${({ theme }) => theme.bg1};
background-color: ${({ theme }) => theme.bg0};
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
0px 24px 32px rgba(0, 0, 0, 0.01);
border-radius: 20px;
@@ -75,7 +74,7 @@ const FlyoutMenu = styled.div`
`
const FlyoutRow = styled.div<{ active: boolean }>`
align-items: center;
background-color: ${({ active, theme }) => (active ? theme.bg2 : 'transparent')};
background-color: ${({ active, theme }) => (active ? theme.bg1 : 'transparent')};
border-radius: 8px;
cursor: pointer;
display: flex;
@@ -113,8 +112,8 @@ const SelectorLabel = styled(NetworkLabel)`
`
const SelectorControls = styled.div<{ interactive: boolean }>`
align-items: center;
background-color: ${({ theme }) => theme.bg1};
border: 2px solid ${({ theme }) => theme.bg1};
background-color: ${({ theme }) => theme.bg0};
border: 2px solid ${({ theme }) => theme.bg0};
border-radius: 12px;
color: ${({ theme }) => theme.text1};
cursor: ${({ interactive }) => (interactive ? 'pointer' : 'auto')};

View File

@@ -1,11 +1,20 @@
import { Trans } from '@lingui/macro'
import { RowFixed } from 'components/Row'
import { CHAIN_INFO } from 'constants/chains'
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
import useGasPrice from 'hooks/useGasPrice'
import useMachineTimeMs from 'hooks/useMachineTime'
import useTheme from 'hooks/useTheme'
import { useActiveWeb3React } from 'hooks/web3'
import JSBI from 'jsbi'
import ms from 'ms.macro'
import { useEffect, useState } from 'react'
import { useAppSelector } from 'state/hooks'
import { useBlockNumber } from 'state/application/hooks'
import styled, { keyframes } from 'styled-components/macro'
import { ExternalLink, ThemedText } from 'theme'
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
import { useActiveWeb3React } from '../../hooks/web3'
import { useBlockNumber } from '../../state/application/hooks'
import { ExternalLink, TYPE } from '../../theme'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { MouseoverTooltip } from '../Tooltip'
import { ChainConnectivityWarning } from './ChainConnectivityWarning'
const StyledPolling = styled.div<{ warning: boolean }>`
@@ -22,12 +31,20 @@ const StyledPolling = styled.div<{ warning: boolean }>`
display: none;
`}
`
const StyledPollingNumber = styled(TYPE.small)<{ breathe: boolean; hovering: boolean }>`
const StyledPollingNumber = styled(ThemedText.Small)<{ breathe: boolean; hovering: boolean }>`
transition: opacity 0.25s ease;
opacity: ${({ breathe, hovering }) => (hovering ? 0.7 : breathe ? 1 : 0.5)};
:hover {
opacity: 1;
}
a {
color: unset;
}
a:hover {
text-decoration: none;
color: unset;
}
`
const StyledPollingDot = styled.div<{ warning: boolean }>`
width: 8px;
@@ -40,6 +57,17 @@ const StyledPollingDot = styled.div<{ warning: boolean }>`
transition: 250ms ease background-color;
`
const StyledGasDot = styled.div`
background-color: ${({ theme }) => theme.text3};
border-radius: 50%;
height: 4px;
min-height: 4px;
min-width: 4px;
position: relative;
transition: 250ms ease background-color;
width: 4px;
`
const rotate360 = keyframes`
from {
transform: rotate(0deg);
@@ -68,12 +96,25 @@ const Spinner = styled.div<{ warning: boolean }>`
top: -3px;
`
const DEFAULT_MS_BEFORE_WARNING = ms`10m`
const NETWORK_HEALTH_CHECK_MS = ms`10s`
export default function Polling() {
const { chainId } = useActiveWeb3React()
const blockNumber = useBlockNumber()
const [isMounting, setIsMounting] = useState(false)
const [isHover, setIsHover] = useState(false)
const chainConnectivityWarning = useAppSelector((state) => state.application.chainConnectivityWarning)
const machineTime = useMachineTimeMs(NETWORK_HEALTH_CHECK_MS)
const blockTime = useCurrentBlockTimestamp()
const theme = useTheme()
const ethGasPrice = useGasPrice()
const priceGwei = ethGasPrice ? JSBI.divide(ethGasPrice, JSBI.BigInt(1000000000)) : undefined
const waitMsBeforeWarning =
(chainId ? CHAIN_INFO[chainId]?.blockWaitMsBeforeWarning : DEFAULT_MS_BEFORE_WARNING) ?? DEFAULT_MS_BEFORE_WARNING
const warning = Boolean(!!blockTime && machineTime - blockTime.mul(1000).toNumber() > waitMsBeforeWarning)
useEffect(
() => {
@@ -93,25 +134,48 @@ export default function Polling() {
//if you pass a value to array, like this [data] than clearTimeout will run every time this value changes (useEffect re-run)
)
//TODO - chainlink gas oracle is really slow. Can we get a better data source?
return (
<>
<ExternalLink
href={chainId && blockNumber ? getExplorerLink(chainId, blockNumber.toString(), ExplorerDataType.BLOCK) : ''}
>
<StyledPolling
onMouseEnter={() => setIsHover(true)}
onMouseLeave={() => setIsHover(false)}
warning={chainConnectivityWarning}
>
<RowFixed>
<StyledPolling onMouseEnter={() => setIsHover(true)} onMouseLeave={() => setIsHover(false)} warning={warning}>
<ExternalLink href={'https://etherscan.io/gastracker'}>
{priceGwei ? (
<RowFixed style={{ marginRight: '8px' }}>
<ThemedText.Main fontSize="11px" mr="8px" color={theme.text3}>
<MouseoverTooltip
text={
<Trans>
{`The current fast gas amount for sending a transaction on L1.
Gas fees are paid in Ethereum's native currency Ether (ETH) and denominated in gwei. `}
</Trans>
}
>
{priceGwei.toString()} <Trans>gwei</Trans>
</MouseoverTooltip>
</ThemedText.Main>
<StyledGasDot />
</RowFixed>
) : null}
</ExternalLink>
<StyledPollingNumber breathe={isMounting} hovering={isHover}>
{blockNumber}&ensp;
<ExternalLink
href={
chainId && blockNumber ? getExplorerLink(chainId, blockNumber.toString(), ExplorerDataType.BLOCK) : ''
}
>
<MouseoverTooltip
text={<Trans>{`The most recent block number on this network. Prices update on every block.`}</Trans>}
>
{blockNumber}&ensp;
</MouseoverTooltip>
</ExternalLink>
</StyledPollingNumber>
<StyledPollingDot warning={chainConnectivityWarning}>
{isMounting && <Spinner warning={chainConnectivityWarning} />}
</StyledPollingDot>{' '}
<StyledPollingDot warning={warning}>{isMounting && <Spinner warning={warning} />}</StyledPollingDot>{' '}
</StyledPolling>
</ExternalLink>
{chainConnectivityWarning && <ChainConnectivityWarning />}
{warning && <ChainConnectivityWarning />}
</RowFixed>
</>
)
}

View File

@@ -14,7 +14,7 @@ import useUSDCPrice from '../../hooks/useUSDCPrice'
import { useActiveWeb3React } from '../../hooks/web3'
import { useTotalUniEarned } from '../../state/stake/hooks'
import { useAggregateUniBalance, useTokenBalance } from '../../state/wallet/hooks'
import { ExternalLink, StyledInternalLink, TYPE, UniTokenAnimated } from '../../theme'
import { ExternalLink, StyledInternalLink, ThemedText, UniTokenAnimated } from '../../theme'
import { computeUniCirculation } from '../../utils/computeUniCirculation'
import { AutoColumn } from '../Column'
import { Break, CardBGImage, CardNoise, CardSection, DataCard } from '../earn/styled'
@@ -70,9 +70,9 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
<CardNoise />
<CardSection gap="md">
<RowBetween>
<TYPE.white color="white">
<ThemedText.White color="white">
<Trans>Your UNI Breakdown</Trans>
</TYPE.white>
</ThemedText.White>
<StyledClose stroke="white" onClick={() => setShowUniBalanceModal(false)} />
</RowBetween>
</CardSection>
@@ -82,29 +82,29 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
<CardSection gap="sm">
<AutoColumn gap="md" justify="center">
<UniTokenAnimated width="48px" src={tokenLogo} />{' '}
<TYPE.white fontSize={48} fontWeight={600} color="white">
<ThemedText.White fontSize={48} fontWeight={600} color="white">
{total?.toFixed(2, { groupSeparator: ',' })}
</TYPE.white>
</ThemedText.White>
</AutoColumn>
<AutoColumn gap="md">
<RowBetween>
<TYPE.white color="white">
<ThemedText.White color="white">
<Trans>Balance:</Trans>
</TYPE.white>
<TYPE.white color="white">{uniBalance?.toFixed(2, { groupSeparator: ',' })}</TYPE.white>
</ThemedText.White>
<ThemedText.White color="white">{uniBalance?.toFixed(2, { groupSeparator: ',' })}</ThemedText.White>
</RowBetween>
<RowBetween>
<TYPE.white color="white">
<ThemedText.White color="white">
<Trans>Unclaimed:</Trans>
</TYPE.white>
<TYPE.white color="white">
</ThemedText.White>
<ThemedText.White color="white">
{uniToClaim?.toFixed(4, { groupSeparator: ',' })}{' '}
{uniToClaim && uniToClaim.greaterThan('0') && (
<StyledInternalLink onClick={() => setShowUniBalanceModal(false)} to="/uni">
<Trans>(claim)</Trans>
</StyledInternalLink>
)}
</TYPE.white>
</ThemedText.White>
</RowBetween>
</AutoColumn>
</CardSection>
@@ -114,22 +114,22 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
<CardSection gap="sm">
<AutoColumn gap="md">
<RowBetween>
<TYPE.white color="white">
<ThemedText.White color="white">
<Trans>UNI price:</Trans>
</TYPE.white>
<TYPE.white color="white">${uniPrice?.toFixed(2) ?? '-'}</TYPE.white>
</ThemedText.White>
<ThemedText.White color="white">${uniPrice?.toFixed(2) ?? '-'}</ThemedText.White>
</RowBetween>
<RowBetween>
<TYPE.white color="white">
<ThemedText.White color="white">
<Trans>UNI in circulation:</Trans>
</TYPE.white>
<TYPE.white color="white">{circulation?.toFixed(0, { groupSeparator: ',' })}</TYPE.white>
</ThemedText.White>
<ThemedText.White color="white">{circulation?.toFixed(0, { groupSeparator: ',' })}</ThemedText.White>
</RowBetween>
<RowBetween>
<TYPE.white color="white">
<ThemedText.White color="white">
<Trans>Total Supply</Trans>
</TYPE.white>
<TYPE.white color="white">{totalSupply?.toFixed(0, { groupSeparator: ',' })}</TYPE.white>
</ThemedText.White>
<ThemedText.White color="white">{totalSupply?.toFixed(0, { groupSeparator: ',' })}</ThemedText.White>
</RowBetween>
{uni && uni.chainId === 1 ? (
<ExternalLink href={`${infoLink}/token/${uni.address}`}>

View File

@@ -15,7 +15,7 @@ import styled from 'styled-components/macro'
import { ReactComponent as Logo } from '../../assets/svg/logo.svg'
import { useActiveWeb3React } from '../../hooks/web3'
import { ExternalLink, TYPE } from '../../theme'
import { ExternalLink, ThemedText } from '../../theme'
import ClaimModal from '../claim/ClaimModal'
import { CardNoise } from '../earn/styled'
import Menu from '../Menu'
@@ -309,7 +309,7 @@ export default function Header() {
{availableClaim && !showClaimPopup && (
<UNIWrapper onClick={toggleClaimModal}>
<UNIAmount active={!!account && !availableClaim} style={{ pointerEvents: 'auto' }}>
<TYPE.white padding="0 2px">
<ThemedText.White padding="0 2px">
{claimTxn && !claimTxn?.receipt ? (
<Dots>
<Trans>Claiming UNI</Trans>
@@ -317,7 +317,7 @@ export default function Header() {
) : (
<Trans>Claim UNI</Trans>
)}
</TYPE.white>
</ThemedText.White>
</UNIAmount>
<CardNoise />
</UNIWrapper>

View File

@@ -2,9 +2,15 @@ import Tooltip from 'components/Tooltip'
import { useState } from 'react'
import styled from 'styled-components/macro'
const TextWrapper = styled.span<{ margin: boolean; link?: boolean; fontSize?: string; adjustSize?: boolean }>`
const TextWrapper = styled.span<{
margin: boolean
link?: boolean
fontSize?: string
adjustSize?: boolean
textColor?: string
}>`
margin-left: ${({ margin }) => margin && '4px'};
color: ${({ theme, link }) => (link ? theme.blue1 : theme.text1)};
color: ${({ theme, link, textColor }) => (link ? theme.blue1 : textColor ?? theme.text1)};
font-size: ${({ fontSize }) => fontSize ?? 'inherit'};
@media screen and (max-width: 600px) {
@@ -18,6 +24,7 @@ const HoverInlineText = ({
margin = false,
adjustSize = false,
fontSize,
textColor,
link,
...rest
}: {
@@ -26,6 +33,7 @@ const HoverInlineText = ({
margin?: boolean
adjustSize?: boolean
fontSize?: string
textColor?: string
link?: boolean
}) => {
const [showHover, setShowHover] = useState(false)
@@ -42,6 +50,7 @@ const HoverInlineText = ({
onMouseLeave={() => setShowHover(false)}
margin={margin}
adjustSize={adjustSize}
textColor={textColor}
link={link}
fontSize={fontSize}
{...rest}
@@ -53,7 +62,14 @@ const HoverInlineText = ({
}
return (
<TextWrapper margin={margin} adjustSize={adjustSize} link={link} fontSize={fontSize} {...rest}>
<TextWrapper
margin={margin}
adjustSize={adjustSize}
link={link}
fontSize={fontSize}
textColor={textColor}
{...rest}
>
{text}
</TextWrapper>
)

View File

@@ -0,0 +1,25 @@
import { AbstractConnector } from '@web3-react/abstract-connector'
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
import FortmaticIcon from '../../assets/images/fortmaticIcon.png'
import PortisIcon from '../../assets/images/portisIcon.png'
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
import { fortmatic, injected, portis, walletconnect, walletlink } from '../../connectors'
import Identicon from '../Identicon'
export default function StatusIcon({ connector }: { connector: AbstractConnector }) {
switch (connector) {
case injected:
return <Identicon />
case walletconnect:
return <img src={WalletConnectIcon} alt={'WalletConnect'} />
case walletlink:
return <img src={CoinbaseWalletIcon} alt={'Coinbase Wallet'} />
case fortmatic:
return <img src={FortmaticIcon} alt={'Fortmatic'} />
case portis:
return <img src={PortisIcon} alt={'Portis'} />
default:
return null
}
}

View File

@@ -1,22 +1,53 @@
import Davatar from '@davatar/react'
import jazzicon from '@metamask/jazzicon'
import useENSAvatar from 'hooks/useENSAvatar'
import { useLayoutEffect, useMemo, useRef, useState } from 'react'
import styled from 'styled-components/macro'
import { useActiveWeb3React } from '../../hooks/web3'
const StyledIdenticonContainer = styled.div`
const StyledIdenticon = styled.div`
height: 1rem;
width: 1rem;
border-radius: 1.125rem;
background-color: ${({ theme }) => theme.bg4};
font-size: initial;
`
const StyledAvatar = styled.img`
height: inherit;
width: inherit;
border-radius: inherit;
`
export default function Identicon() {
const { account, library } = useActiveWeb3React()
const { account } = useActiveWeb3React()
const { avatar } = useENSAvatar(account ?? undefined)
const [fetchable, setFetchable] = useState(true)
const icon = useMemo(() => account && jazzicon(16, parseInt(account.slice(2, 10), 16)), [account])
const iconRef = useRef<HTMLDivElement>(null)
useLayoutEffect(() => {
const current = iconRef.current
if (icon) {
current?.appendChild(icon)
return () => {
try {
current?.removeChild(icon)
} catch (e) {
console.error('Avatar icon not found')
}
}
}
return
}, [icon, iconRef])
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451
return (
<StyledIdenticonContainer>
{account && library?.provider && <Davatar address={account} size={16} provider={library.provider} />}
</StyledIdenticonContainer>
<StyledIdenticon>
{avatar && fetchable ? (
<StyledAvatar alt="avatar" src={avatar} onError={() => setFetchable(false)}></StyledAvatar>
) : (
<span ref={iconRef} />
)}
</StyledIdenticon>
)
}

View File

@@ -6,7 +6,7 @@ import { AutoColumn } from 'components/Column'
import { ReactNode, useCallback, useEffect, useState } from 'react'
import { Minus, Plus } from 'react-feather'
import styled, { keyframes } from 'styled-components/macro'
import { TYPE } from 'theme'
import { ThemedText } from 'theme'
import { Input as NumericalInput } from '../NumericalInput'
@@ -57,13 +57,13 @@ const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
`};
`
const InputTitle = styled(TYPE.small)`
const InputTitle = styled(ThemedText.Small)`
color: ${({ theme }) => theme.text2};
font-size: 12px;
font-weight: 500;
`
const ButtonLabel = styled(TYPE.white)<{ disabled: boolean }>`
const ButtonLabel = styled(ThemedText.White)<{ disabled: boolean }>`
color: ${({ theme, disabled }) => (disabled ? theme.text2 : theme.text1)} !important;
`

View File

@@ -37,7 +37,7 @@ export const Area = ({
.y0(yScale(0))(
series.filter((d) => {
const value = xScale(xValue(d))
return value > 0 && value <= innerWidth
return value > 0 && value <= window.innerWidth
}) as Iterable<[number, number]>
) ?? undefined
}

View File

@@ -14,12 +14,18 @@ import { batch } from 'react-redux'
import { Bound } from 'state/mint/v3/actions'
import styled from 'styled-components/macro'
import { TYPE } from '../../theme'
import { ThemedText } from '../../theme'
import { Chart } from './Chart'
import { useDensityChartData } from './hooks'
import { ZoomLevels } from './types'
const ZOOM_LEVELS: Record<FeeAmount, ZoomLevels> = {
[FeeAmount.LOWEST]: {
initialMin: 0.999,
initialMax: 1.001,
min: 0.00001,
max: 1.5,
},
[FeeAmount.LOW]: {
initialMin: 0.999,
initialMax: 1.001,
@@ -52,9 +58,9 @@ function InfoBox({ message, icon }: { message?: ReactNode; icon: ReactNode }) {
<ColumnCenter style={{ height: '100%', justifyContent: 'center' }}>
{icon}
{message && (
<TYPE.mediumHeader padding={10} marginTop="20px" textAlign="center">
<ThemedText.MediumHeader padding={10} marginTop="20px" textAlign="center">
{message}
</TYPE.mediumHeader>
</ThemedText.MediumHeader>
)}
</ColumnCenter>
)

View File

@@ -1,11 +1,24 @@
import { t } from '@lingui/macro'
import { Trans } from '@lingui/macro'
import { CHAIN_INFO, L2_CHAIN_IDS, SupportedChainId } from 'constants/chains'
// eslint-disable-next-line no-restricted-imports
import { t, Trans } from '@lingui/macro'
import { PrivacyPolicyModal } from 'components/PrivacyPolicy'
import { L2_CHAIN_IDS } from 'constants/chains'
import { LOCALE_LABEL, SUPPORTED_LOCALES, SupportedLocale } from 'constants/locales'
import { useActiveLocale } from 'hooks/useActiveLocale'
import { useLocationLinkProps } from 'hooks/useLocationLinkProps'
import React, { useEffect, useRef, useState } from 'react'
import { BookOpen, Check, ChevronLeft, Code, Globe, Info, MessageCircle, Moon, PieChart, Sun } from 'react-feather'
import {
BookOpen,
Check,
ChevronLeft,
Coffee,
FileText,
Globe,
HelpCircle,
Info,
MessageCircle,
Moon,
Sun,
} from 'react-feather'
import { Link } from 'react-router-dom'
import { useDarkModeManager } from 'state/user/hooks'
import styled, { css } from 'styled-components/macro'
@@ -13,8 +26,8 @@ import styled, { css } from 'styled-components/macro'
import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg'
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
import { useActiveWeb3React } from '../../hooks/web3'
import { ApplicationModal } from '../../state/application/actions'
import { useModalOpen, useToggleModal } from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'
import { ExternalLink } from '../../theme'
import { ButtonPrimary } from '../Button'
@@ -166,8 +179,6 @@ const ToggleMenuItem = styled.button`
}
`
const CODE_LINK = 'https://github.com/Uniswap/uniswap-interface'
function LanguageMenuItem({ locale, active, key }: { locale: SupportedLocale; active: boolean; key: string }) {
const { to, onClick } = useLocationLinkProps(locale)
@@ -201,11 +212,11 @@ export default function Menu() {
const node = useRef<HTMLDivElement>()
const open = useModalOpen(ApplicationModal.MENU)
const toggle = useToggleModal(ApplicationModal.MENU)
useOnClickOutside(node, open ? toggle : undefined)
const toggleMenu = useToggleModal(ApplicationModal.MENU)
useOnClickOutside(node, open ? toggleMenu : undefined)
const togglePrivacyPolicy = useToggleModal(ApplicationModal.PRIVACY_POLICY)
const openClaimModal = useToggleModal(ApplicationModal.ADDRESS_CLAIM)
const showUNIClaimOption = Boolean(!!account && !!chainId && !L2_CHAIN_IDS.includes(chainId))
const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET]
const [darkMode, toggleDarkMode] = useDarkModeManager()
@@ -216,77 +227,86 @@ export default function Menu() {
}, [open])
return (
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451
<StyledMenu ref={node as any}>
<StyledMenuButton onClick={toggle} aria-label={t`Menu`}>
<StyledMenuIcon />
</StyledMenuButton>
<>
{/* // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451 */}
<StyledMenu ref={node as any}>
<StyledMenuButton onClick={toggleMenu} aria-label={t`Menu`}>
<StyledMenuIcon />
</StyledMenuButton>
{open &&
(() => {
switch (menu) {
case 'lang':
return <LanguageMenu close={() => setMenu('main')} />
case 'main':
default:
return (
<MenuFlyout>
<MenuItem href="https://uniswap.org/">
<div>
<Trans>About</Trans>
</div>
<Info opacity={0.6} size={16} />
</MenuItem>
<MenuItem href="https://docs.uniswap.org/">
<div>
<Trans>Docs</Trans>
</div>
<BookOpen opacity={0.6} size={16} />
</MenuItem>
<MenuItem href={CODE_LINK}>
<div>
<Trans>Code</Trans>
</div>
<Code opacity={0.6} size={16} />
</MenuItem>
<MenuItem href="https://discord.gg/FCfyBSbCU5">
<div>
<Trans>Discord</Trans>
</div>
<MessageCircle opacity={0.6} size={16} />
</MenuItem>
<MenuItem href={infoLink}>
<div>
<Trans>Analytics</Trans>
</div>
<PieChart opacity={0.6} size={16} />
</MenuItem>
<ToggleMenuItem onClick={() => setMenu('lang')}>
<div>
<Trans>Language</Trans>
</div>
<Globe opacity={0.6} size={16} />
</ToggleMenuItem>
<ToggleMenuItem onClick={() => toggleDarkMode()}>
<div>{darkMode ? <Trans>Light Theme</Trans> : <Trans>Dark Theme</Trans>}</div>
{darkMode ? <Moon opacity={0.6} size={16} /> : <Sun opacity={0.6} size={16} />}
</ToggleMenuItem>
{showUNIClaimOption && (
<UNIbutton
onClick={openClaimModal}
padding="8px 16px"
width="100%"
$borderRadius="12px"
mt="0.5rem"
>
<Trans>Claim UNI</Trans>
</UNIbutton>
)}
</MenuFlyout>
)
}
})()}
</StyledMenu>
{open &&
(() => {
switch (menu) {
case 'lang':
return <LanguageMenu close={() => setMenu('main')} />
case 'main':
default:
return (
<MenuFlyout>
<MenuItem href="https://uniswap.org/">
<div>
<Trans>About</Trans>
</div>
<Info opacity={0.6} size={16} />
</MenuItem>
<MenuItem href="https://help.uniswap.org/">
<div>
<Trans>Help Center</Trans>
</div>
<HelpCircle opacity={0.6} size={16} />
</MenuItem>
<MenuItem href="https://uniswap.canny.io/feature-requests">
<div>
<Trans>Request Features</Trans>
</div>
<Coffee opacity={0.6} size={16} />
</MenuItem>
<MenuItem href="https://discord.gg/FCfyBSbCU5">
<div>
<Trans>Discord</Trans>
</div>
<MessageCircle opacity={0.6} size={16} />
</MenuItem>
<ToggleMenuItem onClick={() => setMenu('lang')}>
<div>
<Trans>Language</Trans>
</div>
<Globe opacity={0.6} size={16} />
</ToggleMenuItem>
<ToggleMenuItem onClick={() => toggleDarkMode()}>
<div>{darkMode ? <Trans>Light Theme</Trans> : <Trans>Dark Theme</Trans>}</div>
{darkMode ? <Moon opacity={0.6} size={16} /> : <Sun opacity={0.6} size={16} />}
</ToggleMenuItem>
<MenuItem href="https://docs.uniswap.org/">
<div>
<Trans>Docs</Trans>
</div>
<BookOpen opacity={0.6} size={16} />
</MenuItem>
<ToggleMenuItem onClick={() => togglePrivacyPolicy()}>
<div>
<Trans>Legal & Privacy</Trans>
</div>
<FileText opacity={0.6} size={16} />
</ToggleMenuItem>
{showUNIClaimOption && (
<UNIbutton
onClick={openClaimModal}
padding="8px 16px"
width="100%"
$borderRadius="12px"
mt="0.5rem"
>
<Trans>Claim UNI</Trans>
</UNIbutton>
)}
</MenuFlyout>
)
}
})()}
</StyledMenu>
<PrivacyPolicyModal />
</>
)
}

View File

@@ -5,7 +5,7 @@ import styled, { ThemeContext } from 'styled-components/macro'
import Circle from '../../assets/images/blue-loader.svg'
import { useActiveWeb3React } from '../../hooks/web3'
import { CloseIcon, CustomLightSpinner, TYPE } from '../../theme'
import { CloseIcon, CustomLightSpinner, ThemedText } from '../../theme'
import { ExternalLink } from '../../theme/components'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { AutoColumn, ColumnCenter } from '../Column'
@@ -32,9 +32,9 @@ export function LoadingView({ children, onDismiss }: { children: any; onDismiss:
</ConfirmedIcon>
<AutoColumn gap="100px" justify={'center'}>
{children}
<TYPE.subHeader>
<ThemedText.SubHeader>
<Trans>Confirm this transaction in your wallet</Trans>
</TYPE.subHeader>
</ThemedText.SubHeader>
</AutoColumn>
</ConfirmOrLoadingWrapper>
)
@@ -68,9 +68,9 @@ export function SubmittedView({
href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}
style={{ marginLeft: '4px' }}
>
<TYPE.subHeader>
<ThemedText.SubHeader>
<Trans>View transaction on Explorer</Trans>
</TYPE.subHeader>
</ThemedText.SubHeader>
</ExternalLink>
)}
</AutoColumn>

View File

@@ -10,7 +10,7 @@ import { useAppDispatch } from 'state/hooks'
import { resetMintState } from 'state/mint/actions'
import { resetMintState as resetMintV3State } from 'state/mint/v3/actions'
import styled from 'styled-components/macro'
import { TYPE } from 'theme'
import { ThemedText } from 'theme'
import Row, { RowBetween } from '../Row'
import SettingsTab from '../Settings'
@@ -136,7 +136,7 @@ export function AddRemoveTabs({
>
<StyledArrowLeft stroke={theme.text2} />
</StyledHistoryLink>
<TYPE.mediumHeader
<ThemedText.MediumHeader
fontWeight={500}
fontSize={20}
style={{ flex: '1', margin: 'auto', textAlign: children ? 'start' : 'center' }}
@@ -148,7 +148,7 @@ export function AddRemoveTabs({
) : (
<Trans>Remove Liquidity</Trans>
)}
</TYPE.mediumHeader>
</ThemedText.MediumHeader>
<Box style={{ marginRight: '.5rem' }}>{children}</Box>
<SettingsTab placeholderSlippage={defaultSlippage} />
</RowBetween>

View File

@@ -260,7 +260,7 @@ export function NetworkAlert(props: NetworkAlertProps) {
</Header>
<Body>
<Trans>
To starting trading on {info.label}, first bridge your assets from L1 to L2. Please treat this as a beta
To start trading on {info.label}, first bridge your assets from L1 to L2. Please treat this as a beta
release and learn about the risks before using {info.label}.
</Trans>
</Body>

View File

@@ -12,7 +12,7 @@ const StyledInput = styled.input<{ error?: boolean; fontSize?: string; align?: s
border: none;
flex: 1 1 auto;
background-color: ${({ theme }) => theme.bg1};
font-size: ${({ fontSize }) => fontSize ?? '24px'};
font-size: ${({ fontSize }) => fontSize ?? '28px'};
text-align: ${({ align }) => align && align};
white-space: nowrap;
overflow: hidden;

View File

@@ -6,17 +6,17 @@ import ReactGA from 'react-ga'
import styled, { keyframes } from 'styled-components/macro'
import tokenLogo from '../../assets/images/token-logo.png'
import { ButtonPrimary } from '../../components/Button'
import { useActiveWeb3React } from '../../hooks/web3'
import { ApplicationModal } from '../../state/application/actions'
import {
useModalOpen,
useShowClaimPopup,
useToggleSelfClaimModal,
useToggleShowClaimPopup,
} from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'
import { useUserHasAvailableClaim, useUserUnclaimedAmount } from '../../state/claim/hooks'
import { TYPE } from '../../theme'
import { ThemedText } from '../../theme'
import { ButtonPrimary } from '../Button'
import { AutoColumn } from '../Column'
import { CardBGImage, CardNoise } from '../earn/styled'
@@ -98,10 +98,10 @@ export default function ClaimPopup() {
<StyledClose stroke="white" onClick={toggleShowClaimPopup} />
<AutoColumn style={{ padding: '2rem 0', zIndex: 10 }} justify="center">
<UniToken width="48px" src={tokenLogo} />{' '}
<TYPE.white style={{ marginTop: '1rem' }} fontSize={36} fontWeight={600}>
<ThemedText.White style={{ marginTop: '1rem' }} fontSize={36} fontWeight={600}>
{unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} UNI
</TYPE.white>
<TYPE.white style={{ paddingTop: '1.25rem', textAlign: 'center' }} fontWeight={600} color="white">
</ThemedText.White>
<ThemedText.White style={{ paddingTop: '1.25rem', textAlign: 'center' }} fontWeight={600} color="white">
<span role="img" aria-label="party">
🎉
</span>{' '}
@@ -109,12 +109,12 @@ export default function ClaimPopup() {
<span role="img" aria-label="party">
🎉
</span>
</TYPE.white>
<TYPE.subHeader style={{ paddingTop: '0.5rem', textAlign: 'center' }} color="white">
</ThemedText.White>
<ThemedText.SubHeader style={{ paddingTop: '0.5rem', textAlign: 'center' }} color="white">
<Trans>
Thanks for being part of the Uniswap community <Heart size={12} />
</Trans>
</TYPE.subHeader>
</ThemedText.SubHeader>
</AutoColumn>
<AutoColumn style={{ zIndex: 10 }} justify="center">
<ButtonPrimary padding="8px" $borderRadius="8px" width={'fit-content'} onClick={handleToggleSelfClaimModal}>

View File

@@ -4,8 +4,8 @@ import { animated } from 'react-spring'
import { useSpring } from 'react-spring/web'
import styled, { ThemeContext } from 'styled-components/macro'
import { PopupContent } from '../../state/application/actions'
import { useRemovePopup } from '../../state/application/hooks'
import { PopupContent } from '../../state/application/reducer'
import TransactionPopup from './TransactionPopup'
const StyledClose = styled(X)`

View File

@@ -4,8 +4,8 @@ import styled, { ThemeContext } from 'styled-components/macro'
import { useActiveWeb3React } from '../../hooks/web3'
import { useTransaction } from '../../state/transactions/hooks'
import { TYPE } from '../../theme'
import { ExternalLink } from '../../theme/components'
import { ThemedText } from '../../theme'
import { ExternalLink } from '../../theme'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { TransactionSummary } from '../AccountDetails/TransactionSummary'
import { AutoColumn } from '../Column'
@@ -30,9 +30,9 @@ export default function TransactionPopup({ hash }: { hash: string }) {
{success ? <CheckCircle color={theme.green1} size={24} /> : <AlertCircle color={theme.red1} size={24} />}
</div>
<AutoColumn gap="8px">
<TYPE.body fontWeight={500}>
<ThemedText.Body fontWeight={500}>
<TransactionSummary info={tx.info} />
</TYPE.body>
</ThemedText.Body>
{chainId && (
<ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}>
View on Explorer

View File

@@ -14,7 +14,7 @@ import { useColor } from '../../hooks/useColor'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useActiveWeb3React } from '../../hooks/web3'
import { useTokenBalance } from '../../state/wallet/hooks'
import { ExternalLink, TYPE } from '../../theme'
import { ExternalLink, ThemedText } from '../../theme'
import { currencyId } from '../../utils/currencyId'
import { unwrappedToken } from '../../utils/unwrappedToken'
import { ButtonEmpty, ButtonPrimary, ButtonSecondary } from '../Button'
@@ -142,7 +142,7 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
</GreyCard>
) : (
<LightCard>
<TYPE.subHeader style={{ textAlign: 'center' }}>
<ThemedText.SubHeader style={{ textAlign: 'center' }}>
<span role="img" aria-label="wizard-icon">
</span>{' '}
@@ -150,7 +150,7 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
By adding liquidity you&apos;ll earn 0.3% of all trades on this pair proportional to your share of the
pool. Fees are added to the pool, accrue in real time and can be claimed by withdrawing your liquidity.
</Trans>{' '}
</TYPE.subHeader>
</ThemedText.SubHeader>
</LightCard>
)}
</>

View File

@@ -13,7 +13,7 @@ import JSBI from 'jsbi'
import { ReactNode, useCallback, useContext, useState } from 'react'
import { Bound } from 'state/mint/v3/actions'
import { ThemeContext } from 'styled-components/macro'
import { TYPE } from 'theme'
import { ThemedText } from 'theme'
import { formatTickPrice } from 'utils/formatTickPrice'
import { unwrappedToken } from 'utils/unwrappedToken'
@@ -70,9 +70,9 @@ export const PositionPreview = ({
size={24}
margin={true}
/>
<TYPE.label ml="10px" fontSize="24px">
<ThemedText.Label ml="10px" fontSize="24px">
{currency0?.symbol} / {currency1?.symbol}
</TYPE.label>
</ThemedText.Label>
</RowFixed>
<RangeBadge removed={removed} inRange={inRange} />
</RowBetween>
@@ -82,36 +82,36 @@ export const PositionPreview = ({
<RowBetween>
<RowFixed>
<CurrencyLogo currency={currency0} />
<TYPE.label ml="8px">{currency0?.symbol}</TYPE.label>
<ThemedText.Label ml="8px">{currency0?.symbol}</ThemedText.Label>
</RowFixed>
<RowFixed>
<TYPE.label mr="8px">{position.amount0.toSignificant(4)}</TYPE.label>
<ThemedText.Label mr="8px">{position.amount0.toSignificant(4)}</ThemedText.Label>
</RowFixed>
</RowBetween>
<RowBetween>
<RowFixed>
<CurrencyLogo currency={currency1} />
<TYPE.label ml="8px">{currency1?.symbol}</TYPE.label>
<ThemedText.Label ml="8px">{currency1?.symbol}</ThemedText.Label>
</RowFixed>
<RowFixed>
<TYPE.label mr="8px">{position.amount1.toSignificant(4)}</TYPE.label>
<ThemedText.Label mr="8px">{position.amount1.toSignificant(4)}</ThemedText.Label>
</RowFixed>
</RowBetween>
<Break />
<RowBetween>
<TYPE.label>
<ThemedText.Label>
<Trans>Fee Tier</Trans>
</TYPE.label>
<TYPE.label>
</ThemedText.Label>
<ThemedText.Label>
<Trans>{position?.pool?.fee / 10000}%</Trans>
</TYPE.label>
</ThemedText.Label>
</RowBetween>
</AutoColumn>
</LightCard>
<AutoColumn gap="md">
<RowBetween>
{title ? <TYPE.main>{title}</TYPE.main> : <div />}
{title ? <ThemedText.Main>{title}</ThemedText.Main> : <div />}
<RateToggle
currencyA={sorted ? currency0 : currency1}
currencyB={sorted ? currency1 : currency0}
@@ -122,57 +122,57 @@ export const PositionPreview = ({
<RowBetween>
<LightCard width="48%" padding="8px">
<AutoColumn gap="4px" justify="center">
<TYPE.main fontSize="12px">
<ThemedText.Main fontSize="12px">
<Trans>Min Price</Trans>
</TYPE.main>
<TYPE.mediumHeader textAlign="center">{`${formatTickPrice(
</ThemedText.Main>
<ThemedText.MediumHeader textAlign="center">{`${formatTickPrice(
priceLower,
ticksAtLimit,
Bound.LOWER
)}`}</TYPE.mediumHeader>
<TYPE.main textAlign="center" fontSize="12px">
)}`}</ThemedText.MediumHeader>
<ThemedText.Main textAlign="center" fontSize="12px">
<Trans>
{quoteCurrency.symbol} per {baseCurrency.symbol}
</Trans>
</TYPE.main>
<TYPE.small textAlign="center" color={theme.text3} style={{ marginTop: '4px' }}>
</ThemedText.Main>
<ThemedText.Small textAlign="center" color={theme.text3} style={{ marginTop: '4px' }}>
<Trans>Your position will be 100% composed of {baseCurrency?.symbol} at this price</Trans>
</TYPE.small>
</ThemedText.Small>
</AutoColumn>
</LightCard>
<LightCard width="48%" padding="8px">
<AutoColumn gap="4px" justify="center">
<TYPE.main fontSize="12px">
<ThemedText.Main fontSize="12px">
<Trans>Max Price</Trans>
</TYPE.main>
<TYPE.mediumHeader textAlign="center">{`${formatTickPrice(
</ThemedText.Main>
<ThemedText.MediumHeader textAlign="center">{`${formatTickPrice(
priceUpper,
ticksAtLimit,
Bound.UPPER
)}`}</TYPE.mediumHeader>
<TYPE.main textAlign="center" fontSize="12px">
)}`}</ThemedText.MediumHeader>
<ThemedText.Main textAlign="center" fontSize="12px">
<Trans>
{quoteCurrency.symbol} per {baseCurrency.symbol}
</Trans>
</TYPE.main>
<TYPE.small textAlign="center" color={theme.text3} style={{ marginTop: '4px' }}>
</ThemedText.Main>
<ThemedText.Small textAlign="center" color={theme.text3} style={{ marginTop: '4px' }}>
<Trans>Your position will be 100% composed of {quoteCurrency?.symbol} at this price</Trans>
</TYPE.small>
</ThemedText.Small>
</AutoColumn>
</LightCard>
</RowBetween>
<LightCard padding="12px ">
<AutoColumn gap="4px" justify="center">
<TYPE.main fontSize="12px">
<ThemedText.Main fontSize="12px">
<Trans>Current price</Trans>
</TYPE.main>
<TYPE.mediumHeader>{`${price.toSignificant(5)} `}</TYPE.mediumHeader>
<TYPE.main textAlign="center" fontSize="12px">
</ThemedText.Main>
<ThemedText.MediumHeader>{`${price.toSignificant(5)} `}</ThemedText.MediumHeader>
<ThemedText.Main textAlign="center" fontSize="12px">
<Trans>
{quoteCurrency.symbol} per {baseCurrency.symbol}
</Trans>
</TYPE.main>
</ThemedText.Main>
</AutoColumn>
</LightCard>
</AutoColumn>

View File

@@ -0,0 +1,179 @@
import { Trans } from '@lingui/macro'
import Card, { DarkGreyCard } from 'components/Card'
import Row, { AutoRow, RowBetween } from 'components/Row'
import { useEffect, useRef } from 'react'
import { ArrowDown, Info, X } from 'react-feather'
import ReactGA from 'react-ga'
import styled from 'styled-components/macro'
import { ExternalLink, ThemedText } from 'theme'
import { isMobile } from 'utils/userAgent'
import { useModalOpen, useTogglePrivacyPolicy } from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'
import { AutoColumn } from '../Column'
import Modal from '../Modal'
const Wrapper = styled.div`
max-height: 70vh;
overflow: auto;
padding: 0 1rem;
`
const StyledExternalCard = styled(Card)`
background-color: ${({ theme }) => theme.primary5};
padding: 0.5rem;
width: 100%;
:hover,
:focus,
:active {
background-color: ${({ theme }) => theme.primary4};
}
`
const HoverText = styled.div`
text-decoration: none;
color: ${({ theme }) => theme.text1};
display: flex;
align-items: center;
:hover {
cursor: pointer;
}
`
const StyledLinkOut = styled(ArrowDown)`
transform: rotate(230deg);
`
const EXTERNAL_APIS = [
{
name: 'Auto Router',
description: <Trans>The app fetches the optimal trade route from a Uniswap Labs server.</Trans>,
},
{
name: 'Infura',
description: <Trans>The app fetches on-chain data and constructs contract calls with an Infura API.</Trans>,
},
{
name: 'TRM Labs',
description: (
<>
<Trans>
The app securely collects your wallet address and shares it with TRM Labs Inc. for risk and compliance
reasons.
</Trans>{' '}
<ExternalLink href="https://help.uniswap.org/en/articles/5675203-terms-of-service-faq">
<Trans>Learn more</Trans>
</ExternalLink>
</>
),
},
{
name: 'Google Analytics',
description: <Trans>The app logs anonymized usage statistics in order to improve over time.</Trans>,
},
{
name: 'The Graph',
description: <Trans>The app fetches blockchain data from The Graphs hosted service.</Trans>,
},
]
export function PrivacyPolicyModal() {
const node = useRef<HTMLDivElement>()
const open = useModalOpen(ApplicationModal.PRIVACY_POLICY)
const toggle = useTogglePrivacyPolicy()
useEffect(() => {
if (!open) return
ReactGA.event({
category: 'Modal',
action: 'Show Legal',
})
}, [open])
return (
<Modal isOpen={open} onDismiss={() => toggle()}>
<AutoColumn gap="12px" ref={node as any}>
<RowBetween padding="1rem 1rem 0.5rem 1rem">
<ThemedText.MediumHeader>
<Trans>Legal & Privacy</Trans>
</ThemedText.MediumHeader>
<HoverText onClick={() => toggle()}>
<X size={24} />
</HoverText>
</RowBetween>
<PrivacyPolicy />
</AutoColumn>
</Modal>
)
}
export function PrivacyPolicy() {
return (
<Wrapper
draggable="true"
onTouchMove={(e) => {
// prevent modal gesture handler from dismissing modal when content is scrolling
if (isMobile) {
e.stopPropagation()
}
}}
>
<AutoColumn gap="16px">
<AutoColumn gap="8px" style={{ width: '100%' }}>
<StyledExternalCard>
<ExternalLink href={'https://uniswap.org/terms-of-service'}>
<RowBetween>
<AutoRow gap="4px">
<Info size={20} />
<ThemedText.Main fontSize={14} color={'primaryText1'}>
<Trans>Uniswap Labs&apos; Terms of Service</Trans>
</ThemedText.Main>
</AutoRow>
<StyledLinkOut size={20} />
</RowBetween>
</ExternalLink>
</StyledExternalCard>
<StyledExternalCard>
<ExternalLink href={'https://uniswap.org/disclaimer/'}>
<RowBetween>
<AutoRow gap="4px">
<Info size={20} />
<ThemedText.Main fontSize={14} color={'primaryText1'}>
<Trans>Protocol Disclaimer</Trans>
</ThemedText.Main>
</AutoRow>
<StyledLinkOut size={20} />
</RowBetween>
</ExternalLink>
</StyledExternalCard>
</AutoColumn>
<ThemedText.Main fontSize={14}>
<Trans>This app uses the following third-party APIs:</Trans>
</ThemedText.Main>
<AutoColumn gap="12px">
{EXTERNAL_APIS.map(({ name, description }, i) => (
<DarkGreyCard key={i}>
<AutoColumn gap="8px">
<AutoRow gap="4px">
<Info size={18} />
<ThemedText.Main fontSize={14} color={'text1'}>
{name}
</ThemedText.Main>
</AutoRow>
<ThemedText.Main fontSize={14}>{description}</ThemedText.Main>
</AutoColumn>
</DarkGreyCard>
))}
<Row justify="center" marginBottom="1rem">
<ExternalLink href="https://help.uniswap.org/en/articles/5675203-terms-of-service-faq">
<Trans>Learn more</Trans>
</ExternalLink>
</Row>
</AutoColumn>
</AutoColumn>
</Wrapper>
)
}

View File

@@ -2,7 +2,7 @@ import { useContext } from 'react'
import styled from 'styled-components/macro'
import { ThemeContext } from 'styled-components/macro'
import { TYPE } from '../../theme'
import { ThemedText } from '../../theme'
import { AutoColumn } from '../Column'
const Wrapper = styled(AutoColumn)`
@@ -65,7 +65,7 @@ export default function ProgressCircles({ steps, disabled = false, ...rest }: Pr
<Circle confirmed={step} disabled={disabled || (!steps[i - 1] && i !== 0)}>
{step ? '✓' : i + 1 + '.'}
</Circle>
<TYPE.main color={theme.text4}>|</TYPE.main>
<ThemedText.Main color={theme.text4}>|</ThemedText.Main>
</CircleRow>
)
})}

View File

@@ -4,7 +4,7 @@ import { AutoRow } from 'components/Row'
import React from 'react'
import ReactGA from 'react-ga'
import styled from 'styled-components/macro'
import { TYPE } from 'theme'
import { ThemedText } from 'theme'
const Button = styled(ButtonOutlined).attrs(() => ({
padding: '8px',
@@ -26,9 +26,9 @@ export default function PresetsButtons({ setFullRange }: { setFullRange: () => v
})
}}
>
<TYPE.body fontSize={12}>
<ThemedText.Body fontSize={12}>
<Trans>Full Range</Trans>
</TYPE.body>
</ThemedText.Body>
</Button>
</AutoRow>
)

View File

@@ -1,3 +1,4 @@
import { Protocol } from '@uniswap/router-sdk'
import { Currency, Percent } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk'
import { DAI, USDC, WBTC } from 'constants/tokens'
@@ -7,16 +8,21 @@ import RoutingDiagram, { RoutingDiagramEntry } from './RoutingDiagram'
const percent = (strings: TemplateStringsArray) => new Percent(parseInt(strings[0]), 100)
const singleRoute: RoutingDiagramEntry = { percent: percent`100`, path: [[USDC, DAI, FeeAmount.LOW]] }
const singleRoute: RoutingDiagramEntry = {
percent: percent`100`,
path: [[USDC, DAI, FeeAmount.LOW]],
protocol: Protocol.V3,
}
const multiRoute: RoutingDiagramEntry[] = [
{ percent: percent`75`, path: [[USDC, DAI, FeeAmount.LOW]] },
{ percent: percent`75`, path: [[USDC, DAI, FeeAmount.LOWEST]], protocol: Protocol.V2 },
{
percent: percent`25`,
path: [
[USDC, WBTC, FeeAmount.MEDIUM],
[WBTC, DAI, FeeAmount.HIGH],
],
protocol: Protocol.V3,
},
]

View File

@@ -1,3 +1,5 @@
import { Trans } from '@lingui/macro'
import { Protocol } from '@uniswap/router-sdk'
import { Currency, Percent } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk'
import Badge from 'components/Badge'
@@ -7,22 +9,24 @@ import Row, { AutoRow } from 'components/Row'
import { useTokenInfoFromActiveList } from 'hooks/useTokenInfoFromActiveList'
import { Box } from 'rebass'
import styled from 'styled-components/macro'
import { TYPE } from 'theme'
import { ThemedText, Z_INDEX } from 'theme'
import { ReactComponent as DotLine } from '../../assets/svg/dot_line.svg'
import { MouseoverTooltip } from '../Tooltip'
export interface RoutingDiagramEntry {
percent: Percent
path: [Currency, Currency, FeeAmount][]
protocol: Protocol
}
const Wrapper = styled(Box)`
align-items: center;
background-color: ${({ theme }) => theme.bg0};
width: 400px;
width: 100%;
`
const RouteContainerRow = styled(Row)`
display: grid;
grid-gap: 8px;
grid-template-columns: 24px 1fr 24px;
`
@@ -36,22 +40,47 @@ const RouteRow = styled(Row)`
const PoolBadge = styled(Badge)`
display: flex;
padding: 0.25rem 0.5rem;
padding: 4px 4px;
`
const DottedLine = styled.div`
border-color: ${({ theme }) => theme.bg4};
border-top-style: dotted;
border-width: 4px;
height: 0px;
display: flex;
align-items: center;
position: absolute;
width: calc(100%);
z-index: 1;
opacity: 0.5;
`
const DotColor = styled(DotLine)`
path {
stroke: ${({ theme }) => theme.bg4};
}
`
const OpaqueBadge = styled(Badge)`
background-color: ${({ theme }) => theme.bg2};
z-index: 2;
border-radius: 8px;
display: grid;
font-size: 12px;
grid-gap: 4px;
grid-auto-flow: column;
justify-content: start;
padding: 4px 6px 4px 4px;
z-index: ${Z_INDEX.sticky};
`
const ProtocolBadge = styled(Badge)`
background-color: ${({ theme }) => theme.bg3};
border-radius: 4px;
color: ${({ theme }) => theme.text2};
font-size: 10px;
padding: 2px 4px;
z-index: ${Z_INDEX.sticky + 1};
`
const BadgeText = styled(ThemedText.Small)`
word-break: normal;
`
export default function RoutingDiagram({
@@ -68,27 +97,31 @@ export default function RoutingDiagram({
return (
<Wrapper>
{routes.map(({ percent, path }, index) => (
{routes.map((entry, index) => (
<RouteContainerRow key={index}>
<CurrencyLogo currency={tokenIn} />
<Route percent={percent} path={path} />
<CurrencyLogo currency={tokenOut} />
<CurrencyLogo currency={tokenIn} size={'20px'} />
<Route entry={entry} />
<CurrencyLogo currency={tokenOut} size={'20px'} />
</RouteContainerRow>
))}
</Wrapper>
)
}
function Route({ percent, path }: { percent: RoutingDiagramEntry['percent']; path: RoutingDiagramEntry['path'] }) {
function Route({ entry: { percent, path, protocol } }: { entry: RoutingDiagramEntry }) {
return (
<RouteRow>
<DottedLine />
<DottedLine>
<DotColor />
</DottedLine>
<OpaqueBadge>
<TYPE.small fontSize={12} style={{ wordBreak: 'normal' }}>
<ProtocolBadge>
<BadgeText fontSize={12}>{protocol.toUpperCase()}</BadgeText>
</ProtocolBadge>
<BadgeText fontSize={14} style={{ minWidth: 'auto' }}>
{percent.toSignificant(2)}%
</TYPE.small>
</BadgeText>
</OpaqueBadge>
<AutoRow gap="1px" width="100%" style={{ justifyContent: 'space-evenly', zIndex: 2 }}>
{path.map(([currency0, currency1, feeAmount], index) => (
<Pool key={index} currency0={currency0} currency1={currency1} feeAmount={feeAmount} />
@@ -102,12 +135,17 @@ function Pool({ currency0, currency1, feeAmount }: { currency0: Currency; curren
const tokenInfo0 = useTokenInfoFromActiveList(currency0)
const tokenInfo1 = useTokenInfoFromActiveList(currency1)
// TODO - link pool icon to info.uniswap.org via query params
return (
<PoolBadge>
<Box margin="0 5px 0 10px">
<DoubleCurrencyLogo currency0={tokenInfo1} currency1={tokenInfo0} size={20} />
</Box>
<TYPE.small fontSize={12}>{feeAmount / 10000}%</TYPE.small>
</PoolBadge>
<MouseoverTooltip
text={<Trans>{tokenInfo0?.symbol + '/' + tokenInfo1?.symbol + ' ' + feeAmount / 10000}% pool</Trans>}
>
<PoolBadge>
<Box margin="0 4px 0 12px">
<DoubleCurrencyLogo currency0={tokenInfo1} currency1={tokenInfo0} size={20} />
</Box>
<ThemedText.Small fontSize={14}>{feeAmount / 10000}%</ThemedText.Small>
</PoolBadge>
</MouseoverTooltip>
)
}

View File

@@ -3,24 +3,39 @@
exports[`renders multi route 1`] = `
<DocumentFragment>
<div
class="RoutingDiagram__Wrapper-sc-o1ook0-0 fUoVYh css-vurnku"
class="RoutingDiagram__Wrapper-sc-o1ook0-0 ePDWDk css-vurnku"
>
<div
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteContainerRow-sc-o1ook0-1 lmTMKd itvFNV iiQQUx"
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteContainerRow-sc-o1ook0-1 lmTMKd itvFNV ibRCpr"
>
CurrencyLogo currency=USDC
<div
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteRow-sc-o1ook0-2 lmTMKd itvFNV fzMiot"
>
<div
class="RoutingDiagram__DottedLine-sc-o1ook0-4 gwivhA"
/>
class="RoutingDiagram__DottedLine-sc-o1ook0-4 kkXINS"
>
<svg
class="RoutingDiagram__DotColor-sc-o1ook0-5 kgYqrO"
>
dot_line.svg
</svg>
</div>
<div
class="Badge-sc-1mhw5si-0 RoutingDiagram__OpaqueBadge-sc-o1ook0-5 gayll cvyxdH"
class="Badge-sc-1mhw5si-0 RoutingDiagram__OpaqueBadge-sc-o1ook0-6 gayll OurGh"
>
<div
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab css-15li2d9"
style="word-break: normal;"
class="Badge-sc-1mhw5si-0 RoutingDiagram__ProtocolBadge-sc-o1ook0-7 gayll bNVqMw"
>
<div
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab RoutingDiagram__BadgeText-sc-o1ook0-8 dYpdfO css-15li2d9"
>
V2
</div>
</div>
<div
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab RoutingDiagram__BadgeText-sc-o1ook0-8 dYpdfO css-1aekuku"
style="min-width: auto;"
>
75%
</div>
@@ -30,40 +45,42 @@ exports[`renders multi route 1`] = `
style="justify-content: space-evenly; z-index: 2;"
width="100%"
>
<div
class="Badge-sc-1mhw5si-0 RoutingDiagram__PoolBadge-sc-o1ook0-3 gayll bRJvWg"
>
<div
class="css-1t7xebc"
>
DoubleCurrencyLogo currency0=DAI currency1=USDC
</div>
<div
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab css-15li2d9"
>
0.05%
</div>
</div>
Popover
</div>
</div>
CurrencyLogo currency=DAI
</div>
<div
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteContainerRow-sc-o1ook0-1 lmTMKd itvFNV iiQQUx"
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteContainerRow-sc-o1ook0-1 lmTMKd itvFNV ibRCpr"
>
CurrencyLogo currency=USDC
<div
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteRow-sc-o1ook0-2 lmTMKd itvFNV fzMiot"
>
<div
class="RoutingDiagram__DottedLine-sc-o1ook0-4 gwivhA"
/>
class="RoutingDiagram__DottedLine-sc-o1ook0-4 kkXINS"
>
<svg
class="RoutingDiagram__DotColor-sc-o1ook0-5 kgYqrO"
>
dot_line.svg
</svg>
</div>
<div
class="Badge-sc-1mhw5si-0 RoutingDiagram__OpaqueBadge-sc-o1ook0-5 gayll cvyxdH"
class="Badge-sc-1mhw5si-0 RoutingDiagram__OpaqueBadge-sc-o1ook0-6 gayll OurGh"
>
<div
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab css-15li2d9"
style="word-break: normal;"
class="Badge-sc-1mhw5si-0 RoutingDiagram__ProtocolBadge-sc-o1ook0-7 gayll bNVqMw"
>
<div
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab RoutingDiagram__BadgeText-sc-o1ook0-8 dYpdfO css-15li2d9"
>
V3
</div>
</div>
<div
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab RoutingDiagram__BadgeText-sc-o1ook0-8 dYpdfO css-1aekuku"
style="min-width: auto;"
>
25%
</div>
@@ -73,34 +90,7 @@ exports[`renders multi route 1`] = `
style="justify-content: space-evenly; z-index: 2;"
width="100%"
>
<div
class="Badge-sc-1mhw5si-0 RoutingDiagram__PoolBadge-sc-o1ook0-3 gayll bRJvWg"
>
<div
class="css-1t7xebc"
>
DoubleCurrencyLogo currency0=WBTC currency1=USDC
</div>
<div
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab css-15li2d9"
>
0.3%
</div>
</div>
<div
class="Badge-sc-1mhw5si-0 RoutingDiagram__PoolBadge-sc-o1ook0-3 gayll bRJvWg"
>
<div
class="css-1t7xebc"
>
DoubleCurrencyLogo currency0=DAI currency1=WBTC
</div>
<div
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab css-15li2d9"
>
1%
</div>
</div>
PopoverPopover
</div>
</div>
CurrencyLogo currency=DAI
@@ -112,24 +102,39 @@ exports[`renders multi route 1`] = `
exports[`renders single route 1`] = `
<DocumentFragment>
<div
class="RoutingDiagram__Wrapper-sc-o1ook0-0 fUoVYh css-vurnku"
class="RoutingDiagram__Wrapper-sc-o1ook0-0 ePDWDk css-vurnku"
>
<div
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteContainerRow-sc-o1ook0-1 lmTMKd itvFNV iiQQUx"
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteContainerRow-sc-o1ook0-1 lmTMKd itvFNV ibRCpr"
>
CurrencyLogo currency=USDC
<div
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteRow-sc-o1ook0-2 lmTMKd itvFNV fzMiot"
>
<div
class="RoutingDiagram__DottedLine-sc-o1ook0-4 gwivhA"
/>
class="RoutingDiagram__DottedLine-sc-o1ook0-4 kkXINS"
>
<svg
class="RoutingDiagram__DotColor-sc-o1ook0-5 kgYqrO"
>
dot_line.svg
</svg>
</div>
<div
class="Badge-sc-1mhw5si-0 RoutingDiagram__OpaqueBadge-sc-o1ook0-5 gayll cvyxdH"
class="Badge-sc-1mhw5si-0 RoutingDiagram__OpaqueBadge-sc-o1ook0-6 gayll OurGh"
>
<div
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab css-15li2d9"
style="word-break: normal;"
class="Badge-sc-1mhw5si-0 RoutingDiagram__ProtocolBadge-sc-o1ook0-7 gayll bNVqMw"
>
<div
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab RoutingDiagram__BadgeText-sc-o1ook0-8 dYpdfO css-15li2d9"
>
V3
</div>
</div>
<div
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab RoutingDiagram__BadgeText-sc-o1ook0-8 dYpdfO css-1aekuku"
style="min-width: auto;"
>
100%
</div>
@@ -139,20 +144,7 @@ exports[`renders single route 1`] = `
style="justify-content: space-evenly; z-index: 2;"
width="100%"
>
<div
class="Badge-sc-1mhw5si-0 RoutingDiagram__PoolBadge-sc-o1ook0-3 gayll bRJvWg"
>
<div
class="css-1t7xebc"
>
DoubleCurrencyLogo currency0=DAI currency1=USDC
</div>
<div
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab css-15li2d9"
>
0.05%
</div>
</div>
Popover
</div>
</div>
CurrencyLogo currency=DAI
@@ -164,7 +156,7 @@ exports[`renders single route 1`] = `
exports[`renders when no routes are provided 1`] = `
<DocumentFragment>
<div
class="RoutingDiagram__Wrapper-sc-o1ook0-0 fUoVYh css-vurnku"
class="RoutingDiagram__Wrapper-sc-o1ook0-0 ePDWDk css-vurnku"
/>
</DocumentFragment>
`;

View File

@@ -0,0 +1,71 @@
import { Trans } from '@lingui/macro'
import { Token } from '@uniswap/sdk-core'
import { ButtonPrimary } from 'components/Button'
import { AlertCircle, ArrowLeft } from 'react-feather'
import styled from 'styled-components/macro'
import { CloseIcon, ThemedText } from 'theme'
import TokenImportCard from './TokenImportCard'
const Wrapper = styled.div`
align-items: center;
display: flex;
flex-direction: column;
flex: 1 1 auto;
height: 100%;
width: 100%;
`
const Button = styled(ButtonPrimary)`
margin-top: 1em;
padding: 10px 1em;
`
const Content = styled.div`
padding: 1em;
`
const Copy = styled(ThemedText.Body)`
text-align: center;
margin: 0 2em 1em !important;
font-weight: 400;
font-size: 16px;
`
const Header = styled.div`
align-items: center;
display: flex;
gap: 14px;
justify-content: space-between;
padding: 20px;
width: 100%;
`
const Icon = styled(AlertCircle)`
stroke: ${({ theme }) => theme.text2};
width: 48px;
height: 48px;
`
interface BlockedTokenProps {
onBack: (() => void) | undefined
onDismiss: (() => void) | undefined
blockedTokens: Token[]
}
const BlockedToken = ({ onBack, onDismiss, blockedTokens }: BlockedTokenProps) => (
<Wrapper>
<Header>
{onBack ? <ArrowLeft style={{ cursor: 'pointer' }} onClick={onBack} /> : <div />}
<ThemedText.MediumHeader>
<Trans>Token not supported</Trans>
</ThemedText.MediumHeader>
{onDismiss ? <CloseIcon onClick={onDismiss} /> : <div />}
</Header>
<Icon />
<Content>
<Copy>
<Trans>This token is not supported in the Uniswap Labs app</Trans>
</Copy>
<TokenImportCard token={blockedTokens[0]} />
<Button disabled>
<Trans>Import</Trans>
</Button>
</Content>
</Wrapper>
)
export default BlockedToken

View File

@@ -5,6 +5,7 @@ import CurrencyLogo from 'components/CurrencyLogo'
import QuestionHelper from 'components/QuestionHelper'
import { AutoRow } from 'components/Row'
import { COMMON_BASES } from 'constants/routing'
import { useTokenInfoFromActiveList } from 'hooks/useTokenInfoFromActiveList'
import { Text } from 'rebass'
import styled from 'styled-components/macro'
import { currencyId } from 'utils/currencyId'
@@ -60,7 +61,7 @@ export default function CommonBases({
disable={isSelected}
key={currencyId(currency)}
>
<CurrencyLogo currency={currency} style={{ marginRight: 8 }} />
<CurrencyLogoFromList currency={currency} />
<Text fontWeight={500} fontSize={16}>
{currency.symbol}
</Text>
@@ -71,3 +72,10 @@ export default function CommonBases({
</MobileWrapper>
) : null
}
/** helper component to retrieve a base currency from the active token lists */
function CurrencyLogoFromList({ currency }: { currency: Currency }) {
const token = useTokenInfoFromActiveList(currency)
return <CurrencyLogo currency={token} style={{ marginRight: 8 }} />
}

View File

@@ -14,7 +14,7 @@ import { useActiveWeb3React } from '../../hooks/web3'
import { useCombinedActiveList } from '../../state/lists/hooks'
import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
import { useCurrencyBalance } from '../../state/wallet/hooks'
import { TYPE } from '../../theme'
import { ThemedText } from '../../theme'
import { isTokenOnList } from '../../utils'
import Column from '../Column'
import CurrencyLogo from '../CurrencyLogo'
@@ -135,13 +135,13 @@ function CurrencyRow({
<Text title={currency.name} fontWeight={500}>
{currency.symbol}
</Text>
<TYPE.darkGray ml="0px" fontSize={'12px'} fontWeight={300}>
<ThemedText.DarkGray ml="0px" fontSize={'12px'} fontWeight={300}>
{!currency.isNative && !isOnSelectedList && customAdded ? (
<Trans>{currency.name} Added by user</Trans>
) : (
currency.name
)}
</TYPE.darkGray>
</ThemedText.DarkGray>
</Column>
<TokenTags currency={currency} />
{showCurrencyAmount && (
@@ -167,9 +167,9 @@ function BreakLineComponent({ style }: { style: CSSProperties }) {
<RowBetween>
<RowFixed>
<TokenListLogoWrapper src={TokenListLogo} />
<TYPE.main ml="6px" fontSize="12px" color={theme.text1}>
<ThemedText.Main ml="6px" fontSize="12px" color={theme.text1}>
<Trans>Expanded results from inactive Token Lists</Trans>
</TYPE.main>
</ThemedText.Main>
</RowFixed>
<QuestionHelper
text={

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line no-restricted-imports
import { t, Trans } from '@lingui/macro'
import { Currency, Token } from '@uniswap/sdk-core'
import useDebounce from 'hooks/useDebounce'
@@ -15,7 +16,7 @@ import styled from 'styled-components/macro'
import { ExtendedEther } from '../../constants/tokens'
import { useAllTokens, useIsUserAddedToken, useSearchInactiveTokenLists, useToken } from '../../hooks/Tokens'
import { useActiveWeb3React } from '../../hooks/web3'
import { ButtonText, CloseIcon, IconWrapper, TYPE } from '../../theme'
import { ButtonText, CloseIcon, IconWrapper, ThemedText } from '../../theme'
import { isAddress } from '../../utils'
import Column from '../Column'
import Row, { RowBetween, RowFixed } from '../Row'
@@ -223,9 +224,9 @@ export function CurrencySearch({
</div>
) : (
<Column style={{ padding: '20px', height: '100%' }}>
<TYPE.main color={theme.text3} textAlign="center" mb="20px">
<ThemedText.Main color={theme.text3} textAlign="center" mb="20px">
<Trans>No results found.</Trans>
</TYPE.main>
</ThemedText.Main>
</Column>
)}
<Footer>
@@ -235,9 +236,9 @@ export function CurrencySearch({
<IconWrapper size="16px" marginRight="6px" stroke={theme.primaryText1}>
<Edit />
</IconWrapper>
<TYPE.main color={theme.primaryText1}>
<ThemedText.Main color={theme.primaryText1}>
<Trans>Manage Token Lists</Trans>
</TYPE.main>
</ThemedText.Main>
</RowFixed>
</ButtonText>
</Row>

View File

@@ -66,12 +66,19 @@ export default function CurrencySearchModal({
const [importList, setImportList] = useState<TokenList | undefined>()
const [listURL, setListUrl] = useState<string | undefined>()
const showImportView = useCallback(() => setModalView(CurrencyModalView.importToken), [setModalView])
const showManageView = useCallback(() => setModalView(CurrencyModalView.manage), [setModalView])
const handleBackImport = useCallback(
() => setModalView(prevView && prevView !== CurrencyModalView.importToken ? prevView : CurrencyModalView.search),
[setModalView, prevView]
)
// change min height if not searching
const minHeight = modalView === CurrencyModalView.importToken || modalView === CurrencyModalView.importList ? 40 : 80
return (
<Modal isOpen={isOpen} onDismiss={onDismiss} maxHeight={80} minHeight={minHeight}>
{modalView === CurrencyModalView.search ? (
let content = null
switch (modalView) {
case CurrencyModalView.search:
content = (
<CurrencySearch
isOpen={isOpen}
onDismiss={onDismiss}
@@ -81,23 +88,32 @@ export default function CurrencySearchModal({
showCommonBases={showCommonBases}
showCurrencyAmount={showCurrencyAmount}
disableNonToken={disableNonToken}
showImportView={() => setModalView(CurrencyModalView.importToken)}
showImportView={showImportView}
setImportToken={setImportToken}
showManageView={() => setModalView(CurrencyModalView.manage)}
showManageView={showManageView}
/>
) : modalView === CurrencyModalView.importToken && importToken ? (
<ImportToken
tokens={[importToken]}
onDismiss={onDismiss}
list={importToken instanceof WrappedTokenInfo ? importToken.list : undefined}
onBack={() =>
setModalView(prevView && prevView !== CurrencyModalView.importToken ? prevView : CurrencyModalView.search)
}
handleCurrencySelect={handleCurrencySelect}
/>
) : modalView === CurrencyModalView.importList && importList && listURL ? (
<ImportList list={importList} listURL={listURL} onDismiss={onDismiss} setModalView={setModalView} />
) : modalView === CurrencyModalView.manage ? (
)
break
case CurrencyModalView.importToken:
if (importToken) {
content = (
<ImportToken
tokens={[importToken]}
onDismiss={onDismiss}
list={importToken instanceof WrappedTokenInfo ? importToken.list : undefined}
onBack={handleBackImport}
handleCurrencySelect={handleCurrencySelect}
/>
)
}
break
case CurrencyModalView.importList:
if (importList && listURL) {
content = <ImportList list={importList} listURL={listURL} onDismiss={onDismiss} setModalView={setModalView} />
}
break
case CurrencyModalView.manage:
content = (
<Manage
onDismiss={onDismiss}
setModalView={setModalView}
@@ -105,9 +121,12 @@ export default function CurrencySearchModal({
setImportList={setImportList}
setListUrl={setListUrl}
/>
) : (
''
)}
)
break
}
return (
<Modal isOpen={isOpen} onDismiss={onDismiss} maxHeight={80} minHeight={minHeight}>
{content}
</Modal>
)
}

View File

@@ -16,9 +16,9 @@ import { useAppDispatch } from 'state/hooks'
import { enableList, removeList } from 'state/lists/actions'
import { useAllLists } from 'state/lists/hooks'
import styled from 'styled-components/macro'
import { CloseIcon, TYPE } from 'theme'
import { CloseIcon, ThemedText } from 'theme'
import { ExternalLink } from '../../theme/components'
import { ExternalLink } from '../../theme'
import { CurrencyModalView } from './CurrencySearchModal'
import { Checkbox, PaddedColumn, TextDot } from './styleds'
@@ -81,9 +81,9 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
<PaddedColumn gap="14px" style={{ width: '100%', flex: '1 1' }}>
<RowBetween>
<ArrowLeft style={{ cursor: 'pointer' }} onClick={() => setModalView(CurrencyModalView.manage)} />
<TYPE.mediumHeader>
<ThemedText.MediumHeader>
<Trans>Import List</Trans>
</TYPE.mediumHeader>
</ThemedText.MediumHeader>
<CloseIcon onClick={onDismiss} />
</RowBetween>
</PaddedColumn>
@@ -96,18 +96,18 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
{list.logoURI && <ListLogo logoURI={list.logoURI} size="40px" />}
<AutoColumn gap="sm" style={{ marginLeft: '20px' }}>
<RowFixed>
<TYPE.body fontWeight={600} mr="6px">
<ThemedText.Body fontWeight={600} mr="6px">
{list.name}
</TYPE.body>
</ThemedText.Body>
<TextDot />
<TYPE.main fontSize={'16px'} ml="6px">
<ThemedText.Main fontSize={'16px'} ml="6px">
<Trans>{list.tokens.length} tokens</Trans>
</TYPE.main>
</ThemedText.Main>
</RowFixed>
<ExternalLink href={`https://tokenlists.org/token-list?url=${listURL}`}>
<TYPE.main fontSize={'12px'} color={theme.blue1}>
<ThemedText.Main fontSize={'12px'} color={theme.blue1}>
{listURL}
</TYPE.main>
</ThemedText.Main>
</ExternalLink>
</AutoColumn>
</RowFixed>
@@ -116,22 +116,22 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
<Card style={{ backgroundColor: transparentize(0.8, theme.red1) }}>
<AutoColumn justify="center" style={{ textAlign: 'center', gap: '16px', marginBottom: '12px' }}>
<AlertTriangle stroke={theme.red1} size={32} />
<TYPE.body fontWeight={500} fontSize={20} color={theme.red1}>
<ThemedText.Body fontWeight={500} fontSize={20} color={theme.red1}>
<Trans>Import at your own risk</Trans>
</TYPE.body>
</ThemedText.Body>
</AutoColumn>
<AutoColumn style={{ textAlign: 'center', gap: '16px', marginBottom: '12px' }}>
<TYPE.body fontWeight={500} color={theme.red1}>
<ThemedText.Body fontWeight={500} color={theme.red1}>
<Trans>
By adding this list you are implicitly trusting that the data is correct. Anyone can create a list,
including creating fake versions of existing lists and lists that claim to represent projects that do
not have one.
</Trans>
</TYPE.body>
<TYPE.body fontWeight={600} color={theme.red1}>
</ThemedText.Body>
<ThemedText.Body fontWeight={600} color={theme.red1}>
<Trans>If you purchase a token from this list, you may not be able to sell it back.</Trans>
</TYPE.body>
</ThemedText.Body>
</AutoColumn>
<AutoRow justify="center" style={{ cursor: 'pointer' }} onClick={() => setConfirmed(!confirmed)}>
<Checkbox
@@ -140,9 +140,9 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
checked={confirmed}
onChange={() => setConfirmed(!confirmed)}
/>
<TYPE.body ml="10px" fontSize="16px" color={theme.red1} fontWeight={500}>
<ThemedText.Body ml="10px" fontSize="16px" color={theme.red1} fontWeight={500}>
<Trans>I understand</Trans>
</TYPE.body>
</ThemedText.Body>
</AutoRow>
</Card>
@@ -156,9 +156,9 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
<Trans>Import</Trans>
</ButtonPrimary>
{addError ? (
<TYPE.error title={addError} style={{ textOverflow: 'ellipsis', overflow: 'hidden' }} error>
<ThemedText.Error title={addError} style={{ textOverflow: 'ellipsis', overflow: 'hidden' }} error>
{addError}
</TYPE.error>
</ThemedText.Error>
) : null}
</AutoColumn>
{/* </Card> */}

View File

@@ -10,7 +10,7 @@ import useTheme from 'hooks/useTheme'
import { CSSProperties } from 'react'
import { CheckCircle } from 'react-feather'
import styled from 'styled-components/macro'
import { TYPE } from 'theme'
import { ThemedText } from 'theme'
import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
@@ -67,16 +67,16 @@ export default function ImportRow({
<CurrencyLogo currency={token} size={'24px'} style={{ opacity: dim ? '0.6' : '1' }} />
<AutoColumn gap="4px" style={{ opacity: dim ? '0.6' : '1' }}>
<AutoRow>
<TYPE.body fontWeight={500}>{token.symbol}</TYPE.body>
<TYPE.darkGray ml="8px" fontWeight={300}>
<ThemedText.Body fontWeight={500}>{token.symbol}</ThemedText.Body>
<ThemedText.DarkGray ml="8px" fontWeight={300}>
<NameOverflow title={token.name}>{token.name}</NameOverflow>
</TYPE.darkGray>
</ThemedText.DarkGray>
</AutoRow>
{list && list.logoURI && (
<RowFixed>
<TYPE.small mr="4px" color={theme.text3}>
<ThemedText.Small mr="4px" color={theme.text3}>
<Trans>via {list.name} </Trans>
</TYPE.small>
</ThemedText.Small>
<ListLogo logoURI={list.logoURI} size="12px" />
</RowFixed>
)}
@@ -97,9 +97,9 @@ export default function ImportRow({
) : (
<RowFixed style={{ minWidth: 'fit-content' }}>
<CheckIcon />
<TYPE.main color={theme.green1}>
<ThemedText.Main color={theme.green1}>
<Trans>Active</Trans>
</TYPE.main>
</ThemedText.Main>
</RowFixed>
)}
</TokenSection>

View File

@@ -2,23 +2,19 @@ import { Plural, Trans } from '@lingui/macro'
import { Currency, Token } from '@uniswap/sdk-core'
import { TokenList } from '@uniswap/token-lists'
import { ButtonPrimary } from 'components/Button'
import Card from 'components/Card'
import { AutoColumn } from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo'
import ListLogo from 'components/ListLogo'
import { RowBetween, RowFixed } from 'components/Row'
import { RowBetween } from 'components/Row'
import { SectionBreak } from 'components/swap/styleds'
import { useUnsupportedTokens } from 'hooks/Tokens'
import useTheme from 'hooks/useTheme'
import { useActiveWeb3React } from 'hooks/web3'
import { transparentize } from 'polished'
import { AlertCircle, ArrowLeft } from 'react-feather'
import { useAddUserToken } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { CloseIcon, TYPE } from 'theme'
import { CloseIcon, ThemedText } from 'theme'
import { ExternalLink } from '../../theme/components'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import BlockedToken from './BlockedToken'
import { PaddedColumn } from './styleds'
import TokenImportCard from './TokenImportCard'
const Wrapper = styled.div`
position: relative;
@@ -26,21 +22,6 @@ const Wrapper = styled.div`
overflow: auto;
`
const WarningWrapper = styled(Card)<{ highWarning: boolean }>`
background-color: ${({ theme, highWarning }) =>
highWarning ? transparentize(0.8, theme.red1) : transparentize(0.8, theme.yellow2)};
width: fit-content;
`
const AddressText = styled(TYPE.blue)`
font-size: 12px;
word-break: break-all;
${({ theme }) => theme.mediaWidth.upToSmall`
font-size: 10px;
`}
`
interface ImportProps {
tokens: Token[]
list?: TokenList
@@ -49,21 +30,26 @@ interface ImportProps {
handleCurrencySelect?: (currency: Currency) => void
}
export function ImportToken({ tokens, list, onBack, onDismiss, handleCurrencySelect }: ImportProps) {
export function ImportToken(props: ImportProps) {
const { tokens, list, onBack, onDismiss, handleCurrencySelect } = props
const theme = useTheme()
const { chainId } = useActiveWeb3React()
const addToken = useAddUserToken()
const unsupportedTokens = useUnsupportedTokens()
const unsupportedSet = new Set(Object.keys(unsupportedTokens))
const intersection = new Set(tokens.filter((token) => unsupportedSet.has(token.address)))
if (intersection.size > 0) {
return <BlockedToken onBack={onBack} onDismiss={onDismiss} blockedTokens={Array.from(intersection)} />
}
return (
<Wrapper>
<PaddedColumn gap="14px" style={{ width: '100%', flex: '1 1' }}>
<RowBetween>
{onBack ? <ArrowLeft style={{ cursor: 'pointer' }} onClick={onBack} /> : <div />}
<TYPE.mediumHeader>
<ThemedText.MediumHeader>
<Plural value={tokens.length} one="Import token" other="Import tokens" />
</TYPE.mediumHeader>
</ThemedText.MediumHeader>
{onDismiss ? <CloseIcon onClick={onDismiss} /> : <div />}
</RowBetween>
</PaddedColumn>
@@ -71,59 +57,16 @@ export function ImportToken({ tokens, list, onBack, onDismiss, handleCurrencySel
<AutoColumn gap="md" style={{ marginBottom: '32px', padding: '1rem' }}>
<AutoColumn justify="center" style={{ textAlign: 'center', gap: '16px', padding: '1rem' }}>
<AlertCircle size={48} stroke={theme.text2} strokeWidth={1} />
<TYPE.body fontWeight={400} fontSize={16}>
<ThemedText.Body fontWeight={400} fontSize={16}>
<Trans>
This token doesn&apos;t appear on the active token list(s). Make sure this is the token that you want to
trade.
</Trans>
</TYPE.body>
</ThemedText.Body>
</AutoColumn>
{tokens.map((token) => {
return (
<Card
backgroundColor={theme.bg2}
key={'import' + token.address}
className=".token-warning-container"
padding="2rem"
>
<AutoColumn gap="10px" justify="center">
<CurrencyLogo currency={token} size={'32px'} />
<AutoColumn gap="4px" justify="center">
<TYPE.body ml="8px" mr="8px" fontWeight={500} fontSize={20}>
{token.symbol}
</TYPE.body>
<TYPE.darkGray fontWeight={400} fontSize={14}>
{token.name}
</TYPE.darkGray>
</AutoColumn>
{chainId && (
<ExternalLink href={getExplorerLink(chainId, token.address, ExplorerDataType.ADDRESS)}>
<AddressText fontSize={12}>{token.address}</AddressText>
</ExternalLink>
)}
{list !== undefined ? (
<RowFixed>
{list.logoURI && <ListLogo logoURI={list.logoURI} size="16px" />}
<TYPE.small ml="6px" fontSize={14} color={theme.text3}>
<Trans>via {list.name} token list</Trans>
</TYPE.small>
</RowFixed>
) : (
<WarningWrapper $borderRadius="4px" padding="4px" highWarning={true}>
<RowFixed>
<AlertCircle stroke={theme.red1} size="10px" />
<TYPE.body color={theme.red1} ml="4px" fontSize="10px" fontWeight={500}>
<Trans>Unknown Source</Trans>
</TYPE.body>
</RowFixed>
</WarningWrapper>
)}
</AutoColumn>
</Card>
)
})}
{tokens.map((token) => (
<TokenImportCard token={token} list={list} key={'import' + token.address} />
))}
<ButtonPrimary
altDisabledStyle={true}
$borderRadius="20px"

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line no-restricted-imports
import { t, Trans } from '@lingui/macro'
import { TokenList } from '@uniswap/token-lists'
import Card from 'components/Card'
@@ -17,7 +18,7 @@ import useTheme from '../../hooks/useTheme'
import useToggle from '../../hooks/useToggle'
import { acceptListUpdate, disableList, enableList, removeList } from '../../state/lists/actions'
import { useActiveListUrls, useAllLists, useIsListActive } from '../../state/lists/hooks'
import { ExternalLink, IconWrapper, LinkStyledButton, TYPE } from '../../theme'
import { ExternalLink, IconWrapper, LinkStyledButton, ThemedText } from '../../theme'
import listVersionLabel from '../../utils/listVersionLabel'
import { parseENSAddress } from '../../utils/parseENSAddress'
import uriToHttp from '../../utils/uriToHttp'
@@ -74,7 +75,7 @@ const StyledTitleText = styled.div<{ active: boolean }>`
color: ${({ theme, active }) => (active ? theme.white : theme.text2)};
`
const StyledListUrlText = styled(TYPE.main)<{ active: boolean }>`
const StyledListUrlText = styled(ThemedText.Main)<{ active: boolean }>`
font-size: 12px;
color: ${({ theme, active }) => (active ? theme.white : theme.text2)};
`
@@ -360,9 +361,9 @@ export function ManageLists({
/>
</Row>
{addError ? (
<TYPE.error title={addError} style={{ textOverflow: 'ellipsis', overflow: 'hidden' }} error>
<ThemedText.Error title={addError} style={{ textOverflow: 'ellipsis', overflow: 'hidden' }} error>
{addError}
</TYPE.error>
</ThemedText.Error>
) : null}
</PaddedColumn>
{tempList && (
@@ -372,10 +373,10 @@ export function ManageLists({
<RowFixed>
{tempList.logoURI && <ListLogo logoURI={tempList.logoURI} size="40px" />}
<AutoColumn gap="4px" style={{ marginLeft: '20px' }}>
<TYPE.body fontWeight={600}>{tempList.name}</TYPE.body>
<TYPE.main fontSize={'12px'}>
<ThemedText.Body fontWeight={600}>{tempList.name}</ThemedText.Body>
<ThemedText.Main fontSize={'12px'}>
<Trans>{tempList.tokens.length} tokens</Trans>
</TYPE.main>
</ThemedText.Main>
</AutoColumn>
</RowFixed>
{isImported ? (
@@ -383,9 +384,9 @@ export function ManageLists({
<IconWrapper stroke={theme.text2} size="16px" marginRight={'10px'}>
<CheckCircle />
</IconWrapper>
<TYPE.body color={theme.text2}>
<ThemedText.Body color={theme.text2}>
<Trans>Loaded</Trans>
</TYPE.body>
</ThemedText.Body>
</RowFixed>
) : (
<ButtonPrimary

View File

@@ -9,7 +9,7 @@ import { useActiveWeb3React } from 'hooks/web3'
import { RefObject, useCallback, useMemo, useRef, useState } from 'react'
import { useRemoveUserAddedToken, useUserAddedTokens } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { ButtonText, ExternalLink, ExternalLinkIcon, TrashIcon, TYPE } from 'theme'
import { ButtonText, ExternalLink, ExternalLinkIcon, ThemedText, TrashIcon } from 'theme'
import { isAddress } from 'utils'
import useTheme from '../../hooks/useTheme'
@@ -81,9 +81,9 @@ export default function ManageTokens({
<RowFixed>
<CurrencyLogo currency={token} size={'20px'} />
<ExternalLink href={getExplorerLink(chainId, token.address, ExplorerDataType.ADDRESS)}>
<TYPE.main ml={'10px'} fontWeight={600}>
<ThemedText.Main ml={'10px'} fontWeight={600}>
{token.symbol}
</TYPE.main>
</ThemedText.Main>
</ExternalLink>
</RowFixed>
<RowFixed>
@@ -111,9 +111,9 @@ export default function ManageTokens({
/>
</Row>
{searchQuery !== '' && !isAddressSearch && (
<TYPE.error error={true}>
<ThemedText.Error error={true}>
<Trans>Enter valid token address</Trans>
</TYPE.error>
</ThemedText.Error>
)}
{searchToken && (
<Card backgroundColor={theme.bg2} padding="10px 0">
@@ -129,14 +129,14 @@ export default function ManageTokens({
<Separator />
<PaddedColumn gap="lg" style={{ overflow: 'auto', marginBottom: '10px' }}>
<RowBetween>
<TYPE.main fontWeight={600}>
<ThemedText.Main fontWeight={600}>
<Trans>{userAddedTokens?.length} Custom Tokens</Trans>
</TYPE.main>
</ThemedText.Main>
{userAddedTokens.length > 0 && (
<ButtonText onClick={handleRemoveAll}>
<TYPE.blue>
<ThemedText.Blue>
<Trans>Clear all</Trans>
</TYPE.blue>
</ThemedText.Blue>
</ButtonText>
)}
</RowBetween>
@@ -144,9 +144,9 @@ export default function ManageTokens({
</PaddedColumn>
</Column>
<Footer>
<TYPE.darkGray>
<ThemedText.DarkGray>
<Trans>Tip: Custom tokens are stored locally in your browser</Trans>
</TYPE.darkGray>
</ThemedText.DarkGray>
</Footer>
</Wrapper>
)

View File

@@ -0,0 +1,76 @@
import { Trans } from '@lingui/macro'
import { Token } from '@uniswap/sdk-core'
import { TokenList } from '@uniswap/token-lists'
import Card from 'components/Card'
import { AutoColumn } from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo'
import ListLogo from 'components/ListLogo'
import { RowFixed } from 'components/Row'
import { useActiveWeb3React } from 'hooks/web3'
import { transparentize } from 'polished'
import { AlertCircle } from 'react-feather'
import styled, { useTheme } from 'styled-components/macro'
import { ExternalLink, ThemedText } from 'theme'
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
const WarningWrapper = styled(Card)<{ highWarning: boolean }>`
background-color: ${({ theme, highWarning }) =>
highWarning ? transparentize(0.8, theme.red1) : transparentize(0.8, theme.yellow2)};
width: fit-content;
`
const AddressText = styled(ThemedText.Blue)`
font-size: 12px;
word-break: break-all;
${({ theme }) => theme.mediaWidth.upToSmall`
font-size: 10px;
`}
`
interface TokenImportCardProps {
list?: TokenList
token: Token
}
const TokenImportCard = ({ list, token }: TokenImportCardProps) => {
const theme = useTheme()
const { chainId } = useActiveWeb3React()
return (
<Card backgroundColor={theme.bg2} padding="2rem">
<AutoColumn gap="10px" justify="center">
<CurrencyLogo currency={token} size={'32px'} />
<AutoColumn gap="4px" justify="center">
<ThemedText.Body ml="8px" mr="8px" fontWeight={500} fontSize={20}>
{token.symbol}
</ThemedText.Body>
<ThemedText.DarkGray fontWeight={400} fontSize={14}>
{token.name}
</ThemedText.DarkGray>
</AutoColumn>
{chainId && (
<ExternalLink href={getExplorerLink(chainId, token.address, ExplorerDataType.ADDRESS)}>
<AddressText fontSize={12}>{token.address}</AddressText>
</ExternalLink>
)}
{list !== undefined ? (
<RowFixed>
{list.logoURI && <ListLogo logoURI={list.logoURI} size="16px" />}
<ThemedText.Small ml="6px" fontSize={14} color={theme.text3}>
<Trans>via {list.name} token list</Trans>
</ThemedText.Small>
</RowFixed>
) : (
<WarningWrapper $borderRadius="4px" padding="4px" highWarning={true}>
<RowFixed>
<AlertCircle stroke={theme.red1} size="10px" />
<ThemedText.Body color={theme.red1} ml="4px" fontSize="10px" fontWeight={500}>
<Trans>Unknown Source</Trans>
</ThemedText.Body>
</RowFixed>
</WarningWrapper>
)}
</AutoColumn>
</Card>
)
}
export default TokenImportCard

View File

@@ -1,18 +1,19 @@
// eslint-disable-next-line no-restricted-imports
import { t, Trans } from '@lingui/macro'
import { Percent } from '@uniswap/sdk-core'
import { SupportedChainId } from 'constants/chains'
import { useActiveWeb3React } from 'hooks/web3'
import { useContext, useRef, useState } from 'react'
import { Settings, X } from 'react-feather'
import ReactGA from 'react-ga'
import { Text } from 'rebass'
import { AUTO_ROUTER_SUPPORTED_CHAINS } from 'state/routing/clientSideSmartOrderRouter/constants'
import styled, { ThemeContext } from 'styled-components/macro'
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
import { ApplicationModal } from '../../state/application/actions'
import { useModalOpen, useToggleSettingsMenu } from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'
import { useClientSideRouter, useExpertModeManager } from '../../state/user/hooks'
import { TYPE } from '../../theme'
import { ThemedText } from '../../theme'
import { ButtonError } from '../Button'
import { AutoColumn } from '../Column'
import Modal from '../Modal'
@@ -26,7 +27,7 @@ const StyledMenuIcon = styled(Settings)`
width: 20px;
> * {
stroke: ${({ theme }) => theme.text2};
stroke: ${({ theme }) => theme.text1};
}
:hover {
@@ -198,16 +199,13 @@ export default function SettingsTab({ placeholderSlippage }: { placeholderSlippa
<Text fontWeight={600} fontSize={14}>
<Trans>Interface Settings</Trans>
</Text>
{chainId === SupportedChainId.MAINNET && (
{chainId && AUTO_ROUTER_SUPPORTED_CHAINS.includes(chainId) && (
<RowBetween>
<RowFixed>
<TYPE.black fontWeight={400} fontSize={14} color={theme.text2}>
<Trans>Auto Router</Trans>
</TYPE.black>
<QuestionHelper
text={<Trans>Use the Uniswap Labs API to get better pricing through a more efficient route.</Trans>}
/>
<ThemedText.Black fontWeight={400} fontSize={14} color={theme.text2}>
<Trans>Auto Router API</Trans>
</ThemedText.Black>
<QuestionHelper text={<Trans>Use the Uniswap Labs API to get faster quotes.</Trans>} />
</RowFixed>
<Toggle
id="toggle-optimized-router-button"
@@ -222,12 +220,11 @@ export default function SettingsTab({ placeholderSlippage }: { placeholderSlippa
/>
</RowBetween>
)}
<RowBetween>
<RowFixed>
<TYPE.black fontWeight={400} fontSize={14} color={theme.text2}>
<ThemedText.Black fontWeight={400} fontSize={14} color={theme.text2}>
<Trans>Expert Mode</Trans>
</TYPE.black>
</ThemedText.Black>
<QuestionHelper
text={
<Trans>Allow high price impact trades and skip the confirm screen. Use at your own risk.</Trans>

View File

@@ -5,9 +5,9 @@ import styled from 'styled-components/macro'
import { DEFAULT_LOCALE, LOCALE_LABEL, SupportedLocale } from '../../constants/locales'
import { navigatorLocale, useActiveLocale } from '../../hooks/useActiveLocale'
import { StyledInternalLink, TYPE } from '../../theme'
import { StyledInternalLink, ThemedText } from '../../theme'
const Container = styled(TYPE.small)`
const Container = styled(ThemedText.Small)`
opacity: 0.6;
:hover {
opacity: 1;

View File

@@ -1,7 +1,7 @@
import { Trans } from '@lingui/macro'
import styled from 'styled-components/macro'
import { TYPE } from '../../theme'
import { ThemedText } from '../../theme'
const Wrapper = styled.button<{ isActive?: boolean; activeElement?: boolean }>`
border-radius: 20px;
@@ -25,7 +25,7 @@ const ToggleElement = styled.span<{ isActive?: boolean; bgColor?: string }>`
}
`
const StatusText = styled(TYPE.main)<{ isActive?: boolean }>`
const StatusText = styled(ThemedText.Main)<{ isActive?: boolean }>`
margin: 0 10px;
width: 24px;
color: ${({ theme, isActive }) => (isActive ? theme.text1 : theme.text3)};

View File

@@ -5,7 +5,7 @@ import styled from 'styled-components/macro'
import Popover, { PopoverProps } from '../Popover'
export const TooltipContainer = styled.div`
width: 256px;
max-width: 256px;
padding: 0.6rem 1rem;
font-weight: 400;
word-break: break-word;
@@ -25,6 +25,7 @@ interface TooltipContentProps extends Omit<PopoverProps, 'content'> {
onOpen?: () => void
// whether to wrap the content in a `TooltipContainer`
wrap?: boolean
disableHover?: boolean // disable the hover and content display
}
export default function Tooltip({ text, ...rest }: TooltipProps) {
@@ -52,6 +53,7 @@ export function MouseoverTooltipContent({
content,
children,
onOpen: openCallback = undefined,
disableHover,
...rest
}: Omit<TooltipContentProps, 'show'>) {
const [show, setShow] = useState(false)
@@ -61,7 +63,7 @@ export function MouseoverTooltipContent({
}, [openCallback])
const close = useCallback(() => setShow(false), [setShow])
return (
<TooltipContent {...rest} show={show} content={content}>
<TooltipContent {...rest} show={show} content={disableHover ? null : content}>
<div
style={{ display: 'inline-block', lineHeight: 0, padding: '0.25rem' }}
onMouseEnter={open}

View File

@@ -13,7 +13,7 @@ import Circle from '../../assets/images/blue-loader.svg'
import MetaMaskLogo from '../../assets/images/metamask.png'
import { useActiveWeb3React } from '../../hooks/web3'
import { ExternalLink } from '../../theme'
import { CloseIcon, CustomLightSpinner } from '../../theme/components'
import { CloseIcon, CustomLightSpinner } from '../../theme'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { TransactionSummary } from '../AccountDetails/TransactionSummary'
import { ButtonLight, ButtonPrimary } from '../Button'
@@ -284,11 +284,11 @@ function L2Content({
</Text>
</ExternalLink>
) : (
<div style={{ height: '17px' }}></div>
<div style={{ height: '17px' }} />
)}
<Text color={theme.text3} style={{ margin: '20px 0 0 0' }} fontSize={'14px'}>
{!secondsToConfirm ? (
<div style={{ height: '24px' }}></div>
<div style={{ height: '24px' }} />
) : (
<div>
<Trans>Transaction completed in </Trans>

View File

@@ -1,14 +1,15 @@
import { t, Trans } from '@lingui/macro'
import { Trans } from '@lingui/macro'
import { Percent } from '@uniswap/sdk-core'
import { L2_CHAIN_IDS } from 'constants/chains'
import { DEFAULT_DEADLINE_FROM_NOW } from 'constants/misc'
import { useActiveWeb3React } from 'hooks/web3'
import ms from 'ms.macro'
import { darken } from 'polished'
import { useContext, useState } from 'react'
import { useSetUserSlippageTolerance, useUserSlippageTolerance, useUserTransactionTTL } from 'state/user/hooks'
import styled, { ThemeContext } from 'styled-components/macro'
import { TYPE } from '../../theme'
import { ThemedText } from '../../theme'
import { AutoColumn } from '../Column'
import QuestionHelper from '../QuestionHelper'
import { RowBetween, RowFixed } from '../Row'
@@ -85,7 +86,7 @@ const OptionCustom = styled(FancyButton)<{ active?: boolean; warning?: boolean }
const SlippageEmojiContainer = styled.span`
color: #f3841e;
${({ theme }) => theme.mediaWidth.upToSmall`
display: none;
display: none;
`}
`
@@ -93,6 +94,8 @@ interface TransactionSettingsProps {
placeholderSlippage: Percent // varies according to the context in which the settings dialog is placed
}
const THREE_DAYS_IN_SECONDS = ms`3 days` / 1000
export default function TransactionSettings({ placeholderSlippage }: TransactionSettingsProps) {
const { chainId } = useActiveWeb3React()
const theme = useContext(ThemeContext)
@@ -142,7 +145,7 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction
} else {
try {
const parsed: number = Math.floor(Number.parseFloat(value) * 60)
if (!Number.isInteger(parsed) || parsed < 60 || parsed > 180 * 60) {
if (!Number.isInteger(parsed) || parsed < 60 || parsed > THREE_DAYS_IN_SECONDS) {
setDeadlineError(DeadlineError.InvalidInput)
} else {
setDeadline(parsed)
@@ -160,9 +163,9 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction
<AutoColumn gap="md">
<AutoColumn gap="sm">
<RowFixed>
<TYPE.black fontWeight={400} fontSize={14} color={theme.text2}>
<ThemedText.Black fontWeight={400} fontSize={14} color={theme.text2}>
<Trans>Slippage tolerance</Trans>
</TYPE.black>
</ThemedText.Black>
<QuestionHelper
text={
<Trans>Your transaction will revert if the price changes unfavorably by more than this percentage.</Trans>
@@ -229,11 +232,11 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction
{showCustomDeadlineRow && (
<AutoColumn gap="sm">
<RowFixed>
<TYPE.black fontSize={14} fontWeight={400} color={theme.text2}>
<ThemedText.Black fontSize={14} fontWeight={400} color={theme.text2}>
<Trans>Transaction deadline</Trans>
</TYPE.black>
</ThemedText.Black>
<QuestionHelper
text={t`Your transaction will revert if it is pending for more than this period of time.`}
text={<Trans>Your transaction will revert if it is pending for more than this period of time.</Trans>}
/>
</RowFixed>
<RowFixed>
@@ -255,9 +258,9 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction
color={deadlineError ? 'red' : ''}
/>
</OptionCustom>
<TYPE.body style={{ paddingLeft: '8px' }} fontSize={14}>
<ThemedText.Body style={{ paddingLeft: '8px' }} fontSize={14}>
<Trans>minutes</Trans>
</TYPE.body>
</ThemedText.Body>
</RowFixed>
</AutoColumn>
)}

View File

@@ -2,8 +2,12 @@ import { Trans } from '@lingui/macro'
import { AbstractConnector } from '@web3-react/abstract-connector'
import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core'
import { WalletConnectConnector } from '@web3-react/walletconnect-connector'
import { AutoRow } from 'components/Row'
import { AutoColumn } from 'components/Column'
import { PrivacyPolicy } from 'components/PrivacyPolicy'
import Row, { AutoRow, RowBetween } from 'components/Row'
import { useWalletConnectMonitoringEventCallback } from 'hooks/useMonitoringEventCallback'
import { useEffect, useState } from 'react'
import { ArrowLeft, ArrowRight, Info } from 'react-feather'
import ReactGA from 'react-ga'
import styled from 'styled-components/macro'
@@ -13,12 +17,12 @@ import { fortmatic, injected, portis } from '../../connectors'
import { OVERLAY_READY } from '../../connectors/Fortmatic'
import { SUPPORTED_WALLETS } from '../../constants/wallet'
import usePrevious from '../../hooks/usePrevious'
import { ApplicationModal } from '../../state/application/actions'
import { useModalOpen, useWalletModalToggle } from '../../state/application/hooks'
import { ExternalLink, TYPE } from '../../theme'
import { ApplicationModal } from '../../state/application/reducer'
import { ExternalLink, ThemedText } from '../../theme'
import { isMobile } from '../../utils/userAgent'
import AccountDetails from '../AccountDetails'
import { LightCard } from '../Card'
import Card, { LightCard } from '../Card'
import Modal from '../Modal'
import Option from './Option'
import PendingView from './PendingView'
@@ -105,11 +109,22 @@ const HoverText = styled.div`
}
`
const LinkCard = styled(Card)`
background-color: ${({ theme }) => theme.bg1};
color: ${({ theme }) => theme.white};
:hover {
cursor: pointer;
filter: brightness(0.9);
}
`
const WALLET_VIEWS = {
OPTIONS: 'options',
OPTIONS_SECONDARY: 'options_secondary',
ACCOUNT: 'account',
PENDING: 'pending',
LEGAL: 'legal',
}
export default function WalletModal({
@@ -125,6 +140,7 @@ export default function WalletModal({
const { active, account, connector, activate, error } = useWeb3React()
const [walletView, setWalletView] = useState(WALLET_VIEWS.ACCOUNT)
const previousWalletView = usePrevious(walletView)
const [pendingWallet, setPendingWallet] = useState<AbstractConnector | undefined>()
@@ -135,6 +151,8 @@ export default function WalletModal({
const previousAccount = usePrevious(account)
const logMonitoringEvent = useWalletConnectMonitoringEventCallback()
// close on connection, when logged out before
useEffect(() => {
if (account && !previousAccount && walletModalOpen) {
@@ -177,18 +195,23 @@ export default function WalletModal({
setWalletView(WALLET_VIEWS.PENDING)
// if the connector is walletconnect and the user has already tried to connect, manually reset the connector
if (connector instanceof WalletConnectConnector && connector.walletConnectProvider?.wc?.uri) {
if (connector instanceof WalletConnectConnector) {
connector.walletConnectProvider = undefined
}
connector &&
activate(connector, undefined, true).catch((error) => {
if (error instanceof UnsupportedChainIdError) {
activate(connector) // a little janky...can't use setError because the connector isn't set
} else {
setPendingError(true)
}
})
activate(connector, undefined, true)
.then(async () => {
const walletAddress = await connector.getAccount()
logMonitoringEvent({ walletAddress })
})
.catch((error) => {
if (error instanceof UnsupportedChainIdError) {
activate(connector) // a little janky...can't use setError because the connector isn't set
} else {
setPendingError(true)
}
})
}
// close wallet modal if fortmatic modal is active
@@ -306,6 +329,30 @@ export default function WalletModal({
</UpperSection>
)
}
if (walletView === WALLET_VIEWS.LEGAL) {
return (
<UpperSection>
<HeaderRow>
<HoverText
onClick={() => {
setWalletView(
(previousWalletView === WALLET_VIEWS.LEGAL ? WALLET_VIEWS.ACCOUNT : previousWalletView) ??
WALLET_VIEWS.ACCOUNT
)
}}
>
<ArrowLeft />
</HoverText>
<Row justify="center">
<ThemedText.MediumHeader>
<Trans>Legal & Privacy</Trans>
</ThemedText.MediumHeader>
</Row>
</HeaderRow>
<PrivacyPolicy />
</UpperSection>
)
}
if (account && walletView === WALLET_VIEWS.ACCOUNT) {
return (
<AccountDetails
@@ -330,40 +377,53 @@ export default function WalletModal({
setWalletView(WALLET_VIEWS.ACCOUNT)
}}
>
<Trans>Back</Trans>
<ArrowLeft />
</HoverText>
</HeaderRow>
) : (
<HeaderRow>
<HoverText>
<Trans>Connect to a wallet</Trans>
<Trans>Connect a wallet</Trans>
</HoverText>
</HeaderRow>
)}
<ContentWrapper>
<LightCard style={{ marginBottom: '16px' }}>
<AutoRow style={{ flexWrap: 'nowrap' }}>
<TYPE.main fontSize={14}>
<Trans>
By connecting a wallet, you agree to Uniswap Labs{' '}
<ExternalLink href="https://uniswap.org/terms-of-service/">Terms of Service</ExternalLink> and
acknowledge that you have read and understand the{' '}
<ExternalLink href="https://uniswap.org/disclaimer/">Uniswap protocol disclaimer</ExternalLink>.
</Trans>
</TYPE.main>
</AutoRow>
</LightCard>
{walletView === WALLET_VIEWS.PENDING ? (
<PendingView
connector={pendingWallet}
error={pendingError}
setPendingError={setPendingError}
tryActivation={tryActivation}
/>
) : (
<OptionGrid>{getOptions()}</OptionGrid>
)}
<AutoColumn gap="16px">
<LightCard>
<AutoRow style={{ flexWrap: 'nowrap' }}>
<ThemedText.Black fontSize={14}>
<Trans>
By connecting a wallet, you agree to Uniswap Labs{' '}
<ExternalLink href="https://uniswap.org/terms-of-service/">Terms of Service</ExternalLink> and
acknowledge that you have read and understand the Uniswap{' '}
<ExternalLink href="https://uniswap.org/disclaimer/">Protocol Disclaimer</ExternalLink>.
</Trans>
</ThemedText.Black>
</AutoRow>
</LightCard>
{walletView === WALLET_VIEWS.PENDING ? (
<PendingView
connector={pendingWallet}
error={pendingError}
setPendingError={setPendingError}
tryActivation={tryActivation}
/>
) : (
<OptionGrid>{getOptions()}</OptionGrid>
)}
<LinkCard padding=".5rem" $borderRadius=".75rem" onClick={() => setWalletView(WALLET_VIEWS.LEGAL)}>
<RowBetween>
<AutoRow gap="4px">
<Info size={20} />
<ThemedText.White fontSize={14}>
<Trans>How this app uses APIs</Trans>
</ThemedText.White>
</AutoRow>
<ArrowRight size={16} />
</RowBetween>
</LinkCard>
</AutoColumn>
</ContentWrapper>
</UpperSection>
)

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line no-restricted-imports
import { t, Trans } from '@lingui/macro'
import { AbstractConnector } from '@web3-react/abstract-connector'
import { UnsupportedChainIdError, useWeb3React } from '@web3-react/core'
@@ -6,11 +7,6 @@ import { useMemo } from 'react'
import { Activity } from 'react-feather'
import styled, { css } from 'styled-components/macro'
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
import FortmaticIcon from '../../assets/images/fortmaticIcon.png'
import PortisIcon from '../../assets/images/portisIcon.png'
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
import { fortmatic, injected, portis, walletconnect, walletlink } from '../../connectors'
import { NetworkContextName } from '../../constants/misc'
import useENSName from '../../hooks/useENSName'
import { useHasSocks } from '../../hooks/useSocksBalance'
@@ -19,7 +15,7 @@ import { isTransactionRecent, useAllTransactions } from '../../state/transaction
import { TransactionDetails } from '../../state/transactions/reducer'
import { shortenAddress } from '../../utils'
import { ButtonSecondary } from '../Button'
import Identicon from '../Identicon'
import StatusIcon from '../Identicon/StatusIcon'
import Loader from '../Loader'
import { RowBetween } from '../Row'
import WalletModal from '../WalletModal'
@@ -131,36 +127,12 @@ function Sock() {
)
}
// eslint-disable-next-line react/prop-types
function StatusIcon({ connector }: { connector: AbstractConnector }) {
if (connector === injected) {
return <Identicon />
} else if (connector === walletconnect) {
return (
<IconWrapper size={16}>
<img src={WalletConnectIcon} alt={'WalletConnect'} />
</IconWrapper>
)
} else if (connector === walletlink) {
return (
<IconWrapper size={16}>
<img src={CoinbaseWalletIcon} alt={'CoinbaseWallet'} />
</IconWrapper>
)
} else if (connector === fortmatic) {
return (
<IconWrapper size={16}>
<img src={FortmaticIcon} alt={'Fortmatic'} />
</IconWrapper>
)
} else if (connector === portis) {
return (
<IconWrapper size={16}>
<img src={PortisIcon} alt={'Portis'} />
</IconWrapper>
)
}
return null
function WrappedStatusIcon({ connector }: { connector: AbstractConnector }) {
return (
<IconWrapper size={16}>
<StatusIcon connector={connector} />
</IconWrapper>
)
}
function Web3StatusInner() {
@@ -197,7 +169,7 @@ function Web3StatusInner() {
<Text>{ENSName || shortenAddress(account)}</Text>
</>
)}
{!hasPendingTransactions && connector && <StatusIcon connector={connector} />}
{!hasPendingTransactions && connector && <WrappedStatusIcon connector={connector} />}
</Web3StatusConnected>
)
} else if (error) {
@@ -211,7 +183,7 @@ function Web3StatusInner() {
return (
<Web3StatusConnect id="connect-wallet" onClick={toggleWalletModal} faded={!account}>
<Text>
<Trans>Connect to a wallet</Trans>
<Trans>Connect Wallet</Trans>
</Text>
</Web3StatusConnect>
)

View File

@@ -24,7 +24,8 @@ export default function GoogleAnalyticsReporter({ location: { pathname, search }
const { chainId } = useActiveWeb3React()
useEffect(() => {
ReactGA.set({ ['Chain ID']: chainId ?? 0 })
// cd1 - custom dimension 1 - chainId
ReactGA.set({ cd1: chainId ?? 0 })
}, [chainId])
useEffect(() => {

View File

@@ -11,7 +11,7 @@ import useENS from '../../hooks/useENS'
import { useActiveWeb3React } from '../../hooks/web3'
import { useClaimCallback, useUserHasAvailableClaim, useUserUnclaimedAmount } from '../../state/claim/hooks'
import { useIsTransactionPending } from '../../state/transactions/hooks'
import { CloseIcon, CustomLightSpinner, ExternalLink, TYPE, UniTokenAnimated } from '../../theme'
import { CloseIcon, CustomLightSpinner, ExternalLink, ThemedText, UniTokenAnimated } from '../../theme'
import { shortenAddress } from '../../utils'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import AddressInputPanel from '../AddressInputPanel'
@@ -105,29 +105,29 @@ export default function AddressClaimModal({ isOpen, onDismiss }: { isOpen: boole
<CardNoise />
<CardSection gap="md">
<RowBetween>
<TYPE.white fontWeight={500}>
<ThemedText.White fontWeight={500}>
<Trans>Claim UNI Token</Trans>
</TYPE.white>
</ThemedText.White>
<CloseIcon onClick={wrappedOnDismiss} style={{ zIndex: 99 }} stroke="white" />
</RowBetween>
<TYPE.white fontWeight={700} fontSize={36}>
<ThemedText.White fontWeight={700} fontSize={36}>
<Trans>{unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} UNI</Trans>
</TYPE.white>
</ThemedText.White>
</CardSection>
<Break />
</ModalUpper>
<AutoColumn gap="md" style={{ padding: '1rem', paddingTop: '0' }} justify="center">
<TYPE.subHeader fontWeight={500}>
<ThemedText.SubHeader fontWeight={500}>
<Trans>
Enter an address to trigger a UNI claim. If the address has any claimable UNI it will be sent to them on
submission.
</Trans>
</TYPE.subHeader>
</ThemedText.SubHeader>
<AddressInputPanel value={typed} onChange={handleRecipientType} />
{parsedAddress && !hasAvailableClaim && (
<TYPE.error error={true}>
<ThemedText.Error error={true}>
<Trans>Address has no available claim</Trans>
</TYPE.error>
</ThemedText.Error>
)}
<ButtonPrimary
disabled={!isAddress(parsedAddress ?? '') || !hasAvailableClaim}
@@ -159,23 +159,23 @@ export default function AddressClaimModal({ isOpen, onDismiss }: { isOpen: boole
</ConfirmedIcon>
<AutoColumn gap="100px" justify={'center'}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader fontWeight={600} color="black">
<ThemedText.LargeHeader fontWeight={600} color="black">
{claimConfirmed ? <Trans>Claimed</Trans> : <Trans>Claiming</Trans>}
</TYPE.largeHeader>
</ThemedText.LargeHeader>
{!claimConfirmed && (
<Text fontSize={36} color={'#ff007a'} fontWeight={800}>
<Trans>{unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} UNI</Trans>
</Text>
)}
{parsedAddress && (
<TYPE.largeHeader fontWeight={600} color="black">
<ThemedText.LargeHeader fontWeight={600} color="black">
<Trans>for {shortenAddress(parsedAddress)}</Trans>
</TYPE.largeHeader>
</ThemedText.LargeHeader>
)}
</AutoColumn>
{claimConfirmed && (
<>
<TYPE.subHeader fontWeight={500} color="black">
<ThemedText.SubHeader fontWeight={500} color="black">
<span role="img" aria-label="party-hat">
🎉{' '}
</span>
@@ -183,13 +183,13 @@ export default function AddressClaimModal({ isOpen, onDismiss }: { isOpen: boole
<span role="img" aria-label="party-hat">
🎉
</span>
</TYPE.subHeader>
</ThemedText.SubHeader>
</>
)}
{attempting && !hash && (
<TYPE.subHeader color="black">
<ThemedText.SubHeader color="black">
<Trans>Confirm this transaction in your wallet</Trans>
</TYPE.subHeader>
</ThemedText.SubHeader>
)}
{attempting && hash && !claimConfirmed && chainId && hash && (
<ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)} style={{ zIndex: 99 }}>

View File

@@ -9,11 +9,11 @@ import styled from 'styled-components/macro'
import Circle from '../../assets/images/blue-loader.svg'
import tokenLogo from '../../assets/images/token-logo.png'
import { useActiveWeb3React } from '../../hooks/web3'
import { ApplicationModal } from '../../state/application/actions'
import { useModalOpen, useToggleSelfClaimModal } from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'
import { useClaimCallback, useUserClaimData, useUserUnclaimedAmount } from '../../state/claim/hooks'
import { useUserHasSubmittedClaim } from '../../state/transactions/hooks'
import { CloseIcon, CustomLightSpinner, ExternalLink, TYPE, UniTokenAnimated } from '../../theme'
import { CloseIcon, CustomLightSpinner, ExternalLink, ThemedText, UniTokenAnimated } from '../../theme'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { ButtonPrimary } from '../Button'
import { AutoColumn, ColumnCenter } from '../Column'
@@ -100,63 +100,63 @@ export default function ClaimModal() {
<CardNoise />
<CardSection gap="md">
<RowBetween>
<TYPE.white fontWeight={500}>
<ThemedText.White fontWeight={500}>
<Trans>Claim UNI</Trans>
</TYPE.white>
</ThemedText.White>
<CloseIcon onClick={toggleClaimModal} style={{ zIndex: 99 }} color="white" />
</RowBetween>
<TYPE.white fontWeight={700} fontSize={36}>
<ThemedText.White fontWeight={700} fontSize={36}>
<Trans>{unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} UNI</Trans>
</TYPE.white>
</ThemedText.White>
</CardSection>
<Break />
<CardSection gap="sm">
{userClaimData?.flags?.isSOCKS && (
<RowBetween>
<TYPE.subHeader color="white">SOCKS</TYPE.subHeader>
<TYPE.subHeader color="white">
<ThemedText.SubHeader color="white">SOCKS</ThemedText.SubHeader>
<ThemedText.SubHeader color="white">
<Trans>{SOCKS_AMOUNT} UNI</Trans>
</TYPE.subHeader>
</ThemedText.SubHeader>
</RowBetween>
)}
{userClaimData?.flags?.isLP &&
unclaimedAmount &&
JSBI.greaterThanOrEqual(unclaimedAmount.quotient, nonLPAmount) && (
<RowBetween>
<TYPE.subHeader color="white">
<ThemedText.SubHeader color="white">
<Trans>Liquidity</Trans>
</TYPE.subHeader>
<TYPE.subHeader color="white">
</ThemedText.SubHeader>
<ThemedText.SubHeader color="white">
<Trans>
{unclaimedAmount
.subtract(CurrencyAmount.fromRawAmount(unclaimedAmount.currency, nonLPAmount))
.toFixed(0, { groupSeparator: ',' })}{' '}
UNI
</Trans>
</TYPE.subHeader>
</ThemedText.SubHeader>
</RowBetween>
)}
{userClaimData?.flags?.isUser && (
<RowBetween>
<TYPE.subHeader color="white">
<ThemedText.SubHeader color="white">
<Trans>User</Trans>
</TYPE.subHeader>
<TYPE.subHeader color="white">
</ThemedText.SubHeader>
<ThemedText.SubHeader color="white">
<Trans>{USER_AMOUNT} UNI</Trans>
</TYPE.subHeader>
</ThemedText.SubHeader>
</RowBetween>
)}
</CardSection>
</ModalUpper>
<AutoColumn gap="md" style={{ padding: '1rem', paddingTop: '0' }} justify="center">
<TYPE.subHeader fontWeight={500}>
<ThemedText.SubHeader fontWeight={500}>
<Trans>
As a member of the Uniswap community you may claim UNI to be used for voting and governance.
<br />
<br />
<ExternalLink href="https://uniswap.org/blog/uni">Read more about UNI</ExternalLink>
</Trans>
</TYPE.subHeader>
</ThemedText.SubHeader>
<ButtonPrimary
disabled={!isAddress(account ?? '')}
padding="16px 16px"
@@ -187,9 +187,9 @@ export default function ClaimModal() {
</ConfirmedIcon>
<AutoColumn gap="100px" justify={'center'}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader fontWeight={600} color="black">
<ThemedText.LargeHeader fontWeight={600} color="black">
{claimConfirmed ? <Trans>Claimed!</Trans> : <Trans>Claiming</Trans>}
</TYPE.largeHeader>
</ThemedText.LargeHeader>
{!claimConfirmed && (
<Text fontSize={36} color={'#ff007a'} fontWeight={800}>
<Trans>{unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} UNI</Trans>
@@ -198,7 +198,7 @@ export default function ClaimModal() {
</AutoColumn>
{claimConfirmed && (
<>
<TYPE.subHeader fontWeight={500} color="black">
<ThemedText.SubHeader fontWeight={500} color="black">
<Trans>
<span role="img" aria-label="party-hat">
🎉{' '}
@@ -208,13 +208,13 @@ export default function ClaimModal() {
🎉
</span>
</Trans>
</TYPE.subHeader>
</ThemedText.SubHeader>
</>
)}
{attempting && !claimSubmitted && (
<TYPE.subHeader color="black">
<ThemedText.SubHeader color="black">
<Trans>Confirm this transaction in your wallet</Trans>
</TYPE.subHeader>
</ThemedText.SubHeader>
)}
{attempting && claimSubmitted && !claimConfirmed && chainId && claimTxn?.hash && (
<ExternalLink

View File

@@ -1,6 +1,6 @@
import { TransactionResponse } from '@ethersproject/providers'
import { t, Trans } from '@lingui/macro'
import { useState } from 'react'
import { Trans } from '@lingui/macro'
import { ReactNode, useState } from 'react'
import styled from 'styled-components/macro'
import { useStakingContract } from '../../hooks/useContract'
@@ -8,7 +8,7 @@ import { useActiveWeb3React } from '../../hooks/web3'
import { StakingInfo } from '../../state/stake/hooks'
import { TransactionType } from '../../state/transactions/actions'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { CloseIcon, TYPE } from '../../theme'
import { CloseIcon, ThemedText } from '../../theme'
import { ButtonError } from '../Button'
import { AutoColumn } from '../Column'
import Modal from '../Modal'
@@ -61,12 +61,12 @@ export default function ClaimRewardModal({ isOpen, onDismiss, stakingInfo }: Sta
}
}
let error: string | undefined
let error: ReactNode | undefined
if (!account) {
error = t`Connect Wallet`
error = <Trans>Connect Wallet</Trans>
}
if (!stakingInfo?.stakedAmount) {
error = error ?? t`Enter an amount`
error = error ?? <Trans>Enter an amount</Trans>
}
return (
@@ -74,24 +74,24 @@ export default function ClaimRewardModal({ isOpen, onDismiss, stakingInfo }: Sta
{!attempting && !hash && (
<ContentWrapper gap="lg">
<RowBetween>
<TYPE.mediumHeader>
<ThemedText.MediumHeader>
<Trans>Claim</Trans>
</TYPE.mediumHeader>
</ThemedText.MediumHeader>
<CloseIcon onClick={wrappedOnDismiss} />
</RowBetween>
{stakingInfo?.earnedAmount && (
<AutoColumn justify="center" gap="md">
<TYPE.body fontWeight={600} fontSize={36}>
<ThemedText.Body fontWeight={600} fontSize={36}>
{stakingInfo?.earnedAmount?.toSignificant(6)}
</TYPE.body>
<TYPE.body>
</ThemedText.Body>
<ThemedText.Body>
<Trans>Unclaimed UNI</Trans>
</TYPE.body>
</ThemedText.Body>
</AutoColumn>
)}
<TYPE.subHeader style={{ textAlign: 'center' }}>
<ThemedText.SubHeader style={{ textAlign: 'center' }}>
<Trans>When you claim without withdrawing your liquidity remains in the mining pool.</Trans>
</TYPE.subHeader>
</ThemedText.SubHeader>
<ButtonError disabled={!!error} error={!!error && !!stakingInfo?.stakedAmount} onClick={onClaimReward}>
{error ?? <Trans>Claim</Trans>}
</ButtonError>
@@ -100,21 +100,21 @@ export default function ClaimRewardModal({ isOpen, onDismiss, stakingInfo }: Sta
{attempting && !hash && (
<LoadingView onDismiss={wrappedOnDismiss}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.body fontSize={20}>
<ThemedText.Body fontSize={20}>
<Trans>Claiming {stakingInfo?.earnedAmount?.toSignificant(6)} UNI</Trans>
</TYPE.body>
</ThemedText.Body>
</AutoColumn>
</LoadingView>
)}
{hash && (
<SubmittedView onDismiss={wrappedOnDismiss} hash={hash}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader>
<ThemedText.LargeHeader>
<Trans>Transaction Submitted</Trans>
</TYPE.largeHeader>
<TYPE.body fontSize={20}>
</ThemedText.LargeHeader>
<ThemedText.Body fontSize={20}>
<Trans>Claimed UNI!</Trans>
</TYPE.body>
</ThemedText.Body>
</AutoColumn>
</SubmittedView>
)}

View File

@@ -9,7 +9,7 @@ import { useTotalSupply } from '../../hooks/useTotalSupply'
import useUSDCPrice from '../../hooks/useUSDCPrice'
import { useV2Pair } from '../../hooks/useV2Pairs'
import { StakingInfo } from '../../state/stake/hooks'
import { StyledInternalLink, TYPE } from '../../theme'
import { StyledInternalLink, ThemedText } from '../../theme'
import { currencyId } from '../../utils/currencyId'
import { unwrappedToken } from '../../utils/unwrappedToken'
import { ButtonPrimary } from '../Button'
@@ -115,9 +115,9 @@ export default function PoolCard({ stakingInfo }: { stakingInfo: StakingInfo })
<TopSection>
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} size={24} />
<TYPE.white fontWeight={600} fontSize={24} style={{ marginLeft: '8px' }}>
<ThemedText.White fontWeight={600} fontSize={24} style={{ marginLeft: '8px' }}>
{currency0.symbol}-{currency1.symbol}
</TYPE.white>
</ThemedText.White>
<StyledInternalLink to={`/uni/${currencyId(currency0)}/${currencyId(currency1)}`} style={{ width: '100%' }}>
<ButtonPrimary padding="8px" $borderRadius="8px">
@@ -128,22 +128,22 @@ export default function PoolCard({ stakingInfo }: { stakingInfo: StakingInfo })
<StatContainer>
<RowBetween>
<TYPE.white>
<ThemedText.White>
<Trans>Total deposited</Trans>
</TYPE.white>
<TYPE.white>
</ThemedText.White>
<ThemedText.White>
{valueOfTotalStakedAmountInUSDC ? (
<Trans>${valueOfTotalStakedAmountInUSDC.toFixed(0, { groupSeparator: ',' })}</Trans>
) : (
<Trans>{valueOfTotalStakedAmountInWETH?.toSignificant(4, { groupSeparator: ',' }) ?? '-'} ETH</Trans>
)}
</TYPE.white>
</ThemedText.White>
</RowBetween>
<RowBetween>
<TYPE.white>
<ThemedText.White>
<Trans>Pool rate</Trans>
</TYPE.white>
<TYPE.white>
</ThemedText.White>
<ThemedText.White>
{stakingInfo ? (
stakingInfo.active ? (
<Trans>
@@ -156,7 +156,7 @@ export default function PoolCard({ stakingInfo }: { stakingInfo: StakingInfo })
) : (
'-'
)}
</TYPE.white>
</ThemedText.White>
</RowBetween>
</StatContainer>
@@ -164,13 +164,13 @@ export default function PoolCard({ stakingInfo }: { stakingInfo: StakingInfo })
<>
<Break />
<BottomSection showBackground={true}>
<TYPE.black color={'white'} fontWeight={500}>
<ThemedText.Black color={'white'} fontWeight={500}>
<span>
<Trans>Your rate</Trans>
</span>
</TYPE.black>
</ThemedText.Black>
<TYPE.black style={{ textAlign: 'right' }} color={'white'} fontWeight={500}>
<ThemedText.Black style={{ textAlign: 'right' }} color={'white'} fontWeight={500}>
<span role="img" aria-label="wizard-icon" style={{ marginRight: '0.5rem' }}>
</span>
@@ -188,7 +188,7 @@ export default function PoolCard({ stakingInfo }: { stakingInfo: StakingInfo })
) : (
'-'
)}
</TYPE.black>
</ThemedText.Black>
</BottomSection>
</>
)}

View File

@@ -13,7 +13,7 @@ import { useActiveWeb3React } from '../../hooks/web3'
import { StakingInfo, useDerivedStakeInfo } from '../../state/stake/hooks'
import { TransactionType } from '../../state/transactions/actions'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { CloseIcon, TYPE } from '../../theme'
import { CloseIcon, ThemedText } from '../../theme'
import { formatCurrencyAmount } from '../../utils/formatCurrencyAmount'
import { maxAmountSpend } from '../../utils/maxAmountSpend'
import { ButtonConfirmed, ButtonError } from '../Button'
@@ -159,9 +159,9 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui
{!attempting && !hash && (
<ContentWrapper gap="lg">
<RowBetween>
<TYPE.mediumHeader>
<ThemedText.MediumHeader>
<Trans>Deposit</Trans>
</TYPE.mediumHeader>
</ThemedText.MediumHeader>
<CloseIcon onClick={wrappedOnDismiss} />
</RowBetween>
<CurrencyInputPanel
@@ -178,19 +178,19 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui
<HypotheticalRewardRate dim={!hypotheticalRewardRate.greaterThan('0')}>
<div>
<TYPE.black fontWeight={600}>
<ThemedText.Black fontWeight={600}>
<Trans>Weekly Rewards</Trans>
</TYPE.black>
</ThemedText.Black>
</div>
<TYPE.black>
<ThemedText.Black>
<Trans>
{hypotheticalRewardRate
.multiply((60 * 60 * 24 * 7).toString())
.toSignificant(4, { groupSeparator: ',' })}{' '}
UNI / week
</Trans>
</TYPE.black>
</ThemedText.Black>
</HypotheticalRewardRate>
<RowBetween>
@@ -216,24 +216,24 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui
{attempting && !hash && (
<LoadingView onDismiss={wrappedOnDismiss}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader>
<ThemedText.LargeHeader>
<Trans>Depositing Liquidity</Trans>
</TYPE.largeHeader>
<TYPE.body fontSize={20}>
</ThemedText.LargeHeader>
<ThemedText.Body fontSize={20}>
<Trans>{parsedAmount?.toSignificant(4)} UNI-V2</Trans>
</TYPE.body>
</ThemedText.Body>
</AutoColumn>
</LoadingView>
)}
{attempting && hash && (
<SubmittedView onDismiss={wrappedOnDismiss} hash={hash}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader>
<ThemedText.LargeHeader>
<Trans>Transaction Submitted</Trans>
</TYPE.largeHeader>
<TYPE.body fontSize={20}>
</ThemedText.LargeHeader>
<ThemedText.Body fontSize={20}>
<Trans>Deposited {parsedAmount?.toSignificant(4)} UNI-V2</Trans>
</TYPE.body>
</ThemedText.Body>
</AutoColumn>
</SubmittedView>
)}

View File

@@ -1,6 +1,6 @@
import { TransactionResponse } from '@ethersproject/providers'
import { t, Trans } from '@lingui/macro'
import { useState } from 'react'
import { Trans } from '@lingui/macro'
import { ReactNode, useState } from 'react'
import styled from 'styled-components/macro'
import { useStakingContract } from '../../hooks/useContract'
@@ -8,7 +8,7 @@ import { useActiveWeb3React } from '../../hooks/web3'
import { StakingInfo } from '../../state/stake/hooks'
import { TransactionType } from '../../state/transactions/actions'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { CloseIcon, TYPE } from '../../theme'
import { CloseIcon, ThemedText } from '../../theme'
import { ButtonError } from '../Button'
import { AutoColumn } from '../Column'
import FormattedCurrencyAmount from '../FormattedCurrencyAmount'
@@ -63,12 +63,12 @@ export default function UnstakingModal({ isOpen, onDismiss, stakingInfo }: Staki
}
}
let error: string | undefined
let error: ReactNode | undefined
if (!account) {
error = t`Connect a wallet`
error = <Trans>Connect a wallet</Trans>
}
if (!stakingInfo?.stakedAmount) {
error = error ?? t`Enter an amount`
error = error ?? <Trans>Enter an amount</Trans>
}
return (
@@ -76,34 +76,34 @@ export default function UnstakingModal({ isOpen, onDismiss, stakingInfo }: Staki
{!attempting && !hash && (
<ContentWrapper gap="lg">
<RowBetween>
<TYPE.mediumHeader>
<ThemedText.MediumHeader>
<Trans>Withdraw</Trans>
</TYPE.mediumHeader>
</ThemedText.MediumHeader>
<CloseIcon onClick={wrappedOndismiss} />
</RowBetween>
{stakingInfo?.stakedAmount && (
<AutoColumn justify="center" gap="md">
<TYPE.body fontWeight={600} fontSize={36}>
<ThemedText.Body fontWeight={600} fontSize={36}>
{<FormattedCurrencyAmount currencyAmount={stakingInfo.stakedAmount} />}
</TYPE.body>
<TYPE.body>
</ThemedText.Body>
<ThemedText.Body>
<Trans>Deposited liquidity:</Trans>
</TYPE.body>
</ThemedText.Body>
</AutoColumn>
)}
{stakingInfo?.earnedAmount && (
<AutoColumn justify="center" gap="md">
<TYPE.body fontWeight={600} fontSize={36}>
<ThemedText.Body fontWeight={600} fontSize={36}>
{<FormattedCurrencyAmount currencyAmount={stakingInfo?.earnedAmount} />}
</TYPE.body>
<TYPE.body>
</ThemedText.Body>
<ThemedText.Body>
<Trans>Unclaimed UNI</Trans>
</TYPE.body>
</ThemedText.Body>
</AutoColumn>
)}
<TYPE.subHeader style={{ textAlign: 'center' }}>
<ThemedText.SubHeader style={{ textAlign: 'center' }}>
<Trans>When you withdraw, your UNI is claimed and your liquidity is removed from the mining pool.</Trans>
</TYPE.subHeader>
</ThemedText.SubHeader>
<ButtonError disabled={!!error} error={!!error && !!stakingInfo?.stakedAmount} onClick={onWithdraw}>
{error ?? <Trans>Withdraw & Claim</Trans>}
</ButtonError>
@@ -112,27 +112,27 @@ export default function UnstakingModal({ isOpen, onDismiss, stakingInfo }: Staki
{attempting && !hash && (
<LoadingView onDismiss={wrappedOndismiss}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.body fontSize={20}>
<ThemedText.Body fontSize={20}>
<Trans>Withdrawing {stakingInfo?.stakedAmount?.toSignificant(4)} UNI-V2</Trans>
</TYPE.body>
<TYPE.body fontSize={20}>
</ThemedText.Body>
<ThemedText.Body fontSize={20}>
<Trans>Claiming {stakingInfo?.earnedAmount?.toSignificant(4)} UNI</Trans>
</TYPE.body>
</ThemedText.Body>
</AutoColumn>
</LoadingView>
)}
{hash && (
<SubmittedView onDismiss={wrappedOndismiss} hash={hash}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader>
<ThemedText.LargeHeader>
<Trans>Transaction Submitted</Trans>
</TYPE.largeHeader>
<TYPE.body fontSize={20}>
</ThemedText.LargeHeader>
<ThemedText.Body fontSize={20}>
<Trans>Withdrew UNI-V2!</Trans>
</TYPE.body>
<TYPE.body fontSize={20}>
</ThemedText.Body>
<ThemedText.Body fontSize={20}>
<Trans>Claimed UNI!</Trans>
</TYPE.body>
</ThemedText.Body>
</AutoColumn>
</SubmittedView>
)}

View File

@@ -1,22 +1,28 @@
import { Trans } from '@lingui/macro'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import Card from 'components/Card'
import { LoadingRows } from 'components/Loader/styled'
import { useActiveWeb3React } from 'hooks/web3'
import { useContext, useMemo } from 'react'
import { ThemeContext } from 'styled-components/macro'
import { InterfaceTrade } from 'state/routing/types'
import styled, { ThemeContext } from 'styled-components/macro'
import { TYPE } from '../../theme'
import { Separator, ThemedText } from '../../theme'
import { computeRealizedLPFeePercent } from '../../utils/prices'
import { AutoColumn } from '../Column'
import { RowBetween, RowFixed } from '../Row'
import FormattedPriceImpact from './FormattedPriceImpact'
import { TransactionDetailsLabel } from './styleds'
import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from './GasEstimateBadge'
const StyledCard = styled(Card)`
padding: 0;
`
interface AdvancedSwapDetailsProps {
trade?: V2Trade<Currency, Currency, TradeType> | V3Trade<Currency, Currency, TradeType>
trade?: InterfaceTrade<Currency, Currency, TradeType>
allowedSlippage: Percent
syncing?: boolean
hideRouteDiagram?: boolean
}
function TextWithLoadingPlaceholder({
@@ -39,74 +45,78 @@ function TextWithLoadingPlaceholder({
export function AdvancedSwapDetails({ trade, allowedSlippage, syncing = false }: AdvancedSwapDetailsProps) {
const theme = useContext(ThemeContext)
const { chainId } = useActiveWeb3React()
const { realizedLPFee, priceImpact } = useMemo(() => {
if (!trade) return { realizedLPFee: undefined, priceImpact: undefined }
const { expectedOutputAmount, priceImpact } = useMemo(() => {
if (!trade) return { expectedOutputAmount: undefined, priceImpact: undefined }
const expectedOutputAmount = trade.outputAmount
const realizedLpFeePercent = computeRealizedLPFeePercent(trade)
const realizedLPFee = trade.inputAmount.multiply(realizedLpFeePercent)
const priceImpact = trade.priceImpact.subtract(realizedLpFeePercent)
return { priceImpact, realizedLPFee }
return { expectedOutputAmount, priceImpact }
}, [trade])
return !trade ? null : (
<AutoColumn gap="8px">
<TransactionDetailsLabel fontWeight={500} fontSize={14}>
<Trans>Transaction Details</Trans>
</TransactionDetailsLabel>
<RowBetween>
<RowFixed>
<TYPE.subHeader color={theme.text1}>
<Trans>Liquidity Provider Fee</Trans>
</TYPE.subHeader>
</RowFixed>
<TextWithLoadingPlaceholder syncing={syncing} width={65}>
<TYPE.black textAlign="right" fontSize={14}>
{realizedLPFee ? `${realizedLPFee.toSignificant(4)} ${realizedLPFee.currency.symbol}` : '-'}
</TYPE.black>
</TextWithLoadingPlaceholder>
</RowBetween>
<RowBetween>
<RowFixed>
<TYPE.subHeader color={theme.text1}>
<Trans>Price Impact</Trans>
</TYPE.subHeader>
</RowFixed>
<TextWithLoadingPlaceholder syncing={syncing} width={50}>
<TYPE.black textAlign="right" fontSize={14}>
<FormattedPriceImpact priceImpact={priceImpact} />
</TYPE.black>
</TextWithLoadingPlaceholder>
</RowBetween>
<RowBetween>
<RowFixed>
<TYPE.subHeader color={theme.text1}>
<Trans>Allowed Slippage</Trans>
</TYPE.subHeader>
</RowFixed>
<TextWithLoadingPlaceholder syncing={syncing} width={45}>
<TYPE.black textAlign="right" fontSize={14}>
{allowedSlippage.toFixed(2)}%
</TYPE.black>
</TextWithLoadingPlaceholder>
</RowBetween>
<RowBetween>
<RowFixed>
<TYPE.subHeader color={theme.text1}>
{trade.tradeType === TradeType.EXACT_INPUT ? <Trans>Minimum received</Trans> : <Trans>Maximum sent</Trans>}
</TYPE.subHeader>
</RowFixed>
<TextWithLoadingPlaceholder syncing={syncing} width={70}>
<TYPE.black textAlign="right" fontSize={14}>
{trade.tradeType === TradeType.EXACT_INPUT
? `${trade.minimumAmountOut(allowedSlippage).toSignificant(6)} ${trade.outputAmount.currency.symbol}`
: `${trade.maximumAmountIn(allowedSlippage).toSignificant(6)} ${trade.inputAmount.currency.symbol}`}
</TYPE.black>
</TextWithLoadingPlaceholder>
</RowBetween>
</AutoColumn>
<StyledCard>
<AutoColumn gap="8px">
<RowBetween>
<RowFixed>
<ThemedText.SubHeader color={theme.text1}>
<Trans>Expected Output</Trans>
</ThemedText.SubHeader>
</RowFixed>
<TextWithLoadingPlaceholder syncing={syncing} width={65}>
<ThemedText.Black textAlign="right" fontSize={14}>
{expectedOutputAmount
? `${expectedOutputAmount.toSignificant(6)} ${expectedOutputAmount.currency.symbol}`
: '-'}
</ThemedText.Black>
</TextWithLoadingPlaceholder>
</RowBetween>
<RowBetween>
<RowFixed>
<ThemedText.SubHeader color={theme.text1}>
<Trans>Price Impact</Trans>
</ThemedText.SubHeader>
</RowFixed>
<TextWithLoadingPlaceholder syncing={syncing} width={50}>
<ThemedText.Black textAlign="right" fontSize={14}>
<FormattedPriceImpact priceImpact={priceImpact} />
</ThemedText.Black>
</TextWithLoadingPlaceholder>
</RowBetween>
<Separator />
<RowBetween>
<RowFixed style={{ marginRight: '20px' }}>
<ThemedText.SubHeader color={theme.text3}>
{trade.tradeType === TradeType.EXACT_INPUT ? (
<Trans>Minimum received</Trans>
) : (
<Trans>Maximum sent</Trans>
)}{' '}
<Trans>after slippage</Trans> ({allowedSlippage.toFixed(2)}%)
</ThemedText.SubHeader>
</RowFixed>
<TextWithLoadingPlaceholder syncing={syncing} width={70}>
<ThemedText.Black textAlign="right" fontSize={14} color={theme.text3}>
{trade.tradeType === TradeType.EXACT_INPUT
? `${trade.minimumAmountOut(allowedSlippage).toSignificant(6)} ${trade.outputAmount.currency.symbol}`
: `${trade.maximumAmountIn(allowedSlippage).toSignificant(6)} ${trade.inputAmount.currency.symbol}`}
</ThemedText.Black>
</TextWithLoadingPlaceholder>
</RowBetween>
{!trade?.gasUseEstimateUSD || !chainId || !SUPPORTED_GAS_ESTIMATE_CHAIN_IDS.includes(chainId) ? null : (
<RowBetween>
<ThemedText.SubHeader color={theme.text3}>
<Trans>Network Fee</Trans>
</ThemedText.SubHeader>
<TextWithLoadingPlaceholder syncing={syncing} width={50}>
<ThemedText.Black textAlign="right" fontSize={14} color={theme.text3}>
~${trade.gasUseEstimateUSD.toFixed(2)}
</ThemedText.Black>
</TextWithLoadingPlaceholder>
</RowBetween>
)}
</AutoColumn>
</StyledCard>
)
}

View File

@@ -1,8 +1,8 @@
import { Trans } from '@lingui/macro'
import { Trade } from '@uniswap/router-sdk'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import { ReactNode, useCallback, useMemo } from 'react'
import { InterfaceTrade } from 'state/routing/types'
import TransactionConfirmationModal, {
ConfirmationModalContent,
@@ -16,9 +16,7 @@ import SwapModalHeader from './SwapModalHeader'
* @param args either a pair of V2 trades or a pair of V3 trades
*/
function tradeMeaningfullyDiffers(
...args:
| [V2Trade<Currency, Currency, TradeType>, V2Trade<Currency, Currency, TradeType>]
| [V3Trade<Currency, Currency, TradeType>, V3Trade<Currency, Currency, TradeType>]
...args: [Trade<Currency, Currency, TradeType>, Trade<Currency, Currency, TradeType>]
): boolean {
const [tradeA, tradeB] = args
return (
@@ -44,8 +42,8 @@ export default function ConfirmSwapModal({
txHash,
}: {
isOpen: boolean
trade: V2Trade<Currency, Currency, TradeType> | V3Trade<Currency, Currency, TradeType> | undefined
originalTrade: V2Trade<Currency, Currency, TradeType> | V3Trade<Currency, Currency, TradeType> | undefined
trade: InterfaceTrade<Currency, Currency, TradeType> | undefined
originalTrade: Trade<Currency, Currency, TradeType> | undefined
attemptingTxn: boolean
txHash: string | undefined
recipient: string | null
@@ -56,15 +54,7 @@ export default function ConfirmSwapModal({
onDismiss: () => void
}) {
const showAcceptChanges = useMemo(
() =>
Boolean(
(trade instanceof V2Trade &&
originalTrade instanceof V2Trade &&
tradeMeaningfullyDiffers(trade, originalTrade)) ||
(trade instanceof V3Trade &&
originalTrade instanceof V3Trade &&
tradeMeaningfullyDiffers(trade, originalTrade))
),
() => Boolean(trade && originalTrade && tradeMeaningfullyDiffers(trade, originalTrade)),
[originalTrade, trade]
)

View File

@@ -0,0 +1,105 @@
import { Trans } from '@lingui/macro'
import { Currency, TradeType } from '@uniswap/sdk-core'
import { ChainId } from '@uniswap/smart-order-router'
import { AutoColumn } from 'components/Column'
import { LoadingOpacityContainer } from 'components/Loader/styled'
import { RowFixed } from 'components/Row'
import { MouseoverTooltipContent } from 'components/Tooltip'
import ReactGA from 'react-ga'
import { InterfaceTrade } from 'state/routing/types'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { ReactComponent as GasIcon } from '../../assets/images/gas-icon.svg'
import { ResponsiveTooltipContainer } from './styleds'
import SwapRoute from './SwapRoute'
const GasWrapper = styled(RowFixed)`
border-radius: 8px;
padding: 4px 6px;
height: 24px;
color: ${({ theme }) => theme.text3};
background-color: ${({ theme }) => theme.bg1};
font-size: 14px;
font-weight: 500;
user-select: none;
`
const StyledGasIcon = styled(GasIcon)`
margin-right: 4px;
height: 14px;
& > * {
stroke: ${({ theme }) => theme.text3};
}
`
export const SUPPORTED_GAS_ESTIMATE_CHAIN_IDS = [ChainId.MAINNET]
export default function GasEstimateBadge({
trade,
loading,
showRoute,
disableHover,
}: {
trade: InterfaceTrade<Currency, Currency, TradeType> | undefined | null // dollar amount in active chain's stablecoin
loading: boolean
showRoute?: boolean // show route instead of gas estimation summary
disableHover?: boolean
}) {
const formattedGasPriceString = trade?.gasUseEstimateUSD
? trade.gasUseEstimateUSD.toFixed(2) === '0.00'
? '<$0.01'
: '$' + trade.gasUseEstimateUSD.toFixed(2)
: undefined
return (
<MouseoverTooltipContent
wrap={false}
disableHover={disableHover}
content={
loading ? null : (
<ResponsiveTooltipContainer
origin="top right"
style={{
padding: showRoute ? '0' : '12px',
border: 'none',
borderRadius: showRoute ? '16px' : '12px',
maxWidth: '400px',
}}
>
{showRoute ? (
trade ? (
<SwapRoute trade={trade} syncing={loading} fixedOpen={showRoute} />
) : null
) : (
<AutoColumn gap="4px" justify="center">
<ThemedText.Main fontSize="12px" textAlign="center">
<Trans>Estimated network fee</Trans>
</ThemedText.Main>
<ThemedText.Body textAlign="center" fontWeight={500} style={{ userSelect: 'none' }}>
<Trans>${trade?.gasUseEstimateUSD?.toFixed(2)}</Trans>
</ThemedText.Body>
<ThemedText.Main fontSize="10px" textAlign="center" maxWidth="140px" color="text3">
<Trans>Estimate may differ due to your wallet gas settings</Trans>
</ThemedText.Main>
</AutoColumn>
)}
</ResponsiveTooltipContainer>
)
}
placement="bottom"
onOpen={() =>
ReactGA.event({
category: 'Gas',
action: 'Gas Details Tooltip Open',
})
}
>
<LoadingOpacityContainer $loading={loading}>
<GasWrapper>
<StyledGasIcon />
{formattedGasPriceString ?? null}
</GasWrapper>
</LoadingOpacityContainer>
</MouseoverTooltipContent>
)
}

View File

@@ -1,7 +1,7 @@
import { Trans } from '@lingui/macro'
import { useRoutingAPIEnabled } from 'state/user/hooks'
import useAutoRouterSupported from 'hooks/useAutoRouterSupported'
import styled from 'styled-components/macro'
import { TYPE } from 'theme'
import { ThemedText } from 'theme'
import { ReactComponent as AutoRouterIcon } from '../../assets/svg/auto_router.svg'
import { ReactComponent as StaticRouterIcon } from '../../assets/svg/static_route.svg'
@@ -26,7 +26,7 @@ const StyledStaticRouterIcon = styled(StaticRouterIcon)`
}
`
const StyledAutoRouterLabel = styled(TYPE.black)`
const StyledAutoRouterLabel = styled(ThemedText.Black)`
line-height: 1rem;
/* fallback color */
@@ -40,19 +40,19 @@ const StyledAutoRouterLabel = styled(TYPE.black)`
`
export function AutoRouterLogo() {
const routingAPIEnabled = useRoutingAPIEnabled()
const autoRouterSupported = useAutoRouterSupported()
return routingAPIEnabled ? <StyledAutoRouterIcon /> : <StyledStaticRouterIcon />
return autoRouterSupported ? <StyledAutoRouterIcon /> : <StyledStaticRouterIcon />
}
export function AutoRouterLabel() {
const routingAPIEnabled = useRoutingAPIEnabled()
const autoRouterSupported = useAutoRouterSupported()
return routingAPIEnabled ? (
return autoRouterSupported ? (
<StyledAutoRouterLabel fontSize={14}>Auto Router</StyledAutoRouterLabel>
) : (
<TYPE.black fontSize={14}>
<ThemedText.Black fontSize={14}>
<Trans>Trade Route</Trans>
</TYPE.black>
</ThemedText.Black>
)
}

View File

@@ -0,0 +1,202 @@
import { Trans } from '@lingui/macro'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import AnimatedDropdown from 'components/AnimatedDropdown'
import Card, { OutlineCard } from 'components/Card'
import { AutoColumn } from 'components/Column'
import { LoadingOpacityContainer } from 'components/Loader/styled'
import Row, { RowBetween, RowFixed } from 'components/Row'
import { MouseoverTooltipContent } from 'components/Tooltip'
import { useActiveWeb3React } from 'hooks/web3'
import { darken } from 'polished'
import { useState } from 'react'
import { ChevronDown, Info } from 'react-feather'
import { InterfaceTrade } from 'state/routing/types'
import styled, { keyframes, useTheme } from 'styled-components/macro'
import { HideSmall, ThemedText } from 'theme'
import { AdvancedSwapDetails } from './AdvancedSwapDetails'
import GasEstimateBadge, { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from './GasEstimateBadge'
import { ResponsiveTooltipContainer } from './styleds'
import SwapRoute from './SwapRoute'
import TradePrice from './TradePrice'
const Wrapper = styled(Row)`
width: 100%;
justify-content: center;
`
const StyledInfoIcon = styled(Info)`
height: 16px;
width: 16px;
margin-right: 4px;
color: ${({ theme }) => theme.text3};
`
const StyledCard = styled(OutlineCard)`
padding: 12px;
border: 1px solid ${({ theme }) => theme.bg2};
`
const StyledHeaderRow = styled(RowBetween)<{ disabled: boolean; open: boolean }>`
padding: 4px 8px;
border-radius: 12px;
background-color: ${({ open, theme }) => (open ? theme.bg1 : 'transparent')};
align-items: center;
cursor: ${({ disabled }) => (disabled ? 'initial' : 'pointer')};
min-height: 40px;
:hover {
background-color: ${({ theme, disabled }) => (disabled ? theme.bg1 : darken(0.015, theme.bg1))};
}
`
const RotatingArrow = styled(ChevronDown)<{ open?: boolean }>`
transform: ${({ open }) => (open ? 'rotate(180deg)' : 'none')};
transition: transform 0.1s linear;
`
const StyledPolling = styled.div`
display: flex;
height: 16px;
width: 16px;
margin-right: 2px;
margin-left: 10px;
align-items: center;
color: ${({ theme }) => theme.text1};
transition: 250ms ease color;
${({ theme }) => theme.mediaWidth.upToMedium`
display: none;
`}
`
const StyledPollingDot = styled.div`
width: 8px;
height: 8px;
min-height: 8px;
min-width: 8px;
border-radius: 50%;
position: relative;
background-color: ${({ theme }) => theme.bg2};
transition: 250ms ease background-color;
`
const rotate360 = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`
const Spinner = styled.div`
animation: ${rotate360} 1s cubic-bezier(0.83, 0, 0.17, 1) infinite;
transform: translateZ(0);
border-top: 1px solid transparent;
border-right: 1px solid transparent;
border-bottom: 1px solid transparent;
border-left: 2px solid ${({ theme }) => theme.text1};
background: transparent;
width: 14px;
height: 14px;
border-radius: 50%;
position: relative;
transition: 250ms ease border-color;
left: -3px;
top: -3px;
`
interface SwapDetailsInlineProps {
trade: InterfaceTrade<Currency, Currency, TradeType> | undefined
syncing: boolean
loading: boolean
showInverted: boolean
setShowInverted: React.Dispatch<React.SetStateAction<boolean>>
allowedSlippage: Percent
}
export default function SwapDetailsDropdown({
trade,
syncing,
loading,
showInverted,
setShowInverted,
allowedSlippage,
}: SwapDetailsInlineProps) {
const theme = useTheme()
const { chainId } = useActiveWeb3React()
const [showDetails, setShowDetails] = useState(false)
return (
<Wrapper>
<AutoColumn gap={'8px'} style={{ width: '100%', marginBottom: '-8px' }}>
<StyledHeaderRow onClick={() => setShowDetails(!showDetails)} disabled={!trade} open={showDetails}>
<RowFixed style={{ position: 'relative' }}>
{loading || syncing ? (
<StyledPolling>
<StyledPollingDot>
<Spinner />
</StyledPollingDot>
</StyledPolling>
) : (
<HideSmall>
<MouseoverTooltipContent
wrap={false}
content={
<ResponsiveTooltipContainer origin="top right" style={{ padding: '0' }}>
<Card padding="12px">
<AdvancedSwapDetails trade={trade} allowedSlippage={allowedSlippage} syncing={syncing} />
</Card>
</ResponsiveTooltipContainer>
}
placement="bottom"
disableHover={showDetails}
>
<StyledInfoIcon color={trade ? theme.text3 : theme.bg3} />
</MouseoverTooltipContent>
</HideSmall>
)}
{trade ? (
<LoadingOpacityContainer $loading={syncing}>
<TradePrice
price={trade.executionPrice}
showInverted={showInverted}
setShowInverted={setShowInverted}
/>
</LoadingOpacityContainer>
) : loading || syncing ? (
<ThemedText.Main fontSize={14}>
<Trans>Fetching best price...</Trans>
</ThemedText.Main>
) : null}
</RowFixed>
<RowFixed>
{!trade?.gasUseEstimateUSD ||
showDetails ||
!chainId ||
!SUPPORTED_GAS_ESTIMATE_CHAIN_IDS.includes(chainId) ? null : (
<GasEstimateBadge
trade={trade}
loading={syncing || loading}
showRoute={!showDetails}
disableHover={showDetails}
/>
)}
<RotatingArrow stroke={trade ? theme.text3 : theme.bg3} open={Boolean(trade && showDetails)} />
</RowFixed>
</StyledHeaderRow>
<AnimatedDropdown open={showDetails}>
<AutoColumn gap={'8px'} style={{ padding: '0', paddingBottom: '8px' }}>
{trade ? (
<StyledCard>
<AdvancedSwapDetails trade={trade} allowedSlippage={allowedSlippage} syncing={syncing} />
</StyledCard>
) : null}
{trade ? <SwapRoute trade={trade} syncing={syncing} /> : null}
</AutoColumn>
</AnimatedDropdown>
</AutoColumn>
</Wrapper>
)
}

View File

@@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro'
import { Percent } from '@uniswap/sdk-core'
import styled from 'styled-components/macro'
import { TYPE } from '../../theme'
import { ThemedText } from '../../theme'
import { RowBetween, RowFixed } from '../Row'
import SettingsTab from '../Settings'
@@ -17,9 +17,9 @@ export default function SwapHeader({ allowedSlippage }: { allowedSlippage: Perce
<StyledSwapHeader>
<RowBetween>
<RowFixed>
<TYPE.black fontWeight={500} fontSize={16} style={{ marginRight: '8px' }}>
<ThemedText.Black fontWeight={500} fontSize={16} style={{ marginRight: '8px' }}>
<Trans>Swap</Trans>
</TYPE.black>
</ThemedText.Black>
</RowFixed>
<RowFixed>
<SettingsTab placeholderSlippage={allowedSlippage} />

View File

@@ -1,7 +1,6 @@
import { Trans } from '@lingui/macro'
import { Trade } from '@uniswap/router-sdk'
import { Currency, TradeType } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import { ReactNode } from 'react'
import { Text } from 'rebass'
@@ -14,7 +13,7 @@ export default function SwapModalFooter({
swapErrorMessage,
disabledConfirm,
}: {
trade: V2Trade<Currency, Currency, TradeType> | V3Trade<Currency, Currency, TradeType>
trade: Trade<Currency, Currency, TradeType>
onConfirm: () => void
swapErrorMessage: ReactNode | undefined
disabledConfirm: boolean

View File

@@ -1,14 +1,13 @@
import { Trans } from '@lingui/macro'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import { useContext, useState } from 'react'
import { AlertTriangle, ArrowDown } from 'react-feather'
import { Text } from 'rebass'
import { InterfaceTrade } from 'state/routing/types'
import styled, { ThemeContext } from 'styled-components/macro'
import { useUSDCValue } from '../../hooks/useUSDCPrice'
import { TYPE } from '../../theme'
import { ThemedText } from '../../theme'
import { isAddress, shortenAddress } from '../../utils'
import { computeFiatValuePriceImpact } from '../../utils/computeFiatValuePriceImpact'
import { ButtonPrimary } from '../Button'
@@ -46,7 +45,7 @@ export default function SwapModalHeader({
showAcceptChanges,
onAcceptChanges,
}: {
trade: V2Trade<Currency, Currency, TradeType> | V3Trade<Currency, Currency, TradeType>
trade: InterfaceTrade<Currency, Currency, TradeType>
allowedSlippage: Percent
recipient: string | null
showAcceptChanges: boolean
@@ -63,19 +62,7 @@ export default function SwapModalHeader({
<AutoColumn gap={'4px'} style={{ marginTop: '1rem' }}>
<LightCard padding="0.75rem 1rem">
<AutoColumn gap={'8px'}>
<RowBetween>
<TYPE.body color={theme.text3} fontWeight={500} fontSize={14}>
<Trans>From</Trans>
</TYPE.body>
<FiatValue fiatValue={fiatValueInput} />
</RowBetween>
<RowBetween align="center">
<RowFixed gap={'0px'}>
<CurrencyLogo currency={trade.inputAmount.currency} size={'20px'} style={{ marginRight: '12px' }} />
<Text fontSize={20} fontWeight={500}>
{trade.inputAmount.currency.symbol}
</Text>
</RowFixed>
<RowFixed gap={'0px'}>
<TruncatedText
fontSize={24}
@@ -85,6 +72,15 @@ export default function SwapModalHeader({
{trade.inputAmount.toSignificant(6)}
</TruncatedText>
</RowFixed>
<RowFixed gap={'0px'}>
<CurrencyLogo currency={trade.inputAmount.currency} size={'20px'} style={{ marginRight: '12px' }} />
<Text fontSize={20} fontWeight={500}>
{trade.inputAmount.currency.symbol}
</Text>
</RowFixed>
</RowBetween>
<RowBetween>
<FiatValue fiatValue={fiatValueInput} />
</RowBetween>
</AutoColumn>
</LightCard>
@@ -93,51 +89,43 @@ export default function SwapModalHeader({
</ArrowWrapper>
<LightCard padding="0.75rem 1rem" style={{ marginBottom: '0.25rem' }}>
<AutoColumn gap={'8px'}>
<RowBetween>
<TYPE.body color={theme.text3} fontWeight={500} fontSize={14}>
<Trans>To</Trans>
</TYPE.body>
<TYPE.body fontSize={14} color={theme.text3}>
<FiatValue
fiatValue={fiatValueOutput}
priceImpact={computeFiatValuePriceImpact(fiatValueInput, fiatValueOutput)}
/>
</TYPE.body>
</RowBetween>
<RowBetween align="flex-end">
<RowFixed gap={'0px'}>
<TruncatedText fontSize={24} fontWeight={500}>
{trade.outputAmount.toSignificant(6)}
</TruncatedText>
</RowFixed>
<RowFixed gap={'0px'}>
<CurrencyLogo currency={trade.outputAmount.currency} size={'20px'} style={{ marginRight: '12px' }} />
<Text fontSize={20} fontWeight={500}>
{trade.outputAmount.currency.symbol}
</Text>
</RowFixed>
<RowFixed gap={'0px'}>
<TruncatedText fontSize={24} fontWeight={500}>
{trade.outputAmount.toSignificant(6)}
</TruncatedText>
</RowFixed>
</RowBetween>
<RowBetween>
<ThemedText.Body fontSize={14} color={theme.text3}>
<FiatValue
fiatValue={fiatValueOutput}
priceImpact={computeFiatValuePriceImpact(fiatValueInput, fiatValueOutput)}
/>
</ThemedText.Body>
</RowBetween>
</AutoColumn>
</LightCard>
<RowBetween style={{ marginTop: '0.25rem', padding: '0 1rem' }}>
<TYPE.body color={theme.text2} fontWeight={500} fontSize={14}>
<Trans>Price</Trans>
</TYPE.body>
<TradePrice price={trade.executionPrice} showInverted={showInverted} setShowInverted={setShowInverted} />
</RowBetween>
<LightCard style={{ padding: '.75rem', marginTop: '0.5rem' }}>
<AdvancedSwapDetails trade={trade} allowedSlippage={allowedSlippage} />
</LightCard>
{showAcceptChanges ? (
<SwapShowAcceptChanges justify="flex-start" gap={'0px'}>
<RowBetween>
<RowFixed>
<AlertTriangle size={20} style={{ marginRight: '8px', minWidth: 24 }} />
<TYPE.main color={theme.primary1}>
<ThemedText.Main color={theme.primary1}>
<Trans>Price Updated</Trans>
</TYPE.main>
</ThemedText.Main>
</RowFixed>
<ButtonPrimary
style={{ padding: '.5rem', width: 'fit-content', fontSize: '0.825rem', borderRadius: '12px' }}
@@ -151,7 +139,7 @@ export default function SwapModalHeader({
<AutoColumn justify="flex-start" gap="sm" style={{ padding: '.75rem 1rem' }}>
{trade.tradeType === TradeType.EXACT_INPUT ? (
<TYPE.italic fontWeight={400} textAlign="left" style={{ width: '100%' }}>
<ThemedText.Italic fontWeight={400} textAlign="left" style={{ width: '100%' }}>
<Trans>
Output is estimated. You will receive at least{' '}
<b>
@@ -159,9 +147,9 @@ export default function SwapModalHeader({
</b>{' '}
or the transaction will revert.
</Trans>
</TYPE.italic>
</ThemedText.Italic>
) : (
<TYPE.italic fontWeight={400} textAlign="left" style={{ width: '100%' }}>
<ThemedText.Italic fontWeight={400} textAlign="left" style={{ width: '100%' }}>
<Trans>
Input is estimated. You will sell at most{' '}
<b>
@@ -169,17 +157,17 @@ export default function SwapModalHeader({
</b>{' '}
or the transaction will revert.
</Trans>
</TYPE.italic>
</ThemedText.Italic>
)}
</AutoColumn>
{recipient !== null ? (
<AutoColumn justify="flex-start" gap="sm" style={{ padding: '12px 0 0 0px' }}>
<TYPE.main>
<ThemedText.Main>
<Trans>
Output will be sent to{' '}
<b title={recipient}>{isAddress(recipient) ? shortenAddress(recipient) : recipient}</b>
</Trans>
</TYPE.main>
</ThemedText.Main>
</AutoColumn>
) : null}
</AutoColumn>

View File

@@ -1,92 +1,113 @@
import { Trans } from '@lingui/macro'
import { Trade } from '@uniswap/router-sdk'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { FeeAmount, Trade as V3Trade } from '@uniswap/v3-sdk'
import Badge from 'components/Badge'
import { Pair } from '@uniswap/v2-sdk'
import AnimatedDropdown from 'components/AnimatedDropdown'
import { AutoColumn } from 'components/Column'
import { LoadingRows } from 'components/Loader/styled'
import RoutingDiagram, { RoutingDiagramEntry } from 'components/RoutingDiagram/RoutingDiagram'
import { AutoRow, RowBetween } from 'components/Row'
import { Version } from 'hooks/useToggledVersion'
import { memo } from 'react'
import { useRoutingAPIEnabled } from 'state/user/hooks'
import useAutoRouterSupported from 'hooks/useAutoRouterSupported'
import { useActiveWeb3React } from 'hooks/web3'
import { memo, useState } from 'react'
import { Plus } from 'react-feather'
import { InterfaceTrade } from 'state/routing/types'
import { useDarkModeManager } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { TYPE } from 'theme'
import { getTradeVersion } from 'utils/getTradeVersion'
import { Separator, ThemedText } from 'theme'
import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from './GasEstimateBadge'
import { AutoRouterLabel, AutoRouterLogo } from './RouterLabel'
const Separator = styled.div`
border-top: 1px solid ${({ theme }) => theme.bg2};
height: 1px;
width: 100%;
const Wrapper = styled(AutoColumn)<{ darkMode?: boolean; fixedOpen?: boolean }>`
padding: ${({ fixedOpen }) => (fixedOpen ? '12px' : '12px 8px 12px 12px')};
border-radius: 16px;
border: 1px solid ${({ theme, fixedOpen }) => (fixedOpen ? 'transparent' : theme.bg2)};
cursor: pointer;
`
const OpenCloseIcon = styled(Plus)<{ open?: boolean }>`
margin-left: 8px;
height: 20px;
stroke-width: 2px;
transition: transform 0.1s;
transform: ${({ open }) => (open ? 'rotate(45deg)' : 'none')};
stroke: ${({ theme }) => theme.text3};
cursor: pointer;
:hover {
opacity: 0.8;
}
`
const V2_DEFAULT_FEE_TIER = 3000
export default memo(function SwapRoute({
trade,
syncing,
}: {
trade: V2Trade<Currency, Currency, TradeType> | V3Trade<Currency, Currency, TradeType>
interface SwapRouteProps extends React.HTMLAttributes<HTMLDivElement> {
trade: InterfaceTrade<Currency, Currency, TradeType>
syncing: boolean
}) {
const routingAPIEnabled = useRoutingAPIEnabled()
fixedOpen?: boolean // fixed in open state, hide open/close icon
}
export default memo(function SwapRoute({ trade, syncing, fixedOpen = false, ...rest }: SwapRouteProps) {
const autoRouterSupported = useAutoRouterSupported()
const routes = getTokenPath(trade)
const [open, setOpen] = useState(false)
const { chainId } = useActiveWeb3React()
const [darkMode] = useDarkModeManager()
const formattedGasPriceString = trade?.gasUseEstimateUSD
? trade.gasUseEstimateUSD.toFixed(2) === '0.00'
? '<$0.01'
: '$' + trade.gasUseEstimateUSD.toFixed(2)
: undefined
return (
<AutoColumn gap="12px">
<RowBetween>
<Wrapper {...rest} darkMode={darkMode} fixedOpen={fixedOpen}>
<RowBetween onClick={() => setOpen(!open)}>
<AutoRow gap="4px" width="auto">
<AutoRouterLogo />
<AutoRouterLabel />
</AutoRow>
{syncing ? (
<LoadingRows>
<div style={{ width: '30px', height: '24px' }} />
</LoadingRows>
) : (
<Badge>
<TYPE.black fontSize={12}>
{getTradeVersion(trade) === Version.v2 ? <Trans>V2</Trans> : <Trans>V3</Trans>}
</TYPE.black>
</Badge>
)}
{fixedOpen ? null : <OpenCloseIcon open={open} />}
</RowBetween>
<Separator />
{syncing ? (
<LoadingRows>
<div style={{ width: '400px', height: '30px' }} />
</LoadingRows>
) : (
<RoutingDiagram
currencyIn={trade.inputAmount.currency}
currencyOut={trade.outputAmount.currency}
routes={getTokenPath(trade)}
/>
)}
{routingAPIEnabled && (
<TYPE.main fontSize={12} width={400}>
<Trans>This route optimizes your price by considering split routes, multiple hops, and gas costs.</Trans>
</TYPE.main>
)}
</AutoColumn>
<AnimatedDropdown open={open || fixedOpen}>
<AutoRow gap="4px" width="auto" style={{ paddingTop: '12px', margin: 0 }}>
{syncing ? (
<LoadingRows>
<div style={{ width: '400px', height: '30px' }} />
</LoadingRows>
) : (
<RoutingDiagram
currencyIn={trade.inputAmount.currency}
currencyOut={trade.outputAmount.currency}
routes={routes}
/>
)}
<Separator />
{autoRouterSupported &&
(syncing ? (
<LoadingRows>
<div style={{ width: '250px', height: '15px' }} />
</LoadingRows>
) : (
<ThemedText.Main fontSize={12} width={400} margin={0}>
{trade?.gasUseEstimateUSD && chainId && SUPPORTED_GAS_ESTIMATE_CHAIN_IDS.includes(chainId) ? (
<Trans>Best price route costs ~{formattedGasPriceString} in gas. </Trans>
) : null}{' '}
<Trans>
This route optimizes your total output by considering split routes, multiple hops, and the gas cost of
each step.
</Trans>
</ThemedText.Main>
))}
</AutoRow>
</AnimatedDropdown>
</Wrapper>
)
})
function getTokenPath(
trade: V2Trade<Currency, Currency, TradeType> | V3Trade<Currency, Currency, TradeType>
): RoutingDiagramEntry[] {
// convert V2 path to a list of routes
if (trade instanceof V2Trade) {
const { path: tokenPath } = (trade as V2Trade<Currency, Currency, TradeType>).route
const path = []
for (let i = 1; i < tokenPath.length; i++) {
path.push([tokenPath[i - 1], tokenPath[i], V2_DEFAULT_FEE_TIER] as RoutingDiagramEntry['path'][0])
}
return [{ percent: new Percent(100, 100), path }]
}
return trade.swaps.map(({ route: { tokenPath, pools }, inputAmount, outputAmount }) => {
function getTokenPath(trade: Trade<Currency, Currency, TradeType>): RoutingDiagramEntry[] {
return trade.swaps.map(({ route: { path: tokenPath, pools, protocol }, inputAmount, outputAmount }) => {
const portion =
trade.tradeType === TradeType.EXACT_INPUT
? inputAmount.divide(trade.inputAmount)
@@ -94,18 +115,25 @@ function getTokenPath(
const percent = new Percent(portion.numerator, portion.denominator)
const path: [Currency, Currency, FeeAmount][] = []
const path: RoutingDiagramEntry['path'] = []
for (let i = 0; i < pools.length; i++) {
const nextPool = pools[i]
const tokenIn = tokenPath[i]
const tokenOut = tokenPath[i + 1]
path.push([tokenIn, tokenOut, nextPool.fee])
const entry: RoutingDiagramEntry['path'][0] = [
tokenIn,
tokenOut,
nextPool instanceof Pair ? V2_DEFAULT_FEE_TIER : nextPool.fee,
]
path.push(entry)
}
return {
percent,
path,
protocol,
}
})
}

View File

@@ -0,0 +1,75 @@
import { Trans } from '@lingui/macro'
import { Currency, CurrencyAmount, Token, TradeType } from '@uniswap/sdk-core'
import { RowBetween } from 'components/Row'
import { MouseoverTooltipContent } from 'components/Tooltip'
import { Info } from 'react-feather'
import { InterfaceTrade } from 'state/routing/types'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { ResponsiveTooltipContainer } from './styleds'
const Wrapper = styled.div`
background-color: ${({ theme }) => theme.bg1};
border-bottom-left-radius: 20px;
border-bottom-right-radius: 20px;
padding: 14px;
margin-top: -20px;
padding-top: 32px;
`
const StyledInfoIcon = styled(Info)`
stroke: ${({ theme }) => theme.text3};
`
/**
* @returns Dropdown card for showing edge case warnings outside of button
*/
export default function SwapWarningDropdown({
fiatValueInput,
trade,
}: {
fiatValueInput: CurrencyAmount<Token> | null
trade: InterfaceTrade<Currency, Currency, TradeType> | undefined
}) {
// gas cost estimate is more than half of input value
const showNetworkFeeWarning = Boolean(
fiatValueInput &&
trade?.gasUseEstimateUSD &&
parseFloat(trade.gasUseEstimateUSD.toSignificant(6)) > parseFloat(fiatValueInput.toFixed(6)) / 2
)
if (!showNetworkFeeWarning) {
return null
}
return (
<Wrapper>
{showNetworkFeeWarning ? (
<RowBetween>
<ThemedText.Main fontSize="14px" color="text3">
<Trans>Network fees exceed 50% of the swap amount!</Trans>
</ThemedText.Main>
<MouseoverTooltipContent
wrap={false}
content={
<ResponsiveTooltipContainer origin="top right" style={{ padding: '12px' }}>
<ThemedText.Main fontSize="12px" color="text3" maxWidth="200px">
<Trans>
The cost of sending this transaction is more than half of the value of the input amount.
</Trans>
</ThemedText.Main>
<ThemedText.Main fontSize="12px" color="text3" maxWidth="200px" mt="8px">
<Trans>You might consider waiting until the network fees go down to complete this transaction.</Trans>
</ThemedText.Main>
</ResponsiveTooltipContainer>
}
placement="bottom"
>
<StyledInfoIcon size={16} />
</MouseoverTooltipContent>
</RowBetween>
) : null}
</Wrapper>
)
}

View File

@@ -1,7 +1,10 @@
import { Trans } from '@lingui/macro'
import { Currency, Price } from '@uniswap/sdk-core'
import useUSDCPrice from 'hooks/useUSDCPrice'
import { useCallback, useContext } from 'react'
import { Text } from 'rebass'
import styled, { ThemeContext } from 'styled-components/macro'
import { ThemedText } from 'theme'
interface TradePriceProps {
price: Price<Currency, Currency>
@@ -10,21 +13,27 @@ interface TradePriceProps {
}
const StyledPriceContainer = styled.button`
display: flex;
justify-content: center;
align-items: center;
padding: 0;
font-size: 0.875rem;
font-weight: 400;
background-color: transparent;
border: none;
height: 24px;
cursor: pointer;
align-items: center
justify-content: flex-start;
padding: 0;
grid-template-columns: 1fr auto;
grid-gap: 0.25rem;
display: flex;
flex-direction: row;
text-align: left;
flex-wrap: wrap;
padding: 8px 0;
user-select: text;
`
export default function TradePrice({ price, showInverted, setShowInverted }: TradePriceProps) {
const theme = useContext(ThemeContext)
const usdcPrice = useUSDCPrice(showInverted ? price.baseCurrency : price.quoteCurrency)
let formattedPrice: string
try {
formattedPrice = showInverted ? price.toSignificant(4) : price.invert()?.toSignificant(4)
@@ -39,12 +48,21 @@ export default function TradePrice({ price, showInverted, setShowInverted }: Tra
const text = `${'1 ' + labelInverted + ' = ' + formattedPrice ?? '-'} ${label}`
return (
<StyledPriceContainer onClick={flipPrice} title={text}>
<div style={{ alignItems: 'center', display: 'flex', width: 'fit-content' }}>
<Text fontWeight={500} fontSize={14} color={theme.text1}>
{text}
</Text>
</div>
<StyledPriceContainer
onClick={(e) => {
e.stopPropagation() // dont want this click to affect dropdowns / hovers
flipPrice()
}}
title={text}
>
<Text fontWeight={500} color={theme.text1}>
{text}
</Text>{' '}
{usdcPrice && (
<ThemedText.DarkGray>
<Trans>(${usdcPrice.toSignificant(6, { groupSeparator: ',' })})</Trans>
</ThemedText.DarkGray>
)}
</StyledPriceContainer>
)
}

View File

@@ -1,5 +1,5 @@
import { Trans } from '@lingui/macro'
import { Currency, Token } from '@uniswap/sdk-core'
import { Currency } from '@uniswap/sdk-core'
import { ButtonEmpty } from 'components/Button'
import Card, { OutlineCard } from 'components/Card'
import { AutoColumn } from 'components/Column'
@@ -9,7 +9,7 @@ import { AutoRow, RowBetween } from 'components/Row'
import { useActiveWeb3React } from 'hooks/web3'
import { useState } from 'react'
import styled from 'styled-components/macro'
import { CloseIcon, ExternalLink, TYPE } from 'theme'
import { CloseIcon, ExternalLink, ThemedText, Z_INDEX } from 'theme'
import { useUnsupportedTokens } from '../../hooks/Tokens'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
@@ -17,6 +17,8 @@ import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
const DetailsFooter = styled.div<{ show: boolean }>`
padding-top: calc(16px + 2rem);
padding-bottom: 20px;
margin-left: auto;
margin-right: auto;
margin-top: -2rem;
width: 100%;
max-width: 400px;
@@ -24,14 +26,18 @@ const DetailsFooter = styled.div<{ show: boolean }>`
border-bottom-right-radius: 20px;
color: ${({ theme }) => theme.text2};
background-color: ${({ theme }) => theme.advancedBG};
z-index: -1;
z-index: ${Z_INDEX.deprecated_zero};
transform: ${({ show }) => (show ? 'translateY(0%)' : 'translateY(-100%)')};
transition: transform 300ms ease-in-out;
text-align: center;
`
const AddressText = styled(TYPE.blue)`
const StyledButtonEmpty = styled(ButtonEmpty)`
text-decoration: none;
`
const AddressText = styled(ThemedText.Blue)`
font-size: 12px;
${({ theme }) => theme.mediaWidth.upToSmall`
@@ -56,7 +62,7 @@ export default function UnsupportedCurrencyFooter({
})
: []
const unsupportedTokens: { [address: string]: Token } = useUnsupportedTokens()
const unsupportedTokens = useUnsupportedTokens()
return (
<DetailsFooter show={show}>
@@ -64,9 +70,9 @@ export default function UnsupportedCurrencyFooter({
<Card padding="2rem">
<AutoColumn gap="lg">
<RowBetween>
<TYPE.mediumHeader>
<ThemedText.MediumHeader>
<Trans>Unsupported Assets</Trans>
</TYPE.mediumHeader>
</ThemedText.MediumHeader>
<CloseIcon onClick={() => setShowDetails(false)} />
</RowBetween>
{tokens.map((token) => {
@@ -78,7 +84,7 @@ export default function UnsupportedCurrencyFooter({
<AutoColumn gap="10px">
<AutoRow gap="5px" align="center">
<CurrencyLogo currency={token} size={'24px'} />
<TYPE.body fontWeight={500}>{token.symbol}</TYPE.body>
<ThemedText.Body fontWeight={500}>{token.symbol}</ThemedText.Body>
</AutoRow>
{chainId && (
<ExternalLink href={getExplorerLink(chainId, token.address, ExplorerDataType.ADDRESS)}>
@@ -91,21 +97,21 @@ export default function UnsupportedCurrencyFooter({
)
})}
<AutoColumn gap="lg">
<TYPE.body fontWeight={500}>
<ThemedText.Body fontWeight={500}>
<Trans>
Some assets are not available through this interface because they may not work well with the smart
contracts or we are unable to allow trading for legal reasons.
</Trans>
</TYPE.body>
</ThemedText.Body>
</AutoColumn>
</AutoColumn>
</Card>
</Modal>
<ButtonEmpty padding={'0'} onClick={() => setShowDetails(true)}>
<TYPE.blue>
<StyledButtonEmpty padding={'0'} onClick={() => setShowDetails(true)}>
<ThemedText.Blue>
<Trans>Read more about unsupported assets</Trans>
</TYPE.blue>
</ButtonEmpty>
</ThemedText.Blue>
</StyledButtonEmpty>
</DetailsFooter>
)
}

View File

@@ -5,8 +5,8 @@ import { ReactNode } from 'react'
import { AlertTriangle } from 'react-feather'
import { Text } from 'rebass'
import styled, { css } from 'styled-components/macro'
import { TYPE } from 'theme'
import { ThemedText } from '../../theme'
import { AutoColumn } from '../Column'
import TradePrice from './TradePrice'
@@ -133,7 +133,7 @@ export const SwapShowAcceptChanges = styled(AutoColumn)`
margin-top: 8px;
`
export const TransactionDetailsLabel = styled(TYPE.black)`
export const TransactionDetailsLabel = styled(ThemedText.Black)`
border-bottom: 1px solid ${({ theme }) => theme.bg2};
padding-bottom: 0.5rem;
`

View File

@@ -10,7 +10,7 @@ import useENS from '../../hooks/useENS'
import { useActiveWeb3React } from '../../hooks/web3'
import { useDelegateCallback } from '../../state/governance/hooks'
import { useTokenBalance } from '../../state/wallet/hooks'
import { TYPE } from '../../theme'
import { ThemedText } from '../../theme'
import AddressInputPanel from '../AddressInputPanel'
import { ButtonPrimary } from '../Button'
import { AutoColumn } from '../Column'
@@ -95,23 +95,25 @@ export default function DelegateModal({ isOpen, onDismiss, title }: VoteModalPro
<ContentWrapper gap="lg">
<AutoColumn gap="lg" justify="center">
<RowBetween>
<TYPE.mediumHeader fontWeight={500}>{title}</TYPE.mediumHeader>
<ThemedText.MediumHeader fontWeight={500}>{title}</ThemedText.MediumHeader>
<StyledClosed stroke="black" onClick={wrappedOndismiss} />
</RowBetween>
<TYPE.body>
<ThemedText.Body>
<Trans>Earned UNI tokens represent voting shares in Uniswap governance.</Trans>
</TYPE.body>
<TYPE.body>
</ThemedText.Body>
<ThemedText.Body>
<Trans>You can either vote on each proposal yourself or delegate your votes to a third party.</Trans>
</TYPE.body>
</ThemedText.Body>
{usingDelegate && <AddressInputPanel value={typed} onChange={handleRecipientType} />}
<ButtonPrimary disabled={!isAddress(parsedAddress ?? '')} onClick={onDelegate}>
<TYPE.mediumHeader color="white">
<ThemedText.MediumHeader color="white">
{usingDelegate ? <Trans>Delegate Votes</Trans> : <Trans>Self Delegate</Trans>}
</TYPE.mediumHeader>
</ThemedText.MediumHeader>
</ButtonPrimary>
<TextButton onClick={() => setUsingDelegate(!usingDelegate)}>
<TYPE.blue>{usingDelegate ? <Trans>Remove Delegate</Trans> : <Trans>Add Delegate +</Trans>}</TYPE.blue>
<ThemedText.Blue>
{usingDelegate ? <Trans>Remove Delegate</Trans> : <Trans>Add Delegate +</Trans>}
</ThemedText.Blue>
</TextButton>
</AutoColumn>
</ContentWrapper>
@@ -119,20 +121,20 @@ export default function DelegateModal({ isOpen, onDismiss, title }: VoteModalPro
{attempting && !hash && (
<LoadingView onDismiss={wrappedOndismiss}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader>
<ThemedText.LargeHeader>
{usingDelegate ? <Trans>Delegating votes</Trans> : <Trans>Unlocking Votes</Trans>}
</TYPE.largeHeader>
<TYPE.main fontSize={36}> {formatCurrencyAmount(uniBalance, 4)}</TYPE.main>
</ThemedText.LargeHeader>
<ThemedText.Main fontSize={36}> {formatCurrencyAmount(uniBalance, 4)}</ThemedText.Main>
</AutoColumn>
</LoadingView>
)}
{hash && (
<SubmittedView onDismiss={wrappedOndismiss} hash={hash}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader>
<ThemedText.LargeHeader>
<Trans>Transaction Submitted</Trans>
</TYPE.largeHeader>
<TYPE.main fontSize={36}>{formatCurrencyAmount(uniBalance, 4)}</TYPE.main>
</ThemedText.LargeHeader>
<ThemedText.Main fontSize={36}>{formatCurrencyAmount(uniBalance, 4)}</ThemedText.Main>
</AutoColumn>
</SubmittedView>
)}

View File

@@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro'
import { L2_CHAIN_IDS } from 'constants/chains'
import { useActiveWeb3React } from 'hooks/web3'
import styled from 'styled-components/macro'
import { TYPE } from 'theme'
import { ThemedText } from 'theme'
const EmptyProposals = styled.div`
border: 1px solid ${({ theme }) => theme.text4};
@@ -25,14 +25,14 @@ interface EmptyStateProps {
}
const EmptyState = ({ HeaderContent, SubHeaderContent }: EmptyStateProps) => (
<EmptyProposals>
<TYPE.body style={{ marginBottom: '8px' }}>
<ThemedText.Body style={{ marginBottom: '8px' }}>
<HeaderContent />
</TYPE.body>
<TYPE.subHeader>
</ThemedText.Body>
<ThemedText.SubHeader>
<Sub>
<SubHeaderContent />
</Sub>
</TYPE.subHeader>
</ThemedText.SubHeader>
</EmptyProposals>
)

View File

@@ -8,8 +8,8 @@ import Circle from '../../assets/images/blue-loader.svg'
import { useActiveWeb3React } from '../../hooks/web3'
import { useUserVotes, useVoteCallback } from '../../state/governance/hooks'
import { VoteOption } from '../../state/governance/types'
import { CustomLightSpinner, TYPE } from '../../theme'
import { ExternalLink } from '../../theme/components'
import { CustomLightSpinner, ThemedText } from '../../theme'
import { ExternalLink } from '../../theme'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { ButtonPrimary } from '../Button'
import { AutoColumn, ColumnCenter } from '../Column'
@@ -85,7 +85,7 @@ export default function VoteModal({ isOpen, onDismiss, proposalId, voteOption }:
<ContentWrapper gap="lg">
<AutoColumn gap="lg" justify="center">
<RowBetween>
<TYPE.mediumHeader fontWeight={500}>
<ThemedText.MediumHeader fontWeight={500}>
{voteOption === VoteOption.Against ? (
<Trans>Vote against proposal {proposalId}</Trans>
) : voteOption === VoteOption.For ? (
@@ -93,14 +93,14 @@ export default function VoteModal({ isOpen, onDismiss, proposalId, voteOption }:
) : (
<Trans>Vote to abstain on proposal {proposalId}</Trans>
)}
</TYPE.mediumHeader>
</ThemedText.MediumHeader>
<StyledClosed stroke="black" onClick={wrappedOndismiss} />
</RowBetween>
<TYPE.largeHeader>
<ThemedText.LargeHeader>
<Trans>{formatCurrencyAmount(availableVotes, 4)} Votes</Trans>
</TYPE.largeHeader>
</ThemedText.LargeHeader>
<ButtonPrimary onClick={onVote}>
<TYPE.mediumHeader color="white">
<ThemedText.MediumHeader color="white">
{voteOption === VoteOption.Against ? (
<Trans>Vote against proposal {proposalId}</Trans>
) : voteOption === VoteOption.For ? (
@@ -108,7 +108,7 @@ export default function VoteModal({ isOpen, onDismiss, proposalId, voteOption }:
) : (
<Trans>Vote to abstain on proposal {proposalId}</Trans>
)}
</TYPE.mediumHeader>
</ThemedText.MediumHeader>
</ButtonPrimary>
</AutoColumn>
</ContentWrapper>
@@ -124,13 +124,13 @@ export default function VoteModal({ isOpen, onDismiss, proposalId, voteOption }:
</ConfirmedIcon>
<AutoColumn gap="100px" justify={'center'}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader>
<ThemedText.LargeHeader>
<Trans>Submitting Vote</Trans>
</TYPE.largeHeader>
</ThemedText.LargeHeader>
</AutoColumn>
<TYPE.subHeader>
<ThemedText.SubHeader>
<Trans>Confirm this transaction in your wallet</Trans>
</TYPE.subHeader>
</ThemedText.SubHeader>
</AutoColumn>
</ConfirmOrLoadingWrapper>
)}
@@ -145,18 +145,18 @@ export default function VoteModal({ isOpen, onDismiss, proposalId, voteOption }:
</ConfirmedIcon>
<AutoColumn gap="100px" justify={'center'}>
<AutoColumn gap="12px" justify={'center'}>
<TYPE.largeHeader>
<ThemedText.LargeHeader>
<Trans>Transaction Submitted</Trans>
</TYPE.largeHeader>
</ThemedText.LargeHeader>
</AutoColumn>
{chainId && (
<ExternalLink
href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}
style={{ marginLeft: '4px' }}
>
<TYPE.subHeader>
<ThemedText.SubHeader>
<Trans>View transaction on Explorer</Trans>
</TYPE.subHeader>
</ThemedText.SubHeader>
</ExternalLink>
)}
</AutoColumn>

View File

@@ -5,10 +5,10 @@ export const OVERLAY_READY = 'OVERLAY_READY'
type FormaticSupportedChains = 1 | 3 | 4 | 42
const CHAIN_ID_NETWORK_ARGUMENT: { readonly [chainId in FormaticSupportedChains]: string | undefined } = {
[1]: undefined,
[3]: 'ropsten',
[4]: 'rinkeby',
[42]: 'kovan',
1: undefined,
3: 'ropsten',
4: 'rinkeby',
42: 'kovan',
}
export class FortmaticConnector extends FortmaticConnectorCore {

View File

@@ -19,7 +19,7 @@ if (typeof INFURA_KEY === 'undefined') {
throw new Error(`REACT_APP_INFURA_KEY must be a defined environment variable`)
}
const NETWORK_URLS: { [key in SupportedChainId]: string } = {
export const NETWORK_URLS: { [key in SupportedChainId]: string } = {
[SupportedChainId.MAINNET]: `https://mainnet.infura.io/v3/${INFURA_KEY}`,
[SupportedChainId.RINKEBY]: `https://rinkeby.infura.io/v3/${INFURA_KEY}`,
[SupportedChainId.ROPSTEN]: `https://ropsten.infura.io/v3/${INFURA_KEY}`,

View File

@@ -8,13 +8,28 @@ type AddressMap = { [chainId: number]: string }
export const UNI_ADDRESS: AddressMap = constructSameAddressMap('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
export const MULTICALL_ADDRESS: AddressMap = {
...constructSameAddressMap('0x1F98415757620B543A52E61c46B32eB19261F984', [SupportedChainId.OPTIMISTIC_KOVAN]),
[SupportedChainId.OPTIMISM]: '0x90f872b3d8f33f305e0250db6A2761B354f7710A',
...constructSameAddressMap('0x1F98415757620B543A52E61c46B32eB19261F984', [
SupportedChainId.OPTIMISTIC_KOVAN,
SupportedChainId.OPTIMISM,
]),
[SupportedChainId.ARBITRUM_ONE]: '0xadF885960B47eA2CD9B55E6DAc6B42b7Cb2806dB',
[SupportedChainId.ARBITRUM_RINKEBY]: '0xa501c031958F579dB7676fF1CE78AD305794d579',
}
export const V2_FACTORY_ADDRESSES: AddressMap = constructSameAddressMap(V2_FACTORY_ADDRESS)
export const V2_ROUTER_ADDRESS: AddressMap = constructSameAddressMap('0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D')
export const V3_ROUTER_ADDRESS: AddressMap = constructSameAddressMap('0xE592427A0AEce92De3Edee1F18E0157C05861564', [
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISTIC_KOVAN,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
])
export const SWAP_ROUTER_ADDRESSES: AddressMap = constructSameAddressMap('0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45', [
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISTIC_KOVAN,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
])
/**
* The oldest V0 governance address
@@ -73,12 +88,7 @@ export const ENS_REGISTRAR_ADDRESSES: AddressMap = {
export const SOCKS_CONTROLLER_ADDRESSES: AddressMap = {
[SupportedChainId.MAINNET]: '0x65770b5283117639760beA3F867b69b3697a91dd',
}
export const SWAP_ROUTER_ADDRESSES: AddressMap = constructSameAddressMap('0xE592427A0AEce92De3Edee1F18E0157C05861564', [
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISTIC_KOVAN,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
])
export const V3_MIGRATOR_ADDRESSES: AddressMap = constructSameAddressMap('0xA5644E29708357803b5A882D272c41cC0dF92B34', [
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,

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