Compare commits

...

262 Commits

Author SHA1 Message Date
Crowdin Bot
a20b2ff7fa chore(i18n): synchronize translations from crowdin [skip ci] 2021-09-10 07:05:03 +00:00
Noah Zinsmeister
a52b7bf06b feat: use SDK to format calldata passed to quoter (#2305)
* replace useSingleCallResult implementation

use useSingleContractMultipleData instead

* use sdk methods to format quoter calldata

* create cleaner useSingleContractWithCallData fn

* clean up useMultipleContractSingleData

* address comments
2021-09-09 16:40:37 -04:00
Moody Salem
0ab1e5f144 fix: cherry pick the logs hook fix from the liquidity incentives branch (#2315) 2021-09-08 14:06:41 -05:00
Justin Domingue
115c72db9e chore: start tracking approvals in ReactGA (#2311)
* chore: start tracking approvals in ReactGA

* move reactga to swap-only code
2021-09-08 10:22:44 -04:00
Ian Lapham
2bb695d84c reduce popup dismiss time on l2; (#2262) 2021-09-07 12:42:30 -04:00
kun
3ea6f6e0c8 chore(ci): upgrade crust pinner adapt to crust mainnet (#2281)
* Add Pin to Crust workflow

* Upgrade crust pinner adapt Crust Mainnet
2021-09-07 09:37:07 -05:00
Noah Zinsmeister
627a5d3a98 Squashed commit of the following:
commit 5f1d1af62bcd47286aafacc18788f4e7e22dd7c0
Author: Aseem Sood <asood123@yahoo.com>
Date:   Fri Sep 3 12:12:39 2021 -0400

    update readme

    tweak

commit 80a5b95c0e0ae8934b5591c982077eaa813db747
Author: Noah Zinsmeister <noahwz@gmail.com>
Date:   Fri Sep 3 11:52:56 2021 -0400

    consolidate and standardize unsupported tokens
2021-09-05 13:26:47 -04:00
Crowdin Bot
353c4751b8 chore(i18n): synchronize translations from crowdin [skip ci] 2021-09-03 17:08:55 +00:00
Zach Pomerantz
416a3f9433 chore: use native flatMap (#2231) 2021-09-03 12:29:37 -04:00
Noah Zinsmeister
30cffb7442 feat: governor bravo support (#2271)
* first pass

* Fix code style issues with ESLint

* address comments

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-09-03 12:29:25 -04:00
Zach Pomerantz
f59c5f6877 chore: paint currency selector on initialization (#2245)
Avoids a flash of "Select a token" as the App initializes.
2021-09-02 10:57:01 -07:00
Zach Pomerantz
4bdf3c191a fix: always paint StyledPollingNumber (#2254)
Adds a en-space to StyledPollingNumber so it is always painted. Fixes a layout shift when rendering the first block number.
StyledPollingNumber is taller than its container, so waiting to paint it causes a layout shift when rendering.
2021-09-02 10:42:07 -07:00
Zach Pomerantz
7b829135df fix: make non-interactive header elements unselectable (#2288) 2021-09-01 15:30:17 -07:00
Zach Pomerantz
5831328364 style: enforce object-shorthand (#2286) 2021-09-01 13:32:20 -07:00
Jordan Frankfurt
6b99740f91 use network-specific help center link (#2285) 2021-09-01 16:01:24 -04:00
Zach Pomerantz
f5557a434c chore: upgrade type system (#2276)
* chore: upgrade type system

Upgrade types, typescript, and formatters.

* fix: ListsState tests
2021-09-01 12:38:10 -07:00
circlehotarux
5dfe44dad4 fix: older browser suppport (#2228)
* fix: Object.fromEntries polyfill

* fix: update polyfills

* fix: update polyfills
2021-09-01 11:17:49 -07:00
Justin Domingue
cd50d576d4 update optimism subgraph url (#2282) 2021-09-01 11:58:10 -04:00
dependabot[bot]
d922447ef2 chore(deps): bump tar from 6.1.4 to 6.1.11 (#2280)
Bumps [tar](https://github.com/npm/node-tar) from 6.1.4 to 6.1.11.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-tar/compare/v6.1.4...v6.1.11)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-01 09:31:25 -04:00
Jordan Frankfurt
81b1346937 update explorer links for arbitrum (#2279) 2021-08-31 18:19:26 -04:00
Zach Pomerantz
f51335df9d fix: disable serviceworker in integration tests (#2274) 2021-08-31 11:00:19 -07:00
Zach Pomerantz
13a289f6f1 Revert "test: support stubbing subgraph data in cypress (#2259)" (#2269)
This reverts commit 00403a4810.
2021-08-31 11:10:07 -04:00
Jordan Frankfurt
0afeb5acaf add arbitrum token list (#2268) 2021-08-31 11:05:12 -04:00
Zach Pomerantz
9e8d08e22c fix(governance): special case bravo parsing (#2247)
* fix(governance): special case bravo parsing

* fix(governance): explicitly parse U+2018, U+2026

* chore: upgrade ethers to ^5.4.6

Updates parseLog to delay parsing nested properties, so that they will only fail when accessed.

* fix(vote): recover from invalid UTF-8 in proposal descriptions

* fix(vote): special case bravo proposal newlines

* chore: rm dead reference
2021-08-31 10:47:06 -04:00
Ian Lapham
299286a0d9 update token list (#2270) 2021-08-31 10:37:58 -04:00
Justin Domingue
00403a4810 test: support stubbing subgraph data in cypress (#2259)
* upgrade to 7.0

* first iteration of stubbing subgraph in integration tests

* added fixtures

* add tests for fee tier distribution and liquidity chart

* remove unused test utils

* update yarn.lock

* fixed merge artifacts
2021-08-30 15:56:38 -04:00
Moody Salem
b8a9191653 fix: turn service workers back on (#1944)
reconfigure service workers to only cache used assets (excluding .po language files and non-.var.woff2 font files)

* fix: turn service workers back on

* chore: configure service worker caches

* chore: add newline

* Fix code style issues with ESLint

* chore: limit service-worker caching

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>
Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-08-25 12:46:56 -07:00
Moody Salem
1304acd8c7 fix: walletconnect on L2 issues with Rainbow (#2242)
* fix: remove the walletconnect bridge url

* fix: use the latest ethereum provider which fixes the optimism rainbow issue
2021-08-24 12:01:53 -05:00
Zach Pomerantz
8112e48e9a chore: rm redux-devtools-extension (#2230)
redux-devtools-extension integration is already provided through @reduxjs/toolkit.
2021-08-23 10:17:24 -05:00
Zach Pomerantz
f3c400c514 chore: remove unused cross-env (#2232) 2021-08-23 10:15:54 -05:00
Zach Pomerantz
c2c7d87ca3 chore: update multicodec usage (#2233) 2021-08-23 10:15:43 -05:00
Zach Pomerantz
db18b486b4 chore: rm unused styled-system (#2234) 2021-08-23 10:13:57 -05:00
Noah Zinsmeister
1613cc473b fix: proposal formatting 2021-08-20 16:57:45 -04:00
Noah Zinsmeister
e1f37b0ec6 fix(governance): hot fix for parseLog crash 2021-08-20 00:04:06 -04:00
Zach Pomerantz
805af2fdb4 chore(accessibility): increase contrast for connections (#2223) 2021-08-19 16:53:06 -04:00
Zach Pomerantz
b833d07b4f chore: change language icon to globe (#2222)
* chore: change language icon to globe
2021-08-19 16:44:28 -04:00
Moody Salem
ec5da7ec8f fix: update walletconnect for deep links and scalability (#2215) 2021-08-18 12:26:00 -05:00
Crowdin Bot
ff10346b06 chore(i18n): synchronize translations from crowdin [skip ci] 2021-08-17 11:04:45 +00:00
Crowdin Bot
f15522a47c chore(i18n): synchronize translations from crowdin [skip ci] 2021-08-17 00:09:22 +00:00
Crowdin Bot
67b66235ba chore(i18n): synchronize translations from crowdin [skip ci] 2021-08-16 23:05:26 +00:00
Zach Pomerantz
7153908935 chore: fix SEO and accessibility nits for Lighthouse (#2212)
* refactor: index.html

* chore: preconnect to GA

* chore(SEO): add meta content

* chore(SEO): add alt to ethereum logo

* chore(accessibility): add aria-labels to menus

* chore(accessibility): mark AppBody as main

* chore(accessibility): update nav link ids

* chore(accessibility): set html.lang to match i18n locale

* chore(refactor): mv html.lang to useEffect
2021-08-16 18:30:52 -04:00
Crowdin Bot
4541e37388 chore(i18n): synchronize translations from crowdin [skip ci] 2021-08-16 13:18:27 +00:00
Crowdin Bot
3bbcdcb8ae chore(i18n): synchronize translations from crowdin [skip ci] 2021-08-13 22:05:18 +00:00
Zach Pomerantz
2daee152ef chore(i18n): do not compile i18n messages (#2211) 2021-08-13 14:03:14 -07:00
Zach Pomerantz
9af7e0e0c3 chore(i18n): touch en-US to prevent lingui from adding a creation date (#2210)
Fixes #2198. Touching en-US.po before extracting messages prevents lingui from adding a creation date, maintaining deterministic builds and IPFS hashes.
2021-08-13 14:02:59 -07:00
Noah Zinsmeister
757741c3df fix: use @walletconnect/ethereum-provider instead of @walletconnect/web3-provider (#2175)
* use @walletconnect/ethereum-provider

* bump to support client-side disconnect
2021-08-13 10:01:56 -05:00
Moody Salem
a3aa6647e7 chore: set default_bump to false per https://github.com/mathieudutour/github-tag-action/issues/85 2021-08-11 10:19:37 -05:00
Moody Salem
f463df97bf fix: do not construct routes with the same pool twice (#2196) 2021-08-11 10:15:53 -05:00
Ian Lapham
99a282b7b4 update blocked address list (#2182)
Co-authored-by: Ian Lapham <ianlapham@Ians-MacBook-Pro.local>
2021-08-10 10:30:54 -04:00
Moody Salem
5a3165358f fix: #isTradeBetter should check that output currencies are equal (#2177)
* fix: isTradeBetter should check that input/output are equal

* fix the fix
2021-08-10 09:25:43 -05:00
Moody Salem
26e334c78f fix: proposal descriptions should be pulled from the right governor (#2176) 2021-08-10 08:53:22 -05:00
Justin Domingue
bd8192c81e update chinese simplified and traditional display names (#2180) 2021-08-10 09:05:17 -04:00
Justin Domingue
ff6bd38992 add ReactGA event for full range (#2181) 2021-08-10 09:05:05 -04:00
Justin Domingue
a48b7ce702 fix: avoid running BestV3Trade when tokens are the same (#2172)
* avoid running BestV3Trade when tokens are the same

* flipped condition
2021-08-09 10:38:23 -04:00
Crowdin Bot
fc08d1fc6a chore(i18n): synchronize translations from crowdin [skip ci] 2021-08-06 20:05:37 +00:00
Ian Lapham
b416603ff4 update unsupported list (#2143)
Co-authored-by: Ian Lapham <ianlapham@Ians-MacBook-Pro.local>
2021-08-05 20:52:17 -04:00
Justin Domingue
c4dc26e1b2 fix: rename weth9 to weth (#2133)
* update @uniswap/sdk-core and @uniswap/v3-sdk

* reorder
2021-08-05 14:43:02 -04:00
Justin Domingue
adfe7225a8 upgrade to walletconnect 1.5.2 (#2134) 2021-08-04 08:49:46 -04:00
dependabot[bot]
13e347ee02 chore(deps): bump tar from 6.1.0 to 6.1.4 (#2132)
Bumps [tar](https://github.com/npm/node-tar) from 6.1.0 to 6.1.4.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-tar/compare/v6.1.0...v6.1.4)

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

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-04 08:40:22 -04:00
Crowdin Bot
b527bfbeea chore(i18n): synchronize translations from crowdin [skip ci] 2021-08-03 12:05:13 +00:00
Crowdin Bot
a25df00ef6 chore(i18n): synchronize translations from crowdin [skip ci] 2021-08-03 08:05:04 +00:00
Crowdin Bot
2446a6e88b chore(i18n): synchronize translations from crowdin [skip ci] 2021-08-03 07:04:40 +00:00
Crowdin Bot
3b3bf14e8a chore(i18n): synchronize translations from crowdin [skip ci] 2021-08-02 19:04:33 +00:00
Crowdin Bot
017e79f7ae chore(i18n): synchronize translations from crowdin [skip ci] 2021-08-02 17:09:09 +00:00
Crowdin Bot
804fe8f5ee chore(i18n): synchronize translations from crowdin [skip ci] 2021-08-02 15:05:55 +00:00
Jordan Frankfurt
bf01b0d342 fix: optimism link and some copy changes (#2126)
* fix: optimism link

* fix: (un)planned downtime copy changes
2021-08-02 10:50:46 -04:00
Crowdin Bot
f46f73f35f chore(i18n): synchronize translations from crowdin [skip ci] 2021-08-02 06:05:04 +00:00
Crowdin Bot
cad3575247 chore(i18n): synchronize translations from crowdin [skip ci] 2021-08-02 03:10:20 +00:00
Crowdin Bot
17aa9fcdb0 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-31 12:05:49 +00:00
Crowdin Bot
087745d7c6 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-31 08:05:54 +00:00
Lint Action
20e6ca6fd5 Fix code style issues with ESLint 2021-07-30 22:06:48 +00:00
Justin Domingue
d51b7e779b update full range documentation link 2021-07-30 15:05:43 -07:00
Matthew Quinn
cfd0412d78 fix: center import pool text V2 (#2122)
* fix: center import pool text V2

* fix: updated based on feedback

Co-authored-by: Matthew Quinn <matthew.quinn@libertymutual.com>
2021-07-30 14:39:46 -04:00
Matthew Quinn
50afef03cb fix: update mobile header to fix overflow (#2125)
Co-authored-by: Matthew Quinn <matthew.quinn@libertymutual.com>
2021-07-30 14:38:40 -04:00
Crowdin Bot
43b6e7abf4 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-30 05:04:40 +00:00
Moody Salem
64b9df8710 fix: upgrade ethers to allow constructing transaction data compatible with eip-1559 (#2121)
* fix: upgrade ethers to allow constructing transaction data compatible with eip-1559

* bump the experimental provider
2021-07-29 17:46:35 -05:00
Crowdin Bot
d75271484a chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-29 21:04:53 +00:00
Justin Domingue
952cc98df3 fix: add support for full range positions in add liquidity (#2090)
* remove arbitrary range buttons and move full range button

* better align full range to deposit

* support dragging range when in full range

* hack to support full range brushing

* restore zoom levels

* adjusted for mocks

* fix styling

* simplify type

* restore rate toggle change

* fix lower bound by looking at isSorted

* better align bottoms

* add reset button for full range positions

* only flip when not at limit

* fix +/- buttons in range selector

* add help link
2021-07-29 13:47:03 -07:00
Crowdin Bot
9b7637e012 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-29 19:04:45 +00:00
Jordan Frankfurt
a7f599127b add link (#2113) 2021-07-29 14:16:47 -04:00
Jordan Frankfurt
676890d89c fix(L2): update downtime warning copy (#2114) 2021-07-29 14:16:09 -04:00
Lint Action
beb1bf3bdc Fix code style issues with ESLint 2021-07-29 17:26:43 +00:00
Justin Domingue
631c202c49 document useQueryCacheInvalidator
explains why `chainId` is pulled directly from the store.
2021-07-29 10:25:29 -07:00
Moody Salem
491c9b4fd3 fix: upgrade walletconnect to 1.5.1 (#2120)
* upgrade walletconnect to 1.5.0

* bump again
2021-07-28 13:48:10 -05:00
Crowdin Bot
dcaed9094e chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-28 11:04:43 +00:00
Jeffrey Lin
8996ba6e38 fix: toggle liquidity position when toggling rates (#2117)
If field A and field B are already populated, chances are the user wants
them to remain the same and only wants to invert the price range
selector.

The conversion isn't exact, but should be preferable to the previous
behavior.
2021-07-27 16:57:52 -07:00
Crowdin Bot
c8570e5427 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-27 20:04:39 +00:00
Ian Lapham
2806f6513a feat: use modal for txn confirmation on l2 (#2055)
* use modal for txn confirmation on l2

* update with error state

* remove 1 translation

* dedicated L2 component

* update styling on to be less jumpy

* add success animation to modal

* revert regular submitted content

* remove useless hook in popups

* remove import

Co-authored-by: Ian Lapham <ian_lapham@alumni.brown.edu>
2021-07-27 15:55:47 -04:00
Noah Zinsmeister
422c703e71 Revert "feat(uma): uma call option routing (#1385)"
This reverts commit 89d484d882.
2021-07-26 14:19:29 -04:00
Noah Zinsmeister
506493e8ab Revert "feat(routing): support mirror protocol routing as additional bases (#1375)"
This reverts commit 3198129af2.
2021-07-26 13:51:53 -04:00
Justin Domingue
8dfd143208 test: set up component snapshot testing (#2102)
* set up snapshot testing

* improvements

* add tests for TextInput as an example

* Fix code style issues with ESLint

* add comment to custom-test-env file

* only set up needed providers

* include style rules in snapshots

* disable redux storage warning

* added setupTests to avoid boilerplate

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-07-26 10:41:08 -07:00
Moody Salem
718003b6f2 fix: prevent error if user selects ETH and WETH in add liquidity (#2112)
* fix: prevent error if user selects ETH and WETH in add liquidity

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

* linting error

* revert changes to Tokens.ts
2021-07-26 11:08:15 -05:00
Moody Salem
80b3aa9e61 fix: remove html ids and unnecessary keys from the menu 2021-07-26 07:34:11 -05:00
Ian Lapham
4078390a48 update unsupported and broken lists (#2100) 2021-07-23 18:05:14 -04:00
Justin Domingue
e07599ef0f fix: better support decimal overflow (#2075)
* better support decimal overflow

* improve tests

* explicitly define num digits in toSignificant

* add tests for 0 decimals

* undo change to tryParseAmount and instead compute price without relying on parse

* remove console log and add test

* removed comment

* addressed pr feedback

* improve parsing logic

* addressed pr feedback
2021-07-23 11:11:30 -07:00
Ian Lapham
311bdc19b3 add UniH to unsupported list (#2097) 2021-07-23 13:46:05 -04:00
Moody Salem
a6342d40f1 feat: support connecting to a gnosis safe (#2096)
* feat: support connecting to a gnosis safe

* add the manifest.json attributes required by gnosis safe

* remove background fill

* copy

* only try injected provider if not active after trying safe
2021-07-23 12:04:18 -05:00
Moody Salem
3686506f2a perf: remove extra spread operators from the combine maps function 2021-07-23 09:27:23 -05:00
Moody Salem
634d010d92 perf: list to token map function was doing too many reduces (#2095) 2021-07-23 09:16:56 -05:00
Moody Salem
979f29ad62 chore: copy edit on CONTRIBUTING.md 2021-07-22 16:14:55 -05:00
Justin Domingue
4d3ed5d6ba feat: routing api integration (#2080)
* initial routing api integration

* add routing api slice

* display route in dialog

* addressed pr feedback

* switch to `get`

* renamed useRouterTradeExactIn to useRouter

* moving few files to later iteration

* removed unnecessary `as`

* switch to polling

* add todo for blocknumber freshness

* remove account-slippage-deadline
2021-07-21 15:08:35 -07:00
Moody Salem
ea79fbc2e0 chore: add blurb about release process (#2061)
* chore: add blurb about release process

* additional standard

* how to collect feedback
2021-07-20 16:10:58 -05:00
Jordan Frankfurt
4d19122bd2 set 5 min deadline on swaps and hide UI on L2 (#2081) 2021-07-20 12:41:49 -04:00
Moody Salem
13d7d2c992 fix(swap): double max gas for quoting a v3 swap for large trades 2021-07-19 19:00:52 -05:00
Brendan Weinstein
02cf33e115 Update WalletLink and @web3-react/walletlink-connector (#2079) 2021-07-19 14:52:11 -07:00
Justin Domingue
1f62cdf7a2 fix: invert selected range on RateToggle instead of clearing inputs (#2078) 2021-07-19 11:22:46 -07:00
Crowdin Bot
701e8fe116 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-19 13:09:18 +00:00
Crowdin Bot
3207e6026e chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-19 10:05:27 +00:00
Justin Domingue
b0d93dbc0e increase zoom out range (#2076) 2021-07-16 19:14:39 -07:00
Justin Domingue
975a5e3434 remove unused dependencies (#2074) 2021-07-16 15:03:19 -07:00
Ian Lapham
07f52f02ff feat: only block on app url, mobile UI tweaks (#2073)
* start iframe ui updates

* replace hook with constant

* add origin check for blocked lists

* remove origin check for local unsupported list

* remove redundant retun

* remove iframe check

* undo local change
2021-07-16 15:28:19 -04:00
Crowdin Bot
5f112611c8 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-16 18:22:45 +00:00
Justin Domingue
1d82a4c71e fix: set language using url param in language dropdown (#2063)
* set language using url param in language dropdown

* refactor common code into a hook

* add hook

* address pr feedback
2021-07-16 10:56:33 -07:00
Justin Domingue
6004c4be3e fix: change panning cursor to col-resize (#2071)
* change panning cursor to col-resize

* revert to grabby, and support grabbing
2021-07-16 10:49:27 -07:00
Justin Domingue
a00432c1c3 fix: remove chart clamping and show indicator when handle is off screen (#2064)
* initial off screen indicator

* adjust offscreen indicator

* add off screen handle indicator

* hide reset until we get a better behavior

* add svg.tsx
2021-07-15 16:31:06 -07:00
Crowdin Bot
a184afa41e chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-15 22:05:15 +00:00
Crowdin Bot
49fd256e79 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-15 17:09:03 +00:00
Crowdin Bot
c006edf696 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-15 16:05:00 +00:00
Moody Salem
b16dd2a930 chore: add one more unit test for ALL_SUPPORTED_CHAIN_IDS 2021-07-15 10:04:43 -05:00
Moody Salem
08a8f669e8 fix: all supported chain ids should not include the string keys of the enum 2021-07-15 09:59:26 -05:00
Justin Domingue
95d91c8528 fix: add language selector in top-level menu (#2057)
* initial language selector

* set > icon opacity for consistency

* always reset menu to main on close

* always reset menu on menu open
2021-07-15 07:46:05 -07:00
Justin Domingue
03902fe5c1 remove duplicate set price range title when there is liquidity (#2058) 2021-07-14 17:43:45 -07:00
Ian Lapham
631052e6a5 fix: update balance check in derived swap state (#2054)
* update balance check in derived swap state

* replace balance check with correct version of trade
2021-07-14 19:23:46 -04:00
Moody Salem
c63bb84f09 fix(block number): link to the correct page in etherscan for optimism block number links 2021-07-14 11:56:37 -05:00
Moody Salem
f0d10ba0df fix: add DAI and WBTC and USDT to routing for Optimism
fixes https://github.com/Uniswap/uniswap-interface/issues/2016
2021-07-14 11:50:04 -05:00
Moody Salem
88600d3a78 refactor: clean up some constants 2021-07-14 11:20:37 -05:00
Crowdin Bot
a1060d5f75 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-14 12:05:09 +00:00
Crowdin Bot
83762b3c6a chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-14 07:04:47 +00:00
Crowdin Bot
7784f647e1 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-14 02:12:32 +00:00
Moody Salem
65ba56c5f0 fix(arbitrum): use infura endpoints 2021-07-13 20:44:37 -05:00
Crowdin Bot
963514cd51 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-14 01:25:14 +00:00
Crowdin Bot
6d0c8037a8 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-13 22:04:48 +00:00
Justin Domingue
d5ef7f9b24 fix: revert remove x-axis clamping behavior in liquidity chart (#2045)
* Revert "fix: remove x-axis clamping behavior in liquidity chart (#2042)"

This reverts commit f86db00464.

* removed extra curly braces around errorMessage
2021-07-13 14:33:13 -07:00
Justin Domingue
f86db00464 fix: remove x-axis clamping behavior in liquidity chart (#2042)
* remove x-axis clamping behavior

* remove extra curly braces around errorMessage
2021-07-13 14:17:52 -07:00
Justin Domingue
8884020fab chore: support stubbing subgraph in integration tests (#2039)
* upgrade to 7.0

* first iteration of stubbing subgraph in integration tests
2021-07-13 12:54:49 -07:00
Ian Lapham
db145658db update links (#2040) 2021-07-13 15:44:10 -04:00
Ian Lapham
55bbaffe15 fix: support for isolated pool creation (#2029)
* revert to remove ui polish changes

* left align blurb and fix bp issue

* fix translation wrapper

* remove preview screen for create, hide submitted txn UI for create, copy updates

* small fixes

* copy updates for create blurb

* add set price range title

* add translation

* fix some translations

Co-authored-by: Justin Domingue <judo@uniswap.org>
2021-07-13 15:18:09 -04:00
Callil Capuozzo
e1c3ad8f54 fix(L2): Design tweaks for L2 (Optimism) (#2038)
Style tweaks to header layout, network dropdown and pool pages

Co-authored-by: Jordan Frankfurt <jordanwfrankfurt@gmail.com>
2021-07-13 15:08:23 -04:00
Ian Lapham
730af2eed4 fix: dismiss txn confirmation modal if on L2 (#2022)
* dismiss txn confirmation modal if on l2

* line up far right margin;

* remove submitted view on txns if on L2

* remove uneeded chainid

* wrap dismissal in useEffect

* update variable names
2021-07-13 14:37:35 -04:00
Jordan Frankfurt
c625d15feb fix(L2): add downtime warning to add liquidity and adjust styles of network notification (#2034)
* add liquidity warning and network notification

* remove unneeded width

* use externalLink in LinkOutToBridge component

Co-authored-by: Moody Salem <moody.salem@gmail.com>
2021-07-13 12:30:24 -05:00
Justin Domingue
f9de85251c fix overflow on mobile (#2037) 2021-07-13 10:11:50 -07:00
Moody Salem
a186833b7a fix(L2): enable optimism token list when switched to optimism (#2036)
* add title text and opacity variability to token lists

* add optimism token list

* show tokens from active lists

* sort up token lists
with tokens on the current chain

* fix up some type issues

prune out chainId changes

* clean up leftover any

* refactor token count mechanism

* handle plurals in title text string

* new combineMaps implementation

* remove custom plural

* address a couple nits

* show the number of tokens on current chain

Co-authored-by: Jordan Frankfurt <jordanwfrankfurt@gmail.com>
Co-authored-by: Justin Domingue <judo@uniswap.org>
2021-07-13 11:51:59 -05:00
Luke Donato
aa06db7684 add optional chaining for stakingInfo (#1950) 2021-07-13 11:06:29 -05:00
Moody Salem
f5b601ee99 fix(L2): use 10 bips slippage for swaps by default on L2 (#2035)
* use 10bip slippage on L2

* update deps

Co-authored-by: Jordan Frankfurt <jordanwfrankfurt@gmail.com>
2021-07-13 10:41:46 -05:00
Justin Domingue
bf30013b6c fix: add liquidity ux polish (#2024)
* zoom out on intiial

* adjust initial zoom ranges

* remove unneeded reactga event

* adjust full range warning copy

* update zoom

* adjust zoom ranges and label around current price
2021-07-13 08:23:24 -07:00
Justin Domingue
2bc2a2c76e compare with undefined rather than falsy (#2027) 2021-07-12 16:13:45 -07:00
Justin Domingue
28bf95123e load pool data for each tier to determine state (#2026) 2021-07-12 15:56:55 -07:00
Justin Domingue
b1d88317b9 fix: properly set upper/lower base/quote on manual inversion (#2023) 2021-07-12 14:58:44 -07:00
Moody Salem
f332315de9 fix(routing): updates routing constants for layer 2 networks to use ETH (#2018) 2021-07-12 15:30:28 -05:00
Ian Lapham
2fbb86ac44 fix: update CMC priority within lists (#2019) 2021-07-12 16:07:08 -04:00
Justin Domingue
8d567e4d4a fix: add liquidity flow polish (#2017)
* addressed feedback

* set initial, min and max zoom levels

* better handle 0

* avoid formatting range selector

* polish `not created` state

* remove unused import
2021-07-12 10:19:12 -07:00
Moody Salem
77fbccd3f1 fix: remove dollar sign from translation of eth amount
fixes https://github.com/Uniswap/uniswap-interface/issues/1986
2021-07-12 11:09:45 -05:00
Moody Salem
41330ab080 fix: price tokens on optimism mainnet using DAI 2021-07-12 11:03:10 -05:00
Jordan Frankfurt
4ba9d98e67 fix(L2): polish (#1988)
* polish

move getINfoLinkByChainId to chain constants

pr review - translations

optimism token list, typo, text color for L1 switch

use <Trans /> instead of t

undo unintentional tokenlist order change

use {'Optimism'} instead of {name}

switch deadline implementation

remove unused TYPE import

switch to generalized CHAIN_INFO instead of L2_INFO so we don't have to check in components

add target chain id param to optimism bridge

fix a minor breakpoint issue

reduce sigfigs for header balance

update network card dropdown text for optimism

remove list code

refactor SupportedChainId

* SupportedL1ChainId | SupportedL2ChainId -> SupportedChainId
2021-07-12 11:44:05 -04:00
Justin Domingue
52790ac92e support optimism and arbitrum subgraphs (#2001) 2021-07-12 08:26:11 -07:00
Crowdin Bot
380ca11dfd chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-10 23:05:01 +00:00
Crowdin Bot
e4261cc01b chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-10 22:05:10 +00:00
Crowdin Bot
99148dfd3f chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-09 18:05:05 +00:00
Moody Salem
8bf9a9ce8d chore(token lists): bump the token list spec version 2021-07-09 12:35:14 -05:00
Crowdin Bot
b6c0c1e607 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-09 16:05:04 +00:00
Crowdin Bot
0739613abe chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-09 12:05:06 +00:00
Crowdin Bot
3401dfdebe chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-09 06:05:09 +00:00
Justin Domingue
ab9c27edad fix: make liquidity chart and input step counter responsive (#2005)
* make chart responsive

* make input step counter text responsive

* clean up chart
2021-07-08 15:52:31 -07:00
Moody Salem
f2dfc57002 fix: retry calls that return header not found errors (#2004) 2021-07-08 17:12:55 -05:00
Justin Domingue
318252b6a9 fix: min/max price inverted in positions page (#2002)
* fix param ordering in inverter hook

* use object param
2021-07-08 17:06:24 -04:00
Noah Zinsmeister
440c50dd3a update multicall address 2021-07-08 15:49:06 -04:00
Crowdin Bot
eacf4e6049 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-08 19:04:32 +00:00
Justin Domingue
ffbd2d107a feat(pool): add liquidity distribution range slider and update add liquidity layout (#1829)
* first iteration of useTicks and useActiveLiquidity

* feat(pools): add liquidity depth chart (#1835)

* cleanup

* use area chart instead of bar chart

* check for undefined rather than falsy

* feat: range buttons based on fee amount (#1870)

* range buttons based on fee amount

* hardcode percentages as ticks

* increase blocksperfect

* feat: optimize add liquidity charts (#1880)

* ignore syncing state

* remove surrounding ticks

* avoid processing price1 as it is unused

* cleanup

* feat: add zoom buttons to liquidity depth chart (#1882)

* ignore syncing state

* remove surrounding ticks

* avoid processing price1 as it is unused

* cleanup

* first pass at +/- zoom buttons

* remove console.log, cleanup

* use real price for price line

* updated brush handles to latest spec

* added % labels to handles

* round tick to nearest usable tick

* first pass at brushable area chart with d3

* first pass at brushable area chart with d3

* rework

* address PR comments

* add brush handles

* address PR comments

* further improvements

* feat(pools): improve full range support + capital efficiency warning (#1903)

* handle min and max prices in add liquidity

* cleaned up

* use flag to denote full range

* reset inputs on fee select

* fixed merge conflict

* handle full range in positions preview

* fixed invalid range when tokens are reversed

* use formatTickData

* updated layout

* cleaned up layout

* fixed address

* avoid re-rendering deposit amounts

* added zoom behavior and more styling

* renamed chart

* renamed main chart file;

* add brush tooltips

* remove chart title

* added accents to brush handles

* more work

* moved to file

* modularize chart components

* fix maximum depth

* added brush labels

* cleanup

* cleanup

* set up zoom

* added new components

* improved brush and zoom integration

* cleaned up clip path

* fixed clip paths

* integrated with the graph changes

* adjust fee selector

* fix data error

* add bar chart

* polish

* merged

* clean up error

* cleaned up after merge

* visual improvements

* moved +/- buttons to the right/left

* removed margin bottom

* removed unsused

* fix brush labels % change

* use d3.axisBottom

* updated labels

* improve brush range

* fix one brush change only

* adjust zoom and clippath

* use bars

* use area

* adjust axis bottom to mocks;

* improved bars

* show bar colors

* better handle full range

* adjust colors for light mode

* updated to mocks

* adjusted handles for visibility

* switch to area

* add react ga events

* adjusted to mocks

* memo brush domain to avoid re-renders

* fix inputstepcounter color

* adjust handles

* rely on the graph sorting tickidx

* use curvestepafter

* updated polish

* merged main

* add clamping and other fixes

* highlight selected area using a mask

* use price instead of % for labels

* delete unused

* refine ux

* relayout

* improve hooks

* adjust layout for mobile

* fixed card color

* adjust padding

* preent tick overflow

* flip handles sooner

* delete bars.tsx
2021-07-08 11:30:43 -07:00
Justin Domingue
2db29f205b fix: simplify liqudity depth hooks and improve chart primitives (#1999)
* updated hooks and chart primitives

* add lodash.inrange to package.json
2021-07-08 11:12:36 -07:00
Crowdin Bot
3ae9e2af2a chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-08 17:12:35 +00:00
Noah Zinsmeister
5ff2cff841 feat: Add Optimism (#1923)
* preliminary support

* fix multicall abi

add explorer support

* Fix code style issues with ESLint

* fix gas estimation

* fix todo tests

* fix gas estimation comments

* anonymize links to kovan etherscan and poll more frequently on optimistic kovan

* separate pool creation

* remove supported chain id

* remove the blocktag

* do not use the blocktag on optimism only

* give more gas to tokenURI for optimism

* update sdk

* temp fix to the block tag thing (do not update the block number from the fetch block number)

* remove unused import

* gasRequired -> multi-network

* bump quoter gas limit to 6m on optimism

* move the gas required parameter by chain id one level up

* missed a hook

* retry fetching receipts for optimism as many times as arbitrum

* fix duplicate enum, add optimism as well to retry options config

* fix: state not getting updated after a transaction confirmation

* bump sdk version

* update for mainnet optimism (#1998)

* fix for calculateGasMargin on optimism

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
Co-authored-by: Moody Salem <moody.salem@gmail.com>
2021-07-08 12:46:21 -04:00
Crowdin Bot
0c0968864b chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-08 15:04:48 +00:00
Crowdin Bot
9230473c36 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-08 14:05:18 +00:00
ChrisFox.eth
b791afac59 Updating two public-facing URLs to docs (#1992) 2021-07-08 08:22:31 -05:00
Crowdin Bot
2899c8af45 chore(i18n): synchronize translations from crowdin [skip ci] 2021-07-08 06:04:59 +00:00
Moody Salem
5f13f7824a chore: update the contribution guidelines (#1987)
* chore: update the contribution guidelines

* move

* note about non-deterministic steps

* little bit of separation

* language nits, add accessibility

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

* add tickprocessed type

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

* Fix code style issues with ESLint

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

This reverts commit 9f5764aab3.

* Revert "fix l2 routing bug"

This reverts commit f6dea47907.

* drops routing changes for L2

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

* add optimism L2 UI

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

* enhance generated api with provide tags

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

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

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

* fix comment

* bit of cleanup

* unused imports

* improve loading indicator on vote page

* some testnet behavior improvements

* fix loader state

* loading state nit

* show correct indexes

* remove the unnecessary retry code

* first pass at the slice

* no throws

* loading indicator should go away if not connected

* use the logs slice for the logs hook state

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

* cleanup

* add polling interval

* moved ms.macro types to dev deps

* generate graphql schema on build

* added @types/ms.macro

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

* restored

* addressed some design feedback

* add pulsing animation on feeAmount change

* simplify fee tier title

* adjust button radios

* addressed some design feedback

* log ReactGA events

* ignore data while fetching

* update to use new generated queries

* remove deleted file

* add usefeetierdistribution hook

* invalidate cache and standardize the outlined card

* added react ga

* fix show options logic

* address design feedback

* show % select in minified view

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

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

* removed dependency on rtk-query/graphql

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

* yarn.lock

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

* remove introspection

* generated .ts and add to prettierignore

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

* Update src/state/data/slice.ts

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

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

* Remove React import

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

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

* Add TextInput component

* Allow adjusting AppBody maxwidth

* Add create proposal layout page

* Fix style consistency

* create proposal ui goes brrr

* various fixes

* minor tweaks and bug fixes

* merge conflict clean up + bug fix

* i18n

* always show create proposal button

* adjust proposal title top margin and show ButtonError styling

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

* revert mistaken css change

* default params for address input panel

* Refactor & fixes

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

* Fixed create proposal padding on medium screen

* refactor

* refactor pt. 2

* single column styling

* change AddressInputPanel font color

* Design adjustments

* change route for create proposal

* Add autonomous proposal CTA

* cta text edit

* Add link to docs for custom action

* small cleanup

* work with latest governor

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2021-06-22 15:51:32 -04:00
Crowdin Bot
c2fe17615f chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-22 13:12:10 +00:00
Crowdin Bot
a4cab75d09 chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-22 08:04:47 +00:00
Crowdin Bot
882457cfef chore(i18n): synchronize translations from crowdin [skip ci] 2021-06-21 16:05:32 +00:00
Justin Domingue
9a1bb5dbfb Add missing semicolon in English source 2021-06-21 08:10:00 -07:00
315 changed files with 27787 additions and 10523 deletions

1
.env
View File

@@ -1,2 +1 @@
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
REACT_APP_WALLETCONNECT_BRIDGE_URL="https://uniswap.bridge.walletconnect.org"

View File

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

View File

@@ -42,9 +42,11 @@ jobs:
env:
CI: false
REACT_APP_NETWORK_URL: "https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
REACT_APP_SERVICE_WORKER: false
- run: yarn integration-test
env:
CYPRESS_INTEGRATION_TEST_PRIVATE_KEY: ${{ secrets.CYPRESS_INTEGRATION_TEST_PRIVATE_KEY }}
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}

View File

@@ -23,6 +23,7 @@ jobs:
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
release_branches: .*
default_bump: false
create_release:
name: Create Release
@@ -55,7 +56,7 @@ jobs:
pinata-secret-api-key: ${{ secrets.PINATA_API_SECRET_KEY }}
- name: Pin to Crust
uses: crustio/ipfs-crust-action@v1.0.8
uses: crustio/ipfs-crust-action@v2.0.3
continue-on-error: true
timeout-minutes: 2
with:

3
.gitignore vendored
View File

@@ -7,6 +7,7 @@
/src/locales/**/*.ts
/src/locales/**/*.json
/src/locales/**/en-US.po
/src/state/data/generated.ts
# dependencies
/node_modules
@@ -39,4 +40,4 @@ package-lock.json
cypress/videos
cypress/screenshots
cypress/fixtures/example.json
cypress/fixtures/example.json

1
.prettierignore Normal file
View File

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

View File

@@ -1,46 +1,75 @@
# Development
## Install Dependencies
```bash
yarn install
```
## Generate locale files
```
yarn i18n:extract
```
## Run the interface
```bash
yarn start
```
# Contributing
Thank you for your interest in contributing to the Uniswap interface! 🦄
# Development
## Running the interface locally
1. `yarn install`
1. `yarn start`
## Creating a production build
1. `yarn install`
1. `yarn build`
## Engineering standards
Code merged into the `main` branch of this repository should adhere to high standards of correctness and maintainability.
Use your best judgment when applying these standards. If code is in the critical path, will be frequently visited, or
makes large architectural changes, consider following all the standards.
- Have at least one engineer approve of large code refactorings
- At least manually test small code changes, prefer automated tests
- Thoroughly unit test when code is not obviously correct
- If something breaks, add automated tests so it doesn't break again
- Add integration tests for new pages or flows
- Verify that all CI checks pass before merging
- Have at least one product manager or designer approve of significant UX changes
## Guidelines
The following points should help guide your development:
- Security: the interface is safe to use
- Avoid adding unnecessary dependencies due to [supply chain risk](https://github.com/LavaMoat/lavamoat#further-reading-on-software-supplychain-security)
- Reproducibility: anyone can build the interface
- Avoid adding steps to the development/build processes
- The build must be deterministic, i.e. a particular commit hash always produces the same build
- Decentralization: anyone can run the interface
- An Ethereum node should be the only critical dependency
- All other external dependencies should only enhance the UX ([graceful degradation](https://developer.mozilla.org/en-US/docs/Glossary/Graceful_degradation))
- Accessibility: anyone can use the interface
- The interface should be responsive, small and run well on low performance devices (majority of swaps on mobile!)
## Release process
Releases are cut automatically from the `main` branch Monday-Thursday in the morning according to the [release workflow](./.github/workflows/release.yaml).
Fix pull requests should be merged whenever ready and tested.
If a fix is urgently needed in production, releases can be manually triggered on [GitHub](https://github.com/Uniswap/uniswap-interface/actions/workflows/release.yaml)
after the fix is merged into `main`.
Features should not be merged into `main` until they are ready for users.
When building larger features or collaborating with other developers, create a new branch from `main` to track its development.
Use the automatic Vercel preview for sharing the feature to collect feedback.
When the feature is ready for review, create a new pull request from the feature branch into `main` and request reviews from
the appropriate UX reviewers (PMs or designers).
## Finding a first issue
Start with issues with the label
[`good first issue`](https://github.com/Uniswap/uniswap-interface/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
## Pull requests
**Please open all pull requests against the `main` branch.**
CI checks will run against all PRs.
# Translations
Help Uniswap reach a global audience!
Uniswap uses [Crowdin](https://crowdin.com/project/uniswap-interface) for managing translations.
[This workflow](./.github/workflows/crowdin.yaml) uploads new strings for translation to the Crowdin project whenever code using the [lingui translation macros](https://lingui.js.org/ref/macro.html) is merged into `main`.
Uniswap uses [Crowdin](https://crowdin.com/project/uniswap-interface)
for managing translations. Whenever a new string is added to the project,
it gets uploaded to Crowdin for translation by [this workflow](./.github/workflows/crowdin.yaml).
Every hour, translations are synced from Crowdin in [this other workflow](./.github/workflows/crowdin-sync.yaml).
Every hour, translations are synced back down from Crowdin to the repository in [this other workflow](./.github/workflows/crowdin-sync.yaml).
We sync to the repository on a schedule, rather than download translations at build time, so that builds are always reproducible.
You can contribute by joining Crowdin to proofread existing translations [here](https://crowdin.com/project/uniswap-interface/invite?d=93i5n413q403t4g473p443o4c3t2g3s21343u2c3n403l4b3v2735353i4g4k4l4g453j4g4o4j4e4k4b323l4a3h463s4g453q443m4e3t2b303s2a35353l403o443v293e303k4g4n4r4g483i4g4r4j4e4o473i5n4a3t463t4o4)

View File

@@ -26,14 +26,19 @@ To access the Uniswap Interface, use an IPFS gateway link from the
[latest release](https://github.com/Uniswap/uniswap-interface/releases/latest),
or visit [app.uniswap.org](https://app.uniswap.org).
## Unsupported tokens
Check out `useUnsupportedTokenList()` in [src/state/lists/hooks.ts](./src/state/lists/hooks.ts) for blocking tokens in your instance of the interface.
You can block an entire list of tokens by passing in a tokenlist like [here](./src/constants/lists.ts) or you can block specific tokens by adding them to [unsupported.tokenlist.json](./src/constants/tokenLists/unsupported.tokenlist.json).
## Contributions
See [CONTRIBUTING](./CONTRIBUTING.md).
For steps on local deployment, development, and code contribution, please see [CONTRIBUTING](./CONTRIBUTING.md).
## Accessing Uniswap V2
The Uniswap Interface supports swapping, adding liquidity, removing liquidity and migrating liquidity for
Uniswap protocol V2.
The Uniswap Interface supports swapping, adding liquidity, removing liquidity and migrating liquidity for Uniswap protocol V2.
- Swap on Uniswap V2: https://app.uniswap.org/#/swap?use=v2
- View V2 liquidity: https://app.uniswap.org/#/pool/v2
@@ -41,6 +46,6 @@ Uniswap protocol V2.
- Migrate V2 liquidity to V3: https://app.uniswap.org/#/migrate/v2
## Accessing Uniswap V1
The Uniswap V1 interface for mainnet and testnets is accessible via IPFS gateways
The Uniswap V1 interface for mainnet and testnets is accessible via IPFS gateways
linked from the [v1.0.0 release](https://github.com/Uniswap/uniswap-interface/releases/tag/v1.0.0).

11
codegen.yml Normal file
View File

@@ -0,0 +1,11 @@
overrideExisting: true
schema: 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3'
documents: 'src/**/!(*.d).{ts,tsx}'
generates:
./src/state/data/generated.ts:
plugins:
- typescript
- typescript-operations
- typescript-rtk-query:
importBaseApiFrom: './slice'
exportHooks: true

16
custom-test-env.js Normal file
View File

@@ -0,0 +1,16 @@
// Custom test environment to provide `TextEncoder`/`TextDecoder`
// eslint-disable-next-line @typescript-eslint/no-var-requires
const Environment = require('jest-environment-jsdom')
module.exports = class CustomTestEnvironment extends Environment {
async setup() {
await super.setup()
if (typeof this.global.TextEncoder === 'undefined') {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { TextEncoder, TextDecoder } = require('util')
this.global.TextEncoder = TextEncoder
this.global.TextDecoder = TextDecoder
}
}
}

View File

@@ -1,7 +1,7 @@
{
"projectId": "yp82ef",
"baseUrl": "http://localhost:3000",
"pluginsFile": false,
"fixturesFolder": false,
"supportFile": "cypress/support/index.js",
"video": false,
"defaultCommandTimeout": 10000

View File

@@ -0,0 +1,25 @@
{
"_meta": {
"block": {
"number": 99999999
}
},
"asToken0": [
{
"feeTier": "500",
"totalValueLockedToken0": "0",
"totalValueLockedToken1": "1"
},
{
"feeTier": "3000",
"totalValueLockedToken0": "0",
"totalValueLockedToken1": "7"
},
{
"feeTier": "10000",
"totalValueLockedToken0": "0",
"totalValueLockedToken1": "2"
}
],
"asToken1": []
}

View File

@@ -1,4 +1,13 @@
import { CyHttpMessages } from 'cypress/types/net-stubbing'
import { aliasQuery, hasQuery } from '../utils/graphql-test-utils'
describe('Add Liquidity', () => {
beforeEach(() => {
cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3', (req) => {
aliasQuery(req, 'feeTierDistribution')
})
})
it('loads the two correct tokens', () => {
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab/500')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
@@ -23,4 +32,32 @@ describe('Add Liquidity', () => {
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
})
it('loads fee tier distribution', () => {
cy.fixture('feeTierDistribution.json').then((feeTierDistribution) => {
cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3', (req: CyHttpMessages.IncomingHttpRequest) => {
if (hasQuery(req, 'feeTierDistribution')) {
req.alias = 'feeTierDistributionQuery'
req.reply({
body: {
data: {
...feeTierDistribution,
},
},
headers: {
'access-control-allow-origin': '*',
},
})
}
})
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab')
cy.wait('@feeTierDistributionQuery')
cy.get('#add-liquidity-selected-fee .selected-fee-label').should('contain.text', '0.3% fee tier')
cy.get('#add-liquidity-selected-fee .selected-fee-percentage').should('contain.text', '70%')
})
})
})

View File

@@ -4,6 +4,7 @@ describe('Landing Page', () => {
beforeEach(() => cy.visit('/'))
it('loads swap page', () => {
cy.get('#swap-page')
cy.screenshot()
})
it('redirects to url /swap', () => {

View File

@@ -0,0 +1,12 @@
// Utility to match GraphQL mutation based on the query name
export const hasQuery = (req: any, queryName: string) => {
const { body } = req
return body.hasOwnProperty('query') && body.query.includes(queryName)
}
// Alias query if queryName matches
export const aliasQuery = (req: any, queryName: string) => {
if (hasQuery(req, queryName)) {
req.alias = `${queryName}Query`
}
}

View File

@@ -4,7 +4,12 @@
"homepage": ".",
"private": true,
"devDependencies": {
"@ethersproject/experimental": "^5.2.0",
"@ethersproject/experimental": "^5.4.0",
"@gnosis.pm/safe-apps-web3-react": "^0.6.0",
"@graphql-codegen/cli": "1.21.5",
"@graphql-codegen/typescript": "1.22.3",
"@graphql-codegen/typescript-operations": "^1.18.2",
"@graphql-codegen/typescript-rtk-query": "^1.1.1",
"@lingui/cli": "^3.9.0",
"@lingui/loader": "^3.9.0",
"@lingui/macro": "^3.9.0",
@@ -14,14 +19,18 @@
"@reach/dialog": "^0.10.3",
"@reach/portal": "^0.10.3",
"@react-hook/window-scroll": "^1.3.0",
"@reduxjs/toolkit": "^1.3.5",
"@reduxjs/toolkit": "^1.6.1",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@typechain/ethers-v5": "^7.0.0",
"@types/d3": "^6.7.1",
"@types/jest": "^25.2.1",
"@types/lingui__core": "^2.7.1",
"@types/lingui__macro": "^2.7.4",
"@types/lingui__react": "^2.8.3",
"@types/lodash.flatmap": "^4.5.6",
"@types/luxon": "^1.24.4",
"@types/ms.macro": "^2.0.0",
"@types/multicodec": "^1.0.0",
"@types/node": "^13.13.5",
"@types/qs": "^6.9.2",
@@ -42,38 +51,42 @@
"@uniswap/governance": "^1.0.2",
"@uniswap/liquidity-staker": "^1.0.2",
"@uniswap/merkle-distributor": "1.0.1",
"@uniswap/token-lists": "^1.0.0-beta.19",
"@uniswap/sdk-core": "^3.0.1",
"@uniswap/token-lists": "^1.0.0-beta.25",
"@uniswap/v2-core": "1.0.0",
"@uniswap/v2-periphery": "^1.1.0-beta.0",
"@uniswap/v2-sdk": "^3.0.0-alpha.2",
"@uniswap/v3-core": "1.0.0",
"@uniswap/v3-periphery": "1.0.0",
"@uniswap/v3-sdk": "^3.0.0-alpha.9",
"@uniswap/v3-periphery": "^1.1.1",
"@uniswap/v3-sdk": "^3.4.1",
"@web3-react/core": "^6.0.9",
"@web3-react/fortmatic-connector": "^6.0.9",
"@web3-react/injected-connector": "^6.0.7",
"@web3-react/portis-connector": "^6.0.9",
"@web3-react/walletconnect-connector": "^6.2.0",
"@web3-react/walletlink-connector": "^6.2.0",
"@web3-react/walletconnect-connector": "^7.0.2-alpha.0",
"@web3-react/walletlink-connector": "^6.2.3",
"ajv": "^6.12.3",
"cids": "^1.0.0",
"copy-to-clipboard": "^3.2.0",
"cross-env": "^7.0.2",
"cypress": "^4.11.0",
"cypress": "^7.7.0",
"d3": "^7.0.0",
"eslint": "^7.11.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-react": "^7.19.0",
"eslint-plugin-react-hooks": "^4.0.0",
"ethers": "^5.2.0",
"ethers": "^5.4.6",
"graphql": "^15.5.0",
"graphql-request": "^3.4.0",
"inter-ui": "^3.13.1",
"lightweight-charts": "^3.3.0",
"lodash.flatmap": "^4.5.0",
"jest-styled-components": "^7.0.5",
"luxon": "^1.25.0",
"ms.macro": "^2.0.0",
"multicodec": "^3.0.1",
"multihashes": "^4.0.2",
"node-vibrant": "^3.1.5",
"polished": "^3.3.2",
"polyfill-object.fromentries": "^1.0.1",
"prettier": "^2.2.1",
"qs": "^6.9.4",
"react": "^17.0.1",
@@ -82,6 +95,7 @@
"react-dom": "^17.0.1",
"react-feather": "^2.0.8",
"react-ga": "^2.5.7",
"react-is": "^17.0.2",
"react-markdown": "^4.3.1",
"react-popper": "^2.2.3",
"react-redux": "^7.2.2",
@@ -95,33 +109,32 @@
"redux-localstorage-simple": "^2.3.1",
"serve": "^11.3.2",
"start-server-and-test": "^1.11.0",
"styled-components": "^4.2.0",
"styled-system": "^5.1.5",
"styled-components": "^5.3.0",
"typechain": "^5.0.0",
"typescript": "^4.2.3",
"ua-parser-js": "^0.7.28",
"use-count-up": "^2.2.5",
"wcag-contrast": "^3.0.0",
"workbox-core": "^6.1.0",
"workbox-expiration": "^6.1.0",
"workbox-precaching": "^6.1.0",
"workbox-routing": "^6.1.0",
"workbox-strategies": "^6.1.0"
"workbox-routing": "^6.1.0"
},
"resolutions": {
"@walletconnect/web3-provider": "1.4.2-rc.2"
"@walletconnect/ethereum-provider": "1.6.4"
},
"scripts": {
"compile-contract-types": "yarn compile-external-abi-types && yarn compile-v3-contract-types",
"compile-external-abi-types": "typechain --target ethers-v5 --out-dir src/abis/types './src/abis/**/*.json'",
"compile-v3-contract-types": "typechain --target ethers-v5 --out-dir src/types/v3 './node_modules/@uniswap/?(v3-core|v3-periphery)/artifacts/contracts/**/*.json'",
"build": "yarn compile-contract-types && yarn i18n:extract && yarn i18n:compile && react-scripts build",
"build": "yarn compile-contract-types && yarn graphql:generate && yarn i18n:extract && react-scripts build",
"i18n:extract": "lingui extract --locale en-US",
"i18n:compile": "lingui compile",
"integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'",
"integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run --record'",
"graphql:generate": "graphql-codegen --config codegen.yml",
"postinstall": "yarn compile-contract-types",
"start": "yarn compile-contract-types && react-scripts start",
"test": "react-scripts test --env=jsdom"
"test": "react-scripts test --env=./custom-test-env.js",
"prei18n:extract": "touch src/locales/en-US.po",
"prestart": "yarn graphql:generate && yarn prei18n:extract"
},
"eslintConfig": {
"extends": "react-app",

View File

@@ -0,0 +1,11 @@
<svg width="257" height="256" viewBox="0 0 257 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M96.4347 60.2768C94.3434 59.9524 94.2552 59.9143 95.2394 59.7631C97.1254 59.473 101.579 59.8683 104.648 60.5981C111.812 62.3011 118.331 66.6637 125.29 74.4117L127.139 76.4701L129.784 76.0449C140.926 74.2544 152.26 75.6774 161.741 80.057C164.349 81.2619 168.461 83.6603 168.975 84.2766C169.138 84.473 169.439 85.7374 169.642 87.0866C170.347 91.7544 169.994 95.3323 168.567 98.0046C167.79 99.4588 167.746 99.9197 168.268 101.164C168.686 102.157 169.848 102.892 170.999 102.891C173.355 102.888 175.891 99.0791 177.066 93.78L177.532 91.6751L178.457 92.7224C183.528 98.4681 187.512 106.304 188.196 111.881L188.374 113.336L187.522 112.014C186.054 109.739 184.58 108.19 182.693 106.941C179.29 104.69 175.693 103.923 166.164 103.421C157.559 102.968 152.689 102.233 147.859 100.658C139.643 97.9792 135.501 94.4114 125.74 81.6059C121.405 75.918 118.726 72.7711 116.06 70.2368C110.004 64.4784 104.053 61.4584 96.4347 60.2768Z" fill="#FF007A"/>
<path d="M170.916 72.9763C171.132 69.1649 171.649 66.651 172.688 64.3552C173.099 63.4465 173.485 62.7027 173.544 62.7027C173.604 62.7027 173.425 63.3735 173.147 64.1931C172.391 66.4212 172.267 69.4687 172.788 73.0144C173.448 77.5132 173.824 78.1623 178.579 83.022C180.809 85.3014 183.403 88.1762 184.344 89.4105L186.054 91.6547L184.344 90.0508C182.253 88.0895 177.444 84.2644 176.381 83.7176C175.669 83.3509 175.563 83.3573 175.124 83.7946C174.719 84.1975 174.634 84.803 174.577 87.6654C174.49 92.1267 173.882 94.9901 172.414 97.8533C171.621 99.4019 171.495 99.0714 172.214 97.3235C172.75 96.0184 172.805 95.4446 172.801 91.1259C172.792 82.4485 171.762 80.3624 165.721 76.7887C164.19 75.8834 161.668 74.5778 160.117 73.8872C158.565 73.1965 157.333 72.595 157.378 72.5501C157.549 72.3798 163.441 74.0995 165.812 75.0117C169.339 76.3686 169.922 76.5444 170.35 76.3807C170.637 76.271 170.776 75.4347 170.916 72.9763Z" fill="#FF007A"/>
<path d="M100.497 87.8209C96.2514 81.9758 93.6246 73.0138 94.1933 66.3144L94.3691 64.2413L95.3355 64.4176C97.1504 64.7486 100.28 65.9133 101.745 66.8033C105.766 69.2453 107.506 72.4605 109.277 80.7164C109.796 83.1346 110.477 85.8712 110.79 86.7976C111.294 88.2889 113.199 91.7721 114.748 94.0343C115.864 95.6636 115.123 96.4356 112.657 96.213C108.89 95.873 103.788 92.3519 100.497 87.8209Z" fill="#FF007A"/>
<path d="M165.766 131.323C145.925 123.335 138.937 116.4 138.937 104.7C138.937 102.979 138.996 101.57 139.068 101.57C139.14 101.57 139.908 102.138 140.774 102.833C144.797 106.06 149.303 107.438 161.776 109.258C169.115 110.328 173.245 111.193 177.056 112.457C189.166 116.473 196.658 124.624 198.445 135.725C198.964 138.951 198.66 145 197.818 148.188C197.153 150.706 195.125 155.245 194.588 155.419C194.439 155.468 194.292 154.896 194.254 154.118C194.05 149.95 191.943 145.891 188.406 142.851C184.383 139.395 178.979 136.643 165.766 131.323Z" fill="#FF007A"/>
<path d="M151.837 134.642C151.588 133.163 151.157 131.273 150.879 130.444L150.372 128.935L151.313 129.991C152.614 131.451 153.642 133.32 154.514 135.81C155.179 137.71 155.254 138.275 155.249 141.362C155.244 144.393 155.161 145.029 154.546 146.739C153.578 149.436 152.376 151.348 150.359 153.4C146.735 157.089 142.075 159.131 135.351 159.978C134.182 160.125 130.775 160.373 127.78 160.529C120.232 160.922 115.264 161.733 110.801 163.3C110.159 163.525 109.586 163.662 109.528 163.604C109.347 163.425 112.386 161.613 114.897 160.404C118.436 158.699 121.96 157.768 129.854 156.454C133.754 155.804 137.781 155.016 138.804 154.702C148.461 151.741 153.426 144.1 151.837 134.642Z" fill="#FF007A"/>
<path d="M160.932 150.795C158.296 145.128 157.691 139.657 159.135 134.554C159.289 134.009 159.538 133.562 159.687 133.562C159.837 133.562 160.459 133.899 161.07 134.31C162.284 135.127 164.721 136.505 171.212 140.044C179.311 144.46 183.929 147.879 187.07 151.786C189.82 155.208 191.522 159.104 192.341 163.856C192.805 166.548 192.533 173.024 191.843 175.735C189.665 184.281 184.604 190.993 177.385 194.911C176.327 195.484 175.377 195.955 175.275 195.958C175.172 195.96 175.557 194.98 176.131 193.78C178.56 188.703 178.836 183.765 177 178.269C175.876 174.904 173.584 170.797 168.956 163.857C163.575 155.788 162.256 153.641 160.932 150.795Z" fill="#FF007A"/>
<path d="M86.4067 181.371C93.7696 175.154 102.931 170.738 111.276 169.382C114.872 168.798 120.864 169.03 124.194 169.882C129.532 171.248 134.307 174.308 136.791 177.954C139.218 181.517 140.259 184.622 141.343 191.53C141.771 194.255 142.236 196.992 142.377 197.611C143.191 201.192 144.775 204.054 146.739 205.491C149.857 207.773 155.227 207.915 160.509 205.855C161.405 205.505 162.184 205.263 162.238 205.318C162.43 205.508 159.77 207.288 157.894 208.226C155.369 209.487 153.361 209.975 150.693 209.975C145.855 209.975 141.839 207.514 138.487 202.495C137.828 201.508 136.346 198.55 135.193 195.922C131.655 187.851 129.907 185.393 125.799 182.702C122.223 180.361 117.612 179.941 114.143 181.642C109.586 183.876 108.315 189.699 111.579 193.39C112.876 194.856 115.295 196.121 117.273 196.367C120.973 196.828 124.153 194.013 124.153 190.277C124.153 187.851 123.221 186.467 120.873 185.407C117.668 183.961 114.222 185.652 114.238 188.663C114.245 189.947 114.805 190.754 116.092 191.336C116.918 191.71 116.937 191.74 116.263 191.6C113.322 190.99 112.633 187.444 114.998 185.09C117.837 182.264 123.709 183.511 125.725 187.368C126.572 188.988 126.67 192.215 125.932 194.164C124.279 198.525 119.46 200.819 114.571 199.571C111.243 198.721 109.887 197.801 105.874 193.667C98.9012 186.484 96.1941 185.092 86.141 183.523L84.2146 183.222L86.4067 181.371Z" fill="#FF007A"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.8241 20.4645C57.1114 48.7285 93.0139 92.734 94.7963 95.1977C96.2679 97.232 95.7141 99.0609 93.1929 100.494C91.7909 101.291 88.9084 102.099 87.4652 102.099C85.8329 102.099 83.9939 101.313 82.6548 100.042C81.7084 99.1447 77.8882 93.4402 69.0694 79.7566C62.3216 69.2863 56.6747 60.6007 56.5206 60.4553C56.1644 60.119 56.1705 60.1303 68.3813 81.9787C76.0487 95.6979 78.6371 100.548 78.6371 101.197C78.6371 102.516 78.2771 103.21 76.6495 105.025C73.9363 108.052 72.7235 111.453 71.8479 118.492C70.8664 126.382 68.1064 131.955 60.4577 141.494C55.9803 147.078 55.2477 148.102 54.118 150.352C52.695 153.187 52.3037 154.774 52.1451 158.353C51.9775 162.137 52.3039 164.581 53.46 168.199C54.4721 171.366 55.5285 173.458 58.2292 177.641C60.5599 181.251 61.9019 183.933 61.9019 184.983C61.9019 185.818 62.0613 185.819 65.6729 185.003C74.316 183.052 81.3341 179.619 85.2812 175.412C87.724 172.808 88.2975 171.371 88.3161 167.802C88.3283 165.469 88.2462 164.98 87.6153 163.637C86.5884 161.452 84.7188 159.635 80.5983 156.818C75.1992 153.127 72.8932 150.156 72.2562 146.07C71.7337 142.717 72.3399 140.351 75.3267 134.091C78.4182 127.612 79.1843 124.85 79.7025 118.319C80.0372 114.1 80.5008 112.435 81.7131 111.1C82.9776 109.707 84.1158 109.235 87.245 108.808C92.3466 108.111 95.5951 106.791 98.2652 104.33C100.582 102.196 101.551 100.139 101.7 97.0427L101.813 94.6959L100.518 93.1867C95.8304 87.7209 30.6848 16.168 30.3963 16.168C30.3347 16.168 31.8773 18.1015 33.8241 20.4645ZM64.5333 162.634C65.5932 160.757 65.0301 158.344 63.2572 157.166C61.5821 156.052 58.9799 156.576 58.9799 158.028C58.9799 158.471 59.2248 158.793 59.7768 159.077C60.7063 159.555 60.7738 160.093 60.0425 161.192C59.3019 162.306 59.3616 163.284 60.2111 163.949C61.5802 165.021 63.5183 164.432 64.5333 162.634Z" fill="#FF007A"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M105.032 110.039C102.637 110.774 100.309 113.312 99.5884 115.974C99.1487 117.598 99.3982 120.446 100.057 121.325C101.121 122.746 102.149 123.12 104.935 123.101C110.389 123.063 115.131 120.724 115.682 117.799C116.134 115.402 114.051 112.08 111.183 110.622C109.703 109.87 106.555 109.571 105.032 110.039ZM111.408 115.024C112.249 113.829 111.881 112.537 110.451 111.664C107.726 110 103.607 111.377 103.607 113.95C103.607 115.231 105.755 116.629 107.724 116.629C109.035 116.629 110.829 115.847 111.408 115.024Z" fill="#FF007A"/>
</svg>

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@@ -2,6 +2,14 @@
<html translate="no">
<head>
<meta charset="utf-8" />
<title>Uniswap Interface</title>
<meta name="description" content="Uniswap Interface" />
<!--
%PUBLIC_URL% will be replaced with the URL of the `public` folder during build.
Only files inside the `public` folder can be referenced from the HTML.
-->
<link rel="shortcut icon" type="image/png" href="%PUBLIC_URL%/favicon.png" />
<link rel="apple-touch-icon" sizes="192x192" href="%PUBLIC_URL%/images/192x192_App_Icon.png" />
<link rel="apple-touch-icon" sizes="512x512" href="%PUBLIC_URL%/images/512x512_App_Icon.png" />
@@ -9,20 +17,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#ff007a" />
<meta name="fortmatic-site-verification" content="j93LgcVZk79qcgyo" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
manifest.json provides metadata used when the app is installed as a PWA.
See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<link rel="preconnect" href="https://www.google-analytics.com/">
<style>
* {
@@ -63,7 +65,6 @@
pointer-events: none;
width: 200vw;
height: 200vh;
mix-blend-mode: color;
background: radial-gradient(50% 50% at 50% 50%, #fc077d10 0%, rgba(255, 255, 255, 0) 100%);
transform: translate(-50vw, -100vh);
z-index: -1;
@@ -84,8 +85,6 @@
}
}
</style>
<title>Uniswap Interface</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@@ -2,6 +2,7 @@
"background_color": "#fff",
"display": "standalone",
"homepage_url": "https://app.uniswap.org",
"providedBy": { "name": "Uniswap", "url": "https://uniswap.org" },
"icons": [
{
"src": "./images/192x192_App_Icon.png",
@@ -18,6 +19,8 @@
],
"orientation": "portrait",
"name": "Uniswap",
"description": "Swap or provide liquidity on the Uniswap Protocol",
"iconPath": "./images/256x256_App_Icon_Pink.svg",
"short_name": "Uniswap",
"start_url": ".",
"theme_color": "#ff007a"

1046
src/abis/governor-bravo.json Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,181 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 470.287 514.251" enable-background="new 0 0 470.287 514.251" xml:space="preserve">
<g id="Background">
</g>
<g id="Logos_and_symbols">
<g id="SYMBOL_VER_3">
</g>
<g id="SYMBOL_VER_3_3_">
</g>
<g id="SYMBOL_VER_4">
</g>
<g id="SYMBOL_VER_4_1_">
<g id="SYMBOL_VER_4_3_">
</g>
</g>
<g id="SYMBOL_VER_5_1_">
</g>
<g id="off_2_1_">
</g>
<g id="VER_3_1_">
<g id="SYMBOL_VER_2_1_">
</g>
</g>
<g id="VER_3">
<g id="SYMBOL_VER_2">
</g>
</g>
<g id="off_2">
</g>
<g id="SYMBOL_VER_5">
</g>
<g id="SYMBOL_VER_1">
</g>
<g id="SYMBOL_VER_1_1_">
</g>
<g id="SYMBOL_VER_1-1_3_">
</g>
<g id="SYMBOL_VER_1-1_2_">
</g>
<g id="SYMBOL_VER_1-1">
</g>
<g id="SYMBOL_VER_1-1_1_">
<g id="_x31_-3">
</g>
<g id="Symbol_-_Original_14_">
<path fill="#2D374B" d="M291.134,237.469l35.654-60.5l96.103,149.684l0.046,28.727l-0.313-197.672
c-0.228-4.832-2.794-9.252-6.887-11.859L242.715,46.324c-4.045-1.99-9.18-1.967-13.22,0.063c-0.546,0.272-1.06,0.57-1.548,0.895
l-0.604,0.379L59.399,144.983l-0.651,0.296c-0.838,0.385-1.686,0.875-2.48,1.444c-3.185,2.283-5.299,5.66-5.983,9.448
c-0.103,0.574-0.179,1.158-0.214,1.749l0.264,161.083l89.515-138.745c11.271-18.397,35.825-24.323,58.62-24.001l26.753,0.706
L67.588,409.765l18.582,10.697L245.692,157.22l70.51-0.256L157.091,426.849l66.306,38.138l7.922,4.556
c3.351,1.362,7.302,1.431,10.681,0.21l175.453-101.678l-33.544,19.438L291.134,237.469z M304.736,433.395l-66.969-105.108
l40.881-69.371l87.952,138.628L304.736,433.395z"/>
<polygon fill="#28A0F0" points="237.768,328.286 304.736,433.395 366.601,397.543 278.648,258.915 "/>
<path fill="#28A0F0" d="M422.937,355.379l-0.046-28.727l-96.103-149.684l-35.654,60.5l92.774,150.043l33.544-19.438
c3.29-2.673,5.281-6.594,5.49-10.825L422.937,355.379z"/>
<path fill="#FFFFFF" d="M20.219,382.469l47.369,27.296l157.634-252.801l-26.753-0.706c-22.795-0.322-47.35,5.604-58.62,24.001
L50.334,319.004l-30.115,46.271V382.469z"/>
<polygon fill="#FFFFFF" points="316.202,156.964 245.692,157.22 86.17,420.462 141.928,452.565 157.091,426.849 "/>
<path fill="#96BEDC" d="M452.65,156.601c-0.59-14.746-8.574-28.245-21.08-36.104L256.28,19.692
c-12.371-6.229-27.825-6.237-40.218-0.004c-1.465,0.739-170.465,98.752-170.465,98.752c-2.339,1.122-4.592,2.458-6.711,3.975
c-11.164,8.001-17.969,20.435-18.668,34.095v208.765l30.115-46.271L50.07,157.921c0.035-0.589,0.109-1.169,0.214-1.741
c0.681-3.79,2.797-7.171,5.983-9.456c0.795-0.569,172.682-100.064,173.228-100.337c4.04-2.029,9.175-2.053,13.22-0.063
l173.022,99.523c4.093,2.607,6.659,7.027,6.887,11.859v199.542c-0.209,4.231-1.882,8.152-5.172,10.825l-33.544,19.438
l-17.308,10.031l-61.864,35.852l-62.737,36.357c-3.379,1.221-7.33,1.152-10.681-0.21l-74.228-42.693l-15.163,25.717
l66.706,38.406c2.206,1.255,4.171,2.367,5.784,3.272c2.497,1.4,4.199,2.337,4.8,2.629c4.741,2.303,11.563,3.643,17.71,3.643
c5.636,0,11.132-1.035,16.332-3.072l182.225-105.531c10.459-8.104,16.612-20.325,17.166-33.564V156.601z"/>
</g>
<g id="Symbol_-_Original_13_">
</g>
<g id="Symbol_-_Original_6_">
</g>
<g id="Symbol_-_Original_4_">
</g>
<g id="One_color_version_-_White_3_">
<g id="Symbol_-_Original_15_">
</g>
</g>
<g id="One_color_version_-_White">
<g id="Symbol_-_Original">
</g>
</g>
<g id="Symbol_-_Monochromatic_3_">
<g id="_x33__7_">
</g>
</g>
<g id="Symbol_-_Monochromatic">
<g id="_x33__3_">
</g>
</g>
<g id="_x33__2_">
</g>
<g id="_x33__1_">
</g>
<g id="_x33_">
</g>
<g id="Symbol_-_Original_10_">
</g>
<g id="Symbol_-_Original_1_">
</g>
<g id="Symbol_-_Original_2_">
</g>
<g id="_x34__1_">
</g>
<g id="Symbol_-_Monochromatic_2_">
<g id="_x33__6_">
</g>
</g>
<g id="One_color_version_-_White_2_">
<g id="Symbol_-_Original_11_">
</g>
</g>
<g id="Symbol_-_Original_5_">
<g id="Symbol_-_Original_12_">
</g>
</g>
<g id="One_color_version_-_White_1_">
<g id="Symbol_-_Original_9_">
</g>
</g>
</g>
<g id="SYMBOL_VER_1_2_">
<g id="SYMBOL_VER_2_4_">
</g>
<g id="SYMBOL_VER_2-1-1_1_">
</g>
<g id="SYMBOL_VER_2-2-1_1_">
</g>
<g id="SYMBOL_VER_2-3-1_4_">
</g>
<g id="New_Symbol_1_">
<g id="SYMBOL_VER_2-3-1_3_">
</g>
</g>
<g id="New_Symbol">
<g id="SYMBOL_VER_2-3-1_1_">
</g>
</g>
</g>
<g id="SYMBOL_VER_2_2_">
</g>
<g id="SYMBOL_VER_4_2_">
</g>
<g id="SYMBOL_VER_3_2_">
</g>
<g id="SYMBOL_VER_3_1_">
</g>
<g id="SYMBOL_VER_1-1-1_1_">
</g>
<g id="SYMBOL_VER_1-1-1">
</g>
<g id="SYMBOL_VER_1-1-1_2_2_">
</g>
<g id="SYMBOL_VER_1-1-1_2">
</g>
<g id="SYMBOL_VER_1-1-1_2_1_">
</g>
<g id="Symbol_-_Original_7_">
</g>
<g id="Symbol_-_Original_8_">
</g>
<g id="SYMBOL_VER_2-1-1">
</g>
<g id="SYMBOL_VER_2-2-1">
</g>
<g id="SYMBOL_VER_2-3-1">
</g>
<g id="SYMBOL_VER_5-1_1_">
</g>
<g id="SYMBOL_VER_5-1">
</g>
<g id="SYMBOL_VER_5-2_1_">
</g>
<g id="SYMBOL_VER_5-2">
</g>
<g id="Symbol_-_Monochromatic_1_">
<g id="_x33__4_">
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1,5 @@
<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="250" cy="250" r="250" fill="#FF0420"/>
<path d="M177.133 316.446C162.247 316.446 150.051 312.943 140.544 305.938C131.162 298.808 126.471 288.676 126.471 275.541C126.471 272.789 126.784 269.411 127.409 265.408C129.036 256.402 131.35 245.581 134.352 232.947C142.858 198.547 164.812 181.347 200.213 181.347C209.845 181.347 218.476 182.973 226.107 186.225C233.738 189.352 239.742 194.106 244.12 200.486C248.498 206.74 250.688 214.246 250.688 223.002C250.688 225.629 250.375 228.944 249.749 232.947C247.873 244.08 245.621 254.901 242.994 265.408C238.616 282.546 231.048 295.368 220.29 303.874C209.532 312.255 195.147 316.446 177.133 316.446ZM179.76 289.426C186.766 289.426 192.707 287.362 197.586 283.234C202.59 279.106 206.155 272.789 208.281 264.283C211.158 252.524 213.348 242.266 214.849 233.51C215.349 230.883 215.599 228.194 215.599 225.441C215.599 214.058 209.657 208.366 197.774 208.366C190.768 208.366 184.764 210.43 179.76 214.558C174.882 218.687 171.379 225.004 169.253 233.51C167.001 241.891 164.749 252.149 162.498 264.283C161.997 266.784 161.747 269.411 161.747 272.163C161.747 283.672 167.752 289.426 179.76 289.426Z" fill="white"/>
<path d="M259.303 314.57C257.927 314.57 256.863 314.132 256.113 313.256C255.487 312.255 255.3 311.13 255.55 309.879L281.444 187.914C281.694 186.538 282.382 185.412 283.508 184.536C284.634 183.661 285.822 183.223 287.073 183.223H336.985C350.87 183.223 362.003 186.1 370.384 191.854C378.891 197.609 383.144 205.927 383.144 216.81C383.144 219.937 382.769 223.19 382.018 226.567C378.891 240.953 372.574 251.586 363.067 258.466C353.685 265.346 340.8 268.786 324.413 268.786H299.082L290.451 309.879C290.2 311.255 289.512 312.38 288.387 313.256C287.261 314.132 286.072 314.57 284.822 314.57H259.303ZM325.727 242.892C330.98 242.892 335.546 241.453 339.424 238.576C343.427 235.699 346.054 231.571 347.305 226.192C347.68 224.065 347.868 222.189 347.868 220.563C347.868 216.935 346.805 214.183 344.678 212.307C342.551 210.305 338.924 209.305 333.795 209.305H311.278L304.148 242.892H325.727Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1,4 +1,3 @@
import React from 'react'
import styled from 'styled-components/macro'
import { CheckCircle, Triangle } from 'react-feather'

View File

@@ -1,5 +1,5 @@
import React, { useCallback, useContext } from 'react'
import styled, { ThemeContext } from 'styled-components'
import { useCallback, useContext } from 'react'
import styled, { ThemeContext } from 'styled-components/macro'
import { SUPPORTED_WALLETS } from '../../constants/wallet'
import { useActiveWeb3React } from '../../hooks/web3'
import { clearAllTransactions } from '../../state/transactions/actions'

View File

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

View File

@@ -1,5 +1,3 @@
import React from 'react'
import Badge, { BadgeVariant } from 'components/Badge'
import styled from 'styled-components/macro'
@@ -26,13 +24,6 @@ const ActiveDot = styled.span`
margin-right: 4px;
`
export const DarkBadge = styled.div`
width: fit-content;
border-radius: 8px;
background-color: ${({ theme }) => theme.bg0};
padding: 4px 6px;
`
export default function RangeBadge({
removed,
inRange,

View File

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

View File

@@ -1,4 +1,4 @@
import React, { ReactNode, useMemo } from 'react'
import { ReactNode, useMemo } from 'react'
import { useActiveWeb3React } from '../../hooks/web3'
import { Trans } from '@lingui/macro'
@@ -9,6 +9,7 @@ const BLOCKED_ADDRESSES: string[] = [
'0x901bb9583b24D97e995513C6778dc6888AB6870e',
'0xA7e5d5A720f06526557c513402f2e6B5fA20b008',
'0x8576aCC5C05D6Ce88f4e49bf65BdF0C62F91353C',
'0xC8a65Fadf0e0dDAf421F28FEAb69Bf6E2E589963',
]
export default function Blocklist({ children }: { children: ReactNode }) {

View File

@@ -1,4 +1,3 @@
import React from 'react'
import styled from 'styled-components/macro'
import { darken } from 'polished'
@@ -13,16 +12,15 @@ const Base = styled(RebassButton)<
{
padding?: string
width?: string
borderRadius?: string
$borderRadius?: string
altDisabledStyle?: boolean
} & ButtonProps
>`
padding: ${({ padding }) => (padding ? padding : '16px')};
width: ${({ width }) => (width ? width : '100%')};
padding: ${({ padding }) => padding ?? '16px'};
width: ${({ width }) => width ?? '100%'};
font-weight: 500;
text-align: center;
border-radius: 20px;
border-radius: ${({ borderRadius }) => borderRadius && borderRadius};
border-radius: ${({ $borderRadius }) => $borderRadius ?? '20px'};
outline: none;
border: 1px solid transparent;
color: white;
@@ -43,10 +41,6 @@ const Base = styled(RebassButton)<
transition: transform 450ms ease;
transform: perspective(1px) translateZ(0);
&:hover {
transform: scale(0.99);
}
> * {
user-select: none;
}
@@ -114,9 +108,7 @@ export const ButtonGray = styled(Base)`
color: ${({ theme }) => theme.text2};
font-size: 16px;
font-weight: 500;
&:focus {
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
}
&:hover {
background-color: ${({ theme, disabled }) => !disabled && darken(0.05, theme.bg2)};
}
@@ -153,63 +145,41 @@ export const ButtonSecondary = styled(Base)`
}
`
export const ButtonPink = styled(Base)`
background-color: ${({ theme }) => theme.primary1};
color: white;
export const ButtonOutlined = styled(Base)`
border: 1px solid ${({ theme }) => theme.bg2};
background-color: transparent;
color: ${({ theme }) => theme.text1};
&:focus {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.primary1)};
background-color: ${({ theme }) => darken(0.05, theme.primary1)};
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4};
}
&:hover {
background-color: ${({ theme }) => darken(0.05, theme.primary1)};
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4};
}
&:active {
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.primary1)};
background-color: ${({ theme }) => darken(0.1, theme.primary1)};
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4};
}
&:disabled {
background-color: ${({ theme }) => theme.primary1};
opacity: 50%;
cursor: auto;
}
`
export const ButtonUNIGradient = styled(ButtonPrimary)`
export const ButtonYellow = styled(Base)`
background-color: ${({ theme }) => theme.yellow3};
color: white;
padding: 4px 8px;
height: 36px;
font-weight: 500;
background-color: ${({ theme }) => theme.bg3};
background: radial-gradient(174.47% 188.91% at 1.84% 0%, #ff007a 0%, #2172e5 100%), #edeef2;
width: fit-content;
position: relative;
cursor: pointer;
border: none;
white-space: no-wrap;
:hover {
opacity: 0.8;
}
:active {
opacity: 0.9;
}
`
export const ButtonOutlined = styled(Base)`
border: 1px solid ${({ theme }) => theme.bg2};
background-color: transparent;
color: ${({ theme }) => theme.text1};
&:focus {
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4};
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.05, theme.yellow3)};
background-color: ${({ theme }) => darken(0.05, theme.yellow3)};
}
&:hover {
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4};
background-color: ${({ theme }) => darken(0.05, theme.yellow3)};
}
&:active {
box-shadow: 0 0 0 1px ${({ theme }) => theme.bg4};
box-shadow: 0 0 0 1pt ${({ theme }) => darken(0.1, theme.yellow3)};
background-color: ${({ theme }) => darken(0.1, theme.yellow3)};
}
&:disabled {
background-color: ${({ theme }) => theme.yellow3};
opacity: 50%;
cursor: auto;
}
@@ -259,27 +229,6 @@ export const ButtonText = styled(Base)`
}
`
export const ButtonWhite = styled(Base)`
border: 1px solid #edeef2;
background-color: ${({ theme }) => theme.bg1};
color: black;
&:focus {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
box-shadow: 0 0 0 1pt ${darken(0.05, '#edeef2')};
}
&:hover {
box-shadow: 0 0 0 1pt ${darken(0.1, '#edeef2')};
}
&:active {
box-shadow: 0 0 0 1pt ${darken(0.1, '#edeef2')};
}
&:disabled {
opacity: 50%;
cursor: auto;
}
`
const ButtonConfirmedStyle = styled(Base)`
background-color: ${({ theme }) => theme.bg3};
color: ${({ theme }) => theme.text1};
@@ -348,17 +297,6 @@ export function ButtonDropdown({ disabled = false, children, ...rest }: { disabl
)
}
export function ButtonDropdownGrey({ disabled = false, children, ...rest }: { disabled?: boolean } & ButtonProps) {
return (
<ButtonGray {...rest} disabled={disabled} style={{ borderRadius: '20px' }}>
<RowBetween>
<div style={{ display: 'flex', alignItems: 'center' }}>{children}</div>
<ChevronDown size={24} />
</RowBetween>
</ButtonGray>
)
}
export function ButtonDropdownLight({ disabled = false, children, ...rest }: { disabled?: boolean } & ButtonProps) {
return (
<ButtonOutlined {...rest} disabled={disabled}>
@@ -370,14 +308,6 @@ export function ButtonDropdownLight({ disabled = false, children, ...rest }: { d
)
}
export function ButtonRadio({ active, ...rest }: { active?: boolean } & ButtonProps) {
if (!active) {
return <ButtonWhite {...rest} />
} else {
return <ButtonPrimary {...rest} />
}
}
const ActiveOutlined = styled(ButtonOutlined)`
border: 1px solid;
border-color: ${({ theme }) => theme.primary1};
@@ -410,13 +340,13 @@ export function ButtonRadioChecked({ active = false, children, ...rest }: { acti
if (!active) {
return (
<ButtonOutlined borderRadius="12px" padding="12px 8px" {...rest}>
<ButtonOutlined $borderRadius="12px" padding="12px 8px" {...rest}>
{<RowBetween>{children}</RowBetween>}
</ButtonOutlined>
)
} else {
return (
<ActiveOutlined {...rest} padding="12px 8px" borderRadius="12px">
<ActiveOutlined {...rest} padding="12px 8px" $borderRadius="12px">
{
<RowBetween>
{children}

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
import React, { useMemo } from 'react'
import { useMemo } from 'react'
import useTheme from '../../hooks/useTheme'
import { TYPE } from '../../theme'
import { warningSeverity } from '../../utils/prices'

View File

@@ -1,6 +1,6 @@
import { Pair } from '@uniswap/v2-sdk'
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
import React, { useState, useCallback, ReactNode } from 'react'
import { useState, useCallback, ReactNode } from 'react'
import styled from 'styled-components/macro'
import { darken } from 'polished'
import { useCurrencyBalance } from '../../state/wallet/hooks'
@@ -53,7 +53,8 @@ const Container = styled.div<{ hideInput: boolean }>`
}
`
const CurrencySelect = styled(ButtonGray)<{ selected: boolean; 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;
@@ -160,6 +161,8 @@ interface CurrencyInputPanelProps {
priceImpact?: Percent
id: string
showCommonBases?: boolean
showCurrencyAmount?: boolean
disableNonToken?: boolean
renderBalance?: (amount: CurrencyAmount<Currency>) => ReactNode
locked?: boolean
}
@@ -174,6 +177,8 @@ export default function CurrencyInputPanel({
otherCurrency,
id,
showCommonBases,
showCurrencyAmount,
disableNonToken,
renderBalance,
fiatValue,
priceImpact,
@@ -207,6 +212,7 @@ export default function CurrencyInputPanel({
<Container hideInput={hideInput}>
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={!onCurrencySelect}>
<CurrencySelect
visible={currency !== null}
selected={!!currency}
hideInput={hideInput}
className="open-currency-select-button"
@@ -298,6 +304,8 @@ export default function CurrencyInputPanel({
selectedCurrency={currency}
otherSelectedCurrency={otherCurrency}
showCommonBases={showCommonBases}
showCurrencyAmount={showCurrencyAmount}
disableNonToken={disableNonToken}
/>
)}
</InputPanel>

View File

@@ -7,7 +7,7 @@ import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
import Logo from '../Logo'
export const getTokenLogoURL = (address: string) =>
`https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/${address}/logo.png`
`https://raw.githubusercontent.com/uniswap/assets/master/blockchains/ethereum/assets/${address}/logo.png`
const StyledEthereumLogo = styled.img<{ size: string }>`
width: ${({ size }) => size};
@@ -30,7 +30,7 @@ export default function CurrencyLogo({
style,
...rest
}: {
currency?: Currency
currency?: Currency | null
size?: string
style?: React.CSSProperties
}) {
@@ -50,7 +50,7 @@ export default function CurrencyLogo({
}, [currency, uriLocations])
if (currency?.isNative) {
return <StyledEthereumLogo src={EthereumLogo} size={size} style={style} {...rest} />
return <StyledEthereumLogo src={EthereumLogo} alt="ethereum logo" size={size} style={style} {...rest} />
}
return <StyledLogo size={size} srcs={srcs} alt={`${currency?.symbol ?? 'token'} logo`} style={style} {...rest} />

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,235 @@
import { Trans } from '@lingui/macro'
import { YellowCard } from 'components/Card'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import { useActiveWeb3React } from 'hooks/web3'
import { useEffect, useRef, useState } from 'react'
import { ArrowDownCircle, ChevronDown, ToggleLeft } from 'react-feather'
import { ApplicationModal } from 'state/application/actions'
import { useModalOpen, useToggleModal } from 'state/application/hooks'
import styled, { css } from 'styled-components/macro'
import { ExternalLink } from 'theme'
import { switchToNetwork } from 'utils/switchToNetwork'
import { CHAIN_INFO, L2_CHAIN_IDS, SupportedChainId, SupportedL2ChainId } from '../../constants/chains'
const BaseWrapper = css`
position: relative;
margin-right: 8px;
${({ theme }) => theme.mediaWidth.upToMedium`
justify-self: end;
`};
${({ theme }) => theme.mediaWidth.upToSmall`
margin: 0 0.5rem 0 0;
width: initial;
text-overflow: ellipsis;
flex-shrink: 1;
`};
`
const L2Wrapper = styled.div`
${BaseWrapper}
`
const BaseMenuItem = css`
align-items: center;
background-color: transparent;
border-radius: 12px;
color: ${({ theme }) => theme.text2};
cursor: pointer;
display: flex;
flex: 1;
flex-direction: row;
font-size: 16px;
font-weight: 400;
justify-content: space-between;
:hover {
color: ${({ theme }) => theme.text1};
text-decoration: none;
}
`
const DisabledMenuItem = styled.div`
${BaseMenuItem}
align-items: center;
background-color: ${({ theme }) => theme.bg2};
cursor: auto;
display: flex;
font-size: 10px;
font-style: italic;
justify-content: center;
:hover,
:active,
:focus {
color: ${({ theme }) => theme.text2};
}
`
const FallbackWrapper = styled(YellowCard)`
${BaseWrapper}
width: auto;
border-radius: 12px;
padding: 8px 12px;
width: 100%;
user-select: none;
`
const Icon = styled.img`
width: 16px;
margin-right: 2px;
${({ theme }) => theme.mediaWidth.upToSmall`
margin-right: 4px;
`};
`
const MenuFlyout = styled.span`
background-color: ${({ theme }) => theme.bg1};
border: 1px solid ${({ theme }) => theme.bg0};
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
0px 24px 32px rgba(0, 0, 0, 0.01);
border-radius: 12px;
padding: 1rem;
display: flex;
flex-direction: column;
font-size: 1rem;
position: absolute;
left: 0rem;
top: 3rem;
z-index: 100;
width: 237px;
${({ theme }) => theme.mediaWidth.upToMedium`
bottom: unset;
top: 4.5em
right: 0;
`};
> {
padding: 12px;
}
> :not(:first-child) {
margin-top: 8px;
}
> :not(:last-child) {
margin-bottom: 8px;
}
`
const LinkOutCircle = styled(ArrowDownCircle)`
transform: rotate(230deg);
width: 16px;
height: 16px;
opacity: 0.6;
`
const MenuItem = styled(ExternalLink)`
${BaseMenuItem}
`
const ButtonMenuItem = styled.button`
${BaseMenuItem}
border: none;
box-shadow: none;
color: ${({ theme }) => theme.text2};
outline: none;
padding: 0;
`
const NetworkInfo = styled.button<{ chainId: SupportedChainId }>`
align-items: center;
background-color: ${({ theme }) => theme.bg0};
border-radius: 12px;
border: 1px solid ${({ theme }) => theme.bg0};
color: ${({ theme }) => theme.text1};
display: flex;
flex-direction: row;
font-weight: 500;
font-size: 12px;
height: 100%;
margin: 0;
height: 38px;
padding: 0.7rem;
:hover,
:focus {
cursor: pointer;
outline: none;
border: 1px solid ${({ theme }) => theme.bg3};
}
`
const NetworkName = styled.div<{ chainId: SupportedChainId }>`
border-radius: 6px;
font-size: 16px;
font-weight: 500;
padding: 0 2px 0.5px 4px;
margin: 0 2px;
white-space: pre;
${({ theme }) => theme.mediaWidth.upToSmall`
display: none;
`};
`
export default function NetworkCard() {
const { chainId, library } = useActiveWeb3React()
const node = useRef<HTMLDivElement>(null)
const open = useModalOpen(ApplicationModal.ARBITRUM_OPTIONS)
const toggle = useToggleModal(ApplicationModal.ARBITRUM_OPTIONS)
useOnClickOutside(node, open ? toggle : undefined)
const [implements3085, setImplements3085] = useState(false)
useEffect(() => {
// metamask is currently the only known implementer of this EIP
// here we proceed w/ a noop feature check to ensure the user's version of metamask supports network switching
// if not, we hide the UI
if (!library?.provider?.request || !chainId || !library?.provider?.isMetaMask) {
return
}
switchToNetwork({ library, chainId })
.then((x) => x ?? setImplements3085(true))
.catch(() => setImplements3085(false))
}, [library, chainId])
const info = chainId ? CHAIN_INFO[chainId] : undefined
if (!chainId || chainId === SupportedChainId.MAINNET || !info || !library) {
return null
}
if (L2_CHAIN_IDS.includes(chainId)) {
const info = CHAIN_INFO[chainId as SupportedL2ChainId]
const isArbitrum = [SupportedChainId.ARBITRUM_ONE, SupportedChainId.ARBITRUM_RINKEBY].includes(chainId)
return (
<L2Wrapper ref={node}>
<NetworkInfo onClick={toggle} chainId={chainId}>
<Icon src={info.logoUrl} />
<NetworkName chainId={chainId}>{info.label}</NetworkName>
<ChevronDown size={16} style={{ marginTop: '2px' }} strokeWidth={2.5} />
</NetworkInfo>
{open && (
<MenuFlyout>
<MenuItem href={info.bridge}>
<div>{isArbitrum ? <Trans>{info.label} Bridge</Trans> : <Trans>Optimistic L2 Gateway</Trans>}</div>
<LinkOutCircle />
</MenuItem>
<MenuItem href={info.explorer}>
{isArbitrum ? <Trans>{info.label} Explorer</Trans> : <Trans>Optimistic Etherscan</Trans>}
<LinkOutCircle />
</MenuItem>
<MenuItem href={info.docs}>
<div>
<Trans>Learn more</Trans>
</div>
<LinkOutCircle />
</MenuItem>
{implements3085 ? (
<ButtonMenuItem onClick={() => switchToNetwork({ library, chainId: SupportedChainId.MAINNET })}>
<div>
<Trans>Switch to L1 (Mainnet)</Trans>
</div>
<ToggleLeft opacity={0.6} size={16} />
</ButtonMenuItem>
) : (
<DisabledMenuItem>
<Trans>Change your network to go back to L1</Trans>
</DisabledMenuItem>
)}
</MenuFlyout>
)}
</L2Wrapper>
)
}
return <FallbackWrapper title={info.label}>{info.label}</FallbackWrapper>
}

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react'
import styled, { keyframes } from 'styled-components'
import { useEffect, useState } from 'react'
import styled, { keyframes } from 'styled-components/macro'
import { useActiveWeb3React } from '../../hooks/web3'
import { useBlockNumber } from '../../state/application/hooks'
@@ -31,7 +31,6 @@ const StyledPollingDot = styled.div`
height: 8px;
min-height: 8px;
min-width: 8px;
margin-left: 0.5rem;
border-radius: 50%;
position: relative;
background-color: ${({ theme }) => theme.green1};
@@ -96,7 +95,7 @@ export default function Polling() {
>
<StyledPolling onMouseEnter={() => setIsHover(true)} onMouseLeave={() => setIsHover(false)}>
<StyledPollingNumber breathe={isMounting} hovering={isHover}>
{blockNumber}
{blockNumber}&ensp;
</StyledPollingNumber>
<StyledPollingDot>{isMounting && <Spinner />}</StyledPollingDot>
</StyledPolling>

View File

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

View File

@@ -1,22 +1,23 @@
import { Trans } from '@lingui/macro'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import React, { useMemo } from 'react'
import { CHAIN_INFO, SupportedChainId } from 'constants/chains'
import { useMemo } from 'react'
import { X } from 'react-feather'
import styled from 'styled-components/macro'
import tokenLogo from '../../assets/images/token-logo.png'
import { UNI } from '../../constants/tokens'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useActiveWeb3React } from '../../hooks/web3'
import { useMerkleDistributorContract } from '../../hooks/useContract'
import useCurrentBlockTimestamp from '../../hooks/useCurrentBlockTimestamp'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import useUSDCPrice from '../../hooks/useUSDCPrice'
import { useActiveWeb3React } from '../../hooks/web3'
import { useTotalUniEarned } from '../../state/stake/hooks'
import { useAggregateUniBalance, useTokenBalance } from '../../state/wallet/hooks'
import { ExternalLink, StyledInternalLink, TYPE, UniTokenAnimated } from '../../theme'
import { computeUniCirculation } from '../../utils/computeUniCirculation'
import useUSDCPrice from '../../hooks/useUSDCPrice'
import { AutoColumn } from '../Column'
import { RowBetween } from '../Row'
import { Break, CardBGImage, CardNoise, CardSection, DataCard } from '../earn/styled'
import { Trans } from '@lingui/macro'
import { RowBetween } from '../Row'
const ContentWrapper = styled(AutoColumn)`
width: 100%;
@@ -59,6 +60,8 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
[blockTimestamp, chainId, totalSupply, unclaimedUni, uni]
)
const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET]
return (
<ContentWrapper gap="lg">
<ModalUpper>
@@ -128,7 +131,7 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
<TYPE.white color="white">{totalSupply?.toFixed(0, { groupSeparator: ',' })}</TYPE.white>
</RowBetween>
{uni && uni.chainId === 1 ? (
<ExternalLink href={`https://info.uniswap.org/token/${uni.address}`}>
<ExternalLink href={`${infoLink}/token/${uni.address}`}>
<Trans>View UNI Analytics</Trans>
</ExternalLink>
) : null}

View File

@@ -1,8 +1,8 @@
import { Trans } from '@lingui/macro'
import useScrollPosition from '@react-hook/window-scroll'
import { CHAIN_INFO, SupportedChainId } from 'constants/chains'
import { darken } from 'polished'
import React, { useState } from 'react'
import { Moon, Sun } from 'react-feather'
import { useState } from 'react'
import { NavLink } from 'react-router-dom'
import { Text } from 'rebass'
import { useShowClaimPopup, useToggleSelfClaimModal } from 'state/application/hooks'
@@ -13,17 +13,16 @@ import { useETHBalances } from 'state/wallet/hooks'
import styled from 'styled-components/macro'
import Logo from '../../assets/svg/logo.svg'
import LogoDark from '../../assets/svg/logo_white.svg'
import { NETWORK_LABELS, SupportedChainId } from '../../constants/chains'
import { useActiveWeb3React } from '../../hooks/web3'
import { ExternalLink, TYPE } from '../../theme'
import { YellowCard } from '../Card'
import ClaimModal from '../claim/ClaimModal'
import { CardNoise } from '../earn/styled'
import Menu from '../Menu'
import Modal from '../Modal'
import Row, { RowFixed } from '../Row'
import Row from '../Row'
import { Dots } from '../swap/styleds'
import Web3Status from '../Web3Status'
import NetworkCard from './NetworkCard'
import UniBalanceContent from './UniBalanceContent'
const HeaderFrame = styled.div<{ showBackground: boolean }>`
@@ -39,22 +38,27 @@ const HeaderFrame = styled.div<{ showBackground: boolean }>`
padding: 1rem;
z-index: 21;
position: relative;
/* Background slide effect on scroll. */
background-image: ${({ theme }) => `linear-gradient(to bottom, transparent 50%, ${theme.bg0} 50% )}}`}
background-image: ${({ theme }) => `linear-gradient(to bottom, transparent 50%, ${theme.bg0} 50% )}}`};
background-position: ${({ showBackground }) => (showBackground ? '0 -100%' : '0 0')};
background-size: 100% 200%;
box-shadow: 0px 0px 0px 1px ${({ theme, showBackground }) => (showBackground ? theme.bg2 : 'transparent;')};
transition: background-position .1s, box-shadow .1s;
transition: background-position 0.1s, box-shadow 0.1s;
background-blend-mode: hard-light;
${({ theme }) => theme.mediaWidth.upToLarge`
grid-template-columns: 48px 1fr 1fr;
`};
${({ theme }) => theme.mediaWidth.upToMedium`
padding: 1rem;
grid-template-columns: auto 1fr;
grid-template-columns: 1fr 1fr;
`};
${({ theme }) => theme.mediaWidth.upToExtraSmall`
padding: 1rem;
`}
${({ theme }) => theme.mediaWidth.upToSmall`
padding: 1rem;
grid-template-columns: 36px 1fr;
`};
`
const HeaderControls = styled.div`
@@ -62,23 +66,6 @@ const HeaderControls = styled.div`
flex-direction: row;
align-items: center;
justify-self: flex-end;
${({ theme }) => theme.mediaWidth.upToMedium`
flex-direction: row;
justify-content: space-between;
justify-self: center;
width: 100%;
max-width: 960px;
padding: 1rem;
position: fixed;
bottom: 0px;
left: 0px;
width: 100%;
z-index: 99;
height: 72px;
border-radius: 12px 12px 0 0;
background-color: ${({ theme }) => theme.bg1};
`};
`
const HeaderElement = styled.div`
@@ -91,22 +78,10 @@ const HeaderElement = styled.div`
}
${({ theme }) => theme.mediaWidth.upToMedium`
flex-direction: row-reverse;
align-items: center;
`};
`
const HeaderElementWrap = styled.div`
display: flex;
align-items: center;
`
const HeaderRow = styled(RowFixed)`
${({ theme }) => theme.mediaWidth.upToMedium`
width: 100%;
`};
`
const HeaderLinks = styled(Row)`
justify-self: center;
background-color: ${({ theme }) => theme.bg0};
@@ -117,8 +92,25 @@ const HeaderLinks = styled(Row)`
grid-auto-flow: column;
grid-gap: 10px;
overflow: auto;
align-items: center;
${({ theme }) => theme.mediaWidth.upToLarge`
justify-self: start;
`};
${({ theme }) => theme.mediaWidth.upToMedium`
justify-self: flex-end;
justify-self: center;
`};
${({ theme }) => theme.mediaWidth.upToMedium`
flex-direction: row;
justify-content: space-between;
justify-self: center;
z-index: 99;
position: fixed;
bottom: 0; right: 50%;
transform: translate(50%,-50%);
margin: 0 auto;
background-color: ${({ theme }) => theme.bg0};
border: 1px solid ${({ theme }) => theme.bg2};
box-shadow: 0px 6px 10px rgb(0 0 0 / 2%);
`};
`
@@ -126,11 +118,10 @@ const AccountElement = styled.div<{ active: boolean }>`
display: flex;
flex-direction: row;
align-items: center;
background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg2)};
background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg1)};
border-radius: 12px;
white-space: nowrap;
width: 100%;
cursor: pointer;
:focus {
border: 1px solid blue;
@@ -160,25 +151,6 @@ const UNIWrapper = styled.span`
}
`
const HideSmall = styled.span`
${({ theme }) => theme.mediaWidth.upToSmall`
display: none;
`};
`
const NetworkCard = styled(YellowCard)`
border-radius: 12px;
padding: 8px 12px;
${({ theme }) => theme.mediaWidth.upToSmall`
margin: 0;
margin-right: 0.5rem;
width: initial;
overflow: hidden;
text-overflow: ellipsis;
flex-shrink: 1;
`};
`
const BalanceText = styled(Text)`
${({ theme }) => theme.mediaWidth.upToExtraSmall`
display: none;
@@ -219,14 +191,15 @@ const StyledNavLink = styled(NavLink).attrs({
text-decoration: none;
color: ${({ theme }) => theme.text2};
font-size: 1rem;
width: fit-content;
font-weight: 500;
padding: 8px 12px;
word-break: break-word;
overflow: hidden;
white-space: nowrap;
&.${activeClassName} {
border-radius: 12px;
font-weight: 600;
justify-content: center;
color: ${({ theme }) => theme.text1};
background-color: ${({ theme }) => theme.bg2};
}
@@ -263,47 +236,13 @@ const StyledExternalLink = styled(ExternalLink).attrs({
color: ${({ theme }) => darken(0.1, theme.text1)};
text-decoration: none;
}
${({ theme }) => theme.mediaWidth.upToExtraSmall`
display: none;
`}
`
export const StyledMenuButton = styled.button`
position: relative;
width: 100%;
height: 100%;
border: none;
background-color: transparent;
margin: 0;
padding: 0;
height: 35px;
background-color: ${({ theme }) => theme.bg2};
margin-left: 8px;
padding: 0.15rem 0.5rem;
border-radius: 0.5rem;
:hover,
:focus {
cursor: pointer;
outline: none;
background-color: ${({ theme }) => theme.bg4};
}
svg {
margin-top: 2px;
}
> * {
stroke: ${({ theme }) => theme.text1};
}
`
export default function Header() {
const { account, chainId } = useActiveWeb3React()
const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? '']
// const [isDark] = useDarkModeManager()
const [darkMode, toggleDarkMode] = useDarkModeManager()
const [darkMode] = useDarkModeManager()
const toggleClaimModal = useToggleSelfClaimModal()
@@ -316,19 +255,18 @@ export default function Header() {
const scrollY = useScrollPosition()
const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET]
return (
<HeaderFrame showBackground={scrollY > 45}>
<ClaimModal />
<Modal isOpen={showUniBalanceModal} onDismiss={() => setShowUniBalanceModal(false)}>
<UniBalanceContent setShowUniBalanceModal={setShowUniBalanceModal} />
</Modal>
<HeaderRow>
<Title href=".">
<UniIcon>
<img width={'24px'} src={darkMode ? LogoDark : Logo} alt="logo" />
</UniIcon>
</Title>
</HeaderRow>
<Title href=".">
<UniIcon>
<img width={'24px'} src={darkMode ? LogoDark : Logo} alt="logo" />
</UniIcon>
</Title>
<HeaderLinks>
<StyledNavLink id={`swap-nav-link`} to={'/swap'}>
<Trans>Swap</Trans>
@@ -346,21 +284,20 @@ export default function Header() {
>
<Trans>Pool</Trans>
</StyledNavLink>
<StyledNavLink id={`stake-nav-link`} to={'/vote'}>
<Trans>Vote</Trans>
</StyledNavLink>
<StyledExternalLink id={`stake-nav-link`} href={'https://info.uniswap.org'}>
{chainId && chainId === SupportedChainId.MAINNET && (
<StyledNavLink id={`vote-nav-link`} to={'/vote'}>
<Trans>Vote</Trans>
</StyledNavLink>
)}
<StyledExternalLink id={`charts-nav-link`} href={infoLink}>
<Trans>Charts</Trans>
<sup></sup>
</StyledExternalLink>
</HeaderLinks>
<HeaderControls>
<NetworkCard />
<HeaderElement>
<HideSmall>
{chainId && chainId !== SupportedChainId.MAINNET && NETWORK_LABELS[chainId] && (
<NetworkCard title={NETWORK_LABELS[chainId]}>{NETWORK_LABELS[chainId]}</NetworkCard>
)}
</HideSmall>
{availableClaim && !showClaimPopup && (
<UNIWrapper onClick={toggleClaimModal}>
<UNIAmount active={!!account && !availableClaim} style={{ pointerEvents: 'auto' }}>
@@ -377,21 +314,16 @@ export default function Header() {
<CardNoise />
</UNIWrapper>
)}
<AccountElement active={!!account} style={{ pointerEvents: 'auto' }}>
<AccountElement active={!!account}>
{account && userEthBalance ? (
<BalanceText style={{ flexShrink: 0 }} pl="0.75rem" pr="0.5rem" fontWeight={500}>
<Trans>{userEthBalance?.toSignificant(4)} ETH</Trans>
<BalanceText style={{ flexShrink: 0, userSelect: 'none' }} pl="0.75rem" pr="0.5rem" fontWeight={500}>
<Trans>{userEthBalance?.toSignificant(3)} ETH</Trans>
</BalanceText>
) : null}
<Web3Status />
</AccountElement>
</HeaderElement>
<HeaderElementWrap>
<StyledMenuButton onClick={() => toggleDarkMode()}>
{darkMode ? <Moon size={20} /> : <Sun size={20} />}
</StyledMenuButton>
<Menu />
</HeaderElementWrap>
</HeaderElement>
</HeaderControls>
</HeaderFrame>
)

View File

@@ -1,5 +1,5 @@
import Tooltip from 'components/Tooltip'
import React, { useState } from 'react'
import { useState } from 'react'
import styled from 'styled-components/macro'
const TextWrapper = styled.span<{ margin: boolean; link?: boolean; fontSize?: string; adjustSize?: boolean }>`

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useRef } from 'react'
import { useEffect, useRef } from 'react'
import styled from 'styled-components/macro'

View File

@@ -1,14 +1,13 @@
import React, { useState, useCallback, useEffect, ReactNode } from 'react'
import { LightCard } from 'components/Card'
import { RowBetween } from 'components/Row'
import { useState, useCallback, useEffect, ReactNode } from 'react'
import { OutlineCard } from 'components/Card'
import { Input as NumericalInput } from '../NumericalInput'
import styled, { keyframes } from 'styled-components'
import styled, { keyframes } from 'styled-components/macro'
import { TYPE } from 'theme'
import { AutoColumn } from 'components/Column'
import { ButtonPrimary } from 'components/Button'
import { ButtonGray } from 'components/Button'
import { FeeAmount } from '@uniswap/v3-sdk'
import { formattedFeeAmount } from 'utils'
import { Trans } from '@lingui/macro'
import { Plus, Minus } from 'react-feather'
const pulse = (color: string) => keyframes`
0% {
@@ -24,25 +23,37 @@ const pulse = (color: string) => keyframes`
}
`
const SmallButton = styled(ButtonPrimary)`
/* background-color: ${({ theme }) => theme.bg2}; */
border-radius: 8px;
padding: 4px 6px;
width: 48%;
const InputRow = styled.div`
display: grid;
grid-template-columns: 30px 1fr 30px;
`
const FocusedOutlineCard = styled(LightCard)<{ active?: boolean; pulsing?: boolean }>`
const SmallButton = styled(ButtonGray)`
border-radius: 8px;
padding: 4px;
`
const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boolean }>`
border-color: ${({ active, theme }) => active && theme.blue1};
padding: 12px;
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.blue1)} 0.8s linear;
`
const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
/* background-color: ${({ theme }) => theme.bg0}; */
background-color: transparent;
text-align: center;
margin-right: 12px;
width: 100%;
font-weight: 500;
padding: 0 10px;
${({ theme }) => theme.mediaWidth.upToSmall`
font-size: 16px;
`};
${({ theme }) => theme.mediaWidth.upToExtraSmall`
font-size: 12px;
`};
`
const InputTitle = styled(TYPE.small)`
@@ -51,11 +62,17 @@ const InputTitle = styled(TYPE.small)`
font-weight: 500;
`
const ButtonLabel = styled(TYPE.white)<{ disabled: boolean }>`
color: ${({ theme, disabled }) => (disabled ? theme.text2 : theme.text1)} !important;
`
interface StepCounterProps {
value: string
onUserInput: (value: string) => void
decrement: () => string
increment: () => string
decrementDisabled?: boolean
incrementDisabled?: boolean
feeAmount?: FeeAmount
label?: string
width?: string
@@ -69,7 +86,8 @@ const StepCounter = ({
value,
decrement,
increment,
feeAmount,
decrementDisabled = false,
incrementDisabled = false,
width,
locked,
onUserInput,
@@ -87,9 +105,6 @@ const StepCounter = ({
// animation if parent value updates local value
const [pulsing, setPulsing] = useState<boolean>(false)
// format fee amount
const feeAmountFormatted = feeAmount ? formattedFeeAmount(feeAmount * 2) : ''
const handleOnFocus = () => {
setUseLocalValue(true)
setActive(true)
@@ -126,39 +141,45 @@ const StepCounter = ({
return (
<FocusedOutlineCard pulsing={pulsing} active={active} onFocus={handleOnFocus} onBlur={handleOnBlur} width={width}>
<AutoColumn gap="6px" style={{ marginBottom: '12px' }}>
<AutoColumn gap="6px">
<InputTitle fontSize={12} textAlign="center">
{title}
</InputTitle>
<StyledInput
className="rate-input-0"
value={localValue}
fontSize="20px"
disabled={locked}
onUserInput={(val) => {
setLocalValue(val)
}}
/>
<InputRow>
{!locked && (
<SmallButton onClick={handleDecrement} disabled={decrementDisabled}>
<ButtonLabel disabled={decrementDisabled} fontSize="12px">
<Minus size={18} />
</ButtonLabel>
</SmallButton>
)}
<StyledInput
className="rate-input-0"
value={localValue}
fontSize="20px"
disabled={locked}
onUserInput={(val) => {
setLocalValue(val)
}}
/>
{!locked && (
<SmallButton onClick={handleIncrement} disabled={incrementDisabled}>
<ButtonLabel disabled={incrementDisabled} fontSize="12px">
<Plus size={18} />
</ButtonLabel>
</SmallButton>
)}
</InputRow>
<InputTitle fontSize={12} textAlign="center">
<Trans>
{tokenB} per {tokenA}
</Trans>
</InputTitle>
</AutoColumn>
{!locked ? (
<RowBetween>
<SmallButton onClick={handleDecrement}>
<TYPE.white fontSize="12px">
<Trans>-{feeAmountFormatted}%</Trans>
</TYPE.white>
</SmallButton>
<SmallButton onClick={handleIncrement}>
<TYPE.white fontSize="12px">
<Trans>+{feeAmountFormatted}%</Trans>
</TYPE.white>
</SmallButton>
</RowBetween>
) : null}
</FocusedOutlineCard>
)
}

View File

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

View File

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

View File

@@ -0,0 +1,44 @@
import React, { useMemo } from 'react'
import { area, curveStepAfter, ScaleLinear } from 'd3'
import styled from 'styled-components/macro'
import { ChartEntry } from './types'
import inRange from 'lodash/inRange'
const Path = styled.path<{ fill: string | undefined }>`
opacity: 0.5;
stroke: ${({ fill, theme }) => fill ?? theme.blue2};
fill: ${({ fill, theme }) => fill ?? theme.blue2};
`
export const Area = ({
series,
xScale,
yScale,
xValue,
yValue,
fill,
}: {
series: ChartEntry[]
xScale: ScaleLinear<number, number>
yScale: ScaleLinear<number, number>
xValue: (d: ChartEntry) => number
yValue: (d: ChartEntry) => number
fill?: string | undefined
}) =>
useMemo(
() => (
<Path
fill={fill}
d={
area()
.curve(curveStepAfter)
.x((d: unknown) => xScale(xValue(d as ChartEntry)))
.y1((d: unknown) => yScale(yValue(d as ChartEntry)))
.y0(yScale(0))(
series.filter((d) => inRange(xScale(xValue(d)), 0, innerWidth)) as Iterable<[number, number]>
) ?? undefined
}
/>
),
[fill, series, xScale, xValue, yScale, yValue]
)

View File

@@ -0,0 +1,43 @@
import React, { useMemo } from 'react'
import { Axis as d3Axis, axisBottom, NumberValue, ScaleLinear, select } from 'd3'
import styled from 'styled-components/macro'
const StyledGroup = styled.g`
line {
display: none;
}
text {
color: ${({ theme }) => theme.text2};
transform: translateY(5px);
}
`
const Axis = ({ axisGenerator }: { axisGenerator: d3Axis<NumberValue> }) => {
const axisRef = (axis: SVGGElement) => {
axis &&
select(axis)
.call(axisGenerator)
.call((g) => g.select('.domain').remove())
}
return <g ref={axisRef} />
}
export const AxisBottom = ({
xScale,
innerHeight,
offset = 0,
}: {
xScale: ScaleLinear<number, number>
innerHeight: number
offset?: number
}) =>
useMemo(
() => (
<StyledGroup transform={`translate(0, ${innerHeight + offset})`}>
<Axis axisGenerator={axisBottom(xScale).ticks(6)} />
</StyledGroup>
),
[innerHeight, offset, xScale]
)

View File

@@ -0,0 +1,265 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { BrushBehavior, brushX, D3BrushEvent, ScaleLinear, select } from 'd3'
import styled from 'styled-components/macro'
import { brushHandleAccentPath, brushHandlePath, OffScreenHandle } from 'components/LiquidityChartRangeInput/svg'
import usePrevious from 'hooks/usePrevious'
const Handle = styled.path<{ color: string }>`
cursor: ew-resize;
pointer-events: none;
stroke-width: 3;
stroke: ${({ color }) => color};
fill: ${({ color }) => color};
`
const HandleAccent = styled.path`
cursor: ew-resize;
pointer-events: none;
stroke-width: 1.5;
stroke: ${({ theme }) => theme.white};
opacity: 0.6;
`
const LabelGroup = styled.g<{ visible: boolean }>`
opacity: ${({ visible }) => (visible ? '1' : '0')};
transition: opacity 300ms;
`
const TooltipBackground = styled.rect`
fill: ${({ theme }) => theme.bg2};
`
const Tooltip = styled.text`
text-anchor: middle;
font-size: 13px;
fill: ${({ theme }) => theme.text1};
`
// flips the handles draggers when close to the container edges
const FLIP_HANDLE_THRESHOLD_PX = 20
// margin to prevent tick snapping from putting the brush off screen
const BRUSH_EXTENT_MARGIN_PX = 2
const compare = (a1: [number, number], a2: [number, number], xScale: ScaleLinear<number, number>): boolean =>
xScale(a1[0]) !== xScale(a2[0]) || xScale(a1[1]) !== xScale(a2[1])
export const Brush = ({
id,
xScale,
interactive,
brushLabelValue,
brushExtent,
setBrushExtent,
innerWidth,
innerHeight,
westHandleColor,
eastHandleColor,
}: {
id: string
xScale: ScaleLinear<number, number>
interactive: boolean
brushLabelValue: (d: 'w' | 'e', x: number) => string
brushExtent: [number, number]
setBrushExtent: (extent: [number, number], mode: string | undefined) => void
innerWidth: number
innerHeight: number
westHandleColor: string
eastHandleColor: string
}) => {
const brushRef = useRef<SVGGElement | null>(null)
const brushBehavior = useRef<BrushBehavior<SVGGElement> | null>(null)
// only used to drag the handles on brush for performance
const [localBrushExtent, setLocalBrushExtent] = useState<[number, number] | null>(brushExtent)
const [showLabels, setShowLabels] = useState(false)
const [hovering, setHovering] = useState(false)
const previousBrushExtent = usePrevious(brushExtent)
const brushed = useCallback(
(event: D3BrushEvent<unknown>) => {
const { type, selection, mode } = event
if (!selection) {
setLocalBrushExtent(null)
return
}
const scaled = (selection as [number, number]).map(xScale.invert) as [number, number]
// avoid infinite render loop by checking for change
if (type === 'end' && compare(brushExtent, scaled, xScale)) {
setBrushExtent(scaled, mode)
}
setLocalBrushExtent(scaled)
},
[xScale, brushExtent, setBrushExtent]
)
// keep local and external brush extent in sync
// i.e. snap to ticks on bruhs end
useEffect(() => {
setLocalBrushExtent(brushExtent)
}, [brushExtent])
// initialize the brush
useEffect(() => {
if (!brushRef.current) return
brushBehavior.current = brushX<SVGGElement>()
.extent([
[Math.max(0 + BRUSH_EXTENT_MARGIN_PX, xScale(0)), 0],
[innerWidth - BRUSH_EXTENT_MARGIN_PX, innerHeight],
])
.handleSize(30)
.filter(() => interactive)
.on('brush end', brushed)
brushBehavior.current(select(brushRef.current))
if (previousBrushExtent && compare(brushExtent, previousBrushExtent, xScale)) {
select(brushRef.current)
.transition()
.call(brushBehavior.current.move as any, brushExtent.map(xScale))
}
// brush linear gradient
select(brushRef.current)
.selectAll('.selection')
.attr('stroke', 'none')
.attr('fill-opacity', '0.1')
.attr('fill', `url(#${id}-gradient-selection)`)
}, [brushExtent, brushed, id, innerHeight, innerWidth, interactive, previousBrushExtent, xScale])
// respond to xScale changes only
useEffect(() => {
if (!brushRef.current || !brushBehavior.current) return
brushBehavior.current.move(select(brushRef.current) as any, brushExtent.map(xScale) as any)
}, [brushExtent, xScale])
// show labels when local brush changes
useEffect(() => {
setShowLabels(true)
const timeout = setTimeout(() => setShowLabels(false), 1500)
return () => clearTimeout(timeout)
}, [localBrushExtent])
// variables to help render the SVGs
const flipWestHandle = localBrushExtent && xScale(localBrushExtent[0]) > FLIP_HANDLE_THRESHOLD_PX
const flipEastHandle = localBrushExtent && xScale(localBrushExtent[1]) > innerWidth - FLIP_HANDLE_THRESHOLD_PX
const showWestArrow = localBrushExtent && (xScale(localBrushExtent[0]) < 0 || xScale(localBrushExtent[1]) < 0)
const showEastArrow =
localBrushExtent && (xScale(localBrushExtent[0]) > innerWidth || xScale(localBrushExtent[1]) > innerWidth)
const westHandleInView =
localBrushExtent && xScale(localBrushExtent[0]) >= 0 && xScale(localBrushExtent[0]) <= innerWidth
const eastHandleInView =
localBrushExtent && xScale(localBrushExtent[1]) >= 0 && xScale(localBrushExtent[1]) <= innerWidth
return useMemo(
() => (
<>
<defs>
<linearGradient id={`${id}-gradient-selection`} x1="0%" y1="100%" x2="100%" y2="100%">
<stop stopColor={westHandleColor} />
<stop stopColor={eastHandleColor} offset="1" />
</linearGradient>
{/* clips at exactly the svg area */}
<clipPath id={`${id}-brush-clip`}>
<rect x="0" y="0" width={innerWidth} height={innerHeight} />
</clipPath>
</defs>
{/* will host the d3 brush */}
<g
ref={brushRef}
clipPath={`url(#${id}-brush-clip)`}
onMouseEnter={() => setHovering(true)}
onMouseLeave={() => setHovering(false)}
/>
{/* custom brush handles */}
{localBrushExtent && (
<>
{/* west handle */}
{westHandleInView ? (
<g
transform={`translate(${Math.max(0, xScale(localBrushExtent[0]))}, 0), scale(${
flipWestHandle ? '-1' : '1'
}, 1)`}
>
<g>
<Handle color={westHandleColor} d={brushHandlePath(innerHeight)} />
<HandleAccent d={brushHandleAccentPath()} />
</g>
<LabelGroup
transform={`translate(50,0), scale(${flipWestHandle ? '1' : '-1'}, 1)`}
visible={showLabels || hovering}
>
<TooltipBackground y="0" x="-30" height="30" width="60" rx="8" />
<Tooltip transform={`scale(-1, 1)`} y="15" dominantBaseline="middle">
{brushLabelValue('w', localBrushExtent[0])}
</Tooltip>
</LabelGroup>
</g>
) : null}
{/* east handle */}
{eastHandleInView ? (
<g transform={`translate(${xScale(localBrushExtent[1])}, 0), scale(${flipEastHandle ? '-1' : '1'}, 1)`}>
<g>
<Handle color={eastHandleColor} d={brushHandlePath(innerHeight)} />
<HandleAccent d={brushHandleAccentPath()} />
</g>
<LabelGroup
transform={`translate(50,0), scale(${flipEastHandle ? '-1' : '1'}, 1)`}
visible={showLabels || hovering}
>
<TooltipBackground y="0" x="-30" height="30" width="60" rx="8" />
<Tooltip y="15" dominantBaseline="middle">
{brushLabelValue('e', localBrushExtent[1])}
</Tooltip>
</LabelGroup>
</g>
) : null}
{showWestArrow && <OffScreenHandle color={westHandleColor} />}
{showEastArrow && (
<g transform={`translate(${innerWidth}, 0) scale(-1, 1)`}>
<OffScreenHandle color={eastHandleColor} />
</g>
)}
</>
)}
</>
),
[
brushLabelValue,
eastHandleColor,
eastHandleInView,
flipEastHandle,
flipWestHandle,
hovering,
id,
innerHeight,
innerWidth,
localBrushExtent,
showEastArrow,
showLabels,
showWestArrow,
westHandleColor,
westHandleInView,
xScale,
]
)
}

View File

@@ -0,0 +1,146 @@
import { max, scaleLinear, ZoomTransform } from 'd3'
import { useEffect, useMemo, useRef, useState } from 'react'
import { Bound } from 'state/mint/v3/actions'
import { Area } from './Area'
import { AxisBottom } from './AxisBottom'
import { Brush } from './Brush'
import { Line } from './Line'
import { ChartEntry, LiquidityChartRangeInputProps } from './types'
import Zoom, { ZoomOverlay } from './Zoom'
export const xAccessor = (d: ChartEntry) => d.price0
export const yAccessor = (d: ChartEntry) => d.activeLiquidity
export function Chart({
id = 'liquidityChartRangeInput',
data: { series, current },
ticksAtLimit,
styles,
dimensions: { width, height },
margins,
interactive = true,
brushDomain,
brushLabels,
onBrushDomainChange,
zoomLevels,
}: LiquidityChartRangeInputProps) {
const zoomRef = useRef<SVGRectElement | null>(null)
const [zoom, setZoom] = useState<ZoomTransform | null>(null)
const [innerHeight, innerWidth] = useMemo(
() => [height - margins.top - margins.bottom, width - margins.left - margins.right],
[width, height, margins]
)
const { xScale, yScale } = useMemo(() => {
const scales = {
xScale: scaleLinear()
.domain([current * zoomLevels.initialMin, current * zoomLevels.initialMax] as number[])
.range([0, innerWidth]),
yScale: scaleLinear()
.domain([0, max(series, yAccessor)] as number[])
.range([innerHeight, 0]),
}
if (zoom) {
const newXscale = zoom.rescaleX(scales.xScale)
scales.xScale.domain(newXscale.domain())
}
return scales
}, [current, zoomLevels.initialMin, zoomLevels.initialMax, innerWidth, series, innerHeight, zoom])
useEffect(() => {
// reset zoom as necessary
setZoom(null)
}, [zoomLevels])
useEffect(() => {
if (!brushDomain) {
onBrushDomainChange(xScale.domain() as [number, number], undefined)
}
}, [brushDomain, onBrushDomainChange, xScale])
return (
<>
<Zoom
svg={zoomRef.current}
xScale={xScale}
setZoom={setZoom}
width={innerWidth}
height={
// allow zooming inside the x-axis
height
}
resetBrush={() => {
onBrushDomainChange(
[current * zoomLevels.initialMin, current * zoomLevels.initialMax] as [number, number],
'reset'
)
}}
showResetButton={Boolean(ticksAtLimit[Bound.LOWER] || ticksAtLimit[Bound.UPPER])}
zoomLevels={zoomLevels}
/>
<svg width="100%" height="100%" viewBox={`0 0 ${width} ${height}`} style={{ overflow: 'visible' }}>
<defs>
<clipPath id={`${id}-chart-clip`}>
<rect x="0" y="0" width={innerWidth} height={height} />
</clipPath>
{brushDomain && (
// mask to highlight selected area
<mask id={`${id}-chart-area-mask`}>
<rect
fill="white"
x={xScale(brushDomain[0])}
y="0"
width={xScale(brushDomain[1]) - xScale(brushDomain[0])}
height={innerHeight}
/>
</mask>
)}
</defs>
<g transform={`translate(${margins.left},${margins.top})`}>
<g clipPath={`url(#${id}-chart-clip)`}>
<Area series={series} xScale={xScale} yScale={yScale} xValue={xAccessor} yValue={yAccessor} />
{brushDomain && (
// duplicate area chart with mask for selected area
<g mask={`url(#${id}-chart-area-mask)`}>
<Area
series={series}
xScale={xScale}
yScale={yScale}
xValue={xAccessor}
yValue={yAccessor}
fill={styles.area.selection}
/>
</g>
)}
<Line value={current} xScale={xScale} innerHeight={innerHeight} />
<AxisBottom xScale={xScale} innerHeight={innerHeight} />
</g>
<ZoomOverlay width={innerWidth} height={height} ref={zoomRef} />
<Brush
id={id}
xScale={xScale}
interactive={interactive}
brushLabelValue={brushLabels}
brushExtent={brushDomain ?? (xScale.domain() as [number, number])}
innerWidth={innerWidth}
innerHeight={innerHeight}
setBrushExtent={onBrushDomainChange}
westHandleColor={styles.brush.handle.west}
eastHandleColor={styles.brush.handle.east}
/>
</g>
</svg>
</>
)
}

View File

@@ -0,0 +1,24 @@
import React, { useMemo } from 'react'
import { ScaleLinear } from 'd3'
import styled from 'styled-components/macro'
const StyledLine = styled.line`
opacity: 0.5;
stroke-width: 2;
stroke: ${({ theme }) => theme.text1};
fill: none;
`
export const Line = ({
value,
xScale,
innerHeight,
}: {
value: number
xScale: ScaleLinear<number, number>
innerHeight: number
}) =>
useMemo(
() => <StyledLine x1={xScale(value)} y1="0" x2={xScale(value)} y2={innerHeight} />,
[value, xScale, innerHeight]
)

View File

@@ -0,0 +1,130 @@
import React, { useEffect, useMemo, useRef } from 'react'
import { ButtonGray } from 'components/Button'
import styled from 'styled-components/macro'
import { ScaleLinear, select, ZoomBehavior, zoom, ZoomTransform, zoomIdentity } from 'd3'
import { RefreshCcw, ZoomIn, ZoomOut } from 'react-feather'
import { ZoomLevels } from './types'
const Wrapper = styled.div<{ count: number }>`
display: grid;
grid-template-columns: repeat(${({ count }) => count.toString()}, 1fr);
grid-gap: 6px;
position: absolute;
top: -75px;
right: 0;
`
const Button = styled(ButtonGray)`
&:hover {
background-color: ${({ theme }) => theme.bg2};
color: ${({ theme }) => theme.text1};
}
width: 32px;
height: 32px;
padding: 4px;
`
export const ZoomOverlay = styled.rect`
fill: transparent;
cursor: grab;
&:active {
cursor: grabbing;
}
`
export default function Zoom({
svg,
xScale,
setZoom,
width,
height,
resetBrush,
showResetButton,
zoomLevels,
}: {
svg: SVGElement | null
xScale: ScaleLinear<number, number>
setZoom: (transform: ZoomTransform) => void
width: number
height: number
resetBrush: () => void
showResetButton: boolean
zoomLevels: ZoomLevels
}) {
const zoomBehavior = useRef<ZoomBehavior<Element, unknown>>()
const [zoomIn, zoomOut, zoomInitial, zoomReset] = useMemo(
() => [
() =>
svg &&
zoomBehavior.current &&
select(svg as Element)
.transition()
.call(zoomBehavior.current.scaleBy, 2),
() =>
svg &&
zoomBehavior.current &&
select(svg as Element)
.transition()
.call(zoomBehavior.current.scaleBy, 0.5),
() =>
svg &&
zoomBehavior.current &&
select(svg as Element)
.transition()
.call(zoomBehavior.current.scaleTo, 0.5),
() =>
svg &&
zoomBehavior.current &&
select(svg as Element)
.call(zoomBehavior.current.transform, zoomIdentity.translate(0, 0).scale(1))
.transition()
.call(zoomBehavior.current.scaleTo, 0.5),
],
[svg]
)
useEffect(() => {
if (!svg) return
zoomBehavior.current = zoom()
.scaleExtent([zoomLevels.min, zoomLevels.max])
.extent([
[0, 0],
[width, height],
])
.on('zoom', ({ transform }: { transform: ZoomTransform }) => setZoom(transform))
select(svg as Element).call(zoomBehavior.current)
}, [height, width, setZoom, svg, xScale, zoomBehavior, zoomLevels, zoomLevels.max, zoomLevels.min])
useEffect(() => {
// reset zoom to initial on zoomLevel change
zoomInitial()
}, [zoomInitial, zoomLevels])
return (
<Wrapper count={showResetButton ? 3 : 2}>
{showResetButton && (
<Button
onClick={() => {
resetBrush()
zoomReset()
}}
disabled={false}
>
<RefreshCcw size={16} />
</Button>
)}
<Button onClick={zoomIn} disabled={false}>
<ZoomIn size={16} />
</Button>
<Button onClick={zoomOut} disabled={false}>
<ZoomOut size={16} />
</Button>
</Wrapper>
)
}

View File

@@ -0,0 +1,56 @@
import { useCallback, useMemo } from 'react'
import { Currency } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk'
import { usePoolActiveLiquidity } from 'hooks/usePoolTickData'
import { ChartEntry } from './types'
import JSBI from 'jsbi'
export interface TickProcessed {
liquidityActive: JSBI
price0: string
}
export function useDensityChartData({
currencyA,
currencyB,
feeAmount,
}: {
currencyA: Currency | undefined
currencyB: Currency | undefined
feeAmount: FeeAmount | undefined
}) {
const { isLoading, isUninitialized, isError, error, data } = usePoolActiveLiquidity(currencyA, currencyB, feeAmount)
const formatData = useCallback(() => {
if (!data?.length) {
return undefined
}
const newData: ChartEntry[] = []
for (let i = 0; i < data.length; i++) {
const t: TickProcessed = data[i]
const chartEntry = {
activeLiquidity: parseFloat(t.liquidityActive.toString()),
price0: parseFloat(t.price0),
}
if (chartEntry.activeLiquidity > 0) {
newData.push(chartEntry)
}
}
return newData
}, [data])
return useMemo(() => {
return {
isLoading,
isUninitialized,
isError,
error,
formattedData: !isLoading && !isUninitialized ? formatData() : undefined,
}
}, [isLoading, isUninitialized, isError, error, formatData])
}

View File

@@ -0,0 +1,208 @@
import React, { ReactNode, useCallback, useMemo } from 'react'
import { Trans } from '@lingui/macro'
import { Currency, Price, Token } from '@uniswap/sdk-core'
import { AutoColumn, ColumnCenter } from 'components/Column'
import Loader from 'components/Loader'
import { useColor } from 'hooks/useColor'
import useTheme from 'hooks/useTheme'
import { saturate } from 'polished'
import { BarChart2, Inbox, CloudOff } from 'react-feather'
import { batch } from 'react-redux'
import styled from 'styled-components/macro'
import { TYPE } from '../../theme'
import { Chart } from './Chart'
import { useDensityChartData } from './hooks'
import { format } from 'd3'
import { Bound } from 'state/mint/v3/actions'
import { FeeAmount } from '@uniswap/v3-sdk'
import ReactGA from 'react-ga'
import { ZoomLevels } from './types'
const ZOOM_LEVELS: Record<FeeAmount, ZoomLevels> = {
[FeeAmount.LOW]: {
initialMin: 0.999,
initialMax: 1.001,
min: 0.00001,
max: 1.5,
},
[FeeAmount.MEDIUM]: {
initialMin: 0.5,
initialMax: 2,
min: 0.00001,
max: 20,
},
[FeeAmount.HIGH]: {
initialMin: 0.5,
initialMax: 2,
min: 0.00001,
max: 20,
},
}
const ChartWrapper = styled.div`
position: relative;
justify-content: center;
align-content: center;
`
function InfoBox({ message, icon }: { message?: ReactNode; icon: ReactNode }) {
return (
<ColumnCenter style={{ height: '100%', justifyContent: 'center' }}>
{icon}
{message && (
<TYPE.mediumHeader padding={10} marginTop="20px" textAlign="center">
{message}
</TYPE.mediumHeader>
)}
</ColumnCenter>
)
}
export default function LiquidityChartRangeInput({
currencyA,
currencyB,
feeAmount,
ticksAtLimit,
price,
priceLower,
priceUpper,
onLeftRangeInput,
onRightRangeInput,
interactive,
}: {
currencyA: Currency | undefined
currencyB: Currency | undefined
feeAmount?: FeeAmount
ticksAtLimit: { [bound in Bound]?: boolean | undefined }
price: number | undefined
priceLower?: Price<Token, Token>
priceUpper?: Price<Token, Token>
onLeftRangeInput: (typedValue: string) => void
onRightRangeInput: (typedValue: string) => void
interactive: boolean
}) {
const theme = useTheme()
const tokenAColor = useColor(currencyA?.wrapped)
const tokenBColor = useColor(currencyB?.wrapped)
const isSorted = currencyA && currencyB && currencyA?.wrapped.sortsBefore(currencyB?.wrapped)
const { isLoading, isUninitialized, isError, error, formattedData } = useDensityChartData({
currencyA,
currencyB,
feeAmount,
})
const onBrushDomainChangeEnded = useCallback(
(domain, mode) => {
let leftRangeValue = Number(domain[0])
const rightRangeValue = Number(domain[1])
if (leftRangeValue <= 0) {
leftRangeValue = 1 / 10 ** 6
}
batch(() => {
// simulate user input for auto-formatting and other validations
if (
(!ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER] || mode === 'handle' || mode === 'reset') &&
leftRangeValue > 0
) {
onLeftRangeInput(leftRangeValue.toFixed(6))
}
if ((!ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER] || mode === 'reset') && rightRangeValue > 0) {
// todo: remove this check. Upper bound for large numbers
// sometimes fails to parse to tick.
if (rightRangeValue < 1e35) {
onRightRangeInput(rightRangeValue.toFixed(6))
}
}
})
},
[isSorted, onLeftRangeInput, onRightRangeInput, ticksAtLimit]
)
interactive = interactive && Boolean(formattedData?.length)
const brushDomain: [number, number] | undefined = useMemo(() => {
const leftPrice = isSorted ? priceLower : priceUpper?.invert()
const rightPrice = isSorted ? priceUpper : priceLower?.invert()
return leftPrice && rightPrice
? [parseFloat(leftPrice?.toSignificant(6)), parseFloat(rightPrice?.toSignificant(6))]
: undefined
}, [isSorted, priceLower, priceUpper])
const brushLabelValue = useCallback(
(d: 'w' | 'e', x: number) => {
if (!price) return ''
if (d === 'w' && ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER]) return '0'
if (d === 'e' && ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER]) return '∞'
const percent = (x < price ? -1 : 1) * ((Math.max(x, price) - Math.min(x, price)) / price) * 100
return price ? `${format(Math.abs(percent) > 1 ? '.2~s' : '.2~f')(percent)}%` : ''
},
[isSorted, price, ticksAtLimit]
)
if (isError) {
ReactGA.exception({
...error,
category: 'Liquidity',
fatal: false,
})
}
return (
<AutoColumn gap="md" style={{ minHeight: '200px' }}>
{isUninitialized ? (
<InfoBox
message={<Trans>Your position will appear here.</Trans>}
icon={<Inbox size={56} stroke={theme.text1} />}
/>
) : isLoading ? (
<InfoBox icon={<Loader size="40px" stroke={theme.text4} />} />
) : isError ? (
<InfoBox
message={<Trans>Liquidity data not available.</Trans>}
icon={<CloudOff size={56} stroke={theme.text4} />}
/>
) : !formattedData || formattedData === [] || !price ? (
<InfoBox
message={<Trans>There is no liquidity data.</Trans>}
icon={<BarChart2 size={56} stroke={theme.text4} />}
/>
) : (
<ChartWrapper>
<Chart
data={{ series: formattedData, current: price }}
dimensions={{ width: 400, height: 200 }}
margins={{ top: 10, right: 2, bottom: 20, left: 0 }}
styles={{
area: {
selection: theme.blue1,
},
brush: {
handle: {
west: saturate(0.1, tokenAColor) ?? theme.red1,
east: saturate(0.1, tokenBColor) ?? theme.blue1,
},
},
}}
interactive={interactive}
brushLabels={brushLabelValue}
brushDomain={brushDomain}
onBrushDomainChange={onBrushDomainChangeEnded}
zoomLevels={ZOOM_LEVELS[feeAmount ?? FeeAmount.MEDIUM]}
ticksAtLimit={ticksAtLimit}
/>
</ChartWrapper>
)}
</AutoColumn>
)
}

View File

@@ -0,0 +1,61 @@
/*
* Generates an SVG path for the east brush handle.
* Apply `scale(-1, 1)` to generate west brush handle.
*
* |```````\
* | | | |
* |______/
* |
* |
* |
* |
* |
*
* https://medium.com/@dennismphil/one-side-rounded-rectangle-using-svg-fb31cf318d90
*/
export const brushHandlePath = (height: number) =>
[
// handle
`M 0 0`, // move to origin
`v ${height}`, // vertical line
'm 1 0', // move 1px to the right
`V 0`, // second vertical line
`M 0 1`, // move to origin
// head
'h 12', // horizontal line
'q 2 0, 2 2', // rounded corner
'v 22', // vertical line
'q 0 2 -2 2', // rounded corner
'h -12', // horizontal line
`z`, // close path
].join(' ')
export const brushHandleAccentPath = () =>
[
'm 5 7', // move to first accent
'v 14', // vertical line
'M 0 0', // move to origin
'm 9 7', // move to second accent
'v 14', // vertical line
'z',
].join(' ')
export const OffScreenHandle = ({
color,
size = 10,
margin = 10,
}: {
color: string
size?: number
margin?: number
}) => (
<polygon
points={`0 0, ${size} ${size}, 0 ${size}`}
transform={` translate(${size + margin}, ${margin}) rotate(45) `}
fill={color}
stroke={color}
strokeWidth="4"
strokeLinejoin="round"
/>
)

View File

@@ -0,0 +1,61 @@
import { Bound } from 'state/mint/v3/actions'
export interface ChartEntry {
activeLiquidity: number
price0: number
}
export interface Dimensions {
width: number
height: number
}
export interface Margins {
top: number
right: number
bottom: number
left: number
}
export interface ZoomLevels {
initialMin: number
initialMax: number
min: number
max: number
}
export interface LiquidityChartRangeInputProps {
// to distringuish between multiple charts in the DOM
id?: string
data: {
series: ChartEntry[]
current: number
}
ticksAtLimit: { [bound in Bound]?: boolean | undefined }
styles: {
area: {
// color of the ticks in range
selection: string
}
brush: {
handle: {
west: string
east: string
}
}
}
dimensions: Dimensions
margins: Margins
interactive?: boolean
brushLabels: (d: 'w' | 'e', x: number) => string
brushDomain: [number, number] | undefined
onBrushDomainChange: (domain: [number, number], mode: string | undefined) => void
zoomLevels: ZoomLevels
}

View File

@@ -1,6 +1,4 @@
import React from 'react'
import styled, { keyframes } from 'styled-components'
import styled, { keyframes } from 'styled-components/macro'
const rotate = keyframes`
from {

View File

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

View File

@@ -1,16 +1,22 @@
import React, { useRef } from 'react'
import { BookOpen, Code, Info, MessageCircle, PieChart } from 'react-feather'
import { t } from '@lingui/macro'
import React, { useEffect, useRef, useState } from 'react'
import { BookOpen, Code, Info, MessageCircle, PieChart, Moon, Sun, Globe, ChevronLeft, Check } from 'react-feather'
import { Link } from 'react-router-dom'
import styled, { css } from 'styled-components'
import styled, { css } from 'styled-components/macro'
import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg'
import { useActiveWeb3React } from '../../hooks/web3'
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
import { ApplicationModal } from '../../state/application/actions'
import { useModalOpen, useToggleModal } from '../../state/application/hooks'
import { Trans } from '@lingui/macro'
import { ExternalLink } from '../../theme'
import { ButtonPrimary } from '../Button'
import { useDarkModeManager } from 'state/user/hooks'
import { L2_CHAIN_IDS, CHAIN_INFO, SupportedChainId } from 'constants/chains'
import { LOCALE_LABEL, SupportedLocale, SUPPORTED_LOCALES } from 'constants/locales'
import { useLocationLinkProps } from 'hooks/useLocationLinkProps'
import { useActiveLocale } from 'hooks/useActiveLocale'
export enum FlyoutAlignment {
LEFT = 'LEFT',
@@ -30,17 +36,18 @@ const StyledMenuButton = styled.button`
background-color: transparent;
margin: 0;
padding: 0;
height: 35px;
background-color: ${({ theme }) => theme.bg2};
height: 38px;
background-color: ${({ theme }) => theme.bg0};
border: 1px solid ${({ theme }) => theme.bg0};
padding: 0.15rem 0.5rem;
border-radius: 0.5rem;
border-radius: 12px;
:hover,
:focus {
cursor: pointer;
outline: none;
background-color: ${({ theme }) => theme.bg3};
border: 1px solid ${({ theme }) => theme.bg3};
}
svg {
@@ -65,18 +72,22 @@ const StyledMenu = styled.div`
`
const MenuFlyout = styled.span<{ flyoutAlignment?: FlyoutAlignment }>`
min-width: 8.125rem;
background-color: ${({ theme }) => theme.bg2};
min-width: 196px;
max-height: 350px;
overflow: auto;
background-color: ${({ theme }) => theme.bg1};
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
0px 24px 32px rgba(0, 0, 0, 0.01);
border: 1px solid ${({ theme }) => theme.bg0};
border-radius: 12px;
padding: 0.5rem;
display: flex;
flex-direction: column;
font-size: 1rem;
font-size: 16px;
position: absolute;
top: 3rem;
z-index: 100;
${({ flyoutAlignment = FlyoutAlignment.RIGHT }) =>
flyoutAlignment === FlyoutAlignment.RIGHT
? css`
@@ -86,7 +97,9 @@ const MenuFlyout = styled.span<{ flyoutAlignment?: FlyoutAlignment }>`
left: 0rem;
`};
${({ theme }) => theme.mediaWidth.upToMedium`
top: -17.25rem;
bottom: unset;
right: 0;
left: unset;
`};
`
@@ -96,15 +109,13 @@ const MenuItem = styled(ExternalLink)`
flex-direction: row;
align-items: center;
padding: 0.5rem 0.5rem;
justify-content: space-between;
color: ${({ theme }) => theme.text2};
:hover {
color: ${({ theme }) => theme.text1};
cursor: pointer;
text-decoration: none;
}
> svg {
margin-right: 8px;
}
`
const InternalMenuItem = styled(Link)`
@@ -121,63 +132,161 @@ const InternalMenuItem = styled(Link)`
}
`
const InternalLinkMenuItem = styled(InternalMenuItem)`
display: flex;
flex-direction: row;
align-items: center;
padding: 0.5rem 0.5rem;
justify-content: space-between;
text-decoration: none;
:hover {
color: ${({ theme }) => theme.text1};
cursor: pointer;
text-decoration: none;
}
`
const ToggleMenuItem = styled.button`
background-color: transparent;
margin: 0;
padding: 0;
border: none;
display: flex;
flex: 1;
flex-direction: row;
align-items: center;
padding: 0.5rem 0.5rem;
justify-content: space-between;
font-size: 1rem;
font-weight: 500;
color: ${({ theme }) => theme.text2};
:hover {
color: ${({ theme }) => theme.text1};
cursor: pointer;
text-decoration: none;
}
`
const CODE_LINK = 'https://github.com/Uniswap/uniswap-interface'
function LanguageMenuItem({ locale, active, key }: { locale: SupportedLocale; active: boolean; key: string }) {
const { to, onClick } = useLocationLinkProps(locale)
if (!to) return null
return (
<InternalLinkMenuItem onClick={onClick} key={key} to={to}>
<div>{LOCALE_LABEL[locale]}</div>
{active && <Check opacity={0.6} size={16} />}
</InternalLinkMenuItem>
)
}
function LanguageMenu({ close }: { close: () => void }) {
const activeLocale = useActiveLocale()
return (
<MenuFlyout>
<ToggleMenuItem onClick={close}>
<ChevronLeft size={16} />
</ToggleMenuItem>
{SUPPORTED_LOCALES.map((locale) => (
<LanguageMenuItem locale={locale} active={activeLocale === locale} key={locale} />
))}
</MenuFlyout>
)
}
export default function Menu() {
const { account } = useActiveWeb3React()
const { account, chainId } = useActiveWeb3React()
const node = useRef<HTMLDivElement>()
const open = useModalOpen(ApplicationModal.MENU)
const toggle = useToggleModal(ApplicationModal.MENU)
useOnClickOutside(node, open ? toggle : undefined)
const openClaimModal = useToggleModal(ApplicationModal.ADDRESS_CLAIM)
const showUNIClaimOption = Boolean(!!account && !!chainId && !L2_CHAIN_IDS.includes(chainId))
const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET]
const [darkMode, toggleDarkMode] = useDarkModeManager()
const [menu, setMenu] = useState<'main' | 'lang'>('main')
useEffect(() => {
setMenu('main')
}, [open])
return (
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451
<StyledMenu ref={node as any}>
<StyledMenuButton onClick={toggle}>
<StyledMenuButton onClick={toggle} aria-label={t`Menu`}>
<StyledMenuIcon />
</StyledMenuButton>
{open && (
<MenuFlyout>
<MenuItem href="https://uniswap.org/">
<Info size={14} />
<div>
<Trans>About</Trans>
</div>
</MenuItem>
<MenuItem href="https://docs.uniswap.org/">
<BookOpen size={14} />
<div>
<Trans>Docs</Trans>
</div>
</MenuItem>
<MenuItem href={CODE_LINK}>
<Code size={14} />
<div>
<Trans>Code</Trans>
</div>
</MenuItem>
<MenuItem href="https://discord.gg/FCfyBSbCU5">
<MessageCircle size={14} />
<div>
<Trans>Discord</Trans>
</div>
</MenuItem>
<MenuItem href="https://info.uniswap.org/">
<PieChart size={14} />
<div>
<Trans>Analytics</Trans>
</div>
</MenuItem>
{account && (
<UNIbutton onClick={openClaimModal} padding="8px 16px" width="100%" borderRadius="12px" mt="0.5rem">
<Trans>Claim UNI</Trans>
</UNIbutton>
)}
</MenuFlyout>
)}
{open &&
(() => {
switch (menu) {
case 'lang':
return <LanguageMenu close={() => setMenu('main')} />
case 'main':
default:
return (
<MenuFlyout>
<MenuItem href="https://uniswap.org/">
<div>
<Trans>About</Trans>
</div>
<Info opacity={0.6} size={16} />
</MenuItem>
<MenuItem href="https://docs.uniswap.org/">
<div>
<Trans>Docs</Trans>
</div>
<BookOpen opacity={0.6} size={16} />
</MenuItem>
<MenuItem href={CODE_LINK}>
<div>
<Trans>Code</Trans>
</div>
<Code opacity={0.6} size={16} />
</MenuItem>
<MenuItem href="https://discord.gg/FCfyBSbCU5">
<div>
<Trans>Discord</Trans>
</div>
<MessageCircle opacity={0.6} size={16} />
</MenuItem>
<MenuItem href={infoLink}>
<div>
<Trans>Analytics</Trans>
</div>
<PieChart opacity={0.6} size={16} />
</MenuItem>
<ToggleMenuItem onClick={() => setMenu('lang')}>
<div>
<Trans>Language</Trans>
</div>
<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>
)
}
@@ -218,11 +327,11 @@ export const NewMenu = ({ flyoutAlignment = FlyoutAlignment.RIGHT, ToggleUI, men
<NewMenuFlyout flyoutAlignment={flyoutAlignment}>
{menuItems.map(({ content, link, external }, i) =>
external ? (
<ExternalMenuItem id="link" href={link} key={link + i}>
<ExternalMenuItem href={link} key={i}>
{content}
</ExternalMenuItem>
) : (
<NewMenuItem id="link" to={link} key={link + i}>
<NewMenuItem to={link} key={i}>
{content}
</NewMenuItem>
)

View File

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

View File

@@ -1,9 +1,9 @@
import React, { useContext } from 'react'
import { useContext } from 'react'
import { useActiveWeb3React } from '../../hooks/web3'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { AutoColumn, ColumnCenter } from '../Column'
import styled, { ThemeContext } from 'styled-components'
import styled, { ThemeContext } from 'styled-components/macro'
import { RowBetween } from '../Row'
import { TYPE, CloseIcon, CustomLightSpinner } from '../../theme'
import { ArrowUpCircle } from 'react-feather'

View File

@@ -1,4 +1,3 @@
import React from 'react'
import styled from 'styled-components/macro'
import { darken } from 'polished'
import { Trans } from '@lingui/macro'
@@ -6,7 +5,7 @@ import { NavLink, Link as HistoryLink, useLocation } from 'react-router-dom'
import { Percent } from '@uniswap/sdk-core'
import { ArrowLeft } from 'react-feather'
import { RowBetween } from '../Row'
import Row, { RowBetween } from '../Row'
import SettingsTab from '../Settings'
import { useAppDispatch } from 'state/hooks'
@@ -14,6 +13,8 @@ import { resetMintState } from 'state/mint/actions'
import { resetMintState as resetMintV3State } from 'state/mint/v3/actions'
import { TYPE } from 'theme'
import useTheme from 'hooks/useTheme'
import { ReactNode } from 'react'
import { Box } from 'rebass'
const Tabs = styled.div`
${({ theme }) => theme.flexRowNoWrap}
@@ -50,6 +51,15 @@ const StyledNavLink = styled(NavLink).attrs({
}
`
const StyledHistoryLink = styled(HistoryLink)<{ flex: string | undefined }>`
flex: ${({ flex }) => flex ?? 'none'};
${({ theme }) => theme.mediaWidth.upToMedium`
flex: none;
margin-right: 10px;
`};
`
const ActiveText = styled.div`
font-weight: 500;
font-size: 20px;
@@ -75,11 +85,11 @@ export function SwapPoolTabs({ active }: { active: 'swap' | 'pool' }) {
export function FindPoolTabs({ origin }: { origin: string }) {
return (
<Tabs>
<RowBetween style={{ padding: '1rem 1rem 0 1rem' }}>
<RowBetween style={{ padding: '1rem 1rem 0 1rem', position: 'relative' }}>
<HistoryLink to={origin}>
<StyledArrowLeft />
</HistoryLink>
<ActiveText>
<ActiveText style={{ position: 'absolute', left: '50%', transform: 'translateX(-50%)' }}>
<Trans>Import V2 Pool</Trans>
</ActiveText>
</RowBetween>
@@ -92,11 +102,14 @@ export function AddRemoveTabs({
creating,
defaultSlippage,
positionID,
children,
}: {
adding: boolean
creating: boolean
defaultSlippage: Percent
positionID?: string | undefined
showBackLink?: boolean
children?: ReactNode | undefined
}) {
const theme = useTheme()
// reset states on back
@@ -111,7 +124,7 @@ export function AddRemoveTabs({
return (
<Tabs>
<RowBetween style={{ padding: '1rem 1rem 0 1rem' }}>
<HistoryLink
<StyledHistoryLink
to={poolLink}
onClick={() => {
if (adding) {
@@ -120,10 +133,15 @@ export function AddRemoveTabs({
dispatch(resetMintV3State())
}
}}
flex={children ? '1' : undefined}
>
<StyledArrowLeft stroke={theme.text2} />
</HistoryLink>
<TYPE.mediumHeader fontWeight={500} fontSize={20}>
</StyledHistoryLink>
<TYPE.mediumHeader
fontWeight={500}
fontSize={20}
style={{ flex: '1', margin: 'auto', textAlign: children ? 'start' : 'center' }}
>
{creating ? (
<Trans>Create a pair</Trans>
) : adding ? (
@@ -132,8 +150,22 @@ export function AddRemoveTabs({
<Trans>Remove Liquidity</Trans>
)}
</TYPE.mediumHeader>
<Box style={{ marginRight: '.5rem' }}>{children}</Box>
<SettingsTab placeholderSlippage={defaultSlippage} />
</RowBetween>
</Tabs>
)
}
export function CreateProposalTabs() {
return (
<Tabs>
<Row style={{ padding: '1rem 1rem 0 1rem' }}>
<HistoryLink to="/vote">
<StyledArrowLeft />
</HistoryLink>
<ActiveText style={{ marginLeft: 'auto', marginRight: 'auto' }}>Create Proposal</ActiveText>
</Row>
</Tabs>
)
}

View File

@@ -0,0 +1,134 @@
import { Trans } from '@lingui/macro'
import {
ArbitrumWrapperBackgroundDarkMode,
ArbitrumWrapperBackgroundLightMode,
OptimismWrapperBackgroundDarkMode,
OptimismWrapperBackgroundLightMode,
} from 'components/NetworkAlert/NetworkAlert'
import { CHAIN_INFO, L2_CHAIN_IDS, SupportedChainId, SupportedL2ChainId } from 'constants/chains'
import { useActiveWeb3React } from 'hooks/web3'
import { ArrowDownCircle } from 'react-feather'
import { useArbitrumAlphaAlert, useDarkModeManager } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
import { ReadMoreLink } from './styles'
const L2Icon = styled.img`
display: none;
height: 40px;
margin: auto 20px auto 4px;
width: 40px;
@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) {
display: block;
}
`
const DesktopTextBreak = styled.div`
display: none;
@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) {
display: block;
}
`
const Wrapper = styled.div<{ chainId: SupportedL2ChainId; darkMode: boolean; logoUrl: string }>`
${({ chainId, darkMode }) =>
[SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId)
? darkMode
? OptimismWrapperBackgroundDarkMode
: OptimismWrapperBackgroundLightMode
: darkMode
? ArbitrumWrapperBackgroundDarkMode
: ArbitrumWrapperBackgroundLightMode};
border-radius: 20px;
display: flex;
flex-direction: column;
overflow: hidden;
padding: 12px;
position: relative;
width: 100%;
:before {
background-image: url(${({ logoUrl }) => logoUrl});
background-repeat: no-repeat;
background-size: 300px;
content: '';
height: 300px;
opacity: 0.1;
position: absolute;
transform: rotate(25deg) translate(-90px, -40px);
width: 300px;
z-index: -1;
}
@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) {
flex-direction: row;
padding: 16px 20px;
}
`
const Body = styled.div`
font-size: 12px;
line-height: 143%;
margin: 12px;
@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) {
flex: 1 1 auto;
margin: auto 0;
}
`
const LinkOutCircle = styled(ArrowDownCircle)`
transform: rotate(230deg);
width: 20px;
height: 20px;
margin-left: 12px;
`
const LinkOutToBridge = styled(ExternalLink)`
align-items: center;
background-color: black;
border-radius: 16px;
color: white;
display: flex;
font-size: 14px;
justify-content: space-between;
margin: 0;
max-height: 47px;
padding: 16px 12px;
text-decoration: none;
width: auto;
:hover,
:focus,
:active {
background-color: black;
}
@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) {
margin: auto 0 auto auto;
padding: 14px 16px;
min-width: 226px;
}
`
export function AddLiquidityNetworkAlert() {
const { chainId } = useActiveWeb3React()
const [darkMode] = useDarkModeManager()
const [arbitrumAlphaAcknowledged] = useArbitrumAlphaAlert()
if (!chainId || !L2_CHAIN_IDS.includes(chainId) || arbitrumAlphaAcknowledged) {
return null
}
const info = CHAIN_INFO[chainId as SupportedL2ChainId]
const isOptimism = [SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId)
const depositUrl = isOptimism ? `${info.bridge}?chainId=1` : info.bridge
const readMoreLink = isOptimism
? 'https://help.uniswap.org/en/articles/5392809-how-to-deposit-tokens-to-optimism'
: 'https://help.uniswap.org/en/articles/5538618-how-to-deposit-tokens-to-arbitrum'
return (
<Wrapper darkMode={darkMode} chainId={chainId} logoUrl={info.logoUrl}>
<L2Icon src={info.logoUrl} />
<Body>
<Trans>This is an alpha release of Uniswap on the {info.label} network.</Trans>
<DesktopTextBreak /> <Trans>You must bridge L1 assets to the network to use them.</Trans>{' '}
<ReadMoreLink href={readMoreLink}>
<Trans>Read more</Trans>
</ReadMoreLink>
</Body>
<LinkOutToBridge href={depositUrl}>
<Trans>Deposit to {info.label}</Trans>
<LinkOutCircle />
</LinkOutToBridge>
</Wrapper>
)
}

View File

@@ -0,0 +1,134 @@
import { Trans } from '@lingui/macro'
import {
ArbitrumWrapperBackgroundDarkMode,
ArbitrumWrapperBackgroundLightMode,
OptimismWrapperBackgroundDarkMode,
OptimismWrapperBackgroundLightMode,
} from 'components/NetworkAlert/NetworkAlert'
import { CHAIN_INFO, L2_CHAIN_IDS, SupportedChainId, SupportedL2ChainId } from 'constants/chains'
import { useActiveWeb3React } from 'hooks/web3'
import { ArrowDownCircle } from 'react-feather'
import { useArbitrumAlphaAlert, useDarkModeManager } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
import { ReadMoreLink } from './styles'
const L2Icon = styled.img`
display: none;
height: 40px;
margin: auto 20px auto 4px;
width: 40px;
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
display: block;
}
`
const DesktopTextBreak = styled.div`
display: none;
@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) {
display: block;
}
`
const Wrapper = styled.div<{ chainId: SupportedL2ChainId; darkMode: boolean; logoUrl: string }>`
${({ chainId, darkMode }) =>
[SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId)
? darkMode
? OptimismWrapperBackgroundDarkMode
: OptimismWrapperBackgroundLightMode
: darkMode
? ArbitrumWrapperBackgroundDarkMode
: ArbitrumWrapperBackgroundLightMode};
border-radius: 20px;
display: flex;
flex-direction: column;
overflow: hidden;
padding: 12px;
position: relative;
width: 100%;
:before {
background-image: url(${({ logoUrl }) => logoUrl});
background-repeat: no-repeat;
background-size: 300px;
content: '';
height: 300px;
opacity: 0.1;
position: absolute;
transform: rotate(25deg) translate(-90px, -40px);
width: 300px;
z-index: -1;
}
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
flex-direction: row;
padding: 16px 20px;
}
`
const Body = styled.div`
font-size: 12px;
line-height: 143%;
margin: 12px;
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
flex: 1 1 auto;
margin: auto 0;
}
`
const LinkOutCircle = styled(ArrowDownCircle)`
transform: rotate(230deg);
width: 20px;
height: 20px;
margin-left: 12px;
`
const LinkOutToBridge = styled(ExternalLink)`
align-items: center;
background-color: black;
border-radius: 16px;
color: white;
display: flex;
font-size: 14px;
justify-content: space-between;
margin: 0;
max-height: 47px;
padding: 16px 8px;
text-decoration: none;
width: auto;
:hover,
:focus,
:active {
background-color: black;
}
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
margin: auto 0 auto auto;
padding: 14px 17px;
min-width: 226px;
}
`
export function MinimalNetworkAlert() {
const { chainId } = useActiveWeb3React()
const [darkMode] = useDarkModeManager()
const [arbitrumAlphaAcknowledged] = useArbitrumAlphaAlert()
if (!chainId || !L2_CHAIN_IDS.includes(chainId) || arbitrumAlphaAcknowledged) {
return null
}
const info = CHAIN_INFO[chainId as SupportedL2ChainId]
const isOptimism = [SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId)
const depositUrl = isOptimism ? `${info.bridge}?chainId=1` : info.bridge
const readMoreLink = isOptimism
? 'https://help.uniswap.org/en/articles/5392809-how-to-deposit-tokens-to-optimism'
: 'https://help.uniswap.org/en/articles/5538618-how-to-deposit-tokens-to-arbitrum'
return (
<Wrapper darkMode={darkMode} chainId={chainId} logoUrl={info.logoUrl}>
<L2Icon src={info.logoUrl} />
<Body>
<Trans>This is an alpha release of Uniswap on the {info.label} network.</Trans>
<DesktopTextBreak /> <Trans>You must bridge L1 assets to the network to use them.</Trans>{' '}
<ReadMoreLink href={readMoreLink}>
<Trans>Read more</Trans>
</ReadMoreLink>
</Body>
<LinkOutToBridge href={depositUrl}>
<Trans>Deposit to {info.label}</Trans>
<LinkOutCircle />
</LinkOutToBridge>
</Wrapper>
)
}

View File

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

View File

@@ -0,0 +1,7 @@
import styled from 'styled-components/macro'
import { ExternalLink } from 'theme'
export const ReadMoreLink = styled(ExternalLink)`
color: ${({ theme }) => theme.text1};
text-decoration: underline;
`

View File

@@ -0,0 +1,63 @@
import { Trans } from '@lingui/macro'
import { SupportedChainId } from 'constants/chains'
import { useActiveWeb3React } from 'hooks/web3'
import { AlertOctagon } from 'react-feather'
import styled from 'styled-components/macro'
import { ExternalLink } from 'theme'
const Root = styled.div`
background-color: ${({ theme }) => theme.yellow3};
border-radius: 18px;
color: black;
margin-top: 16px;
padding: 16px;
width: 100%;
max-width: 880px;
`
const WarningIcon = styled(AlertOctagon)`
margin: 0 8px 0 0;
`
const TitleRow = styled.div`
align-items: center;
display: flex;
flex-direction: row;
justify-content: flex-start;
margin: 0;
font-size: 20px;
font-weight: 600;
line-height: 25px;
`
const Body = styled.div`
font-size: 12px;
line-height: 15px;
margin: 8px 0 0 0;
`
const ReadMoreLink = styled(ExternalLink)`
color: black;
text-decoration: underline;
`
export default function OptimismDowntimeWarning() {
const { chainId } = useActiveWeb3React()
if (!chainId || ![SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId)) {
return null
}
return (
<Root>
<TitleRow>
<WarningIcon />
<Trans>Optimism Planned Downtime</Trans>
</TitleRow>
<Body>
<Trans>
Optimism expects planned downtime in the near future. Unplanned downtime may also occur. While the network is
down, fees will not be generated and you will be unable to remove liquidity.{' '}
<ReadMoreLink href="https://help.uniswap.org/en/articles/5406082-what-happens-if-the-optimistic-ethereum-network-experiences-downtime">
Read more.
</ReadMoreLink>
</Trans>
</Body>
</Root>
)
}

View File

@@ -1,9 +1,9 @@
import { Trans } from '@lingui/macro'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import React, { useCallback, useEffect } from 'react'
import { useCallback, useEffect } from 'react'
import ReactGA from 'react-ga'
import { Heart, X } from 'react-feather'
import styled, { keyframes } from 'styled-components'
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'
@@ -117,7 +117,7 @@ export default function ClaimPopup() {
</TYPE.subHeader>
</AutoColumn>
<AutoColumn style={{ zIndex: 10 }} justify="center">
<ButtonPrimary padding="8px" borderRadius="8px" width={'fit-content'} onClick={handleToggleSelfClaimModal}>
<ButtonPrimary padding="8px" $borderRadius="8px" width={'fit-content'} onClick={handleToggleSelfClaimModal}>
<Trans>Claim your UNI tokens</Trans>
</ButtonPrimary>
</AutoColumn>

View File

@@ -1,13 +1,13 @@
import React, { useCallback, useContext, useEffect } from 'react'
import { useCallback, useContext, useEffect } from 'react'
import { X } from 'react-feather'
import { useSpring } from 'react-spring/web'
import styled, { ThemeContext } from 'styled-components'
import styled, { ThemeContext } from 'styled-components/macro'
import { animated } from 'react-spring'
import { PopupContent } from '../../state/application/actions'
import { useRemovePopup } from '../../state/application/hooks'
import TransactionPopup from './TransactionPopup'
export const StyledClose = styled(X)`
const StyledClose = styled(X)`
position: absolute;
right: 10px;
top: 10px;
@@ -16,7 +16,7 @@ export const StyledClose = styled(X)`
cursor: pointer;
}
`
export const Popup = styled.div`
const Popup = styled.div`
display: inline-block;
width: 100%;
padding: 1em;

View File

@@ -1,6 +1,6 @@
import React, { useContext } from 'react'
import { useContext } from 'react'
import { AlertCircle, CheckCircle } from 'react-feather'
import styled, { ThemeContext } from 'styled-components'
import styled, { ThemeContext } from 'styled-components/macro'
import { useActiveWeb3React } from '../../hooks/web3'
import { TYPE } from '../../theme'
import { ExternalLink } from '../../theme/components'

View File

@@ -1,10 +1,12 @@
import React from 'react'
import styled from 'styled-components/macro'
import { useActivePopups } from '../../state/application/hooks'
import { AutoColumn } from '../Column'
import PopupItem from './PopupItem'
import ClaimPopup from './ClaimPopup'
import { useURLWarningVisible } from '../../state/user/hooks'
import { useActiveWeb3React } from 'hooks/web3'
import { SupportedChainId } from 'constants/chains'
import { MEDIA_WIDTHS } from 'theme'
const MobilePopupWrapper = styled.div<{ height: string | number }>`
position: relative;
@@ -31,9 +33,13 @@ const MobilePopupInner = styled.div`
}
`
const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean }>`
const StopOverflowQuery = `@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium + 1}px) and (max-width: ${
MEDIA_WIDTHS.upToMedium + 500
}px)`
const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean; xlPadding: boolean }>`
position: fixed;
top: ${({ extraPadding }) => (extraPadding ? '80px' : '88px')};
top: ${({ extraPadding }) => (extraPadding ? '64px' : '56px')};
right: 1rem;
max-width: 355px !important;
width: 100%;
@@ -42,6 +48,10 @@ const FixedPopupColumn = styled(AutoColumn)<{ extraPadding: boolean }>`
${({ theme }) => theme.mediaWidth.upToSmall`
display: none;
`};
${StopOverflowQuery} {
top: ${({ extraPadding, xlPadding }) => (xlPadding ? '64px' : extraPadding ? '64px' : '56px')};
}
`
export default function Popups() {
@@ -50,9 +60,13 @@ export default function Popups() {
const urlWarningActive = useURLWarningVisible()
// need extra padding if network is not L1 Ethereum
const { chainId } = useActiveWeb3React()
const isNotOnMainnet = Boolean(chainId && chainId !== SupportedChainId.MAINNET)
return (
<>
<FixedPopupColumn gap="20px" extraPadding={urlWarningActive}>
<FixedPopupColumn gap="20px" extraPadding={urlWarningActive} xlPadding={isNotOnMainnet}>
<ClaimPopup />
{activePopups.map((item) => (
<PopupItem key={item.key} content={item.content} popKey={item.key} removeAfterMs={item.removeAfterMs} />

View File

@@ -1,4 +1,3 @@
import React from 'react'
import { Token } from '@uniswap/sdk-core'
import { Link } from 'react-router-dom'
import { Text } from 'rebass'
@@ -63,7 +62,7 @@ export default function SushiPositionCard({ tokenA, tokenB, liquidityToken, bord
<RowFixed gap="8px">
<ButtonEmpty
padding="0px 35px 0px 0px"
borderRadius="12px"
$borderRadius="12px"
width="fit-content"
as={Link}
to={`/migrate/v2/${liquidityToken.address}`}

View File

@@ -1,5 +1,5 @@
import JSBI from 'jsbi'
import React, { useState } from 'react'
import { useState } from 'react'
import { Percent, CurrencyAmount, Token } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import { ChevronDown, ChevronUp } from 'react-feather'
@@ -98,7 +98,7 @@ export default function V2PositionCard({ pair, border, stakedBalance }: Position
<RowFixed gap="8px">
<ButtonEmpty
padding="6px 8px"
borderRadius="12px"
$borderRadius="12px"
width="fit-content"
onClick={() => setShowMore(!showMore)}
>
@@ -188,7 +188,7 @@ export default function V2PositionCard({ pair, border, stakedBalance }: Position
<RowBetween marginTop="10px">
<ButtonPrimary
padding="8px"
borderRadius="8px"
$borderRadius="8px"
as={Link}
to={`/migrate/v2/${pair.liquidityToken.address}`}
width="64%"
@@ -197,7 +197,7 @@ export default function V2PositionCard({ pair, border, stakedBalance }: Position
</ButtonPrimary>
<ButtonSecondary
padding="8px"
borderRadius="8px"
$borderRadius="8px"
as={Link}
width="32%"
to={`/remove/v2/${currencyId(currency0)}/${currencyId(currency1)}`}

View File

@@ -1,8 +1,7 @@
import JSBI from 'jsbi'
import { Percent, CurrencyAmount, Token } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import { darken } from 'polished'
import React, { useState } from 'react'
import { useState } from 'react'
import { ChevronDown, ChevronUp } from 'react-feather'
import { Link } from 'react-router-dom'
import { Text } from 'rebass'
@@ -21,7 +20,7 @@ import { CardNoise } from '../earn/styled'
import { useColor } from '../../hooks/useColor'
import Card, { GreyCard, LightCard } from '../Card'
import { GreyCard, LightCard } from '../Card'
import { AutoColumn } from '../Column'
import CurrencyLogo from '../CurrencyLogo'
import DoubleCurrencyLogo from '../DoubleLogo'
@@ -33,12 +32,6 @@ export const FixedHeightRow = styled(RowBetween)`
height: 24px;
`
export const HoverCard = styled(Card)`
border: 1px solid transparent;
:hover {
border: 1px solid ${({ theme }) => darken(0.06, theme.bg2)};
}
`
const StyledPositionCard = styled(LightCard)<{ bgColor: any }>`
border: none;
background: ${({ theme, bgColor }) =>
@@ -219,7 +212,7 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
</Text>
</AutoRow>
<RowFixed gap="8px" style={{ marginRight: '4px' }}>
<ButtonEmpty padding="6px 8px" borderRadius="12px" width="100%" onClick={() => setShowMore(!showMore)}>
<ButtonEmpty padding="6px 8px" $borderRadius="12px" width="100%" onClick={() => setShowMore(!showMore)}>
{showMore ? (
<>
<Trans>Manage</Trans>
@@ -306,7 +299,7 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
</Text>
</FixedHeightRow>
<ButtonSecondary padding="8px" borderRadius="8px">
<ButtonSecondary padding="8px" $borderRadius="8px">
<ExternalLink
style={{ width: '100%', textAlign: 'center' }}
href={`https://v2.info.uniswap.org/account/${account}`}
@@ -320,7 +313,7 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
<RowBetween marginTop="10px">
<ButtonPrimary
padding="8px"
borderRadius="8px"
$borderRadius="8px"
as={Link}
to={`/migrate/v2/${pair.liquidityToken.address}`}
width="32%"
@@ -329,7 +322,7 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
</ButtonPrimary>
<ButtonPrimary
padding="8px"
borderRadius="8px"
$borderRadius="8px"
as={Link}
to={`/add/v2/${currencyId(currency0)}/${currencyId(currency1)}`}
width="32%"
@@ -338,7 +331,7 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
</ButtonPrimary>
<ButtonPrimary
padding="8px"
borderRadius="8px"
$borderRadius="8px"
as={Link}
width="32%"
to={`/remove/v2/${currencyId(currency0)}/${currencyId(currency1)}`}
@@ -350,7 +343,7 @@ export default function FullPositionCard({ pair, border, stakedBalance }: Positi
{stakedBalance && JSBI.greaterThan(stakedBalance.quotient, BIG_INT_ZERO) && (
<ButtonPrimary
padding="8px"
borderRadius="8px"
$borderRadius="8px"
as={Link}
to={`/uni/${currencyId(currency0)}/${currencyId(currency1)}`}
width="100%"

View File

@@ -34,7 +34,7 @@ const MobileHeader = styled.div`
}
`
export type PositionListProps = React.PropsWithChildren<{
type PositionListProps = React.PropsWithChildren<{
positions: PositionDetails[]
}>
@@ -47,7 +47,7 @@ export default function PositionList({ positions }: PositionListProps) {
{positions && ' (' + positions.length + ')'}
</div>
<div>
<Trans>Price range</Trans>
<Trans>Status</Trans>
</div>
</DesktopHeader>
<MobileHeader>

View File

@@ -1,4 +1,4 @@
import React, { useMemo } from 'react'
import { useMemo } from 'react'
import { Position } from '@uniswap/v3-sdk'
import Badge from 'components/Badge'
import DoubleCurrencyLogo from 'components/DoubleLogo'
@@ -9,14 +9,16 @@ import styled from 'styled-components/macro'
import { HideSmall, MEDIA_WIDTHS, SmallOnly } from 'theme'
import { PositionDetails } from 'types/position'
import { Price, Token, Percent } from '@uniswap/sdk-core'
import { formatPrice } from 'utils/formatCurrencyAmount'
import { formatTickPrice } from 'utils/formatTickPrice'
import Loader from 'components/Loader'
import { unwrappedToken } from 'utils/unwrappedToken'
import RangeBadge from 'components/Badge/RangeBadge'
import { RowFixed } from 'components/Row'
import { RowBetween } from 'components/Row'
import HoverInlineText from 'components/HoverInlineText'
import { DAI, USDC, USDT, WBTC, WETH9_EXTENDED } from '../../constants/tokens'
import { Trans } from '@lingui/macro'
import useIsTickAtLimit from 'hooks/useIsTickAtLimit'
import { Bound } from 'state/mint/v3/actions'
const LinkRow = styled(Link)`
align-items: center;
@@ -24,6 +26,9 @@ const LinkRow = styled(Link)`
display: flex;
cursor: pointer;
user-select: none;
display: flex;
flex-direction: column;
justify-content: space-between;
color: ${({ theme }) => theme.text1};
margin: 8px 0;
@@ -32,25 +37,23 @@ const LinkRow = styled(Link)`
font-weight: 500;
background-color: ${({ theme }) => theme.bg1};
&:first-of-type {
margin: 0 0 8px 0;
}
&:last-of-type {
margin: 8px 0 0 0;
}
& > div:not(:first-child) {
text-align: right;
text-align: center;
}
:hover {
background-color: ${({ theme }) => theme.bg2};
}
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
flex-direction: row;
/* flex-direction: row; */
}
${({ theme }) => theme.mediaWidth.upToSmall`
flex-direction: column;
row-gap: 24px;
row-gap: 12px;
`};
`
@@ -70,11 +73,14 @@ const RangeLineItem = styled(DataLineItem)`
display: flex;
flex-direction: row;
align-items: center;
justify-self: flex-end;
margin-top: 4px;
width: 100%;
${({ theme }) => theme.mediaWidth.upToSmall`
flex-direction: column;
row-gap: 4px;
background-color: ${({ theme }) => theme.bg2};
border-radius: 12px;
padding: 8px 0;
`};
`
@@ -97,6 +103,9 @@ const ExtentsText = styled.span`
color: ${({ theme }) => theme.text3};
font-size: 14px;
margin-right: 4px;
${({ theme }) => theme.mediaWidth.upToSmall`
display: none;
`};
`
const PrimaryPositionIdData = styled.div`
@@ -117,7 +126,7 @@ const DataText = styled.div`
`};
`
export interface PositionListItemProps {
interface PositionListItemProps {
positionDetails: PositionDetails
}
@@ -201,6 +210,8 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr
return undefined
}, [liquidity, pool, tickLower, tickUpper])
const tickAtLimit = useIsTickAtLimit(feeAmount, tickLower, tickUpper)
// prices
const { priceLower, priceUpper, quote, base } = getPriceOrderingFromPositionForUI(position)
@@ -216,7 +227,7 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr
return (
<LinkRow to={positionSummaryLink}>
<RowFixed>
<RowBetween>
<PrimaryPositionIdData>
<DoubleCurrencyLogo currency0={currencyBase} currency1={currencyQuote} size={18} margin />
<DataText>
@@ -230,7 +241,7 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr
</Badge>
</PrimaryPositionIdData>
<RangeBadge removed={removed} inRange={!outOfRange} />
</RowFixed>
</RowBetween>
{priceLower && priceUpper ? (
<RangeLineItem>
@@ -239,23 +250,23 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr
<Trans>Min: </Trans>
</ExtentsText>
<Trans>
{formatPrice(priceLower, 5)} <HoverInlineText text={currencyQuote?.symbol} /> per{' '}
<HoverInlineText text={currencyBase?.symbol ?? ''} />
{formatTickPrice(priceLower, tickAtLimit, Bound.LOWER)} <HoverInlineText text={currencyQuote?.symbol} />{' '}
per <HoverInlineText text={currencyBase?.symbol ?? ''} />
</Trans>
</RangeText>{' '}
<HideSmall>
<DoubleArrow></DoubleArrow>{' '}
</HideSmall>
<SmallOnly>
<DoubleArrow></DoubleArrow>{' '}
<DoubleArrow></DoubleArrow>{' '}
</SmallOnly>
<RangeText>
<ExtentsText>
<Trans>Max:</Trans>
</ExtentsText>
<Trans>
{formatPrice(priceUpper, 5)} <HoverInlineText text={currencyQuote?.symbol} /> per{' '}
<HoverInlineText maxCharacters={10} text={currencyBase?.symbol} />
{formatTickPrice(priceUpper, tickAtLimit, Bound.UPPER)} <HoverInlineText text={currencyQuote?.symbol} />{' '}
per <HoverInlineText maxCharacters={10} text={currencyBase?.symbol} />
</Trans>
</RangeText>
</RangeLineItem>

View File

@@ -1,4 +1,4 @@
import React, { useState, useCallback, useContext, ReactNode } from 'react'
import { useState, useCallback, useContext, ReactNode } from 'react'
import { Position } from '@uniswap/v3-sdk'
import { LightCard } from 'components/Card'
import { AutoColumn } from 'components/Column'
@@ -12,19 +12,23 @@ import { Currency } from '@uniswap/sdk-core'
import RateToggle from 'components/RateToggle'
import DoubleCurrencyLogo from 'components/DoubleLogo'
import RangeBadge from 'components/Badge/RangeBadge'
import { ThemeContext } from 'styled-components'
import { ThemeContext } from 'styled-components/macro'
import JSBI from 'jsbi'
import { Bound } from 'state/mint/v3/actions'
import { formatTickPrice } from 'utils/formatTickPrice'
export const PositionPreview = ({
position,
title,
inRange,
baseCurrencyDefault,
ticksAtLimit,
}: {
position: Position
title?: ReactNode
inRange: boolean
baseCurrencyDefault?: Currency | undefined
ticksAtLimit: { [bound: string]: boolean | undefined }
}) => {
const theme = useContext(ThemeContext)
@@ -121,7 +125,11 @@ export const PositionPreview = ({
<TYPE.main fontSize="12px">
<Trans>Min Price</Trans>
</TYPE.main>
<TYPE.mediumHeader textAlign="center">{`${priceLower.toSignificant(5)}`}</TYPE.mediumHeader>
<TYPE.mediumHeader textAlign="center">{`${formatTickPrice(
priceLower,
ticksAtLimit,
Bound.LOWER
)}`}</TYPE.mediumHeader>
<TYPE.main textAlign="center" fontSize="12px">
<Trans>
{quoteCurrency.symbol} per {baseCurrency.symbol}
@@ -138,7 +146,11 @@ export const PositionPreview = ({
<TYPE.main fontSize="12px">
<Trans>Max Price</Trans>
</TYPE.main>
<TYPE.mediumHeader textAlign="center">{`${priceUpper.toSignificant(5)}`}</TYPE.mediumHeader>
<TYPE.mediumHeader textAlign="center">{`${formatTickPrice(
priceUpper,
ticksAtLimit,
Bound.UPPER
)}`}</TYPE.mediumHeader>
<TYPE.main textAlign="center" fontSize="12px">
<Trans>
{quoteCurrency.symbol} per {baseCurrency.symbol}

View File

@@ -1,7 +1,7 @@
import React, { useContext } from 'react'
import { useContext } from 'react'
import styled from 'styled-components/macro'
import { AutoColumn } from '../Column'
import { ThemeContext } from 'styled-components'
import { ThemeContext } from 'styled-components/macro'
import { TYPE } from '../../theme'
const Wrapper = styled(AutoColumn)`

View File

@@ -1,4 +1,4 @@
import React, { ReactNode, useCallback, useState } from 'react'
import { ReactNode, useCallback, useState } from 'react'
import styled from 'styled-components/macro'
import Tooltip from '../Tooltip'

View File

@@ -0,0 +1,35 @@
import React from 'react'
import { ButtonOutlined } from 'components/Button'
import { AutoRow } from 'components/Row'
import { TYPE } from 'theme'
import styled from 'styled-components/macro'
import { Trans } from '@lingui/macro'
import ReactGA from 'react-ga'
const Button = styled(ButtonOutlined).attrs(() => ({
padding: '8px',
$borderRadius: '8px',
}))`
color: ${({ theme }) => theme.text1};
flex: 1;
`
export default function PresetsButtons({ setFullRange }: { setFullRange: () => void }) {
return (
<AutoRow gap="4px" width="auto">
<Button
onClick={() => {
setFullRange()
ReactGA.event({
category: 'Liquidity',
action: 'Full Range Clicked',
})
}}
>
<TYPE.body fontSize={12}>
<Trans>Full Range</Trans>
</TYPE.body>
</Button>
</AutoRow>
)
}

View File

@@ -1,8 +1,9 @@
import { Trans } from '@lingui/macro'
import React from 'react'
import { Currency, Price, Token } from '@uniswap/sdk-core'
import StepCounter from 'components/InputStepCounter/InputStepCounter'
import { RowBetween } from 'components/Row'
import { AutoColumn } from 'components/Column'
import { Bound } from 'state/mint/v3/actions'
// currencyA is the base token
export default function RangeSelector({
@@ -17,6 +18,7 @@ export default function RangeSelector({
currencyA,
currencyB,
feeAmount,
ticksAtLimit,
}: {
priceLower?: Price<Token, Token>
priceUpper?: Price<Token, Token>
@@ -29,6 +31,7 @@ export default function RangeSelector({
currencyA?: Currency | null
currencyB?: Currency | null
feeAmount?: number
ticksAtLimit: { [bound in Bound]?: boolean | undefined }
}) {
const tokenA = (currencyA ?? undefined)?.wrapped
const tokenB = (currencyB ?? undefined)?.wrapped
@@ -38,31 +41,37 @@ export default function RangeSelector({
const rightPrice = isSorted ? priceUpper : priceLower?.invert()
return (
<RowBetween>
<StepCounter
value={leftPrice?.toSignificant(5) ?? ''}
onUserInput={onLeftRangeInput}
width="48%"
decrement={isSorted ? getDecrementLower : getIncrementUpper}
increment={isSorted ? getIncrementLower : getDecrementUpper}
feeAmount={feeAmount}
label={leftPrice ? `${currencyB?.symbol}` : '-'}
title={<Trans>Min Price</Trans>}
tokenA={currencyA?.symbol}
tokenB={currencyB?.symbol}
/>
<StepCounter
value={rightPrice?.toSignificant(5) ?? ''}
onUserInput={onRightRangeInput}
width="48%"
decrement={isSorted ? getDecrementUpper : getIncrementLower}
increment={isSorted ? getIncrementUpper : getDecrementLower}
feeAmount={feeAmount}
label={rightPrice ? `${currencyB?.symbol}` : '-'}
tokenA={currencyA?.symbol}
tokenB={currencyB?.symbol}
title={<Trans>Max Price</Trans>}
/>
</RowBetween>
<AutoColumn gap="md">
<RowBetween>
<StepCounter
value={ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER] ? '0' : leftPrice?.toSignificant(5) ?? ''}
onUserInput={onLeftRangeInput}
width="48%"
decrement={isSorted ? getDecrementLower : getIncrementUpper}
increment={isSorted ? getIncrementLower : getDecrementUpper}
decrementDisabled={ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER]}
incrementDisabled={ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER]}
feeAmount={feeAmount}
label={leftPrice ? `${currencyB?.symbol}` : '-'}
title={<Trans>Min Price</Trans>}
tokenA={currencyA?.symbol}
tokenB={currencyB?.symbol}
/>
<StepCounter
value={ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER] ? '∞' : rightPrice?.toSignificant(5) ?? ''}
onUserInput={onRightRangeInput}
width="48%"
decrement={isSorted ? getDecrementUpper : getIncrementLower}
increment={isSorted ? getIncrementUpper : getDecrementLower}
incrementDisabled={ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER]}
decrementDisabled={ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER]}
feeAmount={feeAmount}
label={rightPrice ? `${currencyB?.symbol}` : '-'}
tokenA={currencyA?.symbol}
tokenB={currencyB?.symbol}
title={<Trans>Max Price</Trans>}
/>
</RowBetween>
</AutoColumn>
)
}

View File

@@ -1,5 +1,4 @@
import { Trans } from '@lingui/macro'
import React from 'react'
import { Currency } from '@uniswap/sdk-core'
import { ToggleElement, ToggleWrapper } from 'components/Toggle/MultiToggle'
@@ -23,10 +22,10 @@ export default function RateToggle({
<div style={{ width: 'fit-content', display: 'flex', alignItems: 'center' }} onClick={handleRateToggle}>
<ToggleWrapper width="fit-content">
<ToggleElement isActive={isSorted} fontSize="12px">
<Trans>{isSorted ? currencyA.symbol : currencyB.symbol} price</Trans>
<Trans>{isSorted ? currencyA.symbol : currencyB.symbol}</Trans>
</ToggleElement>
<ToggleElement isActive={!isSorted} fontSize="12px">
<Trans>{isSorted ? currencyB.symbol : currencyA.symbol} price</Trans>
<Trans>{isSorted ? currencyB.symbol : currencyA.symbol}</Trans>
</ToggleElement>
</ToggleWrapper>
</div>

View File

@@ -1,5 +1,4 @@
import { Trans } from '@lingui/macro'
import React from 'react'
import { Text } from 'rebass'
import { Currency } from '@uniswap/sdk-core'
import styled from 'styled-components/macro'
@@ -11,6 +10,12 @@ import QuestionHelper from '../QuestionHelper'
import { AutoRow } from '../Row'
import CurrencyLogo from '../CurrencyLogo'
const MobileWrapper = styled(AutoColumn)`
${({ theme }) => theme.mediaWidth.upToSmall`
display: none;
`};
`
const BaseWrapper = styled.div<{ disable?: boolean }>`
border: 1px solid ${({ theme, disable }) => (disable ? 'transparent' : theme.bg3)};
border-radius: 10px;
@@ -40,7 +45,7 @@ export default function CommonBases({
const bases = typeof chainId !== 'undefined' ? COMMON_BASES[chainId] ?? [] : []
return bases.length > 0 ? (
<AutoColumn gap="md">
<MobileWrapper gap="md">
<AutoRow>
<Text fontWeight={500} fontSize={14}>
<Trans>Common bases</Trans>
@@ -64,6 +69,6 @@ export default function CommonBases({
)
})}
</AutoRow>
</AutoColumn>
</MobileWrapper>
) : null
}

View File

@@ -1,6 +1,6 @@
import { Trans } from '@lingui/macro'
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
import React, { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react'
import { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react'
import { FixedSizeList } from 'react-window'
import { Text } from 'rebass'
import styled from 'styled-components/macro'
@@ -104,12 +104,14 @@ function CurrencyRow({
isSelected,
otherSelected,
style,
showCurrencyAmount,
}: {
currency: Currency
onSelect: () => void
isSelected: boolean
otherSelected: boolean
style: CSSProperties
showCurrencyAmount?: boolean
}) {
const { account } = useActiveWeb3React()
const key = currencyKey(currency)
@@ -141,9 +143,11 @@ function CurrencyRow({
</TYPE.darkGray>
</Column>
<TokenTags currency={currency} />
<RowFixed style={{ justifySelf: 'flex-end' }}>
{balance ? <Balance balance={balance} /> : account ? <Loader /> : null}
</RowFixed>
{showCurrencyAmount && (
<RowFixed style={{ justifySelf: 'flex-end' }}>
{balance ? <Balance balance={balance} /> : account ? <Loader /> : null}
</RowFixed>
)}
</MenuItem>
)
}
@@ -158,7 +162,7 @@ function BreakLineComponent({ style }: { style: CSSProperties }) {
const theme = useTheme()
return (
<FixedContentRow style={style}>
<LightGreyCard padding="8px 12px" borderRadius="8px">
<LightGreyCard padding="8px 12px" $borderRadius="8px">
<RowBetween>
<RowFixed>
<TokenListLogoWrapper src={TokenListLogo} />
@@ -189,6 +193,7 @@ export default function CurrencyList({
fixedListRef,
showImportView,
setImportToken,
showCurrencyAmount,
}: {
height: number
currencies: Currency[]
@@ -199,6 +204,7 @@ export default function CurrencyList({
fixedListRef?: MutableRefObject<FixedSizeList | undefined>
showImportView: () => void
setImportToken: (token: Token) => void
showCurrencyAmount?: boolean
}) {
const itemData: (Currency | BreakLine)[] = useMemo(() => {
if (otherListTokens && otherListTokens?.length > 0) {
@@ -237,13 +243,22 @@ export default function CurrencyList({
isSelected={isSelected}
onSelect={handleSelect}
otherSelected={otherSelected}
showCurrencyAmount={showCurrencyAmount}
/>
)
} else {
return null
}
},
[currencies.length, onCurrencySelect, otherCurrency, selectedCurrency, setImportToken, showImportView]
[
currencies.length,
onCurrencySelect,
otherCurrency,
selectedCurrency,
setImportToken,
showImportView,
showCurrencyAmount,
]
)
const itemKey = useCallback((index: number, data: typeof itemData) => {

View File

@@ -1,5 +1,5 @@
import { Currency, Token } from '@uniswap/sdk-core'
import React, { KeyboardEvent, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { KeyboardEvent, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import ReactGA from 'react-ga'
import { t, Trans } from '@lingui/macro'
import { FixedSizeList } from 'react-window'
@@ -48,6 +48,8 @@ interface CurrencySearchProps {
onCurrencySelect: (currency: Currency) => void
otherSelectedCurrency?: Currency | null
showCommonBases?: boolean
showCurrencyAmount?: boolean
disableNonToken?: boolean
showManageView: () => void
showImportView: () => void
setImportToken: (token: Token) => void
@@ -58,6 +60,8 @@ export function CurrencySearch({
onCurrencySelect,
otherSelectedCurrency,
showCommonBases,
showCurrencyAmount,
disableNonToken,
onDismiss,
isOpen,
showManageView,
@@ -203,7 +207,7 @@ export function CurrencySearch({
{({ height }) => (
<CurrencyList
height={height}
currencies={filteredSortedTokensWithETH}
currencies={disableNonToken ? filteredSortedTokens : filteredSortedTokensWithETH}
otherListTokens={filteredInactiveTokens}
onCurrencySelect={handleCurrencySelect}
otherCurrency={otherSelectedCurrency}
@@ -211,6 +215,7 @@ export function CurrencySearch({
fixedListRef={fixedList}
showImportView={showImportView}
setImportToken={setImportToken}
showCurrencyAmount={showCurrencyAmount}
/>
)}
</AutoSizer>

View File

@@ -1,5 +1,5 @@
import { Currency, Token } from '@uniswap/sdk-core'
import React, { useCallback, useEffect, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import useLast from '../../hooks/useLast'
import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
import Modal from '../Modal'
@@ -17,6 +17,8 @@ interface CurrencySearchModalProps {
onCurrencySelect: (currency: Currency) => void
otherSelectedCurrency?: Currency | null
showCommonBases?: boolean
showCurrencyAmount?: boolean
disableNonToken?: boolean
}
export enum CurrencyModalView {
@@ -33,6 +35,8 @@ export default function CurrencySearchModal({
selectedCurrency,
otherSelectedCurrency,
showCommonBases = false,
showCurrencyAmount = true,
disableNonToken = false,
}: CurrencySearchModalProps) {
const [modalView, setModalView] = useState<CurrencyModalView>(CurrencyModalView.manage)
const lastOpen = useLast(isOpen)
@@ -74,6 +78,8 @@ export default function CurrencySearchModal({
selectedCurrency={selectedCurrency}
otherSelectedCurrency={otherSelectedCurrency}
showCommonBases={showCommonBases}
showCurrencyAmount={showCurrencyAmount}
disableNonToken={disableNonToken}
showImportView={() => setModalView(CurrencyModalView.importToken)}
setImportToken={setImportToken}
showManageView={() => setModalView(CurrencyModalView.manage)}

View File

@@ -1,4 +1,4 @@
import React, { useState, useCallback } from 'react'
import { useState, useCallback } from 'react'
import styled from 'styled-components/macro'
import ReactGA from 'react-ga'
import { TYPE, CloseIcon } from 'theme'
@@ -149,7 +149,7 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
<ButtonPrimary
disabled={!confirmed}
altDisabledStyle={true}
borderRadius="20px"
$borderRadius="20px"
padding="10px 1rem"
onClick={handleAddList}
>

View File

@@ -1,4 +1,4 @@
import React, { CSSProperties } from 'react'
import { CSSProperties } from 'react'
import { Token } from '@uniswap/sdk-core'
import { AutoRow, RowFixed } from 'components/Row'
import { AutoColumn } from 'components/Column'

View File

@@ -1,5 +1,4 @@
import { TokenList } from '@uniswap/token-lists/dist/types'
import React from 'react'
import { Token, Currency } from '@uniswap/sdk-core'
import styled from 'styled-components/macro'
import { TYPE, CloseIcon } from 'theme'
@@ -34,10 +33,11 @@ const WarningWrapper = styled(Card)<{ highWarning: boolean }>`
const AddressText = styled(TYPE.blue)`
font-size: 12px;
word-break: break-all;
${({ theme }) => theme.mediaWidth.upToSmall`
font-size: 10px;
`}
`}
`
interface ImportProps {
@@ -109,7 +109,7 @@ export function ImportToken({ tokens, list, onBack, onDismiss, handleCurrencySel
</TYPE.small>
</RowFixed>
) : (
<WarningWrapper borderRadius="4px" padding="4px" highWarning={true}>
<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}>
@@ -125,7 +125,7 @@ export function ImportToken({ tokens, list, onBack, onDismiss, handleCurrencySel
<ButtonPrimary
altDisabledStyle={true}
borderRadius="20px"
$borderRadius="20px"
padding="10px 1rem"
onClick={() => {
tokens.map((token) => addToken(token))

View File

@@ -1,5 +1,5 @@
import { Trans } from '@lingui/macro'
import React, { useState } from 'react'
import { useState } from 'react'
import { PaddedColumn, Separator } from './styleds'
import { RowBetween } from 'components/Row'
import { ArrowLeft } from 'react-feather'

View File

@@ -1,36 +1,34 @@
import React, { memo, useCallback, useMemo, useRef, useState, useEffect } from 'react'
import { Settings, CheckCircle } from 'react-feather'
import { t, Trans } from '@lingui/macro'
import { TokenList } from '@uniswap/token-lists'
import Card from 'components/Card'
import { UNSUPPORTED_LIST_URLS } from 'constants/lists'
import { useListColor } from 'hooks/useColor'
import { useActiveWeb3React } from 'hooks/web3'
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { CheckCircle, Settings } from 'react-feather'
import ReactGA from 'react-ga'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import { usePopper } from 'react-popper'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import styled from 'styled-components/macro'
import { useFetchListCallback } from '../../hooks/useFetchListCallback'
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
import { TokenList } from '@uniswap/token-lists'
import { t, Trans } from '@lingui/macro'
import useTheme from '../../hooks/useTheme'
import useToggle from '../../hooks/useToggle'
import { acceptListUpdate, removeList, disableList, enableList } from '../../state/lists/actions'
import { useIsListActive, useAllLists, useActiveListUrls } from '../../state/lists/hooks'
import { ExternalLink, LinkStyledButton, TYPE, IconWrapper } from '../../theme'
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 listVersionLabel from '../../utils/listVersionLabel'
import { parseENSAddress } from '../../utils/parseENSAddress'
import uriToHttp from '../../utils/uriToHttp'
import { ButtonEmpty, ButtonPrimary } from '../Button'
import Column, { AutoColumn } from '../Column'
import ListLogo from '../ListLogo'
import Row, { RowFixed, RowBetween } from '../Row'
import { PaddedColumn, SearchInput, Separator, SeparatorDark } from './styleds'
import { useListColor } from 'hooks/useColor'
import useTheme from '../../hooks/useTheme'
import Row, { RowBetween, RowFixed } from '../Row'
import ListToggle from '../Toggle/ListToggle'
import Card from 'components/Card'
import { CurrencyModalView } from './CurrencySearchModal'
import { UNSUPPORTED_LIST_URLS } from 'constants/lists'
import { PaddedColumn, SearchInput, Separator, SeparatorDark } from './styleds'
const Wrapper = styled(Column)`
width: 100%;
height: 100%;
`
@@ -80,8 +78,9 @@ const StyledListUrlText = styled(TYPE.main)<{ active: boolean }>`
color: ${({ theme, active }) => (active ? theme.white : theme.text2)};
`
const RowWrapper = styled(Row)<{ bgColor: string; active: boolean }>`
const RowWrapper = styled(Row)<{ bgColor: string; active: boolean; hasActiveTokens: boolean }>`
background-color: ${({ bgColor, active, theme }) => (active ? bgColor ?? 'transparent' : theme.bg2)};
opacity: ${({ hasActiveTokens }) => (hasActiveTokens ? 1 : 0.4)};
transition: 200ms;
align-items: center;
padding: 1rem;
@@ -93,10 +92,18 @@ function listUrlRowHTMLId(listUrl: string) {
}
const ListRow = memo(function ListRow({ listUrl }: { listUrl: string }) {
const { chainId } = useActiveWeb3React()
const listsByUrl = useAppSelector((state) => state.lists.byUrl)
const dispatch = useAppDispatch()
const { current: list, pendingUpdate: pending } = listsByUrl[listUrl]
const activeTokensOnThisChain = useMemo(() => {
if (!list || !chainId) {
return 0
}
return list.tokens.reduce((acc, cur) => (cur.chainId === chainId ? acc + 1 : acc), 0)
}, [chainId, list])
const theme = useTheme()
const listColor = useListColor(list?.logoURI)
const isActive = useIsListActive(listUrl)
@@ -130,7 +137,7 @@ const ListRow = memo(function ListRow({ listUrl }: { listUrl: string }) {
action: 'Start Remove List',
label: listUrl,
})
if (window.prompt(`Please confirm you would like to remove this list by typing REMOVE`) === `REMOVE`) {
if (window.prompt(t`Please confirm you would like to remove this list by typing REMOVE`) === `REMOVE`) {
ReactGA.event({
category: 'Lists',
action: 'Confirm Remove List',
@@ -161,7 +168,13 @@ const ListRow = memo(function ListRow({ listUrl }: { listUrl: string }) {
if (!list) return null
return (
<RowWrapper active={isActive} bgColor={listColor} key={listUrl} id={listUrlRowHTMLId(listUrl)}>
<RowWrapper
active={isActive}
hasActiveTokens={activeTokensOnThisChain > 0}
bgColor={listColor}
key={listUrl}
id={listUrlRowHTMLId(listUrl)}
>
{list.logoURI ? (
<ListLogo size="40px" style={{ marginRight: '1rem' }} logoURI={list.logoURI} alt={`${list.name} list logo`} />
) : (
@@ -173,7 +186,7 @@ const ListRow = memo(function ListRow({ listUrl }: { listUrl: string }) {
</Row>
<RowFixed mt="4px">
<StyledListUrlText active={isActive} mr="6px">
<Trans>{list.tokens.length} tokens</Trans>
<Trans>{activeTokensOnThisChain} tokens</Trans>
</StyledListUrlText>
<StyledMenu ref={node as any}>
<ButtonEmpty onClick={toggle} ref={setReferenceElement} padding="0">
@@ -226,20 +239,29 @@ export function ManageLists({
setImportList: (list: TokenList) => void
setListUrl: (url: string) => void
}) {
const { chainId } = useActiveWeb3React()
const theme = useTheme()
const [listUrlInput, setListUrlInput] = useState<string>('')
const lists = useAllLists()
const tokenCountByListName = useMemo<Record<string, number>>(
() =>
Object.values(lists).reduce((acc, { current: list }) => {
if (!list) {
return acc
}
return {
...acc,
[list.name]: list.tokens.reduce((count: number, token) => (token.chainId === chainId ? count + 1 : count), 0),
}
}, {}),
[chainId, lists]
)
// sort by active but only if not visible
const activeListUrls = useActiveListUrls()
const [activeCopy, setActiveCopy] = useState<string[] | undefined>()
useEffect(() => {
if (!activeCopy && activeListUrls) {
setActiveCopy(activeListUrls)
}
}, [activeCopy, activeListUrls])
const handleInput = useCallback((e) => {
setListUrlInput(e.target.value)
@@ -258,30 +280,36 @@ export function ManageLists({
// only show loaded lists, hide unsupported lists
return Boolean(lists[listUrl].current) && !Boolean(UNSUPPORTED_LIST_URLS.includes(listUrl))
})
.sort((u1, u2) => {
const { current: l1 } = lists[u1]
const { current: l2 } = lists[u2]
.sort((listUrlA, listUrlB) => {
const { current: listA } = lists[listUrlA]
const { current: listB } = lists[listUrlB]
// first filter on active lists
if (activeCopy?.includes(u1) && !activeCopy?.includes(u2)) {
if (activeListUrls?.includes(listUrlA) && !activeListUrls?.includes(listUrlB)) {
return -1
}
if (!activeCopy?.includes(u1) && activeCopy?.includes(u2)) {
if (!activeListUrls?.includes(listUrlA) && activeListUrls?.includes(listUrlB)) {
return 1
}
if (l1 && l2) {
return l1.name.toLowerCase() < l2.name.toLowerCase()
if (listA && listB) {
if (tokenCountByListName[listA.name] > tokenCountByListName[listB.name]) {
return -1
}
if (tokenCountByListName[listA.name] < tokenCountByListName[listB.name]) {
return 1
}
return listA.name.toLowerCase() < listB.name.toLowerCase()
? -1
: l1.name.toLowerCase() === l2.name.toLowerCase()
: listA.name.toLowerCase() === listB.name.toLowerCase()
? 0
: 1
}
if (l1) return -1
if (l2) return 1
if (listA) return -1
if (listB) return 1
return 0
})
}, [lists, activeCopy])
}, [lists, activeListUrls, tokenCountByListName])
// temporary fetched list for import flow
const [tempList, setTempList] = useState<TokenList>()

View File

@@ -1,4 +1,4 @@
import React, { useRef, RefObject, useCallback, useState, useMemo } from 'react'
import { useRef, RefObject, useCallback, useState, useMemo } from 'react'
import Column from 'components/Column'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { PaddedColumn, Separator, SearchInput } from './styleds'

View File

@@ -1,34 +0,0 @@
import React from 'react'
import { Text } from 'rebass'
import styled from 'styled-components/macro'
import { RowFixed } from '../Row'
export const FilterWrapper = styled(RowFixed)`
padding: 8px;
background-color: ${({ theme }) => theme.bg2};
color: ${({ theme }) => theme.text1};
border-radius: 8px;
user-select: none;
& > * {
user-select: none;
}
:hover {
cursor: pointer;
}
`
export default function SortButton({
toggleSortOrder,
ascending,
}: {
toggleSortOrder: () => void
ascending: boolean
}) {
return (
<FilterWrapper onClick={toggleSortOrder}>
<Text fontSize={14} fontWeight={500}>
{ascending ? '↑' : '↓'}
</Text>
</FilterWrapper>
)
}

View File

@@ -1,43 +1,6 @@
import styled from 'styled-components/macro'
import { AutoColumn } from '../Column'
import { RowBetween, RowFixed } from '../Row'
export const ModalInfo = styled.div`
${({ theme }) => theme.flexRowNoWrap}
align-items: center;
padding: 1rem 1rem;
margin: 0.25rem 0.5rem;
justify-content: center;
flex: 1;
user-select: none;
`
export const StyledMenu = styled.div`
display: flex;
justify-content: center;
align-items: center;
position: relative;
border: none;
`
export const PopoverContainer = styled.div<{ show: boolean }>`
z-index: 100;
visibility: ${(props) => (props.show ? 'visible' : 'hidden')};
opacity: ${(props) => (props.show ? 1 : 0)};
transition: visibility 150ms linear, opacity 150ms linear;
background: ${({ theme }) => theme.bg2};
border: 1px solid ${({ theme }) => theme.bg3};
box-shadow: 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);
color: ${({ theme }) => theme.text2};
border-radius: 0.5rem;
padding: 1rem;
display: grid;
grid-template-rows: 1fr;
grid-gap: 8px;
font-size: 1rem;
text-align: left;
top: 80px;
`
import { RowBetween } from '../Row'
export const TextDot = styled.div`
height: 3px;
@@ -46,10 +9,6 @@ export const TextDot = styled.div`
border-radius: 50%;
`
export const FadedSpan = styled(RowFixed)`
color: ${({ theme }) => theme.primary1};
font-size: 14px;
`
export const Checkbox = styled.input`
border: 1px solid ${({ theme }) => theme.red3};
height: 20px;

View File

@@ -1,9 +1,9 @@
import { t, Trans } from '@lingui/macro'
import React, { useContext, useRef, useState } from 'react'
import { useContext, useRef, useState } from 'react'
import { Settings, X } from 'react-feather'
import ReactGA from 'react-ga'
import { Text } from 'rebass'
import styled, { ThemeContext } from 'styled-components'
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'
@@ -173,7 +173,7 @@ export default function SettingsTab({ placeholderSlippage }: { placeholderSlippa
</AutoColumn>
</ModalContentWrapper>
</Modal>
<StyledMenuButton onClick={toggle} id="open-settings-dialog-button">
<StyledMenuButton onClick={toggle} id="open-settings-dialog-button" aria-label={t`Transaction Settings`}>
<StyledMenuIcon />
{expertMode ? (
<EmojiWrapper>

View File

@@ -1,4 +1,4 @@
import React, { useCallback } from 'react'
import { useCallback } from 'react'
import styled from 'styled-components/macro'
const StyledRangeInput = styled.input<{ size: number }>`

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