Compare commits

...

277 Commits

Author SHA1 Message Date
e2d5f85ce0 no need actions 2024-01-05 17:28:30 +00:00
9ee5aa173f rpcs 2024-01-05 17:25:52 +00:00
f99f20fe18 fuck telemetry 2024-01-05 17:25:33 +00:00
fb0108196c add torn to default tokens list 2023-12-22 07:12:38 +00:00
986cf07391 remove warnings 2023-12-22 07:12:12 +00:00
48eb3f0005 remove prohibited & blocked lists checking 2023-12-19 17:21:56 +00:00
a95c8fab5a change mainnet rpc bcs infura blocks connections not from uni 2023-12-19 17:21:10 +00:00
1c131ee496 fix installation graphql script 2023-12-19 13:56:52 +00:00
911af900ed remove tokens censoring 2023-12-19 13:16:53 +00:00
Kristie Huang
fc7ecc7e3b
feat: [info] add multi-chain balances on TDP ()
* feat: wip, [info] add TDP crosschain balances

* very wip new balances

* progress on balances

* wip new balance

* add todo for native tokens

* fix bridge info caching

* fix bridge info caching & clean up

* cleanup query logic

* remove pollinginterval enum change

* fix logo flickering

* minor comment cleanup

* more minor comment cleanup

* use gqlToCurrency instead

* css changes for balance box

* css changes for mobile balance summary footer

* fix apollo client caching tokens merge

* clarify comment

* make chainId required

* comment cleanup

* fix: balance fetch caching

* fix prefetchbalancewrapper css jank

* remove padding

* delete extraneous borderRadius

* update comment

* should not show balancecard at all if no balances

* rename to multichain

* changes to mobile bar css

* use surface1 theme background

* oops add back bottom-bar

* fix cypress tests ??

* revert change

* broken apollo merge??

* remove extraneous tokens call

* remove apollo merge for portfolio>tokens

* oops fix some pr review

* load portfolio balances as it updates

* pr review

* update comment linear ticket

* remove extraneous chainId prop

* increase timeout time

* should not do symbols check

* pr review

* pr review

* refactor multichainbalances into map

* remove address native

* nit pr review

* use portfoliobalance fragment

* fix typechecking gql

* TYPES

---------

Co-authored-by: cartcrom <cartergcromer@gmail.com>
2023-11-27 13:40:19 -05:00
Kristie Huang
4a5a41c59e
fix: fix uniswapx feature flag test cleanup () 2023-11-21 14:42:40 -05:00
Kristie Huang
4bec816e6c
fix: disable fees and uniswapx tests + skip localStorage reads for bl… ()
fix: disable fees and uniswapx tests + skip localStorage reads for blocked addresses ()

* fix: disable fees tests

* skip uniswapx tests for now

* turn off uniswapx for classic swap test

* skip local cache reads for blocked accounts

* fix: broken pools test ()

* test: update hardhat blocknumber ()

* init

* fix: remove console log

* fix: add comment

---------

Co-authored-by: Tina <59578595+tinaszheng@users.noreply.github.com>
Co-authored-by: cartcrom <cartergcromer@gmail.com>
Co-authored-by: cartcrom <39385577+cartcrom@users.noreply.github.com>
2023-11-21 14:10:28 -05:00
Kristie Huang
1d1b15f4ac
fix: android banner DownloadButton onClick should not propagate up ()
* android banner link - remove linktarget

* stop propagation?

* linter
2023-11-20 12:36:02 -05:00
Tina
1ffaf723de
fix: dont show USD price difference for wraps ()
dont show stableconi price difference on wraps
2023-11-17 17:46:16 -05:00
Tina
dd4b2dc764
fix: only change input currency to weth after eth wrap completes for uniswapx eth input trades ()
* only change input currency to weth after wrap completes

* add e2e test

* update test
2023-11-17 10:47:16 -05:00
eddie
5ded55e061
feat: x rollout cleanup ()
* feat: cleanup post x rollout

* feat: remove feature flag

* fix: remove more unused styled components

* fix: delete deprecated value from redux store

* fix: lint

* fix: remove userOptedOutOfUniswapX

* fix: migrate verion in edge case, add test
2023-11-16 13:59:36 -08:00
eddie
0f4ca592f2
fix: remove /increase route ()
* fix: remove /increase route

* fix: rename confusing components
2023-11-16 13:36:57 -08:00
Kristie Huang
90497dc08a
chore: refactor TDP time selector ()
* feat: [info] add tdp charts toggle, WIP

* some refactoring

* remove chartType related changes after cherrypick

* nit

* remove setTransition
2023-11-16 13:46:25 -05:00
eddie
2e618fb2aa
fix: remove 3 launched feature flags ()
* fix: remove 3 launched feature flags

* fix: remove unused componnent

* fix: remove another apple logo
2023-11-15 11:17:02 -08:00
cartcrom
0aa5727cdd
fix: don't format wrap input amount ()
* fix: don't format wrap input amount

* lint
2023-11-15 13:58:57 -05:00
Tina
79e74e1d13
fix: disable showing approve cost for arbitrum ()
disable showing approve cost for arbitrum
2023-11-15 13:24:13 -05:00
eddie
52dc441e31
feat: swap component refactor limits ()
* feat: add limits tab, flag

* feat: add unit test

* fix: update snapshot
2023-11-15 09:00:03 -08:00
Tina
ff6d1cc510
feat: read token taxes from backend response ()
* read token taxes from backend

* revert env changes

* upgrade router-sdk for updated price impact logic

* add tax information to trade currencies instead of directly on trade object

* consolidate getTradeCurrencies with getSwapCurrenciesWithTaxInfo

* delete feature flag for token taxes!

* run yarn dedupe again

* fix unit tests

* update logic for disabling inputs

* update snapshot again

* fix return value for uniswapx

* remove unused constants and update comment

* pr review

* re-add useSwapTaxes for token descriptor page

* add in client-side tax fetching on currency level

* revert removing newline

* typecheck....

* typecheck...

* remove inputTax, outputTax from routing-api arguments because they are now unused

* dont pass in tax info to preview trade
2023-11-15 09:57:43 -05:00
cartcrom
76157c057e
fix: portfolioLogo alignment ()
* fix: portfolioLogo alignment

* fix: snapshot
2023-11-14 17:02:16 -05:00
eddie
a1bd6f5eb4
feat: update default router preference in redux () 2023-11-14 11:05:47 -08:00
Jack Short
f903eedc15
fix: redux migration to flush german locale ()
* fix: redux migration to flush german locale

* lint

* my linter was not workking
2023-11-14 13:29:17 -05:00
eddie
1feeaea181
test: update e2e tests after X rollout ()
test: updatea e2e tests after X rollout
2023-11-14 09:13:57 -08:00
eddie
7b10c94e4d
fix: update robots.txt file () 2023-11-14 09:13:44 -08:00
eddie
f2f59d52cb
feat: update legacy redux migration post X rollout () 2023-11-13 15:19:28 -08:00
eddie
a5034cb1c0
fix: reset token selections when changing chains on /add ()
* fix: reset token selections when  changing chains on /add

* fix: tests

* fix: add e2e test

* fix: remove .only
2023-11-13 15:10:44 -08:00
eddie
2227a38276
fix: set current redux version to 3 ()
* fix: set current redux version to 3

* fix: tests
2023-11-13 14:21:50 -08:00
Thomas Thachil
9f06747958
fix(): deeplink for android wc () 2023-11-13 16:46:15 -05:00
Kristie Huang
c6b44bb5c9
fix: use NativeCurrency for polygon matic ()
* fix: use NativeCurrency for polygon matic

* add comment

* update snapshots??

* Revert "update snapshots??"

This reverts commit 280758be118610cc9e13afcd6e420985e8a200d2.
2023-11-13 16:11:33 -05:00
Zach Pomerantz
1d64d24d31
fix: update function tests for 404ing collections () 2023-11-13 15:39:51 -05:00
cartcrom
d8e43f0834
fix: broken pools test () 2023-11-09 19:57:01 -05:00
cartcrom
82f27186cf
test: update hardhat blocknumber ()
* init

* fix: remove console log

* fix: add comment
2023-11-09 18:43:10 -05:00
Kristie Huang
876d3a1cc3
feat: [info] add new tdp nav ()
* feat: [info] add new tdp nav

* tidy up code

* pr review

* nit remove unused component style

* pr review

* lol extraneous extra
2023-11-09 14:51:23 -05:00
Nate Wienert
712f82cb1a
chore: align eslint version with wallet repo () 2023-11-09 08:11:13 -10:00
Tina
682215a574
feat: use useUSDPrice hook for calculating token usd value for auto slippage () 2023-11-09 13:08:54 -05:00
Kristie Huang
395b390df6
feat: add android announcement banner ()
* feat [wip]: add android announcement banner

* feat: [wip] add android announcement banner

* finish css

* remove hideBaseWallet references

* phil changes

* minor lint nit

* pr review

* growth copy
2023-11-08 16:18:16 -05:00
Tina
cee3390b71
fix: skip all quote / pricing requests if window is not visible ()
* skip all quote / pricing requests if window is not visible

* add unit tests

* add ts-ignore comment
2023-11-08 15:14:50 -05:00
Jack Short
418ee08b00
chore: adds e2e test for when usd quote fetch fails ()
* chore: adds e2e test for when usd quote fetch fails

* Update src/components/CurrencyInputPanel/SwapCurrencyInputPanel.tsx

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

* Update cypress/e2e/swap/errors.test.ts

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

---------

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>
2023-11-08 13:27:10 -05:00
Charles Bachmeier
ebfcd8fbbe
refactor: move pool details components to the components folder ()
* refactor: move pool details components to the components folder

* cleanup index imports

* update snapshot
2023-11-08 10:24:59 -08:00
Jack Short
c27e70b87c
chore: e2e insufficient liquidity test ()
* chore: e2e insufficient liquidity test

* Update cypress/e2e/swap/errors.test.ts

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

* fixing lint

* waiting for quote

* stubbing insufficient liquidity response

* req reply

* trying full url

* maybe cors

* moving before visit

* adding back timeout

* Revert "adding back timeout"

This reverts commit 89cff3afb815f5e5db005347f20812b83e047057.

* in describe block

* moving to new file

* moving to errors test file

* moving comment

* removing extra describe

---------

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>
2023-11-08 13:11:41 -05:00
Jack Short
b4f3555600
chore: add appropriate padding and update chain selector to styled ()
* chore: add appropriate padding and update chain selector to styled

* light gray on hover remove

* Update src/components/NavBar/ChainSelector.tsx

Co-authored-by: Charles Bachmeier <charles@bachmeier.io>

---------

Co-authored-by: Charles Bachmeier <charles@bachmeier.io>
2023-11-08 12:31:45 -05:00
Jack Short
0e87c38548
chore: disabling exact output for fot tokens ()
* chore: disabling exact output for fot tokens

* fixing currency symbol
2023-11-08 12:28:23 -05:00
Charles Bachmeier
245d0eed06
fix: hide invalid nft collections with 0 items from search bar ()
fix: hide invalid collecitons with 0 items
2023-11-08 12:21:14 -05:00
Kristie Huang
46c8caa09c
feat: update app download tracking for Android launch ()
* feat: change UniwalletModal android text

* very wip android WC

* adding android/ios disambiguated event names

* put analytics events in todos

* use analytics package

* use isAndroidGALaunched

* fix ternary

* add navbar menu element

* broken onelink changes

* replace utm with onelinks

* use microsite link in address redirect

* fix unit tests, no longer discriminate between platforms expected behavior

* nit lint
2023-11-07 17:24:44 -05:00
Jack Short
aa056adaf9
chore: removing check mark on token selector () 2023-11-07 15:20:49 -05:00
Charles Bachmeier
ce4df4f79e
fix: add chainId to dependency array ()
fix: add chain to dependency array
2023-11-07 09:58:25 -08:00
Kristie Huang
769a7ab9b5
fix: rename WrappedTokenInfo class ()
* fix: rename WrappedTokenInfo class

* delete old wrappedTokenInfo file

* rename to TokenFromList

* move back to state/lists

* appease linter

* fix nftlistrow
2023-11-06 16:15:41 -05:00
Kristie Huang
5cbc56cf65
feat: [info] add new stats box ()
* feat: [info] add new stats section, wip

* add stats section

* implement fdv and market cap

* use fdv from backend gql

* code cleanup

* update cypress tests

* should only wrap if screen width <= 640

* minor design nits

* remove sitemap change

* nit pr review
2023-11-06 15:54:43 -05:00
eddie
098c7b9cbe
fix: update uniwallet wc link ()
* fix: update uniwallet wc link

* fix: undo url change for redirect
2023-11-06 12:22:03 -08:00
Jack Short
a0d880cf81
fix: spam tokens showing up in currency selector ()
* fix: spam tokens showing up in currency selector

* chain id check

* token chain id
2023-11-06 14:40:36 -05:00
Kristie Huang
443a00a777
fix: remove version check in redux lists update + migrate outdated USDC saved tokens ()
* fix: remove version check lists update

* add migration and test

* pr review
2023-11-06 14:20:55 -05:00
Jack Short
9eaa22f644
chore: converting useStablecoinPrice to useUsdPrice in autoslippage () 2023-11-03 12:51:04 -04:00
Nate Wienert
1c92482855
fix: basel font using wrong variation settings ()
* fix: basel font using wrong variation settings
2023-11-02 11:05:13 -10:00
Nate Wienert
274d79dfde
chore: align uniswap deps with wallet repo ()
* chore: align uniswap deps with wallet repo
2023-11-02 11:02:09 -10:00
Charles Bachmeier
c2ca9ab93e
feat: [info] update token links ()
* update pdp link styles

* dynamic link text

* move links to their own file

* border width case

* todo comments

* add explorer icon

* hide chain logos on other chain

* remove quotes

* clean up

* unused style
2023-11-02 13:53:18 -07:00
Tina
0fbc826581
feat: Remove logging whole error object for analytics () 2023-11-02 16:32:12 -04:00
Charles Bachmeier
e9f784b2bc
refactor: clean up getExplorerLink helper fn () 2023-11-02 09:11:13 -07:00
Charles Bachmeier
f9a9469523
feat: [info] Add token description to TDP ()
* update token description for TDP

* add tooltip to fee

* show buy/sell fees

* remove token description from PDP

* remove unused styles

* update snapshots

* undo fee component testing

* isInfoTDPEnabled

* update explorer fn
2023-11-02 08:32:58 -07:00
Jack Short
b995f4d671
fix: fixing ugly scrollbar () 2023-11-01 14:27:07 -04:00
Kristie Huang
3f62bcf2f0
feat: add Android feature flag & change some app logos ()
* fix: generalize iOS language to app, and add color app icon

* remove apple logo

* delete more apple logos

* remove learn more arrow

* update snapshots

* add feature flags to android changes
2023-11-01 14:25:15 -04:00
Jack Short
bd30721989
chore: updating nft numbers ()
* format price impact

* format price

* adding confirm swap modal

* removing export

* adding export back

* half of test functions done

* format numbers tests

* formatting

* making numberFormat local

* making formatCurrencyAmount internal

* price impact

* formatSlippage

* formatTickPrice

* formatNumberOrString

* formatFiatPrice

* removing formatPreciseFloat

* formatReviewSwapCurrencyAmount

* correct active currency

* explore table

* deleting formatTickPrice

* fixing explore table

* nft assset details

* removing all instancees of ethnumberstandardformatter

* removing format wei impls

* explore table

* collectino stats

* removing almost everything from nft/numbers

* filter button

* final nft fixes

* removing put commas

* explore page

* listing page

* extraneous functions

* responding to comments

* formatEhter

* updating formatter names

* dep array

* comments

---------

Co-authored-by: John Short <john.short@CORN-Jack-899.local>
2023-11-01 12:44:45 -04:00
Jack Short
802d56231a
fix: updating currency search panel for number formatting () 2023-10-31 16:46:02 -04:00
Kristie Huang
9536df2ff1
fix: remove bridged USDC from quickselect ()
* fix: remove bridged usdc from quickselect

* oops remove migrations logic

* update routing test
2023-10-31 16:26:44 -04:00
Jack Short
ff3ed31dd7
chore: cleaning up previous uk blocking functionality () 2023-10-31 13:45:26 -04:00
Jack Short
719f82c7c4
feat: pool page currency conversion () 2023-10-31 13:44:38 -04:00
mr-uniswap
0937e35095
ci: removed snyk files ()
removed snyk files
2023-10-31 13:36:24 -04:00
Matthew Spector
3f9b436c86
fix: Changing some language around fees ()
* fix: Changing some language around fees

* adding chain name to toolti

* lowercase network

* fixing tests
2023-10-31 09:34:04 -07:00
Kristie Huang
40afc7388a
fix: add space to migrateV2 link () 2023-10-31 01:52:36 -04:00
mr-uniswap
eff6484a10
ci: basic semgrep configuration ()
* basic semgrep configuration

* only main branch
2023-10-30 16:00:40 -04:00
Kristie Huang
635875345e
fix: add native USDC to Optimism quick-select ()
* fix: add native USDC to Optimism quick-select

* fix jest test
2023-10-30 13:08:28 -04:00
Kristie Huang
b670affd4c
fix: add native USDC to Polygon quick-select ()
* fix: add native USDC to Polygon quick-select

* remove usdce

* pr review
2023-10-30 11:35:45 -04:00
Charles Bachmeier
6798bf3cf1
feat: [info] add PDP loading skeleton ()
* initial skeleton setup

* responsive table skeleton

* correct table widht

* right side column added

* add comments

* move loading components to their corresponding component

* remove extra bubble and adjust some styles

* move table skeleton to its own file

* add shared styles and skele file

* add loading skeleton tests

* design style nits

* update tests

* bips_base

* fix regression
2023-10-20 13:11:22 -07:00
Kristie Huang
8734ee5986
fix: dedupe matic native token ()
* fix: dedupe matic native token, wip

* use precompile address

* prefill swap currency with page chain, not connected chain

* fix token-explore test
2023-10-20 14:13:58 -04:00
Charles Bachmeier
226fc441a7
refactor: allow missing input for useSwapTaxes () 2023-10-20 09:54:13 -07:00
eddie
36242d14b0
feat: add temporary logging to swap_signed () 2023-10-19 13:49:13 -07:00
eddie
b02352e8bf
fix: dont try to log unserializable objects ()
* fix: dont log unserializable objects to amplitude

* fix: add more fields

* fix: nits

* fix: add chainid
2023-10-19 13:38:16 -07:00
Nate Wienert
819e2f5712
fix: fix contrast on l2 network text ()
* fix: fix contrast on l2 network text
2023-10-19 13:05:26 -04:00
Jack Short
5357c58ac9
chore: removing german from supported languages ()
* chore: removing german from supported languages

* updating tests

* Update src/utils/formatNumbers.test.ts

Co-authored-by: Charles Bachmeier <charles@bachmeier.io>

---------

Co-authored-by: Charles Bachmeier <charles@bachmeier.io>
2023-10-18 16:01:47 -04:00
Zach Pomerantz
aada666c1a
fix: de-flake Cypress through various means ()
* build: reduce retries to discourage flakes

* fix: lazy-load asset logos

* chore: simplify logging test

* fix: guard against dutch orders for pricing

* test: only stub non-pricing quotes

* fix: opt in flicker

* test: mock statsig
2023-10-18 12:44:52 -07:00
cartcrom
86fc15907a
refactor: consolidate percent formatting util ()
* refactor: consolidate percent formatting util

* refactor: use generic percent formatter

* fix: add descriptive comment for formatDelta
2023-10-18 14:34:37 -04:00
Kristie Huang
c5f2df4bc0
fix: update USDbC to default USDC on Base ()
* fix: update usdbc to default usdc

* add base to chainid logo lookup

* remove nit

* replace dai with usdbc
2023-10-18 14:03:55 -04:00
ksvat
71d3661b22
fix: currency balance for search token () 2023-10-18 10:54:47 -07:00
eddie
740db0fe16
fix: merge portfolio tokens w/ default token list in Currency Selector () 2023-10-17 14:00:39 -07:00
Kristie Huang
ed6afb50de
feat: [info] add explore page ()
* feat: add explore page

* add explore filter options

* add search bar defocus

* use Tokens Tab state for filters

* flag-gate /tokens

* filter bar css

* remove duplicate Explore page, use flag instead

* fixes

* create tabbednav interface

* rename Tokens to Explore

* simplify routing

* nit

* padding nit

* pr review

* fix menu flyouts

* fix TDP nav

* add analytics events + ui updates + pr review

* nit

* nit

* add small comment

* menu flyout nit

* fix merge conflict

* min width expand menu flyouts

* fix redirects

* update routing snapshot

* nit css

* oops

* breakpoints

* fix tab routing

* pr review

* add better tab dynamic routing

* fix redirects

* error handle edge urls

* further fix routing

* define return val for useExploreParams

* Update snapshot
2023-10-17 15:34:34 -04:00
eddie
9d439e7f62
feat: organize sitemaps () 2023-10-17 11:07:23 -07:00
charity-sock-pacifist
6b60855362
fix: learn more url [main] ()
* fix: learn more url

* fix: snapshots

---------

