Compare commits

..

157 Commits

Author SHA1 Message Date
gzeon
2ddf3fe6bd feat: add arbitrum goerli (#5457)
* chore: add ARBITRUM_GOERLI

* chore: add arb goerli custom deployment

* fix: typo

* chore: add arbitrum goerli rpc

* fix: add rpc url for arb goerli

* chore: update providers.ts

* fix: arb goerli everywhere

* fix: declare ARBITRUM_GOERLI as testnet

* chore: add ARBITRUM_GOERLI to theme

* Merge remote-tracking branch 'upstream/main'

* chore: add todos

* fix: patch router

* fix: use bridged goerli usdc

* chore: remove patch

* feat: add usdc to arb goerli default

* chore: bump smart-order-router to 3.4.0

* fix: yarn-deduplicate

* chore: bump sdk-core to 3.1.1

* revert: smart-order-router 2.10.0

* Revert "chore: remove patch"

This reverts commit 84311c1828.

* fix: remove debug log in patch file

* fix: use bridged official Goerli USDC in patch

* fix: revert unnecessary change to yarn.lock

* fix: yarn yarn-deduplicate

* docs: fix comments

* remove patch

* rm rink

* link

---------

Co-authored-by: Vignesh Mohankumar <me@vig.xyz>
2023-03-03 20:29:49 -05:00
Vignesh Mohankumar
79ad611e79 fix: remove $ in hexlified string (#6075)
* fix

* Update cypress/support/ethereum.ts

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>

---------

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>
2023-03-03 20:09:36 -05:00
Zach Pomerantz
e6d0d96ce4 fix: omit gas estimation from token.approve (#6069) 2023-03-03 15:01:34 -08:00
Vignesh Mohankumar
dcb660d052 refactor: variable for goerli in test (#6066) 2023-03-03 17:45:06 -05:00
Vignesh Mohankumar
1f0b8ffaf0 test: disable /send redirect test temporarily (#6070) 2023-03-03 17:42:56 -05:00
Vignesh Mohankumar
c59b3d7d88 build: upgrade sdk-core (#6065)
upgrade
2023-03-03 16:09:35 -05:00
Zach Pomerantz
513c90723b build: upgrade ethers@5.7.2, smart-order-router@3.6.0, widgets@2.43.2 (#5958)
* build: upgrade ethers@5.7.2

* fix: use namehash directly

* fix: TS7030

* build: upgrade widgets version and smart-order-router (#6053)

* upgrade widgets

* build: dedup router packages

---------

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>

* chore: remove deprecated chains (#6054)

* start

* more

* more

* more

* use SupportedChainId from sdk

* add support for arbitrum goerli

* add arbitrum_goerli to jsonrpc list

* local sdk-core

* Revert "add arbitrum_goerli to jsonrpc list"

This reverts commit 51b29764f5.

* add local chain ids

* Revert "add support for arbitrum goerli"

This reverts commit 46cdd15543.

* rm arb goerli in supported chain ids

* autofix

* fix import

* fix import

* fix import

* move back down for sdk-core

* use the supportedchainid

* add-types

* comment

---------

Co-authored-by: Jordan Frankfurt <jordanwfrankfurt@gmail.com>

* combine

---------

Co-authored-by: Vignesh Mohankumar <vignesh@vigneshmohankumar.com>
Co-authored-by: Vignesh Mohankumar <me@vig.xyz>
Co-authored-by: Jordan Frankfurt <jordanwfrankfurt@gmail.com>
2023-03-03 15:18:42 -05:00
eddie
ef388e17d5 test: e2e tsts for goerli (#6063)
* fix: e2e tsts for goerli

* fix: test
2023-03-03 14:20:41 -05:00
Marcus Milton-Ellis
e2f5041707 refactor: Improve A11y (#5913)
* Imporove a11y

* fix cypress test

* Add translations to labels
2023-03-03 10:42:59 -08:00
Jordan Frankfurt
ac8d294ef6 fix: don't show a price impact warning if the user is (un)wrapping (#6059) 2023-03-03 13:04:14 -05:00
Zach Pomerantz
2df0ca178c build: fix vanilla-extract for M1 (#6058) 2023-03-03 09:56:33 -08:00
Jordan Frankfurt
02c970a077 chore: update types to match schema changes (#6062) 2023-03-03 12:10:19 -05:00
cartcrom
a965c3792b feat: Unicons (#6061)
* feat: unicons

* fix: linted

* fix: deduplicate
2023-03-03 11:30:51 -05:00
Charles Bachmeier
e8c689e1d4 feat: Migrate trending nfts endpoint to graphql (#6049)
* defined useTrendingCollections hook and made fields optional

* working trending collections table

* add gql file

* add gql to search suggestions and handle floor wei vs eth

* gql carousel

* fontWeight typo and skip if flag disabled

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-03-01 16:18:17 -08:00
cartcrom
70cd7272a1 fix: hide missing % changes/prices (#6046) 2023-03-01 10:23:06 -05:00
Jack Short
2b5769ac86 chore: removing VE from cards (#6038)
* moving cards to styled components

* converting rows to styled components

* image

* finished images

* handling all media

* removed VE from cards

* no content colors

* removing aspect ratio calc

* responding to all comments
2023-02-28 16:49:51 -05:00
Charles Bachmeier
b811afd134 feat: add new statsig backed feature flag for nft graphql migration (#6044)
* NFT-1113 add new statsig backed feature flag for nft graphql migration

* remove comment

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-28 13:38:10 -08:00
Charles Bachmeier
6131e6bfab chore: Cleanup and refactor significant portion of listing logic (#6042)
* NFT-91 move listing mode to shared

* move expiration setting out of useEffect

* simplify price input color logic

* unused boolean

* simplify removal of local listing market

* handle global same pricing

* undo local market change

* added comment

* undo expiration changes

* remove old listing state logic

* formatting

* small cleanup

* cleanup

* deprecate global listing state

* use stablecoin values

* remove unused pausing functionality

* remove unused pausing functionality

* remove unused warning logic

* remove unused royalty field

* use royalty helper fn

* simplify global vs normal price input

* simplified price setting logic

* price inputs need to respond to global price method changes

* slight simplifcations

* move dynamic price logic to hook

* move utils file

* move enum to shared

* only usdc check

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-28 11:47:22 -08:00
Vignesh Mohankumar
5979635939 chore: update coinbase-wallet sdk (#6037) 2023-02-27 17:18:21 -05:00
dependabot[bot]
5399bdb550 chore(deps): bump @uniswap/widgets from 2.39.0 to 2.40.0 (#6032)
Bumps [@uniswap/widgets](https://github.com/Uniswap/widgets) from 2.39.0 to 2.40.0.
- [Release notes](https://github.com/Uniswap/widgets/releases)
- [Changelog](https://github.com/Uniswap/widgets/blob/main/.releaserc.json)
- [Commits](https://github.com/Uniswap/widgets/compare/v2.39.0...v2.40.0)

---
updated-dependencies:
- dependency-name: "@uniswap/widgets"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-27 15:01:55 -05:00
Charles Bachmeier
1df9da9eff chore: Refactor ListingButton with deprecation of Listing V1 (#6023)
* remove unused wrapper

* remove old listing modal

* simplify button styling

* deprecate outdated listing logic and move button to styled components

* remove unused linting protection

* remove unused functions from sellAssets hook

* undo and save this refactor for different PR

* remove more unused items

* hook dependencies

* more trash cleanup

* styled continue button

* slight continue button tweaks

* cleanup

* add new standard hover state

* no mixed conditionals

* lint

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-27 10:42:30 -08:00
Jordan Frankfurt
b1e6d0ab7a chore: schema changed gen types (#6033)
schema changed gen types
2023-02-27 13:03:20 -05:00
eddie
a7fcbb4cfc revert: dont hideConnectionUI for widget (#6030)
Revert "fix: dont hideConnectionUI for widget (#6010)"

This reverts commit c18522159b.
2023-02-27 09:18:47 -08:00
Moody Salem
a5a6a037e5 ci: references to old id upload (#6034)
fix: references to old id `upload`

`upload` seems to have been changed to `pinata`
2023-02-27 09:09:50 -08:00
Andrew MacPherson
8bfebd37a2 Updating .env.production file to also include the same comment as .env about reporting these public API keys 2023-02-27 10:00:22 -05:00
Vignesh Mohankumar
4029819090 chore: update widget (#6029) 2023-02-24 17:27:08 -05:00
Vignesh Mohankumar
021ae5e74e fix: reset tokens, amounts when switching chains (#6026)
* fix: reset tokens when switching chains on widget

* clear amounts
2023-02-24 16:35:53 -05:00
eddie
2b9720705f feat: log errors on swap page (#6008)
* feat: log error from widget

* fix: upgrade analytics

* feat: add swap error to main Error Boundary too
2023-02-24 12:09:41 -08:00
blairmason
8f1ea32e5e feat: config statsig init to use reverse proxy (#6018)
* config statsig init to use reverse proxy

* formatting fixes

* update statsig proxy endpoints to prod url
2023-02-24 09:28:01 -08:00
eddie
23acb3b395 feat: add statsig feature gate for widget (#6011)
* feat: add statsig feature gate for widget

* feat: move statsig check into featureFlag infra

* fix: move statsig check deeper into feature flag code

* Update src/featureFlags/flags/featureFlags.ts

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>

* Update src/featureFlags/flags/dummyFeatureGate.ts

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>

---------

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>
2023-02-23 12:47:26 -08:00
Charles Bachmeier
772416cc7a feat: add collection blocklist for ip infringement (#6022)
Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-23 10:44:27 -08:00
Jordan Frankfurt
f15e5725f1 fix: token list load issue WEB-2083 (#6019) 2023-02-22 22:24:30 -06:00
Jordan Frankfurt
83c8393f19 fix: back arrow from migration should take you to v3 pool page (#6020) 2023-02-22 22:24:22 -06:00
Jack Short
1348eb3322 fix: token selector correct zIndex on mobile for pwat (#6017) 2023-02-22 20:43:35 -05:00
eddie
a2271ba428 fix: widget defaultChainId (#6012) 2023-02-22 14:14:12 -08:00
Charles Bachmeier
1845cb3b7b chore: remove listv2 feature flag (#6009)
* remove listv2 feature flag

* no longer used styles and icons

* missed one

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-22 09:43:39 -08:00
Jordan Frankfurt
6a02bde8e0 fix: darkmode FoR iframe wrapper background color (#5993)
* fix: darkmode FoR iframe wrapper background color

* pr feedback
2023-02-22 10:38:52 -06:00
eddie
c18522159b fix: dont hideConnectionUI for widget (#6010) 2023-02-22 09:23:56 -06:00
Jack Short
5ea7b1de3f feat: enable pay with any token (#6002) 2023-02-22 08:25:09 -05:00
Charles Bachmeier
6efe8f3260 feat: [ListV2] Several Small Polish Changes (#5998)
* NFT-1075 update floor and last price to use body primary

* NFT-1080 update image thumbnail size and adjusted row accordingly

* NFT-1081 force page refresh on success screen

* NFT-1089 update same price icons

* NFT-1082 show warning colors over focus

* remove unused state var

* replace margin with padding for sticky header

* NFT-1109 properly calc if listing date is over 6mo

* NFT-1076 change listing button text to light and mobile profile bar to backgroundSurface

* NFT-1079 persist row data when listing to multiple markets

* perf improvement

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-21 13:56:26 -08:00
lynn
9ac28a4571 fix: token selector scroll margin (#6005)
* fix

* update tests

* remove gql file

* fix breakng snapshot
2023-02-21 16:40:36 -05:00
eddie
bde1421ffb fix: use ipfs token list as fallback for unsupported list (#6007) 2023-02-21 12:55:40 -08:00
eddie
3dceb45d9e fix: use ipfs token list temporarily (#6006)
fix: use ipfs token list as fallback
2023-02-21 12:19:39 -08:00
Jack Short
7b589561bc fix: not using stablecoin to calc price impact pwat (#5997)
* feat: implementing graphql endpoint

* changing from hook to function call

* initial gql routing works

* feat: initial pwatRouting setup

* sending correct amount

* removing console

* it is working

* sufficient balance

* 0 if no inputCurrency

* removing value to send if erc20

* removing console

* permit2 optional flag

* removing not necessary stuff

* mobile fixes

* overlay needs to be here

* changing swap amount to pool reserves

* refactoring routing logic

* no route found button state

* better price loading for insufficient liquidity

* refactoring graphql routing code

* overflow

* initial comments

* resetting bag status on input currency change

* locking

* done

* remove helper text for eth

* fix: pay with any token routing bug

* reordering button

* zindex

* price updated

* keeping debounce

* feat: adding amplitude events for pwat

* bumping analytics version

* types and hooks

* moving erc20 flag to useSendTransaction

* why did i put it in a hook

* no return

* fix: not using usdc value to calculate price impact

* refactor + loading state
2023-02-21 12:55:35 -05:00
eddie
c9d3dc36b8 feat: introduce statsig and expose dummy feature gate (#5974)
* feat: introduce statsig and expose dummy feature gate

* feat: use the flag for different copy

* feat: use amplitude user ID

* fix: lock file

* feat: upgrade analytics package

* fix: update snapshots

* fix: unit test snapshot

* fix: fix tests and value check

* feat: switch user ID to device ID

* fix: move statsig wrapper

* fix: move statsig wrapper

* fix: e2e tests

* fix: e2e tests

* fix: env var

* fix: env var

* fix: try testEnv

* fix: undo last commit

* fix: e2e tests

* fix: typo

* fix: dont wait for init

* wip

* fix: undo wip change
2023-02-17 17:42:57 -08:00
eddie
ef4d8fc269 fix: change e2e test language (#5999) 2023-02-17 17:29:59 -08:00
Jack Short
ff9cc5cb69 feat: pwat amp events (#5991)
* feat: implementing graphql endpoint

* changing from hook to function call

* initial gql routing works

* feat: initial pwatRouting setup

* sending correct amount

* removing console

* it is working

* sufficient balance

* 0 if no inputCurrency

* removing value to send if erc20

* removing console

* permit2 optional flag

* removing not necessary stuff

* mobile fixes

* overlay needs to be here

* changing swap amount to pool reserves

* refactoring routing logic

* no route found button state

* better price loading for insufficient liquidity

* refactoring graphql routing code

* overflow

* initial comments

* resetting bag status on input currency change

* locking

* done

* remove helper text for eth

* fix: pay with any token routing bug

* reordering button

* zindex

* price updated

* keeping debounce

* feat: adding amplitude events for pwat

* bumping analytics version

* types and hooks

* moving erc20 flag to useSendTransaction

* why did i put it in a hook

* no return
2023-02-17 16:26:57 -05:00
lynn
719fd524ed fix: update user rejected txn errors to stay up to date with widget (#5996)
init
2023-02-17 16:03:26 -05:00
github-actions[bot]
f15dd1e61e chore(i18n): new Crowdin translations (#5855)
chore(i18n): synchronize translations from crowdin [skip ci]

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-02-17 11:12:47 -05:00
Jack Short
8c372c6142 fix: fixing pwat routing edge case (#5987)
* feat: implementing graphql endpoint

* changing from hook to function call

* initial gql routing works

* feat: initial pwatRouting setup

* sending correct amount

* removing console

* it is working

* sufficient balance

* 0 if no inputCurrency

* removing value to send if erc20

* removing console

* permit2 optional flag

* removing not necessary stuff

* mobile fixes

* overlay needs to be here

* changing swap amount to pool reserves

* refactoring routing logic

* no route found button state

* better price loading for insufficient liquidity

* refactoring graphql routing code

* overflow

* initial comments

* resetting bag status on input currency change

* locking

* done

* remove helper text for eth

* fix: pay with any token routing bug

* reordering button

* zindex

* price updated

* keeping debounce
2023-02-16 18:46:42 -05:00
Charles Bachmeier
8c0998bd59 feat: [ListV2] Updated page responsiveness and breakpoints (#5972)
* updated responsiveness for grid

* add sell nfts back header

* input responsiveness

* lint

* remove shrink property

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-16 14:23:26 -08:00
Charles Bachmeier
ad3a4ea808 feat: [ListV2] Proceeds bar polish (#5966)
* update nft info row height

* update proceeds styling

* update text

* move issues to hook

* update br padding

* mobile bar padding

* fix bug where no expiration wouldn't create issue

* build issue

* update bar padding

* reorganize statement

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-16 14:22:54 -08:00
Charles Bachmeier
d30c5173f5 feat: [ListV2] Dropdown and Modal Polish (#5990)
* update market dropdown border and shadow

* duration modal polish

* update twitter button text color

* reduce height of durection switcher

* NFT-1074 update approval modal text styling

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-16 14:02:33 -08:00
Jordan Frankfurt
689100afa2 feat: support moonpay iframe theme (#5986)
* feat: support moonpay iframe theme

* move theme param to body
2023-02-16 14:52:48 -06:00
Charles Bachmeier
d546ffec1c fix: Market icons displaying over dropdown (#5989)
* remove correct market

* fix market icons displaying overtooltip

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-16 12:11:51 -08:00
Jordan Frankfurt
4a72d8835c chore: remove FoR feature flag (#5985)
remove feature flag
2023-02-16 13:02:02 -06:00
eddie
19c6023601 fix: remove gap on tdp bottom bar (#5973)
* fix: remove gap on tdp bottom bar

* fix: adjust bottom and border
2023-02-16 08:43:19 -08:00
Jordan Frankfurt
a4a954c8af fix: update subgraph query to actually fetch all tick data (#5982) 2023-02-15 20:50:36 -06:00
Charles Bachmeier
82dcdcec55 feat: [ListV2] Updated fee tooltip style (#5976)
* updated tooltip style

* never forget your colon

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-15 14:35:07 -08:00
Jack Short
27e20d7230 feat: pay with any token routing (#5959)
* feat: implementing graphql endpoint

* changing from hook to function call

* initial gql routing works

* feat: initial pwatRouting setup

* sending correct amount

* removing console

* it is working

* sufficient balance

* 0 if no inputCurrency

* removing value to send if erc20

* removing console

* permit2 optional flag

* removing not necessary stuff

* mobile fixes

* overlay needs to be here

* changing swap amount to pool reserves

* refactoring routing logic

* no route found button state

* better price loading for insufficient liquidity

* refactoring graphql routing code

* overflow

* initial comments

* resetting bag status on input currency change

* locking

* done

* remove helper text for eth
2023-02-15 16:59:17 -05:00
eddie
95eafbab7d fix: wait for page to load in e2e tests (#5983)
* fix: make FoR announcement click optional in e2e tests

* fix: optional chaining

* fix: build error

* fix: wait for page to load
2023-02-15 13:55:35 -08:00
Jordan Frankfurt
639fe2f73b fix: add default case to switch (#5984) 2023-02-15 13:55:42 -06:00
Vignesh Mohankumar
72cd27f045 fix: allow signing with smart contract wallets (#5970)
* chore: upgrade universal-router-sdk

* dedupe

* update widgets

* empty
2023-02-15 11:30:09 -05:00
eddie
4e64c0e88f fix: round corners of CurrencySearch (#5969) 2023-02-14 15:17:54 -08:00
Charles Bachmeier
fda28d9be3 fix: Mixed success modal wrapping (#5967)
fix mixed success wrapping

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-14 14:19:45 -08:00
cartcrom
bc92af6c15 fix: use latest version of tokenlists over cache (#5975)
fix: use constants in fetch interval callback to prevent updates
2023-02-14 15:48:53 -05:00
lynn
9a257e0ca8 fix: correct copy on TVL (#5968)
fix copy
2023-02-13 19:28:00 -05:00
cartcrom
82646b77dd feat: migrate search tokens to gql (#5802)
* init

* feat: search tokens hook

* feat: search ordering

* feat: separated FungibleToken parsing into sep function

* refactor: memoized search token sorting

* fix: cache waterfall issue

* fix: removed no longer relevant test

* feat: trending tokens from gql (#5805)

* feat: trending tokens from gql

* fix: reverted out-of-scope change

* refactor: remove trendingTokensFetcher

* fix: linted

* fix: removed fetch policy overrides

* fix: loading state cache

* fix: unwrap native trending tokens

* feat: refetch recently searched (#5894)

* feat: trending tokens from gql

* fix: reverted out-of-scope change

* refactor: remove trendingTokensFetcher

* feat: recently searched tokens query

* fix: linted

* feat: combined query function

* feat: recently searched hooks

* feat: combined query

* fix: removed fetch policy overrides

* fix: loading state cache

* fix: empty history loading state

* fix: revert change

* fix: revert unintended nft query change

* refactor: state functions

* fix: removed unnused query var

* fix: unwrap native trending tokens

* feat: remove fungible token type (#5896)

* feat: trending tokens from gql

* fix: reverted out-of-scope change

* refactor: remove trendingTokensFetcher

* feat: recently searched tokens query

* fix: linted

* feat: combined query function

* feat: recently searched hooks

* feat: combined query

* fix: removed fetch policy overrides

* fix: loading state cache

* fix: empty history loading state

* fix: revert change

* fix: revert unintended nft query change

* refactor: state functions

* fix: removed unnused query var

* refactor: remove FungibleToken type

* refactor: use TokenStandard.Native instead of string

* refactor: improve boolean logic readability

* refactor: removed duplicate code

* fix: unwrap native trending tokens

* fix: type error

* fix: duplicate entry bug

* refactor: use undefined instead of null/string cast

* fix: update apollo types

* fix: polygon edge case & polish
2023-02-13 18:45:13 -05:00
Jordan Frankfurt
1992c5de06 fix: don't render LP position when token name or symbol includes a url (#5961) 2023-02-13 13:45:08 -06:00
Charles Bachmeier
0208ccd7d2 feat: [ListV2] Changes to Multiple Market handling and row hovering (#5953)
* add overlay to row hover

* add remove icon on hover

* working expand and collapse icons

* fixed margin

* don't show collpase on nonhovered rows

* display mutliple markets

* remove market

* 0 margin divider

* hide on mobile

* better mobile check

* margin adjustment and hover border radius

* address comments

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-09 11:58:59 -08:00
Jordan Frankfurt
12df4b3981 fix: update some urls to match current page structure (#5957) 2023-02-09 13:35:12 -06:00
Jack Short
3eaeb65b07 feat: implementing permit2 with pay with any token (#5926)
* feat: implementing permit2 with pay with any token

* permit2 hook

* usePayWithAnyTokenHook

* removing ternary operators

* weird export type bug

* resolving merge

* fixing nft test

* styles

* refactoring styles

* reformatting

* price impact warnings

* forgot trans tag

* responding to comments

* fixes

* disabling pay with any token when on the wrong chain

* missing enabled flag

* vertically centering button
2023-02-09 13:41:30 -05:00
Jordan Frankfurt
6df2f3677e fix: use general sort rank instead of only volume rank for row numbers (#5955) 2023-02-09 12:37:13 -06:00
lynn
80edf5a0d6 fix: follow custom design for token selector scrollbar (#5952)
* fix

* remove unused styled component

* update snapshot

* disable scroll on larger currency search modal

* update snapshot
2023-02-09 13:17:58 -05:00
eddie
96f6929127 fix: showCommonBases for widget token selector (#5951) 2023-02-08 16:50:21 -08:00
eddie
4ec95d0927 fix: URL params for widget (#5943)
* fix: URL params for widget

* fix: remove output token from tokenDetails callsite

* fix: combine props, rename initial state values

* fix: better prop types

* fix: rename prop type
2023-02-08 16:50:11 -08:00
yyip-dev
fba6cc9e02 fix: test pr for github<>jira integration (#5950)
* test pr for github<>jira integration

* remove diff
2023-02-08 18:13:04 -05:00
lynn
bc2f68565b fix: Web 1992 token details update blocked token detail pages (#5948)
* init

* Update src/components/Tokens/TokenDetails/index.tsx

Co-authored-by: eddie <66155195+just-toby@users.noreply.github.com>

* oops last commit leads to error :/

---------

Co-authored-by: eddie <66155195+just-toby@users.noreply.github.com>
2023-02-08 18:03:36 -05:00
Zach Pomerantz
f232643d8e feat: upgrade conedison to improve signing (#5947)
* feat: upgrade conedison to improve signing

* fix: deduplicate conedison
2023-02-08 13:32:19 -08:00
lynn
527270e33f fix: handle wallets that don't send default 4001 error code for user rejected txns (#5941)
* init

* add more to comment

* zzmp comments

* regex

* oops

* fix
2023-02-08 13:34:21 -05:00
cartcrom
18cd5ec9d9 fix: default to null when query address is undefined (#5937) 2023-02-08 00:17:39 -05:00
Jordan Frankfurt
5ddb565805 feat: add feedback link to ... menu (#5933)
add feedback link to ... menu
2023-02-07 18:21:00 -06:00
Jack Short
f0b4b92b88 chore: setting up feature flags and boilerplate gql for routing gql endpoint (#5935)
* feat: routing gql endpoint

* adding feature flags

* adding to modal

* adding mocked provider to test

* adding apollo client to mocked providers

* comment
2023-02-07 18:33:41 -05:00
Charles Bachmeier
4d82f9fb3a fix: Handle Different Listing Failure Responses (#5940)
fix: handle different failure responses

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-07 10:26:36 -08:00
Charles Bachmeier
654b26dc54 feat: [ListV2] Enable ListV2 feature flag (#5939)
enable v2 feature flag

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-07 09:05:43 -08:00
Charles Bachmeier
c0753ae52f fix: [ListV2] Better Collection and Listing Retry and Removal Logic (#5938)
* keep approved collection status when removing

* better map name

* improve callback logic

* improve callback logic for collection approval

* reduce zustand calls

* additional zustand reductions

* add back correct check

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-07 08:29:58 -08:00
Charles Bachmeier
16bb9470ae feat: [ListV2] Changes based off Bug Bash (#5936)
* duration wrap

* better styling a different breakpoints

* throw error on listing same price

* don't show spinner on v2

* remove unused check

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-06 19:04:56 -08:00
Jordan Frankfurt
6dcfca24cb fix: remove roll list (#5934) 2023-02-06 20:41:01 -06:00
Charles Bachmeier
9cac9f8299 feat: [ListV2] error and warning states (#5921)
* update error message for price inputs

* add grid and button warning states

* add list below floor warning modal

* only warn for prices 20% below floor

* highlight unentered price in red on button press

* missing dependency

* updated modal name and mobile height

* add new file

* fix column presence

* rookie mistake

* bulk zustand imports

* below floor threshold

* move issue check higher

* cleanup mouseEvent

* rename color var

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-06 13:00:29 -08:00
Charles Bachmeier
5def0dd166 feat: [ListV2] Add retry and remove logic to listings and collection approvals (#5923)
* add remove/retry buttons

* add retry logic functionality

* add scroll to active row

* properly update rejected status

* replace loadingicon with loader

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-06 12:55:05 -08:00
Charles Bachmeier
7229637c4c feat: [ListV2] Update price and duration dropdowns (#5925)
* add custom option and style market dropdown

* working dropdown for price

* hide dropdown on mobile

* update duration dropdown

* themed opacity hover

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-06 12:54:56 -08:00
Charles Bachmeier
f26b09537d feat: [ListV2] Success modal + Twitter Share (#5924)
* added success screen with images

* add listing proceeds

* working return

* working single tweet

* working tweet multiple

* update how we parse twitterName

* add scrollbar styles

* usestablecoinvalue

* math.min

* add collection name backup to tweet

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-06 12:45:13 -08:00
Charles Bachmeier
8f922b665a feat: [ListV2] Add extra bottom padding to collection assets (#5930)
add extra bottom padding to collection assets

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-06 12:43:59 -08:00
Zach Pomerantz
134b1d708f revert: "fix: surface connection error" (#5932)
Revert "fix: surface connection error (#5931)"

This reverts commit e9bddcb670.
2023-02-06 12:20:11 -08:00
Zach Pomerantz
e9bddcb670 fix: surface connection error (#5931)
* fix: surface connection error

* fix: to spec

* fix: sentence case
2023-02-06 15:01:12 -05:00
Callil Capuozzo
19e45fd119 fix: style collect and create a proposal buttons (#5927)
* Add responsive button styles

* Clean up responsive

* Use SmallButtonPrimary

* Allow button to accept padding props

* Fix vote padding and update link

* Hide position NFT in mobile
2023-02-06 12:49:58 -05:00
Noah Zinsmeister
ae4135fa49 fix: block time is 12 seconds now (#5929)
* block time is 12 seconds now

* fix lint error

* Update governance.ts
2023-02-06 11:18:30 -05:00
Jordan Frankfurt
89e438bcc5 chore: update generated types (#5922) 2023-02-03 15:15:03 -06:00
cartcrom
92af2167ee refactor: L2 icons in component (#5901)
* feat: feature complete

* fix: hide l2 icons on most currencylogos

* linted

* refactor: remove unecessary logo container skeleton

* fix: removed todo comment and linted
2023-02-03 14:15:08 -05:00
eddie
db6084d717 feat: widget on tx success callback (#5916)
feat: log SWAP_TRANSACTION_COMPLETED for widget transactions
2023-02-03 10:23:41 -08:00
Jack Short
927d35d59e fix: useStablecoinPrice switching between WETH and ETH (#5904) 2023-02-03 11:05:47 -05:00
eddie
b4e981b2fd fix: landing page styling of widget (#5917) 2023-02-02 18:08:48 -08:00
Zach Pomerantz
967a698178 fix: upgrade @web3-react connectors (#5920) 2023-02-02 16:24:31 -08:00
Zach Pomerantz
7818426b53 fix: use conedison signTypedData (#5875)
* fix: use conedison signTypedData

* chore: rm old signTypedData

* fix: deduplicate
2023-02-02 14:02:45 -08:00
Jordan Frankfurt
93e0054f10 feat: add jest coverage to github action (#5850)
* add jest coverage to github action

* working on action config

* fix yaml syntax
2023-02-02 14:06:15 -06:00
eddie
661d2b6a33 feat: add zIndex to widget theme (#5915)
* feat: create feature flag for swap widget

* feat: add new flag to modal

* fix: missing defaultField usage

* feat: add zIndex to widget theme
2023-02-02 11:28:15 -08:00
eddie
c560b94366 feat: create feature flag for swap widget (#5909)
* feat: create feature flag for swap widget

* feat: add new flag to modal

* fix: missing defaultField usage
2023-02-02 11:26:24 -08:00
Jack Short
93a4f00287 fix: adding usd_value back to trace event (#5912) 2023-02-02 12:05:29 -07:00
Charles Bachmeier
48833f27e3 feat: [ListV2] Connect modal styling to functionality (#5905)
* dynamic styles based on listing status

* connect collection approval to v2 modal

* import cleanup

* add comments

* connect sign listing logic

* correct pending styles

* correct pending status for collection approval

* correct pending status for os listings

* use check from react-feather

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-02-01 13:41:50 -08:00
Jack Short
35a03e2681 fix: using the swap token selector for pay with any token (#5902)
* feat: adds input token quote for nfts

* remove eslint

* correct usdc pricing

* fix: uses swap token selector for pay with any token selector

* check when balances are loaded

* removing token selector

* only showing active tokens in pay with any token
2023-02-01 15:59:06 -05:00
Charles Bachmeier
ac0badfb1d feat: [ListV2] Add mobile styles (#5906)
* correct mobile modal width

* better mobile styling for listing page

* add height to modal
2023-02-01 12:54:55 -08:00
eddie
149b18f02e fix: about footer link weights (#5898) 2023-02-01 09:13:15 -08:00
Jack Short
52a43f3db0 chore: removeing fetch usd price for bag footer (#5903)
* chore: removing fetchUsdprice

* removing console
2023-02-01 12:00:39 -05:00
Jack Short
0a2a46d506 feat: quote token price for nfts (#5897)
* feat: adds input token quote for nfts

* remove eslint

* correct usdc pricing
2023-01-31 15:45:02 -05:00
Charles Bachmeier
a7c1bd4391 feat: [ListV2] Setup styles for Listing Modal (#5870)
* init modal title

* active and closed section headers

* added left border to section

* add tooltip

* complete collection row

* enforce max height

* add sign listings section

* fix top margin for section

* file re-org

* make modal section re-useable and move to its own file

* format

* define section props

* use accentAction for icons

* improved index

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-01-30 12:22:48 -08:00
cartcrom
13221e6935 feat: caching and polling on apollo token queries (#5874)
* fix: caching on apollo token queries

* refactor: rename state variable

* added documentation for state variable purpose

* added documentation for nullish operator usage
2023-01-30 14:53:53 -05:00
lynn
26fc3caa55 fix: Revert "fix: remove gwei indicator", restore block number (#5891)
* Revert "fix: remove gwei indicator (#5873)"

This reverts commit 409ba72f9f.

* add back block number
2023-01-30 14:30:01 -05:00
Charles Bachmeier
6072bb1be0 fix: Broken NFT Details test (#5893)
fix broken NFT test

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-01-30 10:09:45 -08:00
eddie
302af21a22 fix: widget theme integration (#5880)
* fix: add missing colors to widget theme integration

* feat: upgrade widget version

* fix: conedison in jest tests
2023-01-25 17:12:23 -05:00
Rachel-Eichenberger
b61a2d4111 fix: Update footer Github and Discord links (#5841)
Update footer github and discord links

Discord and github links were reversed
2023-01-25 16:15:23 -05:00
Zach Pomerantz
9be26788a2 test: skip flaky universal search test (#5881)
test: skip flaky test
2023-01-24 12:57:08 -05:00
Zach Pomerantz
ed393de481 fix: use wallet modal for widget-prompted connection (#5879)
* fix: use wallet modal for widget-prompted connection

* fix: prevent widget modal

* fix: invoke modal toggle
2023-01-24 12:36:50 -05:00
Zach Pomerantz
cf5c393d97 fix: sanitize legacy signature format (#5878)
Upgrades @uniswap/universal-router-sdk to fix a bug where legacy wallets submitted unhandled legacy signature formats, with suffixes that failed to execute when calling estimateGas.

See ticket CX-140
2023-01-23 09:37:06 -05:00
lynn
68d81a0040 fix: txn header styling for long ens names (#5872)
done fix
2023-01-22 22:00:29 -05:00
Mike Grabowski
53caa51ac3 fix: disconnection error while switching networks with MetaMask (#5871)
* fix: disconnection error while switching networks with MetaMask

* nit: oops

* fix: tests
2023-01-21 02:20:43 +04:00
lynn
409ba72f9f fix: remove gwei indicator (#5873)
* remove polling

* remove dead code

* lint issues
2023-01-20 15:58:30 -05:00
Mike Grabowski
9d9b3dca78 feat: detect Brave Wallet (#5836)
* feat: support brave better

* fix: apply review comments

* address revieW

* chore: fix lint

* chore: add comment
2023-01-21 00:42:43 +04:00
Jack Short
a11c7e9573 feat: select input token for pay with any token (#5865)
* feat: select input token for pay with any token

* creating zustand state

* updating symbol

* cursor pointer
2023-01-20 13:02:43 -05:00
Charles Bachmeier
31bbcae1ed fix: slideout menu title overlap on firefox (#5869)
fix wallet overlap on firefox

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-01-20 12:03:42 -05:00
Zach Pomerantz
a1f6c7270e fix: amend gas estimate error message (#5868)
* fix: amend gas estimate error message

* fix: language

* Update src/utils/swapErrorToUserReadableMessage.tsx

Co-authored-by: Tina <59578595+tinaszheng@users.noreply.github.com>

Co-authored-by: Tina <59578595+tinaszheng@users.noreply.github.com>
2023-01-19 17:48:54 -08:00
Zach Pomerantz
8471d9b46f test: skip token explore filter tests (#5867) 2023-01-19 16:16:00 -08:00
lynn
5fc4d98faa chore: universal search e2e test (#5839)
* init

* change comments

* init

* done

* charlie comments

* fix lint

* fix explore filter test
2023-01-19 18:12:40 -05:00
Zach Pomerantz
8d9ddf36a2 fix: display specific gas estimation error (#5863) 2023-01-19 14:11:01 -08:00
Zach Pomerantz
6cfd5fa475 fix: allow signing from safepal (#5862) 2023-01-19 14:10:03 -08:00
Zach Pomerantz
f2c5a7c09c fix: track universal router swaps and err on wallet-edited calldata (#5864)
fix: track universal router swaps
2023-01-19 13:04:36 -08:00
lynn
fb52770953 chore: token explore filter e2e test (#5835)
* init

* fix

* init

* remove old token test

* fixes

* remove extraneous tokens test

* init

* tests working

* remove unnecessary data-cy
2023-01-19 14:29:05 -05:00
Zach Pomerantz
94aa8ae2c9 fix: avoid error swap state while loading (#5860)
fix: omit price impact warning while loading
2023-01-19 10:41:58 -08:00
Zach Pomerantz
6cb0824a0b fix: rm "approval failed" UI (#5861) 2023-01-19 10:32:04 -08:00
Zach Pomerantz
777887b25d fix: rm faulty revert flow (#5859) 2023-01-19 08:57:14 -08:00
Zach Pomerantz
d15d5d85f5 feat: enable permit2 (#5858) 2023-01-19 10:11:18 -05:00
Zach Pomerantz
43218d5655 feat: retry gas estimate and log to console (#5856) 2023-01-18 20:53:20 -08:00
Zach Pomerantz
a534ba41ed revert: "feat: enable permit2" (#5854)
Revert "feat: enable permit2 (#5833)"

This reverts commit a9ab5717de.
2023-01-18 16:27:09 -08:00
Zach Pomerantz
4715115743 fix: compute price impact off of debounced trade (#5852)
* fix: compute price impact off of debounced trade

* chore: rm console log
2023-01-18 16:23:19 -08:00
Zach Pomerantz
3389d01213 fix: fetch allowances without debouncing (#5853) 2023-01-18 16:04:28 -08:00
Zach Pomerantz
d9a0aa3ff0 fix: use non-warning colors for allowance button (#5851) 2023-01-18 16:03:29 -08:00
github-actions[bot]
e9e5d2e43e chore(i18n): new Crowdin translations (#5627)
chore(i18n): synchronize translations from crowdin [skip ci]

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-01-18 18:57:43 -05:00
Jack Short
d58dc14bd5 feat: initial token selector for pay with any token (#5848)
* feat: initial token selector for pay with any token

* addressing some comments

* addressing comments

* linting
2023-01-18 18:52:17 -05:00
Charles Bachmeier
909e18cb23 chore: convert top level profile page to styled components (#5849)
* move const to shared file

* convert top level page wrappers to styled components

* style empty content

* simplify and wrap empty content and remove unused ve styles

* better margins

* delete unused common style

* isListingNfts bool

* remove comment

* cleanup bools

* import bag_width
2023-01-18 15:22:07 -08:00
Zach Pomerantz
a9ab5717de feat: enable permit2 (#5833) 2023-01-18 12:17:30 -08:00
Zach Pomerantz
94544de74b fix: fallback to eth_sign for permit approval (#5847)
* fix: fallback to eth_sign for permit approval

* chore: clean up comments/logs

* refactor: mirror ethers impl

* chore: add comment re: impl
2023-01-18 12:14:55 -08:00
lynn
96f24d5a9b fix: txn and language dropdown styling fix (#5845)
fix
2023-01-18 14:10:04 -05:00
Charles Bachmeier
8e59a352c0 feat: [NFTListV2] convert old bag to modal (#5838)
* hide bag on list page

* use feature flag

* maxWidth

* convert existing bag to modal'

* add new file

* add overlay

* only show old padding with flag off

* set margin const

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-01-18 10:58:25 -08:00
lynn
3b765b4f05 chore: token details test (#5830)
* init

* fix

* init

* remove old token test

* fixes

* remove extraneous tokens test

* respond to mike

* oops
2023-01-18 13:36:30 -05:00
cartcrom
9f4a1f48a5 feat: fixed modal positioning on mobile & updated theming (#5790)
* feat: fixed modal positioning on mobile & updated theming

* fix: undid spacing change
2023-01-18 13:27:21 -05:00
Zach Pomerantz
de9533399a build: re-enable releases for Monday (#5844)
Revert "build: skip Monday releases (#5834)"

This reverts commit 6f147c1ff3.
2023-01-18 10:14:35 -08:00
Jordan Frankfurt
a02afd50b5 fix: add react state hookup for fiat announcement dismissal (#5840) 2023-01-18 11:35:33 -06:00
lynn
1f7ba5ae9f chore: add token explore integration test (#5820)
* init

* fix

* fixes

* remove extraneous tokens test
2023-01-17 21:40:08 -05:00
277 changed files with 29024 additions and 21460 deletions

1
.env
View File

@@ -1,5 +1,6 @@
# These API keys are intentionally public. Please do not report them - thank you for your concern.
REACT_APP_AMPLITUDE_PROXY_URL="https://api.uniswap.org/v1/amplitude-proxy"
REACT_APP_STATSIG_PROXY_URL="https://api.uniswap.org/v1/statsig-proxy"
REACT_APP_AWS_API_REGION="us-east-2"
REACT_APP_AWS_API_ENDPOINT="https://beta.api.uniswap.org/v1/graphql"
REACT_APP_TEMP_API_URL="https://temp.api.uniswap.org/v1"

View File

@@ -1,4 +1,6 @@
# These API keys are intentionally public. Please do not report them - thank you for your concern.
REACT_APP_AMPLITUDE_PROXY_URL="https://api.uniswap.org/v1/amplitude-proxy"
REACT_APP_STATSIG_PROXY_URL="https://api.uniswap.org/v1/statsig-proxy"
REACT_APP_AWS_API_ENDPOINT="https://api.uniswap.org/v1/graphql"
REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
REACT_APP_GOOGLE_ANALYTICS_ID="G-KDP9B6W4H8"

View File

@@ -1,7 +1,7 @@
name: Release
on:
schedule:
- cron: '0 12 * * 2-4' # every day 12:00 UTC Monday-Thursday
- cron: '0 12 * * 1-4' # every day 12:00 UTC Monday-Thursday
# manual trigger
workflow_dispatch:
@@ -62,7 +62,7 @@ jobs:
continue-on-error: true
timeout-minutes: 2
with:
cid: ${{ steps.upload.outputs.hash }}
cid: ${{ steps.pinata.outputs.hash }}
seeds: ${{ secrets.CRUST_SEEDS }}
- name: Convert CIDv0 to CIDv1
@@ -93,7 +93,7 @@ jobs:
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.pinata.outputs.hash }}/)
- [ipfs://${{ steps.pinata.outputs.hash }}/](ipfs://${{ steps.pinata.outputs.hash }}/)
${{ needs.tag.outputs.changelog }}

View File

@@ -1,27 +0,0 @@
name: Revert
on:
# manual trigger
workflow_dispatch:
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: yarn prepare
- run: yarn build
- name: Setup node@16 (required by Cloudflare Pages)
uses: actions/setup-node@v3
with:
node-version: 16
- name: Update Cloudflare Pages deployment
uses: cloudflare/pages-action@364c7ca09a4b57837c5967871d64a2c31adb8c0d
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }}
directory: build
githubToken: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -30,6 +30,11 @@ jobs:
- uses: ./.github/actions/setup
- run: yarn prepare
- run: yarn test
- uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
verbose: true
cypress-build:
runs-on: ubuntu-latest

View File

@@ -1,5 +1,7 @@
# Uniswap Labs Interface
[![codecov](https://codecov.io/gh/Uniswap/interface/branch/main/graph/badge.svg?token=YVT2Y86O82)](https://codecov.io/gh/Uniswap/interface)
[![Unit Tests](https://github.com/Uniswap/interface/actions/workflows/unit-tests.yaml/badge.svg)](https://github.com/Uniswap/interface/actions/workflows/unit-tests.yaml)
[![Integration Tests](https://github.com/Uniswap/interface/actions/workflows/integration-tests.yaml/badge.svg)](https://github.com/Uniswap/interface/actions/workflows/integration-tests.yaml)
[![Lint](https://github.com/Uniswap/interface/actions/workflows/lint.yml/badge.svg)](https://github.com/Uniswap/interface/actions/workflows/lint.yml)
@@ -40,10 +42,10 @@ For steps on local deployment, development, and code contribution, please see [C
The Uniswap Interface supports swapping, adding liquidity, removing liquidity and migrating liquidity for Uniswap protocol V2.
- Swap on Uniswap V2: https://app.uniswap.org/#/swap?use=v2
- View V2 liquidity: https://app.uniswap.org/#/pool/v2
- Add V2 liquidity: https://app.uniswap.org/#/add/v2
- Migrate V2 liquidity to V3: https://app.uniswap.org/#/migrate/v2
- Swap on Uniswap V2: <https://app.uniswap.org/#/swap?use=v2>
- View V2 liquidity: <https://app.uniswap.org/#/pool/v2>
- Add V2 liquidity: <https://app.uniswap.org/#/add/v2>
- Migrate V2 liquidity to V3: <https://app.uniswap.org/#/migrate/v2>
## Accessing Uniswap V1

View File

@@ -12,16 +12,17 @@ module.exports = {
jest: {
configure(jestConfig) {
return Object.assign({}, jestConfig, {
transformIgnorePatterns: ['@uniswap/conedison/format'],
transformIgnorePatterns: ['@uniswap/conedison/format', '@uniswap/conedison/provider'],
moduleNameMapper: {
'@uniswap/conedison/format': '@uniswap/conedison/dist/format',
'@uniswap/conedison/provider': '@uniswap/conedison/dist/provider',
},
})
},
},
webpack: {
plugins: [
new VanillaExtractPlugin(),
new VanillaExtractPlugin({ identifiers: 'short' }),
new DefinePlugin({
'process.env.REACT_APP_GIT_COMMIT_HASH': JSON.stringify(commitHash.toString()),
}),

View File

@@ -10,28 +10,28 @@ describe('Add Liquidity', () => {
})
it('loads the two correct tokens', () => {
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab/500')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6/500')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'UNI')
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'ETH')
})
it('does not crash if ETH is duplicated', () => {
cy.visit('/add/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xc778417E063141139Fce010982780140Aa0cD5Ab')
cy.visit('/add/0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6/0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'ETH')
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('not.contain.text', 'ETH')
})
it.skip('token not in storage is loaded', () => {
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'SKL')
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'MKR')
cy.visit('/add/0x07865c6e87b9f70255377e024ace6630c1eaa37f/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'USDC')
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'UNI')
})
it.skip('single token can be selected', () => {
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'SKL')
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
cy.visit('/add/0x07865c6e87b9f70255377e024ace6630c1eaa37f')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'USDC')
cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'UNI')
})
it.skip('loads fee tier distribution', () => {
@@ -53,7 +53,7 @@ describe('Add Liquidity', () => {
}
})
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab')
cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6')
cy.wait('@FeeTierDistributionQuery')

View File

@@ -25,7 +25,7 @@ describe('Landing Page', () => {
})
it('allows navigation to pool', () => {
cy.get('#pool-nav-link').click()
cy.get(getTestSelector('pool-nav-link')).first().click()
cy.url().should('include', '/pool')
})
})

View File

@@ -1,11 +1,13 @@
import { getTestSelector } from '../utils'
const COLLECTION_ADDRESS = '0xbd3531da5cf5857e7cfaa92426877b022e612cf8'
const PUDGY_COLLECTION_ADDRESS = '0xbd3531da5cf5857e7cfaa92426877b022e612cf8'
const BONSAI_COLLECTION_ADDRESS = '0xec9c519d49856fd2f8133a0741b4dbe002ce211b'
describe('Testing nfts', () => {
beforeEach(() => {
cy.visit('/')
cy.get(getTestSelector('FiatOnrampAnnouncement-close')).first().click()
cy.visit('/').then(() => {
cy.get(getTestSelector('FiatOnrampAnnouncement-close')).first().click()
})
})
it('should load nft leaderboard', () => {
@@ -16,7 +18,7 @@ describe('Testing nfts', () => {
})
it('should load pudgy penguin collection page', () => {
cy.visit(`/#/nfts/collection/${COLLECTION_ADDRESS}`)
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
cy.get(getTestSelector('nft-collection-asset')).should('exist')
cy.get(getTestSelector('nft-collection-filter-buy-now')).should('not.exist')
cy.get(getTestSelector('nft-filter')).first().click()
@@ -24,13 +26,13 @@ describe('Testing nfts', () => {
})
it('should be able to navigate to activity', () => {
cy.visit(`/#/nfts/collection/${COLLECTION_ADDRESS}`)
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
cy.get(getTestSelector('nft-activity')).first().click()
cy.get(getTestSelector('nft-activity-row')).should('exist')
})
it('should go to the details page', () => {
cy.visit(`/#/nfts/collection/${COLLECTION_ADDRESS}`)
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
cy.get(getTestSelector('nft-filter')).first().click()
cy.get(getTestSelector('nft-collection-filter-buy-now')).click()
cy.get(getTestSelector('nft-details-link')).first().click()
@@ -41,7 +43,7 @@ describe('Testing nfts', () => {
})
it('should toggle buy now on details page', () => {
cy.visit(`#/nfts/asset/${COLLECTION_ADDRESS}/2043`)
cy.visit(`#/nfts/asset/${BONSAI_COLLECTION_ADDRESS}/7580`)
cy.get(getTestSelector('nft-details-description-text')).should('exist')
cy.get(getTestSelector('nft-details-description')).click()
cy.get(getTestSelector('nft-details-description-text')).should('not.exist')

View File

@@ -1,11 +1,25 @@
import { getTestSelector } from '../utils'
describe('Pool', () => {
beforeEach(() => cy.visit('/pool'))
beforeEach(() => {
cy.visit('/pool').then(() => {
cy.wait('@eth_blockNumber')
})
})
it('add liquidity links to /add/ETH', () => {
cy.get(getTestSelector('FiatOnrampAnnouncement-close')).first().click()
cy.get('#join-pool-button').click()
cy.url().should('contain', '/add/ETH')
cy.get('body')
.then((body) => {
if (body.find(getTestSelector('FiatOnrampAnnouncement-close')).length > 0) {
cy.get(getTestSelector('FiatOnrampAnnouncement-close')).click()
}
})
.then(() => {
cy.get('#join-pool-button')
.click()
.then(() => {
cy.url().should('contain', '/add/ETH')
})
})
})
})

View File

@@ -1,24 +1,24 @@
describe('Remove Liquidity', () => {
it('eth remove', () => {
cy.visit('/remove/v2/ETH/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.visit('/remove/v2/ETH/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'ETH')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'UNI')
})
it('eth remove swap order', () => {
cy.visit('/remove/v2/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/ETH')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'MKR')
cy.visit('/remove/v2/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/ETH')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'UNI')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'ETH')
})
it('loads the two correct tokens', () => {
cy.visit('/remove/v2/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
cy.visit('/remove/v2/0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'WETH')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'MKR')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'UNI')
})
it('does not crash if ETH is duplicated', () => {
cy.visit('/remove/v2/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xc778417E063141139Fce010982780140Aa0cD5Ab')
cy.visit('/remove/v2/0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6/0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6')
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'WETH')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'WETH')
})

View File

@@ -4,7 +4,7 @@ describe('Send', () => {
cy.url().should('include', '/swap')
})
it('should redirect with url params', () => {
it.skip('should redirect with url params', () => {
cy.visit('/send?outputCurrency=ETH&recipient=bob.argent.xyz')
cy.url().should('contain', '/swap?outputCurrency=ETH&recipient=bob.argent.xyz')
})

View File

@@ -0,0 +1,92 @@
import { getTestSelector } from '../utils'
describe('Token details', () => {
before(() => {
cy.visit('/')
})
it('Uniswap token should have all information populated', () => {
// Uniswap token
cy.visit('/tokens/ethereum/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
// Price chart should be filled in
cy.get('[data-cy="chart-header"]').should('include.text', '$')
cy.get('[data-cy="price-chart"]').should('exist')
// Stats should have: TVL, 24H Volume, 52W low, 52W high
cy.get(getTestSelector('token-details-stats')).should('exist')
cy.get(getTestSelector('token-details-stats')).within(() => {
cy.get('[data-cy="tvl"]').should('include.text', '$')
cy.get('[data-cy="volume-24h"]').should('include.text', '$')
cy.get('[data-cy="52w-low"]').should('include.text', '$')
cy.get('[data-cy="52w-high"]').should('include.text', '$')
})
// About section should have description of token
cy.get(getTestSelector('token-details-about-section')).should('exist')
cy.contains('UNI is the governance token for Uniswap').should('exist')
// Links section should link out to Etherscan, More analytics, Website, Twitter
cy.get('[data-cy="resources-container"]').within(() => {
cy.contains('Etherscan')
.should('have.attr', 'href')
.and('include', 'etherscan.io/address/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
cy.contains('More analytics')
.should('have.attr', 'href')
.and('include', 'info.uniswap.org/#/tokens/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
cy.contains('Website').should('have.attr', 'href').and('include', 'uniswap.org')
cy.contains('Twitter').should('have.attr', 'href').and('include', 'twitter.com/Uniswap')
})
// Contract address should be displayed
cy.contains('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984').should('exist')
// Swap widget should have this token pre-selected as the “destination” token
cy.get(getTestSelector('token-select')).should('include.text', 'UNI')
})
it('token with warning and low trading volume should have all information populated', () => {
// Shiba predator token, low trading volume and also has warning modal
cy.visit('/tokens/ethereum/0xa71d0588EAf47f12B13cF8eC750430d21DF04974')
// Should have missing price chart when price unavailable (expected for this token)
if (cy.get('[data-cy="chart-header"]').contains('Price Unavailable')) {
cy.get('[data-cy="missing-chart"]').should('exist')
}
// Stats should have: TVL, 24H Volume, 52W low, 52W high
cy.get(getTestSelector('token-details-stats')).should('exist')
cy.get(getTestSelector('token-details-stats')).within(() => {
cy.get('[data-cy="tvl"]').should('exist')
cy.get('[data-cy="volume-24h"]').should('exist')
cy.get('[data-cy="52w-low"]').should('exist')
cy.get('[data-cy="52w-high"]').should('exist')
})
// About section should have description of token
cy.get(getTestSelector('token-details-about-section')).should('exist')
cy.contains('QOM is the Shiba Predator').should('exist')
// Links section should link out to Etherscan, More analytics, Website, Twitter
cy.get('[data-cy="resources-container"]').within(() => {
cy.contains('Etherscan')
.should('have.attr', 'href')
.and('include', 'etherscan.io/address/0xa71d0588EAf47f12B13cF8eC750430d21DF04974')
cy.contains('More analytics')
.should('have.attr', 'href')
.and('include', 'info.uniswap.org/#/tokens/0xa71d0588EAf47f12B13cF8eC750430d21DF04974')
cy.contains('Website').should('have.attr', 'href').and('include', 'qom')
cy.contains('Twitter').should('have.attr', 'href').and('include', 'twitter.com/ShibaPredator1')
})
// Contract address should be displayed
cy.contains('0xa71d0588EAf47f12B13cF8eC750430d21DF04974').should('exist')
// Swap widget should have this token pre-selected as the “destination” token
cy.get(getTestSelector('token-select')).should('include.text', 'QOM')
// Warning label should show if relevant ([spec](https://www.notion.so/3f7fce6f93694be08a94a6984d50298e))
cy.get('[data-cy="token-safety-message"]')
.should('include.text', 'Warning')
.and('include.text', "This token isn't traded on leading U.S. centralized exchanges")
})
})

View File

@@ -0,0 +1,83 @@
describe.skip('Token explore filter', () => {
before(() => {
cy.visit('/')
})
it('should filter correctly by uni search term', () => {
cy.visit('/tokens')
cy.get('[data-cy="token-name"]').then(($els) => {
const tokenNames = Array.from($els, (el) => el.innerText)
const filteredByUni = tokenNames.filter((tokenName) => tokenName.toLowerCase().includes('uni'))
cy.wrap(filteredByUni).as('filteredByUni')
})
cy.get('[data-cy="explore-tokens-search-input"]')
.clear()
.type('uni')
.type('{enter}')
.then(() => {
cy.get('[data-cy="token-name"]').its('length').should('be.lt', 100)
cy.get('@filteredByUni').then((filteredByUni) => {
cy.get('[data-cy="token-name"]').then(($els) => {
const tokenNames = Array.from($els, (el) => el.innerText)
expect(tokenNames.length).to.equal(filteredByUni.length)
tokenNames.forEach((tokenName) => {
expect(filteredByUni).to.include(tokenName)
})
})
})
})
})
it('should filter correctly by dao search term', () => {
cy.visit('/tokens')
cy.get('[data-cy="token-name"]').then(($els) => {
const tokenNames = Array.from($els, (el) => el.innerText)
const filteredByDao = tokenNames.filter((tokenName) => tokenName.toLowerCase().includes('dao'))
cy.wrap(filteredByDao).as('filteredByDao')
})
cy.get('[data-cy="explore-tokens-search-input"]')
.clear()
.type('dao')
.type('{enter}')
.then(() => {
cy.get('[data-cy="token-name"]').its('length').should('be.lt', 100)
cy.get('@filteredByDao').then((filteredByDao) => {
cy.get('[data-cy="token-name"]').then(($els) => {
const tokenNames = Array.from($els, (el) => el.innerText)
expect(tokenNames.length).to.equal(filteredByDao.length)
tokenNames.forEach((tokenName) => {
expect(filteredByDao).to.include(tokenName)
})
})
})
})
})
it('should filter correctly by ax search term', () => {
cy.visit('/tokens')
cy.get('[data-cy="token-name"]').then(($els) => {
const tokenNames = Array.from($els, (el) => el.innerText)
const filteredByAx = tokenNames.filter((tokenName) => tokenName.toLowerCase().includes('ax'))
cy.wrap(filteredByAx).as('filteredByAx')
})
cy.get('[data-cy="explore-tokens-search-input"]')
.clear()
.type('ax')
.type('{enter}')
.then(() => {
cy.get('[data-cy="token-name"]').its('length').should('be.lt', 100)
cy.get('@filteredByAx').then((filteredByAx) => {
cy.get('[data-cy="token-name"]').then(($els) => {
const tokenNames = Array.from($els, (el) => el.innerText)
expect(tokenNames.length).to.equal(filteredByAx.length)
tokenNames.forEach((tokenName) => {
expect(filteredByAx).to.include(tokenName)
})
})
})
})
})
})

View File

@@ -0,0 +1,74 @@
import { getTestSelector, getTestSelectorStartsWith } from '../utils'
describe('Token explore', () => {
before(() => {
cy.visit('/')
})
it('should load token leaderboard', () => {
cy.visit('/tokens/ethereum')
cy.get(getTestSelectorStartsWith('token-table')).its('length').should('be.eq', 100)
// check sorted svg icon is present in volume cell, since tokens are sorted by volume by default
cy.get(getTestSelector('header-row')).find(getTestSelector('volume-cell')).find('svg').should('exist')
cy.get(getTestSelector('token-table-row-ETH')).find(getTestSelector('name-cell')).should('include.text', 'Ether')
cy.get(getTestSelector('token-table-row-ETH')).find(getTestSelector('volume-cell')).should('include.text', '$')
cy.get(getTestSelector('token-table-row-ETH')).find(getTestSelector('price-cell')).should('include.text', '$')
cy.get(getTestSelector('token-table-row-ETH')).find(getTestSelector('tvl-cell')).should('include.text', '$')
cy.get(getTestSelector('token-table-row-ETH'))
.find(getTestSelector('percent-change-cell'))
.should('include.text', '%')
cy.get(getTestSelector('header-row')).find(getTestSelector('price-cell')).click()
cy.get(getTestSelector('header-row')).find(getTestSelector('price-cell')).find('svg').should('exist')
})
it('should update when time window toggled', () => {
cy.visit('/tokens/ethereum')
cy.get(getTestSelector('time-selector')).should('contain', '1D')
cy.get(getTestSelector('token-table-row-ETH'))
.find(getTestSelector('volume-cell'))
.then(function ($elem) {
cy.wrap($elem.text()).as('dailyEthVol')
})
cy.get(getTestSelector('time-selector')).click()
cy.get(getTestSelector('1Y')).click()
cy.get(getTestSelector('token-table-row-ETH'))
.find(getTestSelector('volume-cell'))
.then(function ($elem) {
cy.wrap($elem.text()).as('yearlyEthVol')
})
expect(cy.get('@dailyEthVol')).to.not.equal(cy.get('@yearlyEthVol'))
})
it('should navigate to token detail page when row clicked', () => {
cy.visit('/tokens/ethereum')
cy.get(getTestSelector('token-table-row-ETH')).click()
cy.get(getTestSelector('token-details-about-section')).should('exist')
cy.get(getTestSelector('token-details-stats')).should('exist')
cy.get(getTestSelector('token-info-container')).should('exist')
cy.get(getTestSelector('chart-container')).should('exist')
cy.contains('Ethereum is a smart contract platform that enables developers to build tokens').should('exist')
cy.contains('Etherscan').should('exist')
})
it('should update when global network changed', () => {
cy.visit('/tokens/ethereum')
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Ethereum')
cy.get(getTestSelector('token-table-row-ETH')).should('exist')
// note: cannot switch global chain via UI because we cannot approve the network switch
// in metamask modal using plain cypress. this is a workaround.
cy.visit('/tokens/polygon')
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Polygon')
cy.get(getTestSelector('token-table-row-MATIC')).should('exist')
})
it('should update when token explore table network changed', () => {
cy.visit('/tokens/ethereum')
cy.get(getTestSelector('tokens-network-filter-selected')).click()
cy.get(getTestSelector('tokens-network-filter-option-optimism')).click()
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism')
cy.reload()
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism')
cy.get(getTestSelector('chain-selector')).last().should('contain', 'Ethereum')
})
})

View File

@@ -1,55 +0,0 @@
import { getTestSelector, getTestSelectorStartsWith } from '../utils'
describe('Testing tokens on uniswap page', () => {
before(() => {
cy.visit('/')
})
it('should load token leaderboard', () => {
cy.visit('/tokens/ethereum')
cy.get(getTestSelectorStartsWith('token-table')).its('length').should('be.gte', 25)
})
it('should keep the same configuration when reloaded: ETH global, OP local', () => {
cy.visit('/tokens/ethereum')
cy.get(getTestSelector('tokens-network-filter-selected')).click()
cy.get(getTestSelector('tokens-network-filter-option-optimism')).click()
cy.reload()
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism')
})
it('should have the correct network configuration when reloaded: OP global, Polygon local', () => {
cy.get(getTestSelector('chain-selector')).last().click()
cy.get(getTestSelector('chain-selector-option-optimism')).click()
cy.visit('/tokens/ethereum')
cy.get(getTestSelector('tokens-network-filter-selected')).click()
cy.get(getTestSelector('tokens-network-filter-option-polygon')).click()
cy.reload()
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Polygon')
// With no wallet connected, reloading the page resets the global network.
cy.get(getTestSelector('chain-selector')).last().should('contain', 'Ethereum')
})
it('should load go to ethereum token and return to token list page', () => {
cy.visit('/tokens/ethereum')
cy.get(getTestSelector('token-table-row-Ether')).click()
cy.get(getTestSelector('token-details-stats')).should('exist')
cy.get(getTestSelector('token-details-return-button')).click()
cy.get(getTestSelectorStartsWith('token-table')).its('length').should('be.gte', 25)
})
it('should go to native token on ethereum and render description', () => {
cy.visit('/tokens/ethereum/NATIVE')
cy.get(getTestSelector('token-details-about-section')).should('exist')
cy.contains('Ethereum is a smart contract platform that enables developers').should('exist')
cy.contains('Etherscan').should('exist')
})
it('should go to native token on polygon and render description and links', () => {
cy.visit('/tokens/polygon/NATIVE')
cy.get(getTestSelector('token-details-about-section')).should('exist')
cy.contains('Wrapped Matic on Polygon').should('exist')
cy.contains('Block Explorer').should('exist')
})
})

View File

@@ -0,0 +1,64 @@
import { getTestSelector } from '../utils'
describe('Universal search bar', () => {
before(() => {
cy.visit('/')
cy.get('[data-cy="magnifying-icon"]')
.parent()
.then(($navIcon) => {
$navIcon.click()
})
})
it('should yield clickable result for regular token or nft collection search term', () => {
// Search for uni token by name.
cy.get('[data-cy="search-bar-input"]').last().clear().type('uni')
cy.get('[data-cy="searchbar-token-row-UNI"]')
.should('contain.text', 'Uniswap')
.and('contain.text', 'UNI')
.and('contain.text', '$')
.and('contain.text', '%')
cy.get('[data-cy="searchbar-token-row-UNI"]').click()
cy.get('div').contains('Uniswap').should('exist')
// Stats should have: TVL, 24H Volume, 52W low, 52W high.
cy.get(getTestSelector('token-details-stats')).should('exist')
cy.get(getTestSelector('token-details-stats')).within(() => {
cy.get('[data-cy="tvl"]').should('include.text', '$')
cy.get('[data-cy="volume-24h"]').should('include.text', '$')
cy.get('[data-cy="52w-low"]').should('include.text', '$')
cy.get('[data-cy="52w-high"]').should('include.text', '$')
})
// About section should have description of token.
cy.get(getTestSelector('token-details-about-section')).should('exist')
cy.contains('UNI is the governance token for Uniswap').should('exist')
})
it.skip('should show recent tokens and popular tokens with empty search term', () => {
cy.get('[data-cy="magnifying-icon"]')
.parent()
.then(($navIcon) => {
$navIcon.click()
})
// Recently searched UNI token should exist.
cy.get('[data-cy="search-bar-input"]').last().clear()
cy.get('[data-cy="searchbar-dropdown"]')
.contains('[data-cy="searchbar-dropdown"]', 'Recent searches')
.find('[data-cy="searchbar-token-row-UNI"]')
.should('exist')
// Most popular 3 tokens should be shown.
cy.get('[data-cy="searchbar-dropdown"]')
.contains('[data-cy="searchbar-dropdown"]', 'Popular tokens')
.find('[data-cy^="searchbar-token-row"]')
.its('length')
.should('be.eq', 3)
})
it.skip('should show blocked badge when blocked token is searched for', () => {
// Search for mTSLA, which is a blocked token.
cy.get('[data-cy="search-bar-input"]').last().clear().type('mtsla')
cy.get('[data-cy="searchbar-token-row-mTSLA"]').find('[data-cy="blocked-icon"]').should('exist')
})
})

View File

@@ -13,8 +13,8 @@ describe('Wallet Dropdown', () => {
it('should select a language', () => {
cy.get(getTestSelector('wallet-select-language')).click()
cy.get(getTestSelector('wallet-language-item')).contains('Afrikaans').click({ force: true })
cy.get(getTestSelector('wallet-header')).should('contain', 'Taal')
cy.get(getTestSelector('wallet-language-item')).contains('Deutsch').click({ force: true })
cy.get(getTestSelector('wallet-header')).should('contain', 'Sprache')
cy.get(getTestSelector('wallet-language-item')).contains('English').click({ force: true })
cy.get(getTestSelector('wallet-header')).should('contain', 'Language')
cy.get(getTestSelector('wallet-back')).click()
@@ -36,8 +36,8 @@ describe('Wallet Dropdown', () => {
it('should select a language when not connected', () => {
cy.get(getTestSelector('wallet-select-language')).click()
cy.get(getTestSelector('wallet-language-item')).contains('Afrikaans').click({ force: true })
cy.get(getTestSelector('wallet-header')).should('contain', 'Taal')
cy.get(getTestSelector('wallet-language-item')).contains('Deutsch').click({ force: true })
cy.get(getTestSelector('wallet-header')).should('contain', 'Sprache')
cy.get(getTestSelector('wallet-language-item')).contains('English').click({ force: true })
cy.get(getTestSelector('wallet-header')).should('contain', 'Language')
cy.get(getTestSelector('wallet-back')).click()

View File

@@ -70,6 +70,7 @@ beforeEach(() => {
// These are stripped by cypress because chromeWebSecurity === false; this adds them back in.
cy.intercept(/infura.io/, (res) => {
res.headers['origin'] = 'http://localhost:3000'
res.alias = res.body.method
res.continue()
})

View File

@@ -7,17 +7,21 @@ import { Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
import { JsonRpcProvider } from '@ethersproject/providers'
import { Wallet } from '@ethersproject/wallet'
import { SupportedChainId } from '../../src/constants/chains'
// todo: figure out how env vars actually work in CI
// const TEST_PRIVATE_KEY = Cypress.env('INTEGRATION_TEST_PRIVATE_KEY')
const TEST_PRIVATE_KEY = '0xe580410d7c37d26c6ad1a837bbae46bc27f9066a466fb3a66e770523b4666d19'
// address of the above key
const TEST_ADDRESS_NEVER_USE = new Wallet(TEST_PRIVATE_KEY).address
const CHAIN_ID = SupportedChainId.GOERLI
const HEXLIFIED_CHAIN_ID = `0x${CHAIN_ID.toString(16)}`
const provider = new JsonRpcProvider('https://goerli.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847', 4)
const provider = new JsonRpcProvider('https://goerli.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847', 5)
const signer = new Wallet(TEST_PRIVATE_KEY, provider)
export const injected = new (class extends Eip1193Bridge {
chainId = /* GOERLI= */ 5
chainId = CHAIN_ID
async sendAsync(...args: any[]) {
console.debug('sendAsync called', ...args)
@@ -46,9 +50,9 @@ export const injected = new (class extends Eip1193Bridge {
}
if (method === 'eth_chainId') {
if (isCallbackForm) {
callback(null, { result: '0x4' })
callback(null, { result: HEXLIFIED_CHAIN_ID })
} else {
return Promise.resolve('0x4')
return Promise.resolve(HEXLIFIED_CHAIN_ID)
}
}
try {

View File

@@ -111,7 +111,7 @@
},
"dependencies": {
"@apollo/client": "^3.7.2",
"@coinbase/wallet-sdk": "^3.3.0",
"@coinbase/wallet-sdk": "^3.6.4",
"@fontsource/ibm-plex-mono": "^4.5.1",
"@fontsource/inter": "^4.5.1",
"@graphql-codegen/cli": "^2.15.0",
@@ -133,26 +133,26 @@
"@reduxjs/toolkit": "^1.6.1",
"@sentry/react": "^7.29.0",
"@types/react-window-infinite-loader": "^1.0.6",
"@uniswap/analytics": "1.2.0",
"@uniswap/analytics-events": "^2.1.0",
"@uniswap/conedison": "^1.1.1",
"@uniswap/analytics": "^1.3.1",
"@uniswap/analytics-events": "^2.4.0",
"@uniswap/conedison": "^1.4.0",
"@uniswap/governance": "^1.0.2",
"@uniswap/liquidity-staker": "^1.0.2",
"@uniswap/merkle-distributor": "1.0.1",
"@uniswap/permit2-sdk": "1.2.0",
"@uniswap/redux-multicall": "^1.1.8",
"@uniswap/router-sdk": "^1.3.0",
"@uniswap/sdk-core": "^3.0.1",
"@uniswap/smart-order-router": "^2.10.0",
"@uniswap/sdk-core": "^3.2.0",
"@uniswap/smart-order-router": "^3.6.0",
"@uniswap/token-lists": "^1.0.0-beta.30",
"@uniswap/universal-router-sdk": "1.3.0",
"@uniswap/universal-router-sdk": "^1.3.6",
"@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",
"@uniswap/widgets": "2.25.1",
"@uniswap/widgets": "^2.43.2",
"@vanilla-extract/css": "^1.7.2",
"@vanilla-extract/css-utils": "^0.1.2",
"@vanilla-extract/dynamic": "^2.0.2",
@@ -165,23 +165,23 @@
"@visx/responsive": "^2.10.0",
"@visx/shape": "^2.11.1",
"@walletconnect/ethereum-provider": "^1.8.0",
"@web3-react/coinbase-wallet": "8.0.34-beta.0",
"@web3-react/coinbase-wallet": "8.0.35-beta.0",
"@web3-react/core": "8.0.35-beta.0",
"@web3-react/eip1193": "8.0.26-beta.0",
"@web3-react/eip1193": "8.0.27-beta.0",
"@web3-react/empty": "8.0.20-beta.0",
"@web3-react/gnosis-safe": "8.0.7-beta.0",
"@web3-react/metamask": "8.0.28-beta.0",
"@web3-react/metamask": "8.0.30-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.36-beta.0",
"@web3-react/walletconnect": "8.0.37-beta.0",
"array.prototype.flat": "^1.2.4",
"array.prototype.flatmap": "^1.2.4",
"cids": "^1.0.0",
"clsx": "^1.1.1",
"copy-to-clipboard": "^3.2.0",
"d3": "^7.6.1",
"ethers": "^5.1.4",
"ethers": "^5.7.2",
"firebase": "^9.1.3",
"focus-visible": "^5.2.0",
"get-graphql-schema": "^2.1.2",
@@ -222,6 +222,7 @@
"redux": "^4.1.2",
"redux-localstorage-simple": "^2.3.1",
"setimmediate": "^1.0.5",
"statsig-react": "^1.22.0",
"styled-components": "^5.3.5",
"tiny-invariant": "^1.2.0",
"ua-parser-js": "^0.7.28",

View File

@@ -1,9 +1,8 @@
import { TraceEvent } from '@uniswap/analytics'
import { BrowserEvent, InterfaceElementName, SharedEventName } from '@uniswap/analytics-events'
import { Link } from 'react-router-dom'
import { useIsDarkMode } from 'state/user/hooks'
import styled from 'styled-components/macro'
import { BREAKPOINTS, ExternalLink } from 'theme'
import { BREAKPOINTS, ExternalLink, StyledRouterLink } from 'theme'
import { DiscordIcon, GithubIcon, TwitterIcon } from './Icons'
import darkUnicornImgSrc from './images/unicornEmbossDark.png'
@@ -97,14 +96,10 @@ const ExternalTextLink = styled(ExternalLink)`
color: ${({ theme }) => theme.textSecondary};
`
const TextLink = styled(Link)`
const TextLink = styled(StyledRouterLink)`
font-size: 16px;
line-height: 20px;
color: ${({ theme }) => theme.textSecondary};
text-decoration: none;
&:hover {
text-decoration: underline;
}
`
const Copyright = styled.span`
@@ -120,7 +115,7 @@ const LogoSectionContent = () => {
<>
<StyledLogo src={isDarkMode ? darkUnicornImgSrc : lightUnicornImgSrc} alt="Uniswap Logo" />
<SocialLinks>
<SocialLink href="https://github.com/Uniswap" target="_blank" rel="noopener noreferrer">
<SocialLink href="https://discord.gg/FCfyBSbCU5" target="_blank" rel="noopener noreferrer">
<DiscordIcon size={32} />
</SocialLink>
<TraceEvent
@@ -132,7 +127,7 @@ const LogoSectionContent = () => {
<TwitterIcon size={32} />
</SocialLink>
</TraceEvent>
<SocialLink href="https://discord.gg/FCfyBSbCU5" target="_blank" rel="noopener noreferrer">
<SocialLink href="https://github.com/Uniswap" target="_blank" rel="noopener noreferrer">
<GithubIcon size={32} />
</SocialLink>
</SocialLinks>
@@ -158,15 +153,9 @@ export const AboutFooter = () => {
</LinkGroup>
<LinkGroup>
<LinkGroupTitle>Protocol</LinkGroupTitle>
<ExternalTextLink href="https://uniswap.org/community" target="_blank" rel="noopener noreferrer">
Community
</ExternalTextLink>
<ExternalTextLink href="https://uniswap.org/governance" target="_blank" rel="noopener noreferrer">
Governance
</ExternalTextLink>
<ExternalTextLink href="https://uniswap.org/developers" target="_blank" rel="noopener noreferrer">
Developers
</ExternalTextLink>
<ExternalTextLink href="https://uniswap.org/community">Community</ExternalTextLink>
<ExternalTextLink href="https://uniswap.org/governance">Governance</ExternalTextLink>
<ExternalTextLink href="https://uniswap.org/developers">Developers</ExternalTextLink>
</LinkGroup>
<LinkGroup>
<LinkGroupTitle>Company</LinkGroupTitle>
@@ -175,18 +164,14 @@ export const AboutFooter = () => {
name={SharedEventName.ELEMENT_CLICKED}
element={InterfaceElementName.CAREERS_LINK}
>
<ExternalTextLink href="https://boards.greenhouse.io/uniswaplabs" target="_blank" rel="noopener noreferrer">
Careers
</ExternalTextLink>
<ExternalTextLink href="https://boards.greenhouse.io/uniswaplabs">Careers</ExternalTextLink>
</TraceEvent>
<TraceEvent
events={[BrowserEvent.onClick]}
name={SharedEventName.ELEMENT_CLICKED}
element={InterfaceElementName.BLOG_LINK}
>
<ExternalTextLink href="https://uniswap.org/blog" target="_blank" rel="noopener noreferrer">
Blog
</ExternalTextLink>
<ExternalTextLink href="https://uniswap.org/blog">Blog</ExternalTextLink>
</TraceEvent>
</LinkGroup>
<LinkGroup>
@@ -209,9 +194,7 @@ export const AboutFooter = () => {
name={SharedEventName.ELEMENT_CLICKED}
element={InterfaceElementName.SUPPORT_LINK}
>
<ExternalTextLink href="https://support.uniswap.org/hc/en-us" target="_blank" rel="noopener noreferrer">
Help Center
</ExternalTextLink>
<ExternalTextLink href="https://support.uniswap.org/hc/en-us">Help Center</ExternalTextLink>
</TraceEvent>
</LinkGroup>
</FooterLinks>

View File

@@ -1,3 +1,4 @@
<svg viewBox="0 0 71 55" xmlns="http://www.w3.org/2000/svg">
<title>Discord</title>
<path d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z" />
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -1,3 +1,4 @@
<svg viewBox="0 0 32 32" role="img" xmlns="http://www.w3.org/2000/svg">
<title>Twitter</title>
<path d="M31.2746 5.92398C30.7719 6.14694 30.2551 6.33512 29.727 6.4879C30.3522 5.7808 30.8289 4.9488 31.1199 4.03835C31.1851 3.83427 31.1175 3.61089 30.9498 3.47742C30.7822 3.34385 30.5495 3.32785 30.365 3.43716C29.2434 4.10235 28.0334 4.58039 26.7647 4.85993C25.4866 3.6111 23.7508 2.90039 21.9563 2.90039C18.1684 2.90039 15.0867 5.98199 15.0867 9.76975C15.0867 10.0681 15.1056 10.3647 15.143 10.6573C10.4426 10.2446 6.07276 7.9343 3.07198 4.25337C2.96504 4.12217 2.80029 4.05146 2.63162 4.06498C2.46285 4.0782 2.31121 4.17337 2.22595 4.31964C1.61733 5.36398 1.29557 6.5584 1.29557 7.77368C1.29557 9.4289 1.88654 10.9994 2.93046 12.2265C2.61304 12.1166 2.30502 11.9792 2.01103 11.816C1.8532 11.7282 1.66058 11.7295 1.50378 11.8194C1.34687 11.9093 1.2485 12.0747 1.24437 12.2554C1.24365 12.2859 1.24365 12.3163 1.24365 12.3472C1.24365 14.8179 2.5734 17.0423 4.60644 18.2547C4.43178 18.2373 4.25722 18.212 4.0838 18.1788C3.90502 18.1447 3.72117 18.2073 3.6006 18.3437C3.47983 18.4799 3.43988 18.6699 3.49552 18.8433C4.24804 21.1927 6.18548 22.9208 8.52767 23.4477C6.58507 24.6644 4.36355 25.3017 2.03147 25.3017C1.54486 25.3017 1.05547 25.2731 0.5765 25.2165C0.338565 25.1882 0.111055 25.3287 0.0300229 25.5549C-0.0510093 25.7813 0.0348745 26.0337 0.2373 26.1634C3.23322 28.0844 6.69738 29.0997 10.2551 29.0997C17.249 29.0997 21.6242 25.8016 24.063 23.0349C27.104 19.585 28.8481 15.0186 28.8481 10.5067C28.8481 10.3182 28.8452 10.1278 28.8394 9.93812C30.0392 9.03417 31.0722 7.94018 31.9128 6.68279C32.0404 6.49182 32.0266 6.23943 31.8787 6.06364C31.731 5.88774 31.4848 5.83087 31.2746 5.92398Z" />
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,11 +1,6 @@
import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import {
getConnection,
getConnectionName,
getHasCoinbaseExtensionInstalled,
getHasMetaMaskExtensionInstalled,
} from 'connection/utils'
import { getConnection, getConnectionName, getIsCoinbaseWallet, getIsMetaMaskWallet } from 'connection/utils'
import { useCallback } from 'react'
import { ExternalLink as LinkIcon } from 'react-feather'
import { useAppDispatch } from 'state/hooks'
@@ -215,8 +210,8 @@ export default function AccountDetails({
const theme = useTheme()
const dispatch = useAppDispatch()
const hasMetaMaskExtension = getHasMetaMaskExtensionInstalled()
const hasCoinbaseExtension = getHasCoinbaseExtensionInstalled()
const hasMetaMaskExtension = getIsMetaMaskWallet()
const hasCoinbaseExtension = getIsCoinbaseWallet()
const isInjectedMobileBrowser = (hasMetaMaskExtension || hasCoinbaseExtension) && isMobile
function formatConnectorName() {

View File

@@ -97,7 +97,8 @@ export const ButtonPrimary = styled(BaseButton)`
export const SmallButtonPrimary = styled(ButtonPrimary)`
width: auto;
font-size: 16px;
padding: 10px 16px;
padding: ${({ padding }) => padding ?? '8px 12px'};
border-radius: 12px;
`

View File

@@ -17,10 +17,10 @@ interface DoubleCurrencyLogoProps {
currency1?: Currency
}
const HigherLogo = styled(CurrencyLogo)`
const HigherLogoWrapper = styled.div`
z-index: 1;
`
const CoveredLogo = styled(CurrencyLogo)<{ sizeraw: number }>`
const CoveredLogoWapper = styled.div<{ sizeraw: number }>`
position: absolute;
left: ${({ sizeraw }) => '-' + (sizeraw / 2).toString() + 'px'} !important;
`
@@ -33,8 +33,16 @@ export default function DoubleCurrencyLogo({
}: DoubleCurrencyLogoProps) {
return (
<Wrapper sizeraw={size} margin={margin}>
{currency0 && <HigherLogo currency={currency0} size={size.toString() + 'px'} />}
{currency1 && <CoveredLogo currency={currency1} size={size.toString() + 'px'} sizeraw={size} />}
{currency0 && (
<HigherLogoWrapper>
<CurrencyLogo hideL2Icon currency={currency0} size={size.toString() + 'px'} />
</HigherLogoWrapper>
)}
{currency1 && (
<CoveredLogoWapper sizeraw={size}>
<CurrencyLogo hideL2Icon currency={currency1} size={size.toString() + 'px'} />
</CoveredLogoWapper>
)}
</Wrapper>
)
}

View File

@@ -1,10 +1,13 @@
import { Trans } from '@lingui/macro'
import * as Sentry from '@sentry/react'
import { sendAnalyticsEvent } from '@uniswap/analytics'
import { SwapEventName } from '@uniswap/analytics-events'
import { ButtonLight, SmallButtonPrimary } from 'components/Button'
import { ChevronUpIcon } from 'nft/components/icons'
import { useIsMobile } from 'nft/hooks'
import React, { PropsWithChildren, useState } from 'react'
import { Copy } from 'react-feather'
import { useLocation } from 'react-router-dom'
import styled from 'styled-components/macro'
import { isSentryEnabled } from 'utils/env'
@@ -217,13 +220,19 @@ const updateServiceWorkerInBackground = async () => {
}
export default function ErrorBoundary({ children }: PropsWithChildren): JSX.Element {
const { pathname } = useLocation()
return (
<Sentry.ErrorBoundary
fallback={({ error, eventId }) => <Fallback error={error} eventId={eventId} />}
beforeCapture={(scope) => {
scope.setLevel('fatal')
}}
onError={updateServiceWorkerInBackground}
onError={(error) => {
updateServiceWorkerInBackground()
if (pathname === '/swap') {
sendAnalyticsEvent(SwapEventName.SWAP_ERROR, { error })
}
}}
>
{children}
</Sentry.ErrorBoundary>

View File

@@ -1,8 +1,9 @@
import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags'
import { useFiatOnrampFlag } from 'featureFlags/flags/fiatOnramp'
import { NftListV2Variant, useNftListV2Flag } from 'featureFlags/flags/nftListV2'
import { GqlRoutingVariant, useGqlRoutingFlag } from 'featureFlags/flags/gqlRouting'
import { NftGraphqlVariant, useNftGraphqlFlag } from 'featureFlags/flags/nftlGraphql'
import { PayWithAnyTokenVariant, usePayWithAnyTokenFlag } from 'featureFlags/flags/payWithAnyToken'
import { Permit2Variant, usePermit2Flag } from 'featureFlags/flags/permit2'
import { SwapWidgetVariant, useSwapWidgetFlag } from 'featureFlags/flags/swapWidget'
import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc'
import { useAtomValue, useUpdateAtom } from 'jotai/utils'
import { Children, PropsWithChildren, ReactElement, ReactNode, useCallback, useState } from 'react'
@@ -211,24 +212,30 @@ export default function FeatureFlagModal() {
featureFlag={FeatureFlag.permit2}
label="Permit 2 / Universal Router"
/>
<FeatureFlagOption
variant={BaseVariant}
value={useFiatOnrampFlag()}
featureFlag={FeatureFlag.fiatOnramp}
label="Fiat on-ramp"
/>
<FeatureFlagOption
variant={NftListV2Variant}
value={useNftListV2Flag()}
featureFlag={FeatureFlag.nftListV2}
label="NFT Listing Page v2"
/>
<FeatureFlagOption
variant={PayWithAnyTokenVariant}
value={usePayWithAnyTokenFlag()}
featureFlag={FeatureFlag.payWithAnyToken}
label="Pay With Any Token"
/>
<FeatureFlagOption
variant={SwapWidgetVariant}
value={useSwapWidgetFlag()}
featureFlag={FeatureFlag.swapWidget}
label="Swap Widget"
/>
<FeatureFlagOption
variant={GqlRoutingVariant}
value={useGqlRoutingFlag()}
featureFlag={FeatureFlag.gqlRouting}
label="GraphQL NFT Routing"
/>
<FeatureFlagOption
variant={NftGraphqlVariant}
value={useNftGraphqlFlag()}
featureFlag={FeatureFlag.nftGraphql}
label="Migrate NFT read endpoints to GQL"
/>
<FeatureFlagGroup name="Debug">
<FeatureFlagOption
variant={TraceJsonRpcVariant}

View File

@@ -3,9 +3,7 @@ import { sendAnalyticsEvent } from '@uniswap/analytics'
import { InterfaceEventName } from '@uniswap/analytics-events'
import { useWeb3React } from '@web3-react/core'
import fiatMaskUrl from 'assets/svg/fiat_mask.svg'
import { BaseVariant } from 'featureFlags'
import { useFiatOnrampFlag } from 'featureFlags/flags/fiatOnramp'
import { useCallback, useEffect } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { X } from 'react-feather'
import { useToggleWalletDropdown } from 'state/application/hooks'
import { useAppSelector } from 'state/hooks'
@@ -100,6 +98,7 @@ const MAX_RENDER_COUNT = 3
export function FiatOnrampAnnouncement() {
const { account } = useWeb3React()
const [acks, acknowledge] = useFiatOnrampAck()
const [localClose, setLocalClose] = useState(false)
useEffect(() => {
if (!sessionStorage.getItem(ANNOUNCEMENT_RENDERED)) {
acknowledge({ renderCount: acks?.renderCount + 1 })
@@ -108,6 +107,7 @@ export function FiatOnrampAnnouncement() {
}, [acknowledge, acks])
const handleClose = useCallback(() => {
setLocalClose(true)
localStorage.setItem(ANNOUNCEMENT_DISMISSED, 'true')
}, [])
@@ -118,17 +118,16 @@ export function FiatOnrampAnnouncement() {
acknowledge({ user: true })
}, [acknowledge, toggleWalletDropdown])
const fiatOnrampFlag = useFiatOnrampFlag()
const openModal = useAppSelector((state) => state.application.openModal)
if (
!account ||
acks?.user ||
fiatOnrampFlag === BaseVariant.Control ||
localStorage.getItem(ANNOUNCEMENT_DISMISSED) ||
acks?.renderCount >= MAX_RENDER_COUNT ||
isMobile ||
openModal !== null
openModal !== null ||
localClose
) {
return null
}

View File

@@ -3,14 +3,17 @@ import { useWeb3React } from '@web3-react/core'
import { useCallback, useEffect, useState } from 'react'
import { useCloseModal, useModalIsOpen } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import { useIsDarkMode } from 'state/user/hooks'
import styled, { useTheme } from 'styled-components/macro'
import { CustomLightSpinner, ThemedText } from 'theme'
import Circle from '../../assets/images/blue-loader.svg'
import Modal from '../Modal'
const Wrapper = styled.div`
background-color: ${({ theme }) => theme.white};
const MOONPAY_DARK_BACKGROUND = '#1c1c1e'
const Wrapper = styled.div<{ isDarkMode: boolean }>`
// #1c1c1e is the background color for the darkmode moonpay iframe as of 2/16/2023
background-color: ${({ isDarkMode, theme }) => (isDarkMode ? MOONPAY_DARK_BACKGROUND : theme.white)};
border-radius: 20px;
box-shadow: ${({ theme }) => theme.deepShadow};
display: flex;
@@ -28,8 +31,9 @@ const ErrorText = styled(ThemedText.BodyPrimary)`
text-align: center;
width: 90%;
`
const StyledIframe = styled.iframe`
background-color: ${({ theme }) => theme.white};
const StyledIframe = styled.iframe<{ isDarkMode: boolean }>`
// #1c1c1e is the background color for the darkmode moonpay iframe as of 2/16/2023
background-color: ${({ isDarkMode, theme }) => (isDarkMode ? MOONPAY_DARK_BACKGROUND : theme.white)};
border-radius: 12px;
bottom: 0;
left: 0;
@@ -67,6 +71,7 @@ const MOONPAY_SUPPORTED_CURRENCY_CODES = [
export default function FiatOnrampModal() {
const { account } = useWeb3React()
const theme = useTheme()
const isDarkMode = useIsDarkMode()
const closeModal = useCloseModal()
const fiatOnrampModalOpen = useModalIsOpen(ApplicationModal.FIAT_ONRAMP)
@@ -90,6 +95,7 @@ export default function FiatOnrampModal() {
},
method: 'POST',
body: JSON.stringify({
theme: isDarkMode ? 'dark' : 'light',
colorCode: theme.accentAction,
defaultCurrencyCode: 'eth',
redirectUrl: 'https://app.uniswap.org/#/swap',
@@ -112,7 +118,7 @@ export default function FiatOnrampModal() {
} finally {
setLoading(false)
}
}, [account, theme.accentAction])
}, [account, isDarkMode, theme.accentAction])
useEffect(() => {
fetchSignedIframeUrl()
@@ -120,7 +126,7 @@ export default function FiatOnrampModal() {
return (
<Modal isOpen={fiatOnrampModalOpen} onDismiss={closeModal} maxHeight={720}>
<Wrapper data-testid="fiat-onramp-modal">
<Wrapper data-testid="fiat-onramp-modal" isDarkMode={isDarkMode}>
{error ? (
<>
<ThemedText.MediumHeader>
@@ -135,7 +141,12 @@ export default function FiatOnrampModal() {
) : loading ? (
<StyledSpinner src={Circle} alt="loading spinner" size="90px" />
) : (
<StyledIframe src={signedIframeUrl ?? ''} frameBorder="0" title="fiat-onramp-iframe" />
<StyledIframe
src={signedIframeUrl ?? ''}
frameBorder="0"
title="fiat-onramp-iframe"
isDarkMode={isDarkMode}
/>
)}
</Wrapper>
</Modal>

View File

@@ -1,7 +1,13 @@
import { getWalletMeta } from '@uniswap/conedison/provider/meta'
import { useWeb3React } from '@web3-react/core'
import { MouseoverTooltip } from 'components/Tooltip'
import { Unicon } from 'components/Unicon'
import { ConnectionType } from 'connection'
import useENSAvatar from 'hooks/useENSAvatar'
import ms from 'ms.macro'
import { PropsWithChildren } from 'react'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { flexColumnNoWrap } from 'theme/styles'
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
@@ -50,14 +56,53 @@ const Socks = () => {
)
}
const useIcon = (connectionType: ConnectionType) => {
const { account } = useWeb3React()
const Divider = styled.div`
border-bottom: 1px solid ${({ theme }) => theme.backgroundOutline};
margin: 12px 0;
`
function UniconTooltip({ children, enabled }: PropsWithChildren<{ enabled?: boolean }>) {
return (
<MouseoverTooltip
timeout={ms`3s`}
offsetY={8}
disableHover={!enabled}
text={
// TODO(cartcrom): add Learn More link when unicon microsite is polished
<>
<ThemedText.SubHeaderSmall color="textPrimary" paddingTop="4px">
This is your Unicon
</ThemedText.SubHeaderSmall>
<Divider />
<ThemedText.Caption paddingBottom="4px">
Unicons are avatars for your wallet, generated from your address.
</ThemedText.Caption>
</>
}
placement="bottom"
>
<div>{children}</div>
</MouseoverTooltip>
)
}
const useIcon = (connectionType: ConnectionType, size?: number, enableInfotips?: boolean) => {
const { account, provider } = useWeb3React()
const { avatar } = useENSAvatar(account ?? undefined)
const isUniswapWallet = Boolean(provider && getWalletMeta(provider)?.name === 'Uniswap Wallet')
if (!account) return null
if (avatar || connectionType === ConnectionType.INJECTED) {
return <Identicon />
} else if (connectionType === ConnectionType.WALLET_CONNECT) {
return <img src={WalletConnectIcon} alt="WalletConnect" />
return isUniswapWallet ? (
<UniconTooltip enabled={enableInfotips}>
<Unicon address={account} size={size} />
</UniconTooltip>
) : (
<img src={WalletConnectIcon} alt="WalletConnect" />
)
} else if (connectionType === ConnectionType.COINBASE_WALLET) {
return <img src={CoinbaseWalletIcon} alt="Coinbase Wallet" />
}
@@ -65,9 +110,17 @@ const useIcon = (connectionType: ConnectionType) => {
return undefined
}
export default function StatusIcon({ connectionType, size }: { connectionType: ConnectionType; size?: number }) {
export default function StatusIcon({
connectionType,
size,
enableInfotips,
}: {
connectionType: ConnectionType
size?: number
enableInfotips?: boolean
}) {
const hasSocks = useHasSocks()
const icon = useIcon(connectionType)
const icon = useIcon(connectionType, size, enableInfotips)
return (
<IconWrapper size={size ?? 16}>

View File

@@ -1,3 +1,4 @@
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import useTokenLogoSource from 'hooks/useAssetLogoSource'
import React from 'react'
@@ -29,10 +30,28 @@ export type AssetLogoBaseProps = {
backupImg?: string | null
size?: string
style?: React.CSSProperties
hideL2Icon?: boolean
}
type AssetLogoProps = AssetLogoBaseProps & { isNative?: boolean; address?: string | null; chainId?: number }
// TODO(cartcrom): add prop to optionally render an L2Icon w/ the logo
const LogoContainer = styled.div`
position: relative;
display: flex;
`
const L2NetworkLogo = styled.div<{ networkUrl?: string; parentSize: string }>`
--size: ${({ parentSize }) => `calc(${parentSize} / 2)`};
width: var(--size);
height: var(--size);
position: absolute;
left: 50%;
bottom: 0;
background: url(${({ networkUrl }) => networkUrl});
background-repeat: no-repeat;
background-size: ${({ parentSize }) => `calc(${parentSize} / 2) calc(${parentSize} / 2)`};
display: ${({ networkUrl }) => !networkUrl && 'none'};
`
/**
* Renders an image by prioritizing a list of sources, and then eventually a fallback triangle alert
*/
@@ -44,25 +63,27 @@ export default function AssetLogo({
backupImg,
size = '24px',
style,
...rest
hideL2Icon = false,
}: AssetLogoProps) {
const imageProps = {
alt: `${symbol ?? 'token'} logo`,
size,
style,
...rest,
}
const [src, nextSrc] = useTokenLogoSource(address, chainId, isNative, backupImg)
const L2Icon = getChainInfo(chainId)?.circleLogoUrl
if (src) {
return <LogoImage {...imageProps} src={src} onError={nextSrc} />
} else {
return (
<MissingImageLogo size={size}>
{/* use only first 3 characters of Symbol for design reasons */}
{symbol?.toUpperCase().replace('$', '').replace(/\s+/g, '').slice(0, 3)}
</MissingImageLogo>
)
}
return (
<LogoContainer style={style}>
{src ? (
<LogoImage {...imageProps} src={src} onError={nextSrc} />
) : (
<MissingImageLogo size={size}>
{/* use only first 3 characters of Symbol for design reasons */}
{symbol?.toUpperCase().replace('$', '').replace(/\s+/g, '').slice(0, 3)}
</MissingImageLogo>
)}
{!hideL2Icon && <L2NetworkLogo networkUrl={L2Icon} parentSize={size} />}
</LogoContainer>
)
}

View File

@@ -15,6 +15,7 @@ export default function CurrencyLogo(
address={props.currency?.wrapped.address}
symbol={props.symbol ?? props.currency?.symbol}
backupImg={(props.currency as TokenInfo)?.logoURI}
hideL2Icon={props.hideL2Icon ?? true}
{...props}
/>
)

View File

@@ -1,4 +1,6 @@
import { NATIVE_CHAIN_ID } from 'constants/tokens'
import { TokenStandard } from 'graphql/data/__generated__/types-and-hooks'
import { SearchToken } from 'graphql/data/SearchTokens'
import { TokenQueryData } from 'graphql/data/Token'
import { TopToken } from 'graphql/data/TopTokens'
import { CHAIN_NAME_TO_CHAIN_ID } from 'graphql/data/util'
@@ -7,14 +9,14 @@ import AssetLogo, { AssetLogoBaseProps } from './AssetLogo'
export default function QueryTokenLogo(
props: AssetLogoBaseProps & {
token?: TopToken | TokenQueryData
token?: TopToken | TokenQueryData | SearchToken
}
) {
const chainId = props.token?.chain ? CHAIN_NAME_TO_CHAIN_ID[props.token?.chain] : undefined
return (
<AssetLogo
isNative={props.token?.address === NATIVE_CHAIN_ID}
isNative={props.token?.standard === TokenStandard.Native || props.token?.address === NATIVE_CHAIN_ID}
chainId={chainId}
address={props.token?.address}
symbol={props.token?.symbol}

View File

@@ -1,98 +0,0 @@
import { Trans } from '@lingui/macro'
import { ButtonPrimary } from 'components/Button'
import { AutoColumn } from 'components/Column'
import Modal from 'components/Modal'
import { RowBetween } from 'components/Row'
import { AlertTriangle } from 'react-feather'
import { Text } from 'rebass'
import styled from 'styled-components/macro'
import { CloseIcon, ThemedText } from 'theme'
import { useModalIsOpen, useToggleMetaMaskConnectionErrorModal } from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'
const Wrapper = styled.div`
width: 100%;
position: relative;
display: flex;
flex-flow: column;
align-items: center;
`
const Container = styled.div`
width: 100%;
padding: 32px 32px;
display: flex;
flex-flow: column;
align-items: center;
`
const LogoContainer = styled.div`
display: flex;
gap: 16px;
`
const ShortColumn = styled(AutoColumn)`
margin-top: 10px;
`
const InfoText = styled(Text)`
padding: 0 12px 0 12px;
font-size: 14px;
line-height: 20px;
text-align: center;
`
const StyledButton = styled(ButtonPrimary)`
margin-top: 24px;
width: 100%;
font-weight: 600;
`
const WarningIcon = styled(AlertTriangle)`
width: 76px;
height: 76px;
margin-top: 4px;
margin-bottom: 28px;
stroke-width: 1px;
margin-right: 4px;
color: ${({ theme }) => theme.accentCritical};
`
const onReconnect = () => window.location.reload()
export default function MetaMaskConnectionErrorModal() {
const modalOpen = useModalIsOpen(ApplicationModal.METAMASK_CONNECTION_ERROR)
const toggleModal = useToggleMetaMaskConnectionErrorModal()
return (
<Modal isOpen={modalOpen} onDismiss={toggleModal} minHeight={false} maxHeight={90}>
<Wrapper>
<RowBetween style={{ padding: '1rem' }}>
<div />
<CloseIcon onClick={toggleModal} />
</RowBetween>
<Container>
<AutoColumn>
<LogoContainer>
<WarningIcon />
</LogoContainer>
</AutoColumn>
<ShortColumn>
<InfoText>
<ThemedText.HeadlineSmall marginBottom="8px">
<Trans>Wallet disconnected</Trans>
</ThemedText.HeadlineSmall>
<ThemedText.BodySmall>
<Trans>A MetaMask error caused your wallet to disconnect. Reload the page to reconnect.</Trans>
</ThemedText.BodySmall>
</InfoText>
</ShortColumn>
<StyledButton onClick={onReconnect}>
<Trans>Reload</Trans>
</StyledButton>
</Container>
</Wrapper>
</Modal>
)
}

View File

@@ -17,6 +17,9 @@ const StyledDialogOverlay = styled(AnimatedDialogOverlay)<{ $scrollOverlay?: boo
display: flex;
align-items: center;
@media screen and (max-width: ${({ theme }) => theme.breakpoint.sm}px) {
align-items: flex-end;
}
overflow-y: ${({ $scrollOverlay }) => $scrollOverlay && 'scroll'};
justify-content: center;
@@ -27,7 +30,6 @@ const StyledDialogOverlay = styled(AnimatedDialogOverlay)<{ $scrollOverlay?: boo
type StyledDialogProps = {
$minHeight?: number | false
$maxHeight?: number
$isBottomSheet?: boolean
$scrollOverlay?: boolean
$hideBorder?: boolean
$maxWidth: number
@@ -40,14 +42,12 @@ const StyledDialogContent = styled(AnimatedDialogContent)<StyledDialogProps>`
&[data-reach-dialog-content] {
margin: auto;
background-color: ${({ theme }) => theme.backgroundSurface};
border: ${({ theme, $hideBorder }) => !$hideBorder && `1px solid ${theme.deprecated_bg1}`};
border: ${({ theme, $hideBorder }) => !$hideBorder && `1px solid ${theme.backgroundOutline}`};
box-shadow: ${({ theme }) => theme.deepShadow};
padding: 0px;
width: 50vw;
overflow-y: auto;
overflow-x: hidden;
align-self: ${({ $isBottomSheet }) => $isBottomSheet && 'flex-end'};
max-width: ${({ $maxWidth }) => $maxWidth}px;
${({ $maxHeight }) =>
$maxHeight &&
@@ -61,22 +61,17 @@ const StyledDialogContent = styled(AnimatedDialogContent)<StyledDialogProps>`
`}
display: ${({ $scrollOverlay }) => ($scrollOverlay ? 'inline-table' : 'flex')};
border-radius: 20px;
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
@media screen and (max-width: ${({ theme }) => theme.breakpoint.md}px) {
width: 65vw;
margin: auto;
`}
${({ theme, $isBottomSheet }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
width: 85vw;
${
$isBottomSheet &&
css`
width: 100vw;
border-radius: 20px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
`
}
`}
}
@media screen and (max-width: ${({ theme }) => theme.breakpoint.sm}px) {
margin: 0;
width: 100vw;
border-radius: 20px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
`
@@ -91,7 +86,6 @@ interface ModalProps {
children?: React.ReactNode
$scrollOverlay?: boolean
hideBorder?: boolean
isBottomSheet?: boolean
}
export default function Modal({
@@ -104,7 +98,6 @@ export default function Modal({
children,
onSwipe = onDismiss,
$scrollOverlay,
isBottomSheet = isMobile,
hideBorder = false,
}: ModalProps) {
const fadeTransition = useTransition(isOpen, {
@@ -148,7 +141,6 @@ export default function Modal({
aria-label="dialog"
$minHeight={minHeight}
$maxHeight={maxHeight}
$isBottomSheet={isBottomSheet}
$scrollOverlay={$scrollOverlay}
$hideBorder={hideBorder}
$maxWidth={maxWidth}

View File

@@ -1,4 +1,4 @@
import { Trans } from '@lingui/macro'
import { t, Trans } from '@lingui/macro'
import FeatureFlagModal from 'components/FeatureFlagModal/FeatureFlagModal'
import { PrivacyPolicyModal } from 'components/PrivacyPolicy'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
@@ -127,7 +127,7 @@ export const MenuDropdown = () => {
return (
<>
<Box position="relative" ref={ref}>
<NavIcon isActive={isOpen} onClick={toggleOpen}>
<NavIcon isActive={isOpen} onClick={toggleOpen} label={isOpen ? t`Show resources` : t`Hide resources`}>
<EllipsisIcon viewBox="0 0 20 20" width={24} height={24} />
</NavIcon>
@@ -166,6 +166,9 @@ export const MenuDropdown = () => {
<SecondaryLinkedText href="https://docs.uniswap.org/">
<Trans>Documentation</Trans>
</SecondaryLinkedText>
<SecondaryLinkedText href="https://uniswap.canny.io/feature-requests">
<Trans>Feedback</Trans>
</SecondaryLinkedText>
<SecondaryLinkedText
onClick={() => {
toggleOpen()

View File

@@ -1,3 +1,4 @@
import { t } from '@lingui/macro'
import { Box } from 'nft/components/Box'
import { ReactNode } from 'react'
@@ -6,10 +7,11 @@ import * as styles from './NavIcon.css'
interface NavIconProps {
children: ReactNode
isActive?: boolean
label?: string
onClick: () => void
}
export const NavIcon = ({ children, isActive, onClick }: NavIconProps) => {
export const NavIcon = ({ children, isActive, label = t`Navigation button`, onClick }: NavIconProps) => {
return (
<Box
as="button"
@@ -18,6 +20,7 @@ export const NavIcon = ({ children, isActive, onClick }: NavIconProps) => {
onClick={onClick}
height="40"
width="40"
aria-label={label}
>
{children}
</Box>

View File

@@ -0,0 +1,102 @@
import { SupportedChainId } from 'constants/chains'
import { NATIVE_CHAIN_ID, nativeOnChain } from 'constants/tokens'
import { Chain, NftCollection, useRecentlySearchedAssetsQuery } from 'graphql/data/__generated__/types-and-hooks'
import { SearchToken } from 'graphql/data/SearchTokens'
import { CHAIN_NAME_TO_CHAIN_ID } from 'graphql/data/util'
import { useAtom } from 'jotai'
import { atomWithStorage, useAtomValue } from 'jotai/utils'
import { GenieCollection } from 'nft/types'
import { useCallback, useMemo } from 'react'
import { getNativeTokenDBAddress } from 'utils/nativeTokens'
type RecentlySearchedAsset = {
isNft?: boolean
address: string
chain: Chain
}
// Temporary measure used until backend supports addressing by "NATIVE"
const NATIVE_QUERY_ADDRESS_INPUT = null as unknown as string
function getQueryAddress(chain: Chain) {
return getNativeTokenDBAddress(chain) ?? NATIVE_QUERY_ADDRESS_INPUT
}
const recentlySearchedAssetsAtom = atomWithStorage<RecentlySearchedAsset[]>('recentlySearchedAssets', [])
export function useAddRecentlySearchedAsset() {
const [searchHistory, updateSearchHistory] = useAtom(recentlySearchedAssetsAtom)
return useCallback(
(asset: RecentlySearchedAsset) => {
// Removes the new asset if it was already in the array
const newHistory = searchHistory.filter(
(oldAsset) => !(oldAsset.address === asset.address && oldAsset.chain === asset.chain)
)
newHistory.unshift(asset)
updateSearchHistory(newHistory)
},
[searchHistory, updateSearchHistory]
)
}
export function useRecentlySearchedAssets() {
const history = useAtomValue(recentlySearchedAssetsAtom)
const shortenedHistory = useMemo(() => history.slice(0, 4), [history])
const { data: queryData, loading } = useRecentlySearchedAssetsQuery({
variables: {
collectionAddresses: shortenedHistory.filter((asset) => asset.isNft).map((asset) => asset.address),
contracts: shortenedHistory
.filter((asset) => !asset.isNft)
.map((token) => ({
address: token.address === NATIVE_CHAIN_ID ? getQueryAddress(token.chain) : token.address,
chain: token.chain,
})),
},
})
const data = useMemo(() => {
if (shortenedHistory.length === 0) return []
else if (!queryData) return undefined
// Collects both tokens and collections in a map, so they can later be returned in original order
const resultsMap: { [key: string]: GenieCollection | SearchToken } = {}
const queryCollections = queryData?.nftCollections?.edges.map((edge) => edge.node as NonNullable<NftCollection>)
const collections = queryCollections?.map(
(queryCollection): GenieCollection => {
return {
address: queryCollection.nftContracts?.[0]?.address ?? '',
isVerified: queryCollection?.isVerified,
name: queryCollection?.name,
stats: {
floor_price: queryCollection?.markets?.[0]?.floorPrice?.value,
total_supply: queryCollection?.numAssets,
},
imageUrl: queryCollection?.image?.url ?? '',
}
},
[queryCollections]
)
collections?.forEach((collection) => (resultsMap[collection.address] = collection))
queryData.tokens?.filter(Boolean).forEach((token) => {
resultsMap[token.address ?? `NATIVE-${token.chain}`] = token
})
const data: (SearchToken | GenieCollection)[] = []
shortenedHistory.forEach((asset) => {
if (asset.address === 'NATIVE') {
// Handles special case where wMATIC data needs to be used for MATIC
const native = nativeOnChain(CHAIN_NAME_TO_CHAIN_ID[asset.chain] ?? SupportedChainId.MAINNET)
const queryAddress = getQueryAddress(asset.chain)?.toLowerCase() ?? `NATIVE-${asset.chain}`
const result = resultsMap[queryAddress]
if (result) data.push({ ...result, address: 'NATIVE', ...native })
} else {
const result = resultsMap[asset.address]
if (result) data.push(result)
}
})
return data
}, [queryData, shortenedHistory])
return { data, loading }
}

View File

@@ -2,7 +2,9 @@
import { t, Trans } from '@lingui/macro'
import { sendAnalyticsEvent, Trace, TraceEvent, useTrace } from '@uniswap/analytics'
import { BrowserEvent, InterfaceElementName, InterfaceEventName, InterfaceSectionName } from '@uniswap/analytics-events'
import { useWeb3React } from '@web3-react/core'
import clsx from 'clsx'
import { useSearchTokens } from 'graphql/data/SearchTokens'
import useDebounce from 'hooks/useDebounce'
import { useIsNftPage } from 'hooks/useIsNftPage'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
@@ -12,7 +14,6 @@ import { Row } from 'nft/components/Flex'
import { magicalGradientOnHover } from 'nft/css/common.css'
import { useIsMobile, useIsTablet } from 'nft/hooks'
import { fetchSearchCollections } from 'nft/queries'
import { fetchSearchTokens } from 'nft/queries/genie/SearchTokensFetcher'
import { ChangeEvent, useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react'
import { useQuery } from 'react-query'
import { useLocation } from 'react-router-dom'
@@ -64,16 +65,8 @@ export const SearchBar = () => {
}
)
const { data: tokens, isLoading: tokensAreLoading } = useQuery(
['searchTokens', debouncedSearchValue],
() => fetchSearchTokens(debouncedSearchValue),
{
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
enabled: !!debouncedSearchValue.length,
}
)
const { chainId } = useWeb3React()
const { data: tokens, loading: tokensAreLoading } = useSearchTokens(debouncedSearchValue, chainId ?? 1)
const isNFTPage = useIsNftPage()
@@ -148,6 +141,7 @@ export const SearchBar = () => {
return (
<Trace section={InterfaceSectionName.NAVBAR_SEARCH}>
<Box
data-cy="search-bar"
position={{ sm: 'fixed', md: 'absolute', xl: 'relative' }}
width={{ sm: isOpen ? 'viewWidth' : 'auto', md: 'auto' }}
ref={searchRef}
@@ -187,6 +181,7 @@ export const SearchBar = () => {
render={({ translation }) => (
<Box
as="input"
data-cy="search-bar-input"
placeholder={translation as string}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
!isOpen && toggleOpen()
@@ -219,7 +214,7 @@ export const SearchBar = () => {
</Box>
</Box>
{isMobileOrTablet && (
<NavIcon onClick={toggleOpen}>
<NavIcon onClick={toggleOpen} label={placeholderText}>
<NavMagnifyingGlassIcon />
</NavIcon>
)}

View File

@@ -1,30 +1,35 @@
import { Trans } from '@lingui/macro'
import { useTrace } from '@uniswap/analytics'
import { InterfaceSectionName, NavBarSearchTypes } from '@uniswap/analytics-events'
import { useWeb3React } from '@web3-react/core'
import { useNftGraphqlEnabled } from 'featureFlags/flags/nftlGraphql'
import { HistoryDuration, SafetyLevel } from 'graphql/data/__generated__/types-and-hooks'
import { useTrendingCollections } from 'graphql/data/nft/TrendingCollections'
import { SearchToken } from 'graphql/data/SearchTokens'
import useTrendingTokens from 'graphql/data/TrendingTokens'
import { useIsNftPage } from 'hooks/useIsNftPage'
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
import { subheadSmall } from 'nft/css/common.css'
import { useSearchHistory } from 'nft/hooks'
import { fetchTrendingCollections } from 'nft/queries'
import { fetchTrendingTokens } from 'nft/queries/genie/TrendingTokensFetcher'
import { FungibleToken, GenieCollection, TimePeriod, TrendingCollection } from 'nft/types'
import { GenieCollection, TimePeriod, TrendingCollection } from 'nft/types'
import { formatEthPrice } from 'nft/utils/currency'
import { ReactNode, useEffect, useMemo, useState } from 'react'
import { useQuery } from 'react-query'
import { useLocation } from 'react-router-dom'
import { ClockIcon, TrendingArrow } from '../../nft/components/icons'
import { useRecentlySearchedAssets } from './RecentlySearchedAssets'
import * as styles from './SearchBar.css'
import { CollectionRow, SkeletonRow, TokenRow } from './SuggestionRow'
function isCollection(suggestion: GenieCollection | FungibleToken | TrendingCollection) {
return (suggestion as FungibleToken).decimals === undefined
function isCollection(suggestion: GenieCollection | SearchToken | TrendingCollection) {
return (suggestion as SearchToken).decimals === undefined
}
interface SearchBarDropdownSectionProps {
toggleOpen: () => void
suggestions: (GenieCollection | FungibleToken)[]
suggestions: (GenieCollection | SearchToken)[]
header: JSX.Element
headerIcon?: JSX.Element
hoveredIndex: number | undefined
@@ -46,14 +51,14 @@ const SearchBarDropdownSection = ({
eventProperties,
}: SearchBarDropdownSectionProps) => {
return (
<Column gap="12">
<Column gap="12" data-cy="searchbar-dropdown">
<Row paddingX="16" paddingY="4" gap="8" color="gray300" className={subheadSmall} style={{ lineHeight: '20px' }}>
{headerIcon ? headerIcon : null}
<Box>{header}</Box>
</Row>
<Column gap="12">
{suggestions.map((suggestion, index) =>
isLoading ? (
isLoading || !suggestion ? (
<SkeletonRow key={index} />
) : isCollection(suggestion) ? (
<CollectionRow
@@ -73,7 +78,7 @@ const SearchBarDropdownSection = ({
) : (
<TokenRow
key={suggestion.address}
token={suggestion as FungibleToken}
token={suggestion as SearchToken}
isHovered={hoveredIndex === index + startingIndex}
setHoveredIndex={setHoveredIndex}
toggleOpen={toggleOpen}
@@ -92,9 +97,13 @@ const SearchBarDropdownSection = ({
)
}
function isKnownToken(token: SearchToken) {
return token.project?.safetyLevel == SafetyLevel.Verified || token.project?.safetyLevel == SafetyLevel.MediumWarning
}
interface SearchBarDropdownProps {
toggleOpen: () => void
tokens: FungibleToken[]
tokens: SearchToken[]
collections: GenieCollection[]
queryText: string
hasInput: boolean
@@ -110,10 +119,13 @@ export const SearchBarDropdown = ({
isLoading,
}: SearchBarDropdownProps) => {
const [hoveredIndex, setHoveredIndex] = useState<number | undefined>(0)
const { history: searchHistory, updateItem: updateSearchHistory } = useSearchHistory()
const shortenedHistory = useMemo(() => searchHistory.slice(0, 2), [searchHistory])
const { data: searchHistory } = useRecentlySearchedAssets()
const shortenedHistory = useMemo(() => searchHistory?.slice(0, 2) ?? [...Array<SearchToken>(2)], [searchHistory])
const { pathname } = useLocation()
const isNFTPage = useIsNftPage()
const isNftGraphqlEnabled = useNftGraphqlEnabled()
const isTokenPage = pathname.includes('/tokens')
const [resultsState, setResultsState] = useState<ReactNode>()
@@ -122,45 +134,32 @@ export const SearchBarDropdown = ({
() => fetchTrendingCollections({ volumeType: 'eth', timePeriod: 'ONE_DAY' as TimePeriod, size: 3 })
)
const trendingCollections = useMemo(
() =>
trendingCollectionResults
? trendingCollectionResults
.map((collection) => ({
...collection,
collectionAddress: collection.address,
floorPrice: formatEthPrice(collection.floor?.toString()),
stats: {
total_supply: collection.totalSupply,
one_day_change: collection.floorChange,
floor_price: formatEthPrice(collection.floor?.toString()),
},
}))
.slice(0, isNFTPage ? 3 : 2)
: [...Array<GenieCollection>(isNFTPage ? 3 : 2)],
[isNFTPage, trendingCollectionResults]
)
const { data: gqlData, loading } = useTrendingCollections(3, HistoryDuration.Day)
const { data: trendingTokenResults, isLoading: trendingTokensAreLoading } = useQuery(
['trendingTokens'],
() => fetchTrendingTokens(4),
{
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
}
)
useEffect(() => {
trendingTokenResults?.forEach(updateSearchHistory)
}, [trendingTokenResults, updateSearchHistory])
const trendingCollections = useMemo(() => {
const gatedTrendingCollections = isNftGraphqlEnabled ? gqlData : trendingCollectionResults
return gatedTrendingCollections && (!isNftGraphqlEnabled || !loading)
? gatedTrendingCollections
.map((collection) => ({
...collection,
collectionAddress: collection.address,
floorPrice: isNftGraphqlEnabled ? collection.floor : formatEthPrice(collection.floor?.toString()),
stats: {
total_supply: collection.totalSupply,
one_day_change: collection.floorChange,
floor_price: isNftGraphqlEnabled ? collection.floor : formatEthPrice(collection.floor?.toString()),
},
}))
.slice(0, isNFTPage ? 3 : 2)
: [...Array<GenieCollection>(isNFTPage ? 3 : 2)]
}, [gqlData, isNFTPage, isNftGraphqlEnabled, loading, trendingCollectionResults])
const { data: trendingTokenData } = useTrendingTokens(useWeb3React().chainId)
const trendingTokensLength = isTokenPage ? 3 : 2
const trendingTokens = useMemo(
() =>
trendingTokenResults
? trendingTokenResults.slice(0, trendingTokensLength)
: [...Array<FungibleToken>(trendingTokensLength)],
[trendingTokenResults, trendingTokensLength]
() => trendingTokenData?.slice(0, trendingTokensLength) ?? [...Array<SearchToken>(trendingTokensLength)],
[trendingTokenData, trendingTokensLength]
)
const totalSuggestions = hasInput
@@ -197,10 +196,9 @@ export const SearchBarDropdown = ({
}, [toggleOpen, hoveredIndex, totalSuggestions])
const hasVerifiedCollection = collections.some((collection) => collection.isVerified)
const hasVerifiedToken = tokens.some((token) => token.onDefaultList)
const hasKnownToken = tokens.some(isKnownToken)
const showCollectionsFirst =
(isNFTPage && (hasVerifiedCollection || !hasVerifiedToken)) ||
(!isNFTPage && !hasVerifiedToken && hasVerifiedCollection)
(isNFTPage && (hasVerifiedCollection || !hasKnownToken)) || (!isNFTPage && !hasKnownToken && hasVerifiedCollection)
const trace = JSON.stringify(useTrace({ section: InterfaceSectionName.NAVBAR_SEARCH }))
@@ -277,6 +275,7 @@ export const SearchBarDropdown = ({
}}
header={<Trans>Recent searches</Trans>}
headerIcon={<ClockIcon />}
isLoading={!searchHistory}
/>
)}
{!isNFTPage && (
@@ -292,7 +291,7 @@ export const SearchBarDropdown = ({
}}
header={<Trans>Popular tokens</Trans>}
headerIcon={<TrendingArrow />}
isLoading={trendingTokensAreLoading}
isLoading={!trendingTokenData}
/>
)}
{!isTokenPage && (
@@ -323,7 +322,7 @@ export const SearchBarDropdown = ({
trendingCollections,
trendingCollectionsAreLoading,
trendingTokens,
trendingTokensAreLoading,
trendingTokenData,
hoveredIndex,
toggleOpen,
shortenedHistory,
@@ -334,6 +333,7 @@ export const SearchBarDropdown = ({
queryText,
totalSuggestions,
trace,
searchHistory,
])
return (

View File

@@ -1,44 +1,34 @@
import { sendAnalyticsEvent } from '@uniswap/analytics'
import { InterfaceEventName } from '@uniswap/analytics-events'
import { formatUSDPrice } from '@uniswap/conedison/format'
import { useWeb3React } from '@web3-react/core'
import clsx from 'clsx'
import AssetLogo from 'components/Logo/AssetLogo'
import { L2NetworkLogo, LogoContainer } from 'components/Tokens/TokenTable/TokenRow'
import QueryTokenLogo from 'components/Logo/QueryTokenLogo'
import TokenSafetyIcon from 'components/TokenSafety/TokenSafetyIcon'
import { getChainInfo } from 'constants/chainInfo'
import { NATIVE_CHAIN_ID } from 'constants/tokens'
import { checkWarning } from 'constants/tokenSafety'
import { checkSearchTokenWarning } from 'constants/tokenSafety'
import { Chain, TokenStandard } from 'graphql/data/__generated__/types-and-hooks'
import { SearchToken } from 'graphql/data/SearchTokens'
import { getTokenDetailsURL } from 'graphql/data/util'
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
import { VerifiedIcon } from 'nft/components/icons'
import { vars } from 'nft/css/sprinkles.css'
import { useSearchHistory } from 'nft/hooks'
import { FungibleToken, GenieCollection } from 'nft/types'
import { GenieCollection } from 'nft/types'
import { ethNumberStandardFormatter } from 'nft/utils/currency'
import { putCommas } from 'nft/utils/putCommas'
import { useCallback, useEffect, useState } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { getDeltaArrow } from '../Tokens/TokenDetails/PriceChart'
import { DeltaText, getDeltaArrow } from '../Tokens/TokenDetails/PriceChart'
import { useAddRecentlySearchedAsset } from './RecentlySearchedAssets'
import * as styles from './SearchBar.css'
const StyledLogoContainer = styled(LogoContainer)`
margin-right: 8px;
`
const PriceChangeContainer = styled.div`
display: flex;
align-items: center;
`
const PriceChangeText = styled.span<{ isNegative: boolean }>`
font-size: 14px;
line-height: 20px;
color: ${({ theme, isNegative }) => (isNegative ? theme.accentFailure : theme.accentSuccess)};
`
const ArrowCell = styled.span`
padding-top: 5px;
padding-right: 3px;
@@ -63,16 +53,15 @@ export const CollectionRow = ({
}: CollectionRowProps) => {
const [brokenImage, setBrokenImage] = useState(false)
const [loaded, setLoaded] = useState(false)
const addToSearchHistory = useSearchHistory(
(state: { addItem: (item: FungibleToken | GenieCollection) => void }) => state.addItem
)
const addRecentlySearchedAsset = useAddRecentlySearchedAsset()
const navigate = useNavigate()
const handleClick = useCallback(() => {
addToSearchHistory(collection)
addRecentlySearchedAsset({ ...collection, isNft: true, chain: Chain.Ethereum })
toggleOpen()
sendAnalyticsEvent(InterfaceEventName.NAVBAR_RESULT_SELECTED, { ...eventProperties })
}, [addToSearchHistory, collection, toggleOpen, eventProperties])
}, [addRecentlySearchedAsset, collection, toggleOpen, eventProperties])
useEffect(() => {
const keyDownHandler = (event: KeyboardEvent) => {
@@ -130,17 +119,8 @@ export const CollectionRow = ({
)
}
function useBridgedAddress(token: FungibleToken): [string | undefined, number | undefined, string | undefined] {
const { chainId: connectedChainId } = useWeb3React()
const bridgedAddress = connectedChainId ? token.extensions?.bridgeInfo?.[connectedChainId]?.tokenAddress : undefined
if (bridgedAddress && connectedChainId) {
return [bridgedAddress, connectedChainId, getChainInfo(connectedChainId)?.circleLogoUrl]
}
return [undefined, undefined, undefined]
}
interface TokenRowProps {
token: FungibleToken
token: SearchToken
isHovered: boolean
setHoveredIndex: (index: number | undefined) => void
toggleOpen: () => void
@@ -149,19 +129,18 @@ interface TokenRowProps {
}
export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index, eventProperties }: TokenRowProps) => {
const addToSearchHistory = useSearchHistory(
(state: { addItem: (item: FungibleToken | GenieCollection) => void }) => state.addItem
)
const addRecentlySearchedAsset = useAddRecentlySearchedAsset()
const navigate = useNavigate()
const handleClick = useCallback(() => {
addToSearchHistory(token)
const address = !token.address && token.standard === TokenStandard.Native ? 'NATIVE' : token.address
address && addRecentlySearchedAsset({ address, chain: token.chain })
toggleOpen()
sendAnalyticsEvent(InterfaceEventName.NAVBAR_RESULT_SELECTED, { ...eventProperties })
}, [addToSearchHistory, toggleOpen, token, eventProperties])
}, [addRecentlySearchedAsset, token, toggleOpen, eventProperties])
const [bridgedAddress, bridgedChain, L2Icon] = useBridgedAddress(token)
const tokenDetailsPath = getTokenDetailsURL(bridgedAddress ?? token.address, undefined, bridgedChain ?? token.chainId)
const tokenDetailsPath = getTokenDetailsURL(token)
// Close the modal on escape
useEffect(() => {
const keyDownHandler = (event: KeyboardEvent) => {
@@ -177,10 +156,11 @@ export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index,
}
}, [toggleOpen, isHovered, token, navigate, handleClick, tokenDetailsPath])
const arrow = getDeltaArrow(token.price24hChange, 18)
const arrow = getDeltaArrow(token.market?.pricePercentChange?.value, 18)
return (
<Link
data-cy={`searchbar-token-row-${token.symbol}`}
to={tokenDetailsPath}
onClick={handleClick}
onMouseEnter={() => !isHovered && setHoveredIndex(index)}
@@ -189,39 +169,37 @@ export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index,
style={{ background: isHovered ? vars.color.lightGrayOverlay : 'none' }}
>
<Row style={{ width: '65%' }}>
<StyledLogoContainer>
<AssetLogo
isNative={token.address === NATIVE_CHAIN_ID}
address={token.address}
chainId={token.chainId}
symbol={token.symbol}
size="36px"
backupImg={token.logoURI}
/>
<L2NetworkLogo networkUrl={L2Icon} size="16px" />
</StyledLogoContainer>
<QueryTokenLogo
token={token}
symbol={token.symbol}
size="36px"
backupImg={token.project?.logoUrl}
style={{ paddingRight: '8px' }}
/>
<Column className={styles.suggestionPrimaryContainer}>
<Row gap="4" width="full">
<Box className={styles.primaryText}>{token.name}</Box>
<TokenSafetyIcon warning={checkWarning(token.address)} />
<TokenSafetyIcon warning={checkSearchTokenWarning(token)} />
</Row>
<Box className={styles.secondaryText}>{token.symbol}</Box>
</Column>
</Row>
<Column className={styles.suggestionSecondaryContainer}>
{token.priceUsd && (
<Row gap="4">
<Box className={styles.primaryText}>{formatUSDPrice(token.priceUsd)}</Box>
</Row>
)}
{token.price24hChange && (
<PriceChangeContainer>
<ArrowCell>{arrow}</ArrowCell>
<PriceChangeText isNegative={token.price24hChange < 0}>
{Math.abs(token.price24hChange).toFixed(2)}%
</PriceChangeText>
</PriceChangeContainer>
{!!token.market?.price?.value && (
<>
<Row gap="4">
<Box className={styles.primaryText}>{formatUSDPrice(token.market.price.value)}</Box>
</Row>
<PriceChangeContainer>
<ArrowCell>{arrow}</ArrowCell>
<ThemedText.BodySmall>
<DeltaText delta={token.market?.pricePercentChange?.value}>
{Math.abs(token.market?.pricePercentChange?.value ?? 0).toFixed(2)}%
</DeltaText>
</ThemedText.BodySmall>
</PriceChangeContainer>
</>
)}
</Column>
</Link>

View File

@@ -6,6 +6,8 @@ import { useIsNftPage } from 'hooks/useIsNftPage'
import { Box } from 'nft/components/Box'
import { Row } from 'nft/components/Flex'
import { UniIcon } from 'nft/components/icons'
import { useProfilePageState } from 'nft/hooks'
import { ProfilePageStateType } from 'nft/types'
import { ReactNode } from 'react'
import { NavLink, NavLinkProps, useLocation, useNavigate } from 'react-router-dom'
import styled from 'styled-components/macro'
@@ -54,8 +56,7 @@ export const PageTabs = () => {
pathname.startsWith('/pool') ||
pathname.startsWith('/add') ||
pathname.startsWith('/remove') ||
pathname.startsWith('/increase') ||
pathname.startsWith('/find')
pathname.startsWith('/increase')
const isNftPage = useIsNftPage()
@@ -70,7 +71,7 @@ export const PageTabs = () => {
<MenuItem dataTestId="nft-nav" href="/nfts" isActive={isNftPage}>
<Trans>NFTs</Trans>
</MenuItem>
<MenuItem href="/pool" id="pool-nav-link" isActive={isPoolActive}>
<MenuItem href="/pool" dataTestId="pool-nav-link" isActive={isPoolActive}>
<Trans>Pool</Trans>
</MenuItem>
</>
@@ -79,6 +80,7 @@ export const PageTabs = () => {
const Navbar = () => {
const isNftPage = useIsNftPage()
const sellPageState = useProfilePageState((state) => state.state)
const navigate = useNavigate()
return (
@@ -120,7 +122,7 @@ const Navbar = () => {
<Box display={{ sm: 'none', lg: 'flex' }}>
<MenuDropdown />
</Box>
{isNftPage && <Bag />}
{isNftPage && sellPageState !== ProfilePageStateType.LISTING && <Bag />}
{!isNftPage && (
<Box display={{ sm: 'none', lg: 'flex' }}>
<ChainSelector />

View File

@@ -32,7 +32,7 @@ const SHOULD_SHOW_ALERT = {
[SupportedChainId.OPTIMISM]: true,
[SupportedChainId.OPTIMISM_GOERLI]: true,
[SupportedChainId.ARBITRUM_ONE]: true,
[SupportedChainId.ARBITRUM_RINKEBY]: true,
[SupportedChainId.ARBITRUM_GOERLI]: true,
[SupportedChainId.POLYGON]: true,
[SupportedChainId.POLYGON_MUMBAI]: true,
[SupportedChainId.CELO]: true,
@@ -59,7 +59,7 @@ const BG_COLORS_BY_DARK_MODE_AND_CHAIN_ID: {
'radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.04) 100%),radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.04) 0%, rgba(235, 0, 255, 0.01 96%)',
[SupportedChainId.ARBITRUM_ONE]:
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.01) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(75% 75% at 0% 0%, rgba(150, 190, 220, 0.05) 0%, rgba(33, 114, 229, 0.05) 100%), hsla(0, 0%, 100%, 0.05)',
[SupportedChainId.ARBITRUM_RINKEBY]:
[SupportedChainId.ARBITRUM_GOERLI]:
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.05) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(75% 75% at 0% 0%, rgba(150, 190, 220, 0.05) 0%, rgba(33, 114, 229, 0.1) 100%), hsla(0, 0%, 100%, 0.05)',
},
light: {
@@ -77,7 +77,7 @@ const BG_COLORS_BY_DARK_MODE_AND_CHAIN_ID: {
'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.ARBITRUM_ONE]:
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(circle at top left, hsla(206, 50%, 75%, 0.01), hsla(215, 79%, 51%, 0.12)), hsla(0, 0%, 100%, 0.1)',
[SupportedChainId.ARBITRUM_RINKEBY]:
[SupportedChainId.ARBITRUM_GOERLI]:
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(circle at top left, hsla(206, 50%, 75%, 0.01), hsla(215, 79%, 51%, 0.12)), hsla(0, 0%, 100%, 0.1)',
},
}
@@ -137,7 +137,7 @@ const TEXT_COLORS: { [chainId in NetworkAlertChains]: string } = {
[SupportedChainId.OPTIMISM]: '#ff3856',
[SupportedChainId.OPTIMISM_GOERLI]: '#ff3856',
[SupportedChainId.ARBITRUM_ONE]: '#0490ed',
[SupportedChainId.ARBITRUM_RINKEBY]: '#0490ed',
[SupportedChainId.ARBITRUM_GOERLI]: '#0490ed',
}
function shouldShowAlert(chainId: number | undefined): chainId is NetworkAlertChains {

View File

@@ -3,11 +3,9 @@ import { useWeb3React } from '@web3-react/core'
import { RowFixed } from 'components/Row'
import { getChainInfo } from 'constants/chainInfo'
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
import useGasPrice from 'hooks/useGasPrice'
import { useIsLandingPage } from 'hooks/useIsLandingPage'
import { useIsNftPage } from 'hooks/useIsNftPage'
import useMachineTimeMs from 'hooks/useMachineTime'
import JSBI from 'jsbi'
import useBlockNumber from 'lib/hooks/useBlockNumber'
import ms from 'ms.macro'
import { useEffect, useMemo, useState } from 'react'
@@ -71,17 +69,6 @@ const StyledPollingDot = styled.div<{ warning: boolean }>`
transition: 250ms ease background-color;
`
const StyledGasDot = styled.div`
background-color: ${({ theme }) => theme.textTertiary};
border-radius: 50%;
height: 4px;
min-height: 4px;
min-width: 4px;
position: relative;
transition: 250ms ease background-color;
width: 4px;
`
const rotate360 = keyframes`
from {
transform: rotate(0deg);
@@ -123,9 +110,6 @@ export default function Polling() {
const isNftPage = useIsNftPage()
const isLandingPage = useIsLandingPage()
const ethGasPrice = useGasPrice()
const priceGwei = ethGasPrice ? JSBI.divide(ethGasPrice, JSBI.BigInt(1000000000)) : undefined
const waitMsBeforeWarning =
(chainId ? getChainInfo(chainId)?.blockWaitMsBeforeWarning : DEFAULT_MS_BEFORE_WARNING) ?? DEFAULT_MS_BEFORE_WARNING
@@ -163,25 +147,6 @@ export default function Polling() {
return (
<RowFixed>
<StyledPolling onMouseEnter={() => setIsHover(true)} onMouseLeave={() => setIsHover(false)}>
<ExternalLink href="https://etherscan.io/gastracker">
{!!priceGwei && (
<RowFixed style={{ marginRight: '8px' }}>
<ThemedText.DeprecatedMain fontSize="11px" mr="8px">
<MouseoverTooltip
text={
<Trans>
The current fast gas amount for sending a transaction on L1. Gas fees are paid in Ethereum&apos;s
native currency Ether (ETH) and denominated in GWEI.
</Trans>
}
>
{priceGwei.toString()} <Trans>gwei</Trans>
</MouseoverTooltip>
</ThemedText.DeprecatedMain>
<StyledGasDot />
</RowFixed>
)}
</ExternalLink>
<StyledPollingBlockNumber breathe={isMounting} hovering={isHover} warning={warning}>
<ExternalLink href={blockExternalLinkHref}>
<MouseoverTooltip

View File

@@ -99,9 +99,9 @@ export default function PositionList({
</ToggleLabel>
</ToggleWrap>
</MobileHeader>
{positions.map((p) => {
return <PositionListItem key={p.tokenId.toString()} positionDetails={p} />
})}
{positions.map((p) => (
<PositionListItem key={p.tokenId.toString()} {...p} />
))}
</>
)
}

View File

@@ -0,0 +1,38 @@
import { BigNumber } from '@ethersproject/bignumber'
import { render, screen } from 'test-utils'
import PositionListItem from '.'
jest.mock('hooks/Tokens', () => {
const originalModule = jest.requireActual('hooks/Tokens')
const uniSDK = jest.requireActual('@uniswap/sdk-core')
return {
__esModule: true,
...originalModule,
useToken: jest.fn(
() =>
new uniSDK.Token(
1,
'0x39AA39c021dfbaE8faC545936693aC917d5E7563',
8,
'https://www.example.com',
'example.com coin'
)
),
}
})
test('PositionListItem should not render when the name contains a url', () => {
const positionDetails = {
token0: '0x39AA39c021dfbaE8faC545936693aC917d5E7563',
token1: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
tokenId: BigNumber.from(436148),
fee: 100,
liquidity: BigNumber.from('0x5c985aff8059be04'),
tickLower: -800,
tickUpper: 1600,
}
render(<PositionListItem {...positionDetails} />)
screen.debug()
expect(screen.queryByText('.com', { exact: false })).toBe(null)
})

View File

@@ -1,3 +1,4 @@
import { BigNumber } from '@ethersproject/bignumber'
import { Trans } from '@lingui/macro'
import { Percent, Price, Token } from '@uniswap/sdk-core'
import { Position } from '@uniswap/v3-sdk'
@@ -15,9 +16,9 @@ import { Link } from 'react-router-dom'
import { Bound } from 'state/mint/v3/actions'
import styled from 'styled-components/macro'
import { HideSmall, MEDIA_WIDTHS, SmallOnly } from 'theme'
import { PositionDetails } from 'types/position'
import { formatTickPrice } from 'utils/formatTickPrice'
import { unwrappedToken } from 'utils/unwrappedToken'
import { hasURL } from 'utils/urlChecks'
import { DAI, USDC_MAINNET, USDT, WBTC, WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens'
@@ -109,7 +110,13 @@ const DataText = styled.div`
`
interface PositionListItemProps {
positionDetails: PositionDetails
token0: string
token1: string
tokenId: BigNumber
fee: number
liquidity: BigNumber
tickLower: number
tickUpper: number
}
export function getPriceOrderingFromPositionForUI(position?: Position): {
@@ -166,16 +173,15 @@ export function getPriceOrderingFromPositionForUI(position?: Position): {
}
}
export default function PositionListItem({ positionDetails }: PositionListItemProps) {
const {
token0: token0Address,
token1: token1Address,
fee: feeAmount,
liquidity,
tickLower,
tickUpper,
} = positionDetails
export default function PositionListItem({
token0: token0Address,
token1: token1Address,
tokenId,
fee: feeAmount,
liquidity,
tickLower,
tickUpper,
}: PositionListItemProps) {
const token0 = useToken(token0Address)
const token1 = useToken(token1Address)
@@ -203,10 +209,23 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr
// check if price is within range
const outOfRange: boolean = pool ? pool.tickCurrent < tickLower || pool.tickCurrent >= tickUpper : false
const positionSummaryLink = '/pool/' + positionDetails.tokenId
const positionSummaryLink = '/pool/' + tokenId
const removed = liquidity?.eq(0)
const containsURL = useMemo(
() =>
[token0?.name, token0?.symbol, token1?.name, token1?.symbol].reduce(
(acc, testString) => acc || Boolean(testString && hasURL(testString)),
false
),
[token0?.name, token0?.symbol, token1?.name, token1?.symbol]
)
if (containsURL) {
return null
}
return (
<LinkRow to={positionSummaryLink}>
<RowBetween>

View File

@@ -105,235 +105,240 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
}
<div
style="position: relative; height: 10px; width: 100%; overflow: auto; will-change: transform; direction: ltr;"
style="padding-right: 4px;"
>
<div
style="height: 168px; width: 100%;"
class="CurrencyList_scrollbarStyle__1pi21y70"
style="position: relative; height: 10px; width: 100%; overflow: auto; will-change: transform; direction: ltr;"
>
<div
class="c0 c1 c2 c3 token-item-0x6B175474E89094C44Da98b954EedeAC495271d0F"
style="position: absolute; left: 0px; top: 0px; height: 56px; width: 100%;"
tabindex="0"
style="height: 168px; width: 100%;"
>
<div
class="c4"
>
CurrencyLogo currency=DAI
</div>
<div
class="c5"
style="opacity: 1;"
class="c0 c1 c2 c3 token-item-0x6B175474E89094C44Da98b954EedeAC495271d0F"
style="position: absolute; left: 0px; top: 0px; height: 56px; width: 100%;"
tabindex="0"
>
<div
class="c0 c1"
class="c4"
>
CurrencyLogo currency=DAI
</div>
<div
class="c5"
style="opacity: 1;"
>
<div
class="c6 css-vurnku"
title="Dai Stablecoin"
>
Dai Stablecoin
</div>
<div
class="c7"
class="c0 c1"
>
<div
class="c8"
class="c6 css-vurnku"
title="Dai Stablecoin"
>
<svg
class="c9"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
Dai Stablecoin
</div>
<div
class="c7"
>
<div
class="c8"
>
<path
d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"
/>
<line
x1="12"
x2="12"
y1="9"
y2="13"
/>
<line
x1="12"
x2="12.01"
y1="17"
y2="17"
/>
</svg>
<svg
class="c9"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"
/>
<line
x1="12"
x2="12"
y1="9"
y2="13"
/>
<line
x1="12"
x2="12.01"
y1="17"
y2="17"
/>
</svg>
</div>
</div>
</div>
</div>
<div
class="c10 css-yfjwjl"
>
DAI
</div>
</div>
<div
class="c4"
>
<div
class="c0 c1 c11"
style="justify-self: flex-end;"
/>
</div>
</div>
<div
class="c0 c1 c2 c3 token-item-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
style="position: absolute; left: 0px; top: 56px; height: 56px; width: 100%;"
tabindex="0"
>
<div
class="c4"
>
CurrencyLogo currency=USDC
</div>
<div
class="c5"
style="opacity: 1;"
>
<div
class="c0 c1"
>
<div
class="c6 css-vurnku"
title="USD//C"
class="c10 css-yfjwjl"
>
USD//C
DAI
</div>
</div>
<div
class="c4"
>
<div
class="c7"
class="c0 c1 c11"
style="justify-self: flex-end;"
/>
</div>
</div>
<div
class="c0 c1 c2 c3 token-item-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
style="position: absolute; left: 0px; top: 56px; height: 56px; width: 100%;"
tabindex="0"
>
<div
class="c4"
>
CurrencyLogo currency=USDC
</div>
<div
class="c5"
style="opacity: 1;"
>
<div
class="c0 c1"
>
<div
class="c8"
class="c6 css-vurnku"
title="USD//C"
>
<svg
class="c9"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
USD//C
</div>
<div
class="c7"
>
<div
class="c8"
>
<path
d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"
/>
<line
x1="12"
x2="12"
y1="9"
y2="13"
/>
<line
x1="12"
x2="12.01"
y1="17"
y2="17"
/>
</svg>
<svg
class="c9"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"
/>
<line
x1="12"
x2="12"
y1="9"
y2="13"
/>
<line
x1="12"
x2="12.01"
y1="17"
y2="17"
/>
</svg>
</div>
</div>
</div>
</div>
<div
class="c10 css-yfjwjl"
>
USDC
</div>
</div>
<div
class="c4"
>
<div
class="c0 c1 c11"
style="justify-self: flex-end;"
/>
</div>
</div>
<div
class="c0 c1 c2 c3 token-item-0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
style="position: absolute; left: 0px; top: 112px; height: 56px; width: 100%;"
tabindex="0"
>
<div
class="c4"
>
CurrencyLogo currency=WBTC
</div>
<div
class="c5"
style="opacity: 1;"
>
<div
class="c0 c1"
>
<div
class="c6 css-vurnku"
title="Wrapped BTC"
class="c10 css-yfjwjl"
>
Wrapped BTC
USDC
</div>
</div>
<div
class="c4"
>
<div
class="c7"
class="c0 c1 c11"
style="justify-self: flex-end;"
/>
</div>
</div>
<div
class="c0 c1 c2 c3 token-item-0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"
style="position: absolute; left: 0px; top: 112px; height: 56px; width: 100%;"
tabindex="0"
>
<div
class="c4"
>
CurrencyLogo currency=WBTC
</div>
<div
class="c5"
style="opacity: 1;"
>
<div
class="c0 c1"
>
<div
class="c8"
class="c6 css-vurnku"
title="Wrapped BTC"
>
<svg
class="c9"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"
/>
<line
x1="12"
x2="12"
y1="9"
y2="13"
/>
<line
x1="12"
x2="12.01"
y1="17"
y2="17"
/>
</svg>
Wrapped BTC
</div>
<div
class="c7"
>
<div
class="c8"
>
<svg
class="c9"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"
/>
<line
x1="12"
x2="12"
y1="9"
y2="13"
/>
<line
x1="12"
x2="12.01"
y1="17"
y2="17"
/>
</svg>
</div>
</div>
</div>
<div
class="c10 css-yfjwjl"
>
WBTC
</div>
</div>
<div
class="c10 css-yfjwjl"
class="c4"
>
WBTC
<div
class="c0 c1 c11"
style="justify-self: flex-end;"
/>
</div>
</div>
<div
class="c4"
>
<div
class="c0 c1 c11"
style="justify-self: flex-end;"
/>
</div>
</div>
</div>
</div>
@@ -383,31 +388,36 @@ exports[`renders loading rows when isLoading is true 1`] = `
}
<div
style="position: relative; height: 10px; width: 100%; overflow: auto; will-change: transform; direction: ltr;"
style="padding-right: 4px;"
>
<div
style="height: 560px; width: 100%;"
class="CurrencyList_scrollbarStyle__1pi21y70"
style="position: relative; height: 10px; width: 100%; overflow: auto; will-change: transform; direction: ltr;"
>
<div
class="c0 c1"
style="height: 560px; width: 100%;"
>
<div />
<div />
<div />
</div>
<div
class="c0 c1"
>
<div />
<div />
<div />
</div>
<div
class="c0 c1"
>
<div />
<div />
<div />
<div
class="c0 c1"
>
<div />
<div />
<div />
</div>
<div
class="c0 c1"
>
<div />
<div />
<div />
</div>
<div
class="c0 c1"
>
<div />
<div />
<div />
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,20 @@
import { style } from '@vanilla-extract/css'
import { themeVars } from 'nft/css/sprinkles.css'
export const scrollbarStyle = style([
{
scrollbarWidth: 'thin',
scrollbarColor: `${themeVars.colors.backgroundOutline} transparent`,
height: '100%',
selectors: {
'&::-webkit-scrollbar': {
background: 'transparent',
width: '4px',
},
'&::-webkit-scrollbar-thumb': {
background: `${themeVars.colors.backgroundOutline}`,
borderRadius: '8px',
},
},
},
])

View File

@@ -20,6 +20,7 @@ import CurrencyLogo from '../../Logo/CurrencyLogo'
import Row, { RowFixed } from '../../Row'
import { MouseoverTooltip } from '../../Tooltip'
import { LoadingRows, MenuItem } from '../styleds'
import * as styles from './index.css'
function currencyKey(currency: Currency): string {
return currency.isToken ? currency.address : 'ETHER'
@@ -288,21 +289,34 @@ export default function CurrencyList({
return currencyKey(currency)
}, [])
return isLoading ? (
<FixedSizeList height={height} ref={fixedListRef as any} width="100%" itemData={[]} itemCount={10} itemSize={56}>
{LoadingRow}
</FixedSizeList>
) : (
<FixedSizeList
height={height}
ref={fixedListRef as any}
width="100%"
itemData={itemData}
itemCount={itemData.length}
itemSize={56}
itemKey={itemKey}
>
{Row}
</FixedSizeList>
return (
<div style={{ paddingRight: '4px' }}>
{isLoading ? (
<FixedSizeList
className={styles.scrollbarStyle}
height={height}
ref={fixedListRef as any}
width="100%"
itemData={[]}
itemCount={10}
itemSize={56}
>
{LoadingRow}
</FixedSizeList>
) : (
<FixedSizeList
className={styles.scrollbarStyle}
height={height}
ref={fixedListRef as any}
width="100%"
itemData={itemData}
itemCount={itemData.length}
itemSize={56}
itemKey={itemKey}
>
{Row}
</FixedSizeList>
)}
</div>
)
}

View File

@@ -32,8 +32,10 @@ import { PaddedColumn, SearchInput, Separator } from './styleds'
const ContentWrapper = styled(Column)`
background-color: ${({ theme }) => theme.backgroundSurface};
width: 100%;
overflow: hidden;
flex: 1 1;
position: relative;
border-radius: 20px;
`
interface CurrencySearchProps {
@@ -45,6 +47,7 @@ interface CurrencySearchProps {
showCommonBases?: boolean
showCurrencyAmount?: boolean
disableNonToken?: boolean
onlyShowCurrenciesWithBalance?: boolean
}
export function CurrencySearch({
@@ -56,6 +59,7 @@ export function CurrencySearch({
disableNonToken,
onDismiss,
isOpen,
onlyShowCurrenciesWithBalance,
}: CurrencySearchProps) {
const { chainId } = useWeb3React()
const theme = useTheme()
@@ -92,6 +96,10 @@ export function CurrencySearch({
!balancesAreLoading
? filteredTokens
.filter((token) => {
if (onlyShowCurrenciesWithBalance) {
return balances[token.address]?.greaterThan(0)
}
// If there is no query, filter out unselected user-added tokens with no balance.
if (!debouncedQuery && token instanceof UserAddedToken) {
if (selectedCurrency?.equals(token) || otherSelectedCurrency?.equals(token)) return true
@@ -101,7 +109,15 @@ export function CurrencySearch({
})
.sort(tokenComparator.bind(null, balances))
: [],
[balances, balancesAreLoading, debouncedQuery, filteredTokens, otherSelectedCurrency, selectedCurrency]
[
balances,
balancesAreLoading,
debouncedQuery,
filteredTokens,
otherSelectedCurrency,
selectedCurrency,
onlyShowCurrenciesWithBalance,
]
)
const isLoading = Boolean(balancesAreLoading && !tokenLoaderTimerElapsed)
@@ -114,11 +130,23 @@ export function CurrencySearch({
const s = debouncedQuery.toLowerCase().trim()
const tokens = filteredSortedTokens.filter((t) => !(t.equals(wrapped) || (disableNonToken && t.isNative)))
const natives = (disableNonToken || native.equals(wrapped) ? [wrapped] : [native, wrapped]).filter(
(n) => n.symbol?.toLowerCase()?.indexOf(s) !== -1 || n.name?.toLowerCase()?.indexOf(s) !== -1
)
const shouldShowWrapped =
!onlyShowCurrenciesWithBalance || (!balancesAreLoading && balances[wrapped.address]?.greaterThan(0))
const natives = (
disableNonToken || native.equals(wrapped) ? [wrapped] : shouldShowWrapped ? [native, wrapped] : [native]
).filter((n) => n.symbol?.toLowerCase()?.indexOf(s) !== -1 || n.name?.toLowerCase()?.indexOf(s) !== -1)
return [...natives, ...tokens]
}, [debouncedQuery, filteredSortedTokens, wrapped, disableNonToken, native])
}, [
debouncedQuery,
filteredSortedTokens,
onlyShowCurrenciesWithBalance,
balancesAreLoading,
balances,
wrapped,
disableNonToken,
native,
])
const handleCurrencySelect = useCallback(
(currency: Currency, hasWarning?: boolean) => {
@@ -168,7 +196,9 @@ export function CurrencySearch({
// if no results on main list, show option to expand into inactive
const filteredInactiveTokens = useSearchInactiveTokenLists(
filteredTokens.length === 0 || (debouncedQuery.length > 2 && !isAddressSearch) ? debouncedQuery : undefined
!onlyShowCurrenciesWithBalance && (filteredTokens.length === 0 || (debouncedQuery.length > 2 && !isAddressSearch))
? debouncedQuery
: undefined
)
// Timeout token loader after 3 seconds to avoid hanging in a loading state.

View File

@@ -17,6 +17,7 @@ interface CurrencySearchModalProps {
showCommonBases?: boolean
showCurrencyAmount?: boolean
disableNonToken?: boolean
onlyShowCurrenciesWithBalance?: boolean
}
enum CurrencyModalView {
@@ -34,6 +35,7 @@ export default memo(function CurrencySearchModal({
showCommonBases = false,
showCurrencyAmount = true,
disableNonToken = false,
onlyShowCurrenciesWithBalance = false,
}: CurrencySearchModalProps) {
const [modalView, setModalView] = useState<CurrencyModalView>(CurrencyModalView.search)
const lastOpen = useLast(isOpen)
@@ -84,6 +86,7 @@ export default memo(function CurrencySearchModal({
showCommonBases={showCommonBases}
showCurrencyAmount={showCurrencyAmount}
disableNonToken={disableNonToken}
onlyShowCurrenciesWithBalance={onlyShowCurrenciesWithBalance}
/>
)
break

View File

@@ -28,7 +28,7 @@ export default function TokenSafetyIcon({ warning }: { warning: Warning | null }
case WARNING_LEVEL.BLOCKED:
return (
<WarningContainer>
<BlockedIcon strokeWidth={2.5} />
<BlockedIcon data-cy="blocked-icon" strokeWidth={2.5} />
</WarningContainer>
)
case WARNING_LEVEL.UNKNOWN:

View File

@@ -50,7 +50,7 @@ export default function TokenSafetyMessage({ warning, tokenAddress }: TokenSafet
const { heading, description } = getWarningCopy(warning)
return (
<Label color={textColor} backgroundColor={backgroundColor}>
<Label data-cy="token-safety-message" color={textColor} backgroundColor={backgroundColor}>
<TitleRow>
{warning.canProceed ? <AlertTriangle size="16px" /> : <Slash size="16px" />}
<Title marginLeft="7px">{warning.message}</Title>

View File

@@ -1,6 +1,6 @@
import { Trans } from '@lingui/macro'
import { SupportedChainId } from '@uniswap/sdk-core'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { darken } from 'polished'
import { useState } from 'react'
import styled from 'styled-components/macro'
@@ -103,9 +103,8 @@ export function AboutSection({ address, chainId, description, homepageUrl, twitt
<ThemedText.SubHeaderSmall>
<Trans>Links</Trans>
</ThemedText.SubHeaderSmall>
<ResourcesContainer>
<ResourcesContainer data-cy="resources-container">
<Resource
data-testid="token-details-about-section-explorer-link"
name={chainId === SupportedChainId.MAINNET ? 'Etherscan' : 'Block Explorer'}
link={`${baseExplorerUrl}${address === 'NATIVE' ? '' : 'address/' + address}`}
/>

View File

@@ -1,9 +1,10 @@
import { Trans } from '@lingui/macro'
import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import { Currency, SupportedChainId } from '@uniswap/sdk-core'
import { Currency } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { isSupportedChain } from 'constants/chains'
import { useStablecoinValue } from 'hooks/useStablecoinPrice'
import useCurrencyBalance from 'lib/hooks/useCurrencyBalance'
@@ -81,7 +82,7 @@ export default function BalanceSummary({ token }: { token: Currency }) {
<Trans>Your balance on {label}</Trans>
</ThemedText.SubHeaderSmall>
<BalanceRow>
<CurrencyLogo currency={token} size="2rem" />
<CurrencyLogo currency={token} size="2rem" hideL2Icon={false} />
<BalanceContainer>
<BalanceAmountsContainer>
<BalanceItem>

View File

@@ -13,7 +13,7 @@ import TimePeriodSelector from './TimeSelector'
function usePriceHistory(tokenPriceData: TokenPriceQuery): PricePoint[] | undefined {
// Appends the current price to the end of the priceHistory array
const priceHistory = useMemo(() => {
const market = tokenPriceData.tokens?.[0]?.market
const market = tokenPriceData.token?.market
const priceHistory = market?.priceHistory?.filter(isPricePoint)
const currentPrice = market?.price?.value
if (Array.isArray(priceHistory) && currentPrice !== undefined) {
@@ -58,7 +58,7 @@ function Chart({
const timePeriod = useAtomValue(pageTimePeriodAtom)
return (
<ChartContainer>
<ChartContainer data-testid="chart-container">
<ParentSize>
{({ width }) => <PriceChart prices={prices ?? null} width={width} height={436} timePeriod={timePeriod} />}
</ParentSize>

View File

@@ -3,6 +3,7 @@ import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import { Currency } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { NATIVE_CHAIN_ID } from 'constants/tokens'
import { useDummyGateEnabled } from 'featureFlags/flags/dummyFeatureGate'
import { CHAIN_ID_TO_BACKEND_NAME } from 'graphql/data/util'
import { useStablecoinValue } from 'hooks/useStablecoinPrice'
import useCurrencyBalance from 'lib/hooks/useCurrencyBalance'
@@ -13,9 +14,10 @@ const Wrapper = styled.div`
align-content: center;
align-items: center;
border: 1px solid ${({ theme }) => theme.backgroundOutline};
border-bottom: none;
background-color: ${({ theme }) => theme.backgroundSurface};
border-radius: 20px 20px 0px 0px;
bottom: 56px;
bottom: 52px;
color: ${({ theme }) => theme.textSecondary};
display: flex;
flex-direction: row;
@@ -86,6 +88,7 @@ export default function MobileBalanceSummaryFooter({ token }: { token: Currency
const formattedBalance = formatCurrencyAmount(balance, NumberType.TokenNonTx)
const formattedUsdValue = formatCurrencyAmount(useStablecoinValue(balance), NumberType.FiatTokenStats)
const chain = CHAIN_ID_TO_BACKEND_NAME[token.chainId].toLowerCase()
const isDummyGateFlagEnabled = useDummyGateEnabled()
return (
<Wrapper>
@@ -101,7 +104,7 @@ export default function MobileBalanceSummaryFooter({ token }: { token: Currency
</BalanceInfo>
)}
<SwapButton to={`/swap?chainName=${chain}&outputCurrency=${token.isNative ? NATIVE_CHAIN_ID : token.address}`}>
<Trans>Swap</Trans>
<Trans>{isDummyGateFlagEnabled ? 'Go to Swap' : 'Swap'}</Trans>
</SwapButton>
</Wrapper>
)

View File

@@ -275,7 +275,7 @@ export function PriceChart({ width, height, prices: originalPrices, timePeriod }
return (
<>
<ChartHeader>
<ChartHeader data-cy="chart-header">
{displayPrice.value ? (
<>
<TokenPrice>{formatDollar({ num: displayPrice.value, isPrice: true })}</TokenPrice>
@@ -294,7 +294,7 @@ export function PriceChart({ width, height, prices: originalPrices, timePeriod }
{!chartAvailable ? (
<MissingPriceChart width={width} height={graphHeight} message={!!displayPrice.value && missingPricesMessage} />
) : (
<svg width={width} height={graphHeight} style={{ minWidth: '100%' }}>
<svg data-cy="price-chart" width={width} height={graphHeight} style={{ minWidth: '100%' }}>
<AnimatedInLineChart
data={prices}
getX={getX}
@@ -411,7 +411,7 @@ function MissingPriceChart({ width, height, message }: { width: number; height:
const theme = useTheme()
const midPoint = height / 2 + 45
return (
<StyledMissingChart width={width} height={height} style={{ minWidth: '100%' }}>
<StyledMissingChart data-cy="missing-chart" width={width} height={height} style={{ minWidth: '100%' }}>
<path
d={`M 0 ${midPoint} Q 104 ${midPoint - 70}, 208 ${midPoint} T 416 ${midPoint}
M 416 ${midPoint} Q 520 ${midPoint - 70}, 624 ${midPoint} T 832 ${midPoint}`}

View File

@@ -1,12 +1,11 @@
import { WidgetSkeleton } from 'components/Widget'
import { WIDGET_WIDTH } from 'components/Widget'
import { DEFAULT_WIDGET_WIDTH } from 'components/Widget'
import { ArrowLeft } from 'react-feather'
import { useParams } from 'react-router-dom'
import styled, { useTheme } from 'styled-components/macro'
import { textFadeIn } from 'theme/styles'
import { LoadingBubble } from '../loading'
import { LogoContainer } from '../TokenTable/TokenRow'
import { AboutContainer, AboutHeader } from './About'
import { BreadcrumbNavLink } from './BreadcrumbNavLink'
import { TokenPrice } from './PriceChart'
@@ -44,7 +43,7 @@ export const RightPanel = styled.div`
display: none;
flex-direction: column;
gap: 20px;
width: ${WIDGET_WIDTH}px;
width: ${DEFAULT_WIDGET_WIDTH}px;
@media screen and (min-width: ${({ theme }) => theme.breakpoint.lg}px) {
display: flex;
@@ -227,9 +226,7 @@ export default function TokenDetailsSkeleton() {
</BreadcrumbNavLink>
<TokenInfoContainer>
<TokenNameCell>
<LogoContainer>
<TokenLogoBubble />
</LogoContainer>
<TokenLogoBubble />
<TitleBubble />
</TokenNameCell>
</TokenInfoContainer>

View File

@@ -45,9 +45,19 @@ export const StatsWrapper = styled.div`
type NumericStat = number | undefined | null
function Stat({ value, title, description }: { value: NumericStat; title: ReactNode; description?: ReactNode }) {
function Stat({
dataCy,
value,
title,
description,
}: {
dataCy: string
value: NumericStat
title: ReactNode
description?: ReactNode
}) {
return (
<StatWrapper>
<StatWrapper data-cy={`${dataCy}`}>
<MouseoverTooltip text={description}>{title}</MouseoverTooltip>
<StatPrice>{formatNumber(value, NumberType.FiatTokenStats)}</StatPrice>
</StatWrapper>
@@ -71,11 +81,13 @@ export default function StatsSection(props: StatsSectionProps) {
<TokenStatsSection>
<StatPair>
<Stat
dataCy="tvl"
value={TVL}
description={HEADER_DESCRIPTIONS[TokenSortMethod.TOTAL_VALUE_LOCKED]}
title={<Trans>TVL</Trans>}
/>
<Stat
dataCy="volume-24h"
value={volume24H}
description={
<Trans>
@@ -86,8 +98,8 @@ export default function StatsSection(props: StatsSectionProps) {
/>
</StatPair>
<StatPair>
<Stat value={priceLow52W} title={<Trans>52W low</Trans>} />
<Stat value={priceHigh52W} title={<Trans>52W high</Trans>} />
<Stat dataCy="52w-low" value={priceLow52W} title={<Trans>52W low</Trans>} />
<Stat dataCy="52w-high" value={priceHigh52W} title={<Trans>52W high</Trans>} />
</StatPair>
</TokenStatsSection>
</StatsWrapper>

View File

@@ -20,7 +20,6 @@ import TokenDetailsSkeleton, {
TokenNameCell,
} from 'components/Tokens/TokenDetails/Skeleton'
import StatsSection from 'components/Tokens/TokenDetails/StatsSection'
import { L2NetworkLogo, LogoContainer } from 'components/Tokens/TokenTable/TokenRow'
import TokenSafetyMessage from 'components/TokenSafety/TokenSafetyMessage'
import TokenSafetyModal from 'components/TokenSafety/TokenSafetyModal'
import Widget from 'components/Widget'
@@ -111,7 +110,7 @@ export default function TokenDetails({
const pageChainId = CHAIN_NAME_TO_CHAIN_ID[chain]
const tokenQueryData = tokenQuery.tokens?.[0]
const tokenQueryData = tokenQuery.token
const crossChainMap = useMemo(
() =>
tokenQueryData?.project?.tokens.reduce((map, current) => {
@@ -134,25 +133,27 @@ export default function TokenDetails({
if (!address) return
const bridgedAddress = crossChainMap[update]
if (bridgedAddress) {
startTokenTransition(() => navigate(getTokenDetailsURL(bridgedAddress, update)))
startTokenTransition(() => navigate(getTokenDetailsURL({ address: bridgedAddress, chain })))
} else if (didFetchFromChain || token?.isNative) {
startTokenTransition(() => navigate(getTokenDetailsURL(address, update)))
startTokenTransition(() => navigate(getTokenDetailsURL({ address, chain })))
}
},
[address, crossChainMap, didFetchFromChain, navigate, token?.isNative]
[address, chain, crossChainMap, didFetchFromChain, navigate, token?.isNative]
)
useOnGlobalChainSwitch(navigateToTokenForChain)
const navigateToWidgetSelectedToken = useCallback(
(token: Currency) => {
const address = token.isNative ? NATIVE_CHAIN_ID : token.address
startTokenTransition(() => navigate(getTokenDetailsURL(address, chain)))
startTokenTransition(() => navigate(getTokenDetailsURL({ address, chain })))
},
[chain, navigate]
)
const [continueSwap, setContinueSwap] = useState<{ resolve: (value: boolean | PromiseLike<boolean>) => void }>()
const [openTokenSafetyModal, setOpenTokenSafetyModal] = useState(false)
// Show token safety modal if Swap-reviewing a warning token, at all times if the current token is blocked
const shouldShowSpeedbump = !useIsUserAddedTokenOnChain(address, pageChainId) && tokenWarning !== null
const onReviewSwapClick = useCallback(
@@ -168,8 +169,6 @@ export default function TokenDetails({
[continueSwap, setContinueSwap]
)
const L2Icon = getChainInfo(pageChainId)?.circleLogoUrl
// address will never be undefined if token is defined; address is checked here to appease typechecker
if (token === undefined || !address) {
return <InvalidTokenDetails chainName={address && getChainInfo(pageChainId)?.label} />
@@ -186,12 +185,10 @@ export default function TokenDetails({
<BreadcrumbNavLink to={`/tokens/${chain.toLowerCase()}`}>
<ArrowLeft data-testid="token-details-return-button" size={14} /> Tokens
</BreadcrumbNavLink>
<TokenInfoContainer>
<TokenInfoContainer data-testid="token-info-container">
<TokenNameCell>
<LogoContainer>
<CurrencyLogo currency={token} size="32px" />
<L2NetworkLogo networkUrl={L2Icon} size="16px" />
</LogoContainer>
<CurrencyLogo currency={token} size="32px" hideL2Icon={false} />
{token.name ?? <Trans>Name not found</Trans>}
<TokenSymbol>{token.symbol ?? <Trans>Symbol not found</Trans>}</TokenSymbol>
</TokenNameCell>
@@ -220,22 +217,28 @@ export default function TokenDetails({
<TokenDetailsSkeleton />
)}
<RightPanel>
<Widget
token={token ?? undefined}
onTokenChange={navigateToWidgetSelectedToken}
onReviewSwapClick={onReviewSwapClick}
/>
<RightPanel onClick={() => isBlockedToken && setOpenTokenSafetyModal(true)}>
<div style={{ pointerEvents: isBlockedToken ? 'none' : 'auto' }}>
<Widget
defaultTokens={{
default: token ?? undefined,
}}
onDefaultTokenChange={navigateToWidgetSelectedToken}
onReviewSwapClick={onReviewSwapClick}
/>
</div>
{tokenWarning && <TokenSafetyMessage tokenAddress={address} warning={tokenWarning} />}
{token && <BalanceSummary token={token} />}
</RightPanel>
{token && <MobileBalanceSummaryFooter token={token} />}
<TokenSafetyModal
isOpen={isBlockedToken || !!continueSwap}
isOpen={openTokenSafetyModal || !!continueSwap}
tokenAddress={address}
onContinue={() => onResolveSwap(true)}
onBlocked={() => navigate(-1)}
onBlocked={() => {
setOpenTokenSafetyModal(false)
}}
onCancel={() => onResolveSwap(false)}
showCancel={true}
/>

View File

@@ -84,6 +84,7 @@ export default function SearchBar() {
element={InterfaceElementName.EXPLORE_SEARCH_INPUT}
>
<SearchInput
data-cy="explore-tokens-search-input"
type="search"
placeholder={`${translation}`}
id="searchBar"

View File

@@ -111,7 +111,7 @@ export default function TimeSelector() {
return (
<StyledMenu ref={node}>
<FilterOption onClick={toggleMenu} aria-label="timeSelector" active={open}>
<FilterOption onClick={toggleMenu} aria-label="timeSelector" active={open} data-testid="time-selector">
<StyledMenuContent>
{DISPLAYS[activeTime]}
<Chevron open={open}>
@@ -128,6 +128,7 @@ export default function TimeSelector() {
{ORDERED_TIMES.map((time) => (
<InternalLinkMenuItem
key={DISPLAYS[time]}
data-testid={DISPLAYS[time]}
onClick={() => {
setTime(time)
toggleMenu()

View File

@@ -6,7 +6,6 @@ import { ParentSize } from '@visx/responsive'
import SparklineChart from 'components/Charts/SparklineChart'
import QueryTokenLogo from 'components/Logo/QueryTokenLogo'
import { MouseoverTooltip } from 'components/Tooltip'
import { getChainInfo } from 'constants/chainInfo'
import { SparklineMap, TopToken } from 'graphql/data/TopTokens'
import { CHAIN_NAME_TO_CHAIN_ID, getTokenDetailsURL } from 'graphql/data/util'
import { useAtomValue } from 'jotai/utils'
@@ -279,23 +278,6 @@ export const SparkLineLoadingBubble = styled(LongLoadingBubble)`
height: 4px;
`
export const L2NetworkLogo = styled.div<{ networkUrl?: string; size?: string }>`
height: ${({ size }) => size ?? '12px'};
width: ${({ size }) => size ?? '12px'};
position: absolute;
left: 50%;
bottom: 0;
background: url(${({ networkUrl }) => networkUrl});
background-repeat: no-repeat;
background-size: ${({ size }) => (size ? `${size} ${size}` : '12px 12px')};
display: ${({ networkUrl }) => !networkUrl && 'none'};
`
export const LogoContainer = styled.div`
position: relative;
align-items: center;
display: flex;
`
const InfoIconContainer = styled.div`
margin-left: 2px;
display: flex;
@@ -307,7 +289,9 @@ export const HEADER_DESCRIPTIONS: Record<TokenSortMethod, ReactNode | undefined>
[TokenSortMethod.PRICE]: undefined,
[TokenSortMethod.PERCENT_CHANGE]: undefined,
[TokenSortMethod.TOTAL_VALUE_LOCKED]: (
<Trans>Total value locked (TVL) is the amount of the asset thats currently in a Uniswap v3 liquidity pool.</Trans>
<Trans>
Total value locked (TVL) is the aggregate amount of the asset available across all Uniswap v3 liquidity pools.
</Trans>
),
[TokenSortMethod.VOLUME]: (
<Trans>Volume is the amount of the asset that has been traded on Uniswap v3 during the selected time frame.</Trans>
@@ -378,15 +362,23 @@ function TokenRow({
const rowCells = (
<>
<ListNumberCell header={header}>{listNumber}</ListNumberCell>
<NameCell>{tokenInfo}</NameCell>
<PriceCell sortable={header}>{price}</PriceCell>
<PercentChangeCell sortable={header}>{percentChange}</PercentChangeCell>
<TvlCell sortable={header}>{tvl}</TvlCell>
<VolumeCell sortable={header}>{volume}</VolumeCell>
<NameCell data-testid="name-cell">{tokenInfo}</NameCell>
<PriceCell data-testid="price-cell" sortable={header}>
{price}
</PriceCell>
<PercentChangeCell data-testid="percent-change-cell" sortable={header}>
{percentChange}
</PercentChangeCell>
<TvlCell data-testid="tvl-cell" sortable={header}>
{tvl}
</TvlCell>
<VolumeCell data-testid="volume-cell" sortable={header}>
{volume}
</VolumeCell>
<SparkLineCell>{sparkLine}</SparkLineCell>
</>
)
if (header) return <StyledHeaderRow>{rowCells}</StyledHeaderRow>
if (header) return <StyledHeaderRow data-testid="header-row">{rowCells}</StyledHeaderRow>
return <StyledTokenRow {...rest}>{rowCells}</StyledTokenRow>
}
@@ -434,21 +426,17 @@ interface LoadedRowProps {
tokenListLength: number
token: NonNullable<TopToken>
sparklineMap: SparklineMap
volumeRank: number
sortRank: number
}
/* Loaded State: row component with token information */
export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HTMLDivElement>) => {
const { tokenListIndex, tokenListLength, token, volumeRank } = props
const tokenAddress = token.address
const tokenName = token.name
const tokenSymbol = token.symbol
const { tokenListIndex, tokenListLength, token, sortRank } = props
const filterString = useAtomValue(filterStringAtom)
const lowercaseChainName = useParams<{ chainName?: string }>().chainName?.toUpperCase() ?? 'ethereum'
const filterNetwork = lowercaseChainName.toUpperCase()
const chainId = CHAIN_NAME_TO_CHAIN_ID[filterNetwork]
const L2Icon = getChainInfo(chainId)?.circleLogoUrl
const timePeriod = useAtomValue(filterTimeAtom)
const delta = token.market?.pricePercentChange?.value
const arrow = getDeltaArrow(delta)
@@ -457,10 +445,10 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
const exploreTokenSelectedEventProperties = {
chain_id: chainId,
token_address: tokenAddress,
token_symbol: tokenSymbol,
token_address: token.address,
token_symbol: token.symbol,
token_list_index: tokenListIndex,
token_list_rank: volumeRank,
token_list_rank: sortRank,
token_list_length: tokenListLength,
time_frame: timePeriod,
search_token_address_input: filterString,
@@ -468,25 +456,22 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
// TODO: currency logo sizing mobile (32px) vs. desktop (24px)
return (
<div ref={ref} data-testid={`token-table-row-${tokenName}`}>
<div ref={ref} data-testid={`token-table-row-${token.symbol}`}>
<StyledLink
to={getTokenDetailsURL(token.address ?? '', token.chain)}
to={getTokenDetailsURL(token)}
onClick={() =>
sendAnalyticsEvent(InterfaceEventName.EXPLORE_TOKEN_ROW_CLICKED, exploreTokenSelectedEventProperties)
}
>
<TokenRow
header={false}
listNumber={volumeRank}
listNumber={sortRank}
tokenInfo={
<ClickableName>
<LogoContainer>
<QueryTokenLogo token={token} />
<L2NetworkLogo networkUrl={L2Icon} />
</LogoContainer>
<QueryTokenLogo token={token} />
<TokenInfoCell>
<TokenName>{tokenName}</TokenName>
<TokenSymbol>{tokenSymbol}</TokenSymbol>
<TokenName data-cy="token-name">{token.name}</TokenName>
<TokenSymbol>{token.symbol}</TokenSymbol>
</TokenInfoCell>
</ClickableName>
}

View File

@@ -76,12 +76,11 @@ function LoadingTokenTable({ rowCount = PAGE_SIZE }: { rowCount?: number }) {
}
export default function TokenTable() {
// TODO: consider moving prefetched call into app.tsx and passing it here, use a preloaded call & updated on interval every 60s
const chainName = validateUrlChainParam(useParams<{ chainName?: string }>().chainName)
const { tokens, tokenVolumeRank, loadingTokens, sparklines } = useTopTokens(chainName)
const { tokens, tokenSortRank, loadingTokens, sparklines } = useTopTokens(chainName)
/* loading and error state */
if (loadingTokens) {
if (loadingTokens && !tokens) {
return <LoadingTokenTable rowCount={PAGE_SIZE} />
} else if (!tokens) {
return (
@@ -110,7 +109,7 @@ export default function TokenTable() {
tokenListLength={tokens.length}
token={token}
sparklineMap={sparklines}
volumeRank={tokenVolumeRank[token.address]}
sortRank={tokenSortRank[token.address]}
/>
)
)}

View File

@@ -2,8 +2,6 @@ import { useWeb3React } from '@web3-react/core'
import AddressClaimModal from 'components/claim/AddressClaimModal'
import ConnectedAccountBlocked from 'components/ConnectedAccountBlocked'
import FiatOnrampModal from 'components/FiatOnrampModal'
import { BaseVariant } from 'featureFlags'
import { useFiatOnrampFlag } from 'featureFlags/flags/fiatOnramp'
import useAccountRiskCheck from 'hooks/useAccountRiskCheck'
import { lazy } from 'react'
import { useModalIsOpen, useToggleModal } from 'state/application/hooks'
@@ -12,7 +10,6 @@ import { ApplicationModal } from 'state/application/reducer'
const Bag = lazy(() => import('nft/components/bag/Bag'))
const TransactionCompleteModal = lazy(() => import('nft/components/collection/TransactionCompleteModal'))
const AirdropModal = lazy(() => import('components/AirdropModal'))
const MetaMaskConnectionErrorModal = lazy(() => import('components/MetaMaskConnectionErrorModal'))
export default function TopLevelModals() {
const addressClaimOpen = useModalIsOpen(ApplicationModal.ADDRESS_CLAIM)
@@ -21,7 +18,6 @@ export default function TopLevelModals() {
const { account } = useWeb3React()
useAccountRiskCheck(account)
const accountBlocked = Boolean(blockedAccountModalOpen && account)
const fiatOnrampFlagEnabled = useFiatOnrampFlag() === BaseVariant.Enabled
return (
<>
@@ -30,8 +26,7 @@ export default function TopLevelModals() {
<Bag />
<TransactionCompleteModal />
<AirdropModal />
<MetaMaskConnectionErrorModal />
{fiatOnrampFlagEnabled && <FiatOnrampModal />}
<FiatOnrampModal />
</>
)
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,626 @@
export const svgPaths: React.SVGProps<SVGPathElement>[][] = [
[
{
d: 'M0 8C0 3.58172 3.58172 0 8 0V4C5.79086 4 4 5.79086 4 8H0ZM8 8L4 8C4 10.2091 5.79086 12 8 12V16C12.4183 16 16 12.4183 16 8H12C12 5.79086 10.2091 4 8 4L8 8ZM8 8L12 8C12 10.2091 10.2091 12 8 12L8 8Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M6.61962 1.57177C7.38198 0.809409 8.61802 0.80941 9.38038 1.57177L14.4282 6.61962C15.1906 7.38198 15.1906 8.61802 14.4282 9.38038L9.38038 14.4282C8.61802 15.1906 7.38198 15.1906 6.61962 14.4282L1.57177 9.38038C0.809409 8.61802 0.80941 7.38198 1.57177 6.61962L6.61962 1.57177Z',
},
],
[
{
d: 'M8 4.33253L4.33253 8L8 11.6675L11.6675 8L8 4.33253ZM9.38038 1.57177C8.61802 0.80941 7.38198 0.809409 6.61962 1.57177L1.57177 6.61962C0.80941 7.38198 0.809409 8.61802 1.57177 9.38038L6.61962 14.4282C7.38198 15.1906 8.61802 15.1906 9.38038 14.4282L14.4282 9.38038C15.1906 8.61802 15.1906 7.38198 14.4282 6.61962L9.38038 1.57177Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M8 10C9.10457 10 10 9.10457 10 8C10 6.89543 9.10457 6 8 6C6.89543 6 6 6.89543 6 8C6 9.10457 6.89543 10 8 10Z',
},
{
d: 'M16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8ZM4 5C4 4.44772 4.44772 4 5 4H11C11.5523 4 12 4.44772 12 5V11C12 11.5523 11.5523 12 11 12H5C4.44772 12 4 11.5523 4 11V5Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M16 8C12.2937 6.62854 9.37146 3.70632 8 0C6.62854 3.70632 3.70632 6.62854 0 8C3.70632 9.37146 6.62854 12.2937 8 16C9.37146 12.2937 12.2937 9.37146 16 8ZM8 11C9.65685 11 11 9.65685 11 8C11 6.34315 9.65685 5 8 5C6.34315 5 5 6.34315 5 8C5 9.65685 6.34315 11 8 11Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M9.38038 1.57177C8.61802 0.80941 7.38198 0.809409 6.61962 1.57177L1.57177 6.61962C0.80941 7.38198 0.809409 8.61802 1.57177 9.38038L6.61962 14.4282C7.38198 15.1906 8.61802 15.1906 9.38038 14.4282L14.4282 9.38038C15.1906 8.61802 15.1906 7.38198 14.4282 6.61962L9.38038 1.57177ZM8 11.1716C9.65685 11.1716 11 9.82842 11 8.17157C11 6.51472 9.65685 5.17157 8 5.17157C6.34315 5.17157 5 6.51472 5 8.17157C5 9.82842 6.34315 11.1716 8 11.1716Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M15 2C15 1.44772 14.5523 1 14 1H9C8.44772 1 8 1.44772 8 2V8H14C14.5523 8 15 7.55228 15 7V2Z',
},
{
d: 'M8 8H2C1.44771 8 1 8.44772 1 9V14C1 14.5523 1.44772 15 2 15H7C7.55228 15 8 14.5523 8 14V8Z',
},
],
[
{
d: 'M0 8C0 3.58172 3.58172 0 8 0L8 4.41421C7.74408 4.41421 7.48816 4.51184 7.29289 4.70711L4.70711 7.29289C4.51184 7.48816 4.41421 7.74408 4.41421 8L0 8ZM8 8H4.41421C4.41421 8.25592 4.51184 8.51184 4.70711 8.70711L7.29289 11.2929C7.48816 11.4882 7.74408 11.5858 8 11.5858L8 16C12.4183 16 16 12.4183 16 8L11.5858 8C11.5858 7.74408 11.4882 7.48816 11.2929 7.29289L8.70711 4.70711C8.51184 4.51184 8.25592 4.41421 8 4.41421L8 8ZM8 8H11.5858C11.5858 8.25592 11.4882 8.51184 11.2929 8.70711L8.70711 11.2929C8.51184 11.4882 8.25592 11.5858 8 11.5858L8 8Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M1 2C1 1.44772 1.44772 1 2 1H7C7.55228 1 8 1.44772 8 2V8H2C1.44772 8 1 7.55228 1 7V2Z',
},
{
d: 'M8 8H14C14.5523 8 15 8.44772 15 9V14C15 14.5523 14.5523 15 14 15H9C8.44772 15 8 14.5523 8 14V8Z',
},
],
[
{
d: 'M13.6569 2.34315C10.5327 -0.781049 5.46734 -0.781049 2.34315 2.34315L8 8L2.34315 13.6569C5.46734 16.781 10.5327 16.781 13.6569 13.6569L8 8L13.6569 2.34315Z',
},
],
[
{
d: 'M8 0C3.58172 0 0 3.58172 0 8L8 8L8 16C12.4183 16 16 12.4183 16 8L8 8L8 0Z',
},
],
[
{
d: 'M16 8C12.2937 6.62854 9.37146 3.70632 8 0C6.62854 3.70632 3.70632 6.62854 0 8C3.70632 9.37146 6.62854 12.2937 8 16C9.37146 12.2937 12.2937 9.37146 16 8ZM11 8L8 5L5 8L8 11L11 8Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M2 4.4C2 3.07452 3.07452 2 4.4 2H11.6C12.9255 2 14 3.07452 14 4.4V11.6C14 12.9255 12.9255 14 11.6 14H4.4C3.07452 14 2 12.9255 2 11.6V4.4Z',
},
],
[
{
d: 'M5.17158 2.34315C4.39053 1.5621 3.1242 1.5621 2.34315 2.34315C1.5621 3.12419 1.5621 4.39052 2.34315 5.17157L4.46447 7.29289C4.85499 7.68342 4.85499 8.31658 4.46447 8.70711L2.34315 10.8284C1.5621 11.6095 1.5621 12.8758 2.34315 13.6569C3.1242 14.4379 4.39053 14.4379 5.17158 13.6569L7.2929 11.5355C7.68342 11.145 8.31659 11.145 8.70711 11.5355L10.8284 13.6569C11.6095 14.4379 12.8758 14.4379 13.6569 13.6569C14.4379 12.8758 14.4379 11.6095 13.6569 10.8284L11.5355 8.70711C11.145 8.31658 11.145 7.68342 11.5355 7.29289L13.6569 5.17157C14.4379 4.39052 14.4379 3.12419 13.6569 2.34315C12.8758 1.5621 11.6095 1.5621 10.8284 2.34315L8.70711 4.46447C8.31659 4.85499 7.68342 4.85499 7.2929 4.46447L5.17158 2.34315Z',
},
],
[
{
d: 'M7 2C7 1.44772 7.44772 1 8 1C8.55228 1 9 1.44772 9 2V5C9 5.55228 8.55228 6 8 6C7.44772 6 7 5.55228 7 5V2Z',
},
{
d: 'M7 11C7 10.4477 7.44772 10 8 10C8.55228 10 9 10.4477 9 11V14C9 14.5523 8.55228 15 8 15C7.44772 15 7 14.5523 7 14V11Z',
},
{
d: 'M14 7C14.5523 7 15 7.44772 15 8C15 8.55228 14.5523 9 14 9H11C10.4477 9 10 8.55228 10 8C10 7.44772 10.4477 7 11 7H14Z',
},
{
d: 'M5 7C5.55228 7 6 7.44772 6 8C6 8.55228 5.55228 9 5 9H2C1.44772 9 1 8.55228 1 8C1 7.44772 1.44772 7 2 7H5Z',
},
],
[
{
d: 'M15.9585 8.81981C15.9166 9.23189 15.5183 9.49613 15.112 9.41574L11.7175 8.74421C10.9132 8.5851 10.4734 9.64707 11.1547 10.1032L14.0293 12.0277C14.3735 12.2581 14.4683 12.7266 14.2068 13.0478C13.861 13.4725 13.4725 13.861 13.0478 14.2068C12.7266 14.4683 12.2581 14.3735 12.0277 14.0293L10.1032 11.1547C9.64707 10.4734 8.5851 10.9132 8.74421 11.7175L9.41574 15.112C9.49613 15.5183 9.23189 15.9166 8.81981 15.9585C8.55027 15.9859 8.27678 16 8 16C7.72322 16 7.44973 15.9859 7.18019 15.9585C6.76811 15.9166 6.50387 15.5183 6.58426 15.112L7.25579 11.7175C7.4149 10.9132 6.35293 10.4734 5.89682 11.1547L3.97232 14.0293C3.74188 14.3735 3.27344 14.4683 2.95224 14.2068C2.52755 13.861 2.13902 13.4725 1.79322 13.0478C1.53168 12.7266 1.62651 12.2581 1.9707 12.0277L4.84532 10.1032C5.52661 9.64707 5.08681 8.5851 4.28253 8.74421L0.888013 9.41574C0.481675 9.49613 0.0834376 9.23189 0.0414916 8.81981C0.0140555 8.55027 0 8.27678 0 8C0 7.72322 0.0140554 7.44973 0.0414915 7.18019C0.0834375 6.76811 0.481675 6.50387 0.888013 6.58426L4.28253 7.25579C5.08681 7.4149 5.52661 6.35293 4.84532 5.89683L1.9707 3.97232C1.6265 3.74188 1.53168 3.27344 1.79322 2.95224C2.13902 2.52755 2.52755 2.13902 2.95224 1.79322C3.27344 1.53168 3.74188 1.62651 3.97231 1.9707L5.89683 4.84533C6.35293 5.52661 7.4149 5.08681 7.25579 4.28253L6.58426 0.888013C6.50387 0.481675 6.76811 0.0834376 7.18019 0.0414915C7.44973 0.0140555 7.72322 0 8 0C8.27678 0 8.55027 0.0140554 8.81981 0.0414915C9.23189 0.0834375 9.49613 0.481675 9.41574 0.888013L8.74421 4.28253C8.5851 5.08681 9.64707 5.52661 10.1032 4.84532L12.0277 1.9707C12.2581 1.6265 12.7266 1.53168 13.0478 1.79322C13.4725 2.13902 13.861 2.52755 14.2068 2.95223C14.4683 3.27344 14.3735 3.74188 14.0293 3.97231L11.1547 5.89683C10.4734 6.35294 10.9132 7.4149 11.7175 7.25579L15.112 6.58426C15.5183 6.50387 15.9166 6.76811 15.9585 7.18019C15.9859 7.44973 16 7.72322 16 8C16 8.27678 15.9859 8.55027 15.9585 8.81981Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M10 2C10 0.895431 9.10457 0 8 0C6.89543 0 6 0.89543 6 2V5C6 5.55228 5.55228 6 5 6H2C0.895431 6 0 6.89543 0 8C0 9.10457 0.895431 10 2 10H5C5.55228 10 6 10.4477 6 11V14C6 15.1046 6.89543 16 8 16C9.10457 16 10 15.1046 10 14V11C10 10.4477 10.4477 10 11 10H14C15.1046 10 16 9.10457 16 8C16 6.89543 15.1046 6 14 6H11C10.4477 6 10 5.55228 10 5V2Z',
},
],
[
{
d: 'M12 1.07026C9.60879 2.4535 8 5.03887 8 8V0C5.03887 0 2.4535 1.60879 1.07026 4C2.4535 6.39121 5.03887 8 8 8H0C0 10.9611 1.6088 13.5465 4 14.9297C6.39121 13.5465 8 10.9611 8 8L8 16C10.9611 16 13.5465 14.3912 14.9297 12C13.5469 9.60955 10.9628 8.00101 8.00279 8H16C16 5.03887 14.3912 2.4535 12 1.07026Z',
},
],
[
{
d: 'M15.9831 8.52372L12.9894 8.33026C12.9964 8.22148 13 8.11137 13 8C13 7.88863 12.9964 7.77852 12.9894 7.66974L15.9831 7.47627C15.9943 7.64941 16 7.82404 16 8C16 8.17596 15.9943 8.35059 15.9831 8.52372ZM15.8477 6.4387L12.9048 7.02091C12.8622 6.80567 12.8058 6.59565 12.7366 6.39168L15.5774 5.42754C15.6887 5.75556 15.7794 6.09313 15.8477 6.4387ZM15.1766 4.46092L12.487 5.78982C12.3902 5.59393 12.2808 5.40497 12.1596 5.22403L14.6525 3.55507C14.8461 3.84425 15.0214 4.14676 15.1766 4.46092ZM14.0148 2.72519L11.7601 4.70411C11.6156 4.5395 11.4605 4.3844 11.2959 4.23992L13.2748 1.98518C13.5372 2.21551 13.7845 2.46275 14.0148 2.72519ZM12.4449 1.34748L10.776 3.84038C10.595 3.71924 10.4061 3.60981 10.2102 3.51302L11.5391 0.823408C11.8532 0.978631 12.1557 1.15388 12.4449 1.34748ZM10.5725 0.422581L9.60832 3.26343C9.40435 3.19421 9.19433 3.13782 8.97909 3.09524L9.5613 0.152272C9.90687 0.220637 10.2444 0.311259 10.5725 0.422581ZM8.52373 0.0168691L8.33026 3.01062C8.22148 3.0036 8.11137 3 8 3C7.88863 3 7.77852 3.0036 7.66974 3.01062L7.47627 0.0168691C7.64941 0.00568084 7.82404 0 8 0C8.17596 0 8.35059 0.00568083 8.52373 0.0168691ZM6.4387 0.152272L7.02091 3.09524C6.80567 3.13782 6.59565 3.19421 6.39168 3.26343L5.42754 0.422581C5.75556 0.311259 6.09313 0.220638 6.4387 0.152272ZM4.46092 0.823408L5.78982 3.51302C5.59393 3.60981 5.40497 3.71925 5.22403 3.84038L3.55507 1.34748C3.84425 1.15388 4.14676 0.978631 4.46092 0.823408ZM2.72519 1.98518L4.70411 4.23992C4.5395 4.3844 4.3844 4.5395 4.23992 4.70412L1.98517 2.7252C2.21551 2.46275 2.46275 2.21551 2.72519 1.98518ZM1.34748 3.55508L3.84038 5.22403C3.71924 5.40497 3.60981 5.59393 3.51302 5.78982L0.823408 4.46092C0.978631 4.14676 1.15388 3.84425 1.34748 3.55508ZM0.422581 5.42754L3.26343 6.39168C3.19421 6.59565 3.13782 6.80567 3.09524 7.02091L0.152272 6.4387C0.220637 6.09313 0.311259 5.75556 0.422581 5.42754ZM0.0168691 7.47627C0.00568083 7.64941 0 7.82404 0 8C0 8.17596 0.00568084 8.35059 0.0168691 8.52373L3.01062 8.33026C3.0036 8.22148 3 8.11137 3 8C3 7.88863 3.0036 7.77852 3.01062 7.66974L0.0168691 7.47627ZM0.152272 9.5613L3.09524 8.97909C3.13782 9.19433 3.19421 9.40435 3.26343 9.60832L0.422581 10.5725C0.311259 10.2444 0.220637 9.90687 0.152272 9.5613ZM0.823408 11.5391L3.51302 10.2102C3.60981 10.4061 3.71925 10.595 3.84038 10.776L1.34748 12.4449C1.15388 12.1557 0.978631 11.8532 0.823408 11.5391ZM1.98518 13.2748L4.23992 11.2959C4.3844 11.4605 4.5395 11.6156 4.70412 11.7601L2.7252 14.0148C2.46275 13.7845 2.21551 13.5372 1.98518 13.2748ZM3.55508 14.6525L5.22403 12.1596C5.40497 12.2808 5.59393 12.3902 5.78982 12.487L4.46092 15.1766C4.14676 15.0214 3.84425 14.8461 3.55508 14.6525ZM5.42754 15.5774L6.39168 12.7366C6.59565 12.8058 6.80567 12.8622 7.02091 12.9048L6.4387 15.8477C6.09313 15.7794 5.75556 15.6887 5.42754 15.5774ZM7.47628 15.9831L7.66974 12.9894C7.77852 12.9964 7.88863 13 8 13C8.11137 13 8.22148 12.9964 8.33026 12.9894L8.52373 15.9831C8.35059 15.9943 8.17596 16 8 16C7.82404 16 7.64941 15.9943 7.47628 15.9831ZM9.5613 15.8477L8.97909 12.9048C9.19433 12.8622 9.40435 12.8058 9.60832 12.7366L10.5725 15.5774C10.2444 15.6887 9.90687 15.7794 9.5613 15.8477ZM11.5391 15.1766L10.2102 12.487C10.4061 12.3902 10.595 12.2808 10.776 12.1596L12.4449 14.6525C12.1557 14.8461 11.8532 15.0214 11.5391 15.1766ZM13.2748 14.0148L11.2959 11.7601C11.4605 11.6156 11.6156 11.4605 11.7601 11.2959L14.0148 13.2748C13.7845 13.5372 13.5372 13.7845 13.2748 14.0148ZM14.6525 12.4449L12.1596 10.776C12.2808 10.595 12.3902 10.4061 12.487 10.2102L15.1766 11.5391C15.0214 11.8532 14.8461 12.1557 14.6525 12.4449ZM15.5774 10.5725L12.7366 9.60832C12.8058 9.40435 12.8622 9.19433 12.9048 8.97909L15.8477 9.5613C15.7794 9.90687 15.6887 10.2444 15.5774 10.5725Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
{
d: 'M11 8C11 9.65685 9.65685 11 8 11C6.34315 11 5 9.65685 5 8C5 6.34315 6.34315 5 8 5C9.65685 5 11 6.34315 11 8Z',
},
],
[
{
d: 'M15.9026 9.25189L12.939 8.78615C12.979 8.53177 13 8.26937 13 8C13 7.73063 12.979 7.46823 12.939 7.21385L15.9026 6.74811C15.9667 7.15599 16 7.57411 16 8C16 8.42589 15.9667 8.84401 15.9026 9.25189ZM15.4706 5.13214L12.6701 6.20789C12.4812 5.7162 12.2158 5.26012 11.8871 4.85468L14.2174 2.96533C14.7416 3.61188 15.167 4.34182 15.4706 5.13214ZM13.0347 1.78258L11.1453 4.11289C10.7399 3.78417 10.2838 3.51876 9.79211 3.32989L10.8679 0.529397C11.6582 0.832983 12.3881 1.25837 13.0347 1.78258ZM9.25189 0.0973803L8.78615 3.06101C8.53177 3.02103 8.26937 3 8 3C7.73063 3 7.46823 3.02103 7.21385 3.06101L6.74811 0.0973805C7.15599 0.0332797 7.57411 0 8 0C8.42589 0 8.84401 0.0332797 9.25189 0.0973803ZM5.13214 0.529397L6.20789 3.32989C5.7162 3.51876 5.26012 3.78417 4.85468 4.11289L2.96533 1.78258C3.61188 1.25837 4.34182 0.832983 5.13214 0.529397ZM1.78258 2.96533L4.11289 4.85468C3.78417 5.26012 3.51876 5.7162 3.32989 6.20789L0.529397 5.13214C0.832983 4.34182 1.25837 3.61188 1.78258 2.96533ZM0.0973804 6.74811C0.0332797 7.15599 0 7.57411 0 8C0 8.42589 0.0332797 8.84401 0.0973805 9.25189L3.06101 8.78615C3.02103 8.53177 3 8.26937 3 8C3 7.73063 3.02103 7.46823 3.06101 7.21386L0.0973804 6.74811ZM0.529397 10.8679L3.32989 9.79211C3.51876 10.2838 3.78417 10.7399 4.11289 11.1453L1.78258 13.0347C1.25837 12.3881 0.832983 11.6582 0.529397 10.8679ZM2.96533 14.2174L4.85468 11.8871C5.26012 12.2158 5.7162 12.4812 6.20789 12.6701L5.13214 15.4706C4.34182 15.167 3.61188 14.7416 2.96533 14.2174ZM6.74811 15.9026L7.21386 12.939C7.46823 12.979 7.73063 13 8 13C8.26937 13 8.53177 12.979 8.78615 12.939L9.25189 15.9026C8.84401 15.9667 8.42589 16 8 16C7.57411 16 7.15599 15.9667 6.74811 15.9026ZM10.8679 15.4706L9.79211 12.6701C10.2838 12.4812 10.7399 12.2158 11.1453 11.8871L13.0347 14.2174C12.3881 14.7416 11.6582 15.167 10.8679 15.4706ZM14.2174 13.0347L11.8871 11.1453C12.2158 10.7399 12.4812 10.2838 12.6701 9.79211L15.4706 10.8679C15.167 11.6582 14.7416 12.3881 14.2174 13.0347Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
{
d: 'M11 8C11 9.65685 9.65685 11 8 11C6.34315 11 5 9.65685 5 8C5 6.34315 6.34315 5 8 5C9.65685 5 11 6.34315 11 8Z',
},
],
[
{
d: 'M2 14C2 10.6863 4.68629 8 8 8C4.68629 8 2 5.31371 2 2H5C6.65685 2 8 3.34315 8 5C8 3.34315 9.34315 2 11 2H14C14 5.31352 11.3134 7.99969 8 8C11.3134 8.00031 14 10.6865 14 14H11C9.34315 14 8 12.6569 8 11C8 12.6569 6.65685 14 5 14L2 14Z',
},
],
[
{
d: 'M8 8C11.3137 8 14 4.86599 14 1H2C2 4.86567 4.68667 7.99948 8 8C4.68667 8.00053 2 11.1343 2 15H14C14 11.134 11.3137 8 8 8Z',
},
],
[
{
d: 'M15.9026 9.25189L12.939 8.78615C12.979 8.53177 13 8.26937 13 8C13 7.73063 12.979 7.46823 12.939 7.21385L15.9026 6.74811C15.9667 7.15599 16 7.57411 16 8C16 8.42589 15.9667 8.84401 15.9026 9.25189ZM15.4706 5.13214L12.6701 6.20789C12.4812 5.7162 12.2158 5.26012 11.8871 4.85468L14.2174 2.96533C14.7416 3.61188 15.167 4.34182 15.4706 5.13214ZM13.0347 1.78258L11.1453 4.11289C10.7399 3.78417 10.2838 3.51876 9.79211 3.32989L10.8679 0.529397C11.6582 0.832983 12.3881 1.25837 13.0347 1.78258ZM9.25189 0.0973803L8.78615 3.06101C8.53177 3.02103 8.26937 3 8 3C7.73063 3 7.46823 3.02103 7.21385 3.06101L6.74811 0.0973805C7.15599 0.0332797 7.57411 0 8 0C8.42589 0 8.84401 0.0332797 9.25189 0.0973803ZM5.13214 0.529397L6.20789 3.32989C5.7162 3.51876 5.26012 3.78417 4.85468 4.11289L2.96533 1.78258C3.61188 1.25837 4.34182 0.832983 5.13214 0.529397ZM1.78258 2.96533L4.11289 4.85468C3.78417 5.26012 3.51876 5.7162 3.32989 6.20789L0.529397 5.13214C0.832983 4.34182 1.25837 3.61188 1.78258 2.96533ZM0.0973804 6.74811C0.0332797 7.15599 0 7.57411 0 8C0 8.42589 0.0332797 8.84401 0.0973805 9.25189L3.06101 8.78615C3.02103 8.53177 3 8.26937 3 8C3 7.73063 3.02103 7.46823 3.06101 7.21386L0.0973804 6.74811ZM0.529397 10.8679L3.32989 9.79211C3.51876 10.2838 3.78417 10.7399 4.11289 11.1453L1.78258 13.0347C1.25837 12.3881 0.832983 11.6582 0.529397 10.8679ZM2.96533 14.2174L4.85468 11.8871C5.26012 12.2158 5.7162 12.4812 6.20789 12.6701L5.13214 15.4706C4.34182 15.167 3.61188 14.7416 2.96533 14.2174ZM6.74811 15.9026L7.21386 12.939C7.46823 12.979 7.73063 13 8 13C8.26937 13 8.53177 12.979 8.78615 12.939L9.25189 15.9026C8.84401 15.9667 8.42589 16 8 16C7.57411 16 7.15599 15.9667 6.74811 15.9026ZM10.8679 15.4706L9.79211 12.6701C10.2838 12.4812 10.7399 12.2158 11.1453 11.8871L13.0347 14.2174C12.3881 14.7416 11.6582 15.167 10.8679 15.4706ZM14.2174 13.0347L11.8871 11.1453C12.2158 10.7399 12.4812 10.2838 12.6701 9.79211L15.4706 10.8679C15.167 11.6582 14.7416 12.3881 14.2174 13.0347Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
{
d: 'M8 5C8.64911 6.29822 9.70178 7.35089 11 8C9.70178 8.64911 8.64911 9.70178 8 11C7.35089 9.70178 6.29822 8.64911 5 8C6.29822 7.35089 7.35089 6.29822 8 5Z',
},
],
[
{
d: 'M15.9831 8.52372L12.9894 8.33026C12.9964 8.22148 13 8.11137 13 8C13 7.88863 12.9964 7.77852 12.9894 7.66974L15.9831 7.47627C15.9943 7.64941 16 7.82404 16 8C16 8.17596 15.9943 8.35059 15.9831 8.52372ZM15.8477 6.4387L12.9048 7.02091C12.8622 6.80567 12.8058 6.59565 12.7366 6.39168L15.5774 5.42754C15.6887 5.75556 15.7794 6.09313 15.8477 6.4387ZM15.1766 4.46092L12.487 5.78982C12.3902 5.59393 12.2808 5.40497 12.1596 5.22403L14.6525 3.55507C14.8461 3.84425 15.0214 4.14676 15.1766 4.46092ZM14.0148 2.72519L11.7601 4.70411C11.6156 4.5395 11.4605 4.3844 11.2959 4.23992L13.2748 1.98518C13.5372 2.21551 13.7845 2.46275 14.0148 2.72519ZM12.4449 1.34748L10.776 3.84038C10.595 3.71924 10.4061 3.60981 10.2102 3.51302L11.5391 0.823408C11.8532 0.978631 12.1557 1.15388 12.4449 1.34748ZM10.5725 0.422581L9.60832 3.26343C9.40435 3.19421 9.19433 3.13782 8.97909 3.09524L9.5613 0.152272C9.90687 0.220637 10.2444 0.311259 10.5725 0.422581ZM8.52373 0.0168691L8.33026 3.01062C8.22148 3.0036 8.11137 3 8 3C7.88863 3 7.77852 3.0036 7.66974 3.01062L7.47627 0.0168691C7.64941 0.00568084 7.82404 0 8 0C8.17596 0 8.35059 0.00568083 8.52373 0.0168691ZM6.4387 0.152272L7.02091 3.09524C6.80567 3.13782 6.59565 3.19421 6.39168 3.26343L5.42754 0.422581C5.75556 0.311259 6.09313 0.220638 6.4387 0.152272ZM4.46092 0.823408L5.78982 3.51302C5.59393 3.60981 5.40497 3.71925 5.22403 3.84038L3.55507 1.34748C3.84425 1.15388 4.14676 0.978631 4.46092 0.823408ZM2.72519 1.98518L4.70411 4.23992C4.5395 4.3844 4.3844 4.5395 4.23992 4.70412L1.98517 2.7252C2.21551 2.46275 2.46275 2.21551 2.72519 1.98518ZM1.34748 3.55508L3.84038 5.22403C3.71924 5.40497 3.60981 5.59393 3.51302 5.78982L0.823408 4.46092C0.978631 4.14676 1.15388 3.84425 1.34748 3.55508ZM0.422581 5.42754L3.26343 6.39168C3.19421 6.59565 3.13782 6.80567 3.09524 7.02091L0.152272 6.4387C0.220637 6.09313 0.311259 5.75556 0.422581 5.42754ZM0.0168691 7.47627C0.00568083 7.64941 0 7.82404 0 8C0 8.17596 0.00568084 8.35059 0.0168691 8.52373L3.01062 8.33026C3.0036 8.22148 3 8.11137 3 8C3 7.88863 3.0036 7.77852 3.01062 7.66974L0.0168691 7.47627ZM0.152272 9.5613L3.09524 8.97909C3.13782 9.19433 3.19421 9.40435 3.26343 9.60832L0.422581 10.5725C0.311259 10.2444 0.220637 9.90687 0.152272 9.5613ZM0.823408 11.5391L3.51302 10.2102C3.60981 10.4061 3.71925 10.595 3.84038 10.776L1.34748 12.4449C1.15388 12.1557 0.978631 11.8532 0.823408 11.5391ZM1.98518 13.2748L4.23992 11.2959C4.3844 11.4605 4.5395 11.6156 4.70412 11.7601L2.7252 14.0148C2.46275 13.7845 2.21551 13.5372 1.98518 13.2748ZM3.55508 14.6525L5.22403 12.1596C5.40497 12.2808 5.59393 12.3902 5.78982 12.487L4.46092 15.1766C4.14676 15.0214 3.84425 14.8461 3.55508 14.6525ZM5.42754 15.5774L6.39168 12.7366C6.59565 12.8058 6.80567 12.8622 7.02091 12.9048L6.4387 15.8477C6.09313 15.7794 5.75556 15.6887 5.42754 15.5774ZM7.47628 15.9831L7.66974 12.9894C7.77852 12.9964 7.88863 13 8 13C8.11137 13 8.22148 12.9964 8.33026 12.9894L8.52373 15.9831C8.35059 15.9943 8.17596 16 8 16C7.82404 16 7.64941 15.9943 7.47628 15.9831ZM9.5613 15.8477L8.97909 12.9048C9.19433 12.8622 9.40435 12.8058 9.60832 12.7366L10.5725 15.5774C10.2444 15.6887 9.90687 15.7794 9.5613 15.8477ZM11.5391 15.1766L10.2102 12.487C10.4061 12.3902 10.595 12.2808 10.776 12.1596L12.4449 14.6525C12.1557 14.8461 11.8532 15.0214 11.5391 15.1766ZM13.2748 14.0148L11.2959 11.7601C11.4605 11.6156 11.6156 11.4605 11.7601 11.2959L14.0148 13.2748C13.7845 13.5372 13.5372 13.7845 13.2748 14.0148ZM14.6525 12.4449L12.1596 10.776C12.2808 10.595 12.3902 10.4061 12.487 10.2102L15.1766 11.5391C15.0214 11.8532 14.8461 12.1557 14.6525 12.4449ZM15.5774 10.5725L12.7366 9.60832C12.8058 9.40435 12.8622 9.19433 12.9048 8.97909L15.8477 9.5613C15.7794 9.90687 15.6887 10.2444 15.5774 10.5725Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
{
d: 'M8 5C8.64911 6.29822 9.70178 7.35089 11 8C9.70178 8.64911 8.64911 9.70178 8 11C7.35089 9.70178 6.29822 8.64911 5 8C6.29822 7.35089 7.35089 6.29822 8 5Z',
},
],
[
{
d: 'M15.8477 9.5613L8.03733 8.01617L14.6525 12.4449C14.0685 13.3173 13.3173 14.0685 12.4449 14.6525L8.01617 8.03733L9.5613 15.8477C9.05633 15.9476 8.53428 16 8 16C7.46572 16 6.94367 15.9476 6.4387 15.8477L7.98383 8.03733L3.55508 14.6525C2.68266 14.0685 1.93154 13.3173 1.34748 12.4449L7.96267 8.01617L0.152272 9.5613C0.0523756 9.05633 0 8.53428 0 8C0 7.46572 0.0523755 6.94367 0.152272 6.4387L7.96267 7.98383L1.34748 3.55508C1.93154 2.68266 2.68266 1.93154 3.55507 1.34748L7.98383 7.96267L6.4387 0.152272C6.94367 0.0523755 7.46572 0 8 0C8.53428 0 9.05633 0.0523755 9.5613 0.152272L8.01617 7.96267L12.4449 1.34748C13.3173 1.93154 14.0685 2.68266 14.6525 3.55507L8.03733 7.98383L15.8477 6.4387C15.9476 6.94367 16 7.46572 16 8C16 8.53428 15.9476 9.05633 15.8477 9.5613Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M2.34314 2.34314C3.12419 1.5621 4.39052 1.5621 5.17157 2.34314L7.29289 4.46447C7.68342 4.85499 8.31658 4.85499 8.70711 4.46447L10.8284 2.34314C11.6095 1.5621 12.8758 1.5621 13.6569 2.34314C14.4379 3.12419 14.4379 4.39052 13.6569 5.17157L11.5355 7.29289C11.145 7.68342 11.145 8.31658 11.5355 8.70711L13.6569 10.8284C14.4379 11.6095 14.4379 12.8758 13.6569 13.6569C12.8758 14.4379 11.6095 14.4379 10.8284 13.6569L8.70711 11.5355C8.31658 11.145 7.68342 11.145 7.29289 11.5355L5.17157 13.6569C4.39052 14.4379 3.12419 14.4379 2.34314 13.6569C1.5621 12.8758 1.5621 11.6095 2.34314 10.8284L4.46447 8.70711C4.85499 8.31658 4.85499 7.68342 4.46447 7.29289L2.34314 5.17157C1.5621 4.39052 1.5621 3.12419 2.34314 2.34314ZM8 10C9.10457 10 10 9.10457 10 8C10 6.89543 9.10457 6 8 6C6.89543 6 6 6.89543 6 8C6 9.10457 6.89543 10 8 10Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M8 8C8 8 5.5 3.82101 5.5 2.46154C5.5 1.10207 6.61929 0 8 0C9.38071 0 10.5 1.10207 10.5 2.46154C10.5 3.82101 8 8 8 8Z',
},
{
d: 'M8 8C8 8 12.179 5.5 13.5385 5.5C14.8979 5.5 16 6.61929 16 8C16 9.38071 14.8979 10.5 13.5385 10.5C12.1803 10.5 8.00807 8.00483 8 8Z',
},
{
d: 'M8 8C8 8 10.5 12.179 10.5 13.5385C10.5 14.8979 9.38071 16 8 16C6.61929 16 5.5 14.8979 5.5 13.5385C5.5 12.1808 7.99339 8.01105 8 8Z',
},
{
d: 'M2.46154 5.5C3.82101 5.5 8 8 8 8C8 8 3.82101 10.5 2.46154 10.5C1.10207 10.5 0 9.38071 0 8C0 6.61929 1.10207 5.5 2.46154 5.5Z',
},
],
[
{
d: 'M10.3195 2.46119C10.3195 3.82047 8.00012 7.99888 8.00012 7.99888C8.00012 7.99888 5.68069 3.82047 5.68069 2.46119C5.68069 1.10191 6.71913 0 8.00012 0C9.28111 0 10.3195 1.10191 10.3195 2.46119Z',
},
{
d: 'M2.20488 7.30932C3.34269 7.98896 8.00002 8 8.00002 8C7.97235 8.00007 3.33929 8.01307 2.20488 8.69068C1.06707 9.37032 0.66391 10.8517 1.3044 11.9994C1.9449 13.1472 3.3865 13.5267 4.52431 12.847C5.66058 12.1683 7.9937 8.01126 8.00002 8C8.0094 8.01672 10.3402 12.1687 11.4757 12.847C12.6135 13.5266 14.0551 13.1472 14.6956 11.9994C15.3361 10.8517 14.9329 9.37029 13.7951 8.69065C12.6833 8.02652 8.21072 8.00083 8.00717 8C8.21072 7.99917 12.6833 7.97348 13.7951 7.30935C14.9329 6.62971 15.3361 5.14833 14.6956 4.00059C14.0551 2.85285 12.6135 2.47337 11.4757 3.15301C10.3434 3.82938 8.02234 7.96026 8.00002 8C7.99376 7.98884 5.66059 3.83171 4.52431 3.15299C3.3865 2.47335 1.9449 2.85282 1.3044 4.00056C0.66391 5.1483 1.06707 6.62968 2.20488 7.30932Z',
},
{
d: 'M8.00012 8.00112C8.00012 8.00112 10.3195 12.1795 10.3195 13.5388C10.3195 14.8981 9.28111 16 8.00012 16C6.71913 16 5.68069 14.8981 5.68069 13.5388C5.68069 12.1795 8.00012 8.00112 8.00012 8.00112Z',
},
],
[
{
d: 'M8 0C9.10457 0 10 0.895431 10 2V5C10 5.55228 10.4477 6 11 6H14C15.1046 6 16 6.89543 16 8C16 9.10457 15.1046 10 14 10H11C10.4477 10 10 10.4477 10 11V14C10 15.1046 9.10457 16 8 16C6.89543 16 6 15.1046 6 14V11C6 10.4477 5.55228 10 5 10H2C0.895431 10 0 9.10457 0 8C0 6.89543 0.895431 6 2 6H5C5.55228 6 6 5.55228 6 5V2C6 0.89543 6.89543 0 8 0ZM8 10C9.10457 10 10 9.10457 10 8C10 6.89543 9.10457 6 8 6C6.89543 6 6 6.89543 6 8C6 9.10457 6.89543 10 8 10Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M15.9026 9.25189L12.939 8.78615C12.979 8.53177 13 8.26937 13 8C13 7.73063 12.979 7.46823 12.939 7.21385L15.9026 6.74811C15.9667 7.15599 16 7.57411 16 8C16 8.42589 15.9667 8.84401 15.9026 9.25189ZM15.4706 5.13214L12.6701 6.20789C12.4812 5.7162 12.2158 5.26012 11.8871 4.85468L14.2174 2.96533C14.7416 3.61188 15.167 4.34182 15.4706 5.13214ZM13.0347 1.78258L11.1453 4.11289C10.7399 3.78417 10.2838 3.51876 9.79211 3.32989L10.8679 0.529397C11.6582 0.832983 12.3881 1.25837 13.0347 1.78258ZM9.25189 0.0973803L8.78615 3.06101C8.53177 3.02103 8.26937 3 8 3C7.73063 3 7.46823 3.02103 7.21385 3.06101L6.74811 0.0973805C7.15599 0.0332797 7.57411 0 8 0C8.42589 0 8.84401 0.0332797 9.25189 0.0973803ZM5.13214 0.529397L6.20789 3.32989C5.7162 3.51876 5.26012 3.78417 4.85468 4.11289L2.96533 1.78258C3.61188 1.25837 4.34182 0.832983 5.13214 0.529397ZM1.78258 2.96533L4.11289 4.85468C3.78417 5.26012 3.51876 5.7162 3.32989 6.20789L0.529397 5.13214C0.832983 4.34182 1.25837 3.61188 1.78258 2.96533ZM0.0973804 6.74811C0.0332797 7.15599 0 7.57411 0 8C0 8.42589 0.0332797 8.84401 0.0973805 9.25189L3.06101 8.78615C3.02103 8.53177 3 8.26937 3 8C3 7.73063 3.02103 7.46823 3.06101 7.21386L0.0973804 6.74811ZM0.529397 10.8679L3.32989 9.79211C3.51876 10.2838 3.78417 10.7399 4.11289 11.1453L1.78258 13.0347C1.25837 12.3881 0.832983 11.6582 0.529397 10.8679ZM2.96533 14.2174L4.85468 11.8871C5.26012 12.2158 5.7162 12.4812 6.20789 12.6701L5.13214 15.4706C4.34182 15.167 3.61188 14.7416 2.96533 14.2174ZM6.74811 15.9026L7.21386 12.939C7.46823 12.979 7.73063 13 8 13C8.26937 13 8.53177 12.979 8.78615 12.939L9.25189 15.9026C8.84401 15.9667 8.42589 16 8 16C7.57411 16 7.15599 15.9667 6.74811 15.9026ZM10.8679 15.4706L9.79211 12.6701C10.2838 12.4812 10.7399 12.2158 11.1453 11.8871L13.0347 14.2174C12.3881 14.7416 11.6582 15.167 10.8679 15.4706ZM14.2174 13.0347L11.8871 11.1453C12.2158 10.7399 12.4812 10.2838 12.6701 9.79211L15.4706 10.8679C15.167 11.6582 14.7416 12.3881 14.2174 13.0347Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M11.923 8.78487L15.8477 9.5613C15.9476 9.05633 16 8.53428 16 8C16 7.46572 15.9476 6.94367 15.8477 6.4387L11.923 7.21513C11.8192 6.6932 11.6138 6.20786 11.3283 5.78059L14.6525 3.55507C14.0685 2.68266 13.3173 1.93154 12.4449 1.34748L10.2194 4.6717C9.79214 4.38621 9.3068 4.18081 8.78487 4.07697L9.5613 0.152272C9.05633 0.0523755 8.53428 0 8 0C7.46572 0 6.94367 0.0523755 6.4387 0.152272L7.21513 4.07697C6.6932 4.18082 6.20786 4.38621 5.78059 4.6717L3.55507 1.34748C2.68266 1.93154 1.93154 2.68266 1.34748 3.55508L4.6717 5.78059C4.38621 6.20786 4.18081 6.6932 4.07697 7.21513L0.152272 6.4387C0.0523755 6.94367 0 7.46572 0 8C0 8.53428 0.0523756 9.05633 0.152272 9.5613L4.07697 8.78487C4.18082 9.3068 4.38621 9.79214 4.6717 10.2194L1.34748 12.4449C1.93154 13.3173 2.68266 14.0685 3.55508 14.6525L5.78059 11.3283C6.20786 11.6138 6.6932 11.8192 7.21513 11.923L6.4387 15.8477C6.94367 15.9476 7.46572 16 8 16C8.53428 16 9.05633 15.9476 9.5613 15.8477L8.78487 11.923C9.3068 11.8192 9.79214 11.6138 10.2194 11.3283L12.4449 14.6525C13.3173 14.0685 14.0685 13.3173 14.6525 12.4449L11.3283 10.2194C11.6138 9.79214 11.8192 9.3068 11.923 8.78487ZM11.923 8.78487C11.9735 8.53108 12 8.26863 12 8C12 7.73137 11.9735 7.46892 11.923 7.21513L8.03733 7.98383L11.3283 5.78059C11.0353 5.34206 10.6579 4.9647 10.2194 4.6717L8.01617 7.96267L8.78487 4.07697C8.53108 4.02648 8.26863 4 8 4C7.73137 4 7.46892 4.02648 7.21513 4.07697L7.98383 7.96267L5.78059 4.6717C5.34206 4.9647 4.9647 5.34207 4.6717 5.78059L7.96267 7.98383L4.07697 7.21513C4.02648 7.46892 4 7.73137 4 8C4 8.26863 4.02648 8.53108 4.07697 8.78487L7.96267 8.01617L4.6717 10.2194C4.9647 10.6579 5.34207 11.0353 5.78059 11.3283L7.98383 8.03733L7.21513 11.923C7.46892 11.9735 7.73137 12 8 12C8.26863 12 8.53108 11.9735 8.78487 11.923L8.01617 8.03733L10.2194 11.3283C10.6579 11.0353 11.0353 10.6579 11.3283 10.2194L8.03733 8.01617L11.923 8.78487Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M10.9418 8.59077L15.8477 9.5613C15.9476 9.05633 16 8.53428 16 8C16 7.46572 15.9476 6.94367 15.8477 6.4387L10.9418 7.40923C10.98 7.60024 11 7.79778 11 8C11 8.20222 10.98 8.39977 10.9418 8.59077ZM10.4972 9.66303C10.277 9.99308 9.99308 10.277 9.66303 10.4972L12.4449 14.6525C13.3173 14.0685 14.0685 13.3173 14.6525 12.4449L10.4972 9.66303ZM8.59077 10.9418C8.39977 10.98 8.20222 11 8 11C7.79778 11 7.60024 10.98 7.40923 10.9418L6.4387 15.8477C6.94367 15.9476 7.46572 16 8 16C8.53428 16 9.05633 15.9476 9.5613 15.8477L8.59077 10.9418ZM6.33697 10.4972C6.00692 10.277 5.72299 9.99308 5.50276 9.66303L1.34748 12.4449C1.93154 13.3173 2.68266 14.0685 3.55508 14.6525L6.33697 10.4972ZM5.05815 8.59077C5.02001 8.39977 5 8.20222 5 8C5 7.79778 5.02001 7.60024 5.05815 7.40923L0.152272 6.4387C0.0523755 6.94367 0 7.46572 0 8C0 8.53428 0.0523756 9.05633 0.152272 9.5613L5.05815 8.59077ZM5.50276 6.33697C5.72299 6.00692 6.00692 5.72299 6.33697 5.50276L3.55507 1.34748C2.68266 1.93154 1.93154 2.68266 1.34748 3.55508L5.50276 6.33697ZM7.40923 5.05815C7.60024 5.02001 7.79778 5 8 5C8.20222 5 8.39977 5.02001 8.59077 5.05815L9.5613 0.152272C9.05633 0.0523755 8.53428 0 8 0C7.46572 0 6.94367 0.0523755 6.4387 0.152272L7.40923 5.05815ZM9.66303 5.50276L12.4449 1.34748C13.3173 1.93154 14.0685 2.68266 14.6525 3.55507L10.4972 6.33697C10.277 6.00692 9.99308 5.72299 9.66303 5.50276Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M6.65494 5.34506L8 4L9.34522 5.34522C9.85712 4.23711 10.3195 3.06813 10.3195 2.46119C10.3195 1.10191 9.28111 0 8.00012 0C6.71913 0 5.68069 1.10191 5.68069 2.46119C5.68069 3.0681 6.14307 4.237 6.65494 5.34506Z',
},
{
d: 'M6.48343 5.51657L4.20992 7.79008C3.35262 7.68776 2.58369 7.5356 2.20488 7.30932C1.06707 6.62968 0.66391 5.1483 1.3044 4.00056C1.9449 2.85282 3.3865 2.47335 4.52431 3.15299C5.04047 3.4613 5.80362 4.48739 6.48343 5.51657Z',
},
{
d: 'M4.20992 8.20992C3.35262 8.31224 2.58369 8.4644 2.20488 8.69068C1.06707 9.37032 0.66391 10.8517 1.3044 11.9994C1.9449 13.1472 3.3865 13.5267 4.52431 12.847C5.04047 12.5387 5.80362 11.5126 6.48343 10.4834L4.20992 8.20992Z',
},
{
d: 'M6.65494 10.6549C6.14307 11.763 5.68069 12.9319 5.68069 13.5388C5.68069 14.8981 6.71913 16 8.00012 16C9.28111 16 10.3195 14.8981 10.3195 13.5388C10.3195 12.9319 9.85712 11.7629 9.34522 10.6548L8 12L6.65494 10.6549Z',
},
{
d: 'M9.51659 10.4834C10.1964 11.5126 10.9595 12.5387 11.4757 12.847C12.6135 13.5266 14.0551 13.1472 14.6956 11.9994C15.3361 10.8517 14.9329 9.37029 13.7951 8.69065C13.4163 8.46437 12.6474 8.31221 11.7901 8.20989L9.51659 10.4834Z',
},
{
d: 'M11.7901 7.79011L9.5166 5.5166C10.1964 4.4874 10.9596 3.46132 11.4757 3.15301C12.6135 2.47337 14.0551 2.85285 14.6956 4.00059C15.3361 5.14833 14.9329 6.62971 13.7951 7.30935C13.4163 7.53563 12.6474 7.68779 11.7901 7.79011Z',
},
{
d: 'M8 6L10 8L8 10L6 8L8 6Z',
},
],
[
{
d: 'M15.4055 4.96811C14.5941 2.98825 13.0118 1.40593 11.0319 0.5945C10.9114 0.68356 10.796 0.782639 10.6869 0.891746C10.2922 1.28636 9.86718 2.18783 9.47419 3.22087C9.00818 3.07729 8.51312 3 8 3C7.48688 3 6.99182 3.07729 6.52581 3.22087C6.13282 2.18783 5.70776 1.28636 5.31315 0.891746C5.20404 0.782638 5.08865 0.683558 4.96811 0.594498C2.98825 1.40593 1.40593 2.98825 0.594501 4.96812C0.68356 5.08865 0.78264 5.20404 0.891747 5.31315C1.28636 5.70776 2.18783 6.13282 3.22087 6.52581C3.07729 6.99182 3 7.48688 3 8C3 8.51312 3.07729 9.00818 3.22087 9.47419C2.18783 9.86718 1.28636 10.2922 0.891747 10.6869C0.782642 10.796 0.683564 10.9113 0.594506 11.0319C1.40594 13.0117 2.98825 14.5941 4.96812 15.4055C5.08866 15.3164 5.20404 15.2174 5.31315 15.1083C5.70776 14.7136 6.13282 13.8122 6.52581 12.7791C6.99182 12.9227 7.48688 13 8 13C8.51312 13 9.00818 12.9227 9.47419 12.7791C9.86718 13.8122 10.2922 14.7136 10.6869 15.1083C10.796 15.2174 10.9113 15.3164 11.0319 15.4055C13.0117 14.5941 14.5941 13.0117 15.4055 11.0319C15.3164 10.9113 15.2174 10.796 15.1083 10.6869C14.7136 10.2922 13.8122 9.86718 12.7791 9.47419C12.9227 9.00818 13 8.51312 13 8C13 7.48688 12.9227 6.99182 12.7791 6.52581C13.8122 6.13282 14.7136 5.70776 15.1083 5.31315C15.2174 5.20404 15.3164 5.08865 15.4055 4.96811ZM8 8C8 8 8.66996 5.33492 9.47419 3.22087C11.0501 3.70641 12.2936 4.94995 12.7791 6.52581C10.6651 7.33004 8 8 8 8ZM8 8C8 8 5.33492 8.66996 3.22087 9.47419C3.70641 11.0501 4.94995 12.2936 6.52581 12.7791C7.33004 10.6651 8 8 8 8ZM8 8C8 8 10.6651 8.66996 12.7791 9.47419C12.2936 11.0501 11.0501 12.2936 9.47419 12.7791C8.66996 10.6651 8 8 8 8ZM8 8C8 8 5.33492 7.33004 3.22087 6.52581C3.70641 4.94994 4.94995 3.7064 6.52581 3.22087C7.33004 5.33492 8 8 8 8Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16ZM8 8C8 8 12.7852 6.79708 13.7592 5.82309C13.8476 5.73469 13.9278 5.64121 14 5.54355C13.3426 3.93944 12.0606 2.65743 10.4565 2C10.3588 2.07216 10.2653 2.15243 10.1769 2.24083C9.20292 3.21483 8 8 8 8ZM8 8C8 8 9.20292 12.7852 10.1769 13.7592C10.2653 13.8476 10.3588 13.9278 10.4565 14C12.0606 13.3426 13.3426 12.0606 14 10.4565C13.9278 10.3588 13.8476 10.2653 13.7592 10.1769C12.7852 9.20292 8 8 8 8ZM8 8C8 8 6.79708 12.7852 5.82309 13.7592C5.73469 13.8476 5.64121 13.9278 5.54355 14C3.93944 13.3426 2.65743 12.0606 2 10.4565C2.07216 10.3588 2.15243 10.2653 2.24083 10.1769C3.21483 9.20292 8 8 8 8ZM8 8C8 8 3.21483 6.79708 2.24083 5.82309C2.15243 5.73469 2.07216 5.6412 2 5.54354C2.65743 3.93944 3.93944 2.65743 5.54354 2C5.6412 2.07216 5.73469 2.15243 5.82309 2.24083C6.79708 3.21483 8 8 8 8Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M9.17157 1.17157C7.60948 2.73367 7.60948 5.26633 9.17157 6.82843C10.7337 8.39052 13.2663 8.39052 14.8284 6.82843C16.3905 5.26633 16.3905 2.73367 14.8284 1.17157C13.2663 -0.390524 10.7337 -0.390524 9.17157 1.17157Z',
},
{
d: 'M1.17157 14.8284C2.73367 16.3905 5.26633 16.3905 6.82843 14.8284C8.39052 13.2663 8.39052 10.7337 6.82843 9.17157C5.26633 7.60948 2.73367 7.60948 1.17157 9.17157C-0.390524 10.7337 -0.390524 13.2663 1.17157 14.8284Z',
},
{
d: 'M6.82843 1.17157C8.39052 2.73367 8.39052 5.26633 6.82843 6.82843C5.26633 8.39052 2.73367 8.39052 1.17157 6.82843C-0.390524 5.26633 -0.390524 2.73367 1.17157 1.17157C2.73367 -0.390524 5.26633 -0.390524 6.82843 1.17157Z',
},
{
d: 'M9.17157 14.8284C10.7337 16.3905 13.2663 16.3905 14.8284 14.8284C16.3905 13.2663 16.3905 10.7337 14.8284 9.17157C13.2663 7.60948 10.7337 7.60948 9.17157 9.17157C7.60948 10.7337 7.60948 13.2663 9.17157 14.8284Z',
},
],
[
{
d: 'M13.6569 2.34313C12.0059 5.93367 12.0059 10.0663 13.6569 13.6568C10.0663 12.0058 5.93368 12.0058 2.34315 13.6568C3.99414 10.0663 3.99414 5.93367 2.34315 2.34313C5.93368 3.99413 10.0663 3.99413 13.6569 2.34313Z',
},
],
[
{
d: 'M15.1083 5.31314C13.9061 6.5153 8 8 8 8C8 8 9.4847 2.09389 10.6869 0.891741C10.796 0.782636 10.9113 0.683558 11.0319 0.594499C13.0117 1.40593 14.5941 2.98825 15.4055 4.96812C15.3164 5.08865 15.2174 5.20404 15.1083 5.31314Z',
},
{
d: 'M15.4055 11.0319C15.3164 10.9113 15.2174 10.796 15.1083 10.6868C13.9061 9.48469 8 8 8 8C8 8 6.5153 2.09389 5.31315 0.891741C5.20404 0.782635 5.08866 0.683557 4.96812 0.594498C2.98825 1.40593 1.40593 2.98824 0.594501 4.96812C0.683561 5.08865 0.782639 5.20404 0.891746 5.31314C2.0939 6.5153 8 8 8 8C8 8 2.0939 9.48469 0.891746 10.6868C0.78264 10.796 0.683561 10.9113 0.594502 11.0319C1.40593 13.0117 2.98825 14.5941 4.96812 15.4055C5.08866 15.3164 5.20404 15.2174 5.31315 15.1082C6.5153 13.9061 8 8 8 8C8 8 9.4847 13.9061 10.6869 15.1082C10.796 15.2174 10.9113 15.3164 11.0319 15.4055C13.0117 14.5941 14.5941 13.0117 15.4055 11.0319Z',
},
],
[
{
d: 'M8 0C8.33735 2.71168 11.5009 4.02205 13.6569 2.34315C11.9779 4.49913 13.2883 7.66266 16 8C13.2883 8.33735 11.9779 11.5009 13.6569 13.6569C11.5009 11.9779 8.33735 13.2883 8 16C7.66266 13.2883 4.49913 11.9779 2.34315 13.6569C4.02205 11.5009 2.71168 8.33735 0 8C2.71168 7.66266 4.02205 4.49913 2.34315 2.34315C4.49913 4.02205 7.66266 2.71168 8 0Z',
},
],
[
{
d: 'M6.24244 4.40581C6.77293 4.14591 7.36943 4 8 4C8.63065 4 9.22722 4.14595 9.75776 4.40591C10.0814 3.6217 10.3195 2.89673 10.3195 2.46119C10.3195 1.10191 9.28111 0 8.00012 0C6.71913 0 5.68069 1.10191 5.68069 2.46119C5.68069 2.89671 5.9188 3.62164 6.24244 4.40581Z',
},
{
d: 'M5.86339 4.61784C4.80568 5.28742 4.08369 6.43949 4.00681 7.76466C3.23255 7.66304 2.55351 7.51757 2.20488 7.30932C1.06707 6.62968 0.66391 5.1483 1.3044 4.00056C1.9449 2.85282 3.3865 2.47335 4.52431 3.15299C4.88694 3.3696 5.37149 3.94049 5.86339 4.61784Z',
},
{
d: 'M4.00681 8.23534C3.23255 8.33696 2.5535 8.48244 2.20488 8.69068C1.06707 9.37032 0.66391 10.8517 1.3044 11.9994C1.9449 13.1472 3.3865 13.5267 4.52431 12.847C4.88694 12.6304 5.37149 12.0595 5.86339 11.3822C4.80568 10.7126 4.0837 9.56051 4.00681 8.23534Z',
},
{
d: 'M6.24244 11.5942C5.9188 12.3784 5.68069 13.1033 5.68069 13.5388C5.68069 14.8981 6.71913 16 8.00012 16C9.28111 16 10.3195 14.8981 10.3195 13.5388C10.3195 13.1033 10.0814 12.3783 9.75776 11.5941C9.22722 11.8541 8.63065 12 8 12C7.36943 12 6.77293 11.8541 6.24244 11.5942Z',
},
{
d: 'M10.1366 11.3821C10.6285 12.0595 11.1131 12.6304 11.4757 12.847C12.6135 13.5266 14.0551 13.1472 14.6956 11.9994C15.3361 10.8517 14.9329 9.37029 13.7951 8.69065C13.4465 8.4824 12.7675 8.33693 11.9932 8.23531C11.9163 9.56048 11.1943 10.7126 10.1366 11.3821Z',
},
{
d: 'M11.9932 7.76469C11.9163 6.43952 11.1943 5.28745 10.1366 4.61786C10.6285 3.94051 11.1131 3.36962 11.4757 3.15301C12.6135 2.47337 14.0551 2.85285 14.6956 4.00059C15.3361 5.14833 14.9329 6.62971 13.7951 7.30935C13.4465 7.5176 12.7675 7.66307 11.9932 7.76469Z',
},
],
[
{
d: 'M8 0C9.37146 3.70632 12.2937 6.62854 16 8C12.2937 9.37146 9.37146 12.2937 8 16C6.62854 12.2937 3.70632 9.37146 0 8C3.70632 6.62854 6.62854 3.70632 8 0Z',
},
],
[
{
d: 'M12 4C12 6.20914 10.2091 8 8 8C5.79086 8 4 6.20914 4 4C4 1.79086 5.79086 0 8 0C10.2091 0 12 1.79086 12 4Z',
},
{
d: 'M8 11C8 13.2091 6.20914 15 4 15C1.79086 15 0 13.2091 0 11C0 8.79086 1.79086 7 4 7C6.20914 7 8 8.79086 8 11Z',
},
{
d: 'M8 11C8 8.79086 9.79086 7 12 7C14.2091 7 16 8.79086 16 11C16 13.2091 14.2091 15 12 15C9.79086 15 8 13.2091 8 11Z',
},
],
[
{
d: 'M8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16ZM5 4C4.44772 4 4 4.44772 4 5V11C4 11.5523 4.44772 12 5 12H11C11.5523 12 12 11.5523 12 11V5C12 4.44772 11.5523 4 11 4H5Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16ZM12.7207 7.32562L8.67438 3.27934C8.30193 2.90689 7.69807 2.90689 7.32562 3.27934L3.27934 7.32562C2.90689 7.69807 2.90689 8.30193 3.27934 8.67438L7.32562 12.7207C7.69807 13.0931 8.30193 13.0931 8.67438 12.7207L12.7207 8.67438C13.0931 8.30193 13.0931 7.69807 12.7207 7.32562Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M10.3438 10.3438C10.9434 10.9431 11.7715 11.3137 12.6863 11.3137C14.5164 11.3137 16 9.83012 16 8.00001C16 6.1699 14.5164 4.6863 12.6863 4.6863C10.8562 4.6863 9.37259 6.1699 9.37259 8.00001C9.37259 8.91475 9.74323 9.74292 10.3425 10.3425C9.74292 9.74323 8.91475 9.37258 8 9.37258C7.08525 9.37258 6.25708 9.74323 5.65746 10.3425C6.25677 9.74292 6.62741 8.91475 6.62741 8.00001C6.62741 6.1699 5.14382 4.6863 3.31371 4.6863C1.4836 4.6863 -3.70335e-07 6.1699 0 8.00001C-4.9378e-07 9.83012 1.4836 11.3137 3.31371 11.3137C4.22845 11.3137 5.05663 10.9431 5.65625 10.3438C5.05695 10.9434 4.68629 11.7715 4.68629 12.6863C4.68629 14.5164 6.16989 16 8 16C9.83011 16 11.3137 14.5164 11.3137 12.6863C11.3137 11.7715 10.9431 10.9434 10.3438 10.3438Z',
},
{
d: 'M8 6.62742C6.16989 6.62742 4.68629 5.14382 4.68629 3.31371C4.68629 1.4836 6.16989 -7.40671e-08 8 0C9.83011 -7.40671e-08 11.3137 1.4836 11.3137 3.31371C11.3137 5.14382 9.83011 6.62742 8 6.62742Z',
},
],
[
{
d: 'M13.6569 2.34315C11.5009 4.02205 8.33735 2.71168 8 0C7.66266 2.71168 4.49913 4.02205 2.34315 2.34315C4.02205 4.49913 2.71168 7.66266 0 8C2.71168 8.33735 4.02205 11.5009 2.34315 13.6569C4.49913 11.9779 7.66266 13.2883 8 16C8.33735 13.2883 11.5009 11.9779 13.6569 13.6569C11.9779 11.5009 13.2883 8.33735 16 8C13.2883 7.66266 11.9779 4.49913 13.6569 2.34315ZM8 11C9.65685 11 11 9.65685 11 8C11 6.34315 9.65685 5 8 5C6.34315 5 5 6.34315 5 8C5 9.65685 6.34315 11 8 11Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M11.9916 2.34459C12.451 1.88514 13.1959 1.88514 13.6554 2.34459C14.1149 2.80405 14.1149 3.54899 13.6554 4.00845L11.1596 6.50422C10.7002 6.96368 9.95524 6.96368 9.49578 6.50422C9.03632 6.04476 9.03632 5.29983 9.49578 4.84037L11.9916 2.34459Z',
},
{
d: 'M4.84037 9.49578C5.29983 9.03632 6.04476 9.03632 6.50422 9.49578C6.96368 9.95524 6.96368 10.7002 6.50422 11.1596L4.00845 13.6554C3.54899 14.1149 2.80405 14.1149 2.3446 13.6554C1.88514 13.1959 1.88514 12.451 2.3446 11.9916L4.84037 9.49578Z',
},
{
d: 'M13.6554 11.9916C14.1149 12.451 14.1149 13.1959 13.6554 13.6554C13.1959 14.1149 12.451 14.1149 11.9916 13.6554L9.49578 11.1596C9.03632 10.7002 9.03632 9.95524 9.49578 9.49578C9.95524 9.03632 10.7002 9.03632 11.1596 9.49578L13.6554 11.9916Z',
},
{
d: 'M6.50422 4.84037C6.96368 5.29983 6.96368 6.04476 6.50422 6.50422C6.04476 6.96368 5.29983 6.96368 4.84037 6.50422L2.34459 4.00845C1.88513 3.54899 1.88514 2.80405 2.3446 2.34459C2.80405 1.88513 3.54899 1.88514 4.00845 2.34459L6.50422 4.84037Z',
},
],
[
{
d: 'M15 2C15 5.86599 11.866 9 8 9C4.13401 9 1 5.86599 1 2H4.5C6.433 2 8 3.567 8 5.5C8 3.567 9.567 2 11.5 2H15Z',
},
{
d: 'M1 9C1 12.866 4.13401 16 8 16C11.866 16 15 12.866 15 9H11.5C9.567 9 8 10.567 8 12.5C8 10.567 6.433 9 4.5 9H1Z',
},
],
[
{
d: 'M7.99948 0C6.89491 7.11679e-05 5.99954 0.895559 5.99961 2.00013C5.99968 3.1047 6.8957 4.00007 8.00027 4C9.10484 4.00007 10.0003 3.1047 10.0004 2.00013C10.0005 0.895559 9.1051 7.11679e-05 8.00053 0H7.99948Z',
},
{
d: 'M4.00001 7.99973C4.00008 6.89516 3.10471 5.99968 2.00014 5.9996C0.895567 5.99953 7.87973e-05 6.89491 7.62939e-06 7.99948V8.00052C7.87973e-05 9.10509 0.895567 10.0005 2.00014 10.0004C3.10471 10.0003 4.00008 9.1043 4.00001 7.99973Z',
},
{
d: 'M16 7.99948C15.9999 6.89491 15.1044 5.99953 13.9999 5.99961C12.8953 5.99968 11.9999 6.8957 12 8.00027C11.9999 9.10484 12.8953 10.0003 13.9999 10.0004C15.1044 10.0005 15.9999 9.10509 16 8.00052V7.99948Z',
},
{
d: 'M7.99974 12C6.89517 11.9999 5.99968 12.8953 5.99961 13.9999C5.99954 15.1044 6.89491 15.9999 7.99948 16H8.00053C9.1051 15.9999 10.0005 15.1044 10.0004 13.9999C10.0003 12.8953 9.10431 11.9999 7.99974 12Z',
},
{
d: 'M8.00001 11C9.65686 11 11 9.65685 11 8C11 6.34315 9.65686 5 8.00001 5C6.34315 5 5.00001 6.34315 5.00001 8C5.00001 9.65685 6.34315 11 8.00001 11Z',
},
],
[
{
d: 'M13.6569 2.34315C11.5009 4.02205 8.33735 2.71168 8 0C7.66266 2.71168 4.49913 4.02205 2.34315 2.34315C4.02205 4.49913 2.71168 7.66266 0 8C2.71168 8.33735 4.02205 11.5009 2.34315 13.6569C4.49913 11.9779 7.66266 13.2883 8 16C8.33735 13.2883 11.5009 11.9779 13.6569 13.6569C11.9779 11.5009 13.2883 8.33735 16 8C13.2883 7.66266 11.9779 4.49913 13.6569 2.34315ZM12 8L8 4L4 8L8 12L12 8Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8Z',
},
],
[
{
d: 'M13.6569 13.6568C12.0059 10.0663 12.0059 5.93368 13.6569 2.34314C10.0663 3.99414 5.93368 3.99414 2.34315 2.34314C3.99414 5.93368 3.99414 10.0663 2.34315 13.6568C5.93368 12.0059 10.0663 12.0059 13.6569 13.6568ZM11 8L8 5L5 8L8 11L11 8Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M7.99948 0C6.89491 7.11679e-05 5.99954 0.895559 5.99961 2.00013C5.99968 3.1047 6.8957 4.00007 8.00027 4C9.10484 4.00007 10.0003 3.1047 10.0004 2.00013C10.0005 0.895559 9.1051 7.11679e-05 8.00053 0H7.99948Z',
},
{
d: 'M13.6565 2.34278C12.8754 1.56178 11.6091 1.56186 10.8281 2.34296C10.0471 3.12406 10.0475 4.39076 10.8286 5.17176C11.6096 5.95286 12.8759 5.95294 13.657 5.17194C14.4381 4.39094 14.4382 3.12461 13.6572 2.34352L13.6565 2.34278Z',
},
{
d: 'M5.17177 5.17139C5.95287 4.39039 5.95295 3.12406 5.17195 2.34296C4.39095 1.56186 3.12462 1.56178 2.34352 2.34278L2.34278 2.34352C1.56179 3.12462 1.56187 4.39094 2.34297 5.17194C3.12407 5.95294 4.39077 5.95249 5.17177 5.17139Z',
},
{
d: 'M4.00001 7.99973C4.00008 6.89516 3.10471 5.99968 2.00014 5.9996C0.895567 5.99953 7.87973e-05 6.89491 7.62939e-06 7.99948V8.00052C7.87973e-05 9.10509 0.895567 10.0005 2.00014 10.0004C3.10471 10.0003 4.00008 9.1043 4.00001 7.99973Z',
},
{
d: 'M16 7.99948C15.9999 6.89491 15.1044 5.99953 13.9999 5.99961C12.8953 5.99968 11.9999 6.8957 12 8.00027C11.9999 9.10484 12.8953 10.0003 13.9999 10.0004C15.1044 10.0005 15.9999 9.10509 16 8.00052V7.99948Z',
},
{
d: 'M5.1714 10.8282C4.3904 10.0471 3.12407 10.0471 2.34297 10.8281C1.56187 11.6091 1.56179 12.8754 2.34278 13.6565L2.34352 13.6572C3.12462 14.4382 4.39095 14.4381 5.17195 13.657C5.95295 12.8759 5.95249 11.6092 5.1714 10.8282Z',
},
{
d: 'M13.6572 13.6565C14.4382 12.8754 14.4381 11.6091 13.657 10.8281C12.8759 10.0471 11.6092 10.0475 10.8282 10.8286C10.0471 11.6096 10.0471 12.8759 10.8281 13.657C11.6091 14.4381 12.8754 14.4382 13.6565 13.6572L13.6572 13.6565Z',
},
{
d: 'M7.99974 12C6.89517 11.9999 5.99968 12.8953 5.99961 13.9999C5.99954 15.1044 6.89491 15.9999 7.99948 16H8.00053C9.1051 15.9999 10.0005 15.1044 10.0004 13.9999C10.0003 12.8953 9.10431 11.9999 7.99974 12Z',
},
{
d: 'M8.00001 11C9.65686 11 11 9.65685 11 8C11 6.34315 9.65686 5 8.00001 5C6.34315 5 5.00001 6.34315 5.00001 8C5.00001 9.65685 6.34315 11 8.00001 11Z',
},
],
[
{
d: 'M8 8C8 8 13.1678 6.70089 14.2197 5.649C15.2716 4.59712 15.2583 2.87836 14.19 1.81004C13.1216 0.741719 11.4029 0.728395 10.351 1.78028C9.29911 2.83216 8 8 8 8Z',
},
{
d: 'M8 8C8 8 13.1678 9.29911 14.2197 10.351C15.2716 11.4029 15.2583 13.1216 14.19 14.19C13.1216 15.2583 11.4029 15.2716 10.351 14.2197C9.29911 13.1678 8 8 8 8Z',
},
{
d: 'M1.78028 10.351C2.83216 9.29911 8 8 8 8C8 8 6.70089 13.1678 5.649 14.2197C4.59712 15.2716 2.87836 15.2583 1.81004 14.19C0.741719 13.1216 0.728395 11.4029 1.78028 10.351Z',
},
{
d: 'M8 8C8 8 2.83216 6.70089 1.78028 5.649C0.728395 4.59712 0.741719 2.87836 1.81004 1.81004C2.87836 0.741719 4.59712 0.728395 5.649 1.78028C6.70089 2.83216 8 8 8 8Z',
},
],
[
{
d: 'M8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16ZM8 13C10.7614 13 13 10.7614 13 8C13 5.23858 10.7614 3 8 3C5.23858 3 3 5.23858 3 8C3 10.7614 5.23858 13 8 13Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M13.6569 13.6568C12.0059 10.0663 12.0059 5.93368 13.6569 2.34314C10.0663 3.99414 5.93368 3.99414 2.34315 2.34314C3.99414 5.93368 3.99414 10.0663 2.34315 13.6568C5.93368 12.0059 10.0663 12.0059 13.6569 13.6568ZM8 11C9.65685 11 11 9.65686 11 8.00001C11 6.34315 9.65685 5.00001 8 5.00001C6.34315 5.00001 5 6.34315 5 8.00001C5 9.65686 6.34315 11 8 11Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M5.34749 1.59629C5.85108 0.646843 6.8496 0.000111103 7.99922 0H8.00078C9.1504 0.000111103 10.1489 0.646843 10.6525 1.59628C11.68 1.28102 12.8433 1.52977 13.6563 2.34259L13.6574 2.3437C14.4702 3.15668 14.719 4.32005 14.4037 5.34749C15.3532 5.85108 15.9999 6.8496 16 7.99922V8.00078C15.9999 9.1504 15.3532 10.1489 14.4037 10.6525C14.719 11.68 14.4702 12.8433 13.6574 13.6563L13.6563 13.6574C12.8433 14.4702 11.68 14.719 10.6525 14.4037C10.1489 15.3532 9.1504 15.9999 8.00078 16H7.99922C6.8496 15.9999 5.85108 15.3532 5.3475 14.4037C4.32005 14.719 3.15668 14.4702 2.3437 13.6574L2.34259 13.6563C1.52977 12.8433 1.28102 11.68 1.59629 10.6525C0.646843 10.1489 0.000111103 9.1504 0 8.00078V7.99922C0.000111103 6.8496 0.646843 5.85108 1.59628 5.34749C1.28102 4.32005 1.52977 3.15668 2.34259 2.3437L2.3437 2.34259C3.15668 1.52977 4.32005 1.28102 5.34749 1.59629ZM7.11693 5.86807C6.98022 6.12611 6.80322 6.3684 6.58593 6.58565C6.36868 6.80294 6.12611 6.98022 5.86807 7.11693C5.95387 7.39605 6.00003 7.69253 6 7.9998C6.00003 8.30706 5.95387 8.60395 5.86807 8.88307C6.12611 9.01978 6.3684 9.19678 6.58565 9.41407C6.80294 9.63132 6.98022 9.87389 7.11693 10.1319C7.39605 10.0461 7.69253 9.99997 7.9998 10C8.30706 9.99997 8.60394 10.0461 8.88307 10.1319C9.01978 9.87389 9.19678 9.6316 9.41407 9.41435C9.63132 9.19706 9.87389 9.01978 10.1319 8.88307C10.0461 8.60395 9.99997 8.30747 10 8.0002C9.99997 7.69294 10.0461 7.39605 10.1319 7.11693C9.87389 6.98022 9.6316 6.80322 9.41435 6.58593C9.19706 6.36868 9.01978 6.12611 8.88307 5.86807C8.60395 5.95387 8.30747 6.00003 8.0002 6C7.69294 6.00003 7.39605 5.95387 7.11693 5.86807Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M12.6045 11.8471C13.4757 10.8056 14 9.46402 14 8C14 6.53598 13.4756 5.19442 12.6045 4.15289L13.6832 2.76772C13.9157 2.46917 13.5308 2.08434 13.2323 2.31683L11.8471 3.39548C10.8056 2.52434 9.46402 2 8 2C6.53598 2 5.19443 2.52434 4.1529 3.39548L2.76773 2.31682C2.46918 2.08434 2.08435 2.46917 2.31684 2.76772L3.39549 4.15289C2.52435 5.19442 2 6.53598 2 8C2 9.46402 2.52435 10.8056 3.39548 11.8471L2.31684 13.2323C2.08435 13.5308 2.46918 13.9156 2.76773 13.6832L4.15289 12.6045C5.19442 13.4756 6.53598 14 8 14C9.46402 14 10.8056 13.4756 11.8471 12.6045L13.2323 13.6832C13.5308 13.9156 13.9156 13.5308 13.6832 13.2323L12.6045 11.8471ZM12.6045 11.8471L10.3747 8.9836L12.89 8.31882C13.2654 8.2721 13.2654 7.72787 12.8899 7.68116L10.3747 7.01637L12.6045 4.15289C12.375 3.87852 12.1215 3.62497 11.8471 3.39548L8.98361 5.62533L8.31883 3.11004C8.27212 2.73454 7.72788 2.73454 7.68117 3.11004L7.01639 5.62533L4.1529 3.39548C3.87853 3.62497 3.62498 3.87852 3.39549 4.15289L5.62535 7.01637L3.11005 7.68116C2.73455 7.72787 2.73455 8.27211 3.11005 8.31882L5.62535 8.9836L3.39548 11.8471C3.62497 12.1215 3.87852 12.375 4.15289 12.6045L7.01639 10.3746L7.68117 12.8899C7.72788 13.2654 8.27212 13.2654 8.31883 12.8899L8.98361 10.3746L11.8471 12.6045C12.1215 12.375 12.375 12.1215 12.6045 11.8471Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M8.53781 0.379548C8.35784 -0.126516 7.64216 -0.126516 7.4622 0.379548L6.25252 3.78121L2.99181 2.23124C2.50672 2.00065 2.00065 2.50672 2.23124 2.99181L3.78121 6.25252L0.379548 7.4622C-0.126516 7.64216 -0.126516 8.35784 0.379548 8.53781L3.78121 9.74748L2.23124 13.0082C2.00065 13.4933 2.50672 13.9993 2.99181 13.7688L6.25252 12.2188L7.4622 15.6205C7.64216 16.1265 8.35784 16.1265 8.53781 15.6205L9.74748 12.2188L13.0082 13.7688C13.4933 13.9993 13.9993 13.4933 13.7688 13.0082L12.2188 9.74748L15.6205 8.53781C16.1265 8.35784 16.1265 7.64216 15.6205 7.4622L12.2188 6.25252L13.7688 2.99181C13.9993 2.50672 13.4933 2.00065 13.0082 2.23124L9.74748 3.78121L8.53781 0.379548ZM7.99994 10.9999C9.65679 10.9999 10.9999 9.65679 10.9999 7.99994C10.9999 6.34308 9.65679 4.99994 7.99994 4.99994C6.34308 4.99994 4.99994 6.34308 4.99994 7.99994C4.99994 9.65679 6.34308 10.9999 7.99994 10.9999Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M7.4622 0.379548C7.64216 -0.126516 8.35784 -0.126516 8.53781 0.379548L9.74748 3.78121L13.0082 2.23124C13.4933 2.00065 13.9993 2.50672 13.7688 2.99181L12.2188 6.25252L15.6205 7.4622C16.1265 7.64216 16.1265 8.35784 15.6205 8.53781L12.2188 9.74748L13.7688 13.0082C13.9993 13.4933 13.4933 13.9993 13.0082 13.7688L9.74748 12.2188L8.53781 15.6205C8.35784 16.1265 7.64216 16.1265 7.4622 15.6205L6.25252 12.2188L2.99181 13.7688C2.50672 13.9993 2.00065 13.4933 2.23124 13.0082L3.78121 9.74748L0.379548 8.53781C-0.126516 8.35784 -0.126516 7.64216 0.379548 7.4622L3.78121 6.25252L2.23124 2.99181C2.00065 2.50672 2.50672 2.00065 2.99181 2.23124L6.25252 3.78121L7.4622 0.379548Z',
},
],
[
{
d: 'M4.4 2C3.07452 2 2 3.07452 2 4.4V11.6C2 12.9255 3.07452 14 4.4 14H11.6C12.9255 14 14 12.9255 14 11.6V4.4C14 3.07452 12.9255 2 11.6 2H4.4ZM8.70711 4.70711C8.31658 4.31658 7.68342 4.31658 7.29289 4.70711L4.70711 7.29289C4.31658 7.68342 4.31658 8.31658 4.70711 8.70711L7.29289 11.2929C7.68342 11.6834 8.31658 11.6834 8.70711 11.2929L11.2929 8.70711C11.6834 8.31658 11.6834 7.68342 11.2929 7.29289L8.70711 4.70711Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M15 1C11.134 1 8 4.13401 8 8H15V1Z',
},
{
d: 'M1 15C4.86599 15 8 11.866 8 8H1V15Z',
},
{
d: 'M15 15C15 11.134 11.866 8 8 8L8 15H15Z',
},
{
d: 'M1 1C1 4.86599 4.13401 8 8 8V1L1 1Z',
},
],
[
{
d: 'M8.31883 0.281623C8.27212 -0.0938759 7.72788 -0.0938737 7.68117 0.281626L7.46446 2.02358C4.57807 2.27888 2.27889 4.57807 2.02358 7.46446L0.281623 7.68117C-0.0938759 7.72788 -0.0938737 8.27212 0.281626 8.31883L2.02358 8.53554C2.27889 11.4219 4.57807 13.7211 7.46446 13.9764L7.68117 15.7184C7.72788 16.0939 8.27212 16.0939 8.31883 15.7184L8.53554 13.9764C11.4219 13.7211 13.7211 11.4219 13.9764 8.53554L15.7184 8.31883C16.0939 8.27212 16.0939 7.72788 15.7184 7.68117L13.9764 7.46446C13.7211 4.57806 11.4219 2.27888 8.53554 2.02357L8.31883 0.281623ZM8.53554 2.02357C8.35911 2.00797 8.18049 2 8.00001 2C7.81952 2 7.64089 2.00797 7.46446 2.02358L7.01639 5.62535L4.76773 4.31684C4.46918 4.08435 4.08435 4.46918 4.31684 4.76773L5.62535 7.01639L2.02358 7.46446C2.00798 7.64089 2.00001 7.81951 2.00001 8C2.00001 8.18048 2.00798 8.35911 2.02358 8.53554L5.62535 8.98361L4.31684 11.2323C4.08435 11.5308 4.46918 11.9157 4.76773 11.6832L7.01639 10.3747L7.46446 13.9764C7.64089 13.992 7.81952 14 8.00001 14C8.18049 14 8.35911 13.992 8.53554 13.9764L8.98361 10.3747L11.2323 11.6832C11.5308 11.9157 11.9157 11.5308 11.6832 11.2323L10.3747 8.98361L13.9764 8.53554C13.992 8.35911 14 8.18048 14 8C14 7.81952 13.992 7.64089 13.9764 7.46446L10.3747 7.01639L11.6832 4.76773C11.9157 4.46918 11.5308 4.08435 11.2323 4.31684L8.98361 5.62535L8.53554 2.02357Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M2.34315 2.34315C5.46734 -0.781049 10.5327 -0.781049 13.6569 2.34315L10 6L8.70711 4.70711C8.31658 4.31658 7.68342 4.31658 7.2929 4.70711L6 6L2.34315 2.34315ZM8 8L6 6L4.70711 7.29289C4.31658 7.68342 4.31658 8.31658 4.70711 8.70711L6 10L2.34315 13.6569C5.46734 16.781 10.5327 16.781 13.6569 13.6569L10 10L11.2929 8.70711C11.6834 8.31658 11.6834 7.68342 11.2929 7.29289L10 6L8 8ZM8 8L10 10L8.70711 11.2929C8.31658 11.6834 7.68342 11.6834 7.2929 11.2929L6 10L8 8Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M8.53781 0.379548C8.35784 -0.126516 7.64216 -0.126516 7.4622 0.379548L6.25252 3.78121L2.99181 2.23124C2.50672 2.00065 2.00065 2.50672 2.23124 2.99181L3.78121 6.25252L0.379548 7.4622C-0.126516 7.64216 -0.126516 8.35784 0.379548 8.53781L3.78121 9.74748L2.23124 13.0082C2.00065 13.4933 2.50672 13.9993 2.99181 13.7688L6.25252 12.2188L7.4622 15.6205C7.64216 16.1265 8.35784 16.1265 8.53781 15.6205L9.74748 12.2188L13.0082 13.7688C13.4933 13.9993 13.9993 13.4933 13.7688 13.0082L12.2188 9.74748L15.6205 8.53781C16.1265 8.35784 16.1265 7.64216 15.6205 7.4622L12.2188 6.25252L13.7688 2.99181C13.9993 2.50672 13.4933 2.00065 13.0082 2.23124L9.74748 3.78121L8.53781 0.379548ZM10.9999 7.99994L7.99994 4.99994L4.99994 7.99994L7.99994 10.9999L10.9999 7.99994Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M2.34315 2.34315C5.46734 -0.781049 10.5327 -0.781049 13.6569 2.34315L10.8284 5.17157C10.1046 4.44772 9.10457 4 8 4C6.89543 4 5.89543 4.44772 5.17157 5.17157L2.34315 2.34315ZM8 8L5.17157 5.17157C4.44772 5.89543 4 6.89543 4 8C4 9.10457 4.44772 10.1046 5.17157 10.8284L2.34315 13.6569C5.46734 16.781 10.5327 16.781 13.6569 13.6569L10.8284 10.8284C11.5523 10.1046 12 9.10457 12 8C12 6.89543 11.5523 5.89543 10.8284 5.17157L8 8ZM8 8L10.8284 10.8284C10.1046 11.5523 9.10457 12 8 12C6.89543 12 5.89543 11.5523 5.17157 10.8284L8 8Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M8.31882 0.281623C8.27211 -0.0938759 7.72788 -0.0938737 7.68116 0.281626L7.34298 2.99999H3V7.34299L0.281616 7.68117C-0.0938835 7.72788 -0.0938814 8.27212 0.281618 8.31883L3 8.65701V13H7.34298L7.68116 15.7184C7.72788 16.0939 8.27211 16.0939 8.31882 15.7184L8.657 13H13V8.65701L15.7184 8.31883C16.0939 8.27212 16.0939 7.72788 15.7184 7.68117L13 7.34299V2.99999H8.657L8.31882 0.281623ZM8.657 2.99999H7.34298L7.01638 5.62535L4.76772 4.31684C4.46917 4.08435 4.08434 4.46918 4.31683 4.76773L5.62534 7.01639L3 7.34299V8.65701L5.62534 8.98361L4.31683 11.2323C4.08434 11.5308 4.46917 11.9157 4.76772 11.6832L7.01638 10.3747L7.34298 13H8.657L8.98361 10.3747L11.2323 11.6832C11.5308 11.9157 11.9156 11.5308 11.6832 11.2323L10.3746 8.98361L13 8.65701V7.34299L10.3746 7.01639L11.6832 4.76773C11.9156 4.46918 11.5308 4.08435 11.2323 4.31684L8.98361 5.62535L8.657 2.99999Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M4.4 2C3.07452 2 2 3.07452 2 4.4V11.6C2 12.9255 3.07452 14 4.4 14H11.6C12.9255 14 14 12.9255 14 11.6V4.4C14 3.07452 12.9255 2 11.6 2H4.4ZM8 11C9.65685 11 11 9.65685 11 8C11 6.34315 9.65685 5 8 5C6.34315 5 5 6.34315 5 8C5 9.65685 6.34315 11 8 11Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M8 13C10.7614 13 13 10.7614 13 8C13 5.23858 10.7614 3 8 3C5.23858 3 3 5.23858 3 8C3 10.7614 5.23858 13 8 13ZM8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
{
d: 'M11 8C11 9.65685 9.65685 11 8 11C6.34315 11 5 9.65685 5 8C5 6.34315 6.34315 5 8 5C9.65685 5 11 6.34315 11 8Z',
},
],
[
{
d: 'M2 3C5.31371 3 8 5.68629 8 9C8 5.68629 10.6863 3 14 3L14 9C14 12.3137 11.3137 15 8 15C4.68629 15 2 12.3137 2 9V3Z',
},
],
[
{
d: 'M7.46226 0.379609C7.64222 -0.126455 8.3579 -0.126455 8.53787 0.379609L9.74754 3.78127L13.0082 2.2313C13.4933 2.00071 13.9994 2.50678 13.7688 2.99187L12.2189 6.25258L15.6205 7.46226C16.1266 7.64222 16.1266 8.3579 15.6205 8.53787L12.2189 9.74754L13.7688 13.0082C13.9994 13.4933 13.4933 13.9994 13.0082 13.7688L9.74754 12.2189L8.53787 15.6205C8.3579 16.1266 7.64222 16.1266 7.46226 15.6205L6.25258 12.2189L2.99187 13.7688C2.50678 13.9994 2.00071 13.4933 2.2313 13.0082L3.78127 9.74754L0.379609 8.53787C-0.126455 8.3579 -0.126455 7.64222 0.379609 7.46226L3.78127 6.25258L2.2313 2.99187C2.00071 2.50678 2.50678 2.00071 2.99187 2.2313L6.25258 3.78127L7.46226 0.379609Z',
},
],
[
{
d: 'M8 10C9.10457 10 10 9.10457 10 8C10 6.89543 9.10457 6 8 6C6.89543 6 6 6.89543 6 8C6 9.10457 6.89543 10 8 10Z',
},
{
d: 'M16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8ZM8.67438 3.27934L12.7207 7.32562C13.0931 7.69807 13.0931 8.30193 12.7207 8.67438L8.67438 12.7207C8.30193 13.0931 7.69807 13.0931 7.32562 12.7207L3.27934 8.67438C2.90689 8.30193 2.90689 7.69807 3.27934 7.32562L7.32562 3.27934C7.69807 2.90689 8.30193 2.90689 8.67438 3.27934Z',
fillRule: 'evenodd',
clipRule: 'evenodd',
},
],
[
{
d: 'M13.2323 2.31683C13.5308 2.08434 13.9156 2.46917 13.6832 2.76772L10.3747 7.01637L12.8899 7.68116C13.2654 7.72787 13.2654 8.2721 12.89 8.31882L10.3747 8.9836L13.6832 13.2323C13.9156 13.5308 13.5308 13.9156 13.2323 13.6832L8.98361 10.3746L8.31883 12.8899C8.27212 13.2654 7.72788 13.2654 7.68117 12.8899L7.01639 10.3746L2.76773 13.6832C2.46918 13.9156 2.08435 13.5308 2.31684 13.2323L5.62535 8.9836L3.11005 8.31882C2.73455 8.27211 2.73455 7.72787 3.11005 7.68116L5.62535 7.01637L2.31684 2.76772C2.08435 2.46917 2.46918 2.08434 2.76773 2.31682L7.01639 5.62533L7.68117 3.11004C7.72788 2.73454 8.27212 2.73454 8.31883 3.11004L8.98361 5.62533L13.2323 2.31683Z',
},
],
[
{
d: 'M7.68116 0.281626C7.72788 -0.0938737 8.27211 -0.0938759 8.31882 0.281623L8.98361 5.62535L11.2323 4.31684C11.5308 4.08435 11.9156 4.46918 11.6832 4.76773L10.3746 7.01639L15.7184 7.68117C16.0939 7.72788 16.0939 8.27212 15.7184 8.31883L10.3746 8.98361L11.6832 11.2323C11.9156 11.5308 11.5308 11.9157 11.2323 11.6832L8.98361 10.3747L8.31882 15.7184C8.27211 16.0939 7.72788 16.0939 7.68116 15.7184L7.01638 10.3747L4.76772 11.6832C4.46917 11.9157 4.08434 11.5308 4.31683 11.2323L5.62534 8.98361L0.281618 8.31883C-0.0938814 8.27212 -0.0938835 7.72788 0.281616 7.68117L5.62534 7.01639L4.31683 4.76773C4.08434 4.46918 4.46917 4.08435 4.76772 4.31684L7.01638 5.62535L7.68116 0.281626Z',
},
],
[
{
d: 'M14 13C10.6863 13 8 10.3137 8 7C8 10.3137 5.31371 13 2 13L2 7C2 3.68629 4.68629 0.999999 8 1C11.3137 0.999999 14 3.68629 14 7L14 13Z',
},
],
]

View File

@@ -0,0 +1,162 @@
import React, { memo, useMemo } from 'react'
import { useIsDarkMode } from 'state/user/hooks'
import { blurs, UniconAttributeData, UniconAttributes, UniconAttributesToIndices } from './types'
import { deriveUniconAttributeIndices, getUniconAttributeData, isEthAddress } from './utils'
const ORIGINAL_CONTAINER_SIZE = 36
const EMBLEM_XY_SHIFT = 10
function PathMask({
id,
paths,
scale,
shift = 0,
}: {
id: string
paths: React.SVGProps<SVGPathElement>[]
scale: number
shift?: number
}) {
return (
<mask id={id}>
<rect x="0" y="0" width="100%" height="100%" fill="white" />
<g transform={`scale(${scale}) \n translate(${shift}, ${shift})`}>
{paths.map((pathProps) => (
<path key={pathProps.d as string} {...pathProps} fill="black" />
))}
</g>
</mask>
)
}
type UniconMaskProps = { maskId: string; attributeData: UniconAttributeData; size: number }
function UniconMask({ maskId, attributeData, size }: UniconMaskProps) {
const shapeMaskId = `shape-${maskId}`
const containerMaskId = `container-${maskId}`
return (
<defs>
<PathMask
id={containerMaskId}
paths={attributeData[UniconAttributes.Container]}
scale={size / ORIGINAL_CONTAINER_SIZE}
/>
<PathMask
id={shapeMaskId}
paths={attributeData[UniconAttributes.Shape]}
scale={size / ORIGINAL_CONTAINER_SIZE}
shift={EMBLEM_XY_SHIFT}
/>
<mask id={maskId}>
<g fill="white">
<g mask={`url(#${shapeMaskId})`}>
<g transform={`scale(${size / ORIGINAL_CONTAINER_SIZE})`}>
{attributeData[UniconAttributes.Container].map((pathProps) => (
<path key={pathProps.d as string} {...pathProps} />
))}
</g>
</g>
<g mask={`url(#${containerMaskId})`}>
<g
transform={`scale(${size / ORIGINAL_CONTAINER_SIZE})
translate(10, 10)`}
>
{attributeData[UniconAttributes.Shape].map((pathProps) => (
<path key={pathProps.d as string} {...pathProps} />
))}
</g>
</g>
</g>
</mask>
</defs>
)
}
type UniconGradientProps = { gradientId: string; attributeData: UniconAttributeData }
function UniconGradient({ gradientId, attributeData }: UniconGradientProps) {
return (
<linearGradient id={gradientId}>
<stop offset="0%" stopColor={attributeData[UniconAttributes.GradientStart]} />
<stop offset="100%" stopColor={attributeData[UniconAttributes.GradientEnd]} />
</linearGradient>
)
}
function UniconBlur({ blurId, size }: { blurId: string; size: number }) {
return (
<filter id={blurId} x="-50%" y="-50%" height="200%" width="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation={size / 3} />
</filter>
)
}
function UniconSvg({
attributeIndices,
size,
address,
}: {
attributeIndices: UniconAttributesToIndices
size: number
address: string
mobile?: boolean
}) {
const isDarkMode = useIsDarkMode()
const attributeData = useMemo(() => getUniconAttributeData(attributeIndices), [attributeIndices])
const gradientId = `gradient${address + size}`
const maskId = `mask${address + size}`
const blurId = `blur${address + size}`
const svgProps = {
viewBox: `0 0 ${size} ${size}`,
}
if (!attributeIndices || !attributeData) return null
return (
<svg {...svgProps}>
<defs>
<UniconMask maskId={maskId} attributeData={attributeData} size={size} />
<UniconGradient gradientId={gradientId} attributeData={attributeData} />
<UniconBlur blurId={blurId} size={size} />
</defs>
<g mask={`url(#${maskId})`}>
<rect x="0" y="0" width="100%" height="100%" fill={`url(#${gradientId})`} />
{!isDarkMode && <rect x="0" y="0" width="100%" height="100%" fill="black" opacity={0.08} />}
<ellipse
cx={size / 2}
cy={0}
rx={size / 2}
ry={size / 2}
fill={blurs[attributeIndices[UniconAttributes.GradientStart]]}
filter={`url(#${blurId})`}
/>
</g>
</svg>
)
}
interface Props {
address: string
size?: number
randomSeed?: number
border?: boolean
mobile?: boolean
}
function _Unicon({ address, size = 24, randomSeed = 0, mobile }: Props) {
const attributeIndices = useMemo(() => deriveUniconAttributeIndices(address, randomSeed), [address, randomSeed])
if (!address || !isEthAddress(address) || !attributeIndices) return null
return (
<div style={{ height: size, width: size, position: 'relative' }}>
<div style={{ height: size, width: size, overflow: 'visible', position: 'absolute' }}>
<UniconSvg attributeIndices={attributeIndices} size={size} address={address} mobile={mobile} />
</div>
</div>
)
}
export const Unicon = memo(_Unicon)

View File

@@ -0,0 +1,76 @@
import { svgPaths as containerPaths } from './Container'
import { svgPaths as emblemPaths } from './Emblem'
export enum UniconAttributes {
GradientStart = 0,
GradientEnd = 1,
Container = 2,
Shape = 3,
}
export const UniconAttributesArray: UniconAttributes[] = [
UniconAttributes.GradientStart,
UniconAttributes.GradientEnd,
UniconAttributes.Container,
UniconAttributes.Shape,
]
export interface UniconAttributesToIndices {
[UniconAttributes.GradientStart]: number
[UniconAttributes.GradientEnd]: number
[UniconAttributes.Container]: number
[UniconAttributes.Shape]: number
}
export interface UniconAttributeData {
[UniconAttributes.GradientStart]: string
[UniconAttributes.GradientEnd]: string
[UniconAttributes.Container]: React.SVGProps<SVGPathElement>[]
[UniconAttributes.Shape]: React.SVGProps<SVGPathElement>[]
}
export const gradientStarts = [
'#6100FF',
'#5065FD',
'#36DBFF',
'#5CFE9D',
'#B1F13C',
'#F9F40B',
'#FF6F1E',
'#F14544',
'#FC72FF',
'#C0C0C0',
]
export const blurs = [
'#D3EBA3',
'#F06DF3',
'#9D99F5',
'#EDE590',
'#B0EDFE',
'#FBAA7F',
'#C8BB9B',
'#9D99F5',
'#A26AF3',
'#D3EBA3',
]
export const gradientEnds = [
'#D0B2F3',
'#BDB8FA',
'#63CDE8',
'#76D191',
'#9BCD46',
'#EDE590',
'#FBAA7F',
'#FEA79B',
'#F5A1F5',
'#B8C3B7',
]
export const UniconNumOptions = {
[UniconAttributes.GradientStart]: gradientStarts.length,
[UniconAttributes.GradientEnd]: gradientEnds.length,
[UniconAttributes.Container]: containerPaths.length,
[UniconAttributes.Shape]: emblemPaths.length,
}

View File

@@ -0,0 +1,50 @@
import { isAddress } from 'ethers/lib/utils'
import { svgPaths as containerPaths } from './Container'
import { svgPaths as emblemPaths } from './Emblem'
import {
gradientEnds,
gradientStarts,
UniconAttributeData,
UniconAttributes,
UniconAttributesArray,
UniconAttributesToIndices,
UniconNumOptions,
} from './types'
const NUM_CHARS_TO_USE_PER_ATTRIBUTE = 2
export const isEthAddress = (address: string) => {
return address.startsWith('0x') && isAddress(address.toLowerCase())
}
export const deriveUniconAttributeIndices = (
address: string,
randomSeed = 0
): UniconAttributesToIndices | undefined => {
if (!isEthAddress(address)) return
const hexAddr = address.slice(-40)
const newIndices = {
[UniconAttributes.GradientStart]: 0,
[UniconAttributes.GradientEnd]: 0,
[UniconAttributes.Container]: 0,
[UniconAttributes.Shape]: 0,
} as UniconAttributesToIndices
for (const a of UniconAttributesArray) {
const optionHex = hexAddr.slice(NUM_CHARS_TO_USE_PER_ATTRIBUTE * a, NUM_CHARS_TO_USE_PER_ATTRIBUTE * (a + 1))
const optionDec = parseInt(optionHex, 16) + randomSeed
const optionIndex = optionDec % UniconNumOptions[a]
newIndices[a] = optionIndex
}
return newIndices
}
export const getUniconAttributeData = (attributeIndices: UniconAttributesToIndices): UniconAttributeData => {
return {
[UniconAttributes.GradientStart]: gradientStarts[attributeIndices[UniconAttributes.GradientStart]],
[UniconAttributes.GradientEnd]: gradientEnds[attributeIndices[UniconAttributes.GradientEnd]],
[UniconAttributes.Container]: containerPaths[attributeIndices[UniconAttributes.Container]],
[UniconAttributes.Shape]: emblemPaths[attributeIndices[UniconAttributes.Shape]],
} as UniconAttributeData
}

View File

@@ -9,8 +9,6 @@ import Tooltip from 'components/Tooltip'
import { getConnection } from 'connection/utils'
import { getChainInfoOrDefault } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { BaseVariant } from 'featureFlags'
import { useFiatOnrampFlag } from 'featureFlags/flags/fiatOnramp'
import useCopyClipboard from 'hooks/useCopyClipboard'
import useStablecoinPrice from 'hooks/useStablecoinPrice'
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
@@ -126,15 +124,6 @@ const FiatOnrampAvailabilityExternalLink = styled(ExternalLink)`
margin-left: 6px;
width: 14px;
`
const FlexContainer = styled.div`
display: flex;
`
const StatusWrapper = styled.div`
display: inline-block;
margin-top: 4px;
width: 70%;
`
const TruncatedTextStyle = css`
text-overflow: ellipsis;
@@ -142,8 +131,14 @@ const TruncatedTextStyle = css`
white-space: nowrap;
`
const AccountNamesWrapper = styled.div`
const FlexContainer = styled.div`
${TruncatedTextStyle}
padding-right: 4px;
display: inline-flex;
`
const AccountNamesWrapper = styled.div`
min-width: 0;
margin-right: 8px;
`
@@ -228,7 +223,6 @@ const AuthenticatedHeader = () => {
closeModal()
}, [clearCollectionFilters, closeModal, navigate, resetSellAssets, setSellPageState])
const fiatOnrampFlag = useFiatOnrampFlag()
// animate the border of the buy crypto button when a user navigates here from the feature announcement
// can be removed when components/FiatOnrampAnnouncment.tsx is no longer used
const [acknowledgements, acknowledge] = useFiatOnrampAck()
@@ -280,19 +274,17 @@ const AuthenticatedHeader = () => {
return (
<>
<HeaderWrapper>
<StatusWrapper>
<FlexContainer>
<StatusIcon connectionType={connectionType} size={24} />
{ENSName ? (
<AccountNamesWrapper>
<ENSNameContainer>{ENSName}</ENSNameContainer>
<AccountContainer>{account && shortenAddress(account, 2, 4)}</AccountContainer>
</AccountNamesWrapper>
) : (
<ThemedText.SubHeader marginTop="2.5px">{account && shortenAddress(account, 2, 4)}</ThemedText.SubHeader>
)}
</FlexContainer>
</StatusWrapper>
<FlexContainer>
<StatusIcon connectionType={connectionType} size={24} />
{ENSName ? (
<AccountNamesWrapper>
<ENSNameContainer>{ENSName}</ENSNameContainer>
<AccountContainer>{account && shortenAddress(account, 2, 4)}</AccountContainer>
</AccountNamesWrapper>
) : (
<ThemedText.SubHeader marginTop="2.5px">{account && shortenAddress(account, 2, 4)}</ThemedText.SubHeader>
)}
</FlexContainer>
<IconContainer>
<IconButton onClick={copy} Icon={Copy}>
{isCopied ? <Trans>Copied!</Trans> : <Trans>Copy</Trans>}
@@ -321,47 +313,43 @@ const AuthenticatedHeader = () => {
>
<Trans>View and sell NFTs</Trans>
</ProfileButton>
{fiatOnrampFlag === BaseVariant.Enabled && (
<>
<BuyCryptoButton
$animateBorder={animateBuyCryptoButtonBorder}
size={ButtonSize.medium}
emphasis={ButtonEmphasis.medium}
onClick={handleBuyCryptoClick}
disabled={disableBuyCryptoButton}
>
{error ? (
<ThemedText.BodyPrimary>{error}</ThemedText.BodyPrimary>
<BuyCryptoButton
$animateBorder={animateBuyCryptoButtonBorder}
size={ButtonSize.medium}
emphasis={ButtonEmphasis.medium}
onClick={handleBuyCryptoClick}
disabled={disableBuyCryptoButton}
>
{error ? (
<ThemedText.BodyPrimary>{error}</ThemedText.BodyPrimary>
) : (
<>
{fiatOnrampAvailabilityLoading ? (
<StyledLoadingButtonSpinner />
) : (
<>
{fiatOnrampAvailabilityLoading ? (
<StyledLoadingButtonSpinner />
) : (
<CreditCard height="20px" width="20px" />
)}{' '}
<Trans>Buy crypto</Trans>
</>
)}
</BuyCryptoButton>
{Boolean(!fiatOnrampAvailable && fiatOnrampAvailabilityChecked) && (
<FiatOnrampNotAvailableText marginTop="8px">
<Trans>Not available in your region</Trans>
<Tooltip
show={showFiatOnrampUnavailableTooltip}
text={<Trans>Moonpay is not available in some regions. Click to learn more.</Trans>}
>
<FiatOnrampAvailabilityExternalLink
onMouseEnter={openFiatOnrampUnavailableTooltip}
onMouseLeave={closeFiatOnrampUnavailableTooltip}
style={{ color: 'inherit' }}
href="https://support.uniswap.org/hc/en-us/articles/11306664890381-Why-isn-t-MoonPay-available-in-my-region-"
>
<StyledInfoIcon />
</FiatOnrampAvailabilityExternalLink>
</Tooltip>
</FiatOnrampNotAvailableText>
)}
</>
<CreditCard height="20px" width="20px" />
)}{' '}
<Trans>Buy crypto</Trans>
</>
)}
</BuyCryptoButton>
{Boolean(!fiatOnrampAvailable && fiatOnrampAvailabilityChecked) && (
<FiatOnrampNotAvailableText marginTop="8px">
<Trans>Not available in your region</Trans>
<Tooltip
show={showFiatOnrampUnavailableTooltip}
text={<Trans>Moonpay is not available in some regions. Click to learn more.</Trans>}
>
<FiatOnrampAvailabilityExternalLink
onMouseEnter={openFiatOnrampUnavailableTooltip}
onMouseLeave={closeFiatOnrampUnavailableTooltip}
style={{ color: 'inherit' }}
href="https://support.uniswap.org/hc/en-us/articles/11306664890381-Why-isn-t-MoonPay-available-in-my-region-"
>
<StyledInfoIcon />
</FiatOnrampAvailabilityExternalLink>
</Tooltip>
</FiatOnrampNotAvailableText>
)}
{isUnclaimed && (
<UNIButton onClick={openClaimModal} size={ButtonSize.medium} emphasis={ButtonEmphasis.medium}>

View File

@@ -4,7 +4,6 @@ import styled from 'styled-components/macro'
const Menu = styled.div`
width: 100%;
height: 100%;
font-size: 16px;
overflow: auto;
max-height: 450px;
@@ -58,8 +57,9 @@ const StyledChevron = styled(ChevronLeft)`
const BackSection = styled.div`
position: absolute;
background-color: ${({ theme }) => theme.backgroundSurface};
width: 99%;
padding: 0 16px 16px 16px;
width: fill-available;
margin: 0px 2vw 0px 0px;
padding: 0px 0px 2vh 0px;
color: ${({ theme }) => theme.textSecondary};
cursor: default;
display: flex;

View File

@@ -22,9 +22,6 @@ jest.mock('.../../state/application/hooks', () => {
useToggleWalletModal: () => {
return
},
useToggleMetaMaskConnectionErrorModal: () => {
return
},
}
})
@@ -73,8 +70,8 @@ it('loads Wallet Modal on desktop', async () => {
it('loads Wallet Modal on desktop with generic Injected', async () => {
jest.spyOn(connectionUtils, 'getIsInjected').mockReturnValue(true)
jest.spyOn(connectionUtils, 'getHasMetaMaskExtensionInstalled').mockReturnValue(false)
jest.spyOn(connectionUtils, 'getHasCoinbaseExtensionInstalled').mockReturnValue(false)
jest.spyOn(connectionUtils, 'getIsMetaMaskWallet').mockReturnValue(false)
jest.spyOn(connectionUtils, 'getIsCoinbaseWallet').mockReturnValue(false)
render(<WalletModal pendingTransactions={[]} confirmedTransactions={[]} />)
expect(screen.getByText('Browser Wallet')).toBeInTheDocument()
@@ -85,8 +82,8 @@ it('loads Wallet Modal on desktop with generic Injected', async () => {
it('loads Wallet Modal on desktop with MetaMask installed', async () => {
jest.spyOn(connectionUtils, 'getIsInjected').mockReturnValue(true)
jest.spyOn(connectionUtils, 'getHasMetaMaskExtensionInstalled').mockReturnValue(true)
jest.spyOn(connectionUtils, 'getHasCoinbaseExtensionInstalled').mockReturnValue(false)
jest.spyOn(connectionUtils, 'getIsMetaMaskWallet').mockReturnValue(true)
jest.spyOn(connectionUtils, 'getIsCoinbaseWallet').mockReturnValue(false)
render(<WalletModal pendingTransactions={[]} confirmedTransactions={[]} />)
expect(screen.getByText('MetaMask')).toBeInTheDocument()
@@ -99,8 +96,8 @@ it('loads Wallet Modal on mobile', async () => {
UserAgentMock.isMobile = true
jest.spyOn(connectionUtils, 'getIsInjected').mockReturnValue(false)
jest.spyOn(connectionUtils, 'getHasMetaMaskExtensionInstalled').mockReturnValue(false)
jest.spyOn(connectionUtils, 'getHasCoinbaseExtensionInstalled').mockReturnValue(false)
jest.spyOn(connectionUtils, 'getIsMetaMaskWallet').mockReturnValue(false)
jest.spyOn(connectionUtils, 'getIsCoinbaseWallet').mockReturnValue(false)
render(<WalletModal pendingTransactions={[]} confirmedTransactions={[]} />)
expect(screen.getByText('Open in Coinbase Wallet')).toBeInTheDocument()
@@ -112,8 +109,8 @@ it('loads Wallet Modal on MetaMask browser', async () => {
UserAgentMock.isMobile = true
jest.spyOn(connectionUtils, 'getIsInjected').mockReturnValue(true)
jest.spyOn(connectionUtils, 'getHasMetaMaskExtensionInstalled').mockReturnValue(true)
jest.spyOn(connectionUtils, 'getHasCoinbaseExtensionInstalled').mockReturnValue(false)
jest.spyOn(connectionUtils, 'getIsMetaMaskWallet').mockReturnValue(true)
jest.spyOn(connectionUtils, 'getIsCoinbaseWallet').mockReturnValue(false)
render(<WalletModal pendingTransactions={[]} confirmedTransactions={[]} />)
expect(screen.getByText('MetaMask')).toBeInTheDocument()
@@ -124,8 +121,8 @@ it('loads Wallet Modal on Coinbase Wallet browser', async () => {
UserAgentMock.isMobile = true
jest.spyOn(connectionUtils, 'getIsInjected').mockReturnValue(true)
jest.spyOn(connectionUtils, 'getHasMetaMaskExtensionInstalled').mockReturnValue(false)
jest.spyOn(connectionUtils, 'getHasCoinbaseExtensionInstalled').mockReturnValue(true)
jest.spyOn(connectionUtils, 'getIsMetaMaskWallet').mockReturnValue(false)
jest.spyOn(connectionUtils, 'getIsCoinbaseWallet').mockReturnValue(true)
render(<WalletModal pendingTransactions={[]} confirmedTransactions={[]} />)
expect(screen.getByText('Coinbase Wallet')).toBeInTheDocument()

View File

@@ -10,9 +10,9 @@ import { networkConnection } from 'connection'
import {
getConnection,
getConnectionName,
getHasCoinbaseExtensionInstalled,
getHasMetaMaskExtensionInstalled,
getIsCoinbaseWallet,
getIsInjected,
getIsMetaMaskWallet,
} from 'connection/utils'
import usePrevious from 'hooks/usePrevious'
import { useCallback, useEffect, useState } from 'react'
@@ -253,8 +253,8 @@ export default function WalletModal({
function getOptions() {
const isInjected = getIsInjected()
const hasMetaMaskExtension = getHasMetaMaskExtensionInstalled()
const hasCoinbaseExtension = getHasCoinbaseExtensionInstalled()
const hasMetaMaskExtension = getIsMetaMaskWallet()
const hasCoinbaseExtension = getIsCoinbaseWallet()
const isCoinbaseWalletBrowser = isMobile && hasCoinbaseExtension
const isMetaMaskBrowser = isMobile && hasMetaMaskExtension

View File

@@ -1,7 +1,6 @@
import { useWeb3React, Web3ReactHooks, Web3ReactProvider } from '@web3-react/core'
import { Connector } from '@web3-react/types'
import { Connection } from 'connection'
import { setMetMaskErrorHandler } from 'connection'
import { getConnectionName } from 'connection/utils'
import { isSupportedChain } from 'constants/chains'
import { RPC_PROVIDERS } from 'constants/providers'
@@ -9,19 +8,8 @@ import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/tra
import useEagerlyConnect from 'hooks/useEagerlyConnect'
import useOrderedConnections from 'hooks/useOrderedConnections'
import { ReactNode, useEffect, useMemo } from 'react'
import { useToggleMetaMaskConnectionErrorModal } from 'state/application/hooks'
export default function Web3Provider({ children }: { children: ReactNode }) {
// https://github.com/MetaMask/metamask-extension/issues/13375
const toggleMetaMaskConnectionErrorModal = useToggleMetaMaskConnectionErrorModal()
useEffect(() => {
setMetMaskErrorHandler((error) => {
if (error.code === 1013) {
toggleMetaMaskConnectionErrorModal()
}
})
}, [toggleMetaMaskConnectionErrorModal])
useEagerlyConnect()
const connections = useOrderedConnections()
const connectors: [Connector, Web3ReactHooks][] = connections.map(({ hooks, connector }) => [connector, hooks])

View File

@@ -1,4 +1,4 @@
import { Trans } from '@lingui/macro'
import { t, Trans } from '@lingui/macro'
import { sendAnalyticsEvent, TraceEvent } from '@uniswap/analytics'
import { BrowserEvent, InterfaceElementName, InterfaceEventName } from '@uniswap/analytics-events'
import { useWeb3React } from '@web3-react/core'
@@ -242,6 +242,7 @@ function Web3StatusInner() {
const chevronProps = {
...CHEVRON_PROPS,
color: theme.textSecondary,
'aria-label': walletIsOpen ? t`Close wallet connection options` : t`Open wallet connection options`,
}
return (
@@ -251,7 +252,7 @@ function Web3StatusInner() {
pending={hasPendingTransactions}
isClaimAvailable={isClaimAvailable}
>
{!hasPendingTransactions && <StatusIcon size={24} connectionType={connectionType} />}
{!hasPendingTransactions && <StatusIcon enableInfotips={true} size={24} connectionType={connectionType} />}
{hasPendingTransactions ? (
<RowBetween>
<Text>
@@ -272,6 +273,7 @@ function Web3StatusInner() {
...CHEVRON_PROPS,
color: theme.accentAction,
'data-testid': 'navbar-wallet-dropdown',
'aria-label': walletIsOpen ? t`Close wallet connection options` : t`Open wallet connection options`,
}
return (
<TraceEvent

View File

@@ -26,16 +26,17 @@ import {
getTokenAddress,
} from 'lib/utils/analytics'
import { useCallback, useState } from 'react'
import { useToggleWalletModal } from 'state/application/hooks'
import { useIsDarkMode } from 'state/user/hooks'
import { computeRealizedPriceImpact } from 'utils/prices'
import { switchChain } from 'utils/switchChain'
import { useSyncWidgetInputs } from './inputs'
import { DefaultTokens, useSyncWidgetInputs } from './inputs'
import { useSyncWidgetSettings } from './settings'
import { DARK_THEME, LIGHT_THEME } from './theme'
import { useSyncWidgetTransactions } from './transactions'
export const WIDGET_WIDTH = 360
export const DEFAULT_WIDGET_WIDTH = 360
const WIDGET_ROUTER_URL = 'https://api.uniswap.org/v1/'
@@ -44,19 +45,34 @@ function useWidgetTheme() {
}
interface WidgetProps {
token?: Currency
onTokenChange?: (token: Currency) => void
defaultTokens: DefaultTokens
width?: number | string
onDefaultTokenChange?: (token: Currency) => void
onReviewSwapClick?: OnReviewSwapClick
}
export default function Widget({ token, onTokenChange, onReviewSwapClick }: WidgetProps) {
const { connector, provider } = useWeb3React()
export default function Widget({
defaultTokens,
width = DEFAULT_WIDGET_WIDTH,
onDefaultTokenChange,
onReviewSwapClick,
}: WidgetProps) {
const { connector, provider, chainId } = useWeb3React()
const locale = useActiveLocale()
const theme = useWidgetTheme()
const { inputs, tokenSelector } = useSyncWidgetInputs({ token, onTokenChange })
const { inputs, tokenSelector } = useSyncWidgetInputs({
defaultTokens,
onDefaultTokenChange,
})
const { settings } = useSyncWidgetSettings()
const { transactions } = useSyncWidgetTransactions()
const toggleWalletModal = useToggleWalletModal()
const onConnectWalletClick = useCallback(() => {
toggleWalletModal()
return false // prevents the in-widget wallet modal from opening
}, [toggleWalletModal])
const onSwitchChain = useCallback(
// TODO(WEB-1757): Widget should not break if this rejects - upstream the catch to ignore it.
({ chainId }: AddEthereumChainParameter) => switchChain(connector, Number(chainId)).catch(() => undefined),
@@ -152,8 +168,9 @@ export default function Widget({ token, onTokenChange, onReviewSwapClick }: Widg
routerUrl={WIDGET_ROUTER_URL}
locale={locale}
theme={theme}
width={WIDGET_WIDTH}
// defaultChainId is excluded - it is always inferred from the passed provider
width={width}
defaultChainId={chainId}
onConnectWalletClick={onConnectWalletClick}
provider={provider}
onSwitchChain={onSwitchChain}
tokenList={EMPTY_TOKEN_LIST} // prevents loading the default token list, as we use our own token selector UI
@@ -166,13 +183,16 @@ export default function Widget({ token, onTokenChange, onReviewSwapClick }: Widg
onSwapApprove={onApproveToken}
onInitialSwapQuote={onInitialSwapQuote}
onSwapPriceUpdateAck={onSwapPriceUpdateAck}
onError={(error, errorInfo) => {
sendAnalyticsEvent(SwapEventName.SWAP_ERROR, { error, errorInfo, ...trace })
}}
/>
{tokenSelector}
</>
)
}
export function WidgetSkeleton() {
export function WidgetSkeleton({ width = DEFAULT_WIDGET_WIDTH }: { width?: number | string }) {
const theme = useWidgetTheme()
return <SwapWidgetSkeleton theme={theme} width={WIDGET_WIDTH} />
return <SwapWidgetSkeleton theme={theme} width={width} />
}

View File

@@ -1,17 +1,20 @@
import { sendAnalyticsEvent, useTrace } from '@uniswap/analytics'
import { InterfaceSectionName, SwapEventName } from '@uniswap/analytics-events'
import { Currency, Field, SwapController, SwapEventHandlers, TradeType } from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core'
import CurrencySearchModal from 'components/SearchModal/CurrencySearchModal'
import usePrevious from 'hooks/usePrevious'
import { useCallback, useEffect, useMemo, useState } from 'react'
const EMPTY_AMOUNT = ''
type SwapValue = Required<SwapController>['value']
type SwapTokens = Pick<SwapValue, Field.INPUT | Field.OUTPUT> & { default?: Currency }
export type DefaultTokens = Partial<SwapTokens>
function includesDefaultToken(tokens: SwapTokens) {
if (!tokens.default) return true
return tokens[Field.INPUT]?.equals(tokens.default) || tokens[Field.OUTPUT]?.equals(tokens.default)
function missingDefaultToken(tokens: SwapTokens) {
if (!tokens.default) return false
return !tokens[Field.INPUT]?.equals(tokens.default) && !tokens[Field.OUTPUT]?.equals(tokens.default)
}
/**
@@ -20,27 +23,42 @@ function includesDefaultToken(tokens: SwapTokens) {
* Enforces that token is a part of the returned value.
*/
export function useSyncWidgetInputs({
token,
onTokenChange,
defaultTokens,
onDefaultTokenChange,
}: {
token?: Currency
onTokenChange?: (token: Currency) => void
defaultTokens: DefaultTokens
onDefaultTokenChange?: (token: Currency) => void
}) {
const trace = useTrace({ section: InterfaceSectionName.WIDGET })
const { chainId } = useWeb3React()
const previousChainId = usePrevious(chainId)
const [type, setType] = useState<SwapValue['type']>(TradeType.EXACT_INPUT)
const [amount, setAmount] = useState<SwapValue['amount']>(EMPTY_AMOUNT)
const [tokens, setTokens] = useState<SwapTokens>({ [Field.OUTPUT]: token, default: token })
const [tokens, setTokens] = useState<SwapTokens>(defaultTokens)
useEffect(() => {
setTokens((tokens) => {
const update = { ...tokens, default: token }
if (!includesDefaultToken(update)) {
return { [Field.OUTPUT]: update.default, default: update.default }
}
return update
})
}, [token])
if (!tokens[Field.INPUT] && !tokens[Field.OUTPUT]) {
setTokens({
...tokens,
[Field.INPUT]: defaultTokens[Field.INPUT] ?? tokens[Field.INPUT],
[Field.OUTPUT]: defaultTokens[Field.OUTPUT] ?? tokens[Field.OUTPUT] ?? defaultTokens.default,
default: defaultTokens.default,
})
}
}, [defaultTokens, tokens])
useEffect(() => {
if (chainId !== previousChainId && !!previousChainId) {
setTokens({
...tokens,
[Field.INPUT]: undefined,
[Field.OUTPUT]: undefined,
})
setAmount(EMPTY_AMOUNT)
}
}, [chainId, previousChainId, tokens])
const onAmountChange = useCallback(
(field: Field, amount: string, origin?: 'max') => {
@@ -96,13 +114,14 @@ export function useSyncWidgetInputs({
return type
})
if (!includesDefaultToken(update)) {
onTokenChange?.(update[Field.OUTPUT] || selectingToken)
if (missingDefaultToken(update)) {
onDefaultTokenChange?.(update[Field.OUTPUT] ?? selectingToken)
}
setTokens(update)
},
[onTokenChange, selectingField, tokens]
[onDefaultTokenChange, selectingField, tokens]
)
const tokenSelector = (
<CurrencySearchModal
isOpen={selectingField !== undefined}
@@ -110,6 +129,7 @@ export function useSyncWidgetInputs({
selectedCurrency={selectingField && tokens[selectingField]}
otherSelectedCurrency={selectingField && tokens[invertField(selectingField)]}
onCurrencySelect={onTokenSelect}
showCommonBases
/>
)
@@ -117,11 +137,11 @@ export function useSyncWidgetInputs({
() => ({
type,
amount,
// If the default has not yet been handled, preemptively disable the widget by passing no tokens. Effectively,
// If the initial state has not yet been set, preemptively disable the widget by passing no tokens. Effectively,
// this resets the widget - avoiding rendering stale state - because with no tokens the skeleton will be rendered.
...(token && tokens.default?.equals(token) ? tokens : undefined),
...(tokens[Field.INPUT] || tokens[Field.OUTPUT] ? tokens : undefined),
}),
[amount, token, tokens, type]
[amount, tokens, type]
)
const valueHandlers: SwapEventHandlers = useMemo(
() => ({ onAmountChange, onSwitchTokens, onTokenSelectorClick }),

View File

@@ -1,5 +1,5 @@
import { Percent } from '@uniswap/sdk-core'
import { Slippage, SwapController, SwapEventHandlers } from '@uniswap/widgets'
import { RouterPreference, Slippage, SwapController, SwapEventHandlers } from '@uniswap/widgets'
import { DEFAULT_DEADLINE_FROM_NOW } from 'constants/misc'
import { useCallback, useMemo, useState } from 'react'
import { useUserSlippageTolerance, useUserTransactionTTL } from 'state/user/hooks'
@@ -37,6 +37,8 @@ export function useSyncWidgetSettings() {
[setAppSlippage]
)
const [routerPreference, onRouterPreferenceChange] = useState(RouterPreference.API)
const onSettingsReset = useCallback(() => {
setWidgetTtl(undefined)
setAppTtl(DEFAULT_DEADLINE_FROM_NOW)
@@ -46,11 +48,15 @@ export function useSyncWidgetSettings() {
const settings: SwapController['settings'] = useMemo(() => {
const auto = appSlippage === 'auto'
return { slippage: { auto, max: widgetSlippage }, transactionTtl: widgetTtl }
}, [widgetSlippage, widgetTtl, appSlippage])
return {
slippage: { auto, max: widgetSlippage },
transactionTtl: widgetTtl,
routerPreference,
}
}, [appSlippage, widgetSlippage, widgetTtl, routerPreference])
const settingsHandlers: SwapEventHandlers = useMemo(
() => ({ onSettingsReset, onSlippageChange, onTransactionDeadlineChange }),
[onSettingsReset, onSlippageChange, onTransactionDeadlineChange]
() => ({ onSettingsReset, onSlippageChange, onTransactionDeadlineChange, onRouterPreferenceChange }),
[onSettingsReset, onSlippageChange, onTransactionDeadlineChange, onRouterPreferenceChange]
)
return { settings: { settings, ...settingsHandlers } }

View File

@@ -1,45 +1,68 @@
import { Theme } from '@uniswap/widgets'
import { darkTheme, lightTheme } from 'theme/colors'
import { Z_INDEX } from 'theme/zIndex'
const zIndex = {
modal: Z_INDEX.modal,
}
const fonts = {
fontFamily: 'Inter custom',
}
export const LIGHT_THEME = {
export const LIGHT_THEME: Theme = {
// surface
container: lightTheme.backgroundSurface,
interactive: lightTheme.backgroundInteractive,
module: lightTheme.backgroundModule,
accent: lightTheme.accentAction,
dialog: lightTheme.backgroundBackdrop,
accentSoft: lightTheme.accentActionSoft,
container: lightTheme.backgroundSurface,
module: lightTheme.backgroundModule,
interactive: lightTheme.backgroundInteractive,
outline: lightTheme.backgroundOutline,
dialog: lightTheme.backgroundBackdrop,
scrim: lightTheme.backgroundScrim,
// text
onAccent: lightTheme.white,
primary: lightTheme.textPrimary,
secondary: lightTheme.textSecondary,
hint: lightTheme.textTertiary,
onInteractive: lightTheme.accentTextDarkPrimary,
// shadow
deepShadow: lightTheme.deepShadow,
networkDefaultShadow: lightTheme.networkDefaultShadow,
// state
success: lightTheme.accentSuccess,
warning: lightTheme.accentWarning,
error: lightTheme.accentCritical,
...fonts,
zIndex,
}
export const DARK_THEME = {
export const DARK_THEME: Theme = {
// surface
container: darkTheme.backgroundSurface,
interactive: darkTheme.backgroundInteractive,
module: darkTheme.backgroundModule,
accent: darkTheme.accentAction,
dialog: darkTheme.backgroundBackdrop,
accentSoft: darkTheme.accentActionSoft,
container: darkTheme.backgroundSurface,
module: darkTheme.backgroundModule,
interactive: darkTheme.backgroundInteractive,
outline: darkTheme.backgroundOutline,
dialog: darkTheme.backgroundBackdrop,
scrim: darkTheme.backgroundScrim,
// text
onAccent: darkTheme.white,
primary: darkTheme.textPrimary,
secondary: darkTheme.textSecondary,
hint: darkTheme.textTertiary,
onInteractive: darkTheme.accentTextLightPrimary,
// shadow
deepShadow: darkTheme.deepShadow,
networkDefaultShadow: darkTheme.networkDefaultShadow,
// state
success: darkTheme.accentSuccess,
warning: darkTheme.accentWarning,
error: darkTheme.accentCritical,
...fonts,
zIndex,
}

View File

@@ -1,15 +1,23 @@
import { sendAnalyticsEvent, useTrace } from '@uniswap/analytics'
import { InterfaceEventName, InterfaceSectionName, SwapEventName } from '@uniswap/analytics-events'
import { Trade } from '@uniswap/router-sdk'
import { Currency, Percent } from '@uniswap/sdk-core'
import {
OnTxSuccess,
TradeType,
Transaction,
TransactionEventHandlers,
TransactionInfo,
TransactionType,
TransactionType as WidgetTransactionType,
} from '@uniswap/widgets'
import { useWeb3React } from '@web3-react/core'
import { WrapType } from 'hooks/useWrapCallback'
import { formatSwapSignedAnalyticsEventProperties, formatToDecimal, getTokenAddress } from 'lib/utils/analytics'
import {
formatPercentInBasisPointsNumber,
formatSwapSignedAnalyticsEventProperties,
formatToDecimal,
getTokenAddress,
} from 'lib/utils/analytics'
import { useCallback, useMemo } from 'react'
import { useTransactionAdder } from 'state/transactions/hooks'
import {
@@ -19,6 +27,42 @@ import {
WrapTransactionInfo,
} from 'state/transactions/types'
import { currencyId } from 'utils/currencyId'
import { computeRealizedPriceImpact } from 'utils/prices'
interface AnalyticsEventProps {
trade: Trade<Currency, Currency, TradeType>
gasUsed: string | undefined
blockNumber: number | undefined
hash: string | undefined
allowedSlippage: Percent
succeeded: boolean
}
const formatAnalyticsEventProperties = ({
trade,
hash,
allowedSlippage,
succeeded,
gasUsed,
blockNumber,
}: AnalyticsEventProps) => ({
estimated_network_fee_usd: gasUsed,
transaction_hash: hash,
token_in_address: getTokenAddress(trade.inputAmount.currency),
token_out_address: getTokenAddress(trade.outputAmount.currency),
token_in_symbol: trade.inputAmount.currency.symbol,
token_out_symbol: trade.outputAmount.currency.symbol,
token_in_amount: formatToDecimal(trade.inputAmount, trade.inputAmount.currency.decimals),
token_out_amount: formatToDecimal(trade.outputAmount, trade.outputAmount.currency.decimals),
price_impact_basis_points: formatPercentInBasisPointsNumber(computeRealizedPriceImpact(trade)),
allowed_slippage_basis_points: formatPercentInBasisPointsNumber(allowedSlippage),
chain_id:
trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId
? trade.inputAmount.currency.chainId
: undefined,
swap_quote_block_number: blockNumber,
succeeded,
})
/** Integrates the Widget's transactions, showing the widget's transactions in the app. */
export function useSyncWidgetTransactions() {
@@ -46,7 +90,7 @@ export function useSyncWidgetTransactions() {
amount: transactionAmount
? formatToDecimal(transactionAmount, transactionAmount?.currency.decimals)
: undefined,
type: type === WidgetTransactionType.WRAP ? WrapType.WRAP : WrapType.UNWRAP,
type: type === WidgetTransactionType.WRAP ? TransactionType.WRAP : TransactionType.UNWRAP,
...trace,
}
sendAnalyticsEvent(InterfaceEventName.WRAP_TOKEN_TXN_SUBMITTED, eventProperties)
@@ -94,7 +138,24 @@ export function useSyncWidgetTransactions() {
[addTransaction, chainId, trace]
)
const txHandlers: TransactionEventHandlers = useMemo(() => ({ onTxSubmit }), [onTxSubmit])
const onTxSuccess: OnTxSuccess = useCallback((hash: string, tx) => {
if (tx.info.type === TransactionType.SWAP) {
const { trade, slippageTolerance } = tx.info
sendAnalyticsEvent(
SwapEventName.SWAP_TRANSACTION_COMPLETED,
formatAnalyticsEventProperties({
trade,
hash,
gasUsed: tx.receipt?.gasUsed?.toString(),
blockNumber: tx.receipt?.blockNumber,
allowedSlippage: slippageTolerance,
succeeded: tx.receipt?.status === 1,
})
)
}
}, [])
const txHandlers: TransactionEventHandlers = useMemo(() => ({ onTxSubmit, onTxSuccess }), [onTxSubmit, onTxSuccess])
return { transactions: { ...txHandlers } }
}

View File

@@ -25,22 +25,10 @@ export interface Connection {
type: ConnectionType
}
type MetaMaskError = Error & { code: number }
let metaMaskErrorHandler: (error: MetaMaskError) => void | undefined
export function setMetMaskErrorHandler(errorHandler: typeof metaMaskErrorHandler) {
metaMaskErrorHandler = errorHandler
}
function onError(error: Error) {
console.debug(`web3-react error: ${error}`)
}
function onMetaMaskError(error: Error) {
onError(error)
metaMaskErrorHandler?.(error as MetaMaskError)
}
const [web3Network, web3NetworkHooks] = initializeConnector<Network>(
(actions) => new Network({ actions, urlMap: RPC_PROVIDERS, defaultChainId: 1 })
)
@@ -50,9 +38,7 @@ export const networkConnection: Connection = {
type: ConnectionType.NETWORK,
}
const [web3Injected, web3InjectedHooks] = initializeConnector<MetaMask>(
(actions) => new MetaMask({ actions, onError: onMetaMaskError })
)
const [web3Injected, web3InjectedHooks] = initializeConnector<MetaMask>((actions) => new MetaMask({ actions, onError }))
export const injectedConnection: Connection = {
connector: web3Injected,
hooks: web3InjectedHooks,

View File

@@ -12,18 +12,21 @@ export function getIsInjected(): boolean {
return Boolean(window.ethereum)
}
export function getHasMetaMaskExtensionInstalled(): boolean {
return window.ethereum?.isMetaMask ?? false
export function getIsBraveWallet(): boolean {
return window.ethereum?.isBraveWallet ?? false
}
export function getHasCoinbaseExtensionInstalled(): boolean {
export function getIsMetaMaskWallet(): boolean {
// When using Brave browser, `isMetaMask` is set to true when using the built-in wallet
// This function should return true only when using the MetaMask extension
// https://wallet-docs.brave.com/ethereum/wallet-detection#compatability-with-metamask
return (window.ethereum?.isMetaMask ?? false) && !getIsBraveWallet()
}
export function getIsCoinbaseWallet(): boolean {
return window.ethereum?.isCoinbaseWallet ?? false
}
export function getIsMetaMask(connectionType: ConnectionType): boolean {
return connectionType === ConnectionType.INJECTED && getHasMetaMaskExtensionInstalled()
}
const CONNECTIONS = [
gnosisSafeConnection,
injectedConnection,
@@ -56,7 +59,7 @@ export function getConnection(c: Connector | ConnectionType) {
export function getConnectionName(
connectionType: ConnectionType,
hasMetaMaskExtension: boolean = getHasMetaMaskExtensionInstalled()
hasMetaMaskExtension: boolean = getIsMetaMaskWallet()
) {
switch (connectionType) {
case ConnectionType.INJECTED:

View File

@@ -9,8 +9,12 @@ class TokenLogoLookupTable {
initialize() {
const dict: { [key: string]: string[] | undefined } = {}
DEFAULT_LIST_OF_LISTS.forEach((list) =>
store.getState().lists.byUrl[list].current?.tokens.forEach((token) => {
DEFAULT_LIST_OF_LISTS.forEach((list) => {
const listData = store.getState().lists.byUrl[list]
if (!listData) {
return
}
listData.current?.tokens.forEach((token) => {
if (token.logoURI) {
const lowercaseAddress = token.address.toLowerCase()
const currentEntry = dict[lowercaseAddress + ':' + token.chainId]
@@ -21,7 +25,7 @@ class TokenLogoLookupTable {
}
}
})
)
})
this.dict = dict
this.initialized = true
}

View File

@@ -1,8 +1,8 @@
import { FACTORY_ADDRESS as V2_FACTORY_ADDRESS } from '@uniswap/v2-sdk'
import { FACTORY_ADDRESS as V3_FACTORY_ADDRESS } from '@uniswap/v3-sdk'
import { SupportedChainId } from 'constants/chains'
import { constructSameAddressMap } from '../utils/constructSameAddressMap'
import { SupportedChainId } from './chains'
type AddressMap = { [chainId: number]: string }
@@ -22,29 +22,38 @@ const CELO_QUOTER_ADDRESSES = '0x82825d0554fA07f7FC52Ab63c961F330fdEFa8E8'
const CELO_NONFUNGIBLE_POSITION_MANAGER_ADDRESSES = '0x3d79EdAaBC0EaB6F08ED885C05Fc0B014290D95A'
const CELO_TICK_LENS_ADDRESSES = '0x5f115D9113F88e0a0Db1b5033D90D4a9690AcD3D'
// arbitrum goerli v3 addresses
const ARBITRUM_GOERLI_V3_CORE_FACTORY_ADDRESSES = '0x4893376342d5D7b3e31d4184c08b265e5aB2A3f6'
const ARBITRUM_GOERLI_ROUTER_ADDRESS = '0xab7664500b19a7a2362Ab26081e6DfB971B6F1B0'
const ARBITRUM_GOERLI_V3_MIGRATOR_ADDRESSES = '0xA815919D2584Ac3F76ea9CB62E6Fd40a43BCe0C3'
const ARBITRUM_GOERLI_MULTICALL_ADDRESS = '0x8260CB40247290317a4c062F3542622367F206Ee'
const ARBITRUM_GOERLI_QUOTER_ADDRESSES = '0x1dd92b83591781D0C6d98d07391eea4b9a6008FA'
const ARBITRUM_GOERLI_NONFUNGIBLE_POSITION_MANAGER_ADDRESSES = '0x622e4726a167799826d1E1D150b076A7725f5D81'
const ARBITRUM_GOERLI_TICK_LENS_ADDRESSES = '0xb52429333da969a0C79a60930a4Bf0020E5D1DE8'
/* V3 Contract Addresses */
export const V3_CORE_FACTORY_ADDRESSES: AddressMap = {
...constructSameAddressMap(V3_FACTORY_ADDRESS, [
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISM_GOERLI,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
SupportedChainId.POLYGON_MUMBAI,
SupportedChainId.POLYGON,
]),
[SupportedChainId.CELO]: CELO_V3_CORE_FACTORY_ADDRESSES,
[SupportedChainId.CELO_ALFAJORES]: CELO_V3_CORE_FACTORY_ADDRESSES,
[SupportedChainId.ARBITRUM_GOERLI]: ARBITRUM_GOERLI_V3_CORE_FACTORY_ADDRESSES,
}
export const V3_MIGRATOR_ADDRESSES: AddressMap = {
...constructSameAddressMap('0xA5644E29708357803b5A882D272c41cC0dF92B34', [
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
SupportedChainId.POLYGON_MUMBAI,
SupportedChainId.POLYGON,
]),
[SupportedChainId.CELO]: CELO_V3_MIGRATOR_ADDRESSES,
[SupportedChainId.CELO_ALFAJORES]: CELO_V3_MIGRATOR_ADDRESSES,
[SupportedChainId.ARBITRUM_GOERLI]: ARBITRUM_GOERLI_V3_MIGRATOR_ADDRESSES,
}
export const MULTICALL_ADDRESS: AddressMap = {
@@ -55,9 +64,9 @@ export const MULTICALL_ADDRESS: AddressMap = {
SupportedChainId.POLYGON,
]),
[SupportedChainId.ARBITRUM_ONE]: '0xadF885960B47eA2CD9B55E6DAc6B42b7Cb2806dB',
[SupportedChainId.ARBITRUM_RINKEBY]: '0xa501c031958F579dB7676fF1CE78AD305794d579',
[SupportedChainId.CELO]: CELO_MULTICALL_ADDRESS,
[SupportedChainId.CELO_ALFAJORES]: CELO_MULTICALL_ADDRESS,
[SupportedChainId.ARBITRUM_GOERLI]: ARBITRUM_GOERLI_MULTICALL_ADDRESS,
}
export const SWAP_ROUTER_ADDRESSES: AddressMap = {
@@ -65,12 +74,12 @@ export const SWAP_ROUTER_ADDRESSES: AddressMap = {
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISM_GOERLI,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
SupportedChainId.POLYGON,
SupportedChainId.POLYGON_MUMBAI,
]),
[SupportedChainId.CELO]: CELO_ROUTER_ADDRESS,
[SupportedChainId.CELO_ALFAJORES]: CELO_ROUTER_ADDRESS,
[SupportedChainId.ARBITRUM_GOERLI]: ARBITRUM_GOERLI_ROUTER_ADDRESS,
}
/**
@@ -107,12 +116,12 @@ export const QUOTER_ADDRESSES: AddressMap = {
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISM_GOERLI,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
SupportedChainId.POLYGON_MUMBAI,
SupportedChainId.POLYGON,
]),
[SupportedChainId.CELO]: CELO_QUOTER_ADDRESSES,
[SupportedChainId.CELO_ALFAJORES]: CELO_QUOTER_ADDRESSES,
[SupportedChainId.ARBITRUM_GOERLI]: ARBITRUM_GOERLI_QUOTER_ADDRESSES,
}
export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: AddressMap = {
@@ -120,19 +129,17 @@ export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: AddressMap = {
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISM_GOERLI,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
SupportedChainId.POLYGON_MUMBAI,
SupportedChainId.POLYGON,
]),
[SupportedChainId.CELO]: CELO_NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
[SupportedChainId.CELO_ALFAJORES]: CELO_NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
[SupportedChainId.ARBITRUM_GOERLI]: ARBITRUM_GOERLI_NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
}
export const ENS_REGISTRAR_ADDRESSES: AddressMap = {
[SupportedChainId.MAINNET]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
[SupportedChainId.ROPSTEN]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
[SupportedChainId.GOERLI]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
[SupportedChainId.RINKEBY]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
}
export const SOCKS_CONTROLLER_ADDRESSES: AddressMap = {
@@ -141,7 +148,7 @@ export const SOCKS_CONTROLLER_ADDRESSES: AddressMap = {
export const TICK_LENS_ADDRESSES: AddressMap = {
[SupportedChainId.ARBITRUM_ONE]: '0xbfd8137f7d1516D3ea5cA83523914859ec47F573',
[SupportedChainId.ARBITRUM_RINKEBY]: '0xbfd8137f7d1516D3ea5cA83523914859ec47F573',
[SupportedChainId.ARBITRUM_GOERLI]: ARBITRUM_GOERLI_TICK_LENS_ADDRESSES,
[SupportedChainId.CELO]: CELO_TICK_LENS_ADDRESSES,
[SupportedChainId.CELO_ALFAJORES]: CELO_TICK_LENS_ADDRESSES,
}

View File

@@ -5,10 +5,11 @@ import { default as arbitrumCircleLogoUrl, default as arbitrumLogoUrl } from 'as
import celoLogo from 'assets/svg/celo_logo.svg'
import optimismLogoUrl from 'assets/svg/optimistic_ethereum.svg'
import polygonMaticLogo from 'assets/svg/polygon-matic-logo.svg'
import { SupportedChainId } from 'constants/chains'
import ms from 'ms.macro'
import { darkTheme } from 'theme/colors'
import { SupportedChainId, SupportedL1ChainId, SupportedL2ChainId } from './chains'
import { SupportedL1ChainId, SupportedL2ChainId } from './chains'
import { ARBITRUM_LIST, CELO_LIST, OPTIMISM_LIST } from './lists'
export const AVERAGE_L1_BLOCK_TIME = ms`12s`
@@ -64,36 +65,6 @@ const CHAIN_INFO: ChainInfoMap = {
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
color: darkTheme.chain_1,
},
[SupportedChainId.RINKEBY]: {
networkType: NetworkType.L1,
docs: 'https://docs.uniswap.org/',
explorer: 'https://rinkeby.etherscan.io/',
infoLink: 'https://info.uniswap.org/#/',
label: 'Rinkeby',
logoUrl: ethereumLogoUrl,
nativeCurrency: { name: 'Rinkeby Ether', symbol: 'rETH', decimals: 18 },
color: darkTheme.chain_4,
},
[SupportedChainId.ROPSTEN]: {
networkType: NetworkType.L1,
docs: 'https://docs.uniswap.org/',
explorer: 'https://ropsten.etherscan.io/',
infoLink: 'https://info.uniswap.org/#/',
label: 'Ropsten',
logoUrl: ethereumLogoUrl,
nativeCurrency: { name: 'Ropsten Ether', symbol: 'ropETH', decimals: 18 },
color: darkTheme.chain_3,
},
[SupportedChainId.KOVAN]: {
networkType: NetworkType.L1,
docs: 'https://docs.uniswap.org/',
explorer: 'https://kovan.etherscan.io/',
infoLink: 'https://info.uniswap.org/#/',
label: 'Kovan',
logoUrl: ethereumLogoUrl,
nativeCurrency: { name: 'Kovan Ether', symbol: 'kovETH', decimals: 18 },
color: darkTheme.chain_420,
},
[SupportedChainId.GOERLI]: {
networkType: NetworkType.L1,
docs: 'https://docs.uniswap.org/',
@@ -153,19 +124,19 @@ const CHAIN_INFO: ChainInfoMap = {
color: darkTheme.chain_42,
backgroundColor: darkTheme.chain_42161_background,
},
[SupportedChainId.ARBITRUM_RINKEBY]: {
[SupportedChainId.ARBITRUM_GOERLI]: {
networkType: NetworkType.L2,
blockWaitMsBeforeWarning: ms`10m`,
bridge: 'https://bridge.arbitrum.io/',
docs: 'https://offchainlabs.com/',
explorer: 'https://rinkeby-explorer.arbitrum.io/',
explorer: 'https://goerli.arbiscan.io/',
infoLink: 'https://info.uniswap.org/#/arbitrum/',
label: 'Arbitrum Rinkeby',
label: 'Arbitrum Goerli',
logoUrl: arbitrumLogoUrl,
defaultListUrl: ARBITRUM_LIST,
defaultListUrl: ARBITRUM_LIST, // TODO: use arbitrum goerli token list
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137787-uniswap-on-arbitrum',
nativeCurrency: { name: 'Rinkeby Arbitrum Ether', symbol: 'rinkArbETH', decimals: 18 },
color: darkTheme.chain_421611,
nativeCurrency: { name: 'Goerli Arbitrum Ether', symbol: 'goerliArbETH', decimals: 18 },
color: darkTheme.chain_421613,
},
[SupportedChainId.POLYGON]: {
networkType: NetworkType.L1,

View File

@@ -1,15 +1,13 @@
/**
* List of all the networks supported by the Uniswap Interface
* TODO(INFRA-90): Eventually this may be derived from sdk-core.
*/
export enum SupportedChainId {
MAINNET = 1,
ROPSTEN = 3,
RINKEBY = 4,
GOERLI = 5,
KOVAN = 42,
ARBITRUM_ONE = 42161,
ARBITRUM_RINKEBY = 421611,
ARBITRUM_GOERLI = 421613,
OPTIMISM = 10,
OPTIMISM_GOERLI = 420,
@@ -23,16 +21,13 @@ export enum SupportedChainId {
export const CHAIN_IDS_TO_NAMES = {
[SupportedChainId.MAINNET]: 'mainnet',
[SupportedChainId.ROPSTEN]: 'ropsten',
[SupportedChainId.RINKEBY]: 'rinkeby',
[SupportedChainId.GOERLI]: 'goerli',
[SupportedChainId.KOVAN]: 'kovan',
[SupportedChainId.POLYGON]: 'polygon',
[SupportedChainId.POLYGON_MUMBAI]: 'polygon_mumbai',
[SupportedChainId.CELO]: 'celo',
[SupportedChainId.CELO_ALFAJORES]: 'celo_alfajores',
[SupportedChainId.ARBITRUM_ONE]: 'arbitrum',
[SupportedChainId.ARBITRUM_RINKEBY]: 'arbitrum_rinkeby',
[SupportedChainId.ARBITRUM_GOERLI]: 'arbitrum_goerli',
[SupportedChainId.OPTIMISM]: 'optimism',
[SupportedChainId.OPTIMISM_GOERLI]: 'optimism_goerli',
}
@@ -63,15 +58,13 @@ export const UNSUPPORTED_V2POOL_CHAIN_IDS = [
SupportedChainId.POLYGON,
SupportedChainId.OPTIMISM,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_GOERLI,
]
export const TESTNET_CHAIN_IDS = [
SupportedChainId.ROPSTEN,
SupportedChainId.RINKEBY,
SupportedChainId.GOERLI,
SupportedChainId.KOVAN,
SupportedChainId.POLYGON_MUMBAI,
SupportedChainId.ARBITRUM_RINKEBY,
SupportedChainId.ARBITRUM_GOERLI,
SupportedChainId.OPTIMISM_GOERLI,
] as const
@@ -82,10 +75,7 @@ export type SupportedTestnetChainId = typeof TESTNET_CHAIN_IDS[number]
*/
export const L1_CHAIN_IDS = [
SupportedChainId.MAINNET,
SupportedChainId.ROPSTEN,
SupportedChainId.RINKEBY,
SupportedChainId.GOERLI,
SupportedChainId.KOVAN,
SupportedChainId.POLYGON,
SupportedChainId.POLYGON_MUMBAI,
SupportedChainId.CELO,
@@ -100,7 +90,7 @@ export type SupportedL1ChainId = typeof L1_CHAIN_IDS[number]
*/
export const L2_CHAIN_IDS = [
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.ARBITRUM_RINKEBY,
SupportedChainId.ARBITRUM_GOERLI,
SupportedChainId.OPTIMISM,
SupportedChainId.OPTIMISM_GOERLI,
] as const

View File

@@ -1,3 +1,5 @@
import { SupportedChainId } from 'constants/chains'
import {
GOVERNANCE_ALPHA_V0_ADDRESSES,
GOVERNANCE_ALPHA_V1_ADDRESSES,
@@ -5,7 +7,6 @@ import {
TIMELOCK_ADDRESS,
UNI_ADDRESS,
} from './addresses'
import { SupportedChainId } from './chains'
export const COMMON_CONTRACT_NAMES: Record<number, { [address: string]: string }> = {
[SupportedChainId.MAINNET]: {
@@ -20,7 +21,8 @@ export const COMMON_CONTRACT_NAMES: Record<number, { [address: string]: string }
},
}
export const DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS = 13
// in PoS, ethereum block time is 12s, see https://ethereum.org/en/developers/docs/blocks/#block-time
export const DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS = 12
// Block time here is slightly higher (~1s) than average in order to avoid ongoing proposals past the displayed time
export const AVERAGE_BLOCK_TIME_IN_SECS: { [chainId: number]: number } = {

View File

@@ -1,6 +1,6 @@
export const UNI_LIST = 'https://tokens.uniswap.org'
export const UNI_EXTENDED_LIST = 'https://extendedtokens.uniswap.org/'
const UNI_UNSUPPORTED_LIST = 'https://unsupportedtokens.uniswap.org/'
export const UNI_LIST = 'https://gateway.ipfs.io/ipns/tokens.uniswap.org'
export const UNI_EXTENDED_LIST = 'https://gateway.ipfs.io/ipns/extendedtokens.uniswap.org'
const UNI_UNSUPPORTED_LIST = 'https://gateway.ipfs.io/ipns/unsupportedtokens.uniswap.org'
const AAVE_LIST = 'tokenlist.aave.eth'
const BA_LIST = 'https://raw.githubusercontent.com/The-Blockchain-Association/sec-notice-list/master/ba-sec-list.json'
const CMC_ALL_LIST = 'https://api.coinmarketcap.com/data-api/v3/uniswap/all.json'
@@ -8,7 +8,6 @@ const COINGECKO_LIST = 'https://tokens.coingecko.com/uniswap/all.json'
const COMPOUND_LIST = 'https://raw.githubusercontent.com/compound-finance/token-list/master/compound.tokenlist.json'
const GEMINI_LIST = 'https://www.gemini.com/uniswap/manifest.json'
const KLEROS_LIST = 't2crtokens.eth'
const ROLL_LIST = 'https://app.tryroll.com/tokens.json'
const SET_LIST = 'https://raw.githubusercontent.com/SetProtocol/uniswap-tokenlist/main/set.tokenlist.json'
const WRAPPED_LIST = 'wrapped.tokensoft.eth'
@@ -30,7 +29,6 @@ export const DEFAULT_INACTIVE_LIST_URLS: string[] = [
GEMINI_LIST,
WRAPPED_LIST,
SET_LIST,
ROLL_LIST,
ARBITRUM_LIST,
OPTIMISM_LIST,
CELO_LIST,

View File

@@ -1,4 +1,4 @@
import { SupportedChainId } from './chains'
import { SupportedChainId } from 'constants/chains'
const INFURA_KEY = process.env.REACT_APP_INFURA_KEY
if (typeof INFURA_KEY === 'undefined') {
@@ -24,26 +24,12 @@ export const FALLBACK_URLS: { [key in SupportedChainId]: string[] } = {
'https://rpc.ankr.com/eth',
'https://eth-mainnet.public.blastapi.io',
],
[SupportedChainId.ROPSTEN]: [
// "Fallback" URLs
'https://rpc.ankr.com/eth_ropsten',
],
[SupportedChainId.RINKEBY]: [
// "Fallback" URLs
'https://rinkeby-light.eth.linkpool.io/',
],
[SupportedChainId.GOERLI]: [
// "Safe" URLs
'https://rpc.goerli.mudit.blog/',
// "Fallback" URLs
'https://rpc.ankr.com/eth_goerli',
],
[SupportedChainId.KOVAN]: [
// "Safe" URLs
'https://kovan.poa.network',
// "Fallback" URLs
'https://eth-kovan.public.blastapi.io',
],
[SupportedChainId.POLYGON]: [
// "Safe" URLs
'https://polygon-rpc.com/',
@@ -65,9 +51,9 @@ export const FALLBACK_URLS: { [key in SupportedChainId]: string[] } = {
// "Fallback" URLs
'https://arbitrum.public-rpc.com',
],
[SupportedChainId.ARBITRUM_RINKEBY]: [
[SupportedChainId.ARBITRUM_GOERLI]: [
// "Safe" URLs
'https://rinkeby.arbitrum.io/rpc',
'https://goerli-rollup.arbitrum.io/rpc',
],
[SupportedChainId.OPTIMISM]: [
// "Safe" URLs
@@ -98,16 +84,7 @@ export const RPC_URLS: { [key in SupportedChainId]: string[] } = {
`https://mainnet.infura.io/v3/${INFURA_KEY}`,
...FALLBACK_URLS[SupportedChainId.MAINNET],
],
[SupportedChainId.RINKEBY]: [
`https://rinkeby.infura.io/v3/${INFURA_KEY}`,
...FALLBACK_URLS[SupportedChainId.RINKEBY],
],
[SupportedChainId.ROPSTEN]: [
`https://ropsten.infura.io/v3/${INFURA_KEY}`,
...FALLBACK_URLS[SupportedChainId.ROPSTEN],
],
[SupportedChainId.GOERLI]: [`https://goerli.infura.io/v3/${INFURA_KEY}`, ...FALLBACK_URLS[SupportedChainId.GOERLI]],
[SupportedChainId.KOVAN]: [`https://kovan.infura.io/v3/${INFURA_KEY}`, ...FALLBACK_URLS[SupportedChainId.KOVAN]],
[SupportedChainId.OPTIMISM]: [
`https://optimism-mainnet.infura.io/v3/${INFURA_KEY}`,
...FALLBACK_URLS[SupportedChainId.OPTIMISM],
@@ -120,9 +97,9 @@ export const RPC_URLS: { [key in SupportedChainId]: string[] } = {
`https://arbitrum-mainnet.infura.io/v3/${INFURA_KEY}`,
...FALLBACK_URLS[SupportedChainId.ARBITRUM_ONE],
],
[SupportedChainId.ARBITRUM_RINKEBY]: [
`https://arbitrum-rinkeby.infura.io/v3/${INFURA_KEY}`,
...FALLBACK_URLS[SupportedChainId.ARBITRUM_RINKEBY],
[SupportedChainId.ARBITRUM_GOERLI]: [
`https://arbitrum-goerli.infura.io/v3/${INFURA_KEY}`,
...FALLBACK_URLS[SupportedChainId.ARBITRUM_GOERLI],
],
[SupportedChainId.POLYGON]: [
`https://polygon-mainnet.infura.io/v3/${INFURA_KEY}`,

View File

@@ -3,9 +3,10 @@ import { deepCopy } from '@ethersproject/properties'
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { StaticJsonRpcProvider } from '@ethersproject/providers'
import { isPlain } from '@reduxjs/toolkit'
import { SupportedChainId } from 'constants/chains'
import { AVERAGE_L1_BLOCK_TIME } from './chainInfo'
import { CHAIN_IDS_TO_NAMES, SupportedChainId } from './chains'
import { CHAIN_IDS_TO_NAMES } from './chains'
import { RPC_URLS } from './networks'
class AppJsonRpcProvider extends StaticJsonRpcProvider {
@@ -58,14 +59,11 @@ class AppJsonRpcProvider extends StaticJsonRpcProvider {
*/
export const RPC_PROVIDERS: { [key in SupportedChainId]: StaticJsonRpcProvider } = {
[SupportedChainId.MAINNET]: new AppJsonRpcProvider(SupportedChainId.MAINNET),
[SupportedChainId.RINKEBY]: new AppJsonRpcProvider(SupportedChainId.RINKEBY),
[SupportedChainId.ROPSTEN]: new AppJsonRpcProvider(SupportedChainId.ROPSTEN),
[SupportedChainId.GOERLI]: new AppJsonRpcProvider(SupportedChainId.GOERLI),
[SupportedChainId.KOVAN]: new AppJsonRpcProvider(SupportedChainId.KOVAN),
[SupportedChainId.OPTIMISM]: new AppJsonRpcProvider(SupportedChainId.OPTIMISM),
[SupportedChainId.OPTIMISM_GOERLI]: new AppJsonRpcProvider(SupportedChainId.OPTIMISM_GOERLI),
[SupportedChainId.ARBITRUM_ONE]: new AppJsonRpcProvider(SupportedChainId.ARBITRUM_ONE),
[SupportedChainId.ARBITRUM_RINKEBY]: new AppJsonRpcProvider(SupportedChainId.ARBITRUM_RINKEBY),
[SupportedChainId.ARBITRUM_GOERLI]: new AppJsonRpcProvider(SupportedChainId.ARBITRUM_GOERLI),
[SupportedChainId.POLYGON]: new AppJsonRpcProvider(SupportedChainId.POLYGON),
[SupportedChainId.POLYGON_MUMBAI]: new AppJsonRpcProvider(SupportedChainId.POLYGON_MUMBAI),
[SupportedChainId.CELO]: new AppJsonRpcProvider(SupportedChainId.CELO),

View File

@@ -1,7 +1,7 @@
// a list of tokens by chain
import { Currency, Token } from '@uniswap/sdk-core'
import { SupportedChainId } from 'constants/chains'
import { SupportedChainId } from './chains'
import {
AMPL,
CEUR_CELO,
@@ -26,6 +26,7 @@ import {
SWISE,
TRIBE,
USDC_ARBITRUM,
USDC_ARBITRUM_GOERLI,
USDC_MAINNET,
USDC_OPTIMISM,
USDC_POLYGON,
@@ -122,22 +123,10 @@ export const COMMON_BASES: ChainCurrencyList = {
WBTC,
WRAPPED_NATIVE_CURRENCY[SupportedChainId.MAINNET] as Token,
],
[SupportedChainId.ROPSTEN]: [
nativeOnChain(SupportedChainId.ROPSTEN),
WRAPPED_NATIVE_CURRENCY[SupportedChainId.ROPSTEN] as Token,
],
[SupportedChainId.RINKEBY]: [
nativeOnChain(SupportedChainId.RINKEBY),
WRAPPED_NATIVE_CURRENCY[SupportedChainId.RINKEBY] as Token,
],
[SupportedChainId.GOERLI]: [
nativeOnChain(SupportedChainId.GOERLI),
WRAPPED_NATIVE_CURRENCY[SupportedChainId.GOERLI] as Token,
],
[SupportedChainId.KOVAN]: [
nativeOnChain(SupportedChainId.KOVAN),
WRAPPED_NATIVE_CURRENCY[SupportedChainId.KOVAN] as Token,
],
[SupportedChainId.ARBITRUM_ONE]: [
nativeOnChain(SupportedChainId.ARBITRUM_ONE),
DAI_ARBITRUM_ONE,
@@ -146,9 +135,10 @@ export const COMMON_BASES: ChainCurrencyList = {
WBTC_ARBITRUM_ONE,
WRAPPED_NATIVE_CURRENCY[SupportedChainId.ARBITRUM_ONE] as Token,
],
[SupportedChainId.ARBITRUM_RINKEBY]: [
nativeOnChain(SupportedChainId.ARBITRUM_RINKEBY),
WRAPPED_NATIVE_CURRENCY[SupportedChainId.ARBITRUM_RINKEBY] as Token,
[SupportedChainId.ARBITRUM_GOERLI]: [
nativeOnChain(SupportedChainId.ARBITRUM_GOERLI),
WRAPPED_NATIVE_CURRENCY[SupportedChainId.ARBITRUM_GOERLI] as Token,
USDC_ARBITRUM_GOERLI,
],
[SupportedChainId.OPTIMISM]: [
nativeOnChain(SupportedChainId.OPTIMISM),

View File

@@ -1,4 +1,6 @@
import { Plural, Trans } from '@lingui/macro'
import { TokenStandard } from 'graphql/data/__generated__/types-and-hooks'
import { SearchToken } from 'graphql/data/SearchTokens'
import { ZERO_ADDRESS } from './misc'
import { NATIVE_CHAIN_ID } from './tokens'
@@ -94,3 +96,11 @@ export function checkWarning(tokenAddress: string) {
return BlockedWarning
}
}
// TODO(cartcrom): Replace all usage of WARNING_LEVEL with SafetyLevel
export function checkSearchTokenWarning(token: SearchToken) {
if (!token.address) {
return token.standard === TokenStandard.Native ? null : StrongWarning
}
return checkWarning(token.address)
}

View File

@@ -1,8 +1,8 @@
import { Currency, Ether, NativeCurrency, Token, WETH9 } from '@uniswap/sdk-core'
import { SupportedChainId } from 'constants/chains'
import invariant from 'tiny-invariant'
import { UNI_ADDRESS } from './addresses'
import { SupportedChainId } from './chains'
export const NATIVE_CHAIN_ID = 'NATIVE'
@@ -18,20 +18,6 @@ export const USDC_MAINNET = new Token(
'USDC',
'USD//C'
)
const USDC_ROPSTEN = new Token(
SupportedChainId.ROPSTEN,
'0x07865c6e87b9f70255377e024ace6630c1eaa37f',
6,
'USDC',
'USD//C'
)
const USDC_RINKEBY = new Token(
SupportedChainId.RINKEBY,
'0x4DBCdF9B62e891a7cec5A2568C3F4FAF9E8Abe2b',
6,
'tUSDC',
'test USD//C'
)
const USDC_GOERLI = new Token(
SupportedChainId.GOERLI,
'0x07865c6e87b9f70255377e024ace6630c1eaa37f',
@@ -39,7 +25,6 @@ const USDC_GOERLI = new Token(
'USDC',
'USD//C'
)
const USDC_KOVAN = new Token(SupportedChainId.KOVAN, '0x31eeb2d0f9b6fd8642914ab10f4dd473677d80df', 6, 'USDC', 'USD//C')
export const USDC_OPTIMISM = new Token(
SupportedChainId.OPTIMISM,
'0x7F5c764cBc14f9669B88837ca1490cCa17c31607',
@@ -61,9 +46,9 @@ export const USDC_ARBITRUM = new Token(
'USDC',
'USD//C'
)
const USDC_ARBITRUM_RINKEBY = new Token(
SupportedChainId.ARBITRUM_RINKEBY,
'0x09b98f8b2395d076514037ff7d39a091a536206c',
export const USDC_ARBITRUM_GOERLI = new Token(
SupportedChainId.ARBITRUM_GOERLI,
'0x8FB1E3fC51F3b789dED7557E680551d93Ea9d892',
6,
'USDC',
'USD//C'
@@ -311,10 +296,7 @@ export const CEUR_CELO_ALFAJORES = new Token(
export const UNI: { [chainId: number]: Token } = {
[SupportedChainId.MAINNET]: new Token(SupportedChainId.MAINNET, UNI_ADDRESS[1], 18, 'UNI', 'Uniswap'),
[SupportedChainId.RINKEBY]: new Token(SupportedChainId.RINKEBY, UNI_ADDRESS[4], 18, 'UNI', 'Uniswap'),
[SupportedChainId.ROPSTEN]: new Token(SupportedChainId.ROPSTEN, UNI_ADDRESS[3], 18, 'UNI', 'Uniswap'),
[SupportedChainId.GOERLI]: new Token(SupportedChainId.GOERLI, UNI_ADDRESS[5], 18, 'UNI', 'Uniswap'),
[SupportedChainId.KOVAN]: new Token(SupportedChainId.KOVAN, UNI_ADDRESS[42], 18, 'UNI', 'Uniswap'),
}
export const WRAPPED_NATIVE_CURRENCY: { [chainId: number]: Token | undefined } = {
@@ -340,9 +322,9 @@ export const WRAPPED_NATIVE_CURRENCY: { [chainId: number]: Token | undefined } =
'WETH',
'Wrapped Ether'
),
[SupportedChainId.ARBITRUM_RINKEBY]: new Token(
SupportedChainId.ARBITRUM_RINKEBY,
'0xB47e6A5f8b33b3F17603C83a0535A9dcD7E32681',
[SupportedChainId.ARBITRUM_GOERLI]: new Token(
SupportedChainId.ARBITRUM_GOERLI,
'0xe39Ab88f8A4777030A534146A9Ca3B52bd5D43A3',
18,
'WETH',
'Wrapped Ether'
@@ -446,16 +428,13 @@ export const TOKEN_SHORTHANDS: { [shorthand: string]: { [chainId in SupportedCha
USDC: {
[SupportedChainId.MAINNET]: USDC_MAINNET.address,
[SupportedChainId.ARBITRUM_ONE]: USDC_ARBITRUM.address,
[SupportedChainId.ARBITRUM_GOERLI]: USDC_ARBITRUM_GOERLI.address,
[SupportedChainId.OPTIMISM]: USDC_OPTIMISM.address,
[SupportedChainId.ARBITRUM_RINKEBY]: USDC_ARBITRUM_RINKEBY.address,
[SupportedChainId.OPTIMISM_GOERLI]: USDC_OPTIMISM_GOERLI.address,
[SupportedChainId.POLYGON]: USDC_POLYGON.address,
[SupportedChainId.POLYGON_MUMBAI]: USDC_POLYGON_MUMBAI.address,
[SupportedChainId.CELO]: PORTAL_USDC_CELO.address,
[SupportedChainId.CELO_ALFAJORES]: PORTAL_USDC_CELO.address,
[SupportedChainId.GOERLI]: USDC_GOERLI.address,
[SupportedChainId.RINKEBY]: USDC_RINKEBY.address,
[SupportedChainId.KOVAN]: USDC_KOVAN.address,
[SupportedChainId.ROPSTEN]: USDC_ROPSTEN.address,
},
}

View File

@@ -0,0 +1,9 @@
import { BaseVariant, FeatureFlag, useBaseFlag } from '../index'
function useDummyGateFlag(): BaseVariant {
return useBaseFlag(FeatureFlag.statsigDummy)
}
export function useDummyGateEnabled(): boolean {
return useDummyGateFlag() === BaseVariant.Enabled
}

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