Compare commits

...

108 Commits

Author SHA1 Message Date
Zach Pomerantz
b1fd484894 test: install fixed version of yarn-deduplicate (#5390) 2022-11-24 12:23:56 -05:00
unipadmini
aeb636cf8a fix: Display blank row if no listing price. (#5398)
Co-authored-by: Padmini Pyapali <padminipyapali@Padminis-MacBook-Pro.local>
2022-11-23 20:28:38 -05:00
Jack Short
4240908c4c fix: no title for unavailable nft (#5397) 2022-11-23 19:16:46 -05:00
Charles Bachmeier
d53ba64218 fix: reduce success image size (#5399)
* marketplace links

* drop down size of successful purchase image

* align unicorn with header

Co-authored-by: JackShort <john.short.tj@gmail.com>
Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-23 16:14:26 -08:00
Jack Short
89ce5a9805 style: open all containers on details (#5396) 2022-11-23 17:54:23 -05:00
Jack Short
1790492f17 style: adding links to marketplace icons on details (#5394)
marketplace links
2022-11-23 17:54:10 -05:00
Jack Short
304cd72eed fix: adding pool icon to sudo assets (#5395) 2022-11-23 17:53:56 -05:00
Jack Short
9dba68b34c fix: changing buy now to add to bag (#5393) 2022-11-23 17:19:22 -05:00
Charles Bachmeier
588567b900 fix: incorrect looks rare creator royalties (#5392)
* properly show LR royalties in grid

* add LR royalty to total return calculation

* common LR basis points const

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-23 14:14:26 -08:00
Mike Grabowski
ef964ab120 feat: differentiate modal from bottomsheet (#5387)
* feat: differenciate modal from bottomsheet

* chore: add ability to disable esc/overlay tap to dismiss
2022-11-23 21:39:23 +01:00
Charles Bachmeier
f3d64b65da fix: Stop traits from url re-appearing (#5391)
traits no longer re-appear when removed

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-23 12:01:39 -08:00
Mike Grabowski
a9200b2c39 chore: fix invalid css color prop on text components (#5388)
* chore: fix

* chore: fix snapshot tests
2022-11-23 19:49:42 +01:00
Charles Bachmeier
209fd33780 fix: always sort not for sale assets towards the end when sorting by price (#5389)
* always sort not for sale assets towards the end when sorting by price

* default 0

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-23 10:33:30 -08:00
Jack Short
940c1dbb8e fix: a few bugs on details (#5386)
* fix: a few bugs on details

* no seller if no owner

* removing seller for erc1155
2022-11-23 12:55:13 -05:00
aballerr
5dce68a62f feat: Implementing claims logic, adding error state and minor copy change (#5357)
* Implementing claims logic
2022-11-23 10:44:10 -05:00
unipadmini
9dd8ad1db6 fix: Update copy for viewing 1155 assets. (#5375)
* chore: Update copy for viewing 1155 assets.

* Remove is so it looks better.

Co-authored-by: Padmini Pyapali <padminipyapali@Padminis-MacBook-Pro.local>
2022-11-22 22:04:13 -05:00
lynn
0b40f72f0c fix: hide collection search for > 300 collections due to lack of search param input for OS api (#5383)
* init

* fix

* respond to charlie
2022-11-22 19:16:53 -05:00
Mike Grabowski
875171e36a feat: handle 0 in banner card (#5385)
feat: fix web-2411
2022-11-23 00:20:29 +01:00
Mike Grabowski
8e9a20a6c8 chore: various responsive tweaks to carousel (#5377)
* feat: resposive tweaks

* feat: display inline badge

* make animation slightly faster

* chore: change color
2022-11-23 00:00:45 +01:00
Mike Grabowski
1a31560374 feat: welcome nft banner (#5379)
* wip

* finished modal, waiting for assets

* feat: use srcset for better assets optimisation

* feat: bg photo

* various tweaks

* feat: wrap up logic

* chore: use proper link

* chore: bring switch back
2022-11-22 22:37:53 +01:00
lynn
d528b28203 fix: nft promo banner init state fix (#5384)
init
2022-11-22 15:57:18 -05:00
lynn
c1cb712087 feat: nft explore promo banner (#5369)
* init

* it looks good and is working

* don't change image on every render

* un-move dark mode hook

* oops

* handle mobile screen smallest size

* first round comment responses

* second round comments

* rest of comments

* fix analytics changes

* remove unnecesasry z index
2022-11-22 15:18:19 -05:00
cartcrom
ceed5e0b4c fix: always return arrays from typed queries (#5382)
added empty array fallbacks
2022-11-22 14:47:35 -05:00
cartcrom
a449338252 fix: searchbar fetching promise resolve fallback (#5380)
* added promise resolve fallback

* removed extra indent
2022-11-22 14:00:37 -05:00
cartcrom
9662344e24 fix: render invalid address warnings on token details (#5260)
* initial commit
* polishing loading state
* small refactors
* polished on chain token network switching
* fixed commited merge conflict
* small refactor
* fixed merge conflicts
* PR comments and refactors
* fix unintented trace property change
* second round of comments
2022-11-22 11:22:07 -05:00
Jack Short
1536e18784 style: updating details (#5371)
* style: adjusting details page

* moving owners

* removing traits if no traits

* adding accent colors

* trait grid breakpoint

* some comments

* addressing comments
2022-11-22 11:17:36 -05:00
unipadmini
c19431eb20 fix: Issue where decimal appears as 0 on listings page. (#5354)
* Fix issue where decimal appears as 0.

* Checking explicit value.

Co-authored-by: Padmini Pyapali <padminipyapali@Padminis-MacBook-Pro.local>
2022-11-22 11:02:52 -05:00
Charles Bachmeier
634e38529c fix: after retry, put listing button in a ready state (#5372)
* update color confirmation

* update color checkmark

* reset continue button after retry

* reset before listing

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-21 17:08:17 -08:00
Charles Bachmeier
083ec425d0 feat: approve collections in parallel on desktop, sequentially on mobile (#5374)
approve in parallel on desktop

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-21 16:43:59 -08:00
Mike Grabowski
e8c09db146 feat: multiline ellipsis when text is too large (#5365)
* feat: multiline ellipsis

* do not repeat
2022-11-22 01:40:43 +01:00
Charles Bachmeier
9ab4d952ef fix: use lowercase for gray market icons (#5370)
use lowercase

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-21 15:39:54 -08:00
Charles Bachmeier
48883cce8d fix: Can remove marketplace or listing from modal (#5362)
* removing asset doesn't change other listings

* remove marketplace from modal removes asset

* working multiple markets

* better fix

* track signed listing

* simpler solution

* remove unused field

* correct transition

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-21 13:10:56 -08:00
Zach Pomerantz
3d820d39b7 fix: include user-added tokens if selected (#5367) 2022-11-21 16:06:19 -05:00
cartcrom
4fdca48a97 feat: coned on token details/search (#5349)
* fixing alignment

* migrating stats to coned

* fully replaced formatAmount usage

* reformatted pricechart function w/ yannie's suggestions
2022-11-21 15:06:18 -05:00
aballerr
7834ab7979 fix: removing details-marker from safari (#5364)
* removing details-marker from safari and mobile
2022-11-21 14:41:37 -05:00
Zach Pomerantz
dee808cc57 fix: include user-added tokens in selector (#5363)
* fix: include user-added tokens in selector

* fix: omit user-added tokens from queryless selector
2022-11-21 14:03:51 -05:00
vignesh mohankumar
4c23f62a24 fix: show mobile filters on top of nav (#5366) 2022-11-21 14:00:59 -05:00
dependabot[bot]
93633a81a7 build(deps): bump @uniswap/widgets from 2.18.0 to 2.19.2 (#5361)
* build(deps): bump @uniswap/widgets from 2.18.0 to 2.19.2

Bumps [@uniswap/widgets](https://github.com/Uniswap/widgets) from 2.18.0 to 2.19.2.
- [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.18.0...v2.19.2)

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

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

* build: pin web3-react versions and dedup deps

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>
2022-11-21 13:35:12 -05:00
Mike Grabowski
7465a0e999 feat: various improvements to banner (#5317)
* initial commit

* chore: responsive improvements

* feat: responsive carousel

* chore: fix carousel

* chore: less magic, components

* chore: bump friction a bit to compensate for carousel changes

* fix: position of loading bubbles

* chore: fix max-width on fullscreen breakpoints not taking 100 space

* fix full screen max-width

* chore: remove first card offset
2022-11-21 19:14:20 +01:00
lynn
6aac978754 fix: mobile profile bag copywriting change (#5358)
copywriting change
2022-11-21 12:05:19 -05:00
Charles Bachmeier
62775f6091 feat: approve collections 1x1 (#5359)
approve collections 1x1

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-21 08:51:16 -08:00
Zach Pomerantz
97b3725c19 build: include widgets in dependabot (#5356) 2022-11-21 11:36:21 -05:00
Charles Bachmeier
46563ee565 fix: align items on collection page (#5355)
align items on collection page

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-21 07:16:40 -08:00
cartcrom
c68624e048 fix: swap input size difference (#5351)
removed redudant check that broke input box height
2022-11-20 20:04:42 -05:00
Jordan Frankfurt
0fa1c5e6ea fix: update clear all filters button styles for consistency (#5328)
* fix: update clear all filters button styles for consistency

* pr feedback
2022-11-19 14:04:46 -06:00
vignesh mohankumar
a7fd60987e fix: no filters sidebar animation on mobile (#5320) 2022-11-19 11:10:02 -05:00
Jack Short
51fe44d53a fix: no price adjustment popups for pooled assets (#5322)
* fix: no price adjustment popups for pooled assets

* refactoring for readability
2022-11-18 20:36:32 -05:00
Charles Bachmeier
edf67e8e45 fix: add left margin to activity page (#5327)
* add left margin to activity page

* fix margins on filters and on mobile
2022-11-18 15:00:05 -08:00
lynn
babfebccef refactor: view my nfts loading optimization (#5318)
* working

* remove commented code
2022-11-18 17:44:06 -05:00
Charles Bachmeier
ea02d9e919 feat: List Page (#5323)
* working dropdown button

* memo text

* working dropdown modal

* click outside

* move grid to its own file

* add file

* re-organized page layout

* header updates

* text change

* margin

* ETH

* text input

* respond to comments

* move listinggrid to its own file

* add file

* organize imports

* remove 0 margin

* undo testing

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-18 14:31:06 -08:00
Charles Bachmeier
bb3b236cd9 refactor: move listinggrid to its own file (#5326)
* move listinggrid to its own file

* add file

* organize imports

* undo unrelated testing
2022-11-18 14:19:05 -08:00
aballerr
1d3fd512ae feat: Claim rewards (#5308)
*  Adding in claim usdc modal v1.  Logic and pill ui tweak to follow in v2
2022-11-18 16:27:27 -05:00
Charles Bachmeier
5dbd0ae782 feat: Listing marketplace dropdown (#5314)
* working dropdown button

* memo text

* working dropdown modal

* click outside

* respond to comments

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-18 13:22:08 -08:00
pp-hh-ii-ll
b95621758c chore: add Gray950 and remap backgroundBackdrop to it (#5321)
Add Gray950

Adds Gray950 hex and remaps backgroundBackdrop to it
2022-11-18 15:54:34 -05:00
Zach Pomerantz
ce51ffae75 fix: include user-added tokens in selector (#5319) 2022-11-18 12:32:14 -08:00
lynn
8cd32138ac fix: erc 1155 tooltip behavior on mobile (#5305)
fix erc 1155 tooltip behavior on mobile
2022-11-18 15:29:19 -05:00
Charles Bachmeier
785c2a6712 fix: Revert "feat: add glow on all pages" (#5315)
Revert "feat: add glow on all pages (#5275)"

This reverts commit 72c5e64f74.
2022-11-18 11:09:55 -08:00
Jack Short
56a9952546 fix: bag autoclose when going to collection page (#5311)
* fix: bag autoclose when going to collection page

* moved eslint comment
2022-11-18 13:27:19 -05:00
Jack Short
7059a12a25 style: light mode background style for floating sell bag (#5312) 2022-11-18 13:27:08 -05:00
Mike Grabowski
da01254247 feat: add floor change on mobile (#5302)
* few more tweaks

* remove extra code
2022-11-18 18:16:11 +01:00
Jordan Frankfurt
54a7b943ce fix: make carousel responsive (#5300)
* fix: make carousel responsive

* remove padding from CarouselItemIcon
2022-11-18 11:12:26 -06:00
Jordan Frankfurt
ec6c843db6 fix: collection screen for mobile (#5287)
* fix: collection screen for mobile

* margin to match collection item container

* fix loading skeleton

* pr feedback + some minor code cleanup
2022-11-18 11:09:32 -06:00
aballerr
7a6bb369d4 fix: updating to have proper width (#5304)
updating to have proper width
2022-11-18 12:06:10 -05:00
lynn
66a38c8e51 fix: add infinite scroll to view my nfts filter panel (#5216)
* infinite_scroll_working

* fix

* remove extra

* add infinite loading

* working

* remove null

* filter button text fixes

* respond to charlie plus mobile tooltip fixes

* make filter bar sticky

* respond to charlie
2022-11-18 11:20:06 -05:00
Zach Pomerantz
f79d2f821e fix: size down new position button (#5262) 2022-11-18 08:11:14 -08:00
Mike Grabowski
72c5e64f74 feat: add glow on all pages (#5275)
* chore: add more pronounced glow on all nft pages

* chore: add glow effect everywhere

* chore: add in a few more places

* wip: simplification

* chore: describe API

* chore: small refactor

* feat: add glow to two more pages

* chore: add glow on all other pages

* chore: simplify

* chore: tweaks

* chore: update type

* chore: update white bg as per Cal suggestion

* chore: dont export props

* chore: fix type

Co-authored-by: Vignesh Mohankumar <me@vig.xyz>
2022-11-18 10:49:48 -05:00
Charles Bachmeier
01a44d49b0 fix: Can't scroll behind bag (#5299)
* mobile bag scroll behind and snap

* fix laggy animation on profile page when filters are opened

* don't scroll assets behind bag

* refactor duration

* correct placement of isBagExpanded

* simplify conditions

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-17 20:15:29 -08:00
github-actions[bot]
f0412f5d47 chore(i18n): new Crowdin translations (#5256)
* chore(i18n): synchronize translations from crowdin [skip ci]

* empty

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Vignesh Mohankumar <me@vig.xyz>
2022-11-17 22:42:51 -05:00
Felipe Brahm
2887ee9ac8 chore: remove unused file (#5269) 2022-11-17 22:27:11 -05:00
Felipe Brahm
79d5211db4 refactor: remove unnecessary casting (#5270) 2022-11-17 22:25:34 -05:00
yyip-dev
d05e5d028b fix: theme color hex typo (#5294)
theme color hex typo
2022-11-17 22:24:08 -05:00
Jack Short
d3a415ee96 fix: close bag automatically on details (#5296)
* fix: close bag automaticall on details

* auto closing on explore too
2022-11-17 21:11:24 -05:00
Jack Short
db9ac38c64 feat: adding suspicious icons to bag rows (#5298) 2022-11-17 21:11:10 -05:00
Charles Bachmeier
f27d119181 feat: Sell bag styling (#5297)
match buy bag styles

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-17 17:26:29 -08:00
Jack Short
f2b85621f5 fix: adding back listing icon to profile page (#5289)
* fix: adding back listing icon to profile page

* responding to comments
2022-11-17 20:01:24 -05:00
Charles Bachmeier
d7bc0aaf4c fix: empty sell tag state (#5295)
fix empty sell tag state

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-17 16:51:30 -08:00
Jack Short
f50e0ca9f9 style: updating profile skeleton (#5293)
style: updating proflie skeleton
2022-11-17 19:29:41 -05:00
aballerr
84960b0cef fix: search bar width sizing (#5277)
* Updating Search Bar to be more responsive
2022-11-17 19:00:11 -05:00
Jack Short
42646003fe style: correct collection loading (#5288)
* style: correct collection loading

* review comments
2022-11-17 18:49:33 -05:00
Charles Bachmeier
321b8df3a2 fix: working profile scroll on mobile (#5291)
* working scroll on mobile

* just continue

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-17 15:18:36 -08:00
Jack Short
f1990ff001 style: adding header to top of profile page (#5285) 2022-11-17 18:13:32 -05:00
Jack Short
b48af759f1 style: moving mobile sell bag above tab bar (#5292)
* style: moving mobile sell bag above tab bar

* changing color

* changing to toggleBag

* cleanup imports

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-17 18:09:10 -05:00
Jack Short
3969f0414d style: correct padding for view my nfts (#5286) 2022-11-17 17:55:48 -05:00
aballerr
9dc6c60a1a fix: fixing button color (#5290)
fixing approve text color
2022-11-17 17:39:02 -05:00
Mike Grabowski
4b686a0147 fix: border radius on safari for carousel (#5274)
* fix: borders

* chore: potential fix

* chore: add bg to wrapper to avoid transparent lines

* simplify implementation

* chore: simplify code

* chore: add animations
2022-11-17 23:19:53 +01:00
lynn
f9f8eea6f6 fix: add filter text when filter button open (#5217)
* fix

* remove extra

* filter button text fixes
2022-11-17 16:30:54 -05:00
cartcrom
3f6dc180cf feat: update token icon sourcing (#5208)
* initial setup
* working version
* optimized image quality
* refactoring file structure
* updating backup logos
* cleaning up code
* refactored file structure
* fixing state update issue
* updated test
* fixed currency logo bug
* updated image background color
* finished pr comments
* fixed additional PR comments
2022-11-17 16:25:04 -05:00
Jack Short
7ce58b55a1 fix: play video on asset card (#5284) 2022-11-17 15:46:35 -05:00
Charles Bachmeier
e8d1067313 fix: tx screen tweak (#5283)
tx screen tweak

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-17 11:16:12 -08:00
Jordan Frankfurt
c12b0a6dab fix: adjust gap down to 16px on large screens (#5276)
* fix: adjust gap down to 16px on large screens

* pr feedback
2022-11-17 13:05:53 -06:00
Mike Grabowski
effc3d1c4d fix: rendering of glow effect on mobile safari (#5272) 2022-11-17 20:00:45 +01:00
Mike Grabowski
63a9bd4fbf feat: improve formatting of NFT prices with shared module (#5245)
* wip: change formatting of nft

chore: use a different package

use pure webpack instead

* revert changes to yarn.lock

* chore: update lock one more time

* chore: upgrade conedison

* chore: tweaks

* chore: handle 0

* chore: update to latest and use proper method

* chore: update conditional

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>
2022-11-17 19:59:40 +01:00
Charles Bachmeier
ca829a355c fix: tx screen overflow (#5282)
fix tx screen overflow

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-17 10:59:18 -08:00
cartcrom
8464fc70fe build: keep existing gql schema on fetch failure (#5149)
* updated fetch script

* fixed input too long error failing in cypress/vercel
2022-11-17 13:33:08 -05:00
cartcrom
dd68f89bf9 fix: blocked tokens not appearing or having icons (#5251)
* fixed blocked tokens not appearing or having icons
* removed previous blocked Icon
* updated snapshot
* changed to slash icon
2022-11-17 13:32:37 -05:00
Jordan Frankfurt
e42991c066 fix(bag): icon update to remove front handle (#5280) 2022-11-17 12:26:24 -06:00
Mike Grabowski
6eb699712d chore: fix horizontal overflow (#5279)
chore: fix overflow horizontal
2022-11-17 12:00:12 -06:00
Jordan Frankfurt
1d6662dfe3 fix: use ThemeButton for claim and view/sell nfts (#5278)
* fix: use ThemeButton for claim and view/sell nfts

* fix(bag): icon update to remove front handle

* Revert "fix(bag): icon update to remove front handle"

This reverts commit 8d8a40bd4a.
2022-11-17 11:31:31 -06:00
Jack Short
258be9bf65 feat: adding sudoswap to sweep (#5257)
* feat: adding sudoswap amm pricing

* integrated amm pricing on collection

* removing bag recalculation

* feat: adding sudoswap to sweep

* revisions

* typo

* adding back recalculating bag

* reformatting

* simplification

* bag recalculation for sudoswap

* responding to comments

* findindex
2022-11-17 12:04:05 -05:00
Mike Grabowski
8592703931 fix: right arrow in carousel not rotated (#5273)
fix: arrow rotate
2022-11-17 18:02:40 +01:00
Jack Short
44ecc9a203 feat: adding sudoswap price calculation (#5252)
* feat: adding sudoswap amm pricing

* integrated amm pricing on collection

* removing bag recalculation

* revisions

* typo

* adding back recalculating bag

* reformatting

* bag recalculation for sudoswap

* responding to comments
2022-11-17 11:48:10 -05:00
Charles Bachmeier
b19e7809ea feat: simplified duration dropdown (#5264)
* simplified sort dropdown

* add new file

* switch

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-17 08:02:49 -08:00
Callil Capuozzo
43a0bf4c31 style: polish explore banner blur effect (#5267)
* Update Banner.tsx

* Adjust dark theme opacity value
2022-11-16 22:07:30 -05:00
lynn
546423512a feat: Web 2233 erc 1155 card redesign (#5249)
* init

* init

* remove unnecessary style changes

* ensure no pointer events on tooltip
2022-11-16 21:57:25 -05:00
Callil Capuozzo
b13acb33ed style: explore table polish (#5261)
* Normalize explore styles

* slightly reduce row padding
2022-11-16 21:47:18 -05:00
Mike Grabowski
82c3193d21 refactor: better theming (#5259) 2022-11-16 20:26:26 -05:00
Charles Bachmeier
503fc37a4b fix: Can list and relist to all marketplaces (#5250)
* all listing working

* working when connected locally

* update LR and cleanup

* add comment about LR strategy

* use ms 60s

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2022-11-16 15:42:19 -08:00
aballerr
1788c9f3c0 fix: max depth error (#5258)
fixing max depth error
2022-11-16 18:21:50 -05:00
238 changed files with 6036 additions and 3670 deletions

3
.env
View File

@@ -6,5 +6,4 @@ REACT_APP_AWS_API_ACCESS_KEY="AKIAYJJWW6AQ47ODATHN"
REACT_APP_AWS_API_ACCESS_SECRET="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"
REACT_APP_AWS_X_API_KEY="z9dReS5UtHu7iTrUsTuWRozLthi3AxOZlvobrIdr14"
REACT_APP_AWS_API_ENDPOINT="https://beta.api.uniswap.org/v1/graphql"
REACT_APP_TEMP_API_URL="https://temp.api.uniswap.org/v1"
ESLINT_NO_DEV_ERRORS=true
REACT_APP_TEMP_API_URL="https://temp.api.uniswap.org/v1"

View File

@@ -4,3 +4,4 @@ REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
REACT_APP_GOOGLE_ANALYTICS_ID="G-KDP9B6W4H8"
REACT_APP_FIREBASE_KEY="AIzaSyBcZWwTcTJHj_R6ipZcrJkXdq05PuX0Rs0"
REACT_APP_AWS_API_ENDPOINT="https://api.uniswap.org/v1/graphql"
THE_GRAPH_SCHEMA_ENDPOINT="https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3"

View File

@@ -6,5 +6,6 @@ updates:
schedule:
interval: 'daily'
allow:
- dependency-name: '@uniswap/token-lists'
- dependency-name: '@uniswap/default-token-list'
- dependency-name: '@uniswap/token-lists'
- dependency-name: '@uniswap/widgets'

View File

@@ -21,7 +21,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: npx yarn-deduplicate --strategy=highest --list --fail
- run: yarn yarn-deduplicate --strategy=highest --list --fail
unit-tests:
runs-on: ubuntu-latest

View File

@@ -16,4 +16,4 @@
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
}

View File

@@ -21,6 +21,16 @@ module.exports = {
(plugin) => plugin instanceof MiniCssExtractPlugin
)
if (instanceOfMiniCssExtractPlugin !== undefined) instanceOfMiniCssExtractPlugin.options.ignoreOrder = true
// We're currently on Webpack 4.x that doesn't support the `exports` field in package.json.
// See https://github.com/webpack/webpack/issues/9509.
//
// In case you need to add more modules, make sure to remap them to the correct path.
//
// Map @uniswap/conedison to its dist folder.
// This is required because conedison uses * to redirect all imports to its dist.
webpackConfig.resolve.alias['@uniswap/conedison'] = '@uniswap/conedison/dist'
return webpackConfig
},
},

View File

@@ -5,10 +5,18 @@ const dataConfig = require('./relay.config')
const thegraphConfig = require('./relay_thegraph.config')
/* eslint-enable */
const THEGRAPH_API_URL = 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3'
exec(`get-graphql-schema ${THEGRAPH_API_URL} > ${thegraphConfig.schema}`)
function fetchSchema(url, outputFile) {
exec(
`get-graphql-schema --h Origin=https://app.uniswap.org ${url} | tee ${outputFile}.temp`,
(error, stdout, stderr) => {
if (error || stderr) {
console.log(`Failed to fetch schema from ${url}`)
} else if (stdout) {
exec(`mv ${outputFile}.temp ${outputFile}`)
}
}
)
}
console.log(process.env.REACT_APP_AWS_API_ENDPOINT)
exec(
`get-graphql-schema --h Origin=https://app.uniswap.org ${process.env.REACT_APP_AWS_API_ENDPOINT} > ${dataConfig.schema}`
)
fetchSchema(process.env.THE_GRAPH_SCHEMA_ENDPOINT, thegraphConfig.schema)
fetchSchema(process.env.REACT_APP_AWS_API_ENDPOINT, dataConfig.schema)

View File

@@ -20,6 +20,7 @@
"start": "craco start",
"build": "craco build",
"serve": "serve build -l 3000",
"deduplicate": "yarn-deduplicate --strategy=highest",
"lint": "yarn eslint .",
"test": "craco test --coverage",
"cypress:open": "cypress open --browser chrome --e2e",
@@ -113,7 +114,8 @@
"relay-compiler": "^14.1.0",
"serve": "^11.3.2",
"typechain": "^5.0.0",
"typescript": "^4.4.3"
"typescript": "^4.4.3",
"yarn-deduplicate": "^6.0.0"
},
"dependencies": {
"@coinbase/wallet-sdk": "^3.3.0",
@@ -122,7 +124,7 @@
"@lingui/core": "^3.14.0",
"@lingui/macro": "^3.14.0",
"@lingui/react": "^3.14.0",
"@looksrare/sdk": "^0.7.1",
"@looksrare/sdk": "^0.10.2",
"@metamask/jazzicon": "^2.0.0",
"@opensea/seaport-js": "^1.0.2",
"@popperjs/core": "^2.4.4",
@@ -131,8 +133,10 @@
"@react-hook/window-scroll": "^1.3.0",
"@reduxjs/toolkit": "^1.6.1",
"@types/react-relay": "^13.0.2",
"@types/react-window-infinite-loader": "^1.0.6",
"@uniswap/analytics": "1.1.1",
"@uniswap/analytics-events": "1.1.0",
"@uniswap/conedison": "^1.1.0",
"@uniswap/governance": "^1.0.2",
"@uniswap/liquidity-staker": "^1.0.2",
"@uniswap/merkle-distributor": "1.0.1",
@@ -147,7 +151,7 @@
"@uniswap/v3-core": "1.0.0",
"@uniswap/v3-periphery": "^1.1.1",
"@uniswap/v3-sdk": "^3.9.0",
"@uniswap/widgets": "^2.18.0",
"@uniswap/widgets": "^2.19.2",
"@vanilla-extract/css": "^1.7.2",
"@vanilla-extract/css-utils": "^0.1.2",
"@vanilla-extract/dynamic": "^2.0.2",
@@ -160,16 +164,16 @@
"@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/core": "^8.0.35-beta.0",
"@web3-react/eip1193": "^8.0.26-beta.0",
"@web3-react/empty": "^8.0.20-beta.0",
"@web3-react/gnosis-safe": "^8.0.6-beta.0",
"@web3-react/metamask": "^8.0.28-beta.0",
"@web3-react/network": "^8.0.27-beta.0",
"@web3-react/types": "^8.0.20-beta.0",
"@web3-react/url": "^8.0.25-beta.0",
"@web3-react/walletconnect": "^8.0.35-beta.0",
"@web3-react/coinbase-wallet": "8.0.34-beta.0",
"@web3-react/core": "8.0.35-beta.0",
"@web3-react/eip1193": "8.0.26-beta.0",
"@web3-react/empty": "8.0.20-beta.0",
"@web3-react/gnosis-safe": "8.0.7-beta.0",
"@web3-react/metamask": "8.0.28-beta.0",
"@web3-react/network": "8.0.27-beta.0",
"@web3-react/types": "8.0.20-beta.0",
"@web3-react/url": "8.0.25-beta.0",
"@web3-react/walletconnect": "8.0.36-beta.0",
"array.prototype.flat": "^1.2.4",
"array.prototype.flatmap": "^1.2.4",
"cids": "^1.0.0",
@@ -214,6 +218,7 @@
"react-use-gesture": "^6.0.14",
"react-virtualized-auto-sizer": "^1.0.2",
"react-window": "^1.8.5",
"react-window-infinite-loader": "^1.0.8",
"rebass": "^4.0.7",
"redux": "^4.1.2",
"redux-localstorage-simple": "^2.3.1",

View File

@@ -26,5 +26,5 @@
"iconPath": "./images/256x256_App_Icon_Pink.svg",
"short_name": "Uniswap",
"start_url": ".",
"theme_color": "#FC72FFs"
}
"theme_color": "#FC72FF"
}

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.64864 2L1 7.65256L10.5 17.1487L20 7.65256L14.3514 2H6.64864ZM6.13513 5.59458C8.5352 3.18398 12.4648 3.18396 14.8649 5.59456L16.9189 7.64866L14.8649 9.70272C12.4648 12.1133 8.5352 12.1133 6.13513 9.70274L4.08109 7.64866L6.13513 5.59458ZM7.54702 7.64848C7.54702 9.27987 8.86966 10.6012 10.4997 10.6012C12.1298 10.6012 13.4524 9.27987 13.4524 7.64848C13.4524 6.01708 12.1298 4.69576 10.4997 4.69576C8.86966 4.69576 7.54702 6.01708 7.54702 7.64848ZM10.4997 8.93225C9.791 8.93225 9.21593 8.35778 9.21593 7.64848C9.21593 6.93917 9.791 6.3647 10.4997 6.3647C11.2084 6.3647 11.7835 6.93917 11.7835 7.64848C11.7835 8.35778 11.2084 8.93225 10.4997 8.93225Z" fill="#5D6785"/>
</svg>

After

Width:  |  Height:  |  Size: 820 B

View File

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 1C5.0302 1 1 5.0302 1 10C1 14.9698 5.0302 19 10 19C14.9698 19 19 14.9698 19 10C19 5.0302 14.9716 1 10 1ZM5.4406 10.3024L5.4784 10.2412L7.8202 6.5782C7.8544 6.526 7.9354 6.5314 7.9606 6.589C8.3512 7.4656 8.6896 8.5564 8.5312 9.235C8.4646 9.514 8.2792 9.892 8.0704 10.2412C8.0434 10.2916 8.0146 10.342 7.9822 10.3906C7.9678 10.4122 7.9426 10.4248 7.9156 10.4248H5.509C5.4442 10.4248 5.4064 10.3546 5.4406 10.3024ZM15.8752 11.5624C15.8752 11.5966 15.8554 11.6254 15.8266 11.638C15.6448 11.7154 15.0238 12.0016 14.7664 12.3598C14.1076 13.276 13.6054 14.5864 12.4804 14.5864H7.7896C6.1264 14.5864 4.78 13.2346 4.78 11.566V11.512C4.78 11.4688 4.816 11.4328 4.861 11.4328H7.4746C7.5268 11.4328 7.5646 11.4796 7.561 11.5318C7.5412 11.701 7.5736 11.8756 7.6546 12.034C7.8094 12.349 8.1316 12.5452 8.479 12.5452H9.7732V11.5354H8.4934C8.4286 11.5354 8.389 11.4598 8.4268 11.4058C8.4412 11.3842 8.4556 11.3626 8.4736 11.3374C8.5942 11.1646 8.767 10.8982 8.9398 10.594C9.0568 10.3888 9.1702 10.1692 9.262 9.9496C9.28 9.91 9.2944 9.8686 9.3106 9.829C9.3358 9.7588 9.361 9.6922 9.379 9.6274C9.397 9.5716 9.4132 9.514 9.4276 9.46C9.4708 9.2728 9.4888 9.0748 9.4888 8.8696C9.4888 8.7886 9.4852 8.704 9.478 8.6248C9.4744 8.5366 9.4636 8.4484 9.4528 8.3602C9.4456 8.2828 9.4312 8.2054 9.4168 8.1262C9.397 8.0092 9.3718 7.8922 9.343 7.7752L9.3322 7.7302C9.3106 7.6492 9.2908 7.5736 9.2656 7.4926C9.1918 7.2406 9.109 6.994 9.019 6.7636C8.9866 6.6718 8.9506 6.5836 8.9128 6.4972C8.8588 6.364 8.803 6.2434 8.7526 6.13C8.7256 6.0778 8.704 6.031 8.6824 5.9824C8.6572 5.9284 8.632 5.8744 8.605 5.8222C8.587 5.7826 8.5654 5.7448 8.551 5.7088L8.3926 5.4172C8.371 5.3776 8.407 5.329 8.4502 5.3416L9.4402 5.6098H9.4438C9.4456 5.6098 9.4456 5.6098 9.4474 5.6098L9.577 5.6476L9.721 5.6872L9.7732 5.7016V5.1148C9.7732 4.8304 10 4.6 10.2826 4.6C10.423 4.6 10.5508 4.6576 10.6408 4.7512C10.7326 4.8448 10.7902 4.9726 10.7902 5.1148V5.9878L10.8964 6.0166C10.9036 6.0202 10.9126 6.0238 10.9198 6.0292C10.945 6.0472 10.9828 6.076 11.0296 6.112C11.0674 6.1408 11.107 6.1768 11.1538 6.2146C11.2492 6.292 11.3644 6.391 11.4886 6.5044C11.521 6.5332 11.5534 6.562 11.584 6.5926C11.7442 6.742 11.9242 6.9166 12.097 7.111C12.1456 7.1668 12.1924 7.2208 12.241 7.2802C12.2878 7.3396 12.34 7.3972 12.3832 7.4548C12.4426 7.5322 12.5038 7.6132 12.5596 7.6978C12.5848 7.7374 12.6154 7.7788 12.6388 7.8184C12.7108 7.9246 12.772 8.0344 12.8314 8.1442C12.8566 8.1946 12.8818 8.2504 12.9034 8.3044C12.97 8.452 13.0222 8.6014 13.0546 8.7526C13.0654 8.785 13.0726 8.8192 13.0762 8.8516V8.8588C13.087 8.902 13.0906 8.9488 13.0942 8.9974C13.1086 9.1504 13.1014 9.3052 13.069 9.46C13.0546 9.5248 13.0366 9.586 13.015 9.6526C12.9916 9.7156 12.97 9.7804 12.9412 9.8434C12.8854 9.9712 12.8206 10.1008 12.7432 10.2196C12.718 10.2646 12.6874 10.3114 12.6586 10.3564C12.6262 10.4032 12.592 10.4482 12.5632 10.4914C12.5218 10.5472 12.4786 10.6048 12.4336 10.657C12.394 10.711 12.3544 10.765 12.3094 10.8136C12.2482 10.8874 12.1888 10.9558 12.1258 11.0224C12.0898 11.0656 12.0502 11.1106 12.0088 11.1502C11.9692 11.1952 11.9278 11.2348 11.8918 11.2708C11.8288 11.3338 11.7784 11.3806 11.7352 11.422L11.6326 11.5138C11.6182 11.5282 11.5984 11.5354 11.5786 11.5354H10.7902V12.5452H11.782C12.0034 12.5452 12.214 12.4678 12.385 12.322C12.4426 12.2716 12.6964 12.052 12.997 11.7208C13.0078 11.7082 13.0204 11.701 13.0348 11.6974L15.7726 10.9054C15.8248 10.891 15.8752 10.9288 15.8752 10.9828V11.5624Z" fill="#5D6785"/>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.146 4.52803C15.767 3.18049 13.8805 2.35 11.8 2.35C7.57502 2.35 4.15 5.77502 4.15 10C4.15 14.225 7.57502 17.65 11.8 17.65C13.8805 17.65 15.767 16.8195 17.146 15.472C15.501 17.617 12.912 19 10 19C5.02944 19 1 14.9706 1 10C1 5.02944 5.02944 1 10 1C12.912 1 15.501 2.38301 17.146 4.52803Z" fill="#5D6785"/>
<path d="M6.08317 14.3776C7.18644 15.4556 8.69563 16.12 10.36 16.12C13.74 16.12 16.48 13.38 16.48 10C16.48 6.62002 13.74 3.88 10.36 3.88C8.69563 3.88 7.18644 4.54439 6.08317 5.62243C7.39916 3.90641 9.47037 2.8 11.8 2.8C15.7765 2.8 19 6.02355 19 10C19 13.9764 15.7764 17.2 11.8 17.2C9.47037 17.2 7.39916 16.0936 6.08317 14.3776Z" fill="#5D6785"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.4 10C15.4 12.9823 12.9823 15.4 10 15.4C7.01766 15.4 4.6 12.9823 4.6 10C4.6 7.01766 7.01766 4.6 10 4.6C12.9823 4.6 15.4 7.01766 15.4 10ZM13.6 10C13.6 11.9882 11.9882 13.6 10 13.6C8.01177 13.6 6.4 11.9882 6.4 10C6.4 8.01178 8.01177 6.4 10 6.4C11.9882 6.4 13.6 8.01178 13.6 10Z" fill="#5D6785"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,218 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "token_",
"type": "address"
},
{
"internalType": "bytes32",
"name": "merkleRoot_",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "endTime_",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "AlreadyClaimed",
"type": "error"
},
{
"inputs": [],
"name": "ClaimWindowFinished",
"type": "error"
},
{
"inputs": [],
"name": "EndTimeInPast",
"type": "error"
},
{
"inputs": [],
"name": "InvalidProof",
"type": "error"
},
{
"inputs": [],
"name": "NoWithdrawDuringClaim",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "index",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "account",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "Claimed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "index",
"type": "uint256"
},
{
"internalType": "address",
"name": "account",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "bytes32[]",
"name": "merkleProof",
"type": "bytes32[]"
}
],
"name": "claim",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "endTime",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "index",
"type": "uint256"
}
],
"name": "isClaimed",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "merkleRoot",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "token",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "withdraw",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 927 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 KiB

4
src/assets/svg/eye.svg Normal file
View File

@@ -0,0 +1,4 @@
<svg width="54" height="40" viewBox="0 0 54 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.3335 19.9997C1.3335 19.9997 10.6668 1.33301 27.0002 1.33301C43.3335 1.33301 52.6668 19.9997 52.6668 19.9997C52.6668 19.9997 43.3335 38.6663 27.0002 38.6663C10.6668 38.6663 1.3335 19.9997 1.3335 19.9997Z" stroke="#98A1C0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M27.0002 26.9997C30.8662 26.9997 34.0002 23.8657 34.0002 19.9997C34.0002 16.1337 30.8662 12.9997 27.0002 12.9997C23.1342 12.9997 20.0002 16.1337 20.0002 19.9997C20.0002 23.8657 23.1342 26.9997 27.0002 26.9997Z" stroke="#98A1C0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 705 B

View File

@@ -7,6 +7,7 @@ import { useAppDispatch } from 'state/hooks'
import { updateSelectedWallet } from 'state/user/reducer'
import { removeConnectedWallet } from 'state/wallets/reducer'
import styled, { useTheme } from 'styled-components/macro'
import { flexColumnNoWrap, flexRowNoWrap } from 'theme/styles'
import { isMobile } from 'utils/userAgent'
import { ReactComponent as Close } from '../../assets/images/x.svg'
@@ -20,7 +21,7 @@ import { AutoRow } from '../Row'
import Transaction from './Transaction'
const HeaderRow = styled.div`
${({ theme }) => theme.flexRowNoWrap};
${flexRowNoWrap};
padding: 1rem 1rem;
font-weight: 500;
color: ${(props) => (props.color === 'blue' ? ({ theme }) => theme.deprecated_primary1 : 'inherit')};
@@ -60,14 +61,14 @@ const InfoCard = styled.div`
`
const AccountGroupingRow = styled.div`
${({ theme }) => theme.flexRowNoWrap};
${flexColumnNoWrap};
justify-content: space-between;
align-items: center;
font-weight: 400;
color: ${({ theme }) => theme.deprecated_text1};
div {
${({ theme }) => theme.flexRowNoWrap}
${flexColumnNoWrap};
align-items: center;
}
`
@@ -90,7 +91,7 @@ const YourAccount = styled.div`
`
const LowerSection = styled.div`
${({ theme }) => theme.flexColumnNoWrap}
${flexColumnNoWrap};
padding: 1.5rem;
flex-grow: 1;
overflow: auto;
@@ -163,7 +164,7 @@ const WalletName = styled.div`
`
const TransactionListWrapper = styled.div`
${({ theme }) => theme.flexColumnNoWrap};
${flexColumnNoWrap};
`
const WalletAction = styled(ButtonSecondary)`

View File

@@ -5,7 +5,7 @@ import styled, { css } from 'styled-components/macro'
import { nativeOnChain } from '../../constants/tokens'
import { useCurrency } from '../../hooks/Tokens'
import CurrencyLogo from '../CurrencyLogo'
import CurrencyLogo from '../Logo/CurrencyLogo'
const CurrencyWrap = styled.div`
position: relative;

View File

@@ -4,6 +4,7 @@ import { t } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import { ChangeEvent, ReactNode, useCallback } from 'react'
import styled, { useTheme } from 'styled-components/macro'
import { flexColumnNoWrap } from 'theme/styles'
import useENS from '../../hooks/useENS'
import { ExternalLink, ThemedText } from '../../theme'
@@ -12,7 +13,7 @@ import { AutoColumn } from '../Column'
import { RowBetween } from '../Row'
const InputPanel = styled.div`
${({ theme }) => theme.flexColumnNoWrap}
${flexColumnNoWrap};
position: relative;
border-radius: 1.25rem;
background-color: ${({ theme }) => theme.deprecated_bg1};

View File

@@ -0,0 +1,333 @@
import { BigNumber } from '@ethersproject/bignumber'
import type { TransactionResponse } from '@ethersproject/providers'
import { useWeb3React } from '@web3-react/core'
import uniswapNftAirdropClaim from 'abis/uniswap-nft-airdrop-claim.json'
import airdropBackgroundv2 from 'assets/images/airdopBackground.png'
import { ButtonEmphasis, ButtonSize, ThemeButton } from 'components/Button'
import { OpacityHoverState } from 'components/Common'
import Loader from 'components/Loader'
import { UNISWAP_NFT_AIRDROP_CLAIM_ADDRESS } from 'constants/addresses'
import { useContract } from 'hooks/useContract'
import { ChevronRightIcon } from 'nft/components/icons'
import { useIsNftClaimAvailable } from 'nft/hooks/useIsNftClaimAvailable'
import { CollectionRewardsFetcher } from 'nft/queries/genie/GetAirdorpMerkle'
import { Airdrop, Rewards } from 'nft/types/airdrop'
import { useEffect, useState } from 'react'
import { AlertTriangle } from 'react-feather'
import { useModalIsOpen, useToggleModal } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import styled from 'styled-components/macro'
import { CloseIcon, ThemedText } from 'theme'
import Modal from '../Modal'
const ModalWrap = styled.div`
display: flex;
flex-direction: column;
`
const Body = styled.div`
padding: 28px 20px 20px 20px;
`
const ClaimButton = styled(ThemeButton)`
width: 100%;
background-color: ${({ theme }) => theme.accentAction};
border-radius: 12px;
color: ${({ theme }) => theme.white};
`
const Line = styled.div`
height: 1px;
width: 100%;
background-color: ${({ theme }) => theme.white};
opacity: 0.24;
margin-top: 12px;
margin-bottom: 12px;
`
const LinkWrap = styled.a`
text-decoration: none;
${OpacityHoverState}
`
const ImageContainer = styled.div`
position: relative;
width: 100%;
`
const StyledImage = styled.img`
width: 100%;
height: 170px;
`
const USDCLabel = styled.div`
font-weight: 700;
font-size: 36px;
line-height: 44px;
margin-top: 8px;
color: white;
`
const TextContainer = styled.div`
position: absolute;
left: 16px;
top: 16px;
right: 16px;
`
const RewardsDetailsContainer = styled.div`
display: flex;
width: 100%;
justify-content: space-between;
`
const CurrencyText = styled.span`
color: white;
font-weight: 500;
font-size: 12px;
line-height: 14.5px;
`
const ClaimContainer = styled.div`
display: flex;
flex-direction: column;
text-align: center;
height: 380px;
padding: 60px 28px;
padding-bottom: 20px;
`
const SuccessText = styled.div`
font-weight: 400;
font-size: 16px;
line-height: 24px;
margin-top: 24px;
margin-bottom: 8px;
`
const EtherscanLink = styled.a`
text-decoration: none;
${OpacityHoverState}
`
const CloseButton = styled(ThemeButton)`
max-width: 68px;
margin-top: auto;
margin-left: auto;
margin-right: auto;
`
const SyledCloseIcon = styled(CloseIcon)`
float: right;
height: 24px;
${OpacityHoverState}
`
const Error = styled.div`
display: flex;
color: ${({ theme }) => theme.accentCritical};
font-weight: 500;
line-height: 24px;
border-radius: 16px;
padding: 12px 20px;
font-size: 14px;
align-items: center;
gap: 12px;
`
const ReactLinkWrap = styled.div`
margin-bottom: 40px;
`
const RewardsText = styled.span`
font-size: 12px;
line-height: 16px;
color: ${({ theme }) => theme.white};
&:first-child {
margin-bottom: 8px;
}
`
const RewardsInformationText = styled.span`
display: inline-block;
font-size: 14px;
line-height: 20px;
color: ${({ theme }) => theme.textPrimary};
margin-bottom: 28px;
`
const MainHeader = styled.span`
font-weight: 600;
font-size: 16px;
line-height: 20px;
color: ${({ theme }) => theme.white};
`
const EtherscanLinkWrap = styled.div`
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
`
enum RewardAmounts {
tradingRewardAmount = 300,
holderRewardAmount = 1000,
combinedAmount = 1300,
}
const AirdropModal = () => {
const { account, provider } = useWeb3React()
const [claim, setClaim] = useState<Rewards>()
const [isClaimed, setIsClaimed] = useState(false)
const [hash, setHash] = useState('')
const [error, setError] = useState(false)
const setIsClaimAvailable = useIsNftClaimAvailable((state) => state.setIsClaimAvailable)
const [isSubmitting, setIsSubmitting] = useState(false)
const [totalAmount, setTotalAmount] = useState(0)
const isOpen = useModalIsOpen(ApplicationModal.UNISWAP_NFT_AIRDROP_CLAIM)
const usdcAirdropToggle = useToggleModal(ApplicationModal.UNISWAP_NFT_AIRDROP_CLAIM)
const contract = useContract(UNISWAP_NFT_AIRDROP_CLAIM_ADDRESS, uniswapNftAirdropClaim)
const displayError = () => {
setIsSubmitting(false)
setError(true)
setTimeout(() => {
setError(false)
}, 5000)
}
useEffect(() => {
if (account && provider && contract) {
;(async () => {
try {
const { data } = await CollectionRewardsFetcher(account)
const claim = data.find((claim) => claim?.rewardType === Airdrop.GENIE_UNISWAP_USDC_AIRDROP)
if (!claim) return
const [isClaimed] = await contract.connect(provider).functions.isClaimed(claim?.index)
if (claim && isClaimed === false) {
const usdAmount = BigNumber.from(claim.amount).div(10 ** 6)
setClaim(claim)
setTotalAmount(usdAmount.toNumber())
setIsClaimAvailable(true)
}
} catch (err) {
displayError()
}
})()
}
}, [account, contract, provider, setIsClaimAvailable])
const makeClaim = async () => {
try {
if (contract && claim && claim.amount && claim.merkleProof && provider) {
setIsSubmitting(true)
const response: TransactionResponse = await contract
.connect(provider?.getSigner())
.functions.claim(claim.index, account, claim?.amount, claim?.merkleProof)
setHash(response.hash)
setIsSubmitting(false)
setIsClaimed(true)
setIsClaimAvailable(false)
}
} catch (err) {
setIsSubmitting(false)
displayError()
}
}
return (
<>
<Modal hideBorder isOpen={isOpen} onDismiss={usdcAirdropToggle} maxHeight={90} maxWidth={400}>
<ModalWrap>
{isClaimed ? (
<ClaimContainer>
<ThemedText.HeadlineSmall>Congratulations!</ThemedText.HeadlineSmall>
<SuccessText>
You have successfully claimed {totalAmount} USDC. Thank you for supporting Genie.xyz.
</SuccessText>
<EtherscanLink href={`https://etherscan.io/tx/${hash}`} target="_blank">
<ThemedText.Link>
<EtherscanLinkWrap>
<span>Etherscan</span>
<ChevronRightIcon />
</EtherscanLinkWrap>
</ThemedText.Link>
</EtherscanLink>
<CloseButton size={ButtonSize.medium} emphasis={ButtonEmphasis.medium} onClick={usdcAirdropToggle}>
Close
</CloseButton>
</ClaimContainer>
) : (
<>
<ImageContainer>
<TextContainer>
<SyledCloseIcon onClick={usdcAirdropToggle} stroke="white" />
<MainHeader>Uniswap NFT Airdrop</MainHeader>
<USDCLabel>{totalAmount} USDC</USDCLabel>
<Line />
<RewardsDetailsContainer>
<RewardsText>Trading rewards</RewardsText>{' '}
<CurrencyText>
{totalAmount === RewardAmounts.tradingRewardAmount || totalAmount === RewardAmounts.combinedAmount
? `${RewardAmounts.tradingRewardAmount} USDC`
: '0'}
</CurrencyText>
</RewardsDetailsContainer>
<RewardsDetailsContainer>
<RewardsText>Genie NFT holder rewards</RewardsText>{' '}
<CurrencyText>
{totalAmount !== RewardAmounts.tradingRewardAmount
? `${RewardAmounts.holderRewardAmount} USDC`
: '0'}
</CurrencyText>
</RewardsDetailsContainer>
</TextContainer>
<StyledImage src={airdropBackgroundv2} />
</ImageContainer>
<Body>
<RewardsInformationText>
As a long time supporter of Genie, youve been awarded {totalAmount} USDC tokens.
</RewardsInformationText>
<ReactLinkWrap>
<LinkWrap href="https://uniswap.org/blog/uniswap-nft-aggregator-announcement" target="_blank">
<ThemedText.Link>Read more about Uniswap NFT.</ThemedText.Link>
</LinkWrap>
</ReactLinkWrap>
{error && (
<Error>
<AlertTriangle />
Claim USDC failed. Please try again later
</Error>
)}
<ClaimButton
onClick={makeClaim}
size={ButtonSize.medium}
emphasis={ButtonEmphasis.medium}
disabled={isSubmitting}
>
{isSubmitting && <Loader stroke="white" />}
<span>Claim{isSubmitting && 'ing'} USDC</span>
</ClaimButton>
</Body>
</>
)}
</ModalWrap>
</Modal>
</>
)
}
export default AirdropModal

View File

@@ -1,7 +1,6 @@
import { readableColor } from 'polished'
import { PropsWithChildren } from 'react'
import styled, { DefaultTheme } from 'styled-components/macro'
import { Color } from 'theme/styled'
export enum BadgeVariant {
DEFAULT = 'DEFAULT',
@@ -17,7 +16,7 @@ interface BadgeProps {
variant?: BadgeVariant
}
function pickBackgroundColor(variant: BadgeVariant | undefined, theme: DefaultTheme): Color {
function pickBackgroundColor(variant: BadgeVariant | undefined, theme: DefaultTheme): string {
switch (variant) {
case BadgeVariant.NEGATIVE:
return theme.deprecated_error

View File

@@ -414,7 +414,42 @@ function pickThemeButtonBackgroundColor({ theme, emphasis }: { theme: DefaultThe
return theme.backgroundInteractive
}
}
function pickThemeButtonFontSize({ size }: { size: ButtonSize }) {
switch (size) {
case ButtonSize.large:
return '20px'
case ButtonSize.medium:
return '16px'
case ButtonSize.small:
return '14px'
default:
return '16px'
}
}
function pickThemeButtonLineHeight({ size }: { size: ButtonSize }) {
switch (size) {
case ButtonSize.large:
return '24px'
case ButtonSize.medium:
return '20px'
case ButtonSize.small:
return '16px'
default:
return '20px'
}
}
function pickThemeButtonPadding({ size }: { size: ButtonSize }) {
switch (size) {
case ButtonSize.large:
return '16px'
case ButtonSize.medium:
return '10px 12px'
case ButtonSize.small:
return '8px'
default:
return '10px 12px'
}
}
function pickThemeButtonTextColor({ theme, emphasis }: { theme: DefaultTheme; emphasis: ButtonEmphasis }) {
switch (emphasis) {
case ButtonEmphasis.high:
@@ -443,10 +478,12 @@ const BaseThemeButton = styled.button<BaseButtonProps>`
cursor: pointer;
display: flex;
flex-direction: row;
font-size: ${pickThemeButtonFontSize};
font-weight: 600;
gap: 12px;
justify-content: center;
padding: 16px;
line-height: ${pickThemeButtonLineHeight};
padding: ${pickThemeButtonPadding};
position: relative;
transition: 150ms ease opacity;

View File

@@ -4,7 +4,6 @@ import { CurveFactory } from 'd3'
import React from 'react'
import { ReactNode } from 'react'
import { useTheme } from 'styled-components/macro'
import { Color } from 'theme/styled'
export interface LineChartProps<T> {
data: T[]
@@ -12,7 +11,7 @@ export interface LineChartProps<T> {
getY: (t: T) => number
marginTop?: number
curve: CurveFactory
color?: Color
color?: string
strokeWidth: number
children?: ReactNode
width: number

View File

@@ -1,6 +1,6 @@
import { Trans } from '@lingui/macro'
import Column from 'components/Column'
import { AlertOctagon } from 'react-feather'
import { BlockedIcon } from 'components/TokenSafety/TokenSafetyIcon'
import styled, { useTheme } from 'styled-components/macro'
import { ExternalLink, ThemedText } from 'theme'
@@ -13,11 +13,6 @@ const ContentWrapper = styled(Column)`
text-align: center;
font-size: 12px;
`
const WarningIcon = styled(AlertOctagon)`
min-height: 22px;
min-width: 22px;
color: ${({ theme }) => theme.deprecated_warning};
`
const Copy = styled(CopyHelper)`
font-size: 12px;
`
@@ -32,7 +27,7 @@ export default function ConnectedAccountBlocked(props: ConnectedAccountBlockedPr
return (
<Modal isOpen={props.isOpen} onDismiss={Function.prototype()}>
<ContentWrapper>
<WarningIcon />
<BlockedIcon size="22px" />
<ThemedText.DeprecatedLargeHeader lineHeight={2} marginBottom={1} marginTop={1}>
<Trans>Blocked Address</Trans>
</ThemedText.DeprecatedLargeHeader>

View File

@@ -6,18 +6,19 @@ import { Pair } from '@uniswap/v2-sdk'
import { useWeb3React } from '@web3-react/core'
import { AutoColumn } from 'components/Column'
import { LoadingOpacityContainer, loadingOpacityMixin } from 'components/Loader/styled'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
import { isSupportedChain } from 'constants/chains'
import { darken } from 'polished'
import { ReactNode, useCallback, useState } from 'react'
import { Lock } from 'react-feather'
import styled, { useTheme } from 'styled-components/macro'
import { flexColumnNoWrap, flexRowNoWrap } from 'theme/styles'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
import { useCurrencyBalance } from '../../state/connection/hooks'
import { ThemedText } from '../../theme'
import { ButtonGray } from '../Button'
import CurrencyLogo from '../CurrencyLogo'
import DoubleCurrencyLogo from '../DoubleLogo'
import { Input as NumericalInput } from '../NumericalInput'
import { RowBetween, RowFixed } from '../Row'
@@ -25,7 +26,7 @@ import CurrencySearchModal from '../SearchModal/CurrencySearchModal'
import { FiatValue } from './FiatValue'
const InputPanel = styled.div<{ hideInput?: boolean }>`
${({ theme }) => theme.flexColumnNoWrap}
${flexColumnNoWrap};
position: relative;
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
z-index: 1;
@@ -106,13 +107,13 @@ const CurrencySelect = styled(ButtonGray)<{
`
const InputRow = styled.div`
${({ theme }) => theme.flexRowNoWrap}
${flexRowNoWrap};
align-items: center;
justify-content: space-between;
`
const LabelRow = styled.div`
${({ theme }) => theme.flexRowNoWrap}
${flexRowNoWrap};
align-items: center;
color: ${({ theme }) => theme.textSecondary};
font-size: 0.75rem;
@@ -301,7 +302,7 @@ export default function SwapCurrencyInputPanel({
</Aligner>
</CurrencySelect>
</InputRow>
{!hideInput && !hideBalance && currency && (
{Boolean(!hideInput && !hideBalance) && (
<FiatRow>
<RowBetween>
<LoadingOpacityContainer $loading={loading}>

View File

@@ -11,21 +11,22 @@ import { darken } from 'polished'
import { ReactNode, useCallback, useState } from 'react'
import { Lock } from 'react-feather'
import styled, { useTheme } from 'styled-components/macro'
import { flexColumnNoWrap, flexRowNoWrap } from 'theme/styles'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
import { useCurrencyBalance } from '../../state/connection/hooks'
import { ThemedText } from '../../theme'
import { ButtonGray } from '../Button'
import CurrencyLogo from '../CurrencyLogo'
import DoubleCurrencyLogo from '../DoubleLogo'
import CurrencyLogo from '../Logo/CurrencyLogo'
import { Input as NumericalInput } from '../NumericalInput'
import { RowBetween, RowFixed } from '../Row'
import CurrencySearchModal from '../SearchModal/CurrencySearchModal'
import { FiatValue } from './FiatValue'
const InputPanel = styled.div<{ hideInput?: boolean }>`
${({ theme }) => theme.flexColumnNoWrap}
${flexColumnNoWrap};
position: relative;
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
background-color: ${({ theme, hideInput }) => (hideInput ? 'transparent' : theme.deprecated_bg2)};
@@ -95,14 +96,14 @@ const CurrencySelect = styled(ButtonGray)<{
`
const InputRow = styled.div<{ selected: boolean }>`
${({ theme }) => theme.flexRowNoWrap}
${flexRowNoWrap};
align-items: center;
justify-content: space-between;
padding: ${({ selected }) => (selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 1rem 1rem')};
`
const LabelRow = styled.div`
${({ theme }) => theme.flexRowNoWrap}
${flexRowNoWrap};
align-items: center;
color: ${({ theme }) => theme.deprecated_text1};
font-size: 0.75rem;

View File

@@ -1,51 +0,0 @@
import { Currency } from '@uniswap/sdk-core'
import useCurrencyLogoURIs from 'lib/hooks/useCurrencyLogoURIs'
import React, { useMemo } from 'react'
import styled from 'styled-components/macro'
import Logo from '../Logo'
const StyledLogo = styled(Logo)<{ size: string }>`
width: ${({ size }) => size};
height: ${({ size }) => size};
background: radial-gradient(white 50%, #ffffff00 calc(75% + 1px), #ffffff00 100%);
border-radius: 50%;
-mox-box-shadow: 0 0 1px black;
-webkit-box-shadow: 0 0 1px black;
box-shadow: 0 0 1px black;
border: 0px solid rgba(255, 255, 255, 0);
`
const StyledNativeLogo = styled(StyledLogo)`
-mox-box-shadow: 0 0 1px white;
-webkit-box-shadow: 0 0 1px white;
box-shadow: 0 0 1px white;
`
export default function CurrencyLogo({
currency,
symbol,
size = '24px',
style,
src,
...rest
}: {
currency?: Currency | null
symbol?: string | null
size?: string
style?: React.CSSProperties
src?: string | null
}) {
const logoURIs = useCurrencyLogoURIs(currency)
const srcs = useMemo(() => (src ? [src, ...logoURIs] : logoURIs), [src, logoURIs])
const props = {
alt: `${currency?.symbol ?? 'token'} logo`,
size,
srcs,
symbol: symbol ?? currency?.symbol,
style,
...rest,
}
return currency?.isNative ? <StyledNativeLogo {...props} /> : <StyledLogo {...props} />
}

View File

@@ -1,7 +1,7 @@
import { Currency } from '@uniswap/sdk-core'
import styled from 'styled-components/macro'
import CurrencyLogo from '../CurrencyLogo'
import CurrencyLogo from '../Logo/CurrencyLogo'
const Wrapper = styled.div<{ margin: boolean; sizeraw: number }>`
position: relative;

View File

@@ -2,6 +2,7 @@ import { useWeb3React } from '@web3-react/core'
import { ConnectionType } from 'connection'
import useENSAvatar from 'hooks/useENSAvatar'
import styled from 'styled-components/macro'
import { flexColumnNoWrap } from 'theme/styles'
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
@@ -11,7 +12,7 @@ import Identicon from '../Identicon'
export const IconWrapper = styled.div<{ size?: number }>`
position: relative;
${({ theme }) => theme.flexColumnNoWrap};
${flexColumnNoWrap};
align-items: center;
justify-content: center;
margin-right: 8px;

View File

@@ -0,0 +1,68 @@
import { SupportedChainId } from 'constants/chains'
import useTokenLogoSource from 'hooks/useAssetLogoSource'
import React from 'react'
import styled from 'styled-components/macro'
export const MissingImageLogo = styled.div<{ size?: string }>`
--size: ${({ size }) => size};
border-radius: 100px;
color: ${({ theme }) => theme.textPrimary};
background-color: ${({ theme }) => theme.backgroundInteractive};
font-size: calc(var(--size) / 3);
font-weight: 500;
height: ${({ size }) => size ?? '24px'};
line-height: ${({ size }) => size ?? '24px'};
text-align: center;
width: ${({ size }) => size ?? '24px'};
`
const LogoImage = styled.img<{ size: string }>`
width: ${({ size }) => size};
height: ${({ size }) => size};
background: radial-gradient(white 60%, #ffffff00 calc(70% + 1px));
border-radius: 50%;
box-shadow: 0 0 1px white;
`
export type AssetLogoBaseProps = {
symbol?: string | null
backupImg?: string | null
size?: string
style?: React.CSSProperties
}
type AssetLogoProps = AssetLogoBaseProps & { isNative?: boolean; address?: string | null; chainId?: number }
// TODO(cartcrom): add prop to optionally render an L2Icon w/ the logo
/**
* Renders an image by prioritizing a list of sources, and then eventually a fallback triangle alert
*/
export default function AssetLogo({
isNative,
address,
chainId = SupportedChainId.MAINNET,
symbol,
backupImg,
size = '24px',
style,
...rest
}: AssetLogoProps) {
const imageProps = {
alt: `${symbol ?? 'token'} logo`,
size,
style,
...rest,
}
const [src, nextSrc] = useTokenLogoSource(address, chainId, isNative, backupImg)
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>
)
}
}

View File

@@ -0,0 +1,21 @@
import { Currency } from '@uniswap/sdk-core'
import { TokenInfo } from '@uniswap/token-lists'
import AssetLogo, { AssetLogoBaseProps } from './AssetLogo'
export default function CurrencyLogo(
props: AssetLogoBaseProps & {
currency?: Currency | null
}
) {
return (
<AssetLogo
isNative={props.currency?.isNative}
chainId={props.currency?.chainId}
address={props.currency?.wrapped.address}
symbol={props.symbol ?? props.currency?.symbol}
backupImg={(props.currency as TokenInfo)?.logoURI}
{...props}
/>
)
}

View File

@@ -0,0 +1,25 @@
import { NATIVE_CHAIN_ID } from 'constants/tokens'
import { TokenQueryData } from 'graphql/data/Token'
import { TopToken } from 'graphql/data/TopTokens'
import { CHAIN_NAME_TO_CHAIN_ID } from 'graphql/data/util'
import AssetLogo, { AssetLogoBaseProps } from './AssetLogo'
export default function QueryTokenLogo(
props: AssetLogoBaseProps & {
token?: TopToken | TokenQueryData
}
) {
const chainId = props.token?.chain ? CHAIN_NAME_TO_CHAIN_ID[props.token?.chain] : undefined
return (
<AssetLogo
isNative={props.token?.address === NATIVE_CHAIN_ID}
chainId={chainId}
address={props.token?.address}
symbol={props.token?.symbol}
backupImg={props.token?.project?.logoUrl}
{...props}
/>
)
}

View File

@@ -1,55 +0,0 @@
import { useState } from 'react'
import { ImageProps } from 'rebass'
import styled from 'styled-components/macro'
const BAD_SRCS: { [tokenAddress: string]: true } = {}
interface LogoProps extends Pick<ImageProps, 'style' | 'alt' | 'className'> {
srcs: string[]
symbol?: string
size?: string
}
const MissingImageLogo = styled.div<{ size?: string }>`
--size: ${({ size }) => size};
border-radius: 100px;
color: ${({ theme }) => theme.textPrimary};
background-color: ${({ theme }) => theme.backgroundInteractive};
font-size: calc(var(--size) / 3);
font-weight: 500;
height: ${({ size }) => size ?? '24px'};
line-height: ${({ size }) => size ?? '24px'};
text-align: center;
width: ${({ size }) => size ?? '24px'};
`
/**
* Renders an image by sequentially trying a list of URIs, and then eventually a fallback triangle alert
*/
export default function Logo({ srcs, alt, style, size, symbol, ...rest }: LogoProps) {
const [, refresh] = useState<number>(0)
const src: string | undefined = srcs.find((src) => !BAD_SRCS[src])
if (src) {
return (
<img
{...rest}
alt={alt}
src={src}
style={style}
onError={() => {
if (src) BAD_SRCS[src] = true
refresh((i) => i + 1)
}}
/>
)
}
return (
<MissingImageLogo size={size}>
{/* use only first 3 characters of Symbol for design reasons */}
{symbol?.toUpperCase().replace('$', '').replace(/\s+/g, '').slice(0, 3)}
</MissingImageLogo>
)
}

View File

@@ -8,7 +8,7 @@ import { Z_INDEX } from 'theme/zIndex'
import { isMobile } from '../../utils/userAgent'
const AnimatedDialogOverlay = animated(DialogOverlay)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const StyledDialogOverlay = styled(AnimatedDialogOverlay)<{ scrollOverlay?: boolean }>`
&[data-reach-dialog-overlay] {
z-index: ${Z_INDEX.modalBackdrop};
@@ -24,49 +24,51 @@ const StyledDialogOverlay = styled(AnimatedDialogOverlay)<{ scrollOverlay?: bool
}
`
type StyledDialogProps = {
$minHeight?: number | false
$maxHeight?: number
$isBottomSheet?: boolean
$scrollOverlay?: boolean
$hideBorder?: boolean
$maxWidth: number
}
const AnimatedDialogContent = animated(DialogContent)
// destructure to not pass custom props to Dialog DOM element
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, scrollOverlay, ...rest }) => (
<AnimatedDialogContent {...rest} />
)).attrs({
'aria-label': 'dialog',
})`
const StyledDialogContent = styled(AnimatedDialogContent)<StyledDialogProps>`
overflow-y: auto;
&[data-reach-dialog-content] {
margin: auto;
background-color: ${({ theme }) => theme.deprecated_bg0};
border: 1px solid ${({ theme }) => theme.deprecated_bg1};
border: ${({ theme, $hideBorder }) => !$hideBorder && `1px solid ${theme.deprecated_bg1}`};
box-shadow: ${({ theme }) => theme.deepShadow};
padding: 0px;
width: 50vw;
overflow-y: auto;
overflow-x: hidden;
align-self: ${({ mobile }) => mobile && 'flex-end'};
max-width: 420px;
${({ maxHeight }) =>
maxHeight &&
align-self: ${({ $isBottomSheet }) => $isBottomSheet && 'flex-end'};
max-width: ${({ $maxWidth }) => $maxWidth}px;
${({ $maxHeight }) =>
$maxHeight &&
css`
max-height: ${maxHeight}vh;
max-height: ${$maxHeight}vh;
`}
${({ minHeight }) =>
minHeight &&
${({ $minHeight }) =>
$minHeight &&
css`
min-height: ${minHeight}vh;
min-height: ${$minHeight}vh;
`}
display: ${({ scrollOverlay }) => (scrollOverlay ? 'inline-table' : 'flex')};
display: ${({ $scrollOverlay }) => ($scrollOverlay ? 'inline-table' : 'flex')};
border-radius: 20px;
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
width: 65vw;
margin: auto;
`}
${({ theme, mobile }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
${({ theme, $isBottomSheet }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
width: 85vw;
${
mobile &&
$isBottomSheet &&
css`
width: 100vw;
border-radius: 20px;
@@ -80,12 +82,16 @@ const StyledDialogContent = styled(({ minHeight, maxHeight, mobile, isOpen, scro
interface ModalProps {
isOpen: boolean
onDismiss: () => void
onDismiss?: () => void
onSwipe?: () => void
minHeight?: number | false
maxHeight?: number
maxWidth?: number
initialFocusRef?: React.RefObject<any>
children?: React.ReactNode
scrollOverlay?: boolean
hideBorder?: boolean
isBottomSheet?: boolean
}
export default function Modal({
@@ -93,9 +99,13 @@ export default function Modal({
onDismiss,
minHeight = false,
maxHeight = 90,
maxWidth = 420,
initialFocusRef,
children,
onSwipe = onDismiss,
scrollOverlay,
isBottomSheet = isMobile,
hideBorder = false,
}: ModalProps) {
const fadeTransition = useTransition(isOpen, {
config: { duration: 200 },
@@ -111,7 +121,7 @@ export default function Modal({
y: state.down ? state.movement[1] : 0,
})
if (state.movement[1] > 300 || (state.velocity > 3 && state.direction[1] > 0)) {
onDismiss()
onSwipe?.()
}
},
})
@@ -122,7 +132,6 @@ export default function Modal({
({ opacity }, item) =>
item && (
<StyledDialogOverlay
as={AnimatedDialogOverlay}
style={{ opacity: opacity.to({ range: [0.0, 1.0], output: [0, 1] }) }}
onDismiss={onDismiss}
initialFocusRef={initialFocusRef}
@@ -136,11 +145,13 @@ export default function Modal({
style: { transform: y.interpolate((y) => `translateY(${(y as number) > 0 ? y : 0}px)`) },
}
: {})}
aria-label="dialog content"
minHeight={minHeight}
maxHeight={maxHeight}
mobile={isMobile}
scrollOverlay={scrollOverlay}
aria-label="dialog"
$minHeight={minHeight}
$maxHeight={maxHeight}
$isBottomSheet={isBottomSheet}
$scrollOverlay={scrollOverlay}
$hideBorder={hideBorder}
$maxWidth={maxWidth}
>
{/* prevents the automatic focusing of inputs on mobile by the reach dialog */}
{!initialFocusRef && isMobile ? <div tabIndex={1} /> : null}

View File

@@ -1,7 +1,8 @@
import { NavIcon } from 'components/NavBar/NavIcon'
import { BagIcon, HundredsOverflowIcon } from 'nft/components/icons'
import { useBag } from 'nft/hooks'
import { useCallback, useEffect, useState } from 'react'
import { useIsNftProfilePage } from 'hooks/useIsNftPage'
import { BagIcon, HundredsOverflowIcon, TagIcon } from 'nft/components/icons'
import { useBag, useSellAsset } from 'nft/hooks'
import { useCallback } from 'react'
import styled from 'styled-components/macro'
import shallow from 'zustand/shallow'
@@ -22,7 +23,8 @@ const CounterDot = styled.div`
export const Bag = () => {
const itemsInBag = useBag((state) => state.itemsInBag)
const [bagQuantity, setBagQuantity] = useState(0)
const sellAssets = useSellAsset((state) => state.sellAssets)
const isProfilePage = useIsNftProfilePage()
const { bagExpanded, setBagExpanded } = useBag(
({ bagExpanded, setBagExpanded }) => ({ bagExpanded, setBagExpanded }),
@@ -33,15 +35,16 @@ export const Bag = () => {
setBagExpanded({ bagExpanded: !bagExpanded })
}, [bagExpanded, setBagExpanded])
useEffect(() => {
setBagQuantity(itemsInBag.length)
}, [itemsInBag])
const bagQuantity = isProfilePage ? sellAssets.length : itemsInBag.length
const bagHasItems = bagQuantity > 0
return (
<NavIcon isActive={bagExpanded} onClick={handleIconClick}>
<BagIcon viewBox="0 0 24 24" width={24} height={24} />
{isProfilePage ? (
<TagIcon viewBox="0 0 24 24" width={24} height={24} />
) : (
<BagIcon viewBox="0 0 24 24" width={24} height={24} strokeWidth="2px" />
)}
{bagHasItems && <CounterDot>{bagQuantity > 99 ? <HundredsOverflowIcon /> : bagQuantity}</CounterDot>}
</NavIcon>
)

View File

@@ -4,6 +4,9 @@ import { buttonTextSmall, subhead, subheadSmall } from 'nft/css/common.css'
import { breakpoints, sprinkles, vars } from '../../nft/css/sprinkles.css'
const DESKTOP_NAVBAR_WIDTH = 360
const DESKTOP_NAVBAR_WIDTH_L = 440
const DESKTOP_NAVBAR_WIDTH_XL = 540
const DESKTOP_NAVBAR_WIDTH_XXL = 640
const MAGNIFYING_GLASS_ICON_WIDTH = 28
const baseSearchStyle = style([
@@ -23,6 +26,27 @@ const baseSearchStyle = style([
},
])
const baseSearchNftStyle = style([
baseSearchStyle,
{
borderWidth: '2px',
'@media': {
[`screen and (min-width: 1024px) and (max-width: 1080px)`]: {
width: `${330}px`,
},
[`screen and (min-width: 1190px) and (max-width: 1380px)`]: {
width: `${DESKTOP_NAVBAR_WIDTH_L}px`,
},
[`screen and (min-width: 1380px) and (max-width: 1479px)`]: {
width: `${DESKTOP_NAVBAR_WIDTH_XL}px`,
},
[`screen and (min-width: ${1480}px)`]: {
width: `${DESKTOP_NAVBAR_WIDTH_XXL}px`,
},
},
},
])
export const searchBarContainer = style([
sprinkles({
right: '0',
@@ -40,6 +64,35 @@ export const searchBarContainer = style([
},
])
export const searchBarContainerNft = style([
sprinkles({
right: '0',
top: '0',
zIndex: '3',
display: 'inline-block',
}),
{
'@media': {
[`screen and (min-width: ${breakpoints.lg}px)`]: {
right: `-${DESKTOP_NAVBAR_WIDTH / 2}px`,
top: '-6px',
},
[`screen and (min-width: 1024px) and (max-width: 1080px)`]: {
right: `-${300 / 2}px`,
},
[`screen and (min-width: 1190px) and (max-width: 1380px)`]: {
right: `-${DESKTOP_NAVBAR_WIDTH_L / 2}px`,
},
[`screen and (min-width: 1380px) and (max-width: 1479px)`]: {
right: `-${DESKTOP_NAVBAR_WIDTH_XL / 2}px`,
},
[`screen and (min-width: ${1480}px)`]: {
right: `-${DESKTOP_NAVBAR_WIDTH_XXL / 2}px`,
},
},
},
])
export const searchBar = style([
baseSearchStyle,
sprinkles({
@@ -49,6 +102,15 @@ export const searchBar = style([
}),
])
export const nftSearchBar = style([
baseSearchNftStyle,
sprinkles({
color: 'textTertiary',
paddingX: '16',
background: 'backgroundSurface',
}),
])
export const searchBarInput = style([
sprinkles({
padding: '0',
@@ -75,6 +137,19 @@ export const searchBarDropdown = style([
},
])
export const searchBarDropdownNft = style([
baseSearchNftStyle,
sprinkles({
borderBottomLeftRadius: '12',
borderBottomRightRadius: '12',
background: 'backgroundSurface',
height: { sm: 'viewHeight', md: 'auto' },
}),
{
borderTop: 'none',
},
])
export const suggestionRow = style([
sprinkles({
display: 'flex',

View File

@@ -33,6 +33,7 @@ export const SearchBar = () => {
const phase1Flag = useNftFlag()
const isMobile = useIsMobile()
const isTablet = useIsTablet()
const isPhase1 = phase1Flag === NftVariant.Enabled
useOnClickOutside(searchRef, () => {
isOpen && toggleOpen()
@@ -45,7 +46,7 @@ export const SearchBar = () => {
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
enabled: !!debouncedSearchValue.length && phase1Flag === NftVariant.Enabled,
enabled: !!debouncedSearchValue.length && isPhase1,
}
)
@@ -112,19 +113,19 @@ export const SearchBar = () => {
position={{ sm: 'fixed', md: 'absolute' }}
width={{ sm: isOpen ? 'viewWidth' : 'auto', md: 'auto' }}
ref={searchRef}
className={styles.searchBarContainer}
className={isPhase1 ? styles.searchBarContainerNft : styles.searchBarContainer}
display={{ sm: isOpen ? 'inline-block' : 'none', xl: 'inline-block' }}
>
<Row
className={clsx(
` ${styles.searchBar} ${!isOpen && !isMobile && magicalGradientOnHover} ${
isMobileOrTablet && (isOpen ? styles.visible : styles.hidden)
}`
` ${isPhase1 ? styles.nftSearchBar : styles.searchBar} ${
!isOpen && !isMobile && magicalGradientOnHover
} ${isMobileOrTablet && (isOpen ? styles.visible : styles.hidden)}`
)}
borderRadius={isOpen || isMobileOrTablet ? undefined : '12'}
borderTopRightRadius={isOpen && !isMobile ? '12' : undefined}
borderTopLeftRadius={isOpen && !isMobile ? '12' : undefined}
borderBottomWidth={isOpen || isMobileOrTablet ? '0px' : '1px'}
borderBottomWidth={isOpen || isMobileOrTablet ? '0px' : isPhase1 ? '2px' : '1px'}
onClick={() => !isOpen && toggleOpen()}
gap="12"
>

View File

@@ -118,6 +118,7 @@ export const SearchBarDropdown = ({
const isTokenPage = pathname.includes('/tokens')
const phase1Flag = useNftFlag()
const [resultsState, setResultsState] = useState<ReactNode>()
const isPhase1 = phase1Flag === NftVariant.Enabled
const { data: trendingCollectionResults, isLoading: trendingCollectionsAreLoading } = useQuery(
['trendingCollections', 'eth', 'twenty_four_hours'],
@@ -204,10 +205,10 @@ export const SearchBarDropdown = ({
(isNFTPage && (hasVerifiedCollection || !hasVerifiedToken)) ||
(!isNFTPage && !hasVerifiedToken && hasVerifiedCollection)
const trace = useTrace({ section: SectionName.NAVBAR_SEARCH })
const trace = JSON.stringify(useTrace({ section: SectionName.NAVBAR_SEARCH }))
useEffect(() => {
const eventProperties = { total_suggestions: totalSuggestions, query_text: queryText, ...trace }
const eventProperties = { total_suggestions: totalSuggestions, query_text: queryText, ...JSON.parse(trace) }
if (!isLoading) {
const tokenSearchResults =
tokens.length > 0 ? (
@@ -342,7 +343,7 @@ export const SearchBarDropdown = ({
])
return (
<Box className={styles.searchBarDropdown}>
<Box className={isPhase1 ? styles.searchBarDropdownNft : styles.searchBarDropdown}>
<Box opacity={isLoading ? '0.3' : '1'} transition="125">
{resultsState}
</Box>

View File

@@ -1,13 +1,15 @@
import { sendAnalyticsEvent } from '@uniswap/analytics'
import { EventName } 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 TokenSafetyIcon from 'components/TokenSafety/TokenSafetyIcon'
import { getChainInfo } from 'constants/chainInfo'
import { NATIVE_CHAIN_ID } from 'constants/tokens'
import { checkWarning } from 'constants/tokenSafety'
import { getTokenDetailsURL } from 'graphql/data/util'
import uriToHttp from 'lib/utils/uriToHttp'
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
import { VerifiedIcon } from 'nft/components/icons'
@@ -18,10 +20,14 @@ 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 { formatDollar } from 'utils/formatNumbers'
import styled from 'styled-components/macro'
import * as styles from './SearchBar.css'
const StyledLogoContainer = styled(LogoContainer)`
margin-right: 8px;
`
interface CollectionRowProps {
collection: GenieCollection
isHovered: boolean
@@ -127,8 +133,6 @@ interface TokenRowProps {
}
export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index, eventProperties }: TokenRowProps) => {
const [brokenImage, setBrokenImage] = useState(false)
const [loaded, setLoaded] = useState(false)
const addToSearchHistory = useSearchHistory(
(state: { addItem: (item: FungibleToken | GenieCollection) => void }) => state.addItem
)
@@ -167,21 +171,17 @@ export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index,
style={{ background: isHovered ? vars.color.lightGrayOverlay : 'none' }}
>
<Row style={{ width: '65%' }}>
{!brokenImage && token.logoURI ? (
<LogoContainer>
<Box
as="img"
src={token.logoURI.includes('ipfs://') ? uriToHttp(token.logoURI)[0] : token.logoURI}
alt={token.name}
className={clsx(loaded ? styles.suggestionImage : styles.imageHolder)}
onError={() => setBrokenImage(true)}
onLoad={() => setLoaded(true)}
/>
<L2NetworkLogo networkUrl={L2Icon} size="16px" />
</LogoContainer>
) : (
<Box className={styles.imageHolder} />
)}
<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>
<Column className={styles.suggestionPrimaryContainer}>
<Row gap="4" width="full">
<Box className={styles.primaryText}>{token.name}</Box>
@@ -194,7 +194,7 @@ export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index,
<Column className={styles.suggestionSecondaryContainer}>
{token.priceUsd && (
<Row gap="4">
<Box className={styles.primaryText}>{formatDollar({ num: token.priceUsd, isPrice: true })}</Box>
<Box className={styles.primaryText}>{formatUSDPrice(token.priceUsd)}</Box>
</Row>
)}
{token.price24hChange && (

View File

@@ -9,12 +9,13 @@ import { resetMintState } from 'state/mint/actions'
import { resetMintState as resetMintV3State } from 'state/mint/v3/actions'
import styled, { useTheme } from 'styled-components/macro'
import { ThemedText } from 'theme'
import { flexRowNoWrap } from 'theme/styles'
import Row, { RowBetween } from '../Row'
import SettingsTab from '../Settings'
const Tabs = styled.div`
${({ theme }) => theme.flexRowNoWrap}
${flexRowNoWrap};
align-items: center;
border-radius: 3rem;
justify-content: space-evenly;

View File

@@ -8,6 +8,7 @@ import { Z_INDEX } from 'theme/zIndex'
const PopoverContainer = styled.div<{ show: boolean }>`
z-index: ${Z_INDEX.popover};
pointer-events: none;
visibility: ${(props) => (props.show ? 'visible' : 'hidden')};
opacity: ${(props) => (props.show ? 1 : 0)};
transition: visibility 150ms linear, opacity 150ms linear;

View File

@@ -19,9 +19,9 @@ import { unwrappedToken } from '../../utils/unwrappedToken'
import { ButtonEmpty, ButtonPrimary, ButtonSecondary } from '../Button'
import { LightCard } from '../Card'
import { AutoColumn } from '../Column'
import CurrencyLogo from '../CurrencyLogo'
import DoubleCurrencyLogo from '../DoubleLogo'
import { CardNoise } from '../earn/styled'
import CurrencyLogo from '../Logo/CurrencyLogo'
import { AutoRow, RowBetween, RowFixed } from '../Row'
import { Dots } from '../swap/styleds'
import { FixedHeightRow } from '.'

View File

@@ -20,9 +20,9 @@ import { unwrappedToken } from '../../utils/unwrappedToken'
import { ButtonEmpty, ButtonPrimary, ButtonSecondary } from '../Button'
import { GrayCard, LightCard } from '../Card'
import { AutoColumn } from '../Column'
import CurrencyLogo from '../CurrencyLogo'
import DoubleCurrencyLogo from '../DoubleLogo'
import { CardNoise } from '../earn/styled'
import CurrencyLogo from '../Logo/CurrencyLogo'
import { AutoRow, RowBetween, RowFixed } from '../Row'
import { Dots } from '../swap/styleds'

View File

@@ -4,9 +4,9 @@ import { Position } from '@uniswap/v3-sdk'
import RangeBadge from 'components/Badge/RangeBadge'
import { LightCard } from 'components/Card'
import { AutoColumn } from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo'
import DoubleCurrencyLogo from 'components/DoubleLogo'
import { Break } from 'components/earn/styled'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
import RateToggle from 'components/RateToggle'
import { RowBetween, RowFixed } from 'components/Row'
import JSBI from 'jsbi'

View File

@@ -28,7 +28,7 @@ const multiRoute: RoutingDiagramEntry[] = [
]
jest.mock(
'components/CurrencyLogo',
'components/Logo/CurrencyLogo',
() =>
({ currency }: { currency: Currency }) =>
`CurrencyLogo currency=${currency.symbol}`

View File

@@ -3,8 +3,8 @@ import { Protocol } from '@uniswap/router-sdk'
import { Currency } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk'
import Badge from 'components/Badge'
import CurrencyLogo from 'components/CurrencyLogo'
import DoubleCurrencyLogo from 'components/DoubleLogo'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
import Row, { AutoRow } from 'components/Row'
import { RoutingDiagramEntry } from 'components/swap/SwapRoute'
import { useTokenInfoFromActiveList } from 'hooks/useTokenInfoFromActiveList'

View File

@@ -2,7 +2,7 @@ import { TraceEvent } from '@uniswap/analytics'
import { BrowserEvent, ElementName, EventName } from '@uniswap/analytics-events'
import { Currency } from '@uniswap/sdk-core'
import { AutoColumn } from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
import { AutoRow } from 'components/Row'
import { COMMON_BASES } from 'constants/routing'
import { useTokenInfoFromActiveList } from 'hooks/useTokenInfoFromActiveList'

View File

@@ -2,11 +2,11 @@
exports[`renders currency rows correctly when currencies list is non-empty 1`] = `
<DocumentFragment>
.c9 {
.c10 {
color: #98A1C0;
}
.c7 {
.c8 {
margin-left: 4px;
display: -webkit-box;
display: -webkit-flex;
@@ -18,7 +18,7 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
justify-content: center;
}
.c8 {
.c9 {
width: 1em;
height: 1em;
color: #98A1C0;
@@ -73,7 +73,7 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
justify-content: space-between;
}
.c10 {
.c11 {
width: -webkit-fit-content;
width: -moz-fit-content;
width: fit-content;
@@ -100,6 +100,10 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
text-overflow: ellipsis;
}
.c7 {
margin-left: 0.3em;
}
<div
style="position: relative; height: 10px; width: 100%; overflow: auto; will-change: transform; direction: ltr;"
>
@@ -132,38 +136,42 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
<div
class="c7"
>
<svg
<div
class="c8"
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>
<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="c9 css-1j6a53a"
class="c10 css-yfjwjl"
>
DAI
</div>
@@ -172,7 +180,7 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
class="c4"
>
<div
class="c0 c1 c10"
class="c0 c1 c11"
style="justify-self: flex-end;"
/>
</div>
@@ -203,38 +211,42 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
<div
class="c7"
>
<svg
<div
class="c8"
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>
<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="c9 css-1j6a53a"
class="c10 css-yfjwjl"
>
USDC
</div>
@@ -243,7 +255,7 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
class="c4"
>
<div
class="c0 c1 c10"
class="c0 c1 c11"
style="justify-self: flex-end;"
/>
</div>
@@ -274,38 +286,42 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
<div
class="c7"
>
<svg
<div
class="c8"
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>
<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="c9 css-1j6a53a"
class="c10 css-yfjwjl"
>
WBTC
</div>
@@ -314,7 +330,7 @@ exports[`renders currency rows correctly when currencies list is non-empty 1`] =
class="c4"
>
<div
class="c0 c1 c10"
class="c0 c1 c11"
style="justify-self: flex-end;"
/>
</div>

View File

@@ -16,7 +16,7 @@ const mockCurrencyAmt = {
}
jest.mock(
'components/CurrencyLogo',
'components/Logo/CurrencyLogo',
() =>
({ currency }: { currency: Currency }) =>
`CurrencyLogo currency=${currency.symbol}`

View File

@@ -5,7 +5,6 @@ import { useWeb3React } from '@web3-react/core'
import TokenSafetyIcon from 'components/TokenSafety/TokenSafetyIcon'
import { checkWarning } from 'constants/tokenSafety'
import { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react'
import { XOctagon } from 'react-feather'
import { Check } from 'react-feather'
import { FixedSizeList } from 'react-window'
import { Text } from 'rebass'
@@ -16,8 +15,8 @@ import { useCurrencyBalance } from '../../../state/connection/hooks'
import { WrappedTokenInfo } from '../../../state/lists/wrappedTokenInfo'
import { ThemedText } from '../../../theme'
import Column, { AutoColumn } from '../../Column'
import CurrencyLogo from '../../CurrencyLogo'
import Loader from '../../Loader'
import CurrencyLogo from '../../Logo/CurrencyLogo'
import Row, { RowFixed } from '../../Row'
import { MouseoverTooltip } from '../../Tooltip'
import { LoadingRows, MenuItem } from '../styleds'
@@ -61,10 +60,8 @@ const Tag = styled.div`
margin-right: 4px;
`
export const BlockedTokenIcon = styled(XOctagon)<{ size?: string }>`
export const WarningContainer = styled.div`
margin-left: 0.3em;
width: 1em;
height: 1em;
`
function Balance({ balance }: { balance: CurrencyAmount<Currency> }) {
@@ -158,8 +155,9 @@ export function CurrencyRow({
<AutoColumn style={{ opacity: isBlockedToken ? blockedTokenOpacity : '1' }}>
<Row>
<CurrencyName title={currency.name}>{currency.name}</CurrencyName>
<TokenSafetyIcon warning={warning} />
{isBlockedToken && <BlockedTokenIcon />}
<WarningContainer>
<TokenSafetyIcon warning={warning} />
</WarningContainer>
</Row>
<ThemedText.DeprecatedDarkGray ml="0px" fontSize="12px" fontWeight={300}>
{currency.symbol}

View File

@@ -17,8 +17,9 @@ import { FixedSizeList } from 'react-window'
import { Text } from 'rebass'
import { useAllTokenBalances } from 'state/connection/hooks'
import styled, { useTheme } from 'styled-components/macro'
import { UserAddedToken } from 'types/tokens'
import { useActiveTokens, useIsUserAddedToken, useSearchInactiveTokenLists, useToken } from '../../hooks/Tokens'
import { useAllTokens, useIsUserAddedToken, useSearchInactiveTokenLists, useToken } from '../../hooks/Tokens'
import { CloseIcon, ThemedText } from '../../theme'
import { isAddress } from '../../utils'
import Column from '../Column'
@@ -66,15 +67,8 @@ export function CurrencySearch({
const [searchQuery, setSearchQuery] = useState<string>('')
const debouncedQuery = useDebounce(searchQuery, 200)
// Only display 'imported' tokens when the search filter has input
const defaultTokens = useActiveTokens(debouncedQuery.length > 0)
// if they input an address, use it
const isAddressSearch = isAddress(debouncedQuery)
const searchToken = useToken(debouncedQuery)
const searchTokenIsAdded = useIsUserAddedToken(searchToken)
useEffect(() => {
@@ -87,14 +81,27 @@ export function CurrencySearch({
}
}, [isAddressSearch])
const defaultTokens = useAllTokens()
const filteredTokens: Token[] = useMemo(() => {
return Object.values(defaultTokens).filter(getTokenFilter(debouncedQuery))
}, [defaultTokens, debouncedQuery])
const [balances, balancesAreLoading] = useAllTokenBalances()
const sortedTokens: Token[] = useMemo(
() => (!balancesAreLoading ? [...filteredTokens].sort(tokenComparator.bind(null, balances)) : []),
[balances, filteredTokens, balancesAreLoading]
() =>
!balancesAreLoading
? filteredTokens
.filter((token) => {
// 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
return balances[token.address]?.greaterThan(0)
}
return true
})
.sort(tokenComparator.bind(null, balances))
: [],
[balances, balancesAreLoading, debouncedQuery, filteredTokens, otherSelectedCurrency, selectedCurrency]
)
const isLoading = Boolean(balancesAreLoading && !tokenLoaderTimerElapsed)

View File

@@ -123,7 +123,7 @@ const ModalContentWrapper = styled.div`
export default function SettingsTab({ placeholderSlippage }: { placeholderSlippage: Percent }) {
const { chainId } = useWeb3React()
const node = useRef<HTMLDivElement>()
const node = useRef<HTMLDivElement | null>(null)
const open = useModalIsOpen(ApplicationModal.SETTINGS)
const toggle = useToggleSettingsMenu()
@@ -139,8 +139,7 @@ export default function SettingsTab({ placeholderSlippage }: { placeholderSlippa
useOnClickOutside(node, open ? toggle : undefined)
return (
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451
<StyledMenu ref={node as any}>
<StyledMenu ref={node}>
<Modal isOpen={showConfirmation} onDismiss={() => setShowConfirmation(false)} maxHeight={100}>
<ModalContentWrapper>
<AutoColumn gap="lg">

View File

@@ -1,24 +1,43 @@
import { Warning, WARNING_LEVEL } from 'constants/tokenSafety'
import { AlertTriangle } from 'react-feather'
import styled from 'styled-components/macro'
import { AlertTriangle, Slash } from 'react-feather'
import styled, { css } from 'styled-components/macro'
const VerifiedContainer = styled.div`
const WarningContainer = styled.div`
margin-left: 4px;
display: flex;
justify-content: center;
`
export const WarningIcon = styled(AlertTriangle)<{ size?: string }>`
const WarningIconStyle = css<{ size?: string }>`
width: ${({ size }) => size ?? '1em'};
height: ${({ size }) => size ?? '1em'};
`
const WarningIcon = styled(AlertTriangle)`
${WarningIconStyle};
color: ${({ theme }) => theme.textTertiary};
`
export const BlockedIcon = styled(Slash)`
${WarningIconStyle}
color: ${({ theme }) => theme.textSecondary};
`
export default function TokenSafetyIcon({ warning }: { warning: Warning | null }) {
if (warning?.level !== WARNING_LEVEL.UNKNOWN) return null
return (
<VerifiedContainer>
<WarningIcon />
</VerifiedContainer>
)
switch (warning?.level) {
case WARNING_LEVEL.BLOCKED:
return (
<WarningContainer>
<BlockedIcon strokeWidth={2.5} />
</WarningContainer>
)
case WARNING_LEVEL.UNKNOWN:
return (
<WarningContainer>
<WarningIcon />
</WarningContainer>
)
default:
return null
}
}

View File

@@ -1,12 +1,11 @@
import { WARNING_LEVEL } from 'constants/tokenSafety'
import { useTokenWarningColor } from 'hooks/useTokenWarningColor'
import { ReactNode } from 'react'
import { AlertOctagon, AlertTriangle } from 'react-feather'
import { AlertTriangle, Slash } from 'react-feather'
import { Text } from 'rebass'
import styled from 'styled-components/macro'
import { Color } from 'theme/styled'
const Label = styled.div<{ color: Color }>`
const Label = styled.div<{ color: string }>`
padding: 4px 4px;
font-size: 12px;
background-color: ${({ color }) => color + '1F'};
@@ -31,7 +30,7 @@ export default function TokenSafetyLabel({ level, canProceed, children }: TokenW
return (
<Label color={useTokenWarningColor(level)}>
<Title marginRight="5px">{children}</Title>
{canProceed ? <AlertTriangle strokeWidth={2.5} size="14px" /> : <AlertOctagon strokeWidth={2.5} size="14px" />}
{canProceed ? <AlertTriangle strokeWidth={2.5} size="14px" /> : <Slash strokeWidth={2.5} size="14px" />}
</Label>
)
}

View File

@@ -1,13 +1,12 @@
import { Trans } from '@lingui/macro'
import { getWarningCopy, TOKEN_SAFETY_ARTICLE, Warning } from 'constants/tokenSafety'
import { useTokenWarningColor } from 'hooks/useTokenWarningColor'
import { AlertOctagon, AlertTriangle } from 'react-feather'
import { AlertTriangle, Slash } from 'react-feather'
import { Text } from 'rebass'
import styled from 'styled-components/macro'
import { ExternalLink } from 'theme'
import { Color } from 'theme/styled'
const Label = styled.div<{ color: Color }>`
const Label = styled.div<{ color: string }>`
width: 100%;
padding: 12px 20px 16px;
background-color: ${({ color }) => color + '1F'};
@@ -52,7 +51,7 @@ export default function TokenWarningMessage({ warning, tokenAddress }: TokenWarn
return (
<Label color={color}>
<TitleRow>
{warning.canProceed ? <AlertTriangle size="16px" /> : <AlertOctagon size="16px" />}
{warning.canProceed ? <AlertTriangle size="16px" /> : <Slash size="16px" />}
<Title marginLeft="7px">{warning.message}</Title>
</TitleRow>

View File

@@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro'
import { Token } from '@uniswap/sdk-core'
import { ButtonPrimary } from 'components/Button'
import { AutoColumn } from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
import TokenSafetyLabel from 'components/TokenSafety/TokenSafetyLabel'
import { checkWarning, getWarningCopy, TOKEN_SAFETY_ARTICLE, Warning } from 'constants/tokenSafety'
import { useToken } from 'hooks/Tokens'

View File

@@ -3,7 +3,7 @@ import { darken } from 'polished'
import { useState } from 'react'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { textFadeIn } from 'theme/animations'
import { textFadeIn } from 'theme/styles'
import Resource from './Resource'

View File

@@ -1,13 +1,11 @@
import { Trans } from '@lingui/macro'
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import { Currency } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import CurrencyLogo from 'components/CurrencyLogo'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
import { useStablecoinValue } from 'hooks/useStablecoinPrice'
import useCurrencyBalance from 'lib/hooks/useCurrencyBalance'
import { formatToDecimal } from 'lib/utils/analytics'
import { useMemo } from 'react'
import styled from 'styled-components/macro'
import { currencyAmountToPreciseFloat, formatDollar } from 'utils/formatNumbers'
const BalancesCard = styled.div`
box-shadow: ${({ theme }) => theme.shallowShadow};
@@ -44,29 +42,14 @@ const BalanceRow = styled.div`
`
const BalanceItem = styled.div`
display: flex;
align-items: center;
`
export function useFormatBalance(balance: CurrencyAmount<Currency> | undefined) {
return useMemo(
() => (balance ? formatToDecimal(balance, Math.min(balance.currency.decimals, 2)) : undefined),
[balance]
)
}
export function useFormatUsdValue(usdValue: CurrencyAmount<Token> | null) {
return useMemo(() => {
const float = usdValue ? currencyAmountToPreciseFloat(usdValue) : undefined
if (!float) return undefined
return formatDollar({ num: float, isPrice: true })
}, [usdValue])
}
export default function BalanceSummary({ token }: { token: Currency }) {
const { account } = useWeb3React()
const balance = useCurrencyBalance(account, token)
const formattedBalance = useFormatBalance(balance)
const usdValue = useStablecoinValue(balance)
const formattedUsdValue = useFormatUsdValue(usdValue)
const formattedBalance = formatCurrencyAmount(balance, NumberType.TokenNonTx)
const formattedUsdValue = formatCurrencyAmount(useStablecoinValue(balance), NumberType.FiatTokenStats)
if (!account || !balance) return null
return (

View File

@@ -0,0 +1,54 @@
import { Trans } from '@lingui/macro'
import { useNavigate } from 'react-router-dom'
import styled from 'styled-components/macro'
import { ReactComponent as EyeIcon } from '../../../assets/svg/eye.svg'
const InvalidDetailsContainer = styled.div`
padding-top: 128px;
display: flex;
flex-direction: column;
align-items: center;
`
const InvalidDetailsText = styled.span`
margin-top: 28px;
margin-bottom: 20px;
text-align: center;
color: ${({ theme }) => theme.textSecondary};
font-size: 20px;
font-weight: 500;
line-height: 28px;
`
const TokenExploreButton = styled.button`
border: none;
border-radius: 12px;
background-color: ${({ theme }) => theme.accentAction};
padding: 12px 16px;
color: ${({ theme }) => theme.textPrimary};
font-size: 16px;
font-weight: 600;
`
export default function InvalidTokenDetails({ chainName }: { chainName?: string }) {
const navigate = useNavigate()
return (
<InvalidDetailsContainer>
<EyeIcon />
<InvalidDetailsText>
{chainName ? (
<Trans>{`This token doesn't exist on ${chainName}`}</Trans>
) : (
<Trans>This token doesn&apos;t exist</Trans>
)}
</InvalidDetailsText>
<TokenExploreButton onClick={() => navigate('/tokens')}>
<Trans>Explore tokens</Trans>
</TokenExploreButton>
</InvalidDetailsContainer>
)
}

View File

@@ -1,4 +1,5 @@
import { Trans } from '@lingui/macro'
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'
@@ -8,8 +9,6 @@ import useCurrencyBalance from 'lib/hooks/useCurrencyBalance'
import styled from 'styled-components/macro'
import { StyledInternalLink } from 'theme'
import { useFormatBalance, useFormatUsdValue } from './BalanceSummary'
const Wrapper = styled.div`
align-content: center;
align-items: center;
@@ -84,9 +83,8 @@ const SwapButton = styled(StyledInternalLink)`
export default function MobileBalanceSummaryFooter({ token }: { token: Currency }) {
const { account } = useWeb3React()
const balance = useCurrencyBalance(account, token)
const formattedBalance = useFormatBalance(balance)
const usdValue = useStablecoinValue(balance)
const formattedUsdValue = useFormatUsdValue(usdValue)
const formattedBalance = formatCurrencyAmount(balance, NumberType.TokenNonTx)
const formattedUsdValue = formatCurrencyAmount(useStablecoinValue(balance), NumberType.FiatTokenStats)
const chain = CHAIN_ID_TO_BACKEND_NAME[token.chainId].toLowerCase()
return (

View File

@@ -1,4 +1,5 @@
import { Trans } from '@lingui/macro'
import { formatUSDPrice } from '@uniswap/conedison/format'
import { AxisBottom, TickFormatter } from '@visx/axis'
import { localPoint } from '@visx/event'
import { EventType } from '@visx/event/lib/types'
@@ -20,7 +21,6 @@ import {
monthYearDayFormatter,
weekFormatter,
} from 'utils/formatChartTimes'
import { formatDollar } from 'utils/formatNumbers'
export const DATA_EMPTY = { value: 0, timestamp: 0 }
@@ -92,6 +92,16 @@ interface PriceChartProps {
timePeriod: TimePeriod
}
function formatDisplayPrice(value: number) {
const str = value.toFixed(9)
const [digits, decimals] = str.split('.')
// Displays longer string for numbers < $2 to show changes in both stablecoins & small values
if (digits === '0' || digits === '1')
return `$${digits + '.' + decimals.substring(0, 2) + decimals.substring(2).replace(/0+$/, '')}`
return formatUSDPrice(value)
}
export function PriceChart({ width, height, prices, timePeriod }: PriceChartProps) {
const locale = useActiveLocale()
const theme = useTheme()
@@ -104,9 +114,7 @@ export function PriceChart({ width, height, prices, timePeriod }: PriceChartProp
// set display price to ending price when prices have changed.
useEffect(() => {
if (prices) {
setDisplayPrice(endingPrice)
}
setDisplayPrice(endingPrice)
}, [prices, endingPrice])
const [crosshair, setCrosshair] = useState<number | null>(null)
@@ -226,7 +234,7 @@ export function PriceChart({ width, height, prices, timePeriod }: PriceChartProp
return (
<>
<ChartHeader>
<TokenPrice>{formatDollar({ num: displayPrice.value, isPrice: true })}</TokenPrice>
<TokenPrice>{formatDisplayPrice(displayPrice.value)}</TokenPrice>
<DeltaContainer>
{formattedDelta}
<ArrowCell>{arrow}</ArrowCell>

View File

@@ -1,6 +1,7 @@
import { Trans } from '@lingui/macro'
import { Currency } from '@uniswap/sdk-core'
import { NATIVE_CHAIN_ID } from 'constants/tokens'
import { TokenQueryData } from 'graphql/data/Token'
import { chainIdToBackendName } from 'graphql/data/util'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import { useRef } from 'react'
import { Twitter } from 'react-feather'
@@ -63,12 +64,7 @@ const ShareAction = styled.div`
}
`
interface TokenInfo {
token: NonNullable<TokenQueryData>
isNative: boolean
}
export default function ShareButton(tokenInfo: TokenInfo) {
export default function ShareButton({ currency }: { currency: Currency }) {
const theme = useTheme()
const node = useRef<HTMLDivElement | null>(null)
const open = useModalIsOpen(ApplicationModal.SHARE)
@@ -76,12 +72,16 @@ export default function ShareButton(tokenInfo: TokenInfo) {
useOnClickOutside(node, open ? toggleShare : undefined)
const positionX = (window.screen.width - TWITTER_WIDTH) / 2
const positionY = (window.screen.height - TWITTER_HEIGHT) / 2
const tokenAddress = tokenInfo.isNative ? NATIVE_CHAIN_ID : tokenInfo.token.address
const address = currency.isNative ? NATIVE_CHAIN_ID : currency.wrapped.address
const shareTweet = () => {
toggleShare()
window.open(
`https://twitter.com/intent/tweet?text=Check%20out%20${tokenInfo.token.name}%20(${tokenInfo.token.symbol})%20https://app.uniswap.org/%23/tokens/${tokenInfo.token.chain}/${tokenAddress}%20via%20@uniswap`,
`https://twitter.com/intent/tweet?text=Check%20out%20${currency.name}%20(${
currency.symbol
})%20https://app.uniswap.org/%23/tokens/${chainIdToBackendName(
currency.chainId
).toLowerCase()}/${address}%20via%20@uniswap`,
'newwindow',
`left=${positionX}, top=${positionY}, width=${TWITTER_WIDTH}, height=${TWITTER_HEIGHT}`
)

View File

@@ -3,7 +3,7 @@ import { 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/animations'
import { textFadeIn } from 'theme/styles'
import { LoadingBubble } from '../loading'
import { LogoContainer } from '../TokenTable/TokenRow'

View File

@@ -1,9 +1,9 @@
import { Trans } from '@lingui/macro'
import { formatNumber, NumberType } from '@uniswap/conedison/format'
import { ReactNode } from 'react'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { textFadeIn } from 'theme/animations'
import { formatDollar } from 'utils/formatNumbers'
import { textFadeIn } from 'theme/styles'
import { TokenSortMethod } from '../state'
import { HEADER_DESCRIPTIONS } from '../TokenTable/TokenRow'
@@ -69,7 +69,7 @@ function Stat({
{description && <InfoTip text={description}></InfoTip>}
</StatTitle>
<StatPrice>{formatDollar({ num: value, isPrice })}</StatPrice>
<StatPrice>{formatNumber(value, NumberType.FiatTokenStats)}</StatPrice>
</StatWrapper>
)
}

View File

@@ -1,8 +1,9 @@
import { Trans } from '@lingui/macro'
import { Trace } from '@uniswap/analytics'
import { PageName } from '@uniswap/analytics-events'
import { Currency, NativeCurrency, Token } from '@uniswap/sdk-core'
import CurrencyLogo from 'components/CurrencyLogo'
import { Currency } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
import { AboutSection } from 'components/Tokens/TokenDetails/About'
import AddressSection from 'components/Tokens/TokenDetails/AddressSection'
import BalanceSummary from 'components/Tokens/TokenDetails/BalanceSummary'
@@ -24,24 +25,24 @@ import TokenSafetyMessage from 'components/TokenSafety/TokenSafetyMessage'
import TokenSafetyModal from 'components/TokenSafety/TokenSafetyModal'
import Widget from 'components/Widget'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { DEFAULT_ERC20_DECIMALS, NATIVE_CHAIN_ID, nativeOnChain } from 'constants/tokens'
import { NATIVE_CHAIN_ID, nativeOnChain } from 'constants/tokens'
import { checkWarning } from 'constants/tokenSafety'
import { TokenPriceQuery } from 'graphql/data/__generated__/TokenPriceQuery.graphql'
import { Chain, TokenQuery } from 'graphql/data/Token'
import { QueryToken, tokenQuery, TokenQueryData } from 'graphql/data/Token'
import { TopToken } from 'graphql/data/TopTokens'
import { CHAIN_NAME_TO_CHAIN_ID } from 'graphql/data/util'
import { Chain, TokenQuery, TokenQueryData } from 'graphql/data/Token'
import { QueryToken, tokenQuery } from 'graphql/data/Token'
import { CHAIN_NAME_TO_CHAIN_ID, getTokenDetailsURL } from 'graphql/data/util'
import { useIsUserAddedTokenOnChain } from 'hooks/Tokens'
import { useOnGlobalChainSwitch } from 'hooks/useGlobalChainSwitch'
import useCurrencyLogoURIs from 'lib/hooks/useCurrencyLogoURIs'
import { UNKNOWN_TOKEN_SYMBOL, useTokenFromActiveNetwork } from 'lib/hooks/useCurrency'
import { useCallback, useMemo, useState, useTransition } from 'react'
import { ArrowLeft } from 'react-feather'
import { PreloadedQuery, usePreloadedQuery } from 'react-relay'
import { useNavigate } from 'react-router-dom'
import styled from 'styled-components/macro'
import { isAddress } from 'utils'
import { RefetchPricesFunction } from './ChartSection'
import InvalidTokenDetails from './InvalidTokenDetails'
const TokenSymbol = styled.span`
text-transform: uppercase;
@@ -53,65 +54,100 @@ const TokenActions = styled.div`
color: ${({ theme }) => theme.textSecondary};
`
export function useTokenLogoURI(token?: TokenQueryData | TopToken, nativeCurrency?: Token | NativeCurrency) {
const chainId = token ? CHAIN_NAME_TO_CHAIN_ID[token.chain] : SupportedChainId.MAINNET
return [
...useCurrencyLogoURIs(nativeCurrency),
...useCurrencyLogoURIs({ ...token, chainId }),
token?.project?.logoUrl,
][0]
function useOnChainToken(address: string | undefined, skip: boolean) {
const token = useTokenFromActiveNetwork(skip || !address ? undefined : address)
if (skip || !address || (token && token?.symbol === UNKNOWN_TOKEN_SYMBOL)) {
return undefined
} else {
return token
}
}
// Selects most relevant token based on data available, preferring native > query > on-chain
// Token will be null if still loading from on-chain, and undefined if unavailable
function useRelevantToken(
address: string | undefined,
pageChainId: number,
tokenQueryData: TokenQueryData | undefined
) {
const { chainId: activeChainId } = useWeb3React()
const queryToken = useMemo(() => {
if (!address) return undefined
if (address === NATIVE_CHAIN_ID) return nativeOnChain(pageChainId)
if (tokenQueryData) return new QueryToken(tokenQueryData)
return undefined
}, [pageChainId, address, tokenQueryData])
// fetches on-chain token if query data is missing and page chain matches global chain (else fetch won't work)
const skipOnChainFetch = Boolean(queryToken) || pageChainId !== activeChainId
const onChainToken = useOnChainToken(address, skipOnChainFetch)
return useMemo(
() => ({ token: queryToken ?? onChainToken, didFetchFromChain: !queryToken }),
[onChainToken, queryToken]
)
}
type TokenDetailsProps = {
tokenAddress: string | undefined
urlAddress: string | undefined
chain: Chain
tokenQueryReference: PreloadedQuery<TokenQuery>
priceQueryReference: PreloadedQuery<TokenPriceQuery> | null | undefined
refetchTokenPrices: RefetchPricesFunction
}
export default function TokenDetails({
tokenAddress,
urlAddress,
chain,
tokenQueryReference,
priceQueryReference,
refetchTokenPrices,
}: TokenDetailsProps) {
if (!tokenAddress) {
throw new Error(`Invalid token details route: tokenAddress param is undefined`)
if (!urlAddress) {
throw new Error('Invalid token details route: tokenAddress param is undefined')
}
const address = useMemo(
() => (urlAddress === NATIVE_CHAIN_ID ? urlAddress : isAddress(urlAddress) || undefined),
[urlAddress]
)
const pageChainId = CHAIN_NAME_TO_CHAIN_ID[chain]
const nativeCurrency = nativeOnChain(pageChainId)
const isNative = tokenAddress === NATIVE_CHAIN_ID
const tokenQueryData = usePreloadedQuery(tokenQuery, tokenQueryReference).tokens?.[0]
const token = useMemo(() => {
if (isNative) return nativeCurrency
if (tokenQueryData) return new QueryToken(tokenQueryData)
return new Token(pageChainId, tokenAddress, DEFAULT_ERC20_DECIMALS)
}, [isNative, nativeCurrency, pageChainId, tokenAddress, tokenQueryData])
const crossChainMap = useMemo(
() =>
tokenQueryData?.project?.tokens.reduce((map, current) => {
if (current) map[current.chain] = current.address
return map
}, {} as { [key: string]: string | undefined }) ?? {},
[tokenQueryData]
)
const tokenWarning = tokenAddress ? checkWarning(tokenAddress) : null
const { token, didFetchFromChain } = useRelevantToken(address, pageChainId, tokenQueryData)
const tokenWarning = address ? checkWarning(address) : null
const isBlockedToken = tokenWarning?.canProceed === false
const navigate = useNavigate()
// Wrapping navigate in a transition prevents Suspense from unnecessarily showing fallbacks again.
const [isPending, startTokenTransition] = useTransition()
const navigateToTokenForChain = useCallback(
(chain: Chain) => {
const chainName = chain.toLowerCase()
const token = tokenQueryData?.project?.tokens.find((token) => token.chain === chain && token.address)
const address = isNative ? NATIVE_CHAIN_ID : token?.address
(update: Chain) => {
if (!address) return
startTokenTransition(() => navigate(`/tokens/${chainName}/${address}`))
const bridgedAddress = crossChainMap[update]
if (bridgedAddress) {
startTokenTransition(() => navigate(getTokenDetailsURL(bridgedAddress, update)))
} else if (didFetchFromChain || token?.isNative) {
startTokenTransition(() => navigate(getTokenDetailsURL(address, update)))
}
},
[isNative, navigate, startTokenTransition, tokenQueryData?.project?.tokens]
[address, crossChainMap, didFetchFromChain, navigate, token?.isNative]
)
useOnGlobalChainSwitch(navigateToTokenForChain)
const navigateToWidgetSelectedToken = useCallback(
(token: Currency) => {
const address = token.isNative ? NATIVE_CHAIN_ID : token.address
startTokenTransition(() => navigate(`/tokens/${chain.toLowerCase()}/${address}`))
startTokenTransition(() => navigate(getTokenDetailsURL(address, chain)))
},
[chain, navigate]
)
@@ -119,7 +155,7 @@ export default function TokenDetails({
const [continueSwap, setContinueSwap] = useState<{ resolve: (value: boolean | PromiseLike<boolean>) => void }>()
// Show token safety modal if Swap-reviewing a warning token, at all times if the current token is blocked
const shouldShowSpeedbump = !useIsUserAddedTokenOnChain(tokenAddress, pageChainId) && tokenWarning !== null
const shouldShowSpeedbump = !useIsUserAddedTokenOnChain(address, pageChainId) && tokenWarning !== null
const onReviewSwapClick = useCallback(
() => new Promise<boolean>((resolve) => (shouldShowSpeedbump ? setContinueSwap({ resolve }) : resolve(true))),
[shouldShowSpeedbump]
@@ -133,13 +169,20 @@ export default function TokenDetails({
[continueSwap, setContinueSwap]
)
const logoSrc = useTokenLogoURI(tokenQueryData, isNative ? nativeCurrency : undefined)
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} />
}
return (
<Trace page={PageName.TOKEN_DETAILS_PAGE} properties={{ tokenAddress, tokenName: token?.name }} shouldLogImpression>
<Trace
page={PageName.TOKEN_DETAILS_PAGE}
properties={{ tokenAddress: address, tokenName: token?.name }}
shouldLogImpression
>
<TokenDetailsLayout>
{tokenQueryData && !isPending ? (
{token && !isPending ? (
<LeftPanel>
<BreadcrumbNavLink to={`/tokens/${chain.toLowerCase()}`}>
<ArrowLeft size={14} /> Tokens
@@ -147,40 +190,33 @@ export default function TokenDetails({
<TokenInfoContainer>
<TokenNameCell>
<LogoContainer>
<CurrencyLogo
src={logoSrc}
size="32px"
symbol={isNative ? nativeCurrency?.symbol : token?.symbol}
currency={isNative ? nativeCurrency : token}
/>
<CurrencyLogo currency={token} size="32px" />
<L2NetworkLogo networkUrl={L2Icon} size="16px" />
</LogoContainer>
{token?.name ?? <Trans>Name not found</Trans>}
<TokenSymbol>{token?.symbol ?? <Trans>Symbol not found</Trans>}</TokenSymbol>
{token.name ?? <Trans>Name not found</Trans>}
<TokenSymbol>{token.symbol ?? <Trans>Symbol not found</Trans>}</TokenSymbol>
</TokenNameCell>
<TokenActions>
{tokenQueryData?.name && tokenQueryData.symbol && tokenQueryData.address && (
<ShareButton token={tokenQueryData} isNative={!!nativeCurrency} />
)}
<ShareButton currency={token} />
</TokenActions>
</TokenInfoContainer>
<ChartSection priceQueryReference={priceQueryReference} refetchTokenPrices={refetchTokenPrices} />
<StatsSection
TVL={tokenQueryData.market?.totalValueLocked?.value}
volume24H={tokenQueryData.market?.volume24H?.value}
priceHigh52W={tokenQueryData.market?.priceHigh52W?.value}
priceLow52W={tokenQueryData.market?.priceLow52W?.value}
TVL={tokenQueryData?.market?.totalValueLocked?.value}
volume24H={tokenQueryData?.market?.volume24H?.value}
priceHigh52W={tokenQueryData?.market?.priceHigh52W?.value}
priceLow52W={tokenQueryData?.market?.priceLow52W?.value}
/>
{!isNative && (
{!token.isNative && (
<>
<Hr />
<AboutSection
address={tokenQueryData.address ?? ''}
description={tokenQueryData.project?.description}
homepageUrl={tokenQueryData.project?.homepageUrl}
twitterName={tokenQueryData.project?.twitterName}
address={address}
description={tokenQueryData?.project?.description}
homepageUrl={tokenQueryData?.project?.homepageUrl}
twitterName={tokenQueryData?.project?.twitterName}
/>
<AddressSection address={tokenQueryData.address ?? ''} />
<AddressSection address={address} />
</>
)}
</LeftPanel>
@@ -190,25 +226,23 @@ export default function TokenDetails({
<RightPanel>
<Widget
token={token ?? nativeCurrency}
token={token ?? undefined}
onTokenChange={navigateToWidgetSelectedToken}
onReviewSwapClick={onReviewSwapClick}
/>
{tokenWarning && <TokenSafetyMessage tokenAddress={tokenAddress ?? ''} warning={tokenWarning} />}
{tokenWarning && <TokenSafetyMessage tokenAddress={address} warning={tokenWarning} />}
{token && <BalanceSummary token={token} />}
</RightPanel>
{token && <MobileBalanceSummaryFooter token={token} />}
{tokenAddress && (
<TokenSafetyModal
isOpen={isBlockedToken || !!continueSwap}
tokenAddress={tokenAddress}
onContinue={() => onResolveSwap(true)}
onBlocked={() => navigate(-1)}
onCancel={() => onResolveSwap(false)}
showCancel={true}
/>
)}
<TokenSafetyModal
isOpen={isBlockedToken || !!continueSwap}
tokenAddress={address}
onContinue={() => onResolveSwap(true)}
onBlocked={() => navigate(-1)}
onCancel={() => onResolveSwap(false)}
showCancel={true}
/>
</TokenDetailsLayout>
</Trace>
)

View File

@@ -1,9 +1,10 @@
import { Trans } from '@lingui/macro'
import { sendAnalyticsEvent } from '@uniswap/analytics'
import { EventName } from '@uniswap/analytics-events'
import { formatNumber, formatUSDPrice, NumberType } from '@uniswap/conedison/format'
import { ParentSize } from '@visx/responsive'
import SparklineChart from 'components/Charts/SparklineChart'
import CurrencyLogo from 'components/CurrencyLogo'
import QueryTokenLogo from 'components/Logo/QueryTokenLogo'
import { getChainInfo } from 'constants/chainInfo'
import { SparklineMap, TopToken } from 'graphql/data/TopTokens'
import { CHAIN_NAME_TO_CHAIN_ID, getTokenDetailsURL } from 'graphql/data/util'
@@ -14,7 +15,6 @@ import { ArrowDown, ArrowUp } from 'react-feather'
import { Link, useParams } from 'react-router-dom'
import styled, { css, useTheme } from 'styled-components/macro'
import { ClickableStyle } from 'theme'
import { formatDollar } from 'utils/formatNumbers'
import {
LARGE_MEDIA_BREAKPOINT,
@@ -31,7 +31,6 @@ import {
TokenSortMethod,
useSetSortMethod,
} from '../state'
import { useTokenLogoURI } from '../TokenDetails'
import InfoTip from '../TokenDetails/InfoTip'
import { formatDelta, getDeltaArrow } from '../TokenDetails/PriceChart'
@@ -438,7 +437,8 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
const lowercaseChainName = useParams<{ chainName?: string }>().chainName?.toUpperCase() ?? 'ethereum'
const filterNetwork = lowercaseChainName.toUpperCase()
const L2Icon = getChainInfo(CHAIN_NAME_TO_CHAIN_ID[filterNetwork])?.circleLogoUrl
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)
@@ -446,7 +446,7 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
const rank = sortAscending ? tokenListLength - tokenListIndex : tokenListIndex + 1
const exploreTokenSelectedEventProperties = {
chain_id: filterNetwork,
chain_id: chainId,
token_address: tokenAddress,
token_symbol: tokenSymbol,
token_list_index: tokenListIndex,
@@ -469,7 +469,7 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
tokenInfo={
<ClickableName>
<LogoContainer>
<CurrencyLogo src={useTokenLogoURI(token)} symbol={tokenSymbol} />
<QueryTokenLogo token={token} />
<L2NetworkLogo networkUrl={L2Icon} />
</LogoContainer>
<TokenInfoCell>
@@ -481,7 +481,7 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
price={
<ClickableContent>
<PriceInfoCell>
{formatDollar({ num: token.market?.price?.value, isPrice: true, lessPreciseStablecoinValues: true })}
{formatUSDPrice(token.market?.price?.value)}
<PercentChangeInfoCell>
{formattedDelta}
{arrow}
@@ -495,8 +495,14 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
{arrow}
</ClickableContent>
}
tvl={<ClickableContent>{formatDollar({ num: token.market?.totalValueLocked?.value })}</ClickableContent>}
volume={<ClickableContent>{formatDollar({ num: token.market?.volume?.value })}</ClickableContent>}
tvl={
<ClickableContent>
{formatNumber(token.market?.totalValueLocked?.value, NumberType.FiatTokenStats)}
</ClickableContent>
}
volume={
<ClickableContent>{formatNumber(token.market?.volume?.value, NumberType.FiatTokenStats)}</ClickableContent>
}
sparkLine={
<SparkLine>
<ParentSize>

View File

@@ -1,11 +1,12 @@
import { transparentize } from 'polished'
import { ReactNode, useCallback, useState } from 'react'
import { ReactNode, useCallback, useEffect, useState } from 'react'
import styled from 'styled-components/macro'
import Popover, { PopoverProps } from '../Popover'
export const TooltipContainer = styled.div`
max-width: 256px;
cursor: default;
padding: 0.6rem 1rem;
font-weight: 400;
word-break: break-word;
@@ -18,7 +19,11 @@ export const TooltipContainer = styled.div`
interface TooltipProps extends Omit<PopoverProps, 'content'> {
text: ReactNode
open?: () => void
close?: () => void
noOp?: () => void
disableHover?: boolean // disable the hover and content display
timeout?: number
}
interface TooltipContentProps extends Omit<PopoverProps, 'content'> {
@@ -29,8 +34,19 @@ interface TooltipContentProps extends Omit<PopoverProps, 'content'> {
disableHover?: boolean // disable the hover and content display
}
export default function Tooltip({ text, ...rest }: TooltipProps) {
return <Popover content={text && <TooltipContainer>{text}</TooltipContainer>} {...rest} />
export default function Tooltip({ text, open, close, noOp, disableHover, ...rest }: TooltipProps) {
return (
<Popover
content={
text && (
<TooltipContainer onMouseEnter={disableHover ? noOp : open} onMouseLeave={disableHover ? noOp : close}>
{text}
</TooltipContainer>
)
}
{...rest}
/>
)
}
function TooltipContent({ content, wrap = false, ...rest }: TooltipContentProps) {
@@ -38,14 +54,36 @@ function TooltipContent({ content, wrap = false, ...rest }: TooltipContentProps)
}
/** Standard text tooltip. */
export function MouseoverTooltip({ text, disableHover, children, ...rest }: Omit<TooltipProps, 'show'>) {
export function MouseoverTooltip({ text, disableHover, children, timeout, ...rest }: Omit<TooltipProps, 'show'>) {
const [show, setShow] = useState(false)
const open = useCallback(() => setShow(true), [setShow])
const close = useCallback(() => setShow(false), [setShow])
useEffect(() => {
if (show && timeout) {
const tooltipTimer = setTimeout(() => {
setShow(false)
}, timeout)
return () => {
clearTimeout(tooltipTimer)
}
}
return
}, [timeout, show])
const noOp = () => null
return (
<Tooltip {...rest} show={show} text={disableHover ? null : text}>
<div onMouseEnter={disableHover ? noOp : open} onMouseLeave={disableHover ? noOp : close}>
<Tooltip
{...rest}
open={open}
close={close}
noOp={noOp}
disableHover={disableHover}
show={show}
text={disableHover ? null : text}
>
<div onMouseEnter={disableHover ? noOp : open} onMouseLeave={disableHover || timeout ? noOp : close}>
{children}
</div>
</Tooltip>

View File

@@ -3,12 +3,15 @@ import AddressClaimModal from 'components/claim/AddressClaimModal'
import ConnectedAccountBlocked from 'components/ConnectedAccountBlocked'
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
import useAccountRiskCheck from 'hooks/useAccountRiskCheck'
import NftExploreBanner from 'nft/components/nftExploreBanner/NftExploreBanner'
import { lazy } from 'react'
import { useLocation } from 'react-router-dom'
import { useModalIsOpen, useToggleModal } from 'state/application/hooks'
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'))
export default function TopLevelModals() {
const addressClaimOpen = useModalIsOpen(ApplicationModal.ADDRESS_CLAIM)
@@ -16,6 +19,12 @@ export default function TopLevelModals() {
const blockedAccountModalOpen = useModalIsOpen(ApplicationModal.BLOCKED_ACCOUNT)
const { account } = useWeb3React()
const location = useLocation()
const isNftEnabled = useNftFlag() === NftVariant.Enabled
const pageShowsNftPromoBanner =
location.pathname.startsWith('/swap') ||
location.pathname.startsWith('/tokens') ||
location.pathname.startsWith('/pool')
useAccountRiskCheck(account)
const open = Boolean(blockedAccountModalOpen && account)
@@ -24,7 +33,13 @@ export default function TopLevelModals() {
<AddressClaimModal isOpen={addressClaimOpen} onDismiss={addressClaimToggle} />
<ConnectedAccountBlocked account={account} isOpen={open} />
<Bag />
{useNftFlag() === NftVariant.Enabled && <TransactionCompleteModal />}
{isNftEnabled && (
<>
<TransactionCompleteModal />
<AirdropModal />
{pageShowsNftPromoBanner && <NftExploreBanner />}
</>
)}
</>
)
}

View File

@@ -9,6 +9,7 @@ import useCopyClipboard from 'hooks/useCopyClipboard'
import useStablecoinPrice from 'hooks/useStablecoinPrice'
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
import { useProfilePageState, useSellAsset, useWalletCollections } from 'nft/hooks'
import { useIsNftClaimAvailable } from 'nft/hooks/useIsNftClaimAvailable'
import { ProfilePageStateType } from 'nft/types'
import { useCallback, useMemo } from 'react'
import { Copy, ExternalLink, Power } from 'react-feather'
@@ -23,11 +24,11 @@ import { shortenAddress } from '../../nft/utils/address'
import { useCloseModal, useToggleModal } from '../../state/application/hooks'
import { ApplicationModal } from '../../state/application/reducer'
import { useUserHasAvailableClaim, useUserUnclaimedAmount } from '../../state/claim/hooks'
import { ButtonPrimary } from '../Button'
import { ButtonEmphasis, ButtonSize, ThemeButton } from '../Button'
import StatusIcon from '../Identicon/StatusIcon'
import IconButton, { IconHoverText } from './IconButton'
const WalletButton = styled(ButtonPrimary)`
const WalletButton = styled(ThemeButton)`
border-radius: 12px;
padding-top: 10px;
padding-bottom: 10px;
@@ -117,6 +118,7 @@ const AuthenticatedHeader = () => {
const setSellPageState = useProfilePageState((state) => state.setProfilePageState)
const resetSellAssets = useSellAsset((state) => state.reset)
const clearCollectionFilters = useWalletCollections((state) => state.clearCollectionFilters)
const isClaimAvailable = useIsNftClaimAvailable((state) => state.isClaimAvailable)
const unclaimedAmount: CurrencyAmount<Token> | undefined = useUserUnclaimedAmount(account)
const isUnclaimed = useUserHasAvailableClaim(account)
@@ -124,6 +126,7 @@ const AuthenticatedHeader = () => {
const nativeCurrency = useNativeCurrency()
const nativeCurrencyPrice = useStablecoinPrice(nativeCurrency ?? undefined) || 0
const openClaimModal = useToggleModal(ApplicationModal.ADDRESS_CLAIM)
const openNftModal = useToggleModal(ApplicationModal.UNISWAP_NFT_AIRDROP_CLAIM)
const disconnect = useCallback(() => {
if (connector && connector.deactivate) {
connector.deactivate()
@@ -177,15 +180,20 @@ const AuthenticatedHeader = () => {
<USDText>${amountUSD.toFixed(2)} USD</USDText>
</BalanceWrapper>
{nftFlag === NftVariant.Enabled && (
<ProfileButton onClick={navigateToProfile}>
<ProfileButton onClick={navigateToProfile} size={ButtonSize.medium} emphasis={ButtonEmphasis.medium}>
<Trans>View and sell NFTs</Trans>
</ProfileButton>
)}
{isUnclaimed && (
<UNIButton onClick={openClaimModal}>
<UNIButton onClick={openClaimModal} size={ButtonSize.medium} emphasis={ButtonEmphasis.medium}>
<Trans>Claim</Trans> {unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} <Trans>reward</Trans>
</UNIButton>
)}
{nftFlag === NftVariant.Enabled && isClaimAvailable && (
<UNIButton size={ButtonSize.medium} emphasis={ButtonEmphasis.medium} onClick={openNftModal}>
<Trans>Claim Uniswap NFT Airdrop</Trans>
</UNIButton>
)}
</Column>
</AuthenticatedHeaderWrapper>
)

View File

@@ -5,6 +5,7 @@ import ms from 'ms.macro'
import { useCallback, useMemo } from 'react'
import { useAppDispatch } from 'state/hooks'
import styled from 'styled-components/macro'
import { flexColumnNoWrap } from 'theme/styles'
import { useAllTransactions } from '../../state/transactions/hooks'
import { clearAllTransactions } from '../../state/transactions/reducer'
@@ -20,7 +21,7 @@ const Divider = styled.div`
`
const TransactionListWrapper = styled.div`
${({ theme }) => theme.flexColumnNoWrap};
${flexColumnNoWrap};
`
interface TransactionInformation {

View File

@@ -3,6 +3,7 @@ import { BrowserEvent, ElementName, EventName } from '@uniswap/analytics-events'
import React from 'react'
import { Check } from 'react-feather'
import styled from 'styled-components/macro'
import { flexColumnNoWrap, flexRowNoWrap } from 'theme/styles'
import { ExternalLink } from '../../theme'
@@ -20,7 +21,7 @@ const InfoCard = styled.button<{ isActive?: boolean }>`
`
const CheckIcon = styled(Check)`
${({ theme }) => theme.flexColumnNoWrap};
${flexColumnNoWrap};
height: 20px;
width: 20px;
align-items: center;
@@ -41,7 +42,7 @@ const OptionCard = styled(InfoCard as any)`
`
const OptionCardLeft = styled.div`
${({ theme }) => theme.flexColumnNoWrap};
${flexColumnNoWrap};
justify-content: center;
height: 100%;
`
@@ -60,7 +61,7 @@ const OptionCardClickable = styled(OptionCard as any)<{
`
const HeaderText = styled.div`
${({ theme }) => theme.flexRowNoWrap};
${flexRowNoWrap};
align-items: center;
justify-content: center;
color: ${(props) =>
@@ -76,7 +77,7 @@ const SubHeader = styled.div`
`
const IconWrapper = styled.div<{ size?: number | null }>`
${({ theme }) => theme.flexColumnNoWrap};
${flexColumnNoWrap};
align-items: center;
justify-content: center;
padding-right: 12px;

View File

@@ -4,11 +4,12 @@ import { ButtonEmpty, ButtonPrimary } from 'components/Button'
import { AlertTriangle } from 'react-feather'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { flexColumnNoWrap, flexRowNoWrap } from 'theme/styles'
import Loader from '../Loader'
const PendingSection = styled.div`
${({ theme }) => theme.flexColumnNoWrap};
${flexColumnNoWrap};
align-items: center;
justify-content: center;
width: 100%;
@@ -33,14 +34,14 @@ const AlertTriangleIcon = styled(AlertTriangle)`
`
const LoaderContainer = styled.div`
${flexRowNoWrap};
margin: 16px 0;
${({ theme }) => theme.flexRowNoWrap};
align-items: center;
justify-content: center;
`
const LoadingMessage = styled.div`
${({ theme }) => theme.flexRowNoWrap};
${flexRowNoWrap};
align-items: center;
justify-content: center;
border-radius: 12px;
@@ -51,13 +52,13 @@ const LoadingMessage = styled.div`
`
const ErrorGroup = styled.div`
${({ theme }) => theme.flexColumnNoWrap};
${flexColumnNoWrap};
align-items: center;
justify-content: flex-start;
`
const LoadingWrapper = styled.div`
${({ theme }) => theme.flexColumnNoWrap};
${flexColumnNoWrap};
align-items: center;
justify-content: center;
`

View File

@@ -17,6 +17,7 @@ import { useAppDispatch, useAppSelector } from 'state/hooks'
import { updateSelectedWallet } from 'state/user/reducer'
import { useConnectedWallets } from 'state/wallets/hooks'
import styled from 'styled-components/macro'
import { flexColumnNoWrap, flexRowNoWrap } from 'theme/styles'
import { isMobile } from 'utils/userAgent'
import { ReactComponent as Close } from '../../assets/images/x.svg'
@@ -48,7 +49,7 @@ const CloseColor = styled(Close)`
`
const Wrapper = styled.div`
${({ theme }) => theme.flexColumnNoWrap}
${flexColumnNoWrap};
background-color: ${({ theme }) => theme.backgroundSurface};
outline: ${({ theme }) => `1px solid ${theme.backgroundOutline}`};
box-shadow: ${({ theme }) => theme.deepShadow};
@@ -58,7 +59,7 @@ const Wrapper = styled.div`
`
const HeaderRow = styled.div`
${({ theme }) => theme.flexRowNoWrap};
${flexRowNoWrap};
padding: 1rem 1rem;
font-weight: 600;
size: 16px;

View File

@@ -7,6 +7,7 @@ import WalletDropdown from 'components/WalletDropdown'
import { getConnection } from 'connection/utils'
import { NftVariant, useNftFlag } from 'featureFlags/flags/nft'
import { Portal } from 'nft/components/common/Portal'
import { useIsNftClaimAvailable } from 'nft/hooks/useIsNftClaimAvailable'
import { getIsValidSwapQuote } from 'pages/Swap'
import { darken } from 'polished'
import { useMemo, useRef } from 'react'
@@ -14,6 +15,8 @@ import { AlertTriangle, ChevronDown, ChevronUp } from 'react-feather'
import { useAppSelector } from 'state/hooks'
import { useDerivedSwapInfo } from 'state/swap/hooks'
import styled, { useTheme } from 'styled-components/macro'
import { colors } from 'theme/colors'
import { flexRowNoWrap } from 'theme/styles'
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
import {
@@ -53,7 +56,7 @@ const ChevronWrapper = styled.button`
`
const Web3StatusGeneric = styled(ButtonSecondary)`
${({ theme }) => theme.flexRowNoWrap}
${flexRowNoWrap};
width: 100%;
align-items: center;
padding: 0.5rem;
@@ -79,7 +82,7 @@ const Web3StatusError = styled(Web3StatusGeneric)`
`
const Web3StatusConnectWrapper = styled.div<{ faded?: boolean }>`
${({ theme }) => theme.flexRowNoWrap}
${flexRowNoWrap};
align-items: center;
background-color: ${({ theme }) => theme.accentActionSoft};
border-radius: ${FULL_BORDER_RADIUS}px;
@@ -94,11 +97,16 @@ const Web3StatusConnectWrapper = styled.div<{ faded?: boolean }>`
}
`
const Web3StatusConnected = styled(Web3StatusGeneric)<{ pending?: boolean; isNftActive?: boolean }>`
const Web3StatusConnected = styled(Web3StatusGeneric)<{
pending?: boolean
isNftActive?: boolean
isClaimAvailable?: boolean
}>`
background-color: ${({ pending, theme }) => (pending ? theme.deprecated_primary1 : theme.deprecated_bg1)};
border: 1px solid ${({ pending, theme }) => (pending ? theme.deprecated_primary1 : theme.deprecated_bg1)};
color: ${({ pending, theme }) => (pending ? theme.deprecated_white : theme.deprecated_text1)};
font-weight: 500;
border: ${({ isClaimAvailable }) => isClaimAvailable && `1px solid ${colors.purple300}`};
:hover,
:focus {
border: 1px solid ${({ theme }) => darken(0.05, theme.deprecated_bg3)};
@@ -201,6 +209,7 @@ function Web3StatusInner() {
const toggleWalletDropdown = useToggleWalletDropdown()
const toggleWalletModal = useToggleWalletModal()
const walletIsOpen = useModalIsOpen(ApplicationModal.WALLET_DROPDOWN)
const isClaimAvailable = useIsNftClaimAvailable((state) => state.isClaimAvailable)
const error = useAppSelector((state) => state.connection.errorByConnectionType[getConnection(connector).type])
const isNftActive = useNftFlag() === NftVariant.Enabled
@@ -240,6 +249,7 @@ function Web3StatusInner() {
isNftActive={isNftActive}
onClick={toggleWallet}
pending={hasPendingTransactions}
isClaimAvailable={isClaimAvailable}
>
{!hasPendingTransactions && <StatusIcon size={24} connectionType={connectionType} />}
{hasPendingTransactions ? (

View File

@@ -21,12 +21,12 @@ import {
} from 'lib/utils/analytics'
import { useCallback, useState } from 'react'
import { useIsDarkMode } from 'state/user/hooks'
import { DARK_THEME, LIGHT_THEME } from 'theme/widget'
import { computeRealizedPriceImpact } from 'utils/prices'
import { switchChain } from 'utils/switchChain'
import { useSyncWidgetInputs } from './inputs'
import { useSyncWidgetSettings } from './settings'
import { DARK_THEME, LIGHT_THEME } from './theme'
import { useSyncWidgetTransactions } from './transactions'
export const WIDGET_WIDTH = 360

View File

@@ -0,0 +1,45 @@
import { darkTheme, lightTheme } from 'theme/colors'
const fonts = {
fontFamily: 'Inter custom',
}
export const LIGHT_THEME = {
// surface
container: lightTheme.backgroundSurface,
interactive: lightTheme.backgroundInteractive,
module: lightTheme.backgroundModule,
accent: lightTheme.accentAction,
dialog: lightTheme.backgroundBackdrop,
outline: lightTheme.backgroundOutline,
// text
primary: lightTheme.textPrimary,
secondary: lightTheme.textSecondary,
onInteractive: lightTheme.accentTextDarkPrimary,
// state
success: lightTheme.accentSuccess,
warning: lightTheme.accentWarning,
error: lightTheme.accentCritical,
...fonts,
}
export const DARK_THEME = {
// surface
container: darkTheme.backgroundSurface,
interactive: darkTheme.backgroundInteractive,
module: darkTheme.backgroundModule,
accent: darkTheme.accentAction,
dialog: darkTheme.backgroundBackdrop,
outline: darkTheme.backgroundOutline,
// text
primary: darkTheme.textPrimary,
secondary: darkTheme.textSecondary,
onInteractive: darkTheme.accentTextLightPrimary,
// state
success: darkTheme.accentSuccess,
warning: darkTheme.accentWarning,
error: darkTheme.accentCritical,
...fonts,
}

View File

@@ -17,7 +17,7 @@ import { ButtonPrimary } from '../Button'
import { LightCard } from '../Card'
import { AutoColumn } from '../Column'
import { FiatValue } from '../CurrencyInputPanel/FiatValue'
import CurrencyLogo from '../CurrencyLogo'
import CurrencyLogo from '../Logo/CurrencyLogo'
import { RowBetween, RowFixed } from '../Row'
import TradePrice from '../swap/TradePrice'
import { AdvancedSwapDetails } from './AdvancedSwapDetails'

View File

@@ -4,7 +4,7 @@ import { useWeb3React } from '@web3-react/core'
import { ButtonEmpty } from 'components/Button'
import Card, { OutlineCard } from 'components/Card'
import { AutoColumn } from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
import Modal from 'components/Modal'
import { AutoRow, RowBetween } from 'components/Row'
import { useState } from 'react'

View File

@@ -0,0 +1,38 @@
import store from 'state'
import { DEFAULT_LIST_OF_LISTS } from './lists'
class TokenLogoLookupTable {
private dict: { [key: string]: string[] | undefined } = {}
private initialized = false
initialize() {
const dict: { [key: string]: string[] | undefined } = {}
DEFAULT_LIST_OF_LISTS.forEach((list) =>
store.getState().lists.byUrl[list].current?.tokens.forEach((token) => {
if (token.logoURI) {
const lowercaseAddress = token.address.toLowerCase()
const currentEntry = dict[lowercaseAddress]
if (currentEntry) {
currentEntry.push(token.logoURI)
} else {
dict[lowercaseAddress] = [token.logoURI]
}
}
})
)
this.dict = dict
this.initialized = true
}
getIcons(address?: string | null) {
if (!address) return undefined
if (!this.initialized) {
this.initialize()
}
return this.dict[address.toLowerCase()]
}
}
export default new TokenLogoLookupTable()

View File

@@ -8,6 +8,8 @@ type AddressMap = { [chainId: number]: string }
export const UNI_ADDRESS: AddressMap = constructSameAddressMap('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
export const UNISWAP_NFT_AIRDROP_CLAIM_ADDRESS = '0x8B799381ac40b838BBA4131ffB26197C432AFe78'
export const V2_FACTORY_ADDRESSES: AddressMap = constructSameAddressMap(V2_FACTORY_ADDRESS)
export const V2_ROUTER_ADDRESS: AddressMap = constructSameAddressMap('0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D')

View File

@@ -6,7 +6,7 @@ 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 ms from 'ms.macro'
import { colorsDark } from 'theme/colors'
import { darkTheme } from 'theme/colors'
import { SupportedChainId, SupportedL1ChainId, SupportedL2ChainId } from './chains'
import { ARBITRUM_LIST, CELO_LIST, OPTIMISM_LIST } from './lists'
@@ -61,7 +61,7 @@ const CHAIN_INFO: ChainInfoMap = {
label: 'Ethereum',
logoUrl: ethereumLogoUrl,
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
color: colorsDark.chain_1,
color: darkTheme.chain_1,
},
[SupportedChainId.RINKEBY]: {
networkType: NetworkType.L1,
@@ -71,7 +71,7 @@ const CHAIN_INFO: ChainInfoMap = {
label: 'Rinkeby',
logoUrl: ethereumLogoUrl,
nativeCurrency: { name: 'Rinkeby Ether', symbol: 'rETH', decimals: 18 },
color: colorsDark.chain_4,
color: darkTheme.chain_4,
},
[SupportedChainId.ROPSTEN]: {
networkType: NetworkType.L1,
@@ -81,7 +81,7 @@ const CHAIN_INFO: ChainInfoMap = {
label: 'Ropsten',
logoUrl: ethereumLogoUrl,
nativeCurrency: { name: 'Ropsten Ether', symbol: 'ropETH', decimals: 18 },
color: colorsDark.chain_3,
color: darkTheme.chain_3,
},
[SupportedChainId.KOVAN]: {
networkType: NetworkType.L1,
@@ -91,7 +91,7 @@ const CHAIN_INFO: ChainInfoMap = {
label: 'Kovan',
logoUrl: ethereumLogoUrl,
nativeCurrency: { name: 'Kovan Ether', symbol: 'kovETH', decimals: 18 },
color: colorsDark.chain_420,
color: darkTheme.chain_420,
},
[SupportedChainId.GOERLI]: {
networkType: NetworkType.L1,
@@ -101,7 +101,7 @@ const CHAIN_INFO: ChainInfoMap = {
label: 'Görli',
logoUrl: ethereumLogoUrl,
nativeCurrency: { name: 'Görli Ether', symbol: 'görETH', decimals: 18 },
color: colorsDark.chain_5,
color: darkTheme.chain_5,
},
[SupportedChainId.OPTIMISM]: {
networkType: NetworkType.L2,
@@ -118,8 +118,8 @@ const CHAIN_INFO: ChainInfoMap = {
statusPage: 'https://optimism.io/status',
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137778-uniswap-on-optimistic-ethereum-oξ',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
color: colorsDark.chain_10,
backgroundColor: colorsDark.chain_10_background,
color: darkTheme.chain_10,
backgroundColor: darkTheme.chain_10_background,
},
[SupportedChainId.OPTIMISM_GOERLI]: {
networkType: NetworkType.L2,
@@ -134,7 +134,7 @@ const CHAIN_INFO: ChainInfoMap = {
statusPage: 'https://optimism.io/status',
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137778-uniswap-on-optimistic-ethereum-oξ',
nativeCurrency: { name: 'Optimism Goerli Ether', symbol: 'görOpETH', decimals: 18 },
color: colorsDark.chain_420,
color: darkTheme.chain_420,
},
[SupportedChainId.ARBITRUM_ONE]: {
networkType: NetworkType.L2,
@@ -149,8 +149,8 @@ const CHAIN_INFO: ChainInfoMap = {
defaultListUrl: ARBITRUM_LIST,
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137787-uniswap-on-arbitrum',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
color: colorsDark.chain_42,
backgroundColor: colorsDark.chain_42161_background,
color: darkTheme.chain_42,
backgroundColor: darkTheme.chain_42161_background,
},
[SupportedChainId.ARBITRUM_RINKEBY]: {
networkType: NetworkType.L2,
@@ -164,7 +164,7 @@ const CHAIN_INFO: ChainInfoMap = {
defaultListUrl: ARBITRUM_LIST,
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137787-uniswap-on-arbitrum',
nativeCurrency: { name: 'Rinkeby Arbitrum Ether', symbol: 'rinkArbETH', decimals: 18 },
color: colorsDark.chain_421611,
color: darkTheme.chain_421611,
},
[SupportedChainId.POLYGON]: {
networkType: NetworkType.L1,
@@ -177,8 +177,8 @@ const CHAIN_INFO: ChainInfoMap = {
logoUrl: polygonMaticLogo,
circleLogoUrl: polygonCircleLogoUrl,
nativeCurrency: { name: 'Polygon Matic', symbol: 'MATIC', decimals: 18 },
color: colorsDark.chain_137,
backgroundColor: colorsDark.chain_137_background,
color: darkTheme.chain_137,
backgroundColor: darkTheme.chain_137_background,
},
[SupportedChainId.POLYGON_MUMBAI]: {
networkType: NetworkType.L1,

View File

@@ -34,13 +34,7 @@ export const DEFAULT_INACTIVE_LIST_URLS: string[] = [
ARBITRUM_LIST,
OPTIMISM_LIST,
CELO_LIST,
...UNSUPPORTED_LIST_URLS,
]
// this is the default list of lists that are exposed to users
// lower index == higher priority for token import
const DEFAULT_LIST_OF_LISTS_TO_DISPLAY: string[] = [...DEFAULT_ACTIVE_LIST_URLS, ...DEFAULT_INACTIVE_LIST_URLS]
export const DEFAULT_LIST_OF_LISTS: string[] = [
...DEFAULT_LIST_OF_LISTS_TO_DISPLAY,
...UNSUPPORTED_LIST_URLS, // need to load dynamic unsupported tokens as well
]
export const DEFAULT_LIST_OF_LISTS: string[] = [...DEFAULT_ACTIVE_LIST_URLS, ...DEFAULT_INACTIVE_LIST_URLS]

View File

@@ -54,7 +54,7 @@ export function getWarningCopy(warning: Warning | null, plural = false) {
export type Warning = {
level: WARNING_LEVEL
message: JSX.Element
/* canProceed determines whether triangle/octagon alert icon is used, and
/* canProceed determines whether triangle/slash alert icon is used, and
whether this token is supported/able to be traded */
canProceed: boolean
}

View File

@@ -48,8 +48,8 @@ export const tokenQuery = graphql`
twitterName
logoUrl
tokens {
chain
address
chain @required(action: LOG)
address @required(action: LOG)
}
}
}
@@ -61,14 +61,14 @@ export type TokenQueryData = NonNullable<TokenQuery$data['tokens']>[number]
// TODO: Return a QueryToken from useTokenQuery instead of TokenQueryData to make it more usable in Currency-centric interfaces.
export class QueryToken extends WrappedTokenInfo {
constructor(data: NonNullable<TokenQueryData>) {
constructor(data: NonNullable<TokenQueryData>, logoSrc?: string) {
super({
chainId: CHAIN_NAME_TO_CHAIN_ID[data.chain],
address: data.address,
decimals: data.decimals ?? DEFAULT_ERC20_DECIMALS,
symbol: data.symbol ?? '',
name: data.name ?? '',
logoURI: data.project?.logoUrl ?? undefined,
logoURI: logoSrc ?? data.project?.logoUrl ?? undefined,
})
}
}

View File

@@ -14,50 +14,38 @@ import { useUserAddedTokens, useUserAddedTokensOnChain } from '../state/user/hoo
import { TokenAddressMap, useUnsupportedTokenList } from './../state/lists/hooks'
// reduce token map into standard address <-> Token mapping, optionally include user added tokens
function useTokensFromMap(tokenMap: TokenAddressMap, includeUserAdded: boolean): { [address: string]: Token } {
function useTokensFromMap(tokenMap: TokenAddressMap): { [address: string]: Token } {
const { chainId } = useWeb3React()
const userAddedTokens = useUserAddedTokens()
return useMemo(() => {
if (!chainId) return {}
// reduce to just tokens
const mapWithoutUrls = Object.keys(tokenMap[chainId] ?? {}).reduce<{ [address: string]: Token }>(
(newMap, address) => {
newMap[address] = tokenMap[chainId][address].token
return newMap
},
{}
)
if (includeUserAdded) {
return (
userAddedTokens
// reduce into all ALL_TOKENS filtered by the current chain
.reduce<{ [address: string]: Token }>(
(tokenMap, token) => {
tokenMap[token.address] = token
return tokenMap
},
// must make a copy because reduce modifies the map, and we do not
// want to make a copy in every iteration
{ ...mapWithoutUrls }
)
)
}
return mapWithoutUrls
}, [chainId, userAddedTokens, tokenMap, includeUserAdded])
return Object.keys(tokenMap[chainId] ?? {}).reduce<{ [address: string]: Token }>((newMap, address) => {
newMap[address] = tokenMap[chainId][address].token
return newMap
}, {})
}, [chainId, tokenMap])
}
export function useAllTokens(): { [address: string]: Token } {
const allTokens = useCombinedActiveList()
return useTokensFromMap(allTokens, true)
}
export function useActiveTokens(includeUserAdded: boolean): { [address: string]: Token } {
const allTokens = useCombinedActiveList()
return useTokensFromMap(allTokens, includeUserAdded)
const tokensFromMap = useTokensFromMap(allTokens)
const userAddedTokens = useUserAddedTokens()
return useMemo(() => {
return (
userAddedTokens
// reduce into all ALL_TOKENS filtered by the current chain
.reduce<{ [address: string]: Token }>(
(tokenMap, token) => {
tokenMap[token.address] = token
return tokenMap
},
// must make a copy because reduce modifies the map, and we do not
// want to make a copy in every iteration
{ ...tokensFromMap }
)
)
}, [tokensFromMap, userAddedTokens])
}
type BridgeInfo = Record<
@@ -73,7 +61,7 @@ export function useUnsupportedTokens(): { [address: string]: Token } {
const { chainId } = useWeb3React()
const listsByUrl = useAllLists()
const unsupportedTokensMap = useUnsupportedTokenList()
const unsupportedTokens = useTokensFromMap(unsupportedTokensMap, false)
const unsupportedTokens = useTokensFromMap(unsupportedTokensMap)
// checks the default L2 lists to see if `bridgeInfo` has an L1 address value that is unsupported
const l2InferredBlockedTokens: typeof unsupportedTokens = useMemo(() => {

View File

@@ -0,0 +1,81 @@
import TokenLogoLookupTable from 'constants/TokenLogoLookupTable'
import { chainIdToNetworkName, getNativeLogoURI } from 'lib/hooks/useCurrencyLogoURIs'
import uriToHttp from 'lib/utils/uriToHttp'
import { useCallback, useEffect, useState } from 'react'
import { isAddress } from 'utils'
export const BAD_SRCS: { [tokenAddress: string]: true } = {}
// Converts uri's into fetchable urls
function parseLogoSources(uris: string[]) {
const urls: string[] = []
uris.forEach((uri) => urls.push(...uriToHttp(uri)))
return urls
}
// Parses uri's, favors non-coingecko images, and improves coingecko logo quality
function prioritizeLogoSources(uris: string[]) {
const parsedUris = uris.map((uri) => uriToHttp(uri)).flat(1)
const preferredUris: string[] = []
// Consolidate duplicate coingecko urls into one fallback source
let coingeckoUrl: string | undefined = undefined
parsedUris.forEach((uri) => {
if (uri.startsWith('https://assets.coingecko')) {
if (!coingeckoUrl) {
coingeckoUrl = uri.replace(/small|thumb/g, 'large')
}
} else {
preferredUris.push(uri)
}
})
// Places coingecko urls in the back of the source array
return coingeckoUrl ? [...preferredUris, coingeckoUrl] : preferredUris
}
function getInitialUrl(address?: string | null, chainId?: number | null, isNative?: boolean) {
if (chainId && isNative) return getNativeLogoURI(chainId)
const networkName = chainId ? chainIdToNetworkName(chainId) : 'ethereum'
const checksummedAddress = isAddress(address)
if (checksummedAddress) {
return `https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/${networkName}/assets/${checksummedAddress}/logo.png`
} else {
return undefined
}
}
export default function useAssetLogoSource(
address?: string | null,
chainId?: number | null,
isNative?: boolean,
backupImg?: string | null
): [string | undefined, () => void] {
const [current, setCurrent] = useState<string | undefined>(getInitialUrl(address, chainId, isNative))
const [fallbackSrcs, setFallbackSrcs] = useState<string[] | undefined>(undefined)
useEffect(() => {
setCurrent(getInitialUrl(address, chainId, isNative))
setFallbackSrcs(undefined)
}, [address, chainId, isNative])
const nextSrc = useCallback(() => {
if (current) {
BAD_SRCS[current] = true
}
// Parses and stores logo sources from tokenlists if assets repo url fails
if (!fallbackSrcs) {
const uris = TokenLogoLookupTable.getIcons(address) ?? []
if (backupImg) uris.push(backupImg)
const tokenListIcons = prioritizeLogoSources(parseLogoSources(uris))
setCurrent(tokenListIcons.find((src) => !BAD_SRCS[src]))
setFallbackSrcs(tokenListIcons)
} else {
setCurrent(fallbackSrcs.find((src) => !BAD_SRCS[src]))
}
}, [current, fallbackSrcs, address, backupImg])
return [current, nextSrc]
}

View File

@@ -5,7 +5,7 @@ import { useMemo } from 'react'
import useENSContentHash from './useENSContentHash'
export default function useHttpLocations(uri: string | undefined): string[] {
export default function useHttpLocations(uri: string | undefined | null): string[] {
const ens = useMemo(() => (uri ? parseENSAddress(uri) : undefined), [uri])
const resolvedContentHash = useENSContentHash(ens?.ensName)
return useMemo(() => {

View File

@@ -1,26 +0,0 @@
import { RefObject, useEffect, useRef } from 'react'
export function useOnClickOutside<T extends HTMLElement>(
node: RefObject<T | undefined>,
handler: undefined | (() => void)
) {
const handlerRef = useRef<undefined | (() => void)>(handler)
useEffect(() => {
handlerRef.current = handler
}, [handler])
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (node.current?.contains(e.target as Node) ?? false) {
return
}
if (handlerRef.current) handlerRef.current()
}
document.addEventListener('mousedown', handleClickOutside)
return () => {
document.removeEventListener('mousedown', handleClickOutside)
}
}, [node])
}

View File

@@ -25,7 +25,7 @@ import LogsUpdater from './state/logs/updater'
import TransactionUpdater from './state/transactions/updater'
import UserUpdater from './state/user/updater'
import ThemeProvider, { ThemedGlobalStyle } from './theme'
import RadialGradientByChainUpdater from './theme/RadialGradientByChainUpdater'
import RadialGradientByChainUpdater from './theme/components/RadialGradientByChainUpdater'
const queryClient = new QueryClient()

View File

@@ -25,6 +25,9 @@ function parseStringOrBytes32(str: string | undefined, bytes32: string | undefin
: defaultValue
}
export const UNKNOWN_TOKEN_SYMBOL = 'UNKNOWN'
export const UNKNOWN_TOKEN_NAME = 'Unknown Token'
/**
* Returns a Token from the tokenAddress.
* Returns null if token is loading or null was passed.
@@ -51,11 +54,11 @@ export function useTokenFromActiveNetwork(tokenAddress: string | undefined): Tok
const parsedDecimals = useMemo(() => decimals?.result?.[0] ?? DEFAULT_ERC20_DECIMALS, [decimals.result])
const parsedSymbol = useMemo(
() => parseStringOrBytes32(symbol.result?.[0], symbolBytes32.result?.[0], 'UNKNOWN'),
() => parseStringOrBytes32(symbol.result?.[0], symbolBytes32.result?.[0], UNKNOWN_TOKEN_SYMBOL),
[symbol.result, symbolBytes32.result]
)
const parsedName = useMemo(
() => parseStringOrBytes32(tokenName.result?.[0], tokenNameBytes32.result?.[0], 'Unknown Token'),
() => parseStringOrBytes32(tokenName.result?.[0], tokenNameBytes32.result?.[0], UNKNOWN_TOKEN_NAME),
[tokenName.result, tokenNameBytes32.result]
)

View File

@@ -10,7 +10,7 @@ import { isCelo, NATIVE_CHAIN_ID, nativeOnChain } from '../../constants/tokens'
type Network = 'ethereum' | 'arbitrum' | 'optimism' | 'polygon'
function chainIdToNetworkName(networkId: SupportedChainId): Network {
export function chainIdToNetworkName(networkId: SupportedChainId): Network {
switch (networkId) {
case SupportedChainId.MAINNET:
return 'ethereum'
@@ -60,7 +60,7 @@ export default function useCurrencyLogoURIs(
isToken?: boolean
address?: string
chainId: number
logoURI?: string
logoURI?: string | null
}
| null
| undefined

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: uniswap-interface\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: 2022-11-15 00:17\n"
"PO-Revision-Date: 2022-11-17 01:33\n"
"Last-Translator: \n"
"Language: af_ZA\n"
"Language-Team: Afrikaans\n"
@@ -196,6 +196,10 @@ msgstr "Voeg {0}/{1} V3 likiditeit by"
msgid "Added"
msgstr "Bygevoeg"
#: src/nft/components/collection/CollectionAsset.tsx
msgid "Added to bag"
msgstr ""
#: src/components/TransactionConfirmationModal/index.tsx
msgid "Added {0}"
msgstr "{0}bygevoeg"
@@ -350,6 +354,10 @@ msgstr "Beste prys roete kos ~{formattedGasPriceString} in gas."
msgid "Blocked Address"
msgstr "Geblokkeerde adres"
#: src/nft/components/profile/view/ViewMyNftsAsset.tsx
msgid "Blocked from trading"
msgstr ""
#: src/components/PositionCard/index.tsx
msgid "By adding liquidity you'll earn 0.3% of all trades on this pair proportional to your share of the pool. Fees are added to the pool, accrue in real time and can be claimed by withdrawing your liquidity."
msgstr "Deur likiditeit by te voeg, verdien u 0,3% van alle transaksies op hierdie paar in verhouding tot u deel van die poel. Fooie word by die poel gevoeg, word intyds toegeval en kan geëis word deur u likiditeit te onttrek."
@@ -570,7 +578,7 @@ msgstr "Kontrak adres"
#: src/components/WalletDropdown/AuthenticatedHeader.tsx
#: src/nft/components/profile/view/ProfileAccountDetails.tsx
#: src/theme/components.tsx
#: src/theme/components/index.tsx
msgid "Copied!"
msgstr "Gekopieer!"
@@ -713,6 +721,10 @@ msgstr "Deposito van likiditeit"
msgid "Description"
msgstr "Beskrywing"
#: src/nft/components/profile/view/ViewMyNftsAsset.tsx
msgid "Deselected"
msgstr ""
#: src/pages/RemoveLiquidity/index.tsx
msgid "Detailed"
msgstr "Gedetailleerd"
@@ -746,6 +758,10 @@ msgstr "Dokumentasie"
msgid "Dont see one of your v2 positions? <0>Import it.</0>"
msgstr "Sien u nie een van u v2-posisies nie? <0> Voer dit in.</0>"
#: src/nft/components/profile/view/ViewMyNftsAsset.tsx
msgid "ERC-1155 support coming soon"
msgstr ""
#: src/components/vote/DelegateModal.tsx
msgid "Earned UNI tokens represent voting shares in Uniswap governance."
msgstr "Verdiende UNI-tekens verteenwoordig stemgeregtigde aandele in Uniswap-bestuur."
@@ -1537,6 +1553,10 @@ msgstr "Verwyder vloeibaarheid"
msgid "Removed"
msgstr "Verwyder"
#: src/nft/components/collection/CollectionAsset.tsx
msgid "Removed from bag"
msgstr ""
#: src/components/AccountDetailsV2/TransactionBody.tsx
msgid "Removing"
msgstr "Verwydering"
@@ -1595,6 +1615,10 @@ msgstr "Kies 'n aksie"
msgid "Select token"
msgstr "Kies teken"
#: src/nft/components/profile/view/ViewMyNftsAsset.tsx
msgid "Selected"
msgstr ""
#: src/pages/AddLiquidity/index.tsx
msgid "Selected Range"
msgstr "Geselekteerde reeks"
@@ -2680,10 +2704,6 @@ msgstr "{SOCKS_AMOUNT} UNI"
msgid "{USER_AMOUNT} UNI"
msgstr "{USER_AMOUNT} UNI"
#: src/nft/components/profile/view/ViewMyNftsAsset.tsx
msgid "{disabledTooltipText}"
msgstr "{disabledTooltipText}"
#: src/components/Polling/ChainConnectivityWarning.tsx
msgid "{label} might be down right now, or you may have lost your network connection."
msgstr "{label} is dalk op die oomblik af, of jy het dalk jou netwerkverbinding verloor."

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: uniswap-interface\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: 2022-11-15 00:17\n"
"PO-Revision-Date: 2022-11-17 01:33\n"
"Last-Translator: \n"
"Language: ar_SA\n"
"Language-Team: Arabic\n"
@@ -196,6 +196,10 @@ msgstr "إضافة سيولة {0}/{1} V3"
msgid "Added"
msgstr "مضاف"
#: src/nft/components/collection/CollectionAsset.tsx
msgid "Added to bag"
msgstr ""
#: src/components/TransactionConfirmationModal/index.tsx
msgid "Added {0}"
msgstr "تم إضافة {0}"
@@ -350,6 +354,10 @@ msgstr "تكاليف مسار أفضل الأسعار ~{formattedGasPriceString}
msgid "Blocked Address"
msgstr "العنوان المحظور"
#: src/nft/components/profile/view/ViewMyNftsAsset.tsx
msgid "Blocked from trading"
msgstr ""
#: src/components/PositionCard/index.tsx
msgid "By adding liquidity you'll earn 0.3% of all trades on this pair proportional to your share of the pool. Fees are added to the pool, accrue in real time and can be claimed by withdrawing your liquidity."
msgstr "من خلال إضافة السيولة ستكسب 0.3٪ من جميع المعاملات على هذا الزوج بما يتناسب مع نصيبك من المجموعة. تضاف الرسوم إلى المجموعة، تتراكم في الوقت الحقيقي ويمكن المطالبة بها بسحب السيولة الخاصة بك."
@@ -570,7 +578,7 @@ msgstr "عنوان العقد"
#: src/components/WalletDropdown/AuthenticatedHeader.tsx
#: src/nft/components/profile/view/ProfileAccountDetails.tsx
#: src/theme/components.tsx
#: src/theme/components/index.tsx
msgid "Copied!"
msgstr "نسخ!"
@@ -713,6 +721,10 @@ msgstr "ايداع السيولة"
msgid "Description"
msgstr "الوصف"
#: src/nft/components/profile/view/ViewMyNftsAsset.tsx
msgid "Deselected"
msgstr ""
#: src/pages/RemoveLiquidity/index.tsx
msgid "Detailed"
msgstr "مفصل"
@@ -746,6 +758,10 @@ msgstr "توثيق"
msgid "Dont see one of your v2 positions? <0>Import it.</0>"
msgstr "لا ترى أحد مراكز v2 الخاصة بك؟ <0>قم باستيراده.</0>"
#: src/nft/components/profile/view/ViewMyNftsAsset.tsx
msgid "ERC-1155 support coming soon"
msgstr ""
#: src/components/vote/DelegateModal.tsx
msgid "Earned UNI tokens represent voting shares in Uniswap governance."
msgstr "تمثل رموز UNI التي تم الحصول عليها حصص التصويت في إدارة Uniswap."
@@ -1537,6 +1553,10 @@ msgstr "إزالة السيولة"
msgid "Removed"
msgstr "إزالة"
#: src/nft/components/collection/CollectionAsset.tsx
msgid "Removed from bag"
msgstr ""
#: src/components/AccountDetailsV2/TransactionBody.tsx
msgid "Removing"
msgstr "إزالة"
@@ -1595,6 +1615,10 @@ msgstr "حدد إجراء"
msgid "Select token"
msgstr "حدد رمز"
#: src/nft/components/profile/view/ViewMyNftsAsset.tsx
msgid "Selected"
msgstr ""
#: src/pages/AddLiquidity/index.tsx
msgid "Selected Range"
msgstr "نطاق محدد"
@@ -2680,10 +2704,6 @@ msgstr "{SOCKS_AMOUNT} UNI"
msgid "{USER_AMOUNT} UNI"
msgstr "{USER_AMOUNT} UNI"
#: src/nft/components/profile/view/ViewMyNftsAsset.tsx
msgid "{disabledTooltipText}"
msgstr "{disabledTooltipText}"
#: src/components/Polling/ChainConnectivityWarning.tsx
msgid "{label} might be down right now, or you may have lost your network connection."
msgstr "قد يكون الرقم {label} معطلاً الآن ، أو ربما فقدت اتصالك بالشبكة."

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: uniswap-interface\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: 2022-11-15 00:17\n"
"PO-Revision-Date: 2022-11-17 01:33\n"
"Last-Translator: \n"
"Language: ca_ES\n"
"Language-Team: Catalan\n"
@@ -196,6 +196,10 @@ msgstr "Afegir {0}/{1} liquiditat V3"
msgid "Added"
msgstr "Afegit"
#: src/nft/components/collection/CollectionAsset.tsx
msgid "Added to bag"
msgstr ""
#: src/components/TransactionConfirmationModal/index.tsx
msgid "Added {0}"
msgstr "Afegit {0}"
@@ -350,6 +354,10 @@ msgstr "La ruta al millor preu costa ~{formattedGasPriceString} en gasolina."
msgid "Blocked Address"
msgstr "Adreça bloquejada"
#: src/nft/components/profile/view/ViewMyNftsAsset.tsx
msgid "Blocked from trading"
msgstr ""
#: src/components/PositionCard/index.tsx
msgid "By adding liquidity you'll earn 0.3% of all trades on this pair proportional to your share of the pool. Fees are added to the pool, accrue in real time and can be claimed by withdrawing your liquidity."
msgstr "Si afegiu liquiditat, guanyareu el 0,3% de totes les operacions daquest parell proporcional a la vostra quota de grup. Les comissions safegeixen al grup, sacumulen en temps real i es poden reclamar retirant la vostra liquiditat."
@@ -570,7 +578,7 @@ msgstr "Adreça del contracte"
#: src/components/WalletDropdown/AuthenticatedHeader.tsx
#: src/nft/components/profile/view/ProfileAccountDetails.tsx
#: src/theme/components.tsx
#: src/theme/components/index.tsx
msgid "Copied!"
msgstr "Copiat!"
@@ -713,6 +721,10 @@ msgstr "Dipòsit de liquiditat"
msgid "Description"
msgstr "Descripció"
#: src/nft/components/profile/view/ViewMyNftsAsset.tsx
msgid "Deselected"
msgstr ""
#: src/pages/RemoveLiquidity/index.tsx
msgid "Detailed"
msgstr "Detallada"
@@ -746,6 +758,10 @@ msgstr "Documentació"
msgid "Dont see one of your v2 positions? <0>Import it.</0>"
msgstr "No veieu cap de les vostres posicions v2? <0> Importeu-la.</0>"
#: src/nft/components/profile/view/ViewMyNftsAsset.tsx
msgid "ERC-1155 support coming soon"
msgstr ""
#: src/components/vote/DelegateModal.tsx
msgid "Earned UNI tokens represent voting shares in Uniswap governance."
msgstr "Les fitxes UNI obtingudes representen accions de vot en la governança Uniswap."
@@ -1537,6 +1553,10 @@ msgstr "Elimina la liquiditat"
msgid "Removed"
msgstr "Eliminat"
#: src/nft/components/collection/CollectionAsset.tsx
msgid "Removed from bag"
msgstr ""
#: src/components/AccountDetailsV2/TransactionBody.tsx
msgid "Removing"
msgstr "Eliminant"
@@ -1595,6 +1615,10 @@ msgstr "Seleccioneu una acció"
msgid "Select token"
msgstr "Seleccioneu el testimoni"
#: src/nft/components/profile/view/ViewMyNftsAsset.tsx
msgid "Selected"
msgstr ""
#: src/pages/AddLiquidity/index.tsx
msgid "Selected Range"
msgstr "Interval seleccionat"
@@ -2680,10 +2704,6 @@ msgstr "{SOCKS_AMOUNT} UNI"
msgid "{USER_AMOUNT} UNI"
msgstr "{USER_AMOUNT} UNI"
#: src/nft/components/profile/view/ViewMyNftsAsset.tsx
msgid "{disabledTooltipText}"
msgstr "{disabledTooltipText}"
#: src/components/Polling/ChainConnectivityWarning.tsx
msgid "{label} might be down right now, or you may have lost your network connection."
msgstr "És possible que {label} estigui inactiva en aquest moment o que hagis perdut la connexió a la xarxa."

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