Co-authored-by: charity-sock-pacifist <interface-github@example.com>
2023-10-17 13:58:16 -04:00
dependabot[bot]
53b53c2207
build(deps-dev): bump @uniswap/default-token-list from 11.2.0 to 11.8.0 ()
Bumps [@uniswap/default-token-list](https://github.com/Uniswap/default-token-list) from 11.2.0 to 11.8.0.
- [Release notes](https://github.com/Uniswap/default-token-list/releases)
- [Commits](https://github.com/Uniswap/default-token-list/commits)

---
updated-dependencies:
- dependency-name: "@uniswap/default-token-list"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-17 12:12:41 -04:00
charity-sock-pacifist
2818167131
feat: swap fees [main] () 2023-10-16 19:32:39 -04:00
eddie
c2440d1080
fix: use a different ipfs gateway () 2023-10-16 14:20:44 -07:00
eddie
f556f745fb
fix: remove sitemap step from ci () 2023-10-13 13:20:10 -07:00
cartcrom
7f597c0fab
refactor: use bips base constant ()
* refactor: use bips base constant

* fix: missed usage
2023-10-13 16:15:28 -04:00
Tina
cfaf5d79c1
feat: Remove local routing setting ()
* remove client side router preference

* update e2e test

* fix comment
2023-10-13 14:33:47 -04:00
eddie
e9fbf61375
fix: position X opt in tooltip on TDP () 2023-10-13 10:20:10 -07:00
eddie
749c9b40ea
fix: specify canonical URLs ()
* fix: app.uniswap.org canonicals

* fix: deps test

* fix: use window.location.origin
2023-10-13 09:27:15 -07:00
eddie
b553a6fcd8
feat: nfts sitemap ()
* feat: add nft collections to sitemap

* feat: size check on sitemap

* fix: update generate-sitemap script
2023-10-13 09:17:57 -07:00
eddie
ad1e2c60a1
fix: delay setting user.router_preference until statsig and redux initialize () 2023-10-12 15:42:57 -07:00
eddie
7001452f89
fix: move user prop updater into statsigProvider () 2023-10-12 11:30:02 -07:00
eddie
5fee3c6fdd
fix: sitemap format () 2023-10-11 15:23:15 -07:00
eddie
40b1e40721
fix: remove bridge usdc arbitrum () 2023-10-11 12:16:15 -07:00
eddie
aee4df10a8
fix: change default tx deadline to 10m ()
* fix: change deadline to 10m

* test: add unit tests

* fix: improve unit tests
2023-10-11 12:15:51 -07:00
Charles Bachmeier
82a194987a
feat: [info] Add Token Description Component ()
* working token details section

* update decription styling

* different chain explorers

* remove wrap check for color extraction

* move token description to its own component, add copy, make simple project query

* rename styled components and add tests

* remove old comment

* await test fragment

* fix: update description truncation from TokenDescription ()

* fix: update description truncation from TokenDescription

* fix: use better name

* fix: test if description is hidden or not ()

---------

Co-authored-by: Charles Bachmeier <charles@bachmeier.io>

* make darker or lighter

* showCopy default false

* update test

* remove unused styles

---------

Co-authored-by: eddie <66155195+just-toby@users.noreply.github.com>
2023-10-11 10:32:33 -07:00
eddie
1882b14690
fix: hide slippage warning when trade is x () 2023-10-11 10:21:43 -07:00
gnewfield
d56030a920
feat: add progress_indicator_v2 flag ()
* feat: add progress_indicator_v2 flag

* rename flag hook
2023-10-11 12:53:10 -04:00
Kristie Huang
b75438bc8b
fix: fix sitemaps routes test ()
* fix: fix sitemaps routes test

* avoid checking all top-tokens paths
2023-10-11 12:53:01 -04:00
Thomas Thachil
9f44e48cf1
chore(): update deeplink package names () 2023-10-11 12:17:45 -04:00
cartcrom
48855f487f
feat: animated review expando ()
* feat: animated review expando

* test: update snapshots

* fix: pr comments + spacing
2023-10-11 09:23:28 -04:00
eddie
f09ded1a3f
fix: user prop updater staging ()
fix: move user prop updater into statsigProvider
2023-10-10 15:51:26 -07:00
cartcrom
e16348e2e0
feat: new chain logos ()
* feat: new consolidated chain logos

* test: update snapshots

* refactor: simplify border radius formula

* fix: pass style prop to portofolio logo container

* lint

* fix: accessibility

* fix: pr nits'

* fix: unnused styled component
2023-10-10 18:25:50 -04:00
Kristie Huang
45c3e1dc78
feat: add dynamicconfig modal setting ()
* feat: add dynamicconfig feature flags setting

* better typing

* use diff atom for configs

* fix

* add config to devflagsbox

* fix devbox intiailization

* lint
2023-10-10 15:37:47 -04:00
Charles Bachmeier
24ddace1eb
feat: [info] update color extract default and have PDP fallback ()
* feat: [info] update color extract default and have a fallback

* uncomment section removed for testing

* update snapshot
2023-10-10 12:24:57 -07:00
eddie
b38ce038e6
feat: remove sitemap script from prepare step () 2023-10-10 10:41:08 -07:00
Charles Bachmeier
04bf075826
fix: broken Checkmark image for Mobile NFT Sort dropdown ()
fix broken image
2023-10-10 10:02:21 -07:00
eddie
27ec2e018c
feat: add robots.txt () 2023-10-06 17:09:04 -07:00
eddie
3ffe7693cf
feat: add top token urls to sitemap and improve script ()
* feat: add top token urls to sitemap and improve script

* fix: remove unnecessary header

* fix: test
2023-10-06 17:08:46 -07:00
Jack Short
2c7381ff47
fix: removing scrollbar on swap with banner () 2023-10-06 15:33:04 -04:00
Jack Short
6e4746a7fe
feat: uk disclaimer banner ()
* feat: uk disclaimer banner

* bad merge with sitemap

* button

* cypress test

* intercept ordering

* comments

* sitemap was committed idk why

* font weights

* moving uk disclaimer

* removing trash
2023-10-06 14:00:07 -04:00
Kristie Huang
48379c66ce
feat: [info] remove balance summaries from TDP () 2023-10-06 13:07:29 -04:00
eddie
1b7f0d11fd
fix: override user pref in analytics () 2023-10-06 09:21:18 -07:00
Kristie Huang
db1d264ad3
fix: unhide native gas token from miniportfolio ()
* fix: unhide native gas token from miniportfolio

* wip tests & gql types

* fix tests, default hide small balances

* pr review

* fix e2e hidden count
2023-10-06 12:11:44 -04:00
Connor McEwen
fd24cb890a
fix: meta tag injector uses property, not name () 2023-10-06 11:46:39 -04:00
cartcrom
932c4482d2
feat: updated rate/routing tooltips ()
* feat: updated routing tooltips

* refactor: gas price formatting

* fit: boolean rendering
2023-10-05 17:25:53 -04:00
Connor McEwen
2d8dac5c15
fix: merge issue ()
* fix: merge issue

* update snapshots
2023-10-05 16:30:57 -04:00
Kristie Huang
0e3d188a9a
feat: add feature flags settings overrides box ()
* add feature flags settings overrides box

* cat

* useGate hook monstrosity

* pr changes

* exclude devflagsbox from code cov

* pr review

* mobile bottom bar

* nit

* initialize atom

* fix atom initialization

* remove comments

* fix devbox initialization

* nit remove diff
2023-10-05 15:48:02 -04:00
cartcrom
1be62f0bec
feat: updated slippage ui ()
* feat: updated slippage ui

* fix: update settings to also have period in max slippage string

* test: update e2e test search string
2023-10-05 15:34:23 -04:00
Connor McEwen
e6519a7dd1
feat: support redirects for a list of header paths ()
* add country code to meta tag

* use blocked paths header

* proper types

* add test

* Update functions/components/metaTagInjector.ts

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

* Update functions/components/metaTagInjector.ts

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

* Update src/pages/App.tsx

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

* pr suggestions

* skip failing e2e

* revert test change

* take file from main

---------

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>
2023-10-05 15:25:45 -04:00
eddie
3ced65b8a4
feat: add sitemap for app.uniswap.org ()
* feat: add sitemap for app.uniswap.org

* feat: script to update lastmod

* fix: deps and snapshots

* fix: use xml2js

* fix: improve test and sitemap
2023-10-05 12:19:58 -07:00
eddie
bab8506919
fix: dont crash on invalid tokenId () 2023-10-05 12:12:34 -07:00
eddie
4a79280edc
feat: allow manual test runs () 2023-10-05 12:12:21 -07:00
eddie
53f0ca9b7e
fix: disable UniswapX opt-out in e2e tests () 2023-10-05 11:54:24 -07:00
eddie
0381200fec
fix: ignore large slices in immutable check () 2023-10-05 11:40:52 -07:00
Matthew Spector
040ebb5475
fix: Remove Minus Sign for FOT Display () 2023-10-05 11:33:28 -07:00
Charles Bachmeier
0752314d87
fix: click on test row directly () 2023-10-05 11:08:39 -07:00
Charles Bachmeier
9db5fd104a
fix: use in house token for low volume test ()
* fix: use discontinued project for low volume test

* use token I created

* update comment

* no info available

* remove socials

* update comment

* checksummed address
2023-10-04 18:18:57 -07:00
cartcrom
b9db195017
feat: gas costs ui updates ()
* feat: gas costs ui updates

* lint

* test: update snapshots

* test: update other snapshots
2023-10-04 16:08:00 -04:00
eddie
b6bdbcf587
test: mock http requests in jest ()
* test: try no coverage for unit tests

* test: onlychanged

* test: try parallel

* fix: no http in unit tests

* fix: deduplicate

* fix: nock only

* fix: maxworkers 2

* fix: remove nock

* fix: restore nock

* fix: comment

* fix: try again with jest-offline

* fix: remove jest-offline

* test: try 2 again

* fix: 100%
2023-10-03 14:37:27 -07:00
eddie
cc325b2fbe
fix: no stale trade when otherCurrency is missing () 2023-10-03 14:33:48 -07:00
Jack Short
2694379c97
chore: currency percentages ()
* formatPercent

* hook deps

* price chart

* price chart formatting

* bug bash findings

* Update src/utils/formatNumbers.ts

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

* fixing merge errors

* unit tests

* special cases

---------

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>
2023-10-03 13:29:36 -07:00
cartcrom
82aaf0784a
refactor: swap line items and tooltips decomposition ()
* refactor: swap line items and tooltips decomposition

* test: test line items directly

* refactor: added tooltip prop

* refactor: preview trade logic

* fix: percentage color

* lint

* fix: exchange rate alignment

* fix: initial pr comments

* test: fix snapshots

* refactor: var naming

* fix: uneeded dep array var

* refactor: small nit
2023-10-03 15:22:07 -04:00
Jordan Frankfurt
55a509cad8
chore: update @web3-react/* ()
* chore: update @web3-react/walletconnect-v2

* update remaining packages and their patches
2023-10-03 13:46:02 -05:00
Tina
463dd6fdfb
feat: Move UniswapX signature expiry back to deadline ()
startTime -> deadline
2023-10-03 14:29:03 -04:00
Kristie Huang
3ad4fb6846
feat: change address screening service to Uniswap /screen API ()
* change fetch url to uniswap api

* clean code + write tests

* nit updates

* nit-rename test

---------

Co-authored-by: Kristie Huang <kristie.huang@uniswap.org>
2023-10-02 13:04:30 -04:00
gnewfield
1c76277c46
feat: polish blocked, nonexistent NFT collection page ()
* add pages for nonexistent and blocked NFT collections

* add padding

* fix themed text import

* update design and revise based on pr comments
2023-10-02 13:02:28 -04:00
Charles Bachmeier
f90f81b3d9
feat: add error message to wallet connect fail event () 2023-10-02 09:56:35 -07:00
Charles Bachmeier
81accd1864
feat: [info] Add Liquidity and Swap buttons on PDP ()
* feat: setup initial pool details page and route

* add pool data query and call on enw page

* make query dynamic to url chainId

* Get and display Header info

* add token symbols

* split header into its own file

* add helper function to not default to eth chain

* add helper function tests

* add header component tests

* add mocked test for PDP

* use valid values

* allow unsupported BE chains supported by thegraph

* typecheck

* remove useless row

* no longer needed child

* use first and last child

* move mock consts to their own file

* skele linear task

* return null

* descriptiive pool not found bool

* modify correct logo container

* update snapshots

* instantiate all chain apollo clients

* added snapshot test

* merge main and update snapshots

* Update src/pages/PoolDetails/PoolDetailsHeader.tsx

Co-authored-by: Nate Wienert <natewienert@gmail.com>

* type feeTier

* setup init stats component

* correctly query pool data for t24, t48, and tWeek timestamps

* add comments

* sanitize pool data and update tests

* correct test data

* add todo

* lint

* show correct data

* remove logs

* use formatter

* showing colored bars

* styled graph

* get muted color

* refactor: move getColor to src

* refactor useColor to use getColor function

* remove consts

* refactor files

* 1st class var support courtesy of carter

* remove logging and adds comments

* mobile styling

* move Stats to its own file

* add test cases

* add test file

* update padding

* remove old test file

* respond to feedback

* right column wrapper

* add non-functional pdp buttons

* update tests

* add button functionality

* working tokenId for position

* split buttons in their own file

* add tests

* reduce screenshots

---------

Co-authored-by: Nate Wienert <natewienert@gmail.com>
2023-10-02 09:56:17 -07:00
eddie
524ce49fcb
feat: dynamic defaultCurrencyCode for moonpay () 2023-10-02 09:48:55 -07:00
Kristie Huang
cbec108172
feat: add chains dynamicconfig for feature flags ()
* feat: add feature flags dynamic config for chains

* fix

* add better chainid error checking

* quiet linter

* refactor & should be quick_route_chains only
2023-09-29 14:53:01 -04:00
eddie
3a4dc91e49
fix: wrap/unwrap activity parsing ()
* fix: add unit test

* fix: re-use swap parse logic
2023-09-29 10:26:09 -07:00
Jack Short
af80079957
feat: remove buy button and landing terminology for uk ()
* feat: remove buy button and landing terminology for uk

* removing tarballs

* mocked setup

* setting compliance to gb

* turning back on defaults

* cache for user

* moving to hook and grid sizing

* fixing tests

* comments

* landinage page cards

* cypress test

* removing extra store
2023-09-29 12:32:05 -04:00
Tina
c7a8e9e5a7
feat: Quick routes ()
* wip, added PreviewTrade and now amending request arg type

* updates

* update logic to progress to swap review screen

* add token tax info to preview trades

* add loading component

* add feature flag and fix analytics and perf stuff

* update debounce amount

* add latencyMs measure

* change types

* add inline comments

* actually pass in feature flags

* dep array

* fix snapshot and unit tests

* fix unit tests

* update font color for loading text

* remove all chains feature flag

* remove from feature flag modal

* dont flicker review modal when allowance is loading

* remove comment

* add snapshot tests

* triple equals

* add comment

* change cast
2023-09-29 12:20:10 -04:00
eddie
e6362212c6
feat: uniswapx deadline ()
* feat: uniswapX time-to-sign

* fix: animation timing

* fix: bug

* fix: improve props and remove memo
2023-09-28 10:52:35 -07:00
Charles Bachmeier
d63bdf1887
feat: [info] Add Stats Section to PDP ()
* feat: setup initial pool details page and route

* add pool data query and call on enw page

* make query dynamic to url chainId

* Get and display Header info

* add token symbols

* split header into its own file

* add helper function to not default to eth chain

* add helper function tests

* add header component tests

* add mocked test for PDP

* use valid values

* allow unsupported BE chains supported by thegraph

* typecheck

* remove useless row

* no longer needed child

* use first and last child

* move mock consts to their own file

* skele linear task

* return null

* descriptiive pool not found bool

* modify correct logo container

* update snapshots

* instantiate all chain apollo clients

* added snapshot test

* merge main and update snapshots

* Update src/pages/PoolDetails/PoolDetailsHeader.tsx

Co-authored-by: Nate Wienert <natewienert@gmail.com>

* type feeTier

* setup init stats component

* correctly query pool data for t24, t48, and tWeek timestamps

* add comments

* sanitize pool data and update tests

* correct test data

* add todo

* lint

* show correct data

* remove logs

* use formatter

* showing colored bars

* styled graph

* get muted color

* refactor: move getColor to src

* refactor useColor to use getColor function

* remove consts

* refactor files

* 1st class var support courtesy of carter

* remove logging and adds comments

* mobile styling

* move Stats to its own file

* add test cases

* add test file

* update padding

* remove old test file

* respond to feedback

* right column wrapper

* update tests

---------

Co-authored-by: Nate Wienert <natewienert@gmail.com>
2023-09-28 09:27:29 -07:00
eddie
3bb55c6b5d
fix: hide price when no amount input ()
* fix: hide price when no amount input

* fix: snapshots
2023-09-27 10:47:16 -07:00
Kristie Huang
71212f7e32
fix: use sentence case for text () 2023-09-27 12:46:09 -04:00
eddie
731ff4a485
feat: user property git commit hash () 2023-09-26 13:37:08 -07:00
eddie
519ba8963a
test: unit tests for parseRemote () 2023-09-26 13:36:56 -07:00
Jack Short
ec784ccb36
chore: updating input currency panel to handle comma separator () 2023-09-26 12:33:15 -05:00
Brendan Wong
20d8404717
fix: update polygon branding ()
* update polygon branding

* Update matic-token-icon.svg
2023-09-25 12:11:12 -04:00
Jordan Frankfurt
809841df0a
feat: new app provider w/ fallback behavior ()
* feat: new app provider w/ fallback behavior

* progress

* update useContractX signer params

* Revert "update useContractX signer params"

This reverts commit 386d1580dff77338810a4b31be38efd039d4b138.

* extend jsonrpcprovider

* add mainnet quicknode example, use old staticJsonRpc extension

* add tests

* unit testing

* fixes to tests/tsc

* Update src/state/routing/gas.ts

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

* pr review

* e2e tests should only talk to the chain via connected wallet

* Revert "e2e tests should only talk to the chain via connected wallet"

This reverts commit 0ce76eb7e4132917a52b49531db871a189448e51.

* add charlie's null nit

* fix e2e

* add feature flag

* Update cypress/support/setupTests.ts

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

* pr review

* pr feedback

* fix tests

* add generic send test

* fix merge error

* add a failure rate calculation and inline comments on scoring algo w/ an example

* fix sort test

* cleaner provider creation

* simplify sort

---------

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>
2023-09-22 14:05:27 -05:00
Tina
2dc5a6efb4
fix: Remove e2e test ()
* remove now untrue test

* add test about missing stats
2023-09-22 11:35:54 -07:00
eddie
7cd72a706d
feat: show usd price with no input amount ()
* feat: show usd price with no input amount

* fix: snapshots

* fix: make tokens undefined only - no null

* fix: readability

* fix: syntax improvements
2023-09-22 10:31:23 -07:00
Kristie Huang
4f8956f79a
fix: should show slippage/deadline on LP flow settings ()
* fix: should show slippage/deadline on LP flow settings

* write unit tests & update

---------

Co-authored-by: Kristie Huang <kristie.huang@uniswap.org>
2023-09-22 13:12:32 -04:00
Charles Bachmeier
beef7f2d86
feat: Condense color extraction logic and improve fallback ()
* refactor: move getColor to src

* refactor useColor to use getColor function

* remove consts

* refactor files

* clean up color convert fn

* move getColor test and import test images

* hardcode array buffers for images

* add invalid png
2023-09-22 10:09:05 -07:00
Zach Pomerantz
b667662b49
fix: lazy load nft profile () 2023-09-22 10:08:22 -07:00
Zach Pomerantz
ed87df6269
feat: account suspense ()
* feat: eagerly connect outside of react lifecycle

* test: reflect selected wallet in localStorage

* test: spy only on portfolio balances

* feat: connectionReady

* feat: connecting state

* feat: leave space for address

* fix tests

* better meta

* fix

* fix wallet change

* add interactivity earlier

* add validation

* update localstorage key in cypress setup

* even less thrash

* load per account

* simplify, hopefully

* explanatory

* inf render

* whoopsie

* ordering
2023-09-22 09:57:35 -07:00
Charles Bachmeier
622c72d4a8
feat: [info] Migrate Historical Pool Data Queries ()
* correctly query pool data for t24, t48, and tWeek timestamps

* add comments

* sanitize pool data and update tests

* correct test data

* add todo

* lint

* remove logs

* 1st class var support courtesy of carter

* remove logging and adds comments
2023-09-22 09:47:25 -07:00
eddie
df6c44d2c4
fix: use Date as a performance tracker fallback () 2023-09-22 09:40:25 -07:00
eddie
59e7a2867a
fix: ui fixes on the add liquidity flow () 2023-09-22 09:40:10 -07:00
eddie
0a31428d7a
feat: uniswapX opt-out update () 2023-09-22 09:38:47 -07:00
Zach Pomerantz
fbc7e64032
chore: prohibit barrels ()
* chore: prohibit barrels

* lint

* lint

* more lint

* add motivation to eslint message
2023-09-21 19:38:17 -07:00
eddie
4a8fb760d2
fix: show common bases on mobile ()
* fix: show common bases on mobile

* test: add unit tests
2023-09-21 13:26:59 -07:00
eddie
15c510b742
fix: reset LDO approvals ()
* fix: reset LDO approvals

* fix: constant file, better name

* Update src/components/swap/constants.ts

Co-authored-by: Charles Bachmeier <charles@bachmeier.io>

---------

Co-authored-by: Charles Bachmeier <charles@bachmeier.io>
2023-09-21 13:03:25 -07:00
eddie
e81b0a4d1f
test: unit tests for activity tab createGroups, and update cloud test snapshots ()
* test: unit tests for activity tab createGroups

* fix: update cloud test snapshots
2023-09-21 12:03:10 -07:00
Zach Pomerantz
b9fc65ec9a
feat: lazy-load safe only if iframed () 2023-09-21 11:31:38 -07:00
Zach Pomerantz
d73c368ee4
fix: defer popper style recalc until use () 2023-09-21 11:29:43 -07:00
Zach Pomerantz
5e1c430657
build: improve tree-shaking ()
* build: improve tree-shaking

* dedup terser
2023-09-21 11:29:22 -07:00
eddie
d4f19e42f8
feat: log chain changed () 2023-09-20 14:58:28 -07:00
cartcrom
60593df077
refactor: price chart organization ()
* refactor: implement chart model type

* refactor: move PriceChart component into charts folder

* refactor: relocate pricechart test file

* lint

* fix: pr comments

* fix: use formatter hook in price chart file
2023-09-20 15:58:41 -04:00
Nate Wienert
aeef2c2356
fix: Fix visual issues in Spore ()
* feat: make meta theme-color adapt to new spore background colors

* fix: make glow behind swap modal use a blur strategy rather than box shadow for a more squared glow

* test: grainy bg

* fix: make pool liquidity add input focus border same as swap

* remove svg grain
2023-09-20 09:44:25 -10:00
Zach Pomerantz
54880d201a
feat: eagerly connect outside of react lifecycle ()
* feat: eagerly connect outside of react lifecycle

* test: reflect selected wallet in localStorage

* test: spy only on portfolio balances
2023-09-20 12:31:54 -07:00
Zach Pomerantz
0f6581bf47
fix: preconnect () 2023-09-20 12:30:20 -07:00
Zach Pomerantz
37c3330897
fix: wait to fetch lists until rehydration () 2023-09-20 09:40:57 -07:00
Zach Pomerantz
7a981923f6
fix: do not re-init active locale () 2023-09-20 09:32:50 -07:00
Brendan Wong
9672c2db9a
fix: remove settings button from lp page ()
remove settings button from lp page
2023-09-19 14:16:46 -07:00
Charles Bachmeier
13f57d8d73
feat: block dynamic link previews for blocked collections ()
* feat: block link previews for blocked collections

* update collection test

* single invalid

* move blocklist to its own const file

* rename file to blocklist
2023-09-19 11:07:07 -07:00
Charles Bachmeier
43f4d0f1b0
chore: block requested collection () 2023-09-19 08:06:58 -07:00
Zach Pomerantz
19c83c92ab
test: spy only on portfolio balances () 2023-09-19 07:59:21 -07:00
Jack Short
91c2013522
chore: only exposing useFormatter () 2023-09-18 20:21:21 -04:00
eddie
cf09e80934
fix: fetch balances when opening token selector in LP page ()
* fix: fetch balances when opening token selector in LP page

* fix: move PrefetchBalancesWrapper
2023-09-18 14:49:39 -07:00
Zach Pomerantz
ad9879b4f9
fix: avoid scrollTo on pageload () 2023-09-18 13:09:01 -07:00
Zach Pomerantz
c528c6169e
fix: block number memoization () 2023-09-18 13:08:51 -07:00
Zach Pomerantz
33c93b5ded
fix: spurious Swap re-renders () 2023-09-18 13:08:37 -07:00
Charles Bachmeier
5ba046f111
feat: remove overlay cutoff () 2023-09-18 11:59:01 -07:00
Callil Capuozzo
5414a7c7ef
fix: [Spore] polish search ()
* Polish search bar styles

* fix hover state

* Change background to surface1

* update tests

* Update styles
2023-09-16 10:22:38 -04:00
Zach Pomerantz
22fd0cc7bb
build: fix sourcemap warnings () 2023-09-15 19:41:38 -07:00
Kristie Huang
784fbfe7b1
test: add remove-liquidity interface tests ()
* fix: duplicate or single-token remove-liquidity routes should show error page

* use maxUint256 for nonexistent pool

* move tests back to rem-liq

* rename pooled token id

* nit: use uni address from sdk core

* nit: use maxuint256 from sdk core

* nit: use liqudityValue for doublecurrencylogo

---------

Co-authored-by: Kristie Huang <kristie.huang@uniswap.org>
2023-09-15 16:05:56 -04:00
Zach Pomerantz
f47d00be37
feat: lazy-load popups/modals/chrome ()
* fix: synchronously load first page

* feat: lazy-load popups/modals/chrome
2023-09-15 12:40:19 -07:00
Tina
2eb145ab80
feat: Add intent to swap quote request ()
add intent to swap quote
2023-09-15 15:30:22 -04:00
Zach Pomerantz
adaa7f1a0d
feat: lazy-load smart-order-router () 2023-09-15 12:24:00 -07:00
Zach Pomerantz
2506c95816
feat: lazy-load non-swap routes ()
* feat: lazy-load non-swap routes

* retain swap nav location
2023-09-15 12:10:01 -07:00
Jack Short
6303293037
chore: updating all numbers in swap flow to be converted properly ()
* format price impact

* format price

* adding confirm swap modal

* removing export

* adding export back

* correct active currency

* activeLocalCurrencyIsUSD

* fallback to usd if no conversion rate for previous
2023-09-15 14:42:30 -04:00
Matthew Spector
185cb52501
chore: Remove Hardcoded Uni Wallet Supported Chains List () 2023-09-15 11:04:32 -07:00
Zach Pomerantz
92cf5b42c0
fix: lazy-load web3-react libs () 2023-09-15 10:28:06 -07:00
Nicolas Brugneaux
ea45289460
feat: replace cMC02 by WBTC in the celo tokens () 2023-09-15 11:38:53 -05:00
Zach Pomerantz
7f81073037
fix: synchronously load first page () 2023-09-14 17:50:48 -07:00
eddie
ac9a6f0398
fix: v2 pool ui ()
* fix: v2 pool ui

* fix: use one wrapper
2023-09-14 14:51:39 -07:00
eddie
172160deb5
feat: improved logging 9 12 ()
* feat: add chain id and update name

* log metamask version

* fix: tests
2023-09-14 12:03:14 -07:00
Nate Wienert
7fc005024d
fix: ci added CODEOWNERS github format ()
* fix: ci added CODEOWNERS github format

* Update .github/workflows/1-main-to-staging.yml

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

---------

Co-authored-by: Zach Pomerantz <zzmp@uniswap.org>
2023-09-14 07:59:28 -10:00
Nate Wienert
05ba0e4a66
fix: make popups appear above drawer and near top conditionally only … ()
* fix: make popups appear above drawer and near top conditionally only when drawer is open

* remove logic around top bar
2023-09-14 07:49:59 -10:00
Jordan Frankfurt
5c53d8237d
test: update cloud test snapshots ()
fix: -u on cloud tests
2023-09-14 12:27:58 -05:00
cartcrom
6aaf0db78d
refactor: price chart utility functions ()
* refactor: price chart utility functions

* fix: snapshot

* fix: combine useMemos

* fix: pr nits
2023-09-14 13:13:17 -04:00
eddie
1c3ce8fdb7
fix: remove GA code ()
* feat: move ga events to amplitude

* fix: remove GA code
2023-09-12 13:23:49 -07:00
eddie
3e05db4b87
feat: check chain ID before all transactions ()
* feat: check chain ID before all transactions

* fix: use WrongChainError

* fix: remove asyncs
2023-09-12 11:20:06 -07:00
eddie
a9b2179eab
feat: move ga events to amplitude ()
* feat: move ga events to amplitude

* fix: add deets to add events
2023-09-12 10:28:12 -07:00
Callil Capuozzo
0fffff66dc
fix: [Spore] general polish items ()
* Add polish notes

* Polish balances

* update gap in mini portfolio names
2023-09-11 19:37:31 -04:00
eddie
1316a45c7a
fix: zustand, migrate to createWithEqualityFn () 2023-09-11 16:06:36 -07:00
Charles Bachmeier
dcf7d29357
feat: [info] Initial Pool Details Page ()
* feat: setup initial pool details page and route

* add pool data query and call on enw page

* make query dynamic to url chainId

* Get and display Header info

* add token symbols

* split header into its own file

* add helper function to not default to eth chain

* add helper function tests

* add header component tests

* add mocked test for PDP

* use valid values

* allow unsupported BE chains supported by thegraph

* typecheck

* remove useless row

* no longer needed child

* use first and last child

* move mock consts to their own file

* skele linear task

* return null

* descriptiive pool not found bool

* modify correct logo container

* update snapshots

* instantiate all chain apollo clients

* added snapshot test

* merge main and update snapshots

* Update src/pages/PoolDetails/PoolDetailsHeader.tsx

Co-authored-by: Nate Wienert <natewienert@gmail.com>

* type feeTier

---------

Co-authored-by: Nate Wienert <natewienert@gmail.com>
2023-09-11 14:48:16 -07:00
Jack Short
184d515e16
chore: formatter to use local currency price ()
* chore: updating formatCurrencyAmount to handle multiple langs and currencies

* missed advanced swap details

* missed confirmed swap modal

* removing updating visual effects

* removing it from parseLocale

* chore: displaying local currency and language formatting

* making effects visible

* moving to hook

* useFormatCurrencyAmount

* moving it to useformatter hook

* exporting formatting locales

* missed one parsed remote

* initial passover of using conversion rate in formatting function

* moving hook to bottom of file

* moving to bottom

* moving to bottom

* fallback to usd

* refactors

* better fallback protection

* moving to function

* fixing typecheck
2023-09-11 14:42:48 -04:00
Jack Short
86e4dd5153
chore: updating formatCurrencyAmount to handle multiple langs and cur… ()
* chore: updating formatCurrencyAmount to handle multiple langs and currencies

* missed advanced swap details

* missed confirmed swap modal

* removing updating visual effects

* removing it from parseLocale

* chore: displaying local currency and language formatting

* making effects visible

* moving to hook

* useFormatCurrencyAmount

* moving it to useformatter hook

* exporting formatting locales

* missed one parsed remote

* moving hook to bottom of file

* moving to bottom
2023-09-11 13:51:11 -04:00
cartcrom
e4d103b015
refactor: price chart timestamps ()
* refactor: price chart timestamps

* fix: remove unnused file

* refactor: util file name

* fix: use correct var for axis

* refactor: use backup var instead of throwing error for timeMinute interval
2023-09-11 13:16:30 -04:00
eddie
f71c781530
fix: add tax amounts to swap quote received () 2023-09-11 10:15:02 -07:00
Jack Short
736e395cd7
chore: displaying local currency and language formatting ()
* chore: displaying local currency and language formatting

* moving to hook

* moving it to useformatter hook

* moving hook to bottom of file
2023-09-11 13:13:49 -04:00
Jack Short
8021135879
chore: remove extra blocknumber from dep array () 2023-09-11 12:29:39 -04:00
Tina
acdeb402ec
feat: Log auto slippage setting for gas estimate failed event ()
* log auto slippage setting for gas estimate failed

* add to dep array
2023-09-11 08:41:31 -07:00
eddie
84f5d8f94d
fix: only log swap success for swap txs () 2023-09-08 12:03:59 -07:00
cartcrom
bb28235bee
refactor: consolidate price delta components ()
* refactor: move delta component out of price chart file

* refactor: remove PortfolioArrow

* refactor: search row arrow cell

* fix: svg prop + snapshots failing unit tests

* refactor: css nit
2023-09-08 14:58:39 -04:00
cartcrom
f69226c1d1
fix: use updated feature flag () 2023-09-08 14:42:03 -04:00
cartcrom
35ca1b974e
fix: remove sentry warnings for failed tax fetches () 2023-09-08 14:41:47 -04:00
Nate Wienert
fdb9d8a8c9
feat: redirect /address links to the wallet download landing page or app store () 2023-09-08 08:33:21 -10:00
Zach Pomerantz
16cefb9cdb
test: fix cloud tests () 2023-09-08 10:53:22 -07:00
Zach Pomerantz
63bf1c0ac8
feat: path-based routing () 2023-09-08 10:43:59 -07:00
Nate Wienert
44e3b87ae1
fix: CODEOWNERS to use GitHub syntax currently uses google ()
* improve cypress test

* fix: CODEOWNERS uses Github syntax
2023-09-08 07:22:52 -10:00
eddie
a3fbab9163
feat: uniswapx start time buffer ()
* wip

* feat: use startTimeBufferSecs from API response
2023-09-08 09:30:57 -07:00
cartcrom
1ac1002c37
fix: fot ui bug in swap details dropdown ()
* init

* fix: completely hide tax rows while syncing

* test: use syncing = false for fot tests
2023-09-08 10:27:18 -04:00
eddie
08e0fb7f4b
fix: replace base rpc urls with public rpcs ()
* fix: replace base rpc urls with public rpcs

* fix: designation of safe vs fallback

* fix: remove duplicate
2023-09-07 13:56:30 -07:00
eddie
24d62d9594
feat: uniswapX default feature flag ()
* feat: uniswapX default feature flag

* fix: bad merge

* fix: toggle behavior and style issues

* fix: toggle behavior
2023-09-07 12:52:24 -07:00
cartcrom
28ea390863
fix: gate FOT UI ()
* fix: gate FOT UI in swap component

* feat: help article
2023-09-07 14:18:04 -04:00
eddie
4ba0d8ffc0
test: unit tests for swapFlowLoggers () 2023-09-07 10:07:56 -07:00
Jordan Frankfurt
7306423192
fix: eliminate flake from account connect race condition ()
* fix: reduce flake from race condition

* use interface instead of type for props

* closeMenu instead of closeModal with type

* simplify useCloseModal

* fix bug

* pr feedback

* remove unnecessary code

* fix button wrapper style

* remove ApplicationModal.WALLET

* update color for spore
2023-09-06 17:52:47 -05:00
Zach Pomerantz
147a9bcbb2
fix: move off deprecated gql fields () 2023-09-06 13:48:02 -07:00
Charles Bachmeier
652a8305c8
feat: add BNB support to Token Search and Explore () 2023-09-05 10:57:21 -07:00
Jack Short
80a77dd567
fix: typecheck failing on main () 2023-09-05 13:54:19 -04:00
Jack Short
5951d0c40c
feat: adding currency conversion hook to useUSDPrice ()
* feat: adding currency conversion hook

* 5m cache policy

* conversion rate hook

* usd check in outer hook

* converting usd price to localcurrencyprice

* checking usd manually
2023-09-05 13:38:33 -04:00
Jack Short
60acc689ee
chore: updating numberFormat for other currencies and languages ()
* adding currency settings option

* moving menu item to shared component

* adding supported currencies

* currency menu items

* currency url params

* currency selector e2e tests

* fixing tests

* currency icons

* removing eslint

* removing another eslint disable

* renaming to local currency

* more name changes

* design updates

* adding currencies

* changing formatter rules to formatter options

* renaming file

* fixing lint

* custom currency symbol in number formatting

* refactoring input to number formatter

* locale selection

* updating all format numbers with currency and locale

* updating portfolio

* better formatting symbols

* removing adding locale and currency

* upgrading constants to be translatable

* refactoring

* adding tests

* updating comment

* removing 0 from hardcoded input and comment
2023-09-05 12:59:43 -04:00
Charles Bachmeier
cd520a9e2c
refactor: add source to subgraph queries ()
* feat: add source to subgraph queries

* update test
2023-09-05 08:44:57 -07:00
cartcrom
b6e388c68c
feat: dynamic FOT tax fetching ()
* feat: disable exact_output FOT swaps

* fix: pr comments

* test: update snapshots

* feat: dynamically fetch tax rates

* remove tax constants file

* test: update useRoutingAPITrade expected args

* fix: useSwapTaxes nits

* lint: useSwapTaxes dependency array
2023-09-01 14:56:26 -04:00
cartcrom
45a138dec1
fix: detect refunds on exact_out native swaps ()
* fix: detect refunds on exact_out native swaps

* fix: correct comment
2023-09-01 14:55:42 -04:00
cartcrom
26d2ab53e8
feat: disable exact_output FOT swaps ()
* feat: disable exact_output FOT swaps

* fix: pr comments

* test: update snapshots
2023-09-01 14:23:21 -04:00
gnewfield
e50ecc8309
fix: un-nest translation on AddLiquidityV2 page ()
fix nested translation components causing translation error
2023-09-01 13:05:49 -04:00
eddie
406d7fe964
feat: remove dead statsig flag ()
* feat: remove dead statsig flag

* fix: e2e test
2023-08-30 13:08:18 -07:00
eddie
366f4d98ef
fix: remove unused feature flags ()
* fix: remove unused feature flags

* fix: remove flag from tests

* fix: tests

* fix: remove useUniswapX param
2023-08-30 12:37:50 -07:00
Jack Short
4eda18a4d5
feat: currency selector ()
* adding currency settings option

* moving menu item to shared component

* adding supported currencies

* currency menu items

* currency url params

* currency selector e2e tests

* fixing tests

* currency icons

* removing eslint

* removing another eslint disable

* renaming to local currency

* more name changes

* design updates

* renaming file

* fixing lint

* Update src/components/AccountDrawer/SettingsMenu.tsx

Co-authored-by: Charles Bachmeier <charles@bachmeier.io>

* alphabetical ordering currencies

* column padding

* padding only for mobile

* memoizing into switch

---------

Co-authored-by: Charles Bachmeier <charles@bachmeier.io>
2023-08-30 14:48:53 -04:00
eddie
ea66b8b959
fix: null check in lists code () 2023-08-30 11:41:40 -07:00
Charles Bachmeier
1eab4291f6
refactor: log NO_ROUTE found instead of paging ()
* refactor: log no route found instead of paging

* typo

* log router preference

---------

Co-authored-by: Charles Bachmeier <charlie@genie.xyz>
2023-08-29 13:59:51 -07:00
eddie
bed144b994
fix: remove base goerli from chain selector ()
* fix: remove base goerli from chain selector

* fix: remove all base goerli references

* fix: typecheck
2023-08-29 13:25:10 -07:00
Charles Bachmeier
dac334f975
feat: add info site migration feature flags ()
* feat: add info site migration feature flags

* feat: feature flag modal improvements

* scrollbar padding

* button padding
2023-08-28 13:13:31 -07:00
Charles Bachmeier
5478cb0c7b
feat: useUSDPrice for nft eth usd price ()
* feat: useStableCointValue for nft eth usd price

* add new file

* useUSDPrice

* rename
2023-08-28 09:09:30 -07:00
Nate Wienert
b47cebd40b
fix: Spore color fixes ()
* fix: bridge chain popup line break

* fix: background initial gradient to match the default ETH bg for light and dark mode
2023-08-25 11:43:26 -10:00
Tina
bb51be545b
feat: Support UniswapX exact out trades ()
* add feature flag for uniswapx exact out trades

* dont route PRICE lookups through uniswapx

* add e2e test
2023-08-25 15:00:22 -04:00
Charles Bachmeier
ce8105bf08
feat: track connected wallet provider calls to be replaced with BE/infura ()
* feat: track connected wallet provider calls to be replaced with be/infura

* update analytics events

* wrap analytics events in useEffects

* Update src/hooks/useContract.ts

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

---------

Co-authored-by: Jordan Frankfurt <jordanwfrankfurt@gmail.com>
2023-08-25 09:51:56 -07:00
Nate Wienert
59db4c5b02
feat: Spore colors refresh ()
* Add colors and type and fix everywhere

* fix text.tsx

* Color and text adjustments

* Further tweaks

* Removed promotional gradient

Changed to pink

* Add new icons and tweak broken colors

* Kill shadows

Removes shadows from NFT cards, Pools and Tokens tables

* Update icons

Update filled and info icons to filled versions

* Update bag icon

Changed to fill style

* Change share icon

Changed to the new filled arrow

* Fix merge errors

* update tests

* Complete find and replace old colors

* Fix colors on pool pages

* Update index.test.tsx.snap

* fix header hover states

* update test

* Update connect button hover state

* Update styles design bash

* Update tests

* Update fonts

* fix buy button font weight

* update tests

* fix jumping input boxes

* lint

* lints

* update tests

* redo auth header

* fix issues

* fix snapshots

* use individual weights instead of variable for nicer $ signn

* update tests

* make dark mode glow distinct

* remove commented out code

* icons in react

* update textSecondary

* fix feedback

* port over token test fix

* lint

* fix: make popups appear above drawer and near top conditionally only when drawer is open

* Revert "fix: make popups appear above drawer and near top conditionally only when drawer is open"

This reverts commit 994697144374ae3fc0cdf9275bce538fda5fc8de.

---------

Co-authored-by: Callil Capuozzo <callil.capuozzo@gmail.com>
Co-authored-by: pp-hh-ii-ll <111304124+pp-hh-ii-ll@users.noreply.github.com>
Co-authored-by: Callil Capuozzo <callil@uniswap.org>
2023-08-24 17:14:24 -10:00
Jack Short
1f6f1f1dbd
fix: smart contract wallets unsupported ()
* fix: smart contract wallets unsupported

* logging to amp

* response was deleted
2023-08-24 16:56:07 -04:00
cartcrom
1c142bb71f
feat: workaround for FOT swaps ()
* fix: workaround to include tax in slippage tolerance

* refactor: simplify tax line item components

* docs: update FOT comments

* fix: simplify tax calculation logic

* docs: update comment and add TODO

* fix: use postTaxAmountOut for price change calculation

* fix: use preTaxStablecoinPriceImpact for swab button warnings

* test: unit test for swap details/modal

* feat: add feature flag to gate FOT changes

* docs: add comment to postTaxOutputAmount getter

* fix: reword fee tooltip

* refactor: warning theme color variable usage

* fix: update snapshots

* fix: use preTaxStablecoinPriceImpact for price impact prompt logic

* lint: dependency array
2023-08-24 10:56:48 -04:00
eddie
3e67982bb4
fix: reset USDT logic () 2023-08-23 16:09:07 -07:00
Charles Bachmeier
bb79c73416
feat: add sentry monitoring for NO_ROUTE error ()
feat: add sentry monitoring for no_route error
2023-08-23 14:59:26 -07:00
Jordan Frankfurt
b1da7d87f9
fix: proposal creation layout on safari ()
* fix: proposal creation layout on safari

* add important to header margin
2023-08-23 16:06:24 -05:00
Jordan Frankfurt
a79324f23c
fix: repair broken link () 2023-08-23 16:06:09 -05:00
Jordan Frankfurt
9008db5166
fix: add v2 to lp importer link () 2023-08-23 15:58:09 -05:00
eddie
bcc57e4612
fix: null check on elapsedTime for some browsers () 2023-08-23 10:53:51 -07:00
eddie
8c7315760a
fix: make WrongChainError more specific ()
* fix: setTraceError in swap callback

* fix: move error class to util file
2023-08-23 09:30:51 -07:00
Jordan Frankfurt
290083b414
fix: isolate assertions into single tests () 2023-08-22 16:34:29 -05:00
cartcrom
4bc778ff3e
feat: add client and router blocknumbers to analytics () 2023-08-22 16:03:23 -04:00
Jordan Frankfurt
49ae6ef6bd
fix: ensure lists are present before running update ()
* fix: ensure lists are present before running update

* pr input from zzmp
2023-08-22 14:52:59 -05:00
Nate Wienert
3dab1da5ea
fix: cypress flaky token details test ()
improve cypress test
2023-08-21 08:46:08 -10:00
837 changed files with 48088 additions and 18012 deletions
.env.env.production.eslintrc.js
.github
.snykREADME.mdcodecov.ymlcodegen.ymlcraco.config.cjscypress.config.ts
cypress
functions
hardhat.config.jslingui.config.tspackage.json
patches
public

13
.env

@ -1,17 +1,16 @@
# These API keys are intentionally public. Please do not report them - thank you for your concern.
ESLINT_NO_DEV_ERRORS=true
REACT_APP_AMPLITUDE_PROXY_URL="https://api.uniswap.org/v1/amplitude-proxy"
REACT_APP_AMPLITUDE_PROXY_URL="https://null.null"
REACT_APP_AWS_API_REGION="us-east-2"
REACT_APP_AWS_API_ENDPOINT="https://beta.api.uniswap.org/v1/graphql"
REACT_APP_AWS_API_ENDPOINT="https://null.null"
REACT_APP_BNB_RPC_URL="https://rough-sleek-hill.bsc.quiknode.pro/413cc98cbc776cda8fdf1d0f47003583ff73d9bf"
REACT_APP_BASE_GOERLI_RPC_URL="https://wiser-compatible-mansion.base-goerli.quiknode.pro/5874f36248e17020a1006149e7f68c63967e1f45/"
REACT_APP_BASE_MAINNET_RPC_URL="https://cool-white-diagram.base-mainnet.quiknode.pro/d8f036f35dfab2c68f32dfa822cd971e7a25a117/"
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
REACT_APP_QUICKNODE_MAINNET_RPC_URL="https://magical-alien-tab.quiknode.pro/669e87e569a8277d3fbd9e202f9df93189f19f4c"
REACT_APP_MOONPAY_API="https://api.moonpay.com"
REACT_APP_MOONPAY_LINK="https://us-central1-uniswap-mobile.cloudfunctions.net/signMoonpayLinkV2?platform=web&env=staging"
REACT_APP_MOONPAY_PUBLISHABLE_KEY="pk_test_DycfESRid31UaSxhI5yWKe1r5E5kKSz"
REACT_APP_SENTRY_DSN="https://a3c62e400b8748b5a8d007150e2f38b7@o1037921.ingest.sentry.io/4504255148851200"
REACT_APP_STATSIG_PROXY_URL="https://api.uniswap.org/v1/statsig-proxy"
REACT_APP_TEMP_API_URL="https://temp.api.uniswap.org/v1"
REACT_APP_UNISWAP_API_URL="https://api.uniswap.org/v2"
REACT_APP_STATSIG_PROXY_URL="https://null.null"
REACT_APP_TEMP_API_URL="https://null.null"
REACT_APP_UNISWAP_API_URL="https://null.null"
REACT_APP_WALLET_CONNECT_PROJECT_ID="c6c9bacd35afa3eb9e6cccf6d8464395"

@ -1,15 +1,16 @@
# These API keys are intentionally public. Please do not report them - thank you for your concern.
REACT_APP_AMPLITUDE_PROXY_URL="https://api.uniswap.org/v1/amplitude-proxy"
REACT_APP_AWS_API_ENDPOINT="https://api.uniswap.org/v1/graphql"
REACT_APP_AMPLITUDE_PROXY_URL="https://null.null"
REACT_APP_AWS_API_ENDPOINT="https://null.null"
REACT_APP_BNB_RPC_URL="https://old-wispy-arrow.bsc.quiknode.pro/f5c060177236065c1058531a0615ab4f7a34a2fd"
REACT_APP_FIREBASE_KEY="AIzaSyBcZWwTcTJHj_R6ipZcrJkXdq05PuX0Rs0"
REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
REACT_APP_GOOGLE_ANALYTICS_ID="G-KDP9B6W4H8"
REACT_APP_INFURA_KEY="099fc58e0de9451d80b18d7c74caa7c1"
REACT_APP_MOONPAY_API="https://api.moonpay.com"
REACT_APP_MOONPAY_LINK="https://us-central1-uniswap-mobile.cloudfunctions.net/signMoonpayLinkV2?platform=web&env=production"
REACT_APP_MOONPAY_LINK="https://null.null"
REACT_APP_MOONPAY_PUBLISHABLE_KEY="pk_live_uQG4BJC4w3cxnqpcSqAfohdBFDTsY6E"
REACT_APP_SENTRY_ENABLED=true
REACT_APP_SENTRY_TRACES_SAMPLE_RATE=0.00003
REACT_APP_STATSIG_PROXY_URL="https://api.uniswap.org/v1/statsig-proxy"
THE_GRAPH_SCHEMA_ENDPOINT="https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3"
REACT_APP_STATSIG_PROXY_URL="https://null.null"
REACT_APP_QUICKNODE_MAINNET_RPC_URL="https://ultra-blue-flower.quiknode.pro/770b22d5f362c537bc8fe19b034c45b22958f880"
THE_GRAPH_SCHEMA_ENDPOINT="https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3?source=uniswap"

@ -28,7 +28,20 @@ module.exports = {
{
files: ['**/*.ts', '**/*.tsx'],
rules: {
'@typescript-eslint/no-restricted-imports': ['error', restrictedImports],
'@typescript-eslint/no-restricted-imports': [
'error',
{
...restrictedImports,
paths: [
...restrictedImports.paths,
{
name: '@uniswap/smart-order-router',
message: 'Only import types, unless you are in the client-side SOR, to preserve lazy-loading.',
allowTypeImports: true,
},
],
},
],
'import/no-restricted-paths': [
'error',
{
@ -57,6 +70,13 @@ module.exports = {
],
},
],
'no-restricted-syntax': [
'error',
{
selector: ':matches(ExportAllDeclaration)',
message: 'Barrel exports bloat the bundle size by preventing tree-shaking.',
},
],
},
},
{

1
.github/CODEOWNERS vendored

@ -1 +0,0 @@
@uniswap/web-admins

@ -1,22 +0,0 @@
---
name: Bug Report
about: Describe an issue in the Uniswap Interface
title: ''
labels: bug
assignees: ''
---
**Bug Description**
A clear and concise description of the bug.
**Steps to Reproduce**
1. Go to ...
2. Click on ...
...
**Expected Behavior**
A clear and concise description of what you expected to happen.
**Additional Context**
Add any other context about the problem here (screenshots, whether the bug only occurs only in certain mobile/desktop/browser environments, etc.)

@ -1,8 +0,0 @@
blank_issues_enabled: true
contact_links:
- name: Support
url: https://discord.gg/FCfyBSbCU5
about: Please ask and answer questions here
- name: List a token
url: https://github.com/Uniswap/default-token-list#adding-a-token
about: Any requests to add a token to Uniswap should go here

@ -1,19 +0,0 @@
---
name: Feature Request
about: Suggest an idea for improving the UX of the Uniswap Interface
title: ''
labels: 'improvement'
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

@ -1,48 +0,0 @@
name: Report
description: Report test failures via Slack
inputs:
name:
description: The name of the failing test
required: true
SLACK_WEBHOOK_URL:
description: The webhook URL to send the report to
required: true
runs:
using: composite
steps:
- uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
with:
payload: |
{
"text": "${{ inputs.name }} failing on `${{ github.ref_name }}`",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*${{ inputs.name }} failing on `${{ github.ref_name }}`:* <https://github.com/${{ github.repository}}/actions/runs/${{ github.run_id }}|view failing action>"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "_This is blocking pull requests and branch promotions._\n_Please prioritize fixing the build._"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ inputs.SLACK_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
# The !oncall bot requires its own message:
- uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
with:
payload: |
{
"text": "!oncall web"
}
env:
SLACK_WEBHOOK_URL: ${{ inputs.SLACK_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK

@ -1,49 +0,0 @@
name: Setup
description: checkout repo, setup node, and install node_modules
runs:
using: composite
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
registry-url: https://registry.npmjs.org
# cache is intentionally omitted, as it is faster with yarn v1 to cache node_modules.
- uses: actions/cache@v3
id: install-cache
with:
# node_modules/.cache is intentionally omitted, as this is used for build tool caches.
path: |
node_modules
!node_modules/.cache
key: ${{ runner.os }}-install-${{ hashFiles('yarn.lock') }}
- if: steps.install-cache.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile --ignore-scripts
shell: bash
# Run patch-package to apply patches to dependencies.
- run: yarn patch-package
shell: bash
# Contracts are compiled from source. If source hasn't changed, the contracts do not need to be re-compiled.
- uses: actions/cache@v3
id: contracts-cache
with:
path: |
src/abis/types
src/types/v3
key: ${{ runner.os }}-contracts-${{ hashFiles('src/abis/**/*.json', 'node_modules/@uniswap/**/artifacts/contracts/**/*.json') }}
- if: steps.contracts-cache.outputs.cache-hit != 'true'
run: yarn contracts
shell: bash
# These operations cannot be cached, so they are run concurrently
# - ajv: Validators compile quickly, so caching can be omitted.
# - graphql: GraphQL is generated from schema and client-side graphql queries. The schema is always fetched and
# changes to client-side queries are hard to detect, so it is always re-generated.
# - i18n: Messages are extracted from source and compiled. No caching extractor is available (out-of-the-box).
- run: yarn concurrently --max-processes=100% npm:ajv npm:graphql npm:i18n
shell: bash

@ -1,12 +0,0 @@
version: 2
updates:
- package-ecosystem: npm
# Files stored in repository root
directory: '/'
schedule:
interval: 'daily'
allow:
- dependency-name: '@uniswap/default-token-list'
- dependency-name: '@uniswap/token-lists'
reviewers:
- 'Uniswap/dependabot-reviewers'

@ -1,52 +0,0 @@
<!-- Your PR title must follow conventional commits: https://github.com/Uniswap/interface#pr-title -->
## Description
<!-- Summary of change, including motivation and context. -->
<!-- Use verb-driven language: "Fixes XYZ" instead of "This change fixes XYZ" -->
<!-- Delete inapplicable lines: -->
_Linear ticket:_
_Slack thread:_
_Relevant docs:_
<!-- Delete this section if your change does not affect UI. -->
## Screen capture
### Before
| Mobile | Desktop |
| ------------ | ------------ |
| paste_before | paste_before |
### After
| Mobile | Desktop |
| ------------ | ----------- |
| paste_after | paste_after |
## Test plan
<!-- Delete this section if your change is not a bug fix. -->
### Reproducing the error
<!-- Include steps to reproduce the bug. -->
1.
### QA (ie manual testing)
<!-- Include steps to test the change, ensuring no regression. -->
- [ ] N/A
#### Devices
<!-- If applicable, include different devices and screen sizes that may be affected, and how you've tested them. -->
### Automated testing
<!-- If N/A, check and note so it is obvious to your reviewers and does not show up as an incomplete task. -->
<!-- eg - [x] Unit test N/A -->
- [ ] Unit test
- [ ] Integration/E2E test

@ -1,73 +0,0 @@
name: 1 | Push main -> staging
# This CI job is responsible for pushing the current contents of the `main` branch to the
# `releases/staging` branch, which will in turn kick off a deploy to the staging environment.
on:
workflow_dispatch:
# https://stackoverflow.com/questions/57921401/push-to-origin-from-github-action
jobs:
push-staging:
name: 'Push to staging branch'
runs-on: ubuntu-latest
environment:
name: push/staging
steps:
- name: Check test status
uses: actions/github-script@v6.4.1
with:
script: |
const statuses = await github.rest.repos.listCommitStatusesForRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.sha
})
const status = statuses.data.find(status => status.context === 'Test / promotion')?.state || 'missing'
core.info('Status: ' + status)
if (status !== 'success') {
core.setFailed('"Test / promotion" must be successful before pushing')
}
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
with:
token: ${{ secrets.RELEASE_SERVICE_ACCESS_TOKEN }}
ref: main
# The source file must exist for the corresponding translation messages to be downloaded.
- run: touch src/locales/en-US.po
- name: Download translations
uses: crowdin/github-action@3133cc916c35590475cf6705f482fb653d8e36e9
with:
upload_sources: false
download_translations: true
project_id: 458284
token: ${{ secrets.CROWDIN_PERSONAL_TOKEN_SECRET }}
source: 'src/locales/en-US.po'
translation: 'src/locales/%locale%.po'
localization_branch_name: main
create_pull_request: false
push_translations: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Git config
run: |
git config user.name 'UL Service Account'
git config user.email 'hello-happy-puppy@users.noreply.github.com'
- name: Add translations
run: |
rm src/locales/en-US.po
git add -f src/locales/*.po
git commit -m 'ci(t9n): download translations from crowdin'
- name: Add CODEOWNERS
run: |
echo '@uniswap/web-admins' > CODEOWNERS
git add CODEOWNERS
git commit -m 'ci: add global CODEOWNERS'
- name: Git push
run: |
git push origin main:releases/staging --force

@ -1,64 +0,0 @@
name: 2 | Deploy staging
on:
push:
branches:
- 'releases/staging'
jobs:
deploy-to-staging:
runs-on: ubuntu-latest
environment:
name: deploy/staging
steps:
- uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
continue-on-error: true
with:
payload: |
{
"text": "Deploy _started_ for ${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: yarn build
env:
REACT_APP_STAGING: 1
- name: Update Cloudflare Pages deployment
id: pages-deployment
uses: cloudflare/pages-action@364c7ca09a4b57837c5967871d64a2c31adb8c0d
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: interface-staging
directory: build
githubToken: ${{ secrets.GITHUB_TOKEN }}
# Cloudflare uses `main` as the default production branch, so we push using the `main` branch so that it can be aliased by a custom domain.
branch: main
- uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
continue-on-error: true
if: always()
with:
payload: |
{
"text": "Deploy *${{ steps.pages-deployment.outcome }}* for ${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
- name: Upload source maps to Sentry
uses: getsentry/action-release@bd5f874fcda966ba48139b0140fb3ec0cb3aabdd
continue-on-error: true
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
with:
environment: staging
sourcemaps: './build/static/js'
url_prefix: '~/static/js'

@ -1,42 +0,0 @@
name: 3 | Push staging -> prod
# This CI job is responsible for force pushing the content of releases/staging to releases/prod. It
# is restricted to web-reviewers through virtue of the GitHub environment protection rules for the
# prod environment.
on:
workflow_dispatch:
jobs:
push-prod:
name: 'Push to prod branch'
runs-on: ubuntu-latest
environment:
name: push/prod
steps:
- name: Check test status
uses: actions/github-script@v6.4.1
with:
script: |
const statuses = await github.rest.repos.listCommitStatusesForRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.sha
})
const status = statuses.data.find(status => status.context === 'Test / promotion')?.state || 'missing'
core.info('Status: ' + status)
if (status !== 'success') {
core.setFailed('"Test / promotion" must be successful before pushing')
}
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
with:
token: ${{ secrets.RELEASE_SERVICE_ACCESS_TOKEN }}
ref: releases/staging
- name: Git config
run: |
git config user.name "UL Service Account"
git config user.email "hello-happy-puppy@users.noreply.github.com"
- name: Git push
run: |
git push origin releases/staging:releases/prod --force

@ -1,111 +0,0 @@
name: 4 | Deploy prod
on:
push:
branches:
- 'releases/prod'
jobs:
deploy-to-prod:
runs-on: ubuntu-latest
environment:
name: deploy/prod
steps:
- uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
continue-on-error: true
with:
payload: |
{
"text": "Deploy _started_ for ${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: yarn build
- name: Bump and tag
id: github-tag-action
uses: mathieudutour/github-tag-action@d745f2e74aaf1ee82e747b181f7a0967978abee0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
release_branches: releases/prod
default_bump: patch
- name: Pin to IPFS
id: pinata
uses: anantaramdas/ipfs-pinata-deploy-action@39bbda1ce1fe24c69c6f57861b8038278d53688d
with:
pin-name: Uniswap ${{ steps.github-tag-action.outputs.new_tag }}
path: './build'
pinata-api-key: ${{ secrets.PINATA_API_KEY }}
pinata-secret-api-key: ${{ secrets.PINATA_API_SECRET_KEY }}
- name: Convert CIDv0 to CIDv1
id: convert-cidv0
uses: uniswap/convert-cidv0-cidv1@v1.0.0
with:
cidv0: ${{ steps.pinata.outputs.hash }}
- name: Publish release
uses: actions/create-release@v1.1.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.github-tag-action.outputs.new_tag }}
release_name: Release ${{ steps.github-tag-action.outputs.new_tag }}
body: |
IPFS hash of the deployment:
- CIDv0: `${{ steps.pinata.outputs.hash }}`
- CIDv1: `${{ steps.convert-cidv0.outputs.cidv1 }}`
The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org).
You can also access the Uniswap Interface from an IPFS gateway.
**BEWARE**: The Uniswap interface uses [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to remember your settings, such as which tokens you have imported.
**You should always use an IPFS gateway that enforces origin separation**, or our hosted deployment of the latest release at [app.uniswap.org](https://app.uniswap.org).
Your Uniswap settings are never remembered across different URLs.
IPFS gateways:
- https://${{ steps.convert-cidv0.outputs.cidv1 }}.ipfs.dweb.link/
- https://${{ steps.convert-cidv0.outputs.cidv1 }}.ipfs.cf-ipfs.com/
- [ipfs://${{ steps.pinata.outputs.hash }}/](ipfs://${{ steps.pinata.outputs.hash }}/)
${{ steps.github-tag-action.outputs.changelog }}
- name: Update Cloudflare Pages deployment
uses: cloudflare/pages-action@364c7ca09a4b57837c5967871d64a2c31adb8c0d
id: pages-deployment
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }}
directory: build
githubToken: ${{ secrets.GITHUB_TOKEN }}
# Cloudflare uses `main` as the default production branch, so we push using the `main` branch so that it can be aliased by a custom domain.
branch: main
- uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
continue-on-error: true
if: always()
with:
payload: |
{
"text": "Deploy *${{ steps.pages-deployment.outcome }}* for ${{ github.ref_name }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
- name: Upload source maps to Sentry
uses: getsentry/action-release@4744f6a65149f441c5f396d5b0877307c0db52c7
continue-on-error: true
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
with:
environment: production
sourcemaps: './build/static/js'
url_prefix: '~/static/js'

@ -1,17 +0,0 @@
name: Check PR Title
on:
pull_request_target:
types:
- opened
- edited
- synchronize
jobs:
# Ensures that the PR title adheres to [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/).
conventional-commit:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v3.4.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

@ -1,26 +0,0 @@
name: Crowdin Upload
on:
push:
branches:
- main
jobs:
upload-sources:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: yarn i18n:extract
- name: Upload Crowdin sources
uses: crowdin/github-action@3133cc916c35590475cf6705f482fb653d8e36e9
with:
upload_sources: true
download_translations: false
project_id: 458284
token: ${{ secrets.CROWDIN_PERSONAL_TOKEN_SECRET }}
source: 'src/locales/en-US.po'
translation: 'src/locales/%locale%.po'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

@ -1,91 +0,0 @@
name: Slack notification on pushes to releases/*
# This CI job will push notifications to Slack whenever code is merged into any releases/* branch
#
# The steps of the command line kung-fu shown below are as follows:
# First we take the JSON-formatted Github context
# echo $GITHUB_CONTEXT \
# Then we parse out the specific fields we want for our messages using jq and format it into tab-separated values
# | jq '.event.commits[] | [.url, .id[0:7], .author.username, .timestamp, .message] | @tsv' \
# We need to do some cleaning on this output - specifically removing quotes and replacing newlines with something easier to split
# | sed 's/"//g' | sed 's/\\t/;/g' | sed 's/\\n/;/g' | sed 's/\\//g' \
# We then use awk to format the TSV into a Slack message
# | awk -F';' '{print "• <"$1"|"$2"> (<https://github.com/"$3"|"$3">, "$4") - "$5}' \
# We need to deal with some escaping issues with newlines so that we don't break the Slack message format
# | sed 's/$/\\n/g' | tr -d '\n' \
# Finally we have to truncate the message to 3,000 characters max, otherwise Slack will reject it
# | awk '{print substr($0,0,3000);}' \
# Then shove the bytes into a file to store them in their exact format
# > /tmp/parsed_github_context
on:
push:
branches:
- 'releases/*'
jobs:
notify-slack:
runs-on: ubuntu-latest
environment:
name: notify/releases
steps:
- name: Parse event to slug
id: parse-slug
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
# Formats the contents of the GitHub event into slugs: one line per commit, formatted for Slack.
# Explanation for each line is in the comments above.
run: |
echo $GITHUB_CONTEXT \
| jq '.event.commits[] | [.url, .id[0:7], .author.username, .timestamp, .message] | @tsv' \
| sed 's/"//g' | sed 's/\\t/;/g' | sed 's/\\n/;/g' | sed 's/\\//g' \
| awk -F';' '{print "• <"$1"|"$2"> (<https://github.com/"$3"|"$3">, "$4") - "$5}' \
| sed 's/$/\\n/g' | tr -d '\n' \
| awk '{print substr($0,0,3000);}' \
> /tmp/parsed_github_context
echo "SLACK_COMMITS=$(cat /tmp/parsed_github_context)" >> "$GITHUB_OUTPUT"
- uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
with:
payload: |
{
"text": "GitHub Action build result: ${{ job.status }}\n${{ github.event.pull_request.html_url || github.event.head_commit.url }}",
"blocks": [
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Code merged to <https://github.com/Uniswap/interface/tree/${{ github.ref }}|${{ github.ref_name }}> branch:*\n"
}
},
{
"type": "divider"
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Actor*: <https://github.com/${{ github.triggering_actor }}/|${{ github.triggering_actor }}>\n*Force pushed*: ${{ github.event.forced || false }}\n"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "${{ steps.parse-slug.outputs.SLACK_COMMITS || 'New branch created' }}"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "<${{ github.event.compare}}|View Diff>"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK

@ -1,275 +0,0 @@
name: Test
# Many build steps have their own caches, so each job has its own cache to improve subsequent build times.
# Build tools are configured to cache to node_modules/.cache, so they are cached independently of node_modules.
# Caches are saved every run (by keying on github.run_id), and the most recent available cache is loaded.
# See https://jongleberry.medium.com/speed-up-your-ci-and-dx-with-node-modules-cache-ac8df82b7bb0.
on:
push:
branches:
- main
- releases/staging
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- uses: actions/cache@v3
with:
path: node_modules/.cache
key: ${{ runner.os }}-eslint-${{ github.run_id }}
restore-keys: ${{ runner.os }}-eslint-
- run: yarn lint
- if: failure() && github.ref_name == 'main'
uses: ./.github/actions/report
with:
name: Lint
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- uses: actions/cache@v3
with:
path: node_modules/.cache
key: ${{ runner.os }}-tsc-${{ github.run_id }}
restore-keys: ${{ runner.os }}-tsc-
- run: yarn typecheck
- if: failure() && github.ref_name == 'main'
uses: ./.github/actions/report
with:
name: Typecheck
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
deps-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: yarn yarn-deduplicate --strategy=highest --list --fail
- if: failure() && github.ref_name == 'main'
uses: ./.github/actions/report
with:
name: Dependency checks
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- uses: actions/cache@v3
with:
path: node_modules/.cache
key: ${{ runner.os }}-jest-${{ github.run_id }}
restore-keys: ${{ runner.os }}-jest-
- run: yarn test --coverage --maxWorkers=100%
- uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
flags: unit-tests
- if: failure() && github.ref_name == 'main'
uses: ./.github/actions/report
with:
name: Unit tests
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- uses: actions/cache@v3
with:
path: node_modules/.swc
key: ${{ runner.os }}-swc-${{ github.run_id }}
restore-keys: ${{ runner.os }}-swc-
- run: yarn build
- uses: actions/upload-artifact@v3
with:
name: build
path: build
if-no-files-found: error
cypress-typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- uses: actions/cache@v3
with:
path: node_modules/.cache
key: ${{ runner.os }}-cypress-tsc-${{ github.run_id }}
restore-keys: ${{ runner.os }}-cypress-tsc-
- run: yarn typecheck:cypress
- if: failure() && github.ref_name == 'main'
uses: ./.github/actions/report
with:
name: Cypress typecheck
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
# Allows for parallel re-runs of cypress tests without re-building.
cypress-rerun:
runs-on: ubuntu-latest
steps:
- run: exit 0
cypress-test-matrix:
needs: [build, cypress-rerun]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
containers: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- uses: actions/cache@v3
with:
path: /root/.cache/Cypress
key: ${{ runner.os }}-cypress-${{ hashFiles('**/node_modules/cypress/package.json') }}
- run: |
yarn cypress install
yarn cypress info
- uses: actions/download-artifact@v3
with:
name: build
path: build
- uses: actions/cache/restore@v3
with:
path: cache
key: ${{ runner.os }}-hardhat-${{ hashFiles('hardhat.config.js') }}-${{ github.run_id }}
restore-keys: ${{ runner.os }}-hardhat-${{ hashFiles('hardhat.config.js') }}-
- uses: cypress-io/github-action@v4
with:
install: false
record: true
parallel: true
start: yarn serve
wait-on: 'http://localhost:3000'
browser: electron
group: e2e
spec: ${{ github.ref_name == 'releases/staging' && 'cypress/{e2e,staging}/**/*.test.ts' || 'cypress/e2e/**/*.test.ts' }}
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COMMIT_INFO_BRANCH: ${{ github.event.pull_request.head.ref || github.ref_name }}
COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title || github.event.head_commit.message }}
COMMIT_INFO_AUTHOR: ${{ github.event.sender.login || github.event.head_commit.author.login }}
# Cypress requires an email for filtering by author, but GitHub does not expose one.
# GitHub's public profile email can be deterministically produced from user id/login.
COMMIT_INFO_EMAIL: ${{ github.event.sender.id || github.event.head_commit.author.id }}+${{ github.event.sender.login || github.event.head_commit.author.login }}@users.noreply.github.com
COMMIT_INFO_SHA: ${{ github.event.pull_request.head.sha || github.event.head_commit.sha }}
COMMIT_INFO_TIMESTAMP: ${{ github.event.pull_request.updated_at || github.event.head_commit.timestamp }}
CYPRESS_PULL_REQUEST_ID: ${{ github.event.pull_request.number }}
CYPRESS_PULL_REQUEST_URL: ${{ github.event.pull_request.html_url }}
- if: failure() && github.ref_name == 'main'
uses: ./.github/actions/report
with:
name: Cypress tests
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
- uses: actions/upload-artifact@v3
with:
name: hardhat-cache
path: cache
hardhat-cache:
needs: [cypress-test-matrix]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v3
with:
name: hardhat-cache
path: cache
- uses: actions/cache/save@v3
with:
path: cache
key: ${{ runner.os }}-hardhat-${{ hashFiles('hardhat.config.js') }}-${{ github.run_id }}
cloud-typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- uses: actions/cache@v3
with:
path: node_modules/.cache
key: ${{ runner.os }}-cloud-tsc-${{ github.run_id }}
restore-keys: ${{ runner.os }}-cloud-tsc-
- run: yarn typecheck:cloud
- if: failure() && github.ref_name == 'main'
uses: ./.github/actions/report
with:
name: Cloud typecheck
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
cloud-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- uses: actions/cache@v3
with:
path: node_modules/.cache
key: ${{ runner.os }}-cloud-jest-${{ github.run_id }}
restore-keys: ${{ runner.os }}-cloud-jest-
# Only use 1 worker, so the other can be used for the proxy server under test.
- run: yarn test:cloud --coverage --maxWorkers=1
- uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
flags: cloud-tests
pre:
if: ${{ github.ref_name == 'main' || github.ref_name == 'releases/staging' }}
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6.4.1
with:
script: |
github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.sha,
state: 'pending',
context: 'Test / promotion',
description: 'Running tests...',
target_url: 'https://github.com/Uniswap/interface/actions/runs/' + context.runId
})
post:
if: ${{ github.ref_name == 'main' || github.ref_name == 'releases/staging' }}
needs: [pre, lint, typecheck, deps-tests, unit-tests, cypress-test-matrix]
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6.4.1
with:
script: |
github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: context.sha,
state: ${{ env.STATUS }} ? 'success' : 'failure',
context: 'Test / promotion',
description: ${{ env.STATUS }} ? 'All tests passed' : 'One or more tests failed and are blocking promotion',
target_url: 'https://github.com/Uniswap/interface/actions/runs/' + context.runId
})
env:
STATUS: |
${{ needs.lint.result == 'success' }} &&
${{ needs.typecheck.result == 'success' }} &&
${{ needs.deps-tests.result == 'success' }} &&
${{ needs.unit-tests.result == 'success' }} &&
${{ needs.cypress-test-matrix.result == 'success' }}

