Compare commits

...

84 Commits

Author SHA1 Message Date
Jesse
4ba6275b71 fix: add celo gas override (#4147)
fix: add celo gas override to circumvent 'out of gas' error from multicall
2022-07-20 14:48:53 -04:00
Rachel-Eichenberger
17c7a9ee9d fix: increase Polygon gas limit (#3882)
* Update graph link

* Add Gas over ride temp for Polygon

* removal of personal tweaks

* Update index.tsx

* reset to original file

* missing EOL

* Update useClientSideV3Trade.ts

* remove space
2022-07-20 11:59:44 -04:00
Sam Chen
0e36944b23 refactor: clean floating Route (#4144) 2022-07-20 09:47:42 -04:00
Vignesh Mohankumar
5050fe7b06 fix: unused onClickOutside reference (#4140) 2022-07-19 17:54:06 -04:00
Vignesh Mohankumar
80b965f2ca fix: sync chain query parameter (#4019)
* replaceURLChain

* reorder stuff

* don't use usePrevious for previousChainId

* remove the replace param call in promise

* variable names

* comment

* confirm isActive

* wrong place for isActive

* change ret type

* add comments

* check if not previous chain id
2022-07-19 21:42:52 +00:00
Vignesh Mohankumar
417e940c0a test: fix swap test flake (#4135)
* remove all the funky logic

* clear stuff

* uncomment some tests

* remove expert mode tests

* skip these tests again, smh
2022-07-19 16:42:45 -04:00
Sam Chen
c42a5ccf26 chore: access router data with hooks (#4121)
* chore: access router data with hooks

* chore: clean RouteComponentProps

* chore: use children instead of render

* add import
2022-07-19 16:14:16 -04:00
Vignesh Mohankumar
7ba9b1faf6 fix: don't toggle desktop NetworkSelector on click (#4134)
fix: don't NetworkSelector onClick on desktop
2022-07-19 14:35:24 -04:00
Vignesh Mohankumar
5d43d08ff3 build: lock jest-styled-components@7.0.7 (#4132) 2022-07-18 21:42:07 -04:00
lynn
85625d09f0 fix: increase celo blocksPerFetch to 5 to improve interface performance (#4130)
* init commit

* revert yarn.lock changes

* update test snapshots
2022-07-18 18:25:14 -04:00
Vignesh Mohankumar
d85f1f44b9 build: change project name to @uniswap/interface (#4125) 2022-07-18 17:41:32 -04:00
lynn
f41580e43c feat: implement connect wallet category events (#4111)
* init commit

* wallet connected event init commit

* add received_swap_quote event property

* add page context, connect wallet event log

* add received_swap_quote property

* fix typo

* respond to cmcewen comments

* respond to vm comments

* move trace to app.tsx from header

* respond to vm comments
2022-07-18 17:26:29 -04:00
Vignesh Mohankumar
47fe9911fa chore: move prettier, jest-styled-components to devDependencies (#4128)
* change package

* yarn.lock
2022-07-18 16:32:03 -04:00
Noah Zinsmeister
e0f6d82d6c feat: enable 1bp optimism fee tier (#4124)
enable new optimism fee tier
2022-07-18 10:48:28 -04:00
Sam Chen
fb691cf17b fix: catch vibrant failure (#4123)
fix: catch CORS error
2022-07-18 10:05:56 -04:00
Vignesh Mohankumar
f596293b6c build: don't fail cypress on unhandled exception (#4122) 2022-07-18 09:46:31 -04:00
Sam Chen
8012789f69 fix: fixes Popover arrow positioning (#4119)
fix: fix arrow position
2022-07-17 11:22:50 -04:00
Sam Chen
4013743473 chore: upgrades react-router-dom, fixes dev-mode linking (#4115)
* fix: stale route

* fix: add e2e test

* fix: update e2e test
2022-07-17 11:15:04 -04:00
Jesse
e9ef3193ab refactor: remaining changes from the large celo merge (#4088)
* refactor: useUSDCValue -> useStablecoinValue

* refactor: use the isCelo() helper

* refactor: remove unneeded white space
2022-07-15 10:36:19 -04:00
Vignesh Mohankumar
6a1506ade6 build: Revert "build: pause deploy" (#4107)
* Revert "build: pause deploy (#4102)"

This reverts commit 3a1ea3df85.

* prettier
2022-07-14 12:19:23 -04:00
cartcrom
839d4ac8e2 refactor: adding safe getter for ChainInfo (#4110)
* replaced CHAIN_INFO access with a function call
* updated CTACard tests to work with getChainInfo
* updated typechecking, removed console.log
2022-07-14 11:52:37 -04:00
Vignesh Mohankumar
29fdcb80f6 build: upgrade prettier to v2.7.1 (#4109)
* style: prettier based on v2.2

* 2.7.1 instead?

* npx

* ^
2022-07-14 11:28:51 -04:00
lynn
817ea44e8d fix: update styled-components in package.json to latest to remove react invalid hook call warnings (#4103)
* fix warning vig found by updating styled-components

* revert unnecessary yarn.lock changes

* reduce unnecessary changes

* dedup

* manual fix and dedup of yarn.lock

* manually dedup @emotion/is-prop-valid

* update snapshot tests
2022-07-14 10:15:46 -04:00
Vignesh Mohankumar
3a1ea3df85 build: pause deploy (#4102) 2022-07-13 21:24:58 -04:00
Vignesh Mohankumar
2667a897a1 chore(web3-react): fix connectEagerly for MetaMask mobile (#4101)
* chore(web3-react): fix connectEagerly for MetaMask mobile

* fix
2022-07-13 18:20:50 -04:00
lynn
65129604bd chore: upgrade to react 18 (#3992)
* chore: upgrade to react 18

* fix: update tests

* fix: fix lint issues and remove unnecessary react hooks testing library

* fix: add types for stricter typescript checks

* fix: fix additional typescript check issues

* fix: revert to prev commmit

* rebase

* rebase

* fix: fix lint issues and remove unnecessary react hooks testing library

* fix: add types for stricter typescript checks

* fix: fix additional typescript check issues

* rebase

* fix: rebase

* fix

* eslint fix

* fix: package.json changes

* fix: package.json changes

* fix yarn lock

* fix version package.json

* fix: downgrade react-router-dom to original

* fix: undo modification of .github/workflows/release.yaml

* fix: revert cypress testing version update

* rebase

* rebase

* fix: fix lint issues and remove unnecessary react hooks testing library

* fix: add types for stricter typescript checks

* fix: fix additional typescript check issues

* rebase

* chore: upgrade to react 18

* fix: update tests

* fix: fix lint issues and remove unnecessary react hooks testing library

* fix: add types for stricter typescript checks

* fix: fix additional typescript check issues

* fix

* eslint fix

* fix: package.json changes

* fix: package.json changes

* fix yarn lock

* fix version package.json

* fix: downgrade react-router-dom to original

* fix: undo modification of .github/workflows/release.yaml

* fix: revert cypress testing version update

* fix

* fix: error boundary change

* yarn.lock change

* fix: cypress tests finally passing due to zzmp redux multicall fix HOORAY

* undo service worker changes

* build: dedup lockfile

* yarn.lock + lint

* update snapshot tests

* checkpoint

* yarn.lock

* fix: fix type errors during build

* fixes

* fix yarn.lock

* dedup yarn

* fix: import react components explicitly instead of all of react

* dedup

* yarn.lock

* yarn.lock

* dedup

* yarn

* dedup

* dedupe use-sync-external-store

* fix build issues

* dedup use-sync-external-store

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>
2022-07-13 16:56:09 -04:00
lynn
4e0c9b36a0 feat: implement-page-viewed-event-for-all-main-pages-of-app (#4089)
* init commit: initial constants for pages, implement vote page viewed

* implement swap

* implement pool

* remove charts

* simplify shouldLogImpression
2022-07-13 16:16:42 -04:00
Vignesh Mohankumar
64cb9f3ff2 chore: updates web3-react, adds key for changing connector order (#4085)
* fix connectors changing

* update package

* add connection name

* rename file

* de-dupe

* cb wallet fix

* fix

* yarn change

* log the key

* re-order connections

* memoize the key

* some updates

* rm console

* prevent memory leak

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2022-07-13 15:44:42 -04:00
lynn
cb094a1f4b feat: implement token selector events (#4067)
* init commit

* add amplitude ts sdk to package.json

* add more comments and documentation

* respond to vm comments

* respond to cmcewen comments

* fix: remove unused constants

* init commit

* adapt to web

* add optional event properties to trace

* correct telemetry to analytics

* change telemetry to analytics in doc

* fix: respond to cmcewen comments + initialize analytics in app.tsx + add missing return statement

* init commit

* respond to zzmp comments

* add token selected event

* fixes

* eliminate unnecessary state

* respond to part of zzmp comments

* respond to zzmp comments round 2

* fixes

* respond to zzmp comments

* add imported token event and other fixes

* also log onKeyPress for suggested tokens

* respond to cmcewen comments
2022-07-13 15:43:51 -04:00
Zach Pomerantz
d05fefc231 test: enforce deps deduplication (#4097)
* build: use fewer babel versions

* build: dedup

* test: test deps dedups

* fix: test.yml

* fix: typo

* test: failing

* fix: dedup

* fix: dedup

* test: comment dedup tests

* chore: whitespace
2022-07-13 11:31:19 -04:00
Zach Pomerantz
3e1805a20f build: update caniuse-lite (#4093) 2022-07-13 10:16:21 -04:00
Zach Pomerantz
f58dfe1284 build: upgrade @typescript-eslint (#4095)
build: update @typescript-eslint
2022-07-13 10:14:53 -04:00
github-actions[bot]
f67b7f8b66 chore(i18n): new Crowdin translations (#4090) 2022-07-12 13:09:59 -10:00
Vignesh Mohankumar
869691d43f refactor: wallet specific Option components (#4065)
* refactor: wallet specific Option components

* fix

* fix

* fix coinbase wallet logic

* injected logic

* remove wallet.ts

* install metamask

* move all into InjectedOption

* fix mobile metamask

* wip

* more mocking

* more test fixes

* refactor

* more special casing

* isMetaMask

* simplify components

* fix imports

* fix coinbase wallet

* test fix

* fix connectors changing

* Revert "fix connectors changing"

This reverts commit 2acfe645ca.

* more to typescript logic instead of jsx
2022-07-12 18:33:24 -04:00
lynn
817d808ec5 feat: implement trace framework for analytics (#4060)
* init commit

* add amplitude ts sdk to package.json

* add more comments and documentation

* respond to vm comments

* respond to cmcewen comments

* fix: remove unused constants

* init commit

* adapt to web

* add optional event properties to trace

* correct telemetry to analytics

* change telemetry to analytics in doc

* fix: respond to cmcewen comments + initialize analytics in app.tsx + add missing return statement

* respond to zzmp comments

* fixes

* eliminate unnecessary state

* respond to part of zzmp comments

* respond to zzmp comments round 2

* fixes

* respond to zzmp comments
2022-07-12 16:43:37 -04:00
github-actions[bot]
aee1bce612 chore(i18n): new Crowdin translations (#4084)
chore(i18n): synchronize translations from crowdin [skip ci]

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-12 13:52:06 -04:00
Bruno Crosier
8b38a9c4e0 fix: add crossplatform prei18n-extract script (#3728)
* fix: 🐛 add crossplatform `prei18n-extract` script

* fix: 🚨 add newline

* Revert "fix: 🐛 add crossplatform `prei18n-extract` script"

This reverts commit 201bd2308a.

* build: 📦 add `shx` as dev dep, use it in `prei18n:extract` script

* fix: 🐛 use platform-specific commands for prei18n-extract
2022-07-12 18:51:24 +02:00
cartcrom
0a115fab17 fix: unsupported chain displays message instead of crash (#4054)
* made initial changes for pools page displaying w/ unsupported chains
* condensed styling
* added chain validation to CTACards and wrote tests for both CTAcards and Pools page
* linted changes
* switched from snapshot to text matching tests
* switched test to use check for text instead of testid
2022-07-12 12:02:02 -04:00
Kaylee George
882147b533 fix: revert "fix button jump on currency panel" (#4083)
fix padding
2022-07-12 11:43:04 -04:00
Jesse
eb06aef199 feat: add support for Celo (#3915)
* feat: Support for Celo

* fix: wrong condition

* combine celo and alfajores lists

* use celo erc20 representation

* fix: refactor infura.ts to networks.ts & add celo to rpc urls

* feature: add celo contract addresses
fix: remove celo from supported gas estimate chains until feature is available

* refactor: useUSDCPrice to useStablecoinPrice
fix: add celo to supported gas estimate chains

* fix: use unique factory address for getting pool address

* fix: darkmode background graident

* fix: removing a comment left behind

* fix: remove bad import

* fix: remove dead link until the Celo is live on info.uniswap.org

* fix: add asset to common bases & minor refactoring

* fix: celo info links point to root info.uniswap.org

* fix: change celo token bridge to portal

* fix: update redux-multicall to latest version

* refactor: for code readability

* fix: celo banner colors & remove unused alternative logo

* fix: change celo token list to hosted version

* fix: update celo banner colors

* fix: move celo to the bottom of the network selector list

* fix: dedup dependencies @uniswap/router-sdk @uniswap/v3-sdk

* fix: refactoring + move Celo above L2s

* fix: update celo contract addresses

* fix: update celo subgraph

* fix: update v3-sdk and smart-order-router versions

* fix: move Celo to the bottom of the network selector list

* fix: downgrade smart-order-router and add casting fix

* fix: downgrade smart-order-router and add casting fix

* fix: resolve Pool dependency

* fix: bridge chain id types

* fix: explorer link test

* fix: use quoter v2 ABI in useClientSideV3Trade fro Celo

* fix: update connection "infura_rpc" to networks

* fix: revert yarn.lock and force install

* fix: dedup router and v3 sdk

* refactor: mv quoter v2 to client side v3 trade

* build: dedup lockfile

* feature: add portal ether to common bases

* fix: add comment for chains that use QuoterV2

* fix: use token as native asset

* fix: supply correct factory address to getPoolAddress call & refactor nativeOnChain method

* feature: adjust celo tokens presetned

* fix: update celo explorer to celoscan

* fix: celo token casting

* fix: celo celo explorer it

* fix: celo chain info should be consistent with block explorer used.

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>
2022-07-12 16:48:03 +02:00
Vignesh Mohankumar
1b91e7ce30 fix: Revert "refactor: remove coinbase wallet resetState" (#4081)
Revert "refactor: remove coinbase wallet resetState (#4024)"

This reverts commit e36722ccb4.
2022-07-12 00:37:45 -04:00
Anas Yousef
535e670c63 refactor: remove hideRouteDiagram prop (#3763) 2022-07-11 20:30:16 -04:00
Bruno Crosier
0b4c77155e fix: NetworkSelect mobile toggle bug (#3698)
* fix windows dev environment bugs

* fix NetworkSelector toggle bug

* revert:  add `prei18n:extract` script back

this change to make the script Windows-compatible will be dealt with in a separate PR

* revert:  revert prettier endOfLine fix

Will be dealt with in a separate PR

* updates

Co-authored-by: Vignesh Mohankumar <vignesh@vigneshmohankumar.com>
Co-authored-by: Vignesh Mohankumar <me@vig.xyz>
2022-07-11 15:55:59 -04:00
Vignesh Mohankumar
e36722ccb4 refactor: remove coinbase wallet resetState (#4024)
* refactor: remove coinbase wallet resetState

* unused import

* bump web3-react

* rm unused

* bump everything
2022-07-11 13:49:49 -04:00
Ian Lapham
ec0b94a920 fix: Fix spacing type (#4073)
* fix: Fix spacing type

* prettier

Co-authored-by: Vignesh Mohankumar <me@vig.xyz>
2022-07-11 13:37:40 -04:00
Zach Pomerantz
d5eed8b15f build: wait 1m between release tests (#4077) 2022-07-11 13:24:01 -04:00
Zach Pomerantz
2447afc43e build: wait on passing cypress (#4076)
* build: wait on passing cypress

* build: video upload
2022-07-11 18:55:03 +02:00
Zach Pomerantz
8eef757f7f refactor: analytics initialization (#4070)
* refactor: analytics initialization

* fix: typings
2022-07-11 12:46:58 -04:00
Zach Pomerantz
b1c29b3bf1 build: resolve cypress matrix to single job (#4075)
* build: resolve cypress matrix to single job

* build: output on cypress-tests

* build: explicitly name matrix steps

* build: quote cypress string

* build: omit protocol

* build: test

* build: fix indentation

* build: test

* build: test

* build: test

* build: cleanup

* build: cleanup
2022-07-11 17:36:48 +02:00
Zach Pomerantz
b211c9f150 build: wait on tests (#4074) 2022-07-11 16:42:52 +02:00
lynn
66cae715f4 feat: implement initial setup of amplitude SDK (#4044)
* init commit

* add amplitude ts sdk to package.json

* add more comments and documentation

* respond to vm comments

* respond to cmcewen comments

* fix: remove unused constants

* fix: respond to cmcewen comments + initialize analytics in app.tsx + add missing return statement
2022-07-08 11:57:47 -04:00
github-actions[bot]
5a4a2782e8 chore(i18n): new Crowdin translations (#4045)
chore(i18n): synchronize translations from crowdin [skip ci]

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-07 13:03:44 -07:00
Vignesh Mohankumar
4e462ddbef refactor: extract Web3Provider hooks, create internal Connection representation (#4040)
* refactor: separate hooks file for Web3Provider

* move utils

* rename + comments

* rename Wallet enum to ConnectionType

* more wallet -> connectiontype

* more wallet -> connectiontype

* move hooks

* use Connection everywhere

* connector -> connection

* generic getConnection

* rename injected -> injectedConnection

* check connectionType

* rm unused
2022-07-07 15:17:49 -04:00
Zach Pomerantz
48a962a750 build: enable auto-release (#4058) 2022-07-07 11:56:03 -07:00
Zach Pomerantz
9a55402bdf build: add sleep in release (#4057) 2022-07-07 11:09:47 -07:00
lynn
663644553e feat: update privacy policy to include amplitude (#4046)
init commit
2022-07-06 18:15:12 -04:00
Vignesh Mohankumar
ceec3f0e65 feat: remove Tally Ho specific rendering (#4023)
feat: remove tally
2022-07-06 16:39:24 -04:00
Vignesh Mohankumar
904d1835d2 style: rename library to provider (#4038) 2022-07-06 12:36:54 -04:00
lynn
77366bf81b feat: configure blocks per fetch for l2s (#4028)
* init commit

* update redux multicall version to 1.1.5 in package.json

* fix respond to zzmp comments

* update optimism blocks per fetch to 15

* simplify

Co-authored-by: Lynn Yu <lynn.yu@uniswap.org>
2022-07-05 12:13:20 -04:00
Zach Pomerantz
cd8b048829 fix: deploy after pinning 2022-07-05 09:04:13 -07:00
Zach Pomerantz
d62596ecfc fix: release file 2022-07-05 08:52:54 -07:00
Zach Pomerantz
2b496c62f1 build: deploy without needing check suite 2022-07-05 08:50:28 -07:00
Zach Pomerantz
a3c77708e2 build: deploy without waiting on check suite 2022-07-05 08:49:11 -07:00
Zach Pomerantz
b9e8139699 fix: alter headers on cached response (#4032)
* fix: alter headers on cached response

* test: deflake
2022-07-05 08:31:06 -07:00
Zach Pomerantz
c82dcabd19 build: disable scheduled release (#4022) 2022-07-01 15:26:42 -07:00
Zach Pomerantz
26b37d5274 build: fix await-check-suite with (#4021)
* build: fix await-check-suites

* build: fix await-check-suite with
2022-07-01 15:12:51 -07:00
Zach Pomerantz
d5c464b26b build: fix await-check-suites (#4020) 2022-07-01 15:10:08 -07:00
Zach Pomerantz
c48d4c5425 build: simplify actions (#4014)
* build: simplify actions

* build: add checkout

* build: fix path

* build: fix all paths

* build: missing steps

* build: update build name

* build: rename action

* build: add shell

* build: formalize lint

* build: cleanup

* build: update release

* build: comment workflows
2022-07-01 15:05:37 -07:00
Kaylee George
b28cd9c8b0 fix: token button jump (#4017)
* fix token button jump

* Update index.tsx

* move inline style to CSS
2022-07-01 16:18:46 -04:00
Vignesh Mohankumar
071017879c feat: don't allow disconnect/change on injected mobile wallets (#4015)
* feat: don't allow disconnect/change on injected mobile wallets

* new variable
2022-07-01 13:57:15 -04:00
github-actions[bot]
5535c83db5 chore(i18n): new Crowdin translations (#4013)
chore(i18n): synchronize translations from crowdin [skip ci]

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-07-01 09:03:06 -07:00
Andrii Bodnar
8f9eccabaf fix: Crowdin translations download (#3927) 2022-07-01 09:00:20 -07:00
Vignesh Mohankumar
322cdaf888 refactor: rm useActiveWeb3React (#4004)
* rm activeweb3react

* wrap in web3provider?
2022-06-30 16:38:02 -07:00
Vignesh Mohankumar
edcdbfd8f5 feat: disconnect for coinbase wallet (#3993)
* feat: disconnect for coinbase wallet

* change isActive logic

* remove logs

* reset state for coinbase wallet

* active -> isActive

* rm data-cy
2022-06-30 18:04:42 -04:00
Vignesh Mohankumar
d36f13d7e2 fix: resolves network switch race condition (#4005)
* don't need undo logic

* maybe need to return connector

* make sure the urlChainId doesn't equal existing chainId

* fix network switching

* add back revert logic

* undo changes to switchChain

* move revert logic into the hook
2022-06-30 11:51:23 -05:00
Vignesh Mohankumar
8548b33bd9 feat: show injected options in wallet browsers (#3995)
* feat: show injected options in wallet browsers

* initial testing

* more mocking

* mock more

* mobile tests

* updates

* add data test

* finally got the mock to work

* WORKING

* uncomment

* rm console.log

* fix

* check length

* fix tests to use useWeb3React

* rm

* rename tests
2022-06-30 12:26:22 -04:00
Vignesh Mohankumar
0e148bb1b3 style: use data-testid in cypress test (#4001)
* use data-testid

* findByTestId

test -> it

add types

temp

rm exclude

get -> find

* Revert "findByTestId"

This reverts commit 1e1c483ef9.

* rm some test ids

* fix
2022-06-29 16:57:21 -04:00
Vignesh Mohankumar
9ddb37a982 chore: move cypress to devDependencies (#4002) 2022-06-29 13:41:20 -07:00
Zach Pomerantz
63227cd2c5 build: dedup lockfile (#3985)
* build: dedup ethers

* build: dedup token-lists

* build: dedup deps

* build: clean deps
2022-06-29 09:43:01 -07:00
Zach Pomerantz
dc38dc1c10 build: disable release (#3994) 2022-06-28 18:03:40 -07:00
Zach Pomerantz
2c6757ff62 fix: set content-type on cached document (#3990)
* fix: set content-type on cached document

* fix: delete old content-types

* fix: avoid immutable headers

* test: content-type

* fix: do not destructure response

* test: serve from cache with vercel

* fix: inject cache marker into body
2022-06-28 16:31:05 -07:00
Zach Pomerantz
0d03b09ae9 build: sleep for 10m after pinning (#3991) 2022-06-28 15:40:33 -07:00
Jordan Frankfurt
566da07448 feat(risk): cache risk check with ttl (#3965) 2022-06-24 11:11:32 -05:00
Vignesh Mohankumar
31a3840b1f feat: fix metamask mobile browser connection (#3964)
* fix metamask

* forceActivate

* remove forceActivate

* unused change
2022-06-23 16:50:04 -04:00
250 changed files with 7567 additions and 5721 deletions

View File

@@ -1,3 +1,4 @@
REACT_APP_AMPLITUDE_KEY="1c694b28cd089acc2c386d518f93a775"
REACT_APP_INFURA_KEY="099fc58e0de9451d80b18d7c74caa7c1"
REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
REACT_APP_GOOGLE_ANALYTICS_ID="G-KDP9B6W4H8"

21
.github/actions/setup/action.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: Setup
runs:
using: composite
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 14
registry-url: https://registry.npmjs.org
cache: yarn
- uses: actions/cache@v3
id: install-cache
with:
path: node_modules/
key: ${{ runner.os }}-install-${{ hashFiles('**/yarn.lock') }}
- if: steps.install-cache.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile --ignore-scripts
shell: bash

View File

@@ -2,9 +2,9 @@ version: 2
updates:
- package-ecosystem: npm
# Files stored in repository root
directory: "/"
directory: '/'
schedule:
interval: "daily"
interval: 'daily'
allow:
- dependency-name: "@uniswap/token-lists"
- dependency-name: "@uniswap/default-token-list"
- dependency-name: '@uniswap/token-lists'
- dependency-name: '@uniswap/default-token-list'

View File

@@ -1,4 +1,4 @@
name: "Check PR Title"
name: Check PR Title
on:
pull_request_target:
@@ -8,8 +8,8 @@ on:
- synchronize
jobs:
check-pr-title:
name: Check PR Title
# Ensures that the PR title adheres to [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/).
conventional-commit:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v3.4.0

View File

@@ -2,34 +2,18 @@ name: Crowdin Download
on:
schedule:
- cron: '0 * * * *' # every hour we download translations and update the pr from crowdin
# Download translations every hour.
# This is not done as part of the build so that builds remain reproducible.
- cron: '0 * * * *'
# manual trigger
workflow_dispatch:
jobs:
download-translations:
name: Download translations
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 14
registry-url: https://registry.npmjs.org
cache: 'yarn'
- uses: actions/cache@v3
id: install-cache
with:
path: node_modules/
key: ${{ runner.os }}-install-${{ hashFiles('**/yarn.lock') }}
- if: steps.install-cache.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile --ignore-scripts
- uses: ./.github/actions/setup
- run: yarn i18n:extract
- name: Download Crowdin translations
@@ -41,8 +25,9 @@ jobs:
token: ${{ secrets.CROWDIN_PERSONAL_TOKEN_SECRET }}
source: 'src/locales/en-US.po'
translation: 'src/locales/%locale%.po'
create_pull_request: false
localization_branch_name: main
commit_message: "chore(i18n): synchronize translations from crowdin [skip ci]"
create_pull_request: true
pull_request_title: 'chore(i18n): new Crowdin translations'
localization_branch_name: l10n_crowdin
commit_message: 'chore(i18n): synchronize translations from crowdin [skip ci]'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -6,28 +6,11 @@ on:
- main
jobs:
synchronize-with-crowdin:
name: Upload sources to Crowdin
upload-sources:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 14
registry-url: https://registry.npmjs.org
cache: 'yarn'
- uses: actions/cache@v3
id: install-cache
with:
path: node_modules/
key: ${{ runner.os }}-install-${{ hashFiles('**/yarn.lock') }}
- if: steps.install-cache.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile --ignore-scripts
- uses: ./.github/actions/setup
- run: yarn i18n:extract
- name: Upload Crowdin sources

View File

@@ -1,45 +0,0 @@
name: Lint
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
run-linters:
name: Run linters
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 14
registry-url: https://registry.npmjs.org
cache: 'yarn'
- uses: actions/cache@v3
id: install-cache
with:
path: node_modules/
key: ${{ runner.os }}-install-${{ hashFiles('**/yarn.lock') }}
- if: steps.install-cache.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile --ignore-scripts
- name: Run eslint w/ autofix
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login == github.repository_owner }}
uses: wearerequired/lint-action@36c7e6689e80d785d27a22f71d970f3a3b4fcb70
with:
github_token: ${{ secrets.github_token }}
eslint: true
eslint_args: "-c .eslintrc.json"
auto_fix: true
- name: Run eslint
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.owner.login != github.repository_owner }}
run: yarn eslint .

View File

@@ -1,61 +1,58 @@
name: Release
on:
schedule:
- cron: '0 12 * * 1-4' # every day 12:00 UTC Monday-Thursday
- cron: '0 12 * * 1-4' # every day 12:00 UTC Monday-Thursday
# manual trigger
workflow_dispatch:
jobs:
bump_version:
name: Bump Version
wait-on-tests:
runs-on: ubuntu-latest
steps:
- id: unit-tests
uses: fountainhead/action-wait-for-check@v1.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
checkName: unit-tests
- id: cypress-tests
uses: fountainhead/action-wait-for-check@v1.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
checkName: cypress-tests
- if: steps.unit-tests.outputs.conclusion != 'success' || steps.cypress-tests.outputs.conclusion != 'success'
run: exit 1
tag:
needs: wait-on-tests
runs-on: ubuntu-latest
outputs:
new_tag: ${{ steps.github_tag_action.outputs.new_tag }}
changelog: ${{ steps.github_tag_action.outputs.changelog }}
new_tag: ${{ steps.github-tag-action.outputs.new_tag }}
changelog: ${{ steps.github-tag-action.outputs.changelog }}
steps:
- uses: actions/checkout@v3
- name: Bump version and push tag
id: github_tag_action
uses: mathieudutour/github-tag-action@331898d5052eedac9b15fec867b5ba66ebf9b692
- name: Bump and tag
id: github-tag-action
uses: mathieudutour/github-tag-action@v6.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
release_branches: .*
default_bump: false
default_bump: patch
create_release:
name: Create Release
release:
needs: tag
if: ${{ needs.tag.outputs.new_tag != null }}
runs-on: ubuntu-latest
needs: bump_version
if: ${{ needs.bump_version.outputs.new_tag != null }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 14
registry-url: https://registry.npmjs.org
cache: 'yarn'
- uses: actions/cache@v3
id: install-cache
with:
path: node_modules/
key: ${{ runner.os }}-install-${{ hashFiles('**/yarn.lock') }}
- if: steps.install-cache.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile --ignore-scripts
- uses: ./.github/actions/setup
- run: yarn prepare
- run: yarn build
- name: Pin to IPFS
id: upload
id: pinata
uses: anantaramdas/ipfs-pinata-deploy-action@39bbda1ce1fe24c69c6f57861b8038278d53688d
with:
pin-name: Uniswap ${{ needs.bump_version.outputs.new_tag }}
pin-name: Uniswap ${{ needs.tag.outputs.new_tag }}
path: './build'
pinata-api-key: ${{ secrets.PINATA_API_KEY }}
pinata-secret-api-key: ${{ secrets.PINATA_API_SECRET_KEY }}
@@ -69,10 +66,24 @@ jobs:
seeds: ${{ secrets.CRUST_SEEDS }}
- name: Convert CIDv0 to CIDv1
id: convert_cidv0
id: convert-cidv0
uses: uniswap/convert-cidv0-cidv1@v1.0.0
with:
cidv0: ${{ steps.upload.outputs.hash }}
cidv0: ${{ steps.pinata.outputs.hash }}
- uses: actions/cache@v3
id: cypress-cache
with:
path: /home/runner/.cache/Cypress
key: ${{ runner.os }}-cypress-${{ hashFiles('node_modules/cypress') }}
- if: steps.cypress-cache.outputs.cache-hit != 'true'
run: yarn cypress install
- uses: cypress-io/github-action@v4
with:
install: false
browser: chrome
config-file: cypress.release.config.ts
config: baseUrl=https://cloudflare-ipfs.com/ipfs/${{ steps.pinata.outputs.hash }}
- name: Update DNS with new IPFS hash
env:
@@ -82,20 +93,19 @@ jobs:
CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
uses: textileio/cloudflare-update-dnslink@0fe7b7a1ffc865db3a4da9773f0f987447ad5848
with:
cid: ${{ steps.upload.outputs.hash }}
cid: ${{ steps.pinata.outputs.hash }}
- name: Create GitHub Release
id: create_release
- name: Release
uses: actions/create-release@v1.1.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ needs.bump_version.outputs.new_tag }}
release_name: Release ${{ needs.bump_version.outputs.new_tag }}
tag_name: ${{ needs.tag.outputs.new_tag }}
release_name: Release ${{ needs.tag.outputs.new_tag }}
body: |
IPFS hash of the deployment:
- CIDv0: `${{ steps.upload.outputs.hash }}`
- CIDv1: `${{ steps.convert_cidv0.outputs.cidv1 }}`
- CIDv0: `${{ steps.pinata.outputs.hash }}`
- CIDv1: `${{ steps.convert-cidv0.outputs.cidv1 }}`
The latest release is always accessible via our alias to the Cloudflare IPFS gateway at [app.uniswap.org](https://app.uniswap.org).
@@ -105,8 +115,8 @@ jobs:
Your Uniswap settings are never remembered across different URLs.
IPFS gateways:
- https://${{ steps.convert_cidv0.outputs.cidv1 }}.ipfs.dweb.link/
- https://${{ steps.convert_cidv0.outputs.cidv1 }}.ipfs.cf-ipfs.com/
- [ipfs://${{ steps.upload.outputs.hash }}/](ipfs://${{ steps.upload.outputs.hash }}/)
- https://${{ steps.convert-cidv0.outputs.cidv1 }}.ipfs.dweb.link/
- https://${{ steps.convert-cidv0.outputs.cidv1 }}.ipfs.cf-ipfs.com/
- [ipfs://${{ steps.upload.outputs.hash }}/](ipfs://${{ steps.pinata.outputs.hash }}/)
${{ needs.bump_version.outputs.changelog }}
${{ needs.tag.outputs.changelog }}

View File

@@ -1,4 +1,4 @@
name: End-to-End Tests
name: Test
on:
push:
@@ -7,33 +7,44 @@ on:
pull_request:
branches:
- main
# manual trigger
workflow_dispatch:
jobs:
build:
name: Build
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: yarn lint
- uses: actions/setup-node@v3
with:
node-version: 14
registry-url: https://registry.npmjs.org
cache: 'yarn'
deps-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
# Test that deps satisfy both "fewer" and "highest" strategies. This is to ensure we are
# up-to-date ("highest") while avoiding increasing package size ("fewer").
# These are readonly (--list) and explicitly exclude packages which only satisfy one strategy.
- run: npx yarn-deduplicate --strategy=fewer --list --fail --exclude commander safe-buffer
- run: npx yarn-deduplicate --strategy=highest --list --fail --exclude commander safe-buffer
- uses: actions/cache@v3
id: install-cache
with:
path: node_modules/
key: ${{ runner.os }}-install-${{ hashFiles('**/yarn.lock') }}
- if: steps.install-cache.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile --ignore-scripts
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: yarn prepare
- run: yarn test
cypress-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: yarn prepare
- run: yarn build
- uses: actions/upload-artifact@v2
with:
name: build
@@ -45,35 +56,19 @@ jobs:
with:
path: /home/runner/.cache/Cypress
key: ${{ runner.os }}-cypress-${{ hashFiles('node_modules/cypress') }}
- if: steps.cypress-cache.outputs.cache-hit != 'true'
run: yarn cypress install
cypress-tests:
name: Run tests
cypress-test-matrix:
needs: cypress-build
runs-on: ubuntu-latest
needs: build
strategy:
fail-fast: false
matrix:
containers: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 14
registry-url: https://registry.npmjs.org
cache: 'yarn'
- uses: actions/cache@v3
id: install-cache
with:
path: node_modules/ # this should always be a cache hit, from install
key: ${{ runner.os }}-install-${{ hashFiles('**/yarn.lock') }}
- if: steps.install-cache.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile --ignore-scripts
- uses: ./.github/actions/setup
- uses: actions/download-artifact@v2
with:
@@ -85,7 +80,6 @@ jobs:
with:
path: /home/runner/.cache/Cypress
key: ${{ runner.os }}-cypress-${{ hashFiles('node_modules/cypress') }}
- if: steps.cypress-cache.outputs.cache-hit != 'true'
run: yarn cypress install
@@ -98,6 +92,12 @@ jobs:
record: true
parallel: true
env:
CI: false # disables lint checks when building
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Included as a single job to check against for cypress test success, as cypress runs in a matrix.
cypress-tests:
needs: cypress-test-matrix
runs-on: ubuntu-latest
steps:
- run: echo 'Finished cypress tests https\://dashboard.cypress.io/projects/yp82ef'

View File

@@ -1,35 +0,0 @@
name: Unit Tests
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
unit-tests:
name: Run tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 14
registry-url: https://registry.npmjs.org
cache: 'yarn'
- uses: actions/cache@v3
id: install-cache
with:
path: node_modules/
key: ${{ runner.os }}-install-${{ hashFiles('**/yarn.lock') }}
- if: steps.install-cache.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile --ignore-scripts
- run: yarn prepare
- run: yarn test

View File

@@ -48,4 +48,4 @@ The Uniswap Interface supports swapping, adding liquidity, removing liquidity an
## Accessing Uniswap V1
The Uniswap V1 interface for mainnet and testnets is accessible via IPFS gateways
linked from the [v1.0.0 release](https://github.com/Uniswap/uniswap-interface/releases/tag/v1.0.0).
linked from the [v1.0.0 release](https://github.com/Uniswap/uniswap-interface/releases/tag/v1.0.0).

View File

@@ -0,0 +1,8 @@
import { defineConfig } from 'cypress'
export default defineConfig({
projectId: 'yp82ef',
e2e: {
specPattern: 'cypress/release.ts',
},
})

8
cypress/e2e/link.test.ts Normal file
View File

@@ -0,0 +1,8 @@
// see https://github.com/Uniswap/interface/pull/4115
describe('Link', () => {
it('should update route', () => {
cy.visit('/')
cy.get('[data-cy="pool-nav-link"]').click()
cy.get('[data-cy="join-pool-button"]').should('exist')
})
})

View File

@@ -1,5 +1,5 @@
describe('Swap', () => {
beforeEach(() => {
before(() => {
cy.visit('/swap')
})
@@ -11,73 +11,43 @@ describe('Swap', () => {
})
it('can enter an amount into input', () => {
cy.get('#swap-currency-input .token-amount-input')
.clear()
.type('0.001', { delay: 200 })
.should('have.value', '0.001')
cy.get('#swap-currency-input .token-amount-input').clear().type('0.001').should('have.value', '0.001')
})
it('zero swap amount', () => {
cy.get('#swap-currency-input .token-amount-input').clear().type('0.0', { delay: 200 }).should('have.value', '0.0')
cy.get('#swap-currency-input .token-amount-input').clear().type('0.0').should('have.value', '0.0')
})
it('invalid swap amount', () => {
cy.get('#swap-currency-input .token-amount-input').clear().type('\\', { delay: 200 }).should('have.value', '')
cy.get('#swap-currency-input .token-amount-input').clear().type('\\').should('have.value', '')
})
it('can enter an amount into output', () => {
cy.get('#swap-currency-output .token-amount-input').type('0.001', { delay: 200 }).should('have.value', '0.001')
cy.get('#swap-currency-output .token-amount-input').clear().type('0.001').should('have.value', '0.001')
})
it('zero output amount', () => {
cy.get('#swap-currency-output .token-amount-input').type('0.0', { delay: 200 }).should('have.value', '0.0')
cy.get('#swap-currency-output .token-amount-input').clear().type('0.0').should('have.value', '0.0')
})
it.skip('can swap ETH for DAI', () => {
cy.get('#swap-currency-output .open-currency-select-button').click()
cy.get('.token-item-0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735').should('be.visible')
cy.get('.token-item-0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735').click({ force: true })
cy.get('#swap-currency-input .token-amount-input').should('be.visible')
cy.get('#swap-currency-input .token-amount-input').type('0.001', { force: true, delay: 200 })
cy.get('.token-item-0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735').click()
cy.get('#swap-currency-input .token-amount-input').clear().type('0.0000001')
cy.get('#swap-currency-output .token-amount-input').should('not.equal', '')
cy.get('#swap-button').click()
cy.get('#confirm-swap-or-send').should('contain', 'Confirm Swap')
cy.get('[data-cy="confirmation-close-icon"]').click()
})
it.skip('add a recipient does not exist unless in expert mode', () => {
it('add a recipient does not exist unless in expert mode', () => {
cy.get('#add-recipient-button').should('not.exist')
})
it('ETH to wETH is same value (wrapped swaps have no price impact)', () => {
it.skip('ETH to wETH is same value (wrapped swaps have no price impact)', () => {
cy.get('#swap-currency-output .open-currency-select-button').click()
cy.get('.token-item-0xc778417E063141139Fce010982780140Aa0cD5Ab').click({ force: true })
cy.get('#swap-currency-input .token-amount-input').type('0.01', { force: true, delay: 100 })
cy.get('.token-item-0xc778417E063141139Fce010982780140Aa0cD5Ab').click()
cy.get('#swap-currency-input .token-amount-input').clear().type('0.01')
cy.get('#swap-currency-output .token-amount-input').should('have.value', '0.01')
})
describe('expert mode', () => {
beforeEach(() => {
cy.window().then((win) => {
cy.stub(win, 'prompt').returns('confirm')
})
cy.get('#open-settings-dialog-button').click()
cy.get('#toggle-expert-mode-button').click()
cy.get('#confirm-expert-mode').click()
})
it.skip('add a recipient is visible', () => {
cy.get('#add-recipient-button').should('be.visible')
})
it.skip('add a recipient', () => {
cy.get('#add-recipient-button').click()
cy.get('#recipient').should('exist')
})
it.skip('remove recipient', () => {
cy.get('#add-recipient-button').click()
cy.get('#remove-recipient-button').click()
cy.get('#recipient').should('not.exist')
})
})
})

View File

@@ -6,25 +6,25 @@ describe('Wallet', () => {
})
it('displays account details', () => {
cy.get('#web3-status-connected').contains(TEST_ADDRESS_NEVER_USE_SHORTENED).click()
cy.get('[data-testid=web3-status-connected]').contains(TEST_ADDRESS_NEVER_USE_SHORTENED).click()
})
it('displays account view in wallet modal', () => {
cy.get('#web3-account-identifier-row').contains(TEST_ADDRESS_NEVER_USE_SHORTENED)
cy.get('[data-testid=web3-account-identifier-row]').contains(TEST_ADDRESS_NEVER_USE_SHORTENED)
})
it('changes back to the options grid', () => {
cy.get('[data-cy=wallet-change]').click()
cy.get('[data-cy=option-grid]').should('exist')
cy.contains('Change').click()
cy.get('[data-testid=option-grid]').should('exist')
})
it('selects injected wallet option', () => {
cy.contains('Injected').click()
cy.get('#web3-account-identifier-row').contains(TEST_ADDRESS_NEVER_USE_SHORTENED)
cy.get('[data-testid=web3-account-identifier-row]').contains(TEST_ADDRESS_NEVER_USE_SHORTENED)
})
it('shows connect buttons after disconnect', () => {
cy.get('[data-cy=wallet-disconnect]').click()
cy.get('[data-cy=option-grid]').should('exist')
cy.contains('Disconnect').click()
cy.get('[data-testid=option-grid]').should('exist')
})
})

20
cypress/release.ts Normal file
View File

@@ -0,0 +1,20 @@
const ONE_MINUTE = 60_000
describe(
'Release',
{
pageLoadTimeout: ONE_MINUTE,
retries: 30,
},
() => {
it('loads swap page', () => {
// We *must* wait in order to space out the retry attempts.
cy.wait(ONE_MINUTE)
.visit('/', {
retryOnStatusCodeFailure: true,
retryOnNetworkFailure: true,
})
.get('#swap-page')
})
}
)

View File

@@ -50,3 +50,9 @@ beforeEach(() => {
res.continue()
})
})
Cypress.on('uncaught:exception', (_err, _runnable) => {
// returning false here prevents Cypress from
// failing the test
return false
})

View File

@@ -1,5 +1,5 @@
{
"name": "@uniswap/widgets",
"name": "@uniswap/interface",
"version": "1.0.7",
"description": "Uniswap Interface",
"homepage": ".",
@@ -9,7 +9,7 @@
"contracts:compile:v3": "typechain --target ethers-v5 --out-dir src/types/v3 \"./node_modules/@uniswap/**/artifacts/contracts/**/*[!dbg].json\"",
"contracts:compile": "yarn contracts:compile:abi && yarn contracts:compile:v3",
"graphql:generate": "graphql-codegen --config codegen.yml",
"prei18n:extract": "touch src/locales/en-US.po",
"prei18n:extract": "node prei18n-extract.js",
"i18n:extract": "lingui extract --locale en-US",
"i18n:compile": "yarn i18n:extract && lingui compile",
"i18n:pseudo": "lingui extract --locale pseudo && lingui compile",
@@ -17,6 +17,7 @@
"start": "react-scripts start",
"build": "react-scripts build",
"serve": "serve build -l 3000",
"lint": "yarn eslint .",
"test": "react-scripts test --coverage",
"cypress:open": "cypress open --browser chrome --e2e",
"cypress:run": "cypress run --browser chrome --e2e"
@@ -53,29 +54,15 @@
"last 1 safari version"
]
},
"dependencies": {
"@coinbase/wallet-sdk": "^3.2.0",
"devDependencies": {
"@ethersproject/experimental": "^5.4.0",
"@fontsource/ibm-plex-mono": "^4.5.1",
"@fontsource/inter": "^4.5.1",
"@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/core": "^3.9.0",
"@lingui/macro": "^3.9.0",
"@lingui/react": "^3.9.0",
"@metamask/jazzicon": "^2.0.0",
"@popperjs/core": "^2.4.4",
"@reach/dialog": "^0.10.3",
"@reach/portal": "^0.10.3",
"@react-hook/window-scroll": "^1.3.0",
"@reduxjs/toolkit": "^1.6.1",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@testing-library/react-hooks": "^7.0.2",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.1",
"@typechain/ethers-v5": "^7.0.0",
"@types/array.prototype.flat": "^1.2.1",
"@types/array.prototype.flatmap": "^1.2.2",
@@ -88,52 +75,20 @@
"@types/multicodec": "^1.0.0",
"@types/node": "^13.13.5",
"@types/qs": "^6.9.2",
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.1",
"@types/react-redux": "^7.1.16",
"@types/react-router-dom": "^5.0.0",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"@types/react-redux": "^7.1.24",
"@types/react-router-dom": "^5.3.3",
"@types/react-virtualized-auto-sizer": "^1.0.0",
"@types/react-window": "^1.8.2",
"@types/rebass": "^4.0.7",
"@types/styled-components": "^5.1.0",
"@types/styled-components": "^5.1.25",
"@types/testing-library__cypress": "^5.0.5",
"@types/ua-parser-js": "^0.7.35",
"@types/wcag-contrast": "^3.0.0",
"@typescript-eslint/eslint-plugin": "^4.1.0",
"@typescript-eslint/parser": "^4.1.0",
"@uniswap/default-token-list": "^3.0.0",
"@uniswap/governance": "^1.0.2",
"@uniswap/liquidity-staker": "^1.0.2",
"@uniswap/merkle-distributor": "1.0.1",
"@uniswap/redux-multicall": "^1.1.1",
"@uniswap/router-sdk": "^1.0.3",
"@uniswap/sdk-core": "^3.0.1",
"@uniswap/smart-order-router": "^2.5.26",
"@uniswap/token-lists": "^1.0.0-beta.30",
"@uniswap/v2-core": "1.0.0",
"@uniswap/v2-periphery": "^1.1.0-beta.0",
"@uniswap/v2-sdk": "^3.0.1",
"@uniswap/v3-core": "1.0.0",
"@uniswap/v3-periphery": "^1.1.1",
"@uniswap/v3-sdk": "^3.8.2",
"@walletconnect/ethereum-provider": "1.7.1",
"@web3-react/coinbase-wallet": "^8.0.33-beta.0",
"@web3-react/core": "^8.0.33-beta.0",
"@web3-react/eip1193": "^8.0.25-beta.0",
"@web3-react/empty": "^8.0.19-beta.0",
"@web3-react/gnosis-safe": "^8.0.5-beta.0",
"@web3-react/metamask": "^8.0.26-beta.0",
"@web3-react/network": "^8.0.26-beta.0",
"@web3-react/types": "^8.0.19-beta.0",
"@web3-react/url": "^8.0.24-beta.0",
"@web3-react/walletconnect": "^8.0.34-beta.0",
"ajv": "^6.12.3",
"array.prototype.flat": "^1.2.4",
"array.prototype.flatmap": "^1.2.4",
"cids": "^1.0.0",
"copy-to-clipboard": "^3.2.0",
"@typescript-eslint/eslint-plugin": "^4",
"@typescript-eslint/parser": "^4",
"cypress": "^10.1.0",
"d3": "^7.0.0",
"env-cmd": "^10.1.0",
"eslint": "^7.11.0",
"eslint-config-prettier": "^6.11.0",
@@ -143,6 +98,59 @@
"eslint-plugin-react-hooks": "^4.0.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"eslint-plugin-unused-imports": "^2.0.0",
"jest-styled-components": "7.0.7",
"ms.macro": "^2.0.0",
"prettier": "^2.7.1",
"react-scripts": "^4.0.3",
"serve": "^11.3.2",
"typechain": "^5.0.0",
"typescript": "^4.4.3"
},
"dependencies": {
"@amplitude/analytics-browser": "^0.5.1",
"@coinbase/wallet-sdk": "^3.3.0",
"@fontsource/ibm-plex-mono": "^4.5.1",
"@fontsource/inter": "^4.5.1",
"@lingui/core": "^3.14.0",
"@lingui/macro": "^3.14.0",
"@lingui/react": "^3.14.0",
"@metamask/jazzicon": "^2.0.0",
"@popperjs/core": "^2.4.4",
"@reach/dialog": "^0.10.3",
"@reach/portal": "^0.10.3",
"@react-hook/window-scroll": "^1.3.0",
"@reduxjs/toolkit": "^1.6.1",
"@uniswap/governance": "^1.0.2",
"@uniswap/liquidity-staker": "^1.0.2",
"@uniswap/merkle-distributor": "1.0.1",
"@uniswap/redux-multicall": "^1.1.5",
"@uniswap/router-sdk": "^1.0.6",
"@uniswap/sdk-core": "^3.0.1",
"@uniswap/smart-order-router": "^2.5.26",
"@uniswap/token-lists": "^1.0.0-beta.30",
"@uniswap/v2-core": "1.0.0",
"@uniswap/v2-periphery": "^1.1.0-beta.0",
"@uniswap/v2-sdk": "^3.0.1",
"@uniswap/v3-core": "1.0.0",
"@uniswap/v3-periphery": "^1.1.1",
"@uniswap/v3-sdk": "^3.9.0",
"@walletconnect/ethereum-provider": "1.7.1",
"@web3-react/coinbase-wallet": "^8.0.34-beta.0",
"@web3-react/core": "^8.0.35-beta.0",
"@web3-react/eip1193": "^8.0.26-beta.0",
"@web3-react/empty": "^8.0.20-beta.0",
"@web3-react/gnosis-safe": "^8.0.6-beta.0",
"@web3-react/metamask": "^8.0.28-beta.0",
"@web3-react/network": "^8.0.27-beta.0",
"@web3-react/types": "^8.0.20-beta.0",
"@web3-react/url": "^8.0.25-beta.0",
"@web3-react/walletconnect": "^8.0.35-beta.0",
"ajv": "^6.12.3",
"array.prototype.flat": "^1.2.4",
"array.prototype.flatmap": "^1.2.4",
"cids": "^1.0.0",
"copy-to-clipboard": "^3.2.0",
"d3": "^7.0.0",
"ethers": "^5.1.4",
"firebase": "^9.1.3",
"fortmatic": "^2.4.0",
@@ -150,30 +158,26 @@
"graphql-request": "^3.4.0",
"immer": "^9.0.6",
"inter-ui": "^3.13.1",
"jest-styled-components": "^7.0.5",
"jotai": "^1.3.7",
"jsbi": "^3.1.4",
"make-plural": "^7.0.0",
"ms.macro": "^2.0.0",
"multicodec": "^3.0.1",
"multihashes": "^4.0.2",
"node-vibrant": "^3.2.1-alpha.1",
"polished": "^3.3.2",
"polyfill-object.fromentries": "^1.0.1",
"popper-max-size-modifier": "^0.2.0",
"prettier": "^2.2.1",
"qs": "^6.9.4",
"react": "^17.0.1",
"react": "^18.2.0",
"react-confetti": "^6.0.0",
"react-dom": "^17.0.1",
"react-dom": "^18.2.0",
"react-feather": "^2.0.8",
"react-ga4": "^1.4.1",
"react-is": "^17.0.2",
"react-markdown": "^4.3.1",
"react-popper": "^2.2.3",
"react-redux": "^7.2.2",
"react-router-dom": "^5.0.0",
"react-scripts": "^4.0.3",
"react-redux": "^8.0.2",
"react-router-dom": "^5.3.3",
"react-spring": "^8.0.27",
"react-use-gesture": "^6.0.14",
"react-virtualized-auto-sizer": "^1.0.2",
@@ -181,13 +185,9 @@
"rebass": "^4.0.7",
"redux": "^4.1.2",
"redux-localstorage-simple": "^2.3.1",
"sass": "^1.45.1",
"serve": "^11.3.2",
"setimmediate": "^1.0.5",
"styled-components": "^5.3.0",
"styled-components": "^5.3.5",
"tiny-invariant": "^1.2.0",
"typechain": "^5.0.0",
"typescript": "^4.4.3",
"ua-parser-js": "^0.7.28",
"use-count-up": "^2.2.5",
"use-resize-observer": "^8.0.0",
@@ -197,8 +197,5 @@
"workbox-navigation-preload": "^6.1.0",
"workbox-precaching": "^6.1.0",
"workbox-routing": "^6.1.0"
},
"resolutions": {
"@walletconnect/ethereum-provider": "1.7.1"
}
}

9
prei18n-extract.js Normal file
View File

@@ -0,0 +1,9 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const exec = require('child_process').exec
const isWindows = process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE)
if (isWindows) {
exec(`type nul > src/locales/en-US.po`)
} else {
exec(`touch src/locales/en-US.po`)
}

View File

@@ -24,83 +24,83 @@
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="preconnect" href="https://www.google-analytics.com/">
<link rel="preconnect" href="https://www.google-analytics.com/" />
<link rel="preload" href="%PUBLIC_URL%/fonts/Inter-roman.var.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="%PUBLIC_URL%/fonts/Inter-roman.var.woff2" as="font" type="font/woff2" crossorigin />
<style>
* {
font-family: 'Inter', sans-serif;
box-sizing: border-box;
}
* {
font-family: 'Inter', sans-serif;
box-sizing: border-box;
}
/**
/**
Explicitly load Inter var from public/ so it does not block LCP's critical path.
*/
@font-face {
font-family: 'Inter custom';
font-weight: 100 900;
font-style: normal;
font-display: block;
font-named-instance: 'Regular';
src: url(%PUBLIC_URL%/fonts/Inter-roman.var.woff2) format("woff2 supports variations(gvar)"),
url(%PUBLIC_URL%/fonts/Inter-roman.var.woff2) format("woff2-variations"),
url(%PUBLIC_URL%/fonts/Inter-roman.var.woff2) format("woff2");
}
@font-face {
font-family: 'Inter custom';
font-weight: 100 900;
font-style: normal;
font-display: block;
font-named-instance: 'Regular';
src: url(%PUBLIC_URL%/fonts/Inter-roman.var.woff2) format('woff2 supports variations(gvar)'),
url(%PUBLIC_URL%/fonts/Inter-roman.var.woff2) format('woff2-variations'),
url(%PUBLIC_URL%/fonts/Inter-roman.var.woff2) format('woff2');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Inter custom', sans-serif;
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Inter custom', sans-serif;
}
}
html,
body {
margin: 0;
padding: 0;
}
html,
body {
margin: 0;
padding: 0;
}
button {
user-select: none;
}
button {
user-select: none;
}
html {
font-size: 16px;
font-variant: none;
font-smooth: always;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
font-feature-settings: 'ss01' on, 'ss02' on, 'cv01' on, 'cv03' on;
}
#background-radial-gradient {
position: fixed;
top: 0;
left: 0;
right: 0;
pointer-events: none;
width: 200vw;
height: 200vh;
background: radial-gradient(50% 50% at 50% 50%, #fc077d10 0%, rgba(255, 255, 255, 0) 100%);
transform: translate(-50vw, -100vh);
z-index: -1;
}
html {
min-height: 100%;
}
@media (prefers-color-scheme: dark) {
html {
font-size: 16px;
font-variant: none;
font-smooth: always;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
font-feature-settings: 'ss01' on, 'ss02' on, 'cv01' on, 'cv03' on;
background-color: #212429;
}
#background-radial-gradient {
position: fixed;
top: 0;
left: 0;
right: 0;
pointer-events: none;
width: 200vw;
height: 200vh;
background: radial-gradient(50% 50% at 50% 50%, #fc077d10 0%, rgba(255, 255, 255, 0) 100%);
transform: translate(-50vw, -100vh);
z-index: -1;
}
}
@media (prefers-color-scheme: light) {
html {
min-height: 100%;
}
@media (prefers-color-scheme: dark) {
html {
background-color: #212429;
}
}
@media (prefers-color-scheme: light) {
html {
background-color: #F7F8FA;
}
background-color: #f7f8fa;
}
}
</style>
</head>
<body>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1 @@
<svg id="Celo_Rings" data-name="Celo Rings" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 950 950"><defs><style>.cls-1{fill:#fbcc5c;}.cls-2{fill:#35d07f;}.cls-3{fill:#5ea33b;}</style></defs><title>Artboard 1</title><path id="Bottom_Ring" data-name="Bottom Ring" class="cls-1" d="M375,850c151.88,0,275-123.12,275-275S526.88,300,375,300,100,423.12,100,575,223.12,850,375,850Zm0,100C167.9,950,0,782.1,0,575S167.9,200,375,200,750,367.9,750,575,582.1,950,375,950Z"/><path id="Top_Ring" data-name="Top Ring" class="cls-2" d="M575,650c151.88,0,275-123.12,275-275S726.88,100,575,100,300,223.12,300,375,423.12,650,575,650Zm0,100c-207.1,0-375-167.9-375-375S367.9,0,575,0,950,167.9,950,375,782.1,750,575,750Z"/><path id="Rings_Overlap" data-name="Rings Overlap" class="cls-3" d="M587.39,750a274.38,274.38,0,0,0,54.55-108.06A274.36,274.36,0,0,0,750,587.4a373.63,373.63,0,0,1-29.16,133.45A373.62,373.62,0,0,1,587.39,750ZM308.06,308.06A274.36,274.36,0,0,0,200,362.6a373.63,373.63,0,0,1,29.16-133.45A373.62,373.62,0,0,1,362.61,200,274.38,274.38,0,0,0,308.06,308.06Z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,4 +1,4 @@
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import { CheckCircle, Triangle } from 'react-feather'
import styled from 'styled-components/macro'
@@ -35,7 +35,7 @@ const IconWrapper = styled.div<{ pending: boolean; success?: boolean }>`
`
export default function Transaction({ hash }: { hash: string }) {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const allTransactions = useAllTransactions()
const tx = allTransactions?.[hash]

View File

@@ -1,16 +1,16 @@
import { Trans } from '@lingui/macro'
import { Connector } from '@web3-react/types'
import { useWeb3React } from '@web3-react/core'
import CopyHelper from 'components/AccountDetails/Copy'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useCallback, useContext } from 'react'
import { getConnection, getConnectionName, getIsCoinbaseWallet, getIsMetaMask } from 'connection/utils'
import { Context, useCallback, useContext } from 'react'
import { ExternalLink as LinkIcon } from 'react-feather'
import { useAppDispatch } from 'state/hooks'
import { updateSelectedWallet } from 'state/user/reducer'
import { DefaultTheme } from 'styled-components/macro'
import styled, { ThemeContext } from 'styled-components/macro'
import { isMobile } from 'utils/userAgent'
import { ReactComponent as Close } from '../../assets/images/x.svg'
import { coinbaseWallet, injected } from '../../connectors'
import { SUPPORTED_WALLETS } from '../../constants/wallet'
import { clearAllTransactions } from '../../state/transactions/reducer'
import { ExternalLink, LinkStyledButton, ThemedText } from '../../theme'
import { shortenAddress } from '../../utils'
@@ -162,29 +162,6 @@ const WalletName = styled.div`
color: ${({ theme }) => theme.text3};
`
const IconWrapper = styled.div<{ size?: number }>`
${({ theme }) => theme.flexColumnNoWrap};
align-items: center;
justify-content: center;
margin-right: 8px;
& > img,
span {
height: ${({ size }) => (size ? size + 'px' : '32px')};
width: ${({ size }) => (size ? size + 'px' : '32px')};
}
${({ theme }) => theme.mediaWidth.upToMedium`
align-items: flex-end;
`};
`
function WrappedStatusIcon({ connector }: { connector: Connector }) {
return (
<IconWrapper size={16}>
<StatusIcon connector={connector} />
</IconWrapper>
)
}
const TransactionListWrapper = styled.div`
${({ theme }) => theme.flexColumnNoWrap};
`
@@ -226,22 +203,20 @@ export default function AccountDetails({
ENSName,
openOptions,
}: AccountDetailsProps) {
const { chainId, account, connector } = useActiveWeb3React()
const theme = useContext(ThemeContext)
const { chainId, account, connector } = useWeb3React()
const connectionType = getConnection(connector).type
const theme = useContext(ThemeContext as Context<DefaultTheme>)
const dispatch = useAppDispatch()
const isMetaMask = getIsMetaMask()
const isCoinbaseWallet = getIsCoinbaseWallet()
const isInjectedMobileBrowser = (isMetaMask || isCoinbaseWallet) && isMobile
function formatConnectorName() {
const { ethereum } = window
const isMetaMask = !!(ethereum && ethereum.isMetaMask)
const name = Object.keys(SUPPORTED_WALLETS)
.filter(
(k) =>
SUPPORTED_WALLETS[k].connector === connector && (connector !== injected || isMetaMask === (k === 'METAMASK'))
)
.map((k) => SUPPORTED_WALLETS[k].name)[0]
return (
<WalletName>
<Trans>Connected with {name}</Trans>
<Trans>Connected with</Trans> {getConnectionName(connectionType, isMetaMask)}
</WalletName>
)
}
@@ -265,48 +240,41 @@ export default function AccountDetails({
<AccountGroupingRow>
{formatConnectorName()}
<div>
{/* Coinbase Wallet reloads the page right now, which breaks the selectedWallet from being set properly on localStorage */}
{connector !== coinbaseWallet && (
<WalletAction
style={{ fontSize: '.825rem', fontWeight: 400, marginRight: '8px' }}
onClick={() => {
connector.deactivate ? connector.deactivate() : connector.resetState()
dispatch(updateSelectedWallet({ wallet: undefined }))
openOptions()
}}
data-cy="wallet-disconnect"
>
<Trans>Disconnect</Trans>
</WalletAction>
{!isInjectedMobileBrowser && (
<>
<WalletAction
style={{ fontSize: '.825rem', fontWeight: 400, marginRight: '8px' }}
onClick={() => {
if (connector.deactivate) {
connector.deactivate()
} else {
connector.resetState()
}
dispatch(updateSelectedWallet({ wallet: undefined }))
openOptions()
}}
>
<Trans>Disconnect</Trans>
</WalletAction>
<WalletAction
style={{ fontSize: '.825rem', fontWeight: 400 }}
onClick={() => {
openOptions()
}}
>
<Trans>Change</Trans>
</WalletAction>
</>
)}
<WalletAction
style={{ fontSize: '.825rem', fontWeight: 400 }}
onClick={() => {
openOptions()
}}
data-cy="wallet-change"
>
<Trans>Change</Trans>
</WalletAction>
</div>
</AccountGroupingRow>
<AccountGroupingRow id="web3-account-identifier-row">
<AccountGroupingRow data-testid="web3-account-identifier-row">
<AccountControl>
{ENSName ? (
<>
<div>
{connector && <WrappedStatusIcon connector={connector} />}
<p> {ENSName}</p>
</div>
</>
) : (
<>
<div>
{connector && <WrappedStatusIcon connector={connector} />}
<p> {account && shortenAddress(account)}</p>
</div>
</>
)}
<div>
<StatusIcon connectionType={connectionType} />
<p>{ENSName ? ENSName : account && shortenAddress(account)}</p>
</div>
</AccountControl>
</AccountGroupingRow>
<AccountGroupingRow>

View File

@@ -1,9 +1,9 @@
import { Trans } from '@lingui/macro'
// eslint-disable-next-line no-restricted-imports
import { t } from '@lingui/macro'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { ReactNode, useCallback, useContext } from 'react'
import styled, { ThemeContext } from 'styled-components/macro'
import { useWeb3React } from '@web3-react/core'
import { ChangeEvent, Context, ReactNode, useCallback, useContext } from 'react'
import styled, { DefaultTheme, ThemeContext } from 'styled-components/macro'
import useENS from '../../hooks/useENS'
import { ExternalLink, ThemedText } from '../../theme'
@@ -86,13 +86,13 @@ export default function AddressInputPanel({
// triggers whenever the typed value changes
onChange: (value: string) => void
}) {
const { chainId } = useActiveWeb3React()
const theme = useContext(ThemeContext)
const { chainId } = useWeb3React()
const theme = useContext(ThemeContext as Context<DefaultTheme>)
const { address, loading, name } = useENS(value)
const handleInput = useCallback(
(event) => {
(event: ChangeEvent<HTMLInputElement>) => {
const input = event.target.value
const withoutSpaces = input.replace(/\s+/g, '')
onChange(withoutSpaces)

View File

@@ -0,0 +1,57 @@
import { createContext, memo, PropsWithChildren, useContext, useEffect, useMemo } from 'react'
import { sendAnalyticsEvent } from '.'
import { ElementName, EventName, ModalName, PageName, SectionName } from './constants'
export interface ITraceContext {
// Highest order context: eg Swap or Explore.
page?: PageName
// Enclosed section name. For contexts with modals, refers to the
// section of the page from which the user triggered the modal.
section?: SectionName
modal?: ModalName
// Element name mostly used to identify events sources
// Does not need to be unique given the higher order page and section.
element?: ElementName
}
export const TraceContext = createContext<ITraceContext>({})
type TraceProps = {
shouldLogImpression?: boolean // whether to log impression on mount
name?: EventName
properties?: Record<string, unknown>
} & ITraceContext
/**
* Sends an analytics event on mount (if shouldLogImpression is set),
* and propagates the context to child traces.
*/
export const Trace = memo(
({ shouldLogImpression, name, children, page, section, element, properties }: PropsWithChildren<TraceProps>) => {
const parentTrace = useContext(TraceContext)
const combinedProps = useMemo(
() => ({
...parentTrace,
...Object.fromEntries(Object.entries({ page, section, element }).filter(([_, v]) => v !== undefined)),
}),
[element, parentTrace, page, section]
)
useEffect(() => {
if (shouldLogImpression) {
sendAnalyticsEvent(name ?? EventName.PAGE_VIEWED, { ...combinedProps, ...properties })
}
// Impressions should only be logged on mount.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return <TraceContext.Provider value={combinedProps}>{children}</TraceContext.Provider>
}
)
Trace.displayName = 'Trace'

View File

@@ -0,0 +1,70 @@
import { Children, cloneElement, isValidElement, memo, PropsWithChildren, SyntheticEvent } from 'react'
import { sendAnalyticsEvent } from '.'
import { Event, EventName } from './constants'
import { ITraceContext, Trace, TraceContext } from './Trace'
type TraceEventProps = {
events: Event[]
name: EventName
properties?: Record<string, unknown>
} & ITraceContext
/**
* Analytics instrumentation component that wraps event callbacks with logging logic.
*
* @example
* <TraceEvent events={[Event.onClick]} element={ElementName.SWAP_BUTTON}>
* <Button onClick={() => console.log('clicked')}>Click me</Button>
* </TraceEvent>
*/
export const TraceEvent = memo((props: PropsWithChildren<TraceEventProps>) => {
const { name, properties, events, children, ...traceProps } = props
return (
<Trace {...traceProps}>
<TraceContext.Consumer>
{(traceContext) =>
Children.map(children, (child) => {
if (!isValidElement(child)) {
return child
}
// For each child, augment event handlers defined in `events` with event tracing.
return cloneElement(child, getEventHandlers(child, traceContext, events, name, properties))
})
}
</TraceContext.Consumer>
</Trace>
)
})
TraceEvent.displayName = 'TraceEvent'
/**
* Given a set of child element and event props, returns a spreadable
* object of the event handlers augmented with analytics logging.
*/
function getEventHandlers(
child: React.ReactElement,
traceContext: ITraceContext,
events: Event[],
name: EventName,
properties?: Record<string, unknown>
) {
const eventHandlers: Partial<Record<Event, (e: SyntheticEvent<Element, Event>) => void>> = {}
for (const event of events) {
eventHandlers[event] = (eventHandlerArgs: unknown) => {
// call child event handler with original arguments, must be in array
const args = Array.isArray(eventHandlerArgs) ? eventHandlerArgs : [eventHandlerArgs]
child.props[event]?.apply(child, args)
// augment handler with analytics logging
sendAnalyticsEvent(name, { ...traceContext, ...properties, action: event })
}
}
// return a spreadable event handler object
return eventHandlers
}

View File

@@ -0,0 +1,78 @@
/**
* Event names that can occur in this application.
*
* Subject to change as new features are added and new events are defined
* and logged.
*/
export enum EventName {
CONNECT_WALLET_BUTTON_CLICKED = 'Connect Wallet Button Clicked',
PAGE_VIEWED = 'Page Viewed',
SWAP_SUBMITTED = 'Swap Submitted',
TOKEN_IMPORTED = 'Token Imported',
TOKEN_SELECTED = 'Token Selected',
TOKEN_SELECTOR_OPENED = 'Token Selector Opened',
WALLET_CONNECT_TXN_COMPLETED = 'Wallet Connect Transaction Completed',
WALLET_SELECTED = 'Wallet Selected',
// alphabetize additional event names.
}
export enum WALLET_CONNECTION_RESULT {
SUCCEEDED = 'Succeeded',
FAILED = 'Failed',
}
/**
* Known pages in the app. Highest order context.
*/
export const enum PageName {
EXPLORE_PAGE = 'explore-page',
POOL_PAGE = 'pool-page',
SWAP_PAGE = 'swap-page',
VOTE_PAGE = 'vote-page',
// alphabetize additional page names.
}
/**
* Sections. Disambiguates low-level elements that may share a name.
* eg a `back` button in a modal will have the same `element`,
* but a different `section`.
*/
export const enum SectionName {
CURRENCY_INPUT_PANEL = 'swap-currency-input',
CURRENCY_OUTPUT_PANEL = 'swap-currency-output',
// alphabetize additional section names.
}
/** Known modals for analytics purposes. */
export const enum ModalName {
SWAP = 'swap-modal',
TOKEN_SELECTOR = 'token-selector-modal',
// alphabetize additional modal names.
}
/**
* Known element names for analytics purposes.
* Use to identify low-level components given a TraceContext
*/
export const enum ElementName {
COMMON_BASES_CURRENCY_BUTTON = 'common-bases-currency-button',
CONFIRM_SWAP_BUTTON = 'confirm-swap-or-send',
CONNECT_WALLET_BUTTON = 'connect-wallet-button',
IMPORT_TOKEN_BUTTON = 'import-token-button',
SWAP_BUTTON = 'swap-button',
TOKEN_SELECTOR_ROW = 'token-selector-row',
WALLET_TYPE_OPTION = 'wallet-type-option',
// alphabetize additional element names.
}
/**
* Known events that trigger callbacks.
* @example
* <TraceEvent events={[Event.onClick]} element={name}>
*/
export enum Event {
onClick = 'onClick',
onKeyPress = 'onKeyPress',
onSelect = 'onSelect',
// alphabetize additional events.
}

View File

@@ -0,0 +1,88 @@
import { Identify, identify, init, track } from '@amplitude/analytics-browser'
/**
* Initializes Amplitude with API key for project.
*
* Uniswap has two Amplitude projects: test and production. You must be a
* member of the organization on Amplitude to view details.
*/
export function initializeAnalytics(isDevEnvironment = process.env.NODE_ENV === 'development') {
if (isDevEnvironment) return
const API_KEY = process.env.REACT_APP_AMPLITUDE_KEY
if (typeof API_KEY === 'undefined') {
throw new Error(`REACT_APP_AMPLITUDE_KEY must be a defined environment variable`)
}
init(
API_KEY,
/* userId= */ undefined, // User ID should be undefined to let Amplitude default to Device ID
/* options= */ {
// Disable tracking of private user information by Amplitude
trackingOptions: {
ipAddress: false,
carrier: false,
city: false,
region: false,
country: false,
dma: false, // designated market area
},
}
)
}
/** Sends an event to Amplitude. */
export function sendAnalyticsEvent(eventName: string, eventProperties?: Record<string, unknown>) {
if (process.env.NODE_ENV === 'development') {
console.log(`[amplitude(${eventName})]: ${JSON.stringify(eventProperties)}`)
return
}
track(eventName, eventProperties)
}
/**
* Class that exposes methods to mutate the User Model's properties in
* Amplitude that represents the current session's user.
*
* See https://help.amplitude.com/hc/en-us/articles/115002380567-User-properties-and-event-properties
* for details.
*/
class UserModel {
constructor(private isDevEnvironment = process.env.NODE_ENV === 'development') {}
private log(method: string, ...parameters: unknown[]) {
console.debug(`[amplitude(Identify)]: ${method}(${parameters})`)
}
private call(mutate: (event: Identify) => Identify) {
if (this.isDevEnvironment) {
const log = (_: Identify, method: string) => this.log.bind(this, method)
mutate(new Proxy(new Identify(), { get: log }))
return
}
identify(mutate(new Identify()))
}
set(key: string, value: string | number) {
this.call((event) => event.set(key, value))
}
setOnce(key: string, value: string | number) {
this.call((event) => event.setOnce(key, value))
}
add(key: string, value: string | number) {
this.call((event) => event.add(key, typeof value === 'number' ? value : 0))
}
postInsert(key: string, value: string | number) {
this.call((event) => event.postInsert(key, value))
}
remove(key: string, value: string | number) {
this.call((event) => event.remove(key, value))
}
}
export const user = new UserModel()

View File

@@ -1,5 +1,5 @@
import { Trans } from '@lingui/macro'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import { ReactNode, useMemo } from 'react'
const BLOCKED_ADDRESSES: string[] = [
@@ -38,7 +38,7 @@ const BLOCKED_ADDRESSES: string[] = [
]
export default function Blocklist({ children }: { children: ReactNode }) {
const { account } = useActiveWeb3React()
const { account } = useWeb3React()
const blocked: boolean = useMemo(() => Boolean(account && BLOCKED_ADDRESSES.indexOf(account) !== -1), [account])
if (blocked) {
return (

View File

@@ -1,9 +1,9 @@
import { Trans } from '@lingui/macro'
import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import { useWeb3React } from '@web3-react/core'
import { AutoColumn } from 'components/Column'
import { LoadingOpacityContainer, loadingOpacityMixin } from 'components/Loader/styled'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { darken } from 'polished'
import { ReactNode, useCallback, useState } from 'react'
import { Lock } from 'react-feather'
@@ -12,7 +12,7 @@ import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
import useTheme from '../../hooks/useTheme'
import { useCurrencyBalance } from '../../state/wallet/hooks'
import { useCurrencyBalance } from '../../state/connection/hooks'
import { ThemedText } from '../../theme'
import { ButtonGray } from '../Button'
import CurrencyLogo from '../CurrencyLogo'
@@ -103,6 +103,7 @@ const LabelRow = styled.div`
const FiatRow = styled(LabelRow)`
justify-content: flex-end;
height: 16px;
`
const Aligner = styled.span`
@@ -201,7 +202,7 @@ export default function CurrencyInputPanel({
...rest
}: CurrencyInputPanelProps) {
const [modalOpen, setModalOpen] = useState(false)
const { account } = useActiveWeb3React()
const { account } = useWeb3React()
const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined)
const theme = useTheme()

View File

@@ -1,6 +1,6 @@
import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import { SupportedChainId } from 'constants/chains'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { AlertOctagon } from 'react-feather'
import styled from 'styled-components/macro'
import { ExternalLink } from 'theme'
@@ -42,7 +42,7 @@ function Wrapper({ children }: { children: React.ReactNode }) {
* Shows a downtime warning for the network if it's relevant
*/
export default function DowntimeWarning() {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
if (!isL2ChainId(chainId)) {
return null
}

View File

@@ -1,6 +1,6 @@
import { Trans } from '@lingui/macro'
import { sendEvent } from 'components/analytics'
import React, { ErrorInfo } from 'react'
import React, { ErrorInfo, PropsWithChildren } from 'react'
import styled from 'styled-components/macro'
import store, { AppState } from '../../state'
@@ -56,8 +56,8 @@ async function updateServiceWorker(): Promise<ServiceWorkerRegistration> {
return ready.update() as unknown as Promise<ServiceWorkerRegistration>
}
export default class ErrorBoundary extends React.Component<unknown, ErrorBoundaryState> {
constructor(props: unknown) {
export default class ErrorBoundary extends React.Component<PropsWithChildren<unknown>, ErrorBoundaryState> {
constructor(props: PropsWithChildren<unknown>) {
super(props)
this.state = { error: null }
}

View File

@@ -1,12 +1,12 @@
import { Trans } from '@lingui/macro'
import { Currency } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk'
import { useWeb3React } from '@web3-react/core'
import { sendEvent } from 'components/analytics'
import { ButtonGray } from 'components/Button'
import Card from 'components/Card'
import { AutoColumn } from 'components/Column'
import { RowBetween } from 'components/Row'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useFeeTierDistribution } from 'hooks/useFeeTierDistribution'
import { PoolState, usePools } from 'hooks/usePools'
import usePrevious from 'hooks/usePrevious'
@@ -59,7 +59,7 @@ export default function FeeSelector({
currencyA?: Currency | undefined
currencyB?: Currency | undefined
}) {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const { isLoading, isError, largestUsageFeeTier, distributions } = useFeeTierDistribution(currencyA, currencyB)

View File

@@ -1,7 +1,7 @@
import { Trans } from '@lingui/macro'
import { FeeAmount } from '@uniswap/v3-sdk'
import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from 'constants/chains'
import { ReactNode } from 'react'
import type { ReactNode } from 'react'
export const FEE_AMOUNT_DETAIL: Record<
FeeAmount,
@@ -10,7 +10,14 @@ export const FEE_AMOUNT_DETAIL: Record<
[FeeAmount.LOWEST]: {
label: '0.01',
description: <Trans>Best for very stable pairs.</Trans>,
supportedChains: [SupportedChainId.MAINNET, SupportedChainId.POLYGON, SupportedChainId.POLYGON_MUMBAI],
supportedChains: [
SupportedChainId.MAINNET,
SupportedChainId.POLYGON,
SupportedChainId.POLYGON_MUMBAI,
SupportedChainId.CELO,
SupportedChainId.CELO_ALFAJORES,
SupportedChainId.OPTIMISM,
],
},
[FeeAmount.LOW]: {
label: '0.05',

View File

@@ -1,7 +1,7 @@
import { Trans } from '@lingui/macro'
import { CHAIN_INFO, L2ChainInfo } from 'constants/chainInfo'
import { useWeb3React } from '@web3-react/core'
import { getChainInfoOrDefault, L2ChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { AlertOctagon } from 'react-feather'
import styled from 'styled-components/macro'
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
@@ -45,8 +45,8 @@ const Wrapper = styled.div`
`
export function ChainConnectivityWarning() {
const { chainId } = useActiveWeb3React()
const info = CHAIN_INFO[chainId ?? SupportedChainId.MAINNET]
const { chainId } = useWeb3React()
const info = getChainInfoOrDefault(chainId)
const label = info?.label
return (

View File

@@ -1,23 +1,23 @@
import { Trans } from '@lingui/macro'
import { getWalletForConnector } from 'connectors'
import { CHAIN_INFO } from 'constants/chainInfo'
import { useWeb3React } from '@web3-react/core'
import { getConnection } from 'connection/utils'
import { getChainInfo } from 'constants/chainInfo'
import { CHAIN_IDS_TO_NAMES, SupportedChainId } from 'constants/chains'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import useParsedQueryString from 'hooks/useParsedQueryString'
import usePrevious from 'hooks/usePrevious'
import { ParsedQs } from 'qs'
import { useCallback, useEffect, useRef } from 'react'
import { useCallback, useEffect, useRef, useState } from 'react'
import { ArrowDownCircle, ChevronDown } from 'react-feather'
import { useHistory } from 'react-router-dom'
import { useModalOpen, useToggleModal } from 'state/application/hooks'
import { useCloseModal, useModalIsOpen, useOpenModal, useToggleModal } from 'state/application/hooks'
import { addPopup, ApplicationModal } from 'state/application/reducer'
import { updateConnectionError } from 'state/connection/reducer'
import { useAppDispatch } from 'state/hooks'
import { updateWalletError } from 'state/wallet/reducer'
import styled from 'styled-components/macro'
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
import { replaceURLParam } from 'utils/routes'
import { isChainAllowed, switchChain } from 'utils/switchChain'
import { isMobile } from 'utils/userAgent'
const ActiveRowLinkList = styled.div`
display: flex;
@@ -121,20 +121,18 @@ const SelectorLabel = styled(NetworkLabel)`
margin-right: 8px;
}
`
const SelectorControls = styled.div<{ interactive: boolean }>`
const SelectorControls = styled.div`
align-items: center;
background-color: ${({ theme }) => theme.bg0};
border: 2px solid ${({ theme }) => theme.bg0};
border-radius: 16px;
color: ${({ theme }) => theme.text1};
cursor: ${({ interactive }) => (interactive ? 'pointer' : 'auto')};
display: flex;
font-weight: 500;
justify-content: space-between;
padding: 6px 8px;
`
const SelectorLogo = styled(Logo)<{ interactive?: boolean }>`
margin-right: ${({ interactive }) => (interactive ? 8 : 0)}px;
const SelectorLogo = styled(Logo)`
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
margin-right: 8px;
}
@@ -158,6 +156,9 @@ const BridgeLabel = ({ chainId }: { chainId: SupportedChainId }) => {
case SupportedChainId.POLYGON:
case SupportedChainId.POLYGON_MUMBAI:
return <Trans>Polygon Bridge</Trans>
case SupportedChainId.CELO:
case SupportedChainId.CELO_ALFAJORES:
return <Trans>Portal Bridge</Trans>
default:
return <Trans>Bridge</Trans>
}
@@ -173,6 +174,9 @@ const ExplorerLabel = ({ chainId }: { chainId: SupportedChainId }) => {
case SupportedChainId.POLYGON:
case SupportedChainId.POLYGON_MUMBAI:
return <Trans>Polygonscan</Trans>
case SupportedChainId.CELO:
case SupportedChainId.CELO_ALFAJORES:
return <Trans>Blockscout</Trans>
default:
return <Trans>Etherscan</Trans>
}
@@ -185,12 +189,12 @@ function Row({
targetChain: SupportedChainId
onSelectChain: (targetChain: number) => void
}) {
const { provider, chainId } = useActiveWeb3React()
const { provider, chainId } = useWeb3React()
if (!provider || !chainId) {
return null
}
const active = chainId === targetChain
const { helpCenterUrl, explorer, bridge, label, logoUrl } = CHAIN_INFO[targetChain]
const { helpCenterUrl, explorer, bridge, label, logoUrl } = getChainInfo(targetChain)
const rowContent = (
<FlyoutRow onClick={() => onSelectChain(targetChain)} active={active}>
@@ -242,9 +246,9 @@ function Row({
const getParsedChainId = (parsedQs?: ParsedQs) => {
const chain = parsedQs?.chain
if (!chain || typeof chain !== 'string') return { urlChain: undefined, urlChainId: undefined }
if (!chain || typeof chain !== 'string') return
return { urlChain: chain.toLowerCase(), urlChainId: getChainIdFromName(chain) }
return getChainIdFromName(chain)
}
const getChainIdFromName = (name: string) => {
@@ -263,89 +267,110 @@ const NETWORK_SELECTOR_CHAINS = [
SupportedChainId.POLYGON,
SupportedChainId.OPTIMISM,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.CELO,
]
export default function NetworkSelector() {
const dispatch = useAppDispatch()
const { chainId, provider, connector } = useActiveWeb3React()
const { chainId, provider, connector, isActive } = useWeb3React()
const [previousChainId, setPreviousChainId] = useState<number | undefined>(undefined)
// Can't use `usePrevious` because `chainId` can be undefined while activating.
useEffect(() => {
if (chainId && chainId !== previousChainId) {
setPreviousChainId(chainId)
}
}, [chainId, previousChainId])
const parsedQs = useParsedQueryString()
const { urlChain, urlChainId } = getParsedChainId(parsedQs)
const prevChainId = usePrevious(chainId)
const node = useRef<HTMLDivElement>()
const open = useModalOpen(ApplicationModal.NETWORK_SELECTOR)
const toggle = useToggleModal(ApplicationModal.NETWORK_SELECTOR)
useOnClickOutside(node, open ? toggle : undefined)
const urlChainId = getParsedChainId(parsedQs)
const previousUrlChainId = usePrevious(urlChainId)
const history = useHistory()
const info = chainId ? CHAIN_INFO[chainId] : undefined
const node = useRef<HTMLDivElement>(null)
const isOpen = useModalIsOpen(ApplicationModal.NETWORK_SELECTOR)
const openModal = useOpenModal(ApplicationModal.NETWORK_SELECTOR)
const closeModal = useCloseModal(ApplicationModal.NETWORK_SELECTOR)
const toggleModal = useToggleModal(ApplicationModal.NETWORK_SELECTOR)
const info = getChainInfo(chainId)
const replaceURLChainParam = useCallback(() => {
if (chainId) {
history.replace({ search: replaceURLParam(history.location.search, 'chain', getChainNameFromId(chainId)) })
}
}, [chainId, history])
const onSelectChain = useCallback(
async (targetChain: number, skipToggle?: boolean) => {
async (targetChain: SupportedChainId, skipClose?: boolean) => {
if (!connector) return
const wallet = getWalletForConnector(connector)
const connectionType = getConnection(connector).type
try {
dispatch(updateWalletError({ wallet, error: undefined }))
dispatch(updateConnectionError({ connectionType, error: undefined }))
await switchChain(connector, targetChain)
if (!skipToggle) {
toggle()
}
history.replace({
search: replaceURLParam(history.location.search, 'chain', getChainNameFromId(targetChain)),
})
} catch (error) {
console.error('Failed to switch networks', error)
// we want app network <-> chainId param to be in sync, so if user changes the network by changing the URL
// but the request fails, revert the URL back to current chainId
if (chainId) {
history.replace({ search: replaceURLParam(history.location.search, 'chain', getChainNameFromId(chainId)) })
}
if (!skipToggle) {
toggle()
}
dispatch(updateWalletError({ wallet, error: error.message }))
dispatch(updateConnectionError({ connectionType, error: error.message }))
dispatch(addPopup({ content: { failedSwitchNetwork: targetChain }, key: `failed-network-switch` }))
// If we activate a chain and it fails, reset the query param to the current chainId
replaceURLChainParam()
}
if (!skipClose) {
closeModal()
}
},
[connector, toggle, dispatch, history, chainId]
[connector, closeModal, dispatch, replaceURLChainParam]
)
// If there is no chain query param, set it to the current chain
useEffect(() => {
if (!chainId || !prevChainId) return
const chainQueryUnpopulated = !urlChainId
if (chainQueryUnpopulated && chainId) {
replaceURLChainParam()
}
}, [chainId, urlChainId, replaceURLChainParam])
// when network change originates from wallet or dropdown selector, just update URL
if (chainId !== prevChainId) {
history.replace({ search: replaceURLParam(history.location.search, 'chain', getChainNameFromId(chainId)) })
// otherwise assume network change originates from URL
} else if (urlChainId && urlChainId !== chainId) {
// If the chain changed but the query param is stale, update to the current chain
useEffect(() => {
const chainChanged = chainId !== previousChainId
const chainQueryStale = urlChainId !== chainId
if (chainChanged && chainQueryStale) {
replaceURLChainParam()
}
}, [chainId, previousChainId, replaceURLChainParam, urlChainId])
// If the query param changed, and the chain didn't change, then activate the new chain
useEffect(() => {
const chainQueryManuallyUpdated = urlChainId && urlChainId !== previousUrlChainId
if (chainQueryManuallyUpdated && isActive) {
onSelectChain(urlChainId, true)
}
}, [chainId, urlChainId, prevChainId, onSelectChain, history])
// set chain parameter on initial load if not there
useEffect(() => {
if (chainId && !urlChainId) {
history.replace({ search: replaceURLParam(history.location.search, 'chain', getChainNameFromId(chainId)) })
}
}, [chainId, history, urlChainId, urlChain])
}, [onSelectChain, urlChainId, previousUrlChainId, isActive])
if (!chainId || !info || !provider) {
return null
}
return (
<SelectorWrapper ref={node as any} onMouseEnter={toggle} onMouseLeave={toggle}>
<SelectorControls interactive>
<SelectorLogo interactive src={info.logoUrl} />
<SelectorWrapper
ref={node}
onMouseEnter={openModal}
onMouseLeave={closeModal}
onClick={isMobile ? toggleModal : undefined}
>
<SelectorControls>
<SelectorLogo src={info.logoUrl} />
<SelectorLabel>{info.label}</SelectorLabel>
<StyledChevronDown />
</SelectorControls>
{open && (
{isOpen && (
<FlyoutMenu>
<FlyoutMenuContents>
<FlyoutHeader>

View File

@@ -1,7 +1,7 @@
import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import { RowFixed } from 'components/Row'
import { CHAIN_INFO } from 'constants/chainInfo'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { getChainInfo } from 'constants/chainInfo'
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
import useGasPrice from 'hooks/useGasPrice'
import useMachineTimeMs from 'hooks/useMachineTime'
@@ -100,7 +100,7 @@ const DEFAULT_MS_BEFORE_WARNING = ms`10m`
const NETWORK_HEALTH_CHECK_MS = ms`10s`
export default function Polling() {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const blockNumber = useBlockNumber()
const [isMounting, setIsMounting] = useState(false)
const [isHover, setIsHover] = useState(false)
@@ -112,7 +112,7 @@ export default function Polling() {
const priceGwei = ethGasPrice ? JSBI.divide(ethGasPrice, JSBI.BigInt(1000000000)) : undefined
const waitMsBeforeWarning =
(chainId ? CHAIN_INFO[chainId]?.blockWaitMsBeforeWarning : DEFAULT_MS_BEFORE_WARNING) ?? DEFAULT_MS_BEFORE_WARNING
(chainId ? getChainInfo(chainId)?.blockWaitMsBeforeWarning : DEFAULT_MS_BEFORE_WARNING) ?? DEFAULT_MS_BEFORE_WARNING
const warning = Boolean(!!blockTime && machineTime - blockTime.mul(1000).toNumber() > waitMsBeforeWarning)

View File

@@ -1,19 +1,18 @@
import { Trans } from '@lingui/macro'
import useScrollPosition from '@react-hook/window-scroll'
import { CHAIN_INFO } from 'constants/chainInfo'
import { useWeb3React } from '@web3-react/core'
import { getChainInfoOrDefault } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import useTheme from 'hooks/useTheme'
import { darken } from 'polished'
import { NavLink } from 'react-router-dom'
import { Text } from 'rebass'
import { useShowClaimPopup, useToggleSelfClaimModal } from 'state/application/hooks'
import { useUserHasAvailableClaim } from 'state/claim/hooks'
import { useNativeCurrencyBalances } from 'state/connection/hooks'
import { useUserHasSubmittedClaim } from 'state/transactions/hooks'
import { useDarkModeManager } from 'state/user/hooks'
import { useNativeCurrencyBalances } from 'state/wallet/hooks'
import styled from 'styled-components/macro'
import { isChainAllowed } from 'utils/switchChain'
import { ReactComponent as Logo } from '../../assets/svg/logo.svg'
import { ExternalLink, ThemedText } from '../../theme'
@@ -99,7 +98,7 @@ const HeaderLinks = styled(Row)`
overflow: auto;
align-items: center;
${({ theme }) => theme.mediaWidth.upToLarge`
justify-self: start;
justify-self: start;
`};
${({ theme }) => theme.mediaWidth.upToMedium`
justify-self: center;
@@ -247,9 +246,7 @@ const StyledExternalLink = styled(ExternalLink).attrs({
`
export default function Header() {
const { account, chainId, connector } = useActiveWeb3React()
const chainAllowed = chainId && isChainAllowed(connector, chainId)
const { account, chainId } = useWeb3React()
const userEthBalance = useNativeCurrencyBalances(account ? [account] : [])?.[account ?? '']
const [darkMode] = useDarkModeManager()
@@ -268,7 +265,7 @@ export default function Header() {
const {
infoLink,
nativeCurrency: { symbol: nativeCurrencySymbol },
} = CHAIN_INFO[!chainId || !chainAllowed ? SupportedChainId.MAINNET : chainId]
} = getChainInfoOrDefault(chainId)
return (
<HeaderFrame showBackground={scrollY > 45}>
@@ -284,6 +281,7 @@ export default function Header() {
<Trans>Swap</Trans>
</StyledNavLink>
<StyledNavLink
data-cy="pool-nav-link"
id={`pool-nav-link`}
to={'/pool'}
isActive={(match, { pathname }) =>

View File

@@ -1,22 +1,42 @@
import { Connector } from '@web3-react/types'
import { ConnectionType } from 'connection'
import styled from 'styled-components/macro'
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
import FortmaticIcon from '../../assets/images/fortmaticIcon.png'
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
import { coinbaseWallet, fortmatic, injected, walletConnect } from '../../connectors'
import Identicon from '../Identicon'
export default function StatusIcon({ connector }: { connector: Connector }) {
switch (connector) {
case injected:
return <Identicon />
case walletConnect:
return <img src={WalletConnectIcon} alt="WalletConnect" />
case coinbaseWallet:
return <img src={CoinbaseWalletIcon} alt="Coinbase Wallet" />
case fortmatic:
return <img src={FortmaticIcon} alt="Fortmatic" />
default:
return null
const IconWrapper = styled.div<{ size?: number }>`
${({ theme }) => theme.flexColumnNoWrap};
align-items: center;
justify-content: center;
margin-right: 8px;
& > img,
span {
height: ${({ size }) => (size ? size + 'px' : '32px')};
width: ${({ size }) => (size ? size + 'px' : '32px')};
}
${({ theme }) => theme.mediaWidth.upToMedium`
align-items: flex-end;
`};
`
export default function StatusIcon({ connectionType }: { connectionType: ConnectionType }) {
let image
switch (connectionType) {
case ConnectionType.INJECTED:
image = <Identicon />
break
case ConnectionType.WALLET_CONNECT:
image = <img src={WalletConnectIcon} alt="WalletConnect" />
break
case ConnectionType.COINBASE_WALLET:
image = <img src={CoinbaseWalletIcon} alt="Coinbase Wallet" />
break
case ConnectionType.FORTMATIC:
image = <img src={FortmaticIcon} alt="Fortmatic" />
break
}
return <IconWrapper size={16}>{image}</IconWrapper>
}

View File

@@ -1,5 +1,5 @@
import jazzicon from '@metamask/jazzicon'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import useENSAvatar from 'hooks/useENSAvatar'
import { useLayoutEffect, useMemo, useRef, useState } from 'react'
import styled from 'styled-components/macro'
@@ -19,7 +19,7 @@ const StyledAvatar = styled.img`
`
export default function Identicon() {
const { account } = useActiveWeb3React()
const { account } = useWeb3React()
const { avatar } = useENSAvatar(account ?? undefined)
const [fetchable, setFetchable] = useState(true)

View File

@@ -103,7 +103,7 @@ export default function LiquidityChartRangeInput({
})
const onBrushDomainChangeEnded = useCallback(
(domain, mode) => {
(domain: [number, number], mode: string | undefined) => {
let leftRangeValue = Number(domain[0])
const rightRangeValue = Number(domain[1])

View File

@@ -1,12 +1,12 @@
// eslint-disable-next-line no-restricted-imports
import { t, Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import { PrivacyPolicyModal } from 'components/PrivacyPolicy'
import { L2_CHAIN_IDS } from 'constants/chains'
import { LOCALE_LABEL, SUPPORTED_LOCALES, SupportedLocale } from 'constants/locales'
import { useActiveLocale } from 'hooks/useActiveLocale'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useLocationLinkProps } from 'hooks/useLocationLinkProps'
import React, { useEffect, useRef, useState } from 'react'
import { FunctionComponent, PropsWithChildren, useEffect, useRef, useState } from 'react'
import {
BookOpen,
Check,
@@ -26,7 +26,7 @@ import styled, { css } from 'styled-components/macro'
import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg'
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
import { useModalOpen, useToggleModal } from '../../state/application/hooks'
import { useModalIsOpen, useToggleModal } from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'
import { ExternalLink } from '../../theme'
import { ButtonPrimary } from '../Button'
@@ -207,10 +207,10 @@ function LanguageMenu({ close }: { close: () => void }) {
}
export default function Menu() {
const { account, chainId } = useActiveWeb3React()
const { account, chainId } = useWeb3React()
const node = useRef<HTMLDivElement>()
const open = useModalOpen(ApplicationModal.MENU)
const open = useModalIsOpen(ApplicationModal.MENU)
const toggleMenu = useToggleModal(ApplicationModal.MENU)
useOnClickOutside(node, open ? toggleMenu : undefined)
const togglePrivacyPolicy = useToggleModal(ApplicationModal.PRIVACY_POLICY)
@@ -311,7 +311,7 @@ export default function Menu() {
interface NewMenuProps {
flyoutAlignment?: FlyoutAlignment
ToggleUI?: React.FunctionComponent
ToggleUI?: FunctionComponent<PropsWithChildren<unknown>>
menuItems: {
content: any
link: string
@@ -334,7 +334,7 @@ const ExternalMenuItem = styled(MenuItem)`
export const NewMenu = ({ flyoutAlignment = FlyoutAlignment.RIGHT, ToggleUI, menuItems, ...rest }: NewMenuProps) => {
const node = useRef<HTMLDivElement>()
const open = useModalOpen(ApplicationModal.POOL_OVERVIEW_OPTIONS)
const open = useModalIsOpen(ApplicationModal.POOL_OVERVIEW_OPTIONS)
const toggle = useToggleModal(ApplicationModal.POOL_OVERVIEW_OPTIONS)
useOnClickOutside(node, open ? toggle : undefined)
const ToggleElement = ToggleUI || StyledMenuIcon

View File

@@ -1,5 +1,5 @@
import { Trans } from '@lingui/macro'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import { useContext } from 'react'
import { ArrowUpCircle } from 'react-feather'
import styled, { ThemeContext } from 'styled-components/macro'
@@ -50,7 +50,7 @@ export function SubmittedView({
hash: string | undefined
}) {
const theme = useContext(ThemeContext)
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
return (
<ConfirmOrLoadingWrapper>

View File

@@ -1,7 +1,7 @@
import { Trans } from '@lingui/macro'
import { CHAIN_INFO } from 'constants/chainInfo'
import { useWeb3React } from '@web3-react/core'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { ArrowUpRight } from 'react-feather'
import { useDarkModeManager } from 'state/user/hooks'
import styled from 'styled-components/macro'
@@ -42,6 +42,8 @@ const SHOULD_SHOW_ALERT = {
[SupportedChainId.ARBITRUM_RINKEBY]: true,
[SupportedChainId.POLYGON]: true,
[SupportedChainId.POLYGON_MUMBAI]: true,
[SupportedChainId.CELO]: true,
[SupportedChainId.CELO_ALFAJORES]: true,
}
type NetworkAlertChains = keyof typeof SHOULD_SHOW_ALERT
@@ -54,6 +56,10 @@ const BG_COLORS_BY_DARK_MODE_AND_CHAIN_ID: {
'radial-gradient(100% 93.36% at 0% 6.64%, rgba(160, 108, 247, 0.1) 0%, rgba(82, 32, 166, 0.1) 100%)',
[SupportedChainId.POLYGON_MUMBAI]:
'radial-gradient(100% 93.36% at 0% 6.64%, rgba(160, 108, 247, 0.1) 0%, rgba(82, 32, 166, 0.1) 100%)',
[SupportedChainId.CELO]:
'radial-gradient(182.71% 150.59% at 2.81% 7.69%, rgba(90, 190, 170, 0.15) 0%, rgba(80, 160, 40, 0.15) 100%)',
[SupportedChainId.CELO_ALFAJORES]:
'radial-gradient(182.71% 150.59% at 2.81% 7.69%, rgba(90, 190, 170, 0.15) 0%, rgba(80, 160, 40, 0.15) 100%)',
[SupportedChainId.OPTIMISM]:
'radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.01) 0%, rgba(255, 255, 255, 0.04) 100%),radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.01) 0%, rgba(235, 0, 255, 0.01) 96%)',
[SupportedChainId.OPTIMISTIC_KOVAN]:
@@ -68,6 +74,10 @@ const BG_COLORS_BY_DARK_MODE_AND_CHAIN_ID: {
'radial-gradient(182.71% 205.59% at 2.81% 7.69%, rgba(130, 71, 229, 0.2) 0%, rgba(167, 202, 255, 0.2) 100%)',
[SupportedChainId.POLYGON_MUMBAI]:
'radial-gradient(182.71% 205.59% at 2.81% 7.69%, rgba(130, 71, 229, 0.2) 0%, rgba(167, 202, 255, 0.2) 100%)',
[SupportedChainId.CELO]:
'radial-gradient(182.71% 150.59% at 2.81% 7.69%, rgba(63, 208, 137, 0.15) 0%, rgba(49, 205, 50, 0.15) 100%)',
[SupportedChainId.CELO_ALFAJORES]:
'radial-gradient(182.71% 150.59% at 2.81% 7.69%, rgba(63, 208, 137, 0.15) 0%, rgba(49, 205, 50, 0.15) 100%)',
[SupportedChainId.OPTIMISM]:
'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.1)',
[SupportedChainId.OPTIMISTIC_KOVAN]:
@@ -129,6 +139,8 @@ const StyledArrowUpRight = styled(ArrowUpRight)`
const TEXT_COLORS: { [chainId in NetworkAlertChains]: string } = {
[SupportedChainId.POLYGON]: 'rgba(130, 71, 229)',
[SupportedChainId.POLYGON_MUMBAI]: 'rgba(130, 71, 229)',
[SupportedChainId.CELO]: 'rgba(53, 178, 97)',
[SupportedChainId.CELO_ALFAJORES]: 'rgba(53, 178, 97)',
[SupportedChainId.OPTIMISM]: '#ff3856',
[SupportedChainId.OPTIMISTIC_KOVAN]: '#ff3856',
[SupportedChainId.ARBITRUM_ONE]: '#0490ed',
@@ -140,14 +152,14 @@ function shouldShowAlert(chainId: number | undefined): chainId is NetworkAlertCh
}
export function NetworkAlert() {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const [darkMode] = useDarkModeManager()
if (!shouldShowAlert(chainId)) {
return null
}
const { label, logoUrl, bridge } = CHAIN_INFO[chainId]
const { label, logoUrl, bridge } = getChainInfo(chainId)
const textColor = TEXT_COLORS[chainId]
return bridge ? (

View File

@@ -26,6 +26,7 @@ const Arrow = styled.div`
position: absolute;
width: 8px;
height: 8px;
box-sizing: border-box;
z-index: 9998;
content: '';
@@ -35,7 +36,7 @@ const Arrow = styled.div`
}
&.arrow-top {
bottom: -5px;
bottom: -4px;
::before {
border-top: none;
border-left: none;
@@ -43,7 +44,7 @@ const Arrow = styled.div`
}
&.arrow-bottom {
top: -5px;
top: -4px;
::before {
border-bottom: none;
border-right: none;
@@ -51,7 +52,7 @@ const Arrow = styled.div`
}
&.arrow-left {
right: -5px;
right: -4px;
::before {
border-bottom: none;
@@ -60,7 +61,7 @@ const Arrow = styled.div`
}
&.arrow-right {
left: -5px;
left: -4px;
::before {
border-right: none;
border-top: none;

View File

@@ -1,14 +1,14 @@
import { Trans } from '@lingui/macro'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { sendEvent } from 'components/analytics'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useCallback, useEffect } from 'react'
import { Heart, X } from 'react-feather'
import styled, { keyframes } from 'styled-components/macro'
import tokenLogo from '../../assets/images/token-logo.png'
import {
useModalOpen,
useModalIsOpen,
useShowClaimPopup,
useToggleSelfClaimModal,
useToggleShowClaimPopup,
@@ -55,14 +55,14 @@ const UniToken = styled.img`
`
export default function ClaimPopup() {
const { account } = useActiveWeb3React()
const { account } = useWeb3React()
// dont store these in persisted state yet
const showClaimPopup: boolean = useShowClaimPopup()
const toggleShowClaimPopup = useToggleShowClaimPopup()
// toggle for showing this modal
const showClaimModal = useModalOpen(ApplicationModal.SELF_CLAIM)
const showClaimModal = useModalIsOpen(ApplicationModal.SELF_CLAIM)
const toggleSelfClaimModal = useToggleSelfClaimModal()
const handleToggleSelfClaimModal = useCallback(() => {
sendEvent({

View File

@@ -1,5 +1,5 @@
import { Trans } from '@lingui/macro'
import { CHAIN_INFO } from 'constants/chainInfo'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { useContext } from 'react'
import { AlertCircle } from 'react-feather'
@@ -14,7 +14,7 @@ const RowNoFlex = styled(AutoRow)`
`
export default function FailedNetworkSwitchPopup({ chainId }: { chainId: SupportedChainId }) {
const chainInfo = CHAIN_INFO[chainId]
const chainInfo = getChainInfo(chainId)
const theme = useContext(ThemeContext)
return (

View File

@@ -1,4 +1,4 @@
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import { useContext } from 'react'
import { AlertCircle, CheckCircle } from 'react-feather'
import styled, { ThemeContext } from 'styled-components/macro'
@@ -16,7 +16,7 @@ const RowNoFlex = styled(AutoRow)`
`
export default function TransactionPopup({ hash }: { hash: string }) {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const tx = useTransaction(hash)
const theme = useContext(ThemeContext)

View File

@@ -1,5 +1,5 @@
import { useWeb3React } from '@web3-react/core'
import { SupportedChainId } from 'constants/chains'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import styled from 'styled-components/macro'
import { MEDIA_WIDTHS } from 'theme'
@@ -63,7 +63,7 @@ export default function Popups() {
const urlWarningActive = useURLWarningVisible()
// need extra padding if network is not L1 Ethereum
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const isNotOnMainnet = Boolean(chainId && chainId !== SupportedChainId.MAINNET)
return (

View File

@@ -1,7 +1,7 @@
import { Trans } from '@lingui/macro'
import { CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import JSBI from 'jsbi'
import { transparentize } from 'polished'
import { useState } from 'react'
@@ -13,7 +13,7 @@ import styled from 'styled-components/macro'
import { BIG_INT_ZERO } from '../../constants/misc'
import { useColor } from '../../hooks/useColor'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useTokenBalance } from '../../state/wallet/hooks'
import { useTokenBalance } from '../../state/connection/hooks'
import { currencyId } from '../../utils/currencyId'
import { unwrappedToken } from '../../utils/unwrappedToken'
import { ButtonEmpty, ButtonPrimary, ButtonSecondary } from '../Button'
@@ -42,7 +42,7 @@ interface PositionCardProps {
}
export default function V2PositionCard({ pair, border, stakedBalance }: PositionCardProps) {
const { account } = useActiveWeb3React()
const { account } = useWeb3React()
const currency0 = unwrappedToken(pair.token0)
const currency1 = unwrappedToken(pair.token1)

View File

@@ -1,7 +1,7 @@
import { Trans } from '@lingui/macro'
import { CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import JSBI from 'jsbi'
import { transparentize } from 'polished'
import { useState } from 'react'
@@ -13,7 +13,7 @@ import styled from 'styled-components/macro'
import { BIG_INT_ZERO } from '../../constants/misc'
import { useColor } from '../../hooks/useColor'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useTokenBalance } from '../../state/wallet/hooks'
import { useTokenBalance } from '../../state/connection/hooks'
import { ExternalLink, ThemedText } from '../../theme'
import { currencyId } from '../../utils/currencyId'
import { unwrappedToken } from '../../utils/unwrappedToken'
@@ -46,7 +46,7 @@ interface PositionCardProps {
}
export function MinimalPositionCard({ pair, showUnwrapped = false, border }: PositionCardProps) {
const { account } = useActiveWeb3React()
const { account } = useWeb3React()
const currency0 = showUnwrapped ? pair.token0 : unwrappedToken(pair.token0)
const currency1 = showUnwrapped ? pair.token1 : unwrappedToken(pair.token1)
@@ -158,7 +158,7 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
}
export default function FullPositionCard({ pair, border, stakedBalance }: PositionCardProps) {
const { account } = useActiveWeb3React()
const { account } = useWeb3React()
const currency0 = unwrappedToken(pair.token0)
const currency1 = unwrappedToken(pair.token1)

View File

@@ -8,7 +8,7 @@ import styled from 'styled-components/macro'
import { ExternalLink, ThemedText } from 'theme'
import { isMobile } from 'utils/userAgent'
import { useModalOpen, useTogglePrivacyPolicy } from '../../state/application/hooks'
import { useModalIsOpen, useTogglePrivacyPolicy } from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'
import { AutoColumn } from '../Column'
import Modal from '../Modal'
@@ -70,7 +70,7 @@ const EXTERNAL_APIS = [
),
},
{
name: 'Google Analytics',
name: 'Google Analytics & Amplitude',
description: <Trans>The app logs anonymized usage statistics in order to improve over time.</Trans>,
},
{
@@ -81,7 +81,7 @@ const EXTERNAL_APIS = [
export function PrivacyPolicyModal() {
const node = useRef<HTMLDivElement>()
const open = useModalOpen(ApplicationModal.PRIVACY_POLICY)
const open = useModalIsOpen(ApplicationModal.PRIVACY_POLICY)
const toggle = useTogglePrivacyPolicy()
useEffect(() => {

View File

@@ -1,10 +1,11 @@
import { Protocol } from '@uniswap/router-sdk'
import { Currency, Percent } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk'
import { RoutingDiagramEntry } from 'components/swap/SwapRoute'
import { DAI, USDC_MAINNET, WBTC } from 'constants/tokens'
import { render } from 'test-utils'
import RoutingDiagram, { RoutingDiagramEntry } from './RoutingDiagram'
import RoutingDiagram from './RoutingDiagram'
const percent = (strings: TemplateStringsArray) => new Percent(parseInt(strings[0]), 100)

View File

@@ -3,45 +3,45 @@
exports[`renders multi route 1`] = `
<DocumentFragment>
<div
class="RoutingDiagram__Wrapper-sc-i2tbb-0 ivndgC css-vurnku"
class="RoutingDiagram__Wrapper-sc-o1ook0-0 kfWRgd css-vurnku"
>
<div
class="sc-bdnxRM Row-sc-u7azg8-0 RoutingDiagram__RouteContainerRow-sc-i2tbb-1 lmTMKd hLLNig hDkZVB"
class="sc-bczRLJ Row-sc-nrd8cx-0 RoutingDiagram__RouteContainerRow-sc-o1ook0-1 jITePI cSETNY dmzxCy"
>
CurrencyLogo currency=USDC
<div
class="sc-bdnxRM Row-sc-u7azg8-0 RoutingDiagram__RouteRow-sc-i2tbb-2 lmTMKd hLLNig hUDqOH"
class="sc-bczRLJ Row-sc-nrd8cx-0 RoutingDiagram__RouteRow-sc-o1ook0-2 jITePI cSETNY fclIfk"
>
<div
class="RoutingDiagram__DottedLine-sc-i2tbb-4 cKqYfU"
class="RoutingDiagram__DottedLine-sc-o1ook0-4 iRYKBb"
>
<svg
class="RoutingDiagram__DotColor-sc-i2tbb-5 fhSaBA"
class="RoutingDiagram__DotColor-sc-o1ook0-5 fEbpBT"
>
dot_line.svg
</svg>
</div>
<div
class="Badge-sc-3epor3-0 RoutingDiagram__OpaqueBadge-sc-i2tbb-6 knpfHF gGARxH"
class="Badge-sc-1mhw5si-0 RoutingDiagram__OpaqueBadge-sc-o1ook0-6 gbzyaI iSegVg"
>
<div
class="Badge-sc-3epor3-0 RoutingDiagram__ProtocolBadge-sc-i2tbb-7 knpfHF lbdUti"
class="Badge-sc-1mhw5si-0 RoutingDiagram__ProtocolBadge-sc-o1ook0-7 gbzyaI lidWLN"
>
<div
class="theme__TextWrapper-sc-5lu8um-0 chxxqs RoutingDiagram__BadgeText-sc-i2tbb-8 ijjHig css-15li2d9"
class="theme__TextWrapper-sc-18nh1jk-0 chnOFO RoutingDiagram__BadgeText-sc-o1ook0-8 iwzpuz css-15li2d9"
>
V2
</div>
</div>
<div
class="theme__TextWrapper-sc-5lu8um-0 chxxqs RoutingDiagram__BadgeText-sc-i2tbb-8 ijjHig css-1aekuku"
class="theme__TextWrapper-sc-18nh1jk-0 chnOFO RoutingDiagram__BadgeText-sc-o1ook0-8 iwzpuz css-1aekuku"
style="min-width: auto;"
>
75%
</div>
</div>
<div
class="sc-bdnxRM Row-sc-u7azg8-0 Row__AutoRow-sc-u7azg8-3 iqvZFe hLLNig cUhARX"
class="sc-bczRLJ Row-sc-nrd8cx-0 Row__AutoRow-sc-nrd8cx-3 cYXLjH cSETNY dllMrH"
style="justify-content: space-evenly; z-index: 2;"
width="100%"
>
@@ -51,42 +51,42 @@ exports[`renders multi route 1`] = `
CurrencyLogo currency=DAI
</div>
<div
class="sc-bdnxRM Row-sc-u7azg8-0 RoutingDiagram__RouteContainerRow-sc-i2tbb-1 lmTMKd hLLNig hDkZVB"
class="sc-bczRLJ Row-sc-nrd8cx-0 RoutingDiagram__RouteContainerRow-sc-o1ook0-1 jITePI cSETNY dmzxCy"
>
CurrencyLogo currency=USDC
<div
class="sc-bdnxRM Row-sc-u7azg8-0 RoutingDiagram__RouteRow-sc-i2tbb-2 lmTMKd hLLNig hUDqOH"
class="sc-bczRLJ Row-sc-nrd8cx-0 RoutingDiagram__RouteRow-sc-o1ook0-2 jITePI cSETNY fclIfk"
>
<div
class="RoutingDiagram__DottedLine-sc-i2tbb-4 cKqYfU"
class="RoutingDiagram__DottedLine-sc-o1ook0-4 iRYKBb"
>
<svg
class="RoutingDiagram__DotColor-sc-i2tbb-5 fhSaBA"
class="RoutingDiagram__DotColor-sc-o1ook0-5 fEbpBT"
>
dot_line.svg
</svg>
</div>
<div
class="Badge-sc-3epor3-0 RoutingDiagram__OpaqueBadge-sc-i2tbb-6 knpfHF gGARxH"
class="Badge-sc-1mhw5si-0 RoutingDiagram__OpaqueBadge-sc-o1ook0-6 gbzyaI iSegVg"
>
<div
class="Badge-sc-3epor3-0 RoutingDiagram__ProtocolBadge-sc-i2tbb-7 knpfHF lbdUti"
class="Badge-sc-1mhw5si-0 RoutingDiagram__ProtocolBadge-sc-o1ook0-7 gbzyaI lidWLN"
>
<div
class="theme__TextWrapper-sc-5lu8um-0 chxxqs RoutingDiagram__BadgeText-sc-i2tbb-8 ijjHig css-15li2d9"
class="theme__TextWrapper-sc-18nh1jk-0 chnOFO RoutingDiagram__BadgeText-sc-o1ook0-8 iwzpuz css-15li2d9"
>
V3
</div>
</div>
<div
class="theme__TextWrapper-sc-5lu8um-0 chxxqs RoutingDiagram__BadgeText-sc-i2tbb-8 ijjHig css-1aekuku"
class="theme__TextWrapper-sc-18nh1jk-0 chnOFO RoutingDiagram__BadgeText-sc-o1ook0-8 iwzpuz css-1aekuku"
style="min-width: auto;"
>
25%
</div>
</div>
<div
class="sc-bdnxRM Row-sc-u7azg8-0 Row__AutoRow-sc-u7azg8-3 iqvZFe hLLNig cUhARX"
class="sc-bczRLJ Row-sc-nrd8cx-0 Row__AutoRow-sc-nrd8cx-3 cYXLjH cSETNY dllMrH"
style="justify-content: space-evenly; z-index: 2;"
width="100%"
>
@@ -102,45 +102,45 @@ exports[`renders multi route 1`] = `
exports[`renders single route 1`] = `
<DocumentFragment>
<div
class="RoutingDiagram__Wrapper-sc-i2tbb-0 ivndgC css-vurnku"
class="RoutingDiagram__Wrapper-sc-o1ook0-0 kfWRgd css-vurnku"
>
<div
class="sc-bdnxRM Row-sc-u7azg8-0 RoutingDiagram__RouteContainerRow-sc-i2tbb-1 lmTMKd hLLNig hDkZVB"
class="sc-bczRLJ Row-sc-nrd8cx-0 RoutingDiagram__RouteContainerRow-sc-o1ook0-1 jITePI cSETNY dmzxCy"
>
CurrencyLogo currency=USDC
<div
class="sc-bdnxRM Row-sc-u7azg8-0 RoutingDiagram__RouteRow-sc-i2tbb-2 lmTMKd hLLNig hUDqOH"
class="sc-bczRLJ Row-sc-nrd8cx-0 RoutingDiagram__RouteRow-sc-o1ook0-2 jITePI cSETNY fclIfk"
>
<div
class="RoutingDiagram__DottedLine-sc-i2tbb-4 cKqYfU"
class="RoutingDiagram__DottedLine-sc-o1ook0-4 iRYKBb"
>
<svg
class="RoutingDiagram__DotColor-sc-i2tbb-5 fhSaBA"
class="RoutingDiagram__DotColor-sc-o1ook0-5 fEbpBT"
>
dot_line.svg
</svg>
</div>
<div
class="Badge-sc-3epor3-0 RoutingDiagram__OpaqueBadge-sc-i2tbb-6 knpfHF gGARxH"
class="Badge-sc-1mhw5si-0 RoutingDiagram__OpaqueBadge-sc-o1ook0-6 gbzyaI iSegVg"
>
<div
class="Badge-sc-3epor3-0 RoutingDiagram__ProtocolBadge-sc-i2tbb-7 knpfHF lbdUti"
class="Badge-sc-1mhw5si-0 RoutingDiagram__ProtocolBadge-sc-o1ook0-7 gbzyaI lidWLN"
>
<div
class="theme__TextWrapper-sc-5lu8um-0 chxxqs RoutingDiagram__BadgeText-sc-i2tbb-8 ijjHig css-15li2d9"
class="theme__TextWrapper-sc-18nh1jk-0 chnOFO RoutingDiagram__BadgeText-sc-o1ook0-8 iwzpuz css-15li2d9"
>
V3
</div>
</div>
<div
class="theme__TextWrapper-sc-5lu8um-0 chxxqs RoutingDiagram__BadgeText-sc-i2tbb-8 ijjHig css-1aekuku"
class="theme__TextWrapper-sc-18nh1jk-0 chnOFO RoutingDiagram__BadgeText-sc-o1ook0-8 iwzpuz css-1aekuku"
style="min-width: auto;"
>
100%
</div>
</div>
<div
class="sc-bdnxRM Row-sc-u7azg8-0 Row__AutoRow-sc-u7azg8-3 iqvZFe hLLNig cUhARX"
class="sc-bczRLJ Row-sc-nrd8cx-0 Row__AutoRow-sc-nrd8cx-3 cYXLjH cSETNY dllMrH"
style="justify-content: space-evenly; z-index: 2;"
width="100%"
>
@@ -156,7 +156,7 @@ exports[`renders single route 1`] = `
exports[`renders when no routes are provided 1`] = `
<DocumentFragment>
<div
class="RoutingDiagram__Wrapper-sc-i2tbb-0 ivndgC css-vurnku"
class="RoutingDiagram__Wrapper-sc-o1ook0-0 kfWRgd css-vurnku"
/>
</DocumentFragment>
`;

View File

@@ -1,4 +1,6 @@
import { Currency } from '@uniswap/sdk-core'
import { Currency, Token } from '@uniswap/sdk-core'
import { ElementName, Event, EventName } from 'components/AmplitudeAnalytics/constants'
import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent'
import { AutoColumn } from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo'
import { AutoRow } from 'components/Row'
@@ -31,14 +33,35 @@ const BaseWrapper = styled.div<{ disable?: boolean }>`
filter: ${({ disable }) => disable && 'grayscale(1)'};
`
const formatAnalyticsEventProperties = (
currency: Currency,
tokenAddress: string | undefined,
searchQuery: string,
isAddressSearch: string | false
) => ({
token_symbol: currency?.symbol,
token_chain_id: currency?.chainId,
...(tokenAddress ? { token_address: tokenAddress } : {}),
is_suggested_token: true,
is_selected_from_list: false,
is_imported_by_user: false,
...(isAddressSearch === false
? { search_token_symbol_input: searchQuery }
: { search_token_address_input: isAddressSearch }),
})
export default function CommonBases({
chainId,
onSelect,
selectedCurrency,
searchQuery,
isAddressSearch,
}: {
chainId?: number
selectedCurrency?: Currency | null
onSelect: (currency: Currency) => void
searchQuery: string
isAddressSearch: string | false
}) {
const bases = typeof chainId !== 'undefined' ? COMMON_BASES[chainId] ?? [] : []
@@ -47,19 +70,29 @@ export default function CommonBases({
<AutoRow gap="4px">
{bases.map((currency: Currency) => {
const isSelected = selectedCurrency?.equals(currency)
const tokenAddress = currency instanceof Token ? currency?.address : undefined
return (
<BaseWrapper
tabIndex={0}
onKeyPress={(e) => !isSelected && e.key === 'Enter' && onSelect(currency)}
onClick={() => !isSelected && onSelect(currency)}
disable={isSelected}
<TraceEvent
events={[Event.onClick, Event.onKeyPress]}
name={EventName.TOKEN_SELECTED}
properties={formatAnalyticsEventProperties(currency, tokenAddress, searchQuery, isAddressSearch)}
element={ElementName.COMMON_BASES_CURRENCY_BUTTON}
key={currencyId(currency)}
>
<CurrencyLogoFromList currency={currency} />
<Text fontWeight={500} fontSize={16}>
{currency.symbol}
</Text>
</BaseWrapper>
<BaseWrapper
tabIndex={0}
onKeyPress={(e) => !isSelected && e.key === 'Enter' && onSelect(currency)}
onClick={() => !isSelected && onSelect(currency)}
disable={isSelected}
key={currencyId(currency)}
>
<CurrencyLogoFromList currency={currency} />
<Text fontWeight={500} fontSize={16}>
{currency.symbol}
</Text>
</BaseWrapper>
</TraceEvent>
)
})}
</AutoRow>

View File

@@ -9,13 +9,13 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
style="height: 168px; width: 100%;"
>
<div
class="sc-bdnxRM Row-sc-u7azg8-0 Row__RowBetween-sc-u7azg8-1 styleds__MenuItem-sc-muzgnq-3 lmTMKd hLLNig hzJkYd firMKT token-item-0x6B175474E89094C44Da98b954EedeAC495271d0F"
class="sc-bczRLJ Row-sc-nrd8cx-0 Row__RowBetween-sc-nrd8cx-1 styleds__MenuItem-sc-1xp9ndq-3 jITePI cSETNY ekbhWd dxhejL token-item-0x6B175474E89094C44Da98b954EedeAC495271d0F"
style="position: absolute; left: 0px; top: 0px; height: 56px; width: 100%;"
tabindex="0"
>
CurrencyLogo currency=DAI
<div
class="Column-sc-1r2yyln-0 cYEAJI"
class="Column-sc-1kykgp9-0 gdySCE"
>
<div
class="css-8mokm4"
@@ -24,7 +24,7 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
DAI
</div>
<div
class="theme__TextWrapper-sc-5lu8um-0 gVIOIC css-165qfk5"
class="theme__TextWrapper-sc-18nh1jk-0 gykQyY css-165qfk5"
>
Dai Stablecoin
</div>
@@ -32,13 +32,13 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
<span />
</div>
<div
class="sc-bdnxRM Row-sc-u7azg8-0 Row__RowBetween-sc-u7azg8-1 styleds__MenuItem-sc-muzgnq-3 lmTMKd hLLNig hzJkYd firMKT token-item-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
class="sc-bczRLJ Row-sc-nrd8cx-0 Row__RowBetween-sc-nrd8cx-1 styleds__MenuItem-sc-1xp9ndq-3 jITePI cSETNY ekbhWd dxhejL token-item-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
style="position: absolute; left: 0px; top: 56px; height: 56px; width: 100%;"
tabindex="0"
>
CurrencyLogo currency=USDC
<div
class="Column-sc-1r2yyln-0 cYEAJI"
class="Column-sc-1kykgp9-0 gdySCE"
>
<div
class="css-8mokm4"
@@ -47,7 +47,7 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
USDC
</div>
<div
class="theme__TextWrapper-sc-5lu8um-0 gVIOIC css-165qfk5"
class="theme__TextWrapper-sc-18nh1jk-0 gykQyY css-165qfk5"
>
USD//C
</div>
@@ -55,13 +55,13 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
<span />
</div>
<div
class="sc-bdnxRM Row-sc-u7azg8-0 Row__RowBetween-sc-u7azg8-1 styleds__MenuItem-sc-muzgnq-3 lmTMKd hLLNig hzJkYd firMKT token-item-0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
class="sc-bczRLJ Row-sc-nrd8cx-0 Row__RowBetween-sc-nrd8cx-1 styleds__MenuItem-sc-1xp9ndq-3 jITePI cSETNY ekbhWd dxhejL token-item-0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
style="position: absolute; left: 0px; top: 112px; height: 56px; width: 100%;"
tabindex="0"
>
CurrencyLogo currency=WBTC
<div
class="Column-sc-1r2yyln-0 cYEAJI"
class="Column-sc-1kykgp9-0 gdySCE"
>
<div
class="css-8mokm4"
@@ -70,7 +70,7 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
WBTC
</div>
<div
class="theme__TextWrapper-sc-5lu8um-0 gVIOIC css-165qfk5"
class="theme__TextWrapper-sc-18nh1jk-0 gykQyY css-165qfk5"
>
Wrapped BTC
</div>

View File

@@ -22,20 +22,21 @@ jest.mock(
`CurrencyLogo currency=${currency.symbol}`
)
jest.mock('hooks/useActiveWeb3React', () => {
jest.mock('@web3-react/core', () => {
const web3React = jest.requireActual('@web3-react/core')
return {
__esModule: true,
default: () => ({
useWeb3React: () => ({
account: '123',
active: true,
isActive: true,
}),
...web3React,
}
})
jest.mock('../../../state/wallet/hooks', () => {
jest.mock('../../../state/connection/hooks', () => {
return {
useCurrencyBalance: (currency: Currency) => {
return mockCurrencyAmt[(currency as mockToken).address]
return mockCurrencyAmt[(currency as mockToken)?.address]
},
}
})

View File

@@ -1,8 +1,10 @@
import { Trans } from '@lingui/macro'
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { ElementName, Event, EventName } from 'components/AmplitudeAnalytics/constants'
import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent'
import { LightGreyCard } from 'components/Card'
import QuestionHelper from 'components/QuestionHelper'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import useTheme from 'hooks/useTheme'
import { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react'
import { FixedSizeList } from 'react-window'
@@ -11,9 +13,9 @@ import styled from 'styled-components/macro'
import TokenListLogo from '../../../assets/svg/tokenlist.svg'
import { useIsUserAddedToken } from '../../../hooks/Tokens'
import { useCurrencyBalance } from '../../../state/connection/hooks'
import { useCombinedActiveList } from '../../../state/lists/hooks'
import { WrappedTokenInfo } from '../../../state/lists/wrappedTokenInfo'
import { useCurrencyBalance } from '../../../state/wallet/hooks'
import { ThemedText } from '../../../theme'
import { isTokenOnList } from '../../../utils'
import Column from '../../Column'
@@ -106,6 +108,7 @@ function CurrencyRow({
otherSelected,
style,
showCurrencyAmount,
eventProperties,
}: {
currency: Currency
onSelect: () => void
@@ -113,8 +116,9 @@ function CurrencyRow({
otherSelected: boolean
style: CSSProperties
showCurrencyAmount?: boolean
eventProperties: Record<string, unknown>
}) {
const { account } = useActiveWeb3React()
const { account } = useWeb3React()
const key = currencyKey(currency)
const selectedTokenList = useCombinedActiveList()
const isOnSelectedList = isTokenOnList(selectedTokenList, currency.isToken ? currency : undefined)
@@ -123,35 +127,42 @@ function CurrencyRow({
// only show add or remove buttons if not on selected list
return (
<MenuItem
tabIndex={0}
style={style}
className={`token-item-${key}`}
onKeyPress={(e) => (!isSelected && e.key === 'Enter' ? onSelect() : null)}
onClick={() => (isSelected ? null : onSelect())}
disabled={isSelected}
selected={otherSelected}
<TraceEvent
events={[Event.onClick, Event.onKeyPress]}
name={EventName.TOKEN_SELECTED}
properties={{ is_imported_by_user: customAdded, ...eventProperties }}
element={ElementName.TOKEN_SELECTOR_ROW}
>
<CurrencyLogo currency={currency} size={'24px'} />
<Column>
<Text title={currency.name} fontWeight={500}>
{currency.symbol}
</Text>
<ThemedText.DarkGray ml="0px" fontSize={'12px'} fontWeight={300}>
{!currency.isNative && !isOnSelectedList && customAdded ? (
<Trans>{currency.name} Added by user</Trans>
) : (
currency.name
)}
</ThemedText.DarkGray>
</Column>
<TokenTags currency={currency} />
{showCurrencyAmount && (
<RowFixed style={{ justifySelf: 'flex-end' }}>
{balance ? <Balance balance={balance} /> : account ? <Loader /> : null}
</RowFixed>
)}
</MenuItem>
<MenuItem
tabIndex={0}
style={style}
className={`token-item-${key}`}
onKeyPress={(e) => (!isSelected && e.key === 'Enter' ? onSelect() : null)}
onClick={() => (isSelected ? null : onSelect())}
disabled={isSelected}
selected={otherSelected}
>
<CurrencyLogo currency={currency} size={'24px'} />
<Column>
<Text title={currency.name} fontWeight={500}>
{currency.symbol}
</Text>
<ThemedText.DarkGray ml="0px" fontSize={'12px'} fontWeight={300}>
{!currency.isNative && !isOnSelectedList && customAdded ? (
<Trans>{currency.name} Added by user</Trans>
) : (
currency.name
)}
</ThemedText.DarkGray>
</Column>
<TokenTags currency={currency} />
{showCurrencyAmount && (
<RowFixed style={{ justifySelf: 'flex-end' }}>
{balance ? <Balance balance={balance} /> : account ? <Loader /> : null}
</RowFixed>
)}
</MenuItem>
</TraceEvent>
)
}
@@ -186,6 +197,31 @@ function BreakLineComponent({ style }: { style: CSSProperties }) {
)
}
interface TokenRowProps {
data: Array<Currency | BreakLine>
index: number
style: CSSProperties
}
const formatAnalyticsEventProperties = (
token: Token,
index: number,
data: any[],
searchQuery: string,
isAddressSearch: string | false
) => ({
token_symbol: token?.symbol,
token_address: token?.address,
is_suggested_token: false,
is_selected_from_list: true,
scroll_position: '',
token_list_index: index,
token_list_length: data.length,
...(isAddressSearch === false
? { search_token_symbol_input: searchQuery }
: { search_token_address_input: isAddressSearch }),
})
export default function CurrencyList({
height,
currencies,
@@ -198,6 +234,8 @@ export default function CurrencyList({
setImportToken,
showCurrencyAmount,
isLoading,
searchQuery,
isAddressSearch,
}: {
height: number
currencies: Currency[]
@@ -210,6 +248,8 @@ export default function CurrencyList({
setImportToken: (token: Token) => void
showCurrencyAmount?: boolean
isLoading: boolean
searchQuery: string
isAddressSearch: string | false
}) {
const itemData: (Currency | BreakLine)[] = useMemo(() => {
if (otherListTokens && otherListTokens?.length > 0) {
@@ -219,7 +259,7 @@ export default function CurrencyList({
}, [currencies, otherListTokens])
const Row = useCallback(
function TokenRow({ data, index, style }) {
function TokenRow({ data, index, style }: TokenRowProps) {
const row: Currency | BreakLine = data[index]
if (isBreakLine(row)) {
@@ -257,6 +297,7 @@ export default function CurrencyList({
onSelect={handleSelect}
otherSelected={otherSelected}
showCurrencyAmount={showCurrencyAmount}
eventProperties={formatAnalyticsEventProperties(token, index, data, searchQuery, isAddressSearch)}
/>
)
} else {
@@ -272,6 +313,8 @@ export default function CurrencyList({
showImportView,
showCurrencyAmount,
isLoading,
isAddressSearch,
searchQuery,
]
)

View File

@@ -1,8 +1,10 @@
// eslint-disable-next-line no-restricted-imports
import { t, Trans } from '@lingui/macro'
import { Currency, Token } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { EventName, ModalName } from 'components/AmplitudeAnalytics/constants'
import { Trace } from 'components/AmplitudeAnalytics/Trace'
import { sendEvent } from 'components/analytics'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import useDebounce from 'hooks/useDebounce'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import useTheme from 'hooks/useTheme'
@@ -10,12 +12,12 @@ import useToggle from 'hooks/useToggle'
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
import { getTokenFilter } from 'lib/hooks/useTokenList/filtering'
import { tokenComparator, useSortTokensByQuery } from 'lib/hooks/useTokenList/sorting'
import { KeyboardEvent, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { ChangeEvent, KeyboardEvent, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Edit } from 'react-feather'
import AutoSizer from 'react-virtualized-auto-sizer'
import { FixedSizeList } from 'react-window'
import { Text } from 'rebass'
import { useAllTokenBalances } from 'state/wallet/hooks'
import { useAllTokenBalances } from 'state/connection/hooks'
import styled from 'styled-components/macro'
import { useAllTokens, useIsUserAddedToken, useSearchInactiveTokenLists, useToken } from '../../hooks/Tokens'
@@ -71,7 +73,7 @@ export function CurrencySearch({
showImportView,
setImportToken,
}: CurrencySearchProps) {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const theme = useTheme()
const [tokenLoaderTimerElapsed, setTokenLoaderTimerElapsed] = useState(false)
@@ -116,11 +118,15 @@ export function CurrencySearch({
const native = useNativeCurrency()
const filteredSortedTokensWithETH: Currency[] = useMemo(() => {
if (!native) return filteredSortedTokens
// Use Celo ERC20 Implementation and exclude the native asset
if (!native) {
return filteredSortedTokens
}
const s = debouncedQuery.toLowerCase().trim()
if (native.symbol?.toLowerCase()?.indexOf(s) !== -1) {
return native ? [native, ...filteredSortedTokens] : filteredSortedTokens
// Always bump the native token to the top of the list.
return native ? [native, ...filteredSortedTokens.filter((t) => !t.equals(native))] : filteredSortedTokens
}
return filteredSortedTokens
}, [debouncedQuery, native, filteredSortedTokens])
@@ -140,7 +146,7 @@ export function CurrencySearch({
// manage focus on modal show
const inputRef = useRef<HTMLInputElement>()
const handleInput = useCallback((event) => {
const handleInput = useCallback((event: ChangeEvent<HTMLInputElement>) => {
const input = event.target.value
const checksummedInput = isAddress(input)
setSearchQuery(checksummedInput || input)
@@ -185,76 +191,86 @@ export function CurrencySearch({
}, [])
return (
<ContentWrapper>
<PaddedColumn gap="16px">
<RowBetween>
<Text fontWeight={500} fontSize={16}>
<Trans>Select a token</Trans>
</Text>
<CloseIcon onClick={onDismiss} />
</RowBetween>
<Row>
<SearchInput
type="text"
id="token-search-input"
placeholder={t`Search name or paste address`}
autoComplete="off"
value={searchQuery}
ref={inputRef as RefObject<HTMLInputElement>}
onChange={handleInput}
onKeyDown={handleEnter}
/>
</Row>
{showCommonBases && (
<CommonBases chainId={chainId} onSelect={handleCurrencySelect} selectedCurrency={selectedCurrency} />
<Trace name={EventName.TOKEN_SELECTOR_OPENED} modal={ModalName.TOKEN_SELECTOR} shouldLogImpression={true}>
<ContentWrapper>
<PaddedColumn gap="16px">
<RowBetween>
<Text fontWeight={500} fontSize={16}>
<Trans>Select a token</Trans>
</Text>
<CloseIcon onClick={onDismiss} />
</RowBetween>
<Row>
<SearchInput
type="text"
id="token-search-input"
placeholder={t`Search name or paste address`}
autoComplete="off"
value={searchQuery}
ref={inputRef as RefObject<HTMLInputElement>}
onChange={handleInput}
onKeyDown={handleEnter}
/>
</Row>
{showCommonBases && (
<CommonBases
chainId={chainId}
onSelect={handleCurrencySelect}
selectedCurrency={selectedCurrency}
searchQuery={searchQuery}
isAddressSearch={isAddressSearch}
/>
)}
</PaddedColumn>
<Separator />
{searchToken && !searchTokenIsAdded ? (
<Column style={{ padding: '20px 0', height: '100%' }}>
<ImportRow token={searchToken} showImportView={showImportView} setImportToken={setImportToken} />
</Column>
) : filteredSortedTokens?.length > 0 || filteredInactiveTokens?.length > 0 ? (
<div style={{ flex: '1' }}>
<AutoSizer disableWidth>
{({ height }) => (
<CurrencyList
height={height}
currencies={disableNonToken ? filteredSortedTokens : filteredSortedTokensWithETH}
otherListTokens={filteredInactiveTokens}
onCurrencySelect={handleCurrencySelect}
otherCurrency={otherSelectedCurrency}
selectedCurrency={selectedCurrency}
fixedListRef={fixedList}
showImportView={showImportView}
setImportToken={setImportToken}
showCurrencyAmount={showCurrencyAmount}
isLoading={balancesIsLoading && !tokenLoaderTimerElapsed}
searchQuery={searchQuery}
isAddressSearch={isAddressSearch}
/>
)}
</AutoSizer>
</div>
) : (
<Column style={{ padding: '20px', height: '100%' }}>
<ThemedText.Main color={theme.text3} textAlign="center" mb="20px">
<Trans>No results found.</Trans>
</ThemedText.Main>
</Column>
)}
</PaddedColumn>
<Separator />
{searchToken && !searchTokenIsAdded ? (
<Column style={{ padding: '20px 0', height: '100%' }}>
<ImportRow token={searchToken} showImportView={showImportView} setImportToken={setImportToken} />
</Column>
) : filteredSortedTokens?.length > 0 || filteredInactiveTokens?.length > 0 ? (
<div style={{ flex: '1' }}>
<AutoSizer disableWidth>
{({ height }) => (
<CurrencyList
height={height}
currencies={disableNonToken ? filteredSortedTokens : filteredSortedTokensWithETH}
otherListTokens={filteredInactiveTokens}
onCurrencySelect={handleCurrencySelect}
otherCurrency={otherSelectedCurrency}
selectedCurrency={selectedCurrency}
fixedListRef={fixedList}
showImportView={showImportView}
setImportToken={setImportToken}
showCurrencyAmount={showCurrencyAmount}
isLoading={balancesIsLoading && !tokenLoaderTimerElapsed}
/>
)}
</AutoSizer>
</div>
) : (
<Column style={{ padding: '20px', height: '100%' }}>
<ThemedText.Main color={theme.text3} textAlign="center" mb="20px">
<Trans>No results found.</Trans>
</ThemedText.Main>
</Column>
)}
<Footer>
<Row justify="center">
<ButtonText onClick={showManageView} color={theme.primary1} className="list-token-manage-button">
<RowFixed>
<IconWrapper size="16px" marginRight="6px" stroke={theme.primaryText1}>
<Edit />
</IconWrapper>
<ThemedText.Main color={theme.primaryText1}>
<Trans>Manage Token Lists</Trans>
</ThemedText.Main>
</RowFixed>
</ButtonText>
</Row>
</Footer>
</ContentWrapper>
<Footer>
<Row justify="center">
<ButtonText onClick={showManageView} color={theme.primary1} className="list-token-manage-button">
<RowFixed>
<IconWrapper size="16px" marginRight="6px" stroke={theme.primaryText1}>
<Edit />
</IconWrapper>
<ThemedText.Main color={theme.primaryText1}>
<Trans>Manage Token Lists</Trans>
</ThemedText.Main>
</RowFixed>
</ButtonText>
</Row>
</Footer>
</ContentWrapper>
</Trace>
)
}

View File

@@ -1,6 +1,8 @@
import { Plural, Trans } from '@lingui/macro'
import { Currency, Token } from '@uniswap/sdk-core'
import { TokenList } from '@uniswap/token-lists'
import { ElementName, Event, EventName } from 'components/AmplitudeAnalytics/constants'
import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent'
import { ButtonPrimary } from 'components/Button'
import { AutoColumn } from 'components/Column'
import { RowBetween } from 'components/Row'
@@ -30,6 +32,12 @@ interface ImportProps {
handleCurrencySelect?: (currency: Currency) => void
}
const formatAnalyticsEventProperties = (tokens: Token[]) => ({
token_symbols: tokens.map((token) => token?.symbol),
token_addresses: tokens.map((token) => token?.address),
token_chain_ids: tokens.map((token) => token?.chainId),
})
export function ImportToken(props: ImportProps) {
const { tokens, list, onBack, onDismiss, handleCurrencySelect } = props
const theme = useTheme()
@@ -42,6 +50,7 @@ export function ImportToken(props: ImportProps) {
if (intersection.size > 0) {
return <BlockedToken onBack={onBack} onDismiss={onDismiss} blockedTokens={Array.from(intersection)} />
}
return (
<Wrapper>
<PaddedColumn gap="14px" style={{ width: '100%', flex: '1 1' }}>
@@ -67,18 +76,25 @@ export function ImportToken(props: ImportProps) {
{tokens.map((token) => (
<TokenImportCard token={token} list={list} key={'import' + token.address} />
))}
<ButtonPrimary
altDisabledStyle={true}
$borderRadius="20px"
padding="10px 1rem"
onClick={() => {
tokens.map((token) => addToken(token))
handleCurrencySelect && handleCurrencySelect(tokens[0])
}}
className=".token-dismiss-button"
<TraceEvent
events={[Event.onClick]}
name={EventName.TOKEN_IMPORTED}
properties={formatAnalyticsEventProperties(tokens)}
element={ElementName.IMPORT_TOKEN_BUTTON}
>
<Trans>Import</Trans>
</ButtonPrimary>
<ButtonPrimary
altDisabledStyle={true}
$borderRadius="20px"
padding="10px 1rem"
onClick={() => {
tokens.map((token) => addToken(token))
handleCurrencySelect && handleCurrencySelect(tokens[0])
}}
className=".token-dismiss-button"
>
<Trans>Import</Trans>
</ButtonPrimary>
</TraceEvent>
</AutoColumn>
</Wrapper>
)

View File

@@ -1,14 +1,14 @@
// eslint-disable-next-line no-restricted-imports
import { t, Trans } from '@lingui/macro'
import { TokenList } from '@uniswap/token-lists'
import { useWeb3React } from '@web3-react/core'
import { sendEvent } from 'components/analytics'
import Card from 'components/Card'
import { UNSUPPORTED_LIST_URLS } from 'constants/lists'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useListColor } from 'hooks/useColor'
import parseENSAddress from 'lib/utils/parseENSAddress'
import uriToHttp from 'lib/utils/uriToHttp'
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { ChangeEvent, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { CheckCircle, Settings } from 'react-feather'
import { usePopper } from 'react-popper'
import { useAppDispatch, useAppSelector } from 'state/hooks'
@@ -95,7 +95,7 @@ function listUrlRowHTMLId(listUrl: string) {
}
const ListRow = memo(function ListRow({ listUrl }: { listUrl: string }) {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const listsByUrl = useAppSelector((state) => state.lists.byUrl)
const dispatch = useAppDispatch()
const { current: list, pendingUpdate: pending } = listsByUrl[listUrl]
@@ -242,7 +242,7 @@ export function ManageLists({
setImportList: (list: TokenList) => void
setListUrl: (url: string) => void
}) {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const theme = useTheme()
const [listUrlInput, setListUrlInput] = useState<string>('')
@@ -266,7 +266,7 @@ export function ManageLists({
// sort by active but only if not visible
const activeListUrls = useActiveListUrls()
const handleInput = useCallback((e) => {
const handleInput = useCallback((e: ChangeEvent<HTMLInputElement>) => {
setListUrlInput(e.target.value)
}, [])

View File

@@ -1,12 +1,12 @@
import { Trans } from '@lingui/macro'
import { Token } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import Card from 'components/Card'
import Column from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo'
import Row, { RowBetween, RowFixed } from 'components/Row'
import { useToken } from 'hooks/Tokens'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { RefObject, useCallback, useMemo, useRef, useState } from 'react'
import { ChangeEvent, RefObject, useCallback, useMemo, useRef, useState } from 'react'
import { useRemoveUserAddedToken, useUserAddedTokens } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { ButtonText, ExternalLink, ExternalLinkIcon, ThemedText, TrashIcon } from 'theme'
@@ -44,14 +44,14 @@ export default function ManageTokens({
setModalView: (view: CurrencyModalView) => void
setImportToken: (token: Token) => void
}) {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const [searchQuery, setSearchQuery] = useState<string>('')
const theme = useTheme()
// manage focus on modal show
const inputRef = useRef<HTMLInputElement>()
const handleInput = useCallback((event) => {
const handleInput = useCallback((event: ChangeEvent<HTMLInputElement>) => {
const input = event.target.value
const checksummedInput = isAddress(input)
setSearchQuery(checksummedInput || input)

View File

@@ -1,12 +1,12 @@
import { Trans } from '@lingui/macro'
import { Token } from '@uniswap/sdk-core'
import { TokenList } from '@uniswap/token-lists'
import { useWeb3React } from '@web3-react/core'
import Card from 'components/Card'
import { AutoColumn } from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo'
import ListLogo from 'components/ListLogo'
import { RowFixed } from 'components/Row'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { transparentize } from 'polished'
import { AlertCircle } from 'react-feather'
import styled, { useTheme } from 'styled-components/macro'
@@ -33,7 +33,7 @@ interface TokenImportCardProps {
}
const TokenImportCard = ({ list, token }: TokenImportCardProps) => {
const theme = useTheme()
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
return (
<Card backgroundColor={theme.bg2} padding="2rem">
<AutoColumn gap="10px" justify="center">

View File

@@ -1,16 +1,16 @@
// eslint-disable-next-line no-restricted-imports
import { t, Trans } from '@lingui/macro'
import { Percent } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { sendEvent } from 'components/analytics'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { AUTO_ROUTER_SUPPORTED_CHAINS } from 'lib/hooks/routing/clientSideSmartOrderRouter'
import { isSupportedChainId } from 'lib/hooks/routing/clientSideSmartOrderRouter'
import { useContext, useRef, useState } from 'react'
import { Settings, X } from 'react-feather'
import { Text } from 'rebass'
import styled, { ThemeContext } from 'styled-components/macro'
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
import { useModalOpen, useToggleSettingsMenu } from '../../state/application/hooks'
import { useModalIsOpen, useToggleSettingsMenu } from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'
import { useClientSideRouter, useExpertModeManager } from '../../state/user/hooks'
import { ThemedText } from '../../theme'
@@ -119,10 +119,10 @@ const ModalContentWrapper = styled.div`
`
export default function SettingsTab({ placeholderSlippage }: { placeholderSlippage: Percent }) {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const node = useRef<HTMLDivElement>()
const open = useModalOpen(ApplicationModal.SETTINGS)
const open = useModalIsOpen(ApplicationModal.SETTINGS)
const toggle = useToggleSettingsMenu()
const theme = useContext(ThemeContext)
@@ -199,7 +199,7 @@ export default function SettingsTab({ placeholderSlippage }: { placeholderSlippa
<Text fontWeight={600} fontSize={14}>
<Trans>Interface Settings</Trans>
</Text>
{chainId && AUTO_ROUTER_SUPPORTED_CHAINS.includes(chainId) && (
{isSupportedChainId(chainId) && (
<RowBetween>
<RowFixed>
<ThemedText.Black fontWeight={400} fontSize={14} color={theme.text2}>

View File

@@ -1,4 +1,4 @@
import { useCallback } from 'react'
import { ChangeEvent, useCallback } from 'react'
import styled from 'styled-components/macro'
const StyledRangeInput = styled.input<{ size: number }>`
@@ -106,7 +106,7 @@ export default function Slider({
...rest
}: InputSliderProps) {
const changeCallback = useCallback(
(e) => {
(e: ChangeEvent<HTMLInputElement>) => {
onChange(parseInt(e.target.value))
},
[onChange]

View File

@@ -1,4 +1,4 @@
import React, { memo, useCallback, useRef } from 'react'
import React, { ChangeEvent, memo, useCallback, useRef } from 'react'
import styled from 'styled-components/macro'
const Input = styled.input<{ error?: boolean; fontSize?: string }>`
@@ -77,7 +77,7 @@ export const TextInput = ({
fontSize: string
}) => {
const handleInput = useCallback(
(event) => {
(event: ChangeEvent<HTMLInputElement>) => {
onUserInput(event.target.value)
},
[onUserInput]
@@ -117,7 +117,7 @@ export const ResizingTextArea = memo(
const inputRef = useRef<HTMLTextAreaElement>(document.createElement('textarea'))
const handleInput = useCallback(
(event) => {
(event: ChangeEvent<HTMLTextAreaElement>) => {
inputRef.current.style.height = 'auto'
inputRef.current.style.height = inputRef.current.scrollHeight + 'px'
onUserInput(event.target.value)

View File

@@ -1,16 +1,16 @@
import { useWeb3React } from '@web3-react/core'
import AddressClaimModal from 'components/claim/AddressClaimModal'
import ConnectedAccountBlocked from 'components/ConnectedAccountBlocked'
import useAccountRiskCheck from 'hooks/useAccountRiskCheck'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useModalOpen, useToggleModal } from 'state/application/hooks'
import { useModalIsOpen, useToggleModal } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
export default function TopLevelModals() {
const addressClaimOpen = useModalOpen(ApplicationModal.ADDRESS_CLAIM)
const addressClaimOpen = useModalIsOpen(ApplicationModal.ADDRESS_CLAIM)
const addressClaimToggle = useToggleModal(ApplicationModal.ADDRESS_CLAIM)
const blockedAccountModalOpen = useModalOpen(ApplicationModal.BLOCKED_ACCOUNT)
const { account } = useActiveWeb3React()
const blockedAccountModalOpen = useModalIsOpen(ApplicationModal.BLOCKED_ACCOUNT)
const { account } = useWeb3React()
useAccountRiskCheck(account)
const open = Boolean(blockedAccountModalOpen && account)

View File

@@ -1,15 +1,16 @@
import { Trans } from '@lingui/macro'
import { Currency } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import Badge from 'components/Badge'
import { CHAIN_INFO } from 'constants/chainInfo'
import { L2_CHAIN_IDS, SupportedL2ChainId } from 'constants/chains'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedL2ChainId } from 'constants/chains'
import useCurrencyLogoURIs from 'lib/hooks/useCurrencyLogoURIs'
import { ReactNode, useCallback, useContext, useState } from 'react'
import { AlertCircle, AlertTriangle, ArrowUpCircle, CheckCircle } from 'react-feather'
import { Text } from 'rebass'
import { useIsTransactionConfirmed, useTransaction } from 'state/transactions/hooks'
import styled, { ThemeContext } from 'styled-components/macro'
import { isL2ChainId } from 'utils/chains'
import Circle from '../../assets/images/blue-loader.svg'
import { ExternalLink } from '../../theme'
@@ -96,7 +97,7 @@ function TransactionSubmittedContent({
}) {
const theme = useContext(ThemeContext)
const { connector } = useActiveWeb3React()
const { connector } = useWeb3React()
const token = currencyToAdd?.wrapped
const logoURL = useCurrencyLogoURIs(token)[0]
@@ -182,7 +183,7 @@ export function ConfirmationModalContent({
<Text fontWeight={500} fontSize={16}>
{title}
</Text>
<CloseIcon onClick={onDismiss} />
<CloseIcon onClick={onDismiss} data-cy="confirmation-close-icon" />
</RowBetween>
{topContent()}
</Section>
@@ -232,7 +233,7 @@ function L2Content({
}: {
onDismiss: () => void
hash: string | undefined
chainId: number
chainId: SupportedL2ChainId
currencyToAdd?: Currency | undefined
pendingText: ReactNode
inline?: boolean // not in modal
@@ -248,7 +249,7 @@ function L2Content({
? (transaction.confirmedTime - transaction.addedTime) / 1000
: undefined
const info = CHAIN_INFO[chainId as SupportedL2ChainId]
const info = getChainInfo(chainId)
return (
<Wrapper>
@@ -342,16 +343,14 @@ export default function TransactionConfirmationModal({
content,
currencyToAdd,
}: ConfirmationModalProps) {
const { chainId } = useActiveWeb3React()
const isL2 = Boolean(chainId && L2_CHAIN_IDS.includes(chainId))
const { chainId } = useWeb3React()
if (!chainId) return null
// confirmation screen
return (
<Modal isOpen={isOpen} onDismiss={onDismiss} maxHeight={90}>
{isL2 && (hash || attemptingTxn) ? (
{isL2ChainId(chainId) && (hash || attemptingTxn) ? (
<L2Content chainId={chainId} hash={hash} onDismiss={onDismiss} pendingText={pendingText} />
) : attemptingTxn ? (
<ConfirmationPendingContent onDismiss={onDismiss} pendingText={pendingText} />

View File

@@ -1,8 +1,8 @@
import { Trans } from '@lingui/macro'
import { Percent } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { L2_CHAIN_IDS } from 'constants/chains'
import { DEFAULT_DEADLINE_FROM_NOW } from 'constants/misc'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import ms from 'ms.macro'
import { darken } from 'polished'
import { useContext, useState } from 'react'
@@ -97,7 +97,7 @@ interface TransactionSettingsProps {
const THREE_DAYS_IN_SECONDS = ms`3 days` / 1000
export default function TransactionSettings({ placeholderSlippage }: TransactionSettingsProps) {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const theme = useContext(ThemeContext)
const userSlippageTolerance = useUserSlippageTolerance()

View File

@@ -0,0 +1,36 @@
import { Connector } from '@web3-react/types'
import COINBASE_ICON_URL from 'assets/images/coinbaseWalletIcon.svg'
import { coinbaseWalletConnection, ConnectionType } from 'connection'
import { getConnectionName } from 'connection/utils'
import Option from './Option'
const BASE_PROPS = {
color: '#315CF5',
icon: COINBASE_ICON_URL,
id: 'coinbase-wallet',
}
export function OpenCoinbaseWalletOption() {
const isActive = coinbaseWalletConnection.hooks.useIsActive()
return (
<Option
{...BASE_PROPS}
isActive={isActive}
link="https://go.cb-w.com/mtUDhEZPy1"
header="Open in Coinbase Wallet"
/>
)
}
export function CoinbaseWalletOption({ tryActivation }: { tryActivation: (connector: Connector) => void }) {
const isActive = coinbaseWalletConnection.hooks.useIsActive()
return (
<Option
{...BASE_PROPS}
isActive={isActive}
onClick={() => tryActivation(coinbaseWalletConnection.connector)}
header={getConnectionName(ConnectionType.COINBASE_WALLET)}
/>
)
}

View File

@@ -0,0 +1,24 @@
import { Connector } from '@web3-react/types'
import FORTMATIC_ICON_URL from 'assets/images/fortmaticIcon.png'
import { ConnectionType, fortmaticConnection } from 'connection'
import { getConnectionName } from 'connection/utils'
import Option from './Option'
const BASE_PROPS = {
color: '#6748FF',
icon: FORTMATIC_ICON_URL,
id: 'fortmatic',
}
export function FortmaticOption({ tryActivation }: { tryActivation: (connector: Connector) => void }) {
const isActive = fortmaticConnection.hooks.useIsActive()
return (
<Option
{...BASE_PROPS}
isActive={isActive}
onClick={() => tryActivation(fortmaticConnection.connector)}
header={getConnectionName(ConnectionType.FORTMATIC)}
/>
)
}

View File

@@ -0,0 +1,48 @@
import { Trans } from '@lingui/macro'
import { Connector } from '@web3-react/types'
import INJECTED_ICON_URL from 'assets/images/arrow-right.svg'
import METAMASK_ICON_URL from 'assets/images/metamask.png'
import { ConnectionType, injectedConnection } from 'connection'
import { getConnectionName } from 'connection/utils'
import Option from './Option'
const INJECTED_PROPS = {
color: '#010101',
icon: INJECTED_ICON_URL,
id: 'injected',
}
const METAMASK_PROPS = {
color: '#E8831D',
icon: METAMASK_ICON_URL,
id: 'metamask',
}
export function InstallMetaMaskOption() {
return <Option {...METAMASK_PROPS} header={<Trans>Install MetaMask</Trans>} link={'https://metamask.io/'} />
}
export function MetaMaskOption({ tryActivation }: { tryActivation: (connector: Connector) => void }) {
const isActive = injectedConnection.hooks.useIsActive()
return (
<Option
{...METAMASK_PROPS}
isActive={isActive}
header={getConnectionName(ConnectionType.INJECTED, true)}
onClick={() => tryActivation(injectedConnection.connector)}
/>
)
}
export function InjectedOption({ tryActivation }: { tryActivation: (connector: Connector) => void }) {
const isActive = injectedConnection.hooks.useIsActive()
return (
<Option
{...INJECTED_PROPS}
isActive={isActive}
header={getConnectionName(ConnectionType.INJECTED, false)}
onClick={() => tryActivation(injectedConnection.connector)}
/>
)
}

View File

@@ -1,10 +1,12 @@
import { ElementName, Event, EventName } from 'components/AmplitudeAnalytics/constants'
import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent'
import React from 'react'
import styled from 'styled-components/macro'
import { ExternalLink } from '../../theme'
const InfoCard = styled.button<{ active?: boolean }>`
background-color: ${({ theme, active }) => (active ? theme.bg3 : theme.bg2)};
const InfoCard = styled.button<{ isActive?: boolean }>`
background-color: ${({ theme, isActive }) => (isActive ? theme.bg3 : theme.bg2)};
padding: 1rem;
outline: none;
border: 1px solid;
@@ -13,7 +15,7 @@ const InfoCard = styled.button<{ active?: boolean }>`
&:focus {
box-shadow: 0 0 0 1px ${({ theme }) => theme.primary1};
}
border-color: ${({ theme, active }) => (active ? 'transparent' : theme.bg3)};
border-color: ${({ theme, isActive }) => (isActive ? 'transparent' : theme.bg3)};
`
const OptionCard = styled(InfoCard as any)`
@@ -95,9 +97,9 @@ export default function Option({
onClick = null,
color,
header,
subheader = null,
subheader,
icon,
active = false,
isActive = false,
id,
}: {
link?: string | null
@@ -106,32 +108,45 @@ export default function Option({
onClick?: null | (() => void)
color: string
header: React.ReactNode
subheader: React.ReactNode | null
subheader?: React.ReactNode
icon: string
active?: boolean
isActive?: boolean
id: string
}) {
const content = (
<OptionCardClickable id={id} onClick={onClick} clickable={clickable && !active} active={active}>
<OptionCardLeft>
<HeaderText color={color}>
{active ? (
<CircleWrapper>
<GreenCircle>
<div />
</GreenCircle>
</CircleWrapper>
) : (
''
)}
{header}
</HeaderText>
{subheader && <SubHeader>{subheader}</SubHeader>}
</OptionCardLeft>
<IconWrapper size={size}>
<img src={icon} alt={'Icon'} />
</IconWrapper>
</OptionCardClickable>
<TraceEvent
events={[Event.onClick]}
name={EventName.WALLET_SELECTED}
properties={{ wallet_type: header }}
element={ElementName.WALLET_TYPE_OPTION}
>
<OptionCardClickable
id={id}
onClick={onClick}
clickable={clickable && !isActive}
active={isActive}
data-testid="wallet-modal-option"
>
<OptionCardLeft>
<HeaderText color={color}>
{isActive ? (
<CircleWrapper>
<GreenCircle>
<div />
</GreenCircle>
</CircleWrapper>
) : (
''
)}
{header}
</HeaderText>
{subheader && <SubHeader>{subheader}</SubHeader>}
</OptionCardLeft>
<IconWrapper size={size}>
<img src={icon} alt={'Icon'} />
</IconWrapper>
</OptionCardClickable>
</TraceEvent>
)
if (link) {
return <ExternalLink href={link}>{content}</ExternalLink>

View File

@@ -0,0 +1,24 @@
import { Connector } from '@web3-react/types'
import WALLET_CONNECT_ICON_URL from 'assets/images/walletConnectIcon.svg'
import { ConnectionType, walletConnectConnection } from 'connection'
import { getConnectionName } from 'connection/utils'
import Option from './Option'
const BASE_PROPS = {
color: '#4196FC',
icon: WALLET_CONNECT_ICON_URL,
id: 'wallet-connect',
}
export function WalletConnectOption({ tryActivation }: { tryActivation: (connector: Connector) => void }) {
const isActive = walletConnectConnection.hooks.useIsActive()
return (
<Option
{...BASE_PROPS}
isActive={isActive}
onClick={() => tryActivation(walletConnectConnection.connector)}
header={getConnectionName(ConnectionType.WALLET_CONNECT)}
/>
)
}

View File

@@ -0,0 +1,110 @@
import * as connectionUtils from 'connection/utils'
import { ApplicationModal } from 'state/application/reducer'
import { render, screen } from '../../test-utils'
import WalletModal from './index'
afterEach(() => {
jest.clearAllMocks()
jest.resetModules()
})
const UserAgentMock = jest.requireMock('utils/userAgent')
jest.mock('utils/userAgent', () => ({
isMobile: false,
}))
jest.mock('.../../state/application/hooks', () => {
return {
useModalIsOpen: (_modal: ApplicationModal) => true,
useToggleWalletModal: () => {
return
},
}
})
jest.mock('@web3-react/core', () => {
const web3React = jest.requireActual('@web3-react/core')
return {
useWeb3React: () => ({
account: undefined,
isActive: false,
isActivating: false,
connector: undefined,
}),
...web3React,
}
})
it('loads Wallet Modal on desktop', async () => {
render(<WalletModal pendingTransactions={[]} confirmedTransactions={[]} />)
expect(screen.getByText('Install MetaMask')).toBeInTheDocument()
expect(screen.getByText('Coinbase Wallet')).toBeInTheDocument()
expect(screen.getByText('WalletConnect')).toBeInTheDocument()
expect(screen.getByText('Fortmatic')).toBeInTheDocument()
expect(screen.getAllByTestId('wallet-modal-option')).toHaveLength(4)
})
it('loads Wallet Modal on desktop with generic Injected', async () => {
jest.spyOn(connectionUtils, 'getIsInjected').mockReturnValue(true)
jest.spyOn(connectionUtils, 'getIsMetaMask').mockReturnValue(false)
jest.spyOn(connectionUtils, 'getIsCoinbaseWallet').mockReturnValue(false)
render(<WalletModal pendingTransactions={[]} confirmedTransactions={[]} />)
expect(screen.getByText('Injected')).toBeInTheDocument()
expect(screen.getByText('Coinbase Wallet')).toBeInTheDocument()
expect(screen.getByText('WalletConnect')).toBeInTheDocument()
expect(screen.getByText('Fortmatic')).toBeInTheDocument()
expect(screen.getAllByTestId('wallet-modal-option')).toHaveLength(4)
})
it('loads Wallet Modal on desktop with MetaMask installed', async () => {
jest.spyOn(connectionUtils, 'getIsInjected').mockReturnValue(true)
jest.spyOn(connectionUtils, 'getIsMetaMask').mockReturnValue(true)
jest.spyOn(connectionUtils, 'getIsCoinbaseWallet').mockReturnValue(false)
render(<WalletModal pendingTransactions={[]} confirmedTransactions={[]} />)
expect(screen.getByText('MetaMask')).toBeInTheDocument()
expect(screen.getByText('Coinbase Wallet')).toBeInTheDocument()
expect(screen.getByText('WalletConnect')).toBeInTheDocument()
expect(screen.getByText('Fortmatic')).toBeInTheDocument()
expect(screen.getAllByTestId('wallet-modal-option')).toHaveLength(4)
})
it('loads Wallet Modal on mobile', async () => {
UserAgentMock.isMobile = true
jest.spyOn(connectionUtils, 'getIsInjected').mockReturnValue(false)
jest.spyOn(connectionUtils, 'getIsMetaMask').mockReturnValue(false)
jest.spyOn(connectionUtils, 'getIsCoinbaseWallet').mockReturnValue(false)
render(<WalletModal pendingTransactions={[]} confirmedTransactions={[]} />)
expect(screen.getByText('Open in Coinbase Wallet')).toBeInTheDocument()
expect(screen.getByText('WalletConnect')).toBeInTheDocument()
expect(screen.getByText('Fortmatic')).toBeInTheDocument()
expect(screen.getAllByTestId('wallet-modal-option')).toHaveLength(3)
})
it('loads Wallet Modal on MetaMask browser', async () => {
UserAgentMock.isMobile = true
jest.spyOn(connectionUtils, 'getIsInjected').mockReturnValue(true)
jest.spyOn(connectionUtils, 'getIsMetaMask').mockReturnValue(true)
jest.spyOn(connectionUtils, 'getIsCoinbaseWallet').mockReturnValue(false)
render(<WalletModal pendingTransactions={[]} confirmedTransactions={[]} />)
expect(screen.getByText('MetaMask')).toBeInTheDocument()
expect(screen.getAllByTestId('wallet-modal-option')).toHaveLength(1)
})
it('loads Wallet Modal on Coinbase Wallet browser', async () => {
UserAgentMock.isMobile = true
jest.spyOn(connectionUtils, 'getIsInjected').mockReturnValue(true)
jest.spyOn(connectionUtils, 'getIsMetaMask').mockReturnValue(false)
jest.spyOn(connectionUtils, 'getIsCoinbaseWallet').mockReturnValue(true)
render(<WalletModal pendingTransactions={[]} confirmedTransactions={[]} />)
expect(screen.getByText('Coinbase Wallet')).toBeInTheDocument()
expect(screen.getAllByTestId('wallet-modal-option')).toHaveLength(1)
})

View File

@@ -1,30 +1,33 @@
import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import { Connector } from '@web3-react/types'
import { sendAnalyticsEvent } from 'components/AmplitudeAnalytics'
import { EventName, WALLET_CONNECTION_RESULT } from 'components/AmplitudeAnalytics/constants'
import { sendEvent } from 'components/analytics'
import { AutoColumn } from 'components/Column'
import { AutoRow } from 'components/Row'
import { ConnectionType } from 'connection'
import { getConnection, getConnectionName, getIsCoinbaseWallet, getIsInjected, getIsMetaMask } from 'connection/utils'
import { useCallback, useEffect, useState } from 'react'
import { ArrowLeft } from 'react-feather'
import { updateConnectionError } from 'state/connection/reducer'
import { useAppDispatch, useAppSelector } from 'state/hooks'
import { updateSelectedWallet } from 'state/user/reducer'
import { updateWalletError } from 'state/wallet/reducer'
import styled from 'styled-components/macro'
import { isMobile } from 'utils/userAgent'
import MetamaskIcon from '../../assets/images/metamask.png'
import TallyIcon from '../../assets/images/tally.png'
import { ReactComponent as Close } from '../../assets/images/x.svg'
import { fortmatic, getWalletForConnector, injected } from '../../connectors'
import { SUPPORTED_WALLETS } from '../../constants/wallet'
import { useModalOpen, useWalletModalToggle } from '../../state/application/hooks'
import { useModalIsOpen, useToggleWalletModal } from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'
import { ExternalLink, ThemedText } from '../../theme'
import { isMobile } from '../../utils/userAgent'
import AccountDetails from '../AccountDetails'
import { LightCard } from '../Card'
import Modal from '../Modal'
import Option from './Option'
import { CoinbaseWalletOption, OpenCoinbaseWalletOption } from './CoinbaseWalletOption'
import { FortmaticOption } from './FortmaticOption'
import { InjectedOption, InstallMetaMaskOption, MetaMaskOption } from './InjectedOption'
import PendingView from './PendingView'
import { WalletConnectOption } from './WalletConnectOption'
const CloseIcon = styled.div`
position: absolute;
@@ -123,14 +126,15 @@ export default function WalletModal({
const { connector, account } = useWeb3React()
const [walletView, setWalletView] = useState(WALLET_VIEWS.ACCOUNT)
const [lastActiveWalletAddress, setLastActiveWalletAddress] = useState<string | undefined>(account)
const [pendingConnector, setPendingConnector] = useState<Connector | undefined>()
const pendingError = useAppSelector((state) =>
pendingConnector ? state.wallet.errorByWallet[getWalletForConnector(pendingConnector)] : undefined
pendingConnector ? state.connection.errorByConnectionType[getConnection(pendingConnector).type] : undefined
)
const walletModalOpen = useModalOpen(ApplicationModal.WALLET)
const toggleWalletModal = useWalletModalToggle()
const walletModalOpen = useModalIsOpen(ApplicationModal.WALLET)
const toggleWalletModal = useToggleWalletModal()
const openOptions = useCallback(() => {
setWalletView(WALLET_VIEWS.OPTIONS)
@@ -144,144 +148,104 @@ export default function WalletModal({
useEffect(() => {
if (pendingConnector && walletView !== WALLET_VIEWS.PENDING) {
updateWalletError({ wallet: getWalletForConnector(pendingConnector), error: undefined })
updateConnectionError({ connectionType: getConnection(pendingConnector).type, error: undefined })
setPendingConnector(undefined)
}
}, [pendingConnector, walletView])
// When new wallet is successfully set by the user, trigger logging of Amplitude analytics event.
useEffect(() => {
if (account && account !== lastActiveWalletAddress) {
sendAnalyticsEvent(EventName.WALLET_CONNECT_TXN_COMPLETED, {
result: WALLET_CONNECTION_RESULT.SUCCEEDED,
wallet_address: account,
wallet_type: getConnectionName(getConnection(connector).type, getIsMetaMask()),
// TODO(lynnshaoyu): Send correct is_reconnect value after modifying user state.
})
}
setLastActiveWalletAddress(account)
}, [lastActiveWalletAddress, account, connector])
const tryActivation = useCallback(
async (connector: Connector) => {
const wallet = getWalletForConnector(connector)
const connectionType = getConnection(connector).type
// log selected wallet
sendEvent({
category: 'Wallet',
action: 'Change Wallet',
label: wallet,
label: connectionType,
})
try {
// Fortmatic opens it's own modal on activation to log in. This modal has a tabIndex
// collision into the WalletModal, so we special case by closing the modal.
if (connector === fortmatic) {
if (connectionType === ConnectionType.FORTMATIC) {
toggleWalletModal()
}
setPendingConnector(connector)
setWalletView(WALLET_VIEWS.PENDING)
dispatch(updateWalletError({ wallet, error: undefined }))
dispatch(updateConnectionError({ connectionType, error: undefined }))
await connector.activate()
dispatch(updateSelectedWallet({ wallet }))
dispatch(updateSelectedWallet({ wallet: connectionType }))
} catch (error) {
console.debug(`web3-react connection error: ${error}`)
dispatch(updateWalletError({ wallet, error: error.message }))
dispatch(updateConnectionError({ connectionType, error: error.message }))
sendAnalyticsEvent(EventName.WALLET_CONNECT_TXN_COMPLETED, {
result: WALLET_CONNECTION_RESULT.FAILED,
wallet_type: getConnectionName(connectionType, getIsMetaMask()),
})
}
},
[dispatch, toggleWalletModal]
)
// get wallets user can switch too, depending on device/browser
function getOptions() {
const isMetamask = !!window.ethereum?.isMetaMask
const isTally = !!window.ethereum?.isTally
return Object.keys(SUPPORTED_WALLETS).map((key) => {
const option = SUPPORTED_WALLETS[key]
const isActive = option.connector === connector
const isInjected = getIsInjected()
const isMetaMask = getIsMetaMask()
const isCoinbaseWallet = getIsCoinbaseWallet()
const optionProps = {
active: isActive,
id: `connect-${key}`,
link: option.href,
header: option.name,
color: option.color,
key,
icon: option.iconURL,
const isCoinbaseWalletBrowser = isMobile && isCoinbaseWallet
const isMetaMaskBrowser = isMobile && isMetaMask
const isInjectedMobileBrowser = isCoinbaseWalletBrowser || isMetaMaskBrowser
let injectedOption
if (!isInjected) {
if (!isMobile) {
injectedOption = <InstallMetaMaskOption />
}
// check for mobile options
if (isMobile) {
if (!window.web3 && !window.ethereum && option.mobile) {
return (
<Option
{...optionProps}
onClick={() => {
if (!isActive && !option.href && !!option.connector) {
tryActivation(option.connector)
}
}}
subheader={null}
/>
)
}
return null
} else if (!isCoinbaseWallet) {
if (isMetaMask) {
injectedOption = <MetaMaskOption tryActivation={tryActivation} />
} else {
injectedOption = <InjectedOption tryActivation={tryActivation} />
}
}
// overwrite injected when needed
if (option.connector === injected) {
// don't show injected if there's no injected provider
if (!(window.web3 || window.ethereum)) {
if (option.name === 'MetaMask') {
return (
<Option
id={`connect-${key}`}
key={key}
color={'#E8831D'}
header={<Trans>Install Metamask</Trans>}
subheader={null}
link={'https://metamask.io/'}
icon={MetamaskIcon}
/>
)
} else {
return null //dont want to return install twice
}
}
// don't return metamask if injected provider isn't metamask
else if (option.name === 'MetaMask' && !isMetamask) {
return null
}
// likewise for generic
else if (option.name === 'Injected' && isMetamask) {
return null
} else if (option.name === 'Injected' && isTally) {
return (
<Option
id={`connect-${key}`}
key={key}
onClick={() => {
option.connector === connector
? setWalletView(WALLET_VIEWS.ACCOUNT)
: !option.href && option.connector && tryActivation(option.connector)
}}
color={'#E8831D'}
header={<Trans>Tally</Trans>}
active={option.connector === connector}
subheader={null}
link={null}
icon={TallyIcon}
/>
)
}
}
let coinbaseWalletOption
if (isMobile && !isInjectedMobileBrowser) {
coinbaseWalletOption = <OpenCoinbaseWalletOption />
} else if (!isMobile || isCoinbaseWalletBrowser) {
coinbaseWalletOption = <CoinbaseWalletOption tryActivation={tryActivation} />
}
// return rest of options
return (
!isMobile &&
!option.mobileOnly && (
<Option
{...optionProps}
onClick={() => {
option.connector === connector
? setWalletView(WALLET_VIEWS.ACCOUNT)
: !option.href && option.connector && tryActivation(option.connector)
}}
subheader={null} //use option.descriptio to bring back multi-line
/>
)
)
})
const walletConnectionOption =
(!isInjectedMobileBrowser && <WalletConnectOption tryActivation={tryActivation} />) ?? null
const fortmaticOption = (!isInjectedMobileBrowser && <FortmaticOption tryActivation={tryActivation} />) ?? null
return (
<>
{injectedOption}
{coinbaseWalletOption}
{walletConnectionOption}
{fortmaticOption}
</>
)
}
function getModalContent() {
@@ -334,7 +298,7 @@ export default function WalletModal({
tryActivation={tryActivation}
/>
)}
{walletView !== WALLET_VIEWS.PENDING && <OptionGrid data-cy="option-grid">{getOptions()}</OptionGrid>}
{walletView !== WALLET_VIEWS.PENDING && <OptionGrid data-testid="option-grid">{getOptions()}</OptionGrid>}
{!pendingError && (
<LightCard>
<AutoRow style={{ flexWrap: 'nowrap' }}>

View File

@@ -1,38 +1,21 @@
import { Web3ReactProvider } from '@web3-react/core'
import { Web3ReactHooks, Web3ReactProvider } from '@web3-react/core'
import { Connector } from '@web3-react/types'
import { BACKFILLABLE_WALLETS, getConnectorForWallet, gnosisSafe, network, useConnectors } from 'connectors'
import { ReactNode, useEffect } from 'react'
import { useAppSelector } from 'state/hooks'
const connect = async (connector: Connector) => {
try {
if (connector.connectEagerly) {
await connector.connectEagerly()
} else {
await connector.activate()
}
} catch (error) {
console.debug(`web3-react eager connection error: ${error}`)
}
}
import { Connection } from 'connection'
import { getConnectionName } from 'connection/utils'
import useEagerlyConnect from 'hooks/useEagerlyConnect'
import useOrderedConnections from 'hooks/useOrderedConnections'
import { ReactNode, useMemo } from 'react'
export default function Web3Provider({ children }: { children: ReactNode }) {
const selectedWalletBackfilled = useAppSelector((state) => state.user.selectedWalletBackfilled)
const selectedWallet = useAppSelector((state) => state.user.selectedWallet)
useEagerlyConnect()
const connections = useOrderedConnections()
const connectors: [Connector, Web3ReactHooks][] = connections.map(({ hooks, connector }) => [connector, hooks])
const connectors = useConnectors(selectedWallet)
const key = useMemo(() => connections.map(({ type }: Connection) => getConnectionName(type)).join('-'), [connections])
useEffect(() => {
connect(gnosisSafe)
connect(network)
if (selectedWallet) {
connect(getConnectorForWallet(selectedWallet))
} else if (!selectedWalletBackfilled) {
BACKFILLABLE_WALLETS.map(getConnectorForWallet).forEach(connect)
}
// The dependency list is empty so this is only run once on mount
}, []) // eslint-disable-line react-hooks/exhaustive-deps
return <Web3ReactProvider connectors={connectors}>{children}</Web3ReactProvider>
return (
<Web3ReactProvider connectors={connectors} key={key}>
{children}
</Web3ReactProvider>
)
}

View File

@@ -1,17 +1,20 @@
// eslint-disable-next-line no-restricted-imports
import { t, Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import { Connector } from '@web3-react/types'
import { getWalletForConnector } from 'connectors'
import { ElementName, Event, EventName } from 'components/AmplitudeAnalytics/constants'
import { TraceEvent } from 'components/AmplitudeAnalytics/TraceEvent'
import { getConnection } from 'connection/utils'
import { getIsValidSwapQuote } from 'pages/Swap'
import { darken } from 'polished'
import { useMemo } from 'react'
import { Activity } from 'react-feather'
import { useAppSelector } from 'state/hooks'
import { useDerivedSwapInfo } from 'state/swap/hooks'
import styled, { css } from 'styled-components/macro'
import { isChainAllowed } from 'utils/switchChain'
import { useHasSocks } from '../../hooks/useSocksBalance'
import { useWalletModalToggle } from '../../state/application/hooks'
import { useToggleWalletModal } from '../../state/application/hooks'
import { isTransactionRecent, useAllTransactions } from '../../state/transactions/hooks'
import { TransactionDetails } from '../../state/transactions/types'
import { shortenAddress } from '../../utils'
@@ -21,16 +24,6 @@ import Loader from '../Loader'
import { RowBetween } from '../Row'
import WalletModal from '../WalletModal'
const IconWrapper = styled.div<{ size?: number }>`
${({ theme }) => theme.flexColumnNoWrap};
align-items: center;
justify-content: center;
& > * {
height: ${({ size }) => (size ? size + 'px' : '32px')};
width: ${({ size }) => (size ? size + 'px' : '32px')};
}
`
const Web3StatusGeneric = styled(ButtonSecondary)`
${({ theme }) => theme.flexRowNoWrap}
width: 100%;
@@ -131,18 +124,16 @@ function Sock() {
)
}
function WrappedStatusIcon({ connector }: { connector: Connector }) {
return (
<IconWrapper size={16}>
<StatusIcon connector={connector} />
</IconWrapper>
)
}
function Web3StatusInner() {
const { account, connector, chainId, ENSName } = useWeb3React()
const connectionType = getConnection(connector).type
const {
trade: { state: tradeState, trade },
inputError: swapInputError,
} = useDerivedSwapInfo()
const validSwapQuote = getIsValidSwapQuote(trade, tradeState, swapInputError)
const error = useAppSelector((state) => state.wallet.errorByWallet[getWalletForConnector(connector)])
const error = useAppSelector((state) => state.connection.errorByConnectionType[getConnection(connector).type])
const chainAllowed = chainId && isChainAllowed(connector, chainId)
@@ -157,7 +148,7 @@ function Web3StatusInner() {
const hasPendingTransactions = !!pending.length
const hasSocks = useHasSocks()
const toggleWalletModal = useWalletModalToggle()
const toggleWalletModal = useToggleWalletModal()
if (!chainId) {
return null
@@ -181,7 +172,11 @@ function Web3StatusInner() {
)
} else if (account) {
return (
<Web3StatusConnected id="web3-status-connected" onClick={toggleWalletModal} pending={hasPendingTransactions}>
<Web3StatusConnected
data-testid="web3-status-connected"
onClick={toggleWalletModal}
pending={hasPendingTransactions}
>
{hasPendingTransactions ? (
<RowBetween>
<Text>
@@ -195,16 +190,23 @@ function Web3StatusInner() {
<Text>{ENSName || shortenAddress(account)}</Text>
</>
)}
{!hasPendingTransactions && connector && <WrappedStatusIcon connector={connector} />}
{!hasPendingTransactions && <StatusIcon connectionType={connectionType} />}
</Web3StatusConnected>
)
} else {
return (
<Web3StatusConnect id="connect-wallet" onClick={toggleWalletModal} faded={!account}>
<Text>
<Trans>Connect Wallet</Trans>
</Text>
</Web3StatusConnect>
<TraceEvent
events={[Event.onClick]}
name={EventName.CONNECT_WALLET_BUTTON_CLICKED}
properties={{ received_swap_quote: validSwapQuote }}
element={ElementName.CONNECT_WALLET_BUTTON}
>
<Web3StatusConnect onClick={toggleWalletModal} faded={!account}>
<Text>
<Trans>Connect Wallet</Trans>
</Text>
</Web3StatusConnect>
</TraceEvent>
)
}
}

View File

@@ -1,7 +1,7 @@
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import { useEffect } from 'react'
import { UaEventOptions } from 'react-ga4/types/ga4'
import { RouteComponentProps } from 'react-router-dom'
import { useLocation } from 'react-router-dom'
import { isMobile } from 'utils/userAgent'
import { getCLS, getFCP, getFID, getLCP, Metric } from 'web-vitals'
@@ -63,7 +63,8 @@ function reportWebVitals({ name, delta, id }: Metric) {
}
// tracks web vitals and pageviews
export function useAnalyticsReporter({ pathname, search }: RouteComponentProps['location']) {
export function useAnalyticsReporter() {
const { pathname, search } = useLocation()
useEffect(() => {
getFCP(reportWebVitals)
getFID(reportWebVitals)
@@ -71,7 +72,7 @@ export function useAnalyticsReporter({ pathname, search }: RouteComponentProps['
getCLS(reportWebVitals)
}, [])
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
useEffect(() => {
// cd1 - custom dimension 1 - chainId
googleAnalytics.set({ cd1: chainId ?? 0 })

View File

@@ -1,7 +1,7 @@
import { isAddress } from '@ethersproject/address'
import { Trans } from '@lingui/macro'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import { useState } from 'react'
import { Text } from 'rebass'
import styled from 'styled-components/macro'
@@ -46,7 +46,7 @@ const ConfirmedIcon = styled(ColumnCenter)`
`
export default function AddressClaimModal({ isOpen, onDismiss }: { isOpen: boolean; onDismiss: () => void }) {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
// state for smart contract input
const [typed, setTyped] = useState('')

View File

@@ -1,7 +1,7 @@
import { isAddress } from '@ethersproject/address'
import { Trans } from '@lingui/macro'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import JSBI from 'jsbi'
import { useEffect, useState } from 'react'
import { Text } from 'rebass'
@@ -9,7 +9,7 @@ import styled from 'styled-components/macro'
import Circle from '../../assets/images/blue-loader.svg'
import tokenLogo from '../../assets/images/token-logo.png'
import { useModalOpen, useToggleSelfClaimModal } from '../../state/application/hooks'
import { useModalIsOpen, useToggleSelfClaimModal } from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'
import { useClaimCallback, useUserClaimData, useUserUnclaimedAmount } from '../../state/claim/hooks'
import { useUserHasSubmittedClaim } from '../../state/transactions/hooks'
@@ -48,10 +48,10 @@ const SOCKS_AMOUNT = 1000
const USER_AMOUNT = 400
export default function ClaimModal() {
const isOpen = useModalOpen(ApplicationModal.SELF_CLAIM)
const isOpen = useModalIsOpen(ApplicationModal.SELF_CLAIM)
const toggleClaimModal = useToggleSelfClaimModal()
const { account, chainId } = useActiveWeb3React()
const { account, chainId } = useWeb3React()
// used for UI loading states
const [attempting, setAttempting] = useState<boolean>(false)

View File

@@ -1,7 +1,7 @@
import { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro'
import StakingRewardsJson from '@uniswap/liquidity-staker/build/StakingRewards.json'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import { ReactNode, useState } from 'react'
import styled from 'styled-components/macro'
@@ -34,7 +34,7 @@ interface StakingModalProps {
}
export default function ClaimRewardModal({ isOpen, onDismiss, stakingInfo }: StakingModalProps) {
const { account } = useActiveWeb3React()
const { account } = useWeb3React()
// monitor call to help UI loading state
const addTransaction = useTransactionAdder()

View File

@@ -5,8 +5,8 @@ import styled from 'styled-components/macro'
import { BIG_INT_SECONDS_IN_WEEK } from '../../constants/misc'
import { useColor } from '../../hooks/useColor'
import useStablecoinPrice from '../../hooks/useStablecoinPrice'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import useUSDCPrice from '../../hooks/useUSDCPrice'
import { useV2Pair } from '../../hooks/useV2Pairs'
import { StakingInfo } from '../../state/stake/hooks'
import { StyledInternalLink, ThemedText } from '../../theme'
@@ -104,7 +104,7 @@ export default function PoolCard({ stakingInfo }: { stakingInfo: StakingInfo })
}
// get the USD value of staked WETH
const USDPrice = useUSDCPrice(WETH)
const USDPrice = useStablecoinPrice(WETH)
const valueOfTotalStakedAmountInUSDC =
valueOfTotalStakedAmountInWETH && USDPrice?.quote(valueOfTotalStakedAmountInWETH)

View File

@@ -3,7 +3,7 @@ import { Trans } from '@lingui/macro'
import StakingRewardsJson from '@uniswap/liquidity-staker/build/StakingRewards.json'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import { useV2LiquidityTokenPermit } from 'hooks/useV2LiquidityTokenPermit'
import { useCallback, useState } from 'react'
import styled from 'styled-components/macro'
@@ -53,7 +53,7 @@ interface StakingModalProps {
}
export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiquidityUnstaked }: StakingModalProps) {
const { provider } = useActiveWeb3React()
const { provider } = useWeb3React()
// track and parse user input
const [typedValue, setTypedValue] = useState('')

View File

@@ -1,7 +1,7 @@
import { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro'
import StakingRewardsJson from '@uniswap/liquidity-staker/build/StakingRewards.json'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import { ReactNode, useState } from 'react'
import styled from 'styled-components/macro'
@@ -35,7 +35,7 @@ interface StakingModalProps {
}
export default function UnstakingModal({ isOpen, onDismiss, stakingInfo }: StakingModalProps) {
const { account } = useActiveWeb3React()
const { account } = useWeb3React()
// monitor call to help UI loading state
const addTransaction = useTransactionAdder()

View File

@@ -1,9 +1,9 @@
import { Trans } from '@lingui/macro'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import Card from 'components/Card'
import { LoadingRows } from 'components/Loader/styled'
import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
import { useContext, useMemo } from 'react'
import { InterfaceTrade } from 'state/routing/types'
@@ -24,7 +24,6 @@ interface AdvancedSwapDetailsProps {
trade?: InterfaceTrade<Currency, Currency, TradeType>
allowedSlippage: Percent
syncing?: boolean
hideRouteDiagram?: boolean
hideInfoTooltips?: boolean
}
@@ -53,7 +52,7 @@ export function AdvancedSwapDetails({
hideInfoTooltips = false,
}: AdvancedSwapDetailsProps) {
const theme = useContext(ThemeContext)
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const nativeCurrency = useNativeCurrency()
const { expectedOutputAmount, priceImpact } = useMemo(() => {

View File

@@ -1,5 +1,6 @@
import { Trans } from '@lingui/macro'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import AnimatedDropdown from 'components/AnimatedDropdown'
import Card, { OutlineCard } from 'components/Card'
import { AutoColumn } from 'components/Column'
@@ -7,7 +8,6 @@ import { LoadingOpacityContainer } from 'components/Loader/styled'
import Row, { RowBetween, RowFixed } from 'components/Row'
import { MouseoverTooltipContent } from 'components/Tooltip'
import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { darken } from 'polished'
import { useState } from 'react'
import { ChevronDown, Info } from 'react-feather'
@@ -126,7 +126,7 @@ export default function SwapDetailsDropdown({
allowedSlippage,
}: SwapDetailsInlineProps) {
const theme = useTheme()
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const [showDetails, setShowDetails] = useState(false)
return (

View File

@@ -6,7 +6,7 @@ import { Text } from 'rebass'
import { InterfaceTrade } from 'state/routing/types'
import styled, { ThemeContext } from 'styled-components/macro'
import { useUSDCValue } from '../../hooks/useUSDCPrice'
import { useStablecoinValue } from '../../hooks/useStablecoinPrice'
import { ThemedText } from '../../theme'
import { isAddress, shortenAddress } from '../../utils'
import { computeFiatValuePriceImpact } from '../../utils/computeFiatValuePriceImpact'
@@ -55,8 +55,8 @@ export default function SwapModalHeader({
const [showInverted, setShowInverted] = useState<boolean>(false)
const fiatValueInput = useUSDCValue(trade.inputAmount)
const fiatValueOutput = useUSDCValue(trade.outputAmount)
const fiatValueInput = useStablecoinValue(trade.inputAmount)
const fiatValueOutput = useStablecoinValue(trade.outputAmount)
return (
<AutoColumn gap={'4px'} style={{ marginTop: '1rem' }}>

View File

@@ -3,13 +3,13 @@ import { Protocol } from '@uniswap/router-sdk'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import { FeeAmount } from '@uniswap/v3-sdk'
import { useWeb3React } from '@web3-react/core'
import AnimatedDropdown from 'components/AnimatedDropdown'
import { AutoColumn } from 'components/Column'
import { LoadingRows } from 'components/Loader/styled'
import RoutingDiagram from 'components/RoutingDiagram/RoutingDiagram'
import { AutoRow, RowBetween } from 'components/Row'
import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import useAutoRouterSupported from 'hooks/useAutoRouterSupported'
import { memo, useState } from 'react'
import { Plus } from 'react-feather'
@@ -50,7 +50,7 @@ export default memo(function SwapRoute({ trade, syncing, fixedOpen = false, ...r
const autoRouterSupported = useAutoRouterSupported()
const routes = getTokenPath(trade)
const [open, setOpen] = useState(false)
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const [darkMode] = useDarkModeManager()

View File

@@ -1,6 +1,6 @@
import { Trans } from '@lingui/macro'
import { Currency, Price } from '@uniswap/sdk-core'
import useUSDCPrice from 'hooks/useUSDCPrice'
import useStablecoinPrice from 'hooks/useStablecoinPrice'
import { useCallback, useContext } from 'react'
import { Text } from 'rebass'
import styled, { ThemeContext } from 'styled-components/macro'
@@ -32,7 +32,7 @@ const StyledPriceContainer = styled.button`
export default function TradePrice({ price, showInverted, setShowInverted }: TradePriceProps) {
const theme = useContext(ThemeContext)
const usdcPrice = useUSDCPrice(showInverted ? price.baseCurrency : price.quoteCurrency)
const usdcPrice = useStablecoinPrice(showInverted ? price.baseCurrency : price.quoteCurrency)
/*
* calculate needed amount of decimal prices, for prices between 0.95-1.05 use 4 decimal places
*/

View File

@@ -1,12 +1,12 @@
import { Trans } from '@lingui/macro'
import { Currency } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { ButtonEmpty } from 'components/Button'
import Card, { OutlineCard } from 'components/Card'
import { AutoColumn } from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo'
import Modal from 'components/Modal'
import { AutoRow, RowBetween } from 'components/Row'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useState } from 'react'
import styled from 'styled-components/macro'
import { CloseIcon, ExternalLink, ThemedText, Z_INDEX } from 'theme'
@@ -52,7 +52,7 @@ export default function UnsupportedCurrencyFooter({
show: boolean
currencies: (Currency | undefined | null)[]
}) {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const [showDetails, setShowDetails] = useState(false)
const tokens =

View File

@@ -1,6 +1,6 @@
import { isAddress } from '@ethersproject/address'
import { Trans } from '@lingui/macro'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import { ReactNode, useState } from 'react'
import { X } from 'react-feather'
import styled from 'styled-components/macro'
@@ -8,8 +8,8 @@ import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { UNI } from '../../constants/tokens'
import useENS from '../../hooks/useENS'
import { useTokenBalance } from '../../state/connection/hooks'
import { useDelegateCallback } from '../../state/governance/hooks'
import { useTokenBalance } from '../../state/wallet/hooks'
import { ThemedText } from '../../theme'
import AddressInputPanel from '../AddressInputPanel'
import { ButtonPrimary } from '../Button'
@@ -42,7 +42,7 @@ interface VoteModalProps {
}
export default function DelegateModal({ isOpen, onDismiss, title }: VoteModalProps) {
const { account, chainId } = useActiveWeb3React()
const { account, chainId } = useWeb3React()
// state for delegate input
const [usingDelegate, setUsingDelegate] = useState(false)

View File

@@ -1,5 +1,5 @@
import { Trans } from '@lingui/macro'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import { useContext, useState } from 'react'
import { ArrowUpCircle, X } from 'react-feather'
import styled, { ThemeContext } from 'styled-components/macro'
@@ -41,7 +41,7 @@ interface ExecuteModalProps {
}
export default function ExecuteModal({ isOpen, onDismiss, proposalId }: ExecuteModalProps) {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const executeCallback = useExecuteCallback()
// monitor call to help UI loading state

View File

@@ -1,6 +1,6 @@
import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import { SupportedChainId } from 'constants/chains'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
@@ -37,7 +37,7 @@ const EmptyState = ({ HeaderContent, SubHeaderContent }: EmptyStateProps) => (
)
export default function ProposalEmptyState() {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
if (chainId && chainId !== SupportedChainId.MAINNET) {
return (
<EmptyState

View File

@@ -1,5 +1,5 @@
import { Trans } from '@lingui/macro'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import { useContext, useState } from 'react'
import { ArrowUpCircle, X } from 'react-feather'
import styled, { ThemeContext } from 'styled-components/macro'
@@ -41,7 +41,7 @@ interface QueueModalProps {
}
export default function QueueModal({ isOpen, onDismiss, proposalId }: QueueModalProps) {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const queueCallback = useQueueCallback()
// monitor call to help UI loading state

View File

@@ -1,5 +1,5 @@
import { Trans } from '@lingui/macro'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useWeb3React } from '@web3-react/core'
import { useContext, useState } from 'react'
import { ArrowUpCircle, X } from 'react-feather'
import styled, { ThemeContext } from 'styled-components/macro'
@@ -44,7 +44,7 @@ interface VoteModalProps {
}
export default function VoteModal({ isOpen, onDismiss, proposalId, voteOption }: VoteModalProps) {
const { chainId } = useActiveWeb3React()
const { chainId } = useWeb3React()
const voteCallback = useVoteCallback()
const { votes: availableVotes } = useUserVotes()

100
src/connection/index.ts Normal file
View File

@@ -0,0 +1,100 @@
import { CoinbaseWallet } from '@web3-react/coinbase-wallet'
import { initializeConnector, Web3ReactHooks } from '@web3-react/core'
import { EIP1193 } from '@web3-react/eip1193'
import { GnosisSafe } from '@web3-react/gnosis-safe'
import { MetaMask } from '@web3-react/metamask'
import { Network } from '@web3-react/network'
import { Connector } from '@web3-react/types'
import { WalletConnect } from '@web3-react/walletconnect'
import { SupportedChainId } from 'constants/chains'
import Fortmatic from 'fortmatic'
import UNISWAP_LOGO_URL from '../assets/svg/logo.svg'
import { RPC_URLS } from '../constants/networks'
export enum ConnectionType {
INJECTED = 'INJECTED',
COINBASE_WALLET = 'COINBASE_WALLET',
WALLET_CONNECT = 'WALLET_CONNECT',
FORTMATIC = 'FORTMATIC',
NETWORK = 'NETWORK',
GNOSIS_SAFE = 'GNOSIS_SAFE',
}
export interface Connection {
connector: Connector
hooks: Web3ReactHooks
type: ConnectionType
}
function onError(error: Error) {
console.debug(`web3-react error: ${error}`)
}
const [web3Network, web3NetworkHooks] = initializeConnector<Network>(
(actions) => new Network({ actions, urlMap: RPC_URLS, defaultChainId: 1 })
)
export const networkConnection: Connection = {
connector: web3Network,
hooks: web3NetworkHooks,
type: ConnectionType.NETWORK,
}
const [web3Injected, web3InjectedHooks] = initializeConnector<MetaMask>((actions) => new MetaMask({ actions, onError }))
export const injectedConnection: Connection = {
connector: web3Injected,
hooks: web3InjectedHooks,
type: ConnectionType.INJECTED,
}
const [web3GnosisSafe, web3GnosisSafeHooks] = initializeConnector<GnosisSafe>((actions) => new GnosisSafe({ actions }))
export const gnosisSafeConnection: Connection = {
connector: web3GnosisSafe,
hooks: web3GnosisSafeHooks,
type: ConnectionType.GNOSIS_SAFE,
}
const [web3WalletConnect, web3WalletConnectHooks] = initializeConnector<WalletConnect>(
(actions) =>
new WalletConnect({
actions,
options: {
rpc: RPC_URLS,
qrcode: true,
},
onError,
})
)
export const walletConnectConnection: Connection = {
connector: web3WalletConnect,
hooks: web3WalletConnectHooks,
type: ConnectionType.WALLET_CONNECT,
}
const [web3Fortmatic, web3FortmaticHooks] = initializeConnector<EIP1193>(
(actions) => new EIP1193({ actions, provider: new Fortmatic(process.env.REACT_APP_FORTMATIC_KEY).getProvider() })
)
export const fortmaticConnection: Connection = {
connector: web3Fortmatic,
hooks: web3FortmaticHooks,
type: ConnectionType.FORTMATIC,
}
const [web3CoinbaseWallet, web3CoinbaseWalletHooks] = initializeConnector<CoinbaseWallet>(
(actions) =>
new CoinbaseWallet({
actions,
options: {
url: RPC_URLS[SupportedChainId.MAINNET],
appName: 'Uniswap',
appLogoUrl: UNISWAP_LOGO_URL,
reloadOnDisconnect: false,
},
onError,
})
)
export const coinbaseWalletConnection: Connection = {
connector: web3CoinbaseWallet,
hooks: web3CoinbaseWalletHooks,
type: ConnectionType.COINBASE_WALLET,
}

72
src/connection/utils.ts Normal file
View File

@@ -0,0 +1,72 @@
import { Connector } from '@web3-react/types'
import {
coinbaseWalletConnection,
ConnectionType,
fortmaticConnection,
gnosisSafeConnection,
injectedConnection,
networkConnection,
walletConnectConnection,
} from 'connection'
export function getIsInjected(): boolean {
return Boolean(window.ethereum)
}
export function getIsMetaMask(): boolean {
return window.ethereum?.isMetaMask ?? false
}
export function getIsCoinbaseWallet(): boolean {
return window.ethereum?.isCoinbaseWallet ?? false
}
const CONNECTIONS = [
gnosisSafeConnection,
injectedConnection,
coinbaseWalletConnection,
walletConnectConnection,
fortmaticConnection,
networkConnection,
]
export function getConnection(c: Connector | ConnectionType) {
if (c instanceof Connector) {
const connection = CONNECTIONS.find((connection) => connection.connector === c)
if (!connection) {
throw Error('unsupported connector')
}
return connection
} else {
switch (c) {
case ConnectionType.INJECTED:
return injectedConnection
case ConnectionType.COINBASE_WALLET:
return coinbaseWalletConnection
case ConnectionType.WALLET_CONNECT:
return walletConnectConnection
case ConnectionType.FORTMATIC:
return fortmaticConnection
case ConnectionType.NETWORK:
return networkConnection
case ConnectionType.GNOSIS_SAFE:
return gnosisSafeConnection
}
}
}
export function getConnectionName(connectionType: ConnectionType, isMetaMask?: boolean) {
switch (connectionType) {
case ConnectionType.INJECTED:
return isMetaMask ? 'MetaMask' : 'Injected'
case ConnectionType.COINBASE_WALLET:
return 'Coinbase Wallet'
case ConnectionType.WALLET_CONNECT:
return 'WalletConnect'
case ConnectionType.FORTMATIC:
return 'Fortmatic'
case ConnectionType.NETWORK:
return 'Network'
case ConnectionType.GNOSIS_SAFE:
return 'Gnosis Safe'
}
}

View File

@@ -1,150 +0,0 @@
import { CoinbaseWallet } from '@web3-react/coinbase-wallet'
import { initializeConnector, Web3ReactHooks } from '@web3-react/core'
import { EIP1193 } from '@web3-react/eip1193'
import { GnosisSafe } from '@web3-react/gnosis-safe'
import { MetaMask } from '@web3-react/metamask'
import { Network } from '@web3-react/network'
import { Connector } from '@web3-react/types'
import { WalletConnect } from '@web3-react/walletconnect'
import { SupportedChainId } from 'constants/chains'
import { INFURA_NETWORK_URLS } from 'constants/infura'
import Fortmatic from 'fortmatic'
import { useMemo } from 'react'
import UNISWAP_LOGO_URL from '../assets/svg/logo.svg'
export enum Wallet {
INJECTED = 'INJECTED',
COINBASE_WALLET = 'COINBASE_WALLET',
WALLET_CONNECT = 'WALLET_CONNECT',
FORTMATIC = 'FORTMATIC',
NETWORK = 'NETWORK',
GNOSIS_SAFE = 'GNOSIS_SAFE',
}
export const BACKFILLABLE_WALLETS = [Wallet.COINBASE_WALLET, Wallet.WALLET_CONNECT, Wallet.INJECTED]
export const SELECTABLE_WALLETS = [...BACKFILLABLE_WALLETS, Wallet.FORTMATIC]
function onError(error: Error) {
console.debug(`web3-react error: ${error}`)
}
export function getWalletForConnector(connector: Connector) {
switch (connector) {
case injected:
return Wallet.INJECTED
case coinbaseWallet:
return Wallet.COINBASE_WALLET
case walletConnect:
return Wallet.WALLET_CONNECT
case fortmatic:
return Wallet.FORTMATIC
case network:
return Wallet.NETWORK
case gnosisSafe:
return Wallet.GNOSIS_SAFE
default:
throw Error('unsupported connector')
}
}
export function getConnectorForWallet(wallet: Wallet) {
switch (wallet) {
case Wallet.INJECTED:
return injected
case Wallet.COINBASE_WALLET:
return coinbaseWallet
case Wallet.WALLET_CONNECT:
return walletConnect
case Wallet.FORTMATIC:
return fortmatic
case Wallet.NETWORK:
return network
case Wallet.GNOSIS_SAFE:
return gnosisSafe
}
}
function getHooksForWallet(wallet: Wallet) {
switch (wallet) {
case Wallet.INJECTED:
return injectedHooks
case Wallet.COINBASE_WALLET:
return coinbaseWalletHooks
case Wallet.WALLET_CONNECT:
return walletConnectHooks
case Wallet.FORTMATIC:
return fortmaticHooks
case Wallet.NETWORK:
return networkHooks
case Wallet.GNOSIS_SAFE:
return gnosisSafeHooks
}
}
export const [network, networkHooks] = initializeConnector<Network>(
(actions) => new Network({ actions, urlMap: INFURA_NETWORK_URLS, defaultChainId: 1 })
)
export const [injected, injectedHooks] = initializeConnector<MetaMask>((actions) => new MetaMask({ actions, onError }))
export const [gnosisSafe, gnosisSafeHooks] = initializeConnector<GnosisSafe>((actions) => new GnosisSafe({ actions }))
export const [walletConnect, walletConnectHooks] = initializeConnector<WalletConnect>(
(actions) =>
new WalletConnect({
actions,
options: {
rpc: INFURA_NETWORK_URLS,
qrcode: true,
},
onError,
})
)
export const [fortmatic, fortmaticHooks] = initializeConnector<EIP1193>(
(actions) => new EIP1193({ actions, provider: new Fortmatic(process.env.REACT_APP_FORTMATIC_KEY).getProvider() })
)
export const [coinbaseWallet, coinbaseWalletHooks] = initializeConnector<CoinbaseWallet>(
(actions) =>
new CoinbaseWallet({
actions,
options: {
url: INFURA_NETWORK_URLS[SupportedChainId.MAINNET],
appName: 'Uniswap',
appLogoUrl: UNISWAP_LOGO_URL,
},
onError,
})
)
interface ConnectorListItem {
connector: Connector
hooks: Web3ReactHooks
}
function getConnectorListItemForWallet(wallet: Wallet) {
return {
connector: getConnectorForWallet(wallet),
hooks: getHooksForWallet(wallet),
}
}
export function useConnectors(selectedWallet: Wallet | undefined) {
return useMemo(() => {
const connectors: ConnectorListItem[] = [{ connector: gnosisSafe, hooks: gnosisSafeHooks }]
if (selectedWallet) {
connectors.push(getConnectorListItemForWallet(selectedWallet))
}
connectors.push(
...SELECTABLE_WALLETS.filter((wallet) => wallet !== selectedWallet).map(getConnectorListItemForWallet)
)
connectors.push({ connector: network, hooks: networkHooks })
const web3ReactConnectors: [Connector, Web3ReactHooks][] = connectors.map(({ connector, hooks }) => [
connector,
hooks,
])
return web3ReactConnectors
}, [selectedWallet])
}

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