Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b4e192f5d | ||
|
|
c837e379d9 | ||
|
|
370f11d5ae | ||
|
|
c3bf24d8ea |
13
.env
13
.env
@ -1,16 +1,17 @@
|
|||||||
# These API keys are intentionally public. Please do not report them - thank you for your concern.
|
# These API keys are intentionally public. Please do not report them - thank you for your concern.
|
||||||
ESLINT_NO_DEV_ERRORS=true
|
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_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_BNB_RPC_URL="https://rough-sleek-hill.bsc.quiknode.pro/413cc98cbc776cda8fdf1d0f47003583ff73d9bf"
|
||||||
|
REACT_APP_BASE_GOERLI_RPC_URL="https://wiser-compatible-mansion.base-goerli.quiknode.pro/5874f36248e17020a1006149e7f68c63967e1f45/"
|
||||||
|
REACT_APP_BASE_MAINNET_RPC_URL="https://cool-white-diagram.base-mainnet.quiknode.pro/d8f036f35dfab2c68f32dfa822cd971e7a25a117/"
|
||||||
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
|
REACT_APP_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_API="https://api.moonpay.com"
|
||||||
REACT_APP_MOONPAY_LINK="https://us-central1-uniswap-mobile.cloudfunctions.net/signMoonpayLinkV2?platform=web&env=staging"
|
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_MOONPAY_PUBLISHABLE_KEY="pk_test_DycfESRid31UaSxhI5yWKe1r5E5kKSz"
|
||||||
REACT_APP_SENTRY_DSN="https://a3c62e400b8748b5a8d007150e2f38b7@o1037921.ingest.sentry.io/4504255148851200"
|
REACT_APP_SENTRY_DSN="https://a3c62e400b8748b5a8d007150e2f38b7@o1037921.ingest.sentry.io/4504255148851200"
|
||||||
REACT_APP_STATSIG_PROXY_URL="https://null.null"
|
REACT_APP_STATSIG_PROXY_URL="https://api.uniswap.org/v1/statsig-proxy"
|
||||||
REACT_APP_TEMP_API_URL="https://null.null"
|
REACT_APP_TEMP_API_URL="https://temp.api.uniswap.org/v1"
|
||||||
REACT_APP_UNISWAP_API_URL="https://null.null"
|
REACT_APP_UNISWAP_API_URL="https://api.uniswap.org/v2"
|
||||||
REACT_APP_WALLET_CONNECT_PROJECT_ID="c6c9bacd35afa3eb9e6cccf6d8464395"
|
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.
|
# 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_AMPLITUDE_PROXY_URL="https://api.uniswap.org/v1/amplitude-proxy"
|
||||||
REACT_APP_AWS_API_ENDPOINT="https://null.null"
|
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_BNB_RPC_URL="https://old-wispy-arrow.bsc.quiknode.pro/f5c060177236065c1058531a0615ab4f7a34a2fd"
|
||||||
REACT_APP_FIREBASE_KEY="AIzaSyBcZWwTcTJHj_R6ipZcrJkXdq05PuX0Rs0"
|
REACT_APP_FIREBASE_KEY="AIzaSyBcZWwTcTJHj_R6ipZcrJkXdq05PuX0Rs0"
|
||||||
REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
|
REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
|
||||||
REACT_APP_GOOGLE_ANALYTICS_ID="G-KDP9B6W4H8"
|
REACT_APP_GOOGLE_ANALYTICS_ID="G-KDP9B6W4H8"
|
||||||
REACT_APP_INFURA_KEY="099fc58e0de9451d80b18d7c74caa7c1"
|
REACT_APP_INFURA_KEY="099fc58e0de9451d80b18d7c74caa7c1"
|
||||||
REACT_APP_MOONPAY_API="https://api.moonpay.com"
|
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_MOONPAY_PUBLISHABLE_KEY="pk_live_uQG4BJC4w3cxnqpcSqAfohdBFDTsY6E"
|
||||||
REACT_APP_SENTRY_ENABLED=true
|
REACT_APP_SENTRY_ENABLED=true
|
||||||
REACT_APP_SENTRY_TRACES_SAMPLE_RATE=0.00003
|
REACT_APP_SENTRY_TRACES_SAMPLE_RATE=0.00003
|
||||||
REACT_APP_STATSIG_PROXY_URL="https://null.null"
|
REACT_APP_STATSIG_PROXY_URL="https://api.uniswap.org/v1/statsig-proxy"
|
||||||
REACT_APP_QUICKNODE_MAINNET_RPC_URL="https://ultra-blue-flower.quiknode.pro/770b22d5f362c537bc8fe19b034c45b22958f880"
|
THE_GRAPH_SCHEMA_ENDPOINT="https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3"
|
||||||
THE_GRAPH_SCHEMA_ENDPOINT="https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3?source=uniswap"
|
|
||||||
|
|||||||
22
.eslintrc.js
22
.eslintrc.js
@ -28,20 +28,7 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
files: ['**/*.ts', '**/*.tsx'],
|
files: ['**/*.ts', '**/*.tsx'],
|
||||||
rules: {
|
rules: {
|
||||||
'@typescript-eslint/no-restricted-imports': [
|
'@typescript-eslint/no-restricted-imports': ['error', restrictedImports],
|
||||||
'error',
|
|
||||||
{
|
|
||||||
...restrictedImports,
|
|
||||||
paths: [
|
|
||||||
...restrictedImports.paths,
|
|
||||||
{
|
|
||||||
name: '@uniswap/smart-order-router',
|
|
||||||
message: 'Only import types, unless you are in the client-side SOR, to preserve lazy-loading.',
|
|
||||||
allowTypeImports: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'import/no-restricted-paths': [
|
'import/no-restricted-paths': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
@ -70,13 +57,6 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'no-restricted-syntax': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
selector: ':matches(ExportAllDeclaration)',
|
|
||||||
message: 'Barrel exports bloat the bundle size by preventing tree-shaking.',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
@uniswap/web-admins
|
||||||
22
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
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
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
|
||||||
19
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
19
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
@ -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
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
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
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
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
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
|
||||||
64
.github/workflows/2-deploy-to-staging.yml
vendored
Normal file
64
.github/workflows/2-deploy-to-staging.yml
vendored
Normal file
@ -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
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
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
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
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 }}
|
||||||
91
.github/workflows/notify-slack-on-release-merge.yml
vendored
Normal file
91
.github/workflows/notify-slack-on-release-merge.yml
vendored
Normal file
@ -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
|
||||||
275
.github/workflows/test.yml
vendored
Normal file
275
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
name: Test
|
||||||
|
|
||||||
|
# Many build steps have their own caches, so each job has its own cache to improve subsequent build times.
|
||||||
|
# Build tools are configured to cache to node_modules/.cache, so they are cached independently of node_modules.
|
||||||
|
# Caches are saved every run (by keying on github.run_id), and the most recent available cache is loaded.
|
||||||
|
# See https://jongleberry.medium.com/speed-up-your-ci-and-dx-with-node-modules-cache-ac8df82b7bb0.
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- releases/staging
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: ./.github/actions/setup
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules/.cache
|
||||||
|
key: ${{ runner.os }}-eslint-${{ github.run_id }}
|
||||||
|
restore-keys: ${{ runner.os }}-eslint-
|
||||||
|
- run: yarn lint
|
||||||
|
- if: failure() && github.ref_name == 'main'
|
||||||
|
uses: ./.github/actions/report
|
||||||
|
with:
|
||||||
|
name: Lint
|
||||||
|
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||||
|
|
||||||
|
typecheck:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: ./.github/actions/setup
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules/.cache
|
||||||
|
key: ${{ runner.os }}-tsc-${{ github.run_id }}
|
||||||
|
restore-keys: ${{ runner.os }}-tsc-
|
||||||
|
- run: yarn typecheck
|
||||||
|
- if: failure() && github.ref_name == 'main'
|
||||||
|
uses: ./.github/actions/report
|
||||||
|
with:
|
||||||
|
name: Typecheck
|
||||||
|
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||||
|
|
||||||
|
deps-tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: ./.github/actions/setup
|
||||||
|
- run: yarn yarn-deduplicate --strategy=highest --list --fail
|
||||||
|
- if: failure() && github.ref_name == 'main'
|
||||||
|
uses: ./.github/actions/report
|
||||||
|
with:
|
||||||
|
name: Dependency checks
|
||||||
|
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||||
|
|
||||||
|
unit-tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: ./.github/actions/setup
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules/.cache
|
||||||
|
key: ${{ runner.os }}-jest-${{ github.run_id }}
|
||||||
|
restore-keys: ${{ runner.os }}-jest-
|
||||||
|
- run: yarn test --coverage --maxWorkers=100%
|
||||||
|
- uses: codecov/codecov-action@v3
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
fail_ci_if_error: false
|
||||||
|
flags: unit-tests
|
||||||
|
- if: failure() && github.ref_name == 'main'
|
||||||
|
uses: ./.github/actions/report
|
||||||
|
with:
|
||||||
|
name: Unit tests
|
||||||
|
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: ./.github/actions/setup
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules/.swc
|
||||||
|
key: ${{ runner.os }}-swc-${{ github.run_id }}
|
||||||
|
restore-keys: ${{ runner.os }}-swc-
|
||||||
|
- run: yarn build
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: build
|
||||||
|
path: build
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
cypress-typecheck:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: ./.github/actions/setup
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules/.cache
|
||||||
|
key: ${{ runner.os }}-cypress-tsc-${{ github.run_id }}
|
||||||
|
restore-keys: ${{ runner.os }}-cypress-tsc-
|
||||||
|
- run: yarn typecheck:cypress
|
||||||
|
- if: failure() && github.ref_name == 'main'
|
||||||
|
uses: ./.github/actions/report
|
||||||
|
with:
|
||||||
|
name: Cypress typecheck
|
||||||
|
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||||
|
|
||||||
|
# Allows for parallel re-runs of cypress tests without re-building.
|
||||||
|
cypress-rerun:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- run: exit 0
|
||||||
|
|
||||||
|
cypress-test-matrix:
|
||||||
|
needs: [build, cypress-rerun]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
containers: [1, 2, 3, 4]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: ./.github/actions/setup
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: /root/.cache/Cypress
|
||||||
|
key: ${{ runner.os }}-cypress-${{ hashFiles('**/node_modules/cypress/package.json') }}
|
||||||
|
- run: |
|
||||||
|
yarn cypress install
|
||||||
|
yarn cypress info
|
||||||
|
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: build
|
||||||
|
path: build
|
||||||
|
|
||||||
|
- uses: actions/cache/restore@v3
|
||||||
|
with:
|
||||||
|
path: cache
|
||||||
|
key: ${{ runner.os }}-hardhat-${{ hashFiles('hardhat.config.js') }}-${{ github.run_id }}
|
||||||
|
restore-keys: ${{ runner.os }}-hardhat-${{ hashFiles('hardhat.config.js') }}-
|
||||||
|
|
||||||
|
- uses: cypress-io/github-action@v4
|
||||||
|
with:
|
||||||
|
install: false
|
||||||
|
record: true
|
||||||
|
parallel: true
|
||||||
|
start: yarn serve
|
||||||
|
wait-on: 'http://localhost:3000'
|
||||||
|
browser: electron
|
||||||
|
group: e2e
|
||||||
|
spec: ${{ github.ref_name == 'releases/staging' && 'cypress/{e2e,staging}/**/*.test.ts' || 'cypress/e2e/**/*.test.ts' }}
|
||||||
|
env:
|
||||||
|
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
COMMIT_INFO_BRANCH: ${{ github.event.pull_request.head.ref || github.ref_name }}
|
||||||
|
COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title || github.event.head_commit.message }}
|
||||||
|
COMMIT_INFO_AUTHOR: ${{ github.event.sender.login || github.event.head_commit.author.login }}
|
||||||
|
# Cypress requires an email for filtering by author, but GitHub does not expose one.
|
||||||
|
# GitHub's public profile email can be deterministically produced from user id/login.
|
||||||
|
COMMIT_INFO_EMAIL: ${{ github.event.sender.id || github.event.head_commit.author.id }}+${{ github.event.sender.login || github.event.head_commit.author.login }}@users.noreply.github.com
|
||||||
|
COMMIT_INFO_SHA: ${{ github.event.pull_request.head.sha || github.event.head_commit.sha }}
|
||||||
|
COMMIT_INFO_TIMESTAMP: ${{ github.event.pull_request.updated_at || github.event.head_commit.timestamp }}
|
||||||
|
CYPRESS_PULL_REQUEST_ID: ${{ github.event.pull_request.number }}
|
||||||
|
CYPRESS_PULL_REQUEST_URL: ${{ github.event.pull_request.html_url }}
|
||||||
|
- if: failure() && github.ref_name == 'main'
|
||||||
|
uses: ./.github/actions/report
|
||||||
|
with:
|
||||||
|
name: Cypress tests
|
||||||
|
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: hardhat-cache
|
||||||
|
path: cache
|
||||||
|
|
||||||
|
hardhat-cache:
|
||||||
|
needs: [cypress-test-matrix]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: hardhat-cache
|
||||||
|
path: cache
|
||||||
|
- uses: actions/cache/save@v3
|
||||||
|
with:
|
||||||
|
path: cache
|
||||||
|
key: ${{ runner.os }}-hardhat-${{ hashFiles('hardhat.config.js') }}-${{ github.run_id }}
|
||||||
|
|
||||||
|
cloud-typecheck:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: ./.github/actions/setup
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules/.cache
|
||||||
|
key: ${{ runner.os }}-cloud-tsc-${{ github.run_id }}
|
||||||
|
restore-keys: ${{ runner.os }}-cloud-tsc-
|
||||||
|
- run: yarn typecheck:cloud
|
||||||
|
- if: failure() && github.ref_name == 'main'
|
||||||
|
uses: ./.github/actions/report
|
||||||
|
with:
|
||||||
|
name: Cloud typecheck
|
||||||
|
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||||
|
|
||||||
|
cloud-tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: ./.github/actions/setup
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: node_modules/.cache
|
||||||
|
key: ${{ runner.os }}-cloud-jest-${{ github.run_id }}
|
||||||
|
restore-keys: ${{ runner.os }}-cloud-jest-
|
||||||
|
# Only use 1 worker, so the other can be used for the proxy server under test.
|
||||||
|
- run: yarn test:cloud --coverage --maxWorkers=1
|
||||||
|
- uses: codecov/codecov-action@v3
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
fail_ci_if_error: false
|
||||||
|
flags: cloud-tests
|
||||||
|
|
||||||
|
pre:
|
||||||
|
if: ${{ github.ref_name == 'main' || github.ref_name == 'releases/staging' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/github-script@v6.4.1
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
github.rest.repos.createCommitStatus({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
sha: context.sha,
|
||||||
|
state: 'pending',
|
||||||
|
context: 'Test / promotion',
|
||||||
|
description: 'Running tests...',
|
||||||
|
target_url: 'https://github.com/Uniswap/interface/actions/runs/' + context.runId
|
||||||
|
})
|
||||||
|
|
||||||
|
post:
|
||||||
|
if: ${{ github.ref_name == 'main' || github.ref_name == 'releases/staging' }}
|
||||||
|
needs: [pre, lint, typecheck, deps-tests, unit-tests, cypress-test-matrix]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/github-script@v6.4.1
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
github.rest.repos.createCommitStatus({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
sha: context.sha,
|
||||||
|
state: ${{ env.STATUS }} ? 'success' : 'failure',
|
||||||
|
context: 'Test / promotion',
|
||||||
|
description: ${{ env.STATUS }} ? 'All tests passed' : 'One or more tests failed and are blocking promotion',
|
||||||
|
target_url: 'https://github.com/Uniswap/interface/actions/runs/' + context.runId
|
||||||
|
})
|
||||||
|
env:
|
||||||
|
STATUS: |
|
||||||
|
${{ needs.lint.result == 'success' }} &&
|
||||||
|
${{ needs.typecheck.result == 'success' }} &&
|
||||||
|
${{ needs.deps-tests.result == 'success' }} &&
|
||||||
|
${{ needs.unit-tests.result == 'success' }} &&
|
||||||
|
${{ needs.cypress-test-matrix.result == 'success' }}
|
||||||
25
.snyk
Normal file
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
1
CODEOWNERS
Normal file
@ -0,0 +1 @@
|
|||||||
|
@uniswap/web-admins
|
||||||
@ -69,10 +69,10 @@ Other things to note:
|
|||||||
|
|
||||||
The Uniswap Interface supports swapping, adding liquidity, removing liquidity and migrating liquidity for Uniswap protocol V2.
|
The Uniswap Interface supports swapping, adding liquidity, removing liquidity and migrating liquidity for Uniswap protocol V2.
|
||||||
|
|
||||||
- Swap on Uniswap V2: <https://app.uniswap.org/swap?use=v2>
|
- Swap on Uniswap V2: <https://app.uniswap.org/#/swap?use=v2>
|
||||||
- View V2 liquidity: <https://app.uniswap.org/pools/v2>
|
- View V2 liquidity: <https://app.uniswap.org/#/pools/v2>
|
||||||
- Add V2 liquidity: <https://app.uniswap.org/add/v2>
|
- Add V2 liquidity: <https://app.uniswap.org/#/add/v2>
|
||||||
- Migrate V2 liquidity to V3: <https://app.uniswap.org/migrate/v2>
|
- Migrate V2 liquidity to V3: <https://app.uniswap.org/#/migrate/v2>
|
||||||
|
|
||||||
## Accessing Uniswap V1
|
## Accessing Uniswap V1
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,6 @@ ignore:
|
|||||||
- "**/styled.tsx"
|
- "**/styled.tsx"
|
||||||
- "**/constants/**/*"
|
- "**/constants/**/*"
|
||||||
- "constants/**/*"
|
- "constants/**/*"
|
||||||
- "src/dev/*"
|
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
status:
|
status:
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
overrideExisting: true
|
overrideExisting: true
|
||||||
schema: 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3?source=uniswap'
|
schema: 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3'
|
||||||
generates:
|
generates:
|
||||||
./src/graphql/thegraph/schema/schema.graphql:
|
./src/graphql/thegraph/schema/schema.graphql:
|
||||||
plugins:
|
plugins:
|
||||||
|
|||||||
@ -5,6 +5,7 @@ const { execSync } = require('child_process')
|
|||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin')
|
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin')
|
||||||
|
const TerserPlugin = require('terser-webpack-plugin')
|
||||||
const { IgnorePlugin, ProvidePlugin } = require('webpack')
|
const { IgnorePlugin, ProvidePlugin } = require('webpack')
|
||||||
const { RetryChunkLoadPlugin } = require('webpack-retry-chunk-load-plugin')
|
const { RetryChunkLoadPlugin } = require('webpack-retry-chunk-load-plugin')
|
||||||
|
|
||||||
@ -130,12 +131,6 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// Retain source maps for node_modules packages:
|
|
||||||
webpackConfig.module.rules[0] = {
|
|
||||||
...webpackConfig.module.rules[0],
|
|
||||||
exclude: /node_modules/,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure webpack transpilation (create-react-app specifies transpilation rules in a oneOf):
|
// Configure webpack transpilation (create-react-app specifies transpilation rules in a oneOf):
|
||||||
webpackConfig.module.rules[1].oneOf = webpackConfig.module.rules[1].oneOf.map((rule) => {
|
webpackConfig.module.rules[1].oneOf = webpackConfig.module.rules[1].oneOf.map((rule) => {
|
||||||
if (rule.loader && rule.loader.match(/babel-loader/)) {
|
if (rule.loader && rule.loader.match(/babel-loader/)) {
|
||||||
@ -145,20 +140,18 @@ module.exports = {
|
|||||||
return rule
|
return rule
|
||||||
})
|
})
|
||||||
|
|
||||||
// Run terser compression on node_modules before tree-shaking, so that tree-shaking is more effective.
|
|
||||||
// This works by eliminating dead code, so that webpack can identify unused imports and tree-shake them;
|
|
||||||
// it is only necessary for node_modules - it is done through linting for our own source code -
|
|
||||||
// see https://medium.com/engineering-housing/dead-code-elimination-and-tree-shaking-at-housing-part-1-307a94b30f23#7e03:
|
|
||||||
webpackConfig.module.rules.push({
|
|
||||||
enforce: 'post',
|
|
||||||
test: /node_modules.*\.(js)$/,
|
|
||||||
loader: path.join(__dirname, 'scripts/terser-loader.js'),
|
|
||||||
options: { compress: true, mangle: false },
|
|
||||||
})
|
|
||||||
|
|
||||||
// Configure webpack optimization:
|
// Configure webpack optimization:
|
||||||
webpackConfig.optimization = Object.assign(
|
webpackConfig.optimization = Object.assign(
|
||||||
webpackConfig.optimization,
|
webpackConfig.optimization,
|
||||||
|
{
|
||||||
|
minimize: isProduction,
|
||||||
|
minimizer: [
|
||||||
|
new TerserPlugin({
|
||||||
|
minify: TerserPlugin.swcMinify,
|
||||||
|
parallel: require('os').cpus().length,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
isProduction
|
isProduction
|
||||||
? {
|
? {
|
||||||
splitChunks: {
|
splitChunks: {
|
||||||
@ -177,6 +170,13 @@ module.exports = {
|
|||||||
// Configure webpack resolution. webpackConfig.cache is unused with swc-loader, but the resolver can still cache:
|
// Configure webpack resolution. webpackConfig.cache is unused with swc-loader, but the resolver can still cache:
|
||||||
webpackConfig.resolve = Object.assign(webpackConfig.resolve, { unsafeCache: true })
|
webpackConfig.resolve = Object.assign(webpackConfig.resolve, { unsafeCache: true })
|
||||||
|
|
||||||
|
webpackConfig.ignoreWarnings = [
|
||||||
|
// Source mappings for a package will fail if the package does not provide them, but the build will still succeed,
|
||||||
|
// so it is unnecessary (and bothersome) to log it. This should be turned off when debugging missing sourcemaps.
|
||||||
|
// See https://webpack.js.org/loaders/source-map-loader#ignoring-warnings.
|
||||||
|
/Failed to parse source map/,
|
||||||
|
]
|
||||||
|
|
||||||
return webpackConfig
|
return webpackConfig
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -6,7 +6,7 @@ export default defineConfig({
|
|||||||
defaultCommandTimeout: 24000, // 2x average block time
|
defaultCommandTimeout: 24000, // 2x average block time
|
||||||
chromeWebSecurity: false,
|
chromeWebSecurity: false,
|
||||||
experimentalMemoryManagement: true, // better memory management, see https://github.com/cypress-io/cypress/pull/25462
|
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
|
video: false, // GH provides 2 CPUs, and cypress video eats one up, see https://github.com/cypress-io/cypress/issues/20468#issuecomment-1307608025
|
||||||
e2e: {
|
e2e: {
|
||||||
async setupNodeEvents(on, config) {
|
async setupNodeEvents(on, config) {
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { aliasQuery, hasQuery } from '../utils/graphql-test-utils'
|
|||||||
|
|
||||||
describe('Add Liquidity', () => {
|
describe('Add Liquidity', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3?source=uniswap', (req) => {
|
cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3', (req) => {
|
||||||
aliasQuery(req, 'feeTierDistribution')
|
aliasQuery(req, 'feeTierDistribution')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -16,16 +16,6 @@ describe('Add Liquidity', () => {
|
|||||||
cy.contains('0.05% fee tier')
|
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', () => {
|
it('does not crash if token is duplicated', () => {
|
||||||
cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
|
cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
|
||||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'UNI')
|
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'UNI')
|
||||||
@ -39,30 +29,26 @@ describe('Add Liquidity', () => {
|
|||||||
|
|
||||||
it('loads fee tier distribution', () => {
|
it('loads fee tier distribution', () => {
|
||||||
cy.fixture('feeTierDistribution.json').then((feeTierDistribution) => {
|
cy.fixture('feeTierDistribution.json').then((feeTierDistribution) => {
|
||||||
cy.intercept(
|
cy.intercept('POST', '/subgraphs/name/uniswap/uniswap-v3', (req: CyHttpMessages.IncomingHttpRequest) => {
|
||||||
'POST',
|
if (hasQuery(req, 'FeeTierDistribution')) {
|
||||||
'/subgraphs/name/uniswap/uniswap-v3?source=uniswap',
|
req.alias = 'FeeTierDistribution'
|
||||||
(req: CyHttpMessages.IncomingHttpRequest) => {
|
|
||||||
if (hasQuery(req, 'FeeTierDistribution')) {
|
|
||||||
req.alias = 'FeeTierDistribution'
|
|
||||||
|
|
||||||
req.reply({
|
req.reply({
|
||||||
body: {
|
body: {
|
||||||
data: {
|
data: {
|
||||||
...feeTierDistribution,
|
...feeTierDistribution,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
headers: {
|
},
|
||||||
'access-control-allow-origin': '*',
|
headers: {
|
||||||
},
|
'access-control-allow-origin': '*',
|
||||||
})
|
},
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
)
|
})
|
||||||
|
|
||||||
cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/ETH')
|
cy.visit('/add/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/ETH')
|
||||||
cy.wait('@FeeTierDistribution')
|
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')
|
cy.get('#add-liquidity-selected-fee .selected-fee-percentage').should('contain.text', '40% select')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
|
import { FeatureFlag } from '../../src/featureFlags'
|
||||||
import { getTestSelector } from '../utils'
|
import { getTestSelector } from '../utils'
|
||||||
|
|
||||||
describe('Buy Crypto Modal', () => {
|
describe('Buy Crypto Modal', () => {
|
||||||
it('should open and close', () => {
|
it('should open and close', () => {
|
||||||
cy.visit('/')
|
cy.visit('/', { featureFlags: [FeatureFlag.fiatOnRampButtonOnSwap] })
|
||||||
|
|
||||||
// Open the fiat onramp modal
|
// Open the fiat onramp modal
|
||||||
cy.get(getTestSelector('buy-fiat-button')).click()
|
cy.get(getTestSelector('buy-fiat-button')).click()
|
||||||
@ -15,7 +16,7 @@ describe('Buy Crypto Modal', () => {
|
|||||||
|
|
||||||
it('should open and close, mobile viewport', () => {
|
it('should open and close, mobile viewport', () => {
|
||||||
cy.viewport('iphone-6')
|
cy.viewport('iphone-6')
|
||||||
cy.visit('/')
|
cy.visit('/', { featureFlags: [FeatureFlag.fiatOnRampButtonOnSwap] })
|
||||||
|
|
||||||
// Open the fiat onramp modal
|
// Open the fiat onramp modal
|
||||||
cy.get(getTestSelector('buy-fiat-button')).click()
|
cy.get(getTestSelector('buy-fiat-button')).click()
|
||||||
|
|||||||
@ -39,50 +39,4 @@ describe('Landing Page', () => {
|
|||||||
cy.get(getTestSelector('pool-nav-link')).last().click()
|
cy.get(getTestSelector('pool-nav-link')).last().click()
|
||||||
cy.url().should('include', '/pools')
|
cy.url().should('include', '/pools')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('does not render landing page when / path is blocked', () => {
|
|
||||||
cy.intercept('/', (req) => {
|
|
||||||
req.reply((res) => {
|
|
||||||
const parser = new DOMParser()
|
|
||||||
const doc = parser.parseFromString(res.body, 'text/html')
|
|
||||||
const meta = document.createElement('meta')
|
|
||||||
meta.setAttribute('property', 'x:blocked-paths')
|
|
||||||
meta.setAttribute('content', '/,/buy')
|
|
||||||
doc.head.appendChild(meta)
|
|
||||||
|
|
||||||
res.body = doc.documentElement.outerHTML
|
|
||||||
})
|
|
||||||
})
|
|
||||||
cy.visit('/', { userState: DISCONNECTED_WALLET_USER_STATE })
|
|
||||||
|
|
||||||
cy.get(getTestSelector('landing-page')).should('not.exist')
|
|
||||||
cy.get(getTestSelector('buy-fiat-button')).should('not.exist')
|
|
||||||
cy.url().should('include', '/swap')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('does not render uk compliance banner in US', () => {
|
|
||||||
cy.visit('/swap')
|
|
||||||
cy.contains('UK disclaimer').should('not.exist')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('renders uk compliance banner in uk', () => {
|
|
||||||
cy.intercept('https://api.uniswap.org/v1/amplitude-proxy', (req) => {
|
|
||||||
const requestBody = JSON.stringify(req.body)
|
|
||||||
const byteSize = new Blob([requestBody]).size
|
|
||||||
req.alias = 'amplitude'
|
|
||||||
req.reply(
|
|
||||||
JSON.stringify({
|
|
||||||
code: 200,
|
|
||||||
server_upload_time: Date.now(),
|
|
||||||
payload_size_bytes: byteSize,
|
|
||||||
events_ingested: req.body.events.length,
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
'origin-country': 'GB',
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
cy.visit('/swap')
|
|
||||||
cy.contains('UK disclaimer')
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,48 +2,43 @@ import { getTestSelector } from '../../utils'
|
|||||||
|
|
||||||
describe('Mini Portfolio account drawer', () => {
|
describe('Mini Portfolio account drawer', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const portfolioSpy = cy.spy().as('portfolioSpy')
|
cy.intercept(/api.uniswap.org\/v1\/graphql/, cy.spy().as('gqlSpy'))
|
||||||
cy.intercept(/api.uniswap.org\/v1\/graphql/, (req) => {
|
|
||||||
if (req.body.operationName === 'PortfolioBalances') {
|
|
||||||
portfolioSpy(req)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
cy.visit('/swap')
|
cy.visit('/swap')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('fetches balances when account button is first hovered', () => {
|
it('fetches balances when account button is first hovered', () => {
|
||||||
// The balances should not be fetched before the account button is hovered
|
// The balances should not be fetched before the account button is hovered
|
||||||
cy.get('@portfolioSpy').should('not.have.been.called')
|
cy.get('@gqlSpy').should('not.have.been.called')
|
||||||
|
|
||||||
// Balances should have been fetched once after hover
|
// Balances should have been fetched once after hover
|
||||||
cy.get(getTestSelector('web3-status-connected')).trigger('mouseover')
|
cy.get(getTestSelector('web3-status-connected')).trigger('mouseover')
|
||||||
cy.get('@portfolioSpy').should('have.been.calledOnce')
|
cy.get('@gqlSpy').should('have.been.calledOnce')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not re-fetch balances on second hover', () => {
|
it('should not re-fetch balances on second hover', () => {
|
||||||
// The balances should not be fetched before the account button is hovered
|
// The balances should not be fetched before the account button is hovered
|
||||||
cy.get('@portfolioSpy').should('not.have.been.called')
|
cy.get('@gqlSpy').should('not.have.been.called')
|
||||||
|
|
||||||
// Balances should have been fetched once after hover
|
// Balances should have been fetched once after hover
|
||||||
cy.get(getTestSelector('web3-status-connected')).trigger('mouseover')
|
cy.get(getTestSelector('web3-status-connected')).trigger('mouseover')
|
||||||
cy.get('@portfolioSpy').should('have.been.calledOnce')
|
cy.get('@gqlSpy').should('have.been.calledOnce')
|
||||||
|
|
||||||
// Balances should not be refetched upon second hover
|
// Balances should not be refetched upon second hover
|
||||||
cy.get(getTestSelector('web3-status-connected')).trigger('mouseover')
|
cy.get(getTestSelector('web3-status-connected')).trigger('mouseover')
|
||||||
cy.get('@portfolioSpy').should('have.been.calledOnce')
|
cy.get('@gqlSpy').should('have.been.calledOnce')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not re-fetch balances when the account drawer is opened', () => {
|
it('should not re-fetch balances when the account drawer is opened', () => {
|
||||||
// The balances should not be fetched before the account button is hovered
|
// The balances should not be fetched before the account button is hovered
|
||||||
cy.get('@portfolioSpy').should('not.have.been.called')
|
cy.get('@gqlSpy').should('not.have.been.called')
|
||||||
|
|
||||||
// Balances should have been fetched once after hover
|
// Balances should have been fetched once after hover
|
||||||
cy.get(getTestSelector('web3-status-connected')).trigger('mouseover')
|
cy.get(getTestSelector('web3-status-connected')).trigger('mouseover')
|
||||||
cy.get('@portfolioSpy').should('have.been.calledOnce')
|
cy.get('@gqlSpy').should('have.been.calledOnce')
|
||||||
|
|
||||||
// Balances should not be refetched upon opening drawer
|
// Balances should not be refetched upon opening drawer
|
||||||
cy.get(getTestSelector('web3-status-connected')).click()
|
cy.get(getTestSelector('web3-status-connected')).click()
|
||||||
cy.get('@portfolioSpy').should('have.been.calledOnce')
|
cy.get('@gqlSpy').should('have.been.calledOnce')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('fetches account information', () => {
|
it('fetches account information', () => {
|
||||||
@ -53,16 +48,15 @@ describe('Mini Portfolio account drawer', () => {
|
|||||||
|
|
||||||
// Verify that wallet state loads correctly
|
// Verify that wallet state loads correctly
|
||||||
cy.get(getTestSelector('mini-portfolio-navbar')).contains('Tokens')
|
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.intercept(/graphql/, { fixture: 'mini-portfolio/nfts.json' })
|
||||||
cy.get(getTestSelector('mini-portfolio-navbar')).contains('NFTs').click()
|
cy.get(getTestSelector('mini-portfolio-navbar')).contains('NFTs').click()
|
||||||
cy.get(getTestSelector('mini-portfolio-page')).contains('I Got Plenty')
|
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.intercept(/graphql/, { fixture: 'mini-portfolio/pools.json' })
|
cy.get(getTestSelector('mini-portfolio-navbar')).contains('Pools').click()
|
||||||
// cy.get(getTestSelector('mini-portfolio-navbar')).contains('Pools').click()
|
cy.get(getTestSelector('mini-portfolio-page')).contains('No pools yet')
|
||||||
// cy.get(getTestSelector('mini-portfolio-page')).contains('No pools yet')
|
|
||||||
|
|
||||||
cy.intercept(/graphql/, { fixture: 'mini-portfolio/full_activity.json' })
|
cy.intercept(/graphql/, { fixture: 'mini-portfolio/full_activity.json' })
|
||||||
cy.get(getTestSelector('mini-portfolio-navbar')).contains('Activity').click()
|
cy.get(getTestSelector('mini-portfolio-navbar')).contains('Activity').click()
|
||||||
|
|||||||
@ -12,7 +12,7 @@ describe('Testing nfts', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should load pudgy penguin collection page', () => {
|
it('should load pudgy penguin collection page', () => {
|
||||||
cy.visit(`/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
|
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
|
||||||
cy.get(getTestSelector('nft-collection-asset')).should('exist')
|
cy.get(getTestSelector('nft-collection-asset')).should('exist')
|
||||||
cy.get(getTestSelector('nft-collection-filter-buy-now')).should('not.exist')
|
cy.get(getTestSelector('nft-collection-filter-buy-now')).should('not.exist')
|
||||||
cy.get(getTestSelector('nft-filter')).first().click()
|
cy.get(getTestSelector('nft-filter')).first().click()
|
||||||
@ -20,13 +20,13 @@ describe('Testing nfts', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should be able to navigate to activity', () => {
|
it('should be able to navigate to activity', () => {
|
||||||
cy.visit(`/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
|
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
|
||||||
cy.get(getTestSelector('nft-activity')).first().click()
|
cy.get(getTestSelector('nft-activity')).first().click()
|
||||||
cy.get(getTestSelector('nft-activity-row')).should('exist')
|
cy.get(getTestSelector('nft-activity-row')).should('exist')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should go to the details page', () => {
|
it('should go to the details page', () => {
|
||||||
cy.visit(`/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
|
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
|
||||||
cy.get(getTestSelector('nft-filter')).first().click()
|
cy.get(getTestSelector('nft-filter')).first().click()
|
||||||
cy.get(getTestSelector('nft-collection-filter-buy-now')).click()
|
cy.get(getTestSelector('nft-collection-filter-buy-now')).click()
|
||||||
cy.get(getTestSelector('nft-collection-asset')).first().click()
|
cy.get(getTestSelector('nft-collection-asset')).first().click()
|
||||||
@ -37,7 +37,7 @@ describe('Testing nfts', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should toggle buy now on details page', () => {
|
it('should toggle buy now on details page', () => {
|
||||||
cy.visit(`/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
|
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
|
||||||
cy.get(getTestSelector('nft-filter')).first().click()
|
cy.get(getTestSelector('nft-filter')).first().click()
|
||||||
cy.get(getTestSelector('nft-collection-filter-buy-now')).click()
|
cy.get(getTestSelector('nft-collection-filter-buy-now')).click()
|
||||||
cy.get(getTestSelector('nft-collection-asset')).first().click()
|
cy.get(getTestSelector('nft-collection-asset')).first().click()
|
||||||
|
|||||||
@ -1,33 +1,7 @@
|
|||||||
import { ChainId, MaxUint256, UNI_ADDRESSES } from '@uniswap/sdk-core'
|
|
||||||
|
|
||||||
const UNI_MAINNET = UNI_ADDRESSES[ChainId.MAINNET]
|
|
||||||
|
|
||||||
describe('Remove Liquidity', () => {
|
describe('Remove Liquidity', () => {
|
||||||
it('loads the token pair in v2', () => {
|
it('loads the token pair', () => {
|
||||||
cy.visit(`/remove/v2/ETH/${UNI_MAINNET}`)
|
cy.visit('/remove/v2/ETH/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
|
||||||
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'ETH')
|
cy.get('#remove-liquidity-tokena-symbol').should('contain.text', 'ETH')
|
||||||
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'UNI')
|
cy.get('#remove-liquidity-tokenb-symbol').should('contain.text', 'UNI')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('loads the token pair in v3', () => {
|
|
||||||
cy.visit(`/remove/1`)
|
|
||||||
cy.get('#remove-liquidity-tokens').should('contain.text', 'UNI/ETH')
|
|
||||||
|
|
||||||
cy.get('#remove-pooled-tokena-symbol').should('contain.text', 'Pooled UNI')
|
|
||||||
cy.get('#remove-pooled-tokenb-symbol').should('contain.text', 'Pooled ETH')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should redirect to error pages if pool does not exist', () => {
|
|
||||||
// Duplicate-token v2 pools redirect to position unavailable
|
|
||||||
cy.visit(`/remove/v2/ETH/ETH`)
|
|
||||||
cy.contains('Position unavailable')
|
|
||||||
|
|
||||||
// Single-token pools don't exist
|
|
||||||
cy.visit('/remove/v2/ETH')
|
|
||||||
cy.url().should('match', /\/not-found/)
|
|
||||||
|
|
||||||
// Nonexistent v3 pool
|
|
||||||
cy.visit(`/remove/${MaxUint256}`)
|
|
||||||
cy.contains('Position unavailable')
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { BigNumber } from '@ethersproject/bignumber'
|
import { BigNumber } from '@ethersproject/bignumber'
|
||||||
import { InterfaceSectionName } from '@uniswap/analytics-events'
|
|
||||||
import { CurrencyAmount } from '@uniswap/sdk-core'
|
import { CurrencyAmount } from '@uniswap/sdk-core'
|
||||||
|
|
||||||
import { DEFAULT_DEADLINE_FROM_NOW } from '../../../src/constants/misc'
|
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('open-settings-dialog-button')).click()
|
||||||
cy.get(getTestSelector('max-slippage-settings')).click()
|
cy.get(getTestSelector('max-slippage-settings')).click()
|
||||||
cy.get(getTestSelector('slippage-input')).clear().type('0.01')
|
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('body').click('topRight') // close modal
|
||||||
cy.get(getTestSelector('slippage-input')).should('not.exist')
|
cy.get(getTestSelector('slippage-input')).should('not.exist')
|
||||||
|
|
||||||
@ -118,18 +116,4 @@ describe('Swap errors', () => {
|
|||||||
getBalance(DAI).should('be.closeTo', initialBalance + 200, 1)
|
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%)')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@ -1,34 +1,29 @@
|
|||||||
|
import { FeatureFlag } from '../../../src/featureFlags'
|
||||||
import { getTestSelector } from '../../utils'
|
import { getTestSelector } from '../../utils'
|
||||||
|
|
||||||
describe('Swap settings', () => {
|
describe('Swap settings', () => {
|
||||||
it('Opens and closes the settings menu', () => {
|
it('Opens and closes the settings menu', () => {
|
||||||
cy.visit('/swap')
|
cy.visit('/swap', { featureFlags: [FeatureFlag.uniswapXEnabled] })
|
||||||
cy.contains('Settings').should('not.exist')
|
cy.contains('Settings').should('not.exist')
|
||||||
cy.get(getTestSelector('open-settings-dialog-button')).click()
|
cy.get(getTestSelector('open-settings-dialog-button')).click()
|
||||||
cy.get(getTestSelector('mobile-settings-menu')).should('not.exist')
|
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('Transaction deadline').should('exist')
|
||||||
cy.contains('UniswapX').should('exist')
|
cy.contains('UniswapX').should('exist')
|
||||||
|
cy.contains('Local routing').should('exist')
|
||||||
cy.get(getTestSelector('open-settings-dialog-button')).click()
|
cy.get(getTestSelector('open-settings-dialog-button')).click()
|
||||||
cy.contains('Settings').should('not.exist')
|
cy.contains('Settings').should('not.exist')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should open the mobile settings menu', () => {
|
it('should open the mobile settings menu', () => {
|
||||||
// Set viewport to iPhone 6
|
|
||||||
cy.viewport('iphone-6')
|
cy.viewport('iphone-6')
|
||||||
cy.visit('/swap')
|
cy.visit('/swap', { featureFlags: [FeatureFlag.uniswapXEnabled] })
|
||||||
|
cy.get(getTestSelector('open-settings-dialog-button')).click()
|
||||||
// Click the button to open the settings dialog
|
cy.get(getTestSelector('mobile-settings-menu')).should('exist')
|
||||||
cy.get(getTestSelector('open-settings-dialog-button')).click({ waitForAnimations: true })
|
cy.contains('Max slippage').should('exist')
|
||||||
|
cy.contains('Transaction deadline').should('exist')
|
||||||
// Verify the mobile settings menu and its contents
|
cy.contains('UniswapX').should('exist')
|
||||||
cy.get(getTestSelector('mobile-settings-menu'))
|
cy.contains('Local routing').should('exist')
|
||||||
.should('exist')
|
cy.get(getTestSelector('mobile-settings-scrim')).click()
|
||||||
.within(() => {
|
|
||||||
cy.contains('Max. slippage').should('exist')
|
|
||||||
cy.contains('UniswapX').should('exist')
|
|
||||||
cy.contains('Transaction deadline').should('exist')
|
|
||||||
cy.get(getTestSelector('mobile-settings-close')).click()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
27
cypress/e2e/swap/unconnected.test.ts
Normal file
27
cypress/e2e/swap/unconnected.test.ts
Normal file
@ -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 { ChainId, CurrencyAmount } from '@uniswap/sdk-core'
|
||||||
import { CyHttpMessages } from 'cypress/types/net-stubbing'
|
|
||||||
|
|
||||||
import { DAI, nativeOnChain, USDC_MAINNET } from '../../../src/constants/tokens'
|
import { DAI, nativeOnChain, USDC_MAINNET } from '../../../src/constants/tokens'
|
||||||
import { getTestSelector } from '../../utils'
|
import { getTestSelector } from '../../utils'
|
||||||
|
|
||||||
|
const QuoteEndpoint = 'https://api.uniswap.org/v2/quote'
|
||||||
const QuoteWhereUniswapXIsBetter = 'uniswapx/quote1.json'
|
const QuoteWhereUniswapXIsBetter = 'uniswapx/quote1.json'
|
||||||
const QuoteWithEthInput = 'uniswapx/quote2.json'
|
const QuoteWithEthInput = 'uniswapx/quote2.json'
|
||||||
|
|
||||||
const QuoteEndpoint = 'https://api.uniswap.org/v2/quote'
|
|
||||||
const OrderSubmissionEndpoint = 'https://api.uniswap.org/v2/order'
|
const OrderSubmissionEndpoint = 'https://api.uniswap.org/v2/order'
|
||||||
|
|
||||||
const OrderStatusEndpoint =
|
const OrderStatusEndpoint =
|
||||||
'https://api.uniswap.org/v2/orders?swapper=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266&orderHashes=0xa9dd6f05ad6d6c79bee654c31ede4d0d2392862711be0f3bc4a9124af24a6a19'
|
'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 */
|
/** Stubs the provider to return a tx receipt corresponding to the mock filled uniswapx order's txHash */
|
||||||
function stubSwapTxReceipt() {
|
function stubSwapTxReceipt() {
|
||||||
cy.hardhat().then((hardhat) => {
|
cy.hardhat().then((hardhat) => {
|
||||||
@ -41,26 +23,53 @@ function stubSwapTxReceipt() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: FIX THESE TESTS where we should NOT stub for pricing requests
|
describe('UniswapX Toggle', () => {
|
||||||
describe.skip('UniswapX Toggle', () => {
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
stubNonPriceQuoteWith(QuoteWhereUniswapXIsBetter)
|
cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter })
|
||||||
cy.visit(`/swap/?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`)
|
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
|
// Setup a swap
|
||||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
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
|
// UniswapX UI should be visible
|
||||||
cy.get(getTestSelector('gas-estimate-uniswapx-icon')).should('exist')
|
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(() => {
|
beforeEach(() => {
|
||||||
stubNonPriceQuoteWith(QuoteWhereUniswapXIsBetter)
|
cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter })
|
||||||
cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' })
|
cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' })
|
||||||
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/openStatusResponse.json' })
|
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/openStatusResponse.json' })
|
||||||
|
|
||||||
@ -73,7 +82,7 @@ describe.skip('UniswapX Orders', () => {
|
|||||||
it('can swap exact-in trades using uniswapX', () => {
|
it('can swap exact-in trades using uniswapX', () => {
|
||||||
// Setup a swap
|
// Setup a swap
|
||||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
cy.get('#swap-currency-input .token-amount-input').type('300')
|
||||||
cy.wait('@quote')
|
cy.contains('Try it now').click()
|
||||||
|
|
||||||
// Submit uniswapx order signature
|
// Submit uniswapx order signature
|
||||||
cy.get('#swap-button').click()
|
cy.get('#swap-button').click()
|
||||||
@ -92,7 +101,7 @@ describe.skip('UniswapX Orders', () => {
|
|||||||
it('can swap exact-out trades using uniswapX', () => {
|
it('can swap exact-out trades using uniswapX', () => {
|
||||||
// Setup a swap
|
// Setup a swap
|
||||||
cy.get('#swap-currency-output .token-amount-input').type('300')
|
cy.get('#swap-currency-output .token-amount-input').type('300')
|
||||||
cy.wait('@quote')
|
cy.contains('Try it now').click()
|
||||||
|
|
||||||
// Submit uniswapx order signature
|
// Submit uniswapx order signature
|
||||||
cy.get('#swap-button').click()
|
cy.get('#swap-button').click()
|
||||||
@ -111,7 +120,7 @@ describe.skip('UniswapX Orders', () => {
|
|||||||
it('renders proper view if uniswapx order expires', () => {
|
it('renders proper view if uniswapx order expires', () => {
|
||||||
// Setup a swap
|
// Setup a swap
|
||||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
cy.get('#swap-currency-input .token-amount-input').type('300')
|
||||||
cy.wait('@quote')
|
cy.contains('Try it now').click()
|
||||||
|
|
||||||
// Submit uniswapx order signature
|
// Submit uniswapx order signature
|
||||||
cy.get('#swap-button').click()
|
cy.get('#swap-button').click()
|
||||||
@ -127,7 +136,7 @@ describe.skip('UniswapX Orders', () => {
|
|||||||
it('renders proper view if uniswapx order has insufficient funds', () => {
|
it('renders proper view if uniswapx order has insufficient funds', () => {
|
||||||
// Setup a swap
|
// Setup a swap
|
||||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
cy.get('#swap-currency-input .token-amount-input').type('300')
|
||||||
cy.wait('@quote')
|
cy.contains('Try it now').click()
|
||||||
|
|
||||||
// Submit uniswapx order signature
|
// Submit uniswapx order signature
|
||||||
cy.get('#swap-button').click()
|
cy.get('#swap-button').click()
|
||||||
@ -141,9 +150,9 @@ describe.skip('UniswapX Orders', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe.skip('UniswapX Eth Input', () => {
|
describe('UniswapX Eth Input', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
stubNonPriceQuoteWith(QuoteWithEthInput)
|
cy.intercept(QuoteEndpoint, { fixture: QuoteWithEthInput })
|
||||||
cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' })
|
cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' })
|
||||||
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/openStatusResponse.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', () => {
|
it('can swap using uniswapX with ETH as input', () => {
|
||||||
// Setup a swap
|
// Setup a swap
|
||||||
cy.get('#swap-currency-input .token-amount-input').type('1')
|
cy.get('#swap-currency-input .token-amount-input').type('1')
|
||||||
|
cy.contains('Try it now').click()
|
||||||
cy.wait('@quote')
|
|
||||||
|
|
||||||
// Prompt ETH wrap to use for order
|
// Prompt ETH wrap to use for order
|
||||||
cy.get('#swap-button').click()
|
cy.get('#swap-button').click()
|
||||||
@ -191,10 +199,10 @@ describe.skip('UniswapX Eth Input', () => {
|
|||||||
cy.contains('Swapped')
|
cy.contains('Swapped')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('keeps ETH as the input currency before wrap completes', () => {
|
it('switches swap input to WETH after wrap', () => {
|
||||||
// Setup a swap
|
// Setup a swap
|
||||||
cy.get('#swap-currency-input .token-amount-input').type('1')
|
cy.get('#swap-currency-input .token-amount-input').type('1')
|
||||||
cy.wait('@quote')
|
cy.contains('Try it now').click()
|
||||||
|
|
||||||
// Prompt ETH wrap and confirm
|
// Prompt ETH wrap and confirm
|
||||||
cy.get('#swap-button').click()
|
cy.get('#swap-button').click()
|
||||||
@ -203,25 +211,16 @@ describe.skip('UniswapX Eth Input', () => {
|
|||||||
|
|
||||||
// Close review modal before wrap is confirmed on chain
|
// Close review modal before wrap is confirmed on chain
|
||||||
cy.get(getTestSelector('confirmation-close-icon')).click()
|
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())
|
cy.hardhat().then((hardhat) => hardhat.mine())
|
||||||
|
|
||||||
// Confirm wrap is successful and WETH is now input token
|
// Confirm wrap is successful and WETH is now input token
|
||||||
cy.contains('Wrapped')
|
cy.contains('Wrapped')
|
||||||
cy.contains('WETH')
|
cy.contains('WETH')
|
||||||
|
|
||||||
|
// Reopen review modal and continue swap
|
||||||
|
cy.get('#swap-button').click()
|
||||||
|
cy.contains('Confirm swap').click()
|
||||||
|
|
||||||
// Approve WETH spend
|
// Approve WETH spend
|
||||||
cy.wait('@eth_sendRawTransaction')
|
cy.wait('@eth_sendRawTransaction')
|
||||||
cy.hardhat().then((hardhat) => hardhat.mine())
|
cy.hardhat().then((hardhat) => hardhat.mine())
|
||||||
@ -236,15 +235,10 @@ describe.skip('UniswapX Eth Input', () => {
|
|||||||
|
|
||||||
// Verify swap success
|
// Verify swap success
|
||||||
cy.contains('Swapped')
|
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(() => {
|
beforeEach(() => {
|
||||||
cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter })
|
cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter })
|
||||||
cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' })
|
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', () => {
|
it('can view UniswapX order status progress in activity', () => {
|
||||||
// Setup a swap
|
// Setup a swap
|
||||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
cy.get('#swap-currency-input .token-amount-input').type('300')
|
||||||
|
cy.contains('Try it now').click()
|
||||||
|
|
||||||
// Submit uniswapx order signature
|
// Submit uniswapx order signature
|
||||||
cy.get('#swap-button').click()
|
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', () => {
|
it('can view UniswapX order status progress in activity upon expiry', () => {
|
||||||
// Setup a swap
|
// Setup a swap
|
||||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
cy.get('#swap-currency-input .token-amount-input').type('300')
|
||||||
|
cy.contains('Try it now').click()
|
||||||
|
|
||||||
// Submit uniswapx order signature
|
// Submit uniswapx order signature
|
||||||
cy.get('#swap-button').click()
|
cy.get('#swap-button').click()
|
||||||
@ -314,6 +310,7 @@ describe.skip('UniswapX activity history', () => {
|
|||||||
it('deduplicates remote vs local uniswapx orders', () => {
|
it('deduplicates remote vs local uniswapx orders', () => {
|
||||||
// Setup a swap
|
// Setup a swap
|
||||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
cy.get('#swap-currency-input .token-amount-input').type('300')
|
||||||
|
cy.contains('Try it now').click()
|
||||||
|
|
||||||
// Submit uniswapx order signature
|
// Submit uniswapx order signature
|
||||||
cy.get('#swap-button').click()
|
cy.get('#swap-button').click()
|
||||||
@ -338,13 +335,14 @@ describe.skip('UniswapX activity history', () => {
|
|||||||
// Open activity history
|
// Open activity history
|
||||||
cy.get(getTestSelector('mini-portfolio-navbar')).contains('Activity').click()
|
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)
|
cy.get(getTestSelector('activity-content')).contains('Swapped').should('have.length', 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('balances should refetch after uniswapx swap', () => {
|
it('balances should refetch after uniswapx swap', () => {
|
||||||
// Setup a swap
|
// Setup a swap
|
||||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
cy.get('#swap-currency-input .token-amount-input').type('300')
|
||||||
|
cy.contains('Try it now').click()
|
||||||
|
|
||||||
const gqlSpy = cy.spy().as('gqlSpy')
|
const gqlSpy = cy.spy().as('gqlSpy')
|
||||||
cy.intercept(/graphql/, (req) => {
|
cy.intercept(/graphql/, (req) => {
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { ChainId, WETH9 } from '@uniswap/sdk-core'
|
import { ChainId, WETH9 } from '@uniswap/sdk-core'
|
||||||
import { FeatureFlag } from 'featureFlags'
|
|
||||||
|
|
||||||
import { ARB, UNI } from '../../src/constants/tokens'
|
import { ARB, UNI } from '../../src/constants/tokens'
|
||||||
import { getTestSelector } from '../utils'
|
import { getTestSelector } from '../utils'
|
||||||
@ -15,9 +14,8 @@ describe('Token details', () => {
|
|||||||
|
|
||||||
it('Uniswap token should have all information populated', () => {
|
it('Uniswap token should have all information populated', () => {
|
||||||
// Uniswap token
|
// Uniswap token
|
||||||
cy.visit(`/tokens/ethereum/${UNI_ADDRESS}`, {
|
cy.visit(`/tokens/ethereum/${UNI_ADDRESS}`)
|
||||||
featureFlags: [{ name: FeatureFlag.infoTDP, value: false }],
|
|
||||||
})
|
|
||||||
// Price chart should be filled in
|
// Price chart should be filled in
|
||||||
cy.get('[data-cy="chart-header"]').should('include.text', '$')
|
cy.get('[data-cy="chart-header"]').should('include.text', '$')
|
||||||
cy.get('[data-cy="price-chart"]').should('exist')
|
cy.get('[data-cy="price-chart"]').should('exist')
|
||||||
@ -49,50 +47,41 @@ describe('Token details', () => {
|
|||||||
cy.contains(UNI_ADDRESS).should('exist')
|
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', () => {
|
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
|
// Shiba predator token, low trading volume and also has warning modal
|
||||||
cy.visit('/tokens/ethereum/0x1eFBB78C8b917f67986BcE54cE575069c0143681')
|
cy.visit('/tokens/ethereum/0xa71d0588EAf47f12B13cF8eC750430d21DF04974')
|
||||||
|
|
||||||
// Should have missing price chart when price unavailable (expected for this token)
|
// 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')
|
cy.get('[data-cy="missing-chart"]').should('exist')
|
||||||
}
|
}
|
||||||
|
// Stats should have: TVL, 24H Volume, 52W low, 52W high
|
||||||
// Stats should not exist
|
cy.get(getTestSelector('token-details-stats')).should('exist')
|
||||||
cy.get(getTestSelector('token-details-stats')).should('not.exist')
|
cy.get(getTestSelector('token-details-stats')).within(() => {
|
||||||
|
cy.get('[data-cy="tvl"]').should('exist')
|
||||||
|
cy.get('[data-cy="volume-24h"]').should('exist')
|
||||||
|
cy.get('[data-cy="52w-low"]').should('exist')
|
||||||
|
cy.get('[data-cy="52w-high"]').should('exist')
|
||||||
|
})
|
||||||
|
|
||||||
// About section should have description of token
|
// About section should have description of token
|
||||||
cy.get(getTestSelector('token-details-about-section')).should('exist')
|
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.get('[data-cy="resources-container"]').within(() => {
|
||||||
cy.contains('Etherscan')
|
cy.contains('Etherscan')
|
||||||
.should('have.attr', 'href')
|
.should('have.attr', 'href')
|
||||||
.and('include', 'etherscan.io/address/0x1eFBB78C8b917f67986BcE54cE575069c0143681')
|
.and('include', 'etherscan.io/address/0xa71d0588EAf47f12B13cF8eC750430d21DF04974')
|
||||||
cy.contains('More analytics')
|
cy.contains('More analytics')
|
||||||
.should('have.attr', 'href')
|
.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
|
// 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))
|
// Warning label should show if relevant ([spec](https://www.notion.so/3f7fce6f93694be08a94a6984d50298e))
|
||||||
cy.get('[data-cy="token-safety-message"]')
|
cy.get('[data-cy="token-safety-message"]')
|
||||||
|
|||||||
@ -59,7 +59,9 @@ describe('Token explore', () => {
|
|||||||
// in metamask modal using plain cypress. this is a workaround.
|
// in metamask modal using plain cypress. this is a workaround.
|
||||||
cy.visit('/tokens/polygon')
|
cy.visit('/tokens/polygon')
|
||||||
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', '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', () => {
|
it('should update when token explore table network changed', () => {
|
||||||
@ -67,6 +69,8 @@ describe('Token explore', () => {
|
|||||||
cy.get(getTestSelector('tokens-network-filter-selected')).click()
|
cy.get(getTestSelector('tokens-network-filter-selected')).click()
|
||||||
cy.get(getTestSelector('tokens-network-filter-option-optimism')).click()
|
cy.get(getTestSelector('tokens-network-filter-option-optimism')).click()
|
||||||
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism')
|
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism')
|
||||||
cy.get(getTestSelector('chain-selector-logo')).find('title').should('include.text', 'Ethereum logo')
|
cy.reload()
|
||||||
|
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism')
|
||||||
|
cy.get(getTestSelector('chain-selector-logo')).invoke('attr', 'alt').should('eq', 'Ethereum')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -30,7 +30,7 @@ describe('Universal search bar', () => {
|
|||||||
.and('contain.text', '$')
|
.and('contain.text', '$')
|
||||||
.and('contain.text', '%')
|
.and('contain.text', '%')
|
||||||
.click()
|
.click()
|
||||||
cy.location('pathname').should('equal', '/tokens/ethereum/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984')
|
cy.location('hash').should('equal', '#/tokens/ethereum/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984')
|
||||||
|
|
||||||
openSearch()
|
openSearch()
|
||||||
cy.get(getTestSelector('searchbar-dropdown'))
|
cy.get(getTestSelector('searchbar-dropdown'))
|
||||||
@ -65,7 +65,7 @@ describe('Universal search bar', () => {
|
|||||||
cy.get(getTestSelector('searchbar-token-row-ETHEREUM-NATIVE'))
|
cy.get(getTestSelector('searchbar-token-row-ETHEREUM-NATIVE'))
|
||||||
|
|
||||||
// Validate that we go to the searched/selected result.
|
// 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')
|
cy.url().should('contain', 'tokens/ethereum/NATIVE')
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@ -19,7 +19,7 @@ describe('disconnect wallet', () => {
|
|||||||
// Verify wallet has disconnected
|
// Verify wallet has disconnected
|
||||||
cy.contains('Connect a wallet').should('exist')
|
cy.contains('Connect a wallet').should('exist')
|
||||||
cy.get(getTestSelector('navbar-connect-wallet')).contains('Connect')
|
cy.get(getTestSelector('navbar-connect-wallet')).contains('Connect')
|
||||||
cy.contains('Connect wallet')
|
cy.contains('Connect Wallet')
|
||||||
|
|
||||||
// Verify swap input is cleared
|
// Verify swap input is cleared
|
||||||
cy.get('#swap-currency-input .token-amount-input').should('have.value', '1')
|
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'
|
import { getTestSelector } from '../../utils'
|
||||||
|
|
||||||
function waitsForActiveChain(chain: string) {
|
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) {
|
function switchChain(chain: string) {
|
||||||
|
|||||||
@ -32,11 +32,11 @@ describe('Wallet Dropdown', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cy.get(getTestSelector('wallet-language-item')).contains('Afrikaans').click({ force: true })
|
cy.get(getTestSelector('wallet-language-item')).contains('Afrikaans').click({ force: true })
|
||||||
cy.location('search').should('match', /\?lng=af-ZA$/)
|
cy.location('hash').should('match', /\?lng=af-ZA$/)
|
||||||
cy.contains('Uniswap available in: English')
|
cy.contains('Uniswap available in: English')
|
||||||
|
|
||||||
cy.get(getTestSelector('wallet-language-item')).contains('English').click({ force: true })
|
cy.get(getTestSelector('wallet-language-item')).contains('English').click({ force: true })
|
||||||
cy.location('search').should('match', /\?lng=en-US$/)
|
cy.location('hash').should('match', /\?lng=en-US$/)
|
||||||
cy.contains('Uniswap available in: English').should('not.exist')
|
cy.contains('Uniswap available in: English').should('not.exist')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -49,39 +49,11 @@ describe('Wallet Dropdown', () => {
|
|||||||
})
|
})
|
||||||
itChangesTheme()
|
itChangesTheme()
|
||||||
itChangesLocale()
|
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', () => {
|
describe('should change locale with feature flag', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.visit('/', { featureFlags: [{ name: FeatureFlag.currencyConversion, value: true }] })
|
cy.visit('/', { featureFlags: [FeatureFlag.currencyConversion] })
|
||||||
cy.get(getTestSelector('web3-status-connected')).click()
|
cy.get(getTestSelector('web3-status-connected')).click()
|
||||||
cy.get(getTestSelector('wallet-settings')).click()
|
cy.get(getTestSelector('wallet-settings')).click()
|
||||||
})
|
})
|
||||||
@ -172,34 +144,4 @@ describe('Wallet Dropdown', () => {
|
|||||||
cy.get(getTestSelector('wallet-settings')).should('not.be.visible')
|
cy.get(getTestSelector('wallet-settings')).should('not.be.visible')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('local currency', () => {
|
|
||||||
it('loads local currency from the query param', () => {
|
|
||||||
cy.visit('/', { featureFlags: [{ name: FeatureFlag.currencyConversion, value: true }] })
|
|
||||||
cy.get(getTestSelector('web3-status-connected')).click()
|
|
||||||
cy.get(getTestSelector('wallet-settings')).click()
|
|
||||||
cy.contains('USD')
|
|
||||||
|
|
||||||
cy.visit('/?cur=AUD', { featureFlags: [{ name: FeatureFlag.currencyConversion, value: true }] })
|
|
||||||
cy.get(getTestSelector('web3-status-connected')).click()
|
|
||||||
cy.get(getTestSelector('wallet-settings')).click()
|
|
||||||
cy.contains('AUD')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('loads local currency from menu', () => {
|
|
||||||
cy.visit('/', { featureFlags: [{ name: FeatureFlag.currencyConversion, value: true }] })
|
|
||||||
cy.get(getTestSelector('web3-status-connected')).click()
|
|
||||||
cy.get(getTestSelector('wallet-settings')).click()
|
|
||||||
cy.contains('USD')
|
|
||||||
|
|
||||||
cy.get(getTestSelector('local-currency-settings-button')).click()
|
|
||||||
cy.get(getTestSelector('wallet-local-currency-item')).contains('AUD').click({ force: true })
|
|
||||||
cy.location('search').should('match', /\?cur=AUD$/)
|
|
||||||
cy.contains('AUD')
|
|
||||||
|
|
||||||
cy.get(getTestSelector('wallet-local-currency-item')).contains('USD').click({ force: true })
|
|
||||||
cy.location('search').should('match', /\?cur=USD$/)
|
|
||||||
cy.contains('USD')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"errorCode": "QUOTE_ERROR",
|
|
||||||
"detail": "No quotes available",
|
|
||||||
"id": "63363cc1-d474-4584-b386-7c356814b79f"
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -32,7 +32,6 @@
|
|||||||
"quoteId": "f9f47cd7-a62c-4622-9ac7-51d0e662245a",
|
"quoteId": "f9f47cd7-a62c-4622-9ac7-51d0e662245a",
|
||||||
"requestId": "2d16f993-6429-4755-ba50-1383789459dc",
|
"requestId": "2d16f993-6429-4755-ba50-1383789459dc",
|
||||||
"auctionPeriodSecs": 60,
|
"auctionPeriodSecs": 60,
|
||||||
"startTimeBufferSecs": 30,
|
|
||||||
"deadlineBufferSecs": 12,
|
"deadlineBufferSecs": 12,
|
||||||
"slippageTolerance": "0.5",
|
"slippageTolerance": "0.5",
|
||||||
"permitData": {
|
"permitData": {
|
||||||
@ -253,7 +252,6 @@
|
|||||||
"quoteId": "f9f47cd7-a62c-4622-9ac7-51d0e662245a",
|
"quoteId": "f9f47cd7-a62c-4622-9ac7-51d0e662245a",
|
||||||
"requestId": "2d16f993-6429-4755-ba50-1383789459dc",
|
"requestId": "2d16f993-6429-4755-ba50-1383789459dc",
|
||||||
"auctionPeriodSecs": 60,
|
"auctionPeriodSecs": 60,
|
||||||
"startTimeBufferSecs": 30,
|
|
||||||
"deadlineBufferSecs": 12,
|
"deadlineBufferSecs": 12,
|
||||||
"slippageTolerance": "0.5",
|
"slippageTolerance": "0.5",
|
||||||
"permitData": {
|
"permitData": {
|
||||||
|
|||||||
@ -32,7 +32,6 @@
|
|||||||
"quoteId": "09ce28b7-1ddf-4317-a28d-d21092be9f84",
|
"quoteId": "09ce28b7-1ddf-4317-a28d-d21092be9f84",
|
||||||
"requestId": "f00535d4-461a-4363-afbe-7a5ab7061cd1",
|
"requestId": "f00535d4-461a-4363-afbe-7a5ab7061cd1",
|
||||||
"auctionPeriodSecs": 60,
|
"auctionPeriodSecs": 60,
|
||||||
"startTimeBufferSecs": 30,
|
|
||||||
"deadlineBufferSecs": 12,
|
"deadlineBufferSecs": 12,
|
||||||
"slippageTolerance": "0.5",
|
"slippageTolerance": "0.5",
|
||||||
"permitData": {
|
"permitData": {
|
||||||
@ -253,7 +252,6 @@
|
|||||||
"quoteId": "09ce28b7-1ddf-4317-a28d-d21092be9f84",
|
"quoteId": "09ce28b7-1ddf-4317-a28d-d21092be9f84",
|
||||||
"requestId": "f00535d4-461a-4363-afbe-7a5ab7061cd1",
|
"requestId": "f00535d4-461a-4363-afbe-7a5ab7061cd1",
|
||||||
"auctionPeriodSecs": 60,
|
"auctionPeriodSecs": 60,
|
||||||
"startTimeBufferSecs": 30,
|
|
||||||
"deadlineBufferSecs": 12,
|
"deadlineBufferSecs": 12,
|
||||||
"slippageTolerance": "0.5",
|
"slippageTolerance": "0.5",
|
||||||
"permitData": {
|
"permitData": {
|
||||||
|
|||||||
@ -12,7 +12,7 @@ describe('translations', () => {
|
|||||||
cy.get(getTestSelector('web3-status-connected')).click()
|
cy.get(getTestSelector('web3-status-connected')).click()
|
||||||
cy.get(getTestSelector('wallet-settings')).click()
|
cy.get(getTestSelector('wallet-settings')).click()
|
||||||
cy.get(getTestSelector('wallet-language-item')).contains('français').click({ force: true })
|
cy.get(getTestSelector('wallet-language-item')).contains('français').click({ force: true })
|
||||||
cy.location('search').should('match', /\?lng=fr-FR$/)
|
cy.location('hash').should('match', /\?lng=fr-FR$/)
|
||||||
cy.contains('Échanger')
|
cy.contains('Échanger')
|
||||||
cy.contains('Uniswap disponible en : English')
|
cy.contains('Uniswap disponible en : English')
|
||||||
})
|
})
|
||||||
|
|||||||
@ -24,7 +24,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
interface VisitOptions {
|
interface VisitOptions {
|
||||||
serviceWorker?: true
|
serviceWorker?: true
|
||||||
featureFlags?: Array<{ name: FeatureFlag; value: boolean }>
|
featureFlags?: Array<FeatureFlag>
|
||||||
/**
|
/**
|
||||||
* Initial user state.
|
* Initial user state.
|
||||||
* @default {@type import('../utils/user-state').CONNECTED_WALLET_USER_STATE}
|
* @default {@type import('../utils/user-state').CONNECTED_WALLET_USER_STATE}
|
||||||
@ -41,28 +41,29 @@ Cypress.Commands.overwrite(
|
|||||||
(original, url: string | Partial<Cypress.VisitOptions>, options?: Partial<Cypress.VisitOptions>) => {
|
(original, url: string | Partial<Cypress.VisitOptions>, options?: Partial<Cypress.VisitOptions>) => {
|
||||||
if (typeof url !== 'string') throw new Error('Invalid arguments. The first argument to cy.visit must be the path.')
|
if (typeof url !== 'string') throw new Error('Invalid arguments. The first argument to cy.visit must be the path.')
|
||||||
|
|
||||||
|
// Add a hash in the URL if it is not present (to use hash-based routing correctly with queryParams).
|
||||||
|
const hashUrl = url.startsWith('/') && url.length > 2 && !url.startsWith('/#') ? `/#${url}` : url
|
||||||
|
|
||||||
return cy
|
return cy
|
||||||
.intercept('/service-worker.js', options?.serviceWorker ? undefined : { statusCode: 404 })
|
.intercept('/service-worker.js', options?.serviceWorker ? undefined : { statusCode: 404 })
|
||||||
.provider()
|
.provider()
|
||||||
.then((provider) =>
|
.then((provider) =>
|
||||||
original({
|
original({
|
||||||
...options,
|
...options,
|
||||||
url,
|
url: hashUrl,
|
||||||
onBeforeLoad(win) {
|
onBeforeLoad(win) {
|
||||||
options?.onBeforeLoad?.(win)
|
options?.onBeforeLoad?.(win)
|
||||||
|
|
||||||
setInitialUserState(win, {
|
setInitialUserState(win, {
|
||||||
...initialState,
|
...initialState,
|
||||||
|
hideUniswapWalletBanner: true,
|
||||||
...CONNECTED_WALLET_USER_STATE,
|
...CONNECTED_WALLET_USER_STATE,
|
||||||
...(options?.userState ?? {}),
|
...(options?.userState ?? {}),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Set feature flags, if configured.
|
// Set feature flags, if configured.
|
||||||
if (options?.featureFlags) {
|
if (options?.featureFlags) {
|
||||||
const featureFlags = options.featureFlags.reduce(
|
const featureFlags = options.featureFlags.reduce((flags, flag) => ({ ...flags, [flag]: 'enabled' }), {})
|
||||||
(flags, flag) => ({ ...flags, [flag.name]: flag.value ? 'enabled' : 'control' }),
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
win.localStorage.setItem('featureFlags', JSON.stringify(featureFlags))
|
win.localStorage.setItem('featureFlags', JSON.stringify(featureFlags))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,14 +75,18 @@ Cypress.Commands.overwrite(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
Cypress.Commands.add('waitForAmplitudeEvent', (eventName) => {
|
Cypress.Commands.add('waitForAmplitudeEvent', (eventName, timeout = 5000 /* 5s */) => {
|
||||||
|
const startTime = new Date().getTime()
|
||||||
|
|
||||||
function checkRequest() {
|
function checkRequest() {
|
||||||
return cy.wait('@amplitude').then((interception) => {
|
return cy.wait('@amplitude', { timeout }).then((interception) => {
|
||||||
const events = interception.request.body.events
|
const events = interception.request.body.events
|
||||||
const event = events.find((event: any) => event.event_type === eventName)
|
const event = events.find((event: any) => event.event_type === eventName)
|
||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
return cy.wrap(event)
|
return cy.wrap(event)
|
||||||
|
} else if (new Date().getTime() - startTime > timeout) {
|
||||||
|
throw new Error(`Event ${eventName} not found within the specified timeout`)
|
||||||
} else {
|
} else {
|
||||||
return checkRequest()
|
return checkRequest()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,9 +9,8 @@ beforeEach(() => {
|
|||||||
req.headers['origin'] = 'https://app.uniswap.org'
|
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(/infura.io/, { statusCode: 404 })
|
||||||
cy.intercept(/quiknode.pro/, { statusCode: 404 })
|
|
||||||
|
|
||||||
// Log requests to hardhat.
|
// Log requests to hardhat.
|
||||||
cy.intercept(/:8545/, logJsonRpc)
|
cy.intercept(/:8545/, logJsonRpc)
|
||||||
@ -27,16 +26,10 @@ beforeEach(() => {
|
|||||||
server_upload_time: Date.now(),
|
server_upload_time: Date.now(),
|
||||||
payload_size_bytes: byteSize,
|
payload_size_bytes: byteSize,
|
||||||
events_ingested: req.body.events.length,
|
events_ingested: req.body.events.length,
|
||||||
}),
|
})
|
||||||
{
|
|
||||||
'origin-country': 'US',
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}).intercept('https://*.sentry.io', { statusCode: 200 })
|
}).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.
|
// Mock our own token list responses to avoid the latency of IPFS.
|
||||||
cy.intercept('https://gateway.ipfs.io/ipns/tokens.uniswap.org', TokenListJSON)
|
cy.intercept('https://gateway.ipfs.io/ipns/tokens.uniswap.org', TokenListJSON)
|
||||||
.intercept('https://gateway.ipfs.io/ipns/extendedtokens.uniswap.org', { statusCode: 404 })
|
.intercept('https://gateway.ipfs.io/ipns/extendedtokens.uniswap.org', { statusCode: 404 })
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
"incremental": true,
|
"incremental": true,
|
||||||
"isolatedModules": false,
|
"isolatedModules": false,
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"target": "ES6",
|
"target": "ES5",
|
||||||
"tsBuildInfoFile": "../node_modules/.cache/tsbuildinfo/cypress", // avoid clobbering the build tsbuildinfo
|
"tsBuildInfoFile": "../node_modules/.cache/tsbuildinfo/cypress", // avoid clobbering the build tsbuildinfo
|
||||||
"types": ["cypress", "node"],
|
"types": ["cypress", "node"],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { connectionMetaKey } from '../../src/connection/meta'
|
|
||||||
import { ConnectionType } from '../../src/connection/types'
|
import { ConnectionType } from '../../src/connection/types'
|
||||||
import { UserState } from '../../src/state/user/reducer'
|
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
|
* Other persisted slices are not set, so they will be filled with their respective initial values
|
||||||
* when the app runs.
|
* when the app runs.
|
||||||
*/
|
*/
|
||||||
export function setInitialUserState(win: Cypress.AUTWindow, state: UserState) {
|
export function setInitialUserState(win: Cypress.AUTWindow, initialUserState: any) {
|
||||||
// Selected wallet should also be reflected in localStorage, so that eager connections work.
|
|
||||||
if (state.selectedWallet) {
|
|
||||||
win.localStorage.setItem(
|
|
||||||
connectionMetaKey,
|
|
||||||
JSON.stringify({
|
|
||||||
type: state.selectedWallet,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
win.indexedDB.deleteDatabase('redux')
|
win.indexedDB.deleteDatabase('redux')
|
||||||
|
|
||||||
const dbRequest = win.indexedDB.open('redux')
|
const dbRequest = win.indexedDB.open('redux')
|
||||||
|
|
||||||
dbRequest.onsuccess = function () {
|
dbRequest.onsuccess = function () {
|
||||||
const db = dbRequest.result
|
const db = dbRequest.result
|
||||||
const transaction = db.transaction('keyvaluepairs', 'readwrite')
|
const transaction = db.transaction('keyvaluepairs', 'readwrite')
|
||||||
const store = transaction.objectStore('keyvaluepairs')
|
const store = transaction.objectStore('keyvaluepairs')
|
||||||
store.put(
|
store.put(
|
||||||
{
|
{
|
||||||
user: state,
|
user: initialUserState,
|
||||||
},
|
},
|
||||||
'persist:interface'
|
'persist:interface'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
dbRequest.onupgradeneeded = function () {
|
dbRequest.onupgradeneeded = function () {
|
||||||
const db = dbRequest.result
|
const db = dbRequest.result
|
||||||
db.createObjectStore('keyvaluepairs')
|
db.createObjectStore('keyvaluepairs')
|
||||||
|
|||||||
@ -18,9 +18,9 @@ Currently, there are 2 types of cloudflare functions developed
|
|||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
Testing is done utilizing a custom jest environment as well as Cloudflare's local tester: `wrangler`. Wrangler enables testing locally by running a proxy to wrap `localhost`. Tests run against a proxy server, so you'll need to start it before running tests:
|
Testing is done utilizing a custom jest environment as well as Cloudflare's local tester: `wrangler`. Wrangler enables testing locally by running a proxy to wrap `localhost`. Testing can be done the following ways.
|
||||||
- Manually run `yarn start:cloud` to setup wrangler on `localhost:3000`
|
- Manually by running `yarn start:cloud` to setup wrangler on `localhost:3000`
|
||||||
- Run unit tests with `yarn test:cloud`
|
- Automated tests by running `yarn test:cloud` to setup both a jest and wrangler environment and automatically test features
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ export const onRequest: PagesFunction = async ({ request, next }) => {
|
|||||||
}
|
}
|
||||||
const res = next()
|
const res = next()
|
||||||
try {
|
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) {
|
} catch (e) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
439
functions/__snapshots__/default.test.ts.snap
Normal file
439
functions/__snapshots__/default.test.ts.snap
Normal file
@ -0,0 +1,439 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`should inject metadata for valid collections 1`] = `
|
||||||
|
"<!DOCTYPE html>
|
||||||
|
<html translate="no">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
|
||||||
|
<title>Uniswap Interface</title>
|
||||||
|
<meta name="description" content="Swap or provide liquidity on the Uniswap Protocol" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
will be replaced with the URL of the \`public\` folder during build.
|
||||||
|
Only files inside the \`public\` folder can be referenced from the HTML.
|
||||||
|
-->
|
||||||
|
<link rel="shortcut icon" type="image/png" href="/favicon.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="192x192" href="/images/192x192_App_Icon.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_App_Icon.png" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
|
<meta name="theme-color" content="#FC72FF" />
|
||||||
|
<meta
|
||||||
|
http-equiv="Content-Security-Policy"
|
||||||
|
|
||||||
|
content="script-src 'self' 'unsafe-inline'"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Apple Smart App Banner for Safari on iOS
|
||||||
|
https://developer.apple.com/documentation/webkit/promoting_apps_with_smart_app_banners
|
||||||
|
-->
|
||||||
|
<meta name="apple-itunes-app" content="app-id=6443944476">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when the app is installed as a PWA.
|
||||||
|
See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="/manifest.json" />
|
||||||
|
|
||||||
|
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
|
||||||
|
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
font-family: 'Basel', sans-serif;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Explicitly load Basel var from public/ so it does not block LCP's critical path.
|
||||||
|
*/
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Basel';
|
||||||
|
font-weight: 535;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
|
src:
|
||||||
|
url(/fonts/Basel-Medium.woff) format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Basel';
|
||||||
|
font-weight: 485;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
|
src:
|
||||||
|
url(/fonts/Basel-Book.woff) format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@supports (font-variation-settings: normal) {
|
||||||
|
* {
|
||||||
|
font-family: 'Basel', sans-serif;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 485;
|
||||||
|
font-variant: none;
|
||||||
|
font-smooth: always;
|
||||||
|
text-rendering: optimizeLegibility !important;
|
||||||
|
-webkit-font-smoothing: antialiased !important;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use this to apply network-specific gradient backgrounds, in RadialGradientByChainUpdater.ts */
|
||||||
|
#background-radial-gradient {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
width: 200vw;
|
||||||
|
height: 200vh;
|
||||||
|
transform: translate(-50vw, -100vh);
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#root {
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
html {
|
||||||
|
background: linear-gradient(180deg, #202738 0%, #070816 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
html {
|
||||||
|
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script defer src="/static/js/bundle.js"></script><meta property="og:title" content="Uniswap Interface"/><meta property="og:image" content="http://127.0.0.1:3000/images/1200x630_Rich_Link_Preview_Image.png"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="630"/><meta property="og:image:alt" content="Uniswap Interface"/><meta property="og:type" content="website"/><meta property="og:url" content="http://127.0.0.1:3000/"/><meta property="twitter:card" content="summary_large_image"/><meta property="twitter:title" content="Uniswap Interface"/><meta property="twitter:image" content="http://127.0.0.1:3000/images/1200x630_Rich_Link_Preview_Image.png"/><meta property="twitter:image:alt" content="Uniswap Interface"/></head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
|
||||||
|
<div id="root">
|
||||||
|
<!-- Triggers the font to load immediately and then is replaced by the app -->
|
||||||
|
<div> </div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="background-radial-gradient"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`should inject metadata for valid collections 2`] = `
|
||||||
|
"<!DOCTYPE html>
|
||||||
|
<html translate="no">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
|
||||||
|
<title>Uniswap Interface</title>
|
||||||
|
<meta name="description" content="Swap or provide liquidity on the Uniswap Protocol" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
will be replaced with the URL of the \`public\` folder during build.
|
||||||
|
Only files inside the \`public\` folder can be referenced from the HTML.
|
||||||
|
-->
|
||||||
|
<link rel="shortcut icon" type="image/png" href="/favicon.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="192x192" href="/images/192x192_App_Icon.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_App_Icon.png" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
|
<meta name="theme-color" content="#FC72FF" />
|
||||||
|
<meta
|
||||||
|
http-equiv="Content-Security-Policy"
|
||||||
|
|
||||||
|
content="script-src 'self' 'unsafe-inline'"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Apple Smart App Banner for Safari on iOS
|
||||||
|
https://developer.apple.com/documentation/webkit/promoting_apps_with_smart_app_banners
|
||||||
|
-->
|
||||||
|
<meta name="apple-itunes-app" content="app-id=6443944476">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when the app is installed as a PWA.
|
||||||
|
See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="/manifest.json" />
|
||||||
|
|
||||||
|
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
|
||||||
|
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
font-family: 'Basel', sans-serif;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Explicitly load Basel var from public/ so it does not block LCP's critical path.
|
||||||
|
*/
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Basel';
|
||||||
|
font-weight: 535;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
|
src:
|
||||||
|
url(/fonts/Basel-Medium.woff) format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Basel';
|
||||||
|
font-weight: 485;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
|
src:
|
||||||
|
url(/fonts/Basel-Book.woff) format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@supports (font-variation-settings: normal) {
|
||||||
|
* {
|
||||||
|
font-family: 'Basel', sans-serif;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 485;
|
||||||
|
font-variant: none;
|
||||||
|
font-smooth: always;
|
||||||
|
text-rendering: optimizeLegibility !important;
|
||||||
|
-webkit-font-smoothing: antialiased !important;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use this to apply network-specific gradient backgrounds, in RadialGradientByChainUpdater.ts */
|
||||||
|
#background-radial-gradient {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
width: 200vw;
|
||||||
|
height: 200vh;
|
||||||
|
transform: translate(-50vw, -100vh);
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#root {
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
html {
|
||||||
|
background: linear-gradient(180deg, #202738 0%, #070816 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
html {
|
||||||
|
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script defer src="/static/js/bundle.js"></script><meta property="og:title" content="Uniswap Interface"/><meta property="og:image" content="http://127.0.0.1:3000/images/1200x630_Rich_Link_Preview_Image.png"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="630"/><meta property="og:image:alt" content="Uniswap Interface"/><meta property="og:type" content="website"/><meta property="og:url" content="http://127.0.0.1:3000/swap"/><meta property="twitter:card" content="summary_large_image"/><meta property="twitter:title" content="Uniswap Interface"/><meta property="twitter:image" content="http://127.0.0.1:3000/images/1200x630_Rich_Link_Preview_Image.png"/><meta property="twitter:image:alt" content="Uniswap Interface"/></head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
|
||||||
|
<div id="root">
|
||||||
|
<!-- Triggers the font to load immediately and then is replaced by the app -->
|
||||||
|
<div> </div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="background-radial-gradient"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`should inject metadata for valid collections 3`] = `
|
||||||
|
"<!DOCTYPE html>
|
||||||
|
<html translate="no">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
|
||||||
|
<title>Uniswap Interface</title>
|
||||||
|
<meta name="description" content="Swap or provide liquidity on the Uniswap Protocol" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
will be replaced with the URL of the \`public\` folder during build.
|
||||||
|
Only files inside the \`public\` folder can be referenced from the HTML.
|
||||||
|
-->
|
||||||
|
<link rel="shortcut icon" type="image/png" href="/favicon.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="192x192" href="/images/192x192_App_Icon.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_App_Icon.png" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
|
<meta name="theme-color" content="#FC72FF" />
|
||||||
|
<meta
|
||||||
|
http-equiv="Content-Security-Policy"
|
||||||
|
|
||||||
|
content="script-src 'self' 'unsafe-inline'"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Apple Smart App Banner for Safari on iOS
|
||||||
|
https://developer.apple.com/documentation/webkit/promoting_apps_with_smart_app_banners
|
||||||
|
-->
|
||||||
|
<meta name="apple-itunes-app" content="app-id=6443944476">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when the app is installed as a PWA.
|
||||||
|
See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="/manifest.json" />
|
||||||
|
|
||||||
|
<link rel="preload" href="/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
|
||||||
|
<link rel="preload" href="/fonts/Basel-Medium.woff" as="font" type="font/woff" crossorigin />
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
font-family: 'Basel', sans-serif;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Explicitly load Basel var from public/ so it does not block LCP's critical path.
|
||||||
|
*/
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Basel';
|
||||||
|
font-weight: 535;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
|
src:
|
||||||
|
url(/fonts/Basel-Medium.woff) format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Basel';
|
||||||
|
font-weight: 485;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
|
src:
|
||||||
|
url(/fonts/Basel-Book.woff) format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
@supports (font-variation-settings: normal) {
|
||||||
|
* {
|
||||||
|
font-family: 'Basel', sans-serif;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 485;
|
||||||
|
font-variant: none;
|
||||||
|
font-smooth: always;
|
||||||
|
text-rendering: optimizeLegibility !important;
|
||||||
|
-webkit-font-smoothing: antialiased !important;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use this to apply network-specific gradient backgrounds, in RadialGradientByChainUpdater.ts */
|
||||||
|
#background-radial-gradient {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
width: 200vw;
|
||||||
|
height: 200vh;
|
||||||
|
transform: translate(-50vw, -100vh);
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
#root {
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
html {
|
||||||
|
background: linear-gradient(180deg, #202738 0%, #070816 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
html {
|
||||||
|
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script defer src="/static/js/bundle.js"></script><meta property="og:title" content="Uniswap Interface"/><meta property="og:image" content="http://127.0.0.1:3000/images/1200x630_Rich_Link_Preview_Image.png"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="630"/><meta property="og:image:alt" content="Uniswap Interface"/><meta property="og:type" content="website"/><meta property="og:url" content="http://127.0.0.1:3000/pools"/><meta property="twitter:card" content="summary_large_image"/><meta property="twitter:title" content="Uniswap Interface"/><meta property="twitter:image" content="http://127.0.0.1:3000/images/1200x630_Rich_Link_Preview_Image.png"/><meta property="twitter:image:alt" content="Uniswap Interface"/></head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
|
||||||
|
<div id="root">
|
||||||
|
<!-- Triggers the font to load immediately and then is replaced by the app -->
|
||||||
|
<div> </div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="background-radial-gradient"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"
|
||||||
|
`;
|
||||||
@ -2,7 +2,6 @@
|
|||||||
import { ImageResponse } from '@vercel/og'
|
import { ImageResponse } from '@vercel/og'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { blocklistedCollections } from '../../../../../src/nft/utils/blocklist'
|
|
||||||
import { WATERMARK_URL } from '../../../../constants'
|
import { WATERMARK_URL } from '../../../../constants'
|
||||||
import getAsset from '../../../../utils/getAsset'
|
import getAsset from '../../../../utils/getAsset'
|
||||||
import getFont from '../../../../utils/getFont'
|
import getFont from '../../../../utils/getFont'
|
||||||
@ -16,10 +15,6 @@ export const onRequest: PagesFunction = async ({ params, request }) => {
|
|||||||
const tokenId = index[1]?.toString()
|
const tokenId = index[1]?.toString()
|
||||||
const cacheUrl = origin + '/nfts/asset/' + collectionAddress + '/' + tokenId
|
const cacheUrl = origin + '/nfts/asset/' + collectionAddress + '/' + tokenId
|
||||||
|
|
||||||
if (blocklistedCollections.includes(collectionAddress)) {
|
|
||||||
return new Response('Collection unsupported.', { status: 404 })
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await getRequest(
|
const data = await getRequest(
|
||||||
cacheUrl,
|
cacheUrl,
|
||||||
() => getAsset(collectionAddress, tokenId, cacheUrl),
|
() => getAsset(collectionAddress, tokenId, cacheUrl),
|
||||||
|
|||||||
@ -18,12 +18,3 @@ test.each(invalidAssetImageUrl)('invalidAssetImageUrl', async (url) => {
|
|||||||
const response = await fetch(new Request(url))
|
const response = await fetch(new Request(url))
|
||||||
expect(response.status).toBe(404)
|
expect(response.status).toBe(404)
|
||||||
})
|
})
|
||||||
|
|
||||||
const blockedAssetImageUrl = [
|
|
||||||
'http://127.0.0.1:3000/api/image/nfts/asset/0xd4d871419714b778ebec2e22c7c53572b573706e/276',
|
|
||||||
]
|
|
||||||
|
|
||||||
test.each(blockedAssetImageUrl)('blockedAssetImageUrl', async (url) => {
|
|
||||||
const response = await fetch(new Request(url))
|
|
||||||
expect(response.status).toBe(404)
|
|
||||||
})
|
|
||||||
|
|||||||
@ -2,10 +2,9 @@
|
|||||||
import { ImageResponse } from '@vercel/og'
|
import { ImageResponse } from '@vercel/og'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { blocklistedCollections } from '../../../../../src/nft/utils/blocklist'
|
|
||||||
import { getColor } from '../../../../../src/utils/getColor'
|
|
||||||
import { CHECK_URL, WATERMARK_URL } from '../../../../constants'
|
import { CHECK_URL, WATERMARK_URL } from '../../../../constants'
|
||||||
import getCollection from '../../../../utils/getCollection'
|
import getCollection from '../../../../utils/getCollection'
|
||||||
|
import getColor from '../../../../utils/getColor'
|
||||||
import getFont from '../../../../utils/getFont'
|
import getFont from '../../../../utils/getFont'
|
||||||
import { getRequest } from '../../../../utils/getRequest'
|
import { getRequest } from '../../../../utils/getRequest'
|
||||||
|
|
||||||
@ -16,10 +15,6 @@ export const onRequest: PagesFunction = async ({ params, request }) => {
|
|||||||
const collectionAddress = index?.toString()
|
const collectionAddress = index?.toString()
|
||||||
const cacheUrl = origin + '/nfts/collection/' + collectionAddress
|
const cacheUrl = origin + '/nfts/collection/' + collectionAddress
|
||||||
|
|
||||||
if (blocklistedCollections.includes(collectionAddress)) {
|
|
||||||
return new Response('Collection unsupported.', { status: 404 })
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await getRequest(
|
const data = await getRequest(
|
||||||
cacheUrl,
|
cacheUrl,
|
||||||
() => getCollection(collectionAddress, cacheUrl),
|
() => getCollection(collectionAddress, cacheUrl),
|
||||||
|
|||||||
@ -1,34 +1,20 @@
|
|||||||
import * as matchers from 'jest-extended'
|
const collectionImageUrl = [
|
||||||
expect.extend(matchers)
|
|
||||||
|
|
||||||
const collectionImageUrls = [
|
|
||||||
'http://127.0.0.1:3000/api/image/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c544',
|
'http://127.0.0.1:3000/api/image/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c544',
|
||||||
'http://127.0.0.1:3000/api/image/nfts/collection/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d',
|
'http://127.0.0.1:3000/api/image/nfts/collection/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d',
|
||||||
'http://127.0.0.1:3000/api/image/nfts/collection/0x49cf6f5d44e70224e2e23fdcdd2c053f30ada28b',
|
'http://127.0.0.1:3000/api/image/nfts/collection/0x49cf6f5d44e70224e2e23fdcdd2c053f30ada28b',
|
||||||
]
|
]
|
||||||
|
|
||||||
test.each([...collectionImageUrls])('collectionImageUrl', async (url) => {
|
test.each(collectionImageUrl)('collectionImageUrl', async (url) => {
|
||||||
const response = await fetch(new Request(url))
|
const response = await fetch(new Request(url))
|
||||||
expect(response.status).toBe(200)
|
expect(response.status).toBe(200)
|
||||||
expect(response.headers.get('content-type')).toBe('image/png')
|
expect(response.headers.get('content-type')).toBe('image/png')
|
||||||
})
|
})
|
||||||
|
|
||||||
const nonexistentImageUrls = [
|
const invalidCollectionImageUrl = [
|
||||||
'http://127.0.0.1:3000/api/image/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c545',
|
'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(invalidCollectionImageUrl)('invalidAssetImageUrl', async (url) => {
|
||||||
|
|
||||||
test.each([...invalidCollectionImageUrls, ...nonexistentImageUrls])('invalidAssetImageUrl', async (url) => {
|
|
||||||
const response = await fetch(new Request(url))
|
const response = await fetch(new Request(url))
|
||||||
expect(response.status).toBeOneOf([404, 500])
|
expect(response.status).toBe(404)
|
||||||
})
|
|
||||||
|
|
||||||
const blockedCollectionImageUrls = [
|
|
||||||
'http://127.0.0.1:3000/api/image/nfts/collection/0xd4d871419714b778ebec2e22c7c53572b573706e',
|
|
||||||
]
|
|
||||||
|
|
||||||
test.each(blockedCollectionImageUrls)('blockedCollectionImageUrl', async (url) => {
|
|
||||||
const response = await fetch(new Request(url))
|
|
||||||
expect(response.status).toBeOneOf([404, 500])
|
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,8 +2,8 @@
|
|||||||
import { ImageResponse } from '@vercel/og'
|
import { ImageResponse } from '@vercel/og'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { getColor } from '../../../../src/utils/getColor'
|
|
||||||
import { WATERMARK_URL } from '../../../constants'
|
import { WATERMARK_URL } from '../../../constants'
|
||||||
|
import getColor from '../../../utils/getColor'
|
||||||
import getFont from '../../../utils/getFont'
|
import getFont from '../../../utils/getFont'
|
||||||
import getNetworkLogoUrl from '../../../utils/getNetworkLogoURL'
|
import getNetworkLogoUrl from '../../../utils/getNetworkLogoURL'
|
||||||
import { getRequest } from '../../../utils/getRequest'
|
import { getRequest } from '../../../utils/getRequest'
|
||||||
|
|||||||
@ -6,15 +6,12 @@ test('should append meta tag to element', () => {
|
|||||||
} as unknown as Element
|
} as unknown as Element
|
||||||
const property = 'property'
|
const property = 'property'
|
||||||
const content = 'content'
|
const content = 'content'
|
||||||
const injector = new MetaTagInjector(
|
const injector = new MetaTagInjector({
|
||||||
{
|
title: 'test',
|
||||||
title: 'test',
|
url: 'testUrl',
|
||||||
url: 'testUrl',
|
image: 'testImage',
|
||||||
image: 'testImage',
|
description: 'testDescription',
|
||||||
description: 'testDescription',
|
})
|
||||||
},
|
|
||||||
new Request('http://localhost')
|
|
||||||
)
|
|
||||||
injector.append(element, property, content)
|
injector.append(element, property, content)
|
||||||
expect(element.append).toHaveBeenCalledWith(`<meta property="${property}" content="${content}"/>`, { html: true })
|
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)
|
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.
|
* to inject meta tags into the <head> of an HTML document.
|
||||||
*/
|
*/
|
||||||
export class MetaTagInjector implements HTMLRewriterElementContentHandlers {
|
export class MetaTagInjector implements HTMLRewriterElementContentHandlers {
|
||||||
constructor(private input: MetaTagInjectorInput, private request: Request) {}
|
constructor(private input: MetaTagInjectorInput) {}
|
||||||
|
|
||||||
append(element: Element, property: string, content: string) {
|
append(element: Element, property: string, content: string) {
|
||||||
element.append(`<meta property="${property}" content="${content}"/>`, { html: true })
|
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', this.input.image)
|
||||||
this.append(element, 'twitter:image:alt', this.input.title)
|
this.append(element, 'twitter:image:alt', this.input.title)
|
||||||
}
|
}
|
||||||
|
|
||||||
const blockedPaths = this.request.headers.get('x-blocked-paths')
|
|
||||||
if (blockedPaths) {
|
|
||||||
this.append(element, 'x:blocked-paths', blockedPaths)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,2 +1,76 @@
|
|||||||
export const WATERMARK_URL = 'https://app.uniswap.org/images/324x74_App_Watermark.png'
|
export const WATERMARK_URL = 'https://app.uniswap.org/images/324x74_App_Watermark.png'
|
||||||
export const CHECK_URL = 'https://app.uniswap.org/images/54x54_Verified_Check.svg'
|
export const CHECK_URL = 'https://app.uniswap.org/images/54x54_Verified_Check.svg'
|
||||||
|
|
||||||
|
export const DEFAULT_COLOR = [35, 43, 43]
|
||||||
|
|
||||||
|
export const predefinedTokenColors: { [key: string]: number[] } = {
|
||||||
|
// old WBTC
|
||||||
|
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599/logo.png':
|
||||||
|
[240, 146, 65],
|
||||||
|
// new WBTC
|
||||||
|
'https://assets.coingecko.com/coins/images/7598/large/wrapped_bitcoin_wbtc.png?1548822744': [240, 146, 65],
|
||||||
|
// DAI
|
||||||
|
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png':
|
||||||
|
[250, 176, 27],
|
||||||
|
// UNI
|
||||||
|
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/logo.png':
|
||||||
|
[230, 53, 140],
|
||||||
|
// BUSD
|
||||||
|
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x4Fabb145d64652a948d72533023f6E7A623C7C53/logo.png':
|
||||||
|
[239, 186, 9],
|
||||||
|
// AI-X
|
||||||
|
'https://s2.coinmarketcap.com/static/img/coins/64x64/26984.png': [41, 161, 241],
|
||||||
|
// ETH
|
||||||
|
'https://token-icons.s3.amazonaws.com/eth.png': [73, 112, 213],
|
||||||
|
// HARRYPOTTERSHIBAINUBITCOIN
|
||||||
|
'https://assets.coingecko.com/coins/images/30323/large/hpos10i_logo_casino_night-dexview.png?1684117567': [
|
||||||
|
222, 49, 16,
|
||||||
|
],
|
||||||
|
// PEPE
|
||||||
|
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x6982508145454Ce325dDbE47a25d4ec3d2311933/logo.png':
|
||||||
|
[62, 174, 20],
|
||||||
|
// Unibot V2
|
||||||
|
'https://s2.coinmarketcap.com/static/img/coins/64x64/25436.png': [74, 10, 79],
|
||||||
|
// UNIBOT v1
|
||||||
|
'https://assets.coingecko.com/coins/images/30462/small/logonoline_%281%29.png?1687510315': [74, 10, 79],
|
||||||
|
// USDC
|
||||||
|
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png':
|
||||||
|
[0, 102, 217],
|
||||||
|
// HEX
|
||||||
|
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png':
|
||||||
|
[249, 63, 140],
|
||||||
|
// MONG
|
||||||
|
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x1ce270557C1f68Cfb577b856766310Bf8B47FD9C/logo.png':
|
||||||
|
[169, 109, 255],
|
||||||
|
// ARB
|
||||||
|
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1/logo.png':
|
||||||
|
[41, 161, 241],
|
||||||
|
// PSYOP
|
||||||
|
'https://s2.coinmarketcap.com/static/img/coins/64x64/25422.png': [232, 143, 0],
|
||||||
|
// MATIC
|
||||||
|
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0/logo.png':
|
||||||
|
[169, 109, 255],
|
||||||
|
// TURBO
|
||||||
|
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA35923162C49cF95e6BF26623385eb431ad920D3/logo.png':
|
||||||
|
[189, 110, 41],
|
||||||
|
// AIDOGE
|
||||||
|
'https://assets.coingecko.com/coins/images/29852/large/photo_2023-04-18_14-25-28.jpg?1681799160': [41, 161, 241],
|
||||||
|
// SIMPSON
|
||||||
|
'https://assets.coingecko.com/coins/images/30243/large/1111.png?1683692033': [232, 143, 0],
|
||||||
|
// OX
|
||||||
|
'https://assets.coingecko.com/coins/images/30604/large/Logo2.png?1685522119': [41, 89, 217],
|
||||||
|
// ANGLE
|
||||||
|
'https://assets.coingecko.com/coins/images/19060/large/ANGLE_Token-light.png?1666774221': [255, 85, 85],
|
||||||
|
// APE
|
||||||
|
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x4d224452801ACEd8B2F0aebE155379bb5D594381/logo.png':
|
||||||
|
[5, 74, 169],
|
||||||
|
// GUSD
|
||||||
|
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd/logo.png':
|
||||||
|
[0, 164, 189],
|
||||||
|
// OGN
|
||||||
|
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26/logo.png':
|
||||||
|
[5, 74, 169],
|
||||||
|
// RPL
|
||||||
|
'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xD33526068D116cE69F19A9ee46F0bd304F21A51f/logo.png':
|
||||||
|
[255, 123, 79],
|
||||||
|
}
|
||||||
|
|||||||
19
functions/global-setup.ts
Normal file
19
functions/global-setup.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { setup } from 'jest-dev-server'
|
||||||
|
|
||||||
|
module.exports = async function globalSetup() {
|
||||||
|
globalThis.servers = await setup({
|
||||||
|
command: `yarn start:cloud`,
|
||||||
|
port: 3000,
|
||||||
|
launchTimeout: 120000, // takes ~2m on CI
|
||||||
|
})
|
||||||
|
// Wait for wrangler to return a request before running tests
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
const res = await fetch(new Request('http://127.0.0.1:3000/tokens/ethereum/NATIVE'))
|
||||||
|
if (res.ok) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Set timeout to make sure the server isn't flooded with requests if wrangler is not running
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 500 * (i + 1)))
|
||||||
|
}
|
||||||
|
throw new Error('Failed to start server')
|
||||||
|
}
|
||||||
5
functions/global-teardown.ts
Normal file
5
functions/global-teardown.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { teardown } from 'jest-dev-server'
|
||||||
|
|
||||||
|
module.exports = async function globalTeardown() {
|
||||||
|
await teardown(globalThis.servers)
|
||||||
|
}
|
||||||
6
functions/global.d.ts
vendored
Normal file
6
functions/global.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { setup } from 'jest-dev-server'
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
// eslint-disable-next-line no-var
|
||||||
|
var servers: Awaited<ReturnType<typeof setup>>
|
||||||
|
}
|
||||||
@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"globalSetup": "<rootDir>/global-setup.ts",
|
||||||
|
"globalTeardown": "<rootDir>/global-teardown.ts",
|
||||||
"setupFilesAfterEnv": ["<rootDir>/setupAfterEnv.ts"],
|
"setupFilesAfterEnv": ["<rootDir>/setupAfterEnv.ts"],
|
||||||
"preset": "ts-jest",
|
"preset": "ts-jest",
|
||||||
"transform": {
|
"transform": {
|
||||||
|
|||||||
@ -8,7 +8,7 @@ export const onRequest: PagesFunction = async ({ params, request, next }) => {
|
|||||||
const { index } = params
|
const { index } = params
|
||||||
const collectionAddress = index[0]?.toString()
|
const collectionAddress = index[0]?.toString()
|
||||||
const tokenId = index[1]?.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) {
|
} catch (e) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ exports[`should inject metadata for valid assets 1`] = `
|
|||||||
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_App_Icon.png" />
|
<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="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<meta name="theme-color" content="#fff" />
|
<meta name="theme-color" content="#FC72FF" />
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
|
|
||||||
@ -37,13 +37,9 @@ exports[`should inject metadata for valid assets 1`] = `
|
|||||||
-->
|
-->
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<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-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.woff" as="font" type="font/woff" crossorigin />
|
||||||
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
@ -59,9 +55,9 @@ exports[`should inject metadata for valid assets 1`] = `
|
|||||||
font-weight: 535;
|
font-weight: 535;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
src:
|
||||||
url('/fonts/Basel-Medium.woff2') format('woff2'),
|
url(/fonts/Basel-Medium.woff) format('woff');
|
||||||
url('/fonts/Basel-Medium.woff') format('woff');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
@ -69,14 +65,15 @@ exports[`should inject metadata for valid assets 1`] = `
|
|||||||
font-weight: 485;
|
font-weight: 485;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
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) {
|
@supports (font-variation-settings: normal) {
|
||||||
* {
|
* {
|
||||||
font-family: 'Basel', sans-serif;
|
font-family: 'Basel', sans-serif;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,13 +119,13 @@ exports[`should inject metadata for valid assets 1`] = `
|
|||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html {
|
html {
|
||||||
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
|
background: linear-gradient(180deg, #202738 0%, #070816 100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
html {
|
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);
|
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -165,7 +162,7 @@ exports[`should inject metadata for valid assets 2`] = `
|
|||||||
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_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="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<meta name="theme-color" content="#fff" />
|
<meta name="theme-color" content="#FC72FF" />
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
|
|
||||||
@ -185,13 +182,9 @@ exports[`should inject metadata for valid assets 2`] = `
|
|||||||
-->
|
-->
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<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-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.woff" as="font" type="font/woff" crossorigin />
|
||||||
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
@ -207,9 +200,9 @@ exports[`should inject metadata for valid assets 2`] = `
|
|||||||
font-weight: 535;
|
font-weight: 535;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
src:
|
||||||
url('/fonts/Basel-Medium.woff2') format('woff2'),
|
url(/fonts/Basel-Medium.woff) format('woff');
|
||||||
url('/fonts/Basel-Medium.woff') format('woff');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
@ -217,14 +210,15 @@ exports[`should inject metadata for valid assets 2`] = `
|
|||||||
font-weight: 485;
|
font-weight: 485;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
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) {
|
@supports (font-variation-settings: normal) {
|
||||||
* {
|
* {
|
||||||
font-family: 'Basel', sans-serif;
|
font-family: 'Basel', sans-serif;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,13 +264,13 @@ exports[`should inject metadata for valid assets 2`] = `
|
|||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html {
|
html {
|
||||||
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
|
background: linear-gradient(180deg, #202738 0%, #070816 100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
html {
|
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);
|
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -313,7 +307,7 @@ exports[`should inject metadata for valid assets 3`] = `
|
|||||||
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_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="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<meta name="theme-color" content="#fff" />
|
<meta name="theme-color" content="#FC72FF" />
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
|
|
||||||
@ -333,13 +327,9 @@ exports[`should inject metadata for valid assets 3`] = `
|
|||||||
-->
|
-->
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<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-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.woff" as="font" type="font/woff" crossorigin />
|
||||||
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
@ -355,9 +345,9 @@ exports[`should inject metadata for valid assets 3`] = `
|
|||||||
font-weight: 535;
|
font-weight: 535;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
src:
|
||||||
url('/fonts/Basel-Medium.woff2') format('woff2'),
|
url(/fonts/Basel-Medium.woff) format('woff');
|
||||||
url('/fonts/Basel-Medium.woff') format('woff');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
@ -365,14 +355,15 @@ exports[`should inject metadata for valid assets 3`] = `
|
|||||||
font-weight: 485;
|
font-weight: 485;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
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) {
|
@supports (font-variation-settings: normal) {
|
||||||
* {
|
* {
|
||||||
font-family: 'Basel', sans-serif;
|
font-family: 'Basel', sans-serif;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,13 +409,13 @@ exports[`should inject metadata for valid assets 3`] = `
|
|||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html {
|
html {
|
||||||
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
|
background: linear-gradient(180deg, #202738 0%, #070816 100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
html {
|
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);
|
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -7,7 +7,7 @@ export const onRequest: PagesFunction = async ({ params, request, next }) => {
|
|||||||
try {
|
try {
|
||||||
const { index } = params
|
const { index } = params
|
||||||
const collectionAddress = index?.toString()
|
const collectionAddress = index?.toString()
|
||||||
return getMetadataRequest(res, request, () => getCollection(collectionAddress, request.url))
|
return getMetadataRequest(res, request.url, () => getCollection(collectionAddress, request.url))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`should inject metadata for collections 1`] = `
|
exports[`should inject metadata for valid collections 1`] = `
|
||||||
"<!DOCTYPE html>
|
"<!DOCTYPE html>
|
||||||
<html translate="no">
|
<html translate="no">
|
||||||
<head>
|
<head>
|
||||||
@ -17,7 +17,7 @@ exports[`should inject metadata for collections 1`] = `
|
|||||||
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_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="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<meta name="theme-color" content="#fff" />
|
<meta name="theme-color" content="#FC72FF" />
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
|
|
||||||
@ -37,13 +37,9 @@ exports[`should inject metadata for collections 1`] = `
|
|||||||
-->
|
-->
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<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-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.woff" as="font" type="font/woff" crossorigin />
|
||||||
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
@ -59,9 +55,9 @@ exports[`should inject metadata for collections 1`] = `
|
|||||||
font-weight: 535;
|
font-weight: 535;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
src:
|
||||||
url('/fonts/Basel-Medium.woff2') format('woff2'),
|
url(/fonts/Basel-Medium.woff) format('woff');
|
||||||
url('/fonts/Basel-Medium.woff') format('woff');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
@ -69,14 +65,15 @@ exports[`should inject metadata for collections 1`] = `
|
|||||||
font-weight: 485;
|
font-weight: 485;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
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) {
|
@supports (font-variation-settings: normal) {
|
||||||
* {
|
* {
|
||||||
font-family: 'Basel', sans-serif;
|
font-family: 'Basel', sans-serif;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,13 +119,13 @@ exports[`should inject metadata for collections 1`] = `
|
|||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html {
|
html {
|
||||||
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
|
background: linear-gradient(180deg, #202738 0%, #070816 100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
html {
|
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);
|
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -148,7 +145,7 @@ exports[`should inject metadata for collections 1`] = `
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`should inject metadata for collections 2`] = `
|
exports[`should inject metadata for valid collections 2`] = `
|
||||||
"<!DOCTYPE html>
|
"<!DOCTYPE html>
|
||||||
<html translate="no">
|
<html translate="no">
|
||||||
<head>
|
<head>
|
||||||
@ -165,7 +162,7 @@ exports[`should inject metadata for collections 2`] = `
|
|||||||
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_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="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<meta name="theme-color" content="#fff" />
|
<meta name="theme-color" content="#FC72FF" />
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
|
|
||||||
@ -185,13 +182,9 @@ exports[`should inject metadata for collections 2`] = `
|
|||||||
-->
|
-->
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<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-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.woff" as="font" type="font/woff" crossorigin />
|
||||||
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
@ -207,9 +200,9 @@ exports[`should inject metadata for collections 2`] = `
|
|||||||
font-weight: 535;
|
font-weight: 535;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
src:
|
||||||
url('/fonts/Basel-Medium.woff2') format('woff2'),
|
url(/fonts/Basel-Medium.woff) format('woff');
|
||||||
url('/fonts/Basel-Medium.woff') format('woff');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
@ -217,14 +210,15 @@ exports[`should inject metadata for collections 2`] = `
|
|||||||
font-weight: 485;
|
font-weight: 485;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
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) {
|
@supports (font-variation-settings: normal) {
|
||||||
* {
|
* {
|
||||||
font-family: 'Basel', sans-serif;
|
font-family: 'Basel', sans-serif;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,13 +264,13 @@ exports[`should inject metadata for collections 2`] = `
|
|||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html {
|
html {
|
||||||
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
|
background: linear-gradient(180deg, #202738 0%, #070816 100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
html {
|
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);
|
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -296,7 +290,7 @@ exports[`should inject metadata for collections 2`] = `
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`should inject metadata for collections 3`] = `
|
exports[`should inject metadata for valid collections 3`] = `
|
||||||
"<!DOCTYPE html>
|
"<!DOCTYPE html>
|
||||||
<html translate="no">
|
<html translate="no">
|
||||||
<head>
|
<head>
|
||||||
@ -313,7 +307,7 @@ exports[`should inject metadata for collections 3`] = `
|
|||||||
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_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="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<meta name="theme-color" content="#fff" />
|
<meta name="theme-color" content="#FC72FF" />
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
|
|
||||||
@ -333,13 +327,9 @@ exports[`should inject metadata for collections 3`] = `
|
|||||||
-->
|
-->
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<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-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.woff" as="font" type="font/woff" crossorigin />
|
||||||
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
@ -355,9 +345,9 @@ exports[`should inject metadata for collections 3`] = `
|
|||||||
font-weight: 535;
|
font-weight: 535;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
src:
|
||||||
url('/fonts/Basel-Medium.woff2') format('woff2'),
|
url(/fonts/Basel-Medium.woff) format('woff');
|
||||||
url('/fonts/Basel-Medium.woff') format('woff');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
@ -365,14 +355,15 @@ exports[`should inject metadata for collections 3`] = `
|
|||||||
font-weight: 485;
|
font-weight: 485;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
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) {
|
@supports (font-variation-settings: normal) {
|
||||||
* {
|
* {
|
||||||
font-family: 'Basel', sans-serif;
|
font-family: 'Basel', sans-serif;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,13 +409,13 @@ exports[`should inject metadata for collections 3`] = `
|
|||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html {
|
html {
|
||||||
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
|
background: linear-gradient(180deg, #202738 0%, #070816 100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
html {
|
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);
|
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -16,7 +16,7 @@ const collections = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
test.each([...collections])('should inject metadata for collections', async (collection) => {
|
test.each(collections)('should inject metadata for valid collections', async (collection) => {
|
||||||
const url = 'http://127.0.0.1:3000/nfts/collection/' + collection.address
|
const url = 'http://127.0.0.1:3000/nfts/collection/' + collection.address
|
||||||
const body = await fetch(new Request(url)).then((res) => res.text())
|
const body = await fetch(new Request(url)).then((res) => res.text())
|
||||||
expect(body).toMatchSnapshot()
|
expect(body).toMatchSnapshot()
|
||||||
@ -34,22 +34,14 @@ test.each([...collections])('should inject metadata for collections', async (col
|
|||||||
expect(body).toContain(`<meta property="twitter:image:alt" content="${collection.collectionName} on Uniswap"/>`)
|
expect(body).toContain(`<meta property="twitter:image:alt" content="${collection.collectionName} on Uniswap"/>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
const nonexistentCollections = [
|
|
||||||
{
|
|
||||||
address: '0xed5af388653567af2f388e6224dc7c4b3241c545',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const invalidCollections = [
|
const invalidCollections = [
|
||||||
{
|
'http://127.0.0.1:3000/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c545',
|
||||||
address: '0xd3adb33f',
|
'http://127.0.0.1:3000/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c545//',
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
test.each([...invalidCollections, ...nonexistentCollections])(
|
test.each(invalidCollections)(
|
||||||
'should not inject metadata for nonexistent collections',
|
'should not inject metadata for invalid collection urls',
|
||||||
async (collection) => {
|
async (url) => {
|
||||||
const url = 'http://127.0.0.1:3000/nfts/collection/' + collection.address
|
|
||||||
const body = await fetch(new Request(url)).then((res) => res.text())
|
const body = await fetch(new Request(url)).then((res) => res.text())
|
||||||
expect(body).not.toContain('og:title')
|
expect(body).not.toContain('og:title')
|
||||||
expect(body).not.toContain('og:image')
|
expect(body).not.toContain('og:image')
|
||||||
@ -62,5 +54,6 @@ test.each([...invalidCollections, ...nonexistentCollections])(
|
|||||||
expect(body).not.toContain('twitter:title')
|
expect(body).not.toContain('twitter:title')
|
||||||
expect(body).not.toContain('twitter:image')
|
expect(body).not.toContain('twitter:image')
|
||||||
expect(body).not.toContain('twitter:image:alt')
|
expect(body).not.toContain('twitter:image:alt')
|
||||||
}
|
},
|
||||||
|
50000
|
||||||
)
|
)
|
||||||
|
|||||||
@ -11,7 +11,7 @@ export const onRequest: PagesFunction = async ({ params, request, next }) => {
|
|||||||
if (!tokenAddress) {
|
if (!tokenAddress) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
return getMetadataRequest(res, request, () => getToken(networkName, tokenAddress, request.url))
|
return getMetadataRequest(res, request.url, () => getToken(networkName, tokenAddress, request.url))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ exports[`should inject metadata for valid tokens 1`] = `
|
|||||||
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_App_Icon.png" />
|
<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="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<meta name="theme-color" content="#fff" />
|
<meta name="theme-color" content="#FC72FF" />
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
|
|
||||||
@ -37,13 +37,9 @@ exports[`should inject metadata for valid tokens 1`] = `
|
|||||||
-->
|
-->
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<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-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.woff" as="font" type="font/woff" crossorigin />
|
||||||
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
@ -59,9 +55,9 @@ exports[`should inject metadata for valid tokens 1`] = `
|
|||||||
font-weight: 535;
|
font-weight: 535;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
src:
|
||||||
url('/fonts/Basel-Medium.woff2') format('woff2'),
|
url(/fonts/Basel-Medium.woff) format('woff');
|
||||||
url('/fonts/Basel-Medium.woff') format('woff');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
@ -69,14 +65,15 @@ exports[`should inject metadata for valid tokens 1`] = `
|
|||||||
font-weight: 485;
|
font-weight: 485;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
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) {
|
@supports (font-variation-settings: normal) {
|
||||||
* {
|
* {
|
||||||
font-family: 'Basel', sans-serif;
|
font-family: 'Basel', sans-serif;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,13 +119,13 @@ exports[`should inject metadata for valid tokens 1`] = `
|
|||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html {
|
html {
|
||||||
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
|
background: linear-gradient(180deg, #202738 0%, #070816 100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
html {
|
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);
|
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -165,7 +162,7 @@ exports[`should inject metadata for valid tokens 2`] = `
|
|||||||
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_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="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<meta name="theme-color" content="#fff" />
|
<meta name="theme-color" content="#FC72FF" />
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
|
|
||||||
@ -185,13 +182,9 @@ exports[`should inject metadata for valid tokens 2`] = `
|
|||||||
-->
|
-->
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<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-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.woff" as="font" type="font/woff" crossorigin />
|
||||||
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
@ -207,9 +200,9 @@ exports[`should inject metadata for valid tokens 2`] = `
|
|||||||
font-weight: 535;
|
font-weight: 535;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
src:
|
||||||
url('/fonts/Basel-Medium.woff2') format('woff2'),
|
url(/fonts/Basel-Medium.woff) format('woff');
|
||||||
url('/fonts/Basel-Medium.woff') format('woff');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
@ -217,14 +210,15 @@ exports[`should inject metadata for valid tokens 2`] = `
|
|||||||
font-weight: 485;
|
font-weight: 485;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
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) {
|
@supports (font-variation-settings: normal) {
|
||||||
* {
|
* {
|
||||||
font-family: 'Basel', sans-serif;
|
font-family: 'Basel', sans-serif;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,13 +264,13 @@ exports[`should inject metadata for valid tokens 2`] = `
|
|||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html {
|
html {
|
||||||
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
|
background: linear-gradient(180deg, #202738 0%, #070816 100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
html {
|
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);
|
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -313,7 +307,7 @@ exports[`should inject metadata for valid tokens 3`] = `
|
|||||||
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_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="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<meta name="theme-color" content="#fff" />
|
<meta name="theme-color" content="#FC72FF" />
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
|
|
||||||
@ -333,13 +327,9 @@ exports[`should inject metadata for valid tokens 3`] = `
|
|||||||
-->
|
-->
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<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-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.woff" as="font" type="font/woff" crossorigin />
|
||||||
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
@ -355,9 +345,9 @@ exports[`should inject metadata for valid tokens 3`] = `
|
|||||||
font-weight: 535;
|
font-weight: 535;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
src:
|
||||||
url('/fonts/Basel-Medium.woff2') format('woff2'),
|
url(/fonts/Basel-Medium.woff) format('woff');
|
||||||
url('/fonts/Basel-Medium.woff') format('woff');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
@ -365,14 +355,15 @@ exports[`should inject metadata for valid tokens 3`] = `
|
|||||||
font-weight: 485;
|
font-weight: 485;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
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) {
|
@supports (font-variation-settings: normal) {
|
||||||
* {
|
* {
|
||||||
font-family: 'Basel', sans-serif;
|
font-family: 'Basel', sans-serif;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,13 +409,13 @@ exports[`should inject metadata for valid tokens 3`] = `
|
|||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html {
|
html {
|
||||||
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
|
background: linear-gradient(180deg, #202738 0%, #070816 100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
html {
|
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);
|
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -461,7 +452,7 @@ exports[`should inject metadata for valid tokens 4`] = `
|
|||||||
<link rel="apple-touch-icon" sizes="512x512" href="/images/512x512_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="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<meta name="theme-color" content="#fff" />
|
<meta name="theme-color" content="#FC72FF" />
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
|
|
||||||
@ -481,13 +472,9 @@ exports[`should inject metadata for valid tokens 4`] = `
|
|||||||
-->
|
-->
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<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-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.woff" as="font" type="font/woff" crossorigin />
|
||||||
<link rel="preload" href="/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
@ -503,9 +490,9 @@ exports[`should inject metadata for valid tokens 4`] = `
|
|||||||
font-weight: 535;
|
font-weight: 535;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
src:
|
||||||
url('/fonts/Basel-Medium.woff2') format('woff2'),
|
url(/fonts/Basel-Medium.woff) format('woff');
|
||||||
url('/fonts/Basel-Medium.woff') format('woff');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
@ -513,14 +500,15 @@ exports[`should inject metadata for valid tokens 4`] = `
|
|||||||
font-weight: 485;
|
font-weight: 485;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
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) {
|
@supports (font-variation-settings: normal) {
|
||||||
* {
|
* {
|
||||||
font-family: 'Basel', sans-serif;
|
font-family: 'Basel', sans-serif;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -566,13 +554,13 @@ exports[`should inject metadata for valid tokens 4`] = `
|
|||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html {
|
html {
|
||||||
background: linear-gradient(rgb(19, 19, 19) 0%, rgb(19, 19, 19) 100%);
|
background: linear-gradient(180deg, #202738 0%, #070816 100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
html {
|
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);
|
background: radial-gradient(100% 100% at 50% 0%, rgba(255, 184, 226, 0.51) 0%, rgba(255, 255, 255, 0) 100%), #FFFFFF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
39
functions/utils/getColor.test.ts
Normal file
39
functions/utils/getColor.test.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { DEFAULT_COLOR } from '../constants'
|
||||||
|
import getColor from './getColor'
|
||||||
|
|
||||||
|
test('should return the average color of a black PNG image', async () => {
|
||||||
|
const image = 'https://static.vecteezy.com/system/resources/previews/001/209/957/original/square-png.png'
|
||||||
|
const color = await getColor(image)
|
||||||
|
expect(color).toEqual([0, 0, 0])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return the average color of a blue PNG image', async () => {
|
||||||
|
const image = 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTB2Ztcim-RKbOu57kfjYpXnnS1MO5YMUaUH9Lk5Eg&s'
|
||||||
|
const color = await getColor(image)
|
||||||
|
expect(color).toEqual([2, 6, 251])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return the average color of a white PNG image', async () => {
|
||||||
|
const image = 'https://www.cac.cornell.edu/wiki/images/4/44/White_square.png'
|
||||||
|
const color = await getColor(image)
|
||||||
|
expect(color).toEqual([255, 255, 255])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return the average color of a white PNG image with whiteness dimmed', async () => {
|
||||||
|
const image = 'https://www.cac.cornell.edu/wiki/images/4/44/White_square.png'
|
||||||
|
const color = await getColor(image, true)
|
||||||
|
expect(color).toEqual(DEFAULT_COLOR)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return the average color of a black JPG image', async () => {
|
||||||
|
const image =
|
||||||
|
'https://imageio.forbes.com/specials-images/imageserve/5ed6636cdd5d320006caf841/0x0.jpg?format=jpg&width=1200'
|
||||||
|
const color = await getColor(image)
|
||||||
|
expect(color).toEqual([0, 0, 0])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should return default color for a gif image', async () => {
|
||||||
|
const image = 'https://thumbs.gfycat.com/AgitatedLiveAgouti-size_restricted.gif'
|
||||||
|
const color = await getColor(image)
|
||||||
|
expect(color).toEqual(DEFAULT_COLOR)
|
||||||
|
})
|
||||||
@ -2,9 +2,9 @@ import { Buffer } from 'buffer'
|
|||||||
import JPEG from 'jpeg-js'
|
import JPEG from 'jpeg-js'
|
||||||
import PNG from 'png-ts'
|
import PNG from 'png-ts'
|
||||||
|
|
||||||
import { DEFAULT_COLOR, predefinedTokenColors } from '../constants/tokenColors'
|
import { DEFAULT_COLOR, predefinedTokenColors } from '../constants'
|
||||||
|
|
||||||
export async function getColor(image: string | undefined, checkDistance = false) {
|
export default async function getColor(image: string | undefined, checkDistance = false) {
|
||||||
if (!image) {
|
if (!image) {
|
||||||
return DEFAULT_COLOR
|
return DEFAULT_COLOR
|
||||||
}
|
}
|
||||||
@ -4,13 +4,13 @@ import { Data } from './cache'
|
|||||||
|
|
||||||
export async function getMetadataRequest(
|
export async function getMetadataRequest(
|
||||||
res: Promise<Response>,
|
res: Promise<Response>,
|
||||||
request: Request,
|
url: string,
|
||||||
getData: () => Promise<Data | undefined>
|
getData: () => Promise<Data | undefined>
|
||||||
) {
|
) {
|
||||||
try {
|
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) {
|
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 {
|
} else {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,8 +13,7 @@ const forkingConfig = {
|
|||||||
const forks = {
|
const forks = {
|
||||||
[ChainId.MAINNET]: {
|
[ChainId.MAINNET]: {
|
||||||
url: `https://mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_KEY}`,
|
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: UNIVERSAL_ROUTER_CREATION_BLOCK(ChainId.MAINNET),
|
||||||
blockNumber: 18537387,
|
|
||||||
...forkingConfig,
|
...forkingConfig,
|
||||||
},
|
},
|
||||||
[ChainId.POLYGON]: {
|
[ChainId.POLYGON]: {
|
||||||
|
|||||||
@ -28,6 +28,7 @@ const linguiConfig = {
|
|||||||
'ca-ES',
|
'ca-ES',
|
||||||
'cs-CZ',
|
'cs-CZ',
|
||||||
'da-DK',
|
'da-DK',
|
||||||
|
'de-DE',
|
||||||
'el-GR',
|
'el-GR',
|
||||||
'en-US',
|
'en-US',
|
||||||
'es-ES',
|
'es-ES',
|
||||||
|
|||||||
60
package.json
60
package.json
@ -13,7 +13,6 @@
|
|||||||
"graphql:generate:thegraph": "graphql-codegen --config graphql.thegraph.codegen.config.ts",
|
"graphql:generate:thegraph": "graphql-codegen --config graphql.thegraph.codegen.config.ts",
|
||||||
"graphql:generate": "yarn graphql:generate:data && yarn graphql:generate:thegraph",
|
"graphql:generate": "yarn graphql:generate:data && yarn graphql:generate:thegraph",
|
||||||
"graphql": "yarn graphql:fetch && yarn graphql:generate",
|
"graphql": "yarn graphql:fetch && yarn graphql:generate",
|
||||||
"sitemap:generate": "node scripts/generate-sitemap.js",
|
|
||||||
"i18n:extract": "lingui extract --locale en-US",
|
"i18n:extract": "lingui extract --locale en-US",
|
||||||
"i18n:compile": "lingui compile",
|
"i18n:compile": "lingui compile",
|
||||||
"i18n": "yarn i18n:extract --clean && yarn i18n:compile",
|
"i18n": "yarn i18n:extract --clean && yarn i18n:compile",
|
||||||
@ -21,14 +20,14 @@
|
|||||||
"start": "craco start",
|
"start": "craco start",
|
||||||
"start:cloud": "NODE_OPTIONS=--dns-result-order=ipv4first PORT=3001 npx wrangler pages dev --compatibility-flags=nodejs_compat --compatibility-date=2023-08-01 --proxy=3001 --port=3000 -- yarn start",
|
"start:cloud": "NODE_OPTIONS=--dns-result-order=ipv4first PORT=3001 npx wrangler pages dev --compatibility-flags=nodejs_compat --compatibility-date=2023-08-01 --proxy=3001 --port=3000 -- yarn start",
|
||||||
"build": "craco build",
|
"build": "craco build",
|
||||||
"analyze": "source-map-explorer 'build/static/js/*.js' --no-border-checks --gzip",
|
"analyze": "source-map-explorer 'build/static/js/*.js' --only-mapped",
|
||||||
"serve": "serve build -s -l 3000",
|
"serve": "serve build -l 3000",
|
||||||
"lint": "yarn eslint --ignore-path .gitignore --cache --cache-location node_modules/.cache/eslint/ .",
|
"lint": "yarn eslint --ignore-path .gitignore --cache --cache-location node_modules/.cache/eslint/ .",
|
||||||
"typecheck": "tsc",
|
"typecheck": "tsc",
|
||||||
"typecheck:cloud": "tsc -p functions/tsconfig.json",
|
"typecheck:cloud": "tsc -p functions/tsconfig.json",
|
||||||
"typecheck:cypress": "tsc -p cypress/tsconfig.json",
|
"typecheck:cypress": "tsc -p cypress/tsconfig.json",
|
||||||
"test": "craco test",
|
"test": "craco test",
|
||||||
"test:cloud": "yarn jest functions --config=functions/jest.config.json",
|
"test:cloud": "NODE_OPTIONS=--experimental-vm-modules yarn jest functions --config=functions/jest.config.json",
|
||||||
"cypress:open": "cypress open --browser chrome --e2e",
|
"cypress:open": "cypress open --browser chrome --e2e",
|
||||||
"cypress:run": "cypress run --browser chrome --e2e",
|
"cypress:run": "cypress run --browser chrome --e2e",
|
||||||
"deduplicate": "yarn-deduplicate --strategy=highest",
|
"deduplicate": "yarn-deduplicate --strategy=highest",
|
||||||
@ -106,6 +105,7 @@
|
|||||||
"@types/react": "^18.0.15",
|
"@types/react": "^18.0.15",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
"@types/react-redux": "^7.1.24",
|
"@types/react-redux": "^7.1.24",
|
||||||
|
"@types/react-router-dom": "^5.3.3",
|
||||||
"@types/react-table": "^7.7.12",
|
"@types/react-table": "^7.7.12",
|
||||||
"@types/react-virtualized-auto-sizer": "^1.0.0",
|
"@types/react-virtualized-auto-sizer": "^1.0.0",
|
||||||
"@types/react-window": "^1.8.2",
|
"@types/react-window": "^1.8.2",
|
||||||
@ -115,8 +115,7 @@
|
|||||||
"@types/ua-parser-js": "^0.7.36",
|
"@types/ua-parser-js": "^0.7.36",
|
||||||
"@types/uuid": "^8.3.4",
|
"@types/uuid": "^8.3.4",
|
||||||
"@types/wcag-contrast": "^3.0.0",
|
"@types/wcag-contrast": "^3.0.0",
|
||||||
"@types/xml2js": "^0.4.12",
|
"@uniswap/default-token-list": "^11.2.0",
|
||||||
"@uniswap/default-token-list": "^11.8.0",
|
|
||||||
"@uniswap/eslint-config": "^1.2.0",
|
"@uniswap/eslint-config": "^1.2.0",
|
||||||
"@vanilla-extract/jest-transform": "^1.1.1",
|
"@vanilla-extract/jest-transform": "^1.1.1",
|
||||||
"@vanilla-extract/webpack-plugin": "^2.2.0",
|
"@vanilla-extract/webpack-plugin": "^2.2.0",
|
||||||
@ -129,12 +128,13 @@
|
|||||||
"cypress": "12.12.0",
|
"cypress": "12.12.0",
|
||||||
"cypress-hardhat": "^2.5.0",
|
"cypress-hardhat": "^2.5.0",
|
||||||
"env-cmd": "^10.1.0",
|
"env-cmd": "^10.1.0",
|
||||||
"eslint": "8.44.0",
|
"eslint": "^7.11.0",
|
||||||
"eslint-plugin-import": "^2.27",
|
"eslint-plugin-import": "^2.27",
|
||||||
"eslint-plugin-rulesdir": "^0.2.2",
|
"eslint-plugin-rulesdir": "^0.2.2",
|
||||||
"hardhat": "^2.14.0",
|
"hardhat": "^2.14.0",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"jest": "^29.6.1",
|
"jest": "^29.6.1",
|
||||||
|
"jest-dev-server": "^9.0.0",
|
||||||
"jest-extended": "^4.0.1",
|
"jest-extended": "^4.0.1",
|
||||||
"jest-fail-on-console": "^3.1.1",
|
"jest-fail-on-console": "^3.1.1",
|
||||||
"jest-fetch-mock": "^3.0.3",
|
"jest-fetch-mock": "^3.0.3",
|
||||||
@ -152,22 +152,18 @@
|
|||||||
"resize-observer-polyfill": "^1.5.1",
|
"resize-observer-polyfill": "^1.5.1",
|
||||||
"serve": "^11.3.2",
|
"serve": "^11.3.2",
|
||||||
"source-map-explorer": "^2.5.3",
|
"source-map-explorer": "^2.5.3",
|
||||||
"start-server-and-test": "^2.0.0",
|
|
||||||
"swc-loader": "^0.2.3",
|
"swc-loader": "^0.2.3",
|
||||||
"terser": "^5.19.4",
|
|
||||||
"terser-webpack-plugin": "^5.3.9",
|
"terser-webpack-plugin": "^5.3.9",
|
||||||
"ts-jest": "^29.1.1",
|
"ts-jest": "^29.1.1",
|
||||||
|
"ts-transform-graphql-tag": "^0.2.1",
|
||||||
"tsafe": "^1.6.4",
|
"tsafe": "^1.6.4",
|
||||||
"typechain": "^5.0.0",
|
"typechain": "^5.0.0",
|
||||||
"typescript": "^4.9.4",
|
"typescript": "^4.9.4",
|
||||||
"webpack": "^5.88.2",
|
"webpack": "^5.88.2",
|
||||||
"webpack-retry-chunk-load-plugin": "^3.1.1",
|
"webpack-retry-chunk-load-plugin": "^3.1.1",
|
||||||
"wrangler": "^3.7.0",
|
"wrangler": "^3.5.0",
|
||||||
"yarn-deduplicate": "^6.0.0"
|
"yarn-deduplicate": "^6.0.0"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
|
||||||
"@uniswap/sdk-core": "4.0.7"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/client": "^3.7.2",
|
"@apollo/client": "^3.7.2",
|
||||||
"@coinbase/wallet-sdk": "^3.6.4",
|
"@coinbase/wallet-sdk": "^3.6.4",
|
||||||
@ -193,21 +189,20 @@
|
|||||||
"@sentry/react": "^7.45.0",
|
"@sentry/react": "^7.45.0",
|
||||||
"@sentry/tracing": "^7.45.0",
|
"@sentry/tracing": "^7.45.0",
|
||||||
"@sentry/types": "^7.45.0",
|
"@sentry/types": "^7.45.0",
|
||||||
"@types/react-helmet": "^6.1.7",
|
|
||||||
"@types/react-window-infinite-loader": "^1.0.6",
|
"@types/react-window-infinite-loader": "^1.0.6",
|
||||||
"@uniswap/analytics": "1.5.0",
|
"@uniswap/analytics": "^1.4.0",
|
||||||
"@uniswap/analytics-events": "^2.28.0",
|
"@uniswap/analytics-events": "^2.18.0",
|
||||||
"@uniswap/governance": "^1.0.2",
|
"@uniswap/governance": "^1.0.2",
|
||||||
"@uniswap/liquidity-staker": "^1.0.2",
|
"@uniswap/liquidity-staker": "^1.0.2",
|
||||||
"@uniswap/merkle-distributor": "^1.0.1",
|
"@uniswap/merkle-distributor": "^1.0.1",
|
||||||
"@uniswap/permit2-sdk": "^1.2.0",
|
"@uniswap/permit2-sdk": "^1.2.0",
|
||||||
"@uniswap/redux-multicall": "^1.1.8",
|
"@uniswap/redux-multicall": "^1.1.8",
|
||||||
"@uniswap/router-sdk": "^1.7.1",
|
"@uniswap/router-sdk": "^1.6.0",
|
||||||
"@uniswap/sdk-core": "4.0.7",
|
"@uniswap/sdk-core": "^4.0.3",
|
||||||
"@uniswap/smart-order-router": "^3.15.0",
|
"@uniswap/smart-order-router": "^3.15.0",
|
||||||
"@uniswap/token-lists": "^1.0.0-beta.33",
|
"@uniswap/token-lists": "^1.0.0-beta.33",
|
||||||
"@uniswap/uniswapx-sdk": "^1.4.1",
|
"@uniswap/uniswapx-sdk": "^1.3.0",
|
||||||
"@uniswap/universal-router-sdk": "^1.5.8",
|
"@uniswap/universal-router-sdk": "^1.5.6",
|
||||||
"@uniswap/v2-core": "^1.0.1",
|
"@uniswap/v2-core": "^1.0.1",
|
||||||
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
||||||
"@uniswap/v2-sdk": "^3.2.0",
|
"@uniswap/v2-sdk": "^3.2.0",
|
||||||
@ -225,16 +220,16 @@
|
|||||||
"@visx/react-spring": "^2.12.2",
|
"@visx/react-spring": "^2.12.2",
|
||||||
"@visx/responsive": "^2.10.0",
|
"@visx/responsive": "^2.10.0",
|
||||||
"@visx/shape": "^2.11.1",
|
"@visx/shape": "^2.11.1",
|
||||||
"@web3-react/coinbase-wallet": "^8.2.3",
|
"@web3-react/coinbase-wallet": "^8.2.2",
|
||||||
"@web3-react/core": "^8.2.3",
|
"@web3-react/core": "^8.2.2",
|
||||||
"@web3-react/eip1193": "^8.2.3",
|
"@web3-react/eip1193": "^8.2.2",
|
||||||
"@web3-react/empty": "^8.2.3",
|
"@web3-react/empty": "^8.2.2",
|
||||||
"@web3-react/gnosis-safe": "^8.2.4",
|
"@web3-react/gnosis-safe": "^8.2.3",
|
||||||
"@web3-react/metamask": "^8.2.4",
|
"@web3-react/metamask": "^8.2.3",
|
||||||
"@web3-react/network": "^8.2.3",
|
"@web3-react/network": "^8.2.2",
|
||||||
"@web3-react/types": "^8.2.3",
|
"@web3-react/types": "^8.2.2",
|
||||||
"@web3-react/url": "^8.2.3",
|
"@web3-react/url": "^8.2.2",
|
||||||
"@web3-react/walletconnect-v2": "^8.5.1",
|
"@web3-react/walletconnect-v2": "^8.5.0",
|
||||||
"ajv": "^8.11.0",
|
"ajv": "^8.11.0",
|
||||||
"ajv-formats": "^2.1.1",
|
"ajv-formats": "^2.1.1",
|
||||||
"array.prototype.flat": "^1.2.4",
|
"array.prototype.flat": "^1.2.4",
|
||||||
@ -258,8 +253,6 @@
|
|||||||
"ms": "^2.1.3",
|
"ms": "^2.1.3",
|
||||||
"multicodec": "^3.0.1",
|
"multicodec": "^3.0.1",
|
||||||
"multihashes": "^4.0.2",
|
"multihashes": "^4.0.2",
|
||||||
"nock": "^13.3.3",
|
|
||||||
"node-fetch": "^3.3.2",
|
|
||||||
"node-vibrant": "^3.2.1-alpha.1",
|
"node-vibrant": "^3.2.1-alpha.1",
|
||||||
"numbro": "^2.3.6",
|
"numbro": "^2.3.6",
|
||||||
"polished": "^3.3.2",
|
"polished": "^3.3.2",
|
||||||
@ -270,7 +263,7 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-feather": "^2.0.8",
|
"react-feather": "^2.0.8",
|
||||||
"react-helmet": "^6.1.0",
|
"react-ga4": "^1.4.1",
|
||||||
"react-infinite-scroll-component": "^6.1.0",
|
"react-infinite-scroll-component": "^6.1.0",
|
||||||
"react-is": "^17.0.2",
|
"react-is": "^17.0.2",
|
||||||
"react-markdown": "^4.3.1",
|
"react-markdown": "^4.3.1",
|
||||||
@ -300,7 +293,6 @@
|
|||||||
"workbox-navigation-preload": "^6.1.0",
|
"workbox-navigation-preload": "^6.1.0",
|
||||||
"workbox-precaching": "^6.1.0",
|
"workbox-precaching": "^6.1.0",
|
||||||
"workbox-routing": "^6.1.0",
|
"workbox-routing": "^6.1.0",
|
||||||
"xml2js": "^0.6.2",
|
|
||||||
"zustand": "^4.3.6"
|
"zustand": "^4.3.6"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
diff --git a/node_modules/@vercel/og/dist/index.edge.js b/node_modules/@vercel/og/dist/index.edge.js
|
diff --git a/node_modules/@vercel/og/dist/index.edge.js b/node_modules/@vercel/og/dist/index.edge.js
|
||||||
index 5187f88..eda01d0 100644
|
index 5187f88..c4a1c41 100644
|
||||||
--- a/node_modules/@vercel/og/dist/index.edge.js
|
--- a/node_modules/@vercel/og/dist/index.edge.js
|
||||||
+++ b/node_modules/@vercel/og/dist/index.edge.js
|
+++ b/node_modules/@vercel/og/dist/index.edge.js
|
||||||
@@ -18673,8 +18673,8 @@ var Resvg2 = class extends Resvg {
|
@@ -18673,8 +18673,8 @@ var Resvg2 = class extends Resvg {
|
||||||
@ -13,11 +13,12 @@ index 5187f88..eda01d0 100644
|
|||||||
|
|
||||||
// src/emoji/index.ts
|
// src/emoji/index.ts
|
||||||
var U200D = String.fromCharCode(8205);
|
var U200D = String.fromCharCode(8205);
|
||||||
@@ -18809,18 +18809,15 @@ async function render(satori, resvg, opts, defaultFonts, element) {
|
@@ -18809,18 +18809,18 @@ async function render(satori, resvg, opts, defaultFonts, element) {
|
||||||
// src/index.edge.ts
|
// src/index.edge.ts
|
||||||
var initializedResvg = initWasm(resvg_wasm);
|
var initializedResvg = initWasm(resvg_wasm);
|
||||||
var initializedYoga = initYoga(yoga_wasm).then((yoga2) => Ll(yoga2));
|
var initializedYoga = initYoga(yoga_wasm).then((yoga2) => Ll(yoga2));
|
||||||
-var fallbackFont = fetch(new URL("./noto-sans-v27-latin-regular.ttf", import.meta.url)).then((res) => res.arrayBuffer());
|
-var fallbackFont = fetch(new URL("./noto-sans-v27-latin-regular.ttf", import.meta.url)).then((res) => res.arrayBuffer());
|
||||||
|
+// var fallbackFont = fetch(new URL("https://fonts.gstatic.com/s/notosans/v28/o-0IIpQlx3QUlC5A4PNr6zRF.ttf", import.meta.url)).then((res) => res.arrayBuffer());
|
||||||
var ImageResponse = class {
|
var ImageResponse = class {
|
||||||
constructor(element, options = {}) {
|
constructor(element, options = {}) {
|
||||||
const result = new ReadableStream({
|
const result = new ReadableStream({
|
||||||
@ -25,22 +26,22 @@ index 5187f88..eda01d0 100644
|
|||||||
await initializedYoga;
|
await initializedYoga;
|
||||||
await initializedResvg;
|
await initializedResvg;
|
||||||
- const fontData = await fallbackFont;
|
- const fontData = await fallbackFont;
|
||||||
|
+ // const fontData = await fallbackFont;
|
||||||
const fonts = [
|
const fonts = [
|
||||||
{
|
{
|
||||||
name: "sans serif",
|
name: "sans serif",
|
||||||
- data: fontData,
|
- data: fontData,
|
||||||
|
+ // data: fontData,
|
||||||
weight: 700,
|
weight: 700,
|
||||||
style: "normal"
|
style: "normal"
|
||||||
}
|
}
|
||||||
diff --git a/node_modules/@vercel/og/dist/types.d.ts b/node_modules/@vercel/og/dist/types.d.ts
|
diff --git a/node_modules/@vercel/og/dist/types.d.ts b/node_modules/@vercel/og/dist/types.d.ts
|
||||||
index dde26cc..d075e99 100644
|
index dde26cc..eb59ff4 100644
|
||||||
--- a/node_modules/@vercel/og/dist/types.d.ts
|
--- a/node_modules/@vercel/og/dist/types.d.ts
|
||||||
+++ b/node_modules/@vercel/og/dist/types.d.ts
|
+++ b/node_modules/@vercel/og/dist/types.d.ts
|
||||||
@@ -28,9 +28,8 @@ declare type ImageOptions = {
|
@@ -30,7 +30,7 @@ declare type ImageOptions = {
|
||||||
* A list of fonts to use.
|
|
||||||
*
|
|
||||||
* @type {{ data: ArrayBuffer; name: string; weight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900; style?: 'normal' | 'italic' }[]}
|
* @type {{ data: ArrayBuffer; name: string; weight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900; style?: 'normal' | 'italic' }[]}
|
||||||
- * @default Noto Sans Latin Regular.
|
* @default Noto Sans Latin Regular.
|
||||||
*/
|
*/
|
||||||
- fonts?: SatoriOptions['fonts'];
|
- fonts?: SatoriOptions['fonts'];
|
||||||
+ fonts: SatoriOptions['fonts'];
|
+ fonts: SatoriOptions['fonts'];
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
diff --git a/node_modules/@web3-react/coinbase-wallet/dist/index.js b/node_modules/@web3-react/coinbase-wallet/dist/index.js
|
|
||||||
index f38d06e..4f8fa19 100644
|
|
||||||
--- a/node_modules/@web3-react/coinbase-wallet/dist/index.js
|
|
||||||
+++ b/node_modules/@web3-react/coinbase-wallet/dist/index.js
|
|
||||||
@@ -62,7 +62,7 @@ class CoinbaseWallet extends types_1.Connector {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
if (this.eagerConnection)
|
|
||||||
return;
|
|
||||||
- yield (this.eagerConnection = Promise.resolve().then(() => __importStar(require('@coinbase/wallet-sdk'))).then((m) => {
|
|
||||||
+ yield (this.eagerConnection = Promise.resolve().then(async () => __importStar(await import('@coinbase/wallet-sdk'))).then((m) => {
|
|
||||||
const _a = this.options, { url } = _a, options = __rest(_a, ["url"]);
|
|
||||||
this.coinbaseWallet = new m.default(options);
|
|
||||||
this.provider = this.coinbaseWallet.makeWeb3Provider(url);
|
|
||||||
@ -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(),
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
diff --git a/node_modules/@web3-react/metamask/dist/index.js b/node_modules/@web3-react/metamask/dist/index.js
|
|
||||||
index c8476dd..c0bfce7 100644
|
|
||||||
--- a/node_modules/@web3-react/metamask/dist/index.js
|
|
||||||
+++ b/node_modules/@web3-react/metamask/dist/index.js
|
|
||||||
@@ -54,7 +54,7 @@ class MetaMask extends types_1.Connector {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
if (this.eagerConnection)
|
|
||||||
return;
|
|
||||||
- return (this.eagerConnection = Promise.resolve().then(() => __importStar(require('@metamask/detect-provider'))).then((m) => __awaiter(this, void 0, void 0, function* () {
|
|
||||||
+ return (this.eagerConnection = Promise.resolve().then(async () => __importStar(await import('@metamask/detect-provider'))).then((m) => __awaiter(this, void 0, void 0, function* () {
|
|
||||||
var _a, _b;
|
|
||||||
const provider = yield m.default(this.options);
|
|
||||||
if (provider) {
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
diff --git a/node_modules/@web3-react/walletconnect-v2/dist/index.js b/node_modules/@web3-react/walletconnect-v2/dist/index.js
|
|
||||||
index 1a36d14..908b8c5 100644
|
|
||||||
--- a/node_modules/@web3-react/walletconnect-v2/dist/index.js
|
|
||||||
+++ b/node_modules/@web3-react/walletconnect-v2/dist/index.js
|
|
||||||
@@ -84,7 +84,7 @@ class WalletConnect extends types_1.Connector {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const rpcMap = this.rpcMap ? (0, utils_1.getBestUrlMap)(this.rpcMap, this.timeout) : undefined;
|
|
||||||
const chainProps = this.getChainProps(this.chains, this.optionalChains, desiredChainId);
|
|
||||||
- const ethProviderModule = yield Promise.resolve().then(() => __importStar(require('@walletconnect/ethereum-provider')));
|
|
||||||
+ const ethProviderModule = yield Promise.resolve().then(async () => __importStar(await import('@walletconnect/ethereum-provider')));
|
|
||||||
this.provider = yield ethProviderModule.default.init(Object.assign(Object.assign(Object.assign({}, this.options), chainProps), { rpcMap: yield rpcMap }));
|
|
||||||
return this.provider
|
|
||||||
.on('disconnect', this.disconnectListener)
|
|
||||||
diff --git a/node_modules/@web3-react/walletconnect-v2/dist/utils.js b/node_modules/@web3-react/walletconnect-v2/dist/utils.js
|
|
||||||
index 17539b6..9ea6371 100644
|
|
||||||
--- a/node_modules/@web3-react/walletconnect-v2/dist/utils.js
|
|
||||||
+++ b/node_modules/@web3-react/walletconnect-v2/dist/utils.js
|
|
||||||
@@ -62,8 +62,8 @@ function getBestUrl(urls, timeout) {
|
|
||||||
if (urls.length === 1)
|
|
||||||
return urls[0];
|
|
||||||
const [HttpConnection, JsonRpcProvider] = yield Promise.all([
|
|
||||||
- Promise.resolve().then(() => __importStar(require('@walletconnect/jsonrpc-http-connection'))).then(({ HttpConnection }) => HttpConnection),
|
|
||||||
- Promise.resolve().then(() => __importStar(require('@walletconnect/jsonrpc-provider'))).then(({ JsonRpcProvider }) => JsonRpcProvider),
|
|
||||||
+ Promise.resolve().then(async () => __importStar(await import('@walletconnect/jsonrpc-http-connection'))).then(({ HttpConnection }) => HttpConnection),
|
|
||||||
+ Promise.resolve().then(async () => __importStar(await import('@walletconnect/jsonrpc-provider'))).then(({ JsonRpcProvider }) => JsonRpcProvider),
|
|
||||||
]);
|
|
||||||
// the below returns the first url for which there's been a successful call, prioritized by index
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
@ -3,27 +3,27 @@
|
|||||||
"relation": ["delegate_permission/common.handle_all_urls"],
|
"relation": ["delegate_permission/common.handle_all_urls"],
|
||||||
"target": {
|
"target": {
|
||||||
"namespace": "android_app",
|
"namespace": "android_app",
|
||||||
"package_name": "com.uniswap.mobile",
|
"package_name": "com.uniswap",
|
||||||
"sha256_cert_fingerprints":
|
"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"],
|
"relation": ["delegate_permission/common.handle_all_urls"],
|
||||||
"target": {
|
"target": {
|
||||||
"namespace": "android_app",
|
"namespace": "android_app",
|
||||||
"package_name": "com.uniswap.mobile.beta",
|
"package_name": "com.uniswap.beta",
|
||||||
"sha256_cert_fingerprints":
|
"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"],
|
"relation": ["delegate_permission/common.handle_all_urls"],
|
||||||
"target": {
|
"target": {
|
||||||
"namespace": "android_app",
|
"namespace": "android_app",
|
||||||
"package_name": "com.uniswap.mobile.dev",
|
"package_name": "com.uniswap.dev",
|
||||||
"sha256_cert_fingerprints":
|
"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
public/CODEOWNERS
Normal file
1
public/CODEOWNERS
Normal file
@ -0,0 +1 @@
|
|||||||
|
@uniswap/web-admins
|
||||||
@ -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.
@ -14,7 +14,7 @@
|
|||||||
<link rel="apple-touch-icon" sizes="512x512" href="%PUBLIC_URL%/images/512x512_App_Icon.png" />
|
<link rel="apple-touch-icon" sizes="512x512" href="%PUBLIC_URL%/images/512x512_App_Icon.png" />
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<meta name="theme-color" content="#fff" />
|
<meta name="theme-color" content="#FC72FF" />
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
<% if (process.env.REACT_APP_CSP_ALLOW_UNSAFE_EVAL) { %>
|
<% if (process.env.REACT_APP_CSP_ALLOW_UNSAFE_EVAL) { %>
|
||||||
@ -36,13 +36,9 @@
|
|||||||
-->
|
-->
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
|
|
||||||
<link rel="preconnect" href="https://api.uniswap.org/" crossorigin/>
|
|
||||||
<link rel="preconnect" href="https://mainnet.infura.io/" crossorigin/>
|
|
||||||
|
|
||||||
<link rel="preload" href="%PUBLIC_URL%/fonts/Basel-Book.woff" as="font" type="font/woff" crossorigin />
|
<link rel="preload" href="%PUBLIC_URL%/fonts/Basel-Book.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.woff" as="font" type="font/woff" crossorigin />
|
||||||
<link rel="preload" href="%PUBLIC_URL%/fonts/Basel-Medium.woff2" as="font" type="font/woff2" crossorigin />
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
@ -58,9 +54,9 @@
|
|||||||
font-weight: 535;
|
font-weight: 535;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
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 {
|
@font-face {
|
||||||
@ -68,14 +64,15 @@
|
|||||||
font-weight: 485;
|
font-weight: 485;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
|
font-named-instance: 'Book';
|
||||||
src:
|
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) {
|
@supports (font-variation-settings: normal) {
|
||||||
* {
|
* {
|
||||||
font-family: 'Basel', sans-serif;
|
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)
|
const exec = promisify(child_process.exec)
|
||||||
|
|
||||||
async function fetchSchema(url, outputFile) {
|
function fetchSchema(url, outputFile) {
|
||||||
try {
|
exec(`yarn --silent get-graphql-schema --h Origin=https://app.uniswap.org ${url}`)
|
||||||
const { stdout } = await exec(`yarn --silent get-graphql-schema --h Origin=https://app.uniswap.org ${url}`);
|
.then(({ stderr, stdout }) => {
|
||||||
await fs.writeFile(outputFile, stdout);
|
if (stderr) {
|
||||||
} catch(err){
|
throw new Error(stderr)
|
||||||
console.error(`Failed to fetch schema from ${url}`)
|
} 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)
|
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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
/* eslint-env node */
|
|
||||||
|
|
||||||
const { minify } = require('terser')
|
|
||||||
|
|
||||||
module.exports = async function terserLoader(source, map, meta) {
|
|
||||||
const callback = this.async()
|
|
||||||
const options = this.getOptions()
|
|
||||||
try {
|
|
||||||
const data = await minify(source, options)
|
|
||||||
const { code } = data || {}
|
|
||||||
callback(null, code, map, meta)
|
|
||||||
} catch (e) {
|
|
||||||
callback(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user