25
.snyk

@ -1,25 +0,0 @@
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.25.0
# ignores vulnerabilities until expiry date; change duration by modifying expiry date
ignore:
SNYK-JS-OPENZEPPELINCONTRACTS-2964946:
- '*':
reason: None Given
expires: 2099-01-01T00:00:00.000Z
created: 2022-12-08T16:25:57.347Z
SNYK-JS-OPENZEPPELINCONTRACTS-2958047:
- '*':
reason: None Given
expires: 2099-01-01T00:00:00.000Z
created: 2022-12-08T16:26:09.720Z
SNYK-JS-OPENZEPPELINCONTRACTS-2958050:
- '*':
reason: None Given
expires: 2099-01-01T00:00:00.000Z
created: 2022-12-08T16:26:17.702Z
SNYK-JS-OPENZEPPELINCONTRACTS-2965580:
- '*':
reason: None Given
expires: 2099-01-01T00:00:00.000Z
created: 2022-12-08T16:26:34.283Z
patch: {}

@ -69,10 +69,10 @@ Other things to note:
The Uniswap Interface supports swapping, adding liquidity, removing liquidity and migrating liquidity for Uniswap protocol V2.
- Swap on Uniswap V2: <https://app.uniswap.org/#/swap?use=v2>
- View V2 liquidity: <https://app.uniswap.org/#/pools/v2>
- Add V2 liquidity: <https://app.uniswap.org/#/add/v2>
- Migrate V2 liquidity to V3: <https://app.uniswap.org/#/migrate/v2>
- Swap on Uniswap V2: <https://app.uniswap.org/swap?use=v2>
- View V2 liquidity: <https://app.uniswap.org/pools/v2>
- Add V2 liquidity: <https://app.uniswap.org/add/v2>
- Migrate V2 liquidity to V3: <https://app.uniswap.org/migrate/v2>
## Accessing Uniswap V1

