Compare commits

..

3 Commits

Author SHA1 Message Date
Jack Short
d0fbc18306 fix: putting wcv2 behind feature flag (#6819) 2023-06-22 10:28:22 -04:00
UL Service Account
cbe9395225 ci: add global CODEOWNERS 2023-06-16 19:49:35 +00:00
UL Service Account
22aa6319ec ci(t9n): download translations from crowdin 2023-06-16 19:49:35 +00:00
278 changed files with 4001 additions and 11697 deletions

View File

@@ -13,6 +13,7 @@ module.exports = {
files: ['**/*'],
rules: {
'multiline-comment-style': ['error', 'separate-lines'],
'rulesdir/enforce-retry-on-import': 'error',
'rulesdir/no-undefined-or': 'error',
},
},

View File

@@ -1,32 +0,0 @@
name: Cache on main
description: caches node_modules/.cache, but only saves from main
inputs:
path:
description: 'A list of files, directories, and wildcard patterns to cache and store'
required: true
key:
description: 'An explicit key for restoring and saving the cache'
required: true
restore-keys:
description: 'An ordered list of keys to use for restoring stale cache if no cache hit occured for key. Note `cache-hit` returns false in this case.'
required: false
# Many build steps have their own caches 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 *on main* (by keying on github.run_id), and the most recent available cache is loaded.
# Caches are not saved on feature branches because they have limited utility, and extend the runtime of the workflow.
# See https://jongleberry.medium.com/speed-up-your-ci-and-dx-with-node-modules-cache-ac8df82b7bb0.
runs:
using: composite
steps:
- uses: actions/cache/restore@v3
with:
path: ${{ inputs.path }}
key: ${{ inputs.key }}
restore-keys: ${{ inputs.restore-keys }}
- if: github.ref_name == 'main'
uses: actions/cache/save@v3
with:
path: ${{ inputs.path }}
key: ${{ inputs.key }}

View File

@@ -10,7 +10,7 @@ runs:
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.
cache: 'yarn'
- uses: actions/cache@v3
id: install-cache
@@ -19,7 +19,7 @@ runs:
path: |
node_modules
!node_modules/.cache
key: ${{ runner.os }}-install-${{ hashFiles('yarn.lock') }}
key: ${{ runner.os }}-install-${{ hashFiles('**/yarn.lock') }}
- if: steps.install-cache.outputs.cache-hit != 'true'
run: yarn install --frozen-lockfile --ignore-scripts
shell: bash
@@ -55,7 +55,8 @@ runs:
# Messages are extracted from source.
# A record of source file content hashes and catalogs is maintained in node_modules/.cache/lingui.
# Messages are always extracted, but extraction may short-circuit from the custom extractor's cache.
- uses: ./.github/actions/cache-on-main
- uses: actions/cache@v3
id: i18n-extract-cache
with:
path: node_modules/.cache
key: ${{ runner.os }}-i18n-extract-${{ github.run_id }}

View File

@@ -14,21 +14,6 @@ jobs:
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 }}
@@ -59,7 +44,7 @@ jobs:
- name: Add translations
run: |
rm src/locales/en-US.po
git add -f src/locales/*.po
git add src/locales/*.po
git commit -m 'ci(t9n): download translations from crowdin'
- name: Add CODEOWNERS

View File

@@ -10,23 +10,23 @@ jobs:
environment:
name: deploy/staging
steps:
- uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
- name: Send Slack message that deploy is starting
uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
continue-on-error: true
with:
payload: |
{
"text": "Deploy _started_ for ${{ github.ref_name }}"
"text": "Staging deploy started for branch: ${{ 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 prepare
- run: yarn build
env:
REACT_APP_STAGING: 1
- name: Update Cloudflare Pages deployment
id: pages-deployment
uses: cloudflare/pages-action@364c7ca09a4b57837c5967871d64a2c31adb8c0d
@@ -38,19 +38,18 @@ jobs:
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
- name: Send Slack message about deployment outcome
uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
continue-on-error: true
if: always()
with:
payload: |
{
"text": "Deploy *${{ steps.pages-deployment.outcome }}* for ${{ github.ref_name }}"
"text": "Staging 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

View File

@@ -10,21 +10,21 @@ jobs:
environment:
name: deploy/prod
steps:
- uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
- name: Send Slack message that build is starting
uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
continue-on-error: true
with:
payload: |
{
"text": "Deploy _started_ for ${{ github.ref_name }}"
"text": "Production deploy started for branch: ${{ 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 prepare
- run: yarn build
- name: Bump and tag
id: github-tag-action
uses: mathieudutour/github-tag-action@d745f2e74aaf1ee82e747b181f7a0967978abee0
@@ -48,7 +48,7 @@ jobs:
with:
cidv0: ${{ steps.pinata.outputs.hash }}
- name: Publish release
- name: Release
uses: actions/create-release@v1.1.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -86,18 +86,18 @@ jobs:
# 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
- name: Send Slack message about deployment outcome
uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
continue-on-error: true
if: always()
with:
payload: |
{
"text": "Deploy *${{ steps.pages-deployment.outcome }}* for ${{ github.ref_name }}"
"text": "Production 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

33
.github/workflows/crowdin-sync.yaml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Crowdin Download
on:
schedule:
# Download translations every hour.
# This is not done as part of the build so that builds remain reproducible.
- cron: '0 * * * *'
# manual trigger
workflow_dispatch:
jobs:
download-translations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- run: yarn i18n:extract
- name: Download Crowdin 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'
create_pull_request: true
pull_request_title: 'chore(i18n): new Crowdin translations'
localization_branch_name: l10n_crowdin
commit_message: 'chore(i18n): synchronize translations from crowdin [skip ci]'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,4 +1,4 @@
name: Slack notification on pushes to releases/*
name: Slack notifications for releases/* merges
# This CI job will push notifications to Slack whenever code is merged into any releases/* branch
#
@@ -25,6 +25,7 @@ on:
jobs:
notify-slack:
name: 'Emit Slack notification(s)'
runs-on: ubuntu-latest
environment:
name: notify/releases
@@ -44,7 +45,9 @@ jobs:
| 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
- name: Send custom JSON data to Slack workflow
id: slack
uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
with:
payload: |
{

View File

@@ -1,7 +1,7 @@
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.
# Build tools are configured to cache cache to node_modules/.cache, so this is 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.
@@ -9,8 +9,9 @@ on:
push:
branches:
- main
- releases/staging
pull_request:
# manual trigger
workflow_dispatch:
jobs:
lint:
@@ -18,11 +19,12 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- uses: ./.github/actions/cache-on-main
- uses: actions/cache@v3
id: eslint-cache
with:
path: node_modules/.cache
key: ${{ runner.os }}-eslint-${{ github.run_id }}
restore-keys: ${{ runner.os }}-eslint-
key: ${{ runner.os }}-eslint-${{ hashFiles('**/yarn.lock') }}-${{ github.run_id }}
restore-keys: ${{ runner.os }}-eslint-${{ hashFiles('**/yarn.lock') }}-
- run: yarn lint
- if: failure() && github.ref_name == 'main'
uses: ./.github/actions/report
@@ -35,11 +37,12 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- uses: ./.github/actions/cache-on-main
- uses: actions/cache@v3
id: tsc-cache
with:
path: node_modules/.cache
key: ${{ runner.os }}-tsc-${{ github.run_id }}
restore-keys: ${{ runner.os }}-tsc-
key: ${{ runner.os }}-tsc-${{ hashFiles('**/yarn.lock') }}-${{ github.run_id }}
restore-keys: ${{ runner.os }}-tsc-${{ hashFiles('**/yarn.lock') }}-
- run: yarn typecheck
- if: failure() && github.ref_name == 'main'
uses: ./.github/actions/report
@@ -64,11 +67,12 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- uses: ./.github/actions/cache-on-main
- uses: actions/cache@v3
id: jest-cache
with:
path: node_modules/.cache
key: ${{ runner.os }}-jest-${{ github.run_id }}
restore-keys: ${{ runner.os }}-jest-
key: ${{ runner.os }}-jest-${{ hashFiles('**/yarn.lock') }}-${{ github.run_id }}
restore-keys: ${{ runner.os }}-jest-${{ hashFiles('**/yarn.lock') }}-
- run: yarn test --coverage --maxWorkers=100%
- uses: codecov/codecov-action@v3
with:
@@ -86,11 +90,12 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- uses: ./.github/actions/cache-on-main
- uses: actions/cache@v3
id: build-e2e-cache
with:
path: node_modules/.cache
key: ${{ runner.os }}-build-e2e-${{ github.run_id }}
restore-keys: ${{ runner.os }}-build-e2e-
key: ${{ runner.os }}-build-e2e-${{ hashFiles('**/yarn.lock') }}-${{ github.run_id }}
restore-keys: ${{ runner.os }}-build-e2e-${{ hashFiles('**/yarn.lock') }}-
- run: yarn build:e2e
env:
NODE_OPTIONS: "--max_old_space_size=4096"
@@ -109,6 +114,7 @@ jobs:
cypress-test-matrix:
needs: [build-e2e, cypress-rerun]
runs-on: ubuntu-latest
container: cypress/browsers:node-18.14.1-chrome-111.0.5563.64-1-ff-111.0-edge-111.0.1661.43-1
strategy:
fail-fast: false
matrix:
@@ -116,7 +122,8 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup
- uses: ./.github/actions/cache-on-main
- uses: actions/cache@v3
id: cypress-cache
with:
path: /root/.cache/Cypress
key: ${{ runner.os }}-cypress-${{ hashFiles('**/node_modules/cypress/package.json') }}
@@ -129,7 +136,8 @@ jobs:
name: build-e2e
path: build
- uses: ./.github/actions/cache-on-main
- uses: actions/cache@v3
id: hardhat-cache
with:
path: cache
key: ${{ runner.os }}-hardhat-${{ hashFiles('hardhat.config.js') }}-${{ github.run_id }}
@@ -142,9 +150,8 @@ jobs:
parallel: true
start: yarn serve
wait-on: 'http://localhost:3000'
browser: electron
browser: chrome
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 }}
@@ -169,44 +176,11 @@ jobs:
name: Cypress tests
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
pre:
if: ${{ github.ref_name == 'main' || github.ref_name == 'releases/staging' }}
# Included as a single job to check for cypress-test-matrix success, as a matrix cannot be checked.
cypress-tests:
if: always()
needs: [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: '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' }}
- if: needs.cypress-test-matrix.result != 'success'
run: exit 1

3
.gitignore vendored
View File

@@ -5,7 +5,8 @@
/src/types/v3
/src/abis/types
/src/locales/**/*.js
/src/locales/**/*.po
/src/locales/**/en-US.po
/src/locales/**/pseudo.po
# generated files
/src/**/__generated__

View File

@@ -6,7 +6,6 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const path = require('path')
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin')
const { DefinePlugin, IgnorePlugin, ProvidePlugin } = require('webpack')
const { RetryChunkLoadPlugin } = require('webpack-retry-chunk-load-plugin')
const commitHash = execSync('git rev-parse HEAD').toString().trim()
const isProduction = process.env.NODE_ENV === 'production'
@@ -94,16 +93,6 @@ module.exports = {
// See https://vanilla-extract.style/documentation/integrations/webpack/#identifiers for docs.
// See https://github.com/vanilla-extract-css/vanilla-extract/issues/771#issuecomment-1249524366.
new VanillaExtractPlugin({ identifiers: 'short' }),
new RetryChunkLoadPlugin({
cacheBust: `function() {
return 'cache-bust=' + Date.now();
}`,
// Retries with exponential backoff (500ms, 1000ms, 2000ms).
retryDelay: `function(retryAttempt) {
return 2 ** (retryAttempt - 1) * 500;
}`,
maxRetries: 3,
}),
],
configure: (webpackConfig) => {
// Configure webpack plugins:

View File

@@ -25,9 +25,14 @@ export default defineConfig({
}
})
return config
return {
...config,
// Only enable Chrome.
// Electron (the default) has issues injecting window.ethereum before pageload, so it is not viable.
browsers: config.browsers.filter(({ name }) => name === 'chrome'),
}
},
baseUrl: 'http://localhost:3000',
specPattern: 'cypress/{e2e,staging}/**/*.test.ts',
specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
},
})

View File

