Compare commits

..

3 Commits

Author SHA1 Message Date
Charles Bachmeier
2d00edcb01
fix: Remove Eager Connection Refactor ()
* Revert "feat: account suspense ()"

This reverts commit ed87df62694c2c07518b04edf906d022f0023b71.

* Revert "feat: lazy-load safe only if iframed ()"

This reverts commit b9fc65ec9a15e32fb6eb12d53cc45b8778854be9.

* Revert "feat: eagerly connect outside of react lifecycle ()"

This reverts commit 54880d201a3629988868dcd80c87a7794876cf44.
2023-09-29 09:53:18 -07:00
UL Service Account
590247b725 ci: add global CODEOWNERS 2023-09-22 18:49:21 +00:00
UL Service Account
51b6c7a957 ci(t9n): download translations from crowdin 2023-09-22 18:49:21 +00:00
528 changed files with 128613 additions and 33824 deletions
.env.env.production
.github
.snykCODEOWNERScodecov.ymlcypress.config.ts
cypress
functions
hardhat.config.jslingui.config.tspackage.json
patches
public
scripts
src

11
.env

@ -1,16 +1,15 @@
# 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://null.null"
REACT_APP_AMPLITUDE_PROXY_URL="https://api.uniswap.org/v1/amplitude-proxy"
REACT_APP_AWS_API_REGION="us-east-2"
REACT_APP_AWS_API_ENDPOINT="https://null.null"
REACT_APP_AWS_API_ENDPOINT="https://beta.api.uniswap.org/v1/graphql"
REACT_APP_BNB_RPC_URL="https://rough-sleek-hill.bsc.quiknode.pro/413cc98cbc776cda8fdf1d0f47003583ff73d9bf"
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://null.null"
REACT_APP_TEMP_API_URL="https://null.null"
REACT_APP_UNISWAP_API_URL="https://null.null"
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_WALLET_CONNECT_PROJECT_ID="c6c9bacd35afa3eb9e6cccf6d8464395"

@ -1,16 +1,15 @@
# These API keys are intentionally public. Please do not report them - thank you for your concern.
REACT_APP_AMPLITUDE_PROXY_URL="https://null.null"
REACT_APP_AWS_API_ENDPOINT="https://null.null"
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_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://null.null"
REACT_APP_MOONPAY_LINK="https://us-central1-uniswap-mobile.cloudfunctions.net/signMoonpayLinkV2?platform=web&env=production"
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://null.null"
REACT_APP_QUICKNODE_MAINNET_RPC_URL="https://ultra-blue-flower.quiknode.pro/770b22d5f362c537bc8fe19b034c45b22958f880"
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?source=uniswap"

1
.github/CODEOWNERS vendored Normal file

@ -0,0 +1 @@
* @uniswap/web-reviewers

22
.github/ISSUE_TEMPLATE/bug-report.md vendored Normal file

@ -0,0 +1,22 @@
---
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.)

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file

@ -0,0 +1,8 @@
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

@ -0,0 +1,19 @@
---
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.

48
.github/actions/report/action.yml vendored Normal file

@ -0,0 +1,48 @@
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

49
.github/actions/setup/action.yml vendored Normal file

@ -0,0 +1,49 @@
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

12
.github/dependabot.yml vendored Normal file

@ -0,0 +1,12 @@
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'

52
.github/pull_request_template.md vendored Normal file

@ -0,0 +1,52 @@
<!-- 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

73
.github/workflows/1-main-to-staging.yml vendored Normal file

@ -0,0 +1,73 @@
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

@ -0,0 +1,64 @@
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'

42
.github/workflows/3-staging-to-prod.yml vendored Normal file

@ -0,0 +1,42 @@
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

111
.github/workflows/4-deploy-to-prod.yml vendored Normal file

@ -0,0 +1,111 @@
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'

17
.github/workflows/check-pr-title.yaml vendored Normal file

@ -0,0 +1,17 @@
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 }}

26
.github/workflows/crowdin.yaml vendored Normal file

@ -0,0 +1,26 @@
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 }}

@ -0,0 +1,91 @@
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

277
.github/workflows/test.yml vendored Normal file

@ -0,0 +1,277 @@
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-
# Ignore start:cloud output so it doesn't flood the test output.
# Only use 1 worker for testing, as the other is used to run start:cloud (the proxy server under test).
- run: yarn start-server-and-test 'yarn start:cloud >/dev/null' 3000 '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, cloud-tests]
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' }} &&
${{ needs.cloud-tests.result == 'success' }}

25
.snyk Normal file

@ -0,0 +1,25 @@
# 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: {}

1
CODEOWNERS Normal file

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

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

@ -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 : 1 },
retries: { runMode: process.env.CYPRESS_RETRIES ? +process.env.CYPRESS_RETRIES : 2 },
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) {

@ -16,16 +16,6 @@ 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')
@ -62,7 +52,7 @@ describe('Add Liquidity', () => {
cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/ETH')
cy.wait('@FeeTierDistribution')
cy.get('#add-liquidity-selected-fee .selected-fee-label').should('contain.text', '0.30% fee tier')
cy.get('#add-liquidity-selected-fee .selected-fee-label').should('contain.text', '0.3% fee tier')
cy.get('#add-liquidity-selected-fee .selected-fee-percentage').should('contain.text', '40% select')
})
})

@ -39,50 +39,4 @@ 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')
})
})

@ -53,16 +53,15 @@ 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 (197)')
cy.get(getTestSelector('mini-portfolio-page')).contains('Hidden (201)')
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')
// 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/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()

@ -1,5 +1,4 @@
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'
@ -87,7 +86,6 @@ 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')
@ -118,18 +116,4 @@ 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('-')
})
})

@ -1,146 +0,0 @@
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%)')
})
})
})