@ -9,6 +9,7 @@ ignore:
- "**/styled.tsx"
- "**/constants/**/*"
- "constants/**/*"
- "src/dev/*"
coverage:
status:

@ -1,5 +1,5 @@
overrideExisting: true
schema: 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3'
schema: 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3?source=uniswap'
generates:
./src/graphql/thegraph/schema/schema.graphql:
plugins:

@ -5,7 +5,6 @@ const { execSync } = require('child_process')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const path = require('path')
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin')
const TerserPlugin = require('terser-webpack-plugin')
const { IgnorePlugin, ProvidePlugin } = require('webpack')
const { RetryChunkLoadPlugin } = require('webpack-retry-chunk-load-plugin')
@ -131,6 +130,12 @@ module.exports = {
},
})
// Retain source maps for node_modules packages:
webpackConfig.module.rules[0] = {
...webpackConfig.module.rules[0],
exclude: /node_modules/,
}
// Configure webpack transpilation (create-react-app specifies transpilation rules in a oneOf):
webpackConfig.module.rules[1].oneOf = webpackConfig.module.rules[1].oneOf.map((rule) => {
if (rule.loader && rule.loader.match(/babel-loader/)) {
@ -140,18 +145,20 @@ module.exports = {
return rule
})
// Run terser compression on node_modules before tree-shaking, so that tree-shaking is more effective.
// This works by eliminating dead code, so that webpack can identify unused imports and tree-shake them;
// it is only necessary for node_modules - it is done through linting for our own source code -
// see https://medium.com/engineering-housing/dead-code-elimination-and-tree-shaking-at-housing-part-1-307a94b30f23#7e03:
webpackConfig.module.rules.push({
enforce: 'post',
test: /node_modules.*\.(js)$/,
loader: path.join(__dirname, 'scripts/terser-loader.js'),
options: { compress: true, mangle: false },
})
// Configure webpack optimization:
webpackConfig.optimization = Object.assign(
webpackConfig.optimization,
{
minimize: isProduction,
minimizer: [
new TerserPlugin({
minify: TerserPlugin.swcMinify,
parallel: require('os').cpus().length,
}),
],
},
isProduction
? {
splitChunks: {
@ -170,13 +177,6 @@ module.exports = {
// Configure webpack resolution. webpackConfig.cache is unused with swc-loader, but the resolver can still cache:
webpackConfig.resolve = Object.assign(webpackConfig.resolve, { unsafeCache: true })
webpackConfig.ignoreWarnings = [
// Source mappings for a package will fail if the package does not provide them, but the build will still succeed,
// so it is unnecessary (and bothersome) to log it. This should be turned off when debugging missing sourcemaps.
// See https://webpack.js.org/loaders/source-map-loader#ignoring-warnings.
/Failed to parse source map/,
]
return webpackConfig
},
},

@ -6,7 +6,7 @@ export default defineConfig({
defaultCommandTimeout: 24000, // 2x average block time
chromeWebSecurity: false,
experimentalMemoryManagement: true, // better memory management, see https://github.com/cypress-io/cypress/pull/25462
retries: { runMode: 2 },
retries: { runMode: process.env.CYPRESS_RETRIES ? +process.env.CYPRESS_RETRIES : 1 },
video: false, // GH provides 2 CPUs, and cypress video eats one up, see https://github.com/cypress-io/cypress/issues/20468#issuecomment-1307608025
e2e: {
async setupNodeEvents(on, config) {

@ -4,7 +4,7 @@ import { aliasQuery, hasQuery } from '../utils/graphql-test-utils'
describe('Add Liquidity', () => {
beforeEach(() => {
cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3', (req) => {
cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3?source=uniswap', (req) => {
aliasQuery(req, 'feeTierDistribution')
})
})
@ -16,6 +16,16 @@ describe('Add Liquidity', () => {
cy.contains('0.05% fee tier')
})
it('clears the token selection when chain changes', () => {
cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/ETH/500')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'UNI')
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'ETH')
cy.get('[data-testid="chain-selector"]').last().click()
cy.contains('Polygon').click()
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'ETH')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('not.contain.text', 'UNI')
})
it('does not crash if token is duplicated', () => {
cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'UNI')
@ -29,26 +39,30 @@ describe('Add Liquidity', () => {
it('loads fee tier distribution', () => {
cy.fixture('feeTierDistribution.json').then((feeTierDistribution) => {
cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3', (req: CyHttpMessages.IncomingHttpRequest) => {
if (hasQuery(req, 'FeeTierDistribution')) {
req.alias = 'FeeTierDistribution'
cy.intercept(
'POST',
'/subgraphs/name/uniswap/uniswap-v3?source=uniswap',
(req: CyHttpMessages.IncomingHttpRequest) => {
if (hasQuery(req, 'FeeTierDistribution')) {
req.alias = 'FeeTierDistribution'
req.reply({
body: {
data: {
...feeTierDistribution,
req.reply({
body: {
data: {
...feeTierDistribution,
},
},
},
headers: {
'access-control-allow-origin': '*',
},
})
headers: {
'access-control-allow-origin': '*',
},
})
}
}
})
)
cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/ETH')
cy.wait('@FeeTierDistribution')
cy.get('#add-liquidity-selected-fee .selected-fee-label').should('contain.text', '0.3% fee tier')
cy.get('#add-liquidity-selected-fee .selected-fee-label').should('contain.text', '0.30% fee tier')
cy.get('#add-liquidity-selected-fee .selected-fee-percentage').should('contain.text', '40% select')
})
})

@ -1,9 +1,8 @@
import { FeatureFlag } from '../../src/featureFlags'
import { getTestSelector } from '../utils'
describe('Buy Crypto Modal', () => {
it('should open and close', () => {
cy.visit('/', { featureFlags: [FeatureFlag.fiatOnRampButtonOnSwap] })
cy.visit('/')
// Open the fiat onramp modal
cy.get(getTestSelector('buy-fiat-button')).click()
@ -16,7 +15,7 @@ describe('Buy Crypto Modal', () => {
it('should open and close, mobile viewport', () => {
cy.viewport('iphone-6')
cy.visit('/', { featureFlags: [FeatureFlag.fiatOnRampButtonOnSwap] })
cy.visit('/')
// Open the fiat onramp modal
cy.get(getTestSelector('buy-fiat-button')).click()

@ -39,4 +39,50 @@ describe('Landing Page', () => {
cy.get(getTestSelector('pool-nav-link')).last().click()
cy.url().should('include', '/pools')
})
it('does not render landing page when / path is blocked', () => {
cy.intercept('/', (req) => {
req.reply((res) => {
const parser = new DOMParser()
const doc = parser.parseFromString(res.body, 'text/html')
const meta = document.createElement('meta')
meta.setAttribute('property', 'x:blocked-paths')
meta.setAttribute('content', '/,/buy')
doc.head.appendChild(meta)
res.body = doc.documentElement.outerHTML
})
})
cy.visit('/', { userState: DISCONNECTED_WALLET_USER_STATE })
cy.get(getTestSelector('landing-page')).should('not.exist')
cy.get(getTestSelector('buy-fiat-button')).should('not.exist')
cy.url().should('include', '/swap')
})
it('does not render uk compliance banner in US', () => {
cy.visit('/swap')
cy.contains('UK disclaimer').should('not.exist')
})
it('renders uk compliance banner in uk', () => {
cy.intercept('https://api.uniswap.org/v1/amplitude-proxy', (req) => {
const requestBody = JSON.stringify(req.body)
const byteSize = new Blob([requestBody]).size
req.alias = 'amplitude'
req.reply(
JSON.stringify({
code: 200,
server_upload_time: Date.now(),
payload_size_bytes: byteSize,
events_ingested: req.body.events.length,
}),
{
'origin-country': 'GB',
}
)
})
cy.visit('/swap')
cy.contains('UK disclaimer')
})
})

@ -2,30 +2,48 @@ import { getTestSelector } from '../../utils'
describe('Mini Portfolio account drawer', () => {
beforeEach(() => {
cy.intercept(/api.uniswap.org\/v1\/graphql/, cy.spy().as('gqlSpy'))
const portfolioSpy = cy.spy().as('portfolioSpy')
cy.intercept(/api.uniswap.org\/v1\/graphql/, (req) => {
if (req.body.operationName === 'PortfolioBalances') {
portfolioSpy(req)
}
})
cy.visit('/swap')
})
it('fetches balances when account button is first hovered', () => {
// The balances should not be fetched before the account button is hovered
cy.get('@gqlSpy').should('not.have.been.called')
cy.get('@portfolioSpy').should('not.have.been.called')
// Balances should have been fetched once after hover
cy.get(getTestSelector('web3-status-connected')).trigger('mouseover')
cy.get('@gqlSpy').should('have.been.calledOnce')
cy.get('@portfolioSpy').should('have.been.calledOnce')
})
it('should not re-fetch balances on second hover', () => {
// The balances should not be fetched before the account button is hovered
cy.get('@portfolioSpy').should('not.have.been.called')
// Balances should have been fetched once after hover
cy.get(getTestSelector('web3-status-connected')).trigger('mouseover')
cy.get('@portfolioSpy').should('have.been.calledOnce')
// Balances should not be refetched upon second hover
cy.get(getTestSelector('web3-status-connected')).trigger('mouseover')
cy.get('@gqlSpy').should('have.been.calledOnce')
cy.get('@portfolioSpy').should('have.been.calledOnce')
})
it('should not re-fetch balances when the account drawer is opened', () => {
// The balances should not be fetched before the account button is hovered
cy.get('@portfolioSpy').should('not.have.been.called')
// Balances should have been fetched once after hover
cy.get(getTestSelector('web3-status-connected')).trigger('mouseover')
cy.get('@portfolioSpy').should('have.been.calledOnce')
// Balances should not be refetched upon opening drawer
cy.get(getTestSelector('web3-status-connected')).click()
cy.get('@gqlSpy').should('have.been.calledOnce')
// Balances should not be refetched upon closing & reopening drawer
cy.get(getTestSelector('close-account-drawer')).click()
cy.get(getTestSelector('web3-status-connected')).click()
cy.get('@gqlSpy').should('have.been.calledOnce')
cy.get('@portfolioSpy').should('have.been.calledOnce')
})
it('fetches account information', () => {
@ -35,15 +53,16 @@ describe('Mini Portfolio account drawer', () => {
// Verify that wallet state loads correctly
cy.get(getTestSelector('mini-portfolio-navbar')).contains('Tokens')
cy.get(getTestSelector('mini-portfolio-page')).contains('Hidden (201)')
cy.get(getTestSelector('mini-portfolio-page')).contains('Hidden (197)')
cy.intercept(/graphql/, { fixture: 'mini-portfolio/nfts.json' })
cy.get(getTestSelector('mini-portfolio-navbar')).contains('NFTs').click()
cy.get(getTestSelector('mini-portfolio-page')).contains('I Got Plenty')
cy.intercept(/graphql/, { fixture: 'mini-portfolio/pools.json' })
cy.get(getTestSelector('mini-portfolio-navbar')).contains('Pools').click()
cy.get(getTestSelector('mini-portfolio-page')).contains('No pools yet')
// Skip this for now, someone sent test account an NFT on block 17445713 that causes this test to fail
// cy.intercept(/graphql/, { fixture: 'mini-portfolio/pools.json' })
// cy.get(getTestSelector('mini-portfolio-navbar')).contains('Pools').click()
// cy.get(getTestSelector('mini-portfolio-page')).contains('No pools yet')
cy.intercept(/graphql/, { fixture: 'mini-portfolio/full_activity.json' })
cy.get(getTestSelector('mini-portfolio-navbar')).contains('Activity').click()

@ -12,7 +12,7 @@ describe('Testing nfts', () => {
})
it('should load pudgy penguin collection page', () => {
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
cy.visit(`/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
cy.get(getTestSelector('nft-collection-asset')).should('exist')
cy.get(getTestSelector('nft-collection-filter-buy-now')).should('not.exist')
cy.get(getTestSelector('nft-filter')).first().click()
@ -20,13 +20,13 @@ describe('Testing nfts', () => {
})
it('should be able to navigate to activity', () => {
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
cy.visit(`/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
cy.get(getTestSelector('nft-activity')).first().click()
cy.get(getTestSelector('nft-activity-row')).should('exist')
})
it('should go to the details page', () => {
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
cy.visit(`/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
cy.get(getTestSelector('nft-filter')).first().click()
cy.get(getTestSelector('nft-collection-filter-buy-now')).click()
cy.get(getTestSelector('nft-collection-asset')).first().click()
@ -37,7 +37,7 @@ describe('Testing nfts', () => {
})
it('should toggle buy now on details page', () => {
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
cy.visit(`/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
cy.get(getTestSelector('nft-filter')).first().click()
cy.get(getTestSelector('nft-collection-filter-buy-now')).click()
cy.get(getTestSelector('nft-collection-asset')).first().click()

@ -146,6 +146,26 @@ describe('Permit2', () => {
cy.contains('Swap success!')
cy.get(getTestSelector('popups')).contains('Swapped')
})
it('swaps USDT with existing permit, and existing and sufficient token approval', () => {
cy.hardhat().then(async (hardhat) => {
await hardhat.fund(hardhat.wallet, CurrencyAmount.fromRawAmount(USDT, 2e6))
await hardhat.mine()
await hardhat.approval.setTokenAllowanceForPermit2({ owner: hardhat.wallet, token: USDT }, 1e6)
await hardhat.mine()
await hardhat.approval.setPermit2Allowance({ owner: hardhat.wallet, token: USDT })
await hardhat.mine()
})
setupInputs(USDT, USDC_MAINNET)
cy.get('#swap-currency-input .token-amount-input').clear().type('1')
initiateSwap()
// Verify transaction
cy.wait('@eth_sendRawTransaction')
cy.hardhat().then((hardhat) => hardhat.mine())
cy.contains('Swap success!')
cy.get(getTestSelector('popups')).contains('Swapped')
})
})
it('swaps when user has already approved token and permit2', () => {

@ -1,7 +1,33 @@
import { ChainId, MaxUint256, UNI_ADDRESSES } from '@uniswap/sdk-core'
const UNI_MAINNET = UNI_ADDRESSES[ChainId.MAINNET]
describe('Remove Liquidity', () => {
it('loads the token pair', () => {
cy.visit('/remove/v2/ETH/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
it('loads the token pair in v2', () => {
cy.visit(`/remove/v2/ETH/${UNI_MAINNET}`)
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'ETH')
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'UNI')
})
it('loads the token pair in v3', () => {
cy.visit(`/remove/1`)
cy.get('#remove-liquidity-tokens').should('contain.text', 'UNI/ETH')
cy.get('#remove-pooled-tokena-symbol').should('contain.text', 'Pooled UNI')
cy.get('#remove-pooled-tokenb-symbol').should('contain.text', 'Pooled ETH')
})
it('should redirect to error pages if pool does not exist', () => {
// Duplicate-token v2 pools redirect to position unavailable
cy.visit(`/remove/v2/ETH/ETH`)
cy.contains('Position unavailable')
// Single-token pools don't exist
cy.visit('/remove/v2/ETH')
cy.url().should('match', /\/not-found/)
// Nonexistent v3 pool
cy.visit(`/remove/${MaxUint256}`)
cy.contains('Position unavailable')
})
})

@ -1,4 +1,5 @@
import { BigNumber } from '@ethersproject/bignumber'
import { InterfaceSectionName } from '@uniswap/analytics-events'
import { CurrencyAmount } from '@uniswap/sdk-core'
import { DEFAULT_DEADLINE_FROM_NOW } from '../../../src/constants/misc'
@ -86,6 +87,7 @@ describe('Swap errors', () => {
cy.get(getTestSelector('open-settings-dialog-button')).click()
cy.get(getTestSelector('max-slippage-settings')).click()
cy.get(getTestSelector('slippage-input')).clear().type('0.01')
cy.get(getTestSelector('toggle-uniswap-x-button')).click() // turn off uniswapx
cy.get('body').click('topRight') // close modal
cy.get(getTestSelector('slippage-input')).should('not.exist')
@ -116,4 +118,18 @@ describe('Swap errors', () => {
getBalance(DAI).should('be.closeTo', initialBalance + 200, 1)
})
})
it('insufficient liquidity', () => {
// The API response is too variable so stubbing a 404.
cy.intercept('POST', 'https://api.uniswap.org/v2/quote', {
statusCode: 404,
fixture: 'insufficientLiquidity.json',
})
cy.visit(`/swap?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`)
cy.get('#swap-currency-output .token-amount-input').type('100000000000000').should('have.value', '100000000000000') // 100 trillion
cy.contains('Insufficient liquidity for this trade.')
cy.get('#swap-button').should('not.exist')
cy.get(getTestSelector(`fiat-value-${InterfaceSectionName.CURRENCY_OUTPUT_PANEL}`)).contains('-')
})
})

@ -0,0 +1,146 @@
import { CurrencyAmount } from '@uniswap/sdk-core'
import { FeatureFlag } from 'featureFlags'
import { USDC_MAINNET } from '../../../src/constants/tokens'
import { getBalance, getTestSelector } from '../../utils'
describe.skip('Swap with fees', () => {
describe('Classic swaps', () => {
beforeEach(() => {
cy.visit('/swap', { featureFlags: [{ name: FeatureFlag.feesEnabled, value: true }] })
// Store trade quote into alias
cy.intercept({ url: 'https://api.uniswap.org/v2/quote' }, (req) => {
// Avoid tracking stablecoin pricing fetches
if (JSON.parse(req.body).intent !== 'pricing') req.alias = 'quoteFetch'
})
})
it('displays $0 fee on swaps without fees', () => {
// Set up a stablecoin <> stablecoin swap (no fees)
cy.get('#swap-currency-input .open-currency-select-button').click()
cy.contains('DAI').click()
cy.get('#swap-currency-output .open-currency-select-button').click()
cy.contains('USDC').click()
cy.get('#swap-currency-output .token-amount-input').type('1')
// Verify 0 fee UI is displayed
cy.get(getTestSelector('swap-details-header-row')).click()
cy.contains('Fee')
cy.contains('$0')
})
it('swaps ETH for USDC exact-out with swap fee', () => {
cy.hardhat().then((hardhat) => {
getBalance(USDC_MAINNET).then((initialBalance) => {
// Set up swap
cy.get('#swap-currency-output .open-currency-select-button').click()
cy.contains('USDC').click()
cy.get('#swap-currency-output .token-amount-input').type('1')
cy.wait('@quoteFetch')
.its('response.body')
.then(({ quote: { portionBips, portionRecipient, portionAmount } }) => {
// Fees are generally expected to always be enabled for ETH -> USDC swaps
// If the routing api does not include a fee, end the test early rather than manually update routes and hardcode fee vars
if (portionRecipient) return
cy.then(() => hardhat.getBalance(portionRecipient, USDC_MAINNET)).then((initialRecipientBalance) => {
const feeCurrencyAmount = CurrencyAmount.fromRawAmount(USDC_MAINNET, portionAmount)
// Initiate transaction
cy.get('#swap-button').click()
cy.contains('Review swap')
// Verify fee percentage and amount is displayed
cy.contains(`Fee (${portionBips / 100}%)`)
// Confirm transaction
cy.contains('Confirm swap').click()
// Verify transaction
cy.get(getTestSelector('web3-status-connected')).should('not.contain', 'Pending')
cy.get(getTestSelector('popups')).contains('Swapped')
// Verify the post-fee output is the expected exact-out amount
const finalBalance = initialBalance + 1
cy.get('#swap-currency-output').contains(`Balance: ${finalBalance}`)
getBalance(USDC_MAINNET).should('eq', finalBalance)
// Verify fee recipient received fee
cy.then(() => hardhat.getBalance(portionRecipient, USDC_MAINNET)).then((finalRecipientBalance) => {
const expectedFinalRecipientBalance = initialRecipientBalance.add(feeCurrencyAmount)
cy.then(() => finalRecipientBalance.equalTo(expectedFinalRecipientBalance)).should('be.true')
})
})
})
})
})
})
it('swaps ETH for USDC exact-in with swap fee', () => {
cy.hardhat().then((hardhat) => {
// Set up swap
cy.get('#swap-currency-output .open-currency-select-button').click()
cy.contains('USDC').click()
cy.get('#swap-currency-input .token-amount-input').type('.01')
cy.wait('@quoteFetch')
.its('response.body')
.then(({ quote: { portionBips, portionRecipient } }) => {
// Fees are generally expected to always be enabled for ETH -> USDC swaps
// If the routing api does not include a fee, end the test early rather than manually update routes and hardcode fee vars
if (portionRecipient) return
cy.then(() => hardhat.getBalance(portionRecipient, USDC_MAINNET)).then((initialRecipientBalance) => {
// Initiate transaction
cy.get('#swap-button').click()
cy.contains('Review swap')
// Verify fee percentage and amount is displayed
cy.contains(`Fee (${portionBips / 100}%)`)
// Confirm transaction
cy.contains('Confirm swap').click()
// Verify transaction
cy.get(getTestSelector('web3-status-connected')).should('not.contain', 'Pending')
cy.get(getTestSelector('popups')).contains('Swapped')
// Verify fee recipient received fee
cy.then(() => hardhat.getBalance(portionRecipient, USDC_MAINNET)).then((finalRecipientBalance) => {
cy.then(() => finalRecipientBalance.greaterThan(initialRecipientBalance)).should('be.true')
})
})
})
})
})
})
describe('UniswapX swaps', () => {
it('displays UniswapX fee in UI', () => {
cy.visit('/swap', {
featureFlags: [{ name: FeatureFlag.feesEnabled, value: true }],
})
// Intercept the trade quote
cy.intercept({ url: 'https://api.uniswap.org/v2/quote' }, (req) => {
// Avoid intercepting stablecoin pricing fetches
if (JSON.parse(req.body).intent !== 'pricing') {
req.reply({ fixture: 'uniswapx/feeQuote.json' })
}
})
// Setup swap
cy.get('#swap-currency-input .open-currency-select-button').click()
cy.contains('USDC').click()
cy.get('#swap-currency-output .open-currency-select-button').click()
cy.contains('ETH').click()
cy.get('#swap-currency-input .token-amount-input').type('200')
// Verify fee UI is displayed
cy.get(getTestSelector('swap-details-header-row')).click()
cy.contains('Fee (0.15%)')
})
})
})

@ -1,29 +1,34 @@
import { FeatureFlag } from '../../../src/featureFlags'
import { getTestSelector } from '../../utils'
describe('Swap settings', () => {
it('Opens and closes the settings menu', () => {
cy.visit('/swap', { featureFlags: [FeatureFlag.uniswapXEnabled] })
cy.visit('/swap')
cy.contains('Settings').should('not.exist')
cy.get(getTestSelector('open-settings-dialog-button')).click()
cy.get(getTestSelector('mobile-settings-menu')).should('not.exist')
cy.contains('Max slippage').should('exist')
cy.contains('Max. slippage').should('exist')
cy.contains('Transaction deadline').should('exist')
cy.contains('UniswapX').should('exist')
cy.contains('Local routing').should('exist')
cy.get(getTestSelector('open-settings-dialog-button')).click()
cy.contains('Settings').should('not.exist')
})
it('should open the mobile settings menu', () => {
// Set viewport to iPhone 6
cy.viewport('iphone-6')
cy.visit('/swap', { featureFlags: [FeatureFlag.uniswapXEnabled] })
cy.get(getTestSelector('open-settings-dialog-button')).click()
cy.get(getTestSelector('mobile-settings-menu')).should('exist')
cy.contains('Max slippage').should('exist')
cy.contains('Transaction deadline').should('exist')
cy.contains('UniswapX').should('exist')
cy.contains('Local routing').should('exist')
cy.get(getTestSelector('mobile-settings-scrim')).click()
cy.visit('/swap')
// Click the button to open the settings dialog
cy.get(getTestSelector('open-settings-dialog-button')).click({ waitForAnimations: true })
// Verify the mobile settings menu and its contents
cy.get(getTestSelector('mobile-settings-menu'))
.should('exist')
.within(() => {
cy.contains('Max. slippage').should('exist')
cy.contains('UniswapX').should('exist')
cy.contains('Transaction deadline').should('exist')
cy.get(getTestSelector('mobile-settings-close')).click()
})
})
})

@ -1,27 +0,0 @@
import { SwapEventName } from '@uniswap/analytics-events'
import { USDC_MAINNET } from 'constants/tokens'
import { getTestSelector } from '../../utils'
describe('Swap inputs with no wallet connected', () => {
it('can input and load a quote with no wallet connected', () => {
cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${USDC_MAINNET.address}`)
cy.get(getTestSelector('web3-status-connected')).click()
// click twice, first time to show confirmation, second to confirm
cy.get(getTestSelector('wallet-disconnect')).click()
cy.get(getTestSelector('wallet-disconnect')).should('contain', 'Disconnect')
cy.get(getTestSelector('wallet-disconnect')).click()
cy.get(getTestSelector('close-account-drawer')).click()
// Enter amount to swap
cy.get('#swap-currency-output .token-amount-input').type('1').should('have.value', '1')
cy.get('#swap-currency-input .token-amount-input').should('not.have.value', '')
// Verify logging
cy.waitForAmplitudeEvent(SwapEventName.SWAP_QUOTE_RECEIVED).then((event: any) => {
cy.wrap(event.event_properties).should('have.property', 'quote_latency_milliseconds')
cy.wrap(event.event_properties.quote_latency_milliseconds).should('be.a', 'number')
cy.wrap(event.event_properties.quote_latency_milliseconds).should('be.gte', 0)
})
})
})

@ -1,17 +1,35 @@
import { ChainId, CurrencyAmount } from '@uniswap/sdk-core'
import { CyHttpMessages } from 'cypress/types/net-stubbing'
import { DAI, nativeOnChain, USDC_MAINNET } from '../../../src/constants/tokens'
import { getTestSelector } from '../../utils'
const QuoteEndpoint = 'https://api.uniswap.org/v2/quote'
const QuoteWhereUniswapXIsBetter = 'uniswapx/quote1.json'
const QuoteWithEthInput = 'uniswapx/quote2.json'
const QuoteEndpoint = 'https://api.uniswap.org/v2/quote'
const OrderSubmissionEndpoint = 'https://api.uniswap.org/v2/order'
const OrderStatusEndpoint =
'https://api.uniswap.org/v2/orders?swapper=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266&orderHashes=0xa9dd6f05ad6d6c79bee654c31ede4d0d2392862711be0f3bc4a9124af24a6a19'
/**
* Stubs quote to return a quote for non-price requests
* Price quotes are blocked with 409, as the backend would not accept them regardless
*/
function stubNonPriceQuoteWith(fixture: string) {
cy.intercept(QuoteEndpoint, (req: CyHttpMessages.IncomingHttpRequest) => {
let body = req.body
if (typeof body === 'string') {
body = JSON.parse(body)
}
if (body.intent === 'pricing') {
req.reply({ statusCode: 409 })
} else {
req.reply({ fixture })
}
}).as('quote')
}
/** Stubs the provider to return a tx receipt corresponding to the mock filled uniswapx order's txHash */
function stubSwapTxReceipt() {
cy.hardhat().then((hardhat) => {
@ -23,53 +41,26 @@ function stubSwapTxReceipt() {
})
}
describe('UniswapX Toggle', () => {
// TODO: FIX THESE TESTS where we should NOT stub for pricing requests
describe.skip('UniswapX Toggle', () => {
beforeEach(() => {
cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter })
stubNonPriceQuoteWith(QuoteWhereUniswapXIsBetter)
cy.visit(`/swap/?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`)
})
it('only displays uniswapx ui when setting is on', () => {
it('displays uniswapx ui when setting is on', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
// UniswapX UI should not be visible
cy.get(getTestSelector('gas-estimate-uniswapx-icon')).should('not.exist')
// Opt-in to UniswapX
cy.contains('Try it now').click()
cy.wait('@quote')
// UniswapX UI should be visible
cy.get(getTestSelector('gas-estimate-uniswapx-icon')).should('exist')
})
it('prompts opt-in if UniswapX is better', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
// UniswapX should not display in gas estimate row before opt-in
cy.get(getTestSelector('gas-estimate-uniswapx-icon')).should('not.exist')
// UniswapX mustache should be visible
cy.contains('Try it now').click()
// Opt-in dialog should now be hidden
cy.contains('Try it now').should('not.be.visible')
// UniswapX should display in gas estimate row
cy.get(getTestSelector('gas-estimate-uniswapx-icon')).should('exist')
// Opt-in dialog should not reappear if user manually toggles UniswapX off
cy.get(getTestSelector('open-settings-dialog-button')).click()
cy.get(getTestSelector('toggle-uniswap-x-button')).click()
cy.get(getTestSelector('open-settings-dialog-button')).click()
cy.contains('Try it now').should('not.be.visible')
})
})
describe('UniswapX Orders', () => {
describe.skip('UniswapX Orders', () => {
beforeEach(() => {
cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter })
stubNonPriceQuoteWith(QuoteWhereUniswapXIsBetter)
cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' })
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/openStatusResponse.json' })
@ -79,10 +70,29 @@ describe('UniswapX Orders', () => {
cy.visit(`/swap/?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`)
})
it('can swap using uniswapX', () => {
it('can swap exact-in trades using uniswapX', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
cy.contains('Try it now').click()
cy.wait('@quote')
// Submit uniswapx order signature
cy.get('#swap-button').click()
cy.contains('Confirm swap').click()
cy.wait('@eth_signTypedData_v4')
cy.contains('Swap submitted')
cy.contains('Learn more about swapping with UniswapX')
// Return filled order status from uniswapx api
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/filledStatusResponse.json' })
// Verify swap success
cy.contains('Swapped')
})
it('can swap exact-out trades using uniswapX', () => {
// Setup a swap
cy.get('#swap-currency-output .token-amount-input').type('300')
cy.wait('@quote')
// Submit uniswapx order signature
cy.get('#swap-button').click()
@ -101,7 +111,7 @@ describe('UniswapX Orders', () => {
it('renders proper view if uniswapx order expires', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
cy.contains('Try it now').click()
cy.wait('@quote')
// Submit uniswapx order signature
cy.get('#swap-button').click()
@ -117,7 +127,7 @@ describe('UniswapX Orders', () => {
it('renders proper view if uniswapx order has insufficient funds', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
cy.contains('Try it now').click()
cy.wait('@quote')
// Submit uniswapx order signature
cy.get('#swap-button').click()
@ -131,9 +141,9 @@ describe('UniswapX Orders', () => {
})
})
describe('UniswapX Eth Input', () => {
describe.skip('UniswapX Eth Input', () => {
beforeEach(() => {
cy.intercept(QuoteEndpoint, { fixture: QuoteWithEthInput })
stubNonPriceQuoteWith(QuoteWithEthInput)
cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' })
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/openStatusResponse.json' })
@ -151,7 +161,8 @@ describe('UniswapX Eth Input', () => {
it('can swap using uniswapX with ETH as input', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('1')
cy.contains('Try it now').click()
cy.wait('@quote')
// Prompt ETH wrap to use for order
cy.get('#swap-button').click()
@ -180,10 +191,10 @@ describe('UniswapX Eth Input', () => {
cy.contains('Swapped')
})
it('switches swap input to WETH after wrap', () => {
it('keeps ETH as the input currency before wrap completes', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('1')
cy.contains('Try it now').click()
cy.wait('@quote')
// Prompt ETH wrap and confirm
cy.get('#swap-button').click()
@ -192,16 +203,25 @@ describe('UniswapX Eth Input', () => {
// Close review modal before wrap is confirmed on chain
cy.get(getTestSelector('confirmation-close-icon')).click()
// Confirm ETH is still the input token before wrap succeeds
cy.contains('ETH')
})
it('switches swap input to WETH after wrap', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('1')
cy.wait('@quote')
// Prompt ETH wrap and confirm
cy.get('#swap-button').click()
cy.contains('Confirm swap').click()
cy.wait('@eth_sendRawTransaction')
cy.hardhat().then((hardhat) => hardhat.mine())
// Confirm wrap is successful and WETH is now input token
cy.contains('Wrapped')
cy.contains('WETH')
// Reopen review modal and continue swap
cy.get('#swap-button').click()
cy.contains('Confirm swap').click()
// Approve WETH spend
cy.wait('@eth_sendRawTransaction')
cy.hardhat().then((hardhat) => hardhat.mine())
@ -216,10 +236,15 @@ describe('UniswapX Eth Input', () => {
// Verify swap success
cy.contains('Swapped')
// Close modal
cy.get(getTestSelector('confirmation-close-icon')).click()
// The input currency should now be WETH
cy.contains('WETH')
})
})
describe('UniswapX activity history', () => {
describe.skip('UniswapX activity history', () => {
beforeEach(() => {
cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter })
cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' })
@ -236,7 +261,6 @@ describe('UniswapX activity history', () => {
it('can view UniswapX order status progress in activity', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
cy.contains('Try it now').click()
// Submit uniswapx order signature
cy.get('#swap-button').click()
@ -264,7 +288,6 @@ describe('UniswapX activity history', () => {
it('can view UniswapX order status progress in activity upon expiry', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
cy.contains('Try it now').click()
// Submit uniswapx order signature
cy.get('#swap-button').click()
@ -291,7 +314,6 @@ describe('UniswapX activity history', () => {
it('deduplicates remote vs local uniswapx orders', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
cy.contains('Try it now').click()
// Submit uniswapx order signature
cy.get('#swap-button').click()
@ -316,14 +338,13 @@ describe('UniswapX activity history', () => {
// Open activity history
cy.get(getTestSelector('mini-portfolio-navbar')).contains('Activity').click()
// Ensure gql and local order have been deduped, such that there is only one swap activity listed
// Ensure gql and local order have been deduped, such that there is one swap activity listed
cy.get(getTestSelector('activity-content')).contains('Swapped').should('have.length', 1)
})
it('balances should refetch after uniswapx swap', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
cy.contains('Try it now').click()
const gqlSpy = cy.spy().as('gqlSpy')
cy.intercept(/graphql/, (req) => {

@ -1,4 +1,5 @@
import { ChainId, WETH9 } from '@uniswap/sdk-core'
import { FeatureFlag } from 'featureFlags'
import { ARB, UNI } from '../../src/constants/tokens'
import { getTestSelector } from '../utils'
@ -14,8 +15,9 @@ describe('Token details', () => {
it('Uniswap token should have all information populated', () => {
// Uniswap token
cy.visit(`/tokens/ethereum/${UNI_ADDRESS}`)
cy.visit(`/tokens/ethereum/${UNI_ADDRESS}`, {
featureFlags: [{ name: FeatureFlag.infoTDP, value: false }],
})
// Price chart should be filled in
cy.get('[data-cy="chart-header"]').should('include.text', '$')
cy.get('[data-cy="price-chart"]').should('exist')
@ -47,41 +49,50 @@ describe('Token details', () => {
cy.contains(UNI_ADDRESS).should('exist')
})
it('token with warning and low trading volume should have all information populated', () => {
// Shiba predator token, low trading volume and also has warning modal
cy.visit('/tokens/ethereum/0xa71d0588EAf47f12B13cF8eC750430d21DF04974')
it('Uniswap token should have correct stats boxes if infoTDP flag on', () => {
// Uniswap token
cy.visit(`/tokens/ethereum/${UNI_ADDRESS}`, {
featureFlags: [{ name: FeatureFlag.infoTDP, value: true }],
})
// Should have missing price chart when price unavailable (expected for this token)
if (cy.get('[data-cy="chart-header"]').contains('Price Unavailable')) {
cy.get('[data-cy="missing-chart"]').should('exist')
}
// Stats should have: TVL, 24H Volume, 52W low, 52W high
// Stats should have: TVL, FDV, market cap, 24H volume
cy.get(getTestSelector('token-details-stats')).should('exist')
cy.get(getTestSelector('token-details-stats')).within(() => {
cy.get('[data-cy="tvl"]').should('exist')
cy.get('[data-cy="volume-24h"]').should('exist')
cy.get('[data-cy="52w-low"]').should('exist')
cy.get('[data-cy="52w-high"]').should('exist')
cy.get('[data-cy="tvl"]').should('include.text', '$')
cy.get('[data-cy="fdv"]').should('include.text', '$')
cy.get('[data-cy="market-cap"]').should('include.text', '$')
cy.get('[data-cy="volume-24h"]').should('include.text', '$')
})
})
it('token with warning and low trading volume should have all information populated', () => {
// Null token created for this test, 0 trading volume and has warning modal
cy.visit('/tokens/ethereum/0x1eFBB78C8b917f67986BcE54cE575069c0143681')
// Should have missing price chart when price unavailable (expected for this token)
if (cy.get('[data-cy="chart-header"]').contains('Price unavailable')) {
cy.get('[data-cy="missing-chart"]').should('exist')
}
// Stats should not exist
cy.get(getTestSelector('token-details-stats')).should('not.exist')
// About section should have description of token
cy.get(getTestSelector('token-details-about-section')).should('exist')
cy.contains('QOM is the Shiba Predator').should('exist')
cy.contains('No token information available').should('exist')
// Links section should link out to Etherscan, More analytics, Website, Twitter
// Links section should link out to Etherscan, More analytics
cy.get('[data-cy="resources-container"]').within(() => {
cy.contains('Etherscan')
.should('have.attr', 'href')
.and('include', 'etherscan.io/address/0xa71d0588EAf47f12B13cF8eC750430d21DF04974')
.and('include', 'etherscan.io/address/0x1eFBB78C8b917f67986BcE54cE575069c0143681')
cy.contains('More analytics')
.should('have.attr', 'href')
.and('include', 'info.uniswap.org/#/tokens/0xa71d0588EAf47f12B13cF8eC750430d21DF04974')
cy.contains('Website').should('have.attr', 'href').and('include', 'qom')
cy.contains('Twitter').should('have.attr', 'href').and('include', 'twitter.com/ShibaPredator1')
.and('include', 'info.uniswap.org/#/tokens/0x1eFBB78C8b917f67986BcE54cE575069c0143681')
})
// Contract address should be displayed
cy.contains('0xa71d0588EAf47f12B13cF8eC750430d21DF04974').should('exist')
cy.contains('0x1eFBB78C8b917f67986BcE54cE575069c0143681').should('exist')
// Warning label should show if relevant ([spec](https://www.notion.so/3f7fce6f93694be08a94a6984d50298e))
cy.get('[data-cy="token-safety-message"]')
@ -108,7 +119,7 @@ describe('Token details', () => {
it('should automatically navigate to the new TDP', () => {
cy.get(`#swap-currency-output .open-currency-select-button`).click()
cy.contains('WETH').click()
cy.get('[data-reach-dialog-content]').contains('WETH').click()
cy.url().should('include', `${WETH9[1].address}`)
cy.url().should('not.include', `${UNI_MAINNET.address}`)
})

@ -11,6 +11,10 @@ describe('Token explore filter', () => {
function searchFor(filter: string) {
cy.get('[data-cy="explore-tokens-search-input"]').clear().type(filter).type('{enter}')
// wait for it to finish the filtered render
cy.get('[data-cy="token-name"]').first().contains(filter, {
matchCase: false,
})
}
it('should filter correctly by dao search term', () => {

@ -59,9 +59,7 @@ describe('Token explore', () => {
// in metamask modal using plain cypress. this is a workaround.
cy.visit('/tokens/polygon')
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Polygon')
cy.get(getTestSelector('token-table-row-NATIVE'))
.find(getTestSelector('name-cell'))
.should('include.text', 'Polygon Matic')
cy.get(getTestSelector('token-table-row-NATIVE')).find(getTestSelector('name-cell')).should('include.text', 'Matic')
})
it('should update when token explore table network changed', () => {
@ -69,8 +67,6 @@ describe('Token explore', () => {
cy.get(getTestSelector('tokens-network-filter-selected')).click()
cy.get(getTestSelector('tokens-network-filter-option-optimism')).click()
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism')
cy.reload()
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism')
cy.get(getTestSelector('chain-selector-logo')).invoke('attr', 'alt').should('eq', 'Ethereum')
cy.get(getTestSelector('chain-selector-logo')).find('title').should('include.text', 'Ethereum logo')
})
})

@ -30,7 +30,7 @@ describe('Universal search bar', () => {
.and('contain.text', '$')
.and('contain.text', '%')
.click()
cy.location('hash').should('equal', '#/tokens/ethereum/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984')
cy.location('pathname').should('equal', '/tokens/ethereum/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984')
openSearch()
cy.get(getTestSelector('searchbar-dropdown'))
@ -65,7 +65,7 @@ describe('Universal search bar', () => {
cy.get(getTestSelector('searchbar-token-row-ETHEREUM-NATIVE'))
// Validate that we go to the searched/selected result.
getSearchBar().type('{enter}')
cy.get(getTestSelector('searchbar-token-row-ETHEREUM-NATIVE')).click()
cy.url().should('contain', 'tokens/ethereum/NATIVE')
}
)

@ -19,7 +19,7 @@ describe('disconnect wallet', () => {
// Verify wallet has disconnected
cy.contains('Connect a wallet').should('exist')
cy.get(getTestSelector('navbar-connect-wallet')).contains('Connect')
cy.contains('Connect Wallet')
cy.contains('Connect wallet')
// Verify swap input is cleared
cy.get('#swap-currency-input .token-amount-input').should('have.value', '1')

@ -2,7 +2,7 @@ import { createDeferredPromise } from '../../../src/test-utils/promise'
import { getTestSelector } from '../../utils'
function waitsForActiveChain(chain: string) {
cy.get(getTestSelector('chain-selector-logo')).invoke('attr', 'alt').should('eq', chain)
cy.get(getTestSelector('chain-selector-logo')).find('title').should('include.text', `${chain} logo`)
}
function switchChain(chain: string) {

@ -32,11 +32,11 @@ describe('Wallet Dropdown', () => {
}
cy.get(getTestSelector('wallet-language-item')).contains('Afrikaans').click({ force: true })
cy.location('hash').should('match', /\?lng=af-ZA$/)
cy.location('search').should('match', /\?lng=af-ZA$/)
cy.contains('Uniswap available in: English')
cy.get(getTestSelector('wallet-language-item')).contains('English').click({ force: true })
cy.location('hash').should('match', /\?lng=en-US$/)
cy.location('search').should('match', /\?lng=en-US$/)
cy.contains('Uniswap available in: English').should('not.exist')
})
}
@ -49,11 +49,39 @@ describe('Wallet Dropdown', () => {
})
itChangesTheme()
itChangesLocale()
it('should not show buy crypto button in uk', () => {
cy.document().then((doc) => {
const meta = document.createElement('meta')
meta.setAttribute('property', 'x:blocked-paths')
meta.setAttribute('content', '/,/nfts,/buy')
doc.head.appendChild(meta)
})
cy.get(getTestSelector('wallet-buy-crypto')).should('not.exist')
})
})
describe('do not render buy button when /buy is blocked', () => {
beforeEach(() => {
cy.document().then((doc) => {
const meta = document.createElement('meta')
meta.setAttribute('property', 'x:blocked-paths')
meta.setAttribute('content', '/buy')
doc.head.appendChild(meta)
})
cy.visit('/')
cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-settings')).click()
})
it('should not render buy button', () => {
cy.get(getTestSelector('wallet-buy-crypto')).should('not.exist')
})
})
describe('should change locale with feature flag', () => {
beforeEach(() => {
cy.visit('/', { featureFlags: [FeatureFlag.currencyConversion] })
cy.visit('/', { featureFlags: [{ name: FeatureFlag.currencyConversion, value: true }] })
cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-settings')).click()
})
@ -115,7 +143,7 @@ describe('Wallet Dropdown', () => {
cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-settings')).click()
cy.get(getTestSelector('theme-auto')).click()
cy.get(getTestSelector('wallet-header')).should('have.css', 'color', 'rgb(152, 161, 192)')
cy.get(getTestSelector('wallet-header')).should('have.css', 'color', 'rgb(155, 155, 155)')
})
it('should properly use light system theme when auto theme setting is selected', () => {
@ -123,7 +151,7 @@ describe('Wallet Dropdown', () => {
cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-settings')).click()
cy.get(getTestSelector('theme-auto')).click()
cy.get(getTestSelector('wallet-header')).should('have.css', 'color', 'rgb(119, 128, 160)')
cy.get(getTestSelector('wallet-header')).should('have.css', 'color', 'rgb(125, 125, 125)')
})
})
@ -144,4 +172,34 @@ describe('Wallet Dropdown', () => {
cy.get(getTestSelector('wallet-settings')).should('not.be.visible')
})
})
describe('local currency', () => {
it('loads local currency from the query param', () => {
cy.visit('/', { featureFlags: [{ name: FeatureFlag.currencyConversion, value: true }] })
cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-settings')).click()
cy.contains('USD')
cy.visit('/?cur=AUD', { featureFlags: [{ name: FeatureFlag.currencyConversion, value: true }] })
cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-settings')).click()
cy.contains('AUD')
})
it('loads local currency from menu', () => {
cy.visit('/', { featureFlags: [{ name: FeatureFlag.currencyConversion, value: true }] })
cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-settings')).click()
cy.contains('USD')
cy.get(getTestSelector('local-currency-settings-button')).click()
cy.get(getTestSelector('wallet-local-currency-item')).contains('AUD').click({ force: true })
cy.location('search').should('match', /\?cur=AUD$/)
cy.contains('AUD')
cy.get(getTestSelector('wallet-local-currency-item')).contains('USD').click({ force: true })
cy.location('search').should('match', /\?cur=USD$/)
cy.contains('USD')
})
})
})

@ -0,0 +1,5 @@
{
"errorCode": "QUOTE_ERROR",
"detail": "No quotes available",
"id": "63363cc1-d474-4584-b386-7c356814b79f"
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,562 @@
{
"routing": "DUTCH_LIMIT",
"quote": {
"orderInfo": {
"chainId": 1,
"permit2Address": "0x000000000022d473030f116ddee9f6b43ac78ba3",
"reactor": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
"swapper": "0x0938a82F93D5DAB110Dc6277FC236b5b082DC10F",
"nonce": "1993353164669688581970088190602701610528397285201889446578254799128576197633",
"deadline": 1697481666,
"additionalValidationContract": "0x0000000000000000000000000000000000000000",
"additionalValidationData": "0x",
"decayStartTime": 1697481594,
"decayEndTime": 1697481654,
"exclusiveFiller": "0xaAFb85ad4a412dd8adC49611496a7695A22f4aeb",
"exclusivityOverrideBps": "100",
"input": {
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"startAmount": "200000000",
"endAmount": "200000000"
},
"outputs": [
{
"token": "0x0000000000000000000000000000000000000000",
"startAmount": "123803169993201727",
"endAmount": "117908377342236273",
"recipient": "0x0938a82F93D5DAB110Dc6277FC236b5b082DC10F"
},
{
"token": "0x0000000000000000000000000000000000000000",
"startAmount": "185983730585681",
"endAmount": "177128258400955",
"recipient": "0x37a8f295612602f2774d331e562be9e61B83a327"
}
]
},
"encodedOrder": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000652d837a00000000000000000000000000000000000000000000000000000000652d83b6000000000000000000000000aafb85ad4a412dd8adc49611496a7695a22f4aeb0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000bebc200000000000000000000000000000000000000000000000000000000000bebc20000000000000000000000000000000000000000000000000000000000000002000000000000000000000000006000da47483062a0d734ba3dc7576ce6a0b645c40000000000000000000000000938a82f93d5dab110dc6277fc236b5b082dc10f046832aa305880d33daa871e5041a0cd4853599a9ead518917239e206765040100000000000000000000000000000000000000000000000000000000652d83c2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b7d653c183183f00000000000000000000000000000000000000000000000001a2e50b6386d6710000000000000000000000000938a82f93d5dab110dc6277fc236b5b082dc10f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a926b63210510000000000000000000000000000000000000000000000000000a118e2ebf2bb00000000000000000000000037a8f295612602f2774d331e562be9e61b83a327",
"quoteId": "7b924043-f2d8-4f2e-abaa-9f65fbe5f890",
"requestId": "a02ca0ca-7855-4dd0-9330-8b818aaeb59f",
"orderHash": "0xb5b4e3be188f6eb9dbe7e1489595829184a9ebfb5389185ed7ba7c03142278c9",
"startTimeBufferSecs": 45,
"auctionPeriodSecs": 60,
"deadlineBufferSecs": 12,
"slippageTolerance": "0.5",
"permitData": {
"domain": {
"name": "Permit2",
"chainId": 1,
"verifyingContract": "0x000000000022d473030f116ddee9f6b43ac78ba3"
},
"types": {
"PermitWitnessTransferFrom": [
{
"name": "permitted",
"type": "TokenPermissions"
},
{
"name": "spender",
"type": "address"
},
{
"name": "nonce",
"type": "uint256"
},
{
"name": "deadline",
"type": "uint256"
},
{
"name": "witness",
"type": "ExclusiveDutchOrder"
}
],
"TokenPermissions": [
{
"name": "token",
"type": "address"
},
{
"name": "amount",
"type": "uint256"
}
],
"ExclusiveDutchOrder": [
{
"name": "info",
"type": "OrderInfo"
},
{
"name": "decayStartTime",
"type": "uint256"
},
{
"name": "decayEndTime",
"type": "uint256"
},
{
"name": "exclusiveFiller",
"type": "address"
},
{
"name": "exclusivityOverrideBps",
"type": "uint256"
},
{
"name": "inputToken",
"type": "address"
},
{
"name": "inputStartAmount",
"type": "uint256"
},
{
"name": "inputEndAmount",
"type": "uint256"
},
{
"name": "outputs",
"type": "DutchOutput[]"
}
],
"OrderInfo": [
{
"name": "reactor",
"type": "address"
},
{
"name": "swapper",
"type": "address"
},
{
"name": "nonce",
"type": "uint256"
},
{
"name": "deadline",
"type": "uint256"
},
{
"name": "additionalValidationContract",
"type": "address"
},
{
"name": "additionalValidationData",
"type": "bytes"
}
],
"DutchOutput": [
{
"name": "token",
"type": "address"
},
{
"name": "startAmount",
"type": "uint256"
},
{
"name": "endAmount",
"type": "uint256"
},
{
"name": "recipient",
"type": "address"
}
]
},
"values": {
"permitted": {
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"amount": {
"type": "BigNumber",
"hex": "0x0bebc200"
}
},
"spender": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
"nonce": {
"type": "BigNumber",
"hex": "0x046832aa305880d33daa871e5041a0cd4853599a9ead518917239e2067650401"
},
"deadline": 1697481666,
"witness": {
"info": {
"reactor": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
"swapper": "0x0938a82F93D5DAB110Dc6277FC236b5b082DC10F",
"nonce": {
"type": "BigNumber",
"hex": "0x046832aa305880d33daa871e5041a0cd4853599a9ead518917239e2067650401"
},
"deadline": 1697481666,
"additionalValidationContract": "0x0000000000000000000000000000000000000000",
"additionalValidationData": "0x"
},
"decayStartTime": 1697481594,
"decayEndTime": 1697481654,
"exclusiveFiller": "0xaAFb85ad4a412dd8adC49611496a7695A22f4aeb",
"exclusivityOverrideBps": {
"type": "BigNumber",
"hex": "0x64"
},
"inputToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"inputStartAmount": {
"type": "BigNumber",
"hex": "0x0bebc200"
},
"inputEndAmount": {
"type": "BigNumber",
"hex": "0x0bebc200"
},
"outputs": [
{
"token": "0x0000000000000000000000000000000000000000",
"startAmount": {
"type": "BigNumber",
"hex": "0x01b7d653c183183f"
},
"endAmount": {
"type": "BigNumber",
"hex": "0x01a2e50b6386d671"
},
"recipient": "0x0938a82F93D5DAB110Dc6277FC236b5b082DC10F"
},
{
"token": "0x0000000000000000000000000000000000000000",
"startAmount": {
"type": "BigNumber",
"hex": "0xa926b6321051"
},
"endAmount": {
"type": "BigNumber",
"hex": "0xa118e2ebf2bb"
},
"recipient": "0x37a8f295612602f2774d331e562be9e61B83a327"
}
]
}
}
},
"portionBips": 15,
"portionAmount": "185983730585681",
"portionRecipient": "0x37a8f295612602f2774d331e562be9e61B83a327"
},
"requestId": "a02ca0ca-7855-4dd0-9330-8b818aaeb59f",
"allQuotes": [
{
"routing": "DUTCH_LIMIT",
"quote": {
"orderInfo": {
"chainId": 1,
"permit2Address": "0x000000000022d473030f116ddee9f6b43ac78ba3",
"reactor": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
"swapper": "0x0938a82F93D5DAB110Dc6277FC236b5b082DC10F",
"nonce": "1993353164669688581970088190602701610528397285201889446578254799128576197633",
"deadline": 1697481666,
"additionalValidationContract": "0x0000000000000000000000000000000000000000",
"additionalValidationData": "0x",
"decayStartTime": 1697481594,
"decayEndTime": 1697481654,
"exclusiveFiller": "0xaAFb85ad4a412dd8adC49611496a7695A22f4aeb",
"exclusivityOverrideBps": "100",
"input": {
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"startAmount": "200000000",
"endAmount": "200000000"
},
"outputs": [
{
"token": "0x0000000000000000000000000000000000000000",
"startAmount": "123803169993201727",
"endAmount": "117908377342236273",
"recipient": "0x0938a82F93D5DAB110Dc6277FC236b5b082DC10F"
},
{
"token": "0x0000000000000000000000000000000000000000",
"startAmount": "185983730585681",
"endAmount": "177128258400955",
"recipient": "0x37a8f295612602f2774d331e562be9e61B83a327"
}
]
},
"encodedOrder": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000652d837a00000000000000000000000000000000000000000000000000000000652d83b6000000000000000000000000aafb85ad4a412dd8adc49611496a7695a22f4aeb0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000bebc200000000000000000000000000000000000000000000000000000000000bebc20000000000000000000000000000000000000000000000000000000000000002000000000000000000000000006000da47483062a0d734ba3dc7576ce6a0b645c40000000000000000000000000938a82f93d5dab110dc6277fc236b5b082dc10f046832aa305880d33daa871e5041a0cd4853599a9ead518917239e206765040100000000000000000000000000000000000000000000000000000000652d83c2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b7d653c183183f00000000000000000000000000000000000000000000000001a2e50b6386d6710000000000000000000000000938a82f93d5dab110dc6277fc236b5b082dc10f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a926b63210510000000000000000000000000000000000000000000000000000a118e2ebf2bb00000000000000000000000037a8f295612602f2774d331e562be9e61b83a327",
"quoteId": "7b924043-f2d8-4f2e-abaa-9f65fbe5f890",
"requestId": "a02ca0ca-7855-4dd0-9330-8b818aaeb59f",
"orderHash": "0xb5b4e3be188f6eb9dbe7e1489595829184a9ebfb5389185ed7ba7c03142278c9",
"startTimeBufferSecs": 45,
"auctionPeriodSecs": 60,
"deadlineBufferSecs": 12,
"slippageTolerance": "0.5",
"permitData": {
"domain": {
"name": "Permit2",
"chainId": 1,
"verifyingContract": "0x000000000022d473030f116ddee9f6b43ac78ba3"
},
"types": {
"PermitWitnessTransferFrom": [
{
"name": "permitted",
"type": "TokenPermissions"
},
{
"name": "spender",
"type": "address"
},
{
"name": "nonce",
"type": "uint256"
},
{
"name": "deadline",
"type": "uint256"
},
{
"name": "witness",
"type": "ExclusiveDutchOrder"
}
],
"TokenPermissions": [
{
"name": "token",
"type": "address"
},
{
"name": "amount",
"type": "uint256"
}
],
"ExclusiveDutchOrder": [
{
"name": "info",
"type": "OrderInfo"
},
{
"name": "decayStartTime",
"type": "uint256"
},
{
"name": "decayEndTime",
"type": "uint256"
},
{
"name": "exclusiveFiller",
"type": "address"
},
{
"name": "exclusivityOverrideBps",
"type": "uint256"
},
{
"name": "inputToken",
"type": "address"
},
{
"name": "inputStartAmount",
"type": "uint256"
},
{
"name": "inputEndAmount",
"type": "uint256"
},
{
"name": "outputs",
"type": "DutchOutput[]"
}
],
"OrderInfo": [
{
"name": "reactor",
"type": "address"
},
{
"name": "swapper",
"type": "address"
},
{
"name": "nonce",
"type": "uint256"
},
{
"name": "deadline",
"type": "uint256"
},
{
"name": "additionalValidationContract",
"type": "address"
},
{
"name": "additionalValidationData",
"type": "bytes"
}
],
"DutchOutput": [
{
"name": "token",
"type": "address"
},
{
"name": "startAmount",
"type": "uint256"
},
{
"name": "endAmount",
"type": "uint256"
},
{
"name": "recipient",
"type": "address"
}
]
},
"values": {
"permitted": {
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"amount": {
"type": "BigNumber",
"hex": "0x0bebc200"
}
},
"spender": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
"nonce": {
"type": "BigNumber",
"hex": "0x046832aa305880d33daa871e5041a0cd4853599a9ead518917239e2067650401"
},
"deadline": 1697481666,
"witness": {
"info": {
"reactor": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
"swapper": "0x0938a82F93D5DAB110Dc6277FC236b5b082DC10F",
"nonce": {
"type": "BigNumber",
"hex": "0x046832aa305880d33daa871e5041a0cd4853599a9ead518917239e2067650401"
},
"deadline": 1697481666,
"additionalValidationContract": "0x0000000000000000000000000000000000000000",
"additionalValidationData": "0x"
},
"decayStartTime": 1697481594,
"decayEndTime": 1697481654,
"exclusiveFiller": "0xaAFb85ad4a412dd8adC49611496a7695A22f4aeb",
"exclusivityOverrideBps": {
"type": "BigNumber",
"hex": "0x64"
},
"inputToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"inputStartAmount": {
"type": "BigNumber",
"hex": "0x0bebc200"
},
"inputEndAmount": {
"type": "BigNumber",
"hex": "0x0bebc200"
},
"outputs": [
{
"token": "0x0000000000000000000000000000000000000000",
"startAmount": {
"type": "BigNumber",
"hex": "0x01b7d653c183183f"
},
"endAmount": {
"type": "BigNumber",
"hex": "0x01a2e50b6386d671"
},
"recipient": "0x0938a82F93D5DAB110Dc6277FC236b5b082DC10F"
},
{
"token": "0x0000000000000000000000000000000000000000",
"startAmount": {
"type": "BigNumber",
"hex": "0xa926b6321051"
},
"endAmount": {
"type": "BigNumber",
"hex": "0xa118e2ebf2bb"
},
"recipient": "0x37a8f295612602f2774d331e562be9e61B83a327"
}
]
}
}
},
"portionBips": 15,
"portionAmount": "185983730585681",
"portionRecipient": "0x37a8f295612602f2774d331e562be9e61B83a327"
}
},
{
"routing": "CLASSIC",
"quote": {
"methodParameters": {
"calldata": "0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000652d85d0000000000000000000000000000000000000000000000000000000000000000308060c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000bebc20000000000000000000000000000000000000000000000000001bdf1285753b47400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000037a8f295612602f2774d331e562be9e61b83a327000000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000938a82f93d5dab110dc6277fc236b5b082dc10f00000000000000000000000000000000000000000000000001bd45ea74e458eb",
"value": "0x00",
"to": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD"
},
"blockNumber": "18364784",
"amount": "200000000",
"amountDecimals": "200",
"quote": "126149127803342909",
"quoteDecimals": "0.126149127803342909",
"quoteGasAdjusted": "122888348391508943",
"quoteGasAdjustedDecimals": "0.122888348391508943",
"quoteGasAndPortionAdjusted": "122699124699803928",
"quoteGasAndPortionAdjustedDecimals": "0.122699124699803928",
"gasUseEstimateQuote": "3260779411833966",
"gasUseEstimateQuoteDecimals": "0.003260779411833966",
"gasUseEstimate": "240911",
"gasUseEstimateUSD": "5.153332510477604328",
"simulationStatus": "SUCCESS",
"simulationError": false,
"gasPriceWei": "13535203506",
"route": [
[
{
"type": "v2-pool",
"address": "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc",
"tokenIn": {
"chainId": 1,
"decimals": "6",
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"symbol": "USDC"
},
"tokenOut": {
"chainId": 1,
"decimals": "18",
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"symbol": "WETH"
},
"reserve0": {
"token": {
"chainId": 1,
"decimals": "6",
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"symbol": "USDC"
},
"quotient": "27487668611269"
},
"reserve1": {
"token": {
"chainId": 1,
"decimals": "18",
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"symbol": "WETH"
},
"quotient": "17390022942803382004255"
},
"amountIn": "200000000",
"amountOut": "125959904111637894"
}
]
],
"routeString": "[V2] 100.00% = USDC -- [0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc] --> WETH",
"quoteId": "f46cf31c-251e-470c-bd57-13209015694e",
"portionBips": 15,
"portionRecipient": "0x37a8f295612602f2774d331e562be9e61B83a327",
"portionAmount": "189223691705014",
"portionAmountDecimals": "0.000189223691705014",
"requestId": "a02ca0ca-7855-4dd0-9330-8b818aaeb59f",
"tradeType": "EXACT_INPUT",
"slippage": 0.5
}
}
]
}

@ -32,6 +32,7 @@
"quoteId": "f9f47cd7-a62c-4622-9ac7-51d0e662245a",
"requestId": "2d16f993-6429-4755-ba50-1383789459dc",
"auctionPeriodSecs": 60,
"startTimeBufferSecs": 30,
"deadlineBufferSecs": 12,
"slippageTolerance": "0.5",
"permitData": {
@ -252,6 +253,7 @@
"quoteId": "f9f47cd7-a62c-4622-9ac7-51d0e662245a",
"requestId": "2d16f993-6429-4755-ba50-1383789459dc",
"auctionPeriodSecs": 60,
"startTimeBufferSecs": 30,
"deadlineBufferSecs": 12,
"slippageTolerance": "0.5",
"permitData": {

@ -32,6 +32,7 @@
"quoteId": "09ce28b7-1ddf-4317-a28d-d21092be9f84",
"requestId": "f00535d4-461a-4363-afbe-7a5ab7061cd1",
"auctionPeriodSecs": 60,
"startTimeBufferSecs": 30,
"deadlineBufferSecs": 12,
"slippageTolerance": "0.5",
"permitData": {
@ -252,6 +253,7 @@
"quoteId": "09ce28b7-1ddf-4317-a28d-d21092be9f84",
"requestId": "f00535d4-461a-4363-afbe-7a5ab7061cd1",
"auctionPeriodSecs": 60,
"startTimeBufferSecs": 30,
"deadlineBufferSecs": 12,
"slippageTolerance": "0.5",
"permitData": {

@ -12,7 +12,7 @@ describe('translations', () => {
cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-settings')).click()
cy.get(getTestSelector('wallet-language-item')).contains('français').click({ force: true })
cy.location('hash').should('match', /\?lng=fr-FR$/)
cy.location('search').should('match', /\?lng=fr-FR$/)
cy.contains('Échanger')
cy.contains('Uniswap disponible en : English')
})

@ -24,7 +24,7 @@ declare global {
}
interface VisitOptions {
serviceWorker?: true
featureFlags?: Array<FeatureFlag>
featureFlags?: Array<{ name: FeatureFlag; value: boolean }>
/**
* Initial user state.
* @default {@type import('../utils/user-state').CONNECTED_WALLET_USER_STATE}
@ -41,29 +41,28 @@ Cypress.Commands.overwrite(
(original, url: string | Partial<Cypress.VisitOptions>, options?: Partial<Cypress.VisitOptions>) => {
if (typeof url !== 'string') throw new Error('Invalid arguments. The first argument to cy.visit must be the path.')
// Add a hash in the URL if it is not present (to use hash-based routing correctly with queryParams).
const hashUrl = url.startsWith('/') && url.length > 2 && !url.startsWith('/#') ? `/#${url}` : url
return cy
.intercept('/service-worker.js', options?.serviceWorker ? undefined : { statusCode: 404 })
.provider()
.then((provider) =>
original({
...options,
url: hashUrl,
url,
onBeforeLoad(win) {
options?.onBeforeLoad?.(win)
setInitialUserState(win, {
...initialState,
hideUniswapWalletBanner: true,
...CONNECTED_WALLET_USER_STATE,
...(options?.userState ?? {}),
})
// Set feature flags, if configured.
if (options?.featureFlags) {
const featureFlags = options.featureFlags.reduce((flags, flag) => ({ ...flags, [flag]: 'enabled' }), {})
const featureFlags = options.featureFlags.reduce(
(flags, flag) => ({ ...flags, [flag.name]: flag.value ? 'enabled' : 'control' }),
{}
)
win.localStorage.setItem('featureFlags', JSON.stringify(featureFlags))
}
@ -75,18 +74,14 @@ Cypress.Commands.overwrite(
}
)
Cypress.Commands.add('waitForAmplitudeEvent', (eventName, timeout = 5000 /* 5s */) => {
const startTime = new Date().getTime()
Cypress.Commands.add('waitForAmplitudeEvent', (eventName) => {
function checkRequest() {
return cy.wait('@amplitude', { timeout }).then((interception) => {
return cy.wait('@amplitude').then((interception) => {
const events = interception.request.body.events
const event = events.find((event: any) => event.event_type === eventName)
if (event) {
return cy.wrap(event)
} else if (new Date().getTime() - startTime > timeout) {
throw new Error(`Event ${eventName} not found within the specified timeout`)
} else {
return checkRequest()
}

@ -9,8 +9,9 @@ beforeEach(() => {
req.headers['origin'] = 'https://app.uniswap.org'
})
// Infura is disabled for cypress tests - calls should be routed through the connected wallet instead.
// Network RPCs are disabled for cypress tests - calls should be routed through the connected wallet instead.
cy.intercept(/infura.io/, { statusCode: 404 })
cy.intercept(/quiknode.pro/, { statusCode: 404 })
// Log requests to hardhat.
cy.intercept(/:8545/, logJsonRpc)
@ -26,10 +27,16 @@ beforeEach(() => {
server_upload_time: Date.now(),
payload_size_bytes: byteSize,
events_ingested: req.body.events.length,
})
}),
{
'origin-country': 'US',
}
)
}).intercept('https://*.sentry.io', { statusCode: 200 })
// Mock statsig to allow us to mock flags.
cy.intercept(/statsig/, { statusCode: 409 })
// Mock our own token list responses to avoid the latency of IPFS.
cy.intercept('https://gateway.ipfs.io/ipns/tokens.uniswap.org', TokenListJSON)
.intercept('https://gateway.ipfs.io/ipns/extendedtokens.uniswap.org', { statusCode: 404 })

@ -5,7 +5,7 @@
"incremental": true,
"isolatedModules": false,
"noImplicitAny": false,
"target": "ES5",
"target": "ES6",
"tsBuildInfoFile": "../node_modules/.cache/tsbuildinfo/cypress", // avoid clobbering the build tsbuildinfo
"types": ["cypress", "node"],
},

@ -1,3 +1,4 @@
import { connectionMetaKey } from '../../src/connection/meta'
import { ConnectionType } from '../../src/connection/types'
import { UserState } from '../../src/state/user/reducer'
@ -10,23 +11,30 @@ export const DISCONNECTED_WALLET_USER_STATE: Partial<UserState> = { selectedWall
* Other persisted slices are not set, so they will be filled with their respective initial values
* when the app runs.
*/
export function setInitialUserState(win: Cypress.AUTWindow, initialUserState: any) {
export function setInitialUserState(win: Cypress.AUTWindow, state: UserState) {
// Selected wallet should also be reflected in localStorage, so that eager connections work.
if (state.selectedWallet) {
win.localStorage.setItem(
connectionMetaKey,
JSON.stringify({
type: state.selectedWallet,
})
)
}
win.indexedDB.deleteDatabase('redux')
const dbRequest = win.indexedDB.open('redux')
dbRequest.onsuccess = function () {
const db = dbRequest.result
const transaction = db.transaction('keyvaluepairs', 'readwrite')
const store = transaction.objectStore('keyvaluepairs')
store.put(
{
user: initialUserState,
user: state,
},
'persist:interface'
)
}
dbRequest.onupgradeneeded = function () {
const db = dbRequest.result
db.createObjectStore('keyvaluepairs')

@ -18,9 +18,9 @@ Currently, there are 2 types of cloudflare functions developed
## Testing
Testing is done utilizing a custom jest environment as well as Cloudflare's local tester: `wrangler`. Wrangler enables testing locally by running a proxy to wrap `localhost`. Testing can be done the following ways.
- Manually by running `yarn start:cloud` to setup wrangler on `localhost:3000`
- Automated tests by running `yarn test:cloud` to setup both a jest and wrangler environment and automatically test features
Testing is done utilizing a custom jest environment as well as Cloudflare's local tester: `wrangler`. Wrangler enables testing locally by running a proxy to wrap `localhost`. Tests run against a proxy server, so you'll need to start it before running tests:
- Manually run `yarn start:cloud` to setup wrangler on `localhost:3000`
- Run unit tests with `yarn test:cloud`
## Deployment

@ -11,7 +11,7 @@ export const onRequest: PagesFunction = async ({ request, next }) => {
}
const res = next()
try {
return new HTMLRewriter().on('head', new MetaTagInjector(data)).transform(await res)
return new HTMLRewriter().on('head', new MetaTagInjector(data, request)).transform(await res)
} catch (e) {
return res
}

@ -2,6 +2,7 @@
import { ImageResponse } from '@vercel/og'
import React from 'react'
import { blocklistedCollections } from '../../../../../src/nft/utils/blocklist'
import { WATERMARK_URL } from '../../../../constants'
import getAsset from '../../../../utils/getAsset'
import getFont from '../../../../utils/getFont'
@ -15,6 +16,10 @@ export const onRequest: PagesFunction = async ({ params, request }) => {
const tokenId = index[1]?.toString()
const cacheUrl = origin + '/nfts/asset/' + collectionAddress + '/' + tokenId
if (blocklistedCollections.includes(collectionAddress)) {
return new Response('Collection unsupported.', { status: 404 })
}
const data = await getRequest(
cacheUrl,
() => getAsset(collectionAddress, tokenId, cacheUrl),

@ -18,3 +18,12 @@ test.each(invalidAssetImageUrl)('invalidAssetImageUrl', async (url) => {
const response = await fetch(new Request(url))
expect(response.status).toBe(404)
})
const blockedAssetImageUrl = [
'http://127.0.0.1:3000/api/image/nfts/asset/0xd4d871419714b778ebec2e22c7c53572b573706e/276',
]
test.each(blockedAssetImageUrl)('blockedAssetImageUrl', async (url) => {
const response = await fetch(new Request(url))
expect(response.status).toBe(404)
})

@ -2,9 +2,10 @@
import { ImageResponse } from '@vercel/og'
import React from 'react'
import { blocklistedCollections } from '../../../../../src/nft/utils/blocklist'
import { getColor } from '../../../../../src/utils/getColor'
import { CHECK_URL, WATERMARK_URL } from '../../../../constants'
import getCollection from '../../../../utils/getCollection'
import getColor from '../../../../utils/getColor'
import getFont from '../../../../utils/getFont'
import { getRequest } from '../../../../utils/getRequest'
@ -15,6 +16,10 @@ export const onRequest: PagesFunction = async ({ params, request }) => {
const collectionAddress = index?.toString()
const cacheUrl = origin + '/nfts/collection/' + collectionAddress
if (blocklistedCollections.includes(collectionAddress)) {
return new Response('Collection unsupported.', { status: 404 })
}
const data = await getRequest(
cacheUrl,
() => getCollection(collectionAddress, cacheUrl),

@ -1,20 +1,34 @@
const collectionImageUrl = [
import * as matchers from 'jest-extended'
expect.extend(matchers)
const collectionImageUrls = [
'http://127.0.0.1:3000/api/image/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c544',
'http://127.0.0.1:3000/api/image/nfts/collection/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d',
'http://127.0.0.1:3000/api/image/nfts/collection/0x49cf6f5d44e70224e2e23fdcdd2c053f30ada28b',
]
test.each(collectionImageUrl)('collectionImageUrl', async (url) => {
test.each([...collectionImageUrls])('collectionImageUrl', async (url) => {
const response = await fetch(new Request(url))
expect(response.status).toBe(200)
expect(response.headers.get('content-type')).toBe('image/png')
})
const invalidCollectionImageUrl = [
const nonexistentImageUrls = [
'http://127.0.0.1:3000/api/image/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c545',
]
test.each(invalidCollectionImageUrl)('invalidAssetImageUrl', async (url) => {
const invalidCollectionImageUrls = ['http://127.0.0.1:3000/api/image/nfts/collection/0xd3adb33f']
test.each([...invalidCollectionImageUrls, ...nonexistentImageUrls])('invalidAssetImageUrl', async (url) => {
const response = await fetch(new Request(url))
expect(response.status).toBe(404)
expect(response.status).toBeOneOf([404, 500])
})
const blockedCollectionImageUrls = [
'http://127.0.0.1:3000/api/image/nfts/collection/0xd4d871419714b778ebec2e22c7c53572b573706e',
]
test.each(blockedCollectionImageUrls)('blockedCollectionImageUrl', async (url) => {
const response = await fetch(new Request(url))
expect(response.status).toBeOneOf([404, 500])
})

@ -2,8 +2,8 @@
import { ImageResponse } from '@vercel/og'
import React from 'react'
import { getColor } from '../../../../src/utils/getColor'
import { WATERMARK_URL } from '../../../constants'
import getColor from '../../../utils/getColor'
import getFont from '../../../utils/getFont'
import getNetworkLogoUrl from '../../../utils/getNetworkLogoURL'
import { getRequest } from '../../../utils/getRequest'

@ -6,12 +6,15 @@ test('should append meta tag to element', () => {
} as unknown as Element
const property = 'property'
const content = 'content'
const injector = new MetaTagInjector({
title: 'test',
url: 'testUrl',
image: 'testImage',
description: 'testDescription',
})
const injector = new MetaTagInjector(
{
title: 'test',
url: 'testUrl',
image: 'testImage',
description: 'testDescription',
},
new Request('http://localhost')
)
injector.append(element, property, content)
expect(element.append).toHaveBeenCalledWith(`<meta property="${property}" content="${content}"/>`, { html: true })
@ -36,3 +39,22 @@ test('should append meta tag to element', () => {
expect(element.append).toHaveBeenCalledTimes(13)
})
test('should pass through header blocked paths', () => {
const element = {
append: jest.fn(),
} as unknown as Element
const request = new Request('http://localhost')
request.headers.set('x-blocked-paths', '/')
const injector = new MetaTagInjector(
{
title: 'test',
url: 'testUrl',
image: 'testImage',
description: 'testDescription',
},
request
)
injector.element(element)
expect(element.append).toHaveBeenCalledWith(`<meta property="x:blocked-paths" content="/"/>`, { html: true })
})

@ -10,7 +10,7 @@ type MetaTagInjectorInput = {
* to inject meta tags into the <head> of an HTML document.
*/
export class MetaTagInjector implements HTMLRewriterElementContentHandlers {
constructor(private input: MetaTagInjectorInput) {}
constructor(private input: MetaTagInjectorInput, private request: Request) {}
append(element: Element, property: string, content: string) {
element.append(`<meta property="${property}" content="${content}"/>`, { html: true })
@ -38,5 +38,10 @@ export class MetaTagInjector implements HTMLRewriterElementContentHandlers {
this.append(element, 'twitter:image', this.input.image)
this.append(element, 'twitter:image:alt', this.input.title)
}
const blockedPaths = this.request.headers.get('x-blocked-paths')
if (blockedPaths) {
this.append(element, 'x:blocked-paths', blockedPaths)
}
}
}

@ -1,76 +1,2 @@
export const WATERMARK_URL = 'https://app.uniswap.org/images/324x74_App_Watermark.png'
export const CHECK_URL = 'https://app.uniswap.org/images/54x54_Verified_Check.svg'
export const DEFAULT_COLOR = [35, 43, 43]
export const predefinedTokenColors: { [key: string]: number[] } = {
// old WBTC
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599/logo.png':
[240, 146, 65],
// new WBTC
'https://assets.coingecko.com/coins/images/7598/large/wrapped_bitcoin_wbtc.png?1548822744': [240, 146, 65],
// DAI
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png':
[250, 176, 27],
// UNI
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/logo.png':
[230, 53, 140],
// BUSD
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x4Fabb145d64652a948d72533023f6E7A623C7C53/logo.png':
[239, 186, 9],
// AI-X
'https://s2.coinmarketcap.com/static/img/coins/64x64/26984.png': [41, 161, 241],
// ETH
'https://token-icons.s3.amazonaws.com/eth.png': [73, 112, 213],
// HARRYPOTTERSHIBAINUBITCOIN
'https://assets.coingecko.com/coins/images/30323/large/hpos10i_logo_casino_night-dexview.png?1684117567': [
222, 49, 16,
],
// PEPE
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x6982508145454Ce325dDbE47a25d4ec3d2311933/logo.png':
[62, 174, 20],
// Unibot V2
'https://s2.coinmarketcap.com/static/img/coins/64x64/25436.png': [74, 10, 79],
// UNIBOT v1
'https://assets.coingecko.com/coins/images/30462/small/logonoline_%281%29.png?1687510315': [74, 10, 79],
// USDC
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png':
[0, 102, 217],
// HEX
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png':
[249, 63, 140],
// MONG
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x1ce270557C1f68Cfb577b856766310Bf8B47FD9C/logo.png':
[169, 109, 255],
// ARB
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1/logo.png':
[41, 161, 241],
// PSYOP
'https://s2.coinmarketcap.com/static/img/coins/64x64/25422.png': [232, 143, 0],
// MATIC
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0/logo.png':
[169, 109, 255],
// TURBO
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA35923162C49cF95e6BF26623385eb431ad920D3/logo.png':
[189, 110, 41],
// AIDOGE
'https://assets.coingecko.com/coins/images/29852/large/photo_2023-04-18_14-25-28.jpg?1681799160': [41, 161, 241],
// SIMPSON
'https://assets.coingecko.com/coins/images/30243/large/1111.png?1683692033': [232, 143, 0],
// OX
'https://assets.coingecko.com/coins/images/30604/large/Logo2.png?1685522119': [41, 89, 217],
// ANGLE
'https://assets.coingecko.com/coins/images/19060/large/ANGLE_Token-light.png?1666774221': [255, 85, 85],
// APE
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x4d224452801ACEd8B2F0aebE155379bb5D594381/logo.png':
[5, 74, 169],
// GUSD
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd/logo.png':
[0, 164, 189],
// OGN
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26/logo.png':
[5, 74, 169],
// RPL
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xD33526068D116cE69F19A9ee46F0bd304F21A51f/logo.png':
[255, 123, 79],
}

@ -1,19 +0,0 @@
import { setup } from 'jest-dev-server'
module.exports = async function globalSetup() {
globalThis.servers = await setup({
command: `yarn start:cloud`,
port: 3000,
launchTimeout: 120000, // takes ~2m on CI
})
// Wait for wrangler to return a request before running tests
for (let i = 0; i < 3; i++) {
const res = await fetch(new Request('http://127.0.0.1:3000/tokens/ethereum/NATIVE'))
if (res.ok) {
return
}
// Set timeout to make sure the server isn't flooded with requests if wrangler is not running
await new Promise((resolve) => setTimeout(resolve, 500 * (i + 1)))
}
throw new Error('Failed to start server')
}

@ -1,5 +0,0 @@
import { teardown } from 'jest-dev-server'
module.exports = async function globalTeardown() {
await teardown(globalThis.servers)
}

@ -1,6 +0,0 @@
import { setup } from 'jest-dev-server'
declare global {
// eslint-disable-next-line no-var
var servers: Awaited<ReturnType<typeof setup>>
}

@ -1,6 +1,4 @@
{
"globalSetup": "<rootDir>/global-setup.ts",
"globalTeardown": "<rootDir>/global-teardown.ts",
"setupFilesAfterEnv": ["<rootDir>/setupAfterEnv.ts"],
"preset": "ts-jest",
"transform": {

@ -8,7 +8,7 @@ export const onRequest: PagesFunction = async ({ params, request, next }) => {
const { index } = params
const collectionAddress = index[0]?.toString()
const tokenId = index[1]?.toString()
return getMetadataRequest(res, request.url, () => getAsset(collectionAddress, tokenId, request.url))
return getMetadataRequest(res, request, () => getAsset(collectionAddress, tokenId, request.url))
} catch (e) {
return res
}

@ -17,7 +17,7 @@ exports[`should inject metadata for valid assets 1`] = `
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_App_Icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#FC72FF" />
<meta name="theme-color" content="#fff" />
<meta
http-equiv="Content-Security-Policy"
@ -37,31 +37,46 @@ exports[`should inject metadata for valid assets 1`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Inter-roman.var.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preconnect" href="https://api.uniswap.org/" crossorigin/>
<link rel="preconnect" href="https://mainnet.infura.io/" crossorigin/>
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Book.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
<style>
* {
font-family: 'Inter', sans-serif;
font-family: 'Basel', sans-serif;
box-sizing: border-box;
}
/**
Explicitly load Inter var from public/ so it does not block LCP's critical path.
Explicitly load Basel var from public/ so it does not block LCP's critical path.
*/
@font-face {
font-family: 'Inter custom';
font-weight: 100 900;
font-family: 'Basel';
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Regular';
src: url(/fonts/Inter-roman.var.woff2) format('woff2 supports variations(gvar)'),
url(/fonts/Inter-roman.var.woff2) format('woff2-variations'),
url(/fonts/Inter-roman.var.woff2) format('woff2');
src:
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
font-family: 'Basel';
font-weight: 485;
font-style: normal;
font-display: block;
src:
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Inter custom', sans-serif;
font-family: 'Basel', sans-serif;
}
}
@ -77,9 +92,11 @@ exports[`should inject metadata for valid assets 1`] = `
html {
font-size: 16px;
font-weight: 485;
font-variant: none;
font-smooth: always;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility !important;
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
@ -105,13 +122,13 @@ exports[`should inject metadata for valid assets 1`] = `
@media (prefers-color-scheme: dark) {
html {
background: linear-gradient(180deg, #202738 0%, #070816 100%);
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
}
}
@media (prefers-color-scheme: light) {
html {
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0) 0%, rgba(255, 255, 255, 0) 100%), rgb(255, 255, 255);
}
}
</style>
@ -148,7 +165,7 @@ exports[`should inject metadata for valid assets 2`] = `
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_App_Icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#FC72FF" />
<meta name="theme-color" content="#fff" />
<meta
http-equiv="Content-Security-Policy"
@ -168,31 +185,46 @@ exports[`should inject metadata for valid assets 2`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Inter-roman.var.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preconnect" href="https://api.uniswap.org/" crossorigin/>
<link rel="preconnect" href="https://mainnet.infura.io/" crossorigin/>
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Book.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
<style>
* {
font-family: 'Inter', sans-serif;
font-family: 'Basel', sans-serif;
box-sizing: border-box;
}
/**
Explicitly load Inter var from public/ so it does not block LCP's critical path.
Explicitly load Basel var from public/ so it does not block LCP's critical path.
*/
@font-face {
font-family: 'Inter custom';
font-weight: 100 900;
font-family: 'Basel';
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Regular';
src: url(/fonts/Inter-roman.var.woff2) format('woff2 supports variations(gvar)'),
url(/fonts/Inter-roman.var.woff2) format('woff2-variations'),
url(/fonts/Inter-roman.var.woff2) format('woff2');
src:
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
font-family: 'Basel';
font-weight: 485;
font-style: normal;
font-display: block;
src:
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Inter custom', sans-serif;
font-family: 'Basel', sans-serif;
}
}
@ -208,9 +240,11 @@ exports[`should inject metadata for valid assets 2`] = `
html {
font-size: 16px;
font-weight: 485;
font-variant: none;
font-smooth: always;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility !important;
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
@ -236,13 +270,13 @@ exports[`should inject metadata for valid assets 2`] = `
@media (prefers-color-scheme: dark) {
html {
background: linear-gradient(180deg, #202738 0%, #070816 100%);
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
}
}
@media (prefers-color-scheme: light) {
html {
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0) 0%, rgba(255, 255, 255, 0) 100%), rgb(255, 255, 255);
}
}
</style>
@ -279,7 +313,7 @@ exports[`should inject metadata for valid assets 3`] = `
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_App_Icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#FC72FF" />
<meta name="theme-color" content="#fff" />
<meta
http-equiv="Content-Security-Policy"
@ -299,31 +333,46 @@ exports[`should inject metadata for valid assets 3`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Inter-roman.var.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preconnect" href="https://api.uniswap.org/" crossorigin/>
<link rel="preconnect" href="https://mainnet.infura.io/" crossorigin/>
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Book.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
<style>
* {
font-family: 'Inter', sans-serif;
font-family: 'Basel', sans-serif;
box-sizing: border-box;
}
/**
Explicitly load Inter var from public/ so it does not block LCP's critical path.
Explicitly load Basel var from public/ so it does not block LCP's critical path.
*/
@font-face {
font-family: 'Inter custom';
font-weight: 100 900;
font-family: 'Basel';
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Regular';
src: url(/fonts/Inter-roman.var.woff2) format('woff2 supports variations(gvar)'),
url(/fonts/Inter-roman.var.woff2) format('woff2-variations'),
url(/fonts/Inter-roman.var.woff2) format('woff2');
src:
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
font-family: 'Basel';
font-weight: 485;
font-style: normal;
font-display: block;
src:
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Inter custom', sans-serif;
font-family: 'Basel', sans-serif;
}
}
@ -339,9 +388,11 @@ exports[`should inject metadata for valid assets 3`] = `
html {
font-size: 16px;
font-weight: 485;
font-variant: none;
font-smooth: always;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility !important;
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
@ -367,13 +418,13 @@ exports[`should inject metadata for valid assets 3`] = `
@media (prefers-color-scheme: dark) {
html {
background: linear-gradient(180deg, #202738 0%, #070816 100%);
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
}
}
@media (prefers-color-scheme: light) {
html {
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0) 0%, rgba(255, 255, 255, 0) 100%), rgb(255, 255, 255);
}
}
</style>

@ -7,7 +7,7 @@ export const onRequest: PagesFunction = async ({ params, request, next }) => {
try {
const { index } = params
const collectionAddress = index?.toString()
return getMetadataRequest(res, request.url, () => getCollection(collectionAddress, request.url))
return getMetadataRequest(res, request, () => getCollection(collectionAddress, request.url))
} catch (e) {
return res
}

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should inject metadata for valid collections 1`] = `
exports[`should inject metadata for collections 1`] = `
"<!DOCTYPE html>
<html translate="no">
<head>
@ -17,7 +17,7 @@ exports[`should inject metadata for valid collections 1`] = `
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_App_Icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#FC72FF" />
<meta name="theme-color" content="#fff" />
<meta
http-equiv="Content-Security-Policy"
@ -37,31 +37,46 @@ exports[`should inject metadata for valid collections 1`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Inter-roman.var.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preconnect" href="https://api.uniswap.org/" crossorigin/>
<link rel="preconnect" href="https://mainnet.infura.io/" crossorigin/>
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Book.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
<style>
* {
font-family: 'Inter', sans-serif;
font-family: 'Basel', sans-serif;
box-sizing: border-box;
}
/**
Explicitly load Inter var from public/ so it does not block LCP's critical path.
Explicitly load Basel var from public/ so it does not block LCP's critical path.
*/
@font-face {
font-family: 'Inter custom';
font-weight: 100 900;
font-family: 'Basel';
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Regular';
src: url(/fonts/Inter-roman.var.woff2) format('woff2 supports variations(gvar)'),
url(/fonts/Inter-roman.var.woff2) format('woff2-variations'),
url(/fonts/Inter-roman.var.woff2) format('woff2');
src:
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
font-family: 'Basel';
font-weight: 485;
font-style: normal;
font-display: block;
src:
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Inter custom', sans-serif;
font-family: 'Basel', sans-serif;
}
}
@ -77,9 +92,11 @@ exports[`should inject metadata for valid collections 1`] = `
html {
font-size: 16px;
font-weight: 485;
font-variant: none;
font-smooth: always;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility !important;
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
@ -105,13 +122,13 @@ exports[`should inject metadata for valid collections 1`] = `
@media (prefers-color-scheme: dark) {
html {
background: linear-gradient(180deg, #202738 0%, #070816 100%);
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
}
}
@media (prefers-color-scheme: light) {
html {
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0) 0%, rgba(255, 255, 255, 0) 100%), rgb(255, 255, 255);
}
}
</style>
@ -131,7 +148,7 @@ exports[`should inject metadata for valid collections 1`] = `
"
`;
exports[`should inject metadata for valid collections 2`] = `
exports[`should inject metadata for collections 2`] = `
"<!DOCTYPE html>
<html translate="no">
<head>
@ -148,7 +165,7 @@ exports[`should inject metadata for valid collections 2`] = `
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_App_Icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#FC72FF" />
<meta name="theme-color" content="#fff" />
<meta
http-equiv="Content-Security-Policy"
@ -168,31 +185,46 @@ exports[`should inject metadata for valid collections 2`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Inter-roman.var.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preconnect" href="https://api.uniswap.org/" crossorigin/>
<link rel="preconnect" href="https://mainnet.infura.io/" crossorigin/>
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Book.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
<style>
* {
font-family: 'Inter', sans-serif;
font-family: 'Basel', sans-serif;
box-sizing: border-box;
}
/**
Explicitly load Inter var from public/ so it does not block LCP's critical path.
Explicitly load Basel var from public/ so it does not block LCP's critical path.
*/
@font-face {
font-family: 'Inter custom';
font-weight: 100 900;
font-family: 'Basel';
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Regular';
src: url(/fonts/Inter-roman.var.woff2) format('woff2 supports variations(gvar)'),
url(/fonts/Inter-roman.var.woff2) format('woff2-variations'),
url(/fonts/Inter-roman.var.woff2) format('woff2');
src:
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
font-family: 'Basel';
font-weight: 485;
font-style: normal;
font-display: block;
src:
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Inter custom', sans-serif;
font-family: 'Basel', sans-serif;
}
}
@ -208,9 +240,11 @@ exports[`should inject metadata for valid collections 2`] = `
html {
font-size: 16px;
font-weight: 485;
font-variant: none;
font-smooth: always;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility !important;
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
@ -236,13 +270,13 @@ exports[`should inject metadata for valid collections 2`] = `
@media (prefers-color-scheme: dark) {
html {
background: linear-gradient(180deg, #202738 0%, #070816 100%);
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
}
}
@media (prefers-color-scheme: light) {
html {
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0) 0%, rgba(255, 255, 255, 0) 100%), rgb(255, 255, 255);
}
}
</style>
@ -262,7 +296,7 @@ exports[`should inject metadata for valid collections 2`] = `
"
`;
exports[`should inject metadata for valid collections 3`] = `
exports[`should inject metadata for collections 3`] = `
"<!DOCTYPE html>
<html translate="no">
<head>
@ -279,7 +313,7 @@ exports[`should inject metadata for valid collections 3`] = `
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_App_Icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#FC72FF" />
<meta name="theme-color" content="#fff" />
<meta
http-equiv="Content-Security-Policy"
@ -299,31 +333,46 @@ exports[`should inject metadata for valid collections 3`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Inter-roman.var.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preconnect" href="https://api.uniswap.org/" crossorigin/>
<link rel="preconnect" href="https://mainnet.infura.io/" crossorigin/>
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Book.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
<style>
* {
font-family: 'Inter', sans-serif;
font-family: 'Basel', sans-serif;
box-sizing: border-box;
}
/**
Explicitly load Inter var from public/ so it does not block LCP's critical path.
Explicitly load Basel var from public/ so it does not block LCP's critical path.
*/
@font-face {
font-family: 'Inter custom';
font-weight: 100 900;
font-family: 'Basel';
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Regular';
src: url(/fonts/Inter-roman.var.woff2) format('woff2 supports variations(gvar)'),
url(/fonts/Inter-roman.var.woff2) format('woff2-variations'),
url(/fonts/Inter-roman.var.woff2) format('woff2');
src:
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
font-family: 'Basel';
font-weight: 485;
font-style: normal;
font-display: block;
src:
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Inter custom', sans-serif;
font-family: 'Basel', sans-serif;
}
}
@ -339,9 +388,11 @@ exports[`should inject metadata for valid collections 3`] = `
html {
font-size: 16px;
font-weight: 485;
font-variant: none;
font-smooth: always;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility !important;
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
@ -367,13 +418,13 @@ exports[`should inject metadata for valid collections 3`] = `
@media (prefers-color-scheme: dark) {
html {
background: linear-gradient(180deg, #202738 0%, #070816 100%);
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
}
}
@media (prefers-color-scheme: light) {
html {
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0) 0%, rgba(255, 255, 255, 0) 100%), rgb(255, 255, 255);
}
}
</style>

@ -16,7 +16,7 @@ const collections = [
},
]
test.each(collections)('should inject metadata for valid collections', async (collection) => {
test.each([...collections])('should inject metadata for collections', async (collection) => {
const url = 'http://127.0.0.1:3000/nfts/collection/' + collection.address
const body = await fetch(new Request(url)).then((res) => res.text())
expect(body).toMatchSnapshot()
@ -34,14 +34,22 @@ test.each(collections)('should inject metadata for valid collections', async (co
expect(body).toContain(`<meta property="twitter:image:alt" content="${collection.collectionName} on Uniswap"/>`)
})
const invalidCollections = [
'http://127.0.0.1:3000/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c545',
'http://127.0.0.1:3000/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c545//',
const nonexistentCollections = [
{
address: '0xed5af388653567af2f388e6224dc7c4b3241c545',
},
]
test.each(invalidCollections)(
'should not inject metadata for invalid collection urls',
async (url) => {
const invalidCollections = [
{
address: '0xd3adb33f',
},
]
test.each([...invalidCollections, ...nonexistentCollections])(
'should not inject metadata for nonexistent collections',
async (collection) => {
const url = 'http://127.0.0.1:3000/nfts/collection/' + collection.address
const body = await fetch(new Request(url)).then((res) => res.text())
expect(body).not.toContain('og:title')
expect(body).not.toContain('og:image')
@ -54,6 +62,5 @@ test.each(invalidCollections)(
expect(body).not.toContain('twitter:title')
expect(body).not.toContain('twitter:image')
expect(body).not.toContain('twitter:image:alt')
},
50000
}
)

@ -11,7 +11,7 @@ export const onRequest: PagesFunction = async ({ params, request, next }) => {
if (!tokenAddress) {
return res
}
return getMetadataRequest(res, request.url, () => getToken(networkName, tokenAddress, request.url))
return getMetadataRequest(res, request, () => getToken(networkName, tokenAddress, request.url))
} catch (e) {
return res
}

@ -17,7 +17,7 @@ exports[`should inject metadata for valid tokens 1`] = `
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_App_Icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#FC72FF" />
<meta name="theme-color" content="#fff" />
<meta
http-equiv="Content-Security-Policy"
@ -37,31 +37,46 @@ exports[`should inject metadata for valid tokens 1`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Inter-roman.var.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preconnect" href="https://api.uniswap.org/" crossorigin/>
<link rel="preconnect" href="https://mainnet.infura.io/" crossorigin/>
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Book.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
<style>
* {
font-family: 'Inter', sans-serif;
font-family: 'Basel', sans-serif;
box-sizing: border-box;
}
/**
Explicitly load Inter var from public/ so it does not block LCP's critical path.
Explicitly load Basel var from public/ so it does not block LCP's critical path.
*/
@font-face {
font-family: 'Inter custom';
font-weight: 100 900;
font-family: 'Basel';
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Regular';
src: url(/fonts/Inter-roman.var.woff2) format('woff2 supports variations(gvar)'),
url(/fonts/Inter-roman.var.woff2) format('woff2-variations'),
url(/fonts/Inter-roman.var.woff2) format('woff2');
src:
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
font-family: 'Basel';
font-weight: 485;
font-style: normal;
font-display: block;
src:
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Inter custom', sans-serif;
font-family: 'Basel', sans-serif;
}
}
@ -77,9 +92,11 @@ exports[`should inject metadata for valid tokens 1`] = `
html {
font-size: 16px;
font-weight: 485;
font-variant: none;
font-smooth: always;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility !important;
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
@ -105,13 +122,13 @@ exports[`should inject metadata for valid tokens 1`] = `
@media (prefers-color-scheme: dark) {
html {
background: linear-gradient(180deg, #202738 0%, #070816 100%);
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
}
}
@media (prefers-color-scheme: light) {
html {
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0) 0%, rgba(255, 255, 255, 0) 100%), rgb(255, 255, 255);
}
}
</style>
@ -148,7 +165,7 @@ exports[`should inject metadata for valid tokens 2`] = `
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_App_Icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#FC72FF" />
<meta name="theme-color" content="#fff" />
<meta
http-equiv="Content-Security-Policy"
@ -168,31 +185,46 @@ exports[`should inject metadata for valid tokens 2`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Inter-roman.var.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preconnect" href="https://api.uniswap.org/" crossorigin/>
<link rel="preconnect" href="https://mainnet.infura.io/" crossorigin/>
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Book.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
<style>
* {
font-family: 'Inter', sans-serif;
font-family: 'Basel', sans-serif;
box-sizing: border-box;
}
/**
Explicitly load Inter var from public/ so it does not block LCP's critical path.
Explicitly load Basel var from public/ so it does not block LCP's critical path.
*/
@font-face {
font-family: 'Inter custom';
font-weight: 100 900;
font-family: 'Basel';
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Regular';
src: url(/fonts/Inter-roman.var.woff2) format('woff2 supports variations(gvar)'),
url(/fonts/Inter-roman.var.woff2) format('woff2-variations'),
url(/fonts/Inter-roman.var.woff2) format('woff2');
src:
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
font-family: 'Basel';
font-weight: 485;
font-style: normal;
font-display: block;
src:
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Inter custom', sans-serif;
font-family: 'Basel', sans-serif;
}
}
@ -208,9 +240,11 @@ exports[`should inject metadata for valid tokens 2`] = `
html {
font-size: 16px;
font-weight: 485;
font-variant: none;
font-smooth: always;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility !important;
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
@ -236,13 +270,13 @@ exports[`should inject metadata for valid tokens 2`] = `
@media (prefers-color-scheme: dark) {
html {
background: linear-gradient(180deg, #202738 0%, #070816 100%);
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
}
}
@media (prefers-color-scheme: light) {
html {
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0) 0%, rgba(255, 255, 255, 0) 100%), rgb(255, 255, 255);
}
}
</style>
@ -279,7 +313,7 @@ exports[`should inject metadata for valid tokens 3`] = `
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_App_Icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#FC72FF" />
<meta name="theme-color" content="#fff" />
<meta
http-equiv="Content-Security-Policy"
@ -299,31 +333,46 @@ exports[`should inject metadata for valid tokens 3`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Inter-roman.var.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preconnect" href="https://api.uniswap.org/" crossorigin/>
<link rel="preconnect" href="https://mainnet.infura.io/" crossorigin/>
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Book.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
<style>
* {
font-family: 'Inter', sans-serif;
font-family: 'Basel', sans-serif;
box-sizing: border-box;
}
/**
Explicitly load Inter var from public/ so it does not block LCP's critical path.
Explicitly load Basel var from public/ so it does not block LCP's critical path.
*/
@font-face {
font-family: 'Inter custom';
font-weight: 100 900;
font-family: 'Basel';
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Regular';
src: url(/fonts/Inter-roman.var.woff2) format('woff2 supports variations(gvar)'),
url(/fonts/Inter-roman.var.woff2) format('woff2-variations'),
url(/fonts/Inter-roman.var.woff2) format('woff2');
src:
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
font-family: 'Basel';
font-weight: 485;
font-style: normal;
font-display: block;
src:
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Inter custom', sans-serif;
font-family: 'Basel', sans-serif;
}
}
@ -339,9 +388,11 @@ exports[`should inject metadata for valid tokens 3`] = `
html {
font-size: 16px;
font-weight: 485;
font-variant: none;
font-smooth: always;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility !important;
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
@ -367,13 +418,13 @@ exports[`should inject metadata for valid tokens 3`] = `
@media (prefers-color-scheme: dark) {
html {
background: linear-gradient(180deg, #202738 0%, #070816 100%);
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
}
}
@media (prefers-color-scheme: light) {
html {
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0) 0%, rgba(255, 255, 255, 0) 100%), rgb(255, 255, 255);
}
}
</style>
@ -410,7 +461,7 @@ exports[`should inject metadata for valid tokens 4`] = `
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_App_Icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#FC72FF" />
<meta name="theme-color" content="#fff" />
<meta
http-equiv="Content-Security-Policy"
@ -430,31 +481,46 @@ exports[`should inject metadata for valid tokens 4`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Inter-roman.var.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preconnect" href="https://api.uniswap.org/" crossorigin/>
<link rel="preconnect" href="https://mainnet.infura.io/" crossorigin/>
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Book.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
<style>
* {
font-family: 'Inter', sans-serif;
font-family: 'Basel', sans-serif;
box-sizing: border-box;
}
/**
Explicitly load Inter var from public/ so it does not block LCP's critical path.
Explicitly load Basel var from public/ so it does not block LCP's critical path.
*/
@font-face {
font-family: 'Inter custom';
font-weight: 100 900;
font-family: 'Basel';
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Regular';
src: url(/fonts/Inter-roman.var.woff2) format('woff2 supports variations(gvar)'),
url(/fonts/Inter-roman.var.woff2) format('woff2-variations'),
url(/fonts/Inter-roman.var.woff2) format('woff2');
src:
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
font-family: 'Basel';
font-weight: 485;
font-style: normal;
font-display: block;
src:
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Inter custom', sans-serif;
font-family: 'Basel', sans-serif;
}
}
@ -470,9 +536,11 @@ exports[`should inject metadata for valid tokens 4`] = `
html {
font-size: 16px;
font-weight: 485;
font-variant: none;
font-smooth: always;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility !important;
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
@ -498,13 +566,13 @@ exports[`should inject metadata for valid tokens 4`] = `
@media (prefers-color-scheme: dark) {
html {
background: linear-gradient(180deg, #202738 0%, #070816 100%);
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
}
}
@media (prefers-color-scheme: light) {
html {
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0) 0%, rgba(255, 255, 255, 0) 100%), rgb(255, 255, 255);
}
}
</style>

@ -1,39 +0,0 @@
import { DEFAULT_COLOR } from '../constants'
import getColor from './getColor'
test('should return the average color of a black PNG image', async () => {
const image = 'https://static.vecteezy.com/system/resources/previews/001/209/957/original/square-png.png'
const color = await getColor(image)
expect(color).toEqual([0, 0, 0])
})
test('should return the average color of a blue PNG image', async () => {
const image = 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTB2Ztcim-RKbOu57kfjYpXnnS1MO5YMUaUH9Lk5Eg&s'
const color = await getColor(image)
expect(color).toEqual([2, 6, 251])
})
test('should return the average color of a white PNG image', async () => {
const image = 'https://www.cac.cornell.edu/wiki/images/4/44/White_square.png'
const color = await getColor(image)
expect(color).toEqual([255, 255, 255])
})
test('should return the average color of a white PNG image with whiteness dimmed', async () => {
const image = 'https://www.cac.cornell.edu/wiki/images/4/44/White_square.png'
const color = await getColor(image, true)
expect(color).toEqual(DEFAULT_COLOR)
})
test('should return the average color of a black JPG image', async () => {
const image =
'https://imageio.forbes.com/specials-images/imageserve/5ed6636cdd5d320006caf841/0x0.jpg?format=jpg&width=1200'
const color = await getColor(image)
expect(color).toEqual([0, 0, 0])
})
test('should return default color for a gif image', async () => {
const image = 'https://thumbs.gfycat.com/AgitatedLiveAgouti-size_restricted.gif'
const color = await getColor(image)
expect(color).toEqual(DEFAULT_COLOR)
})

@ -4,13 +4,13 @@ import { Data } from './cache'
export async function getMetadataRequest(
res: Promise<Response>,
url: string,
request: Request,
getData: () => Promise<Data | undefined>
) {
try {
const cachedData = await getRequest(url, getData, (data): data is Data => true)
const cachedData = await getRequest(request.url, getData, (data): data is Data => true)
if (cachedData) {
return new HTMLRewriter().on('head', new MetaTagInjector(cachedData)).transform(await res)
return new HTMLRewriter().on('head', new MetaTagInjector(cachedData, request)).transform(await res)
} else {
return res
}

@ -13,7 +13,8 @@ const forkingConfig = {
const forks = {
[ChainId.MAINNET]: {
url: `https://mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_KEY}`,
blockNumber: UNIVERSAL_ROUTER_CREATION_BLOCK(ChainId.MAINNET),
// Temporarily hardcoding this to fix e2e tests as we investigate source of swap tests failing on older blocknumbers
blockNumber: 18537387,
...forkingConfig,
},
[ChainId.POLYGON]: {

@ -28,7 +28,6 @@ const linguiConfig = {
'ca-ES',
'cs-CZ',
'da-DK',
'de-DE',
'el-GR',
'en-US',
'es-ES',

@ -13,6 +13,7 @@
"graphql:generate:thegraph": "graphql-codegen --config graphql.thegraph.codegen.config.ts",
"graphql:generate": "yarn graphql:generate:data && yarn graphql:generate:thegraph",
"graphql": "yarn graphql:fetch && yarn graphql:generate",
"sitemap:generate": "node scripts/generate-sitemap.js",
"i18n:extract": "lingui extract --locale en-US",
"i18n:compile": "lingui compile",
"i18n": "yarn i18n:extract --clean && yarn i18n:compile",
@ -20,14 +21,14 @@
"start": "craco start",
"start:cloud": "NODE_OPTIONS=--dns-result-order=ipv4first PORT=3001 npx wrangler pages dev --compatibility-flags=nodejs_compat --compatibility-date=2023-08-01 --proxy=3001 --port=3000 -- yarn start",
"build": "craco build",
"analyze": "source-map-explorer 'build/static/js/*.js' --only-mapped",
"serve": "serve build -l 3000",
"analyze": "source-map-explorer 'build/static/js/*.js' --no-border-checks --gzip",
"serve": "serve build -s -l 3000",
"lint": "yarn eslint --ignore-path .gitignore --cache --cache-location node_modules/.cache/eslint/ .",
"typecheck": "tsc",
"typecheck:cloud": "tsc -p functions/tsconfig.json",
"typecheck:cypress": "tsc -p cypress/tsconfig.json",
"test": "craco test",
"test:cloud": "NODE_OPTIONS=--experimental-vm-modules yarn jest functions --config=functions/jest.config.json",
"test:cloud": "yarn jest functions --config=functions/jest.config.json",
"cypress:open": "cypress open --browser chrome --e2e",
"cypress:run": "cypress run --browser chrome --e2e",
"deduplicate": "yarn-deduplicate --strategy=highest",
@ -105,7 +106,6 @@
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"@types/react-redux": "^7.1.24",
"@types/react-router-dom": "^5.3.3",
"@types/react-table": "^7.7.12",
"@types/react-virtualized-auto-sizer": "^1.0.0",
"@types/react-window": "^1.8.2",
@ -115,7 +115,8 @@
"@types/ua-parser-js": "^0.7.36",
"@types/uuid": "^8.3.4",
"@types/wcag-contrast": "^3.0.0",
"@uniswap/default-token-list": "^11.2.0",
"@types/xml2js": "^0.4.12",
"@uniswap/default-token-list": "^11.8.0",
"@uniswap/eslint-config": "^1.2.0",
"@vanilla-extract/jest-transform": "^1.1.1",
"@vanilla-extract/webpack-plugin": "^2.2.0",
@ -128,13 +129,12 @@
"cypress": "12.12.0",
"cypress-hardhat": "^2.5.0",
"env-cmd": "^10.1.0",
"eslint": "^7.11.0",
"eslint": "8.44.0",
"eslint-plugin-import": "^2.27",
"eslint-plugin-rulesdir": "^0.2.2",
"hardhat": "^2.14.0",
"husky": "^8.0.3",
"jest": "^29.6.1",
"jest-dev-server": "^9.0.0",
"jest-extended": "^4.0.1",
"jest-fail-on-console": "^3.1.1",
"jest-fetch-mock": "^3.0.3",
@ -152,18 +152,22 @@
"resize-observer-polyfill": "^1.5.1",
"serve": "^11.3.2",
"source-map-explorer": "^2.5.3",
"start-server-and-test": "^2.0.0",
"swc-loader": "^0.2.3",
"terser": "^5.19.4",
"terser-webpack-plugin": "^5.3.9",
"ts-jest": "^29.1.1",
"ts-transform-graphql-tag": "^0.2.1",
"tsafe": "^1.6.4",
"typechain": "^5.0.0",
"typescript": "^4.9.4",
"webpack": "^5.88.2",
"webpack-retry-chunk-load-plugin": "^3.1.1",
"wrangler": "^3.5.0",
"wrangler": "^3.7.0",
"yarn-deduplicate": "^6.0.0"
},
"resolutions": {
"@uniswap/sdk-core": "4.0.7"
},
"dependencies": {
"@apollo/client": "^3.7.2",
"@coinbase/wallet-sdk": "^3.6.4",
@ -189,20 +193,21 @@
"@sentry/react": "^7.45.0",
"@sentry/tracing": "^7.45.0",
"@sentry/types": "^7.45.0",
"@types/react-helmet": "^6.1.7",
"@types/react-window-infinite-loader": "^1.0.6",
"@uniswap/analytics": "^1.4.0",
"@uniswap/analytics-events": "^2.17.0",
"@uniswap/analytics": "1.5.0",
"@uniswap/analytics-events": "^2.28.0",
"@uniswap/governance": "^1.0.2",
"@uniswap/liquidity-staker": "^1.0.2",
"@uniswap/merkle-distributor": "^1.0.1",
"@uniswap/permit2-sdk": "^1.2.0",
"@uniswap/redux-multicall": "^1.1.8",
"@uniswap/router-sdk": "^1.6.0",
"@uniswap/sdk-core": "^4.0.3",
"@uniswap/router-sdk": "^1.7.1",
"@uniswap/sdk-core": "4.0.7",
"@uniswap/smart-order-router": "^3.15.0",
"@uniswap/token-lists": "^1.0.0-beta.33",
"@uniswap/uniswapx-sdk": "^1.3.0",
"@uniswap/universal-router-sdk": "^1.5.6",
"@uniswap/uniswapx-sdk": "^1.4.1",
"@uniswap/universal-router-sdk": "^1.5.8",
"@uniswap/v2-core": "^1.0.1",
"@uniswap/v2-periphery": "^1.1.0-beta.0",
"@uniswap/v2-sdk": "^3.2.0",
@ -220,16 +225,16 @@
"@visx/react-spring": "^2.12.2",
"@visx/responsive": "^2.10.0",
"@visx/shape": "^2.11.1",
"@web3-react/coinbase-wallet": "^8.2.2",
"@web3-react/core": "^8.2.2",
"@web3-react/eip1193": "^8.2.2",
"@web3-react/empty": "^8.2.2",
"@web3-react/gnosis-safe": "^8.2.3",
"@web3-react/metamask": "^8.2.3",
"@web3-react/network": "^8.2.2",
"@web3-react/types": "^8.2.2",
"@web3-react/url": "^8.2.2",
"@web3-react/walletconnect-v2": "^8.5.0",
"@web3-react/coinbase-wallet": "^8.2.3",
"@web3-react/core": "^8.2.3",
"@web3-react/eip1193": "^8.2.3",
"@web3-react/empty": "^8.2.3",
"@web3-react/gnosis-safe": "^8.2.4",
"@web3-react/metamask": "^8.2.4",
"@web3-react/network": "^8.2.3",
"@web3-react/types": "^8.2.3",
"@web3-react/url": "^8.2.3",
"@web3-react/walletconnect-v2": "^8.5.1",
"ajv": "^8.11.0",
"ajv-formats": "^2.1.1",
"array.prototype.flat": "^1.2.4",
@ -253,6 +258,8 @@
"ms": "^2.1.3",
"multicodec": "^3.0.1",
"multihashes": "^4.0.2",
"nock": "^13.3.3",
"node-fetch": "^3.3.2",
"node-vibrant": "^3.2.1-alpha.1",
"numbro": "^2.3.6",
"polished": "^3.3.2",
@ -263,7 +270,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-feather": "^2.0.8",
"react-ga4": "^1.4.1",
"react-helmet": "^6.1.0",
"react-infinite-scroll-component": "^6.1.0",
"react-is": "^17.0.2",
"react-markdown": "^4.3.1",
@ -293,6 +300,7 @@
"workbox-navigation-preload": "^6.1.0",
"workbox-precaching": "^6.1.0",
"workbox-routing": "^6.1.0",
"xml2js": "^0.6.2",
"zustand": "^4.3.6"
},
"engines": {

@ -1,5 +1,5 @@
diff --git a/node_modules/@vercel/og/dist/index.edge.js b/node_modules/@vercel/og/dist/index.edge.js
index 5187f88..c4a1c41 100644
index 5187f88..eda01d0 100644
--- a/node_modules/@vercel/og/dist/index.edge.js
+++ b/node_modules/@vercel/og/dist/index.edge.js
@@ -18673,8 +18673,8 @@ var Resvg2 = class extends Resvg {
@ -13,12 +13,11 @@ index 5187f88..c4a1c41 100644
// src/emoji/index.ts
var U200D = String.fromCharCode(8205);
@@ -18809,18 +18809,18 @@ async function render(satori, resvg, opts, defaultFonts, element) {
@@ -18809,18 +18809,15 @@ async function render(satori, resvg, opts, defaultFonts, element) {
// src/index.edge.ts
var initializedResvg = initWasm(resvg_wasm);
var initializedYoga = initYoga(yoga_wasm).then((yoga2) => Ll(yoga2));
-var fallbackFont = fetch(new URL("./noto-sans-v27-latin-regular.ttf", import.meta.url)).then((res) => res.arrayBuffer());
+// var fallbackFont = fetch(new URL("https://fonts.gstatic.com/s/notosans/v28/o-0IIpQlx3QUlC5A4PNr6zRF.ttf", import.meta.url)).then((res) => res.arrayBuffer());
var ImageResponse = class {
constructor(element, options = {}) {
const result = new ReadableStream({
@ -26,22 +25,22 @@ index 5187f88..c4a1c41 100644
await initializedYoga;
await initializedResvg;
- const fontData = await fallbackFont;
+ // const fontData = await fallbackFont;
const fonts = [
{
name: "sans serif",
- data: fontData,
+ // data: fontData,
weight: 700,
style: "normal"
}
diff --git a/node_modules/@vercel/og/dist/types.d.ts b/node_modules/@vercel/og/dist/types.d.ts
index dde26cc..eb59ff4 100644
index dde26cc..d075e99 100644
--- a/node_modules/@vercel/og/dist/types.d.ts
+++ b/node_modules/@vercel/og/dist/types.d.ts
@@ -30,7 +30,7 @@ declare type ImageOptions = {
@@ -28,9 +28,8 @@ declare type ImageOptions = {
* A list of fonts to use.
*
* @type {{ data: ArrayBuffer; name: string; weight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900; style?: 'normal' | 'italic' }[]}
* @default Noto Sans Latin Regular.
- * @default Noto Sans Latin Regular.
*/
- fonts?: SatoriOptions['fonts'];
+ fonts: SatoriOptions['fonts'];

@ -0,0 +1,13 @@
diff --git a/node_modules/@web3-react/coinbase-wallet/dist/index.js b/node_modules/@web3-react/coinbase-wallet/dist/index.js
index f38d06e..4f8fa19 100644
--- a/node_modules/@web3-react/coinbase-wallet/dist/index.js
+++ b/node_modules/@web3-react/coinbase-wallet/dist/index.js
@@ -62,7 +62,7 @@ class CoinbaseWallet extends types_1.Connector {
return __awaiter(this, void 0, void 0, function* () {
if (this.eagerConnection)
return;
- yield (this.eagerConnection = Promise.resolve().then(() => __importStar(require('@coinbase/wallet-sdk'))).then((m) => {
+ yield (this.eagerConnection = Promise.resolve().then(async () => __importStar(await import('@coinbase/wallet-sdk'))).then((m) => {
const _a = this.options, { url } = _a, options = __rest(_a, ["url"]);
this.coinbaseWallet = new m.default(options);
this.provider = this.coinbaseWallet.makeWeb3Provider(url);

@ -0,0 +1,15 @@
diff --git a/node_modules/@web3-react/gnosis-safe/dist/index.js b/node_modules/@web3-react/gnosis-safe/dist/index.js
index 015a33c..4cd7cde 100644
--- a/node_modules/@web3-react/gnosis-safe/dist/index.js
+++ b/node_modules/@web3-react/gnosis-safe/dist/index.js
@@ -68,8 +68,8 @@ class GnosisSafe extends types_1.Connector {
if (this.eagerConnection)
return;
// kick off import early to minimize waterfalls
- const SafeAppProviderPromise = Promise.resolve().then(() => __importStar(require('@safe-global/safe-apps-provider'))).then(({ SafeAppProvider }) => SafeAppProvider);
- yield (this.eagerConnection = Promise.resolve().then(() => __importStar(require('@safe-global/safe-apps-sdk'))).then((m) => __awaiter(this, void 0, void 0, function* () {
+ const SafeAppProviderPromise = Promise.resolve().then(async () => __importStar(await import('@safe-global/safe-apps-provider'))).then(({ SafeAppProvider }) => SafeAppProvider);
+ yield (this.eagerConnection = Promise.resolve().then(async () => __importStar(await import('@safe-global/safe-apps-sdk'))).then((m) => __awaiter(this, void 0, void 0, function* () {
this.sdk = new m.default(this.options);
const safe = yield Promise.race([
this.sdk.safe.getInfo(),

@ -0,0 +1,13 @@
diff --git a/node_modules/@web3-react/metamask/dist/index.js b/node_modules/@web3-react/metamask/dist/index.js
index c8476dd..c0bfce7 100644
--- a/node_modules/@web3-react/metamask/dist/index.js
+++ b/node_modules/@web3-react/metamask/dist/index.js
@@ -54,7 +54,7 @@ class MetaMask extends types_1.Connector {
return __awaiter(this, void 0, void 0, function* () {
if (this.eagerConnection)
return;
- return (this.eagerConnection = Promise.resolve().then(() => __importStar(require('@metamask/detect-provider'))).then((m) => __awaiter(this, void 0, void 0, function* () {
+ return (this.eagerConnection = Promise.resolve().then(async () => __importStar(await import('@metamask/detect-provider'))).then((m) => __awaiter(this, void 0, void 0, function* () {
var _a, _b;
const provider = yield m.default(this.options);
if (provider) {

@ -0,0 +1,28 @@
diff --git a/node_modules/@web3-react/walletconnect-v2/dist/index.js b/node_modules/@web3-react/walletconnect-v2/dist/index.js
index 1a36d14..908b8c5 100644
--- a/node_modules/@web3-react/walletconnect-v2/dist/index.js
+++ b/node_modules/@web3-react/walletconnect-v2/dist/index.js
@@ -84,7 +84,7 @@ class WalletConnect extends types_1.Connector {
return __awaiter(this, void 0, void 0, function* () {
const rpcMap = this.rpcMap ? (0, utils_1.getBestUrlMap)(this.rpcMap, this.timeout) : undefined;
const chainProps = this.getChainProps(this.chains, this.optionalChains, desiredChainId);
- const ethProviderModule = yield Promise.resolve().then(() => __importStar(require('@walletconnect/ethereum-provider')));
+ const ethProviderModule = yield Promise.resolve().then(async () => __importStar(await import('@walletconnect/ethereum-provider')));
this.provider = yield ethProviderModule.default.init(Object.assign(Object.assign(Object.assign({}, this.options), chainProps), { rpcMap: yield rpcMap }));
return this.provider
.on('disconnect', this.disconnectListener)
diff --git a/node_modules/@web3-react/walletconnect-v2/dist/utils.js b/node_modules/@web3-react/walletconnect-v2/dist/utils.js
index 17539b6..9ea6371 100644
--- a/node_modules/@web3-react/walletconnect-v2/dist/utils.js
+++ b/node_modules/@web3-react/walletconnect-v2/dist/utils.js
@@ -62,8 +62,8 @@ function getBestUrl(urls, timeout) {
if (urls.length === 1)
return urls[0];
const [HttpConnection, JsonRpcProvider] = yield Promise.all([
- Promise.resolve().then(() => __importStar(require('@walletconnect/jsonrpc-http-connection'))).then(({ HttpConnection }) => HttpConnection),
- Promise.resolve().then(() => __importStar(require('@walletconnect/jsonrpc-provider'))).then(({ JsonRpcProvider }) => JsonRpcProvider),
+ Promise.resolve().then(async () => __importStar(await import('@walletconnect/jsonrpc-http-connection'))).then(({ HttpConnection }) => HttpConnection),
+ Promise.resolve().then(async () => __importStar(await import('@walletconnect/jsonrpc-provider'))).then(({ JsonRpcProvider }) => JsonRpcProvider),
]);
// the below returns the first url for which there's been a successful call, prioritized by index
return new Promise((resolve) => {

@ -3,27 +3,27 @@
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.uniswap",
"package_name": "com.uniswap.mobile",
"sha256_cert_fingerprints":
["97:A5:81:51:DA:AF:8F:6E:65:3A:90:1E:82:12:6C:FB:61:2D:36:C7:CF:20:61:6B:A3:4C:52:CA:BC:58:43:8E", "F9:E9:E3:F0:04:28:66:62:81:44:50:7E:D6:A9:5F:B9:65:39:02:70:1D:13:74:15:D3:E1:A3:1B:D4:38:3A:1F"]
["49:D9:3D:5D:FB:AA:64:A4:64:80:85:0F:39:A8:C1:D9:25:D3:D4:BC:8E:6B:1F:45:0C:EA:AF:B1:0C:27:DF:B8", "F9:E9:E3:F0:04:28:66:62:81:44:50:7E:D6:A9:5F:B9:65:39:02:70:1D:13:74:15:D3:E1:A3:1B:D4:38:3A:1F"]
}
},
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.uniswap.beta",
"package_name": "com.uniswap.mobile.beta",
"sha256_cert_fingerprints":
["E5:39:87:DC:4D:FD:4C:1B:A6:74:36:7D:3A:3B:6B:ED:9E:B3:66:89:92:8A:1B:B8:FC:1B:22:56:56:B4:46:A3", "54:4B:62:33:17:9B:5F:A8:E6:5D:D3:A6:E5:9D:80:5F:A5:02:7F:E2:14:B8:C1:7A:AC:4B:8D:E0:65:49:87:41"]
["75:41:9C:2D:01:4A:88:4E:8D:C6:EF:E5:51:54:28:6B:99:05:31:43:AD:84:B4:EB:39:28:B8:C3:C4:CE:48:E3", "54:4B:62:33:17:9B:5F:A8:E6:5D:D3:A6:E5:9D:80:5F:A5:02:7F:E2:14:B8:C1:7A:AC:4B:8D:E0:65:49:87:41"]
}
},
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.uniswap.dev",
"package_name": "com.uniswap.mobile.dev",
"sha256_cert_fingerprints":
["5A:6D:23:50:2F:1E:0D:01:DC:96:65:F3:3A:18:4C:4C:8C:67:E0:09:99:9B:B1:9B:BF:44:99:D0:D1:D0:FC:5E", "02:E6:1C:76:8C:75:C3:78:C8:8C:FE:7B:2E:8F:4B:E1:FA:47:F2:F6:1A:DB:57:69:4A:41:99:C6:71:2C:AB:E3", "FA:C6:17:45:DC:09:03:78:6F:B9:ED:E6:2A:96:2B:39:9F:73:48:F0:BB:6F:89:9B:83:32:66:75:91:03:3B:9C"]
["45:F8:15:02:C5:4F:AD:82:E7:51:F0:9C:D1:CA:77:C8:C9:BF:06:A6:D9:5A:55:4F:9E:B8:5F:81:33:2B:D0:DB", "02:E6:1C:76:8C:75:C3:78:C8:8C:FE:7B:2E:8F:4B:E1:FA:47:F2:F6:1A:DB:57:69:4A:41:99:C6:71:2C:AB:E3", "FA:C6:17:45:DC:09:03:78:6F:B9:ED:E6:2A:96:2B:39:9F:73:48:F0:BB:6F:89:9B:83:32:66:75:91:03:3B:9C"]
}
}
]

@ -1 +0,0 @@
@uniswap/web-admins

99
public/app-sitemap.xml Normal file

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<url>
<loc>https://app.uniswap.org/</loc>
<lastmod>2023-10-11T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://app.uniswap.org/tokens</loc>
<lastmod>2023-10-11T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/send</loc>
<lastmod>2023-10-11T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://app.uniswap.org/swap</loc>
<lastmod>2023-10-11T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://app.uniswap.org/pool/v2/find</loc>
<lastmod>2023-10-11T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://app.uniswap.org/pool/v2</loc>
<lastmod>2023-10-11T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://app.uniswap.org/pool</loc>
<lastmod>2023-10-11T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://app.uniswap.org/pools/v2/find</loc>
<lastmod>2023-10-11T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://app.uniswap.org/pools/v2</loc>
<lastmod>2023-10-11T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://app.uniswap.org/pools</loc>
<lastmod>2023-10-11T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/add/v2</loc>
<lastmod>2023-10-11T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://app.uniswap.org/add</loc>
<lastmod>2023-10-11T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://app.uniswap.org/migrate/v2</loc>
<lastmod>2023-10-11T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts</loc>
<lastmod>2023-10-11T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/profile</loc>
<lastmod>2023-10-11T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://app.uniswap.org/create-proposal</loc>
<lastmod>2023-10-11T19:57:27.976Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
</urlset>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -14,7 +14,7 @@
<link rel="apple-touch-icon" sizes="512x512" href="%PUBLIC_URL%/images/512x512_App_Icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#FC72FF" />
<meta name="theme-color" content="#fff" />
<meta
http-equiv="Content-Security-Policy"
<% if (process.env.REACT_APP_CSP_ALLOW_UNSAFE_EVAL) { %>
@ -36,31 +36,46 @@
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="preload" href="%PUBLIC_URL%/fonts/Inter-roman.var.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preconnect" href="https://api.uniswap.org/" crossorigin/>
<link rel="preconnect" href="https://mainnet.infura.io/" crossorigin/>
<link rel="preload" href="%PUBLIC_URL%/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="%PUBLIC_URL%/fonts/Basel-Book.woff2" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="%PUBLIC_URL%/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="%PUBLIC_URL%/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
<style>
* {
font-family: 'Inter', sans-serif;
font-family: 'Basel', sans-serif;
box-sizing: border-box;
}
/**
Explicitly load Inter var from public/ so it does not block LCP's critical path.
Explicitly load Basel var from public/ so it does not block LCP's critical path.
*/
@font-face {
font-family: 'Inter custom';
font-weight: 100 900;
font-family: 'Basel';
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Regular';
src: url(%PUBLIC_URL%/fonts/Inter-roman.var.woff2) format('woff2 supports variations(gvar)'),
url(%PUBLIC_URL%/fonts/Inter-roman.var.woff2) format('woff2-variations'),
url(%PUBLIC_URL%/fonts/Inter-roman.var.woff2) format('woff2');
src:
url('%PUBLIC_URL%/fonts/Basel-Medium.woff2') format('woff2'),
url('%PUBLIC_URL%/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
font-family: 'Basel';
font-weight: 485;
font-style: normal;
font-display: block;
src:
url('%PUBLIC_URL%/fonts/Basel-Book.woff') format('woff2'),
url('%PUBLIC_URL%/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Inter custom', sans-serif;
font-family: 'Basel', sans-serif;
}
}
@ -76,9 +91,11 @@
html {
font-size: 16px;
font-weight: 485;
font-variant: none;
font-smooth: always;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility !important;
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
@ -104,13 +121,13 @@
@media (prefers-color-scheme: dark) {
html {
background: linear-gradient(180deg, #202738 0%, #070816 100%);
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
}
}
@media (prefers-color-scheme: light) {
html {
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0) 0%, rgba(255, 255, 255, 0) 100%), rgb(255, 255, 255);
}
}
</style>

@ -1,3 +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"/>
<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="#9B9B9B"/>
</svg>

Before

(image error) Size: 820 B

After

(image error) Size: 820 B

@ -1,3 +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"/>
<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="#9B9B9B"/>
</svg>

Before

(image error) Size: 3.5 KiB

After

(image error) Size: 3.5 KiB

@ -1,5 +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"/>
<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="#9B9B9B"/>
<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="#9B9B9B"/>
<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="#9B9B9B"/>
</svg>

Before

(image error) Size: 1.1 KiB

After

(image error) Size: 1.1 KiB

518
public/nfts-sitemap.xml Normal file

@ -0,0 +1,518 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<url>
<loc>https://app.uniswap.org/nfts/collection/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x60e4d786628fea6478f785a6d7e704777c86a7c6</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c544</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x34d85c9cdeb23fa97cb08333b511ac86e1c4e258</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x99a9b7c1116f9ceeb1652de04d5969cce509b069</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x49cf6f5d44e70224e2e23fdcdd2c053f30ada28b</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xb7f7f6c52f2e2fdb1963eab30438024864c313f6</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x23581767a106ae21c074b2276d25e5c3e136a68b</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x8a90cab2b38dba80c64b7734e58ee1db38b8992e</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xba30e5f9bb24caa003e9f2f0497ad287fdf95623</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xbd3531da5cf5857e7cfaa92426877b022e612cf8</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x7bd29408f11d2bfc23c34f18275bbf23bb716bc7</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x306b1ea3ecdf94ab739f1910bbda052ed4a9f949</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x1a92f7381b9f03921564a437210bb9396471050c</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x5cc5b05a8a13e3fbdb0bb9fccd98d38e50f90c38</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x5af0d9827e0c53e4799bb226655a1de152a425a5</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x3bf2922f4520a8ba0c2efc3d2a1539678dad5e9d</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xe785e82358879f061bc3dcac6f0444462d4b5330</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x76be3b62873462d2142405439777e971754e8e77</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xfd43af6d3fe1b916c026f6ac35b3ede068d1ca01</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x1cb1a5e65610aeff2551a50f76a87a7d3fb649c6</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xff9c1b15b16263c61d017ee9f65c50e4ae0113d7</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x6339e5e072086621540d0362c4e3cea0d643e114</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xb932a70a57673d89f4acffbe830e8ed7f75fb9e0</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x79fcdef22feed20eddacbb2587640e45491b757f</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xa3aee8bce55beea1951ef834b99f3ac60d1abeeb</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x769272677fab02575e84945f03eca517acc544cc</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x4db1f25d3d98600140dfc18deb7515be5bd293af</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x34eebee6942d8def3c125458d1a86e0a897fd6f9</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x59468516a8259058bad1ca5f8f4bff190d30e066</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x394e3d3044fc89fcdd966d3cb35ac0b32b0cda91</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x60bb1e2aa1c9acafb4d34f71585d7e959f387769</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x28472a58a490c5e09a238847f66a68a47cc76f0f</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x341a1c534248966c4b6afad165b98daed4b964ef</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x82c7a8f707110f5fbb16184a5933e9f78a34c6ab</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xccc441ac31f02cd96c153db6fd5fe0a2f4e6a68d</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x764aeebcf425d56800ef2c84f2578689415a2daa</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x160c404b2b49cbc3240055ceaee026df1e8497a0</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xd2f668a8461d6761115daf8aeb3cdf5f40c532c6</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x39ee2c7b3cb80254225884ca001f57118c8f21b6</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xd774557b647330c91bf44cfeab205095f7e6c367</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x1792a96e5668ad7c167ab804a100ce42395ce54d</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xf87e31492faf9a91b02ee0deaad50d51d56d5d4d</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x04afa589e2b933f9463c5639f412b183ec062505</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xe75512aa3bec8f00434bbd6ad8b0a3fbff100ad6</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x348fc118bcc65a92dc033a951af153d14d945312</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x892848074ddea461a15f337250da3ce55580ca85</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x5946aeaab44e65eb370ffaa6a7ef2218cff9b47d</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x282bdd42f4eb70e7a9d9f40c8fea0825b7f68c5d</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x4b15a9c28034dc83db40cd810001427d3bd7163d</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x7ea3cca10668b8346aec0bf1844a49e995527c8b</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xb852c6b5892256c264cc2c888ea462189154d8d7</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x9378368ba6b85c1fba5b131b530f5f5bedf21a18</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x2acab3dea77832c09420663b0e1cb386031ba17b</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x0c2e57efddba8c768147d1fdf9176a0a6ebd5d83</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x08d7c0242953446436f34b4c78fe9da38c73668d</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x8943c7bac1914c9a7aba750bf2b6b09fd21037e0</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x364c828ee171616a39897688a831c2499ad972ec</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x7f36182dee28c45de6072a34d29855bae76dbe2f</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xf61f24c2d93bf2de187546b14425bf631f28d6dc</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x797a48c46be32aafcedcfd3d8992493d8a1f256b</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x123b30e25973fecd8354dd5f41cc45a3065ef88c</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x6632a9d63e142f17a668064d41a21193b49b41a0</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xf4ee95274741437636e748ddac70818b4ed7d043</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x57a204aa1042f6e66dd7730813f4024114d74f37</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xd1258db6ac08eb0e625b75b371c023da478e94a9</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x75e95ba5997eb235f40ecf8347cdb11f18ff640b</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xd532b88607b1877fe20c181cba2550e3bbd6b31c</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xa1d4657e0e6507d5a94d06da93e94dc7c8c44b51</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xedb61f74b0d09b2558f1eeb79b247c1f363ae452</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x7d8820fa92eb1584636f4f5b8515b5476b75171a</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x231d3559aa848bf10366fb9868590f01d34bf240</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xad9fd7cb4fc7a0fbce08d64068f60cbde22ed34c</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x0e9d6552b85be180d941f1ca73ae3e318d2d4f1f</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xb716600ed99b4710152582a124c697a7fe78adbf</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xaadc2d4261199ce24a4b0a57370c4fcf43bb60aa</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x4e1f41613c9084fdb9e34e11fae9412427480e56</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x79986af15539de2db9a5086382daeda917a9cf0c</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xc99c679c50033bbc5321eb88752e89a93e9e83c5</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xc36cf0cfcb5d905b8b513860db0cfe63f6cf9f5c</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x9c8ff314c9bc7f6e59a9d9225fb22946427edc03</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x3110ef5f612208724ca51f5761a69081809f03b7</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x036721e5a769cc48b3189efbb9cce4471e8a48b1</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x524cab2ec69124574082676e6f654a18df49a048</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x7ab2352b1d2e185560494d5e577f9d3c238b78c5</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x32973908faee0bf825a343000fe412ebe56f802a</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x7daec605e9e2a1717326eedfd660601e2753a057</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xc1caf0c19a8ac28c41fe59ba6c754e4b9bd54de9</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x33fd426905f149f8376e227d0c9d3340aad17af1</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x466cfcd0525189b573e794f554b8a751279213ac</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x6be69b2a9b153737887cfcdca7781ed1511c7e36</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x80336ad7a747236ef41f47ed2c7641828a480baa</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x9401518f4ebba857baa879d9f76e1cc8b31ed197</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x4b61413d4392c806e6d0ff5ee91e6073c21d6430</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xc3f733ca98e0dad0386979eb96fb1722a1a05e69</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x09233d553058c2f42ba751c87816a8e9fae7ef10</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x960b7a6bcd451c9968473f7bbfd9be826efd549a</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x36d30b3b85255473d27dd0f7fd8f35e36a9d6f06</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x698fbaaca64944376e2cdc4cad86eaa91362cf54</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x497a9a79e82e6fc0ff10a16f6f75e6fcd5ae65a8</loc>
<lastmod>2023-10-11T23:24:32.127Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x41a322b28d0ff354040e2cbc676f0320d8c8850d</loc>
<lastmod>2023-10-16T18:42:53.632Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0xa9c0a07a7cb84ad1f2ffab06de3e55aab7d523e8</loc>
<lastmod>2023-10-16T18:42:53.632Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x942bc2d3e7a589fe5bd4a5c6ef9727dfd82f5c8a</loc>
<lastmod>2023-10-16T18:42:53.632Z</lastmod>
<priority>0.7</priority>
</url>
</urlset>

6
public/robots.txt Normal file

@ -0,0 +1,6 @@
# *
User-agent: *
Disallow:
# Sitemaps
Sitemap: https://app.uniswap.org/sitemap.xml

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