@@ -1,9 +1,9 @@
import { getTestSelector } from '../utils'
import { CONNECTED_WALLET_USER_STATE, DISCONNECTED_WALLET_USER_STATE } from '../utils/user-state'
import { CONNECTED_WALLET_USER_STATE } from '../utils/user-state'
describe('Landing Page', () => {
it('shows landing page when no user state exists', () => {
cy.visit('/', { userState: DISCONNECTED_WALLET_USER_STATE })
cy.visit('/', { userState: {} })
cy.get(getTestSelector('landing-page'))
cy.screenshot()
})

View File

@@ -1,8 +1,6 @@
import { BigNumber } from '@ethersproject/bignumber'
import { MaxUint160, MaxUint256 } from '@uniswap/permit2-sdk'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { DAI, USDC_MAINNET, USDT } from '../../src/constants/tokens'
import { DAI, USDC_MAINNET } from '../../src/constants/tokens'
import { getTestSelector } from '../utils'
/** Initiates a swap. */
@@ -15,26 +13,30 @@ function initiateSwap() {
}
describe('Permit2', () => {
function setupInputs(inputToken: Token, outputToken: Token) {
// Sets up a swap between inputToken and outputToken.
cy.visit(`/swap/?inputCurrency=${inputToken.address}&outputCurrency=${outputToken.address}`, {
// The same tokens are used for all permit2 tests.
const INPUT_TOKEN = DAI
const OUTPUT_TOKEN = USDC_MAINNET
beforeEach(() => {
// Sets up a swap between INPUT_TOKEN and OUTPUT_TOKEN.
cy.visit(`/swap/?inputCurrency=${INPUT_TOKEN.address}&outputCurrency=${OUTPUT_TOKEN.address}`, {
ethereum: 'hardhat',
})
cy.get('#swap-currency-input .token-amount-input').type('0.01')
}
})
/** Asserts permit2 has a max approval for spend of the input token on-chain. */
function expectTokenAllowanceForPermit2ToBeMax(inputToken: Token) {
function expectTokenAllowanceForPermit2ToBeMax() {
// check token approval
cy.hardhat()
.then(({ approval, wallet }) => approval.getTokenAllowanceForPermit2({ owner: wallet, token: inputToken }))
.then(({ approval, wallet }) => approval.getTokenAllowanceForPermit2({ owner: wallet, token: INPUT_TOKEN }))
.should('deep.equal', MaxUint256)
}
/** Asserts the universal router has a max permit2 approval for spend of the input token on-chain. */
function expectPermit2AllowanceForUniversalRouterToBeMax(inputToken: Token) {
function expectPermit2AllowanceForUniversalRouterToBeMax() {
cy.hardhat()
.then((hardhat) => hardhat.approval.getPermit2Allowance({ owner: hardhat.wallet, token: inputToken }))
.then((hardhat) => hardhat.approval.getPermit2Allowance({ owner: hardhat.wallet, token: INPUT_TOKEN }))
.then((allowance) => {
cy.wrap(MaxUint160.eq(allowance.amount)).should('eq', true)
// Asserts that the on-chain expiration is in 30 days, within a tolerance of 40 seconds.
@@ -49,7 +51,6 @@ describe('Permit2', () => {
beforeEach(() => cy.hardhat({ automine: false }))
it('swaps after completing full permit2 approval process', () => {
setupInputs(DAI, USDC_MAINNET)
initiateSwap()
// verify that the modal retains its state when the window loses focus
@@ -60,7 +61,7 @@ describe('Permit2', () => {
cy.wait('@eth_sendRawTransaction')
cy.hardhat().then((hardhat) => hardhat.mine())
cy.get(getTestSelector('popups')).contains('Approved')
expectTokenAllowanceForPermit2ToBeMax(DAI)
expectTokenAllowanceForPermit2ToBeMax()
// Verify permit2 approval
cy.contains('Allow DAI to be used for swapping')
@@ -69,13 +70,12 @@ describe('Permit2', () => {
cy.hardhat().then((hardhat) => hardhat.mine())
cy.contains('Success')
cy.get(getTestSelector('popups')).contains('Swapped')
expectPermit2AllowanceForUniversalRouterToBeMax(DAI)
expectPermit2AllowanceForUniversalRouterToBeMax()
})
it('swaps with existing permit approval and missing token approval', () => {
setupInputs(DAI, USDC_MAINNET)
cy.hardhat().then(async (hardhat) => {
await hardhat.approval.setPermit2Allowance({ owner: hardhat.wallet, token: DAI })
await hardhat.approval.setPermit2Allowance({ owner: hardhat.wallet, token: INPUT_TOKEN })
await hardhat.mine()
})
initiateSwap()
@@ -85,50 +85,7 @@ describe('Permit2', () => {
cy.wait('@eth_sendRawTransaction')
cy.hardhat().then((hardhat) => hardhat.mine())
cy.get(getTestSelector('popups')).contains('Approved')
expectTokenAllowanceForPermit2ToBeMax(DAI)
// Verify transaction
cy.wait('@eth_sendRawTransaction')
cy.hardhat().then((hardhat) => hardhat.mine())
cy.contains('Success')
cy.get(getTestSelector('popups')).contains('Swapped')
})
/**
* On mainnet, you have to revoke USDT approval before increasing it.
* From the token contract:
* To change the approve amount you first have to reduce the addresses`
* allowance to zero by calling `approve(_spender, 0)` if it is not
* already 0 to mitigate the race condition described here:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*/
it('swaps USDT with existing permit, and existing but insufficient token approval', () => {
cy.hardhat().then(async (hardhat) => {
await hardhat.fund(hardhat.wallet, CurrencyAmount.fromRawAmount(USDT, 2e6))
await hardhat.mine()
await hardhat.approval.setTokenAllowanceForPermit2({ owner: hardhat.wallet, token: USDT }, 1e6)
await hardhat.mine()
await hardhat.approval.setPermit2Allowance({ owner: hardhat.wallet, token: USDT })
await hardhat.mine()
})
setupInputs(USDT, USDC_MAINNET)
cy.get('#swap-currency-input .token-amount-input').clear().type('2')
initiateSwap()
// Verify allowance revocation
cy.contains('Reset USDT')
cy.wait('@eth_sendRawTransaction')
cy.hardhat().then((hardhat) => hardhat.mine())
cy.hardhat()
.then(({ approval, wallet }) => approval.getTokenAllowanceForPermit2({ owner: wallet, token: USDT }))
.should('deep.equal', BigNumber.from(0))
// Verify token approval
cy.contains('Enable spending USDT on Uniswap')
cy.wait('@eth_sendRawTransaction')
cy.hardhat().then((hardhat) => hardhat.mine())
cy.get(getTestSelector('popups')).contains('Approved')
expectTokenAllowanceForPermit2ToBeMax(USDT)
expectTokenAllowanceForPermit2ToBeMax()
// Verify transaction
cy.wait('@eth_sendRawTransaction')
@@ -141,11 +98,10 @@ describe('Permit2', () => {
it('swaps when user has already approved token and permit2', () => {
cy.hardhat().then(({ approval, wallet }) =>
Promise.all([
approval.setTokenAllowanceForPermit2({ owner: wallet, token: DAI }),
approval.setPermit2Allowance({ owner: wallet, token: DAI }),
approval.setTokenAllowanceForPermit2({ owner: wallet, token: INPUT_TOKEN }),
approval.setPermit2Allowance({ owner: wallet, token: INPUT_TOKEN }),
])
)
setupInputs(DAI, USDC_MAINNET)
initiateSwap()
// Verify transaction
@@ -154,7 +110,6 @@ describe('Permit2', () => {
})
it('swaps after handling user rejection of both approval and signature', () => {
setupInputs(DAI, USDC_MAINNET)
const USER_REJECTION = { code: 4001 }
cy.hardhat().then((hardhat) => {
// Reject token approval
@@ -177,7 +132,7 @@ describe('Permit2', () => {
// Verify token approval
cy.get(getTestSelector('popups')).contains('Approved')
expectTokenAllowanceForPermit2ToBeMax(DAI)
expectTokenAllowanceForPermit2ToBeMax()
// Verify permit2 approval rejection
cy.wrap(permitApprovalStub).should('be.calledWith', 'eth_signTypedData_v4')
@@ -190,32 +145,30 @@ describe('Permit2', () => {
// Verify permit2 approval
cy.contains('Success')
cy.get(getTestSelector('popups')).contains('Swapped')
expectPermit2AllowanceForUniversalRouterToBeMax(DAI)
expectPermit2AllowanceForUniversalRouterToBeMax()
})
})
it('prompts token approval when existing approval amount is too low', () => {
setupInputs(DAI, USDC_MAINNET)
cy.hardhat().then(({ approval, wallet }) =>
Promise.all([
approval.setPermit2Allowance({ owner: wallet, token: DAI }),
approval.setTokenAllowanceForPermit2({ owner: wallet, token: DAI }, 1),
approval.setPermit2Allowance({ owner: wallet, token: INPUT_TOKEN }),
approval.setTokenAllowanceForPermit2({ owner: wallet, token: INPUT_TOKEN }, 1),
])
)
initiateSwap()
// Verify token approval
cy.get(getTestSelector('popups')).contains('Approved')
expectPermit2AllowanceForUniversalRouterToBeMax(DAI)
expectPermit2AllowanceForUniversalRouterToBeMax()
})
it('prompts signature when existing permit approval is expired', () => {
setupInputs(DAI, USDC_MAINNET)
const expiredAllowance = { expiration: Math.floor((Date.now() - 1) / 1000) }
cy.hardhat().then(({ approval, wallet }) =>
Promise.all([
approval.setTokenAllowanceForPermit2({ owner: wallet, token: DAI }),
approval.setPermit2Allowance({ owner: wallet, token: DAI }, expiredAllowance),
approval.setTokenAllowanceForPermit2({ owner: wallet, token: INPUT_TOKEN }),
approval.setPermit2Allowance({ owner: wallet, token: INPUT_TOKEN }, expiredAllowance),
])
)
initiateSwap()
@@ -224,16 +177,15 @@ describe('Permit2', () => {
cy.wait('@eth_signTypedData_v4')
cy.contains('Success')
cy.get(getTestSelector('popups')).contains('Swapped')
expectPermit2AllowanceForUniversalRouterToBeMax(DAI)
expectPermit2AllowanceForUniversalRouterToBeMax()
})
it('prompts signature when existing permit approval amount is too low', () => {
setupInputs(DAI, USDC_MAINNET)
const smallAllowance = { amount: 1 }
cy.hardhat().then(({ approval, wallet }) =>
Promise.all([
approval.setTokenAllowanceForPermit2({ owner: wallet, token: DAI }),
approval.setPermit2Allowance({ owner: wallet, token: DAI }, smallAllowance),
approval.setTokenAllowanceForPermit2({ owner: wallet, token: INPUT_TOKEN }),
approval.setPermit2Allowance({ owner: wallet, token: INPUT_TOKEN }, smallAllowance),
])
)
initiateSwap()
@@ -242,6 +194,6 @@ describe('Permit2', () => {
cy.wait('@eth_signTypedData_v4')
cy.contains('Success')
cy.get(getTestSelector('popups')).contains('Swapped')
expectPermit2AllowanceForUniversalRouterToBeMax(DAI)
expectPermit2AllowanceForUniversalRouterToBeMax()
})
})

View File

@@ -1,11 +1,11 @@
import { BigNumber } from '@ethersproject/bignumber'
import { ChainId } from '@uniswap/sdk-core'
import { SupportedChainId } from '@uniswap/sdk-core'
import { DEFAULT_DEADLINE_FROM_NOW } from '../../../src/constants/misc'
import { UNI, USDC_MAINNET } from '../../../src/constants/tokens'
import { getBalance, getTestSelector } from '../../utils'
const UNI_MAINNET = UNI[ChainId.MAINNET]
const UNI_MAINNET = UNI[SupportedChainId.MAINNET]
describe('Swap errors', () => {
it('wallet rejection', () => {
@@ -64,7 +64,7 @@ describe('Swap errors', () => {
})
})
it.skip('slippage failure', () => {
it('slippage failure', () => {
cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${UNI_MAINNET.address}`, { ethereum: 'hardhat' })
cy.hardhat({ automine: false })
getBalance(USDC_MAINNET).then((initialBalance) => {

View File

@@ -1,9 +1,9 @@
import { ChainId } from '@uniswap/sdk-core'
import { SupportedChainId } from '@uniswap/sdk-core'
import { UNI, USDC_MAINNET } from '../../../src/constants/tokens'
import { getBalance, getTestSelector } from '../../utils'
const UNI_MAINNET = UNI[ChainId.MAINNET]
const UNI_MAINNET = UNI[SupportedChainId.MAINNET]
describe('Swap', () => {
describe('Swap on main page', () => {

View File

@@ -1,8 +1,8 @@
import { ChainId, CurrencyAmount, WETH9 } from '@uniswap/sdk-core'
import { CurrencyAmount, SupportedChainId, WETH9 } from '@uniswap/sdk-core'
import { getBalance, getTestSelector } from '../../utils'
const WETH = WETH9[ChainId.MAINNET]
const WETH = WETH9[SupportedChainId.MAINNET]
describe('Swap wrap', () => {
beforeEach(() => {

View File

@@ -1,9 +1,9 @@
import { ChainId, WETH9 } from '@uniswap/sdk-core'
import { SupportedChainId, WETH9 } from '@uniswap/sdk-core'
import { ARB, UNI } from '../../src/constants/tokens'
import { UNI } from '../../src/constants/tokens'
import { getTestSelector } from '../utils'
const UNI_MAINNET = UNI[ChainId.MAINNET]
const UNI_MAINNET = UNI[SupportedChainId.MAINNET]
const UNI_ADDRESS = '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984'
@@ -149,7 +149,7 @@ describe('Token details', () => {
cy.get(getTestSelector('tokens-network-filter-selected')).click()
cy.get(getTestSelector('tokens-network-filter-option-arbitrum')).click()
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Arbitrum')
cy.get(getTestSelector(`token-table-row-${ARB.address.toLowerCase()}`)).click()
cy.get(getTestSelector('token-table-row-ARB')).click()
cy.get(`#swap-currency-output .token-symbol-container`).should('contain.text', 'ARB')
cy.get(getTestSelector('open-settings-dialog-button')).should('be.disabled')
cy.contains('Connect to Arbitrum').should('exist')

View File

@@ -10,11 +10,11 @@ describe('Token explore', () => {
cy.get(getTestSelectorStartsWith('token-table')).its('length').should('be.greaterThan', 0)
// check sorted svg icon is present in volume cell, since tokens are sorted by volume by default
cy.get(getTestSelector('header-row')).find(getTestSelector('volume-cell')).find('svg').should('exist')
cy.get(getTestSelector('token-table-row-NATIVE')).find(getTestSelector('name-cell')).should('include.text', 'Ether')
cy.get(getTestSelector('token-table-row-NATIVE')).find(getTestSelector('volume-cell')).should('include.text', '$')
cy.get(getTestSelector('token-table-row-NATIVE')).find(getTestSelector('price-cell')).should('include.text', '$')
cy.get(getTestSelector('token-table-row-NATIVE')).find(getTestSelector('tvl-cell')).should('include.text', '$')
cy.get(getTestSelector('token-table-row-NATIVE'))
cy.get(getTestSelector('token-table-row-ETH')).find(getTestSelector('name-cell')).should('include.text', 'Ether')
cy.get(getTestSelector('token-table-row-ETH')).find(getTestSelector('volume-cell')).should('include.text', '$')
cy.get(getTestSelector('token-table-row-ETH')).find(getTestSelector('price-cell')).should('include.text', '$')
cy.get(getTestSelector('token-table-row-ETH')).find(getTestSelector('tvl-cell')).should('include.text', '$')
cy.get(getTestSelector('token-table-row-ETH'))
.find(getTestSelector('percent-change-cell'))
.should('include.text', '%')
cy.get(getTestSelector('header-row')).find(getTestSelector('price-cell')).click()
@@ -24,14 +24,14 @@ describe('Token explore', () => {
it('should update when time window toggled', () => {
cy.visit('/tokens/ethereum')
cy.get(getTestSelector('time-selector')).should('contain', '1D')
cy.get(getTestSelector('token-table-row-NATIVE'))
cy.get(getTestSelector('token-table-row-ETH'))
.find(getTestSelector('volume-cell'))
.then(function ($elem) {
cy.wrap($elem.text()).as('dailyEthVol')
})
cy.get(getTestSelector('time-selector')).click()
cy.get(getTestSelector('1Y')).click()
cy.get(getTestSelector('token-table-row-NATIVE'))
cy.get(getTestSelector('token-table-row-ETH'))
.find(getTestSelector('volume-cell'))
.then(function ($elem) {
cy.wrap($elem.text()).as('yearlyEthVol')
@@ -41,7 +41,7 @@ describe('Token explore', () => {
it('should navigate to token detail page when row clicked', () => {
cy.visit('/tokens/ethereum')
cy.get(getTestSelector('token-table-row-NATIVE')).click()
cy.get(getTestSelector('token-table-row-ETH')).click()
cy.get(getTestSelector('token-details-about-section')).should('exist')
cy.get(getTestSelector('token-details-stats')).should('exist')
cy.get(getTestSelector('token-info-container')).should('exist')
@@ -53,15 +53,13 @@ describe('Token explore', () => {
it('should update when global network changed', () => {
cy.visit('/tokens/ethereum')
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Ethereum')
cy.get(getTestSelector('token-table-row-NATIVE')).should('exist')
cy.get(getTestSelector('token-table-row-ETH')).should('exist')
// note: cannot switch global chain via UI because we cannot approve the network switch
// in metamask modal using plain cypress. this is a workaround.
cy.visit('/tokens/polygon')
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Polygon')
cy.get(getTestSelector('token-table-row-NATIVE'))
.find(getTestSelector('name-cell'))
.should('include.text', 'Polygon Matic')
cy.get(getTestSelector('token-table-row-MATIC')).should('exist')
})
it('should update when token explore table network changed', () => {

View File

@@ -1,7 +1,13 @@
import { getTestSelector } from '../utils'
describe('Universal search bar', () => {
beforeEach(() => {
before(() => {
cy.visit('/')
cy.get('[data-cy="magnifying-icon"]').parent().eq(1).click()
cy.get('[data-cy="magnifying-icon"]')
.parent()
.then(($navIcon) => {
$navIcon.click()
})
})
it('should yield clickable result for regular token or nft collection search term', () => {
@@ -13,7 +19,20 @@ describe('Universal search bar', () => {
.and('contain.text', '$')
.and('contain.text', '%')
cy.get('[data-cy="searchbar-token-row-UNI"]').first().click()
cy.location('hash').should('equal', '#/tokens/ethereum/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984')
cy.get('div').contains('Uniswap').should('exist')
// Stats should have: TVL, 24H Volume, 52W low, 52W high.
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="volume-24h"]').should('include.text', '$')
cy.get('[data-cy="52w-low"]').should('include.text', '$')
cy.get('[data-cy="52w-high"]').should('include.text', '$')
})
// About section should have description of token.
cy.get(getTestSelector('token-details-about-section')).should('exist')
cy.contains('UNI is the governance token for Uniswap').should('exist')
})
it.skip('should show recent tokens and popular tokens with empty search term', () => {

View File

@@ -1,5 +1,4 @@
import { getTestSelector } from '../../utils'
import { DISCONNECTED_WALLET_USER_STATE } from '../../utils/user-state'
describe('disconnect wallet', () => {
it('should clear state', () => {
@@ -28,7 +27,7 @@ describe('disconnect wallet', () => {
describe('connect wallet', () => {
it('should load state', () => {
cy.visit('/swap', { ethereum: 'hardhat', userState: DISCONNECTED_WALLET_USER_STATE })
cy.visit('/swap', { ethereum: 'hardhat', userState: {} })
// Connect the wallet
cy.get(getTestSelector('navbar-connect-wallet')).contains('Connect').click()

View File

@@ -1,130 +0,0 @@
import { createDeferredPromise } from '../../../src/test-utils/promise'
import { getTestSelector } from '../../utils'
function waitsForActiveChain(chain: string) {
cy.get(getTestSelector('chain-selector-logo')).invoke('attr', 'alt').should('eq', chain)
}
function switchChain(chain: string) {
cy.get(getTestSelector('chain-selector')).eq(1).click()
cy.contains(chain).click()
}
describe('network switching', () => {
beforeEach(() => {
cy.visit('/swap', { ethereum: 'hardhat' })
cy.get(getTestSelector('web3-status-connected'))
})
function rejectsNetworkSwitchWith(rejection: unknown) {
cy.hardhat().then((hardhat) => {
// Reject network switch
const sendStub = cy.stub(hardhat.provider, 'send').log(false).as('switch')
sendStub.withArgs('wallet_switchEthereumChain').rejects(rejection)
sendStub.callThrough() // allows other calls to return non-stubbed values
})
switchChain('Polygon')
// Verify rejected network switch
cy.get('@switch').should('have.been.calledWith', 'wallet_switchEthereumChain')
waitsForActiveChain('Ethereum')
cy.get(getTestSelector('web3-status-connected'))
}
it('should not display message on user rejection', () => {
const USER_REJECTION = { code: 4001 }
rejectsNetworkSwitchWith(USER_REJECTION)
cy.get(getTestSelector('popups')).should('not.contain', 'Failed to switch networks')
})
it('should display message on unknown error', () => {
rejectsNetworkSwitchWith(new Error('Unknown error'))
cy.get(getTestSelector('popups')).contains('Failed to switch networks')
})
it('should add missing chain', () => {
cy.hardhat().then((hardhat) => {
// https://docs.metamask.io/guide/rpc-api.html#unrestricted-methods
const CHAIN_NOT_ADDED = { code: 4902 } // missing message in useSelectChain
// Reject network switch with CHAIN_NOT_ADDED
const sendStub = cy.stub(hardhat.provider, 'send').log(false).as('switch')
let added = false
sendStub
.withArgs('wallet_switchEthereumChain')
.callsFake(() => (added ? Promise.resolve(null) : Promise.reject(CHAIN_NOT_ADDED)))
sendStub.withArgs('wallet_addEthereumChain').callsFake(() => {
added = true
return Promise.resolve(null)
})
sendStub.callThrough() // allows other calls to return non-stubbed values
})
switchChain('Polygon')
// Verify the network was added
cy.get('@switch').should('have.been.calledWith', 'wallet_switchEthereumChain')
cy.get('@switch').should('have.been.calledWith', 'wallet_addEthereumChain', [
{
blockExplorerUrls: ['https://polygonscan.com/'],
chainId: '0x89',
chainName: 'Polygon',
nativeCurrency: { name: 'Polygon Matic', symbol: 'MATIC', decimals: 18 },
rpcUrls: ['https://polygon-rpc.com/'],
},
])
})
it('should not disconnect while switching', () => {
const promise = createDeferredPromise()
cy.hardhat().then((hardhat) => {
// Reject network switch with CHAIN_NOT_ADDED
const sendStub = cy.stub(hardhat.provider, 'send').log(false).as('switch')
sendStub.withArgs('wallet_switchEthereumChain').returns(promise)
sendStub.callThrough() // allows other calls to return non-stubbed values
})
switchChain('Polygon')
// Verify there is no disconnection
cy.get('@switch').should('have.been.calledWith', 'wallet_switchEthereumChain')
cy.contains('Connecting to Polygon')
cy.get(getTestSelector('web3-status-connected')).should('be.disabled')
promise.resolve()
})
it('should switch networks', () => {
// Select an output currency
cy.get('#swap-currency-output .open-currency-select-button').click()
cy.contains('USDC').click()
// Populate input/output fields
cy.get('#swap-currency-input .token-amount-input').clear().type('1')
cy.get('#swap-currency-output .token-amount-input').should('not.equal', '')
// Switch network
switchChain('Polygon')
// Verify network switch
cy.wait('@wallet_switchEthereumChain')
waitsForActiveChain('Polygon')
cy.get(getTestSelector('web3-status-connected'))
// Verify that the input/output fields were reset
cy.get('#swap-currency-input .token-amount-input').should('have.value', '')
cy.get(`#swap-currency-input .token-symbol-container`).should('contain.text', 'MATIC')
cy.get(`#swap-currency-output .token-amount-input`).should('not.have.value')
cy.get(`#swap-currency-output .token-symbol-container`).should('contain.text', 'Select token')
})
})
describe('network switching from URL param', () => {
it('should switch network from URL param', () => {
cy.visit('/swap?chain=polygon', { ethereum: 'hardhat' })
cy.get(getTestSelector('web3-status-connected'))
cy.wait('@wallet_switchEthereumChain')
waitsForActiveChain('Polygon')
})
})

View File

@@ -1,8 +1,8 @@
import { getTestSelector } from '../utils'
describe('Wallet Dropdown', () => {
function itChangesTheme() {
it('should change theme', () => {
function itShouldChangeTheTheme() {
it('should change the theme', () => {
cy.get(getTestSelector('theme-lightmode')).click()
cy.get(getTestSelector('theme-lightmode')).should('not.have.css', 'background-color', 'rgba(0, 0, 0, 0)')
@@ -21,17 +21,13 @@ describe('Wallet Dropdown', () => {
})
}
function itChangesLocale() {
it('should change locale', () => {
cy.contains('Uniswap available in: English').should('not.exist')
cy.get(getTestSelector('wallet-language-item')).contains('Afrikaans').click({ force: true })
cy.location('hash').should('match', /\?lng=af-ZA$/)
cy.contains('Uniswap available in: English')
function itShouldChangeTheLanguage() {
it('should select a language', () => {
cy.get(getTestSelector('wallet-language-item')).contains('Deutsch').click({ force: true })
cy.get(getTestSelector('wallet-header')).should('contain', 'Sprache')
cy.get(getTestSelector('wallet-language-item')).contains('English').click({ force: true })
cy.location('hash').should('match', /\?lng=en-US$/)
cy.contains('Uniswap available in: English').should('not.exist')
cy.get(getTestSelector('wallet-header')).should('contain', 'Language')
cy.get(getTestSelector('wallet-back')).click()
})
}
@@ -41,8 +37,8 @@ describe('Wallet Dropdown', () => {
cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-settings')).click()
})
itChangesTheme()
itChangesLocale()
itShouldChangeTheTheme()
itShouldChangeTheLanguage()
})
describe('testnet toggle', () => {
@@ -72,8 +68,8 @@ describe('Wallet Dropdown', () => {
cy.get(getTestSelector('wallet-disconnect')).click()
cy.get(getTestSelector('wallet-settings')).click()
})
itChangesTheme()
itChangesLocale()
itShouldChangeTheTheme()
itShouldChangeTheLanguage()
})
describe('with color theme', () => {

View File

@@ -1,19 +0,0 @@
import { getTestSelector } from '../utils'
describe('translations', () => {
it('loads locale from the query param', () => {
cy.visit('/?lng=fr-FR')
cy.contains('Échanger')
cy.contains('Uniswap disponible en : English')
})
it('loads locale from menu', () => {
cy.visit('/')
cy.get(getTestSelector('web3-status-connected')).click()
cy.get(getTestSelector('wallet-settings')).click()
cy.get(getTestSelector('wallet-language-item')).contains('français').click({ force: true })
cy.location('hash').should('match', /\?lng=fr-FR$/)
cy.contains('Échanger')
cy.contains('Uniswap disponible en : English')
})
})

View File

@@ -58,7 +58,7 @@ Cypress.Commands.overwrite(
// Set initial user state.
win.localStorage.setItem(
'redux_localstorage_simple_user', // storage key for the user reducer using 'redux-localstorage-simple'
JSON.stringify({ ...CONNECTED_WALLET_USER_STATE, ...(options?.userState ?? {}) })
JSON.stringify(options?.userState ?? CONNECTED_WALLET_USER_STATE)
)
// Set feature flags, if configured.

View File

@@ -3,9 +3,11 @@
*/
import { Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { JsonRpcProvider } from '@ethersproject/providers'
import { Wallet } from '@ethersproject/wallet'
import { ChainId } from '@uniswap/sdk-core'
import { SupportedChainId } from '../../src/constants/chains'
// todo: figure out how env vars actually work in CI
// const TEST_PRIVATE_KEY = Cypress.env('INTEGRATION_TEST_PRIVATE_KEY')
@@ -13,7 +15,7 @@ const TEST_PRIVATE_KEY = '0xe580410d7c37d26c6ad1a837bbae46bc27f9066a466fb3a66e77
// address of the above key
const TEST_ADDRESS_NEVER_USE = new Wallet(TEST_PRIVATE_KEY).address
const CHAIN_ID = ChainId.GOERLI
const CHAIN_ID = SupportedChainId.GOERLI
const HEXLIFIED_CHAIN_ID = `0x${CHAIN_ID.toString(16)}`
const provider = new JsonRpcProvider('https://goerli.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847', 5)

View File

@@ -1,5 +1,3 @@
import { UserState } from '../../src/state/user/reducer'
export const CONNECTED_WALLET_USER_STATE: Partial<UserState> = { selectedWallet: 'INJECTED' }
export const DISCONNECTED_WALLET_USER_STATE: Partial<UserState> = { selectedWallet: undefined }

View File

@@ -0,0 +1,36 @@
/* eslint-env node */
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'enforce use of retry() for dynamic imports',
category: 'Best Practices',
recommended: false,
},
schema: [],
},
create(context) {
return {
ImportExpression(node) {
const grandParent = node.parent.parent
if (
!(
grandParent &&
grandParent.type === 'CallExpression' &&
// Technically, we are only checking that a function named `retry` wraps the dynamic import.
// We do not go as far as enforcing that it is import('utils/retry').retry
grandParent.callee.name === 'retry' &&
grandParent.arguments.length === 1 &&
grandParent.arguments[0].type === 'ArrowFunctionExpression'
)
) {
context.report({
node,
message: 'Dynamic import should be wrapped in retry (see `utils/retry.ts`): `retry(() => import(...))`',
})
}
},
}
},
}

View File

@@ -1 +0,0 @@
export const presets = ['@babel/preset-env']

View File

@@ -1,9 +0,0 @@
import { setup } from 'jest-dev-server'
module.exports = async function globalSetup() {
globalThis.servers = await setup({
command: `yarn start:cloud`,
port: 3000,
launchTimeout: 50000,
})
}

View File

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

View File

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

View File

@@ -1,9 +0,0 @@
{
"globalSetup": "<rootDir>/global-setup.ts",
"globalTeardown": "<rootDir>/global-teardown.ts",
"preset": "ts-jest",
"transform": {
"'^.+\\.(ts|tsx)?$'": "ts-jest",
"^.+\\.(js|jsx)$": "babel-jest"
}
}

View File

@@ -1,3 +0,0 @@
test('example', async () => {
expect(true).toBe(true)
})

View File

@@ -1,19 +0,0 @@
{
"compilerOptions": {
"esModuleInterop": true,
"incremental": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"noEmit": true,
"strict": true,
"target": "ES6",
"tsBuildInfoFile": "../node_modules/.cache/tsbuildinfo/functions", // avoid clobbering the build tsbuildinfo
"types": ["jest", "node"],
"jsx": "react",
"moduleResolution": "NodeNext",
},
"exclude": ["node_modules"],
"include": ["**/*.ts"],
"watchOptions": {
"excludeDirectories": ["node_modules"]
}
}

View File

@@ -15,7 +15,6 @@ const config: CodegenConfig = {
withHooks: true,
// This avoid all generated schemas being wrapped in Maybe https://the-guild.dev/graphql/codegen/plugins/typescript/typescript#maybevalue-string-default-value-t--null
maybeValue: 'T',
immutableTypes: true,
},
},
},

View File

@@ -1,5 +1,3 @@
import { ChainId } from '@uniswap/sdk-core'
/* eslint-env node */
require('dotenv').config()
@@ -7,33 +5,20 @@ require('dotenv').config()
// The only requirement is that all infrastructure under test (eg Permit2 contracts) are already deployed.
// TODO(WEB-2187): Make more dynamic to avoid manually updating
const BLOCK_NUMBER = 17388567
const POLYGON_BLOCK_NUMBER = 43600000
const forkingConfig = {
const mainnetFork = {
url: `https://mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_KEY}`,
blockNumber: BLOCK_NUMBER,
httpHeaders: {
Origin: 'localhost:3000', // infura allowlists requests by origin
},
}
const forks = {
[ChainId.MAINNET]: {
url: `https://mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_KEY}`,
blockNumber: BLOCK_NUMBER,
...forkingConfig,
},
[ChainId.POLYGON]: {
url: `https://polygon-mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_KEY}`,
blockNumber: POLYGON_BLOCK_NUMBER,
...forkingConfig,
},
}
module.exports = {
forks,
networks: {
hardhat: {
chainId: ChainId.MAINNET,
forking: forks[ChainId.MAINNET],
chainId: 1,
forking: mainnetFork,
accounts: {
count: 2,
},

View File

@@ -115,11 +115,13 @@ const linguiConfig = {
'vi-VN',
'zh-CN',
'zh-TW',
'pseudo',
],
orderBy: 'messageId',
rootDir: '.',
runtimeConfigModule: ['@lingui/core', 'i18n'],
sourceLocale: 'en-US',
pseudoLocale: 'pseudo',
extractors: [cachingExtractor],
}

View File

@@ -15,11 +15,11 @@
"graphql:generate": "yarn graphql:generate:data && yarn graphql:generate:thegraph",
"graphql": "yarn graphql:fetch && yarn graphql:generate",
"i18n:extract": "lingui extract --locale en-US",
"i18n:pseudo": "lingui extract --locale pseudo",
"i18n:compile": "lingui compile",
"i18n": "yarn i18n:extract --clean && yarn i18n:compile",
"prepare": "concurrently \"npm:ajv\" \"npm:contracts\" \"npm:graphql\" \"npm:i18n\"",
"start": "craco start",
"start:cloud": "NODE_OPTIONS=--dns-result-order=ipv4first PORT=3001 npx wrangler pages dev --proxy=3001 --port=3000 -- yarn start",
"build": "craco build",
"build:e2e": "REACT_APP_CSP_ALLOW_UNSAFE_EVAL=true REACT_APP_ADD_COVERAGE_INSTRUMENTATION=true craco build",
"analyze": "source-map-explorer 'build/static/js/*.js' --only-mapped",
@@ -27,7 +27,6 @@
"lint": "yarn eslint --ignore-path .gitignore --cache --cache-location node_modules/.cache/eslint/ .",
"typecheck": "tsc",
"test": "craco test",
"test:cloud": "NODE_OPTIONS=--experimental-vm-modules yarn jest functions --watch --config=functions/jest.config.json",
"cypress:open": "cypress open --browser chrome --e2e",
"cypress:run": "cypress run --browser chrome --e2e",
"deduplicate": "yarn-deduplicate --strategy=highest"
@@ -68,8 +67,6 @@
]
},
"devDependencies": {
"@babel/preset-env": "^7.22.7",
"@cloudflare/workers-types": "^4.20230518.0",
"@craco/craco": "^7.1.0",
"@ethersproject/experimental": "^5.4.0",
"@lingui/cli": "^3.9.0",
@@ -101,42 +98,35 @@
"@types/ua-parser-js": "^0.7.36",
"@types/uuid": "^8.3.4",
"@types/wcag-contrast": "^3.0.0",
"@uniswap/default-token-list": "^11.2.0",
"@uniswap/default-token-list": "^9.6.0",
"@uniswap/eslint-config": "^1.2.0",
"@vanilla-extract/babel-plugin": "^1.1.7",
"@vanilla-extract/jest-transform": "^1.1.1",
"@vanilla-extract/webpack-plugin": "^2.1.11",
"@walletconnect/types": "^2.8.6",
"babel-jest": "^29.6.1",
"babel-plugin-istanbul": "^6.1.1",
"buffer": "^6.0.3",
"concurrently": "^8.0.1",
"cypress": "12.12.0",
"cypress-hardhat": "^2.4.2",
"cypress-hardhat": "^2.3.0",
"env-cmd": "^10.1.0",
"eslint": "^7.11.0",
"eslint-plugin-import": "^2.27",
"eslint-plugin-rulesdir": "^0.2.2",
"hardhat": "^2.14.0",
"jest": "^29.6.1",
"jest-dev-server": "^9.0.0",
"jest-fail-on-console": "^3.1.1",
"jest-fetch-mock": "^3.0.3",
"jest-styled-components": "^7.0.8",
"ms.macro": "^2.0.0",
"path-browserify": "^1.0.1",
"prettier": "^2.8.8",
"prettier": "^2.7.1",
"process": "^0.11.10",
"react-scripts": "^5.0.1",
"resize-observer-polyfill": "^1.5.1",
"serve": "^11.3.2",
"source-map-explorer": "^2.5.3",
"ts-jest": "^29.1.1",
"ts-transform-graphql-tag": "^0.2.1",
"typechain": "^5.0.0",
"typescript": "^4.4.3",
"wrangler": "https://prerelease-registry.devprod.cloudflare.dev/workers-sdk/runs/4925945367/npm-package-wrangler-3048",
"webpack-retry-chunk-load-plugin": "^3.1.1",
"yarn-deduplicate": "^6.0.0"
},
"dependencies": {
@@ -175,10 +165,10 @@
"@uniswap/permit2-sdk": "1.2.0",
"@uniswap/redux-multicall": "^1.1.8",
"@uniswap/router-sdk": "^1.3.0",
"@uniswap/sdk-core": "^3.2.6",
"@uniswap/smart-order-router": "3.13.5",
"@uniswap/sdk-core": "^3.2.3",
"@uniswap/smart-order-router": "^3.12.1",
"@uniswap/token-lists": "^1.0.0-beta.31",
"@uniswap/universal-router-sdk": "^1.5.3",
"@uniswap/universal-router-sdk": "^1.5.1",
"@uniswap/v2-core": "1.0.0",
"@uniswap/v2-periphery": "^1.1.0-beta.0",
"@uniswap/v2-sdk": "^3.0.1",
@@ -200,12 +190,13 @@
"@web3-react/core": "^8.2.0",
"@web3-react/eip1193": "^8.2.0",
"@web3-react/empty": "^8.2.0",
"@web3-react/gnosis-safe": "^8.2.1",
"@web3-react/gnosis-safe": "^8.2.0",
"@web3-react/metamask": "^8.2.0",
"@web3-react/network": "^8.2.0",
"@web3-react/types": "^8.2.0",
"@web3-react/url": "^8.2.0",
"@web3-react/walletconnect-v2": "^8.3.7",
"@web3-react/walletconnect": "^8.2.0",
"@web3-react/walletconnect-v2": "^8.3.3",
"ajv": "^8.11.0",
"ajv-formats": "^2.1.1",
"array.prototype.flat": "^1.2.4",

View File

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

View File

@@ -19,9 +19,9 @@
<meta
http-equiv="Content-Security-Policy"
<% if (process.env.REACT_APP_CSP_ALLOW_UNSAFE_EVAL) { %>
content="script-src 'self' 'unsafe-inline' 'unsafe-eval'"
content="script-src 'self' https://www.google-analytics.com https://www.googletagmanager.com 'unsafe-inline' 'unsafe-eval'"
<% } else { %>
content="script-src 'self' 'unsafe-inline'"
content="script-src 'self' https://www.google-analytics.com https://www.googletagmanager.com 'unsafe-inline'"
<% } %>
/>
@@ -37,6 +37,8 @@
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="preconnect" href="https://www.google-analytics.com/" />
<link rel="preload" href="%PUBLIC_URL%/fonts/Inter-roman.var.woff2" as="font" type="font/woff2" crossorigin />
<style>

View File

@@ -1,11 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_13871_12533)">
<path d="M12.9341 2.74118H3.05524V11.7259H12.9341V2.74118Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.9947 8C15.9947 12.4154 12.4154 15.9947 7.99999 15.9947C3.58465 15.9947 0.00531006 12.4154 0.00531006 8C0.00531006 3.58466 3.58465 0.00532532 7.99999 0.00532532C12.4154 0.00532532 15.9947 3.58466 15.9947 8ZM5.73452 11.1815H4.18298C3.85696 11.1815 3.69591 11.1815 3.59772 11.1187C3.49166 11.0499 3.42685 10.936 3.41899 10.8103C3.4131 10.6945 3.49363 10.553 3.65467 10.2702L7.48562 3.51761C7.64864 3.23086 7.73112 3.08749 7.83521 3.03447C7.94715 2.97751 8.08071 2.97751 8.19266 3.03447C8.29675 3.08749 8.37924 3.23086 8.54224 3.51761L9.32981 4.89239L9.33382 4.89941C9.50989 5.20703 9.59917 5.36303 9.63815 5.52675C9.68135 5.70548 9.68135 5.89402 9.63815 6.07274C9.59887 6.23771 9.51049 6.39484 9.33177 6.70711L7.31946 10.2643L7.31426 10.2734C7.13703 10.5836 7.04722 10.7408 6.92274 10.8594C6.78722 10.989 6.62421 11.0832 6.44549 11.1363C6.28247 11.1815 6.09983 11.1815 5.73452 11.1815ZM9.65268 11.1815H11.8759C12.2038 11.1815 12.3689 11.1815 12.4671 11.1168C12.5731 11.048 12.6399 10.9321 12.6458 10.8064C12.6515 10.6943 12.5727 10.5584 12.4184 10.292C12.413 10.283 12.4077 10.2737 12.4023 10.2643L11.2887 8.35928L11.276 8.33783C11.1195 8.07321 11.0405 7.93958 10.9391 7.88793C10.8272 7.83096 10.6955 7.83096 10.5836 7.88793C10.4815 7.94095 10.399 8.0804 10.236 8.36124L9.12633 10.2663L9.12253 10.2729C8.96009 10.5533 8.87891 10.6934 8.88477 10.8084C8.89262 10.9341 8.95743 11.0499 9.06348 11.1187C9.15973 11.1815 9.3247 11.1815 9.65268 11.1815Z" fill="#E84142"/>
</g>
<defs>
<clipPath id="clip0_13871_12533">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

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

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,24 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_13571_129912)">
<rect width="40" height="40" fill="url(#paint0_linear_13571_129912)"/>
<path d="M20 40C31.0457 40 40 31.0457 40 20C40 8.9543 31.0457 0 20 0C8.9543 0 0 8.9543 0 20C0 31.0457 8.9543 40 20 40Z" fill="url(#paint1_linear_13571_129912)"/>
<path d="M34.5575 20.2857H30.9819C30.9819 13.0516 25.0541 7.1875 17.7414 7.1875C10.5191 7.1875 4.64737 12.908 4.50368 20.0182C4.35499 27.3678 11.3253 33.75 18.7558 33.75H19.6904C26.2413 33.75 35.0216 28.6771 36.3934 22.4961C36.6469 21.3567 35.7369 20.2857 34.5575 20.2857ZM12.4278 20.6079C12.4278 21.5753 11.628 22.3665 10.6501 22.3665C9.67215 22.3665 8.87237 21.575 8.87237 20.6079V17.7629C8.87237 16.7955 9.67215 16.0043 10.6501 16.0043C11.628 16.0043 12.4278 16.7955 12.4278 17.7629V20.6079ZM18.6007 20.6079C18.6007 21.5753 17.801 22.3665 16.8231 22.3665C15.8451 22.3665 15.0453 21.575 15.0453 20.6079V17.7629C15.0453 16.7955 15.8455 16.0043 16.8231 16.0043C17.801 16.0043 18.6007 16.7955 18.6007 17.7629V20.6079Z" fill="url(#paint2_linear_13571_129912)"/>
</g>
<defs>
<linearGradient id="paint0_linear_13571_129912" x1="20" y1="0" x2="20" y2="40" gradientUnits="userSpaceOnUse">
<stop stop-color="#534BB1"/>
<stop offset="1" stop-color="#551BF9"/>
</linearGradient>
<linearGradient id="paint1_linear_13571_129912" x1="20" y1="0" x2="20" y2="40" gradientUnits="userSpaceOnUse">
<stop stop-color="#534BB1"/>
<stop offset="1" stop-color="#551BF9"/>
</linearGradient>
<linearGradient id="paint2_linear_13571_129912" x1="20.4687" y1="7.1875" x2="20.4687" y2="33.75" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.82"/>
</linearGradient>
<clipPath id="clip0_13571_129912">
<rect width="40" height="40" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,6 +1,5 @@
import { TraceEvent } from '@uniswap/analytics'
import { BrowserEvent, InterfaceElementName, SharedEventName } from '@uniswap/analytics-events'
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
import styled from 'styled-components/macro'
import { BREAKPOINTS, ExternalLink, StyledRouterLink } from 'theme'
import { useIsDarkMode } from 'theme/components/ThemeToggle'
@@ -138,7 +137,6 @@ const LogoSectionContent = () => {
}
export const AboutFooter = () => {
const shouldDisableNFTRoutes = useDisableNFTRoutes()
return (
<Footer>
<LogoSectionLeft>
@@ -150,7 +148,7 @@ export const AboutFooter = () => {
<LinkGroupTitle>App</LinkGroupTitle>
<TextLink to="/swap">Swap</TextLink>
<TextLink to="/tokens">Tokens</TextLink>
{!shouldDisableNFTRoutes && <TextLink to="/nfts">NFTs</TextLink>}
<TextLink to="/nfts">NFTs</TextLink>
<TextLink to="/pools">Pools</TextLink>
</LinkGroup>
<LinkGroup>

View File

@@ -12,14 +12,14 @@ import { formatDelta } from 'components/Tokens/TokenDetails/PriceChart'
import Tooltip from 'components/Tooltip'
import { getConnection } from 'connection'
import { usePortfolioBalancesQuery } from 'graphql/data/__generated__/types-and-hooks'
import { GQL_MAINNET_CHAINS } from 'graphql/data/util'
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
import { useAtomValue } from 'jotai/utils'
import { useProfilePageState, useSellAsset, useWalletCollections } from 'nft/hooks'
import { useIsNftClaimAvailable } from 'nft/hooks/useIsNftClaimAvailable'
import { ProfilePageStateType } from 'nft/types'
import { useCallback, useState } from 'react'
import { ArrowDownRight, ArrowUpRight, CreditCard, IconProps, Info, LogOut, Settings } from 'react-feather'
import { useNavigate } from 'react-router-dom'
import { shouldDisableNFTRoutesAtom } from 'state/application/atoms'
import { useAppDispatch } from 'state/hooks'
import { updateSelectedWallet } from 'state/user/reducer'
import styled, { useTheme } from 'styled-components/macro'
@@ -40,7 +40,6 @@ const AuthenticatedHeaderWrapper = styled.div`
display: flex;
flex-direction: column;
flex: 1;
overflow: auto;
`
const HeaderButton = styled(ThemeButton)`
@@ -105,6 +104,7 @@ const StatusWrapper = styled.div`
display: inline-block;
width: 70%;
max-width: 70%;
overflow: hidden;
padding-right: 14px;
display: inline-flex;
`
@@ -171,7 +171,7 @@ export default function AuthenticatedHeader({ account, openSettings }: { account
const clearCollectionFilters = useWalletCollections((state) => state.clearCollectionFilters)
const isClaimAvailable = useIsNftClaimAvailable((state) => state.isClaimAvailable)
const shouldDisableNFTRoutes = useDisableNFTRoutes()
const shouldDisableNFTRoutes = useAtomValue(shouldDisableNFTRoutesAtom)
const unclaimedAmount: CurrencyAmount<Token> | undefined = useUserUnclaimedAmount(account)
const isUnclaimed = useUserHasAvailableClaim(account)
@@ -227,10 +227,9 @@ export default function AuthenticatedHeader({ account, openSettings }: { account
const closeFiatOnrampUnavailableTooltip = useCallback(() => setShow(false), [setShow])
const { data: portfolioBalances } = usePortfolioBalancesQuery({
variables: { ownerAddress: account ?? '', chains: GQL_MAINNET_CHAINS },
variables: { ownerAddress: account ?? '' },
fetchPolicy: 'cache-only', // PrefetchBalancesWrapper handles balance fetching/staleness; this component only reads from cache
})
const portfolio = portfolioBalances?.portfolios?.[0]
const totalBalance = portfolio?.tokensTotalDenominatedValue?.value
const absoluteChange = portfolio?.tokensTotalDenominatedValueChange?.absolute?.value
@@ -241,7 +240,7 @@ export default function AuthenticatedHeader({ account, openSettings }: { account
<AuthenticatedHeaderWrapper>
<HeaderWrapper>
<StatusWrapper>
<StatusIcon account={account} connection={connection} size={40} />
<StatusIcon connection={connection} size={40} />
{account && (
<AccountNamesWrapper>
<ThemedText.SubHeader>

View File

@@ -1,4 +1,4 @@
import { ChainId, Token, TradeType as MockTradeType } from '@uniswap/sdk-core'
import { SupportedChainId, Token, TradeType as MockTradeType } from '@uniswap/sdk-core'
import { PERMIT2_ADDRESS } from '@uniswap/universal-router-sdk'
import { DAI as MockDAI, nativeOnChain, USDC_MAINNET as MockUSDC_MAINNET, USDT as MockUSDT } from 'constants/tokens'
import { TransactionStatus as MockTxStatus } from 'graphql/data/__generated__/types-and-hooks'
@@ -46,7 +46,7 @@ function mockSwapInfo(
const mockAccount1 = '0x000000000000000000000000000000000000000001'
const mockAccount2 = '0x000000000000000000000000000000000000000002'
const mockChainId = ChainId.MAINNET
const mockChainId = SupportedChainId.MAINNET
const mockSpenderAddress = PERMIT2_ADDRESS[mockChainId]
const mockCurrencyAmountRaw = '1000000000000000000'
const mockCurrencyAmountRawUSDC = '1000000'
@@ -246,7 +246,7 @@ describe('parseLocalActivity', () => {
status: 1,
},
} as TransactionDetails
const chainId = ChainId.MAINNET
const chainId = SupportedChainId.MAINNET
expect(parseLocalActivity(details, chainId, mockTokenAddressMap)).toEqual({
chainId: 1,
currencies: [MockUSDC_MAINNET, MockDAI],
@@ -287,7 +287,7 @@ describe('parseLocalActivity', () => {
status: 1,
},
} as TransactionDetails
const chainId = ChainId.MAINNET
const chainId = SupportedChainId.MAINNET
expect(parseLocalActivity(details, chainId, mockTokenAddressMap)).toMatchObject({
chainId: 1,
currencies: [MockUSDC_MAINNET, MockDAI],
@@ -311,7 +311,7 @@ describe('parseLocalActivity', () => {
status: 1,
},
} as TransactionDetails
const chainId = ChainId.MAINNET
const chainId = SupportedChainId.MAINNET
const tokens = {} as ChainTokenMap
expect(parseLocalActivity(details, chainId, tokens)).toMatchObject({
chainId: 1,

View File

@@ -1,8 +1,9 @@
import { BigNumber } from '@ethersproject/bignumber'
import { t } from '@lingui/macro'
import { formatCurrencyAmount } from '@uniswap/conedison/format'
import { ChainId, Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { nativeOnChain } from '@uniswap/smart-order-router'
import { SupportedChainId } from 'constants/chains'
import { TransactionStatus } from 'graphql/data/__generated__/types-and-hooks'
import { ChainTokenMap, useAllTokensMultichain } from 'hooks/Tokens'
import { useMemo } from 'react'
@@ -25,7 +26,7 @@ import {
import { getActivityTitle } from '../constants'
import { Activity, ActivityMap } from './types'
function getCurrency(currencyId: string, chainId: ChainId, tokens: ChainTokenMap): Currency | undefined {
function getCurrency(currencyId: string, chainId: SupportedChainId, tokens: ChainTokenMap): Currency | undefined {
return currencyId === 'ETH' ? nativeOnChain(chainId) : tokens[chainId]?.[currencyId]
}
@@ -45,7 +46,7 @@ function buildCurrencyDescriptor(
function parseSwap(
swap: ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo,
chainId: ChainId,
chainId: SupportedChainId,
tokens: ChainTokenMap
): Partial<Activity> {
const tokenIn = getCurrency(swap.inputCurrencyId, chainId, tokens)
@@ -61,7 +62,7 @@ function parseSwap(
}
}
function parseWrap(wrap: WrapTransactionInfo, chainId: ChainId, status: TransactionStatus): Partial<Activity> {
function parseWrap(wrap: WrapTransactionInfo, chainId: SupportedChainId, status: TransactionStatus): Partial<Activity> {
const native = nativeOnChain(chainId)
const wrapped = native.wrapped
const [input, output] = wrap.unwrapped ? [wrapped, native] : [native, wrapped]
@@ -75,7 +76,7 @@ function parseWrap(wrap: WrapTransactionInfo, chainId: ChainId, status: Transact
function parseApproval(
approval: ApproveTransactionInfo,
chainId: ChainId,
chainId: SupportedChainId,
tokens: ChainTokenMap,
status: TransactionStatus
): Partial<Activity> {
@@ -96,7 +97,7 @@ type GenericLPInfo = Omit<
AddLiquidityV3PoolTransactionInfo | RemoveLiquidityV3TransactionInfo | AddLiquidityV2PoolTransactionInfo,
'type'
>
function parseLP(lp: GenericLPInfo, chainId: ChainId, tokens: ChainTokenMap): Partial<Activity> {
function parseLP(lp: GenericLPInfo, chainId: SupportedChainId, tokens: ChainTokenMap): Partial<Activity> {
const baseCurrency = getCurrency(lp.baseCurrencyId, chainId, tokens)
const quoteCurrency = getCurrency(lp.quoteCurrencyId, chainId, tokens)
const [baseRaw, quoteRaw] = [lp.expectedAmountBaseRaw, lp.expectedAmountQuoteRaw]
@@ -107,7 +108,7 @@ function parseLP(lp: GenericLPInfo, chainId: ChainId, tokens: ChainTokenMap): Pa
function parseCollectFees(
collect: CollectFeesTransactionInfo,
chainId: ChainId,
chainId: SupportedChainId,
tokens: ChainTokenMap
): Partial<Activity> {
// Adapts CollectFeesTransactionInfo to generic LP type
@@ -122,7 +123,7 @@ function parseCollectFees(
function parseMigrateCreateV3(
lp: MigrateV2LiquidityToV3TransactionInfo | CreateV3PoolTransactionInfo,
chainId: ChainId,
chainId: SupportedChainId,
tokens: ChainTokenMap
): Partial<Activity> {
const baseCurrency = getCurrency(lp.baseCurrencyId, chainId, tokens)
@@ -136,7 +137,7 @@ function parseMigrateCreateV3(
export function parseLocalActivity(
details: TransactionDetails,
chainId: ChainId,
chainId: SupportedChainId,
tokens: ChainTokenMap
): Activity | undefined {
try {

View File

@@ -1,7 +1,8 @@
import { t } from '@lingui/macro'
import { formatFiatPrice, formatNumberOrString, NumberType } from '@uniswap/conedison/format'
import { ChainId, NONFUNGIBLE_POSITION_MANAGER_ADDRESSES, UNI_ADDRESSES } from '@uniswap/sdk-core'
import { SupportedChainId } from '@uniswap/sdk-core'
import moonpayLogoSrc from 'assets/svg/moonpay.svg'
import { NONFUNGIBLE_POSITION_MANAGER_ADDRESSES, UNI_ADDRESS } from 'constants/addresses'
import { nativeOnChain } from 'constants/tokens'
import {
ActivityType,
@@ -13,7 +14,7 @@ import {
TokenApprovalPartsFragment,
TokenTransferPartsFragment,
} from 'graphql/data/__generated__/types-and-hooks'
import { logSentryErrorForUnsupportedChain, supportedChainIdFromGQLChain } from 'graphql/data/util'
import { fromGraphQLChain } from 'graphql/data/util'
import ms from 'ms.macro'
import { useEffect, useState } from 'react'
import { isAddress } from 'utils'
@@ -37,7 +38,7 @@ const ENS_IMG =
'https://464911102-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/collections%2F2TjMAeHSzwlQgcOdL48E%2Ficon%2FKWP0gk2C6bdRPliWIA6o%2Fens%20transparent%20background.png?alt=media&token=bd28b063-5a75-4971-890c-97becea09076'
const COMMON_CONTRACTS: { [key: string]: Partial<Activity> | undefined } = {
[UNI_ADDRESSES[ChainId.MAINNET].toLowerCase()]: {
[UNI_ADDRESS[SupportedChainId.MAINNET].toLowerCase()]: {
title: t`UNI Governance`,
descriptor: t`Contract Interaction`,
logos: [UNI_IMG],
@@ -75,9 +76,10 @@ function isSameAddress(a?: string, b?: string) {
}
function callsPositionManagerContract(assetActivity: AssetActivityPartsFragment) {
const supportedChain = supportedChainIdFromGQLChain(assetActivity.chain)
if (!supportedChain) return false
return isSameAddress(assetActivity.transaction.to, NONFUNGIBLE_POSITION_MANAGER_ADDRESSES[supportedChain])
return isSameAddress(
assetActivity.transaction.to,
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES[fromGraphQLChain(assetActivity.chain)]
)
}
// Gets counts for number of NFTs in each collection present
@@ -91,24 +93,15 @@ function getCollectionCounts(nftTransfers: NftTransferPartsFragment[]): { [key:
}, {} as { [key: string]: number | undefined })
}
function getSwapTitle(sent: TokenTransferPartsFragment, received: TokenTransferPartsFragment): string | undefined {
const supportedSentChain = supportedChainIdFromGQLChain(sent.asset.chain)
const supportedReceivedChain = supportedChainIdFromGQLChain(received.asset.chain)
if (!supportedSentChain || !supportedReceivedChain) {
logSentryErrorForUnsupportedChain({
extras: { sentAsset: sent.asset, receivedAsset: received.asset },
errorMessage: 'Invalid activity from unsupported chain received from GQL',
})
return undefined
}
function getSwapTitle(sent: TokenTransferPartsFragment, received: TokenTransferPartsFragment) {
if (
sent.tokenStandard === 'NATIVE' &&
isSameAddress(nativeOnChain(supportedSentChain).wrapped.address, received.asset.address)
isSameAddress(nativeOnChain(fromGraphQLChain(sent.asset.chain)).wrapped.address, received.asset.address)
)
return t`Wrapped`
else if (
received.tokenStandard === 'NATIVE' &&
isSameAddress(nativeOnChain(supportedReceivedChain).wrapped.address, received.asset.address)
isSameAddress(nativeOnChain(fromGraphQLChain(received.asset.chain)).wrapped.address, received.asset.address)
) {
return t`Unwrapped`
} else {
@@ -276,17 +269,9 @@ function parseRemoteActivity(assetActivity: AssetActivityPartsFragment): Activit
},
{ NftTransfer: [], TokenTransfer: [], TokenApproval: [], NftApproval: [], NftApproveForAll: [] }
)
const supportedChain = supportedChainIdFromGQLChain(assetActivity.chain)
if (!supportedChain) {
logSentryErrorForUnsupportedChain({
extras: { assetActivity },
errorMessage: 'Invalid activity from unsupported chain received from GQL',
})
return undefined
}
const defaultFields = {
hash: assetActivity.transaction.hash,
chainId: supportedChain,
chainId: fromGraphQLChain(assetActivity.chain),
status: assetActivity.transaction.status,
timestamp: assetActivity.timestamp,
logos: getLogoSrcs(changes),
@@ -304,7 +289,7 @@ function parseRemoteActivity(assetActivity: AssetActivityPartsFragment): Activit
}
}
export function parseRemoteActivities(assetActivities?: readonly AssetActivityPartsFragment[]) {
export function parseRemoteActivities(assetActivities?: AssetActivityPartsFragment[]) {
return assetActivities?.reduce((acc: { [hash: string]: Activity }, assetActivity) => {
const activity = parseRemoteActivity(assetActivity)
if (activity) acc[activity.hash] = activity

View File

@@ -1,11 +1,12 @@
import { ChainId, Currency } from '@uniswap/sdk-core'
import { Currency } from '@uniswap/sdk-core'
import { SupportedChainId } from 'constants/chains'
import { AssetActivityPartsFragment, TransactionStatus } from 'graphql/data/__generated__/types-and-hooks'
type Receipt = AssetActivityPartsFragment['transaction']
export type Activity = {
hash: string
chainId: ChainId
chainId: SupportedChainId
status: TransactionStatus
timestamp: number
title: string

View File

@@ -1,5 +1,6 @@
import { ChainId, Token } from '@uniswap/sdk-core'
import { Token } from '@uniswap/sdk-core'
import { Pool, Position } from '@uniswap/v3-sdk'
import { SupportedChainId } from 'constants/chains'
import { useAllTokensMultichain } from 'hooks/Tokens'
import { atom, useAtom } from 'jotai'
import { atomWithStorage } from 'jotai/utils'
@@ -15,7 +16,7 @@ import { useInterfaceMulticallContracts } from './hooks'
export type PositionInfo = {
owner: string
chainId: ChainId
chainId: SupportedChainId
position: Position
pool: Pool
details: PositionDetails
@@ -58,7 +59,7 @@ export function useCachedPositions(account: string): UseCachedPositionsReturnTyp
return [cachedPositions[account], setPositionsAndStaleTimeout]
}
const poolAddressKey = (details: PositionDetails, chainId: ChainId) =>
const poolAddressKey = (details: PositionDetails, chainId: SupportedChainId) =>
`${chainId}-${details.token0}-${details.token1}-${details.fee}`
type PoolAddressMap = { [key: string]: string | undefined }
@@ -70,11 +71,11 @@ const poolAddressCacheAtom = atomWithStorage<PoolAddressMap>('poolCache', {})
export function usePoolAddressCache() {
const [cache, updateCache] = useAtom(poolAddressCacheAtom)
const get = useCallback(
(details: PositionDetails, chainId: ChainId) => cache[poolAddressKey(details, chainId)],
(details: PositionDetails, chainId: SupportedChainId) => cache[poolAddressKey(details, chainId)],
[cache]
)
const set = useCallback(
(details: PositionDetails, chainId: ChainId, address: string) =>
(details: PositionDetails, chainId: SupportedChainId, address: string) =>
updateCache((c) => ({ ...c, [poolAddressKey(details, chainId)]: address })),
[updateCache]
)
@@ -103,8 +104,8 @@ function useTokenCache() {
return { get, set }
}
type TokenGetterFn = (addresses: string[], chainId: ChainId) => Promise<{ [key: string]: Token | undefined }>
export function useGetCachedTokens(chains: ChainId[]): TokenGetterFn {
type TokenGetterFn = (addresses: string[], chainId: SupportedChainId) => Promise<{ [key: string]: Token | undefined }>
export function useGetCachedTokens(chains: SupportedChainId[]): TokenGetterFn {
const allTokens = useAllTokensMultichain()
const multicallContracts = useInterfaceMulticallContracts(chains)
const tokenCache = useTokenCache()

View File

@@ -1,7 +1,8 @@
import { ChainId, Token } from '@uniswap/sdk-core'
import { Token } from '@uniswap/sdk-core'
import ERC20_ABI from 'abis/erc20.json'
import { Erc20Interface } from 'abis/types/Erc20'
import { Erc20Bytes32Interface } from 'abis/types/Erc20Bytes32'
import { SupportedChainId } from 'constants/chains'
import { DEFAULT_ERC20_DECIMALS } from 'constants/tokens'
import { Interface } from 'ethers/lib/utils'
import { UniswapInterfaceMulticall } from 'types/v3'
@@ -37,7 +38,7 @@ async function fetchChunk(multicall: UniswapInterfaceMulticall, chunk: Call[]):
}
}
function tryParseToken(address: string, chainId: ChainId, data: CallResult[]) {
function tryParseToken(address: string, chainId: SupportedChainId, data: CallResult[]) {
try {
const [nameData, symbolData, decimalsData, nameDataBytes32, symbolDataBytes32] = data
@@ -60,7 +61,7 @@ function tryParseToken(address: string, chainId: ChainId, data: CallResult[]) {
}
}
function parseTokens(addresses: string[], chainId: ChainId, returnData: CallResult[]) {
function parseTokens(addresses: string[], chainId: SupportedChainId, returnData: CallResult[]) {
const tokenDataSlices = arrayToSlices(returnData, 5)
return tokenDataSlices.reduce((acc: TokenMap, slice, index) => {
@@ -89,7 +90,7 @@ const TokenPromiseCache: { [key: CurrencyKey]: Promise<Token | undefined> | unde
// Returns tokens using a single RPC call to the multicall contract
export async function getTokensAsync(
addresses: string[],
chainId: ChainId,
chainId: SupportedChainId,
multicall: UniswapInterfaceMulticall
): Promise<TokenMap> {
if (addresses.length === 0) return {}

View File

@@ -1,14 +1,10 @@
import {
ChainId,
MULTICALL_ADDRESSES,
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES as V3NFT_ADDRESSES,
Token,
} from '@uniswap/sdk-core'
import { Token } from '@uniswap/sdk-core'
import { AddressMap } from '@uniswap/smart-order-router'
import MulticallJSON from '@uniswap/v3-periphery/artifacts/contracts/lens/UniswapInterfaceMulticall.sol/UniswapInterfaceMulticall.json'
import NFTPositionManagerJSON from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
import { useWeb3React } from '@web3-react/core'
import { isSupportedChain } from 'constants/chains'
import { MULTICALL_ADDRESS, NONFUNGIBLE_POSITION_MANAGER_ADDRESSES as V3NFT_ADDRESSES } from 'constants/addresses'
import { isSupportedChain, SupportedChainId } from 'constants/chains'
import { RPC_PROVIDERS } from 'constants/providers'
import { BaseContract } from 'ethers/lib/ethers'
import { ContractInput, useUniswapPricesQuery } from 'graphql/data/__generated__/types-and-hooks'
@@ -27,7 +23,7 @@ type ContractMap<T extends BaseContract> = { [key: number]: T }
function useContractMultichain<T extends BaseContract>(
addressMap: AddressMap,
ABI: any,
chainIds?: ChainId[]
chainIds?: SupportedChainId[]
): ContractMap<T> {
const { chainId: walletChainId, provider: walletProvider } = useWeb3React()
@@ -39,26 +35,19 @@ function useContractMultichain<T extends BaseContract>(
.filter(isSupportedChain)
return relevantChains.reduce((acc: ContractMap<T>, chainId) => {
const provider =
walletProvider && walletChainId === chainId
? walletProvider
: isSupportedChain(chainId)
? RPC_PROVIDERS[chainId]
: undefined
if (provider) {
acc[chainId] = getContract(addressMap[chainId] ?? '', ABI, provider) as T
}
const provider = walletProvider && walletChainId === chainId ? walletProvider : RPC_PROVIDERS[chainId]
acc[chainId] = getContract(addressMap[chainId], ABI, provider) as T
return acc
}, {})
}, [ABI, addressMap, chainIds, walletChainId, walletProvider])
}
export function useV3ManagerContracts(chainIds: ChainId[]): ContractMap<NonfungiblePositionManager> {
export function useV3ManagerContracts(chainIds: SupportedChainId[]): ContractMap<NonfungiblePositionManager> {
return useContractMultichain<NonfungiblePositionManager>(V3NFT_ADDRESSES, NFTPositionManagerJSON.abi, chainIds)
}
export function useInterfaceMulticallContracts(chainIds: ChainId[]): ContractMap<UniswapInterfaceMulticall> {
return useContractMultichain<UniswapInterfaceMulticall>(MULTICALL_ADDRESSES, MulticallJSON.abi, chainIds)
export function useInterfaceMulticallContracts(chainIds: SupportedChainId[]): ContractMap<UniswapInterfaceMulticall> {
return useContractMultichain<UniswapInterfaceMulticall>(MULTICALL_ADDRESS, MulticallJSON.abi, chainIds)
}
type PriceMap = { [key: CurrencyKey]: number | undefined }

View File

@@ -1,5 +1,5 @@
import { BigNumber } from '@ethersproject/bignumber'
import { ChainId, WETH9 } from '@uniswap/sdk-core'
import { SupportedChainId, WETH9 } from '@uniswap/sdk-core'
import { FeeAmount, Pool, Position } from '@uniswap/v3-sdk'
import { USDC_MAINNET } from 'constants/tokens'
import { mocked } from 'test-utils/mocked'
@@ -10,13 +10,11 @@ import useMultiChainPositions from './useMultiChainPositions'
jest.mock('./useMultiChainPositions')
jest.spyOn(console, 'warn').mockImplementation()
const owner = '0xf5b6bb25f5beaea03dd014c6ef9fa9f3926bf36c'
const pool = new Pool(
USDC_MAINNET,
WETH9[ChainId.MAINNET],
WETH9[SupportedChainId.MAINNET],
FeeAmount.MEDIUM,
'1851127709498178402383049949138810',
'7076437181775065414',
@@ -34,7 +32,7 @@ const details = {
tokenId: BigNumber.from('0'),
operator: '0x0',
token0: USDC_MAINNET.address,
token1: WETH9[ChainId.MAINNET].address,
token1: WETH9[SupportedChainId.MAINNET].address,
fee: FeeAmount.MEDIUM,
tickLower: -100,
tickUpper: 100,
@@ -48,7 +46,7 @@ const useMultiChainPositionsReturnValue = {
positions: [
{
owner,
chainId: ChainId.MAINNET,
chainId: SupportedChainId.MAINNET,
position,
pool,
details,

View File

@@ -8,12 +8,12 @@ import { useToggleAccountDrawer } from 'components/AccountDrawer'
import Row from 'components/Row'
import { MouseoverTooltip } from 'components/Tooltip'
import { useFilterPossiblyMaliciousPositions } from 'hooks/useFilterPossiblyMaliciousPositions'
import { useSwitchChain } from 'hooks/useSwitchChain'
import { EmptyWalletModule } from 'nft/components/profile/view/EmptyWalletContent'
import { useCallback, useMemo, useReducer } from 'react'
import { useNavigate } from 'react-router-dom'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import { switchChain } from 'utils/switchChain'
import { ExpandoRow } from '../ExpandoRow'
import { PortfolioLogo } from '../PortfolioLogo'
@@ -126,12 +126,11 @@ function PositionListItem({ positionInfo }: { positionInfo: PositionInfo }) {
const navigate = useNavigate()
const toggleWalletDrawer = useToggleAccountDrawer()
const { chainId: walletChainId, connector } = useWeb3React()
const switchChain = useSwitchChain()
const onClick = useCallback(async () => {
if (walletChainId !== chainId) await switchChain(connector, chainId)
toggleWalletDrawer()
navigate('/pool/' + details.tokenId)
}, [walletChainId, chainId, switchChain, connector, toggleWalletDrawer, navigate, details.tokenId])
}, [walletChainId, chainId, connector, toggleWalletDrawer, navigate, details.tokenId])
const analyticsEventProperties = useMemo(
() => ({
chain_id: chainId,

View File

@@ -1,10 +1,11 @@
import { ChainId, CurrencyAmount, Token, V3_CORE_FACTORY_ADDRESSES } from '@uniswap/sdk-core'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import IUniswapV3PoolStateJSON from '@uniswap/v3-core/artifacts/contracts/interfaces/pool/IUniswapV3PoolState.sol/IUniswapV3PoolState.json'
import { computePoolAddress, Pool, Position } from '@uniswap/v3-sdk'
import { V3_CORE_FACTORY_ADDRESSES } from 'constants/addresses'
import { SupportedChainId } from 'constants/chains'
import { DEFAULT_ERC20_DECIMALS } from 'constants/tokens'
import { BigNumber } from 'ethers/lib/ethers'
import { Interface } from 'ethers/lib/utils'
import { useFilterChainsForAvalanche } from 'featureFlags/flags/avalanche'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { PositionDetails } from 'types/position'
import { NonfungiblePositionManager, UniswapInterfaceMulticall } from 'types/v3'
@@ -17,7 +18,7 @@ import { useInterfaceMulticallContracts, usePoolPriceMap, useV3ManagerContracts
function createPositionInfo(
owner: string,
chainId: ChainId,
chainId: SupportedChainId,
details: PositionDetails,
slot0: any,
tokenA: Token,
@@ -41,13 +42,11 @@ type FeeAmounts = [BigNumber, BigNumber]
const MAX_UINT128 = BigNumber.from(2).pow(128).sub(1)
const DEFAULT_CHAINS = [
ChainId.MAINNET,
ChainId.ARBITRUM_ONE,
ChainId.OPTIMISM,
ChainId.POLYGON,
ChainId.CELO,
ChainId.BNB,
ChainId.AVALANCHE,
SupportedChainId.MAINNET,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.OPTIMISM,
SupportedChainId.POLYGON,
SupportedChainId.CELO,
]
type UseMultiChainPositionsData = { positions?: PositionInfo[]; loading: boolean }
@@ -62,11 +61,10 @@ type UseMultiChainPositionsData = { positions?: PositionInfo[]; loading: boolean
* @returns positions, fees
*/
export default function useMultiChainPositions(account: string, chains = DEFAULT_CHAINS): UseMultiChainPositionsData {
const gatedChains = useFilterChainsForAvalanche(chains)
const pms = useV3ManagerContracts(gatedChains)
const multicalls = useInterfaceMulticallContracts(gatedChains)
const pms = useV3ManagerContracts(chains)
const multicalls = useInterfaceMulticallContracts(chains)
const getTokens = useGetCachedTokens(gatedChains)
const getTokens = useGetCachedTokens(chains)
const poolAddressCache = usePoolAddressCache()
const [cachedPositions, setPositions] = useCachedPositions(account)
@@ -119,7 +117,7 @@ export default function useMultiChainPositions(account: string, chains = DEFAULT
// Combines PositionDetails with Pool data to build our return type
const fetchPositionInfo = useCallback(
async (positionDetails: PositionDetails[], chainId: ChainId, multicall: UniswapInterfaceMulticall) => {
async (positionDetails: PositionDetails[], chainId: SupportedChainId, multicall: UniswapInterfaceMulticall) => {
const poolInterface = new Interface(IUniswapV3PoolStateJSON.abi) as UniswapV3PoolInterface
const tokens = await getTokens(
positionDetails.flatMap((details) => [details.token0, details.token1]),
@@ -160,7 +158,7 @@ export default function useMultiChainPositions(account: string, chains = DEFAULT
)
const fetchPositionsForChain = useCallback(
async (chainId: ChainId): Promise<PositionInfo[]> => {
async (chainId: SupportedChainId): Promise<PositionInfo[]> => {
try {
const pm = pms[chainId]
const multicall = multicalls[chainId]

View File

@@ -1,4 +1,4 @@
import { ChainId } from '@uniswap/sdk-core'
import { SupportedChainId } from '@uniswap/sdk-core'
import { DAI_ARBITRUM } from '@uniswap/smart-order-router'
import { BRIDGED_USDC_ARBITRUM, DAI, USDC_MAINNET } from 'constants/tokens'
import { render } from 'test-utils/render'
@@ -7,13 +7,13 @@ import { PortfolioLogo } from './PortfolioLogo'
describe('PortfolioLogo', () => {
it('renders without L2 icon', () => {
const { container } = render(<PortfolioLogo chainId={ChainId.MAINNET} currencies={[DAI, USDC_MAINNET]} />)
const { container } = render(<PortfolioLogo chainId={SupportedChainId.MAINNET} currencies={[DAI, USDC_MAINNET]} />)
expect(container).toMatchSnapshot()
})
it('renders with L2 icon', () => {
const { container } = render(
<PortfolioLogo chainId={ChainId.ARBITRUM_ONE} currencies={[DAI_ARBITRUM, BRIDGED_USDC_ARBITRUM]} />
<PortfolioLogo chainId={SupportedChainId.ARBITRUM_ONE} currencies={[DAI_ARBITRUM, BRIDGED_USDC_ARBITRUM]} />
)
expect(container).toMatchSnapshot()
})

View File

@@ -1,9 +1,10 @@
import { ChainId, Currency } from '@uniswap/sdk-core'
import { Currency } from '@uniswap/sdk-core'
import blankTokenUrl from 'assets/svg/blank_token.svg'
import { ReactComponent as UnknownStatus } from 'assets/svg/contract-interaction.svg'
import { LogoImage, MissingImageLogo } from 'components/Logo/AssetLogo'
import { Unicon } from 'components/Unicon'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import useTokenLogoSource from 'hooks/useAssetLogoSource'
import useENSAvatar from 'hooks/useENSAvatar'
import React from 'react'
@@ -36,7 +37,7 @@ const DoubleLogoContainer = styled.div`
`
type MultiLogoProps = {
chainId: ChainId
chainId: SupportedChainId
accountAddress?: string
currencies?: Array<Currency | undefined>
images?: (string | undefined)[]
@@ -84,7 +85,7 @@ const L2LogoContainer = styled.div<{ $backgroundColor?: string }>`
* Renders an image by prioritizing a list of sources, and then eventually a fallback triangle alert
*/
export function PortfolioLogo({
chainId = ChainId.MAINNET,
chainId = SupportedChainId.MAINNET,
accountAddress,
currencies,
images,
@@ -141,7 +142,7 @@ export function PortfolioLogo({
}
const L2Logo =
chainId !== ChainId.MAINNET && chainLogo ? (
chainId !== SupportedChainId.MAINNET && chainLogo ? (
<L2LogoContainer $backgroundColor={squareLogoUrl ? theme.backgroundSurface : theme.textPrimary}>
{squareLogoUrl ? (
<SquareChainLogo src={chainLogo} alt="chainLogo" />

View File

@@ -4,12 +4,7 @@ import { formatNumber, NumberType } from '@uniswap/conedison/format'
import Row from 'components/Row'
import { formatDelta } from 'components/Tokens/TokenDetails/PriceChart'
import { PortfolioBalancesQuery, usePortfolioBalancesQuery } from 'graphql/data/__generated__/types-and-hooks'
import {
getTokenDetailsURL,
GQL_MAINNET_CHAINS,
gqlToCurrency,
logSentryErrorForUnsupportedChain,
} from 'graphql/data/util'
import { getTokenDetailsURL, gqlToCurrency } from 'graphql/data/util'
import { useAtomValue } from 'jotai/utils'
import { EmptyWalletModule } from 'nft/components/profile/view/EmptyWalletContent'
import { useCallback, useMemo, useState } from 'react'
@@ -36,7 +31,7 @@ export default function Tokens({ account }: { account: string }) {
const [showHiddenTokens, setShowHiddenTokens] = useState(false)
const { data } = usePortfolioBalancesQuery({
variables: { ownerAddress: account, chains: GQL_MAINNET_CHAINS },
variables: { ownerAddress: account },
fetchPolicy: 'cache-only', // PrefetchBalancesWrapper handles balance fetching/staleness; this component only reads from cache
errorPolicy: 'all',
})
@@ -108,13 +103,6 @@ function TokenRow({ token, quantity, denominatedValue, tokenProjectMarket }: Tok
}, [navigate, token, toggleWalletDrawer])
const currency = gqlToCurrency(token)
if (!currency) {
logSentryErrorForUnsupportedChain({
extras: { token },
errorMessage: 'Token from unsupported chain received from Mini Portfolio Token Balance Query',
})
return null
}
return (
<TraceEvent
events={[BrowserEvent.onClick]}

View File

@@ -4,12 +4,13 @@ import { BrowserEvent, InterfaceElementName, InterfaceSectionName, SharedEventNa
import Column from 'components/Column'
import { LoaderV2 } from 'components/Icons/LoadingSpinner'
import { AutoRow } from 'components/Row'
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
import { useIsNftPage } from 'hooks/useIsNftPage'
import { useAtomValue } from 'jotai/utils'
import { useEffect, useState } from 'react'
import { shouldDisableNFTRoutesAtom } from 'state/application/atoms'
import { useHasPendingTransactions } from 'state/transactions/hooks'
import styled, { useTheme } from 'styled-components/macro'
import { BREAKPOINTS, ThemedText } from 'theme'
import { ThemedText } from 'theme'
import { ActivityTab } from './Activity'
import NFTs from './NFTs'
@@ -24,10 +25,6 @@ const Wrapper = styled(Column)`
height: 100%;
gap: 12px;
@media screen and (max-width: ${BREAKPOINTS.sm}px) {
margin-bottom: 48px;
}
${PortfolioRowWrapper} {
&:hover {
background: ${({ theme }) => theme.hoverDefault};
@@ -98,8 +95,8 @@ export default function MiniPortfolio({ account }: { account: string }) {
const isNftPage = useIsNftPage()
const theme = useTheme()
const [currentPage, setCurrentPage] = useState(isNftPage ? 1 : 0)
const shouldDisableNFTRoutes = useDisableNFTRoutes()
const [activityUnread, setActivityUnread] = useState(false)
const shouldDisableNFTRoutes = useAtomValue(shouldDisableNFTRoutesAtom)
const { component: Page, key: currentKey } = Pages[currentPage]

View File

@@ -1,6 +1,5 @@
import { useWeb3React } from '@web3-react/core'
import { usePortfolioBalancesLazyQuery } from 'graphql/data/__generated__/types-and-hooks'
import { GQL_MAINNET_CHAINS } from 'graphql/data/util'
import usePrevious from 'hooks/usePrevious'
import { PropsWithChildren, useCallback, useEffect, useMemo, useState } from 'react'
import { useAllTransactions } from 'state/transactions/hooks'
@@ -45,7 +44,7 @@ export default function PrefetchBalancesWrapper({ children }: PropsWithChildren)
const [hasUnfetchedBalances, setHasUnfetchedBalances] = useState(true)
const fetchBalances = useCallback(() => {
if (account) {
prefetchPortfolioBalances({ variables: { ownerAddress: account, chains: GQL_MAINNET_CHAINS } })
prefetchPortfolioBalances({ variables: { ownerAddress: account } })
setHasUnfetchedBalances(false)
}
}, [account, prefetchPortfolioBalances])

View File

@@ -1,14 +1,14 @@
import { Trans } from '@lingui/macro'
import { sendAnalyticsEvent } from '@uniswap/analytics'
import { InterfaceElementName } from '@uniswap/analytics-events'
import { WalletConnect as WalletConnectv2 } from '@web3-react/walletconnect-v2'
import { WalletConnect } from '@web3-react/walletconnect'
import Column, { AutoColumn } from 'components/Column'
import Modal from 'components/Modal'
import { RowBetween } from 'components/Row'
import { uniwalletWCV2ConnectConnection } from 'connection'
import { uniwalletConnectConnection } from 'connection'
import { ActivationStatus, useActivationState } from 'connection/activate'
import { ConnectionType } from 'connection/types'
import { UniwalletConnect as UniwalletConnectV2 } from 'connection/WalletConnectV2'
import { UniwalletConnect } from 'connection/WalletConnect'
import { QRCodeSVG } from 'qrcode.react'
import { useEffect, useState } from 'react'
import styled, { useTheme } from 'styled-components/macro'
@@ -44,14 +44,16 @@ export default function UniwalletModal() {
// Displays the modal if a Uniswap Wallet Connection is pending & qrcode URI is available
const open =
activationState.status === ActivationStatus.PENDING &&
activationState.connection.type === ConnectionType.UNISWAP_WALLET_V2 &&
activationState.connection.type === ConnectionType.UNISWAP_WALLET &&
!!uri
useEffect(() => {
const connectorV2 = uniwalletWCV2ConnectConnection.connector as WalletConnectv2
connectorV2.events.addListener(UniwalletConnectV2.UNI_URI_AVAILABLE, (uri: string) => {
uri && setUri(uri)
})
;(uniwalletConnectConnection.connector as WalletConnect).events.addListener(
UniwalletConnect.UNI_URI_AVAILABLE,
(uri) => {
uri && setUri(uri)
}
)
}, [])
useEffect(() => {

View File

@@ -1,12 +1,12 @@
import { BigNumber } from '@ethersproject/bignumber'
import type { TransactionResponse } from '@ethersproject/providers'
import { UNISWAP_NFT_AIRDROP_CLAIM_ADDRESS } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import uniswapNftAirdropClaim from 'abis/uniswap-nft-airdrop-claim.json'
import airdropBackgroundv2 from 'assets/images/airdopBackground.png'
import { ButtonEmphasis, ButtonSize, ThemeButton } from 'components/Button'
import { OpacityHoverState } from 'components/Common'
import Loader from 'components/Icons/LoadingSpinner'
import { UNISWAP_NFT_AIRDROP_CLAIM_ADDRESS } from 'constants/addresses'
import { useContract } from 'hooks/useContract'
import { ChevronRightIcon } from 'nft/components/icons'
import { useIsNftClaimAvailable } from 'nft/hooks/useIsNftClaimAvailable'

View File

@@ -1,7 +1,6 @@
import { Trans } from '@lingui/macro'
import { TraceEvent } from '@uniswap/analytics'
import { BrowserEvent, InterfaceElementName, SwapEventName } from '@uniswap/analytics-events'
import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import { useWeb3React } from '@web3-react/core'
@@ -14,6 +13,7 @@ import { ReactNode, useCallback, useState } from 'react'
import { Lock } from 'react-feather'
import styled, { useTheme } from 'styled-components/macro'
import { flexColumnNoWrap, flexRowNoWrap } from 'theme/styles'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
import { useCurrencyBalance } from '../../state/connection/hooks'
@@ -323,7 +323,7 @@ export default function SwapCurrencyInputPanel({
renderBalance ? (
renderBalance(selectedCurrencyBalance)
) : (
<Trans>Balance: {formatCurrencyAmount(selectedCurrencyBalance, NumberType.TokenNonTx)}</Trans>
<Trans>Balance: {formatCurrencyAmount(selectedCurrencyBalance, 4)}</Trans>
)
) : null}
</ThemedText.DeprecatedBody>

View File

@@ -1,8 +1,9 @@
import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'featureFlags'
import { useAvalancheFlag } from 'featureFlags/flags/avalanche'
import { DetailsV2Variant, useDetailsV2Flag } from 'featureFlags/flags/nftDetails'
import { useRoutingAPIForPriceFlag } from 'featureFlags/flags/priceRoutingApi'
import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc'
import { UnifiedRouterVariant, useRoutingAPIV2Flag } from 'featureFlags/flags/unifiedRouter'
import { useWalletConnectV2Flag } from 'featureFlags/flags/walletConnectV2'
import { useUpdateAtom } from 'jotai/utils'
import { Children, PropsWithChildren, ReactElement, ReactNode, useCallback, useState } from 'react'
import { X } from 'react-feather'
@@ -209,6 +210,12 @@ export default function FeatureFlagModal() {
featureFlag={FeatureFlag.detailsV2}
label="Use the new details page for nfts"
/>
<FeatureFlagOption
variant={UnifiedRouterVariant}
value={useRoutingAPIV2Flag()}
featureFlag={FeatureFlag.uraEnabled}
label="Enable the Unified Routing API"
/>
<FeatureFlagOption
variant={BaseVariant}
value={useRoutingAPIForPriceFlag()}
@@ -217,9 +224,9 @@ export default function FeatureFlagModal() {
/>
<FeatureFlagOption
variant={BaseVariant}
value={useAvalancheFlag()}
featureFlag={FeatureFlag.avalanche}
label="Enable Avalanche chain"
value={useWalletConnectV2Flag()}
featureFlag={FeatureFlag.walletConnectV2}
label="Uses WalletConnect V2 as default wallet connect connection"
/>
<FeatureFlagGroup name="Debug">
<FeatureFlagOption

View File

@@ -1,40 +1,39 @@
import { Trans } from '@lingui/macro'
import { ChainId, SUPPORTED_CHAINS } from '@uniswap/sdk-core'
import { FeeAmount } from '@uniswap/v3-sdk'
import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from 'constants/chains'
import type { ReactNode } from 'react'
export const FEE_AMOUNT_DETAIL: Record<
FeeAmount,
{ label: string; description: ReactNode; supportedChains: readonly ChainId[] }
{ label: string; description: ReactNode; supportedChains: SupportedChainId[] }
> = {
[FeeAmount.LOWEST]: {
label: '0.01',
description: <Trans>Best for very stable pairs.</Trans>,
supportedChains: [
ChainId.ARBITRUM_ONE,
ChainId.BNB,
ChainId.CELO,
ChainId.CELO_ALFAJORES,
ChainId.MAINNET,
ChainId.OPTIMISM,
ChainId.POLYGON,
ChainId.POLYGON_MUMBAI,
ChainId.AVALANCHE,
SupportedChainId.ARBITRUM_ONE,
SupportedChainId.BNB,
SupportedChainId.CELO,
SupportedChainId.CELO_ALFAJORES,
SupportedChainId.MAINNET,
SupportedChainId.OPTIMISM,
SupportedChainId.POLYGON,
SupportedChainId.POLYGON_MUMBAI,
],
},
[FeeAmount.LOW]: {
label: '0.05',
description: <Trans>Best for stable pairs.</Trans>,
supportedChains: SUPPORTED_CHAINS,
supportedChains: ALL_SUPPORTED_CHAIN_IDS,
},
[FeeAmount.MEDIUM]: {
label: '0.3',
description: <Trans>Best for most pairs.</Trans>,
supportedChains: SUPPORTED_CHAINS,
supportedChains: ALL_SUPPORTED_CHAIN_IDS,
},
[FeeAmount.HIGH]: {
label: '1',
description: <Trans>Best for exotic pairs.</Trans>,
supportedChains: SUPPORTED_CHAINS,
supportedChains: ALL_SUPPORTED_CHAIN_IDS,
},
}

View File

@@ -5,8 +5,6 @@ import { render } from 'test-utils/render'
import StatusIcon from './StatusIcon'
const ACCOUNT = '0x0'
jest.mock('../../hooks/useSocksBalance', () => ({
useHasSocks: () => true,
}))
@@ -15,15 +13,15 @@ describe('StatusIcon', () => {
describe('with no account', () => {
it('renders children in correct order', () => {
const supportedConnections = getConnections()
const injectedConnection = supportedConnections[2]
const component = render(<StatusIcon account={ACCOUNT} connection={injectedConnection} />)
const injectedConnection = supportedConnections[1]
const component = render(<StatusIcon connection={injectedConnection} />)
expect(component.getByTestId('StatusIconRoot')).toMatchSnapshot()
})
it('renders without mini icons', () => {
const supportedConnections = getConnections()
const injectedConnection = supportedConnections[2]
const component = render(<StatusIcon account={ACCOUNT} connection={injectedConnection} showMiniIcons={false} />)
const injectedConnection = supportedConnections[1]
const component = render(<StatusIcon connection={injectedConnection} showMiniIcons={false} />)
expect(component.getByTestId('StatusIconRoot').children.length).toEqual(0)
})
})
@@ -38,16 +36,16 @@ describe('StatusIcon', () => {
it('renders children in correct order', () => {
const supportedConnections = getConnections()
const injectedConnection = supportedConnections[2]
const component = render(<StatusIcon account={ACCOUNT} connection={injectedConnection} />)
const injectedConnection = supportedConnections[1]
const component = render(<StatusIcon connection={injectedConnection} />)
expect(component.getByTestId('StatusIconRoot')).toMatchSnapshot()
})
it('renders without mini icons', () => {
const supportedConnections = getConnections()
const injectedConnection = supportedConnections[2]
const component = render(<StatusIcon account={ACCOUNT} connection={injectedConnection} showMiniIcons={false} />)
expect(component.getByTestId('StatusIconRoot').children.length).toEqual(0)
const injectedConnection = supportedConnections[1]
const component = render(<StatusIcon connection={injectedConnection} showMiniIcons={false} />)
expect(component.getByTestId('StatusIconRoot').children.length).toEqual(1)
})
})
})

View File

@@ -1,3 +1,4 @@
import { useWeb3React } from '@web3-react/core'
import { Unicon } from 'components/Unicon'
import { Connection, ConnectionType } from 'connection/types'
import useENSAvatar from 'hooks/useENSAvatar'
@@ -66,25 +67,24 @@ const MiniWalletIcon = ({ connection, side }: { connection: Connection; side: 'l
)
}
const MainWalletIcon = ({ account, connection, size }: { account: string; connection: Connection; size: number }) => {
const MainWalletIcon = ({ connection, size }: { connection: Connection; size: number }) => {
const { account } = useWeb3React()
const { avatar } = useENSAvatar(account ?? undefined)
if (!account) {
return null
} else if (avatar || (connection.type === ConnectionType.INJECTED && connection.getName() === 'MetaMask')) {
return <Identicon account={account} size={size} />
return <Identicon size={size} />
} else {
return <Unicon address={account} size={size} />
}
}
export default function StatusIcon({
account,
connection,
size = 16,
showMiniIcons = true,
}: {
account: string
connection: Connection
size?: number
showMiniIcons?: boolean
@@ -93,7 +93,7 @@ export default function StatusIcon({
return (
<IconWrapper size={size} data-testid="StatusIconRoot">
<MainWalletIcon account={account} connection={connection} size={size} />
<MainWalletIcon connection={connection} size={size} />
{showMiniIcons && <MiniWalletIcon connection={connection} side="right" />}
{hasSocks && showMiniIcons && <Socks />}
</IconWrapper>

View File

@@ -108,13 +108,155 @@ exports[`StatusIcon with account renders children in correct order 1`] = `
data-testid="StatusIconRoot"
size="16"
>
<div
style="height: 16px; width: 16px; position: relative;"
>
<div
style="height: 16px; width: 16px; overflow: visible; position: absolute;"
>
<svg
viewBox="0 0 16 16"
>
<defs>
<defs>
<mask
id="container-mask0x52270d8234b864dcAC9947f510CE9275A8a116Db16"
>
<rect
fill="white"
height="100%"
width="100%"
x="0"
y="0"
/>
<g
transform="scale(0.4444444444444444)
translate(0, 0)"
>
<path
d="M18.1309 3.25957C9.91898 3.40293 3.14567 10.1762 3.00231 18.3882C2.85896 26.6001 9.39985 33.141 17.6118 32.9977C25.8238 32.8543 32.5971 26.081 32.7404 17.8691L33 3L18.1309 3.25957Z"
fill="black"
/>
</g>
</mask>
<mask
id="shape-mask0x52270d8234b864dcAC9947f510CE9275A8a116Db16"
>
<rect
fill="white"
height="100%"
width="100%"
x="0"
y="0"
/>
<g
transform="scale(0.4444444444444444)
translate(10, 10)"
>
<path
clip-rule="evenodd"
d="M13.6569 13.6568C12.0059 10.0663 12.0059 5.93368 13.6569 2.34314C10.0663 3.99414 5.93368 3.99414 2.34315 2.34314C3.99414 5.93368 3.99414 10.0663 2.34315 13.6568C5.93368 12.0059 10.0663 12.0059 13.6569 13.6568ZM8 11C9.65685 11 11 9.65686 11 8.00001C11 6.34315 9.65685 5.00001 8 5.00001C6.34315 5.00001 5 6.34315 5 8.00001C5 9.65686 6.34315 11 8 11Z"
fill="black"
fill-rule="evenodd"
/>
</g>
</mask>
<mask
id="mask0x52270d8234b864dcAC9947f510CE9275A8a116Db16"
>
<g
fill="white"
>
<g
mask="url(#shape-mask0x52270d8234b864dcAC9947f510CE9275A8a116Db16)"
>
<g
transform="scale(0.4444444444444444)"
>
<path
d="M18.1309 3.25957C9.91898 3.40293 3.14567 10.1762 3.00231 18.3882C2.85896 26.6001 9.39985 33.141 17.6118 32.9977C25.8238 32.8543 32.5971 26.081 32.7404 17.8691L33 3L18.1309 3.25957Z"
/>
</g>
</g>
<g
mask="url(#container-mask0x52270d8234b864dcAC9947f510CE9275A8a116Db16)"
>
<g
transform="scale(0.4444444444444444)
translate(10, 10)"
>
<path
clip-rule="evenodd"
d="M13.6569 13.6568C12.0059 10.0663 12.0059 5.93368 13.6569 2.34314C10.0663 3.99414 5.93368 3.99414 2.34315 2.34314C3.99414 5.93368 3.99414 10.0663 2.34315 13.6568C5.93368 12.0059 10.0663 12.0059 13.6569 13.6568ZM8 11C9.65685 11 11 9.65686 11 8.00001C11 6.34315 9.65685 5.00001 8 5.00001C6.34315 5.00001 5 6.34315 5 8.00001C5 9.65686 6.34315 11 8 11Z"
fill-rule="evenodd"
/>
</g>
</g>
</g>
</mask>
</defs>
<lineargradient
id="gradient0x52270d8234b864dcAC9947f510CE9275A8a116Db16"
>
<stop
offset="0%"
stop-color="#36DBFF"
/>
<stop
offset="100%"
stop-color="#B8C3B7"
/>
</lineargradient>
<filter
height="200%"
id="blur0x52270d8234b864dcAC9947f510CE9275A8a116Db16"
width="200%"
x="-50%"
y="-50%"
>
<fegaussianblur
in="SourceGraphic"
stdDeviation="5.333333333333333"
/>
</filter>
</defs>
<g
mask="url(#mask0x52270d8234b864dcAC9947f510CE9275A8a116Db16)"
>
<rect
fill="url(#gradient0x52270d8234b864dcAC9947f510CE9275A8a116Db16)"
height="100%"
width="100%"
x="0"
y="0"
/>
<rect
fill="black"
height="100%"
opacity="0.08"
width="100%"
x="0"
y="0"
/>
<ellipse
cx="8"
cy="0"
fill="#9D99F5"
filter="url(#blur0x52270d8234b864dcAC9947f510CE9275A8a116Db16)"
rx="8"
ry="8"
/>
</g>
</svg>
</div>
</div>
<div
class="c1"
>
<img
alt="WalletConnect icon"
alt="Install MetaMask icon"
class="c2"
src="walletconnect-icon.svg"
src="metamask-icon.svg"
/>
</div>
<div
@@ -240,9 +382,9 @@ exports[`StatusIcon with no account renders children in correct order 1`] = `
class="c1"
>
<img
alt="WalletConnect icon"
alt="Install MetaMask icon"
class="c2"
src="walletconnect-icon.svg"
src="metamask-icon.svg"
/>
</div>
<div

View File

@@ -1,4 +1,5 @@
import jazzicon from '@metamask/jazzicon'
import { useWeb3React } from '@web3-react/core'
import useENSAvatar from 'hooks/useENSAvatar'
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react'
import styled from 'styled-components/macro'
@@ -17,7 +18,8 @@ const StyledAvatar = styled.img`
border-radius: inherit;
`
export default function Identicon({ account, size }: { account: string; size?: number }) {
export default function Identicon({ size }: { size?: number }) {
const { account } = useWeb3React()
const { avatar } = useENSAvatar(account ?? undefined)
const [fetchable, setFetchable] = useState(true)
const iconSize = size ?? 24

View File

@@ -1,5 +1,5 @@
import { ChainId } from '@uniswap/sdk-core'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import useTokenLogoSource from 'hooks/useAssetLogoSource'
import React from 'react'
import styled, { css } from 'styled-components/macro'
@@ -63,7 +63,7 @@ const L2NetworkLogo = styled.div<{ networkUrl?: string; parentSize: string }>`
export default function AssetLogo({
isNative,
address,
chainId = ChainId.MAINNET,
chainId = SupportedChainId.MAINNET,
symbol,
backupImg,
size = '24px',

View File

@@ -3,7 +3,7 @@ import { TokenStandard } from 'graphql/data/__generated__/types-and-hooks'
import { SearchToken } from 'graphql/data/SearchTokens'
import { TokenQueryData } from 'graphql/data/Token'
import { TopToken } from 'graphql/data/TopTokens'
import { supportedChainIdFromGQLChain } from 'graphql/data/util'
import { CHAIN_NAME_TO_CHAIN_ID } from 'graphql/data/util'
import AssetLogo, { AssetLogoBaseProps } from './AssetLogo'
@@ -12,7 +12,7 @@ export default function QueryTokenLogo(
token?: TopToken | TokenQueryData | SearchToken
}
) {
const chainId = props.token?.chain ? supportedChainIdFromGQLChain(props.token?.chain) : undefined
const chainId = props.token?.chain ? CHAIN_NAME_TO_CHAIN_ID[props.token?.chain] : undefined
return (
<AssetLogo

View File

@@ -1,14 +1,18 @@
import { t } from '@lingui/macro'
import { ChainId } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { WalletConnect } from '@web3-react/walletconnect-v2'
import { showTestnetsAtom } from 'components/AccountDrawer/TestnetsToggle'
import { MouseoverTooltip } from 'components/Tooltip'
import { getConnection } from 'connection'
import { ConnectionType } from 'connection/types'
import { WalletConnectV2 } from 'connection/WalletConnectV2'
import { getChainInfo } from 'constants/chainInfo'
import { L1_CHAIN_IDS, L2_CHAIN_IDS, TESTNET_CHAIN_IDS, UniWalletSupportedChains } from 'constants/chains'
import { useIsAvalancheEnabled } from 'featureFlags/flags/avalanche'
import {
L1_CHAIN_IDS,
L2_CHAIN_IDS,
SupportedChainId,
TESTNET_CHAIN_IDS,
UniWalletSupportedChains,
} from 'constants/chains'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import useSelectChain from 'hooks/useSelectChain'
import useSyncChainQuery from 'hooks/useSyncChainQuery'
@@ -17,10 +21,9 @@ import { Box } from 'nft/components/Box'
import { Portal } from 'nft/components/common/Portal'
import { Column, Row } from 'nft/components/Flex'
import { useIsMobile } from 'nft/hooks'
import { useCallback, useMemo, useRef, useState } from 'react'
import { useCallback, useRef, useState } from 'react'
import { AlertTriangle, ChevronDown, ChevronUp } from 'react-feather'
import { useTheme } from 'styled-components/macro'
import { getSupportedChainIdsFromWalletConnectSession } from 'utils/getSupportedChainIdsFromWalletConnectSession'
import * as styles from './ChainSelector.css'
import ChainSelectorRow from './ChainSelectorRow'
@@ -32,15 +35,26 @@ interface ChainSelectorProps {
leftAlign?: boolean
}
function useWalletSupportedChains(): ChainId[] {
// accounts is an array of strings in the format of "eip155:<chainId>:<address>"
function getChainsFromEIP155Accounts(accounts?: string[]): SupportedChainId[] {
if (!accounts) return []
return accounts
.map((account) => {
const splitAccount = account.split(':')
return splitAccount[1] ? parseInt(splitAccount[1]) : undefined
})
.filter((x) => x !== undefined) as SupportedChainId[]
}
function useWalletSupportedChains() {
const { connector } = useWeb3React()
const connectionType = getConnection(connector).type
switch (connectionType) {
case ConnectionType.WALLET_CONNECT_V2:
return getSupportedChainIdsFromWalletConnectSession((connector as WalletConnectV2).provider?.session)
case ConnectionType.UNISWAP_WALLET_V2:
return getChainsFromEIP155Accounts((connector as WalletConnect).provider?.session?.namespaces.eip155.accounts)
case ConnectionType.UNISWAP_WALLET:
return UniWalletSupportedChains
default:
return NETWORK_SELECTOR_CHAINS
@@ -51,30 +65,13 @@ export const ChainSelector = ({ leftAlign }: ChainSelectorProps) => {
const { chainId } = useWeb3React()
const [isOpen, setIsOpen] = useState<boolean>(false)
const isMobile = useIsMobile()
const isAvalancheEnabled = useIsAvalancheEnabled()
const theme = useTheme()
const showTestnets = useAtomValue(showTestnetsAtom)
const walletSupportsChain = useWalletSupportedChains()
const [supportedChains, unsupportedChains] = useMemo(() => {
const { supported, unsupported } = NETWORK_SELECTOR_CHAINS.filter(
(chain: number) =>
(showTestnets || !TESTNET_CHAIN_IDS.includes(chain)) && (isAvalancheEnabled || chain !== ChainId.AVALANCHE)
).reduce(
(acc, chain) => {
if (walletSupportsChain.includes(chain)) {
acc.supported.push(chain)
} else {
acc.unsupported.push(chain)
}
return acc
},
{ supported: [], unsupported: [] } as Record<string, ChainId[]>
)
return [supported, unsupported]
}, [isAvalancheEnabled, showTestnets, walletSupportsChain])
const chains = showTestnets
? NETWORK_SELECTOR_CHAINS
: NETWORK_SELECTOR_CHAINS.filter((chain) => !TESTNET_CHAIN_IDS.has(chain))
const ref = useRef<HTMLDivElement>(null)
const modalRef = useRef<HTMLDivElement>(null)
@@ -85,10 +82,10 @@ export const ChainSelector = ({ leftAlign }: ChainSelectorProps) => {
const selectChain = useSelectChain()
useSyncChainQuery()
const [pendingChainId, setPendingChainId] = useState<ChainId | undefined>(undefined)
const [pendingChainId, setPendingChainId] = useState<SupportedChainId | undefined>(undefined)
const onSelectChain = useCallback(
async (targetChainId: ChainId) => {
async (targetChainId: SupportedChainId) => {
setPendingChainId(targetChainId)
await selectChain(targetChainId)
setPendingChainId(undefined)
@@ -97,16 +94,18 @@ export const ChainSelector = ({ leftAlign }: ChainSelectorProps) => {
[selectChain, setIsOpen]
)
const walletSupportsChain = useWalletSupportedChains()
if (!chainId) {
return null
}
const isSupported = !!info && (isAvalancheEnabled || chainId !== ChainId.AVALANCHE)
const isSupported = !!info
const dropdown = (
<NavDropdown top="56" left={leftAlign ? '0' : 'auto'} right={leftAlign ? 'auto' : '0'} ref={modalRef}>
<Column paddingX="8" data-testid="chain-selector-options">
{supportedChains.map((selectorChain) => (
{chains.map((selectorChain: SupportedChainId) => (
<ChainSelectorRow
disabled={!walletSupportsChain.includes(selectorChain)}
onSelectChain={onSelectChain}
@@ -115,15 +114,6 @@ export const ChainSelector = ({ leftAlign }: ChainSelectorProps) => {
isPending={selectorChain === pendingChainId}
/>
))}
{unsupportedChains.map((selectorChain) => (
<ChainSelectorRow
disabled
onSelectChain={() => undefined}
targetChain={selectorChain}
key={selectorChain}
isPending={false}
/>
))}
</Column>
</NavDropdown>
)

View File

@@ -1,8 +1,8 @@
import { Trans } from '@lingui/macro'
import { ChainId } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import Loader from 'components/Icons/LoadingSpinner'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { CheckMarkIcon } from 'nft/components/icons'
import styled, { useTheme } from 'styled-components/macro'
@@ -18,7 +18,7 @@ const Container = styled.button<{ disabled: boolean }>`
display: grid;
grid-template-columns: min-content 1fr min-content;
justify-content: space-between;
line-height: 20px;
line-height: 24px;
opacity: ${({ disabled }) => (disabled ? 0.6 : 1)};
padding: 10px 8px;
text-align: left;
@@ -63,7 +63,7 @@ const Logo = styled.img`
`
interface ChainSelectorRowProps {
disabled?: boolean
targetChain: ChainId
targetChain: SupportedChainId
onSelectChain: (targetChain: number) => void
isPending: boolean
}
@@ -80,6 +80,7 @@ export default function ChainSelectorRow({ disabled, targetChain, onSelectChain,
onClick={() => {
if (!disabled) onSelectChain(targetChain)
}}
data-testid={`chain-selector-option-${label.toLowerCase()}`}
>
<Logo src={logoUrl} alt={label} />
<Label>{label}</Label>

View File

@@ -1,7 +1,8 @@
import { SupportedChainId } from 'constants/chains'
import { NATIVE_CHAIN_ID, nativeOnChain } from 'constants/tokens'
import { Chain, NftCollection, useRecentlySearchedAssetsQuery } from 'graphql/data/__generated__/types-and-hooks'
import { SearchToken } from 'graphql/data/SearchTokens'
import { logSentryErrorForUnsupportedChain, supportedChainIdFromGQLChain } from 'graphql/data/util'
import { CHAIN_NAME_TO_CHAIN_ID } from 'graphql/data/util'
import { useAtom } from 'jotai'
import { atomWithStorage, useAtomValue } from 'jotai/utils'
import { GenieCollection } from 'nft/types'
@@ -85,15 +86,7 @@ export function useRecentlySearchedAssets() {
shortenedHistory.forEach((asset) => {
if (asset.address === 'NATIVE') {
// Handles special case where wMATIC data needs to be used for MATIC
const chain = supportedChainIdFromGQLChain(asset.chain)
if (!chain) {
logSentryErrorForUnsupportedChain({
extras: { asset },
errorMessage: 'Invalid chain retrieved from Seach Token/Collection Query',
})
return
}
const native = nativeOnChain(chain)
const native = nativeOnChain(CHAIN_NAME_TO_CHAIN_ID[asset.chain] ?? SupportedChainId.MAINNET)
const queryAddress = getQueryAddress(asset.chain)?.toLowerCase() ?? `NATIVE-${asset.chain}`
const result = resultsMap[queryAddress]
if (result) data.push({ ...result, address: 'NATIVE', ...native })

View File

@@ -1,32 +0,0 @@
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
import { useIsMobile, useIsTablet } from 'nft/hooks'
import { useIsNavSearchInputVisible } from 'nft/hooks/useIsNavSearchInputVisible'
import { mocked } from 'test-utils/mocked'
import { render, screen } from 'test-utils/render'
import { SearchBar } from './SearchBar'
jest.mock('hooks/useDisableNFTRoutes')
jest.mock('nft/hooks')
jest.mock('nft/hooks/useIsNavSearchInputVisible')
describe('disable nft on searchbar', () => {
beforeEach(() => {
mocked(useIsMobile).mockReturnValue(false)
mocked(useIsTablet).mockReturnValue(false)
mocked(useIsNavSearchInputVisible).mockReturnValue(true)
})
it('should render text with nfts', () => {
mocked(useDisableNFTRoutes).mockReturnValue(false)
const { container } = render(<SearchBar />)
expect(container).toMatchSnapshot()
expect(screen.queryByPlaceholderText('Search tokens and NFT collections')).toBeVisible()
})
it('should render text without nfts', () => {
mocked(useDisableNFTRoutes).mockReturnValue(true)
const { container } = render(<SearchBar />)
expect(container).toMatchSnapshot()
expect(screen.queryByPlaceholderText('Search tokens')).toBeVisible()
})
})

View File

@@ -1,5 +1,5 @@
// eslint-disable-next-line no-restricted-imports
import { t } from '@lingui/macro'
import { Trans } from '@lingui/macro'
import { sendAnalyticsEvent, Trace, TraceEvent, useTrace } from '@uniswap/analytics'
import { BrowserEvent, InterfaceElementName, InterfaceEventName, InterfaceSectionName } from '@uniswap/analytics-events'
import { useWeb3React } from '@web3-react/core'
@@ -7,7 +7,6 @@ import clsx from 'clsx'
import { useCollectionSearch } from 'graphql/data/nft/CollectionSearch'
import { useSearchTokens } from 'graphql/data/SearchTokens'
import useDebounce from 'hooks/useDebounce'
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
import { useIsNftPage } from 'hooks/useIsNftPage'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import { organizeSearchResults } from 'lib/utils/searchBar'
@@ -51,7 +50,6 @@ export const SearchBar = () => {
const isMobile = useIsMobile()
const isTablet = useIsTablet()
const isNavSearchInputVisible = useIsNavSearchInputVisible()
const shouldDisableNFTRoutes = useDisableNFTRoutes()
useOnClickOutside(searchRef, () => {
isOpen && toggleOpen()
@@ -104,16 +102,8 @@ export const SearchBar = () => {
...trace,
}
const placeholderText = useMemo(() => {
if (isMobileOrTablet) {
return t`Search`
} else {
if (shouldDisableNFTRoutes) {
return t`Search tokens`
} else {
return t`Search tokens and NFT collections`
}
}
}, [isMobileOrTablet, shouldDisableNFTRoutes])
return isMobileOrTablet ? `Search` : `Search tokens and NFT collections`
}, [isMobileOrTablet])
const handleKeyPress = useCallback(
(event: any) => {
@@ -184,19 +174,26 @@ export const SearchBar = () => {
element={InterfaceElementName.NAVBAR_SEARCH_INPUT}
properties={{ ...trace }}
>
<Box
as="input"
data-cy="search-bar-input"
placeholder={placeholderText}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
!isOpen && toggleOpen()
setSearchValue(event.target.value)
}}
onBlur={() => sendAnalyticsEvent(InterfaceEventName.NAVBAR_SEARCH_EXITED, navbarSearchEventProperties)}
className={`${styles.searchBarInput} ${styles.searchContentLeftAlign}`}
value={searchValue}
ref={inputRef}
width="full"
<Trans
id={placeholderText}
render={({ translation }) => (
<Box
as="input"
data-cy="search-bar-input"
placeholder={translation as string}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
!isOpen && toggleOpen()
setSearchValue(event.target.value)
}}
onBlur={() =>
sendAnalyticsEvent(InterfaceEventName.NAVBAR_SEARCH_EXITED, navbarSearchEventProperties)
}
className={`${styles.searchBarInput} ${styles.searchContentLeftAlign}`}
value={searchValue}
ref={inputRef}
width="full"
/>
)}
/>
</TraceEvent>
{!isOpen && <KeyShortCut>/</KeyShortCut>}

View File

@@ -1,32 +0,0 @@
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
import { mocked } from 'test-utils/mocked'
import { render } from 'test-utils/render'
import { SearchBarDropdown } from './SearchBarDropdown'
jest.mock('hooks/useDisableNFTRoutes')
const SearchBarDropdownProps = {
toggleOpen: () => void 0,
tokens: [],
collections: [],
queryText: '',
hasInput: false,
isLoading: false,
}
describe('disable nft on searchbar dropdown', () => {
it('should render popular nft collections', () => {
mocked(useDisableNFTRoutes).mockReturnValue(false)
const { container } = render(<SearchBarDropdown {...SearchBarDropdownProps} />)
expect(container).toMatchSnapshot()
expect(container).toHaveTextContent('Popular NFT collections')
})
it('should not render popular nft collections', () => {
mocked(useDisableNFTRoutes).mockReturnValue(true)
const { container } = render(<SearchBarDropdown {...SearchBarDropdownProps} />)
expect(container).toMatchSnapshot()
expect(container).not.toHaveTextContent('Popular NFT collections')
expect(container).not.toHaveTextContent('NFT')
})
})

View File

@@ -1,18 +1,14 @@
import { Trans } from '@lingui/macro'
import { useTrace } from '@uniswap/analytics'
import { InterfaceSectionName, NavBarSearchTypes } from '@uniswap/analytics-events'
import { ChainId } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import clsx from 'clsx'
import Badge from 'components/Badge'
import { getChainInfo } from 'constants/chainInfo'
import { useFilterChainsForAvalanche } from 'featureFlags/flags/avalanche'
import { SupportedChainId } from 'constants/chains'
import { HistoryDuration, SafetyLevel } from 'graphql/data/__generated__/types-and-hooks'
import { useTrendingCollections } from 'graphql/data/nft/TrendingCollections'
import { SearchToken } from 'graphql/data/SearchTokens'
import useTrendingTokens from 'graphql/data/TrendingTokens'
import { BACKEND_NOT_YET_SUPPORTED_CHAIN_IDS } from 'graphql/data/util'
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
import { useIsNftPage } from 'hooks/useIsNftPage'
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
@@ -23,6 +19,7 @@ import { useLocation } from 'react-router-dom'
import styled from 'styled-components/macro'
import { ThemedText } from 'theme'
import BnbLogoURI from '../../assets/svg/bnb-logo.svg'
import { ClockIcon, TrendingArrow } from '../../nft/components/icons'
import { useRecentlySearchedAssets } from './RecentlySearchedAssets'
import * as styles from './SearchBar.css'
@@ -106,12 +103,12 @@ function isKnownToken(token: SearchToken) {
return token.project?.safetyLevel == SafetyLevel.Verified || token.project?.safetyLevel == SafetyLevel.MediumWarning
}
const ChainLogo = styled.img`
const BNBLogo = styled.img`
height: 20px;
width: 20px;
margin-right: 8px;
`
const ChainComingSoonBadge = styled(Badge)`
const BNBComingSoonBadge = styled(Badge)`
align-items: center;
background-color: ${({ theme }) => theme.backgroundModule};
color: ${({ theme }) => theme.textSecondary};
@@ -151,7 +148,6 @@ export const SearchBarDropdown = ({
const isNFTPage = useIsNftPage()
const isTokenPage = pathname.includes('/tokens')
const [resultsState, setResultsState] = useState<ReactNode>()
const shouldDisableNFTRoutes = useDisableNFTRoutes()
const { data: trendingCollections, loading: trendingCollectionsAreLoading } = useTrendingCollections(
3,
@@ -315,7 +311,7 @@ export const SearchBarDropdown = ({
isLoading={!trendingTokenData}
/>
)}
{Boolean(!isTokenPage && !shouldDisableNFTRoutes) && (
{!isTokenPage && (
<SearchBarDropdownSection
hoveredIndex={hoveredIndex}
startingIndex={shortenedHistory.length + (isNFTPage ? 0 : trendingTokens?.length ?? 0)}
@@ -355,36 +351,23 @@ export const SearchBarDropdown = ({
trace,
searchHistory,
trendingCollectionsAreLoading,
shouldDisableNFTRoutes,
])
const gatedUnsupportedChains = useFilterChainsForAvalanche([...BACKEND_NOT_YET_SUPPORTED_CHAIN_IDS])
const showChainComingSoonBadge = chainId && gatedUnsupportedChains.includes(chainId) && !isLoading
const logoUri = getChainInfo(chainId)?.logoUrl
const showBNBComingSoonBadge = chainId === SupportedChainId.BNB && !isLoading
return (
<Column overflow="hidden" className={clsx(styles.searchBarDropdownNft, styles.searchBarScrollable)}>
<Box opacity={isLoading ? '0.3' : '1'} transition="125">
{resultsState}
{showChainComingSoonBadge && (
<ChainComingSoonBadge>
<ChainLogo src={logoUri} />
{showBNBComingSoonBadge && (
<BNBComingSoonBadge>
<BNBLogo src={BnbLogoURI} />
<ThemedText.BodySmall color="textSecondary" fontSize="14px" fontWeight="400" lineHeight="20px">
<ComingSoonText chainId={chainId} />
<Trans>Coming soon: search and explore tokens on BNB Chain</Trans>
</ThemedText.BodySmall>
</ChainComingSoonBadge>
</BNBComingSoonBadge>
)}
</Box>
</Column>
)
}
function ComingSoonText({ chainId }: { chainId: ChainId }) {
switch (chainId) {
case ChainId.BNB:
return <Trans>Coming soon: search and explore tokens on BNB Chain</Trans>
case ChainId.AVALANCHE:
return <Trans>Coming soon: search and explore tokens on Avalanche Chain</Trans>
default:
return null
}
}

View File

@@ -1,187 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`disable nft on searchbar should render text with nfts 1`] = `
.c0 {
background-color: #ADBCFF3d;
color: #7780A0;
padding: 0px 8px;
width: 20px;
height: 20px;
border-radius: 4px;
font-size: 12px;
font-weight: 800;
line-height: 16px;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
opacity: 0.6;
-webkit-backdrop-filter: blur(60px);
backdrop-filter: blur(60px);
}
<div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_position_relative_sm__rgw6ez491 sprinkles_width_auto_sm__rgw6ez17v sprinkles_width_auto_md__rgw6ez17w SearchBar_searchBarContainerNft__1fbf9sz4 SearchBar__1fbf9sz3 sprinkles_right_0_sm__rgw6ez39p sprinkles_top_0_sm__rgw6ez3f7 sprinkles_zIndex_3_sm__rgw6ez3qj sprinkles_display_flex_sm__rgw6ez44v sprinkles_maxHeight_searchResultsMaxHeight_sm__rgw6ez1zd sprinkles_overflow_hidden_default__rgw6ez7m3 searchBarContainerDisableBlur"
data-cy="search-bar"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_borderRadius_12_default__rgw6ez7bj sprinkles_borderBottomWidth_1px_default__rgw6ez7kj sprinkles_backgroundColor_searchBackground_default__rgw6ez6d1 sprinkles_gap_12_sm__rgw6ez3tj SearchBar_nftSearchBar__1fbf9sz9 SearchBar_baseSearchNftStyle__1fbf9sz2 SearchBar_baseSearchStyle__1fbf9sz1 SearchBar__1fbf9sz0 sprinkles_paddingTop_12_sm__rgw6ez2ov sprinkles_paddingBottom_12_sm__rgw6ez28d sprinkles_width_viewWidth_sm__rgw6ez17p sprinkles_borderStyle_solid_default__rgw6ez7ab sprinkles_borderWidth_1px_default__rgw6ez7jr sprinkles_borderColor_searchOutline_default__rgw6ez51v SearchBar__1fbf9sz8 sprinkles_paddingLeft_16_sm__rgw6ez2e7 sprinkles_paddingRight_16_sm__rgw6ez2jp sprinkles_color_textSecondary_default__rgw6ez4ep common_magicalGradientOnHover__127l8hdb common_magicalGradient__127l8hda"
>
<div
class="reset_base__1klryar0 SearchBar_searchContentLeftAlign__1fbf9sz10"
>
<div
class="reset_base__1klryar0 sprinkles_display_none_sm__rgw6ez44j sprinkles_display_flex_md__rgw6ez44w"
>
<svg
fill="none"
height="16"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15 15L11.2439 11.2439M12.3821 6.69106C12.3821 9.83414 9.83414 12.3821 6.69106 12.3821C3.54797 12.3821 1 9.83414 1 6.69106C1 3.54797 3.54797 1 6.69106 1C9.83414 1 12.3821 3.54797 12.3821 6.69106Z"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
/>
</svg>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_display_none_md__rgw6ez44k sprinkles_color_textTertiary_default__rgw6ez4ev"
>
<svg
fill="none"
height="16"
viewBox="0 0 8 16"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7 1L1 7L7 13"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
/>
</svg>
</div>
</div>
<input
class="reset_base__1klryar0 reset_input__1klryar8 reset_field__1klryar5 reset_appearance__1klryar4 sprinkles_width_full_sm__rgw6ez16v SearchBar_searchBarInput__1fbf9szb SearchBar__1fbf9sza sprinkles_padding_0_sm__rgw6ez2t7 sprinkles_fontWeight_normal_sm__rgw6ezcp sprinkles_fontSize_16_sm__rgw6ezb1 sprinkles_color_textPrimary_default__rgw6ez4ej sprinkles_color_textSecondary_placeholder__rgw6ez4eu sprinkles_border_none_default__rgw6ez7iz sprinkles_background_none_default__rgw6ez4sj sprinkles_lineHeight_24_sm__rgw6ezed sprinkles_height_full_sm__rgw6ez1dv SearchBar_searchContentLeftAlign__1fbf9sz10"
data-cy="search-bar-input"
placeholder="Search tokens and NFT collections"
value=""
/>
<div
class="c0"
>
/
</div>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_overflow_hidden_default__rgw6ez7m3 SearchBar_hidden__1fbf9szx SearchBar__1fbf9szw sprinkles_visibility_hidden_sm__rgw6ez46v sprinkles_opacity_0_sm__rgw6ez4ad sprinkles_padding_0_sm__rgw6ez2t7 sprinkles_height_0_sm__rgw6ez187"
/>
</div>
</div>
`;
exports[`disable nft on searchbar should render text without nfts 1`] = `
.c0 {
background-color: #ADBCFF3d;
color: #7780A0;
padding: 0px 8px;
width: 20px;
height: 20px;
border-radius: 4px;
font-size: 12px;
font-weight: 800;
line-height: 16px;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
opacity: 0.6;
-webkit-backdrop-filter: blur(60px);
backdrop-filter: blur(60px);
}
<div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_position_relative_sm__rgw6ez491 sprinkles_width_auto_sm__rgw6ez17v sprinkles_width_auto_md__rgw6ez17w SearchBar_searchBarContainerNft__1fbf9sz4 SearchBar__1fbf9sz3 sprinkles_right_0_sm__rgw6ez39p sprinkles_top_0_sm__rgw6ez3f7 sprinkles_zIndex_3_sm__rgw6ez3qj sprinkles_display_flex_sm__rgw6ez44v sprinkles_maxHeight_searchResultsMaxHeight_sm__rgw6ez1zd sprinkles_overflow_hidden_default__rgw6ez7m3 searchBarContainerDisableBlur"
data-cy="search-bar"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_borderRadius_12_default__rgw6ez7bj sprinkles_borderBottomWidth_1px_default__rgw6ez7kj sprinkles_backgroundColor_searchBackground_default__rgw6ez6d1 sprinkles_gap_12_sm__rgw6ez3tj SearchBar_nftSearchBar__1fbf9sz9 SearchBar_baseSearchNftStyle__1fbf9sz2 SearchBar_baseSearchStyle__1fbf9sz1 SearchBar__1fbf9sz0 sprinkles_paddingTop_12_sm__rgw6ez2ov sprinkles_paddingBottom_12_sm__rgw6ez28d sprinkles_width_viewWidth_sm__rgw6ez17p sprinkles_borderStyle_solid_default__rgw6ez7ab sprinkles_borderWidth_1px_default__rgw6ez7jr sprinkles_borderColor_searchOutline_default__rgw6ez51v SearchBar__1fbf9sz8 sprinkles_paddingLeft_16_sm__rgw6ez2e7 sprinkles_paddingRight_16_sm__rgw6ez2jp sprinkles_color_textSecondary_default__rgw6ez4ep common_magicalGradientOnHover__127l8hdb common_magicalGradient__127l8hda"
>
<div
class="reset_base__1klryar0 SearchBar_searchContentLeftAlign__1fbf9sz10"
>
<div
class="reset_base__1klryar0 sprinkles_display_none_sm__rgw6ez44j sprinkles_display_flex_md__rgw6ez44w"
>
<svg
fill="none"
height="16"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15 15L11.2439 11.2439M12.3821 6.69106C12.3821 9.83414 9.83414 12.3821 6.69106 12.3821C3.54797 12.3821 1 9.83414 1 6.69106C1 3.54797 3.54797 1 6.69106 1C9.83414 1 12.3821 3.54797 12.3821 6.69106Z"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
/>
</svg>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_display_none_md__rgw6ez44k sprinkles_color_textTertiary_default__rgw6ez4ev"
>
<svg
fill="none"
height="16"
viewBox="0 0 8 16"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7 1L1 7L7 13"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
/>
</svg>
</div>
</div>
<input
class="reset_base__1klryar0 reset_input__1klryar8 reset_field__1klryar5 reset_appearance__1klryar4 sprinkles_width_full_sm__rgw6ez16v SearchBar_searchBarInput__1fbf9szb SearchBar__1fbf9sza sprinkles_padding_0_sm__rgw6ez2t7 sprinkles_fontWeight_normal_sm__rgw6ezcp sprinkles_fontSize_16_sm__rgw6ezb1 sprinkles_color_textPrimary_default__rgw6ez4ej sprinkles_color_textSecondary_placeholder__rgw6ez4eu sprinkles_border_none_default__rgw6ez7iz sprinkles_background_none_default__rgw6ez4sj sprinkles_lineHeight_24_sm__rgw6ezed sprinkles_height_full_sm__rgw6ez1dv SearchBar_searchContentLeftAlign__1fbf9sz10"
data-cy="search-bar-input"
placeholder="Search tokens"
value=""
/>
<div
class="c0"
>
/
</div>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_overflow_hidden_default__rgw6ez7m3 SearchBar_hidden__1fbf9szx SearchBar__1fbf9szw sprinkles_visibility_hidden_sm__rgw6ez46v sprinkles_opacity_0_sm__rgw6ez4ad sprinkles_padding_0_sm__rgw6ez2t7 sprinkles_height_0_sm__rgw6ez187"
/>
</div>
</div>
`;

View File

@@ -1,344 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`disable nft on searchbar dropdown should not render popular nft collections 1`] = `
<div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_overflow_hidden_default__rgw6ez7m3 SearchBar_searchBarDropdownNft__1fbf9szd SearchBar_baseSearchNftStyle__1fbf9sz2 SearchBar_baseSearchStyle__1fbf9sz1 SearchBar__1fbf9sz0 sprinkles_paddingTop_12_sm__rgw6ez2ov sprinkles_paddingBottom_12_sm__rgw6ez28d sprinkles_width_viewWidth_sm__rgw6ez17p sprinkles_borderStyle_solid_default__rgw6ez7ab sprinkles_borderWidth_1px_default__rgw6ez7jr sprinkles_borderColor_searchOutline_default__rgw6ez51v SearchBar__1fbf9szc sprinkles_borderBottomLeftRadius_12_default__rgw6ez7g7 sprinkles_borderBottomRightRadius_12_default__rgw6ez7hr sprinkles_height_viewHeight_sm__rgw6ez1ej sprinkles_height_auto_md__rgw6ez1ew sprinkles_backgroundColor_backgroundSurface_default__rgw6ez6cj SearchBar__1fbf9sze sprinkles_overflowY_auto_default__rgw6ez7nn"
>
<div
class="reset_base__1klryar0 sprinkles_opacity_1_sm__rgw6ez4b7 sprinkles_transition_125_default__rgw6ez7oj"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_gap_20_sm__rgw6ez3u7"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_gap_12_sm__rgw6ez3tj"
data-cy="searchbar-dropdown"
>
<div
class="reset_base__1klryar0 sprinkles_paddingLeft_16_sm__rgw6ez2e7 sprinkles_paddingRight_16_sm__rgw6ez2jp sprinkles_paddingTop_4_sm__rgw6ez2o7 sprinkles_paddingBottom_4_sm__rgw6ez27p sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_gap_8_sm__rgw6ez3t7 sprinkles_color_gray300_default__rgw6ez4k1 common__127l8hd4 sprinkles_fontWeight_medium_sm__rgw6ezcv sprinkles_fontSize_14_sm__rgw6ezav sprinkles_lineHeight_14_sm__rgw6ezdv"
style="line-height: 20px;"
>
<svg
fill="none"
height="20"
viewBox="0 0 20 20"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M17.5 5.8335H18.25C18.25 5.41928 17.9142 5.0835 17.5 5.0835V5.8335ZM11.0227 12.4307L10.4876 12.9562C10.6286 13.0998 10.8214 13.1807 11.0227 13.1807C11.224 13.1807 11.4169 13.0998 11.5579 12.9562L11.0227 12.4307ZM7.61364 8.9585L8.14881 8.43305C8.00778 8.28941 7.81493 8.2085 7.61364 8.2085C7.41234 8.2085 7.21949 8.28941 7.07846 8.43305L7.61364 8.9585ZM1.96483 13.6414C1.67463 13.937 1.67899 14.4118 1.97456 14.702C2.27013 14.9922 2.74498 14.9878 3.03517 14.6923L1.96483 13.6414ZM13.4091 5.0835C12.9949 5.0835 12.6591 5.41928 12.6591 5.8335C12.6591 6.24771 12.9949 6.5835 13.4091 6.5835V5.0835ZM16.75 10.0002C16.75 10.4144 17.0858 10.7502 17.5 10.7502C17.9142 10.7502 18.25 10.4144 18.25 10.0002H16.75ZM16.9648 5.30805L10.4876 11.9053L11.5579 12.9562L18.0352 6.35894L16.9648 5.30805ZM11.5579 11.9053L8.14881 8.43305L7.07846 9.48394L10.4876 12.9562L11.5579 11.9053ZM7.07846 8.43305L1.96483 13.6414L3.03517 14.6923L8.14881 9.48394L7.07846 8.43305ZM13.4091 6.5835H17.5V5.0835H13.4091V6.5835ZM16.75 5.8335V10.0002H18.25V5.8335H16.75Z"
fill="var(--color-gray300__rgw6ez1g)"
/>
</svg>
<div
class="reset_base__1klryar0"
>
Popular tokens
</div>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_gap_12_sm__rgw6ez3tj"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j SearchBar_suggestionRow__1fbf9szg SearchBar__1fbf9szf sprinkles_paddingLeft_16_sm__rgw6ez2e7 sprinkles_paddingRight_16_sm__rgw6ez2jp sprinkles_paddingTop_8_sm__rgw6ez2oj sprinkles_paddingBottom_8_sm__rgw6ez281 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j sprinkles_cursor_pointer_default__rgw6ez79z"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_width_full_sm__rgw6ez16v"
>
<div
class="reset_base__1klryar0 SearchBar_imageHolder__1fbf9szq SearchBar__1fbf9szh sprinkles_width_36_sm__rgw6ez137 sprinkles_height_36_sm__rgw6ez1a7 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_marginRight_8_sm__rgw6ezr1 SearchBar__1fbf9szp sprinkles_background_backgroundModule_default__rgw6ez4p1 sprinkles_flexShrink_0_sm__rgw6ez3xv"
/>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_gap_4_sm__rgw6ez3sv sprinkles_width_full_sm__rgw6ez16v"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j"
>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_20_sm__rgw6ez19d sprinkles_background_backgroundModule_default__rgw6ez4p1"
style="width: 180px;"
/>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_20_sm__rgw6ez19d sprinkles_width_48_sm__rgw6ez13v sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j"
>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_16_sm__rgw6ez191 sprinkles_width_120_sm__rgw6ez15j sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_16_sm__rgw6ez191 sprinkles_width_48_sm__rgw6ez13v sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
</div>
</div>
</div>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j SearchBar_suggestionRow__1fbf9szg SearchBar__1fbf9szf sprinkles_paddingLeft_16_sm__rgw6ez2e7 sprinkles_paddingRight_16_sm__rgw6ez2jp sprinkles_paddingTop_8_sm__rgw6ez2oj sprinkles_paddingBottom_8_sm__rgw6ez281 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j sprinkles_cursor_pointer_default__rgw6ez79z"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_width_full_sm__rgw6ez16v"
>
<div
class="reset_base__1klryar0 SearchBar_imageHolder__1fbf9szq SearchBar__1fbf9szh sprinkles_width_36_sm__rgw6ez137 sprinkles_height_36_sm__rgw6ez1a7 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_marginRight_8_sm__rgw6ezr1 SearchBar__1fbf9szp sprinkles_background_backgroundModule_default__rgw6ez4p1 sprinkles_flexShrink_0_sm__rgw6ez3xv"
/>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_gap_4_sm__rgw6ez3sv sprinkles_width_full_sm__rgw6ez16v"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j"
>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_20_sm__rgw6ez19d sprinkles_background_backgroundModule_default__rgw6ez4p1"
style="width: 180px;"
/>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_20_sm__rgw6ez19d sprinkles_width_48_sm__rgw6ez13v sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j"
>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_16_sm__rgw6ez191 sprinkles_width_120_sm__rgw6ez15j sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_16_sm__rgw6ez191 sprinkles_width_48_sm__rgw6ez13v sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`disable nft on searchbar dropdown should render popular nft collections 1`] = `
<div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_overflow_hidden_default__rgw6ez7m3 SearchBar_searchBarDropdownNft__1fbf9szd SearchBar_baseSearchNftStyle__1fbf9sz2 SearchBar_baseSearchStyle__1fbf9sz1 SearchBar__1fbf9sz0 sprinkles_paddingTop_12_sm__rgw6ez2ov sprinkles_paddingBottom_12_sm__rgw6ez28d sprinkles_width_viewWidth_sm__rgw6ez17p sprinkles_borderStyle_solid_default__rgw6ez7ab sprinkles_borderWidth_1px_default__rgw6ez7jr sprinkles_borderColor_searchOutline_default__rgw6ez51v SearchBar__1fbf9szc sprinkles_borderBottomLeftRadius_12_default__rgw6ez7g7 sprinkles_borderBottomRightRadius_12_default__rgw6ez7hr sprinkles_height_viewHeight_sm__rgw6ez1ej sprinkles_height_auto_md__rgw6ez1ew sprinkles_backgroundColor_backgroundSurface_default__rgw6ez6cj SearchBar__1fbf9sze sprinkles_overflowY_auto_default__rgw6ez7nn"
>
<div
class="reset_base__1klryar0 sprinkles_opacity_1_sm__rgw6ez4b7 sprinkles_transition_125_default__rgw6ez7oj"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_gap_20_sm__rgw6ez3u7"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_gap_12_sm__rgw6ez3tj"
data-cy="searchbar-dropdown"
>
<div
class="reset_base__1klryar0 sprinkles_paddingLeft_16_sm__rgw6ez2e7 sprinkles_paddingRight_16_sm__rgw6ez2jp sprinkles_paddingTop_4_sm__rgw6ez2o7 sprinkles_paddingBottom_4_sm__rgw6ez27p sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_gap_8_sm__rgw6ez3t7 sprinkles_color_gray300_default__rgw6ez4k1 common__127l8hd4 sprinkles_fontWeight_medium_sm__rgw6ezcv sprinkles_fontSize_14_sm__rgw6ezav sprinkles_lineHeight_14_sm__rgw6ezdv"
style="line-height: 20px;"
>
<svg
fill="none"
height="20"
viewBox="0 0 20 20"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M17.5 5.8335H18.25C18.25 5.41928 17.9142 5.0835 17.5 5.0835V5.8335ZM11.0227 12.4307L10.4876 12.9562C10.6286 13.0998 10.8214 13.1807 11.0227 13.1807C11.224 13.1807 11.4169 13.0998 11.5579 12.9562L11.0227 12.4307ZM7.61364 8.9585L8.14881 8.43305C8.00778 8.28941 7.81493 8.2085 7.61364 8.2085C7.41234 8.2085 7.21949 8.28941 7.07846 8.43305L7.61364 8.9585ZM1.96483 13.6414C1.67463 13.937 1.67899 14.4118 1.97456 14.702C2.27013 14.9922 2.74498 14.9878 3.03517 14.6923L1.96483 13.6414ZM13.4091 5.0835C12.9949 5.0835 12.6591 5.41928 12.6591 5.8335C12.6591 6.24771 12.9949 6.5835 13.4091 6.5835V5.0835ZM16.75 10.0002C16.75 10.4144 17.0858 10.7502 17.5 10.7502C17.9142 10.7502 18.25 10.4144 18.25 10.0002H16.75ZM16.9648 5.30805L10.4876 11.9053L11.5579 12.9562L18.0352 6.35894L16.9648 5.30805ZM11.5579 11.9053L8.14881 8.43305L7.07846 9.48394L10.4876 12.9562L11.5579 11.9053ZM7.07846 8.43305L1.96483 13.6414L3.03517 14.6923L8.14881 9.48394L7.07846 8.43305ZM13.4091 6.5835H17.5V5.0835H13.4091V6.5835ZM16.75 5.8335V10.0002H18.25V5.8335H16.75Z"
fill="var(--color-gray300__rgw6ez1g)"
/>
</svg>
<div
class="reset_base__1klryar0"
>
Popular tokens
</div>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_gap_12_sm__rgw6ez3tj"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j SearchBar_suggestionRow__1fbf9szg SearchBar__1fbf9szf sprinkles_paddingLeft_16_sm__rgw6ez2e7 sprinkles_paddingRight_16_sm__rgw6ez2jp sprinkles_paddingTop_8_sm__rgw6ez2oj sprinkles_paddingBottom_8_sm__rgw6ez281 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j sprinkles_cursor_pointer_default__rgw6ez79z"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_width_full_sm__rgw6ez16v"
>
<div
class="reset_base__1klryar0 SearchBar_imageHolder__1fbf9szq SearchBar__1fbf9szh sprinkles_width_36_sm__rgw6ez137 sprinkles_height_36_sm__rgw6ez1a7 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_marginRight_8_sm__rgw6ezr1 SearchBar__1fbf9szp sprinkles_background_backgroundModule_default__rgw6ez4p1 sprinkles_flexShrink_0_sm__rgw6ez3xv"
/>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_gap_4_sm__rgw6ez3sv sprinkles_width_full_sm__rgw6ez16v"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j"
>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_20_sm__rgw6ez19d sprinkles_background_backgroundModule_default__rgw6ez4p1"
style="width: 180px;"
/>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_20_sm__rgw6ez19d sprinkles_width_48_sm__rgw6ez13v sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j"
>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_16_sm__rgw6ez191 sprinkles_width_120_sm__rgw6ez15j sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_16_sm__rgw6ez191 sprinkles_width_48_sm__rgw6ez13v sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
</div>
</div>
</div>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j SearchBar_suggestionRow__1fbf9szg SearchBar__1fbf9szf sprinkles_paddingLeft_16_sm__rgw6ez2e7 sprinkles_paddingRight_16_sm__rgw6ez2jp sprinkles_paddingTop_8_sm__rgw6ez2oj sprinkles_paddingBottom_8_sm__rgw6ez281 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j sprinkles_cursor_pointer_default__rgw6ez79z"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_width_full_sm__rgw6ez16v"
>
<div
class="reset_base__1klryar0 SearchBar_imageHolder__1fbf9szq SearchBar__1fbf9szh sprinkles_width_36_sm__rgw6ez137 sprinkles_height_36_sm__rgw6ez1a7 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_marginRight_8_sm__rgw6ezr1 SearchBar__1fbf9szp sprinkles_background_backgroundModule_default__rgw6ez4p1 sprinkles_flexShrink_0_sm__rgw6ez3xv"
/>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_gap_4_sm__rgw6ez3sv sprinkles_width_full_sm__rgw6ez16v"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j"
>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_20_sm__rgw6ez19d sprinkles_background_backgroundModule_default__rgw6ez4p1"
style="width: 180px;"
/>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_20_sm__rgw6ez19d sprinkles_width_48_sm__rgw6ez13v sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j"
>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_16_sm__rgw6ez191 sprinkles_width_120_sm__rgw6ez15j sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_16_sm__rgw6ez191 sprinkles_width_48_sm__rgw6ez13v sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_gap_12_sm__rgw6ez3tj"
data-cy="searchbar-dropdown"
>
<div
class="reset_base__1klryar0 sprinkles_paddingLeft_16_sm__rgw6ez2e7 sprinkles_paddingRight_16_sm__rgw6ez2jp sprinkles_paddingTop_4_sm__rgw6ez2o7 sprinkles_paddingBottom_4_sm__rgw6ez27p sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_gap_8_sm__rgw6ez3t7 sprinkles_color_gray300_default__rgw6ez4k1 common__127l8hd4 sprinkles_fontWeight_medium_sm__rgw6ezcv sprinkles_fontSize_14_sm__rgw6ezav sprinkles_lineHeight_14_sm__rgw6ezdv"
style="line-height: 20px;"
>
<svg
fill="none"
height="20"
viewBox="0 0 20 20"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M17.5 5.8335H18.25C18.25 5.41928 17.9142 5.0835 17.5 5.0835V5.8335ZM11.0227 12.4307L10.4876 12.9562C10.6286 13.0998 10.8214 13.1807 11.0227 13.1807C11.224 13.1807 11.4169 13.0998 11.5579 12.9562L11.0227 12.4307ZM7.61364 8.9585L8.14881 8.43305C8.00778 8.28941 7.81493 8.2085 7.61364 8.2085C7.41234 8.2085 7.21949 8.28941 7.07846 8.43305L7.61364 8.9585ZM1.96483 13.6414C1.67463 13.937 1.67899 14.4118 1.97456 14.702C2.27013 14.9922 2.74498 14.9878 3.03517 14.6923L1.96483 13.6414ZM13.4091 5.0835C12.9949 5.0835 12.6591 5.41928 12.6591 5.8335C12.6591 6.24771 12.9949 6.5835 13.4091 6.5835V5.0835ZM16.75 10.0002C16.75 10.4144 17.0858 10.7502 17.5 10.7502C17.9142 10.7502 18.25 10.4144 18.25 10.0002H16.75ZM16.9648 5.30805L10.4876 11.9053L11.5579 12.9562L18.0352 6.35894L16.9648 5.30805ZM11.5579 11.9053L8.14881 8.43305L7.07846 9.48394L10.4876 12.9562L11.5579 11.9053ZM7.07846 8.43305L1.96483 13.6414L3.03517 14.6923L8.14881 9.48394L7.07846 8.43305ZM13.4091 6.5835H17.5V5.0835H13.4091V6.5835ZM16.75 5.8335V10.0002H18.25V5.8335H16.75Z"
fill="var(--color-gray300__rgw6ez1g)"
/>
</svg>
<div
class="reset_base__1klryar0"
>
Popular NFT collections
</div>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_gap_12_sm__rgw6ez3tj"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j SearchBar_suggestionRow__1fbf9szg SearchBar__1fbf9szf sprinkles_paddingLeft_16_sm__rgw6ez2e7 sprinkles_paddingRight_16_sm__rgw6ez2jp sprinkles_paddingTop_8_sm__rgw6ez2oj sprinkles_paddingBottom_8_sm__rgw6ez281 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j sprinkles_cursor_pointer_default__rgw6ez79z"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_width_full_sm__rgw6ez16v"
>
<div
class="reset_base__1klryar0 SearchBar_imageHolder__1fbf9szq SearchBar__1fbf9szh sprinkles_width_36_sm__rgw6ez137 sprinkles_height_36_sm__rgw6ez1a7 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_marginRight_8_sm__rgw6ezr1 SearchBar__1fbf9szp sprinkles_background_backgroundModule_default__rgw6ez4p1 sprinkles_flexShrink_0_sm__rgw6ez3xv"
/>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_gap_4_sm__rgw6ez3sv sprinkles_width_full_sm__rgw6ez16v"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j"
>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_20_sm__rgw6ez19d sprinkles_background_backgroundModule_default__rgw6ez4p1"
style="width: 180px;"
/>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_20_sm__rgw6ez19d sprinkles_width_48_sm__rgw6ez13v sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j"
>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_16_sm__rgw6ez191 sprinkles_width_120_sm__rgw6ez15j sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_16_sm__rgw6ez191 sprinkles_width_48_sm__rgw6ez13v sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
</div>
</div>
</div>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j SearchBar_suggestionRow__1fbf9szg SearchBar__1fbf9szf sprinkles_paddingLeft_16_sm__rgw6ez2e7 sprinkles_paddingRight_16_sm__rgw6ez2jp sprinkles_paddingTop_8_sm__rgw6ez2oj sprinkles_paddingBottom_8_sm__rgw6ez281 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j sprinkles_cursor_pointer_default__rgw6ez79z"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_width_full_sm__rgw6ez16v"
>
<div
class="reset_base__1klryar0 SearchBar_imageHolder__1fbf9szq SearchBar__1fbf9szh sprinkles_width_36_sm__rgw6ez137 sprinkles_height_36_sm__rgw6ez1a7 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_marginRight_8_sm__rgw6ezr1 SearchBar__1fbf9szp sprinkles_background_backgroundModule_default__rgw6ez4p1 sprinkles_flexShrink_0_sm__rgw6ez3xv"
/>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_column_sm__rgw6ez477 sprinkles_gap_4_sm__rgw6ez3sv sprinkles_width_full_sm__rgw6ez16v"
>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j"
>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_20_sm__rgw6ez19d sprinkles_background_backgroundModule_default__rgw6ez4p1"
style="width: 180px;"
/>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_20_sm__rgw6ez19d sprinkles_width_48_sm__rgw6ez13v sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
</div>
<div
class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44v sprinkles_flexDirection_row_sm__rgw6ez471 sprinkles_alignItems_center_sm__rgw6ez3j sprinkles_justifyContent_space-between_sm__rgw6ez48j"
>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_16_sm__rgw6ez191 sprinkles_width_120_sm__rgw6ez15j sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
<div
class="reset_base__1klryar0 sprinkles_borderRadius_round_default__rgw6ez7cj sprinkles_height_16_sm__rgw6ez191 sprinkles_width_48_sm__rgw6ez13v sprinkles_background_backgroundModule_default__rgw6ez4p1"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;

View File

@@ -3,9 +3,9 @@ import { useWeb3React } from '@web3-react/core'
import { useAccountDrawer } from 'components/AccountDrawer'
import Web3Status from 'components/Web3Status'
import { chainIdToBackendName } from 'graphql/data/util'
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
import { useIsNftPage } from 'hooks/useIsNftPage'
import { useIsPoolsPage } from 'hooks/useIsPoolsPage'
import { useAtomValue } from 'jotai/utils'
import { Box } from 'nft/components/Box'
import { Row } from 'nft/components/Flex'
import { UniIcon } from 'nft/components/icons'
@@ -13,6 +13,7 @@ import { useProfilePageState } from 'nft/hooks'
import { ProfilePageStateType } from 'nft/types'
import { ReactNode, useCallback } from 'react'
import { NavLink, NavLinkProps, useLocation, useNavigate } from 'react-router-dom'
import { shouldDisableNFTRoutesAtom } from 'state/application/atoms'
import styled from 'styled-components/macro'
import { useIsNavSearchInputVisible } from '../../nft/hooks/useIsNavSearchInputVisible'
@@ -60,7 +61,7 @@ export const PageTabs = () => {
const isPoolActive = useIsPoolsPage()
const isNftPage = useIsNftPage()
const shouldDisableNFTRoutes = useDisableNFTRoutes()
const shouldDisableNFTRoutes = useAtomValue(shouldDisableNFTRoutesAtom)
return (
<>

View File

@@ -1,8 +1,7 @@
import { Trans } from '@lingui/macro'
import { ChainId } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { getChainInfo } from 'constants/chainInfo'
import { useIsAvalancheEnabled } from 'featureFlags/flags/avalanche'
import { SupportedChainId } from 'constants/chains'
import { ArrowUpRight } from 'react-feather'
import styled from 'styled-components/macro'
import { ExternalLink, HideSmall } from 'theme'
@@ -31,16 +30,15 @@ const RootWrapper = styled.div`
`
const SHOULD_SHOW_ALERT = {
[ChainId.OPTIMISM]: true,
[ChainId.OPTIMISM_GOERLI]: true,
[ChainId.ARBITRUM_ONE]: true,
[ChainId.ARBITRUM_GOERLI]: true,
[ChainId.POLYGON]: true,
[ChainId.POLYGON_MUMBAI]: true,
[ChainId.CELO]: true,
[ChainId.CELO_ALFAJORES]: true,
[ChainId.BNB]: true,
[ChainId.AVALANCHE]: true,
[SupportedChainId.OPTIMISM]: true,
[SupportedChainId.OPTIMISM_GOERLI]: true,
[SupportedChainId.ARBITRUM_ONE]: true,
[SupportedChainId.ARBITRUM_GOERLI]: true,
[SupportedChainId.POLYGON]: true,
[SupportedChainId.POLYGON_MUMBAI]: true,
[SupportedChainId.CELO]: true,
[SupportedChainId.CELO_ALFAJORES]: true,
[SupportedChainId.BNB]: true,
}
type NetworkAlertChains = keyof typeof SHOULD_SHOW_ALERT
@@ -49,48 +47,44 @@ const BG_COLORS_BY_DARK_MODE_AND_CHAIN_ID: {
[darkMode in 'dark' | 'light']: { [chainId in NetworkAlertChains]: string }
} = {
dark: {
[ChainId.POLYGON]:
[SupportedChainId.POLYGON]:
'radial-gradient(100% 93.36% at 0% 6.64%, rgba(160, 108, 247, 0.1) 0%, rgba(82, 32, 166, 0.1) 100%)',
[ChainId.POLYGON_MUMBAI]:
[SupportedChainId.POLYGON_MUMBAI]:
'radial-gradient(100% 93.36% at 0% 6.64%, rgba(160, 108, 247, 0.1) 0%, rgba(82, 32, 166, 0.1) 100%)',
[ChainId.CELO]:
[SupportedChainId.CELO]:
'radial-gradient(182.71% 150.59% at 2.81% 7.69%, rgba(90, 190, 170, 0.15) 0%, rgba(80, 160, 40, 0.15) 100%)',
[ChainId.CELO_ALFAJORES]:
[SupportedChainId.CELO_ALFAJORES]:
'radial-gradient(182.71% 150.59% at 2.81% 7.69%, rgba(90, 190, 170, 0.15) 0%, rgba(80, 160, 40, 0.15) 100%)',
[ChainId.BNB]:
[SupportedChainId.BNB]:
'radial-gradient(182.71% 150.59% at 2.81% 7.69%, rgba(240, 185, 11, 0.16) 0%, rgba(255, 168, 0, 0.16) 100%)',
[ChainId.OPTIMISM]:
[SupportedChainId.OPTIMISM]:
'radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.01) 0%, rgba(255, 255, 255, 0.04) 100%),radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.01) 0%, rgba(235, 0, 255, 0.01) 96%)',
[ChainId.OPTIMISM_GOERLI]:
[SupportedChainId.OPTIMISM_GOERLI]:
'radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.04) 100%),radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.04) 0%, rgba(235, 0, 255, 0.01 96%)',
[ChainId.ARBITRUM_ONE]:
[SupportedChainId.ARBITRUM_ONE]:
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.01) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(75% 75% at 0% 0%, rgba(150, 190, 220, 0.05) 0%, rgba(33, 114, 229, 0.05) 100%), hsla(0, 0%, 100%, 0.05)',
[ChainId.ARBITRUM_GOERLI]:
[SupportedChainId.ARBITRUM_GOERLI]:
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.05) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(75% 75% at 0% 0%, rgba(150, 190, 220, 0.05) 0%, rgba(33, 114, 229, 0.1) 100%), hsla(0, 0%, 100%, 0.05)',
[ChainId.AVALANCHE]:
'radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.01) 0%, rgba(255, 255, 255, 0.04) 100%),radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.01) 0%, rgba(235, 0, 255, 0.01) 96%)',
},
light: {
[ChainId.POLYGON]:
[SupportedChainId.POLYGON]:
'radial-gradient(182.71% 205.59% at 2.81% 7.69%, rgba(130, 71, 229, 0.2) 0%, rgba(167, 202, 255, 0.2) 100%)',
[ChainId.POLYGON_MUMBAI]:
[SupportedChainId.POLYGON_MUMBAI]:
'radial-gradient(182.71% 205.59% at 2.81% 7.69%, rgba(130, 71, 229, 0.2) 0%, rgba(167, 202, 255, 0.2) 100%)',
[ChainId.CELO]:
[SupportedChainId.CELO]:
'radial-gradient(182.71% 150.59% at 2.81% 7.69%, rgba(63, 208, 137, 0.15) 0%, rgba(49, 205, 50, 0.15) 100%)',
[ChainId.CELO_ALFAJORES]:
[SupportedChainId.CELO_ALFAJORES]:
'radial-gradient(182.71% 150.59% at 2.81% 7.69%, rgba(63, 208, 137, 0.15) 0%, rgba(49, 205, 50, 0.15) 100%)',
[ChainId.BNB]:
[SupportedChainId.BNB]:
'radial-gradient(182.71% 150.59% at 2.81% 7.69%, rgba(240, 185, 11, 0.16) 0%, rgba(255, 168, 0, 0.16) 100%)',
[ChainId.OPTIMISM]:
[SupportedChainId.OPTIMISM]:
'radial-gradient(92% 105% at 50% 7%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.03) 100%),radial-gradient(100% 97% at 0% 12%, rgba(235, 0, 255, 0.1) 0%, rgba(243, 19, 19, 0.1) 100%), hsla(0, 0%, 100%, 0.1)',
[ChainId.OPTIMISM_GOERLI]:
[SupportedChainId.OPTIMISM_GOERLI]:
'radial-gradient(92% 105% at 50% 7%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.03) 100%),radial-gradient(100% 97% at 0% 12%, rgba(235, 0, 255, 0.1) 0%, rgba(243, 19, 19, 0.1) 100%), hsla(0, 0%, 100%, 0.1)',
[ChainId.ARBITRUM_ONE]:
[SupportedChainId.ARBITRUM_ONE]:
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(circle at top left, hsla(206, 50%, 75%, 0.01), hsla(215, 79%, 51%, 0.12)), hsla(0, 0%, 100%, 0.1)',
[ChainId.ARBITRUM_GOERLI]:
[SupportedChainId.ARBITRUM_GOERLI]:
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(circle at top left, hsla(206, 50%, 75%, 0.01), hsla(215, 79%, 51%, 0.12)), hsla(0, 0%, 100%, 0.1)',
[ChainId.AVALANCHE]:
'radial-gradient(92% 105% at 50% 7%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.03) 100%),radial-gradient(100% 97% at 0% 12%, rgba(235, 0, 255, 0.1) 0%, rgba(243, 19, 19, 0.1) 100%), hsla(0, 0%, 100%, 0.1)',
},
}
@@ -141,16 +135,15 @@ const StyledArrowUpRight = styled(ArrowUpRight)`
`
const TEXT_COLORS: { [chainId in NetworkAlertChains]: string } = {
[ChainId.POLYGON]: 'rgba(130, 71, 229)',
[ChainId.POLYGON_MUMBAI]: 'rgba(130, 71, 229)',
[ChainId.CELO]: 'rgba(53, 178, 97)',
[ChainId.CELO_ALFAJORES]: 'rgba(53, 178, 97)',
[ChainId.OPTIMISM]: '#ff3856',
[ChainId.OPTIMISM_GOERLI]: '#ff3856',
[ChainId.ARBITRUM_ONE]: '#0490ed',
[ChainId.BNB]: colors.gold400,
[ChainId.ARBITRUM_GOERLI]: '#0490ed',
[ChainId.AVALANCHE]: '#ff3856',
[SupportedChainId.POLYGON]: 'rgba(130, 71, 229)',
[SupportedChainId.POLYGON_MUMBAI]: 'rgba(130, 71, 229)',
[SupportedChainId.CELO]: 'rgba(53, 178, 97)',
[SupportedChainId.CELO_ALFAJORES]: 'rgba(53, 178, 97)',
[SupportedChainId.OPTIMISM]: '#ff3856',
[SupportedChainId.OPTIMISM_GOERLI]: '#ff3856',
[SupportedChainId.ARBITRUM_ONE]: '#0490ed',
[SupportedChainId.BNB]: colors.gold400,
[SupportedChainId.ARBITRUM_GOERLI]: '#0490ed',
}
function shouldShowAlert(chainId: number | undefined): chainId is NetworkAlertChains {
@@ -160,14 +153,13 @@ function shouldShowAlert(chainId: number | undefined): chainId is NetworkAlertCh
export function NetworkAlert() {
const { chainId } = useWeb3React()
const [darkMode] = useDarkModeManager()
const isAvalancheEnabled = useIsAvalancheEnabled()
if (!shouldShowAlert(chainId)) {
return null
}
const chainInfo = getChainInfo(chainId)
if (!chainInfo || (!isAvalancheEnabled && chainId === ChainId.AVALANCHE)) return null
if (!chainInfo) return null
const { label, logoUrl, bridge } = chainInfo
const textColor = TEXT_COLORS[chainId]

View File

@@ -1,7 +1,7 @@
import { Trans } from '@lingui/macro'
import { ChainId } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { getChainInfoOrDefault, L2ChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { AlertTriangle } from 'react-feather'
import styled from 'styled-components/macro'
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
@@ -62,7 +62,7 @@ export function ChainConnectivityWarning() {
</TitleText>
</TitleRow>
<BodyRow>
{chainId === ChainId.MAINNET ? (
{chainId === SupportedChainId.MAINNET ? (
<Trans>You may have lost your network connection.</Trans>
) : (
<Trans>{label} might be down right now, or you may have lost your network connection.</Trans>

View File

@@ -1,7 +1,7 @@
import { Trans } from '@lingui/macro'
import { ChainId } from '@uniswap/sdk-core'
import AlertTriangleFilled from 'components/Icons/AlertTriangleFilled'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import styled from 'styled-components/macro'
import { ThemedText } from '../../theme'
@@ -22,7 +22,7 @@ export const PopupAlertTriangle = styled(AlertTriangleFilled)`
height: 32px;
`
export default function FailedNetworkSwitchPopup({ chainId }: { chainId: ChainId }) {
export default function FailedNetworkSwitchPopup({ chainId }: { chainId: SupportedChainId }) {
const chainInfo = getChainInfo(chainId)
return (

View File

@@ -1,5 +1,5 @@
import { ChainId } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { SupportedChainId } from 'constants/chains'
import styled from 'styled-components/macro'
import { MEDIA_WIDTHS } from 'theme'
@@ -62,7 +62,7 @@ export default function Popups() {
// need extra padding if network is not L1 Ethereum
const { chainId } = useWeb3React()
const isNotOnMainnet = Boolean(chainId && chainId !== ChainId.MAINNET)
const isNotOnMainnet = Boolean(chainId && chainId !== SupportedChainId.MAINNET)
return (
<>

View File

@@ -1,5 +1,5 @@
import { BigNumber } from '@ethersproject/bignumber'
import { ChainId, Token, WETH9 } from '@uniswap/sdk-core'
import { SupportedChainId, Token, WETH9 } from '@uniswap/sdk-core'
import { FeeAmount, Pool } from '@uniswap/v3-sdk'
import { USDC_MAINNET } from 'constants/tokens'
import { useToken } from 'hooks/Tokens'
@@ -25,7 +25,7 @@ beforeEach(() => {
// tokenA: Token, tokenB: Token, fee: FeeAmount, sqrtRatioX96: BigintIsh, liquidity: BigintIsh, tickCurrent: number
new Pool(
USDC_MAINNET,
WETH9[ChainId.MAINNET],
WETH9[SupportedChainId.MAINNET],
FeeAmount.MEDIUM,
'1745948049099224684665158875285708',
'4203610460178577802',
@@ -37,7 +37,7 @@ beforeEach(() => {
test('PositionListItem should render a position', () => {
const positionDetails = {
token0: USDC_MAINNET.address,
token1: WETH9[ChainId.MAINNET].address,
token1: WETH9[SupportedChainId.MAINNET].address,
tokenId: BigNumber.from(479689),
fee: FeeAmount.MEDIUM,
liquidity: BigNumber.from('1341008833950736'),

View File

@@ -1,9 +1,11 @@
// eslint-disable-next-line no-restricted-imports
import { Percent } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { AutoColumn } from 'components/Column'
import { isSupportedChain, L2_CHAIN_IDS } from 'constants/chains'
import { L2_CHAIN_IDS } from 'constants/chains'
import useDisableScrolling from 'hooks/useDisableScrolling'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import { isSupportedChainId } from 'lib/hooks/routing/clientSideSmartOrderRouter'
import { useRef } from 'react'
import { useModalIsOpen, useToggleSettingsMenu } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
@@ -52,11 +54,11 @@ export default function SettingsTab({ autoSlippage, chainId }: { autoSlippage: P
useDisableScrolling(isOpen)
const isChainSupported = isSupportedChain(chainId)
const isSupportedChain = isSupportedChainId(chainId)
return (
<Menu ref={node}>
<MenuButton disabled={!isChainSupported || chainId !== connectedChainId} isActive={isOpen} onClick={toggleMenu} />
<MenuButton disabled={!isSupportedChain || chainId !== connectedChainId} isActive={isOpen} onClick={toggleMenu} />
{isOpen && (
<MenuFlyout>
<RouterPreferenceSettings />

View File

@@ -1,6 +1,6 @@
import { Trans } from '@lingui/macro'
import { ChainId } from '@uniswap/sdk-core'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { darken } from 'polished'
import { useState } from 'react'
import styled from 'styled-components/macro'
@@ -67,7 +67,7 @@ const ResourcesContainer = styled.div`
type AboutSectionProps = {
address: string
chainId: ChainId
chainId: SupportedChainId
description?: string | null
homepageUrl?: string | null
twitterName?: string | null
@@ -105,7 +105,7 @@ export function AboutSection({ address, chainId, description, homepageUrl, twitt
</ThemedText.SubHeaderSmall>
<ResourcesContainer data-cy="resources-container">
<Resource
name={chainId === ChainId.MAINNET ? 'Etherscan' : 'Block Explorer'}
name={chainId === SupportedChainId.MAINNET ? 'Etherscan' : 'Block Explorer'}
link={`${explorer}${address === 'NATIVE' ? '' : 'address/' + address}`}
/>
<Resource name="More analytics" link={`${infoLink}tokens/${address}`} />

View File

@@ -1,10 +1,11 @@
import { Trans } from '@lingui/macro'
import { formatCurrencyAmount, NumberType } from '@uniswap/conedison/format'
import { ChainId, Currency } from '@uniswap/sdk-core'
import { Currency } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
import { getChainInfo } from 'constants/chainInfo'
import { asSupportedChain } from 'constants/chains'
import { SupportedChainId } from 'constants/chains'
import { isSupportedChain } from 'constants/chains'
import { useStablecoinValue } from 'hooks/useStablecoinPrice'
import useCurrencyBalance from 'lib/hooks/useCurrencyBalance'
import styled, { useTheme } from 'styled-components/macro'
@@ -66,7 +67,7 @@ const StyledNetworkLabel = styled.div`
export default function BalanceSummary({ token }: { token: Currency }) {
const { account, chainId } = useWeb3React()
const theme = useTheme()
const { label, color } = getChainInfo(asSupportedChain(chainId) ?? ChainId.MAINNET)
const { label, color } = getChainInfo(isSupportedChain(chainId) ? chainId : SupportedChainId.MAINNET)
const balance = useCurrencyBalance(account, token)
const formattedBalance = formatCurrencyAmount(balance, NumberType.TokenNonTx)
const formattedUsdValue = formatCurrencyAmount(useStablecoinValue(balance), NumberType.FiatTokenStats)

View File

@@ -1,8 +1,8 @@
import { Trans } from '@lingui/macro'
import { ChainId } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import { ButtonPrimary } from 'components/Button'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import useSelectChain from 'hooks/useSelectChain'
import { useNavigate } from 'react-router-dom'
import styled from 'styled-components/macro'
@@ -43,7 +43,7 @@ export default function InvalidTokenDetails({
pageChainId,
isInvalidAddress,
}: {
pageChainId: ChainId
pageChainId: SupportedChainId
isInvalidAddress?: boolean
}) {
const { chainId } = useWeb3React()

View File

@@ -1,8 +1,8 @@
import { Trans } from '@lingui/macro'
import { formatNumber, NumberType } from '@uniswap/conedison/format'
import { ChainId } from '@uniswap/sdk-core'
import { MouseoverTooltip } from 'components/Tooltip'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedChainId } from 'constants/chains'
import { ReactNode } from 'react'
import styled from 'styled-components/macro'
import { ExternalLink, ThemedText } from 'theme'
@@ -68,7 +68,7 @@ function Stat({
}
type StatsSectionProps = {
chainId: ChainId
chainId: SupportedChainId
address: string
priceLow52W?: NumericStat
priceHigh52W?: NumericStat

View File

@@ -26,7 +26,7 @@ import { checkWarning } from 'constants/tokenSafety'
import { TokenPriceQuery } from 'graphql/data/__generated__/types-and-hooks'
import { Chain, TokenQuery, TokenQueryData } from 'graphql/data/Token'
import { QueryToken } from 'graphql/data/Token'
import { getTokenDetailsURL, InterfaceGqlChain, supportedChainIdFromGQLChain } from 'graphql/data/util'
import { CHAIN_NAME_TO_CHAIN_ID, getTokenDetailsURL } from 'graphql/data/util'
import { useOnGlobalChainSwitch } from 'hooks/useGlobalChainSwitch'
import { UNKNOWN_TOKEN_SYMBOL, useTokenFromActiveNetwork } from 'lib/hooks/useCurrency'
import { Swap } from 'pages/Swap'
@@ -89,7 +89,7 @@ function useRelevantToken(
type TokenDetailsProps = {
urlAddress?: string
inputTokenAddress?: string
chain: InterfaceGqlChain
chain: Chain
tokenQuery: TokenQuery
tokenPriceQuery?: TokenPriceQuery
onChangeTimePeriod: OnChangeTimePeriod
@@ -111,7 +111,8 @@ export default function TokenDetails({
)
const { chainId: connectedChainId } = useWeb3React()
const pageChainId = supportedChainIdFromGQLChain(chain)
const pageChainId = CHAIN_NAME_TO_CHAIN_ID[chain]
const tokenQueryData = tokenQuery.token
const crossChainMap = useMemo(
() =>
@@ -184,7 +185,6 @@ export default function TokenDetails({
},
[continueSwap, setContinueSwap]
)
// address will never be undefined if token is defined; address is checked here to appease typechecker
if (detailedToken === undefined || !address) {
return <InvalidTokenDetails pageChainId={pageChainId} isInvalidAddress={!address} />

View File

@@ -1,12 +1,6 @@
import Badge from 'components/Badge'
import { getChainInfo } from 'constants/chainInfo'
import { useFilterChainsForAvalanche } from 'featureFlags/flags/avalanche'
import {
BACKEND_NOT_YET_SUPPORTED_CHAIN_IDS,
BACKEND_SUPPORTED_CHAINS,
supportedChainIdFromGQLChain,
validateUrlChainParam,
} from 'graphql/data/util'
import { BACKEND_CHAIN_NAMES, CHAIN_NAME_TO_CHAIN_ID, validateUrlChainParam } from 'graphql/data/util'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import { useRef } from 'react'
import { Check, ChevronDown, ChevronUp } from 'react-feather'
@@ -118,12 +112,12 @@ export default function NetworkFilter() {
const toggleMenu = useToggleModal(ApplicationModal.NETWORK_FILTER)
useOnClickOutside(node, open ? toggleMenu : undefined)
const navigate = useNavigate()
const gatedUnsupportedChains = useFilterChainsForAvalanche([...BACKEND_NOT_YET_SUPPORTED_CHAIN_IDS])
const { chainName } = useParams<{ chainName?: string }>()
const currentChainName = validateUrlChainParam(chainName)
const chainInfo = getChainInfo(supportedChainIdFromGQLChain(currentChainName))
const chainInfo = getChainInfo(CHAIN_NAME_TO_CHAIN_ID[currentChainName])
const BNBChainInfo = getChainInfo(CHAIN_NAME_TO_CHAIN_ID.BNB)
return (
<StyledMenu ref={node}>
@@ -135,7 +129,7 @@ export default function NetworkFilter() {
>
<StyledMenuContent>
<NetworkLabel>
<Logo src={chainInfo.logoUrl} /> {chainInfo.label}
<Logo src={chainInfo?.logoUrl} /> {chainInfo?.label}
</NetworkLabel>
<Chevron open={open}>
{open ? (
@@ -148,8 +142,9 @@ export default function NetworkFilter() {
</NetworkFilterOption>
{open && (
<MenuTimeFlyout>
{BACKEND_SUPPORTED_CHAINS.map((network) => {
const chainInfo = getChainInfo(supportedChainIdFromGQLChain(network))
{BACKEND_CHAIN_NAMES.map((network) => {
const chainInfo = getChainInfo(CHAIN_NAME_TO_CHAIN_ID[network])
if (!chainInfo) return null
return (
<InternalLinkMenuItem
key={network}
@@ -171,22 +166,13 @@ export default function NetworkFilter() {
</InternalLinkMenuItem>
)
})}
{gatedUnsupportedChains.map((network) => {
const chainInfo = getChainInfo(network)
return (
<InternalLinkMenuItem
key={network}
data-testid={`tokens-network-filter-option-${network}-chain`}
disabled
>
<NetworkLabel>
<Logo src={chainInfo.logoUrl} />
{chainInfo.label}
</NetworkLabel>
<Tag>Coming soon</Tag>
</InternalLinkMenuItem>
)
})}
<InternalLinkMenuItem data-testid="tokens-network-filter-option-bnb-chain" disabled>
<NetworkLabel>
<Logo src={BNBChainInfo.logoUrl} />
{BNBChainInfo.label}
</NetworkLabel>
<Tag>Coming soon</Tag>
</InternalLinkMenuItem>
</MenuTimeFlyout>
)}
</StyledMenu>

View File

@@ -1,4 +1,4 @@
import { ChainId } from '@uniswap/sdk-core'
import { SupportedChainId } from 'constants/chains'
import { Currency, TokenStandard } from 'graphql/data/__generated__/types-and-hooks'
import { CHAIN_ID_TO_BACKEND_NAME } from 'graphql/data/util'
import { render, screen } from 'test-utils/render'
@@ -72,7 +72,7 @@ describe('LoadedRow.tsx', () => {
__typename: 'Token',
id: 'VG9rZW46RVRIRVJFVU1fMHhBMGI4Njk5MWM2MjE4YjM2YzFkMTlENGEyZTlFYjBjRTM2MDZlQjQ4',
name: 'USD Coin',
chain: CHAIN_ID_TO_BACKEND_NAME[ChainId.MAINNET],
chain: CHAIN_ID_TO_BACKEND_NAME[SupportedChainId.MAINNET],
address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
symbol: 'USDC',
standard: TokenStandard.Erc20,
@@ -96,7 +96,7 @@ describe('LoadedRow.tsx', () => {
__typename: 'Token',
id: 'VG9rZW46RVRIRVJFVU1fMHhBMGI4Njk5MWM2MjE4YjM2YzFkMTlENGEyZTlFYjBjRTM2MDZlQjQ4',
name: 'USD Coin',
chain: CHAIN_ID_TO_BACKEND_NAME[ChainId.MAINNET],
chain: CHAIN_ID_TO_BACKEND_NAME[SupportedChainId.MAINNET],
address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
symbol: 'USDC',
standard: TokenStandard.Erc20,

View File

@@ -7,7 +7,7 @@ import SparklineChart from 'components/Charts/SparklineChart'
import QueryTokenLogo from 'components/Logo/QueryTokenLogo'
import { MouseoverTooltip } from 'components/Tooltip'
import { SparklineMap, TopToken } from 'graphql/data/TopTokens'
import { getTokenDetailsURL, supportedChainIdFromGQLChain, validateUrlChainParam } from 'graphql/data/util'
import { CHAIN_NAME_TO_CHAIN_ID, getTokenDetailsURL, validateUrlChainParam } from 'graphql/data/util'
import { useAtomValue } from 'jotai/utils'
import { ForwardedRef, forwardRef } from 'react'
import { CSSProperties, ReactNode } from 'react'
@@ -435,7 +435,7 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
const filterString = useAtomValue(filterStringAtom)
const filterNetwork = validateUrlChainParam(useParams<{ chainName?: string }>().chainName?.toUpperCase())
const chainId = supportedChainIdFromGQLChain(filterNetwork)
const chainId = CHAIN_NAME_TO_CHAIN_ID[filterNetwork]
const timePeriod = useAtomValue(filterTimeAtom)
const delta = token.market?.pricePercentChange?.value
const arrow = getDeltaArrow(delta)
@@ -458,7 +458,7 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
// TODO: currency logo sizing mobile (32px) vs. desktop (24px)
return (
<div ref={ref} data-testid={`token-table-row-${token.address}`}>
<div ref={ref} data-testid={`token-table-row-${token.symbol}`}>
<StyledLink
to={getTokenDetailsURL(token)}
onClick={() =>

View File

@@ -327,7 +327,7 @@ exports[`LoadedRow.tsx renders a row 1`] = `
}
<div
data-testid="token-table-row-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
data-testid="token-table-row-USDC"
>
<a
class="c0"

View File

@@ -1,4 +1,4 @@
import { ChainId } from '@uniswap/sdk-core'
import { SupportedChainId } from 'constants/chains'
export const MAX_WIDTH_MEDIA_BREAKPOINT = '1200px'
export const XLARGE_MEDIA_BREAKPOINT = '960px'
@@ -8,4 +8,4 @@ export const SMALL_MEDIA_BREAKPOINT = '540px'
export const MOBILE_MEDIA_BREAKPOINT = '420px'
// includes chains that the backend does not current source off-chain metadata for
export const UNSUPPORTED_METADATA_CHAINS = [ChainId.BNB, ChainId.AVALANCHE]
export const UNSUPPORTED_METADATA_CHAINS = [SupportedChainId.BNB]

View File

@@ -8,10 +8,11 @@ import useAccountRiskCheck from 'hooks/useAccountRiskCheck'
import { lazy } from 'react'
import { useModalIsOpen, useToggleModal } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import { retry } from 'utils/retry'
const Bag = lazy(() => import('nft/components/bag/Bag'))
const TransactionCompleteModal = lazy(() => import('nft/components/collection/TransactionCompleteModal'))
const AirdropModal = lazy(() => import('components/AirdropModal'))
const Bag = lazy(() => retry(() => import('nft/components/bag/Bag')))
const TransactionCompleteModal = lazy(() => retry(() => import('nft/components/collection/TransactionCompleteModal')))
const AirdropModal = lazy(() => retry(() => import('components/AirdropModal')))
export default function TopLevelModals() {
const addressClaimOpen = useModalIsOpen(ApplicationModal.ADDRESS_CLAIM)

View File

@@ -1,9 +1,9 @@
import { t, Trans } from '@lingui/macro'
import { ChainId, Currency } from '@uniswap/sdk-core'
import { Trans } from '@lingui/macro'
import { Currency } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import Badge from 'components/Badge'
import { getChainInfo } from 'constants/chainInfo'
import { SupportedL2ChainId } from 'constants/chains'
import { SupportedChainId, SupportedL2ChainId } from 'constants/chains'
import useCurrencyLogoURIs from 'lib/hooks/useCurrencyLogoURIs'
import { ReactNode, useCallback, useState } from 'react'
import { AlertCircle, ArrowUpCircle, CheckCircle } from 'react-feather'
@@ -120,8 +120,6 @@ function TransactionSubmittedContent({
.catch(() => setSuccess(false))
}, [connector, logoURL, token])
const explorerText = chainId === ChainId.MAINNET ? t`View on Etherscan` : t`View on Block Explorer`
return (
<Wrapper>
<AutoColumn>
@@ -159,7 +157,9 @@ function TransactionSubmittedContent({
</ButtonPrimary>
{chainId && hash && (
<ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}>
<ThemedText.Link color={theme.accentAction}>{explorerText}</ThemedText.Link>
<ThemedText.Link color={theme.accentAction}>
<Trans>View on {chainId === SupportedChainId.MAINNET ? 'Etherscan' : 'Block Explorer'}</Trans>
</ThemedText.Link>
</ExternalLink>
)}
</ConfirmationModalContentWrapper>

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