@ -6,9 +6,10 @@ describe('Swap settings', () => {
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')
})
@ -25,8 +26,9 @@ describe('Swap settings', () => {
cy.get(getTestSelector('mobile-settings-menu'))
.should('exist')
.within(() => {
cy.contains('Max. slippage').should('exist')
cy.contains('Max slippage').should('exist')
cy.contains('UniswapX').should('exist')
cy.contains('Local routing').should('exist')
cy.contains('Transaction deadline').should('exist')
cy.get(getTestSelector('mobile-settings-close')).click()
})

@ -0,0 +1,27 @@
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,35 +1,17 @@
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) => {
@ -41,26 +23,53 @@ function stubSwapTxReceipt() {
})
}
// TODO: FIX THESE TESTS where we should NOT stub for pricing requests
describe.skip('UniswapX Toggle', () => {
describe('UniswapX Toggle', () => {
beforeEach(() => {
stubNonPriceQuoteWith(QuoteWhereUniswapXIsBetter)
cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter })
cy.visit(`/swap/?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`)
})
it('displays uniswapx ui when setting is on', () => {
it('only displays uniswapx ui when setting is on', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
cy.wait('@quote')
// 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()
// 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.skip('UniswapX Orders', () => {
describe('UniswapX Orders', () => {
beforeEach(() => {
stubNonPriceQuoteWith(QuoteWhereUniswapXIsBetter)
cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter })
cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' })
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/openStatusResponse.json' })
@ -73,7 +82,7 @@ describe.skip('UniswapX Orders', () => {
it('can swap exact-in trades using uniswapX', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
cy.wait('@quote')
cy.contains('Try it now').click()
// Submit uniswapx order signature
cy.get('#swap-button').click()
@ -92,7 +101,7 @@ describe.skip('UniswapX Orders', () => {
it('can swap exact-out trades using uniswapX', () => {
// Setup a swap
cy.get('#swap-currency-output .token-amount-input').type('300')
cy.wait('@quote')
cy.contains('Try it now').click()
// Submit uniswapx order signature
cy.get('#swap-button').click()
@ -111,7 +120,7 @@ describe.skip('UniswapX Orders', () => {
it('renders proper view if uniswapx order expires', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('300')
cy.wait('@quote')
cy.contains('Try it now').click()
// Submit uniswapx order signature
cy.get('#swap-button').click()
@ -127,7 +136,7 @@ describe.skip('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.wait('@quote')
cy.contains('Try it now').click()
// Submit uniswapx order signature
cy.get('#swap-button').click()
@ -141,9 +150,9 @@ describe.skip('UniswapX Orders', () => {
})
})
describe.skip('UniswapX Eth Input', () => {
describe('UniswapX Eth Input', () => {
beforeEach(() => {
stubNonPriceQuoteWith(QuoteWithEthInput)
cy.intercept(QuoteEndpoint, { fixture: QuoteWithEthInput })
cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' })
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/openStatusResponse.json' })
@ -161,8 +170,7 @@ describe.skip('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.wait('@quote')
cy.contains('Try it now').click()
// Prompt ETH wrap to use for order
cy.get('#swap-button').click()
@ -191,10 +199,10 @@ describe.skip('UniswapX Eth Input', () => {
cy.contains('Swapped')
})
it('keeps ETH as the input currency before wrap completes', () => {
it('switches swap input to WETH after wrap', () => {
// Setup a swap
cy.get('#swap-currency-input .token-amount-input').type('1')
cy.wait('@quote')
cy.contains('Try it now').click()
// Prompt ETH wrap and confirm
cy.get('#swap-button').click()
@ -203,25 +211,16 @@ describe.skip('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())
@ -236,15 +235,10 @@ describe.skip('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.skip('UniswapX activity history', () => {
describe('UniswapX activity history', () => {
beforeEach(() => {
cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter })
cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' })
@ -261,6 +255,7 @@ describe.skip('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()
@ -288,6 +283,7 @@ describe.skip('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()
@ -314,6 +310,7 @@ describe.skip('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()
@ -338,13 +335,14 @@ describe.skip('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 one swap activity listed
// Ensure gql and local order have been deduped, such that there is only 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,5 +1,4 @@
import { ChainId, WETH9 } from '@uniswap/sdk-core'
import { FeatureFlag } from 'featureFlags'
import { ARB, UNI } from '../../src/constants/tokens'
import { getTestSelector } from '../utils'
@ -15,9 +14,8 @@ describe('Token details', () => {
it('Uniswap token should have all information populated', () => {
// Uniswap token
cy.visit(`/tokens/ethereum/${UNI_ADDRESS}`, {
featureFlags: [{ name: FeatureFlag.infoTDP, value: false }],
})
cy.visit(`/tokens/ethereum/${UNI_ADDRESS}`)
// Price chart should be filled in
cy.get('[data-cy="chart-header"]').should('include.text', '$')
cy.get('[data-cy="price-chart"]').should('exist')
@ -49,28 +47,12 @@ describe('Token details', () => {
cy.contains(UNI_ADDRESS).should('exist')
})
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 }],
})
// 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('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')
// Shiba predator token, low trading volume and also has warning modal
cy.visit('/tokens/ethereum/0xa71d0588EAf47f12B13cF8eC750430d21DF04974')
// Should have missing price chart when price unavailable (expected for this token)
if (cy.get('[data-cy="chart-header"]').contains('Price unavailable')) {
if (cy.get('[data-cy="chart-header"]').contains('Price Unavailable')) {
cy.get('[data-cy="missing-chart"]').should('exist')
}
@ -79,20 +61,22 @@ describe('Token details', () => {
// About section should have description of token
cy.get(getTestSelector('token-details-about-section')).should('exist')
cy.contains('No token information available').should('exist')
cy.contains('QOM is the Shiba Predator').should('exist')
// Links section should link out to Etherscan, More analytics
// Links section should link out to Etherscan, More analytics, Website, Twitter
cy.get('[data-cy="resources-container"]').within(() => {
cy.contains('Etherscan')
.should('have.attr', 'href')
.and('include', 'etherscan.io/address/0x1eFBB78C8b917f67986BcE54cE575069c0143681')
.and('include', 'etherscan.io/address/0xa71d0588EAf47f12B13cF8eC750430d21DF04974')
cy.contains('More analytics')
.should('have.attr', 'href')
.and('include', 'info.uniswap.org/#/tokens/0x1eFBB78C8b917f67986BcE54cE575069c0143681')
.and('include', 'info.uniswap.org/#/tokens/0xa71d0588EAf47f12B13cF8eC750430d21DF04974')
cy.contains('Website').should('have.attr', 'href').and('include', 'qom')
cy.contains('Twitter').should('have.attr', 'href').and('include', 'twitter.com/ShibaPredator1')
})
// Contract address should be displayed
cy.contains('0x1eFBB78C8b917f67986BcE54cE575069c0143681').should('exist')
cy.contains('0xa71d0588EAf47f12B13cF8eC750430d21DF04974').should('exist')
// Warning label should show if relevant ([spec](https://www.notion.so/3f7fce6f93694be08a94a6984d50298e))
cy.get('[data-cy="token-safety-message"]')

@ -59,7 +59,9 @@ 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', 'Matic')
cy.get(getTestSelector('token-table-row-NATIVE'))
.find(getTestSelector('name-cell'))
.should('include.text', 'Polygon Matic')
})
it('should update when token explore table network changed', () => {
@ -67,6 +69,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.get(getTestSelector('chain-selector-logo')).find('title').should('include.text', 'Ethereum logo')
cy.get(getTestSelector('chain-selector-logo')).invoke('attr', 'alt').should('eq', 'Ethereum')
})
})

@ -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.
cy.get(getTestSelector('searchbar-token-row-ETHEREUM-NATIVE')).click()
getSearchBar().type('{enter}')
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')).find('title').should('include.text', `${chain} logo`)
cy.get(getTestSelector('chain-selector-logo')).invoke('attr', 'alt').should('eq', chain)
}
function switchChain(chain: string) {

@ -49,39 +49,11 @@ 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: [{ name: FeatureFlag.currencyConversion, value: true }] })
cy.visit('/', { featureFlags: [FeatureFlag.currencyConversion] })
cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-settings')).click()
})
@ -175,19 +147,19 @@ describe('Wallet Dropdown', () => {
describe('local currency', () => {
it('loads local currency from the query param', () => {
cy.visit('/', { featureFlags: [{ name: FeatureFlag.currencyConversion, value: true }] })
cy.visit('/', { featureFlags: [FeatureFlag.currencyConversion] })
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.visit('/?cur=AUD', { featureFlags: [FeatureFlag.currencyConversion] })
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.visit('/', { featureFlags: [FeatureFlag.currencyConversion] })
cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-settings')).click()
cy.contains('USD')

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

@ -1,562 +0,0 @@
{
"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
}
}
]
}

@ -24,7 +24,7 @@ declare global {
}
interface VisitOptions {
serviceWorker?: true
featureFlags?: Array<{ name: FeatureFlag; value: boolean }>
featureFlags?: Array<FeatureFlag>
/**
* Initial user state.
* @default {@type import('../utils/user-state').CONNECTED_WALLET_USER_STATE}
@ -53,16 +53,14 @@ Cypress.Commands.overwrite(
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.name]: flag.value ? 'enabled' : 'control' }),
{}
)
const featureFlags = options.featureFlags.reduce((flags, flag) => ({ ...flags, [flag]: 'enabled' }), {})
win.localStorage.setItem('featureFlags', JSON.stringify(featureFlags))
}
@ -74,14 +72,18 @@ Cypress.Commands.overwrite(
}
)
Cypress.Commands.add('waitForAmplitudeEvent', (eventName) => {
Cypress.Commands.add('waitForAmplitudeEvent', (eventName, timeout = 5000 /* 5s */) => {
const startTime = new Date().getTime()
function checkRequest() {
return cy.wait('@amplitude').then((interception) => {
return cy.wait('@amplitude', { timeout }).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,9 +9,8 @@ beforeEach(() => {
req.headers['origin'] = 'https://app.uniswap.org'
})
// Network RPCs are disabled for cypress tests - calls should be routed through the connected wallet instead.
// Infura is 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)
@ -27,16 +26,10 @@ 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": "ES6",
"target": "ES5",
"tsBuildInfoFile": "../node_modules/.cache/tsbuildinfo/cypress", // avoid clobbering the build tsbuildinfo
"types": ["cypress", "node"],
},

@ -1,4 +1,3 @@
import { connectionMetaKey } from '../../src/connection/meta'
import { ConnectionType } from '../../src/connection/types'
import { UserState } from '../../src/state/user/reducer'
@ -11,30 +10,23 @@ 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, 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,
})
)
}
export function setInitialUserState(win: Cypress.AUTWindow, initialUserState: any) {
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: state,
user: initialUserState,
},
'persist:interface'
)
}
dbRequest.onupgradeneeded = function () {
const db = dbRequest.result
db.createObjectStore('keyvaluepairs')

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

@ -7,19 +7,19 @@ const collectionImageUrls = [
'http://127.0.0.1:3000/api/image/nfts/collection/0x49cf6f5d44e70224e2e23fdcdd2c053f30ada28b',
]
test.each([...collectionImageUrls])('collectionImageUrl', async (url) => {
const nonexistentImageUrls = [
'http://127.0.0.1:3000/api/image/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c545',
]
test.each([...collectionImageUrls, ...nonexistentImageUrls])('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 nonexistentImageUrls = [
'http://127.0.0.1:3000/api/image/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c545',
]
const invalidCollectionImageUrls = ['http://127.0.0.1:3000/api/image/nfts/collection/0xd3adb33f']
test.each([...invalidCollectionImageUrls, ...nonexistentImageUrls])('invalidAssetImageUrl', async (url) => {
test.each(invalidCollectionImageUrls)('invalidAssetImageUrl', async (url) => {
const response = await fetch(new Request(url))
expect(response.status).toBeOneOf([404, 500])
})

@ -6,15 +6,12 @@ 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',
},
new Request('http://localhost')
)
const injector = new MetaTagInjector({
title: 'test',
url: 'testUrl',
image: 'testImage',
description: 'testDescription',
})
injector.append(element, property, content)
expect(element.append).toHaveBeenCalledWith(`<meta property="${property}" content="${content}"/>`, { html: true })
@ -39,22 +36,3 @@ 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, private request: Request) {}
constructor(private input: MetaTagInjectorInput) {}
append(element: Element, property: string, content: string) {
element.append(`<meta property="${property}" content="${content}"/>`, { html: true })
@ -38,10 +38,5 @@ 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)
}
}
}

@ -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, () => getAsset(collectionAddress, tokenId, request.url))
return getMetadataRequest(res, request.url, () => getAsset(collectionAddress, tokenId, request.url))
} catch (e) {
return res
}

@ -41,9 +41,8 @@ exports[`should inject metadata for valid assets 1`] = `
<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>
* {
@ -59,9 +58,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.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
url(/fonts/Basel-Medium.woff) format('woff');
}
@font-face {
@ -69,14 +68,15 @@ 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('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
url(/fonts/Basel-Book.woff) format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -189,9 +189,8 @@ exports[`should inject metadata for valid assets 2`] = `
<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>
* {
@ -207,9 +206,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.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
url(/fonts/Basel-Medium.woff) format('woff');
}
@font-face {
@ -217,14 +216,15 @@ 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('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
url(/fonts/Basel-Book.woff) format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -337,9 +337,8 @@ exports[`should inject metadata for valid assets 3`] = `
<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>
* {
@ -355,9 +354,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.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
url(/fonts/Basel-Medium.woff) format('woff');
}
@font-face {
@ -365,14 +364,15 @@ 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('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
url(/fonts/Basel-Book.woff) format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}

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

@ -41,9 +41,8 @@ exports[`should inject metadata for collections 1`] = `
<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>
* {
@ -59,9 +58,9 @@ exports[`should inject metadata for collections 1`] = `
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
url(/fonts/Basel-Medium.woff) format('woff');
}
@font-face {
@ -69,14 +68,15 @@ exports[`should inject metadata for collections 1`] = `
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
url(/fonts/Basel-Book.woff) format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -189,9 +189,8 @@ exports[`should inject metadata for collections 2`] = `
<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>
* {
@ -207,9 +206,9 @@ exports[`should inject metadata for collections 2`] = `
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
url(/fonts/Basel-Medium.woff) format('woff');
}
@font-face {
@ -217,14 +216,15 @@ exports[`should inject metadata for collections 2`] = `
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
url(/fonts/Basel-Book.woff) format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -337,9 +337,8 @@ exports[`should inject metadata for collections 3`] = `
<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>
* {
@ -355,9 +354,9 @@ exports[`should inject metadata for collections 3`] = `
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url('/fonts/Basel-Medium.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
url(/fonts/Basel-Medium.woff) format('woff');
}
@font-face {
@ -365,14 +364,15 @@ exports[`should inject metadata for collections 3`] = `
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url('/fonts/Basel-Book.woff') format('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
url(/fonts/Basel-Book.woff) format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -443,3 +443,151 @@ exports[`should inject metadata for collections 3`] = `
</html>
"
`;
exports[`should inject metadata for collections 4`] = `
"<!DOCTYPE html>
<html translate="no">
<head>
<meta charset="utf-8" />
<title>Uniswap Interface</title>
<!--
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="#fff" />
<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="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-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(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) 0%, rgba(255, 255, 255, 0) 100%), rgb(255, 255, 255);
}
}
</style>
<script defer src="/static/js/bundle.js"></script><meta property="og:title" content="0xed5af388653567af2f388e6224dc7c4b3241c545 on Uniswap"/><meta property="og:image" content="http://127.0.0.1:3000/api/image/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c545"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="630"/><meta property="og:image:alt" content="0xed5af388653567af2f388e6224dc7c4b3241c545 on Uniswap"/><meta property="og:type" content="website"/><meta property="og:url" content="http://127.0.0.1:3000/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c545"/><meta property="twitter:card" content="summary_large_image"/><meta property="twitter:title" content="0xed5af388653567af2f388e6224dc7c4b3241c545 on Uniswap"/><meta property="twitter:image" content="http://127.0.0.1:3000/api/image/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c545"/><meta property="twitter:image:alt" content="0xed5af388653567af2f388e6224dc7c4b3241c545 on Uniswap"/></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>
"
`;

@ -16,7 +16,15 @@ const collections = [
},
]
test.each([...collections])('should inject metadata for collections', async (collection) => {
const nonexistentCollections = [
{
address: '0xed5af388653567af2f388e6224dc7c4b3241c545',
collectionName: '0xed5af388653567af2f388e6224dc7c4b3241c545',
image: 'http://127.0.0.1:3000/api/image/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c545',
},
]
test.each([...collections, ...nonexistentCollections])('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,33 +42,24 @@ test.each([...collections])('should inject metadata for collections', async (col
expect(body).toContain(`<meta property="twitter:image:alt" content="${collection.collectionName} on Uniswap"/>`)
})
const nonexistentCollections = [
{
address: '0xed5af388653567af2f388e6224dc7c4b3241c545',
},
]
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')
expect(body).not.toContain('og:image:width')
expect(body).not.toContain('og:image:height')
expect(body).not.toContain('og:type')
expect(body).not.toContain('og:url')
expect(body).not.toContain('og:image:alt')
expect(body).not.toContain('twitter:card')
expect(body).not.toContain('twitter:title')
expect(body).not.toContain('twitter:image')
expect(body).not.toContain('twitter:image:alt')
}
)
test.each(invalidCollections)('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')
expect(body).not.toContain('og:image:width')
expect(body).not.toContain('og:image:height')
expect(body).not.toContain('og:type')
expect(body).not.toContain('og:url')
expect(body).not.toContain('og:image:alt')
expect(body).not.toContain('twitter:card')
expect(body).not.toContain('twitter:title')
expect(body).not.toContain('twitter:image')
expect(body).not.toContain('twitter:image:alt')
})

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

@ -41,9 +41,8 @@ exports[`should inject metadata for valid tokens 1`] = `
<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>
* {
@ -59,9 +58,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.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
url(/fonts/Basel-Medium.woff) format('woff');
}
@font-face {
@ -69,14 +68,15 @@ 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('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
url(/fonts/Basel-Book.woff) format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -189,9 +189,8 @@ exports[`should inject metadata for valid tokens 2`] = `
<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>
* {
@ -207,9 +206,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.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
url(/fonts/Basel-Medium.woff) format('woff');
}
@font-face {
@ -217,14 +216,15 @@ 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('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
url(/fonts/Basel-Book.woff) format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -337,9 +337,8 @@ exports[`should inject metadata for valid tokens 3`] = `
<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>
* {
@ -355,9 +354,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.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
url(/fonts/Basel-Medium.woff) format('woff');
}
@font-face {
@ -365,14 +364,15 @@ 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('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
url(/fonts/Basel-Book.woff) format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}
@ -485,9 +485,8 @@ exports[`should inject metadata for valid tokens 4`] = `
<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>
* {
@ -503,9 +502,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.woff2') format('woff2'),
url('/fonts/Basel-Medium.woff') format('woff');
url(/fonts/Basel-Medium.woff) format('woff');
}
@font-face {
@ -513,14 +512,15 @@ 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('woff2'),
url('/fonts/Basel-Book.woff') format('woff');
url(/fonts/Basel-Book.woff) format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}

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

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

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

@ -13,7 +13,6 @@
"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",
@ -115,8 +114,7 @@
"@types/ua-parser-js": "^0.7.36",
"@types/uuid": "^8.3.4",
"@types/wcag-contrast": "^3.0.0",
"@types/xml2js": "^0.4.12",
"@uniswap/default-token-list": "^11.8.0",
"@uniswap/default-token-list": "^11.2.0",
"@uniswap/eslint-config": "^1.2.0",
"@vanilla-extract/jest-transform": "^1.1.1",
"@vanilla-extract/webpack-plugin": "^2.2.0",
@ -129,7 +127,7 @@
"cypress": "12.12.0",
"cypress-hardhat": "^2.5.0",
"env-cmd": "^10.1.0",
"eslint": "8.44.0",
"eslint": "^7.11.0",
"eslint-plugin-import": "^2.27",
"eslint-plugin-rulesdir": "^0.2.2",
"hardhat": "^2.14.0",
@ -157,6 +155,7 @@
"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",
@ -165,9 +164,6 @@
"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",
@ -193,21 +189,20 @@
"@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.5.0",
"@uniswap/analytics-events": "^2.28.0",
"@uniswap/analytics": "^1.4.0",
"@uniswap/analytics-events": "^2.23.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.7.1",
"@uniswap/sdk-core": "4.0.7",
"@uniswap/router-sdk": "^1.6.0",
"@uniswap/sdk-core": "^4.0.3",
"@uniswap/smart-order-router": "^3.15.0",
"@uniswap/token-lists": "^1.0.0-beta.33",
"@uniswap/uniswapx-sdk": "^1.4.1",
"@uniswap/universal-router-sdk": "^1.5.8",
"@uniswap/uniswapx-sdk": "^1.3.0",
"@uniswap/universal-router-sdk": "^1.5.6",
"@uniswap/v2-core": "^1.0.1",
"@uniswap/v2-periphery": "^1.1.0-beta.0",
"@uniswap/v2-sdk": "^3.2.0",
@ -225,16 +220,16 @@
"@visx/react-spring": "^2.12.2",
"@visx/responsive": "^2.10.0",
"@visx/shape": "^2.11.1",
"@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",
"@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",
"ajv": "^8.11.0",
"ajv-formats": "^2.1.1",
"array.prototype.flat": "^1.2.4",
@ -258,8 +253,6 @@
"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",
@ -270,7 +263,6 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-feather": "^2.0.8",
"react-helmet": "^6.1.0",
"react-infinite-scroll-component": "^6.1.0",
"react-is": "^17.0.2",
"react-markdown": "^4.3.1",
@ -300,7 +292,6 @@
"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,15 +0,0 @@
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(),

@ -3,27 +3,27 @@
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.uniswap.mobile",
"package_name": "com.uniswap",
"sha256_cert_fingerprints":
["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"]
["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"]
}
},
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.uniswap.mobile.beta",
"package_name": "com.uniswap.beta",
"sha256_cert_fingerprints":
["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"]
["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"]
}
},
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.uniswap.mobile.dev",
"package_name": "com.uniswap.dev",
"sha256_cert_fingerprints":
["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"]
["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"]
}
}
]

@ -1,99 +0,0 @@
<?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.

@ -40,9 +40,8 @@
<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>
* {
@ -58,9 +57,9 @@
font-weight: 535;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url('%PUBLIC_URL%/fonts/Basel-Medium.woff2') format('woff2'),
url('%PUBLIC_URL%/fonts/Basel-Medium.woff') format('woff');
url(%PUBLIC_URL%/fonts/Basel-Medium.woff) format('woff');
}
@font-face {
@ -68,14 +67,15 @@
font-weight: 485;
font-style: normal;
font-display: block;
font-named-instance: 'Book';
src:
url('%PUBLIC_URL%/fonts/Basel-Book.woff') format('woff2'),
url('%PUBLIC_URL%/fonts/Basel-Book.woff') format('woff');
url(%PUBLIC_URL%/fonts/Basel-Book.woff) format('woff');
}
@supports (font-variation-settings: normal) {
* {
font-family: 'Basel', sans-serif;
}
}

@ -1,518 +0,0 @@
<?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>

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

@ -1,12 +0,0 @@
<?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>

File diff suppressed because it is too large Load Diff

@ -9,13 +9,19 @@ const thegraphConfig = require('../graphql.thegraph.config')
const exec = promisify(child_process.exec)
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}`)
}
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}`)
})
}
fetchSchema(process.env.THE_GRAPH_SCHEMA_ENDPOINT, thegraphConfig.schema)

@ -1,141 +0,0 @@
/* 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)
}
})

@ -52,8 +52,3 @@ 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)
}

Binary file not shown.

Before

(image error) Size: 11 KiB

Binary file not shown.

Before

(image error) Size: 9.8 KiB

Binary file not shown.

Before

(image error) Size: 1.2 KiB

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 2496 2496" style="enable-background:new 0 0 2496 2496;" xml:space="preserve">
<g>
<path style="fill-rule:evenodd;clip-rule:evenodd;fill:#F0B90B;" d="M1248,0c689.3,0,1248,558.7,1248,1248s-558.7,1248-1248,1248
S0,1937.3,0,1248S558.7,0,1248,0L1248,0z"/>
<path style="fill:#FFFFFF;" d="M685.9,1248l0.9,330l280.4,165v193.2l-444.5-260.7v-524L685.9,1248L685.9,1248z M685.9,918v192.3
l-163.3-96.6V821.4l163.3-96.6l164.1,96.6L685.9,918L685.9,918z M1084.3,821.4l163.3-96.6l164.1,96.6L1247.6,918L1084.3,821.4
L1084.3,821.4z"/>
<path style="fill:#FFFFFF;" d="M803.9,1509.6v-193.2l163.3,96.6v192.3L803.9,1509.6L803.9,1509.6z M1084.3,1812.2l163.3,96.6
l164.1-96.6v192.3l-164.1,96.6l-163.3-96.6V1812.2L1084.3,1812.2z M1645.9,821.4l163.3-96.6l164.1,96.6v192.3l-164.1,96.6V918
L1645.9,821.4L1645.9,821.4L1645.9,821.4z M1809.2,1578l0.9-330l163.3-96.6v524l-444.5,260.7v-193.2L1809.2,1578L1809.2,1578
L1809.2,1578z"/>
<polygon style="fill:#FFFFFF;" points="1692.1,1509.6 1528.8,1605.3 1528.8,1413 1692.1,1316.4 1692.1,1509.6 "/>
<path style="fill:#FFFFFF;" d="M1692.1,986.4l0.9,193.2l-281.2,165v330.8l-163.3,95.7l-163.3-95.7v-330.8l-281.2-165V986.4
L968,889.8l279.5,165.8l281.2-165.8l164.1,96.6H1692.1L1692.1,986.4z M803.9,656.5l443.7-261.6l444.5,261.6l-163.3,96.6
l-281.2-165.8L967.2,753.1L803.9,656.5L803.9,656.5z"/>
</g>
</svg>

After

(image error) Size: 1.6 KiB

Binary file not shown.

After

(image error) Size: 1.5 KiB

Binary file not shown.

After

(image error) Size: 1.5 KiB

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 22.773 22.773" style="enable-background:new 0 0 22.773 22.773;" xml:space="preserve">
<g>
<g>
<path d="M15.769,0c0.053,0,0.106,0,0.162,0c0.13,1.606-0.483,2.806-1.228,3.675c-0.731,0.863-1.732,1.7-3.351,1.573
c-0.108-1.583,0.506-2.694,1.25-3.561C13.292,0.879,14.557,0.16,15.769,0z"/>
<path d="M20.67,16.716c0,0.016,0,0.03,0,0.045c-0.455,1.378-1.104,2.559-1.896,3.655c-0.723,0.995-1.609,2.334-3.191,2.334
c-1.367,0-2.275-0.879-3.676-0.903c-1.482-0.024-2.297,0.735-3.652,0.926c-0.155,0-0.31,0-0.462,0
c-0.995-0.144-1.798-0.932-2.383-1.642c-1.725-2.098-3.058-4.808-3.306-8.276c0-0.34,0-0.679,0-1.019
c0.105-2.482,1.311-4.5,2.914-5.478c0.846-0.52,2.009-0.963,3.304-0.765c0.555,0.086,1.122,0.276,1.619,0.464
c0.471,0.181,1.06,0.502,1.618,0.485c0.378-0.011,0.754-0.208,1.135-0.347c1.116-0.403,2.21-0.865,3.652-0.648
c1.733,0.262,2.963,1.032,3.723,2.22c-1.466,0.933-2.625,2.339-2.427,4.74C17.818,14.688,19.086,15.964,20.67,16.716z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

(image error) Size: 1.6 KiB

@ -0,0 +1,9 @@
<svg width="14" height="16" viewBox="0 0 14 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.53125 5.04465V10.9554C0.53125 11.3328 0.742546 11.6817 1.08477 11.8698L6.44755 14.8258C6.78977 15.0139 7.21107 15.0139 7.55329 14.8258L12.9161 11.8698C13.2583 11.6817 13.4696 11.3328 13.4696 10.9554V5.04465C13.4696 4.66726 13.2583 4.31833 12.9161 4.13026L7.55329 1.17426C7.21107 0.986184 6.78977 0.986184 6.44755 1.17426L1.08347 4.13026C0.74125 4.31833 0.53125 4.66726 0.53125 5.04465Z" fill="#213147"/>
<path d="M8.17051 9.14643L7.40569 11.1484C7.38495 11.2041 7.38495 11.2648 7.40569 11.3204L8.72143 14.7652L10.2433 13.9263L8.4168 9.14643C8.37532 9.03631 8.21199 9.03631 8.17051 9.14643Z" fill="#12AAFF"/>
<path d="M9.70391 5.77961C9.66243 5.66949 9.4991 5.66949 9.45762 5.77961L8.6928 7.78162C8.67206 7.83731 8.67206 7.89793 8.6928 7.95361L10.8485 13.5934L12.3704 12.7545L9.70391 5.77961Z" fill="#12AAFF"/>
<path d="M7 1.39574C7.03759 1.39574 7.07519 1.40564 7.10889 1.42296L12.9124 4.62147C12.9798 4.65859 13.0213 4.72789 13.0213 4.80089V11.1967C13.0213 11.2709 12.9798 11.339 12.9124 11.3761L7.10889 14.5746C7.07648 14.5932 7.03759 14.6018 7 14.6018C6.96241 14.6018 6.92482 14.5919 6.89111 14.5746L1.08759 11.3786C1.02019 11.3415 0.978704 11.2722 0.978704 11.1992V4.80213C0.978704 4.72789 1.02019 4.65983 1.08759 4.62271L6.89111 1.4242C6.92482 1.40564 6.96241 1.39574 7 1.39574ZM7 0.461548C6.79389 0.461548 6.58648 0.512279 6.40111 0.614978L0.598889 3.81226C0.228148 4.01642 0 4.3938 0 4.80213V11.1979C0 11.6062 0.228148 11.9836 0.598889 12.1878L6.40241 15.3863C6.58778 15.4878 6.79389 15.5397 7.0013 15.5397C7.20741 15.5397 7.41482 15.489 7.60019 15.3863L13.4037 12.1878C13.7744 11.9836 14.0026 11.6062 14.0026 11.1979V4.80213C14.0026 4.3938 13.7744 4.01642 13.4037 3.81226L7.59889 0.614978C7.41352 0.512279 7.20611 0.461548 7 0.461548Z" fill="#9DCCED"/>
<path d="M3.16162 13.6008L3.6957 12.2051L4.77033 13.0576L3.7657 13.9336L3.16162 13.6008Z" fill="#213147"/>
<path d="M6.51113 4.3443H5.03983C4.92965 4.3443 4.83113 4.40988 4.79354 4.50887L1.63965 12.7619L3.1615 13.6008L6.63428 4.51258C6.66669 4.43091 6.60317 4.3443 6.51113 4.3443Z" fill="white"/>
<path d="M9.08579 4.3443H7.6145C7.50431 4.3443 7.40579 4.40988 7.3682 4.50887L3.76709 13.9324L5.28894 14.7713L9.20894 4.51258C9.24005 4.43091 9.17653 4.3443 9.08579 4.3443Z" fill="white"/>
</svg>

After

(image error) Size: 2.3 KiB

@ -0,0 +1,11 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_298_130193)">
<path d="M12.9346 2.74121H3.05566V11.7259H12.9346V2.74121Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.9998 0C15.9998 4.41538 15.9998 15.9947 15.9998 15.9947C11.5844 15.9947 0.00590861 15.9947 0.00590861 15.9947L0.00488281 5.43594e-05C4.42027 5.43594e-05 15.9998 0 15.9998 0ZM5.73493 11.1815H4.18339C3.85736 11.1815 3.69632 11.1815 3.59813 11.1187C3.49207 11.0499 3.42726 10.936 3.4194 10.8103C3.41351 10.6945 3.49404 10.553 3.65508 10.2702L7.48603 3.51765C7.64905 3.2309 7.73153 3.08753 7.83562 3.03451C7.94756 2.97755 8.08112 2.97755 8.19307 3.03451C8.29716 3.08753 8.37965 3.2309 8.54265 3.51765L9.33022 4.89243L9.33423 4.89945C9.51029 5.20707 9.59958 5.36307 9.63856 5.52679C9.68176 5.70552 9.68176 5.89406 9.63856 6.07278C9.59928 6.23775 9.5109 6.39488 9.33218 6.70715L7.31987 10.2643L7.31466 10.2734C7.13744 10.5836 7.04762 10.7408 6.92315 10.8594C6.78763 10.9891 6.62462 11.0833 6.44589 11.1364C6.28288 11.1815 6.10024 11.1815 5.73493 11.1815ZM9.65309 11.1815H11.8763C12.2043 11.1815 12.3693 11.1815 12.4675 11.1168C12.5735 11.048 12.6403 10.9321 12.6463 10.8065C12.6519 10.6944 12.5731 10.5584 12.4188 10.2921C12.4134 10.283 12.4081 10.2738 12.4027 10.2644L11.2891 8.35932L11.2764 8.33787C11.1199 8.07325 11.0409 7.93962 10.9395 7.88797C10.8276 7.831 10.6959 7.831 10.584 7.88797C10.4819 7.94099 10.3994 8.08044 10.2364 8.36128L9.12674 10.2663L9.12294 10.2729C8.9605 10.5533 8.87932 10.6934 8.88518 10.8084C8.89303 10.9341 8.95784 11.0499 9.06389 11.1187C9.16014 11.1815 9.32511 11.1815 9.65309 11.1815Z" fill="#E84142"/>
</g>
<defs>
<clipPath id="clip0_298_130193">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

(image error) Size: 1.8 KiB

@ -0,0 +1,11 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 14C0 6.26801 6.26801 0 14 0V0C21.732 0 28 6.26801 28 14V14C28 21.732 21.732 28 14 28V28C6.26801 28 0 21.732 0 14V14Z" fill="#0052FF"/>
<g clip-path="url(#clip0_13924_33076)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.3332 14.0003C23.3332 19.155 19.1472 23.3337 13.9836 23.3337C9.08459 23.3337 5.06565 19.5724 4.6665 14.7849H17.0245V13.2158H4.6665C5.06565 8.42825 9.08459 4.66699 13.9836 4.66699C19.1472 4.66699 23.3332 8.84566 23.3332 14.0003Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_13924_33076">
<rect width="18.6667" height="18.6667" fill="white" transform="translate(4.66675 4.66699)"/>
</clipPath>
</defs>
</svg>

After

(image error) Size: 745 B

@ -0,0 +1,11 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="28" height="28" rx="8" fill="#0052FF"/>
<g clip-path="url(#clip0_13921_13252)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.3332 14.0003C23.3332 19.155 19.1472 23.3337 13.9836 23.3337C9.08459 23.3337 5.06565 19.5724 4.6665 14.7849H17.0245V13.2158H4.6665C5.06565 8.42825 9.08459 4.66699 13.9836 4.66699C19.1472 4.66699 23.3332 8.84566 23.3332 14.0003Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_13921_13252">
<rect width="18.6667" height="18.6667" fill="white" transform="translate(4.66675 4.66699)"/>
</clipPath>
</defs>
</svg>

After

(image error) Size: 651 B

@ -0,0 +1,21 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0" y="0" width="16" height="16" rx="3" fill="#F0B90B"/>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 2496 2496" style="enable-background:new 0 0 2496 2496;" xml:space="preserve">
<g>
<path style="fill-rule:evenodd;clip-rule:evenodd;fill:#F0B90B;" d="M1248,0c689.3,0,1248,558.7,1248,1248s-558.7,1248-1248,1248
S0,1937.3,0,1248S558.7,0,1248,0L1248,0z"/>
<path style="fill:#FFFFFF;" d="M685.9,1248l0.9,330l280.4,165v193.2l-444.5-260.7v-524L685.9,1248L685.9,1248z M685.9,918v192.3
l-163.3-96.6V821.4l163.3-96.6l164.1,96.6L685.9,918L685.9,918z M1084.3,821.4l163.3-96.6l164.1,96.6L1247.6,918L1084.3,821.4
L1084.3,821.4z"/>
<path style="fill:#FFFFFF;" d="M803.9,1509.6v-193.2l163.3,96.6v192.3L803.9,1509.6L803.9,1509.6z M1084.3,1812.2l163.3,96.6
l164.1-96.6v192.3l-164.1,96.6l-163.3-96.6V1812.2L1084.3,1812.2z M1645.9,821.4l163.3-96.6l164.1,96.6v192.3l-164.1,96.6V918
L1645.9,821.4L1645.9,821.4L1645.9,821.4z M1809.2,1578l0.9-330l163.3-96.6v524l-444.5,260.7v-193.2L1809.2,1578L1809.2,1578
L1809.2,1578z"/>
<polygon style="fill:#FFFFFF;" points="1692.1,1509.6 1528.8,1605.3 1528.8,1413 1692.1,1316.4 1692.1,1509.6 "/>
<path style="fill:#FFFFFF;" d="M1692.1,986.4l0.9,193.2l-281.2,165v330.8l-163.3,95.7l-163.3-95.7v-330.8l-281.2-165V986.4
L968,889.8l279.5,165.8l281.2-165.8l164.1,96.6H1692.1L1692.1,986.4z M803.9,656.5l443.7-261.6l444.5,261.6l-163.3,96.6
l-281.2-165.8L967.2,753.1L803.9,656.5L803.9,656.5z"/>
</g>
</svg>
</svg>

After

(image error) Size: 1.6 KiB

@ -0,0 +1,5 @@
<svg width="250" height="250" viewBox="0 0 250 250" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="250" height="250" rx="40" fill="#FCFF52"/>
<path style="fill:black;" d="M188.9,60.7H60.7v128.2h128.2v-44.8h-21.3c-7.3,16.3-23.8,27.7-42.7,27.7c-26,0-47.1-21.3-47.1-47.1c0-25.9,21.1-47,47.1-47
c19.3,0,35.8,11.7,43.1,28.4h20.9V60.7z"/>
</svg>

After

(image error) Size: 398 B

@ -0,0 +1,16 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0" y="0" width="16" height="16" rx="3" fill="#627EEA"/>
<circle cx="8" cy="8" r="8"/>
<g clip-path="url(#clip0_12246_121533)">
<circle cx="8" cy="8" r="8" fill="url(#pattern0)"/>
</g>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_12246_121533" transform="scale(0.0078125)"/>
</pattern>
<clipPath id="clip0_12246_121533">
<rect x="0" y="0" width="16" height="16" rx="8" fill="white"/>
</clipPath>
<image id="image0_12246_121533" width="128" height="128" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAAXNSR0IArs4c6QAADd9JREFUeNrtXdtzE+cV38kweWpm+tI8JZP8EZ0pL+UhferkfyAzFqEpadq0hbRNUlsYQ2ycBEgCgQQIgVxMbC6BAHbEJZg7dW4OpeCWwDTNDOyuLFuybMsXfT2/tWVkWavd1e5K58g6M9+Q8cTS+ju//b5z/R1Nq0F5Ppr46cpm8xcroubyFU3m+oYmY39Do97d0KRfoH/76d/b9K9JP89Yy/pv62f9s/9PN34Hv4vPwGfhM7W68JNVUfWTp9fqv440Ge2RRqOXFHiPFKhCWfTZ+A58F74T313XQIUlGlUP0lv5RCRqtkQa9YukjMnQFO6w8N3WM9Cz4JnwbHUNhSQrovGlkSZ9K216vFoKdwGIOJ4Rz1rXWADyTDTxeCSqv0wbe5Or0kuA4SaeHX9DXZMepSFq/Jw28FBDo5GVpviFy8hafwv9TXXNOh3zzfov6Qjtka90u1NB78HfWNd0gUTWmr+yLPgaVfwCIMCToL+5/sZH44+Qa9W5WBRfxK3sxB4sRlduSSRqrKYjMbVolX//WkhhL7Ani+O4j+rL6Ai8ttgVX+RauIa9qfG3Xm+tDcs+RI+B9qjmToNVLfFHKWJ2vq5gt6eBfr5mbIPIWuNJK9lSV6zXZWLvJB/5D1AApK1+5PsOIrVhL8UlbMiy7agrMKArgfZSTKKpoc14iHLpMUkb/OZHQ6pxa5x5zID2lPaWefIm9TC5M32SlP/MOl0Zg1PqWG9aUY6fu6vY99yG1M94GnsticfozhqQdryeOJ9W09NZ9cU/Rq2TQIBdMIC9ZvfmS1T+39+Kq6mp7BwATl8dVX/caIoAAfaczZ0v7djPrYE7EwqSAwDW/u6UlMhhX9VtAlim0gy+3Np9KKlykg8ArPXvDApJJhmxqnkHlp8v1NV77hVDJUembQHQfSFtGYeCXMTKxwlmgjwy/erevlGVL4UAwHqna1hS+VlbFcK7MiN8698dVNls1hEAWC+8bsqJGFYqbIzEjtTY/oqorn64O6kKxQ4AB0+NIF8vJncA3YSf0hWc1YOFX0zsAIDVvichKosYaip5Jp8vU/l/ftVUY+NZzwCIXRpVz24wBOUN9NbQKnkkZ/a+vD6u7KQUALDeP5KUVlSyLISjX24Z16Z9Q6qUOAEA6+U344KuAuNaoFcBoWqNVOX/pllXOiV7/ALg6NkR9XRUVC3BmsBKtyVX70JxTuIGAFhvfDgkKDagpwIpKyPLskuq8l98I64mJ7OBAeD0lVH1fJshySvo8t2xI7mS5vr3GeVG3AIA6+MTKVF74KsDSXK7FkK5bsULALCatw9KMgh7y27UlKp8+O1DqenQAIAiEhiXYiKg5TSkSu7SxV3tRbwCAGt7p6Rkkd7jze2jXnapysfxDIWGDQCs1a+ZcvbGCz/BDDmDyEZLdefHCeVVygXAgZicZBF06rK+L/G41JDvh8eSqhwpFwBYbbsTYkLEruhqwGsjUfko5hwdm644AGKX0mrVehmxAejWTdh3QCIArvSPqXLFDwCwdh9OSrkGbjqFfZdKVP6rlLP3I34BgPXiFhnJopIUduC4k6b8ldTNc9ecrDoAjpwZsSqOBBjKW+0bOhmTMNqtw6dHlF8JAgBYSDsLuAbiRUvJQXUqTfl/3WyqiYksGwCcogDU71sNAdeA+UQR699skQaA7/6dUUFIUADAgivK3xswW4qlfS9KUv62/cMqKAkSAFjRbbyTRdD1/FJvoj2vJuu217WqxVCDw1NsAXD8XNoyThnbAZPzqO7BfS/p7f/8YloFKUEDAGtrB2+DEDrPj/23S1F+07a452RPNQBwhlrNUYrO+BRoF1n4ceuHiUCVjzax67cy6pOeVOAg6Pw8xdgOyCsUCXXMSoAL9flBSYbcR/QKwHWDsta+PahadyXU3qNJq54gKBBs2MnUICSdW8rHMCQJyod/PTI67VvxQ8lpdfGbsQVKBgByC/wAOw8Mqx6yNfwCAPbKb1t47qk1CAsTsSQA4MLXY74U/6M+qc5SW/gZG0XlAyC31lFxyVtkzKG03A8Idh7kWT0E3dP9bz7FXfk4msuVgTsZetud3+RiAMhfm/YlrDu9XBAgaskwIrgc+f8NrN0VSrDg7fUiE9QL8PUNut8vuz/CnQCQWxupAOSDz5IWoZQXACBnwa16CLrXZocqsgVAVyzlWvGgfbn07ZirN75cAMzZCUQ0sYuO9pgHO+G195lVD5HuWVf/riGGjvGMs89/15hUvV/a3+9hACC3mncMqm1kJxxzYSecujxq8ROxqhbmnAP45sZ4ScX/578ZdSYgl61cAOSvzWQnHIiVthP2HU3yygk0YF4uU/7eYjJJxI7f3oT/ng40aBMEAHKr/b2ElRW0sxMY8RL3a7NDk9nx95qJ+ckexACufDcWaJAmLADk1gayE3YfGlYnL80HKxteYtK9xpHwCdm0nNyLT6lzX83E1sNQfJgAmIsnUGBp+/4hdbz3PhCY8BKb2uz4dHb8vbf/NxG60isFgDmDkQJLW4hj4OCpFA9eYtI9OwAcOzcyF5+v5KoEAObZCXvIaPxgiAEAmF0BSKGCzq3WAfA3KiNn0GFssjQCc02ex3rTNQcAXHG/40I7N2sE9nMOA2+hYxIBFOkAQJ0g7nxm4WDLDbwggd17z6dJsQAA5zDLcTSke5wA3XL6AOIWf68UALxEJFVcawFmVze7ZJBT2hRH6Cs7E6rnAt9IICJ9bhpE/lBtxjErGcQsHbyRwqiXKaPnxL6BaCF6A05f5QOAKC14MU73fO5KA41dVesBmsz1aAlbzu1ogvWPmj3k0J1cJRhWHx1PVR0AuJ5WNjuWYltkk4hzcIgEWgUhHEvCYP3nqn+RE8Cb7vQ7OHaPfjFScQDArXvWBUEE3FqEuPFdqHHg4A1YJWFci0L/sokYP8bvF4HeuJ2xegKcBkKg6OLk5fABALfODWsorrL8knPQz3NpILWKQjmXhReSPaKBA/kBpw3EG7mLsnBhAQCFKk7E0Whfe/fAwmdYt2OQiwt4T0RjCEq4CyVNqWHc+06EDPC/u2KpwAAAo81pmhieCYwlsUsLvRSMqmPZGMK5NQxvkh6fsi31dlNnhzp/sHuWCwDYF25KuWAPgCnEjm5+JSN20XmtYdybQ3FsIkVsJ6gAdoofYPNhedsVlNi5dX9qd3brYAugAsiWZfwqvwlk85pDJbSHoya/lIAWHlY2TgynDiPU5TkBAEaoU4s3XFR0ATvFIl7fy6saeEF7uASCCLyFbujf0fq1y0Unzks0AubTvON6LnxLP3cCERbKvbpdRCM7GNLLLyCIkEIRg+M4lXbXH/g9VRS1OMwABqgQeUT/HlxMN6FZXDUHTrqLNyBczXHiWFGKGCkkUYikeWn9Rk8hgOMUVnbyKJDDf++wt4wko+pfZ5IoSTRxqA/wIpgXCHewHNoW+Pub93mvSeBKJ29LEyeJKBIWfbERsE4Cd9JLDB5eQDlVSchhcJ0wZksUKY0qFj53uRyB/yRGkFJzAJHR6yizLhGJHqcrhy1V7GxQ6KYUEMCd88MLhJxBfn0eijd2dA77yiqilV0sWbREuviv/jXuizgCXgVoYXLegB/lc+r7K5suXtrACIRo/XIGBsEShiCUU55AxMAIiSNjQNrghzpuMdDFux4ZI3Vo1Gdn01UDAJM+v9LLy9AoiWPjVkTL5xD0AwDkKLgPjvI8Nk7q4MgXCqqIwgYAcv4i6OHLGRwpdXSsl5GxfgHAprrHbeHHYhke7ZVTsBwAoORMwl74Gh49Wy/YKQ0ASOfei0+FBgBUH6+UMDuYdKf5FQodPkJGREoaCHA8T05lAwcAij/WCBgZC51Bd1oQEokaqyVeBU5VROUAgB3Xn23Uz1itBSWUPlxCxsQ1aQCwqohuZQIDwMcnUjL+btIVdKYFKRRHXiZxpjAyc2AQ9QsAq7pHxIhYIwtdaWEIfXCrxKsAJBN+AYD0s4yjX2/VwpKZq0A/LxEEpaqInADw9idCXD7STeBHf6Gsaok/ypFb0E8VUSkAHDrFt7qnkPAJutEqIZG1xpMS7QFUAWWKVBHZAQCnRtW5/Nze+6QTrZJC6cU2iVfB3iJVRHYAABOJDG/HaNMqLXTXPEC+ZodEEGBYlBMA9h5JSvH3O6ALrRqC8mLimYlJA0BhFVEhAPhX98xx/MRsS7wrJQ1txkMUeOiTBoK2vCqiQgCA7kVAsKcPe69xkGeiqYfJEBmQBgK0bBcCAJ1HAoy+Aey5xkkiLYnHpIEgV0WUA4CE6h7sMfZa4yhApbTrAH37YBxBdQ+neT52xz67N7+YTSDNMEQfH1i8uBt8bO58N96BVBeRsav3oCZJrDiBFSySFzFkFeGjPayanx9c2Fhe7oBDbL/i4d0wE0hSs4jVyupVLLFTwSthyUw9Qf1KcCjmaA09pVvVK4GqVSSWl1WijCu0Sh6OpwGhfY3EauMwqnexFzX91pcsOW/UuxbxXd8VWOm26GuBulcktqH5adfy3bFTkycCNTFK60r22qVbdqPmYhL0ss+QVNSCx2AFcw557s+vywxdzQxnkbxUs5W1o2d3TctSF0eDcSk47jiTWeLZ8IyOVGx18ZdoAtUp+G5BelxNlnN8t/UM9Cx4JnEJm5oIMxPtObjvMQDB8iTCHH9Dn21Z8PRd+M4FlOt14SEYhoSJWJFG8ylrLiINSIQFPkuJ3z87MNvE+HRrWf9t/ax/5lQhTwS/Q7+Lz8BnzQ1YqjH5P29N0rBVv2N5AAAAAElFTkSuQmCC"/>
</defs>
</svg>

After

(image error) Size: 5.4 KiB

@ -1,3 +0,0 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.6376 8.86202C11.8982 9.12269 11.8982 9.54407 11.6376 9.80473L8.97089 12.4714C8.84089 12.6014 8.6702 12.6667 8.49954 12.6667C8.32887 12.6667 8.15818 12.6014 8.02818 12.4714L5.36152 9.80473C5.10085 9.54407 5.10085 9.12269 5.36152 8.86202C5.62218 8.60136 6.04356 8.60136 6.30422 8.86202L8.49954 11.0573L10.6948 8.86202C10.9555 8.60136 11.3769 8.60136 11.6376 8.86202ZM6.30422 7.13807L8.49954 4.94275L10.6948 7.13807C10.8248 7.26807 10.9955 7.33338 11.1662 7.33338C11.3369 7.33338 11.5076 7.26807 11.6376 7.13807C11.8982 6.8774 11.8982 6.45602 11.6376 6.19536L8.97089 3.52869C8.71022 3.26802 8.28885 3.26802 8.02818 3.52869L5.36152 6.19536C5.10085 6.45602 5.10085 6.8774 5.36152 7.13807C5.62218 7.39873 6.04356 7.39873 6.30422 7.13807Z" fill="#5E5E5E"/>
</svg>

Before

(image error) Size: 866 B

@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.1376 11.5287C11.3982 11.7894 11.3982 12.2107 11.1376 12.4714C11.0076 12.6014 10.8369 12.6667 10.6662 12.6667C10.4955 12.6667 10.3248 12.6014 10.1948 12.4714L7.99954 10.2761L5.80422 12.4714C5.54356 12.7321 5.12218 12.7321 4.86152 12.4714C4.60085 12.2107 4.60085 11.7894 4.86152 11.5287L7.52818 8.86202C7.78885 8.60136 8.21022 8.60136 8.47089 8.86202L11.1376 11.5287ZM7.52818 7.13807C7.65818 7.26807 7.82887 7.33338 7.99954 7.33338C8.1702 7.33338 8.34089 7.26807 8.47089 7.13807L11.1376 4.4714C11.3982 4.21073 11.3982 3.78936 11.1376 3.52869C10.8769 3.26802 10.4555 3.26802 10.1948 3.52869L7.99954 5.724L5.80422 3.52869C5.54356 3.26802 5.12218 3.26802 4.86152 3.52869C4.60085 3.78936 4.60085 4.21073 4.86152 4.4714L7.52818 7.13807Z" fill="#5E5E5E"/>
</svg>

Before

(image error) Size: 864 B

@ -1,15 +1,4 @@
<svg width="490" height="490" viewBox="0 0 490 490" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_7383_35741)">
<circle cx="245" cy="245" r="245" fill="url(#paint0_linear_7383_35741)"/>
<path d="M315.83 297.85L385.12 257.84C388.79 255.72 391.06 251.78 391.06 247.54V167.53C391.06 163.3 388.78 159.35 385.12 157.23L315.83 117.22C312.16 115.1 307.61 115.11 303.94 117.22L234.65 157.23C230.98 159.35 228.71 163.3 228.71 167.53V310.52L180.12 338.57L131.53 310.52V254.41L180.12 226.36L212.17 244.86V207.22L186.06 192.15C184.26 191.11 182.2 190.56 180.11 190.56C178.02 190.56 175.96 191.11 174.17 192.15L104.88 232.16C101.21 234.28 98.9404 238.22 98.9404 242.46V322.47C98.9404 326.7 101.22 330.65 104.88 332.77L174.17 372.78C177.83 374.89 182.39 374.89 186.06 372.78L255.35 332.78C259.02 330.66 261.29 326.71 261.29 322.48V179.49L262.17 178.99L309.88 151.44L358.47 179.49V235.6L309.88 263.65L277.88 245.17V282.81L303.94 297.86C307.61 299.97 312.16 299.97 315.83 297.86V297.85Z" fill="white"/>
</g>
<defs>
<linearGradient id="paint0_linear_7383_35741" x1="-175" y1="4.36391e-07" x2="416" y2="367" gradientUnits="userSpaceOnUse">
<stop stop-color="#A229C5"/>
<stop offset="1" stop-color="#7B3FE4"/>
</linearGradient>
<clipPath id="clip0_7383_35741">
<rect width="490" height="490" fill="white"/>
</clipPath>
</defs>
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="512" cy="512" r="512" fill="#8247E5"/>
<path d="M681.469 402.456C669.189 395.312 653.224 395.312 639.716 402.456L543.928 457.228L478.842 492.949L383.055 547.721C370.774 554.865 354.81 554.865 341.301 547.721L265.162 504.856C252.882 497.712 244.286 484.614 244.286 470.325V385.786C244.286 371.498 251.654 358.4 265.162 351.256L340.073 309.581C352.353 302.437 368.318 302.437 381.827 309.581L456.737 351.256C469.018 358.4 477.614 371.498 477.614 385.786V440.558L542.7 403.646V348.874C542.7 334.586 535.332 321.488 521.824 314.344L383.055 235.758C370.774 228.614 354.81 228.614 341.301 235.758L200.076 314.344C186.567 321.488 179.199 334.586 179.199 348.874V507.237C179.199 521.525 186.567 534.623 200.076 541.767L341.301 620.353C353.582 627.498 369.546 627.498 383.055 620.353L478.842 566.772L543.928 529.86L639.716 476.279C651.996 469.135 667.961 469.135 681.469 476.279L756.38 517.953C768.66 525.098 777.257 538.195 777.257 552.484V637.023C777.257 651.312 769.888 664.409 756.38 671.553L681.469 714.419C669.189 721.563 653.224 721.563 639.716 714.419L564.805 672.744C552.525 665.6 543.928 652.502 543.928 638.214V583.442L478.842 620.353V675.125C478.842 689.414 486.21 702.512 499.719 709.656L640.944 788.242C653.224 795.386 669.189 795.386 682.697 788.242L823.922 709.656C836.203 702.512 844.799 689.414 844.799 675.125V516.763C844.799 502.474 837.431 489.377 823.922 482.232L681.469 402.456Z" fill="white"/>
</svg>

Before

(image error) Size: 1.3 KiB

After

(image error) Size: 1.5 KiB

File diff suppressed because one or more lines are too long

After

(image error) Size: 32 KiB

@ -0,0 +1,5 @@
<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="250" cy="250" r="250" fill="#FF0420"/>
<path d="M177.133 316.446C162.247 316.446 150.051 312.943 140.544 305.938C131.162 298.808 126.471 288.676 126.471 275.541C126.471 272.789 126.784 269.411 127.409 265.408C129.036 256.402 131.35 245.581 134.352 232.947C142.858 198.547 164.812 181.347 200.213 181.347C209.845 181.347 218.476 182.973 226.107 186.225C233.738 189.352 239.742 194.106 244.12 200.486C248.498 206.74 250.688 214.246 250.688 223.002C250.688 225.629 250.375 228.944 249.749 232.947C247.873 244.08 245.621 254.901 242.994 265.408C238.616 282.546 231.048 295.368 220.29 303.874C209.532 312.255 195.147 316.446 177.133 316.446ZM179.76 289.426C186.766 289.426 192.707 287.362 197.586 283.234C202.59 279.106 206.155 272.789 208.281 264.283C211.158 252.524 213.348 242.266 214.849 233.51C215.349 230.883 215.599 228.194 215.599 225.441C215.599 214.058 209.657 208.366 197.774 208.366C190.768 208.366 184.764 210.43 179.76 214.558C174.882 218.687 171.379 225.004 169.253 233.51C167.001 241.891 164.749 252.149 162.498 264.283C161.997 266.784 161.747 269.411 161.747 272.163C161.747 283.672 167.752 289.426 179.76 289.426Z" fill="white"/>
<path d="M259.303 314.57C257.927 314.57 256.863 314.132 256.113 313.256C255.487 312.255 255.3 311.13 255.55 309.879L281.444 187.914C281.694 186.538 282.382 185.412 283.508 184.536C284.634 183.661 285.822 183.223 287.073 183.223H336.985C350.87 183.223 362.003 186.1 370.384 191.854C378.891 197.609 383.144 205.927 383.144 216.81C383.144 219.937 382.769 223.19 382.018 226.567C378.891 240.953 372.574 251.586 363.067 258.466C353.685 265.346 340.8 268.786 324.413 268.786H299.082L290.451 309.879C290.2 311.255 289.512 312.38 288.387 313.256C287.261 314.132 286.072 314.57 284.822 314.57H259.303ZM325.727 242.892C330.98 242.892 335.546 241.453 339.424 238.576C343.427 235.699 346.054 231.571 347.305 226.192C347.68 224.065 347.868 222.189 347.868 220.563C347.868 216.935 346.805 214.183 344.678 212.307C342.551 210.305 338.924 209.305 333.795 209.305H311.278L304.148 242.892H325.727Z" fill="white"/>
</svg>

After

(image error) Size: 2.1 KiB

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 38.4 33.5" style="enable-background:new 0 0 38.4 33.5;" xml:space="preserve">
<style type="text/css">
.st0{fill:#8247E5;}
</style>
<g>
<path class="st0" d="M29,10.2c-0.7-0.4-1.6-0.4-2.4,0L21,13.5l-3.8,2.1l-5.5,3.3c-0.7,0.4-1.6,0.4-2.4,0L5,16.3
c-0.7-0.4-1.2-1.2-1.2-2.1v-5c0-0.8,0.4-1.6,1.2-2.1l4.3-2.5c0.7-0.4,1.6-0.4,2.4,0L16,7.2c0.7,0.4,1.2,1.2,1.2,2.1v3.3l3.8-2.2V7
c0-0.8-0.4-1.6-1.2-2.1l-8-4.7c-0.7-0.4-1.6-0.4-2.4,0L1.2,5C0.4,5.4,0,6.2,0,7v9.4c0,0.8,0.4,1.6,1.2,2.1l8.1,4.7
c0.7,0.4,1.6,0.4,2.4,0l5.5-3.2l3.8-2.2l5.5-3.2c0.7-0.4,1.6-0.4,2.4,0l4.3,2.5c0.7,0.4,1.2,1.2,1.2,2.1v5c0,0.8-0.4,1.6-1.2,2.1
L29,28.8c-0.7,0.4-1.6,0.4-2.4,0l-4.3-2.5c-0.7-0.4-1.2-1.2-1.2-2.1V21l-3.8,2.2v3.3c0,0.8,0.4,1.6,1.2,2.1l8.1,4.7
c0.7,0.4,1.6,0.4,2.4,0l8.1-4.7c0.7-0.4,1.2-1.2,1.2-2.1V17c0-0.8-0.4-1.6-1.2-2.1L29,10.2z"/>
</g>
</svg>

After

(image error) Size: 1.1 KiB

File diff suppressed because one or more lines are too long

After

(image error) Size: 26 KiB

@ -1,85 +0,0 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2450_35919)">
<rect width="20" height="20" rx="4.60595" fill="#FFFBFF"/>
<rect width="20" height="20" rx="4.60595" fill="url(#paint0_linear_2450_35919)"/>
<g filter="url(#filter0_f_2450_35919)">
<ellipse cx="9.99967" cy="12.0001" rx="9.66667" ry="7.66667" fill="url(#paint1_radial_2450_35919)" fill-opacity="0.8"/>
</g>
<g filter="url(#filter1_i_2450_35919)">
<path d="M14.4974 4.34199C14.5183 3.97475 14.5683 3.73252 14.6689 3.51131C14.7087 3.42374 14.746 3.35208 14.7518 3.35208C14.7575 3.35208 14.7402 3.41671 14.7133 3.49569C14.6402 3.71038 14.6282 4.00402 14.6786 4.34566C14.7425 4.77915 14.7788 4.84169 15.239 5.30995C15.4549 5.52958 15.706 5.80659 15.797 5.92551L15.9625 6.14176L15.797 5.98722C15.5946 5.79823 15.1292 5.42966 15.0263 5.37698C14.9574 5.34164 14.9472 5.34225 14.9046 5.38439C14.8655 5.42322 14.8572 5.48156 14.8518 5.75737C14.8433 6.18723 14.7844 6.46314 14.6424 6.73903C14.5656 6.88824 14.5535 6.8564 14.623 6.68797C14.6749 6.56222 14.6802 6.50694 14.6798 6.0908C14.679 5.25469 14.5793 5.05368 13.9946 4.70934C13.8465 4.62211 13.6024 4.4963 13.4522 4.42976C13.3021 4.36322 13.1828 4.30526 13.1871 4.30093C13.2037 4.28452 13.7739 4.45022 14.0034 4.53811C14.3448 4.66886 14.4012 4.6858 14.4426 4.67003C14.4704 4.65946 14.4839 4.57887 14.4974 4.34199Z" fill="url(#paint2_linear_2450_35919)"/>
<path d="M7.28422 3.12561C7.0817 3.09439 7.07316 3.09073 7.16846 3.07618C7.3511 3.04826 7.78237 3.0863 8.07956 3.15653C8.77336 3.32041 9.40468 3.74022 10.0786 4.48582L10.2576 4.68389L10.5137 4.64298C11.5927 4.47068 12.6903 4.60761 13.6084 5.02906C13.8609 5.14501 14.2592 5.3758 14.3089 5.43511C14.3248 5.45402 14.3539 5.57569 14.3736 5.70552C14.4418 6.1547 14.4076 6.499 14.2694 6.75616C14.1941 6.8961 14.19 6.94045 14.2405 7.06021C14.2809 7.15577 14.3935 7.22651 14.5049 7.22636C14.7331 7.22605 14.9787 6.85956 15.0924 6.34963L15.1376 6.14707L15.2272 6.24785C15.7183 6.80076 16.104 7.5548 16.1703 8.09153L16.1875 8.23147L16.105 8.10426C15.9629 7.88534 15.8202 7.73632 15.6374 7.61612C15.3079 7.39947 14.9595 7.32574 14.0368 7.27742C13.2034 7.23379 12.7318 7.16305 12.2641 7.01151C11.4684 6.75372 11.0673 6.41038 10.1221 5.17811C9.70233 4.63077 9.44285 4.32794 9.18474 4.08407C8.59823 3.52993 8.02193 3.23932 7.28422 3.12561Z" fill="url(#paint3_linear_2450_35919)"/>
<path d="M7.67762 5.77618C7.26647 5.2137 7.01209 4.3513 7.06716 3.70661L7.08419 3.50711L7.17778 3.52408C7.35352 3.55594 7.65655 3.66801 7.79844 3.75366C8.18783 3.98865 8.3564 4.29805 8.5279 5.09252C8.57814 5.32522 8.64404 5.58856 8.67437 5.67772C8.7232 5.82122 8.90769 6.15641 9.05769 6.3741C9.16573 6.53088 9.09396 6.60518 8.85514 6.58376C8.49039 6.55104 7.99634 6.21221 7.67762 5.77618Z" fill="url(#paint4_linear_2450_35919)"/>
<path d="M13.9982 9.96242C12.0768 9.19367 11.4001 8.52639 11.4001 7.40049C11.4001 7.2348 11.4058 7.09924 11.4128 7.09924C11.4198 7.09924 11.4941 7.15392 11.578 7.22076C11.9676 7.5313 12.4039 7.66393 13.6118 7.83903C14.3225 7.94208 14.7225 8.0253 15.0915 8.1469C16.2642 8.53338 16.9898 9.3177 17.1628 10.386C17.2131 10.6964 17.1836 11.2785 17.1021 11.5853C17.0377 11.8276 16.8413 12.2644 16.7892 12.2812C16.7748 12.2858 16.7606 12.2308 16.7569 12.156C16.7372 11.7549 16.5332 11.3643 16.1906 11.0718C15.8011 10.7392 15.2777 10.4744 13.9982 9.96242Z" fill="url(#paint5_linear_2450_35919)"/>
<path d="M12.6493 10.2818C12.6252 10.1394 12.5835 9.95763 12.5565 9.87779L12.5075 9.73264L12.5985 9.83421C12.7246 9.97475 12.8241 10.1546 12.9085 10.3942C12.973 10.577 12.9802 10.6314 12.9797 10.9285C12.9792 11.2202 12.9712 11.2813 12.9117 11.4459C12.8179 11.7054 12.7015 11.8894 12.5062 12.0869C12.1552 12.4419 11.704 12.6384 11.0528 12.7199C10.9396 12.734 10.6097 12.7579 10.3197 12.7729C9.58873 12.8107 9.10767 12.8887 8.6754 13.0395C8.61325 13.0612 8.55776 13.0744 8.55213 13.0688C8.53464 13.0515 8.82895 12.8772 9.07204 12.7608C9.41481 12.5968 9.75602 12.5072 10.5205 12.3807C10.8982 12.3182 11.2882 12.2424 11.3872 12.2122C12.3224 11.9273 12.8031 11.1919 12.6493 10.2818Z" fill="url(#paint6_linear_2450_35919)"/>
<path d="M13.5301 11.8362C13.2748 11.2909 13.2162 10.7643 13.356 10.2733C13.371 10.2208 13.3951 10.1779 13.4095 10.1779C13.424 10.1779 13.4842 10.2103 13.5434 10.2498C13.661 10.3285 13.897 10.461 14.5255 10.8016C15.3099 11.2265 15.7571 11.5556 16.0612 11.9316C16.3276 12.2608 16.4924 12.6358 16.5717 13.0931C16.6166 13.3521 16.5903 13.9753 16.5234 14.2361C16.3125 15.0585 15.8224 15.7045 15.1233 16.0814C15.0209 16.1366 14.9289 16.182 14.919 16.1822C14.909 16.1824 14.9464 16.0881 15.0019 15.9726C15.2371 15.4841 15.2639 15.0089 15.0861 14.48C14.9772 14.1562 14.7552 13.761 14.3071 13.0932C13.786 12.3167 13.6583 12.11 13.5301 11.8362Z" fill="url(#paint7_linear_2450_35919)"/>
<path d="M6.31312 14.7786C7.02613 14.1803 7.9133 13.7553 8.72142 13.6248C9.0697 13.5686 9.64988 13.5909 9.97238 13.673C10.4893 13.8044 10.9518 14.0989 11.1923 14.4497C11.4273 14.7926 11.5281 15.0914 11.6331 15.7561C11.6745 16.0184 11.7196 16.2817 11.7332 16.3413C11.8121 16.6859 11.9655 16.9613 12.1556 17.0996C12.4576 17.3192 12.9776 17.3329 13.4891 17.1346C13.5759 17.1009 13.6513 17.0777 13.6566 17.0829C13.6751 17.1012 13.4175 17.2726 13.2358 17.3628C12.9913 17.4841 12.7969 17.531 12.5386 17.531C12.0701 17.531 11.6811 17.2942 11.3566 16.8113C11.2927 16.7163 11.1491 16.4316 11.0376 16.1788C10.6949 15.4021 10.5257 15.1655 10.1278 14.9066C9.78156 14.6813 9.33501 14.6409 8.99908 14.8046C8.55782 15.0196 8.4347 15.58 8.75075 15.9351C8.87636 16.0762 9.1106 16.198 9.30214 16.2216C9.66047 16.2659 9.96842 15.995 9.96842 15.6355C9.96842 15.4021 9.87813 15.2689 9.65083 15.1669C9.34039 15.0277 9.0067 15.1905 9.0083 15.4803C9.00899 15.6038 9.06314 15.6814 9.1878 15.7375C9.26777 15.7734 9.26963 15.7763 9.20442 15.7628C8.91957 15.7041 8.85285 15.3629 9.08187 15.1364C9.35683 14.8644 9.92541 14.9844 10.1207 15.3556C10.2027 15.5116 10.2122 15.8221 10.1407 16.0096C9.98064 16.4293 9.51395 16.65 9.04053 16.5299C8.71821 16.4481 8.58696 16.3596 8.19835 15.9618C7.52307 15.2706 7.26092 15.1366 6.28739 14.9856L6.10084 14.9567L6.31312 14.7786Z" fill="url(#paint8_linear_2450_35919)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.14462 2.40696C5.39974 5.12681 6.95295 6.24897 7.12556 6.48605C7.26807 6.68181 7.21443 6.85781 6.97029 6.99574C6.83452 7.07242 6.55538 7.15013 6.41563 7.15013C6.25755 7.15013 6.20328 7.08953 6.20328 7.08953C6.11163 7.00315 6.06001 7.01826 5.58934 6.18804C4.9359 5.18048 4.38905 4.34467 4.37413 4.33067C4.33964 4.29831 4.34023 4.2994 5.52271 6.40187C5.71377 6.84008 5.56071 7.00093 5.56071 7.06334C5.56071 7.1903 5.52586 7.25704 5.36825 7.43173C5.1055 7.72301 4.98805 8.05029 4.90326 8.72762C4.80821 9.4869 4.54094 10.0233 3.80024 10.9412C3.36666 11.4785 3.29572 11.577 3.18631 11.7936C3.04851 12.0663 3.01062 12.2191 2.99526 12.5635C2.97903 12.9276 3.01064 13.1628 3.12259 13.511C3.2206 13.8158 3.32291 14.017 3.58444 14.4196C3.81014 14.767 3.9401 15.0251 3.9401 15.1261C3.9401 15.2064 3.95554 15.2065 4.30528 15.1281C5.14226 14.9402 5.82189 14.6099 6.20412 14.2051C6.44068 13.9545 6.49622 13.8162 6.49802 13.4728C6.4992 13.2482 6.49125 13.2012 6.43016 13.072C6.33071 12.8617 6.14967 12.6869 5.75064 12.4158C5.2278 12.0606 5.00448 11.7747 4.9428 11.3815C4.8922 11.0588 4.9509 10.8312 5.24014 10.2288C5.53952 9.60524 5.61371 9.33953 5.66389 8.71103C5.69631 8.30498 5.74119 8.14484 5.8586 8.0163C5.98104 7.88226 6.09127 7.83687 6.3943 7.79573C6.88833 7.72866 7.20291 7.60165 7.46149 7.36487C7.6858 7.15946 7.77966 6.96154 7.79407 6.6636L7.805 6.43776L7.67965 6.29253C7.2257 5.76656 2.84061 1.9935 2.81267 1.9935C2.8067 1.9935 2.95609 2.17957 3.14462 2.40696ZM4.19493 12.9755C4.29756 12.7949 4.24303 12.5627 4.07135 12.4492C3.90913 12.342 3.65714 12.3925 3.65714 12.5322C3.65714 12.5748 3.68086 12.6058 3.73431 12.6332C3.82432 12.6792 3.83085 12.731 3.76003 12.8367C3.68832 12.9439 3.6941 13.038 3.77637 13.102C3.90895 13.2052 4.09663 13.1485 4.19493 12.9755Z" fill="url(#paint9_linear_2450_35919)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.11678 7.91419C7.88485 7.98498 7.6594 8.22923 7.58962 8.48535C7.54704 8.6416 7.5712 8.91567 7.63497 9.00032C7.73799 9.13703 7.83762 9.17306 8.10739 9.17117C8.63556 9.16751 9.0947 8.94241 9.14808 8.66101C9.19184 8.43034 8.99019 8.11067 8.71242 7.97032C8.56908 7.89793 8.26425 7.86921 8.11678 7.91419ZM8.7342 8.39395C8.81565 8.27895 8.78002 8.15466 8.64149 8.07059C8.3777 7.91053 7.97877 8.04299 7.97877 8.29063C7.97877 8.4139 8.1868 8.5484 8.37749 8.5484C8.50441 8.5484 8.6781 8.47319 8.7342 8.39395Z" fill="url(#paint10_linear_2450_35919)"/>
</g>
</g>
<rect x="0.0205078" y="0.0205078" width="19.959" height="19.959" rx="4.58544" stroke="black" stroke-opacity="0.1" stroke-width="0.0410156"/>
<defs>
<filter id="filter0_f_2450_35919" x="-3.16699" y="0.833389" width="26.333" height="22.3333" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="1.75" result="effect1_foregroundBlur_2450_35919"/>
</filter>
<filter id="filter1_i_2450_35919" x="2.8125" y="1.9935" width="14.375" height="15.5376" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="0.47168"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.864916 0 0 0 0 0.401042 0 0 0 0 0.875 0 0 0 1 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_2450_35919"/>
</filter>
<linearGradient id="paint0_linear_2450_35919" x1="7.66667" y1="20" x2="7.66667" y2="0" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFF1FF"/>
<stop offset="1" stop-color="white" stop-opacity="0"/>
</linearGradient>
<radialGradient id="paint1_radial_2450_35919" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(10.0123 11.9901) rotate(35.9783) scale(9.71138 9.26994)">
<stop stop-color="white"/>
<stop offset="1" stop-color="#FFE3FF"/>
</radialGradient>
<linearGradient id="paint2_linear_2450_35919" x1="16.4648" y1="15.099" x2="7.33333" y2="7.33334" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC72FF"/>
<stop offset="1" stop-color="#FF41F4"/>
</linearGradient>
<linearGradient id="paint3_linear_2450_35919" x1="16.4648" y1="15.099" x2="7.33333" y2="7.33334" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC72FF"/>
<stop offset="1" stop-color="#FF41F4"/>
</linearGradient>
<linearGradient id="paint4_linear_2450_35919" x1="16.4648" y1="15.099" x2="7.33333" y2="7.33334" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC72FF"/>
<stop offset="1" stop-color="#FF41F4"/>
</linearGradient>
<linearGradient id="paint5_linear_2450_35919" x1="16.4648" y1="15.099" x2="7.33333" y2="7.33334" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC72FF"/>
<stop offset="1" stop-color="#FF41F4"/>
</linearGradient>
<linearGradient id="paint6_linear_2450_35919" x1="16.4648" y1="15.099" x2="7.33333" y2="7.33334" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC72FF"/>
<stop offset="1" stop-color="#FF41F4"/>
</linearGradient>
<linearGradient id="paint7_linear_2450_35919" x1="16.4648" y1="15.099" x2="7.33333" y2="7.33334" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC72FF"/>
<stop offset="1" stop-color="#FF41F4"/>
</linearGradient>
<linearGradient id="paint8_linear_2450_35919" x1="16.4648" y1="15.099" x2="7.33333" y2="7.33334" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC72FF"/>
<stop offset="1" stop-color="#FF41F4"/>
</linearGradient>
<linearGradient id="paint9_linear_2450_35919" x1="16.4648" y1="15.099" x2="7.33333" y2="7.33334" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC72FF"/>
<stop offset="1" stop-color="#FF41F4"/>
</linearGradient>
<linearGradient id="paint10_linear_2450_35919" x1="16.4648" y1="15.099" x2="7.33333" y2="7.33334" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC72FF"/>
<stop offset="1" stop-color="#FF41F4"/>
</linearGradient>
<clipPath id="clip0_2450_35919">
<rect width="20" height="20" rx="4.60595" fill="white"/>
</clipPath>
</defs>
</svg>

Before

(image error) Size: 12 KiB

@ -9,7 +9,7 @@ import { Power } from 'components/Icons/Power'
import { Settings } from 'components/Icons/Settings'
import { AutoRow } from 'components/Row'
import { LoadingBubble } from 'components/Tokens/loading'
import { DeltaArrow } from 'components/Tokens/TokenDetails/Delta'
import { DeltaArrow, formatDelta } from 'components/Tokens/TokenDetails/Delta'
import Tooltip from 'components/Tooltip'
import { getConnection } from 'connection'
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
@ -25,7 +25,6 @@ import { updateSelectedWallet } from 'state/user/reducer'
import styled from 'styled-components'
import { CopyHelper, ExternalLink, ThemedText } from 'theme/components'
import { shortenAddress } from 'utils'
import { isPathBlocked } from 'utils/blockedPaths'
import { NumberType, useFormatter } from 'utils/formatNumbers'
import { useCloseModal, useFiatOnrampAvailability, useOpenModal, useToggleModal } from '../../state/application/hooks'
@ -160,8 +159,7 @@ export default function AuthenticatedHeader({ account, openSettings }: { account
const resetSellAssets = useSellAsset((state) => state.reset)
const clearCollectionFilters = useWalletCollections((state) => state.clearCollectionFilters)
const isClaimAvailable = useIsNftClaimAvailable((state) => state.isClaimAvailable)
const shouldShowBuyFiatButton = !isPathBlocked('/buy')
const { formatNumber, formatDelta } = useFormatter()
const { formatNumber } = useFormatter()
const shouldDisableNFTRoutes = useDisableNFTRoutes()
@ -306,28 +304,26 @@ export default function AuthenticatedHeader({ account, openSettings }: { account
<Trans>View and sell NFTs</Trans>
</HeaderButton>
)}
{shouldShowBuyFiatButton && (
<HeaderButton
size={ButtonSize.medium}
emphasis={ButtonEmphasis.highSoft}
onClick={handleBuyCryptoClick}
disabled={disableBuyCryptoButton}
data-testid="wallet-buy-crypto"
>
{error ? (
<ThemedText.BodyPrimary>{error}</ThemedText.BodyPrimary>
) : (
<>
{fiatOnrampAvailabilityLoading ? (
<StyledLoadingButtonSpinner />
) : (
<CreditCard height="20px" width="20px" />
)}{' '}
<Trans>Buy crypto</Trans>
</>
)}
</HeaderButton>
)}
<HeaderButton
size={ButtonSize.medium}
emphasis={ButtonEmphasis.highSoft}
onClick={handleBuyCryptoClick}
disabled={disableBuyCryptoButton}
data-testid="wallet-buy-crypto"
>
{error ? (
<ThemedText.BodyPrimary>{error}</ThemedText.BodyPrimary>
) : (
<>
{fiatOnrampAvailabilityLoading ? (
<StyledLoadingButtonSpinner />
) : (
<CreditCard height="20px" width="20px" />
)}{' '}
<Trans>Buy crypto</Trans>
</>
)}
</HeaderButton>
{Boolean(!fiatOnrampAvailable && fiatOnrampAvailabilityChecked) && (
<FiatOnrampNotAvailableText marginTop="8px">
<Trans>Not available in your region</Trans>

@ -31,7 +31,7 @@ function BaseButton({ onClick, branded, children }: PropsWithChildren<{ onClick?
)
}
// Launches App/Play Store if on an iOS/Android device, else navigates to Uniswap Wallet microsite
// Launches App Store if on an iOS device, else navigates to Uniswap Wallet microsite
export function DownloadButton({
onClick,
text = 'Download',

@ -90,7 +90,7 @@ const DescriptionText = styled(ThemedText.LabelMicro)`
function useOrderAmounts(
orderDetails?: UniswapXOrderDetails
): Pick<InterfaceTrade, 'inputAmount' | 'outputAmount'> | undefined {
): Pick<InterfaceTrade, 'inputAmount' | 'postTaxOutputAmount'> | undefined {
const inputCurrency = useCurrency(orderDetails?.swapInfo?.inputCurrencyId, orderDetails?.chainId)
const outputCurrency = useCurrency(orderDetails?.swapInfo?.outputCurrencyId, orderDetails?.chainId)
@ -106,7 +106,7 @@ function useOrderAmounts(
if (swapInfo.tradeType === TradeType.EXACT_INPUT) {
return {
inputAmount: CurrencyAmount.fromRawAmount(inputCurrency, swapInfo.inputCurrencyAmountRaw),
outputAmount: CurrencyAmount.fromRawAmount(
postTaxOutputAmount: CurrencyAmount.fromRawAmount(
outputCurrency,
swapInfo.settledOutputCurrencyAmountRaw ?? swapInfo.expectedOutputCurrencyAmountRaw
),
@ -114,7 +114,7 @@ function useOrderAmounts(
} else {
return {
inputAmount: CurrencyAmount.fromRawAmount(inputCurrency, swapInfo.expectedInputCurrencyAmountRaw),
outputAmount: CurrencyAmount.fromRawAmount(outputCurrency, swapInfo.outputCurrencyAmountRaw),
postTaxOutputAmount: CurrencyAmount.fromRawAmount(outputCurrency, swapInfo.outputCurrencyAmountRaw),
}
}
}

@ -1,400 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`parseRemote parseRemoteActivities should parse NFT approval 1`] = `
Object {
"chainId": 1,
"descriptor": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"from": "0x50EC05ADe8280758E2077fcBC08D878D4aef79C3",
"hash": "someHash",
"logos": Array [],
"nonce": 12345,
"status": "CONFIRMED",
"timestamp": 10000,
"title": "Unknown Approval",
}
`;
exports[`parseRemote parseRemoteActivities should parse NFT approval for all 1`] = `
Object {
"chainId": 1,
"descriptor": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"from": "0x50EC05ADe8280758E2077fcBC08D878D4aef79C3",
"hash": "someHash",
"logos": Array [],
"nonce": 12345,
"status": "CONFIRMED",
"timestamp": 10000,
"title": "Unknown Approval",
}
`;
exports[`parseRemote parseRemoteActivities should parse NFT receive 1`] = `
Object {
"chainId": 1,
"currencies": undefined,
"descriptor": "1 SomeCollectionName from ",
"from": "0x50EC05ADe8280758E2077fcBC08D878D4aef79C3",
"hash": "someHash",
"logos": Array [
"imageUrl",
],
"nonce": 12345,
"otherAccount": "0x50EC05ADe8280758E2077fcBC08D878D4aef79C3",
"status": "CONFIRMED",
"timestamp": 10000,
"title": "Received",
}
`;
exports[`parseRemote parseRemoteActivities should parse NFT transfer 1`] = `
Object {
"chainId": 1,
"descriptor": "1 SomeCollectionName",
"from": "0x50EC05ADe8280758E2077fcBC08D878D4aef79C3",
"hash": "someHash",
"logos": Array [
"imageUrl",
],
"nonce": 12345,
"status": "CONFIRMED",
"timestamp": 10000,
"title": "Minted",
}
`;
exports[`parseRemote parseRemoteActivities should parse closed UniswapX order 1`] = `
Object {
"chainId": 1,
"currencies": Array [
Token {
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"buyFeeBps": undefined,
"chainId": 1,
"decimals": 18,
"isNative": false,
"isToken": true,
"name": "DAI",
"sellFeeBps": undefined,
"symbol": "DAI",
},
Token {
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"buyFeeBps": undefined,
"chainId": 1,
"decimals": 18,
"isNative": false,
"isToken": true,
"name": "Wrapped Ether",
"sellFeeBps": undefined,
"symbol": "WETH",
},
],
"descriptor": "100 DAI for 200 WETH",
"from": "someOfferer",
"hash": "someHash",
"logos": Array [
"someUrl",
"someUrl",
],
"offchainOrderStatus": "expired",
"prefixIconSrc": "bolt.svg",
"status": "FAILED",
"statusMessage": "Your swap could not be fulfilled at this time. Please try again.",
"timestamp": 10000,
"title": "Swap expired",
}
`;
exports[`parseRemote parseRemoteActivities should parse eth wrap 1`] = `
Object {
"chainId": 1,
"currencies": Array [
ExtendedEther {
"chainId": 1,
"decimals": 18,
"isNative": true,
"isToken": false,
"name": "Ether",
"symbol": "ETH",
},
Token {
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"buyFeeBps": undefined,
"chainId": 1,
"decimals": 18,
"isNative": false,
"isToken": true,
"name": "Wrapped Ether",
"sellFeeBps": undefined,
"symbol": "WETH",
},
],
"descriptor": "100 ETH for 100 WETH",
"from": "0x50EC05ADe8280758E2077fcBC08D878D4aef79C3",
"hash": "someHash",
"logos": Array [
"https://token-icons.s3.amazonaws.com/eth.png",
"https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/logo.png",
],
"nonce": 12345,
"status": "CONFIRMED",
"timestamp": 10000,
"title": "Wrapped",
}
`;
exports[`parseRemote parseRemoteActivities should parse moonpay purchase 1`] = `
Object {
"chainId": 1,
"currencies": Array [
Token {
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"buyFeeBps": undefined,
"chainId": 1,
"decimals": 18,
"isNative": false,
"isToken": true,
"name": "Wrapped Ether",
"sellFeeBps": undefined,
"symbol": "WETH",
},
],
"descriptor": "100 WETH for 100",
"from": "0x50EC05ADe8280758E2077fcBC08D878D4aef79C3",
"hash": "someHash",
"logos": Array [
"moonpay.svg",
],
"nonce": 12345,
"status": "CONFIRMED",
"timestamp": 10000,
"title": "Purchased",
}
`;
exports[`parseRemote parseRemoteActivities should parse nft purchase 1`] = `
Object {
"chainId": 1,
"descriptor": "1 SomeCollectionName",
"from": "0x50EC05ADe8280758E2077fcBC08D878D4aef79C3",
"hash": "someHash",
"logos": Array [
"imageUrl",
],
"nonce": 12345,
"status": "CONFIRMED",
"timestamp": 10000,
"title": "Bought",
}
`;
exports[`parseRemote parseRemoteActivities should parse receive 1`] = `
Object {
"chainId": 1,
"currencies": Array [
Token {
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"buyFeeBps": undefined,
"chainId": 1,
"decimals": 18,
"isNative": false,
"isToken": true,
"name": "Wrapped Ether",
"sellFeeBps": undefined,
"symbol": "WETH",
},
],
"descriptor": "100 WETH from ",
"from": "0x50EC05ADe8280758E2077fcBC08D878D4aef79C3",
"hash": "someHash",
"logos": Array [
"logoUrl",
],
"nonce": 12345,
"otherAccount": "0x50EC05ADe8280758E2077fcBC08D878D4aef79C3",
"status": "CONFIRMED",
"timestamp": 10000,
"title": "Received",
}
`;
exports[`parseRemote parseRemoteActivities should parse remove liquidity 1`] = `
Object {
"chainId": 1,
"currencies": Array [
Token {
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"buyFeeBps": undefined,
"chainId": 1,
"decimals": 18,
"isNative": false,
"isToken": true,
"name": "Wrapped Ether",
"sellFeeBps": undefined,
"symbol": "WETH",
},
Token {
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"buyFeeBps": undefined,
"chainId": 1,
"decimals": 18,
"isNative": false,
"isToken": true,
"name": "DAI",
"sellFeeBps": undefined,
"symbol": "DAI",
},
],
"descriptor": "100 WETH and 100 DAI",
"from": "0x50EC05ADe8280758E2077fcBC08D878D4aef79C3",
"hash": "someHash",
"logos": Array [
"logoUrl",
"logoUrl",
],
"nonce": 12345,
"status": "CONFIRMED",
"timestamp": 10000,
"title": "Removed Liquidity",
}
`;
exports[`parseRemote parseRemoteActivities should parse send 1`] = `
Object {
"chainId": 1,
"currencies": Array [
Token {
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"buyFeeBps": undefined,
"chainId": 1,
"decimals": 18,
"isNative": false,
"isToken": true,
"name": "DAI",
"sellFeeBps": undefined,
"symbol": "DAI",
},
],
"descriptor": "100 DAI to ",
"from": "0x50EC05ADe8280758E2077fcBC08D878D4aef79C3",
"hash": "someHash",
"logos": Array [
"logoUrl",
],
"nonce": 12345,
"otherAccount": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"status": "CONFIRMED",
"timestamp": 10000,
"title": "Sent",
}
`;
exports[`parseRemote parseRemoteActivities should parse swap 1`] = `
Object {
"chainId": 1,
"currencies": Array [
Token {
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"buyFeeBps": undefined,
"chainId": 1,
"decimals": 18,
"isNative": false,
"isToken": true,
"name": "DAI",
"sellFeeBps": undefined,
"symbol": "DAI",
},
Token {
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"buyFeeBps": undefined,
"chainId": 1,
"decimals": 18,
"isNative": false,
"isToken": true,
"name": "Wrapped Ether",
"sellFeeBps": undefined,
"symbol": "WETH",
},
],
"descriptor": "100 DAI for 100 WETH",
"from": "0x50EC05ADe8280758E2077fcBC08D878D4aef79C3",
"hash": "someHash",
"logos": Array [
"logoUrl",
],
"nonce": 12345,
"status": "CONFIRMED",
"timestamp": 10000,
"title": "Swapped",
}
`;
exports[`parseRemote parseRemoteActivities should parse swap order 1`] = `
Object {
"chainId": 1,
"currencies": Array [
Token {
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"buyFeeBps": undefined,
"chainId": 1,
"decimals": 18,
"isNative": false,
"isToken": true,
"name": "DAI",
"sellFeeBps": undefined,
"symbol": "DAI",
},
Token {
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"buyFeeBps": undefined,
"chainId": 1,
"decimals": 18,
"isNative": false,
"isToken": true,
"name": "Wrapped Ether",
"sellFeeBps": undefined,
"symbol": "WETH",
},
],
"descriptor": "100 DAI for 100 WETH",
"from": "0x50EC05ADe8280758E2077fcBC08D878D4aef79C3",
"hash": "someHash",
"logos": Array [
"logoUrl",
],
"nonce": 12345,
"prefixIconSrc": "bolt.svg",
"status": "CONFIRMED",
"timestamp": 10000,
"title": "Swapped",
}
`;
exports[`parseRemote parseRemoteActivities should parse token approval 1`] = `
Object {
"chainId": 1,
"currencies": Array [
Token {
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"buyFeeBps": undefined,
"chainId": 1,
"decimals": 18,
"isNative": false,
"isToken": true,
"name": "DAI",
"sellFeeBps": undefined,
"symbol": "DAI",
},
],
"descriptor": "DAI",
"from": "0x50EC05ADe8280758E2077fcBC08D878D4aef79C3",
"hash": "someHash",
"logos": Array [
"logoUrl",
],
"nonce": 12345,
"status": "CONFIRMED",
"timestamp": 10000,
"title": "Approved",
}
`;

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