Compare commits

...

260 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
713 changed files with 44574 additions and 15498 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
scripts
src

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: process.env.CYPRESS_RETRIES ? +process.env.CYPRESS_RETRIES : 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,43 +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('@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')
// 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('@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')
// Balances should not be refetched upon opening drawer
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', () => {
@ -48,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()

@ -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' })
@ -82,7 +73,7 @@ describe('UniswapX Orders', () => {
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()
@ -101,7 +92,7 @@ describe('UniswapX Orders', () => {
it('can swap exact-out trades using uniswapX', () => {
// Setup a swap
cy.get('#swap-currency-output .token-amount-input').type('300')
cy.contains('Try it now').click()
cy.wait('@quote')
// Submit uniswapx order signature
cy.get('#swap-button').click()
@ -120,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()
@ -136,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()
@ -150,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' })
@ -170,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()
@ -199,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()
@ -211,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())
@ -235,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' })
@ -255,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()
@ -283,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()
@ -310,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()
@ -335,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"]')

@ -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()
})
@ -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
}

@ -1,439 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should inject metadata for valid collections 1`] = `
"<!DOCTYPE html>
<html translate="no">
<head>
<meta charset="utf-8" />
<title>Uniswap Interface</title>
<meta name="description" content="Swap or provide liquidity on the Uniswap Protocol" />
<!--
will be replaced with the URL of the \`public\` folder during build.
Only files inside the \`public\` folder can be referenced from the HTML.
-->
<link rel="shortcut icon" type="image/png" href="/favicon.png" />
<link rel="apple-touch-icon" sizes="192x192" href="/images/192x192_App_Icon.png" />
<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
http-equiv="Content-Security-Policy"
content="script-src 'self' 'unsafe-inline'"
/>
<!--
Apple Smart App Banner for Safari on iOS
https://developer.apple.com/documentation/webkit/promoting_apps_with_smart_app_banners
-->
<meta name="apple-itunes-app" content="app-id=6443944476">
<!--
manifest.json provides metadata used when the app is installed as a PWA.
See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
<style>
* {
font-family: 'Basel', sans-serif;
box-sizing: border-box;
}
/**
Explicitly load Basel var from public/ so it does not block LCP's critical path.
*/
@font-face {
font-family: 'Basel';
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Medium.woff) format('woff');
}
@font-face {
font-family: 'Basel';
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Book.woff) format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
html,
body {
margin: 0;
padding: 0;
}
button {
user-select: none;
}
html {
font-size: 16px;
font-weight: 485;
font-variant: none;
font-smooth: always;
text-rendering: optimizeLegibility !important;
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
/* Use this to apply network-specific gradient backgrounds, in RadialGradientByChainUpdater.ts */
#background-radial-gradient {
position: fixed;
top: 0;
left: 0;
right: 0;
pointer-events: none;
width: 200vw;
height: 200vh;
transform: translate(-50vw, -100vh);
z-index: -1;
}
html,
body,
#root {
min-height: 100%;
}
@media (prefers-color-scheme: dark) {
html {
background: linear-gradient(180deg, #202738 0%, #070816 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
}
}
</style>
<script defer src="/static/js/bundle.js"></script><meta property="og:title" content="Uniswap Interface"/><meta property="og:image" content="http://127.0.0.1:3000/images/1200x630_Rich_Link_Preview_Image.png"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="630"/><meta property="og:image:alt" content="Uniswap Interface"/><meta property="og:type" content="website"/><meta property="og:url" content="http://127.0.0.1:3000/"/><meta property="twitter:card" content="summary_large_image"/><meta property="twitter:title" content="Uniswap Interface"/><meta property="twitter:image" content="http://127.0.0.1:3000/images/1200x630_Rich_Link_Preview_Image.png"/><meta property="twitter:image:alt" content="Uniswap Interface"/></head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root">
<!-- Triggers the font to load immediately and then is replaced by the app -->
<div>&emsp;</div>
</div>
<div id="background-radial-gradient"></div>
</body>
</html>
"
`;
exports[`should inject metadata for valid collections 2`] = `
"<!DOCTYPE html>
<html translate="no">
<head>
<meta charset="utf-8" />
<title>Uniswap Interface</title>
<meta name="description" content="Swap or provide liquidity on the Uniswap Protocol" />
<!--
will be replaced with the URL of the \`public\` folder during build.
Only files inside the \`public\` folder can be referenced from the HTML.
-->
<link rel="shortcut icon" type="image/png" href="/favicon.png" />
<link rel="apple-touch-icon" sizes="192x192" href="/images/192x192_App_Icon.png" />
<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
http-equiv="Content-Security-Policy"
content="script-src 'self' 'unsafe-inline'"
/>
<!--
Apple Smart App Banner for Safari on iOS
https://developer.apple.com/documentation/webkit/promoting_apps_with_smart_app_banners
-->
<meta name="apple-itunes-app" content="app-id=6443944476">
<!--
manifest.json provides metadata used when the app is installed as a PWA.
See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
<style>
* {
font-family: 'Basel', sans-serif;
box-sizing: border-box;
}
/**
Explicitly load Basel var from public/ so it does not block LCP's critical path.
*/
@font-face {
font-family: 'Basel';
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Medium.woff) format('woff');
}
@font-face {
font-family: 'Basel';
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Book.woff) format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
html,
body {
margin: 0;
padding: 0;
}
button {
user-select: none;
}
html {
font-size: 16px;
font-weight: 485;
font-variant: none;
font-smooth: always;
text-rendering: optimizeLegibility !important;
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
/* Use this to apply network-specific gradient backgrounds, in RadialGradientByChainUpdater.ts */
#background-radial-gradient {
position: fixed;
top: 0;
left: 0;
right: 0;
pointer-events: none;
width: 200vw;
height: 200vh;
transform: translate(-50vw, -100vh);
z-index: -1;
}
html,
body,
#root {
min-height: 100%;
}
@media (prefers-color-scheme: dark) {
html {
background: linear-gradient(180deg, #202738 0%, #070816 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
}
}
</style>
<script defer src="/static/js/bundle.js"></script><meta property="og:title" content="Uniswap Interface"/><meta property="og:image" content="http://127.0.0.1:3000/images/1200x630_Rich_Link_Preview_Image.png"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="630"/><meta property="og:image:alt" content="Uniswap Interface"/><meta property="og:type" content="website"/><meta property="og:url" content="http://127.0.0.1:3000/swap"/><meta property="twitter:card" content="summary_large_image"/><meta property="twitter:title" content="Uniswap Interface"/><meta property="twitter:image" content="http://127.0.0.1:3000/images/1200x630_Rich_Link_Preview_Image.png"/><meta property="twitter:image:alt" content="Uniswap Interface"/></head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root">
<!-- Triggers the font to load immediately and then is replaced by the app -->
<div>&emsp;</div>
</div>
<div id="background-radial-gradient"></div>
</body>
</html>
"
`;
exports[`should inject metadata for valid collections 3`] = `
"<!DOCTYPE html>
<html translate="no">
<head>
<meta charset="utf-8" />
<title>Uniswap Interface</title>
<meta name="description" content="Swap or provide liquidity on the Uniswap Protocol" />
<!--
will be replaced with the URL of the \`public\` folder during build.
Only files inside the \`public\` folder can be referenced from the HTML.
-->
<link rel="shortcut icon" type="image/png" href="/favicon.png" />
<link rel="apple-touch-icon" sizes="192x192" href="/images/192x192_App_Icon.png" />
<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
http-equiv="Content-Security-Policy"
content="script-src 'self' 'unsafe-inline'"
/>
<!--
Apple Smart App Banner for Safari on iOS
https://developer.apple.com/documentation/webkit/promoting_apps_with_smart_app_banners
-->
<meta name="apple-itunes-app" content="app-id=6443944476">
<!--
manifest.json provides metadata used when the app is installed as a PWA.
See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
<style>
* {
font-family: 'Basel', sans-serif;
box-sizing: border-box;
}
/**
Explicitly load Basel var from public/ so it does not block LCP's critical path.
*/
@font-face {
font-family: 'Basel';
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Medium.woff) format('woff');
}
@font-face {
font-family: 'Basel';
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Book.woff) format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
html,
body {
margin: 0;
padding: 0;
}
button {
user-select: none;
}
html {
font-size: 16px;
font-weight: 485;
font-variant: none;
font-smooth: always;
text-rendering: optimizeLegibility !important;
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
/* Use this to apply network-specific gradient backgrounds, in RadialGradientByChainUpdater.ts */
#background-radial-gradient {
position: fixed;
top: 0;
left: 0;
right: 0;
pointer-events: none;
width: 200vw;
height: 200vh;
transform: translate(-50vw, -100vh);
z-index: -1;
}
html,
body,
#root {
min-height: 100%;
}
@media (prefers-color-scheme: dark) {
html {
background: linear-gradient(180deg, #202738 0%, #070816 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
}
}
</style>
<script defer src="/static/js/bundle.js"></script><meta property="og:title" content="Uniswap Interface"/><meta property="og:image" content="http://127.0.0.1:3000/images/1200x630_Rich_Link_Preview_Image.png"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="630"/><meta property="og:image:alt" content="Uniswap Interface"/><meta property="og:type" content="website"/><meta property="og:url" content="http://127.0.0.1:3000/pools"/><meta property="twitter:card" content="summary_large_image"/><meta property="twitter:title" content="Uniswap Interface"/><meta property="twitter:image" content="http://127.0.0.1:3000/images/1200x630_Rich_Link_Preview_Image.png"/><meta property="twitter:image:alt" content="Uniswap Interface"/></head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root">
<!-- Triggers the font to load immediately and then is replaced by the app -->
<div>&emsp;</div>
</div>
<div id="background-radial-gradient"></div>
</body>
</html>
"
`;

@ -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,9 +37,13 @@ exports[`should inject metadata for valid assets 1`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" 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>
* {
@ -55,9 +59,9 @@ exports[`should inject metadata for valid assets 1`] = `
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Medium.woff) format('woff');
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
@ -65,15 +69,14 @@ exports[`should inject metadata for valid assets 1`] = `
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Book.woff) format('woff');
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -119,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>
@ -162,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"
@ -182,9 +185,13 @@ exports[`should inject metadata for valid assets 2`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" 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>
* {
@ -200,9 +207,9 @@ exports[`should inject metadata for valid assets 2`] = `
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Medium.woff) format('woff');
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
@ -210,15 +217,14 @@ exports[`should inject metadata for valid assets 2`] = `
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Book.woff) format('woff');
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -264,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>
@ -307,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"
@ -327,9 +333,13 @@ exports[`should inject metadata for valid assets 3`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" 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>
* {
@ -345,9 +355,9 @@ exports[`should inject metadata for valid assets 3`] = `
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Medium.woff) format('woff');
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
@ -355,15 +365,14 @@ exports[`should inject metadata for valid assets 3`] = `
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Book.woff) format('woff');
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -409,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,9 +37,13 @@ exports[`should inject metadata for valid collections 1`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" 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>
* {
@ -55,9 +59,9 @@ exports[`should inject metadata for valid collections 1`] = `
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Medium.woff) format('woff');
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
@ -65,15 +69,14 @@ exports[`should inject metadata for valid collections 1`] = `
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Book.woff) format('woff');
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -119,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>
@ -145,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>
@ -162,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"
@ -182,9 +185,13 @@ exports[`should inject metadata for valid collections 2`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" 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>
* {
@ -200,9 +207,9 @@ exports[`should inject metadata for valid collections 2`] = `
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Medium.woff) format('woff');
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
@ -210,15 +217,14 @@ exports[`should inject metadata for valid collections 2`] = `
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Book.woff) format('woff');
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -264,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>
@ -290,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>
@ -307,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"
@ -327,9 +333,13 @@ exports[`should inject metadata for valid collections 3`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" 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>
* {
@ -345,9 +355,9 @@ exports[`should inject metadata for valid collections 3`] = `
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Medium.woff) format('woff');
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
@ -355,15 +365,14 @@ exports[`should inject metadata for valid collections 3`] = `
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Book.woff) format('woff');
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -409,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,9 +37,13 @@ exports[`should inject metadata for valid tokens 1`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" 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>
* {
@ -55,9 +59,9 @@ exports[`should inject metadata for valid tokens 1`] = `
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Medium.woff) format('woff');
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
@ -65,15 +69,14 @@ exports[`should inject metadata for valid tokens 1`] = `
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Book.woff) format('woff');
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -119,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>
@ -162,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"
@ -182,9 +185,13 @@ exports[`should inject metadata for valid tokens 2`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" 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>
* {
@ -200,9 +207,9 @@ exports[`should inject metadata for valid tokens 2`] = `
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Medium.woff) format('woff');
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
@ -210,15 +217,14 @@ exports[`should inject metadata for valid tokens 2`] = `
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Book.woff) format('woff');
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -264,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>
@ -307,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"
@ -327,9 +333,13 @@ exports[`should inject metadata for valid tokens 3`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" 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>
* {
@ -345,9 +355,9 @@ exports[`should inject metadata for valid tokens 3`] = `
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Medium.woff) format('woff');
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
@ -355,15 +365,14 @@ exports[`should inject metadata for valid tokens 3`] = `
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Book.woff) format('woff');
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -409,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>
@ -452,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"
@ -472,9 +481,13 @@ exports[`should inject metadata for valid tokens 4`] = `
-->
<link rel="manifest" href="/manifest.json" />
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" 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>
* {
@ -490,9 +503,9 @@ exports[`should inject metadata for valid tokens 4`] = `
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Medium.woff) format('woff');
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
@ -500,15 +513,14 @@ exports[`should inject metadata for valid tokens 4`] = `
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(/fonts/Basel-Book.woff) format('woff');
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -554,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.18.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.

@ -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,9 +36,13 @@
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="preload" href="%PUBLIC_URL%/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
<link rel="preload" href="%PUBLIC_URL%/fonts/Basel-Medium.woff" as="font" type="font/woff" 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>
* {
@ -54,9 +58,9 @@
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(%PUBLIC_URL%/fonts/Basel-Medium.woff) format('woff');
url('%PUBLIC_URL%/fonts/Basel-Medium.woff2') format('woff2'),
url('%PUBLIC_URL%/fonts/Basel-Medium.woff') format('woff');
}
@font-face {
@ -64,15 +68,14 @@
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url(%PUBLIC_URL%/fonts/Basel-Book.woff) format('woff');
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: 'Basel', sans-serif;
}
}

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

12
public/sitemap.xml Normal file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://app.uniswap.org/app-sitemap.xml</loc>
</sitemap>
<sitemap>
<loc>https://app.uniswap.org/tokens-sitemap.xml</loc>
</sitemap>
<sitemap>
<loc>https://app.uniswap.org/nfts-sitemap.xml</loc>
</sitemap>
</sitemapindex>

1663
public/tokens-sitemap.xml Normal file

File diff suppressed because it is too large Load Diff

@ -9,19 +9,13 @@ const thegraphConfig = require('../graphql.thegraph.config')
const exec = promisify(child_process.exec)
function fetchSchema(url, outputFile) {
exec(`yarn --silent get-graphql-schema --h Origin=https://app.uniswap.org ${url}`)
.then(({ stderr, stdout }) => {
if (stderr) {
throw new Error(stderr)
} else {
fs.writeFile(outputFile, stdout)
}
})
.catch((err) => {
console.error(err)
console.error(`Failed to fetch schema from ${url}`)
})
async function fetchSchema(url, outputFile) {
try {
const { stdout } = await exec(`yarn --silent get-graphql-schema --h Origin=https://app.uniswap.org ${url}`);
await fs.writeFile(outputFile, stdout);
} catch(err){
console.error(`Failed to fetch schema from ${url}`)
}
}
fetchSchema(process.env.THE_GRAPH_SCHEMA_ENDPOINT, thegraphConfig.schema)

141
scripts/generate-sitemap.js Normal file

@ -0,0 +1,141 @@
/* eslint-env node */
const fs = require('fs')
const { parseStringPromise, Builder } = require('xml2js')
const weekMs = 7 * 24 * 60 * 60 * 1000
const nowISO = new Date().toISOString()
const getTopTokensQuery = (chain) => `
query {
topTokens(pageSize: 100, page: 1, chain: ${chain}, orderBy: VOLUME) {
address
}
}
`
const chains = ['ETHEREUM', 'ARBITRUM', 'OPTIMISM', 'POLYGON', 'BASE', 'BNB', 'CELO']
const nftTopCollectionsQuery = `
query {
topCollections(first: 100, duration: MAX) {
edges {
node {
nftContracts {
address
}
}
}
}
}
`
fs.readFile('./public/tokens-sitemap.xml', 'utf8', async (err, data) => {
const tokenURLs = {}
try {
const sitemap = await parseStringPromise(data)
if (sitemap.urlset.url) {
sitemap.urlset.url.forEach((url) => {
const lastMod = new Date(url.lastmod).getTime()
if (lastMod < Date.now() - weekMs) {
url.lastmod = nowISO
}
tokenURLs[url.loc] = true
})
}
for (const chainName of chains) {
const tokensResponse = await fetch('https://api.uniswap.org/v1/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Origin: 'https://app.uniswap.org',
},
body: JSON.stringify({ query: getTopTokensQuery(chainName) }),
})
const tokensJSON = await tokensResponse.json()
const tokenAddresses = tokensJSON.data.topTokens.map((token) => token.address.toLowerCase())
tokenAddresses.forEach((address) => {
const tokenURL = `https://app.uniswap.org/tokens/${chainName.toLowerCase()}/${address}`
if (!(tokenURL in tokenURLs)) {
sitemap.urlset.url.push({
loc: [tokenURL],
lastmod: [nowISO],
priority: [0.8],
})
}
})
}
const builder = new Builder()
const xml = builder.buildObject(sitemap)
const path = './public/tokens-sitemap.xml'
fs.writeFile(path, xml, (error) => {
if (error) throw error
const stats = fs.statSync(path)
const fileSizeBytes = stats.size
const fileSizeMegabytes = fileSizeBytes / (1024 * 1024)
if (fileSizeMegabytes > 50) {
throw new Error('Generated tokens-sitemap.xml file size exceeds 50MB')
}
console.log('Tokens sitemap updated')
})
} catch (e) {
console.error(e)
}
})
fs.readFile('./public/nfts-sitemap.xml', 'utf8', async (err, data) => {
const collectionURLs = {}
try {
const sitemap = await parseStringPromise(data)
if (sitemap.urlset.url) {
sitemap.urlset.url.forEach((url) => {
const lastMod = new Date(url.lastmod).getTime()
if (lastMod < Date.now() - weekMs) {
url.lastmod = nowISO
}
collectionURLs[url.loc] = true
})
}
const nftResponse = await fetch('https://api.uniswap.org/v1/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Origin: 'https://app.uniswap.org',
},
body: JSON.stringify({ query: nftTopCollectionsQuery }),
})
const nftJSON = await nftResponse.json()
const collectionAddresses = nftJSON.data.topCollections.edges.map((edge) => edge.node.nftContracts[0].address)
collectionAddresses.forEach((address) => {
const collectionURL = `https://app.uniswap.org/nfts/collection/${address}`
if (!(collectionURL in collectionURLs)) {
sitemap.urlset.url.push({
loc: [collectionURL],
lastmod: [nowISO],
priority: [0.7],
})
}
})
const builder = new Builder()
const xml = builder.buildObject(sitemap)
const path = './public/nfts-sitemap.xml'
fs.writeFile(path, xml, (error) => {
if (error) throw error
const stats = fs.statSync(path)
const fileSizeBytes = stats.size
const fileSizeMegabytes = fileSizeBytes / (1024 * 1024)
if (fileSizeMegabytes > 50) {
throw new Error('Generated nfts-sitemap.xml file size exceeds 50MB')
}
console.log('NFT collections sitemap updated')
})
} catch (e) {
console.error(e)
}
})

15
scripts/terser-loader.js Normal file

@ -0,0 +1,15 @@
/* eslint-env node */
const { minify } = require('terser')
module.exports = async function terserLoader(source, map, meta) {
const callback = this.async()
const options = this.getOptions()
try {
const data = await minify(source, options)
const { code } = data || {}
callback(null, code, map, meta)
} catch (e) {
callback(e)
}
}

@ -0,0 +1,133 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "_factoryV2",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "PairLookupFailed",
"type": "error"
},
{
"inputs": [],
"name": "SameToken",
"type": "error"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "tokens",
"type": "address[]"
},
{
"internalType": "address",
"name": "baseToken",
"type": "address"
},
{
"internalType": "uint256",
"name": "amountToBorrow",
"type": "uint256"
}
],
"name": "batchValidate",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "buyFeeBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "sellFeeBps",
"type": "uint256"
}
],
"internalType": "struct TokenFees[]",
"name": "fotResults",
"type": "tuple[]"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount0",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "uniswapV2Call",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "baseToken",
"type": "address"
},
{
"internalType": "uint256",
"name": "amountToBorrow",
"type": "uint256"
}
],
"name": "validate",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "buyFeeBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "sellFeeBps",
"type": "uint256"
}
],
"internalType": "struct TokenFees",
"name": "fotResult",
"type": "tuple"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
]

@ -52,3 +52,8 @@ export const sendAnalyticsEvent: typeof sendAnalyticsTraceEvent = (event, proper
sendAnalyticsTraceEvent(event, properties)
}
}
// This is only used for initial page load so we can get the user's country
export const sendInitializationEvent: typeof sendAnalyticsTraceEvent = (event, properties) => {
sendAnalyticsTraceEvent(event, properties)
}

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