Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40d8da42a5 | ||
|
|
2151ffbac7 | ||
|
|
6ec9bb7362 | ||
|
|
31d0c3c9b3 | ||
|
|
88b7acf3ae | ||
|
|
877e000da6 | ||
|
|
3f05a88409 | ||
|
|
03ab5c80a8 | ||
|
|
4faaa60aea | ||
|
|
3a94a99293 | ||
|
|
e893bc2685 | ||
|
|
cccf6ac680 | ||
|
|
ea5af12b1d | ||
|
|
bf1f613a4f | ||
|
|
49f1acb52a | ||
|
|
a97005e2e1 | ||
|
|
966b02b2de | ||
|
|
024bbce9a4 | ||
|
|
c960c14170 | ||
|
|
39295e9a33 | ||
|
|
0feddebcc3 | ||
|
|
5e4108fbdc | ||
|
|
38cce46c7b | ||
|
|
69ae42f285 | ||
|
|
ef9619b1bd | ||
|
|
041f3d5ba2 | ||
|
|
666bb79833 | ||
|
|
2736d94432 | ||
|
|
57b098f309 | ||
|
|
c802132bd5 | ||
|
|
485764fe38 | ||
|
|
51dc10b467 | ||
|
|
7cf768b8dd | ||
|
|
59b5e81d16 | ||
|
|
8fc98abb1a | ||
|
|
def4ab3bc0 | ||
|
|
bb6de9056b |
9
.github/workflows/test.yml
vendored
9
.github/workflows/test.yml
vendored
@@ -214,7 +214,6 @@ jobs:
|
||||
name: Cloud typecheck
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||
|
||||
# TODO(WEB-2537): Setup CodeCOV
|
||||
cloud-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -225,7 +224,13 @@ jobs:
|
||||
path: node_modules/.cache
|
||||
key: ${{ runner.os }}-cloud-jest-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-cloud-jest-
|
||||
- run: yarn test:cloud --coverage --maxWorkers=100%
|
||||
# Only use 1 worker, so the other can be used for the proxy server under test.
|
||||
- run: yarn test:cloud --coverage --maxWorkers=1
|
||||
- uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
fail_ci_if_error: false
|
||||
flags: cloud-tests
|
||||
|
||||
pre:
|
||||
if: ${{ github.ref_name == 'main' || github.ref_name == 'releases/staging' }}
|
||||
|
||||
4
.husky/pre-commit
Normal file
4
.husky/pre-commit
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged
|
||||
1
CODEOWNERS
Normal file
1
CODEOWNERS
Normal file
@@ -0,0 +1 @@
|
||||
@uniswap/web-admins
|
||||
@@ -69,10 +69,10 @@ Other things to note:
|
||||
|
||||
The Uniswap Interface supports swapping, adding liquidity, removing liquidity and migrating liquidity for Uniswap protocol V2.
|
||||
|
||||
- Swap on Uniswap V2: <https://app.uniswap.org/swap?use=v2>
|
||||
- View V2 liquidity: <https://app.uniswap.org/pools/v2>
|
||||
- Add V2 liquidity: <https://app.uniswap.org/add/v2>
|
||||
- Migrate V2 liquidity to V3: <https://app.uniswap.org/migrate/v2>
|
||||
- Swap on Uniswap V2: <https://app.uniswap.org/#/swap?use=v2>
|
||||
- View V2 liquidity: <https://app.uniswap.org/#/pools/v2>
|
||||
- Add V2 liquidity: <https://app.uniswap.org/#/add/v2>
|
||||
- Migrate V2 liquidity to V3: <https://app.uniswap.org/#/migrate/v2>
|
||||
|
||||
## Accessing Uniswap V1
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@ flag_management:
|
||||
target: 50%
|
||||
individual_flags:
|
||||
- name: unit-tests
|
||||
- name: cloud-tests
|
||||
statuses:
|
||||
- type: project
|
||||
target: 80%
|
||||
|
||||
comment:
|
||||
layout: flags
|
||||
|
||||
@@ -56,10 +56,10 @@ module.exports = {
|
||||
'\\.css\\.ts$': '@vanilla-extract/jest-transform',
|
||||
'\\.(t|j)sx?$': '@swc/jest',
|
||||
},
|
||||
// Use @uniswap/conedison's build directly, as jest does not support its exports.
|
||||
transformIgnorePatterns: ['@uniswap/conedison/provider'],
|
||||
// Use d3-arrays's build directly, as jest does not support its exports.
|
||||
transformIgnorePatterns: ['d3-array'],
|
||||
moduleNameMapper: {
|
||||
'@uniswap/conedison/provider': '@uniswap/conedison/dist/provider',
|
||||
'd3-array': 'd3-array/dist/d3-array.min.js',
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
@@ -45,7 +45,7 @@ describe('Mini Portfolio account drawer', () => {
|
||||
cy.get(getTestSelector('mini-portfolio-navbar')).contains('Pools').click()
|
||||
cy.get(getTestSelector('mini-portfolio-page')).contains('No pools yet')
|
||||
|
||||
cy.intercept(/graphql/, { fixture: 'mini-portfolio/activity.json' })
|
||||
cy.intercept(/graphql/, { fixture: 'mini-portfolio/full_activity.json' })
|
||||
cy.get(getTestSelector('mini-portfolio-navbar')).contains('Activity').click()
|
||||
cy.get(getTestSelector('mini-portfolio-page')).contains('Contract Interaction')
|
||||
})
|
||||
|
||||
@@ -12,7 +12,7 @@ describe('Testing nfts', () => {
|
||||
})
|
||||
|
||||
it('should load pudgy penguin collection page', () => {
|
||||
cy.visit(`/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
|
||||
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
|
||||
cy.get(getTestSelector('nft-collection-asset')).should('exist')
|
||||
cy.get(getTestSelector('nft-collection-filter-buy-now')).should('not.exist')
|
||||
cy.get(getTestSelector('nft-filter')).first().click()
|
||||
@@ -20,13 +20,13 @@ describe('Testing nfts', () => {
|
||||
})
|
||||
|
||||
it('should be able to navigate to activity', () => {
|
||||
cy.visit(`/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
|
||||
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
|
||||
cy.get(getTestSelector('nft-activity')).first().click()
|
||||
cy.get(getTestSelector('nft-activity-row')).should('exist')
|
||||
})
|
||||
|
||||
it('should go to the details page', () => {
|
||||
cy.visit(`/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
|
||||
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
|
||||
cy.get(getTestSelector('nft-filter')).first().click()
|
||||
cy.get(getTestSelector('nft-collection-filter-buy-now')).click()
|
||||
cy.get(getTestSelector('nft-collection-asset')).first().click()
|
||||
@@ -37,7 +37,7 @@ describe('Testing nfts', () => {
|
||||
})
|
||||
|
||||
it('should toggle buy now on details page', () => {
|
||||
cy.visit(`/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
|
||||
cy.visit(`/#/nfts/collection/${PUDGY_COLLECTION_ADDRESS}`)
|
||||
cy.get(getTestSelector('nft-filter')).first().click()
|
||||
cy.get(getTestSelector('nft-collection-filter-buy-now')).click()
|
||||
cy.get(getTestSelector('nft-collection-asset')).first().click()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { SwapEventName } from '@uniswap/analytics-events'
|
||||
import { ChainId } from '@uniswap/sdk-core'
|
||||
|
||||
import { UNI, USDC_MAINNET } from '../../../src/constants/tokens'
|
||||
@@ -64,6 +65,13 @@ describe('Swap', () => {
|
||||
cy.get('#swap-currency-output .token-amount-input').type('1').should('have.value', '1')
|
||||
cy.get('#swap-currency-input .token-amount-input').should('not.have.value', '')
|
||||
|
||||
// Verify logging
|
||||
cy.waitForAmplitudeEvent(SwapEventName.SWAP_QUOTE_RECEIVED).then((event: any) => {
|
||||
cy.wrap(event.event_properties).should('have.property', 'quote_latency_milliseconds')
|
||||
cy.wrap(event.event_properties.quote_latency_milliseconds).should('be.a', 'number')
|
||||
cy.wrap(event.event_properties.quote_latency_milliseconds).should('be.gte', 0)
|
||||
})
|
||||
|
||||
// Submit transaction
|
||||
cy.get('#swap-button').click()
|
||||
cy.contains('Review swap')
|
||||
|
||||
76
cypress/e2e/swap/swapFlowLogging.test.ts
Normal file
76
cypress/e2e/swap/swapFlowLogging.test.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { SwapEventName } from '@uniswap/analytics-events'
|
||||
|
||||
import { USDC_MAINNET } from '../../../src/constants/tokens'
|
||||
import { getTestSelector } from '../../utils'
|
||||
|
||||
describe('swap flow logging', () => {
|
||||
it('completes two swaps and verifies the TTS logging for the first, plus all intermediate steps along the way', () => {
|
||||
cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${USDC_MAINNET.address}`)
|
||||
cy.hardhat()
|
||||
|
||||
// First swap in the session:
|
||||
// Enter amount to swap
|
||||
cy.get('#swap-currency-output .token-amount-input').type('1').should('have.value', '1')
|
||||
cy.get('#swap-currency-input .token-amount-input').should('not.have.value', '')
|
||||
|
||||
// Verify first swap action
|
||||
cy.waitForAmplitudeEvent(SwapEventName.SWAP_FIRST_ACTION).then((event: any) => {
|
||||
cy.wrap(event.event_properties).should('have.property', 'time_to_first_swap_action')
|
||||
cy.wrap(event.event_properties.time_to_first_swap_action).should('be.a', 'number')
|
||||
cy.wrap(event.event_properties.time_to_first_swap_action).should('be.gte', 0)
|
||||
})
|
||||
|
||||
// Verify Swap Quote
|
||||
cy.waitForAmplitudeEvent(SwapEventName.SWAP_QUOTE_FETCH).then((event: any) => {
|
||||
// Price quotes don't include these values, so we only verify the types if they exist
|
||||
if (event.event_properties.time_to_first_quote_request) {
|
||||
cy.wrap(event.event_properties.time_to_first_quote_request).should('be.a', 'number')
|
||||
cy.wrap(event.event_properties.time_to_first_quote_request).should('be.gte', 0)
|
||||
cy.wrap(event.event_properties.time_to_first_quote_request_since_first_input).should('be.a', 'number')
|
||||
cy.wrap(event.event_properties.time_to_first_quote_request_since_first_input).should('be.gte', 0)
|
||||
}
|
||||
})
|
||||
|
||||
// Submit transaction
|
||||
cy.get('#swap-button').click()
|
||||
cy.contains('Confirm swap').click()
|
||||
cy.get(getTestSelector('confirmation-close-icon')).click()
|
||||
|
||||
cy.get(getTestSelector('popups')).contains('Swapped')
|
||||
|
||||
// Verify logging
|
||||
cy.waitForAmplitudeEvent(SwapEventName.SWAP_TRANSACTION_COMPLETED).then((event: any) => {
|
||||
cy.wrap(event.event_properties).should('have.property', 'time_to_swap')
|
||||
cy.wrap(event.event_properties.time_to_swap).should('be.a', 'number')
|
||||
cy.wrap(event.event_properties.time_to_swap).should('be.gte', 0)
|
||||
cy.wrap(event.event_properties).should('have.property', 'time_to_swap_since_first_input')
|
||||
cy.wrap(event.event_properties.time_to_swap_since_first_input).should('be.a', 'number')
|
||||
cy.wrap(event.event_properties.time_to_swap_since_first_input).should('be.gte', 0)
|
||||
})
|
||||
|
||||
// Second swap in the session:
|
||||
// Enter amount to swap (different from first trade, to trigger a new quote request)
|
||||
cy.get('#swap-currency-output .token-amount-input').clear().type('10').should('have.value', '10')
|
||||
cy.get('#swap-currency-input .token-amount-input').should('not.have.value', '')
|
||||
|
||||
// Verify second Swap Quote
|
||||
cy.waitForAmplitudeEvent(SwapEventName.SWAP_QUOTE_FETCH).then((event: any) => {
|
||||
// Price quotes don't include these values, so we only verify the types if they exist
|
||||
if (event.event_properties.time_to_first_quote_request) {
|
||||
cy.wrap(event.event_properties.time_to_first_quote_request).should('be.undefined')
|
||||
cy.wrap(event.event_properties.time_to_first_quote_request_since_first_input).should('be.undefined')
|
||||
}
|
||||
})
|
||||
|
||||
// Submit transaction
|
||||
cy.get('#swap-button').click()
|
||||
cy.contains('Confirm swap').click()
|
||||
cy.get(getTestSelector('confirmation-close-icon')).click()
|
||||
|
||||
cy.get(getTestSelector('popups')).contains('Swapped')
|
||||
cy.waitForAmplitudeEvent(SwapEventName.SWAP_TRANSACTION_COMPLETED).then((event: any) => {
|
||||
cy.wrap(event.event_properties).should('not.have.property', 'time_to_swap')
|
||||
cy.wrap(event.event_properties).should('not.have.property', 'time_to_swap_since_first_input')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,45 +0,0 @@
|
||||
import { SwapEventName } from '@uniswap/analytics-events'
|
||||
|
||||
import { USDC_MAINNET } from '../../../src/constants/tokens'
|
||||
import { getTestSelector } from '../../utils'
|
||||
|
||||
describe('time-to-swap logging', () => {
|
||||
it('completes two swaps and verifies the TTS logging for the first', () => {
|
||||
cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${USDC_MAINNET.address}`)
|
||||
cy.hardhat()
|
||||
|
||||
// First swap in the session:
|
||||
// Enter amount to swap
|
||||
cy.get('#swap-currency-output .token-amount-input').type('1').should('have.value', '1')
|
||||
cy.get('#swap-currency-input .token-amount-input').should('not.have.value', '')
|
||||
|
||||
// Submit transaction
|
||||
cy.get('#swap-button').click()
|
||||
cy.contains('Confirm swap').click()
|
||||
cy.get(getTestSelector('confirmation-close-icon')).click()
|
||||
|
||||
cy.get(getTestSelector('popups')).contains('Swapped')
|
||||
|
||||
// Verify logging
|
||||
cy.waitForAmplitudeEvent(SwapEventName.SWAP_TRANSACTION_COMPLETED).then((event: any) => {
|
||||
cy.wrap(event.event_properties).should('have.property', 'time_to_swap')
|
||||
cy.wrap(event.event_properties.time_to_swap).should('be.a', 'number')
|
||||
cy.wrap(event.event_properties.time_to_swap).should('be.gte', 0)
|
||||
})
|
||||
|
||||
// Second swap in the session:
|
||||
// Enter amount to swap
|
||||
cy.get('#swap-currency-output .token-amount-input').clear().type('1').should('have.value', '1')
|
||||
cy.get('#swap-currency-input .token-amount-input').should('not.have.value', '')
|
||||
|
||||
// Submit transaction
|
||||
cy.get('#swap-button').click()
|
||||
cy.contains('Confirm swap').click()
|
||||
cy.get(getTestSelector('confirmation-close-icon')).click()
|
||||
|
||||
cy.get(getTestSelector('popups')).contains('Swapped')
|
||||
cy.waitForAmplitudeEvent(SwapEventName.SWAP_TRANSACTION_COMPLETED).then((event: any) => {
|
||||
cy.wrap(event.event_properties).should('not.have.property', 'time_to_swap')
|
||||
})
|
||||
})
|
||||
})
|
||||
27
cypress/e2e/swap/unconnected.test.ts
Normal file
27
cypress/e2e/swap/unconnected.test.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { SwapEventName } from '@uniswap/analytics-events'
|
||||
import { USDC_MAINNET } from 'constants/tokens'
|
||||
|
||||
import { getTestSelector } from '../../utils'
|
||||
|
||||
describe('Swap inputs with no wallet connected', () => {
|
||||
it('can input and load a quote with no wallet connected', () => {
|
||||
cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${USDC_MAINNET.address}`)
|
||||
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
// click twice, first time to show confirmation, second to confirm
|
||||
cy.get(getTestSelector('wallet-disconnect')).click()
|
||||
cy.get(getTestSelector('wallet-disconnect')).should('contain', 'Disconnect')
|
||||
cy.get(getTestSelector('wallet-disconnect')).click()
|
||||
cy.get(getTestSelector('close-account-drawer')).click()
|
||||
|
||||
// Enter amount to swap
|
||||
cy.get('#swap-currency-output .token-amount-input').type('1').should('have.value', '1')
|
||||
cy.get('#swap-currency-input .token-amount-input').should('not.have.value', '')
|
||||
// Verify logging
|
||||
cy.waitForAmplitudeEvent(SwapEventName.SWAP_QUOTE_RECEIVED).then((event: any) => {
|
||||
cy.wrap(event.event_properties).should('have.property', 'quote_latency_milliseconds')
|
||||
cy.wrap(event.event_properties.quote_latency_milliseconds).should('be.a', 'number')
|
||||
cy.wrap(event.event_properties.quote_latency_milliseconds).should('be.gte', 0)
|
||||
})
|
||||
})
|
||||
})
|
||||
355
cypress/e2e/swap/uniswapx.test.ts
Normal file
355
cypress/e2e/swap/uniswapx.test.ts
Normal file
@@ -0,0 +1,355 @@
|
||||
import { ChainId, CurrencyAmount } from '@uniswap/sdk-core'
|
||||
|
||||
import { DAI, nativeOnChain, USDC_MAINNET } from '../../../src/constants/tokens'
|
||||
import { getTestSelector } from '../../utils'
|
||||
|
||||
const QuoteEndpoint = 'https://api.uniswap.org/v2/quote'
|
||||
const QuoteWhereUniswapXIsBetter = 'uniswapx/quote1.json'
|
||||
const QuoteWithEthInput = 'uniswapx/quote2.json'
|
||||
|
||||
const OrderSubmissionEndpoint = 'https://api.uniswap.org/v2/order'
|
||||
|
||||
const OrderStatusEndpoint =
|
||||
'https://api.uniswap.org/v2/orders?swapper=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266&orderHashes=0xa9dd6f05ad6d6c79bee654c31ede4d0d2392862711be0f3bc4a9124af24a6a19'
|
||||
|
||||
/** Stubs the provider to return a tx receipt corresponding to the mock filled uniswapx order's txHash */
|
||||
function stubSwapTxReceipt() {
|
||||
cy.hardhat().then((hardhat) => {
|
||||
cy.fixture('uniswapx/fillTransactionReceipt.json').then((mockTxReceipt) => {
|
||||
const getTransactionReceiptStub = cy.stub(hardhat.provider, 'getTransactionReceipt').log(false)
|
||||
getTransactionReceiptStub.withArgs(mockTxReceipt.transactionHash).resolves(mockTxReceipt)
|
||||
getTransactionReceiptStub.callThrough()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
describe('UniswapX Toggle', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter })
|
||||
cy.visit(`/swap/?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`)
|
||||
})
|
||||
|
||||
it('only displays uniswapx ui when setting is on', () => {
|
||||
// Setup a swap
|
||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
||||
|
||||
// UniswapX UI should not be visible
|
||||
cy.get(getTestSelector('gas-estimate-uniswapx-icon')).should('not.exist')
|
||||
|
||||
// Opt-in to UniswapX
|
||||
cy.contains('Try it now').click()
|
||||
|
||||
// UniswapX UI should be visible
|
||||
cy.get(getTestSelector('gas-estimate-uniswapx-icon')).should('exist')
|
||||
})
|
||||
|
||||
it('prompts opt-in if UniswapX is better', () => {
|
||||
// Setup a swap
|
||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
||||
|
||||
// UniswapX should not display in gas estimate row before opt-in
|
||||
cy.get(getTestSelector('gas-estimate-uniswapx-icon')).should('not.exist')
|
||||
|
||||
// UniswapX mustache should be visible
|
||||
cy.contains('Try it now').click()
|
||||
|
||||
// Opt-in dialog should now be hidden
|
||||
cy.contains('Try it now').should('not.be.visible')
|
||||
|
||||
// UniswapX should display in gas estimate row
|
||||
cy.get(getTestSelector('gas-estimate-uniswapx-icon')).should('exist')
|
||||
|
||||
// Opt-in dialog should not reappear if user manually toggles UniswapX off
|
||||
cy.get(getTestSelector('open-settings-dialog-button')).click()
|
||||
cy.get(getTestSelector('toggle-uniswap-x-button')).click()
|
||||
cy.get(getTestSelector('open-settings-dialog-button')).click()
|
||||
cy.contains('Try it now').should('not.be.visible')
|
||||
})
|
||||
})
|
||||
|
||||
describe('UniswapX Orders', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter })
|
||||
cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' })
|
||||
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/openStatusResponse.json' })
|
||||
|
||||
stubSwapTxReceipt()
|
||||
|
||||
cy.hardhat().then((hardhat) => hardhat.fund(hardhat.wallet, CurrencyAmount.fromRawAmount(USDC_MAINNET, 3e8)))
|
||||
cy.visit(`/swap/?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`)
|
||||
})
|
||||
|
||||
it('can swap using uniswapX', () => {
|
||||
// Setup a swap
|
||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
||||
cy.contains('Try it now').click()
|
||||
|
||||
// Submit uniswapx order signature
|
||||
cy.get('#swap-button').click()
|
||||
cy.contains('Confirm swap').click()
|
||||
cy.wait('@eth_signTypedData_v4')
|
||||
cy.contains('Swap submitted')
|
||||
cy.contains('Learn more about swapping with UniswapX')
|
||||
|
||||
// Return filled order status from uniswapx api
|
||||
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/filledStatusResponse.json' })
|
||||
|
||||
// Verify swap success
|
||||
cy.contains('Swapped')
|
||||
})
|
||||
|
||||
it('renders proper view if uniswapx order expires', () => {
|
||||
// Setup a swap
|
||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
||||
cy.contains('Try it now').click()
|
||||
|
||||
// Submit uniswapx order signature
|
||||
cy.get('#swap-button').click()
|
||||
cy.contains('Confirm swap').click()
|
||||
|
||||
// Return expired order status from uniswapx api
|
||||
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/expiredStatusResponse.json' })
|
||||
|
||||
// Verify swap failure message
|
||||
cy.contains('Swap expired')
|
||||
})
|
||||
|
||||
it('renders proper view if uniswapx order has insufficient funds', () => {
|
||||
// Setup a swap
|
||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
||||
cy.contains('Try it now').click()
|
||||
|
||||
// Submit uniswapx order signature
|
||||
cy.get('#swap-button').click()
|
||||
cy.contains('Confirm swap').click()
|
||||
|
||||
// Return insufficient_funds order status from uniswapx api
|
||||
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/insufficientFundsStatusResponse.json' })
|
||||
|
||||
// Verify swap failure message
|
||||
cy.contains('Insufficient funds')
|
||||
})
|
||||
})
|
||||
|
||||
describe('UniswapX Eth Input', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept(QuoteEndpoint, { fixture: QuoteWithEthInput })
|
||||
cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' })
|
||||
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/openStatusResponse.json' })
|
||||
|
||||
// Turn off automine so that intermediate screens are available to assert on.
|
||||
cy.hardhat({ automine: false }).then(async (hardhat) => {
|
||||
await hardhat.fund(hardhat.wallet, CurrencyAmount.fromRawAmount(nativeOnChain(ChainId.MAINNET), 2e18))
|
||||
await hardhat.mine()
|
||||
})
|
||||
|
||||
stubSwapTxReceipt()
|
||||
|
||||
cy.visit(`/swap/?inputCurrency=ETH&outputCurrency=${DAI.address}`)
|
||||
})
|
||||
|
||||
it('can swap using uniswapX with ETH as input', () => {
|
||||
// Setup a swap
|
||||
cy.get('#swap-currency-input .token-amount-input').type('1')
|
||||
cy.contains('Try it now').click()
|
||||
|
||||
// Prompt ETH wrap to use for order
|
||||
cy.get('#swap-button').click()
|
||||
cy.contains('Confirm swap').click()
|
||||
cy.contains('Wrap ETH')
|
||||
|
||||
// Wrap ETH
|
||||
cy.wait('@eth_sendRawTransaction')
|
||||
cy.contains('Pending...')
|
||||
cy.hardhat().then((hardhat) => hardhat.mine())
|
||||
cy.contains('Wrapped')
|
||||
|
||||
// Approve WETH spend
|
||||
cy.wait('@eth_sendRawTransaction')
|
||||
cy.hardhat().then((hardhat) => hardhat.mine())
|
||||
|
||||
// Verify signed order submission
|
||||
cy.wait('@eth_signTypedData_v4')
|
||||
cy.contains('Swap submitted')
|
||||
cy.contains('Learn more about swapping with UniswapX')
|
||||
|
||||
// Return filled order status from uniswapx api
|
||||
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/filledStatusResponse.json' })
|
||||
|
||||
// Verify swap success
|
||||
cy.contains('Swapped')
|
||||
})
|
||||
|
||||
it('switches swap input to WETH after wrap', () => {
|
||||
// Setup a swap
|
||||
cy.get('#swap-currency-input .token-amount-input').type('1')
|
||||
cy.contains('Try it now').click()
|
||||
|
||||
// Prompt ETH wrap and confirm
|
||||
cy.get('#swap-button').click()
|
||||
cy.contains('Confirm swap').click()
|
||||
cy.wait('@eth_sendRawTransaction')
|
||||
|
||||
// Close review modal before wrap is confirmed on chain
|
||||
cy.get(getTestSelector('confirmation-close-icon')).click()
|
||||
cy.hardhat().then((hardhat) => hardhat.mine())
|
||||
|
||||
// Confirm wrap is successful and WETH is now input token
|
||||
cy.contains('Wrapped')
|
||||
cy.contains('WETH')
|
||||
|
||||
// Reopen review modal and continue swap
|
||||
cy.get('#swap-button').click()
|
||||
cy.contains('Confirm swap').click()
|
||||
|
||||
// Approve WETH spend
|
||||
cy.wait('@eth_sendRawTransaction')
|
||||
cy.hardhat().then((hardhat) => hardhat.mine())
|
||||
|
||||
// Submit uniswapx order signature
|
||||
cy.wait('@eth_signTypedData_v4')
|
||||
cy.contains('Swap submitted')
|
||||
cy.contains('Learn more about swapping with UniswapX')
|
||||
|
||||
// Return filled order status from uniswapx api
|
||||
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/filledStatusResponse.json' })
|
||||
|
||||
// Verify swap success
|
||||
cy.contains('Swapped')
|
||||
})
|
||||
})
|
||||
|
||||
describe('UniswapX activity history', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept(QuoteEndpoint, { fixture: QuoteWhereUniswapXIsBetter })
|
||||
cy.intercept(OrderSubmissionEndpoint, { fixture: 'uniswapx/orderResponse.json' })
|
||||
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/openStatusResponse.json' })
|
||||
|
||||
stubSwapTxReceipt()
|
||||
|
||||
cy.hardhat().then(async (hardhat) => {
|
||||
await hardhat.fund(hardhat.wallet, CurrencyAmount.fromRawAmount(USDC_MAINNET, 3e8))
|
||||
})
|
||||
cy.visit(`/swap/?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`)
|
||||
})
|
||||
|
||||
it('can view UniswapX order status progress in activity', () => {
|
||||
// Setup a swap
|
||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
||||
cy.contains('Try it now').click()
|
||||
|
||||
// Submit uniswapx order signature
|
||||
cy.get('#swap-button').click()
|
||||
cy.contains('Confirm swap').click()
|
||||
cy.wait('@eth_signTypedData_v4')
|
||||
cy.get(getTestSelector('confirmation-close-icon')).click()
|
||||
|
||||
// Open mini portfolio and navigate to activity history
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.intercept(/graphql/, { fixture: 'mini-portfolio/empty_activity.json' })
|
||||
cy.get(getTestSelector('mini-portfolio-navbar')).contains('Activity').click()
|
||||
|
||||
// Open pending order modal
|
||||
cy.contains('Swapping').click()
|
||||
cy.get(getTestSelector('offchain-activity-modal')).contains('Swapping')
|
||||
cy.get(getTestSelector('offchain-activity-modal')).contains('Learn more about swapping with UniswapX')
|
||||
|
||||
// Return filled order status from uniswapx api
|
||||
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/filledStatusResponse.json' })
|
||||
|
||||
cy.get(getTestSelector('offchain-activity-modal')).contains('Swapped')
|
||||
cy.get(getTestSelector('offchain-activity-modal')).contains('View on Explorer')
|
||||
})
|
||||
|
||||
it('can view UniswapX order status progress in activity upon expiry', () => {
|
||||
// Setup a swap
|
||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
||||
cy.contains('Try it now').click()
|
||||
|
||||
// Submit uniswapx order signature
|
||||
cy.get('#swap-button').click()
|
||||
cy.contains('Confirm swap').click()
|
||||
cy.wait('@eth_signTypedData_v4')
|
||||
cy.get(getTestSelector('confirmation-close-icon')).click()
|
||||
|
||||
// Open mini portfolio and navigate to activity history
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.intercept(/graphql/, { fixture: 'mini-portfolio/empty_activity.json' })
|
||||
cy.get(getTestSelector('mini-portfolio-navbar')).contains('Activity').click()
|
||||
|
||||
// Open pending order modal
|
||||
cy.contains('Swapping').click()
|
||||
cy.get(getTestSelector('offchain-activity-modal')).contains('Swapping')
|
||||
|
||||
// Return filled order status from uniswapx api
|
||||
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/expiredStatusResponse.json' })
|
||||
|
||||
cy.get(getTestSelector('offchain-activity-modal')).contains('Swap expired')
|
||||
cy.get(getTestSelector('offchain-activity-modal')).contains('learn more')
|
||||
})
|
||||
|
||||
it('deduplicates remote vs local uniswapx orders', () => {
|
||||
// Setup a swap
|
||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
||||
cy.contains('Try it now').click()
|
||||
|
||||
// Submit uniswapx order signature
|
||||
cy.get('#swap-button').click()
|
||||
cy.contains('Confirm swap').click()
|
||||
cy.wait('@eth_signTypedData_v4')
|
||||
cy.get(getTestSelector('confirmation-close-icon')).click()
|
||||
|
||||
// Return filled order status from uniswapx api
|
||||
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/filledStatusResponse.json' })
|
||||
|
||||
cy.contains('Swapped')
|
||||
|
||||
// Open mini portfolio
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
|
||||
cy.fixture('mini-portfolio/uniswapx_activity.json').then((uniswapXActivity) => {
|
||||
// Replace fixture's timestamp with current time
|
||||
uniswapXActivity.data.portfolios[0].assetActivities[0].timestamp = Date.now() / 1000
|
||||
cy.intercept(/graphql/, uniswapXActivity)
|
||||
})
|
||||
|
||||
// Open activity history
|
||||
cy.get(getTestSelector('mini-portfolio-navbar')).contains('Activity').click()
|
||||
|
||||
// Ensure gql and local order have been deduped, such that there is only one swap activity listed
|
||||
cy.get(getTestSelector('activity-content')).contains('Swapped').should('have.length', 1)
|
||||
})
|
||||
|
||||
it('balances should refetch after uniswapx swap', () => {
|
||||
// Setup a swap
|
||||
cy.get('#swap-currency-input .token-amount-input').type('300')
|
||||
cy.contains('Try it now').click()
|
||||
|
||||
const gqlSpy = cy.spy().as('gqlSpy')
|
||||
cy.intercept(/graphql/, (req) => {
|
||||
// Spy on request frequency
|
||||
req.on('response', gqlSpy)
|
||||
// Reply with a fixture to speed up test
|
||||
req.reply({
|
||||
fixture: 'mini-portfolio/tokens.json',
|
||||
})
|
||||
})
|
||||
|
||||
// Expect balances to fetch upon opening mini portfolio
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get('@gqlSpy').should('have.been.calledOnce')
|
||||
|
||||
// Submit uniswapx order signature
|
||||
cy.get('#swap-button').click()
|
||||
cy.contains('Confirm swap').click()
|
||||
|
||||
// Expect balances to refetch after approval
|
||||
cy.get('@gqlSpy').should('have.been.calledTwice')
|
||||
|
||||
// Return filled order status from uniswapx api
|
||||
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/filledStatusResponse.json' })
|
||||
|
||||
// Expect balances to refetch after swap
|
||||
cy.get('@gqlSpy').should('have.been.calledThrice')
|
||||
})
|
||||
})
|
||||
@@ -18,7 +18,9 @@ describe('Token explore filter', () => {
|
||||
searchFor('dao')
|
||||
|
||||
cy.get('@filteredTokens').then((filteredTokens) => {
|
||||
cy.get('[data-cy="token-name"]').should('deep.equal', filteredTokens)
|
||||
cy.get('[data-cy="token-name"]').then((tokens) => {
|
||||
cy.wrap(Array.from(tokens)).should('deep.equal', Array.from(filteredTokens))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -69,5 +69,8 @@ describe('Token explore', () => {
|
||||
cy.get(getTestSelector('tokens-network-filter-selected')).click()
|
||||
cy.get(getTestSelector('tokens-network-filter-option-optimism')).click()
|
||||
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism')
|
||||
cy.reload()
|
||||
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism')
|
||||
cy.get(getTestSelector('chain-selector-logo')).invoke('attr', 'alt').should('eq', 'Ethereum')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { ChainId } from '@uniswap/sdk-core'
|
||||
import { UNI } from 'constants/tokens'
|
||||
|
||||
import { getTestSelector } from '../utils'
|
||||
|
||||
const UNI_ADDRESS = UNI[ChainId.MAINNET].address.toLowerCase()
|
||||
|
||||
describe('Universal search bar', () => {
|
||||
function openSearch() {
|
||||
// can't just type "/" because on mobile it doesn't respond to that
|
||||
@@ -19,18 +24,18 @@ describe('Universal search bar', () => {
|
||||
openSearch()
|
||||
getSearchBar().clear().type('uni')
|
||||
|
||||
cy.get(getTestSelector('searchbar-token-row-UNI'))
|
||||
cy.get(getTestSelector(`searchbar-token-row-ETHEREUM-${UNI_ADDRESS}`))
|
||||
.should('contain.text', 'Uniswap')
|
||||
.and('contain.text', 'UNI')
|
||||
.and('contain.text', '$')
|
||||
.and('contain.text', '%')
|
||||
.click()
|
||||
cy.location('pathname').should('equal', '/tokens/ethereum/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984')
|
||||
cy.location('hash').should('equal', '#/tokens/ethereum/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984')
|
||||
|
||||
openSearch()
|
||||
cy.get(getTestSelector('searchbar-dropdown'))
|
||||
.contains(getTestSelector('searchbar-dropdown'), 'Recent searches')
|
||||
.find(getTestSelector('searchbar-token-row-UNI'))
|
||||
.find(getTestSelector(`searchbar-token-row-ETHEREUM-${UNI_ADDRESS}`))
|
||||
.should('exist')
|
||||
})
|
||||
|
||||
@@ -51,13 +56,13 @@ describe('Universal search bar', () => {
|
||||
// Seed recent results with UNI.
|
||||
openSearch()
|
||||
getSearchBar().type('uni')
|
||||
cy.get(getTestSelector('searchbar-token-row-UNI'))
|
||||
cy.get(getTestSelector(`searchbar-token-row-ETHEREUM-${UNI_ADDRESS}`))
|
||||
getSearchBar().clear().type('{esc}')
|
||||
|
||||
// Search a different token by name.
|
||||
openSearch()
|
||||
getSearchBar().type('eth')
|
||||
cy.get(getTestSelector('searchbar-token-row-ETH'))
|
||||
cy.get(getTestSelector('searchbar-token-row-ETHEREUM-NATIVE'))
|
||||
|
||||
// Validate that we go to the searched/selected result.
|
||||
getSearchBar().type('{enter}')
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { FeatureFlag } from 'featureFlags'
|
||||
|
||||
import { getTestSelector } from '../utils'
|
||||
|
||||
describe('Wallet Dropdown', () => {
|
||||
@@ -21,16 +23,20 @@ describe('Wallet Dropdown', () => {
|
||||
})
|
||||
}
|
||||
|
||||
function itChangesLocale() {
|
||||
function itChangesLocale({ featureFlag = false }: { featureFlag?: boolean } = {}) {
|
||||
it('should change locale', () => {
|
||||
cy.contains('Uniswap available in: English').should('not.exist')
|
||||
|
||||
if (featureFlag) {
|
||||
cy.get(getTestSelector('language-settings-button')).click()
|
||||
}
|
||||
|
||||
cy.get(getTestSelector('wallet-language-item')).contains('Afrikaans').click({ force: true })
|
||||
cy.location('search').should('match', /\?lng=af-ZA$/)
|
||||
cy.location('hash').should('match', /\?lng=af-ZA$/)
|
||||
cy.contains('Uniswap available in: English')
|
||||
|
||||
cy.get(getTestSelector('wallet-language-item')).contains('English').click({ force: true })
|
||||
cy.location('search').should('match', /\?lng=en-US$/)
|
||||
cy.location('hash').should('match', /\?lng=en-US$/)
|
||||
cy.contains('Uniswap available in: English').should('not.exist')
|
||||
})
|
||||
}
|
||||
@@ -45,6 +51,15 @@ describe('Wallet Dropdown', () => {
|
||||
itChangesLocale()
|
||||
})
|
||||
|
||||
describe('should change locale with feature flag', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/', { featureFlags: [FeatureFlag.currencyConversion] })
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('wallet-settings')).click()
|
||||
})
|
||||
itChangesLocale({ featureFlag: true })
|
||||
})
|
||||
|
||||
describe('testnet toggle', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/swap')
|
||||
|
||||
12
cypress/fixtures/mini-portfolio/empty_activity.json
Normal file
12
cypress/fixtures/mini-portfolio/empty_activity.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"data": {
|
||||
"portfolios": [
|
||||
{
|
||||
"id": "UG9ydGZvbGlvOjB4ZjM5RmQ2ZTUxYWFkODhGNkY0Y2U2YUI4ODI3Mjc5Y2ZmRmI5MjI2Ng==",
|
||||
"assetActivities": [],
|
||||
"__typename": "Portfolio"
|
||||
}
|
||||
]
|
||||
},
|
||||
"errors": []
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
102
cypress/fixtures/mini-portfolio/uniswapx_activity.json
Normal file
102
cypress/fixtures/mini-portfolio/uniswapx_activity.json
Normal file
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"data": {
|
||||
"portfolios": [
|
||||
{
|
||||
"id": "UG9ydGZvbGlvOjB4ZjM5RmQ2ZTUxYWFkODhGNkY0Y2U2YUI4ODI3Mjc5Y2ZmRmI5MjI2Ng==",
|
||||
"assetActivities": [
|
||||
{
|
||||
"id": "QXNzZXRBY3Rpdml0eTpWSEpoYm5OaFkzUnBiMjQ2TUhnNE9EZGpOemN5TlRRNU1qWTVNVEkwWVRkbVpUTXlNams1TjJJNU0yUTJabUV3TjJObE1UQXhOamxrTjJJd1pXUXhObUV6TldabU16SmtOMk13TWpBeVh6QjRaREkzTXpnek1EUTRaalF4WldZMlpXRXhaV1EzWWpBeFltVTVOemRqTjJVME1HSXdaRGswTmw4d2VEUTNZVFF5TVdKalpXTTJORE5oWWpSallURmpZamc0TmpOaU4yWm1PV0ppWm1SaU5HVmlNVE09",
|
||||
"timestamp": 1691001923,
|
||||
"type": "SWAP_ORDER",
|
||||
"chain": "ETHEREUM",
|
||||
"details": {
|
||||
"__typename": "TransactionDetails",
|
||||
"id": "VHJhbnNhY3Rpb246MHg4ODdjNzcyNTQ5MjY5MTI0YTdmZTMyMjk5N2I5M2Q2ZmEwN2NlMTAxNjlkN2IwZWQxNmEzNWZmMzJkN2MwMjAyXzB4ZDI3MzgzMDQ4ZjQxZWY2ZWExZWQ3YjAxYmU5NzdjN2U0MGIwZDk0Nl8weDQ3YTQyMWJjZWM2NDNhYjRjYTFjYjg4NjNiN2ZmOWJiZmRiNGViMTM=",
|
||||
"type": "SWAP_ORDER",
|
||||
"from": "0xd27383048f41ef6ea1ed7b01be977c7e40b0d946",
|
||||
"to": "0x47a421bcec643ab4ca1cb8863b7ff9bbfdb4eb13",
|
||||
"hash": "0x9f8382a94ee80ca119bc690908ab5f69c4c72f7497ee10f37e9ede0ded83cca6",
|
||||
"nonce": 439,
|
||||
"status": "CONFIRMED"
|
||||
},
|
||||
"assetChanges": [
|
||||
{
|
||||
"__typename": "TokenTransfer",
|
||||
"id": "VG9rZW5UcmFuc2ZlcjoweDgwYmVjYjgwOGJmYWRlNDE0MzE4M2U1OGQxOGYyMDgwZTg0ZTU3YTFfMHg0N2E0MjFiY2VjNjQzYWI0Y2ExY2I4ODYzYjdmZjliYmZkYjRlYjEzXzB4ODg3Yzc3MjU0OTI2OTEyNGE3ZmUzMjI5OTdiOTNkNmZhMDdjZTEwMTY5ZDdiMGVkMTZhMzVmZjMyZDdjMDIwMg==",
|
||||
"asset": {
|
||||
"id": "VG9rZW46RVRIRVJFVU1fMHhhMGI4Njk5MWM2MjE4YjM2YzFkMTlkNGEyZTllYjBjZTM2MDZlYjQ4",
|
||||
"name": "USD Coin",
|
||||
"symbol": "USDC",
|
||||
"address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
|
||||
"decimals": 6,
|
||||
"chain": "ETHEREUM",
|
||||
"standard": null,
|
||||
"project": {
|
||||
"id": "VG9rZW5Qcm9qZWN0OkVUSEVSRVVNXzB4YTBiODY5OTFjNjIxOGIzNmMxZDE5ZDRhMmU5ZWIwY2UzNjA2ZWI0OA==",
|
||||
"isSpam": false,
|
||||
"logo": {
|
||||
"id": "SW1hZ2U6aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1VuaXN3YXAvYXNzZXRzL21hc3Rlci9ibG9ja2NoYWlucy9ldGhlcmV1bS9hc3NldHMvMHhBMGI4Njk5MWM2MjE4YjM2YzFkMTlENGEyZTlFYjBjRTM2MDZlQjQ4L2xvZ28ucG5n",
|
||||
"url": "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
|
||||
"__typename": "Image"
|
||||
},
|
||||
"__typename": "TokenProject"
|
||||
},
|
||||
"__typename": "Token"
|
||||
},
|
||||
"tokenStandard": "ERC20",
|
||||
"quantity": "300.0",
|
||||
"sender": "0x80becb808bfade4143183e58d18f2080e84e57a1",
|
||||
"recipient": "0x47a421bcec643ab4ca1cb8863b7ff9bbfdb4eb13",
|
||||
"direction": "OUT",
|
||||
"transactedValue": {
|
||||
"id": "QW1vdW50OjMwMC4xNDkxNTIwOTE5NDE2M19VU0Q=",
|
||||
"currency": "USD",
|
||||
"value": 300.14915209194163,
|
||||
"__typename": "Amount"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__typename": "TokenTransfer",
|
||||
"id": "VG9rZW5UcmFuc2ZlcjoweDQ3YTQyMWJjZWM2NDNhYjRjYTFjYjg4NjNiN2ZmOWJiZmRiNGViMTNfMHg4MGJlY2I4MDhiZmFkZTQxNDMxODNlNThkMThmMjA4MGU4NGU1N2ExXzB4ODg3Yzc3MjU0OTI2OTEyNGE3ZmUzMjI5OTdiOTNkNmZhMDdjZTEwMTY5ZDdiMGVkMTZhMzVmZjMyZDdjMDIwMg==",
|
||||
"asset": {
|
||||
"id": "VG9rZW46RVRIRVJFVU1fMHg2YjE3NTQ3NGU4OTA5NGM0NGRhOThiOTU0ZWVkZWFjNDk1MjcxZDBm",
|
||||
"name": "Dai Stablecoin",
|
||||
"symbol": "DAI",
|
||||
"address": "0x6b175474e89094c44da98b954eedeac495271d0f",
|
||||
"decimals": 18,
|
||||
"chain": "ETHEREUM",
|
||||
"standard": null,
|
||||
"project": {
|
||||
"id": "VG9rZW5Qcm9qZWN0OkVUSEVSRVVNXzB4NmIxNzU0NzRlODkwOTRjNDRkYTk4Yjk1NGVlZGVhYzQ5NTI3MWQwZg==",
|
||||
"isSpam": false,
|
||||
"logo": {
|
||||
"id": "SW1hZ2U6aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1VuaXN3YXAvYXNzZXRzL21hc3Rlci9ibG9ja2NoYWlucy9ldGhlcmV1bS9hc3NldHMvMHg2QjE3NTQ3NEU4OTA5NEM0NERhOThiOTU0RWVkZUFDNDk1MjcxZDBGL2xvZ28ucG5n",
|
||||
"url": "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png",
|
||||
"__typename": "Image"
|
||||
},
|
||||
"__typename": "TokenProject"
|
||||
},
|
||||
"__typename": "Token"
|
||||
},
|
||||
"tokenStandard": "ERC20",
|
||||
"quantity": "280.573117586837733376",
|
||||
"sender": "0x47a421bcec643ab4ca1cb8863b7ff9bbfdb4eb13",
|
||||
"recipient": "0x80becb808bfade4143183e58d18f2080e84e57a1",
|
||||
"direction": "IN",
|
||||
"transactedValue": {
|
||||
"id": "QW1vdW50OjI4MC42ODc3OTU0NTg2ODE4X1VTRA==",
|
||||
"currency": "USD",
|
||||
"value": 280.6877954586818,
|
||||
"__typename": "Amount"
|
||||
}
|
||||
}
|
||||
],
|
||||
"__typename": "AssetActivity"
|
||||
}
|
||||
],
|
||||
"__typename": "Portfolio"
|
||||
}
|
||||
]
|
||||
},
|
||||
"errors": []
|
||||
}
|
||||
26
cypress/fixtures/uniswapx/expiredStatusResponse.json
Normal file
26
cypress/fixtures/uniswapx/expiredStatusResponse.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"orders": [
|
||||
{
|
||||
"outputs": [
|
||||
{
|
||||
"recipient": "0x80becb808bfade4143183e58d18f2080e84e57a1",
|
||||
"startAmount": "91371770080538616664",
|
||||
"endAmount": "90914911230135923580",
|
||||
"token": "0x6B175474E89094C44Da98b954EedeAC495271d0F"
|
||||
}
|
||||
],
|
||||
"encodedOrder": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000064837e2a0000000000000000000000000000000000000000000000000000000064837e6600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000bd7f9d0239f81c94b728d827a87b9864972661ec00000000000000000000000080becb808bfade4143183e58d18f2080e84e57a18e32c6335b6f657322448399bd12ff5c22b7b1aa770850ff4eed36c750e2de000000000000000000000000000000000000000000000000000000000064837e66000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000004f409bcc7a52b6358000000000000000000000000000000000000000000000004edb2a613726c737c00000000000000000000000080becb808bfade4143183e58d18f2080e84e57a1",
|
||||
"signature": "0x973882a290778b5c8aae691ef777385259928cde0513d224ea1131538379258d2db7a69804110320b08558380394879a31ab8dea61152c2dba7623acbfa11d0e1b",
|
||||
"input": {
|
||||
"endAmount": "100000000",
|
||||
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"startAmount": "100000000"
|
||||
},
|
||||
"orderStatus": "expired",
|
||||
"createdAt": 1686339087,
|
||||
"chainId": 1,
|
||||
"orderHash": "0xa9dd6f05ad6d6c79bee654c31ede4d0d2392862711be0f3bc4a9124af24a6a19",
|
||||
"type": "Dutch"
|
||||
}
|
||||
]
|
||||
}
|
||||
114
cypress/fixtures/uniswapx/fillTransactionReceipt.json
Normal file
114
cypress/fixtures/uniswapx/fillTransactionReceipt.json
Normal file
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"to": "0xbD7F9D0239f81C94b728d827a87b9864972661eC",
|
||||
"from": "0xa17Fbb0b5a251A7ACA3BD7377e7eCC4F700A2C09",
|
||||
"contractAddress": null,
|
||||
"transactionIndex": 61,
|
||||
"gasUsed": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x03e0c8"
|
||||
},
|
||||
"logsBloom":
|
||||
"0x00000000000000000000008000200100000020000000000000000000000000000000000000000000000000010000000000000000000020000000000001000000000280000000000808000008000000000000000000000000000000000000200010000000100000000008000000000004402000080000000000000010000800000000000000000800000800000000000000000000010000000000000000000000000000000000200000000000005000000000000000000000000000000000000000000002000000000000000000000000040002000000000000000100000000090000000400000000000400000020080000000000000000000000000000000000",
|
||||
"blockHash": "0x79cf0785f317f984eeaf737c592afff806cabf4fe0c46a84f62a4a0212cfab5c",
|
||||
"transactionHash": "0x9f8382a94ee80ca119bc690908ab5f69c4c72f7497ee10f37e9ede0ded83cca6",
|
||||
"logs": [
|
||||
{
|
||||
"transactionIndex": 61,
|
||||
"blockNumber": 17444757,
|
||||
"transactionHash": "0x9f8382a94ee80ca119bc690908ab5f69c4c72f7497ee10f37e9ede0ded83cca6",
|
||||
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"topics": [
|
||||
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
|
||||
"0x00000000000000000000000080becb808bfade4143183e58d18f2080e84e57a1",
|
||||
"0x000000000000000000000000c59938e2d9ff9a0ecccbedf39031b1600d008eaf"
|
||||
],
|
||||
"data": "0x0000000000000000000000000000000000000000000000000000000005f5e100",
|
||||
"logIndex": 103,
|
||||
"blockHash": "0x79cf0785f317f984eeaf737c592afff806cabf4fe0c46a84f62a4a0212cfab5c"
|
||||
},
|
||||
{
|
||||
"transactionIndex": 61,
|
||||
"blockNumber": 17444757,
|
||||
"transactionHash": "0x9f8382a94ee80ca119bc690908ab5f69c4c72f7497ee10f37e9ede0ded83cca6",
|
||||
"address": "0xbD7F9D0239f81C94b728d827a87b9864972661eC",
|
||||
"topics": [
|
||||
"0x78ad7ec0e9f89e74012afa58738b6b661c024cb0fd185ee2f616c0a28924bd66",
|
||||
"0xd10e1d90145460003d98ba4b788564e9549cc93c65a12c9b297720a9d6a586de",
|
||||
"0x000000000000000000000000a17fbb0b5a251a7aca3bd7377e7ecc4f700a2c09",
|
||||
"0x00000000000000000000000080becb808bfade4143183e58d18f2080e84e57a1"
|
||||
],
|
||||
"data": "0x8e32c6335b6f657322448399bd12ff5c22b7b1aa770850ff4eed36c750e2de00",
|
||||
"logIndex": 104,
|
||||
"blockHash": "0x79cf0785f317f984eeaf737c592afff806cabf4fe0c46a84f62a4a0212cfab5c"
|
||||
},
|
||||
{
|
||||
"transactionIndex": 61,
|
||||
"blockNumber": 17444757,
|
||||
"transactionHash": "0x9f8382a94ee80ca119bc690908ab5f69c4c72f7497ee10f37e9ede0ded83cca6",
|
||||
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
"topics": [
|
||||
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
|
||||
"0x0000000000000000000000005777d92f208679db4b9778590fa3cab3ac9e2168",
|
||||
"0x000000000000000000000000c59938e2d9ff9a0ecccbedf39031b1600d008eaf"
|
||||
],
|
||||
"data": "0x0000000000000000000000000000000000000000000000056b9a675be430b502",
|
||||
"logIndex": 105,
|
||||
"blockHash": "0x79cf0785f317f984eeaf737c592afff806cabf4fe0c46a84f62a4a0212cfab5c"
|
||||
},
|
||||
{
|
||||
"transactionIndex": 61,
|
||||
"blockNumber": 17444757,
|
||||
"transactionHash": "0x9f8382a94ee80ca119bc690908ab5f69c4c72f7497ee10f37e9ede0ded83cca6",
|
||||
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"topics": [
|
||||
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
|
||||
"0x000000000000000000000000c59938e2d9ff9a0ecccbedf39031b1600d008eaf",
|
||||
"0x0000000000000000000000005777d92f208679db4b9778590fa3cab3ac9e2168"
|
||||
],
|
||||
"data": "0x0000000000000000000000000000000000000000000000000000000005f5e100",
|
||||
"logIndex": 106,
|
||||
"blockHash": "0x79cf0785f317f984eeaf737c592afff806cabf4fe0c46a84f62a4a0212cfab5c"
|
||||
},
|
||||
{
|
||||
"transactionIndex": 61,
|
||||
"blockNumber": 17444757,
|
||||
"transactionHash": "0x9f8382a94ee80ca119bc690908ab5f69c4c72f7497ee10f37e9ede0ded83cca6",
|
||||
"address": "0x5777d92f208679DB4b9778590Fa3CAB3aC9e2168",
|
||||
"topics": [
|
||||
"0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67",
|
||||
"0x00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45",
|
||||
"0x000000000000000000000000c59938e2d9ff9a0ecccbedf39031b1600d008eaf"
|
||||
],
|
||||
"data": "0xfffffffffffffffffffffffffffffffffffffffffffffffa946598a41bcf4afe0000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000010c7063b90a5e90d13830000000000000000000000000000000000000000000071b57cb2bb0b5b28224ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc89c",
|
||||
"logIndex": 107,
|
||||
"blockHash": "0x79cf0785f317f984eeaf737c592afff806cabf4fe0c46a84f62a4a0212cfab5c"
|
||||
},
|
||||
{
|
||||
"transactionIndex": 61,
|
||||
"blockNumber": 17444757,
|
||||
"transactionHash": "0x9f8382a94ee80ca119bc690908ab5f69c4c72f7497ee10f37e9ede0ded83cca6",
|
||||
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
"topics": [
|
||||
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
|
||||
"0x000000000000000000000000c59938e2d9ff9a0ecccbedf39031b1600d008eaf",
|
||||
"0x00000000000000000000000080becb808bfade4143183e58d18f2080e84e57a1"
|
||||
],
|
||||
"data": "0x000000000000000000000000000000000000000000000004f409bcc7a52b6358",
|
||||
"logIndex": 108,
|
||||
"blockHash": "0x79cf0785f317f984eeaf737c592afff806cabf4fe0c46a84f62a4a0212cfab5c"
|
||||
}
|
||||
],
|
||||
"blockNumber": 17444757,
|
||||
"confirmations": 392238,
|
||||
"cumulativeGasUsed": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x4065ac"
|
||||
},
|
||||
"effectiveGasPrice": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x04aa792df0"
|
||||
},
|
||||
"status": 1,
|
||||
"type": 2,
|
||||
"byzantium": true
|
||||
}
|
||||
33
cypress/fixtures/uniswapx/filledStatusResponse.json
Normal file
33
cypress/fixtures/uniswapx/filledStatusResponse.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"orders": [
|
||||
{
|
||||
"outputs": [
|
||||
{
|
||||
"recipient": "0x80becb808bfade4143183e58d18f2080e84e57a1",
|
||||
"startAmount": "91371770080538616664",
|
||||
"endAmount": "90914911230135923580",
|
||||
"token": "0x6B175474E89094C44Da98b954EedeAC495271d0F"
|
||||
}
|
||||
],
|
||||
"encodedOrder": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000064837e2a0000000000000000000000000000000000000000000000000000000064837e6600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000bd7f9d0239f81c94b728d827a87b9864972661ec00000000000000000000000080becb808bfade4143183e58d18f2080e84e57a18e32c6335b6f657322448399bd12ff5c22b7b1aa770850ff4eed36c750e2de000000000000000000000000000000000000000000000000000000000064837e66000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000004f409bcc7a52b6358000000000000000000000000000000000000000000000004edb2a613726c737c00000000000000000000000080becb808bfade4143183e58d18f2080e84e57a1",
|
||||
"signature": "0x973882a290778b5c8aae691ef777385259928cde0513d224ea1131538379258d2db7a69804110320b08558380394879a31ab8dea61152c2dba7623acbfa11d0e1b",
|
||||
"input": {
|
||||
"endAmount": "100000000",
|
||||
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"startAmount": "100000000"
|
||||
},
|
||||
"settledAmounts": [
|
||||
{
|
||||
"tokenOut": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
"amountOut": "91371770080538616664"
|
||||
}
|
||||
],
|
||||
"orderStatus": "filled",
|
||||
"txHash": "0x9f8382a94ee80ca119bc690908ab5f69c4c72f7497ee10f37e9ede0ded83cca6",
|
||||
"createdAt": 1686339087,
|
||||
"chainId": 1,
|
||||
"orderHash": "0xa9dd6f05ad6d6c79bee654c31ede4d0d2392862711be0f3bc4a9124af24a6a19",
|
||||
"type": "Dutch"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"orders": [
|
||||
{
|
||||
"outputs": [
|
||||
{
|
||||
"recipient": "0x80becb808bfade4143183e58d18f2080e84e57a1",
|
||||
"startAmount": "91371770080538616664",
|
||||
"endAmount": "90914911230135923580",
|
||||
"token": "0x6B175474E89094C44Da98b954EedeAC495271d0F"
|
||||
}
|
||||
],
|
||||
"encodedOrder": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000064837e2a0000000000000000000000000000000000000000000000000000000064837e6600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000bd7f9d0239f81c94b728d827a87b9864972661ec00000000000000000000000080becb808bfade4143183e58d18f2080e84e57a18e32c6335b6f657322448399bd12ff5c22b7b1aa770850ff4eed36c750e2de000000000000000000000000000000000000000000000000000000000064837e66000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000004f409bcc7a52b6358000000000000000000000000000000000000000000000004edb2a613726c737c00000000000000000000000080becb808bfade4143183e58d18f2080e84e57a1",
|
||||
"signature": "0x973882a290778b5c8aae691ef777385259928cde0513d224ea1131538379258d2db7a69804110320b08558380394879a31ab8dea61152c2dba7623acbfa11d0e1b",
|
||||
"input": {
|
||||
"endAmount": "100000000",
|
||||
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"startAmount": "100000000"
|
||||
},
|
||||
"orderStatus": "insufficient-funds",
|
||||
"createdAt": 1686339087,
|
||||
"chainId": 1,
|
||||
"orderHash": "0xa9dd6f05ad6d6c79bee654c31ede4d0d2392862711be0f3bc4a9124af24a6a19",
|
||||
"type": "Dutch"
|
||||
}
|
||||
]
|
||||
}
|
||||
26
cypress/fixtures/uniswapx/openStatusResponse.json
Normal file
26
cypress/fixtures/uniswapx/openStatusResponse.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"orders": [
|
||||
{
|
||||
"outputs": [
|
||||
{
|
||||
"recipient": "0x80becb808bfade4143183e58d18f2080e84e57a1",
|
||||
"startAmount": "91371770080538616664",
|
||||
"endAmount": "90914911230135923580",
|
||||
"token": "0x6B175474E89094C44Da98b954EedeAC495271d0F"
|
||||
}
|
||||
],
|
||||
"encodedOrder": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000064837e2a0000000000000000000000000000000000000000000000000000000064837e6600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000bd7f9d0239f81c94b728d827a87b9864972661ec00000000000000000000000080becb808bfade4143183e58d18f2080e84e57a18e32c6335b6f657322448399bd12ff5c22b7b1aa770850ff4eed36c750e2de000000000000000000000000000000000000000000000000000000000064837e66000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000004f409bcc7a52b6358000000000000000000000000000000000000000000000004edb2a613726c737c00000000000000000000000080becb808bfade4143183e58d18f2080e84e57a1",
|
||||
"signature": "0x973882a290778b5c8aae691ef777385259928cde0513d224ea1131538379258d2db7a69804110320b08558380394879a31ab8dea61152c2dba7623acbfa11d0e1b",
|
||||
"input": {
|
||||
"endAmount": "100000000",
|
||||
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"startAmount": "100000000"
|
||||
},
|
||||
"orderStatus": "open",
|
||||
"createdAt": 1686339087,
|
||||
"chainId": 1,
|
||||
"orderHash": "0xa9dd6f05ad6d6c79bee654c31ede4d0d2392862711be0f3bc4a9124af24a6a19",
|
||||
"type": "Dutch"
|
||||
}
|
||||
]
|
||||
}
|
||||
1
cypress/fixtures/uniswapx/orderResponse.json
Normal file
1
cypress/fixtures/uniswapx/orderResponse.json
Normal file
@@ -0,0 +1 @@
|
||||
{"hash":"0xa9dd6f05ad6d6c79bee654c31ede4d0d2392862711be0f3bc4a9124af24a6a19"}
|
||||
491
cypress/fixtures/uniswapx/quote1.json
Normal file
491
cypress/fixtures/uniswapx/quote1.json
Normal file
@@ -0,0 +1,491 @@
|
||||
{
|
||||
"routing": "DUTCH_LIMIT",
|
||||
"quote": {
|
||||
"orderInfo": {
|
||||
"chainId": 1,
|
||||
"permit2Address": "0x000000000022d473030f116ddee9f6b43ac78ba3",
|
||||
"reactor": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
|
||||
"swapper": "0x67d615D6bccAA1562B1cca9786384b4840597ecD",
|
||||
"nonce": "57335948072881703373319552024074512292695687510330025934414357004397546394368",
|
||||
"deadline": 1690902198,
|
||||
"additionalValidationContract": "0x0000000000000000000000000000000000000000",
|
||||
"additionalValidationData": "0x",
|
||||
"decayStartTime": 1690902126,
|
||||
"decayEndTime": 1690902186,
|
||||
"exclusiveFiller": "0x0000000000000000000000000000000000000000",
|
||||
"exclusivityOverrideBps": "0",
|
||||
"input": {
|
||||
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"startAmount": "300000000",
|
||||
"endAmount": "300000000"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"token": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
"startAmount": "289951120815684452958",
|
||||
"endAmount": "267060007981523637666",
|
||||
"recipient": "0x67d615D6bccAA1562B1cca9786384b4840597ecD"
|
||||
}
|
||||
]
|
||||
},
|
||||
"encodedOrder": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000064c91e6e0000000000000000000000000000000000000000000000000000000064c91eaa00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000011e1a3000000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000000000000000002000000000000000000000000006000da47483062a0d734ba3dc7576ce6a0b645c400000000000000000000000067d615d6bccaa1562b1cca9786384b4840597ecd7ec2ff20796a08922e11fd828e3871a6aa9a80e6495e30cd41be24b7e37953000000000000000000000000000000000000000000000000000000000064c91eb6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000fb7e15027ad3e025e00000000000000000000000000000000000000000000000e7a33be508bb395a200000000000000000000000067d615d6bccaa1562b1cca9786384b4840597ecd",
|
||||
"quoteId": "f9f47cd7-a62c-4622-9ac7-51d0e662245a",
|
||||
"requestId": "2d16f993-6429-4755-ba50-1383789459dc",
|
||||
"auctionPeriodSecs": 60,
|
||||
"deadlineBufferSecs": 12,
|
||||
"slippageTolerance": "0.5",
|
||||
"permitData": {
|
||||
"domain": {
|
||||
"name": "Permit2",
|
||||
"chainId": 1,
|
||||
"verifyingContract": "0x000000000022d473030f116ddee9f6b43ac78ba3"
|
||||
},
|
||||
"types": {
|
||||
"PermitWitnessTransferFrom": [
|
||||
{
|
||||
"name": "permitted",
|
||||
"type": "TokenPermissions"
|
||||
},
|
||||
{
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "nonce",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "deadline",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "witness",
|
||||
"type": "ExclusiveDutchOrder"
|
||||
}
|
||||
],
|
||||
"TokenPermissions": [
|
||||
{
|
||||
"name": "token",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"ExclusiveDutchOrder": [
|
||||
{
|
||||
"name": "info",
|
||||
"type": "OrderInfo"
|
||||
},
|
||||
{
|
||||
"name": "decayStartTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "decayEndTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "exclusiveFiller",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "exclusivityOverrideBps",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "inputToken",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "inputStartAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "inputEndAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "outputs",
|
||||
"type": "DutchOutput[]"
|
||||
}
|
||||
],
|
||||
"OrderInfo": [
|
||||
{
|
||||
"name": "reactor",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "swapper",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "nonce",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "deadline",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "additionalValidationContract",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "additionalValidationData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"DutchOutput": [
|
||||
{
|
||||
"name": "token",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "startAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "endAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "recipient",
|
||||
"type": "address"
|
||||
}
|
||||
]
|
||||
},
|
||||
"values": {
|
||||
"permitted": {
|
||||
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"amount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x11e1a300"
|
||||
}
|
||||
},
|
||||
"spender": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
|
||||
"nonce": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x7ec2ff20796a08922e11fd828e3871a6aa9a80e6495e30cd41be24b7e3795300"
|
||||
},
|
||||
"deadline": 1690902198,
|
||||
"witness": {
|
||||
"info": {
|
||||
"reactor": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
|
||||
"swapper": "0x67d615D6bccAA1562B1cca9786384b4840597ecD",
|
||||
"nonce": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x7ec2ff20796a08922e11fd828e3871a6aa9a80e6495e30cd41be24b7e3795300"
|
||||
},
|
||||
"deadline": 1690902198,
|
||||
"additionalValidationContract": "0x0000000000000000000000000000000000000000",
|
||||
"additionalValidationData": "0x"
|
||||
},
|
||||
"decayStartTime": 1690902126,
|
||||
"decayEndTime": 1690902186,
|
||||
"exclusiveFiller": "0x0000000000000000000000000000000000000000",
|
||||
"exclusivityOverrideBps": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x00"
|
||||
},
|
||||
"inputToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"inputStartAmount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x11e1a300"
|
||||
},
|
||||
"inputEndAmount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x11e1a300"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"token": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
"startAmount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x0fb7e15027ad3e025e"
|
||||
},
|
||||
"endAmount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x0e7a33be508bb395a2"
|
||||
},
|
||||
"recipient": "0x67d615D6bccAA1562B1cca9786384b4840597ecD"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestId": "2d16f993-6429-4755-ba50-1383789459dc",
|
||||
"allQuotes": [
|
||||
{
|
||||
"routing": "DUTCH_LIMIT",
|
||||
"quote": {
|
||||
"orderInfo": {
|
||||
"chainId": 1,
|
||||
"permit2Address": "0x000000000022d473030f116ddee9f6b43ac78ba3",
|
||||
"reactor": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
|
||||
"swapper": "0x67d615D6bccAA1562B1cca9786384b4840597ecD",
|
||||
"nonce": "57335948072881703373319552024074512292695687510330025934414357004397546394368",
|
||||
"deadline": 1690902198,
|
||||
"additionalValidationContract": "0x0000000000000000000000000000000000000000",
|
||||
"additionalValidationData": "0x",
|
||||
"decayStartTime": 1690902126,
|
||||
"decayEndTime": 1690902186,
|
||||
"exclusiveFiller": "0x0000000000000000000000000000000000000000",
|
||||
"exclusivityOverrideBps": "0",
|
||||
"input": {
|
||||
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"startAmount": "300000000",
|
||||
"endAmount": "300000000"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"token": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
"startAmount": "289951120815684452958",
|
||||
"endAmount": "267060007981523637666",
|
||||
"recipient": "0x67d615D6bccAA1562B1cca9786384b4840597ecD"
|
||||
}
|
||||
]
|
||||
},
|
||||
"encodedOrder": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000064c91e6e0000000000000000000000000000000000000000000000000000000064c91eaa00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000011e1a3000000000000000000000000000000000000000000000000000000000011e1a30000000000000000000000000000000000000000000000000000000000000002000000000000000000000000006000da47483062a0d734ba3dc7576ce6a0b645c400000000000000000000000067d615d6bccaa1562b1cca9786384b4840597ecd7ec2ff20796a08922e11fd828e3871a6aa9a80e6495e30cd41be24b7e37953000000000000000000000000000000000000000000000000000000000064c91eb6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000fb7e15027ad3e025e00000000000000000000000000000000000000000000000e7a33be508bb395a200000000000000000000000067d615d6bccaa1562b1cca9786384b4840597ecd",
|
||||
"quoteId": "f9f47cd7-a62c-4622-9ac7-51d0e662245a",
|
||||
"requestId": "2d16f993-6429-4755-ba50-1383789459dc",
|
||||
"auctionPeriodSecs": 60,
|
||||
"deadlineBufferSecs": 12,
|
||||
"slippageTolerance": "0.5",
|
||||
"permitData": {
|
||||
"domain": {
|
||||
"name": "Permit2",
|
||||
"chainId": 1,
|
||||
"verifyingContract": "0x000000000022d473030f116ddee9f6b43ac78ba3"
|
||||
},
|
||||
"types": {
|
||||
"PermitWitnessTransferFrom": [
|
||||
{
|
||||
"name": "permitted",
|
||||
"type": "TokenPermissions"
|
||||
},
|
||||
{
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "nonce",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "deadline",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "witness",
|
||||
"type": "ExclusiveDutchOrder"
|
||||
}
|
||||
],
|
||||
"TokenPermissions": [
|
||||
{
|
||||
"name": "token",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"ExclusiveDutchOrder": [
|
||||
{
|
||||
"name": "info",
|
||||
"type": "OrderInfo"
|
||||
},
|
||||
{
|
||||
"name": "decayStartTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "decayEndTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "exclusiveFiller",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "exclusivityOverrideBps",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "inputToken",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "inputStartAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "inputEndAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "outputs",
|
||||
"type": "DutchOutput[]"
|
||||
}
|
||||
],
|
||||
"OrderInfo": [
|
||||
{
|
||||
"name": "reactor",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "swapper",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "nonce",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "deadline",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "additionalValidationContract",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "additionalValidationData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"DutchOutput": [
|
||||
{
|
||||
"name": "token",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "startAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "endAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "recipient",
|
||||
"type": "address"
|
||||
}
|
||||
]
|
||||
},
|
||||
"values": {
|
||||
"permitted": {
|
||||
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"amount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x11e1a300"
|
||||
}
|
||||
},
|
||||
"spender": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
|
||||
"nonce": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x7ec2ff20796a08922e11fd828e3871a6aa9a80e6495e30cd41be24b7e3795300"
|
||||
},
|
||||
"deadline": 1690902198,
|
||||
"witness": {
|
||||
"info": {
|
||||
"reactor": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
|
||||
"swapper": "0x67d615D6bccAA1562B1cca9786384b4840597ecD",
|
||||
"nonce": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x7ec2ff20796a08922e11fd828e3871a6aa9a80e6495e30cd41be24b7e3795300"
|
||||
},
|
||||
"deadline": 1690902198,
|
||||
"additionalValidationContract": "0x0000000000000000000000000000000000000000",
|
||||
"additionalValidationData": "0x"
|
||||
},
|
||||
"decayStartTime": 1690902126,
|
||||
"decayEndTime": 1690902186,
|
||||
"exclusiveFiller": "0x0000000000000000000000000000000000000000",
|
||||
"exclusivityOverrideBps": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x00"
|
||||
},
|
||||
"inputToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"inputStartAmount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x11e1a300"
|
||||
},
|
||||
"inputEndAmount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x11e1a300"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"token": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
"startAmount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x0fb7e15027ad3e025e"
|
||||
},
|
||||
"endAmount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x0e7a33be508bb395a2"
|
||||
},
|
||||
"recipient": "0x67d615D6bccAA1562B1cca9786384b4840597ecD"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"routing": "CLASSIC",
|
||||
"quote": {
|
||||
"blockNumber": "17820918",
|
||||
"amount": "300000000",
|
||||
"amountDecimals": "300",
|
||||
"quote": "299952256425393549464",
|
||||
"quoteDecimals": "299.952256425393549464",
|
||||
"quoteGasAdjusted": "289922128602824170541",
|
||||
"quoteGasAdjustedDecimals": "289.922128602824170541",
|
||||
"gasUseEstimateQuote": "10030127822569378922",
|
||||
"gasUseEstimateQuoteDecimals": "10.030127822569378922",
|
||||
"gasUseEstimate": "128000",
|
||||
"gasUseEstimateUSD": "10.031724",
|
||||
"simulationStatus": "UNATTEMPTED",
|
||||
"simulationError": false,
|
||||
"gasPriceWei": "42803167855",
|
||||
"route": [
|
||||
[
|
||||
{
|
||||
"type": "v3-pool",
|
||||
"address": "0x5777d92f208679DB4b9778590Fa3CAB3aC9e2168",
|
||||
"tokenIn": {
|
||||
"chainId": 1,
|
||||
"decimals": "6",
|
||||
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"symbol": "USDC"
|
||||
},
|
||||
"tokenOut": {
|
||||
"chainId": 1,
|
||||
"decimals": "18",
|
||||
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
"symbol": "DAI"
|
||||
},
|
||||
"fee": "100",
|
||||
"liquidity": "534676532046235168447130",
|
||||
"sqrtRatioX96": "79230505815006815109584",
|
||||
"tickCurrent": "-276324",
|
||||
"amountIn": "300000000",
|
||||
"amountOut": "299952256425393549464"
|
||||
}
|
||||
]
|
||||
],
|
||||
"routeString": "[V3] 100.00% = USDC -- 0.01% [0x5777d92f208679DB4b9778590Fa3CAB3aC9e2168] --> DAI",
|
||||
"quoteId": "1dd3bd14-780e-41c6-88e1-30a763f97482",
|
||||
"requestId": "2d16f993-6429-4755-ba50-1383789459dc",
|
||||
"tradeType": "EXACT_INPUT",
|
||||
"slippage": 0.5
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
491
cypress/fixtures/uniswapx/quote2.json
Normal file
491
cypress/fixtures/uniswapx/quote2.json
Normal file
@@ -0,0 +1,491 @@
|
||||
{
|
||||
"routing": "DUTCH_LIMIT",
|
||||
"quote": {
|
||||
"orderInfo": {
|
||||
"chainId": 1,
|
||||
"permit2Address": "0x000000000022d473030f116ddee9f6b43ac78ba3",
|
||||
"reactor": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
|
||||
"swapper": "0x0000000000000000000000000000000000000000",
|
||||
"nonce": "1993350209834725680308575292465150260730647098062962750049345504775310970881",
|
||||
"deadline": 1691176812,
|
||||
"additionalValidationContract": "0x0000000000000000000000000000000000000000",
|
||||
"additionalValidationData": "0x",
|
||||
"decayStartTime": 1691176740,
|
||||
"decayEndTime": 1691176800,
|
||||
"exclusiveFiller": "0x165D98de005d2818176B99B1A93b9325dBE58181",
|
||||
"exclusivityOverrideBps": "100",
|
||||
"input": {
|
||||
"token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
||||
"startAmount": "1000000000000000000",
|
||||
"endAmount": "1000000000000000000"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"token": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
"startAmount": "929502510517534478575",
|
||||
"endAmount": "919795986077127665276",
|
||||
"recipient": "0x0000000000000000000000000000000000000000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"encodedOrder": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000064cd4f240000000000000000000000000000000000000000000000000000000064cd4f60000000000000000000000000165d98de005d2818176b99b1a93b9325dbe581810000000000000000000000000000000000000000000000000000000000000064000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000006000da47483062a0d734ba3dc7576ce6a0b645c400000000000000000000000000000000000000000000000000000000000000000468323c9682990e3dc0646f899b437e62fbfb52a63cc8de721280222d8090010000000000000000000000000000000000000000000000000000000064cd4f6c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff500000000000000000000000000000000000000000000003263704899af6e50ef000000000000000000000000000000000000000000000031dcbbc80c9555e67c0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"quoteId": "09ce28b7-1ddf-4317-a28d-d21092be9f84",
|
||||
"requestId": "f00535d4-461a-4363-afbe-7a5ab7061cd1",
|
||||
"auctionPeriodSecs": 60,
|
||||
"deadlineBufferSecs": 12,
|
||||
"slippageTolerance": "0.5",
|
||||
"permitData": {
|
||||
"domain": {
|
||||
"name": "Permit2",
|
||||
"chainId": 1,
|
||||
"verifyingContract": "0x000000000022d473030f116ddee9f6b43ac78ba3"
|
||||
},
|
||||
"types": {
|
||||
"PermitWitnessTransferFrom": [
|
||||
{
|
||||
"name": "permitted",
|
||||
"type": "TokenPermissions"
|
||||
},
|
||||
{
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "nonce",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "deadline",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "witness",
|
||||
"type": "ExclusiveDutchOrder"
|
||||
}
|
||||
],
|
||||
"TokenPermissions": [
|
||||
{
|
||||
"name": "token",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"ExclusiveDutchOrder": [
|
||||
{
|
||||
"name": "info",
|
||||
"type": "OrderInfo"
|
||||
},
|
||||
{
|
||||
"name": "decayStartTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "decayEndTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "exclusiveFiller",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "exclusivityOverrideBps",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "inputToken",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "inputStartAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "inputEndAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "outputs",
|
||||
"type": "DutchOutput[]"
|
||||
}
|
||||
],
|
||||
"OrderInfo": [
|
||||
{
|
||||
"name": "reactor",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "swapper",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "nonce",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "deadline",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "additionalValidationContract",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "additionalValidationData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"DutchOutput": [
|
||||
{
|
||||
"name": "token",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "startAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "endAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "recipient",
|
||||
"type": "address"
|
||||
}
|
||||
]
|
||||
},
|
||||
"values": {
|
||||
"permitted": {
|
||||
"token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
||||
"amount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x0de0b6b3a7640000"
|
||||
}
|
||||
},
|
||||
"spender": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
|
||||
"nonce": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x0468323c9682990e3dc0646f899b437e62fbfb52a63cc8de721280222d809001"
|
||||
},
|
||||
"deadline": 1691176812,
|
||||
"witness": {
|
||||
"info": {
|
||||
"reactor": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
|
||||
"swapper": "0x0000000000000000000000000000000000000000",
|
||||
"nonce": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x0468323c9682990e3dc0646f899b437e62fbfb52a63cc8de721280222d809001"
|
||||
},
|
||||
"deadline": 1691176812,
|
||||
"additionalValidationContract": "0x0000000000000000000000000000000000000000",
|
||||
"additionalValidationData": "0x"
|
||||
},
|
||||
"decayStartTime": 1691176740,
|
||||
"decayEndTime": 1691176800,
|
||||
"exclusiveFiller": "0x165D98de005d2818176B99B1A93b9325dBE58181",
|
||||
"exclusivityOverrideBps": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x64"
|
||||
},
|
||||
"inputToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
||||
"inputStartAmount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x0de0b6b3a7640000"
|
||||
},
|
||||
"inputEndAmount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x0de0b6b3a7640000"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"token": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
"startAmount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x3263704899af6e50ef"
|
||||
},
|
||||
"endAmount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x31dcbbc80c9555e67c"
|
||||
},
|
||||
"recipient": "0x0000000000000000000000000000000000000000"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"requestId": "f00535d4-461a-4363-afbe-7a5ab7061cd1",
|
||||
"allQuotes": [
|
||||
{
|
||||
"routing": "DUTCH_LIMIT",
|
||||
"quote": {
|
||||
"orderInfo": {
|
||||
"chainId": 1,
|
||||
"permit2Address": "0x000000000022d473030f116ddee9f6b43ac78ba3",
|
||||
"reactor": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
|
||||
"swapper": "0x0000000000000000000000000000000000000000",
|
||||
"nonce": "1993350209834725680308575292465150260730647098062962750049345504775310970881",
|
||||
"deadline": 1691176812,
|
||||
"additionalValidationContract": "0x0000000000000000000000000000000000000000",
|
||||
"additionalValidationData": "0x",
|
||||
"decayStartTime": 1691176740,
|
||||
"decayEndTime": 1691176800,
|
||||
"exclusiveFiller": "0x165D98de005d2818176B99B1A93b9325dBE58181",
|
||||
"exclusivityOverrideBps": "100",
|
||||
"input": {
|
||||
"token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
||||
"startAmount": "1000000000000000000",
|
||||
"endAmount": "1000000000000000000"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"token": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
"startAmount": "929502510517534478575",
|
||||
"endAmount": "919795986077127665276",
|
||||
"recipient": "0x0000000000000000000000000000000000000000"
|
||||
}
|
||||
]
|
||||
},
|
||||
"encodedOrder": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000064cd4f240000000000000000000000000000000000000000000000000000000064cd4f60000000000000000000000000165d98de005d2818176b99b1a93b9325dbe581810000000000000000000000000000000000000000000000000000000000000064000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000006000da47483062a0d734ba3dc7576ce6a0b645c400000000000000000000000000000000000000000000000000000000000000000468323c9682990e3dc0646f899b437e62fbfb52a63cc8de721280222d8090010000000000000000000000000000000000000000000000000000000064cd4f6c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000092d6c1e31e14520e676a687f0a93788b716beff500000000000000000000000000000000000000000000003263704899af6e50ef000000000000000000000000000000000000000000000031dcbbc80c9555e67c0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"quoteId": "09ce28b7-1ddf-4317-a28d-d21092be9f84",
|
||||
"requestId": "f00535d4-461a-4363-afbe-7a5ab7061cd1",
|
||||
"auctionPeriodSecs": 60,
|
||||
"deadlineBufferSecs": 12,
|
||||
"slippageTolerance": "0.5",
|
||||
"permitData": {
|
||||
"domain": {
|
||||
"name": "Permit2",
|
||||
"chainId": 1,
|
||||
"verifyingContract": "0x000000000022d473030f116ddee9f6b43ac78ba3"
|
||||
},
|
||||
"types": {
|
||||
"PermitWitnessTransferFrom": [
|
||||
{
|
||||
"name": "permitted",
|
||||
"type": "TokenPermissions"
|
||||
},
|
||||
{
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "nonce",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "deadline",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "witness",
|
||||
"type": "ExclusiveDutchOrder"
|
||||
}
|
||||
],
|
||||
"TokenPermissions": [
|
||||
{
|
||||
"name": "token",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"ExclusiveDutchOrder": [
|
||||
{
|
||||
"name": "info",
|
||||
"type": "OrderInfo"
|
||||
},
|
||||
{
|
||||
"name": "decayStartTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "decayEndTime",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "exclusiveFiller",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "exclusivityOverrideBps",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "inputToken",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "inputStartAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "inputEndAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "outputs",
|
||||
"type": "DutchOutput[]"
|
||||
}
|
||||
],
|
||||
"OrderInfo": [
|
||||
{
|
||||
"name": "reactor",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "swapper",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "nonce",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "deadline",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "additionalValidationContract",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "additionalValidationData",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"DutchOutput": [
|
||||
{
|
||||
"name": "token",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"name": "startAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "endAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "recipient",
|
||||
"type": "address"
|
||||
}
|
||||
]
|
||||
},
|
||||
"values": {
|
||||
"permitted": {
|
||||
"token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
||||
"amount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x0de0b6b3a7640000"
|
||||
}
|
||||
},
|
||||
"spender": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
|
||||
"nonce": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x0468323c9682990e3dc0646f899b437e62fbfb52a63cc8de721280222d809001"
|
||||
},
|
||||
"deadline": 1691176812,
|
||||
"witness": {
|
||||
"info": {
|
||||
"reactor": "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4",
|
||||
"swapper": "0x0000000000000000000000000000000000000000",
|
||||
"nonce": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x0468323c9682990e3dc0646f899b437e62fbfb52a63cc8de721280222d809001"
|
||||
},
|
||||
"deadline": 1691176812,
|
||||
"additionalValidationContract": "0x0000000000000000000000000000000000000000",
|
||||
"additionalValidationData": "0x"
|
||||
},
|
||||
"decayStartTime": 1691176740,
|
||||
"decayEndTime": 1691176800,
|
||||
"exclusiveFiller": "0x165D98de005d2818176B99B1A93b9325dBE58181",
|
||||
"exclusivityOverrideBps": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x64"
|
||||
},
|
||||
"inputToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
||||
"inputStartAmount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x0de0b6b3a7640000"
|
||||
},
|
||||
"inputEndAmount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x0de0b6b3a7640000"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"token": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
"startAmount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x3263704899af6e50ef"
|
||||
},
|
||||
"endAmount": {
|
||||
"type": "BigNumber",
|
||||
"hex": "0x31dcbbc80c9555e67c"
|
||||
},
|
||||
"recipient": "0x0000000000000000000000000000000000000000"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"routing": "CLASSIC",
|
||||
"quote": {
|
||||
"blockNumber": "17843654",
|
||||
"amount": "1000000000000000000",
|
||||
"amountDecimals": "1",
|
||||
"quote": "931181529570145926787",
|
||||
"quoteDecimals": "931.181529570145926787",
|
||||
"quoteGasAdjusted": "929033336026294051828",
|
||||
"quoteGasAdjustedDecimals": "929.033336026294051828",
|
||||
"gasUseEstimateQuote": "2148193543851874958",
|
||||
"gasUseEstimateQuoteDecimals": "2.148193543851874958",
|
||||
"gasUseEstimate": "128000",
|
||||
"gasUseEstimateUSD": "4.174934",
|
||||
"simulationStatus": "UNATTEMPTED",
|
||||
"simulationError": false,
|
||||
"gasPriceWei": "17811260539",
|
||||
"route": [
|
||||
[
|
||||
{
|
||||
"type": "v3-pool",
|
||||
"address": "0xD8de6af55F618a7Bc69835D55DDC6582220c36c0",
|
||||
"tokenIn": {
|
||||
"chainId": 1,
|
||||
"decimals": "18",
|
||||
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
||||
"symbol": "WETH"
|
||||
},
|
||||
"tokenOut": {
|
||||
"chainId": 1,
|
||||
"decimals": "18",
|
||||
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
"symbol": "DAI"
|
||||
},
|
||||
"fee": "3000",
|
||||
"liquidity": "62287359628325896425115",
|
||||
"sqrtRatioX96": "2591813593283507889384697884",
|
||||
"tickCurrent": "-68403",
|
||||
"amountIn": "1000000000000000000",
|
||||
"amountOut": "931181529570145926787"
|
||||
}
|
||||
]
|
||||
],
|
||||
"routeString": "[V3] 100.00% = WETH -- 0.3% [0xD8de6af55F618a7Bc69835D55DDC6582220c36c0] --> DAI",
|
||||
"quoteId": "414e5f1c-120a-4e35-9760-c54d4b09e91d",
|
||||
"requestId": "f00535d4-461a-4363-afbe-7a5ab7061cd1",
|
||||
"tradeType": "EXACT_INPUT",
|
||||
"slippage": 0.5
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -12,7 +12,7 @@ describe('translations', () => {
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('wallet-settings')).click()
|
||||
cy.get(getTestSelector('wallet-language-item')).contains('français').click({ force: true })
|
||||
cy.location('search').should('match', /\?lng=fr-FR$/)
|
||||
cy.location('hash').should('match', /\?lng=fr-FR$/)
|
||||
cy.contains('Échanger')
|
||||
cy.contains('Uniswap disponible en : English')
|
||||
})
|
||||
|
||||
@@ -3,8 +3,8 @@ import 'cypress-hardhat/lib/browser'
|
||||
import { Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
|
||||
|
||||
import { FeatureFlag } from '../../src/featureFlags'
|
||||
import { UserState } from '../../src/state/user/reducer'
|
||||
import { CONNECTED_WALLET_USER_STATE } from '../utils/user-state'
|
||||
import { initialState, UserState } from '../../src/state/user/reducer'
|
||||
import { CONNECTED_WALLET_USER_STATE, setInitialUserState } from '../utils/user-state'
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
@@ -54,14 +54,12 @@ Cypress.Commands.overwrite(
|
||||
onBeforeLoad(win) {
|
||||
options?.onBeforeLoad?.(win)
|
||||
|
||||
// We want to test from a clean state, so we clear the local storage (which clears redux).
|
||||
win.localStorage.clear()
|
||||
|
||||
// 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 ?? {}) })
|
||||
)
|
||||
setInitialUserState(win, {
|
||||
...initialState,
|
||||
hideUniswapWalletBanner: true,
|
||||
...CONNECTED_WALLET_USER_STATE,
|
||||
...(options?.userState ?? {}),
|
||||
})
|
||||
|
||||
// Set feature flags, if configured.
|
||||
if (options?.featureFlags) {
|
||||
|
||||
@@ -4,3 +4,31 @@ import { UserState } from '../../src/state/user/reducer'
|
||||
export const CONNECTED_WALLET_USER_STATE: Partial<UserState> = { selectedWallet: ConnectionType.INJECTED }
|
||||
|
||||
export const DISCONNECTED_WALLET_USER_STATE: Partial<UserState> = { selectedWallet: undefined }
|
||||
|
||||
/**
|
||||
* This sets the initial value of the "user" slice in IndexedDB.
|
||||
* Other persisted slices are not set, so they will be filled with their respective initial values
|
||||
* when the app runs.
|
||||
*/
|
||||
export function setInitialUserState(win: Cypress.AUTWindow, initialUserState: any) {
|
||||
win.indexedDB.deleteDatabase('redux')
|
||||
|
||||
const dbRequest = win.indexedDB.open('redux')
|
||||
|
||||
dbRequest.onsuccess = function () {
|
||||
const db = dbRequest.result
|
||||
const transaction = db.transaction('keyvaluepairs', 'readwrite')
|
||||
const store = transaction.objectStore('keyvaluepairs')
|
||||
store.put(
|
||||
{
|
||||
user: initialUserState,
|
||||
},
|
||||
'persist:interface'
|
||||
)
|
||||
}
|
||||
|
||||
dbRequest.onupgradeneeded = function () {
|
||||
const db = dbRequest.result
|
||||
db.createObjectStore('keyvaluepairs')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export const onRequest: PagesFunction = async ({ params, request }) => {
|
||||
return new Response('Asset not found.', { status: 404 })
|
||||
}
|
||||
|
||||
const fontData = await getFont()
|
||||
const fontData = await getFont(origin)
|
||||
|
||||
return new ImageResponse(
|
||||
(
|
||||
|
||||
@@ -10,7 +10,7 @@ test.each(assetImageUrl)('assetImageUrl', async (url) => {
|
||||
})
|
||||
|
||||
const invalidAssetImageUrl = [
|
||||
'http://127.0.0.1:3000/api/image/nfts/asset/0xed5af388653567af2f388e6224dc7c4b3241c544/100000',
|
||||
'http://127.0.0.1:3000/api/image/nfts/asset/0xed5af388653567af2f388e6224dc7c4b3241c544/10001',
|
||||
'http://127.0.0.1:3000/api/image/nfts/asset/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d/44700',
|
||||
]
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@ export const onRequest: PagesFunction = async ({ params, request }) => {
|
||||
)
|
||||
|
||||
if (!data) {
|
||||
return new Response('Asset not found.', { status: 404 })
|
||||
return new Response('Collection not found.', { status: 404 })
|
||||
}
|
||||
|
||||
const [fontData, palette] = await Promise.all([getFont(), getColor(data.ogImage)])
|
||||
const [fontData, palette] = await Promise.all([getFont(origin), getColor(data.ogImage)])
|
||||
|
||||
// Split name into words to wrap them since satori does not support inline text wrapping
|
||||
const words = data.name.split(' ')
|
||||
|
||||
@@ -25,12 +25,12 @@ export const onRequest: PagesFunction = async ({ params, request }) => {
|
||||
)
|
||||
|
||||
if (!data) {
|
||||
return new Response('Asset not found.', { status: 404 })
|
||||
return new Response('Token not found.', { status: 404 })
|
||||
}
|
||||
|
||||
const [fontData, palette] = await Promise.all([getFont(), getColor(data.ogImage)])
|
||||
const [fontData, palette] = await Promise.all([getFont(origin), getColor(data.ogImage, true)])
|
||||
|
||||
const networkLogo = getNetworkLogoUrl(networkName.toUpperCase())
|
||||
const networkLogo = getNetworkLogoUrl(networkName.toUpperCase(), origin)
|
||||
|
||||
// Capitalize name such that each word starts with a capital letter
|
||||
let words = data.name.split(' ')
|
||||
@@ -123,7 +123,7 @@ export const onRequest: PagesFunction = async ({ params, request }) => {
|
||||
style={{
|
||||
fontFamily: 'Inter',
|
||||
fontSize: '72px',
|
||||
lineHeight: '58px',
|
||||
lineHeight: '72px',
|
||||
marginLeft: '-5px',
|
||||
marginTop: '24px',
|
||||
}}
|
||||
@@ -145,6 +145,10 @@ export const onRequest: PagesFunction = async ({ params, request }) => {
|
||||
fontSize: '168px',
|
||||
lineHeight: '133px',
|
||||
marginLeft: '-13px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{data.symbol}
|
||||
|
||||
@@ -13,7 +13,6 @@ const invalidTokenImageUrl = [
|
||||
'http://127.0.0.1:3000/api/image/tokens/ethereum/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb49',
|
||||
'http://127.0.0.1:3000/api/image/tokens/ethereum',
|
||||
'http://127.0.0.1:3000/api/image/tokens/ethereun',
|
||||
'http://127.0.0.1:3000/api/image/tokens/ethereum/0x0',
|
||||
'http://127.0.0.1:3000/api/image/tokens/potato/?potato=1',
|
||||
]
|
||||
|
||||
|
||||
38
functions/components/metaTagInjector.test.ts
Normal file
38
functions/components/metaTagInjector.test.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { MetaTagInjector } from './metaTagInjector'
|
||||
|
||||
test('should append meta tag to element', () => {
|
||||
const element = {
|
||||
append: jest.fn(),
|
||||
} as unknown as Element
|
||||
const property = 'property'
|
||||
const content = 'content'
|
||||
const injector = new MetaTagInjector({
|
||||
title: 'test',
|
||||
url: 'testUrl',
|
||||
image: 'testImage',
|
||||
description: 'testDescription',
|
||||
})
|
||||
injector.append(element, property, content)
|
||||
expect(element.append).toHaveBeenCalledWith(`<meta property="${property}" content="${content}"/>`, { html: true })
|
||||
|
||||
injector.element(element)
|
||||
expect(element.append).toHaveBeenCalledWith(`<meta property="og:title" content="test"/>`, { html: true })
|
||||
expect(element.append).toHaveBeenCalledWith(`<meta property="og:description" content="testDescription"/>`, {
|
||||
html: true,
|
||||
})
|
||||
expect(element.append).toHaveBeenCalledWith(`<meta property="og:image" content="testImage"/>`, { html: true })
|
||||
expect(element.append).toHaveBeenCalledWith(`<meta property="og:image:width" content="1200"/>`, { html: true })
|
||||
expect(element.append).toHaveBeenCalledWith(`<meta property="og:image:height" content="630"/>`, { html: true })
|
||||
expect(element.append).toHaveBeenCalledWith(`<meta property="og:image:alt" content="test"/>`, { html: true })
|
||||
expect(element.append).toHaveBeenCalledWith(`<meta property="og:type" content="website"/>`, { html: true })
|
||||
expect(element.append).toHaveBeenCalledWith(`<meta property="og:url" content="testUrl"/>`, { html: true })
|
||||
|
||||
expect(element.append).toHaveBeenCalledWith(`<meta property="twitter:card" content="summary_large_image"/>`, {
|
||||
html: true,
|
||||
})
|
||||
expect(element.append).toHaveBeenCalledWith(`<meta property="twitter:title" content="test"/>`, { html: true })
|
||||
expect(element.append).toHaveBeenCalledWith(`<meta property="twitter:image" content="testImage"/>`, { html: true })
|
||||
expect(element.append).toHaveBeenCalledWith(`<meta property="twitter:image:alt" content="test"/>`, { html: true })
|
||||
|
||||
expect(element.append).toHaveBeenCalledTimes(13)
|
||||
})
|
||||
@@ -4,7 +4,7 @@ module.exports = async function globalSetup() {
|
||||
globalThis.servers = await setup({
|
||||
command: `yarn start:cloud`,
|
||||
port: 3000,
|
||||
launchTimeout: 80000,
|
||||
launchTimeout: 120000, // takes ~2m on CI
|
||||
})
|
||||
// Wait for wrangler to return a request before running tests
|
||||
for (let i = 0; i < 3; i++) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"globalSetup": "<rootDir>/global-setup.ts",
|
||||
"globalTeardown": "<rootDir>/global-teardown.ts",
|
||||
"setupFilesAfterEnv": ["<rootDir>/setupAfterEnv.ts"],
|
||||
"preset": "ts-jest",
|
||||
"transform": {
|
||||
"'^.+\\.(ts|tsx)?$'": "ts-jest",
|
||||
|
||||
1
functions/setupAfterEnv.ts
Normal file
1
functions/setupAfterEnv.ts
Normal file
@@ -0,0 +1 @@
|
||||
jest.retryTimes(3)
|
||||
@@ -19,6 +19,12 @@ test('should return the average color of a white PNG image', async () => {
|
||||
expect(color).toEqual([255, 255, 255])
|
||||
})
|
||||
|
||||
test('should return the average color of a white PNG image with whiteness dimmed', async () => {
|
||||
const image = 'https://www.cac.cornell.edu/wiki/images/4/44/White_square.png'
|
||||
const color = await getColor(image, true)
|
||||
expect(color).toEqual(DEFAULT_COLOR)
|
||||
})
|
||||
|
||||
test('should return the average color of a black JPG image', async () => {
|
||||
const image =
|
||||
'https://imageio.forbes.com/specials-images/imageserve/5ed6636cdd5d320006caf841/0x0.jpg?format=jpg&width=1200'
|
||||
|
||||
@@ -4,7 +4,7 @@ import PNG from 'png-ts'
|
||||
|
||||
import { DEFAULT_COLOR, predefinedTokenColors } from '../constants'
|
||||
|
||||
export default async function getColor(image: string | undefined) {
|
||||
export default async function getColor(image: string | undefined, checkDistance = false) {
|
||||
if (!image) {
|
||||
return DEFAULT_COLOR
|
||||
}
|
||||
@@ -17,13 +17,13 @@ export default async function getColor(image: string | undefined) {
|
||||
const arrayBuffer = Buffer.from(buffer)
|
||||
|
||||
const type = data.headers.get('content-type') ?? ''
|
||||
return getAverageColor(arrayBuffer, type)
|
||||
return getAverageColor(arrayBuffer, type, checkDistance)
|
||||
} catch (e) {
|
||||
return DEFAULT_COLOR
|
||||
}
|
||||
}
|
||||
|
||||
function getAverageColor(arrayBuffer: Uint8Array, type?: string) {
|
||||
function getAverageColor(arrayBuffer: Uint8Array, type: string, checkDistance: boolean) {
|
||||
let pixels
|
||||
switch (type) {
|
||||
case 'image/png': {
|
||||
@@ -63,5 +63,13 @@ function getAverageColor(arrayBuffer: Uint8Array, type?: string) {
|
||||
g = Math.floor(g / (pixelCount - transparentPixels))
|
||||
b = Math.floor(b / (pixelCount - transparentPixels))
|
||||
|
||||
if (checkDistance) {
|
||||
const distance = Math.sqrt(Math.pow(r - 255, 2) + Math.pow(g - 255, 2) + Math.pow(b - 255, 2))
|
||||
|
||||
if (distance < 50) {
|
||||
return DEFAULT_COLOR
|
||||
}
|
||||
}
|
||||
|
||||
return [r, g, b]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
const FONT_URL = 'https://fonts.gstatic.com/s/inter/v12/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuI6fAZFhjQ.ttf'
|
||||
|
||||
export default async function getFont() {
|
||||
const font = await fetch(FONT_URL)
|
||||
export default async function getFont(origin: string) {
|
||||
const url = origin + '/fonts/Inter-normal.var.ttf'
|
||||
const font = await fetch(url)
|
||||
return font.arrayBuffer()
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { Chain } from '../../src/graphql/data/__generated__/types-and-hooks'
|
||||
|
||||
export default function getNetworkLogoUrl(network: string) {
|
||||
export default function getNetworkLogoUrl(network: string, origin: string) {
|
||||
switch (network) {
|
||||
case Chain.Polygon:
|
||||
return 'https://assets.coingecko.com/coins/images/4713/small/matic-token-icon.png?1624446912'
|
||||
return origin + '/images/logos/Polygon_Logo.png'
|
||||
case Chain.Arbitrum:
|
||||
return 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/arbitrum/assets/0x912CE59144191C1204E64559FE8253a0e49E6548/logo.png'
|
||||
return origin + '/images/logos/Arbitrum_Logo.png'
|
||||
case Chain.Optimism:
|
||||
return 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/optimism/assets/0x4200000000000000000000000000000000000042/logo.png'
|
||||
return origin + '/images/logos/Optimism_Logo.png'
|
||||
case Chain.Celo:
|
||||
return 'https://assets.coingecko.com/coins/images/11090/small/InjXBNx9_400x400.jpg?1674707499'
|
||||
return origin + '/images/logos/Celo_Logo.png'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
|
||||
44
package.json
44
package.json
@@ -33,6 +33,16 @@
|
||||
"deduplicate": "yarn-deduplicate --strategy=highest",
|
||||
"postinstall": "yarn patch-package"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"yarn.lock": [
|
||||
"yarn deduplicate"
|
||||
]
|
||||
},
|
||||
"jest": {
|
||||
"collectCoverageFrom": [
|
||||
"src/**/*.ts*",
|
||||
@@ -84,7 +94,7 @@
|
||||
"@types/array.prototype.flat": "^1.2.1",
|
||||
"@types/array.prototype.flatmap": "^1.2.2",
|
||||
"@types/d3": "^6.7.1",
|
||||
"@types/jest": "^25.2.1",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/lingui__core": "^2.7.1",
|
||||
"@types/lingui__macro": "^2.7.4",
|
||||
"@types/lingui__react": "^2.8.3",
|
||||
@@ -95,6 +105,7 @@
|
||||
"@types/react": "^18.0.15",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/react-redux": "^7.1.24",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/react-table": "^7.7.12",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.0",
|
||||
"@types/react-window": "^1.8.2",
|
||||
@@ -121,6 +132,7 @@
|
||||
"eslint-plugin-import": "^2.27",
|
||||
"eslint-plugin-rulesdir": "^0.2.2",
|
||||
"hardhat": "^2.14.0",
|
||||
"husky": "^8.0.3",
|
||||
"jest": "^29.6.1",
|
||||
"jest-dev-server": "^9.0.0",
|
||||
"jest-extended": "^4.0.1",
|
||||
@@ -128,6 +140,7 @@
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"jest-styled-components": "^7.0.8",
|
||||
"jpeg-js": "^0.4.4",
|
||||
"lint-staged": "^14.0.0",
|
||||
"mini-css-extract-plugin": "^2.7.6",
|
||||
"patch-package": "^7.0.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
@@ -143,6 +156,7 @@
|
||||
"terser-webpack-plugin": "^5.3.9",
|
||||
"ts-jest": "^29.1.1",
|
||||
"ts-transform-graphql-tag": "^0.2.1",
|
||||
"tsafe": "^1.6.4",
|
||||
"typechain": "^5.0.0",
|
||||
"typescript": "^4.9.4",
|
||||
"webpack": "^5.88.2",
|
||||
@@ -177,8 +191,7 @@
|
||||
"@sentry/types": "^7.45.0",
|
||||
"@types/react-window-infinite-loader": "^1.0.6",
|
||||
"@uniswap/analytics": "^1.4.0",
|
||||
"@uniswap/analytics-events": "^2.15.0",
|
||||
"@uniswap/conedison": "^1.8.0",
|
||||
"@uniswap/analytics-events": "^2.17.0",
|
||||
"@uniswap/governance": "^1.0.2",
|
||||
"@uniswap/liquidity-staker": "^1.0.2",
|
||||
"@uniswap/merkle-distributor": "^1.0.1",
|
||||
@@ -188,7 +201,7 @@
|
||||
"@uniswap/sdk-core": "^4.0.3",
|
||||
"@uniswap/smart-order-router": "^3.15.0",
|
||||
"@uniswap/token-lists": "^1.0.0-beta.33",
|
||||
"@uniswap/uniswapx-sdk": "^1.2.0",
|
||||
"@uniswap/uniswapx-sdk": "^1.3.0",
|
||||
"@uniswap/universal-router-sdk": "^1.5.6",
|
||||
"@uniswap/v2-core": "^1.0.1",
|
||||
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
||||
@@ -207,16 +220,16 @@
|
||||
"@visx/react-spring": "^2.12.2",
|
||||
"@visx/responsive": "^2.10.0",
|
||||
"@visx/shape": "^2.11.1",
|
||||
"@web3-react/coinbase-wallet": "^8.2.0",
|
||||
"@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/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/coinbase-wallet": "^8.2.2",
|
||||
"@web3-react/core": "^8.2.2",
|
||||
"@web3-react/eip1193": "^8.2.2",
|
||||
"@web3-react/empty": "^8.2.2",
|
||||
"@web3-react/gnosis-safe": "^8.2.3",
|
||||
"@web3-react/metamask": "^8.2.3",
|
||||
"@web3-react/network": "^8.2.2",
|
||||
"@web3-react/types": "^8.2.2",
|
||||
"@web3-react/url": "^8.2.2",
|
||||
"@web3-react/walletconnect-v2": "^8.5.0",
|
||||
"ajv": "^8.11.0",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"array.prototype.flat": "^1.2.4",
|
||||
@@ -235,6 +248,7 @@
|
||||
"inter-ui": "^3.13.1",
|
||||
"jotai": "^1.3.7",
|
||||
"jsbi": "^3.1.4",
|
||||
"localforage": "^1.10.0",
|
||||
"make-plural": "^7.0.0",
|
||||
"ms": "^2.1.3",
|
||||
"multicodec": "^3.0.1",
|
||||
@@ -265,7 +279,7 @@
|
||||
"react-window-infinite-loader": "^1.0.8",
|
||||
"rebass": "^4.0.7",
|
||||
"redux": "^4.1.2",
|
||||
"redux-localstorage-simple": "^2.3.1",
|
||||
"redux-persist": "^6.0.0",
|
||||
"statsig-react": "^1.22.0",
|
||||
"styled-components": "^5.3.5",
|
||||
"tiny-invariant": "^1.2.0",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"namespace": "android_app",
|
||||
"package_name": "com.uniswap.dev",
|
||||
"sha256_cert_fingerprints":
|
||||
["A8:A7:D4:DE:46:8E:BE:F6:DE:3B:62:2B:A7:26:60:F2:9A:4C:CD:AF:A6:96:C9:E5:7C:91:68:A1:29:2A:48:D3", "02:E6:1C:76:8C:75:C3:78:C8:8C:FE:7B:2E:8F:4B:E1:FA:47:F2:F6:1A:DB:57:69:4A:41:99:C6:71:2C:AB:E3", "FA:C6:17:45:DC:09:03:78:6F:B9:ED:E6:2A:96:2B:39:9F:73:48:F0:BB:6F:89:9B:83:32:66:75:91:03:3B:9C"]
|
||||
["5A:6D:23:50:2F:1E:0D:01:DC:96:65:F3:3A:18:4C:4C:8C:67:E0:09:99:9B:B1:9B:BF:44:99:D0:D1:D0:FC:5E", "02:E6:1C:76:8C:75:C3:78:C8:8C:FE:7B:2E:8F:4B:E1:FA:47:F2:F6:1A:DB:57:69:4A:41:99:C6:71:2C:AB:E3", "FA:C6:17:45:DC:09:03:78:6F:B9:ED:E6:2A:96:2B:39:9F:73:48:F0:BB:6F:89:9B:83:32:66:75:91:03:3B:9C"]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -22,6 +22,22 @@
|
||||
{
|
||||
"#": "/address/*",
|
||||
"comment": "Wallet address"
|
||||
},
|
||||
{
|
||||
"/": "/nfts/asset/*",
|
||||
"comment": "NFT Item"
|
||||
},
|
||||
{
|
||||
"/": "/nfts/collection/*",
|
||||
"comment": "NFT Collection"
|
||||
},
|
||||
{
|
||||
"/": "/tokens/*",
|
||||
"comment": "Token address"
|
||||
},
|
||||
{
|
||||
"/": "/address/*",
|
||||
"comment": "Wallet address"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
BIN
public/fonts/Inter-normal.var.ttf
Normal file
BIN
public/fonts/Inter-normal.var.ttf
Normal file
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 22 KiB |
BIN
public/images/logos/Arbitrum_Logo.png
Normal file
BIN
public/images/logos/Arbitrum_Logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
public/images/logos/Celo_Logo.png
Normal file
BIN
public/images/logos/Celo_Logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
BIN
public/images/logos/Optimism_Logo.png
Normal file
BIN
public/images/logos/Optimism_Logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.3 KiB |
BIN
public/images/logos/Polygon_Logo.png
Normal file
BIN
public/images/logos/Polygon_Logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -6,7 +6,14 @@ import {
|
||||
import { atomWithStorage, useAtomValue } from 'jotai/utils'
|
||||
import { memo } from 'react'
|
||||
|
||||
export { getDeviceId, initializeAnalytics, OriginApplication, user, useTrace } from '@uniswap/analytics'
|
||||
export {
|
||||
type ITraceContext,
|
||||
getDeviceId,
|
||||
initializeAnalytics,
|
||||
OriginApplication,
|
||||
user,
|
||||
useTrace,
|
||||
} from '@uniswap/analytics'
|
||||
|
||||
const allowAnalyticsAtomKey = 'allow_analytics'
|
||||
export const allowAnalyticsAtom = atomWithStorage<boolean>(allowAnalyticsAtomKey, true)
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useCallback, useEffect, useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import AuthenticatedHeader from './AuthenticatedHeader'
|
||||
import LanguageMenu from './LanguageMenu'
|
||||
import SettingsMenu from './SettingsMenu'
|
||||
|
||||
const DefaultMenuWrap = styled(Column)`
|
||||
@@ -15,6 +16,7 @@ const DefaultMenuWrap = styled(Column)`
|
||||
enum MenuState {
|
||||
DEFAULT,
|
||||
SETTINGS,
|
||||
LANGUAGE_SETTINGS,
|
||||
}
|
||||
|
||||
function DefaultMenu({ drawerOpen }: { drawerOpen: boolean }) {
|
||||
@@ -24,9 +26,10 @@ function DefaultMenu({ drawerOpen }: { drawerOpen: boolean }) {
|
||||
const [menu, setMenu] = useState<MenuState>(MenuState.DEFAULT)
|
||||
const openSettings = useCallback(() => setMenu(MenuState.SETTINGS), [])
|
||||
const closeSettings = useCallback(() => setMenu(MenuState.DEFAULT), [])
|
||||
const openLanguageSettings = useCallback(() => setMenu(MenuState.LANGUAGE_SETTINGS), [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!drawerOpen && menu === MenuState.SETTINGS) {
|
||||
if (!drawerOpen && menu !== MenuState.DEFAULT) {
|
||||
// wait for the drawer to close before resetting the menu
|
||||
const timer = setTimeout(() => {
|
||||
closeSettings()
|
||||
@@ -44,7 +47,10 @@ function DefaultMenu({ drawerOpen }: { drawerOpen: boolean }) {
|
||||
) : (
|
||||
<WalletModal openSettings={openSettings} />
|
||||
))}
|
||||
{menu === MenuState.SETTINGS && <SettingsMenu onClose={closeSettings} />}
|
||||
{menu === MenuState.SETTINGS && (
|
||||
<SettingsMenu onClose={closeSettings} openLanguageSettings={openLanguageSettings} />
|
||||
)}
|
||||
{menu === MenuState.LANGUAGE_SETTINGS && <LanguageMenu onClose={openSettings} />}
|
||||
</DefaultMenuWrap>
|
||||
)
|
||||
}
|
||||
|
||||
56
src/components/AccountDrawer/LanguageMenu.tsx
Normal file
56
src/components/AccountDrawer/LanguageMenu.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { LOCALE_LABEL, SUPPORTED_LOCALES, SupportedLocale } from 'constants/locales'
|
||||
import { useActiveLocale } from 'hooks/useActiveLocale'
|
||||
import { useLocationLinkProps } from 'hooks/useLocationLinkProps'
|
||||
import { Check } from 'react-feather'
|
||||
import { Link } from 'react-router-dom'
|
||||
import styled, { useTheme } from 'styled-components'
|
||||
import { ClickableStyle, ThemedText } from 'theme'
|
||||
|
||||
import { SlideOutMenu } from './SlideOutMenu'
|
||||
|
||||
const InternalLinkMenuItem = styled(Link)`
|
||||
${ClickableStyle}
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
justify-content: space-between;
|
||||
text-decoration: none;
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
`
|
||||
|
||||
function LanguageMenuItem({ locale, isActive }: { locale: SupportedLocale; isActive: boolean }) {
|
||||
const { to, onClick } = useLocationLinkProps(locale)
|
||||
const theme = useTheme()
|
||||
|
||||
if (!to) return null
|
||||
|
||||
return (
|
||||
<InternalLinkMenuItem onClick={onClick} to={to}>
|
||||
<ThemedText.BodySmall data-testid="wallet-language-item">{LOCALE_LABEL[locale]}</ThemedText.BodySmall>
|
||||
{isActive && <Check color={theme.accentActive} opacity={1} size={20} />}
|
||||
</InternalLinkMenuItem>
|
||||
)
|
||||
}
|
||||
|
||||
export function LanguageMenuItems() {
|
||||
const activeLocale = useActiveLocale()
|
||||
|
||||
return (
|
||||
<>
|
||||
{SUPPORTED_LOCALES.map((locale) => (
|
||||
<LanguageMenuItem locale={locale} isActive={activeLocale === locale} key={locale} />
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default function LanguageMenu({ onClose }: { onClose: () => void }) {
|
||||
return (
|
||||
<SlideOutMenu title={<Trans>Language</Trans>} onClose={onClose}>
|
||||
<LanguageMenuItems />
|
||||
</SlideOutMenu>
|
||||
)
|
||||
}
|
||||
@@ -249,7 +249,7 @@ export function OffchainActivityModal() {
|
||||
|
||||
return (
|
||||
<Modal isOpen={!!syncedSelectedOrder?.modalOpen} onDismiss={reset}>
|
||||
<Wrapper>
|
||||
<Wrapper data-testid="offchain-activity-modal">
|
||||
<StyledXButton onClick={reset} />
|
||||
{syncedSelectedOrder && <OrderContent order={syncedSelectedOrder} />}
|
||||
</Wrapper>
|
||||
|
||||
@@ -86,7 +86,7 @@ export function useAllActivities(account: string) {
|
||||
return { loading, activities: combinedActivities, refetch }
|
||||
}
|
||||
|
||||
export function useHasPendingActivity() {
|
||||
export function usePendingActivity() {
|
||||
const pendingTransactions = usePendingTransactions()
|
||||
const pendingOrders = usePendingOrders()
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ export function ActivityTab({ account }: { account: string }) {
|
||||
<ThemedText.SubHeader color="textSecondary" marginLeft="16px">
|
||||
{activityGroup.title}
|
||||
</ThemedText.SubHeader>
|
||||
<Column>
|
||||
<Column data-testid="activity-content">
|
||||
{activityGroup.transactions.map((activity) => (
|
||||
<ActivityRow key={activity.hash} activity={activity} />
|
||||
))}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { TraceEvent } from 'analytics'
|
||||
import { useCachedPortfolioBalancesQuery } from 'components/AccountDrawer/PrefetchBalancesWrapper'
|
||||
import Row from 'components/Row'
|
||||
import { formatDelta } from 'components/Tokens/TokenDetails/PriceChart'
|
||||
import { PortfolioBalancesQuery } from 'graphql/data/__generated__/types-and-hooks'
|
||||
import { TokenBalance } from 'graphql/data/__generated__/types-and-hooks'
|
||||
import { getTokenDetailsURL, gqlToCurrency, logSentryErrorForUnsupportedChain } from 'graphql/data/util'
|
||||
import { useAtomValue } from 'jotai/utils'
|
||||
import { EmptyWalletModule } from 'nft/components/profile/view/EmptyWalletContent'
|
||||
@@ -12,6 +12,7 @@ import { useNavigate } from 'react-router-dom'
|
||||
import styled from 'styled-components'
|
||||
import { EllipsisStyle, ThemedText } from 'theme'
|
||||
import { formatNumber, NumberType } from 'utils/formatNumbers'
|
||||
import { splitHiddenTokens } from 'utils/splitHiddenTokens'
|
||||
|
||||
import { useToggleAccountDrawer } from '../..'
|
||||
import { PortfolioArrow } from '../../AuthenticatedHeader'
|
||||
@@ -20,12 +21,6 @@ import { ExpandoRow } from '../ExpandoRow'
|
||||
import { PortfolioLogo } from '../PortfolioLogo'
|
||||
import PortfolioRow, { PortfolioSkeleton, PortfolioTabWrapper } from '../PortfolioRow'
|
||||
|
||||
const HIDE_SMALL_USD_BALANCES_THRESHOLD = 1
|
||||
|
||||
function meetsThreshold(tokenBalance: TokenBalance, hideSmallBalances: boolean) {
|
||||
return !hideSmallBalances || (tokenBalance.denominatedValue?.value ?? 0) > HIDE_SMALL_USD_BALANCES_THRESHOLD
|
||||
}
|
||||
|
||||
export default function Tokens({ account }: { account: string }) {
|
||||
const toggleWalletDrawer = useToggleAccountDrawer()
|
||||
const hideSmallBalances = useAtomValue(hideSmallBalancesAtom)
|
||||
@@ -33,27 +28,18 @@ export default function Tokens({ account }: { account: string }) {
|
||||
|
||||
const { data } = useCachedPortfolioBalancesQuery({ account })
|
||||
|
||||
const visibleTokens = useMemo(() => {
|
||||
return !hideSmallBalances
|
||||
? data?.portfolios?.[0].tokenBalances ?? []
|
||||
: data?.portfolios?.[0].tokenBalances?.filter((tokenBalance) =>
|
||||
meetsThreshold(tokenBalance, hideSmallBalances)
|
||||
) ?? []
|
||||
}, [data?.portfolios, hideSmallBalances])
|
||||
const tokenBalances = data?.portfolios?.[0].tokenBalances as TokenBalance[] | undefined
|
||||
|
||||
const hiddenTokens = useMemo(() => {
|
||||
return !hideSmallBalances
|
||||
? []
|
||||
: data?.portfolios?.[0].tokenBalances?.filter(
|
||||
(tokenBalance) => !meetsThreshold(tokenBalance, hideSmallBalances)
|
||||
) ?? []
|
||||
}, [data?.portfolios, hideSmallBalances])
|
||||
const { visibleTokens, hiddenTokens } = useMemo(
|
||||
() => splitHiddenTokens(tokenBalances ?? [], { hideSmallBalances }),
|
||||
[hideSmallBalances, tokenBalances]
|
||||
)
|
||||
|
||||
if (!data) {
|
||||
return <PortfolioSkeleton />
|
||||
}
|
||||
|
||||
if (data?.portfolios?.[0].tokenBalances?.length === 0) {
|
||||
if (tokenBalances?.length === 0) {
|
||||
// TODO: consider launching moonpay here instead of just closing the drawer
|
||||
return <EmptyWalletModule type="token" onNavigateClick={toggleWalletDrawer} />
|
||||
}
|
||||
@@ -64,10 +50,7 @@ export default function Tokens({ account }: { account: string }) {
|
||||
<PortfolioTabWrapper>
|
||||
{visibleTokens.map(
|
||||
(tokenBalance) =>
|
||||
tokenBalance.token &&
|
||||
meetsThreshold(tokenBalance, hideSmallBalances) && (
|
||||
<TokenRow key={tokenBalance.id} {...tokenBalance} token={tokenBalance.token} />
|
||||
)
|
||||
tokenBalance.token && <TokenRow key={tokenBalance.id} {...tokenBalance} token={tokenBalance.token} />
|
||||
)}
|
||||
<ExpandoRow isExpanded={showHiddenTokens} toggle={toggleHiddenTokens} numItems={hiddenTokens.length}>
|
||||
{hiddenTokens.map(
|
||||
@@ -86,10 +69,6 @@ const TokenNameText = styled(ThemedText.SubHeader)`
|
||||
${EllipsisStyle}
|
||||
`
|
||||
|
||||
type TokenBalance = NonNullable<
|
||||
NonNullable<NonNullable<PortfolioBalancesQuery['portfolios']>[number]>['tokenBalances']
|
||||
>[number]
|
||||
|
||||
type PortfolioToken = NonNullable<TokenBalance['token']>
|
||||
|
||||
function TokenRow({ token, quantity, denominatedValue, tokenProjectMarket }: TokenBalance & { token: PortfolioToken }) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import styled, { useTheme } from 'styled-components'
|
||||
import { BREAKPOINTS, ThemedText } from 'theme'
|
||||
|
||||
import { ActivityTab } from './Activity'
|
||||
import { useHasPendingActivity } from './Activity/hooks'
|
||||
import { usePendingActivity } from './Activity/hooks'
|
||||
import NFTs from './NFTs'
|
||||
import Pools from './Pools'
|
||||
import { PortfolioRowWrapper } from './PortfolioRow'
|
||||
@@ -103,7 +103,7 @@ export default function MiniPortfolio({ account }: { account: string }) {
|
||||
|
||||
const { component: Page, key: currentKey } = Pages[currentPage]
|
||||
|
||||
const { hasPendingActivity } = useHasPendingActivity()
|
||||
const { hasPendingActivity } = usePendingActivity()
|
||||
|
||||
useEffect(() => {
|
||||
if (hasPendingActivity && currentKey !== 'activity') setActivityUnread(true)
|
||||
|
||||
@@ -3,36 +3,16 @@ import { usePortfolioBalancesLazyQuery, usePortfolioBalancesQuery } from 'graphq
|
||||
import { GQL_MAINNET_CHAINS } from 'graphql/data/util'
|
||||
import usePrevious from 'hooks/usePrevious'
|
||||
import { atom, useAtom } from 'jotai'
|
||||
import { PropsWithChildren, useCallback, useEffect, useMemo } from 'react'
|
||||
import { useAllTransactions } from 'state/transactions/hooks'
|
||||
import { TransactionDetails } from 'state/transactions/types'
|
||||
import { PropsWithChildren, useCallback, useEffect } from 'react'
|
||||
|
||||
const isTxPending = (tx: TransactionDetails) => !tx.receipt
|
||||
function wasPending(previousTxs: { [hash: string]: TransactionDetails | undefined }, current: TransactionDetails) {
|
||||
const previousTx = previousTxs[current.hash]
|
||||
return previousTx && isTxPending(previousTx)
|
||||
}
|
||||
import { usePendingActivity } from './MiniPortfolio/Activity/hooks'
|
||||
|
||||
function useHasUpdatedTx(account: string | undefined) {
|
||||
// TODO: consider monitoring tx's on chains other than the wallet's current chain
|
||||
const currentChainTxs = useAllTransactions()
|
||||
/** Returns true if the number of pending activities has decreased */
|
||||
function useHasUpdatedTx() {
|
||||
const { pendingActivityCount } = usePendingActivity()
|
||||
const prevPendingActivityCount = usePrevious(pendingActivityCount)
|
||||
|
||||
const pendingTxs = useMemo(() => {
|
||||
return Object.entries(currentChainTxs).reduce((acc: { [hash: string]: TransactionDetails }, [hash, tx]) => {
|
||||
if (!tx.receipt) acc[hash] = tx
|
||||
return acc
|
||||
}, {})
|
||||
}, [currentChainTxs])
|
||||
|
||||
const previousPendingTxs = usePrevious(pendingTxs)
|
||||
|
||||
return useMemo(() => {
|
||||
if (!previousPendingTxs || !account) return false
|
||||
return Object.values(currentChainTxs).some(
|
||||
(tx) => tx.from === account && !isTxPending(tx) && wasPending(previousPendingTxs, tx),
|
||||
[currentChainTxs, previousPendingTxs]
|
||||
)
|
||||
}, [account, currentChainTxs, previousPendingTxs])
|
||||
return !!prevPendingActivityCount && pendingActivityCount < prevPendingActivityCount
|
||||
}
|
||||
|
||||
export function useCachedPortfolioBalancesQuery({ account }: { account?: string }) {
|
||||
@@ -65,7 +45,7 @@ export default function PrefetchBalancesWrapper({
|
||||
|
||||
const prevAccount = usePrevious(account)
|
||||
|
||||
const hasUpdatedTx = useHasUpdatedTx(account)
|
||||
const hasUpdatedTx = useHasUpdatedTx()
|
||||
// Listens for account changes & recently updated transactions to keep portfolio balances fresh in apollo cache
|
||||
useEffect(() => {
|
||||
const accountChanged = prevAccount !== undefined && prevAccount !== account
|
||||
|
||||
@@ -1,46 +1,27 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { LOCALE_LABEL, SUPPORTED_LOCALES, SupportedLocale } from 'constants/locales'
|
||||
import Column from 'components/Column'
|
||||
import Row from 'components/Row'
|
||||
import { LOCALE_LABEL } from 'constants/locales'
|
||||
import { useCurrencyConversionFlagEnabled } from 'featureFlags/flags/currencyConversion'
|
||||
import { useActiveLocale } from 'hooks/useActiveLocale'
|
||||
import { useLocationLinkProps } from 'hooks/useLocationLinkProps'
|
||||
import { Check } from 'react-feather'
|
||||
import { Link } from 'react-router-dom'
|
||||
import styled, { useTheme } from 'styled-components'
|
||||
import { ReactNode } from 'react'
|
||||
import { ChevronRight } from 'react-feather'
|
||||
import styled from 'styled-components'
|
||||
import { ClickableStyle, ThemedText } from 'theme'
|
||||
import ThemeToggle from 'theme/components/ThemeToggle'
|
||||
|
||||
import { AnalyticsToggle } from './AnalyticsToggle'
|
||||
import { GitVersionRow } from './GitVersionRow'
|
||||
import { LanguageMenuItems } from './LanguageMenu'
|
||||
import { SlideOutMenu } from './SlideOutMenu'
|
||||
import { SmallBalanceToggle } from './SmallBalanceToggle'
|
||||
import { TestnetsToggle } from './TestnetsToggle'
|
||||
|
||||
const InternalLinkMenuItem = styled(Link)`
|
||||
${ClickableStyle}
|
||||
flex: 1;
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
const Container = styled(Column)`
|
||||
height: 100%;
|
||||
justify-content: space-between;
|
||||
text-decoration: none;
|
||||
color: ${({ theme }) => theme.textPrimary};
|
||||
`
|
||||
|
||||
function LanguageMenuItem({ locale, isActive }: { locale: SupportedLocale; isActive: boolean }) {
|
||||
const { to, onClick } = useLocationLinkProps(locale)
|
||||
const theme = useTheme()
|
||||
|
||||
if (!to) return null
|
||||
|
||||
return (
|
||||
<InternalLinkMenuItem onClick={onClick} to={to}>
|
||||
<ThemedText.BodySmall data-testid="wallet-language-item">{LOCALE_LABEL[locale]}</ThemedText.BodySmall>
|
||||
{isActive && <Check color={theme.accentActive} opacity={1} size={20} />}
|
||||
</InternalLinkMenuItem>
|
||||
)
|
||||
}
|
||||
|
||||
const SectionTitle = styled(ThemedText.SubHeader)`
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
padding-bottom: 24px;
|
||||
@@ -53,28 +34,81 @@ const ToggleWrapper = styled.div`
|
||||
margin-bottom: 24px;
|
||||
`
|
||||
|
||||
export default function SettingsMenu({ onClose }: { onClose: () => void }) {
|
||||
const SettingsButtonWrapper = styled(Row)`
|
||||
${ClickableStyle}
|
||||
`
|
||||
|
||||
const StyledChevron = styled(ChevronRight)`
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
`
|
||||
|
||||
const LanguageLabel = styled(Row)`
|
||||
white-space: nowrap;
|
||||
`
|
||||
|
||||
const SettingsButton = ({
|
||||
title,
|
||||
currentState,
|
||||
onClick,
|
||||
testId,
|
||||
}: {
|
||||
title: ReactNode
|
||||
currentState: ReactNode
|
||||
onClick: () => void
|
||||
testId?: string
|
||||
}) => (
|
||||
<SettingsButtonWrapper data-testid={testId} align="center" justify="space-between" onClick={onClick}>
|
||||
<ThemedText.SubHeaderSmall color="textPrimary">{title}</ThemedText.SubHeaderSmall>
|
||||
<LanguageLabel gap="xs" align="center" width="min-content">
|
||||
<ThemedText.LabelMedium color="textPrimary">{currentState}</ThemedText.LabelMedium>
|
||||
<StyledChevron size={20} />
|
||||
</LanguageLabel>
|
||||
</SettingsButtonWrapper>
|
||||
)
|
||||
|
||||
export default function SettingsMenu({
|
||||
onClose,
|
||||
openLanguageSettings,
|
||||
}: {
|
||||
onClose: () => void
|
||||
openLanguageSettings: () => void
|
||||
}) {
|
||||
const currencyConversionEnabled = useCurrencyConversionFlagEnabled()
|
||||
const activeLocale = useActiveLocale()
|
||||
|
||||
return (
|
||||
<SlideOutMenu title={<Trans>Settings</Trans>} onClose={onClose}>
|
||||
<SectionTitle>
|
||||
<Trans>Preferences</Trans>
|
||||
</SectionTitle>
|
||||
<ToggleWrapper>
|
||||
<ThemeToggle />
|
||||
<SmallBalanceToggle />
|
||||
<AnalyticsToggle />
|
||||
<TestnetsToggle />
|
||||
</ToggleWrapper>
|
||||
<Container>
|
||||
<div>
|
||||
<SectionTitle data-testid="wallet-header">
|
||||
<Trans>Preferences</Trans>
|
||||
</SectionTitle>
|
||||
<ToggleWrapper>
|
||||
<ThemeToggle />
|
||||
<SmallBalanceToggle />
|
||||
<AnalyticsToggle />
|
||||
<TestnetsToggle />
|
||||
</ToggleWrapper>
|
||||
{!currencyConversionEnabled && (
|
||||
<>
|
||||
<SectionTitle data-testid="wallet-header">
|
||||
<Trans>Language</Trans>
|
||||
</SectionTitle>
|
||||
<LanguageMenuItems />
|
||||
</>
|
||||
)}
|
||||
|
||||
<SectionTitle data-testid="wallet-header">
|
||||
<Trans>Language</Trans>
|
||||
</SectionTitle>
|
||||
{SUPPORTED_LOCALES.map((locale) => (
|
||||
<LanguageMenuItem locale={locale} isActive={activeLocale === locale} key={locale} />
|
||||
))}
|
||||
<GitVersionRow />
|
||||
{currencyConversionEnabled && (
|
||||
<SettingsButton
|
||||
title={<Trans>Language</Trans>}
|
||||
currentState={LOCALE_LABEL[activeLocale]}
|
||||
onClick={openLanguageSettings}
|
||||
testId="language-settings-button"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<GitVersionRow />
|
||||
</Container>
|
||||
</SlideOutMenu>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import Column from 'components/Column'
|
||||
import { ScrollBarStyles } from 'components/Common'
|
||||
import { ArrowLeft } from 'react-feather'
|
||||
import styled from 'styled-components'
|
||||
import { ClickableStyle, ThemedText } from 'theme'
|
||||
|
||||
const Menu = styled.div`
|
||||
const Menu = styled(Column)`
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
margin-top: 4px;
|
||||
|
||||
@@ -2,7 +2,6 @@ import { BaseVariant, FeatureFlag, featureFlagSettings, useUpdateFlag } from 'fe
|
||||
import { useCurrencyConversionFlag } from 'featureFlags/flags/currencyConversion'
|
||||
import { useForceUniswapXOnFlag } from 'featureFlags/flags/forceUniswapXOn'
|
||||
import { useMultichainUXFlag } from 'featureFlags/flags/multichainUx'
|
||||
import { useRoutingAPIForPriceFlag } from 'featureFlags/flags/priceRoutingApi'
|
||||
import { TraceJsonRpcVariant, useTraceJsonRpcFlag } from 'featureFlags/flags/traceJsonRpc'
|
||||
import { UniswapXVariant, useUniswapXFlag } from 'featureFlags/flags/uniswapx'
|
||||
import { useUniswapXEthOutputFlag } from 'featureFlags/flags/uniswapXEthOutput'
|
||||
@@ -231,12 +230,6 @@ export default function FeatureFlagModal() {
|
||||
featureFlag={FeatureFlag.uniswapXEthOutputEnabled}
|
||||
label="Enable eth output for UniswapX orders"
|
||||
/>
|
||||
<FeatureFlagOption
|
||||
variant={BaseVariant}
|
||||
value={useRoutingAPIForPriceFlag()}
|
||||
featureFlag={FeatureFlag.routingAPIPrice}
|
||||
label="Use the routing-api v2 for price fetches"
|
||||
/>
|
||||
<FeatureFlagOption
|
||||
variant={BaseVariant}
|
||||
value={useCurrencyConversionFlag()}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useHref } from 'react-router-dom'
|
||||
import { useCloseModal, useModalIsOpen } from 'state/application/hooks'
|
||||
import { ApplicationModal } from 'state/application/reducer'
|
||||
import styled, { useTheme } from 'styled-components'
|
||||
@@ -80,8 +79,6 @@ export default function FiatOnrampModal() {
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const swapUrl = useHref('/swap')
|
||||
|
||||
const fetchSignedIframeUrl = useCallback(async () => {
|
||||
if (!account) {
|
||||
setError('Please connect an account before making a purchase.')
|
||||
@@ -101,7 +98,7 @@ export default function FiatOnrampModal() {
|
||||
theme: isDarkMode ? 'dark' : 'light',
|
||||
colorCode: theme.accentAction,
|
||||
defaultCurrencyCode: 'eth',
|
||||
redirectUrl: swapUrl,
|
||||
redirectUrl: 'https://app.uniswap.org/#/swap',
|
||||
walletAddresses: JSON.stringify(
|
||||
MOONPAY_SUPPORTED_CURRENCY_CODES.reduce(
|
||||
(acc, currencyCode) => ({
|
||||
@@ -121,7 +118,7 @@ export default function FiatOnrampModal() {
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [account, isDarkMode, swapUrl, theme.accentAction])
|
||||
}, [account, isDarkMode, theme.accentAction])
|
||||
|
||||
useEffect(() => {
|
||||
fetchSignedIframeUrl()
|
||||
|
||||
@@ -35,7 +35,8 @@ export const LoadingRows = styled.div`
|
||||
export const loadingOpacityMixin = css<{ $loading: boolean }>`
|
||||
filter: ${({ $loading }) => ($loading ? 'grayscale(1)' : 'none')};
|
||||
opacity: ${({ $loading }) => ($loading ? '0.4' : '1')};
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
transition: ${({ $loading, theme }) =>
|
||||
$loading ? 'none' : `opacity ${theme.transition.duration.medium} ${theme.transition.timing.inOut}`};
|
||||
`
|
||||
|
||||
export const LoadingOpacityContainer = styled.div<{ $loading: boolean }>`
|
||||
|
||||
@@ -362,9 +362,6 @@ function ComingSoonText({ chainId }: { chainId: ChainId }) {
|
||||
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>
|
||||
case ChainId.BASE:
|
||||
case ChainId.BASE_GOERLI:
|
||||
return <Trans>Coming soon: search and explore tokens on Base</Trans>
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index,
|
||||
|
||||
return (
|
||||
<Link
|
||||
data-testid={`searchbar-token-row-${token.symbol}`}
|
||||
data-testid={`searchbar-token-row-${token.chain}-${token.address ?? 'NATIVE'}`}
|
||||
to={tokenDetailsPath}
|
||||
onClick={handleClick}
|
||||
onMouseEnter={() => !isHovered && setHoveredIndex(index)}
|
||||
@@ -174,7 +174,7 @@ export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index,
|
||||
symbol={token.symbol}
|
||||
size="36px"
|
||||
backupImg={token.project?.logoUrl}
|
||||
style={{ paddingRight: '8px' }}
|
||||
style={{ marginRight: '8px' }}
|
||||
/>
|
||||
<Column className={styles.suggestionPrimaryContainer}>
|
||||
<Row gap="4" width="full">
|
||||
|
||||
@@ -60,8 +60,9 @@ const baseMenuItem = style([
|
||||
subhead,
|
||||
sprinkles({
|
||||
paddingY: '8',
|
||||
paddingX: '14',
|
||||
paddingX: { sm: '6', md: '14' },
|
||||
marginY: '4',
|
||||
marginX: { sm: '4', md: '0' },
|
||||
borderRadius: '12',
|
||||
transition: '250',
|
||||
height: 'min',
|
||||
@@ -69,7 +70,6 @@ const baseMenuItem = style([
|
||||
textAlign: 'center',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4',
|
||||
}),
|
||||
{
|
||||
lineHeight: '24px',
|
||||
|
||||
@@ -21,12 +21,12 @@ export { Gradient as UniswapXGradient }
|
||||
// Uniswap X SVG icon with gradient, copied from Figma.
|
||||
// In order for gradient to work, we must give its definition a unique ID that does not collide
|
||||
// with other occurences of this component on the page.
|
||||
export const UniswapXRouterIcon = () => {
|
||||
export const UniswapXRouterIcon = ({ testId }: { testId?: string }) => {
|
||||
const componentIdRef = useRef(uuid())
|
||||
const componentId = `AutoRouterIconGradient${componentIdRef.current}`
|
||||
|
||||
return (
|
||||
<svg width="10" height="14" viewBox="0 0 10 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="10" height="14" viewBox="0 0 10 14" fill="none" xmlns="http://www.w3.org/2000/svg" data-testid={testId}>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id={componentId}
|
||||
@@ -50,12 +50,18 @@ export const UniswapXRouterIcon = () => {
|
||||
|
||||
export type UnswapXRouterLabelProps = BoxProps & {
|
||||
disableTextGradient?: boolean
|
||||
testId?: string
|
||||
}
|
||||
|
||||
export default function UniswapXRouterLabel({ children, disableTextGradient, ...rest }: UnswapXRouterLabelProps) {
|
||||
export default function UniswapXRouterLabel({
|
||||
children,
|
||||
disableTextGradient,
|
||||
testId,
|
||||
...rest
|
||||
}: UnswapXRouterLabelProps) {
|
||||
return (
|
||||
<Row gap="xs" width="auto" {...rest} style={{ display: 'inline-flex', ...rest.style }}>
|
||||
<UniswapXRouterIcon />
|
||||
<UniswapXRouterIcon testId={testId} />
|
||||
{disableTextGradient ? children : <Gradient>{children}</Gradient>}
|
||||
</Row>
|
||||
)
|
||||
|
||||
47
src/components/Tokens/TokenDetails/PriceChart.test.tsx
Normal file
47
src/components/Tokens/TokenDetails/PriceChart.test.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { TimePeriod } from 'graphql/data/util'
|
||||
import { render } from 'test-utils/render'
|
||||
|
||||
import { PriceChart } from './PriceChart'
|
||||
|
||||
jest.mock('components/Charts/AnimatedInLineChart', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(() => null),
|
||||
}))
|
||||
jest.mock('components/Charts/FadeInLineChart', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(() => null),
|
||||
}))
|
||||
|
||||
describe('PriceChart', () => {
|
||||
it('renders correctly with all prices filled', () => {
|
||||
const mockPrices = Array.from({ length: 13 }, (_, i) => ({
|
||||
value: 1,
|
||||
timestamp: i * 3600,
|
||||
}))
|
||||
|
||||
const { asFragment } = render(
|
||||
<PriceChart prices={mockPrices} width={780} height={436} timePeriod={TimePeriod.HOUR} />
|
||||
)
|
||||
expect(asFragment()).toMatchSnapshot()
|
||||
expect(asFragment().textContent).toContain('$1.00')
|
||||
expect(asFragment().textContent).toContain('0.00%')
|
||||
})
|
||||
it('renders correctly with some prices filled', () => {
|
||||
const mockPrices = Array.from({ length: 13 }, (_, i) => ({
|
||||
value: i < 10 ? 1 : 0,
|
||||
timestamp: i * 3600,
|
||||
}))
|
||||
|
||||
const { asFragment } = render(
|
||||
<PriceChart prices={mockPrices} width={780} height={436} timePeriod={TimePeriod.HOUR} />
|
||||
)
|
||||
expect(asFragment()).toMatchSnapshot()
|
||||
expect(asFragment().textContent).toContain('$1.00')
|
||||
expect(asFragment().textContent).toContain('0.00%')
|
||||
})
|
||||
it('renders correctly with no prices filled', () => {
|
||||
const { asFragment } = render(<PriceChart prices={[]} width={780} height={436} timePeriod={TimePeriod.HOUR} />)
|
||||
expect(asFragment()).toMatchSnapshot()
|
||||
expect(asFragment().textContent).toContain('Price Unavailable')
|
||||
})
|
||||
})
|
||||
@@ -6,12 +6,13 @@ import { GlyphCircle } from '@visx/glyph'
|
||||
import { Line } from '@visx/shape'
|
||||
import AnimatedInLineChart from 'components/Charts/AnimatedInLineChart'
|
||||
import FadedInLineChart from 'components/Charts/FadeInLineChart'
|
||||
import { MouseoverTooltip } from 'components/Tooltip'
|
||||
import { bisect, curveCardinal, NumberValue, scaleLinear, timeDay, timeHour, timeMinute, timeMonth } from 'd3'
|
||||
import { PricePoint } from 'graphql/data/util'
|
||||
import { TimePeriod } from 'graphql/data/util'
|
||||
import { useActiveLocale } from 'hooks/useActiveLocale'
|
||||
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { ArrowDownRight, ArrowUpRight, TrendingUp } from 'react-feather'
|
||||
import { ArrowDownRight, ArrowUpRight, Info, TrendingUp } from 'react-feather'
|
||||
import styled, { useTheme } from 'styled-components'
|
||||
import { ThemedText } from 'theme'
|
||||
import { textFadeIn } from 'theme/styles'
|
||||
@@ -41,18 +42,33 @@ const StyledDownArrow = styled(ArrowDownRight)`
|
||||
color: ${({ theme }) => theme.accentFailure};
|
||||
`
|
||||
|
||||
const DefaultUpArrow = styled(ArrowUpRight)`
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
`
|
||||
const DefaultDownArrow = styled(ArrowDownRight)`
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
`
|
||||
|
||||
function calculateDelta(start: number, current: number) {
|
||||
return (current / start - 1) * 100
|
||||
}
|
||||
|
||||
export function getDeltaArrow(delta: number | null | undefined, iconSize = 20) {
|
||||
export function getDeltaArrow(delta: number | null | undefined, iconSize = 20, styled = true) {
|
||||
// Null-check not including zero
|
||||
if (delta === null || delta === undefined) {
|
||||
return null
|
||||
} else if (Math.sign(delta) < 0) {
|
||||
return <StyledDownArrow size={iconSize} key="arrow-down" aria-label="down" />
|
||||
return styled ? (
|
||||
<StyledDownArrow size={iconSize} key="arrow-down" aria-label="down" />
|
||||
) : (
|
||||
<DefaultDownArrow size={iconSize} key="arrow-down" aria-label="down" />
|
||||
)
|
||||
}
|
||||
return <StyledUpArrow size={iconSize} key="arrow-up" aria-label="up" />
|
||||
return styled ? (
|
||||
<StyledUpArrow size={iconSize} key="arrow-up" aria-label="up" />
|
||||
) : (
|
||||
<DefaultUpArrow size={iconSize} key="arrow-up" aria-label="up" />
|
||||
)
|
||||
}
|
||||
|
||||
export function formatDelta(delta: number | null | undefined) {
|
||||
@@ -84,6 +100,10 @@ const MissingPrice = styled(TokenPrice)`
|
||||
color: ${({ theme }) => theme.textTertiary};
|
||||
`
|
||||
|
||||
const OutdatedContainer = styled.div`
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
`
|
||||
|
||||
const DeltaContainer = styled.div`
|
||||
height: 16px;
|
||||
display: flex;
|
||||
@@ -95,6 +115,13 @@ export const ArrowCell = styled.div`
|
||||
display: flex;
|
||||
`
|
||||
|
||||
const OutdatedPriceContainer = styled.div`
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
font-size: 24px;
|
||||
line-height: 44px;
|
||||
`
|
||||
|
||||
function fixChart(prices: PricePoint[] | undefined | null) {
|
||||
if (!prices) return { prices: null, blanks: [] }
|
||||
|
||||
@@ -148,6 +175,34 @@ export function PriceChart({ width, height, prices: originalPrices, timePeriod }
|
||||
)
|
||||
) : null
|
||||
|
||||
const tooltipMessage = (
|
||||
<>
|
||||
<Trans>This price may not be up-to-date due to low trading volume.</Trans>
|
||||
</>
|
||||
)
|
||||
|
||||
//get the last non-zero price point
|
||||
const lastPrice = useMemo(() => {
|
||||
if (!prices) return DATA_EMPTY
|
||||
for (let i = prices.length - 1; i >= 0; i--) {
|
||||
if (prices[i].value !== 0) return prices[i]
|
||||
}
|
||||
return DATA_EMPTY
|
||||
}, [prices])
|
||||
|
||||
//get the first non-zero price point
|
||||
const firstPrice = useMemo(() => {
|
||||
if (!prices) return DATA_EMPTY
|
||||
for (let i = 0; i < prices.length; i++) {
|
||||
if (prices[i].value !== 0) return prices[i]
|
||||
}
|
||||
return DATA_EMPTY
|
||||
}, [prices])
|
||||
|
||||
const totalDelta = calculateDelta(firstPrice.value, lastPrice.value)
|
||||
const formattedTotalDelta = formatDelta(totalDelta)
|
||||
const defaultArrow = getDeltaArrow(totalDelta, 20, false)
|
||||
|
||||
// first price point on the x-axis of the current time period's chart
|
||||
const startingPrice = originalPrices?.[0] ?? DATA_EMPTY
|
||||
// last price point on the x-axis of the current time period's chart
|
||||
@@ -302,6 +357,19 @@ export function PriceChart({ width, height, prices: originalPrices, timePeriod }
|
||||
<ArrowCell>{arrow}</ArrowCell>
|
||||
</DeltaContainer>
|
||||
</>
|
||||
) : lastPrice.value ? (
|
||||
<OutdatedContainer>
|
||||
<OutdatedPriceContainer>
|
||||
<TokenPrice>{formatUSDPrice(lastPrice.value)}</TokenPrice>
|
||||
<MouseoverTooltip text={tooltipMessage}>
|
||||
<Info size={16} />
|
||||
</MouseoverTooltip>
|
||||
</OutdatedPriceContainer>
|
||||
<DeltaContainer>
|
||||
{formattedTotalDelta}
|
||||
<ArrowCell>{defaultArrow}</ArrowCell>
|
||||
</DeltaContainer>
|
||||
</OutdatedContainer>
|
||||
) : (
|
||||
<>
|
||||
<MissingPrice>Price Unavailable</MissingPrice>
|
||||
|
||||
@@ -0,0 +1,844 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`PriceChart renders correctly with all prices filled 1`] = `
|
||||
<DocumentFragment>
|
||||
.c4 {
|
||||
color: #40B66B;
|
||||
}
|
||||
|
||||
.c0 {
|
||||
position: absolute;
|
||||
-webkit-animation: iAjNNh 125ms ease-in;
|
||||
animation: iAjNNh 125ms ease-in;
|
||||
-webkit-animation-duration: 250ms;
|
||||
animation-duration: 250ms;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
font-size: 36px;
|
||||
line-height: 44px;
|
||||
}
|
||||
|
||||
.c2 {
|
||||
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;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.c3 {
|
||||
padding-right: 3px;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
<div
|
||||
class="c0"
|
||||
data-cy="chart-header"
|
||||
>
|
||||
<span
|
||||
class="c1"
|
||||
>
|
||||
$1.00
|
||||
</span>
|
||||
<div
|
||||
class="c2"
|
||||
>
|
||||
0.00%
|
||||
<div
|
||||
class="c3"
|
||||
>
|
||||
<svg
|
||||
aria-label="up"
|
||||
class="c4"
|
||||
fill="none"
|
||||
height="20"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<line
|
||||
x1="7"
|
||||
x2="17"
|
||||
y1="17"
|
||||
y2="7"
|
||||
/>
|
||||
<polyline
|
||||
points="7 7 17 7 17 17"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<svg
|
||||
data-cy="price-chart"
|
||||
height="392"
|
||||
style="min-width: 100%;"
|
||||
width="780"
|
||||
>
|
||||
<g
|
||||
class="visx-group visx-axis visx-axis-bottom"
|
||||
transform="translate(0, 391)"
|
||||
>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="0"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="0"
|
||||
>
|
||||
0
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="90.27777777777777"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="90.27777777777777"
|
||||
>
|
||||
5,000
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="180.55555555555554"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="180.55555555555554"
|
||||
>
|
||||
10,000
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="270.8333333333333"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="270.8333333333333"
|
||||
>
|
||||
15,000
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="361.1111111111111"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="361.1111111111111"
|
||||
>
|
||||
20,000
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="451.3888888888889"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="451.3888888888889"
|
||||
>
|
||||
25,000
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="541.6666666666666"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="541.6666666666666"
|
||||
>
|
||||
30,000
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="631.9444444444445"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="631.9444444444445"
|
||||
>
|
||||
35,000
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="722.2222222222222"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="722.2222222222222"
|
||||
>
|
||||
40,000
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
</g>
|
||||
<rect
|
||||
fill="transparent"
|
||||
height="392"
|
||||
width="780"
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
</svg>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`PriceChart renders correctly with no prices filled 1`] = `
|
||||
<DocumentFragment>
|
||||
.c3 {
|
||||
color: #0D111C;
|
||||
}
|
||||
|
||||
.c0 {
|
||||
position: absolute;
|
||||
-webkit-animation: iAjNNh 125ms ease-in;
|
||||
animation: iAjNNh 125ms ease-in;
|
||||
-webkit-animation-duration: 250ms;
|
||||
animation-duration: 250ms;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
font-size: 36px;
|
||||
line-height: 44px;
|
||||
}
|
||||
|
||||
.c2 {
|
||||
font-size: 24px;
|
||||
line-height: 44px;
|
||||
color: #98A1C0;
|
||||
}
|
||||
|
||||
<div
|
||||
class="c0"
|
||||
data-cy="chart-header"
|
||||
>
|
||||
<span
|
||||
class="c1 c2"
|
||||
>
|
||||
Price Unavailable
|
||||
</span>
|
||||
<div
|
||||
class="c3 css-4u0e4f"
|
||||
style="color: rgb(152, 161, 192);"
|
||||
>
|
||||
Missing chart data
|
||||
</div>
|
||||
</div>
|
||||
.c0 text {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
<svg
|
||||
class="c0"
|
||||
data-cy="missing-chart"
|
||||
height="392"
|
||||
style="min-width: 100%;"
|
||||
width="780"
|
||||
>
|
||||
<path
|
||||
d="M 0 241 Q 104 171, 208 241 T 416 241
|
||||
M 416 241 Q 520 171, 624 241 T 832 241"
|
||||
fill="transparent"
|
||||
stroke="#D2D9EE"
|
||||
stroke-width="2"
|
||||
/>
|
||||
<text
|
||||
fill="#98A1C0"
|
||||
x="20"
|
||||
y="377"
|
||||
/>
|
||||
</svg>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`PriceChart renders correctly with some prices filled 1`] = `
|
||||
<DocumentFragment>
|
||||
.c4 {
|
||||
display: inline-block;
|
||||
height: inherit;
|
||||
}
|
||||
|
||||
.c7 {
|
||||
color: #98A1C0;
|
||||
}
|
||||
|
||||
.c0 {
|
||||
position: absolute;
|
||||
-webkit-animation: iAjNNh 125ms ease-in;
|
||||
animation: iAjNNh 125ms ease-in;
|
||||
-webkit-animation-duration: 250ms;
|
||||
animation-duration: 250ms;
|
||||
}
|
||||
|
||||
.c3 {
|
||||
font-size: 36px;
|
||||
line-height: 44px;
|
||||
}
|
||||
|
||||
.c1 {
|
||||
color: #7780A0;
|
||||
}
|
||||
|
||||
.c5 {
|
||||
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;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.c6 {
|
||||
padding-right: 3px;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.c2 {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
font-size: 24px;
|
||||
line-height: 44px;
|
||||
}
|
||||
|
||||
<div
|
||||
class="c0"
|
||||
data-cy="chart-header"
|
||||
>
|
||||
<div
|
||||
class="c1"
|
||||
>
|
||||
<div
|
||||
class="c2"
|
||||
>
|
||||
<span
|
||||
class="c3"
|
||||
>
|
||||
$1.00
|
||||
</span>
|
||||
<div
|
||||
class="c4"
|
||||
>
|
||||
<div>
|
||||
<svg
|
||||
fill="none"
|
||||
height="16"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
/>
|
||||
<line
|
||||
x1="12"
|
||||
x2="12"
|
||||
y1="16"
|
||||
y2="12"
|
||||
/>
|
||||
<line
|
||||
x1="12"
|
||||
x2="12.01"
|
||||
y1="8"
|
||||
y2="8"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="c5"
|
||||
>
|
||||
0.00%
|
||||
<div
|
||||
class="c6"
|
||||
>
|
||||
<svg
|
||||
aria-label="up"
|
||||
class="c7"
|
||||
fill="none"
|
||||
height="20"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<line
|
||||
x1="7"
|
||||
x2="17"
|
||||
y1="17"
|
||||
y2="7"
|
||||
/>
|
||||
<polyline
|
||||
points="7 7 17 7 17 17"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<svg
|
||||
data-cy="price-chart"
|
||||
height="392"
|
||||
style="min-width: 100%;"
|
||||
width="780"
|
||||
>
|
||||
<g
|
||||
class="visx-group visx-axis visx-axis-bottom"
|
||||
transform="translate(0, 391)"
|
||||
>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="0"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="0"
|
||||
>
|
||||
0
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="90.27777777777777"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="90.27777777777777"
|
||||
>
|
||||
5,000
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="180.55555555555554"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="180.55555555555554"
|
||||
>
|
||||
10,000
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="270.8333333333333"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="270.8333333333333"
|
||||
>
|
||||
15,000
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="361.1111111111111"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="361.1111111111111"
|
||||
>
|
||||
20,000
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="451.3888888888889"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="451.3888888888889"
|
||||
>
|
||||
25,000
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="541.6666666666666"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="541.6666666666666"
|
||||
>
|
||||
30,000
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="631.9444444444445"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="631.9444444444445"
|
||||
>
|
||||
35,000
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
<g
|
||||
class="visx-group visx-axis-tick"
|
||||
transform="translate(0, 0)"
|
||||
>
|
||||
<svg
|
||||
font-size="10"
|
||||
style="overflow: visible;"
|
||||
x="0"
|
||||
y="0.25em"
|
||||
>
|
||||
<text
|
||||
fill="#222"
|
||||
font-family="Arial"
|
||||
font-size="10"
|
||||
text-anchor="middle"
|
||||
transform=""
|
||||
x="722.2222222222222"
|
||||
y="18"
|
||||
>
|
||||
<tspan
|
||||
dy="0em"
|
||||
x="722.2222222222222"
|
||||
>
|
||||
40,000
|
||||
</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
</g>
|
||||
</g>
|
||||
<rect
|
||||
fill="transparent"
|
||||
height="392"
|
||||
width="780"
|
||||
x="0"
|
||||
y="0"
|
||||
/>
|
||||
</svg>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
@@ -343,7 +343,7 @@ exports[`LoadedRow.tsx renders a row 1`] = `
|
||||
>
|
||||
<a
|
||||
class="c0"
|
||||
href="/tokens/ethereum/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
||||
href="#/tokens/ethereum/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
|
||||
>
|
||||
<div
|
||||
class="c1"
|
||||
|
||||
@@ -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, ChainId.BASE_GOERLI, ChainId.BASE]
|
||||
export const UNSUPPORTED_METADATA_CHAINS = [ChainId.BNB, ChainId.AVALANCHE]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { CustomUserProperties, InterfaceEventName, WalletConnectionResult } from '@uniswap/analytics-events'
|
||||
import { getWalletMeta } from '@uniswap/conedison/provider/meta'
|
||||
import { useWeb3React, Web3ReactHooks, Web3ReactProvider } from '@web3-react/core'
|
||||
import { Connector } from '@web3-react/types'
|
||||
import { sendAnalyticsEvent, user } from 'analytics'
|
||||
@@ -13,6 +12,7 @@ import { ReactNode, useEffect } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { useConnectedWallets } from 'state/wallets/hooks'
|
||||
import { getCurrentPageFromLocation } from 'utils/urlRoutes'
|
||||
import { getWalletMeta } from 'utils/walletMeta'
|
||||
|
||||
export default function Web3Provider({ children }: { children: ReactNode }) {
|
||||
useEagerlyConnect()
|
||||
|
||||
@@ -3,7 +3,7 @@ import { BrowserEvent, InterfaceElementName, InterfaceEventName } from '@uniswap
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { sendAnalyticsEvent, TraceEvent } from 'analytics'
|
||||
import PortfolioDrawer, { useAccountDrawer } from 'components/AccountDrawer'
|
||||
import { useHasPendingActivity } from 'components/AccountDrawer/MiniPortfolio/Activity/hooks'
|
||||
import { usePendingActivity } from 'components/AccountDrawer/MiniPortfolio/Activity/hooks'
|
||||
import PrefetchBalancesWrapper from 'components/AccountDrawer/PrefetchBalancesWrapper'
|
||||
import Loader from 'components/Icons/LoadingSpinner'
|
||||
import { IconWrapper } from 'components/Identicon/StatusIcon'
|
||||
@@ -140,7 +140,7 @@ function Web3StatusInner() {
|
||||
}, [toggleAccountDrawer])
|
||||
const isClaimAvailable = useIsNftClaimAvailable((state) => state.isClaimAvailable)
|
||||
|
||||
const { hasPendingActivity, pendingActivityCount } = useHasPendingActivity()
|
||||
const { hasPendingActivity, pendingActivityCount } = usePendingActivity()
|
||||
|
||||
if (account) {
|
||||
return (
|
||||
|
||||
@@ -267,7 +267,6 @@ export default function ConfirmSwapModal({
|
||||
onCurrencySelection,
|
||||
swapError,
|
||||
swapResult,
|
||||
swapQuoteReceivedDate,
|
||||
fiatValueInput,
|
||||
fiatValueOutput,
|
||||
}: {
|
||||
@@ -282,7 +281,6 @@ export default function ConfirmSwapModal({
|
||||
swapError?: Error
|
||||
onDismiss: () => void
|
||||
onCurrencySelection: (field: Field, currency: Currency) => void
|
||||
swapQuoteReceivedDate?: Date
|
||||
fiatValueInput: { data?: number; isLoading: boolean }
|
||||
fiatValueOutput: { data?: number; isLoading: boolean }
|
||||
}) {
|
||||
@@ -356,7 +354,6 @@ export default function ConfirmSwapModal({
|
||||
swapResult={swapResult}
|
||||
allowedSlippage={allowedSlippage}
|
||||
disabledConfirm={showAcceptChanges}
|
||||
swapQuoteReceivedDate={swapQuoteReceivedDate}
|
||||
fiatValueInput={fiatValueInput}
|
||||
fiatValueOutput={fiatValueOutput}
|
||||
showAcceptChanges={showAcceptChanges}
|
||||
@@ -386,7 +383,6 @@ export default function ConfirmSwapModal({
|
||||
wrapTxHash,
|
||||
allowance,
|
||||
allowedSlippage,
|
||||
swapQuoteReceivedDate,
|
||||
fiatValueInput,
|
||||
fiatValueOutput,
|
||||
onAcceptChanges,
|
||||
|
||||
@@ -44,7 +44,7 @@ export default function GasEstimateTooltip({ trade, loading }: { trade?: Interfa
|
||||
>
|
||||
<LoadingOpacityContainer $loading={loading}>
|
||||
<RowFixed gap="xs">
|
||||
{isUniswapXTrade(trade) ? <UniswapXRouterIcon /> : <StyledGasIcon />}
|
||||
{isUniswapXTrade(trade) ? <UniswapXRouterIcon testId="gas-estimate-uniswapx-icon" /> : <StyledGasIcon />}
|
||||
<ThemedText.BodySmall color="textSecondary">
|
||||
<Row gap="xs">
|
||||
<div>{formatNumber(trade.totalGasUseEstimateUSD, NumberType.FiatGasPrice)}</div>
|
||||
|
||||
@@ -13,7 +13,6 @@ describe('SwapModalFooter.tsx', () => {
|
||||
onConfirm={jest.fn()}
|
||||
swapErrorMessage={undefined}
|
||||
disabledConfirm={false}
|
||||
swapQuoteReceivedDate={undefined}
|
||||
fiatValueInput={{
|
||||
data: undefined,
|
||||
isLoading: false,
|
||||
@@ -49,7 +48,6 @@ describe('SwapModalFooter.tsx', () => {
|
||||
onConfirm={jest.fn()}
|
||||
swapErrorMessage={undefined}
|
||||
disabledConfirm={false}
|
||||
swapQuoteReceivedDate={undefined}
|
||||
fiatValueInput={{
|
||||
data: undefined,
|
||||
isLoading: false,
|
||||
@@ -77,7 +75,6 @@ describe('SwapModalFooter.tsx', () => {
|
||||
onConfirm={jest.fn()}
|
||||
swapErrorMessage={undefined}
|
||||
disabledConfirm={false}
|
||||
swapQuoteReceivedDate={undefined}
|
||||
fiatValueInput={{
|
||||
data: undefined,
|
||||
isLoading: false,
|
||||
|
||||
@@ -53,7 +53,6 @@ export default function SwapModalFooter({
|
||||
onConfirm,
|
||||
swapErrorMessage,
|
||||
disabledConfirm,
|
||||
swapQuoteReceivedDate,
|
||||
fiatValueInput,
|
||||
fiatValueOutput,
|
||||
showAcceptChanges,
|
||||
@@ -65,7 +64,6 @@ export default function SwapModalFooter({
|
||||
onConfirm: () => void
|
||||
swapErrorMessage?: ReactNode
|
||||
disabledConfirm: boolean
|
||||
swapQuoteReceivedDate?: Date
|
||||
fiatValueInput: { data?: number; isLoading: boolean }
|
||||
fiatValueOutput: { data?: number; isLoading: boolean }
|
||||
showAcceptChanges: boolean
|
||||
@@ -187,7 +185,6 @@ export default function SwapModalFooter({
|
||||
transactionDeadlineSecondsSinceEpoch,
|
||||
isAutoSlippage,
|
||||
isAutoRouterApi: routerPreference === RouterPreference.API,
|
||||
swapQuoteReceivedDate,
|
||||
routes,
|
||||
fiatValueInput: fiatValueInput.data,
|
||||
fiatValueOutput: fiatValueOutput.data,
|
||||
|
||||
@@ -110,8 +110,8 @@ exports[`SwapDetailsDropdown.tsx renders a trade 1`] = `
|
||||
-webkit-filter: none;
|
||||
filter: none;
|
||||
opacity: 1;
|
||||
-webkit-transition: opacity 0.2s ease-in-out;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
-webkit-transition: opacity 250ms ease-in-out;
|
||||
transition: opacity 250ms ease-in-out;
|
||||
}
|
||||
|
||||
.c10 {
|
||||
|
||||
@@ -86,6 +86,7 @@ const UniswapXShineInner = styled.div`
|
||||
|
||||
// overflow hidden to hide the SwapMustacheShadow
|
||||
export const SwapOptInSmallContainer = styled.div<{ visible: boolean; shouldAnimate: boolean }>`
|
||||
visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')};
|
||||
overflow: hidden;
|
||||
margin-top: -14px;
|
||||
transform: translateY(${({ visible }) => (visible ? 0 : -80)}px);
|
||||
|
||||
@@ -7,16 +7,18 @@ const chainPriorityTestCases: [ChainId, number][] = [
|
||||
[ChainId.MAINNET, 0],
|
||||
[ChainId.GOERLI, 0],
|
||||
[ChainId.SEPOLIA, 0],
|
||||
[ChainId.POLYGON, 1],
|
||||
[ChainId.POLYGON_MUMBAI, 1],
|
||||
[ChainId.ARBITRUM_ONE, 2],
|
||||
[ChainId.ARBITRUM_GOERLI, 2],
|
||||
[ChainId.OPTIMISM, 3],
|
||||
[ChainId.OPTIMISM_GOERLI, 3],
|
||||
[ChainId.BNB, 4],
|
||||
[ChainId.AVALANCHE, 5],
|
||||
[ChainId.CELO, 6],
|
||||
[ChainId.CELO_ALFAJORES, 6],
|
||||
[ChainId.ARBITRUM_ONE, 1],
|
||||
[ChainId.ARBITRUM_GOERLI, 1],
|
||||
[ChainId.OPTIMISM, 2],
|
||||
[ChainId.OPTIMISM_GOERLI, 2],
|
||||
[ChainId.POLYGON, 3],
|
||||
[ChainId.POLYGON_MUMBAI, 3],
|
||||
[ChainId.BASE, 4],
|
||||
[ChainId.BASE_GOERLI, 4],
|
||||
[ChainId.BNB, 5],
|
||||
[ChainId.AVALANCHE, 6],
|
||||
[ChainId.CELO, 7],
|
||||
[ChainId.CELO_ALFAJORES, 7],
|
||||
]
|
||||
|
||||
test.each(chainPriorityTestCases)(
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { ChainId, SUPPORTED_CHAINS, SupportedChainsType } from '@uniswap/sdk-core'
|
||||
|
||||
export const UniWalletSupportedChains = [ChainId.MAINNET, ChainId.ARBITRUM_ONE, ChainId.OPTIMISM, ChainId.POLYGON]
|
||||
export const UniWalletSupportedChains = [
|
||||
ChainId.MAINNET,
|
||||
ChainId.ARBITRUM_ONE,
|
||||
ChainId.OPTIMISM,
|
||||
ChainId.POLYGON,
|
||||
ChainId.BASE,
|
||||
]
|
||||
|
||||
export const CHAIN_IDS_TO_NAMES = {
|
||||
[ChainId.MAINNET]: 'mainnet',
|
||||
@@ -113,24 +119,27 @@ export function getChainPriority(chainId: ChainId): number {
|
||||
case ChainId.GOERLI:
|
||||
case ChainId.SEPOLIA:
|
||||
return 0
|
||||
case ChainId.POLYGON:
|
||||
case ChainId.POLYGON_MUMBAI:
|
||||
return 1
|
||||
case ChainId.ARBITRUM_ONE:
|
||||
case ChainId.ARBITRUM_GOERLI:
|
||||
return 2
|
||||
return 1
|
||||
case ChainId.OPTIMISM:
|
||||
case ChainId.OPTIMISM_GOERLI:
|
||||
return 2
|
||||
case ChainId.POLYGON:
|
||||
case ChainId.POLYGON_MUMBAI:
|
||||
return 3
|
||||
case ChainId.BNB:
|
||||
case ChainId.BASE:
|
||||
case ChainId.BASE_GOERLI:
|
||||
return 4
|
||||
case ChainId.AVALANCHE:
|
||||
case ChainId.BNB:
|
||||
return 5
|
||||
case ChainId.AVALANCHE:
|
||||
return 6
|
||||
case ChainId.CELO:
|
||||
case ChainId.CELO_ALFAJORES:
|
||||
return 6
|
||||
default:
|
||||
return 7
|
||||
default:
|
||||
return 8
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,3 +3,7 @@ import { BaseVariant, FeatureFlag, useBaseFlag } from '../index'
|
||||
export function useCurrencyConversionFlag(): BaseVariant {
|
||||
return useBaseFlag(FeatureFlag.currencyConversion)
|
||||
}
|
||||
|
||||
export function useCurrencyConversionFlagEnabled(): boolean {
|
||||
return useCurrencyConversionFlag() === BaseVariant.Enabled
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { BaseVariant, FeatureFlag, useBaseFlag } from '../index'
|
||||
|
||||
export function useRoutingAPIForPriceFlag(): BaseVariant {
|
||||
return useBaseFlag(FeatureFlag.routingAPIPrice)
|
||||
}
|
||||
|
||||
export function useRoutingAPIForPrice(): boolean {
|
||||
return useRoutingAPIForPriceFlag() === BaseVariant.Enabled
|
||||
}
|
||||
@@ -12,7 +12,6 @@ export enum FeatureFlag {
|
||||
debounceSwapQuote = 'debounce_swap_quote',
|
||||
uniswapXEnabled = 'uniswapx_enabled', // enables sending dutch_limit config to routing-api
|
||||
uniswapXSyntheticQuote = 'uniswapx_synthetic_quote',
|
||||
routingAPIPrice = 'routing_api_price',
|
||||
forceUniswapXOn = 'uniswapx_force_on', // forces routing-api's feature flag for uniswapx to turn on as well
|
||||
uniswapXEthOutputEnabled = 'uniswapx_eth_output_enabled',
|
||||
multichainUX = 'multichain_ux',
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useMemo } from 'react'
|
||||
import invariant from 'tiny-invariant'
|
||||
|
||||
import { Chain, SearchTokensQuery, useSearchTokensQuery } from './__generated__/types-and-hooks'
|
||||
import { chainIdToBackendName } from './util'
|
||||
import { BACKEND_SUPPORTED_CHAINS, chainIdToBackendName } from './util'
|
||||
|
||||
gql`
|
||||
query SearchTokens($searchQuery: String!) {
|
||||
@@ -96,7 +96,10 @@ export function useSearchTokens(searchQuery: string, chainId: number) {
|
||||
const searchChain = chainIdToBackendName(chainId)
|
||||
// Stores results, allowing overwriting cross-chain tokens w/ more 'relevant token'
|
||||
const selectionMap: { [projectId: string]: SearchToken } = {}
|
||||
data?.searchTokens?.forEach((token) => {
|
||||
const filteredTokens = data?.searchTokens?.filter((token) =>
|
||||
(BACKEND_SUPPORTED_CHAINS as ReadonlyArray<Chain>).includes(token.chain)
|
||||
)
|
||||
filteredTokens?.forEach((token) => {
|
||||
if (token.project?.id) {
|
||||
const existing = selectionMap[token.project.id]
|
||||
selectionMap[token.project.id] = dedupeCrosschainTokens(token, existing, searchChain)
|
||||
|
||||
@@ -35,6 +35,7 @@ gql`
|
||||
tokenProject {
|
||||
id
|
||||
logoUrl
|
||||
isSpam
|
||||
}
|
||||
}
|
||||
token {
|
||||
|
||||
@@ -87,6 +87,7 @@ export const CHAIN_ID_TO_BACKEND_NAME: { [key: number]: InterfaceGqlChain } = {
|
||||
[ChainId.OPTIMISM_GOERLI]: Chain.Optimism,
|
||||
[ChainId.BNB]: Chain.Bnb,
|
||||
[ChainId.AVALANCHE]: Chain.Avalanche,
|
||||
[ChainId.BASE]: Chain.Base,
|
||||
}
|
||||
|
||||
export function chainIdToBackendName(chainId: number | undefined) {
|
||||
@@ -128,6 +129,7 @@ const URL_CHAIN_PARAM_TO_BACKEND: { [key: string]: InterfaceGqlChain } = {
|
||||
optimism: Chain.Optimism,
|
||||
bnb: Chain.Bnb,
|
||||
avalanche: Chain.Avalanche,
|
||||
base: Chain.Base,
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,7 @@ export function useDebouncedTrade(
|
||||
): {
|
||||
state: TradeState
|
||||
trade?: InterfaceTrade
|
||||
swapQuoteLatency?: number
|
||||
}
|
||||
|
||||
export function useDebouncedTrade(
|
||||
@@ -37,6 +38,7 @@ export function useDebouncedTrade(
|
||||
): {
|
||||
state: TradeState
|
||||
trade?: ClassicTrade
|
||||
swapQuoteLatency?: number
|
||||
}
|
||||
/**
|
||||
* Returns the debounced v2+v3 trade for a desired swap.
|
||||
@@ -57,47 +59,38 @@ export function useDebouncedTrade(
|
||||
state: TradeState
|
||||
trade?: InterfaceTrade
|
||||
method?: QuoteMethod
|
||||
swapQuoteLatency?: number
|
||||
} {
|
||||
const { chainId } = useWeb3React()
|
||||
const autoRouterSupported = useAutoRouterSupported()
|
||||
const isWindowVisible = useIsWindowVisible()
|
||||
|
||||
const debouncedSwapQuoteFlagEnabled = useDebounceSwapQuoteFlag() === DebounceSwapQuoteVariant.Enabled
|
||||
const [debouncedAmount, debouncedOtherCurrency] = useDebounce(
|
||||
useMemo(() => [amountSpecified, otherCurrency], [amountSpecified, otherCurrency]),
|
||||
debouncedSwapQuoteFlagEnabled ? DEBOUNCE_TIME_INCREASED : DEBOUNCE_TIME
|
||||
const inputs = useMemo<[CurrencyAmount<Currency> | undefined, Currency | undefined]>(
|
||||
() => [amountSpecified, otherCurrency],
|
||||
[amountSpecified, otherCurrency]
|
||||
)
|
||||
const debouncedSwapQuoteFlagEnabled = useDebounceSwapQuoteFlag() === DebounceSwapQuoteVariant.Enabled
|
||||
const isDebouncing =
|
||||
useDebounce(inputs, debouncedSwapQuoteFlagEnabled ? DEBOUNCE_TIME_INCREASED : DEBOUNCE_TIME) !== inputs
|
||||
|
||||
const isAWrapTransaction = useMemo(() => {
|
||||
if (!chainId || !amountSpecified || !debouncedOtherCurrency) return false
|
||||
const isWrap = useMemo(() => {
|
||||
if (!chainId || !amountSpecified || !otherCurrency) return false
|
||||
const weth = WRAPPED_NATIVE_CURRENCY[chainId]
|
||||
return (
|
||||
(amountSpecified.currency.isNative && weth?.equals(debouncedOtherCurrency)) ||
|
||||
(debouncedOtherCurrency.isNative && weth?.equals(amountSpecified.currency))
|
||||
(amountSpecified.currency.isNative && weth?.equals(otherCurrency)) ||
|
||||
(otherCurrency.isNative && weth?.equals(amountSpecified.currency))
|
||||
)
|
||||
}, [amountSpecified, chainId, debouncedOtherCurrency])
|
||||
}, [amountSpecified, chainId, otherCurrency])
|
||||
|
||||
const shouldGetTrade = !isAWrapTransaction && isWindowVisible
|
||||
const skipFetch = isDebouncing || !autoRouterSupported || !isWindowVisible || isWrap
|
||||
|
||||
const [routerPreference] = useRouterPreference()
|
||||
const routingAPITrade = useRoutingAPITrade(
|
||||
return useRoutingAPITrade(
|
||||
tradeType,
|
||||
amountSpecified ? debouncedAmount : undefined,
|
||||
debouncedOtherCurrency,
|
||||
amountSpecified,
|
||||
otherCurrency,
|
||||
routerPreferenceOverride ?? routerPreference,
|
||||
!(autoRouterSupported && shouldGetTrade), // skip fetching
|
||||
skipFetch,
|
||||
account
|
||||
)
|
||||
|
||||
const inDebounce =
|
||||
(!debouncedAmount && Boolean(amountSpecified)) || (!debouncedOtherCurrency && Boolean(otherCurrency))
|
||||
const isLoading = routingAPITrade.state === TradeState.LOADING || inDebounce
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
...routingAPITrade,
|
||||
...(isLoading ? { state: TradeState.LOADING } : {}),
|
||||
}),
|
||||
[isLoading, routingAPITrade]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Connector } from '@web3-react/types'
|
||||
import { gnosisSafeConnection, networkConnection } from 'connection'
|
||||
import { getConnection } from 'connection'
|
||||
import { Connection } from 'connection/types'
|
||||
import { useEffect } from 'react'
|
||||
import { useAppDispatch, useAppSelector } from 'state/hooks'
|
||||
import { updateSelectedWallet } from 'state/user/reducer'
|
||||
@@ -22,22 +21,25 @@ export default function useEagerlyConnect() {
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const selectedWallet = useAppSelector((state) => state.user.selectedWallet)
|
||||
|
||||
let selectedConnection: Connection | undefined
|
||||
if (selectedWallet) {
|
||||
try {
|
||||
selectedConnection = getConnection(selectedWallet)
|
||||
} catch {
|
||||
dispatch(updateSelectedWallet({ wallet: undefined }))
|
||||
}
|
||||
}
|
||||
const rehydrated = useAppSelector((state) => state._persist.rehydrated)
|
||||
|
||||
useEffect(() => {
|
||||
connect(gnosisSafeConnection.connector)
|
||||
connect(networkConnection.connector)
|
||||
try {
|
||||
connect(gnosisSafeConnection.connector)
|
||||
connect(networkConnection.connector)
|
||||
|
||||
if (selectedConnection) {
|
||||
connect(selectedConnection.connector)
|
||||
} // The dependency list is empty so this is only run once on mount
|
||||
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
if (!selectedWallet) return
|
||||
const selectedConnection = getConnection(selectedWallet)
|
||||
|
||||
if (selectedConnection) {
|
||||
connect(selectedConnection.connector)
|
||||
}
|
||||
} catch {
|
||||
// only clear the persisted wallet type if it failed to connect.
|
||||
if (rehydrated) {
|
||||
dispatch(updateSelectedWallet({ wallet: undefined }))
|
||||
}
|
||||
return
|
||||
}
|
||||
}, [dispatch, rehydrated, selectedWallet])
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { signTypedData } from '@uniswap/conedison/provider/signing'
|
||||
import { AllowanceTransfer, MaxAllowanceTransferAmount, PERMIT2_ADDRESS, PermitSingle } from '@uniswap/permit2-sdk'
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
@@ -9,6 +8,7 @@ import { useSingleCallResult } from 'lib/hooks/multicall'
|
||||
import ms from 'ms'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { toReadableError, UserRejectedRequestError } from 'utils/errors'
|
||||
import { signTypedData } from 'utils/signing'
|
||||
import { didUserReject } from 'utils/swapErrorToUserReadableMessage'
|
||||
|
||||
const PERMIT_EXPIRATION = ms(`30d`)
|
||||
@@ -77,7 +77,6 @@ export function useUpdatePermitAllowance(
|
||||
}
|
||||
|
||||
const { domain, types, values } = AllowanceTransfer.getPermitData(permit, PERMIT2_ADDRESS, chainId)
|
||||
// Use conedison's signTypedData for better x-wallet compatibility.
|
||||
const signature = await signTypedData(provider.getSigner(account), domain, types, values)
|
||||
onPermitSignature?.({ ...permit, signature })
|
||||
return
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ChainId, Currency, CurrencyAmount, Price, TradeType } from '@uniswap/sd
|
||||
import { nativeOnChain } from 'constants/tokens'
|
||||
import { Chain, useTokenSpotPriceQuery } from 'graphql/data/__generated__/types-and-hooks'
|
||||
import { chainIdToBackendName, isGqlSupportedChain, PollingInterval } from 'graphql/data/util'
|
||||
import { useMemo } from 'react'
|
||||
import { INTERNAL_ROUTER_PREFERENCE_PRICE, TradeState } from 'state/routing/types'
|
||||
import { useRoutingAPITrade } from 'state/routing/useRoutingAPITrade'
|
||||
import { getNativeTokenDBAddress } from 'utils/nativeTokens'
|
||||
@@ -19,66 +20,89 @@ const ETH_AMOUNT_OUT: { [chainId: number]: CurrencyAmount<Currency> } = {
|
||||
[ChainId.CELO]: CurrencyAmount.fromRawAmount(nativeOnChain(ChainId.CELO), 10e18),
|
||||
}
|
||||
|
||||
function useETHValue(currencyAmount?: CurrencyAmount<Currency>): {
|
||||
data?: CurrencyAmount<Currency>
|
||||
function useETHPrice(currency?: Currency): {
|
||||
data?: Price<Currency, Currency>
|
||||
isLoading: boolean
|
||||
} {
|
||||
const chainId = currencyAmount?.currency?.chainId
|
||||
const amountOut = isGqlSupportedChain(chainId) ? ETH_AMOUNT_OUT[chainId] : undefined
|
||||
const chainId = currency?.chainId
|
||||
const isSupported = currency && isGqlSupportedChain(chainId)
|
||||
|
||||
const amountOut = isSupported ? ETH_AMOUNT_OUT[chainId] : undefined
|
||||
const { trade, state } = useRoutingAPITrade(
|
||||
TradeType.EXACT_OUTPUT,
|
||||
amountOut,
|
||||
currencyAmount?.currency,
|
||||
INTERNAL_ROUTER_PREFERENCE_PRICE
|
||||
currency,
|
||||
INTERNAL_ROUTER_PREFERENCE_PRICE,
|
||||
!isSupported
|
||||
)
|
||||
|
||||
// Get ETH value of ETH or WETH
|
||||
if (chainId && currencyAmount && currencyAmount.currency.wrapped.equals(nativeOnChain(chainId).wrapped)) {
|
||||
return {
|
||||
data: new Price(currencyAmount.currency, currencyAmount.currency, '1', '1').quote(currencyAmount),
|
||||
isLoading: false,
|
||||
return useMemo(() => {
|
||||
if (!isSupported) {
|
||||
return { data: undefined, isLoading: false }
|
||||
}
|
||||
}
|
||||
|
||||
if (!trade || state === TradeState.LOADING || !currencyAmount?.currency || !isGqlSupportedChain(chainId)) {
|
||||
return { data: undefined, isLoading: state === TradeState.LOADING }
|
||||
}
|
||||
if (currency?.wrapped.equals(nativeOnChain(chainId).wrapped)) {
|
||||
return {
|
||||
data: new Price(currency, currency, '1', '1'),
|
||||
isLoading: false,
|
||||
}
|
||||
}
|
||||
|
||||
const { numerator, denominator } = trade.routes[0].midPrice
|
||||
const price = new Price(currencyAmount?.currency, nativeOnChain(chainId), denominator, numerator)
|
||||
return { data: price.quote(currencyAmount), isLoading: false }
|
||||
if (!trade || state === TradeState.LOADING) {
|
||||
return { data: undefined, isLoading: state === TradeState.LOADING }
|
||||
}
|
||||
|
||||
const { numerator, denominator } = trade.routes[0].midPrice
|
||||
const price = new Price(currency, nativeOnChain(chainId), denominator, numerator)
|
||||
return { data: price, isLoading: false }
|
||||
}, [chainId, currency, isSupported, state, trade])
|
||||
}
|
||||
|
||||
// TODO(WEB-2095): This hook should early return `null` when `currencyAmount` is undefined. Otherwise,
|
||||
// it is not possible to differentiate between a loading state and a state where `currencyAmount`
|
||||
// is undefined
|
||||
export function useUSDPrice(currencyAmount?: CurrencyAmount<Currency>): {
|
||||
export function useUSDPrice(
|
||||
currencyAmount?: CurrencyAmount<Currency>,
|
||||
prefetchCurrency?: Currency
|
||||
): {
|
||||
data?: number
|
||||
isLoading: boolean
|
||||
} {
|
||||
const chain = currencyAmount?.currency.chainId ? chainIdToBackendName(currencyAmount?.currency.chainId) : undefined
|
||||
const currency = currencyAmount?.currency
|
||||
const { data: ethValue, isLoading: isEthValueLoading } = useETHValue(currencyAmount)
|
||||
const currency = currencyAmount?.currency ?? prefetchCurrency
|
||||
const chainId = currency?.chainId
|
||||
const chain = chainId ? chainIdToBackendName(chainId) : undefined
|
||||
|
||||
// Use ETH-based pricing if available.
|
||||
const { data: tokenEthPrice, isLoading: isTokenEthPriceLoading } = useETHPrice(currency)
|
||||
const isTokenEthPriced = Boolean(tokenEthPrice || isTokenEthPriceLoading)
|
||||
const { data, networkStatus } = useTokenSpotPriceQuery({
|
||||
variables: { chain: chain ?? Chain.Ethereum, address: getNativeTokenDBAddress(chain ?? Chain.Ethereum) },
|
||||
skip: !chain || !isGqlSupportedChain(currency?.chainId),
|
||||
skip: !isTokenEthPriced,
|
||||
pollInterval: PollingInterval.Normal,
|
||||
notifyOnNetworkStatusChange: true,
|
||||
fetchPolicy: 'cache-first',
|
||||
})
|
||||
|
||||
// Use USDC price for chains not supported by backend yet
|
||||
const stablecoinPrice = useStablecoinPrice(!isGqlSupportedChain(currency?.chainId) ? currency : undefined)
|
||||
if (!isGqlSupportedChain(currency?.chainId) && currencyAmount && stablecoinPrice) {
|
||||
return { data: parseFloat(stablecoinPrice.quote(currencyAmount).toSignificant()), isLoading: false }
|
||||
}
|
||||
// Use USDC-based pricing for chains not yet supported by backend (for ETH-based pricing).
|
||||
const stablecoinPrice = useStablecoinPrice(isTokenEthPriced ? undefined : currency)
|
||||
|
||||
const isFirstLoad = networkStatus === NetworkStatus.loading
|
||||
|
||||
// Otherwise, get the price of the token in ETH, and then multiple by the price of ETH
|
||||
const ethUSDPrice = data?.token?.project?.markets?.[0]?.price?.value
|
||||
if (!ethUSDPrice || !ethValue) return { data: undefined, isLoading: isEthValueLoading || isFirstLoad }
|
||||
|
||||
return { data: parseFloat(ethValue.toExact()) * ethUSDPrice, isLoading: false }
|
||||
return useMemo(() => {
|
||||
if (!currencyAmount) {
|
||||
return { data: undefined, isLoading: false }
|
||||
} else if (stablecoinPrice) {
|
||||
return { data: parseFloat(stablecoinPrice.quote(currencyAmount).toSignificant()), isLoading: false }
|
||||
} else {
|
||||
// Otherwise, get the price of the token in ETH, and then multiply by the price of ETH.
|
||||
const ethUSDPrice = data?.token?.project?.markets?.[0]?.price?.value
|
||||
if (ethUSDPrice && tokenEthPrice) {
|
||||
return { data: parseFloat(tokenEthPrice.quote(currencyAmount).toExact()) * ethUSDPrice, isLoading: false }
|
||||
} else {
|
||||
return { data: undefined, isLoading: isTokenEthPriceLoading || networkStatus === NetworkStatus.loading }
|
||||
}
|
||||
}
|
||||
}, [
|
||||
currencyAmount,
|
||||
data?.token?.project?.markets,
|
||||
tokenEthPrice,
|
||||
isTokenEthPriceLoading,
|
||||
networkStatus,
|
||||
stablecoinPrice,
|
||||
])
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import * as Sentry from '@sentry/react'
|
||||
import { SwapEventName } from '@uniswap/analytics-events'
|
||||
import { signTypedData } from '@uniswap/conedison/provider/signing'
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
import { DutchOrder, DutchOrderBuilder } from '@uniswap/uniswapx-sdk'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
@@ -11,6 +10,7 @@ import { useCallback } from 'react'
|
||||
import { DutchOrderTrade, TradeFillType } from 'state/routing/types'
|
||||
import { trace } from 'tracing/trace'
|
||||
import { UserRejectedRequestError } from 'utils/errors'
|
||||
import { signTypedData } from 'utils/signing'
|
||||
import { didUserReject, swapErrorToUserReadableMessage } from 'utils/swapErrorToUserReadableMessage'
|
||||
|
||||
const DEFAULT_START_TIME_PADDING_SECONDS = 30
|
||||
@@ -105,6 +105,7 @@ export function useUniswapXSwapCallback({
|
||||
}
|
||||
}
|
||||
|
||||
const beforeSign = Date.now()
|
||||
const { signature, updatedOrder } = await signDutchOrder()
|
||||
|
||||
sendAnalyticsEvent(SwapEventName.SWAP_SIGNED, {
|
||||
@@ -112,6 +113,7 @@ export function useUniswapXSwapCallback({
|
||||
trade,
|
||||
allowedSlippage,
|
||||
fiatValues,
|
||||
timeToSignSinceRequestMs: Date.now() - beforeSign,
|
||||
}),
|
||||
...analyticsContext,
|
||||
})
|
||||
|
||||
@@ -94,6 +94,7 @@ export function useUniversalRouterSwapCallback(
|
||||
}
|
||||
const gasLimit = calculateGasMargin(gasEstimate)
|
||||
setTraceData('gasLimit', gasLimit.toNumber())
|
||||
const beforeSign = Date.now()
|
||||
const response = await provider
|
||||
.getSigner()
|
||||
.sendTransaction({ ...tx, gasLimit })
|
||||
@@ -101,6 +102,7 @@ export function useUniversalRouterSwapCallback(
|
||||
sendAnalyticsEvent(SwapEventName.SWAP_SIGNED, {
|
||||
...formatSwapSignedAnalyticsEventProperties({
|
||||
trade,
|
||||
timeToSignSinceRequestMs: Date.now() - beforeSign,
|
||||
allowedSlippage: options.slippageTolerance,
|
||||
fiatValues,
|
||||
txHash: response.hash,
|
||||
|
||||
@@ -12,9 +12,8 @@ import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { QueryClient, QueryClientProvider } from 'react-query'
|
||||
import { Provider } from 'react-redux'
|
||||
import { BrowserRouter, HashRouter } from 'react-router-dom'
|
||||
import { HashRouter } from 'react-router-dom'
|
||||
import { SystemThemeUpdater } from 'theme/components/ThemeToggle'
|
||||
import { isBrowserRouterEnabled } from 'utils/env'
|
||||
|
||||
import Web3Provider from './components/Web3Provider'
|
||||
import { LanguageProvider } from './i18n'
|
||||
@@ -52,14 +51,12 @@ const queryClient = new QueryClient()
|
||||
|
||||
const container = document.getElementById('root') as HTMLElement
|
||||
|
||||
const Router = isBrowserRouterEnabled() ? BrowserRouter : HashRouter
|
||||
|
||||
createRoot(container).render(
|
||||
<StrictMode>
|
||||
<Provider store={store}>
|
||||
<FeatureFlagsProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Router>
|
||||
<HashRouter>
|
||||
<LanguageProvider>
|
||||
<Web3Provider>
|
||||
<ApolloProvider client={apolloClient}>
|
||||
@@ -73,7 +70,7 @@ createRoot(container).render(
|
||||
</ApolloProvider>
|
||||
</Web3Provider>
|
||||
</LanguageProvider>
|
||||
</Router>
|
||||
</HashRouter>
|
||||
</QueryClientProvider>
|
||||
</FeatureFlagsProvider>
|
||||
</Provider>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user