Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b90fe137e | ||
|
|
05b2711a8a | ||
|
|
d060782242 | ||
|
|
e19e8492c9 | ||
|
|
800b5e0bda | ||
|
|
fc637071f9 | ||
|
|
1b78ceec10 | ||
|
|
e5be3ebf8f | ||
|
|
1c73719766 | ||
|
|
14c91f9bba | ||
|
|
4b762ef5c9 | ||
|
|
c82b4fae64 | ||
|
|
ab8c1e3e90 | ||
|
|
7055d60406 | ||
|
|
c641cec651 | ||
|
|
b6a47c734f | ||
|
|
7aecf5d398 | ||
|
|
5bf2b81743 | ||
|
|
ed247065a7 | ||
|
|
0d0ad633fb | ||
|
|
4a8f1d9b96 | ||
|
|
043fb95d22 | ||
|
|
06536bc925 | ||
|
|
a598a15799 | ||
|
|
b0265c081e | ||
|
|
47aff6ff74 | ||
|
|
56717005e6 | ||
|
|
b50d10cbb2 | ||
|
|
ce96873a72 | ||
|
|
779625a04e | ||
|
|
d1e0812684 | ||
|
|
98e62b4f93 | ||
|
|
9fb0d424c2 | ||
|
|
8d145b908e | ||
|
|
c7633d910b | ||
|
|
1f89a46a3f | ||
|
|
8d54b01878 | ||
|
|
ffe334ccbf | ||
|
|
ffe2bd315e | ||
|
|
cee4b8c77a | ||
|
|
3153db9f73 | ||
|
|
bbdb5f3f56 | ||
|
|
7f9c56b68c | ||
|
|
2b69974fdc | ||
|
|
5236065769 | ||
|
|
52128a2dcd | ||
|
|
c9642c6cd0 | ||
|
|
b878d764e5 | ||
|
|
6a4f067ac0 | ||
|
|
e9407bb6bd | ||
|
|
8d822fd0e0 | ||
|
|
6404ee6e0b | ||
|
|
8ac3ed1128 | ||
|
|
b501974a76 | ||
|
|
567fb0181c | ||
|
|
8a37c427e6 | ||
|
|
034b3e3e58 | ||
|
|
053000e5fc | ||
|
|
b77e7deb49 | ||
|
|
c3321ae793 | ||
|
|
5dec0cf72b | ||
|
|
1efda07e7a | ||
|
|
fd819260f9 | ||
|
|
8e3b2cb4b8 | ||
|
|
d54783a324 | ||
|
|
850a20f6ad | ||
|
|
99f681818f | ||
|
|
1127e74357 | ||
|
|
27843f6189 | ||
|
|
1b10c88c51 | ||
|
|
5d97cbf6ad | ||
|
|
064a73ca1b | ||
|
|
e5a1cb4276 | ||
|
|
e68e1afd9d | ||
|
|
8784a761d6 | ||
|
|
7aa0f500d6 | ||
|
|
06a8151ede | ||
|
|
ac2642fedc | ||
|
|
ac962fb00d | ||
|
|
f3bcf64144 | ||
|
|
711b2ca85c | ||
|
|
aa97ec01d3 | ||
|
|
83b70f3aa6 | ||
|
|
e37dd77680 | ||
|
|
aa37b23126 |
@@ -59,13 +59,17 @@
|
||||
{
|
||||
"paths": [
|
||||
{
|
||||
"name": "@lingui/macro",
|
||||
"importNames": ["t"],
|
||||
"message": "Please use <Trans> instead of t."
|
||||
"name": "ethers",
|
||||
"message": "Please import from '@ethersproject/module' directly to support tree-shaking."
|
||||
},
|
||||
{
|
||||
"name": "styled-components",
|
||||
"message": "Please import from styled-components/macro."
|
||||
},
|
||||
{
|
||||
"name": "@lingui/macro",
|
||||
"importNames": ["t"],
|
||||
"message": "Please use <Trans> instead of t."
|
||||
}
|
||||
],
|
||||
"patterns": [
|
||||
@@ -73,10 +77,6 @@
|
||||
"group": ["**/dist"],
|
||||
"message": "Do not import from dist/ - this is an implementation detail, and breaks tree-shaking."
|
||||
},
|
||||
{
|
||||
"group": ["@ethersproject"],
|
||||
"message": "Please import from 'ethers' to support bundling."
|
||||
},
|
||||
{
|
||||
"group": ["!styled-components/macro"]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Bundle Dependency Check
|
||||
name: Widgets
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -8,8 +8,8 @@ on:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
depcheck:
|
||||
name: Bundle depcheck
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -36,8 +36,5 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Bundle
|
||||
run: yarn bundle
|
||||
|
||||
- name: Depcheck
|
||||
run: yarn bundle:depcheck
|
||||
- name: Build
|
||||
run: yarn widgets:build
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -11,10 +11,6 @@
|
||||
/src/locales/**/pseudo.po
|
||||
/src/state/data/generated.ts
|
||||
|
||||
# generated assets
|
||||
/src/lib/assets/svg/*.tsx
|
||||
/src/lib/assets/fonts/*.css
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
@@ -24,7 +20,7 @@
|
||||
# production
|
||||
/build
|
||||
|
||||
# bundle
|
||||
# widgets
|
||||
/dist
|
||||
|
||||
# misc
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const { DefinePlugin } = require('webpack')
|
||||
|
||||
// Renders the cosmos fixtures in isolation, instead of using public/index.html.
|
||||
module.exports = (webpackConfig) => ({
|
||||
...webpackConfig,
|
||||
plugins: webpackConfig.plugins.map((plugin) =>
|
||||
plugin instanceof HtmlWebpackPlugin
|
||||
? new HtmlWebpackPlugin({
|
||||
templateContent: '<body></body>',
|
||||
})
|
||||
: plugin
|
||||
),
|
||||
plugins: webpackConfig.plugins.map((plugin) => {
|
||||
if (plugin instanceof HtmlWebpackPlugin) {
|
||||
return new HtmlWebpackPlugin({
|
||||
templateContent: '<body></body>',
|
||||
})
|
||||
}
|
||||
if (plugin instanceof DefinePlugin) {
|
||||
return new DefinePlugin({
|
||||
...plugin.definitions,
|
||||
'process.env': {
|
||||
...plugin.definitions['process.env'],
|
||||
REACT_APP_IS_WIDGET: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
return plugin
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
|
||||
import { providers, Wallet } from 'ethers'
|
||||
import { JsonRpcProvider } from '@ethersproject/providers'
|
||||
import { Wallet } from '@ethersproject/wallet'
|
||||
|
||||
// todo: figure out how env vars actually work in CI
|
||||
// const TEST_PRIVATE_KEY = Cypress.env('INTEGRATION_TEST_PRIVATE_KEY')
|
||||
@@ -81,7 +81,7 @@ Cypress.Commands.overwrite('visit', (original, url, options) => {
|
||||
onBeforeLoad(win) {
|
||||
options && options.onBeforeLoad && options.onBeforeLoad(win)
|
||||
win.localStorage.clear()
|
||||
const provider = new providers.JsonRpcProvider('https://rinkeby.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847', 4)
|
||||
const provider = new JsonRpcProvider('https://rinkeby.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847', 4)
|
||||
const signer = new Wallet(TEST_PRIVATE_KEY, provider)
|
||||
win.ethereum = new CustomizedBridge(signer, provider)
|
||||
},
|
||||
|
||||
28
depcheck.js
28
depcheck.js
@@ -1,28 +0,0 @@
|
||||
#!/bin/node
|
||||
/**
|
||||
* Checks if any dependencies have been bundled with the interface library.
|
||||
* Exits with non-zero status if dependencies are included in the bundle.
|
||||
*/
|
||||
/* eslint-disable */
|
||||
|
||||
const { readFile } = require('fs')
|
||||
|
||||
function checkDeps(err, sourcemap) {
|
||||
if (err) {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const includesDeps = sourcemap.includes('node_modules')
|
||||
if (includesDeps) {
|
||||
const deps = [...sourcemap.toString().matchAll(/node_modules[\\\/]([^\\\/]*)/g)].map(([, match]) => match)
|
||||
console.error(`
|
||||
Sourcemap includes node_modules folder(s). External deps must be bundled under "dependencies".
|
||||
|
||||
To fix, run: \`yarn add ${deps.join(' ')}\`
|
||||
`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
readFile('dist/interface.esm.js.map', checkDeps)
|
||||
59
package.json
59
package.json
@@ -23,7 +23,11 @@
|
||||
"@reach/portal": "^0.10.3",
|
||||
"@react-hook/window-scroll": "^1.3.0",
|
||||
"@reduxjs/toolkit": "^1.6.1",
|
||||
"@svgr/cli": "^5.5.0",
|
||||
"@rollup/plugin-eslint": "^8.0.1",
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@rollup/plugin-replace": "^3.0.1",
|
||||
"@rollup/plugin-url": "^6.1.0",
|
||||
"@svgr/rollup": "^6.2.0",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^12.0.0",
|
||||
"@testing-library/react-hooks": "^7.0.2",
|
||||
@@ -52,23 +56,19 @@
|
||||
"@types/wcag-contrast": "^3.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.1.0",
|
||||
"@typescript-eslint/parser": "^4.1.0",
|
||||
"@uniswap/default-token-list": "^3.0.0",
|
||||
"@uniswap/governance": "^1.0.2",
|
||||
"@uniswap/liquidity-staker": "^1.0.2",
|
||||
"@uniswap/merkle-distributor": "1.0.1",
|
||||
"@uniswap/router-sdk": "^1.0.3",
|
||||
"@uniswap/smart-order-router": "^2.5.9",
|
||||
"@uniswap/v2-core": "1.0.0",
|
||||
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
||||
"@uniswap/v2-sdk": "^3.0.1",
|
||||
"@uniswap/v3-core": "1.0.0",
|
||||
"@uniswap/v3-periphery": "^1.1.1",
|
||||
"@uniswap/v3-sdk": "^3.7.1",
|
||||
"@web3-react/core": "^6.0.9",
|
||||
"@web3-react/fortmatic-connector": "^6.0.9",
|
||||
"@web3-react/injected-connector": "^6.0.7",
|
||||
"@web3-react/portis-connector": "^6.0.9",
|
||||
"@web3-react/walletconnect-connector": "^7.0.2-alpha.0",
|
||||
"@web3-react/walletlink-connector": "^6.2.8",
|
||||
"@web3-react/walletlink-connector": "^6.2.11",
|
||||
"array.prototype.flat": "^1.2.4",
|
||||
"array.prototype.flatmap": "^1.2.4",
|
||||
"copy-to-clipboard": "^3.2.0",
|
||||
@@ -88,22 +88,23 @@
|
||||
"graphql-request": "^3.4.0",
|
||||
"inter-ui": "^3.13.1",
|
||||
"jest-styled-components": "^7.0.5",
|
||||
"microbundle": "^0.13.3",
|
||||
"ms.macro": "^2.0.0",
|
||||
"polyfill-object.fromentries": "^1.0.1",
|
||||
"prettier": "^2.2.1",
|
||||
"qs": "^6.9.4",
|
||||
"react-confetti": "^6.0.0",
|
||||
"react-cosmos": "^5.6.3",
|
||||
"react-cosmos": "^5.6.6",
|
||||
"react-ga": "^2.5.7",
|
||||
"react-is": "^17.0.2",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-redux": "^7.2.2",
|
||||
"react-router-dom": "^5.0.0",
|
||||
"react-scripts": "^4.0.3",
|
||||
"react-spring": "^8.0.27",
|
||||
"react-use-gesture": "^6.0.14",
|
||||
"redux-localstorage-simple": "^2.3.1",
|
||||
"rollup": "^2.63.0",
|
||||
"rollup-plugin-dts": "^4.1.0",
|
||||
"rollup-plugin-scss": "^3.0.0",
|
||||
"rollup-plugin-typescript2": "^0.31.1",
|
||||
"sass": "^1.45.1",
|
||||
"serve": "^11.3.2",
|
||||
"start-server-and-test": "^1.11.0",
|
||||
@@ -130,17 +131,13 @@
|
||||
"i18n:extract": "lingui extract --locale en-US",
|
||||
"i18n:compile": "yarn i18n:extract && lingui compile",
|
||||
"i18n:pseudo": "lingui extract --locale pseudo && lingui compile",
|
||||
"postinstall": "yarn contracts:compile && yarn graphql:generate && yarn i18n:compile && yarn assets:generate",
|
||||
"postinstall": "yarn contracts:compile && yarn graphql:generate && yarn i18n:compile",
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=./custom-test-env.js",
|
||||
"test:e2e": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'",
|
||||
"assets:generate": "yarn assets:svg:generate && yarn assets:font:generate",
|
||||
"assets:svg:generate": "svgr -d src/lib/assets/svg --ext tsx --typescript src/lib/assets/svg && rm src/lib/assets/svg/index.tsx",
|
||||
"assets:font:generate": "sass src/lib/assets/fonts/index.scss src/lib/assets/fonts/index.css --no-source-map -I node_modules",
|
||||
"bundle": "microbundle --define process.env.REACT_APP_IS_WIDGET=true --tsconfig tsconfig.lib.json src/lib/index.tsx --format esm,cjs",
|
||||
"bundle:depcheck": "node depcheck.js",
|
||||
"cosmos": "cross-env FAST_REFRESH=false REACT_APP_IS_WIDGET=true cosmos"
|
||||
"test:e2e": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run --record'",
|
||||
"widgets:start": "cosmos",
|
||||
"widgets:build": "rollup --config --failAfterWarnings --configPlugin typescript2"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
@@ -156,6 +153,19 @@
|
||||
},
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@ethersproject/abi": "^5.4.1",
|
||||
"@ethersproject/abstract-provider": "^5.4.1",
|
||||
"@ethersproject/address": "^5.4.0",
|
||||
"@ethersproject/bignumber": "^5.4.2",
|
||||
"@ethersproject/bytes": "^5.4.0",
|
||||
"@ethersproject/constants": "^5.4.0",
|
||||
"@ethersproject/contracts": "^5.4.1",
|
||||
"@ethersproject/hash": "^5.4.0",
|
||||
"@ethersproject/providers": "5.4.0",
|
||||
"@ethersproject/solidity": "^5.4.0",
|
||||
"@ethersproject/strings": "^5.4.0",
|
||||
"@ethersproject/units": "^5.4.0",
|
||||
"@ethersproject/wallet": "^5.4.0",
|
||||
"@fontsource/ibm-plex-mono": "^4.5.1",
|
||||
"@fontsource/inter": "^4.5.1",
|
||||
"@lingui/core": "^3.9.0",
|
||||
@@ -163,14 +173,20 @@
|
||||
"@lingui/react": "^3.9.0",
|
||||
"@popperjs/core": "^2.4.4",
|
||||
"@uniswap/redux-multicall": "^1.0.0",
|
||||
"@uniswap/router-sdk": "^1.0.3",
|
||||
"@uniswap/sdk-core": "^3.0.1",
|
||||
"@uniswap/smart-order-router": "^2.5.10",
|
||||
"@uniswap/token-lists": "^1.0.0-beta.27",
|
||||
"@uniswap/v2-sdk": "^3.0.1",
|
||||
"@uniswap/v3-sdk": "^3.7.1",
|
||||
"@web3-react/core": "^6.0.9",
|
||||
"ajv": "^6.12.3",
|
||||
"cids": "^1.0.0",
|
||||
"ethers": "^5.4.6",
|
||||
"immer": "^9.0.6",
|
||||
"jotai": "^1.3.7",
|
||||
"jsbi": "^3.1.4",
|
||||
"make-plural": "^7.0.0",
|
||||
"ms.macro": "^2.0.0",
|
||||
"multicodec": "^3.0.1",
|
||||
"multihashes": "^4.0.2",
|
||||
"node-vibrant": "^3.2.1-alpha.1",
|
||||
@@ -180,6 +196,7 @@
|
||||
"react-dom": "^17.0.1",
|
||||
"react-feather": "^2.0.8",
|
||||
"react-popper": "^2.2.3",
|
||||
"react-redux": "^7.2.2",
|
||||
"react-virtualized-auto-sizer": "^1.0.2",
|
||||
"react-window": "^1.8.5",
|
||||
"rebass": "^4.0.7",
|
||||
@@ -195,4 +212,4 @@
|
||||
"widgets-web3-react/types": "npm:@web3-react/types@8.0.16-alpha.0",
|
||||
"widgets-web3-react/url": "npm:@web3-react/url@8.0.17-alpha.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
63
rollup.config.ts
Normal file
63
rollup.config.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Bundles the widgets library, which is released independently of the interface application.
|
||||
* This library lives in src/lib, but shares code with the interface application.
|
||||
*/
|
||||
|
||||
import eslint from '@rollup/plugin-eslint'
|
||||
import json from '@rollup/plugin-json'
|
||||
import replace from '@rollup/plugin-replace'
|
||||
import url from '@rollup/plugin-url'
|
||||
import svgr from '@svgr/rollup'
|
||||
import dts from 'rollup-plugin-dts'
|
||||
import sass from 'rollup-plugin-scss'
|
||||
import typescript from 'rollup-plugin-typescript2'
|
||||
|
||||
import { dependencies } from './package.json'
|
||||
|
||||
const deps = Object.keys(dependencies)
|
||||
|
||||
const replacements = {
|
||||
'process.env.REACT_APP_IS_WIDGET': true,
|
||||
}
|
||||
|
||||
const library = {
|
||||
input: 'src/lib/index.tsx',
|
||||
output: [
|
||||
{
|
||||
file: 'dist/widgets.js',
|
||||
format: 'cjs',
|
||||
inlineDynamicImports: true,
|
||||
sourcemap: true,
|
||||
},
|
||||
{
|
||||
file: 'dist/widgets.esm.js',
|
||||
format: 'esm',
|
||||
inlineDynamicImports: true,
|
||||
sourcemap: true,
|
||||
},
|
||||
],
|
||||
// necessary because some nested imports (eg jotai/*) would otherwise not resolve.
|
||||
external: (source: string) => Boolean(deps.find((dep) => source === dep || source.startsWith(dep + '/'))),
|
||||
plugins: [
|
||||
eslint({ include: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'] }),
|
||||
json(), // imports json
|
||||
replace({ ...replacements, preventAssignment: true }),
|
||||
url(), // imports files (including svgs) as data URIs
|
||||
svgr({ exportType: 'named', svgo: false }), // imports svgs as React components
|
||||
sass(), // imports sass styles
|
||||
typescript({ tsconfig: './tsconfig.lib.json', useTsconfigDeclarationDir: true }),
|
||||
],
|
||||
}
|
||||
|
||||
const typings = {
|
||||
input: 'dist/dts/lib/index.d.ts',
|
||||
output: {
|
||||
file: 'dist/widgets.d.ts',
|
||||
format: 'es',
|
||||
},
|
||||
external: (source: string) => source.endsWith('.scss'),
|
||||
plugins: [dts({ compilerOptions: { baseUrl: 'dist/dts' } })],
|
||||
}
|
||||
|
||||
const config = [library, typings]
|
||||
export default config
|
||||
9
src/assets/images/survey-orb.svg
Normal file
9
src/assets/images/survey-orb.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 572 KiB |
@@ -1,7 +1,7 @@
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { CheckCircle, Triangle } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useAllTransactions } from '../../state/transactions/hooks'
|
||||
import { ExternalLink } from '../../theme'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { AbstractConnector } from '@web3-react/abstract-connector'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useCallback, useContext } from 'react'
|
||||
import { ExternalLink as LinkIcon } from 'react-feather'
|
||||
import { useAppDispatch } from 'state/hooks'
|
||||
@@ -9,7 +10,6 @@ import { Connector } from 'widgets-web3-react/types'
|
||||
import { ReactComponent as Close } from '../../assets/images/x.svg'
|
||||
import { injected, portis, walletlink } from '../../connectors'
|
||||
import { SUPPORTED_WALLETS } from '../../constants/wallet'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { clearAllTransactions } from '../../state/transactions/actions'
|
||||
import { ExternalLink, LinkStyledButton, ThemedText } from '../../theme'
|
||||
import { shortenAddress } from '../../utils'
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t } from '@lingui/macro'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { ReactNode, useCallback, useContext } from 'react'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import useENS from '../../hooks/useENS'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { ExternalLink, ThemedText } from '../../theme'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { AutoColumn } from '../Column'
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { ReactNode, useMemo } from 'react'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
|
||||
// SDN OFAC addresses
|
||||
const BLOCKED_ADDRESSES: string[] = [
|
||||
'0x7Db418b5D567A4e0E8c59Ad71BE1FcE48f3E6107',
|
||||
@@ -32,6 +31,8 @@ const BLOCKED_ADDRESSES: string[] = [
|
||||
'0x6acdfba02d390b97ac2b2d42a63e85293bcc160e',
|
||||
'0x48549a34ae37b12f6a30566245176994e17c6b4a',
|
||||
'0x5512d943ed1f7c8a43f3435c85f7ab68b30121b0',
|
||||
'0xc455f7fd3e0e12afd51fba5c106909934d8a0e4a',
|
||||
'0x629e7Da20197a5429d30da36E77d06CdF796b71A',
|
||||
]
|
||||
|
||||
export default function Blocklist({ children }: { children: ReactNode }) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Currency, CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
|
||||
import { Pair } from '@uniswap/v2-sdk'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { LoadingOpacityContainer, loadingOpacityMixin } from 'components/Loader/styled'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { darken } from 'polished'
|
||||
import { ReactNode, useCallback, useState } from 'react'
|
||||
import { Lock } from 'react-feather'
|
||||
@@ -11,7 +12,6 @@ import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
|
||||
|
||||
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
|
||||
import useTheme from '../../hooks/useTheme'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useCurrencyBalance } from '../../state/wallet/hooks'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { ButtonGray } from '../Button'
|
||||
|
||||
@@ -1,60 +1,18 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import EthereumLogo from 'assets/images/ethereum-logo.png'
|
||||
import MaticLogo from 'assets/svg/matic-token-icon.svg'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useHttpLocations from 'hooks/useHttpLocations'
|
||||
import React, { useMemo } from 'react'
|
||||
import { WrappedTokenInfo } from 'state/lists/wrappedTokenInfo'
|
||||
import useCurrencyLogoURIs from 'lib/hooks/useCurrencyLogoURIs'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import Logo from '../Logo'
|
||||
|
||||
type Network = 'ethereum' | 'arbitrum' | 'optimism'
|
||||
|
||||
function chainIdToNetworkName(networkId: SupportedChainId): Network {
|
||||
switch (networkId) {
|
||||
case SupportedChainId.MAINNET:
|
||||
return 'ethereum'
|
||||
case SupportedChainId.ARBITRUM_ONE:
|
||||
return 'arbitrum'
|
||||
case SupportedChainId.OPTIMISM:
|
||||
return 'optimism'
|
||||
default:
|
||||
return 'ethereum'
|
||||
}
|
||||
}
|
||||
|
||||
export const getTokenLogoURL = (
|
||||
address: string,
|
||||
chainId: SupportedChainId = SupportedChainId.MAINNET
|
||||
): string | void => {
|
||||
const networkName = chainIdToNetworkName(chainId)
|
||||
const networksWithUrls = [SupportedChainId.ARBITRUM_ONE, SupportedChainId.MAINNET, SupportedChainId.OPTIMISM]
|
||||
if (networksWithUrls.includes(chainId)) {
|
||||
return `https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/${networkName}/assets/${address}/logo.png`
|
||||
}
|
||||
}
|
||||
|
||||
const StyledNativeLogo = styled.img<{ size: string }>`
|
||||
width: ${({ size }) => size};
|
||||
height: ${({ size }) => size};
|
||||
background: radial-gradient(white 50%, #ffffff00 calc(75% + 1px), #ffffff00 100%);
|
||||
|
||||
border-radius: 50%;
|
||||
-mox-box-shadow: 0 0 1px white;
|
||||
-webkit-box-shadow: 0 0 1px white;
|
||||
box-shadow: 0 0 1px white;
|
||||
border: 0px solid rgba(255, 255, 255, 0);
|
||||
`
|
||||
|
||||
const StyledLogo = styled(Logo)<{ size: string }>`
|
||||
const StyledLogo = styled(Logo)<{ size: string; native: boolean }>`
|
||||
width: ${({ size }) => size};
|
||||
height: ${({ size }) => size};
|
||||
background: radial-gradient(white 50%, #ffffff00 calc(75% + 1px), #ffffff00 100%);
|
||||
border-radius: 50%;
|
||||
-mox-box-shadow: 0 0 1px black;
|
||||
-webkit-box-shadow: 0 0 1px black;
|
||||
box-shadow: 0 0 1px black;
|
||||
-mox-box-shadow: 0 0 1px ${({ native }) => (native ? 'white' : 'black')};
|
||||
-webkit-box-shadow: 0 0 1px ${({ native }) => (native ? 'white' : 'black')};
|
||||
box-shadow: 0 0 1px ${({ native }) => (native ? 'white' : 'black')};
|
||||
border: 0px solid rgba(255, 255, 255, 0);
|
||||
`
|
||||
|
||||
@@ -68,38 +26,16 @@ export default function CurrencyLogo({
|
||||
size?: string
|
||||
style?: React.CSSProperties
|
||||
}) {
|
||||
const uriLocations = useHttpLocations(currency instanceof WrappedTokenInfo ? currency.logoURI : undefined)
|
||||
const logoURIs = useCurrencyLogoURIs(currency)
|
||||
|
||||
const srcs: string[] = useMemo(() => {
|
||||
if (!currency || currency.isNative) return []
|
||||
|
||||
if (currency.isToken) {
|
||||
const defaultUrls = []
|
||||
const url = getTokenLogoURL(currency.address, currency.chainId)
|
||||
if (url) {
|
||||
defaultUrls.push(url)
|
||||
}
|
||||
if (currency instanceof WrappedTokenInfo) {
|
||||
return [...uriLocations, ...defaultUrls]
|
||||
}
|
||||
return defaultUrls
|
||||
}
|
||||
return []
|
||||
}, [currency, uriLocations])
|
||||
|
||||
if (currency?.isNative) {
|
||||
let nativeLogoUrl: string
|
||||
switch (currency.chainId) {
|
||||
case SupportedChainId.POLYGON_MUMBAI:
|
||||
case SupportedChainId.POLYGON:
|
||||
nativeLogoUrl = MaticLogo
|
||||
break
|
||||
default:
|
||||
nativeLogoUrl = EthereumLogo
|
||||
break
|
||||
}
|
||||
return <StyledNativeLogo src={nativeLogoUrl} alt="ethereum logo" size={size} style={style} {...rest} />
|
||||
}
|
||||
|
||||
return <StyledLogo size={size} srcs={srcs} alt={`${currency?.symbol ?? 'token'} logo`} style={style} {...rest} />
|
||||
return (
|
||||
<StyledLogo
|
||||
size={size}
|
||||
native={currency?.isNative ?? false}
|
||||
srcs={logoURIs}
|
||||
alt={`${currency?.symbol ?? 'token'} logo`}
|
||||
style={style}
|
||||
{...rest}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { AlertOctagon } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink } from 'theme'
|
||||
|
||||
@@ -5,10 +5,10 @@ import { ButtonGray } from 'components/Button'
|
||||
import Card from 'components/Card'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { RowBetween } from 'components/Row'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useFeeTierDistribution } from 'hooks/useFeeTierDistribution'
|
||||
import { PoolState, usePools } from 'hooks/usePools'
|
||||
import usePrevious from 'hooks/usePrevious'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { DynamicSection } from 'pages/AddLiquidity/styled'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import ReactGA from 'react-ga'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CHAIN_INFO, L2ChainInfo, SupportedChainId } from 'constants/chains'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { CHAIN_INFO, L2ChainInfo } from 'constants/chainInfo'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { AlertOctagon } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CHAIN_INFO, SupportedChainId } from 'constants/chains'
|
||||
import { CHAIN_INFO } from 'constants/chainInfo'
|
||||
import { CHAIN_IDS_TO_NAMES, SupportedChainId } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useOnClickOutside } from 'hooks/useOnClickOutside'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { useCallback, useRef } from 'react'
|
||||
import useParsedQueryString from 'hooks/useParsedQueryString'
|
||||
import usePrevious from 'hooks/usePrevious'
|
||||
import { ParsedQs } from 'qs'
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { ArrowDownCircle, ChevronDown } from 'react-feather'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
import { useModalOpen, useToggleModal } from 'state/application/hooks'
|
||||
import { addPopup, ApplicationModal } from 'state/application/reducer'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
|
||||
import { replaceURLParam } from 'utils/routes'
|
||||
|
||||
import { useAppDispatch } from '../../state/hooks'
|
||||
import { switchToNetwork } from '../../utils/switchToNetwork'
|
||||
@@ -210,31 +216,90 @@ function Row({
|
||||
return rowContent
|
||||
}
|
||||
|
||||
const getParsedChainId = (parsedQs?: ParsedQs) => {
|
||||
const chain = parsedQs?.chain
|
||||
if (!chain || typeof chain !== 'string') return { urlChain: undefined, urlChainId: undefined }
|
||||
|
||||
return { urlChain: chain.toLowerCase(), urlChainId: getChainIdFromName(chain) }
|
||||
}
|
||||
|
||||
const getChainIdFromName = (name: string) => {
|
||||
const entry = Object.entries(CHAIN_IDS_TO_NAMES).find(([_, n]) => n === name)
|
||||
const chainId = entry?.[0]
|
||||
return chainId ? parseInt(chainId) : undefined
|
||||
}
|
||||
|
||||
const getChainNameFromId = (id: string | number) => {
|
||||
// casting here may not be right but fine to return undefined if it's not a supported chain ID
|
||||
return CHAIN_IDS_TO_NAMES[id as SupportedChainId] || ''
|
||||
}
|
||||
|
||||
export default function NetworkSelector() {
|
||||
const { chainId, library } = useActiveWeb3React()
|
||||
const parsedQs = useParsedQueryString()
|
||||
const { urlChain, urlChainId } = getParsedChainId(parsedQs)
|
||||
const prevChainId = usePrevious(chainId)
|
||||
const node = useRef<HTMLDivElement>()
|
||||
const open = useModalOpen(ApplicationModal.NETWORK_SELECTOR)
|
||||
const toggle = useToggleModal(ApplicationModal.NETWORK_SELECTOR)
|
||||
useOnClickOutside(node, open ? toggle : undefined)
|
||||
|
||||
const history = useHistory()
|
||||
|
||||
const info = chainId ? CHAIN_INFO[chainId] : undefined
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const handleRowClick = useCallback(
|
||||
(targetChain: number) => {
|
||||
const handleChainSwitch = useCallback(
|
||||
(targetChain: number, skipToggle?: boolean) => {
|
||||
if (!library) return
|
||||
switchToNetwork({ library, chainId: targetChain })
|
||||
.then(() => toggle())
|
||||
.then(() => {
|
||||
if (!skipToggle) {
|
||||
toggle()
|
||||
}
|
||||
history.replace({
|
||||
search: replaceURLParam(history.location.search, 'chain', getChainNameFromId(targetChain)),
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to switch networks', error)
|
||||
toggle()
|
||||
|
||||
// we want app network <-> chainId param to be in sync, so if user changes the network by changing the URL
|
||||
// but the request fails, revert the URL back to current chainId
|
||||
if (chainId) {
|
||||
history.replace({ search: replaceURLParam(history.location.search, 'chain', getChainNameFromId(chainId)) })
|
||||
}
|
||||
|
||||
if (!skipToggle) {
|
||||
toggle()
|
||||
}
|
||||
|
||||
dispatch(addPopup({ content: { failedSwitchNetwork: targetChain }, key: `failed-network-switch` }))
|
||||
})
|
||||
},
|
||||
[dispatch, library, toggle]
|
||||
[dispatch, library, toggle, history, chainId]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!chainId || !prevChainId) return
|
||||
|
||||
// when network change originates from wallet or dropdown selector, just update URL
|
||||
if (chainId !== prevChainId) {
|
||||
history.replace({ search: replaceURLParam(history.location.search, 'chain', getChainNameFromId(chainId)) })
|
||||
// otherwise assume network change originates from URL
|
||||
} else if (urlChainId && urlChainId !== chainId) {
|
||||
handleChainSwitch(urlChainId, true)
|
||||
}
|
||||
}, [chainId, urlChainId, prevChainId, handleChainSwitch, history])
|
||||
|
||||
// set chain parameter on initial load if not there
|
||||
useEffect(() => {
|
||||
if (chainId && !urlChainId) {
|
||||
history.replace({ search: replaceURLParam(history.location.search, 'chain', getChainNameFromId(chainId)) })
|
||||
}
|
||||
}, [chainId, history, urlChainId, urlChain])
|
||||
|
||||
if (!chainId || !info || !library) {
|
||||
return null
|
||||
}
|
||||
@@ -251,10 +316,10 @@ export default function NetworkSelector() {
|
||||
<FlyoutHeader>
|
||||
<Trans>Select a network</Trans>
|
||||
</FlyoutHeader>
|
||||
<Row onSelectChain={handleRowClick} targetChain={SupportedChainId.MAINNET} />
|
||||
<Row onSelectChain={handleRowClick} targetChain={SupportedChainId.POLYGON} />
|
||||
<Row onSelectChain={handleRowClick} targetChain={SupportedChainId.OPTIMISM} />
|
||||
<Row onSelectChain={handleRowClick} targetChain={SupportedChainId.ARBITRUM_ONE} />
|
||||
<Row onSelectChain={handleChainSwitch} targetChain={SupportedChainId.MAINNET} />
|
||||
<Row onSelectChain={handleChainSwitch} targetChain={SupportedChainId.POLYGON} />
|
||||
<Row onSelectChain={handleChainSwitch} targetChain={SupportedChainId.OPTIMISM} />
|
||||
<Row onSelectChain={handleChainSwitch} targetChain={SupportedChainId.ARBITRUM_ONE} />
|
||||
</FlyoutMenu>
|
||||
)}
|
||||
</SelectorWrapper>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { RowFixed } from 'components/Row'
|
||||
import { CHAIN_INFO } from 'constants/chains'
|
||||
import { CHAIN_INFO } from 'constants/chainInfo'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
|
||||
import useGasPrice from 'hooks/useGasPrice'
|
||||
import useMachineTimeMs from 'hooks/useMachineTime'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import JSBI from 'jsbi'
|
||||
import useBlockNumber from 'lib/hooks/useBlockNumber'
|
||||
import ms from 'ms.macro'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useBlockNumber } from 'state/application/hooks'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
import { ExternalLink, ThemedText } from 'theme'
|
||||
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import useScrollPosition from '@react-hook/window-scroll'
|
||||
import { CHAIN_INFO, SupportedChainId } from 'constants/chains'
|
||||
import { CHAIN_INFO } from 'constants/chainInfo'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import { darken } from 'polished'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
@@ -13,7 +15,6 @@ import { useNativeCurrencyBalances } from 'state/wallet/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { ReactComponent as Logo } from '../../assets/svg/logo.svg'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { ExternalLink, ThemedText } from '../../theme'
|
||||
import ClaimModal from '../claim/ClaimModal'
|
||||
import { CardNoise } from '../earn/styled'
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import jazzicon from '@metamask/jazzicon'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import useENSAvatar from 'hooks/useENSAvatar'
|
||||
import { useLayoutEffect, useMemo, useRef, useState } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
|
||||
const StyledIdenticon = styled.div`
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import { usePoolActiveLiquidity } from 'hooks/usePoolTickData'
|
||||
import JSBI from 'jsbi'
|
||||
import { TickProcessed, usePoolActiveLiquidity } from 'hooks/usePoolTickData'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
|
||||
import { ChartEntry } from './types'
|
||||
|
||||
export interface TickProcessed {
|
||||
liquidityActive: JSBI
|
||||
price0: string
|
||||
}
|
||||
|
||||
export function useDensityChartData({
|
||||
currencyA,
|
||||
currencyB,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { PrivacyPolicyModal } from 'components/PrivacyPolicy'
|
||||
import { L2_CHAIN_IDS } from 'constants/chains'
|
||||
import { LOCALE_LABEL, SUPPORTED_LOCALES, SupportedLocale } from 'constants/locales'
|
||||
import { useActiveLocale } from 'hooks/useActiveLocale'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useLocationLinkProps } from 'hooks/useLocationLinkProps'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import {
|
||||
@@ -25,7 +26,6 @@ import styled, { css } from 'styled-components/macro'
|
||||
|
||||
import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg'
|
||||
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useModalOpen, useToggleModal } from '../../state/application/hooks'
|
||||
import { ApplicationModal } from '../../state/application/reducer'
|
||||
import { ExternalLink } from '../../theme'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useContext } from 'react'
|
||||
import { ArrowUpCircle } from 'react-feather'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import Circle from '../../assets/images/blue-loader.svg'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { CloseIcon, CustomLightSpinner, ThemedText } from '../../theme'
|
||||
import { ExternalLink } from '../../theme/components'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CHAIN_INFO } from 'constants/chainInfo'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { ArrowUpRight } from 'react-feather'
|
||||
import { useDarkModeManager } from 'state/user/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink, HideSmall } from 'theme'
|
||||
|
||||
import { CHAIN_INFO } from '../../constants/chains'
|
||||
import { AutoRow } from '../Row'
|
||||
|
||||
const L2Icon = styled.img`
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import { Heart, X } from 'react-feather'
|
||||
import ReactGA from 'react-ga'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
|
||||
import tokenLogo from '../../assets/images/token-logo.png'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import {
|
||||
useModalOpen,
|
||||
useShowClaimPopup,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CHAIN_INFO } from 'constants/chainInfo'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { useContext } from 'react'
|
||||
import { AlertCircle } from 'react-feather'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import { CHAIN_INFO, SupportedChainId } from '../../constants/chains'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { AutoRow } from '../Row'
|
||||
|
||||
106
src/components/Popups/SurveyPopup.tsx
Normal file
106
src/components/Popups/SurveyPopup.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { RowFixed } from 'components/Row'
|
||||
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
|
||||
import { useEffect } from 'react'
|
||||
import { MessageCircle, X } from 'react-feather'
|
||||
import ReactGA from 'react-ga'
|
||||
import { useShowSurveyPopup } from 'state/user/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink, ThemedText, Z_INDEX } from 'theme'
|
||||
|
||||
import BGImage from '../../assets/images/survey-orb.svg'
|
||||
import useTheme from '../../hooks/useTheme'
|
||||
|
||||
const Wrapper = styled(AutoColumn)`
|
||||
background: #edeef2;
|
||||
position: relative;
|
||||
border-radius: 12px;
|
||||
padding: 18px;
|
||||
max-width: 360px;
|
||||
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
|
||||
color: ${({ theme }) => theme.text1};
|
||||
overflow: hidden;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
max-width: 100%;
|
||||
`}
|
||||
`
|
||||
|
||||
const BGOrb = styled.img`
|
||||
position: absolute;
|
||||
right: -64px;
|
||||
top: -64px;
|
||||
width: 180px;
|
||||
z-index: ${Z_INDEX.sticky};
|
||||
`
|
||||
|
||||
const WrappedCloseIcon = styled(X)`
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
stroke: #7c7c80;
|
||||
z-index: ${Z_INDEX.fixed};
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
}
|
||||
`
|
||||
|
||||
const END_TIMESTAMP = 1642272346 // Jan 15th
|
||||
|
||||
export default function SurveyPopup() {
|
||||
const theme = useTheme()
|
||||
const [showPopup, setShowSurveyPopup] = useShowSurveyPopup()
|
||||
|
||||
// show popup to 1% of users
|
||||
useEffect(() => {
|
||||
// has not visited page during A/B testing if undefined
|
||||
if (showPopup === undefined) {
|
||||
if (Math.random() < 0.01) {
|
||||
setShowSurveyPopup(true)
|
||||
// log a case of succesful view
|
||||
ReactGA.event({
|
||||
category: 'Survey',
|
||||
action: 'Saw Survey',
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [setShowSurveyPopup, showPopup])
|
||||
|
||||
// limit survey to 24 hours based on timestamps
|
||||
const timestamp = useCurrentBlockTimestamp()
|
||||
const durationOver = timestamp ? timestamp.toNumber() > END_TIMESTAMP : false
|
||||
|
||||
return (
|
||||
<>
|
||||
{!showPopup || durationOver ? null : (
|
||||
<Wrapper gap="10px">
|
||||
<WrappedCloseIcon
|
||||
onClick={() => {
|
||||
ReactGA.event({
|
||||
category: 'Survey',
|
||||
action: 'Clicked Survey Link',
|
||||
})
|
||||
setShowSurveyPopup(false)
|
||||
}}
|
||||
/>
|
||||
<BGOrb src={BGImage} />
|
||||
<ExternalLink href="https://www.surveymonkey.com/r/YGWV9VD">
|
||||
<RowFixed>
|
||||
<MessageCircle stroke={theme.black} size="20px" strokeWidth="1px" />
|
||||
<ThemedText.White fontWeight={600} color={theme.black} ml="6px">
|
||||
<Trans>Tell us what you think ↗</Trans>
|
||||
</ThemedText.White>
|
||||
</RowFixed>
|
||||
</ExternalLink>
|
||||
<ThemedText.Black style={{ zIndex: Z_INDEX.fixed }} fontWeight={400} fontSize="12px" color={theme.black}>
|
||||
<Trans>Take a 10 minute survey to help us improve your experience in the Uniswap app.</Trans>
|
||||
</ThemedText.Black>
|
||||
</Wrapper>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useContext } from 'react'
|
||||
import { AlertCircle, CheckCircle } from 'react-feather'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useTransaction } from '../../state/transactions/hooks'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { ExternalLink } from '../../theme'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import styled from 'styled-components/macro'
|
||||
import { MEDIA_WIDTHS } from 'theme'
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
|
||||
import { Pair } from '@uniswap/v2-sdk'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import JSBI from 'jsbi'
|
||||
import { transparentize } from 'polished'
|
||||
import { useState } from 'react'
|
||||
@@ -12,7 +13,6 @@ import styled from 'styled-components/macro'
|
||||
import { BIG_INT_ZERO } from '../../constants/misc'
|
||||
import { useColor } from '../../hooks/useColor'
|
||||
import { useTotalSupply } from '../../hooks/useTotalSupply'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useTokenBalance } from '../../state/wallet/hooks'
|
||||
import { currencyId } from '../../utils/currencyId'
|
||||
import { unwrappedToken } from '../../utils/unwrappedToken'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CurrencyAmount, Percent, Token } from '@uniswap/sdk-core'
|
||||
import { Pair } from '@uniswap/v2-sdk'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import JSBI from 'jsbi'
|
||||
import { transparentize } from 'polished'
|
||||
import { useState } from 'react'
|
||||
@@ -12,7 +13,6 @@ import styled from 'styled-components/macro'
|
||||
import { BIG_INT_ZERO } from '../../constants/misc'
|
||||
import { useColor } from '../../hooks/useColor'
|
||||
import { useTotalSupply } from '../../hooks/useTotalSupply'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useTokenBalance } from '../../state/wallet/hooks'
|
||||
import { ExternalLink, ThemedText } from '../../theme'
|
||||
import { currencyId } from '../../utils/currencyId'
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Trans } from '@lingui/macro'
|
||||
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { LightGreyCard } from 'components/Card'
|
||||
import QuestionHelper from 'components/QuestionHelper'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react'
|
||||
import { FixedSizeList } from 'react-window'
|
||||
@@ -10,7 +11,6 @@ import styled from 'styled-components/macro'
|
||||
|
||||
import TokenListLogo from '../../assets/svg/tokenlist.svg'
|
||||
import { useIsUserAddedToken } from '../../hooks/Tokens'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useCombinedActiveList } from '../../state/lists/hooks'
|
||||
import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
|
||||
import { useCurrencyBalance } from '../../state/wallet/hooks'
|
||||
|
||||
@@ -1,35 +1,31 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
import { Currency, Token } from '@uniswap/sdk-core'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import useDebounce from 'hooks/useDebounce'
|
||||
import { useOnClickOutside } from 'hooks/useOnClickOutside'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import useToggle from 'hooks/useToggle'
|
||||
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
|
||||
import { getTokenFilter } from 'lib/hooks/useTokenList/filtering'
|
||||
import { tokenComparator, useSortTokensByQuery } from 'lib/hooks/useTokenList/sorting'
|
||||
import { KeyboardEvent, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Edit } from 'react-feather'
|
||||
import ReactGA from 'react-ga'
|
||||
import AutoSizer from 'react-virtualized-auto-sizer'
|
||||
import { FixedSizeList } from 'react-window'
|
||||
import { Text } from 'rebass'
|
||||
import { useAllTokenBalances } from 'state/wallet/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import {
|
||||
useAllTokens,
|
||||
useIsUserAddedToken,
|
||||
useNativeCurrency,
|
||||
useSearchInactiveTokenLists,
|
||||
useToken,
|
||||
} from '../../hooks/Tokens'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useAllTokens, useIsUserAddedToken, useSearchInactiveTokenLists, useToken } from '../../hooks/Tokens'
|
||||
import { ButtonText, CloseIcon, IconWrapper, ThemedText } from '../../theme'
|
||||
import { isAddress } from '../../utils'
|
||||
import Column from '../Column'
|
||||
import Row, { RowBetween, RowFixed } from '../Row'
|
||||
import CommonBases from './CommonBases'
|
||||
import CurrencyList from './CurrencyList'
|
||||
import { filterTokens, useSortedTokensByQuery } from './filtering'
|
||||
import ImportRow from './ImportRow'
|
||||
import { useTokenComparator } from './sorting'
|
||||
import { PaddedColumn, SearchInput, Separator } from './styleds'
|
||||
|
||||
const ContentWrapper = styled(Column)`
|
||||
@@ -84,8 +80,6 @@ export function CurrencySearch({
|
||||
const [searchQuery, setSearchQuery] = useState<string>('')
|
||||
const debouncedQuery = useDebounce(searchQuery, 200)
|
||||
|
||||
const [invertSearchOrder] = useState<boolean>(false)
|
||||
|
||||
const allTokens = useAllTokens()
|
||||
|
||||
// if they input an address, use it
|
||||
@@ -105,17 +99,16 @@ export function CurrencySearch({
|
||||
}
|
||||
}, [isAddressSearch])
|
||||
|
||||
const tokenComparator = useTokenComparator(invertSearchOrder)
|
||||
|
||||
const filteredTokens: Token[] = useMemo(() => {
|
||||
return filterTokens(Object.values(allTokens), debouncedQuery)
|
||||
return Object.values(allTokens).filter(getTokenFilter(debouncedQuery))
|
||||
}, [allTokens, debouncedQuery])
|
||||
|
||||
const balances = useAllTokenBalances()
|
||||
const sortedTokens: Token[] = useMemo(() => {
|
||||
return filteredTokens.sort(tokenComparator)
|
||||
}, [filteredTokens, tokenComparator])
|
||||
return filteredTokens.sort(tokenComparator.bind(null, balances))
|
||||
}, [balances, filteredTokens])
|
||||
|
||||
const filteredSortedTokens = useSortedTokensByQuery(sortedTokens, debouncedQuery)
|
||||
const filteredSortedTokens = useSortTokensByQuery(debouncedQuery, sortedTokens)
|
||||
|
||||
const native = useNativeCurrency()
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ import { t, Trans } from '@lingui/macro'
|
||||
import { TokenList } from '@uniswap/token-lists'
|
||||
import Card from 'components/Card'
|
||||
import { UNSUPPORTED_LIST_URLS } from 'constants/lists'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useListColor } from 'hooks/useColor'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import parseENSAddress from 'lib/utils/parseENSAddress'
|
||||
import uriToHttp from 'lib/utils/uriToHttp'
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
|
||||
@@ -5,7 +5,7 @@ import Column from 'components/Column'
|
||||
import CurrencyLogo from 'components/CurrencyLogo'
|
||||
import Row, { RowBetween, RowFixed } from 'components/Row'
|
||||
import { useToken } from 'hooks/Tokens'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { RefObject, useCallback, useMemo, useRef, useState } from 'react'
|
||||
import { useRemoveUserAddedToken, useUserAddedTokens } from 'state/user/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
@@ -6,7 +6,7 @@ import { AutoColumn } from 'components/Column'
|
||||
import CurrencyLogo from 'components/CurrencyLogo'
|
||||
import ListLogo from 'components/ListLogo'
|
||||
import { RowFixed } from 'components/Row'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { transparentize } from 'polished'
|
||||
import { AlertCircle } from 'react-feather'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
import { Token } from '@uniswap/sdk-core'
|
||||
import { TokenInfo } from '@uniswap/token-lists'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { isAddress } from '../../utils'
|
||||
|
||||
const alwaysTrue = () => true
|
||||
|
||||
/**
|
||||
* Create a filter function to apply to a token for whether it matches a particular search query
|
||||
* @param search the search query to apply to the token
|
||||
*/
|
||||
export function createTokenFilterFunction<T extends Token | TokenInfo>(search: string): (tokens: T) => boolean {
|
||||
const searchingAddress = isAddress(search)
|
||||
|
||||
if (searchingAddress) {
|
||||
const lower = searchingAddress.toLowerCase()
|
||||
return (t: T) => ('isToken' in t ? searchingAddress === t.address : lower === t.address.toLowerCase())
|
||||
}
|
||||
|
||||
const lowerSearchParts = search
|
||||
.toLowerCase()
|
||||
.split(/\s+/)
|
||||
.filter((s) => s.length > 0)
|
||||
|
||||
if (lowerSearchParts.length === 0) return alwaysTrue
|
||||
|
||||
const matchesSearch = (s: string): boolean => {
|
||||
const sParts = s
|
||||
.toLowerCase()
|
||||
.split(/\s+/)
|
||||
.filter((s) => s.length > 0)
|
||||
|
||||
return lowerSearchParts.every((p) => p.length === 0 || sParts.some((sp) => sp.startsWith(p) || sp.endsWith(p)))
|
||||
}
|
||||
|
||||
return ({ name, symbol }: T): boolean => Boolean((symbol && matchesSearch(symbol)) || (name && matchesSearch(name)))
|
||||
}
|
||||
|
||||
export function filterTokens<T extends Token | TokenInfo>(tokens: T[], search: string): T[] {
|
||||
return tokens.filter(createTokenFilterFunction(search))
|
||||
}
|
||||
|
||||
export function useSortedTokensByQuery(tokens: Token[] | undefined, searchQuery: string): Token[] {
|
||||
return useMemo(() => {
|
||||
if (!tokens) {
|
||||
return []
|
||||
}
|
||||
|
||||
const symbolMatch = searchQuery
|
||||
.toLowerCase()
|
||||
.split(/\s+/)
|
||||
.filter((s) => s.length > 0)
|
||||
|
||||
if (symbolMatch.length > 1) {
|
||||
return tokens
|
||||
}
|
||||
|
||||
const exactMatches: Token[] = []
|
||||
const symbolSubtrings: Token[] = []
|
||||
const rest: Token[] = []
|
||||
|
||||
// sort tokens by exact match -> subtring on symbol match -> rest
|
||||
tokens.map((token) => {
|
||||
if (token.symbol?.toLowerCase() === symbolMatch[0]) {
|
||||
return exactMatches.push(token)
|
||||
} else if (token.symbol?.toLowerCase().startsWith(searchQuery.toLowerCase().trim())) {
|
||||
return symbolSubtrings.push(token)
|
||||
} else {
|
||||
return rest.push(token)
|
||||
}
|
||||
})
|
||||
|
||||
return [...exactMatches, ...symbolSubtrings, ...rest]
|
||||
}, [tokens, searchQuery])
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { useAllTokenBalances } from '../../state/wallet/hooks'
|
||||
|
||||
// compare two token amounts with highest one coming first
|
||||
function balanceComparator(balanceA?: CurrencyAmount<Currency>, balanceB?: CurrencyAmount<Currency>) {
|
||||
if (balanceA && balanceB) {
|
||||
return balanceA.greaterThan(balanceB) ? -1 : balanceA.equalTo(balanceB) ? 0 : 1
|
||||
} else if (balanceA && balanceA.greaterThan('0')) {
|
||||
return -1
|
||||
} else if (balanceB && balanceB.greaterThan('0')) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
function getTokenComparator(balances: {
|
||||
[tokenAddress: string]: CurrencyAmount<Currency> | undefined
|
||||
}): (tokenA: Token, tokenB: Token) => number {
|
||||
return function sortTokens(tokenA: Token, tokenB: Token): number {
|
||||
// -1 = a is first
|
||||
// 1 = b is first
|
||||
|
||||
// sort by balances
|
||||
const balanceA = balances[tokenA.address]
|
||||
const balanceB = balances[tokenB.address]
|
||||
|
||||
const balanceComp = balanceComparator(balanceA, balanceB)
|
||||
if (balanceComp !== 0) return balanceComp
|
||||
|
||||
if (tokenA.symbol && tokenB.symbol) {
|
||||
// sort by symbol
|
||||
return tokenA.symbol.toLowerCase() < tokenB.symbol.toLowerCase() ? -1 : 1
|
||||
} else {
|
||||
return tokenA.symbol ? -1 : tokenB.symbol ? -1 : 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function useTokenComparator(inverted: boolean): (tokenA: Token, tokenB: Token) => number {
|
||||
const balances = useAllTokenBalances()
|
||||
const comparator = useMemo(() => getTokenComparator(balances ?? {}), [balances])
|
||||
return useMemo(() => {
|
||||
if (inverted) {
|
||||
return (tokenA: Token, tokenB: Token) => comparator(tokenA, tokenB) * -1
|
||||
} else {
|
||||
return comparator
|
||||
}
|
||||
}, [inverted, comparator])
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { AUTO_ROUTER_SUPPORTED_CHAINS } from 'lib/hooks/routing/clientSideSmartOrderRouter/constants'
|
||||
import { useContext, useRef, useState } from 'react'
|
||||
import { Settings, X } from 'react-feather'
|
||||
import ReactGA from 'react-ga'
|
||||
import { Text } from 'rebass'
|
||||
import { AUTO_ROUTER_SUPPORTED_CHAINS } from 'state/routing/clientSideSmartOrderRouter/constants'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import Badge from 'components/Badge'
|
||||
import { CHAIN_INFO, L2_CHAIN_IDS, SupportedL2ChainId } from 'constants/chains'
|
||||
import { CHAIN_INFO } from 'constants/chainInfo'
|
||||
import { L2_CHAIN_IDS, SupportedL2ChainId } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import useAddTokenToMetamask from 'hooks/useAddTokenToMetamask'
|
||||
import { ReactNode, useContext } from 'react'
|
||||
import { AlertCircle, AlertTriangle, ArrowUpCircle, CheckCircle } from 'react-feather'
|
||||
@@ -11,7 +13,6 @@ import styled, { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import Circle from '../../assets/images/blue-loader.svg'
|
||||
import MetaMaskLogo from '../../assets/images/metamask.png'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { ExternalLink } from '../../theme'
|
||||
import { CloseIcon, CustomLightSpinner } from '../../theme'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Trans } from '@lingui/macro'
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
import { L2_CHAIN_IDS } from 'constants/chains'
|
||||
import { DEFAULT_DEADLINE_FROM_NOW } from 'constants/misc'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import ms from 'ms.macro'
|
||||
import { darken } from 'polished'
|
||||
import { useContext, useState } from 'react'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useEffect } from 'react'
|
||||
import ReactGA from 'react-ga'
|
||||
import { RouteComponentProps } from 'react-router-dom'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { isAddress } from '@ethersproject/address'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { isAddress } from 'ethers/lib/utils'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useState } from 'react'
|
||||
import { Text } from 'rebass'
|
||||
import styled from 'styled-components/macro'
|
||||
@@ -8,7 +9,6 @@ import styled from 'styled-components/macro'
|
||||
import Circle from '../../assets/images/blue-loader.svg'
|
||||
import tokenLogo from '../../assets/images/token-logo.png'
|
||||
import useENS from '../../hooks/useENS'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useClaimCallback, useUserHasAvailableClaim, useUserUnclaimedAmount } from '../../state/claim/hooks'
|
||||
import { useIsTransactionPending } from '../../state/transactions/hooks'
|
||||
import { CloseIcon, CustomLightSpinner, ExternalLink, ThemedText, UniTokenAnimated } from '../../theme'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { isAddress } from '@ethersproject/address'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { isAddress } from 'ethers/lib/utils'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import JSBI from 'jsbi'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Text } from 'rebass'
|
||||
@@ -8,7 +9,6 @@ import styled from 'styled-components/macro'
|
||||
|
||||
import Circle from '../../assets/images/blue-loader.svg'
|
||||
import tokenLogo from '../../assets/images/token-logo.png'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useModalOpen, useToggleSelfClaimModal } from '../../state/application/hooks'
|
||||
import { ApplicationModal } from '../../state/application/reducer'
|
||||
import { useClaimCallback, useUserClaimData, useUserUnclaimedAmount } from '../../state/claim/hooks'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { TransactionResponse } from '@ethersproject/providers'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import type { providers } from 'ethers'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { ReactNode, useState } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { useStakingContract } from '../../hooks/useContract'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { StakingInfo } from '../../state/stake/hooks'
|
||||
import { TransactionType } from '../../state/transactions/actions'
|
||||
import { useTransactionAdder } from '../../state/transactions/hooks'
|
||||
@@ -47,7 +47,7 @@ export default function ClaimRewardModal({ isOpen, onDismiss, stakingInfo }: Sta
|
||||
setAttempting(true)
|
||||
await stakingContract
|
||||
.getReward({ gasLimit: 350000 })
|
||||
.then((response: providers.TransactionResponse) => {
|
||||
.then((response: TransactionResponse) => {
|
||||
addTransaction(response, {
|
||||
type: TransactionType.CLAIM,
|
||||
recipient: account,
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { TransactionResponse } from '@ethersproject/providers'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { Pair } from '@uniswap/v2-sdk'
|
||||
import type { providers } from 'ethers'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useV2LiquidityTokenPermit } from 'hooks/useV2LiquidityTokenPermit'
|
||||
import { useCallback, useState } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
|
||||
import { usePairContract, useStakingContract, useV2RouterContract } from '../../hooks/useContract'
|
||||
import { useV2LiquidityTokenPermit } from '../../hooks/useERC20Permit'
|
||||
import useTransactionDeadline from '../../hooks/useTransactionDeadline'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { StakingInfo, useDerivedStakeInfo } from '../../state/stake/hooks'
|
||||
import { TransactionType } from '../../state/transactions/actions'
|
||||
import { useTransactionAdder } from '../../state/transactions/hooks'
|
||||
@@ -105,7 +105,7 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui
|
||||
signatureData.s,
|
||||
{ gasLimit: 350000 }
|
||||
)
|
||||
.then((response: providers.TransactionResponse) => {
|
||||
.then((response: TransactionResponse) => {
|
||||
addTransaction(response, {
|
||||
type: TransactionType.DEPOSIT_LIQUIDITY_STAKING,
|
||||
token0Address: stakingInfo.tokens[0].address,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { TransactionResponse } from '@ethersproject/providers'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import type { providers } from 'ethers'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { ReactNode, useState } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { useStakingContract } from '../../hooks/useContract'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { StakingInfo } from '../../state/stake/hooks'
|
||||
import { TransactionType } from '../../state/transactions/actions'
|
||||
import { useTransactionAdder } from '../../state/transactions/hooks'
|
||||
@@ -48,7 +48,7 @@ export default function UnstakingModal({ isOpen, onDismiss, stakingInfo }: Staki
|
||||
setAttempting(true)
|
||||
await stakingContract
|
||||
.exit({ gasLimit: 300000 })
|
||||
.then((response: providers.TransactionResponse) => {
|
||||
.then((response: TransactionResponse) => {
|
||||
addTransaction(response, {
|
||||
type: TransactionType.WITHDRAW_LIQUIDITY_STAKING,
|
||||
token0Address: stakingInfo.tokens[0].address,
|
||||
|
||||
@@ -2,7 +2,8 @@ import { Trans } from '@lingui/macro'
|
||||
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
|
||||
import Card from 'components/Card'
|
||||
import { LoadingRows } from 'components/Loader/styled'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useContext, useMemo } from 'react'
|
||||
import { InterfaceTrade } from 'state/routing/types'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
@@ -12,7 +13,6 @@ import { computeRealizedLPFeePercent } from '../../utils/prices'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
import FormattedPriceImpact from './FormattedPriceImpact'
|
||||
import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from './GasEstimateBadge'
|
||||
|
||||
const StyledCard = styled(Card)`
|
||||
padding: 0;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Trade } from '@uniswap/router-sdk'
|
||||
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
|
||||
import { ReactNode, useCallback, useMemo } from 'react'
|
||||
import { InterfaceTrade } from 'state/routing/types'
|
||||
import { tradeMeaningfullyDiffers } from 'utils/tradeMeaningFullyDiffer'
|
||||
|
||||
import TransactionConfirmationModal, {
|
||||
ConfirmationModalContent,
|
||||
@@ -11,23 +12,6 @@ import TransactionConfirmationModal, {
|
||||
import SwapModalFooter from './SwapModalFooter'
|
||||
import SwapModalHeader from './SwapModalHeader'
|
||||
|
||||
/**
|
||||
* Returns true if the trade requires a confirmation of details before we can submit it
|
||||
* @param args either a pair of V2 trades or a pair of V3 trades
|
||||
*/
|
||||
function tradeMeaningfullyDiffers(
|
||||
...args: [Trade<Currency, Currency, TradeType>, Trade<Currency, Currency, TradeType>]
|
||||
): boolean {
|
||||
const [tradeA, tradeB] = args
|
||||
return (
|
||||
tradeA.tradeType !== tradeB.tradeType ||
|
||||
!tradeA.inputAmount.currency.equals(tradeB.inputAmount.currency) ||
|
||||
!tradeA.inputAmount.equalTo(tradeB.inputAmount) ||
|
||||
!tradeA.outputAmount.currency.equals(tradeB.outputAmount.currency) ||
|
||||
!tradeA.outputAmount.equalTo(tradeB.outputAmount)
|
||||
)
|
||||
}
|
||||
|
||||
export default function ConfirmSwapModal({
|
||||
trade,
|
||||
originalTrade,
|
||||
|
||||
@@ -10,7 +10,6 @@ import styled from 'styled-components/macro'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import { ReactComponent as GasIcon } from '../../assets/images/gas-icon.svg'
|
||||
import { SupportedChainId } from '../../constants/chains'
|
||||
import { ResponsiveTooltipContainer } from './styleds'
|
||||
import SwapRoute from './SwapRoute'
|
||||
|
||||
@@ -32,8 +31,6 @@ const StyledGasIcon = styled(GasIcon)`
|
||||
}
|
||||
`
|
||||
|
||||
export const SUPPORTED_GAS_ESTIMATE_CHAIN_IDS = [SupportedChainId.MAINNET, SupportedChainId.POLYGON]
|
||||
|
||||
export default function GasEstimateBadge({
|
||||
trade,
|
||||
loading,
|
||||
|
||||
@@ -6,7 +6,8 @@ import { AutoColumn } from 'components/Column'
|
||||
import { LoadingOpacityContainer } from 'components/Loader/styled'
|
||||
import Row, { RowBetween, RowFixed } from 'components/Row'
|
||||
import { MouseoverTooltipContent } from 'components/Tooltip'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { darken } from 'polished'
|
||||
import { useState } from 'react'
|
||||
import { ChevronDown, Info } from 'react-feather'
|
||||
@@ -15,7 +16,7 @@ import styled, { keyframes, useTheme } from 'styled-components/macro'
|
||||
import { HideSmall, ThemedText } from 'theme'
|
||||
|
||||
import { AdvancedSwapDetails } from './AdvancedSwapDetails'
|
||||
import GasEstimateBadge, { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from './GasEstimateBadge'
|
||||
import GasEstimateBadge from './GasEstimateBadge'
|
||||
import { ResponsiveTooltipContainer } from './styleds'
|
||||
import SwapRoute from './SwapRoute'
|
||||
import TradePrice from './TradePrice'
|
||||
|
||||
@@ -7,8 +7,9 @@ import { AutoColumn } from 'components/Column'
|
||||
import { LoadingRows } from 'components/Loader/styled'
|
||||
import RoutingDiagram, { RoutingDiagramEntry } from 'components/RoutingDiagram/RoutingDiagram'
|
||||
import { AutoRow, RowBetween } from 'components/Row'
|
||||
import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import useAutoRouterSupported from 'hooks/useAutoRouterSupported'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { memo, useState } from 'react'
|
||||
import { Plus } from 'react-feather'
|
||||
import { InterfaceTrade } from 'state/routing/types'
|
||||
@@ -16,7 +17,6 @@ import { useDarkModeManager } from 'state/user/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
import { Separator, ThemedText } from 'theme'
|
||||
|
||||
import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from './GasEstimateBadge'
|
||||
import { AutoRouterLabel, AutoRouterLogo } from './RouterLabel'
|
||||
|
||||
const Wrapper = styled(AutoColumn)<{ darkMode?: boolean; fixedOpen?: boolean }>`
|
||||
|
||||
@@ -16,7 +16,7 @@ const StyledPriceContainer = styled.button`
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
align-items: center
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 0;
|
||||
grid-template-columns: 1fr auto;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { AutoColumn } from 'components/Column'
|
||||
import CurrencyLogo from 'components/CurrencyLogo'
|
||||
import Modal from 'components/Modal'
|
||||
import { AutoRow, RowBetween } from 'components/Row'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useState } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import { CloseIcon, ExternalLink, ThemedText, Z_INDEX } from 'theme'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { isAddress } from '@ethersproject/address'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { isAddress } from 'ethers/lib/utils'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { ReactNode, useState } from 'react'
|
||||
import { X } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
@@ -7,7 +8,6 @@ import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
|
||||
|
||||
import { UNI } from '../../constants/tokens'
|
||||
import useENS from '../../hooks/useENS'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useDelegateCallback } from '../../state/governance/hooks'
|
||||
import { useTokenBalance } from '../../state/wallet/hooks'
|
||||
import { ThemedText } from '../../theme'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { L2_CHAIN_IDS } from 'constants/chains'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
@@ -38,7 +38,7 @@ const EmptyState = ({ HeaderContent, SubHeaderContent }: EmptyStateProps) => (
|
||||
|
||||
export default function ProposalEmptyState() {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
if (chainId && L2_CHAIN_IDS.includes(chainId)) {
|
||||
if (chainId && chainId !== SupportedChainId.MAINNET) {
|
||||
return (
|
||||
<EmptyState
|
||||
HeaderContent={() => <Trans>Please connect to Layer 1 Ethereum</Trans>}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useContext, useState } from 'react'
|
||||
import { ArrowUpCircle, X } from 'react-feather'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
|
||||
|
||||
import Circle from '../../assets/images/blue-loader.svg'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useUserVotes, useVoteCallback } from '../../state/governance/hooks'
|
||||
import { VoteOption } from '../../state/governance/types'
|
||||
import { CustomLightSpinner, ThemedText } from '../../theme'
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { Web3Provider } from '@ethersproject/providers'
|
||||
import { SafeAppConnector } from '@gnosis.pm/safe-apps-web3-react'
|
||||
import { InjectedConnector } from '@web3-react/injected-connector'
|
||||
import { PortisConnector } from '@web3-react/portis-connector'
|
||||
import { WalletConnectConnector } from '@web3-react/walletconnect-connector'
|
||||
import { WalletLinkConnector } from '@web3-react/walletlink-connector'
|
||||
import type { providers } from 'ethers'
|
||||
import { INFURA_NETWORK_URLS } from 'constants/chainInfo'
|
||||
import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from 'constants/chains'
|
||||
|
||||
import UNISWAP_LOGO_URL from '../assets/svg/logo.svg'
|
||||
import { ALL_SUPPORTED_CHAIN_IDS, INFURA_NETWORK_URLS, SupportedChainId } from '../constants/chains'
|
||||
import getLibrary from '../utils/getLibrary'
|
||||
import { FortmaticConnector } from './Fortmatic'
|
||||
import { NetworkConnector } from './NetworkConnector'
|
||||
@@ -19,8 +20,8 @@ export const network = new NetworkConnector({
|
||||
defaultChainId: 1,
|
||||
})
|
||||
|
||||
let networkLibrary: providers.Web3Provider | undefined
|
||||
export function getNetworkLibrary(): providers.Web3Provider {
|
||||
let networkLibrary: Web3Provider | undefined
|
||||
export function getNetworkLibrary(): Web3Provider {
|
||||
return (networkLibrary = networkLibrary ?? getLibrary(network.provider))
|
||||
}
|
||||
|
||||
@@ -52,5 +53,5 @@ export const walletlink = new WalletLinkConnector({
|
||||
url: INFURA_NETWORK_URLS[SupportedChainId.MAINNET],
|
||||
appName: 'Uniswap',
|
||||
appLogoUrl: UNISWAP_LOGO_URL,
|
||||
supportedChainIds: [SupportedChainId.MAINNET, SupportedChainId.POLYGON],
|
||||
supportedChainIds: ALL_SUPPORTED_CHAIN_IDS,
|
||||
})
|
||||
|
||||
@@ -107,3 +107,8 @@ export const V3_MIGRATOR_ADDRESSES: AddressMap = constructSameAddressMap('0xA564
|
||||
SupportedChainId.POLYGON_MUMBAI,
|
||||
SupportedChainId.POLYGON,
|
||||
])
|
||||
|
||||
export const TICK_LENS_ADDRESSES: AddressMap = {
|
||||
[SupportedChainId.ARBITRUM_ONE]: '0xbfd8137f7d1516D3ea5cA83523914859ec47F573',
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: '0xbfd8137f7d1516D3ea5cA83523914859ec47F573',
|
||||
}
|
||||
|
||||
233
src/constants/chainInfo.ts
Normal file
233
src/constants/chainInfo.ts
Normal file
@@ -0,0 +1,233 @@
|
||||
import ms from 'ms.macro'
|
||||
|
||||
import ethereumLogoUrl from '../assets/images/ethereum-logo.png'
|
||||
import arbitrumLogoUrl from '../assets/svg/arbitrum_logo.svg'
|
||||
import optimismLogoUrl from '../assets/svg/optimistic_ethereum.svg'
|
||||
import polygonMaticLogo from '../assets/svg/polygon-matic-logo.svg'
|
||||
import { SupportedChainId, SupportedL1ChainId, SupportedL2ChainId } from './chains'
|
||||
import { ARBITRUM_LIST, OPTIMISM_LIST } from './lists'
|
||||
|
||||
const INFURA_KEY = process.env.REACT_APP_INFURA_KEY
|
||||
if (typeof INFURA_KEY === 'undefined') {
|
||||
throw new Error(`REACT_APP_INFURA_KEY must be a defined environment variable`)
|
||||
}
|
||||
|
||||
/**
|
||||
* These are the network URLs used by the interface when there is not another available source of chain data
|
||||
*/
|
||||
export const INFURA_NETWORK_URLS: { [key in SupportedChainId]: string } = {
|
||||
[SupportedChainId.MAINNET]: `https://mainnet.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.RINKEBY]: `https://rinkeby.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.ROPSTEN]: `https://ropsten.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.GOERLI]: `https://goerli.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.KOVAN]: `https://kovan.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.OPTIMISM]: `https://optimism-mainnet.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: `https://optimism-kovan.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.ARBITRUM_ONE]: `https://arbitrum-mainnet.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: `https://arbitrum-rinkeby.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.POLYGON]: `https://polygon-mainnet.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.POLYGON_MUMBAI]: `https://polygon-mumbai.infura.io/v3/${INFURA_KEY}`,
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used to call the add network RPC
|
||||
*/
|
||||
interface AddNetworkInfo {
|
||||
readonly rpcUrl: string
|
||||
readonly nativeCurrency: {
|
||||
name: string // e.g. 'Goerli ETH',
|
||||
symbol: string // e.g. 'gorETH',
|
||||
decimals: number // e.g. 18,
|
||||
}
|
||||
}
|
||||
|
||||
export enum NetworkType {
|
||||
L1,
|
||||
L2,
|
||||
}
|
||||
|
||||
interface BaseChainInfo {
|
||||
readonly networkType: NetworkType
|
||||
readonly blockWaitMsBeforeWarning?: number
|
||||
readonly docs: string
|
||||
readonly bridge?: string
|
||||
readonly explorer: string
|
||||
readonly infoLink: string
|
||||
readonly logoUrl: string
|
||||
readonly label: string
|
||||
readonly helpCenterUrl?: string
|
||||
readonly addNetworkInfo: AddNetworkInfo
|
||||
}
|
||||
|
||||
export interface L1ChainInfo extends BaseChainInfo {
|
||||
readonly networkType: NetworkType.L1
|
||||
}
|
||||
|
||||
export interface L2ChainInfo extends BaseChainInfo {
|
||||
readonly networkType: NetworkType.L2
|
||||
readonly bridge: string
|
||||
readonly statusPage?: string
|
||||
readonly defaultListUrl: string
|
||||
}
|
||||
|
||||
export type ChainInfoMap = { readonly [chainId: number]: L1ChainInfo | L2ChainInfo } & {
|
||||
readonly [chainId in SupportedL2ChainId]: L2ChainInfo
|
||||
} &
|
||||
{ readonly [chainId in SupportedL1ChainId]: L1ChainInfo }
|
||||
|
||||
export const CHAIN_INFO: ChainInfoMap = {
|
||||
[SupportedChainId.MAINNET]: {
|
||||
networkType: NetworkType.L1,
|
||||
docs: 'https://docs.uniswap.org/',
|
||||
explorer: 'https://etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/',
|
||||
label: 'Ethereum',
|
||||
logoUrl: ethereumLogoUrl,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrl: INFURA_NETWORK_URLS[SupportedChainId.MAINNET],
|
||||
},
|
||||
},
|
||||
[SupportedChainId.RINKEBY]: {
|
||||
networkType: NetworkType.L1,
|
||||
docs: 'https://docs.uniswap.org/',
|
||||
explorer: 'https://rinkeby.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/',
|
||||
label: 'Rinkeby',
|
||||
logoUrl: ethereumLogoUrl,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Rinkeby Ether', symbol: 'rETH', decimals: 18 },
|
||||
rpcUrl: INFURA_NETWORK_URLS[SupportedChainId.RINKEBY],
|
||||
},
|
||||
},
|
||||
[SupportedChainId.ROPSTEN]: {
|
||||
networkType: NetworkType.L1,
|
||||
docs: 'https://docs.uniswap.org/',
|
||||
explorer: 'https://ropsten.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/',
|
||||
label: 'Ropsten',
|
||||
logoUrl: ethereumLogoUrl,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Ropsten Ether', symbol: 'ropETH', decimals: 18 },
|
||||
rpcUrl: INFURA_NETWORK_URLS[SupportedChainId.ROPSTEN],
|
||||
},
|
||||
},
|
||||
[SupportedChainId.KOVAN]: {
|
||||
networkType: NetworkType.L1,
|
||||
docs: 'https://docs.uniswap.org/',
|
||||
explorer: 'https://kovan.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/',
|
||||
label: 'Kovan',
|
||||
logoUrl: ethereumLogoUrl,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Kovan Ether', symbol: 'kovETH', decimals: 18 },
|
||||
rpcUrl: INFURA_NETWORK_URLS[SupportedChainId.KOVAN],
|
||||
},
|
||||
},
|
||||
[SupportedChainId.GOERLI]: {
|
||||
networkType: NetworkType.L1,
|
||||
docs: 'https://docs.uniswap.org/',
|
||||
explorer: 'https://goerli.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/',
|
||||
label: 'Görli',
|
||||
logoUrl: ethereumLogoUrl,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Görli Ether', symbol: 'görETH', decimals: 18 },
|
||||
rpcUrl: INFURA_NETWORK_URLS[SupportedChainId.GOERLI],
|
||||
},
|
||||
},
|
||||
[SupportedChainId.OPTIMISM]: {
|
||||
networkType: NetworkType.L2,
|
||||
blockWaitMsBeforeWarning: ms`25m`,
|
||||
bridge: 'https://gateway.optimism.io/?chainId=1',
|
||||
defaultListUrl: OPTIMISM_LIST,
|
||||
docs: 'https://optimism.io/',
|
||||
explorer: 'https://optimistic.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/optimism/',
|
||||
label: 'Optimism',
|
||||
logoUrl: optimismLogoUrl,
|
||||
statusPage: 'https://optimism.io/status',
|
||||
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137778-uniswap-on-optimistic-ethereum-oξ',
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrl: 'https://mainnet.optimism.io',
|
||||
},
|
||||
},
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: {
|
||||
networkType: NetworkType.L2,
|
||||
blockWaitMsBeforeWarning: ms`25m`,
|
||||
bridge: 'https://gateway.optimism.io/',
|
||||
defaultListUrl: OPTIMISM_LIST,
|
||||
docs: 'https://optimism.io/',
|
||||
explorer: 'https://optimistic.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/optimism/',
|
||||
label: 'Optimistic Kovan',
|
||||
logoUrl: optimismLogoUrl,
|
||||
statusPage: 'https://optimism.io/status',
|
||||
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137778-uniswap-on-optimistic-ethereum-oξ',
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Optimistic Kovan Ether', symbol: 'kovOpETH', decimals: 18 },
|
||||
rpcUrl: 'https://kovan.optimism.io',
|
||||
},
|
||||
},
|
||||
[SupportedChainId.ARBITRUM_ONE]: {
|
||||
networkType: NetworkType.L2,
|
||||
blockWaitMsBeforeWarning: ms`10m`,
|
||||
bridge: 'https://bridge.arbitrum.io/',
|
||||
docs: 'https://offchainlabs.com/',
|
||||
explorer: 'https://arbiscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/arbitrum',
|
||||
label: 'Arbitrum',
|
||||
logoUrl: arbitrumLogoUrl,
|
||||
defaultListUrl: ARBITRUM_LIST,
|
||||
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137787-uniswap-on-arbitrum',
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrl: 'https://arb1.arbitrum.io/rpc',
|
||||
},
|
||||
},
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: {
|
||||
networkType: NetworkType.L2,
|
||||
blockWaitMsBeforeWarning: ms`10m`,
|
||||
bridge: 'https://bridge.arbitrum.io/',
|
||||
docs: 'https://offchainlabs.com/',
|
||||
explorer: 'https://rinkeby-explorer.arbitrum.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/arbitrum/',
|
||||
label: 'Arbitrum Rinkeby',
|
||||
logoUrl: arbitrumLogoUrl,
|
||||
defaultListUrl: ARBITRUM_LIST,
|
||||
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137787-uniswap-on-arbitrum',
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Rinkeby Arbitrum Ether', symbol: 'rinkArbETH', decimals: 18 },
|
||||
rpcUrl: 'https://rinkeby.arbitrum.io/rpc',
|
||||
},
|
||||
},
|
||||
[SupportedChainId.POLYGON]: {
|
||||
networkType: NetworkType.L1,
|
||||
blockWaitMsBeforeWarning: ms`10m`,
|
||||
bridge: 'https://wallet.polygon.technology/bridge',
|
||||
docs: 'https://polygon.io/',
|
||||
explorer: 'https://polygonscan.com/',
|
||||
infoLink: 'https://info.uniswap.org/#/polygon/',
|
||||
label: 'Polygon',
|
||||
logoUrl: polygonMaticLogo,
|
||||
addNetworkInfo: {
|
||||
rpcUrl: 'https://polygon-rpc.com/',
|
||||
nativeCurrency: { name: 'Polygon Matic', symbol: 'MATIC', decimals: 18 },
|
||||
},
|
||||
},
|
||||
[SupportedChainId.POLYGON_MUMBAI]: {
|
||||
networkType: NetworkType.L1,
|
||||
blockWaitMsBeforeWarning: ms`10m`,
|
||||
bridge: 'https://wallet.polygon.technology/bridge',
|
||||
docs: 'https://polygon.io/',
|
||||
explorer: 'https://mumbai.polygonscan.com/',
|
||||
infoLink: 'https://info.uniswap.org/#/polygon/',
|
||||
label: 'Polygon Mumbai',
|
||||
logoUrl: polygonMaticLogo,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Polygon Mumbai Matic', symbol: 'mMATIC', decimals: 18 },
|
||||
rpcUrl: 'https://rpc-endpoints.superfluid.dev/mumbai',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,11 +1,3 @@
|
||||
import ethereumLogoUrl from 'assets/images/ethereum-logo.png'
|
||||
import arbitrumLogoUrl from 'assets/svg/arbitrum_logo.svg'
|
||||
import optimismLogoUrl from 'assets/svg/optimistic_ethereum.svg'
|
||||
import polygonMaticLogo from 'assets/svg/polygon-matic-logo.svg'
|
||||
import ms from 'ms.macro'
|
||||
|
||||
import { ARBITRUM_LIST, OPTIMISM_LIST } from './lists'
|
||||
|
||||
/**
|
||||
* List of all the networks supported by the Uniswap Interface
|
||||
*/
|
||||
@@ -26,9 +18,18 @@ export enum SupportedChainId {
|
||||
POLYGON_MUMBAI = 80001,
|
||||
}
|
||||
|
||||
const INFURA_KEY = process.env.REACT_APP_INFURA_KEY
|
||||
if (typeof INFURA_KEY === 'undefined') {
|
||||
throw new Error(`REACT_APP_INFURA_KEY must be a defined environment variable`)
|
||||
export const CHAIN_IDS_TO_NAMES = {
|
||||
[SupportedChainId.MAINNET]: 'mainnet',
|
||||
[SupportedChainId.ROPSTEN]: 'ropsten',
|
||||
[SupportedChainId.RINKEBY]: 'rinkeby',
|
||||
[SupportedChainId.GOERLI]: 'goerli',
|
||||
[SupportedChainId.KOVAN]: 'kovan',
|
||||
[SupportedChainId.POLYGON]: 'polygon',
|
||||
[SupportedChainId.POLYGON_MUMBAI]: 'polygon_mumbai',
|
||||
[SupportedChainId.ARBITRUM_ONE]: 'arbitrum',
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: 'arbitrum_rinkeby',
|
||||
[SupportedChainId.OPTIMISM]: 'optimism',
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: 'optimistic_kovan',
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,6 +39,8 @@ export const ALL_SUPPORTED_CHAIN_IDS: SupportedChainId[] = Object.values(Support
|
||||
(id) => typeof id === 'number'
|
||||
) as SupportedChainId[]
|
||||
|
||||
export const SUPPORTED_GAS_ESTIMATE_CHAIN_IDS = [SupportedChainId.MAINNET, SupportedChainId.POLYGON]
|
||||
|
||||
/**
|
||||
* All the chain IDs that are running the Ethereum protocol.
|
||||
*/
|
||||
@@ -65,223 +68,3 @@ export const L2_CHAIN_IDS = [
|
||||
] as const
|
||||
|
||||
export type SupportedL2ChainId = typeof L2_CHAIN_IDS[number]
|
||||
|
||||
/**
|
||||
* These are the network URLs used by the interface when there is not another available source of chain data
|
||||
*/
|
||||
export const INFURA_NETWORK_URLS: { [key in SupportedChainId]: string } = {
|
||||
[SupportedChainId.MAINNET]: `https://mainnet.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.RINKEBY]: `https://rinkeby.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.ROPSTEN]: `https://ropsten.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.GOERLI]: `https://goerli.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.KOVAN]: `https://kovan.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.OPTIMISM]: `https://optimism-mainnet.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: `https://optimism-kovan.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.ARBITRUM_ONE]: `https://arbitrum-mainnet.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: `https://arbitrum-rinkeby.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.POLYGON]: `https://polygon-mainnet.infura.io/v3/${INFURA_KEY}`,
|
||||
[SupportedChainId.POLYGON_MUMBAI]: `https://polygon-mumbai.infura.io/v3/${INFURA_KEY}`,
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used to call the add network RPC
|
||||
*/
|
||||
interface AddNetworkInfo {
|
||||
readonly rpcUrl: string
|
||||
readonly nativeCurrency: {
|
||||
name: string // e.g. 'Goerli ETH',
|
||||
symbol: string // e.g. 'gorETH',
|
||||
decimals: number // e.g. 18,
|
||||
}
|
||||
}
|
||||
|
||||
export enum NetworkType {
|
||||
L1,
|
||||
L2,
|
||||
}
|
||||
|
||||
interface BaseChainInfo {
|
||||
readonly networkType: NetworkType
|
||||
readonly blockWaitMsBeforeWarning?: number
|
||||
readonly docs: string
|
||||
readonly bridge?: string
|
||||
readonly explorer: string
|
||||
readonly infoLink: string
|
||||
readonly logoUrl: string
|
||||
readonly label: string
|
||||
readonly helpCenterUrl?: string
|
||||
readonly addNetworkInfo: AddNetworkInfo
|
||||
}
|
||||
|
||||
export interface L1ChainInfo extends BaseChainInfo {
|
||||
readonly networkType: NetworkType.L1
|
||||
}
|
||||
|
||||
export interface L2ChainInfo extends BaseChainInfo {
|
||||
readonly networkType: NetworkType.L2
|
||||
readonly bridge: string
|
||||
readonly statusPage?: string
|
||||
readonly defaultListUrl: string
|
||||
}
|
||||
|
||||
export type ChainInfoMap = { readonly [chainId: number]: L1ChainInfo | L2ChainInfo } & {
|
||||
readonly [chainId in SupportedL2ChainId]: L2ChainInfo
|
||||
} &
|
||||
{ readonly [chainId in SupportedL1ChainId]: L1ChainInfo }
|
||||
|
||||
export const CHAIN_INFO: ChainInfoMap = {
|
||||
[SupportedChainId.MAINNET]: {
|
||||
networkType: NetworkType.L1,
|
||||
docs: 'https://docs.uniswap.org/',
|
||||
explorer: 'https://etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/',
|
||||
label: 'Ethereum',
|
||||
logoUrl: ethereumLogoUrl,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrl: INFURA_NETWORK_URLS[SupportedChainId.MAINNET],
|
||||
},
|
||||
},
|
||||
[SupportedChainId.RINKEBY]: {
|
||||
networkType: NetworkType.L1,
|
||||
docs: 'https://docs.uniswap.org/',
|
||||
explorer: 'https://rinkeby.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/',
|
||||
label: 'Rinkeby',
|
||||
logoUrl: ethereumLogoUrl,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Rinkeby Ether', symbol: 'rETH', decimals: 18 },
|
||||
rpcUrl: INFURA_NETWORK_URLS[SupportedChainId.RINKEBY],
|
||||
},
|
||||
},
|
||||
[SupportedChainId.ROPSTEN]: {
|
||||
networkType: NetworkType.L1,
|
||||
docs: 'https://docs.uniswap.org/',
|
||||
explorer: 'https://ropsten.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/',
|
||||
label: 'Ropsten',
|
||||
logoUrl: ethereumLogoUrl,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Ropsten Ether', symbol: 'ropETH', decimals: 18 },
|
||||
rpcUrl: INFURA_NETWORK_URLS[SupportedChainId.ROPSTEN],
|
||||
},
|
||||
},
|
||||
[SupportedChainId.KOVAN]: {
|
||||
networkType: NetworkType.L1,
|
||||
docs: 'https://docs.uniswap.org/',
|
||||
explorer: 'https://kovan.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/',
|
||||
label: 'Kovan',
|
||||
logoUrl: ethereumLogoUrl,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Kovan Ether', symbol: 'kovETH', decimals: 18 },
|
||||
rpcUrl: INFURA_NETWORK_URLS[SupportedChainId.KOVAN],
|
||||
},
|
||||
},
|
||||
[SupportedChainId.GOERLI]: {
|
||||
networkType: NetworkType.L1,
|
||||
docs: 'https://docs.uniswap.org/',
|
||||
explorer: 'https://goerli.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/',
|
||||
label: 'Görli',
|
||||
logoUrl: ethereumLogoUrl,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Görli Ether', symbol: 'görETH', decimals: 18 },
|
||||
rpcUrl: INFURA_NETWORK_URLS[SupportedChainId.GOERLI],
|
||||
},
|
||||
},
|
||||
[SupportedChainId.OPTIMISM]: {
|
||||
networkType: NetworkType.L2,
|
||||
blockWaitMsBeforeWarning: ms`25m`,
|
||||
bridge: 'https://gateway.optimism.io/?chainId=1',
|
||||
defaultListUrl: OPTIMISM_LIST,
|
||||
docs: 'https://optimism.io/',
|
||||
explorer: 'https://optimistic.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/optimism/',
|
||||
label: 'Optimism',
|
||||
logoUrl: optimismLogoUrl,
|
||||
statusPage: 'https://optimism.io/status',
|
||||
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137778-uniswap-on-optimistic-ethereum-oξ',
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrl: 'https://mainnet.optimism.io',
|
||||
},
|
||||
},
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: {
|
||||
networkType: NetworkType.L2,
|
||||
blockWaitMsBeforeWarning: ms`25m`,
|
||||
bridge: 'https://gateway.optimism.io/',
|
||||
defaultListUrl: OPTIMISM_LIST,
|
||||
docs: 'https://optimism.io/',
|
||||
explorer: 'https://optimistic.etherscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/optimism/',
|
||||
label: 'Optimistic Kovan',
|
||||
logoUrl: optimismLogoUrl,
|
||||
statusPage: 'https://optimism.io/status',
|
||||
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137778-uniswap-on-optimistic-ethereum-oξ',
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Optimistic Kovan Ether', symbol: 'kovOpETH', decimals: 18 },
|
||||
rpcUrl: 'https://kovan.optimism.io',
|
||||
},
|
||||
},
|
||||
[SupportedChainId.ARBITRUM_ONE]: {
|
||||
networkType: NetworkType.L2,
|
||||
blockWaitMsBeforeWarning: ms`10m`,
|
||||
bridge: 'https://bridge.arbitrum.io/',
|
||||
docs: 'https://offchainlabs.com/',
|
||||
explorer: 'https://arbiscan.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/arbitrum',
|
||||
label: 'Arbitrum',
|
||||
logoUrl: arbitrumLogoUrl,
|
||||
defaultListUrl: ARBITRUM_LIST,
|
||||
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137787-uniswap-on-arbitrum',
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrl: 'https://arb1.arbitrum.io/rpc',
|
||||
},
|
||||
},
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: {
|
||||
networkType: NetworkType.L2,
|
||||
blockWaitMsBeforeWarning: ms`10m`,
|
||||
bridge: 'https://bridge.arbitrum.io/',
|
||||
docs: 'https://offchainlabs.com/',
|
||||
explorer: 'https://rinkeby-explorer.arbitrum.io/',
|
||||
infoLink: 'https://info.uniswap.org/#/arbitrum/',
|
||||
label: 'Arbitrum Rinkeby',
|
||||
logoUrl: arbitrumLogoUrl,
|
||||
defaultListUrl: ARBITRUM_LIST,
|
||||
helpCenterUrl: 'https://help.uniswap.org/en/collections/3137787-uniswap-on-arbitrum',
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Rinkeby Arbitrum Ether', symbol: 'rinkArbETH', decimals: 18 },
|
||||
rpcUrl: 'https://rinkeby.arbitrum.io/rpc',
|
||||
},
|
||||
},
|
||||
[SupportedChainId.POLYGON]: {
|
||||
networkType: NetworkType.L1,
|
||||
blockWaitMsBeforeWarning: ms`10m`,
|
||||
bridge: 'https://wallet.polygon.technology/bridge',
|
||||
docs: 'https://polygon.io/',
|
||||
explorer: 'https://polygonscan.com/',
|
||||
infoLink: 'https://info.uniswap.org/#/polygon/',
|
||||
label: 'Polygon',
|
||||
logoUrl: polygonMaticLogo,
|
||||
addNetworkInfo: {
|
||||
rpcUrl: 'https://polygon-rpc.com/',
|
||||
nativeCurrency: { name: 'Polygon Matic', symbol: 'MATIC', decimals: 18 },
|
||||
},
|
||||
},
|
||||
[SupportedChainId.POLYGON_MUMBAI]: {
|
||||
networkType: NetworkType.L1,
|
||||
blockWaitMsBeforeWarning: ms`10m`,
|
||||
bridge: 'https://wallet.polygon.technology/bridge',
|
||||
docs: 'https://polygon.io/',
|
||||
explorer: 'https://mumbai.polygonscan.com/',
|
||||
infoLink: 'https://info.uniswap.org/#/polygon/',
|
||||
label: 'Polygon Mumbai',
|
||||
logoUrl: polygonMaticLogo,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { name: 'Polygon Mumbai Matic', symbol: 'mMATIC', decimals: 18 },
|
||||
rpcUrl: 'https://rpc-endpoints.superfluid.dev/mumbai',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
import { Currency, Token } from '@uniswap/sdk-core'
|
||||
import { CHAIN_INFO, L2_CHAIN_IDS, SupportedChainId, SupportedL2ChainId } from 'constants/chains'
|
||||
import { arrayify, parseBytes32String } from 'ethers/lib/utils'
|
||||
import { CHAIN_INFO } from 'constants/chainInfo'
|
||||
import { L2_CHAIN_IDS, SupportedChainId, SupportedL2ChainId } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useCurrencyFromMap, useTokenFromMap } from 'lib/hooks/useCurrency'
|
||||
import { getTokenFilter } from 'lib/hooks/useTokenList/filtering'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { createTokenFilterFunction } from '../components/SearchModal/filtering'
|
||||
import { nativeOnChain } from '../constants/tokens'
|
||||
import { useAllLists, useCombinedActiveList, useInactiveListUrls } from '../state/lists/hooks'
|
||||
import { WrappedTokenInfo } from '../state/lists/wrappedTokenInfo'
|
||||
import { NEVER_RELOAD, useSingleCallResult } from '../state/multicall/hooks'
|
||||
import { useUserAddedTokens } from '../state/user/hooks'
|
||||
import { isAddress } from '../utils'
|
||||
import { TokenAddressMap, useUnsupportedTokenList } from './../state/lists/hooks'
|
||||
import { useBytes32TokenContract, useTokenContract } from './useContract'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
// reduce token map into standard address <-> Token mapping, optionally include user added tokens
|
||||
function useTokensFromMap(tokenMap: TokenAddressMap, includeUserAdded: boolean): { [address: string]: Token } {
|
||||
@@ -115,7 +112,7 @@ export function useSearchInactiveTokenLists(search: string | undefined, minResul
|
||||
const activeTokens = useAllTokens()
|
||||
return useMemo(() => {
|
||||
if (!search || search.trim().length === 0) return []
|
||||
const tokenFilter = createTokenFilterFunction(search)
|
||||
const tokenFilter = getTokenFilter(search)
|
||||
const result: WrappedTokenInfo[] = []
|
||||
const addressSet: { [address: string]: true } = {}
|
||||
for (const url of inactiveUrls) {
|
||||
@@ -157,95 +154,15 @@ export function useIsUserAddedToken(currency: Currency | undefined | null): bool
|
||||
return !!userAddedTokens.find((token) => currency.equals(token))
|
||||
}
|
||||
|
||||
// parse a name or symbol from a token response
|
||||
const BYTES32_REGEX = /^0x[a-fA-F0-9]{64}$/
|
||||
|
||||
function parseStringOrBytes32(str: string | undefined, bytes32: string | undefined, defaultValue: string): string {
|
||||
return str && str.length > 0
|
||||
? str
|
||||
: // need to check for proper bytes string and valid terminator
|
||||
bytes32 && BYTES32_REGEX.test(bytes32) && arrayify(bytes32)[31] === 0
|
||||
? parseBytes32String(bytes32)
|
||||
: defaultValue
|
||||
}
|
||||
|
||||
// undefined if invalid or does not exist
|
||||
// null if loading or null was passed
|
||||
// otherwise returns the token
|
||||
export function useToken(tokenAddress?: string | null): Token | undefined | null {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
export function useToken(tokenAddress?: string | null): Token | null | undefined {
|
||||
const tokens = useAllTokens()
|
||||
|
||||
const address = isAddress(tokenAddress)
|
||||
|
||||
const tokenContract = useTokenContract(address ? address : undefined, false)
|
||||
const tokenContractBytes32 = useBytes32TokenContract(address ? address : undefined, false)
|
||||
const token: Token | undefined = address ? tokens[address] : undefined
|
||||
|
||||
const tokenName = useSingleCallResult(token ? undefined : tokenContract, 'name', undefined, NEVER_RELOAD)
|
||||
const tokenNameBytes32 = useSingleCallResult(
|
||||
token ? undefined : tokenContractBytes32,
|
||||
'name',
|
||||
undefined,
|
||||
NEVER_RELOAD
|
||||
)
|
||||
const symbol = useSingleCallResult(token ? undefined : tokenContract, 'symbol', undefined, NEVER_RELOAD)
|
||||
const symbolBytes32 = useSingleCallResult(token ? undefined : tokenContractBytes32, 'symbol', undefined, NEVER_RELOAD)
|
||||
const decimals = useSingleCallResult(token ? undefined : tokenContract, 'decimals', undefined, NEVER_RELOAD)
|
||||
|
||||
return useMemo(() => {
|
||||
if (token) return token
|
||||
if (tokenAddress === null) return null
|
||||
if (!chainId || !address) return undefined
|
||||
if (decimals.loading || symbol.loading || tokenName.loading) return null
|
||||
if (decimals.result) {
|
||||
return new Token(
|
||||
chainId,
|
||||
address,
|
||||
decimals.result[0],
|
||||
parseStringOrBytes32(symbol.result?.[0], symbolBytes32.result?.[0], 'UNKNOWN'),
|
||||
parseStringOrBytes32(tokenName.result?.[0], tokenNameBytes32.result?.[0], 'Unknown Token')
|
||||
)
|
||||
}
|
||||
return undefined
|
||||
}, [
|
||||
address,
|
||||
chainId,
|
||||
decimals.loading,
|
||||
decimals.result,
|
||||
symbol.loading,
|
||||
symbol.result,
|
||||
symbolBytes32.result,
|
||||
token,
|
||||
tokenAddress,
|
||||
tokenName.loading,
|
||||
tokenName.result,
|
||||
tokenNameBytes32.result,
|
||||
])
|
||||
return useTokenFromMap(tokens, tokenAddress)
|
||||
}
|
||||
|
||||
export function useNativeCurrency(): Currency {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
return useMemo(
|
||||
() =>
|
||||
chainId
|
||||
? nativeOnChain(chainId)
|
||||
: // display mainnet when not connected
|
||||
nativeOnChain(SupportedChainId.MAINNET),
|
||||
[chainId]
|
||||
)
|
||||
}
|
||||
|
||||
export function useCurrency(currencyId: string | null | undefined): Currency | null | undefined {
|
||||
const nativeCurrency = useNativeCurrency()
|
||||
const isNative = Boolean(nativeCurrency && currencyId?.toUpperCase() === 'ETH')
|
||||
const token = useToken(isNative ? undefined : currencyId)
|
||||
|
||||
if (currencyId === null || currencyId === undefined) return currencyId
|
||||
|
||||
// this case so we use our builtin wrapped token instead of wrapped tokens on token lists
|
||||
const wrappedNative = nativeCurrency?.wrapped
|
||||
if (wrappedNative?.address?.toUpperCase() === currencyId?.toUpperCase()) return wrappedNative
|
||||
|
||||
return isNative ? nativeCurrency : token
|
||||
export function useCurrency(currencyId?: string | null): Currency | null | undefined {
|
||||
const tokens = useAllTokens()
|
||||
return useCurrencyFromMap(tokens, currencyId)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { Web3Provider } from '@ethersproject/providers'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { providers } from 'ethers'
|
||||
import { default as useWidgetsWeb3React } from 'lib/hooks/useActiveWeb3React'
|
||||
|
||||
import { NetworkContextName } from '../constants/misc'
|
||||
|
||||
export default function useActiveWeb3React() {
|
||||
const widgetsContext = useWidgetsWeb3React()
|
||||
const interfaceContext = useWeb3React<providers.Web3Provider>()
|
||||
const interfaceNetworkContext = useWeb3React<providers.Web3Provider>(
|
||||
if (process.env.REACT_APP_IS_WIDGET) {
|
||||
return useWidgetsWeb3React()
|
||||
}
|
||||
|
||||
const interfaceContext = useWeb3React<Web3Provider>()
|
||||
const interfaceNetworkContext = useWeb3React<Web3Provider>(
|
||||
process.env.REACT_APP_IS_WIDGET ? undefined : NetworkContextName
|
||||
)
|
||||
|
||||
if (process.env.REACT_APP_IS_WIDGET) {
|
||||
return widgetsContext
|
||||
}
|
||||
if (interfaceContext.active) {
|
||||
return interfaceContext
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Currency, Token } from '@uniswap/sdk-core'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import useCurrencyLogoURIs from 'lib/hooks/useCurrencyLogoURIs'
|
||||
import { useCallback, useState } from 'react'
|
||||
|
||||
import { getTokenLogoURL } from './../components/CurrencyLogo/index'
|
||||
|
||||
export default function useAddTokenToMetamask(currencyToAdd: Currency | undefined): {
|
||||
addToken: () => void
|
||||
success: boolean | undefined
|
||||
@@ -13,6 +12,7 @@ export default function useAddTokenToMetamask(currencyToAdd: Currency | undefine
|
||||
const token: Token | undefined = currencyToAdd?.wrapped
|
||||
|
||||
const [success, setSuccess] = useState<boolean | undefined>()
|
||||
const logoURL = useCurrencyLogoURIs(token)[0]
|
||||
|
||||
const addToken = useCallback(() => {
|
||||
if (library && library.provider.isMetaMask && library.provider.request && token) {
|
||||
@@ -26,7 +26,7 @@ export default function useAddTokenToMetamask(currencyToAdd: Currency | undefine
|
||||
address: token.address,
|
||||
symbol: token.symbol,
|
||||
decimals: token.decimals,
|
||||
image: getTokenLogoURL(token.address),
|
||||
image: logoURL,
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -37,7 +37,7 @@ export default function useAddTokenToMetamask(currencyToAdd: Currency | undefine
|
||||
} else {
|
||||
setSuccess(false)
|
||||
}
|
||||
}, [library, token])
|
||||
}, [library, logoURL, token])
|
||||
|
||||
return { addToken, success }
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { Pool, Route } from '@uniswap/v3-sdk'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { useV3SwapPools } from './useV3SwapPools'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
/**
|
||||
* Returns true if poolA is equivalent to poolB
|
||||
|
||||
@@ -1,69 +1,25 @@
|
||||
import { Protocol, Trade } from '@uniswap/router-sdk'
|
||||
import { Trade } from '@uniswap/router-sdk'
|
||||
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
|
||||
import { Pair, Route as V2Route, Trade as V2Trade } from '@uniswap/v2-sdk'
|
||||
import { Pool, Route as V3Route, Trade as V3Trade } from '@uniswap/v3-sdk'
|
||||
import type { providers } from 'ethers'
|
||||
import { constants } from 'ethers'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { getTxOptimizedSwapRouter, SwapRouterVersion } from 'utils/getTxOptimizedSwapRouter'
|
||||
import { Trade as V2Trade } from '@uniswap/v2-sdk'
|
||||
import { Trade as V3Trade } from '@uniswap/v3-sdk'
|
||||
import useSwapApproval, { useSwapApprovalOptimizedTrade } from 'lib/hooks/swap/useSwapApproval'
|
||||
import { ApprovalState, useApproval } from 'lib/hooks/useApproval'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
import { SWAP_ROUTER_ADDRESSES, V2_ROUTER_ADDRESS, V3_ROUTER_ADDRESS } from '../constants/addresses'
|
||||
import { TransactionType } from '../state/transactions/actions'
|
||||
import { useHasPendingApproval, useTransactionAdder } from '../state/transactions/hooks'
|
||||
import { calculateGasMargin } from '../utils/calculateGasMargin'
|
||||
import { useTokenContract } from './useContract'
|
||||
import { useTokenAllowance } from './useTokenAllowance'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
export { ApprovalState } from 'lib/hooks/useApproval'
|
||||
|
||||
export enum ApprovalState {
|
||||
UNKNOWN = 'UNKNOWN',
|
||||
NOT_APPROVED = 'NOT_APPROVED',
|
||||
PENDING = 'PENDING',
|
||||
APPROVED = 'APPROVED',
|
||||
}
|
||||
|
||||
export function useApprovalState(amountToApprove?: CurrencyAmount<Currency>, spender?: string) {
|
||||
const { account } = useActiveWeb3React()
|
||||
const token = amountToApprove?.currency?.isToken ? amountToApprove.currency : undefined
|
||||
|
||||
const currentAllowance = useTokenAllowance(token, account ?? undefined, spender)
|
||||
const pendingApproval = useHasPendingApproval(token?.address, spender)
|
||||
|
||||
return useMemo(() => {
|
||||
if (!amountToApprove || !spender) return ApprovalState.UNKNOWN
|
||||
if (amountToApprove.currency.isNative) return ApprovalState.APPROVED
|
||||
// we might not have enough data to know whether or not we need to approve
|
||||
if (!currentAllowance) return ApprovalState.UNKNOWN
|
||||
|
||||
// amountToApprove will be defined if currentAllowance is
|
||||
return currentAllowance.lessThan(amountToApprove)
|
||||
? pendingApproval
|
||||
? ApprovalState.PENDING
|
||||
: ApprovalState.NOT_APPROVED
|
||||
: ApprovalState.APPROVED
|
||||
}, [amountToApprove, currentAllowance, pendingApproval, spender])
|
||||
}
|
||||
|
||||
/** Returns approval state for all known swap routers */
|
||||
export function useAllApprovalStates(
|
||||
trade: Trade<Currency, Currency, TradeType> | undefined,
|
||||
allowedSlippage: Percent
|
||||
) {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
|
||||
const amountToApprove = useMemo(
|
||||
() => (trade && trade.inputAmount.currency.isToken ? trade.maximumAmountIn(allowedSlippage) : undefined),
|
||||
[trade, allowedSlippage]
|
||||
)
|
||||
|
||||
const v2ApprovalState = useApprovalState(amountToApprove, chainId ? V2_ROUTER_ADDRESS[chainId] : undefined)
|
||||
const v3ApprovalState = useApprovalState(amountToApprove, chainId ? V3_ROUTER_ADDRESS[chainId] : undefined)
|
||||
const v2V3ApprovalState = useApprovalState(amountToApprove, chainId ? SWAP_ROUTER_ADDRESSES[chainId] : undefined)
|
||||
|
||||
return useMemo(
|
||||
() => ({ v2: v2ApprovalState, v3: v3ApprovalState, v2V3: v2V3ApprovalState }),
|
||||
[v2ApprovalState, v2V3ApprovalState, v3ApprovalState]
|
||||
)
|
||||
function useGetAndTrackApproval(getApproval: ReturnType<typeof useApproval>[1]) {
|
||||
const addTransaction = useTransactionAdder()
|
||||
return useCallback(() => {
|
||||
return getApproval().then((pending) => {
|
||||
if (pending) {
|
||||
const { response, tokenAddress, spenderAddress: spender } = pending
|
||||
addTransaction(response, { type: TransactionType.APPROVAL, tokenAddress, spender })
|
||||
}
|
||||
})
|
||||
}, [addTransaction, getApproval])
|
||||
}
|
||||
|
||||
// returns a variable indicating the state of the approval and a function which approves if necessary or early returns
|
||||
@@ -71,69 +27,17 @@ export function useApproveCallback(
|
||||
amountToApprove?: CurrencyAmount<Currency>,
|
||||
spender?: string
|
||||
): [ApprovalState, () => Promise<void>] {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const token = amountToApprove?.currency?.isToken ? amountToApprove.currency : undefined
|
||||
|
||||
// check the current approval status
|
||||
const approvalState = useApprovalState(amountToApprove, spender)
|
||||
|
||||
const tokenContract = useTokenContract(token?.address)
|
||||
const addTransaction = useTransactionAdder()
|
||||
|
||||
const approve = useCallback(async (): Promise<void> => {
|
||||
if (approvalState !== ApprovalState.NOT_APPROVED) {
|
||||
console.error('approve was called unnecessarily')
|
||||
return
|
||||
}
|
||||
if (!chainId) {
|
||||
console.error('no chainId')
|
||||
return
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
console.error('no token')
|
||||
return
|
||||
}
|
||||
|
||||
if (!tokenContract) {
|
||||
console.error('tokenContract is null')
|
||||
return
|
||||
}
|
||||
|
||||
if (!amountToApprove) {
|
||||
console.error('missing amount to approve')
|
||||
return
|
||||
}
|
||||
|
||||
if (!spender) {
|
||||
console.error('no spender')
|
||||
return
|
||||
}
|
||||
|
||||
let useExact = false
|
||||
const estimatedGas = await tokenContract.estimateGas.approve(spender, constants.MaxUint256).catch(() => {
|
||||
// general fallback for tokens who restrict approval amounts
|
||||
useExact = true
|
||||
return tokenContract.estimateGas.approve(spender, amountToApprove.quotient.toString())
|
||||
})
|
||||
|
||||
return tokenContract
|
||||
.approve(spender, useExact ? amountToApprove.quotient.toString() : constants.MaxUint256, {
|
||||
gasLimit: calculateGasMargin(estimatedGas),
|
||||
})
|
||||
.then((response: providers.TransactionResponse) => {
|
||||
addTransaction(response, { type: TransactionType.APPROVAL, tokenAddress: token.address, spender })
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
console.debug('Failed to approve token', error)
|
||||
throw error
|
||||
})
|
||||
}, [approvalState, token, tokenContract, amountToApprove, spender, addTransaction, chainId])
|
||||
|
||||
return [approvalState, approve]
|
||||
const [approval, getApproval] = useApproval(amountToApprove, spender, useHasPendingApproval)
|
||||
return [approval, useGetAndTrackApproval(getApproval)]
|
||||
}
|
||||
|
||||
export function useApprovalOptimizedTrade(
|
||||
trade: Trade<Currency, Currency, TradeType> | undefined,
|
||||
allowedSlippage: Percent
|
||||
) {
|
||||
return useSwapApprovalOptimizedTrade(trade, allowedSlippage, useHasPendingApproval)
|
||||
}
|
||||
|
||||
// wraps useApproveCallback in the context of a swap
|
||||
export function useApproveCallbackFromTrade(
|
||||
trade:
|
||||
| V2Trade<Currency, Currency, TradeType>
|
||||
@@ -141,85 +45,7 @@ export function useApproveCallbackFromTrade(
|
||||
| Trade<Currency, Currency, TradeType>
|
||||
| undefined,
|
||||
allowedSlippage: Percent
|
||||
) {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const amountToApprove = useMemo(
|
||||
() => (trade && trade.inputAmount.currency.isToken ? trade.maximumAmountIn(allowedSlippage) : undefined),
|
||||
[trade, allowedSlippage]
|
||||
)
|
||||
|
||||
const approveCallback = useApproveCallback(
|
||||
amountToApprove,
|
||||
chainId
|
||||
? trade instanceof V2Trade
|
||||
? V2_ROUTER_ADDRESS[chainId]
|
||||
: trade instanceof V3Trade
|
||||
? V3_ROUTER_ADDRESS[chainId]
|
||||
: SWAP_ROUTER_ADDRESSES[chainId]
|
||||
: undefined
|
||||
)
|
||||
|
||||
// TODO: remove L162-168 after testing is done. This error will help detect mistakes in the logic.
|
||||
if (
|
||||
(Trade instanceof V2Trade && approveCallback[0] !== ApprovalState.APPROVED) ||
|
||||
(trade instanceof V3Trade && approveCallback[0] !== ApprovalState.APPROVED)
|
||||
) {
|
||||
throw new Error('Trying to approve legacy router')
|
||||
}
|
||||
|
||||
return approveCallback
|
||||
}
|
||||
|
||||
export function useApprovalOptimizedTrade(
|
||||
trade: Trade<Currency, Currency, TradeType> | undefined,
|
||||
allowedSlippage: Percent
|
||||
):
|
||||
| V2Trade<Currency, Currency, TradeType>
|
||||
| V3Trade<Currency, Currency, TradeType>
|
||||
| Trade<Currency, Currency, TradeType>
|
||||
| undefined {
|
||||
const onlyV2Routes = trade?.routes.every((route) => route.protocol === Protocol.V2)
|
||||
const onlyV3Routes = trade?.routes.every((route) => route.protocol === Protocol.V3)
|
||||
const tradeHasSplits = (trade?.routes.length ?? 0) > 1
|
||||
|
||||
const approvalStates = useAllApprovalStates(trade, allowedSlippage)
|
||||
|
||||
const optimizedSwapRouter = useMemo(
|
||||
() => getTxOptimizedSwapRouter({ onlyV2Routes, onlyV3Routes, tradeHasSplits, approvalStates }),
|
||||
[approvalStates, tradeHasSplits, onlyV2Routes, onlyV3Routes]
|
||||
)
|
||||
|
||||
return useMemo(() => {
|
||||
if (!trade) return undefined
|
||||
|
||||
try {
|
||||
switch (optimizedSwapRouter) {
|
||||
case SwapRouterVersion.V2V3:
|
||||
return trade
|
||||
case SwapRouterVersion.V2:
|
||||
const pairs = trade.swaps[0].route.pools.filter((pool) => pool instanceof Pair) as Pair[]
|
||||
const v2Route = new V2Route(pairs, trade.inputAmount.currency, trade.outputAmount.currency)
|
||||
return new V2Trade(v2Route, trade.inputAmount, trade.tradeType)
|
||||
case SwapRouterVersion.V3:
|
||||
return V3Trade.createUncheckedTradeWithMultipleRoutes({
|
||||
routes: trade.swaps.map(({ route, inputAmount, outputAmount }) => ({
|
||||
route: new V3Route(
|
||||
route.pools.filter((p) => p instanceof Pool) as Pool[],
|
||||
inputAmount.currency,
|
||||
outputAmount.currency
|
||||
),
|
||||
inputAmount,
|
||||
outputAmount,
|
||||
})),
|
||||
tradeType: trade.tradeType,
|
||||
})
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
} catch (e) {
|
||||
// TODO(#2989): remove try-catch
|
||||
console.debug(e)
|
||||
return undefined
|
||||
}
|
||||
}, [trade, optimizedSwapRouter])
|
||||
): [ApprovalState, () => Promise<void>] {
|
||||
const [approval, getApproval] = useSwapApproval(trade, allowedSlippage, useHasPendingApproval)
|
||||
return [approval, useGetAndTrackApproval(getApproval)]
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
|
||||
import ArgentWalletContractABI from '../abis/argent-wallet-contract.json'
|
||||
import { ArgentWalletContract } from '../abis/types'
|
||||
import { useContract } from './useContract'
|
||||
import useIsArgentWallet from './useIsArgentWallet'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
export function useArgentWalletContract(): ArgentWalletContract | null {
|
||||
const { account } = useActiveWeb3React()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { AUTO_ROUTER_SUPPORTED_CHAINS } from 'state/routing/clientSideSmartOrderRouter/constants'
|
||||
|
||||
import { useActiveWeb3React } from './web3'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { AUTO_ROUTER_SUPPORTED_CHAINS } from 'lib/hooks/routing/clientSideSmartOrderRouter/constants'
|
||||
|
||||
export default function useAutoRouterSupported(): boolean {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { Trade } from '@uniswap/router-sdk'
|
||||
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
|
||||
import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'components/swap/GasEstimateBadge'
|
||||
import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains'
|
||||
import { L2_CHAIN_IDS } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import JSBI from 'jsbi'
|
||||
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
|
||||
import { useMemo } from 'react'
|
||||
import { InterfaceTrade } from 'state/routing/types'
|
||||
|
||||
import { useUserSlippageToleranceWithDefault } from '../state/user/hooks'
|
||||
import { useNativeCurrency } from './Tokens'
|
||||
import useGasPrice from './useGasPrice'
|
||||
import useUSDCPrice, { useUSDCValue } from './useUSDCPrice'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
const V3_SWAP_DEFAULT_SLIPPAGE = new Percent(50, 10_000) // .50%
|
||||
const ONE_TENTHS_PERCENT = new Percent(10, 10_000) // .10%
|
||||
@@ -29,7 +28,10 @@ function guesstimateGas(trade: Trade<Currency, Currency, TradeType> | undefined)
|
||||
const MIN_AUTO_SLIPPAGE_TOLERANCE = new Percent(5, 1000) // 0.5%
|
||||
const MAX_AUTO_SLIPPAGE_TOLERANCE = new Percent(25, 100) // 25%
|
||||
|
||||
export default function useSwapSlippageTolerance(
|
||||
/**
|
||||
* Returns slippage tolerance based on values from current trade, gas estimates from api, and active network.
|
||||
*/
|
||||
export default function useAutoSlippageTolerance(
|
||||
trade: InterfaceTrade<Currency, Currency, TradeType> | undefined
|
||||
): Percent {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
@@ -41,7 +43,7 @@ export default function useSwapSlippageTolerance(
|
||||
const nativeCurrency = useNativeCurrency()
|
||||
const nativeCurrencyPrice = useUSDCPrice(nativeCurrency ?? undefined)
|
||||
|
||||
const defaultSlippageTolerance = useMemo(() => {
|
||||
return useMemo(() => {
|
||||
if (!trade || onL2) return ONE_TENTHS_PERCENT
|
||||
|
||||
const nativeGasCost =
|
||||
@@ -73,6 +75,4 @@ export default function useSwapSlippageTolerance(
|
||||
|
||||
return V3_SWAP_DEFAULT_SLIPPAGE
|
||||
}, [trade, onL2, nativeGasPrice, gasEstimate, nativeCurrency, nativeCurrencyPrice, chainId, outputDollarValue])
|
||||
|
||||
return useUserSlippageToleranceWithDefault(defaultSlippageTolerance)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
|
||||
import { useMemo } from 'react'
|
||||
import { InterfaceTrade, TradeState } from 'state/routing/types'
|
||||
import { useRoutingAPITrade } from 'state/routing/useRoutingAPITrade'
|
||||
|
||||
@@ -24,7 +25,10 @@ export function useBestTrade(
|
||||
const autoRouterSupported = useAutoRouterSupported()
|
||||
const isWindowVisible = useIsWindowVisible()
|
||||
|
||||
const [debouncedAmount, debouncedOtherCurrency] = useDebounce([amountSpecified, otherCurrency], 200)
|
||||
const [debouncedAmount, debouncedOtherCurrency] = useDebounce(
|
||||
useMemo(() => [amountSpecified, otherCurrency], [amountSpecified, otherCurrency]),
|
||||
200
|
||||
)
|
||||
|
||||
const routingAPITrade = useRoutingAPITrade(
|
||||
tradeType,
|
||||
@@ -56,9 +60,12 @@ export function useBestTrade(
|
||||
)
|
||||
|
||||
// only return gas estimate from api if routing api trade is used
|
||||
return {
|
||||
...(useFallback ? bestV3Trade : routingAPITrade),
|
||||
...(debouncing ? { state: TradeState.SYNCING } : {}),
|
||||
...(isLoading ? { state: TradeState.LOADING } : {}),
|
||||
}
|
||||
return useMemo(
|
||||
() => ({
|
||||
...(useFallback ? bestV3Trade : routingAPITrade),
|
||||
...(debouncing ? { state: TradeState.SYNCING } : {}),
|
||||
...(isLoading ? { state: TradeState.LOADING } : {}),
|
||||
}),
|
||||
[bestV3Trade, debouncing, isLoading, routingAPITrade, useFallback]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
|
||||
import { Route, SwapQuoter } from '@uniswap/v3-sdk'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import JSBI from 'jsbi'
|
||||
import { useSingleContractWithCallData } from 'lib/hooks/multicall'
|
||||
import { useMemo } from 'react'
|
||||
import { InterfaceTrade, TradeState } from 'state/routing/types'
|
||||
|
||||
import { useSingleContractWithCallData } from '../state/multicall/hooks'
|
||||
import { useAllV3Routes } from './useAllV3Routes'
|
||||
import { useV3Quoter } from './useContract'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
const QUOTE_GAS_OVERRIDES: { [chainId: number]: number } = {
|
||||
[SupportedChainId.ARBITRUM_ONE]: 25_000_000,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Contract } from '@ethersproject/contracts'
|
||||
import { abi as GOVERNANCE_ABI } from '@uniswap/governance/build/GovernorAlpha.json'
|
||||
import { abi as UNI_ABI } from '@uniswap/governance/build/Uni.json'
|
||||
import { abi as STAKING_REWARDS_ABI } from '@uniswap/liquidity-staker/build/StakingRewards.json'
|
||||
@@ -5,6 +6,7 @@ import { abi as MERKLE_DISTRIBUTOR_ABI } from '@uniswap/merkle-distributor/build
|
||||
import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.json'
|
||||
import { abi as IUniswapV2Router02ABI } from '@uniswap/v2-periphery/build/IUniswapV2Router02.json'
|
||||
import { abi as QuoterABI } from '@uniswap/v3-periphery/artifacts/contracts/lens/Quoter.sol/Quoter.json'
|
||||
import { abi as TickLensABI } from '@uniswap/v3-periphery/artifacts/contracts/lens/TickLens.sol/TickLens.json'
|
||||
import { abi as MulticallABI } from '@uniswap/v3-periphery/artifacts/contracts/lens/UniswapInterfaceMulticall.sol/UniswapInterfaceMulticall.json'
|
||||
import { abi as NFTPositionManagerABI } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
|
||||
import { abi as V2MigratorABI } from '@uniswap/v3-periphery/artifacts/contracts/V3Migrator.sol/V3Migrator.json'
|
||||
@@ -17,6 +19,7 @@ import ERC20_BYTES32_ABI from 'abis/erc20_bytes32.json'
|
||||
import ERC721_ABI from 'abis/erc721.json'
|
||||
import ERC1155_ABI from 'abis/erc1155.json'
|
||||
import GOVERNOR_BRAVO_ABI from 'abis/governor-bravo.json'
|
||||
import { ArgentWalletDetector, EnsPublicResolver, EnsRegistrar, Erc20, Erc721, Erc1155, Weth } from 'abis/types'
|
||||
import WETH_ABI from 'abis/weth.json'
|
||||
import {
|
||||
ARGENT_WALLET_DETECTOR_ADDRESS,
|
||||
@@ -28,18 +31,17 @@ import {
|
||||
MULTICALL_ADDRESS,
|
||||
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
|
||||
QUOTER_ADDRESSES,
|
||||
TICK_LENS_ADDRESSES,
|
||||
V2_ROUTER_ADDRESS,
|
||||
V3_MIGRATOR_ADDRESSES,
|
||||
} from 'constants/addresses'
|
||||
import { Contract } from 'ethers'
|
||||
import { UNI, WRAPPED_NATIVE_CURRENCY } from 'constants/tokens'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useMemo } from 'react'
|
||||
import { NonfungiblePositionManager, Quoter, UniswapInterfaceMulticall } from 'types/v3'
|
||||
import { NonfungiblePositionManager, Quoter, TickLens, UniswapInterfaceMulticall } from 'types/v3'
|
||||
import { V3Migrator } from 'types/v3/V3Migrator'
|
||||
import { getContract } from 'utils'
|
||||
|
||||
import { ArgentWalletDetector, EnsPublicResolver, EnsRegistrar, Erc20, Erc721, Erc1155, Weth } from '../abis/types'
|
||||
import { UNI, WRAPPED_NATIVE_CURRENCY } from '../constants/tokens'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
import { getContract } from '../utils'
|
||||
|
||||
// returns null on errors
|
||||
export function useContract<T extends Contract = Contract>(
|
||||
@@ -159,3 +161,9 @@ export function useV3NFTPositionManagerContract(withSignerIfPossible?: boolean):
|
||||
export function useV3Quoter() {
|
||||
return useContract<Quoter>(QUOTER_ADDRESSES, QuoterABI)
|
||||
}
|
||||
|
||||
export function useTickLens(): TickLens | null {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const address = chainId ? TICK_LENS_ADDRESSES[chainId] : undefined
|
||||
return useContract(address, TickLensABI) as TickLens | null
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BigNumber } from 'ethers'
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { useSingleCallResult } from 'lib/hooks/multicall'
|
||||
|
||||
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||
import { useInterfaceMulticall } from './useContract'
|
||||
|
||||
// gets the current timestamp from the blockchain
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
/**
|
||||
* Debounces updates to a value.
|
||||
* Non-primitives *must* wrap the value in useMemo, or the value will be updated due to referential inequality.
|
||||
*/
|
||||
// modified from https://usehooks.com/useDebounce/
|
||||
export default function useDebounce<T>(value: T, delay: number): T {
|
||||
const [debouncedValue, setDebouncedValue] = useState<T>(value)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useSingleCallResult } from 'lib/hooks/multicall'
|
||||
import { useMemo } from 'react'
|
||||
import { safeNamehash } from 'utils/safeNamehash'
|
||||
|
||||
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||
import isZero from '../utils/isZero'
|
||||
import { useENSRegistrarContract, useENSResolverContract } from './useContract'
|
||||
import useDebounce from './useDebounce'
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { BigNumber } from 'ethers'
|
||||
import { hexZeroPad, namehash } from 'ethers/lib/utils'
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { hexZeroPad } from '@ethersproject/bytes'
|
||||
import { namehash } from '@ethersproject/hash'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useSingleCallResult } from 'lib/hooks/multicall'
|
||||
import uriToHttp from 'lib/utils/uriToHttp'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { safeNamehash } from 'utils/safeNamehash'
|
||||
|
||||
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||
import { isAddress } from '../utils'
|
||||
import isZero from '../utils/isZero'
|
||||
import { useENSRegistrarContract, useENSResolverContract, useERC721Contract, useERC1155Contract } from './useContract'
|
||||
import useDebounce from './useDebounce'
|
||||
import useENSName from './useENSName'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
/**
|
||||
* Returns the ENS avatar URI, if available.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useSingleCallResult } from 'lib/hooks/multicall'
|
||||
import { useMemo } from 'react'
|
||||
import { safeNamehash } from 'utils/safeNamehash'
|
||||
|
||||
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||
import isZero from '../utils/isZero'
|
||||
import { useENSRegistrarContract, useENSResolverContract } from './useContract'
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { namehash } from 'ethers/lib/utils'
|
||||
import { namehash } from '@ethersproject/hash'
|
||||
import { useSingleCallResult } from 'lib/hooks/multicall'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||
import { isAddress } from '../utils'
|
||||
import isZero from '../utils/isZero'
|
||||
import { useENSRegistrarContract, useENSResolverContract } from './useContract'
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { splitSignature } from '@ethersproject/bytes'
|
||||
import { Trade } from '@uniswap/router-sdk'
|
||||
import { Currency, CurrencyAmount, Percent, Token, TradeType } from '@uniswap/sdk-core'
|
||||
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
|
||||
import { Trade as V2Trade } from '@uniswap/v2-sdk'
|
||||
import { Trade as V3Trade } from '@uniswap/v3-sdk'
|
||||
import { splitSignature } from 'ethers/lib/utils'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import JSBI from 'jsbi'
|
||||
import { useSingleCallResult } from 'lib/hooks/multicall'
|
||||
import { useMemo, useState } from 'react'
|
||||
|
||||
import { SWAP_ROUTER_ADDRESSES, V3_ROUTER_ADDRESS } from '../constants/addresses'
|
||||
import { DAI, UNI, USDC } from '../constants/tokens'
|
||||
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||
import { useEIP2612Contract } from './useContract'
|
||||
import useIsArgentWallet from './useIsArgentWallet'
|
||||
import useTransactionDeadline from './useTransactionDeadline'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
enum PermitType {
|
||||
export enum PermitType {
|
||||
AMOUNT = 1,
|
||||
ALLOWED = 2,
|
||||
}
|
||||
@@ -22,7 +22,7 @@ enum PermitType {
|
||||
// 20 minutes to submit after signing
|
||||
const PERMIT_VALIDITY_BUFFER = 20 * 60
|
||||
|
||||
interface PermitInfo {
|
||||
export interface PermitInfo {
|
||||
type: PermitType
|
||||
name: string
|
||||
// version is optional, and if omitted, will not be included in the domain
|
||||
@@ -116,9 +116,10 @@ const PERMIT_ALLOWED_TYPE = [
|
||||
{ name: 'allowed', type: 'bool' },
|
||||
]
|
||||
|
||||
function useERC20Permit(
|
||||
export function useERC20Permit(
|
||||
currencyAmount: CurrencyAmount<Currency> | null | undefined,
|
||||
spender: string | null | undefined,
|
||||
transactionDeadline: BigNumber | undefined,
|
||||
overridePermitInfo: PermitInfo | undefined | null
|
||||
): {
|
||||
signatureData: SignatureData | null
|
||||
@@ -126,7 +127,6 @@ function useERC20Permit(
|
||||
gatherPermitSignature: null | (() => Promise<void>)
|
||||
} {
|
||||
const { account, chainId, library } = useActiveWeb3React()
|
||||
const transactionDeadline = useTransactionDeadline()
|
||||
const tokenAddress = currencyAmount?.currency?.isToken ? currencyAmount.currency.address : undefined
|
||||
const eip2612Contract = useEIP2612Contract(tokenAddress)
|
||||
const isArgentWallet = useIsArgentWallet()
|
||||
@@ -259,26 +259,14 @@ function useERC20Permit(
|
||||
])
|
||||
}
|
||||
|
||||
const REMOVE_V2_LIQUIDITY_PERMIT_INFO: PermitInfo = {
|
||||
version: '1',
|
||||
name: 'Uniswap V2',
|
||||
type: PermitType.AMOUNT,
|
||||
}
|
||||
|
||||
export function useV2LiquidityTokenPermit(
|
||||
liquidityAmount: CurrencyAmount<Token> | null | undefined,
|
||||
spender: string | null | undefined
|
||||
) {
|
||||
return useERC20Permit(liquidityAmount, spender, REMOVE_V2_LIQUIDITY_PERMIT_INFO)
|
||||
}
|
||||
|
||||
export function useERC20PermitFromTrade(
|
||||
trade:
|
||||
| V2Trade<Currency, Currency, TradeType>
|
||||
| V3Trade<Currency, Currency, TradeType>
|
||||
| Trade<Currency, Currency, TradeType>
|
||||
| undefined,
|
||||
allowedSlippage: Percent
|
||||
allowedSlippage: Percent,
|
||||
transactionDeadline: BigNumber | undefined
|
||||
) {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const swapRouterAddress = chainId
|
||||
@@ -294,5 +282,5 @@ export function useERC20PermitFromTrade(
|
||||
[trade, allowedSlippage]
|
||||
)
|
||||
|
||||
return useERC20Permit(amountToApprove, swapRouterAddress, null)
|
||||
return useERC20Permit(amountToApprove, swapRouterAddress, transactionDeadline, null)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { skipToken } from '@reduxjs/toolkit/query/react'
|
||||
import { Currency, Token } from '@uniswap/sdk-core'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import useBlockNumber from 'lib/hooks/useBlockNumber'
|
||||
import ms from 'ms.macro'
|
||||
import { useMemo } from 'react'
|
||||
import ReactGA from 'react-ga'
|
||||
import { useBlockNumber } from 'state/application/hooks'
|
||||
import { useFeeTierDistributionQuery } from 'state/data/enhanced'
|
||||
import { FeeTierDistributionQuery } from 'state/data/generated'
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { nanoid } from '@reduxjs/toolkit'
|
||||
import { TokenList } from '@uniswap/token-lists'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import getTokenList from 'lib/hooks/useTokenList/fetchTokenList'
|
||||
import resolveENSContentHash from 'lib/utils/resolveENSContentHash'
|
||||
import { useCallback } from 'react'
|
||||
@@ -7,7 +8,6 @@ import { useAppDispatch } from 'state/hooks'
|
||||
|
||||
import { getNetworkLibrary } from '../connectors'
|
||||
import { fetchTokenList } from '../state/lists/actions'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
export function useFetchListCallback(): (listUrl: string, sendDispatch?: boolean) => Promise<TokenList> {
|
||||
const { chainId, library } = useActiveWeb3React()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import JSBI from 'jsbi'
|
||||
import { useSingleCallResult } from 'lib/hooks/multicall'
|
||||
|
||||
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||
import { useContract } from './useContract'
|
||||
import useENSAddress from './useENSAddress'
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { NEVER_RELOAD, useSingleCallResult } from 'lib/hooks/multicall'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { NEVER_RELOAD, useSingleCallResult } from '../state/multicall/hooks'
|
||||
import { useArgentWalletDetectorContract } from './useContract'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
export default function useIsArgentWallet(): boolean {
|
||||
const { account } = useActiveWeb3React()
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { providers } from 'ethers'
|
||||
import { TransactionResponse } from '@ethersproject/providers'
|
||||
import { initializeApp } from 'firebase/app'
|
||||
import { getDatabase, push, ref } from 'firebase/database'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useCallback } from 'react'
|
||||
import { TransactionInfo, TransactionType } from 'state/transactions/actions'
|
||||
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
type PartialTransactionResponse = Pick<providers.TransactionResponse, 'hash' | 'v' | 'r' | 's'>
|
||||
type PartialTransactionResponse = Pick<TransactionResponse, 'hash' | 'v' | 'r' | 's'>
|
||||
|
||||
const SUPPORTED_TRANSACTION_TYPES = [
|
||||
TransactionType.ADD_LIQUIDITY_V2_POOL,
|
||||
@@ -61,7 +60,7 @@ export function useTransactionMonitoringEventCallback() {
|
||||
const log = useMonitoringEventCallback()
|
||||
|
||||
return useCallback(
|
||||
(info: TransactionInfo, transactionResponse: providers.TransactionResponse) => {
|
||||
(info: TransactionInfo, transactionResponse: TransactionResponse) => {
|
||||
if (SUPPORTED_TRANSACTION_TYPES.includes(info.type)) {
|
||||
log(TransactionType[info.type], {
|
||||
transactionResponse: (({ hash, v, r, s }: PartialTransactionResponse) => ({ hash, v, r, s }))(
|
||||
|
||||
@@ -1,30 +1,141 @@
|
||||
import { skipToken } from '@reduxjs/toolkit/query/react'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { FeeAmount, Pool, TICK_SPACINGS, tickToPrice } from '@uniswap/v3-sdk'
|
||||
import { ChainId } from '@uniswap/smart-order-router'
|
||||
import { FeeAmount, nearestUsableTick, Pool, TICK_SPACINGS, tickToPrice } from '@uniswap/v3-sdk'
|
||||
import { ZERO_ADDRESS } from 'constants/misc'
|
||||
import JSBI from 'jsbi'
|
||||
import { useSingleContractMultipleData } from 'lib/hooks/multicall'
|
||||
import ms from 'ms.macro'
|
||||
import { useMemo } from 'react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useAllV3TicksQuery } from 'state/data/enhanced'
|
||||
import { AllV3TicksQuery } from 'state/data/generated'
|
||||
import computeSurroundingTicks from 'utils/computeSurroundingTicks'
|
||||
|
||||
import { useTickLens } from './useContract'
|
||||
import { PoolState, usePool } from './usePools'
|
||||
|
||||
const PRICE_FIXED_DIGITS = 8
|
||||
const CHAIN_IDS_MISSING_SUBGRAPH_DATA = [ChainId.ARBITRUM_ONE, ChainId.ARBITRUM_RINKEBY]
|
||||
|
||||
export interface TickData {
|
||||
tick: number
|
||||
liquidityNet: JSBI
|
||||
liquidityGross: JSBI
|
||||
}
|
||||
|
||||
// Tick with fields parsed to JSBIs, and active liquidity computed.
|
||||
export interface TickProcessed {
|
||||
tickIdx: number
|
||||
tick: number
|
||||
liquidityActive: JSBI
|
||||
liquidityNet: JSBI
|
||||
price0: string
|
||||
}
|
||||
|
||||
const REFRESH_FREQUENCY = { blocksPerFetch: 2 }
|
||||
|
||||
const getActiveTick = (tickCurrent: number | undefined, feeAmount: FeeAmount | undefined) =>
|
||||
tickCurrent && feeAmount ? Math.floor(tickCurrent / TICK_SPACINGS[feeAmount]) * TICK_SPACINGS[feeAmount] : undefined
|
||||
|
||||
// Fetches all ticks for a given pool
|
||||
export function useAllV3Ticks(
|
||||
const bitmapIndex = (tick: number, tickSpacing: number) => {
|
||||
return Math.floor(tick / tickSpacing / 256)
|
||||
}
|
||||
|
||||
function useTicksFromTickLens(
|
||||
currencyA: Currency | undefined,
|
||||
currencyB: Currency | undefined,
|
||||
feeAmount: FeeAmount | undefined,
|
||||
numSurroundingTicks: number | undefined = 125
|
||||
) {
|
||||
const [tickDataLatestSynced, setTickDataLatestSynced] = useState<TickData[]>([])
|
||||
|
||||
const [poolState, pool] = usePool(currencyA, currencyB, feeAmount)
|
||||
|
||||
const tickSpacing = feeAmount && TICK_SPACINGS[feeAmount]
|
||||
|
||||
// Find nearest valid tick for pool in case tick is not initialized.
|
||||
const activeTick = pool?.tickCurrent && tickSpacing ? nearestUsableTick(pool?.tickCurrent, tickSpacing) : undefined
|
||||
|
||||
const poolAddress =
|
||||
currencyA && currencyB && feeAmount && poolState === PoolState.EXISTS
|
||||
? Pool.getAddress(currencyA?.wrapped, currencyB?.wrapped, feeAmount)
|
||||
: undefined
|
||||
|
||||
// it is also possible to grab all tick data but it is extremely slow
|
||||
// bitmapIndex(nearestUsableTick(TickMath.MIN_TICK, tickSpacing), tickSpacing)
|
||||
const minIndex = useMemo(
|
||||
() =>
|
||||
tickSpacing && activeTick ? bitmapIndex(activeTick - numSurroundingTicks * tickSpacing, tickSpacing) : undefined,
|
||||
[tickSpacing, activeTick, numSurroundingTicks]
|
||||
)
|
||||
|
||||
const maxIndex = useMemo(
|
||||
() =>
|
||||
tickSpacing && activeTick ? bitmapIndex(activeTick + numSurroundingTicks * tickSpacing, tickSpacing) : undefined,
|
||||
[tickSpacing, activeTick, numSurroundingTicks]
|
||||
)
|
||||
|
||||
const tickLensArgs: [string, number][] = useMemo(
|
||||
() =>
|
||||
maxIndex && minIndex && poolAddress && poolAddress !== ZERO_ADDRESS
|
||||
? new Array(maxIndex - minIndex + 1)
|
||||
.fill(0)
|
||||
.map((_, i) => i + minIndex)
|
||||
.map((wordIndex) => [poolAddress, wordIndex])
|
||||
: [],
|
||||
[minIndex, maxIndex, poolAddress]
|
||||
)
|
||||
|
||||
const tickLens = useTickLens()
|
||||
const callStates = useSingleContractMultipleData(
|
||||
tickLensArgs.length > 0 ? tickLens : undefined,
|
||||
'getPopulatedTicksInWord',
|
||||
tickLensArgs,
|
||||
REFRESH_FREQUENCY
|
||||
)
|
||||
|
||||
const isError = useMemo(() => callStates.some(({ error }) => error), [callStates])
|
||||
const isLoading = useMemo(() => callStates.some(({ loading }) => loading), [callStates])
|
||||
const IsSyncing = useMemo(() => callStates.some(({ syncing }) => syncing), [callStates])
|
||||
const isValid = useMemo(() => callStates.some(({ valid }) => valid), [callStates])
|
||||
|
||||
const tickData: TickData[] = useMemo(
|
||||
() =>
|
||||
callStates
|
||||
.map(({ result }) => result?.populatedTicks)
|
||||
.reduce(
|
||||
(accumulator, current) => [
|
||||
...accumulator,
|
||||
...(current?.map((tickData: TickData) => {
|
||||
return {
|
||||
tick: tickData.tick,
|
||||
liquidityNet: JSBI.BigInt(tickData.liquidityNet),
|
||||
liquidityGross: JSBI.BigInt(tickData.liquidityGross),
|
||||
}
|
||||
}) ?? []),
|
||||
],
|
||||
[]
|
||||
),
|
||||
[callStates]
|
||||
)
|
||||
|
||||
// reset on input change
|
||||
useEffect(() => {
|
||||
setTickDataLatestSynced([])
|
||||
}, [currencyA, currencyB, feeAmount])
|
||||
|
||||
// return the latest synced tickData even if we are still loading the newest data
|
||||
useEffect(() => {
|
||||
if (!IsSyncing && !isLoading && !isError && isValid) {
|
||||
setTickDataLatestSynced(tickData.sort((a, b) => a.tick - b.tick))
|
||||
}
|
||||
}, [isError, isLoading, IsSyncing, tickData, isValid])
|
||||
|
||||
return useMemo(
|
||||
() => ({ isLoading, IsSyncing, isError, isValid, tickData: tickDataLatestSynced }),
|
||||
[isLoading, IsSyncing, isError, isValid, tickDataLatestSynced]
|
||||
)
|
||||
}
|
||||
|
||||
function useTicksFromSubgraph(
|
||||
currencyA: Currency | undefined,
|
||||
currencyB: Currency | undefined,
|
||||
feeAmount: FeeAmount | undefined
|
||||
@@ -32,19 +143,34 @@ export function useAllV3Ticks(
|
||||
const poolAddress =
|
||||
currencyA && currencyB && feeAmount ? Pool.getAddress(currencyA?.wrapped, currencyB?.wrapped, feeAmount) : undefined
|
||||
|
||||
const { isLoading, isError, error, isUninitialized, data } = useAllV3TicksQuery(
|
||||
poolAddress ? { poolAddress: poolAddress?.toLowerCase(), skip: 0 } : skipToken,
|
||||
{
|
||||
pollingInterval: ms`30s`,
|
||||
}
|
||||
)
|
||||
return useAllV3TicksQuery(poolAddress ? { poolAddress: poolAddress?.toLowerCase(), skip: 0 } : skipToken, {
|
||||
pollingInterval: ms`30s`,
|
||||
})
|
||||
}
|
||||
|
||||
// Fetches all ticks for a given pool
|
||||
function useAllV3Ticks(
|
||||
currencyA: Currency | undefined,
|
||||
currencyB: Currency | undefined,
|
||||
feeAmount: FeeAmount | undefined
|
||||
): {
|
||||
isLoading: boolean
|
||||
isUninitialized: boolean
|
||||
isError: boolean
|
||||
error: unknown
|
||||
ticks: TickData[] | undefined
|
||||
} {
|
||||
const useSubgraph = currencyA ? !CHAIN_IDS_MISSING_SUBGRAPH_DATA.includes(currencyA.chainId) : true
|
||||
|
||||
const tickLensTickData = useTicksFromTickLens(!useSubgraph ? currencyA : undefined, currencyB, feeAmount)
|
||||
const subgraphTickData = useTicksFromSubgraph(useSubgraph ? currencyA : undefined, currencyB, feeAmount)
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
isUninitialized,
|
||||
isError,
|
||||
error,
|
||||
ticks: data?.ticks as AllV3TicksQuery['ticks'],
|
||||
isLoading: useSubgraph ? subgraphTickData.isLoading : tickLensTickData.isLoading,
|
||||
isUninitialized: useSubgraph ? subgraphTickData.isUninitialized : false,
|
||||
isError: useSubgraph ? subgraphTickData.isError : tickLensTickData.isError,
|
||||
error: useSubgraph ? subgraphTickData.error : tickLensTickData.isError,
|
||||
ticks: useSubgraph ? subgraphTickData.data?.ticks : tickLensTickData.tickData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +220,7 @@ export function usePoolActiveLiquidity(
|
||||
// find where the active tick would be to partition the array
|
||||
// if the active tick is initialized, the pivot will be an element
|
||||
// if not, take the previous tick as pivot
|
||||
const pivot = ticks.findIndex(({ tickIdx }) => tickIdx > activeTick) - 1
|
||||
const pivot = ticks.findIndex(({ tick }) => tick > activeTick) - 1
|
||||
|
||||
if (pivot < 0) {
|
||||
// consider setting a local error
|
||||
@@ -111,9 +237,8 @@ export function usePoolActiveLiquidity(
|
||||
|
||||
const activeTickProcessed: TickProcessed = {
|
||||
liquidityActive: JSBI.BigInt(pool[1]?.liquidity ?? 0),
|
||||
tickIdx: activeTick,
|
||||
liquidityNet:
|
||||
Number(ticks[pivot].tickIdx) === activeTick ? JSBI.BigInt(ticks[pivot].liquidityNet) : JSBI.BigInt(0),
|
||||
tick: activeTick,
|
||||
liquidityNet: Number(ticks[pivot].tick) === activeTick ? JSBI.BigInt(ticks[pivot].liquidityNet) : JSBI.BigInt(0),
|
||||
price0: tickToPrice(token0, token1, activeTick).toFixed(PRICE_FIXED_DIGITS),
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Interface } from '@ethersproject/abi'
|
||||
import { Currency, Token } from '@uniswap/sdk-core'
|
||||
import { abi as IUniswapV3PoolStateABI } from '@uniswap/v3-core/artifacts/contracts/interfaces/pool/IUniswapV3PoolState.sol/IUniswapV3PoolState.json'
|
||||
import { computePoolAddress } from '@uniswap/v3-sdk'
|
||||
import { FeeAmount, Pool } from '@uniswap/v3-sdk'
|
||||
import { Interface } from 'ethers/lib/utils'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useMultipleContractSingleData } from 'lib/hooks/multicall'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { V3_CORE_FACTORY_ADDRESSES } from '../constants/addresses'
|
||||
import { useMultipleContractSingleData } from '../state/multicall/hooks'
|
||||
import { IUniswapV3PoolStateInterface } from '../types/v3/IUniswapV3PoolState'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
const POOL_STATE_INTERFACE = new Interface(IUniswapV3PoolStateABI) as IUniswapV3PoolStateInterface
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { BigNumber } from 'ethers'
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import JSBI from 'jsbi'
|
||||
import { NEVER_RELOAD, useSingleCallResult } from 'lib/hooks/multicall'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { NEVER_RELOAD, useSingleCallResult } from '../state/multicall/hooks'
|
||||
import { useV3NFTPositionManagerContract } from './useContract'
|
||||
|
||||
type TokenId = number | JSBI | BigNumber
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Token } from '@uniswap/sdk-core'
|
||||
import { SOCKS_CONTROLLER_ADDRESSES } from 'constants/addresses'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useMemo } from 'react'
|
||||
import { useTokenBalance } from 'state/wallet/hooks'
|
||||
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
// technically a 721, not an ERC20, but suffices for our purposes
|
||||
const SOCKS = new Token(SupportedChainId.MAINNET, SOCKS_CONTROLLER_ADDRESSES[SupportedChainId.MAINNET], 0)
|
||||
|
||||
|
||||
181
src/hooks/useSwapCallArguments.tsx
Normal file
181
src/hooks/useSwapCallArguments.tsx
Normal file
@@ -0,0 +1,181 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { SwapRouter, Trade } from '@uniswap/router-sdk'
|
||||
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
|
||||
import { Router as V2SwapRouter, Trade as V2Trade } from '@uniswap/v2-sdk'
|
||||
import { SwapRouter as V3SwapRouter, Trade as V3Trade } from '@uniswap/v3-sdk'
|
||||
import { SWAP_ROUTER_ADDRESSES, V3_ROUTER_ADDRESS } from 'constants/addresses'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useMemo } from 'react'
|
||||
import approveAmountCalldata from 'utils/approveAmountCalldata'
|
||||
|
||||
import { useArgentWalletContract } from './useArgentWalletContract'
|
||||
import { useV2RouterContract } from './useContract'
|
||||
import useENS from './useENS'
|
||||
import { SignatureData } from './useERC20Permit'
|
||||
|
||||
export type AnyTrade =
|
||||
| V2Trade<Currency, Currency, TradeType>
|
||||
| V3Trade<Currency, Currency, TradeType>
|
||||
| Trade<Currency, Currency, TradeType>
|
||||
|
||||
interface SwapCall {
|
||||
address: string
|
||||
calldata: string
|
||||
value: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the swap calls that can be used to make the trade
|
||||
* @param trade trade to execute
|
||||
* @param allowedSlippage user allowed slippage
|
||||
* @param recipientAddressOrName the ENS name or address of the recipient of the swap output
|
||||
* @param signatureData the signature data of the permit of the input token amount, if available
|
||||
*/
|
||||
export function useSwapCallArguments(
|
||||
trade: AnyTrade | undefined,
|
||||
allowedSlippage: Percent,
|
||||
recipientAddressOrName: string | null | undefined,
|
||||
signatureData: SignatureData | null | undefined,
|
||||
deadline: BigNumber | undefined
|
||||
): SwapCall[] {
|
||||
const { account, chainId, library } = useActiveWeb3React()
|
||||
|
||||
const { address: recipientAddress } = useENS(recipientAddressOrName)
|
||||
const recipient = recipientAddressOrName === null ? account : recipientAddress
|
||||
const routerContract = useV2RouterContract()
|
||||
const argentWalletContract = useArgentWalletContract()
|
||||
|
||||
return useMemo(() => {
|
||||
if (!trade || !recipient || !library || !account || !chainId || !deadline) return []
|
||||
|
||||
if (trade instanceof V2Trade) {
|
||||
if (!routerContract) return []
|
||||
const swapMethods = []
|
||||
|
||||
swapMethods.push(
|
||||
V2SwapRouter.swapCallParameters(trade, {
|
||||
feeOnTransfer: false,
|
||||
allowedSlippage,
|
||||
recipient,
|
||||
deadline: deadline.toNumber(),
|
||||
})
|
||||
)
|
||||
|
||||
if (trade.tradeType === TradeType.EXACT_INPUT) {
|
||||
swapMethods.push(
|
||||
V2SwapRouter.swapCallParameters(trade, {
|
||||
feeOnTransfer: true,
|
||||
allowedSlippage,
|
||||
recipient,
|
||||
deadline: deadline.toNumber(),
|
||||
})
|
||||
)
|
||||
}
|
||||
return swapMethods.map(({ methodName, args, value }) => {
|
||||
if (argentWalletContract && trade.inputAmount.currency.isToken) {
|
||||
return {
|
||||
address: argentWalletContract.address,
|
||||
calldata: argentWalletContract.interface.encodeFunctionData('wc_multiCall', [
|
||||
[
|
||||
approveAmountCalldata(trade.maximumAmountIn(allowedSlippage), routerContract.address),
|
||||
{
|
||||
to: routerContract.address,
|
||||
value,
|
||||
data: routerContract.interface.encodeFunctionData(methodName, args),
|
||||
},
|
||||
],
|
||||
]),
|
||||
value: '0x0',
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
address: routerContract.address,
|
||||
calldata: routerContract.interface.encodeFunctionData(methodName, args),
|
||||
value,
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// swap options shared by v3 and v2+v3 swap routers
|
||||
const sharedSwapOptions = {
|
||||
recipient,
|
||||
slippageTolerance: allowedSlippage,
|
||||
...(signatureData
|
||||
? {
|
||||
inputTokenPermit:
|
||||
'allowed' in signatureData
|
||||
? {
|
||||
expiry: signatureData.deadline,
|
||||
nonce: signatureData.nonce,
|
||||
s: signatureData.s,
|
||||
r: signatureData.r,
|
||||
v: signatureData.v as any,
|
||||
}
|
||||
: {
|
||||
deadline: signatureData.deadline,
|
||||
amount: signatureData.amount,
|
||||
s: signatureData.s,
|
||||
r: signatureData.r,
|
||||
v: signatureData.v as any,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
|
||||
const swapRouterAddress = chainId
|
||||
? trade instanceof V3Trade
|
||||
? V3_ROUTER_ADDRESS[chainId]
|
||||
: SWAP_ROUTER_ADDRESSES[chainId]
|
||||
: undefined
|
||||
if (!swapRouterAddress) return []
|
||||
|
||||
const { value, calldata } =
|
||||
trade instanceof V3Trade
|
||||
? V3SwapRouter.swapCallParameters(trade, {
|
||||
...sharedSwapOptions,
|
||||
deadline: deadline.toString(),
|
||||
})
|
||||
: SwapRouter.swapCallParameters(trade, {
|
||||
...sharedSwapOptions,
|
||||
deadlineOrPreviousBlockhash: deadline.toString(),
|
||||
})
|
||||
|
||||
if (argentWalletContract && trade.inputAmount.currency.isToken) {
|
||||
return [
|
||||
{
|
||||
address: argentWalletContract.address,
|
||||
calldata: argentWalletContract.interface.encodeFunctionData('wc_multiCall', [
|
||||
[
|
||||
approveAmountCalldata(trade.maximumAmountIn(allowedSlippage), swapRouterAddress),
|
||||
{
|
||||
to: swapRouterAddress,
|
||||
value,
|
||||
data: calldata,
|
||||
},
|
||||
],
|
||||
]),
|
||||
value: '0x0',
|
||||
},
|
||||
]
|
||||
}
|
||||
return [
|
||||
{
|
||||
address: swapRouterAddress,
|
||||
calldata,
|
||||
value,
|
||||
},
|
||||
]
|
||||
}
|
||||
}, [
|
||||
trade,
|
||||
recipient,
|
||||
library,
|
||||
account,
|
||||
chainId,
|
||||
deadline,
|
||||
routerContract,
|
||||
allowedSlippage,
|
||||
argentWalletContract,
|
||||
signatureData,
|
||||
])
|
||||
}
|
||||
@@ -1,288 +1,16 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
import { SwapRouter, Trade } from '@uniswap/router-sdk'
|
||||
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
|
||||
import { Router as V2SwapRouter, Trade as V2Trade } from '@uniswap/v2-sdk'
|
||||
import { SwapRouter as V3SwapRouter, Trade as V3Trade } from '@uniswap/v3-sdk'
|
||||
import { BigNumber } from 'ethers'
|
||||
import { Percent, TradeType } from '@uniswap/sdk-core'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { SwapCallbackState, useSwapCallback as useLibSwapCallBack } from 'lib/hooks/swap/useSwapCallback'
|
||||
import { ReactNode, useMemo } from 'react'
|
||||
|
||||
import { SWAP_ROUTER_ADDRESSES, V3_ROUTER_ADDRESS } from '../constants/addresses'
|
||||
import { TransactionType } from '../state/transactions/actions'
|
||||
import { useTransactionAdder } from '../state/transactions/hooks'
|
||||
import approveAmountCalldata from '../utils/approveAmountCalldata'
|
||||
import { calculateGasMargin } from '../utils/calculateGasMargin'
|
||||
import { currencyId } from '../utils/currencyId'
|
||||
import isZero from '../utils/isZero'
|
||||
import { useArgentWalletContract } from './useArgentWalletContract'
|
||||
import { useV2RouterContract } from './useContract'
|
||||
import useENS from './useENS'
|
||||
import { SignatureData } from './useERC20Permit'
|
||||
import { AnyTrade } from './useSwapCallArguments'
|
||||
import useTransactionDeadline from './useTransactionDeadline'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
type AnyTrade =
|
||||
| V2Trade<Currency, Currency, TradeType>
|
||||
| V3Trade<Currency, Currency, TradeType>
|
||||
| Trade<Currency, Currency, TradeType>
|
||||
|
||||
enum SwapCallbackState {
|
||||
INVALID,
|
||||
LOADING,
|
||||
VALID,
|
||||
}
|
||||
|
||||
interface SwapCall {
|
||||
address: string
|
||||
calldata: string
|
||||
value: string
|
||||
}
|
||||
|
||||
interface SwapCallEstimate {
|
||||
call: SwapCall
|
||||
}
|
||||
|
||||
interface SuccessfulCall extends SwapCallEstimate {
|
||||
call: SwapCall
|
||||
gasEstimate: BigNumber
|
||||
}
|
||||
|
||||
interface FailedCall extends SwapCallEstimate {
|
||||
call: SwapCall
|
||||
error: Error
|
||||
}
|
||||
/**
|
||||
* Returns the swap calls that can be used to make the trade
|
||||
* @param trade trade to execute
|
||||
* @param allowedSlippage user allowed slippage
|
||||
* @param recipientAddressOrName the ENS name or address of the recipient of the swap output
|
||||
* @param signatureData the signature data of the permit of the input token amount, if available
|
||||
*/
|
||||
function useSwapCallArguments(
|
||||
trade: AnyTrade | undefined, // trade to execute, required
|
||||
allowedSlippage: Percent, // in bips
|
||||
recipientAddressOrName: string | null, // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender
|
||||
signatureData: SignatureData | null | undefined
|
||||
): SwapCall[] {
|
||||
const { account, chainId, library } = useActiveWeb3React()
|
||||
|
||||
const { address: recipientAddress } = useENS(recipientAddressOrName)
|
||||
const recipient = recipientAddressOrName === null ? account : recipientAddress
|
||||
const deadline = useTransactionDeadline()
|
||||
const routerContract = useV2RouterContract()
|
||||
const argentWalletContract = useArgentWalletContract()
|
||||
|
||||
return useMemo(() => {
|
||||
if (!trade || !recipient || !library || !account || !chainId || !deadline) return []
|
||||
|
||||
if (trade instanceof V2Trade) {
|
||||
if (!routerContract) return []
|
||||
const swapMethods = []
|
||||
|
||||
swapMethods.push(
|
||||
V2SwapRouter.swapCallParameters(trade, {
|
||||
feeOnTransfer: false,
|
||||
allowedSlippage,
|
||||
recipient,
|
||||
deadline: deadline.toNumber(),
|
||||
})
|
||||
)
|
||||
|
||||
if (trade.tradeType === TradeType.EXACT_INPUT) {
|
||||
swapMethods.push(
|
||||
V2SwapRouter.swapCallParameters(trade, {
|
||||
feeOnTransfer: true,
|
||||
allowedSlippage,
|
||||
recipient,
|
||||
deadline: deadline.toNumber(),
|
||||
})
|
||||
)
|
||||
}
|
||||
return swapMethods.map(({ methodName, args, value }) => {
|
||||
if (argentWalletContract && trade.inputAmount.currency.isToken) {
|
||||
return {
|
||||
address: argentWalletContract.address,
|
||||
calldata: argentWalletContract.interface.encodeFunctionData('wc_multiCall', [
|
||||
[
|
||||
approveAmountCalldata(trade.maximumAmountIn(allowedSlippage), routerContract.address),
|
||||
{
|
||||
to: routerContract.address,
|
||||
value,
|
||||
data: routerContract.interface.encodeFunctionData(methodName, args),
|
||||
},
|
||||
],
|
||||
]),
|
||||
value: '0x0',
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
address: routerContract.address,
|
||||
calldata: routerContract.interface.encodeFunctionData(methodName, args),
|
||||
value,
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// swap options shared by v3 and v2+v3 swap routers
|
||||
const sharedSwapOptions = {
|
||||
recipient,
|
||||
slippageTolerance: allowedSlippage,
|
||||
...(signatureData
|
||||
? {
|
||||
inputTokenPermit:
|
||||
'allowed' in signatureData
|
||||
? {
|
||||
expiry: signatureData.deadline,
|
||||
nonce: signatureData.nonce,
|
||||
s: signatureData.s,
|
||||
r: signatureData.r,
|
||||
v: signatureData.v as any,
|
||||
}
|
||||
: {
|
||||
deadline: signatureData.deadline,
|
||||
amount: signatureData.amount,
|
||||
s: signatureData.s,
|
||||
r: signatureData.r,
|
||||
v: signatureData.v as any,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
|
||||
const swapRouterAddress = chainId
|
||||
? trade instanceof V3Trade
|
||||
? V3_ROUTER_ADDRESS[chainId]
|
||||
: SWAP_ROUTER_ADDRESSES[chainId]
|
||||
: undefined
|
||||
if (!swapRouterAddress) return []
|
||||
|
||||
const { value, calldata } =
|
||||
trade instanceof V3Trade
|
||||
? V3SwapRouter.swapCallParameters(trade, {
|
||||
...sharedSwapOptions,
|
||||
deadline: deadline.toString(),
|
||||
})
|
||||
: SwapRouter.swapCallParameters(trade, {
|
||||
...sharedSwapOptions,
|
||||
deadlineOrPreviousBlockhash: deadline.toString(),
|
||||
})
|
||||
|
||||
if (argentWalletContract && trade.inputAmount.currency.isToken) {
|
||||
return [
|
||||
{
|
||||
address: argentWalletContract.address,
|
||||
calldata: argentWalletContract.interface.encodeFunctionData('wc_multiCall', [
|
||||
[
|
||||
approveAmountCalldata(trade.maximumAmountIn(allowedSlippage), swapRouterAddress),
|
||||
{
|
||||
to: swapRouterAddress,
|
||||
value,
|
||||
data: calldata,
|
||||
},
|
||||
],
|
||||
]),
|
||||
value: '0x0',
|
||||
},
|
||||
]
|
||||
}
|
||||
return [
|
||||
{
|
||||
address: swapRouterAddress,
|
||||
calldata,
|
||||
value,
|
||||
},
|
||||
]
|
||||
}
|
||||
}, [
|
||||
trade,
|
||||
recipient,
|
||||
library,
|
||||
account,
|
||||
chainId,
|
||||
deadline,
|
||||
routerContract,
|
||||
allowedSlippage,
|
||||
argentWalletContract,
|
||||
signatureData,
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* This is hacking out the revert reason from the ethers provider thrown error however it can.
|
||||
* This object seems to be undocumented by ethers.
|
||||
* @param error an error from the ethers provider
|
||||
*/
|
||||
function swapErrorToUserReadableMessage(error: any): ReactNode {
|
||||
let reason: string | undefined
|
||||
while (Boolean(error)) {
|
||||
reason = error.reason ?? error.message ?? reason
|
||||
error = error.error ?? error.data?.originalError
|
||||
}
|
||||
|
||||
if (reason?.indexOf('execution reverted: ') === 0) reason = reason.substr('execution reverted: '.length)
|
||||
|
||||
switch (reason) {
|
||||
case 'UniswapV2Router: EXPIRED':
|
||||
return (
|
||||
<Trans>
|
||||
The transaction could not be sent because the deadline has passed. Please check that your transaction deadline
|
||||
is not too low.
|
||||
</Trans>
|
||||
)
|
||||
case 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT':
|
||||
case 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT':
|
||||
return (
|
||||
<Trans>
|
||||
This transaction will not succeed either due to price movement or fee on transfer. Try increasing your
|
||||
slippage tolerance.
|
||||
</Trans>
|
||||
)
|
||||
case 'TransferHelper: TRANSFER_FROM_FAILED':
|
||||
return <Trans>The input token cannot be transferred. There may be an issue with the input token.</Trans>
|
||||
case 'UniswapV2: TRANSFER_FAILED':
|
||||
return <Trans>The output token cannot be transferred. There may be an issue with the output token.</Trans>
|
||||
case 'UniswapV2: K':
|
||||
return (
|
||||
<Trans>
|
||||
The Uniswap invariant x*y=k was not satisfied by the swap. This usually means one of the tokens you are
|
||||
swapping incorporates custom behavior on transfer.
|
||||
</Trans>
|
||||
)
|
||||
case 'Too little received':
|
||||
case 'Too much requested':
|
||||
case 'STF':
|
||||
return (
|
||||
<Trans>
|
||||
This transaction will not succeed due to price movement. Try increasing your slippage tolerance. Note: fee on
|
||||
transfer and rebase tokens are incompatible with Uniswap V3.
|
||||
</Trans>
|
||||
)
|
||||
case 'TF':
|
||||
return (
|
||||
<Trans>
|
||||
The output token cannot be transferred. There may be an issue with the output token. Note: fee on transfer and
|
||||
rebase tokens are incompatible with Uniswap V3.
|
||||
</Trans>
|
||||
)
|
||||
default:
|
||||
if (reason?.indexOf('undefined is not an object') !== -1) {
|
||||
console.error(error, reason)
|
||||
return (
|
||||
<Trans>
|
||||
An error occurred when trying to execute this swap. You may need to increase your slippage tolerance. If
|
||||
that does not work, there may be an incompatibility with the token you are trading. Note: fee on transfer
|
||||
and rebase tokens are incompatible with Uniswap V3.
|
||||
</Trans>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Trans>
|
||||
Unknown error{reason ? `: "${reason}"` : ''}. Try increasing your slippage tolerance. Note: fee on transfer
|
||||
and rebase tokens are incompatible with Uniswap V3.
|
||||
</Trans>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// returns a function that will execute a swap, if the parameters are all valid
|
||||
// and the user has approved the slippage adjusted input amount for the trade
|
||||
@@ -292,139 +20,56 @@ export function useSwapCallback(
|
||||
recipientAddressOrName: string | null, // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender
|
||||
signatureData: SignatureData | undefined | null
|
||||
): { state: SwapCallbackState; callback: null | (() => Promise<string>); error: ReactNode | null } {
|
||||
const { account, chainId, library } = useActiveWeb3React()
|
||||
const { account } = useActiveWeb3React()
|
||||
|
||||
const swapCalls = useSwapCallArguments(trade, allowedSlippage, recipientAddressOrName, signatureData)
|
||||
const deadline = useTransactionDeadline()
|
||||
|
||||
const addTransaction = useTransactionAdder()
|
||||
|
||||
const { address: recipientAddress } = useENS(recipientAddressOrName)
|
||||
const recipient = recipientAddressOrName === null ? account : recipientAddress
|
||||
|
||||
return useMemo(() => {
|
||||
if (!trade || !library || !account || !chainId) {
|
||||
return { state: SwapCallbackState.INVALID, callback: null, error: <Trans>Missing dependencies</Trans> }
|
||||
const {
|
||||
state,
|
||||
callback: libCallback,
|
||||
error,
|
||||
} = useLibSwapCallBack(trade, allowedSlippage, recipient, signatureData, deadline)
|
||||
|
||||
const callback = useMemo(() => {
|
||||
if (!libCallback || !trade) {
|
||||
return null
|
||||
}
|
||||
if (!recipient) {
|
||||
if (recipientAddressOrName !== null) {
|
||||
return { state: SwapCallbackState.INVALID, callback: null, error: <Trans>Invalid recipient</Trans> }
|
||||
} else {
|
||||
return { state: SwapCallbackState.LOADING, callback: null, error: null }
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
state: SwapCallbackState.VALID,
|
||||
callback: async function onSwap(): Promise<string> {
|
||||
const estimatedCalls: SwapCallEstimate[] = await Promise.all(
|
||||
swapCalls.map((call) => {
|
||||
const { address, calldata, value } = call
|
||||
|
||||
const tx =
|
||||
!value || isZero(value)
|
||||
? { from: account, to: address, data: calldata }
|
||||
: {
|
||||
from: account,
|
||||
to: address,
|
||||
data: calldata,
|
||||
value,
|
||||
}
|
||||
|
||||
return library
|
||||
.estimateGas(tx)
|
||||
.then((gasEstimate) => {
|
||||
return {
|
||||
call,
|
||||
gasEstimate,
|
||||
}
|
||||
})
|
||||
.catch((gasError) => {
|
||||
console.debug('Gas estimate failed, trying eth_call to extract error', call)
|
||||
|
||||
return library
|
||||
.call(tx)
|
||||
.then((result) => {
|
||||
console.debug('Unexpected successful call after failed estimate gas', call, gasError, result)
|
||||
return { call, error: <Trans>Unexpected issue with estimating the gas. Please try again.</Trans> }
|
||||
})
|
||||
.catch((callError) => {
|
||||
console.debug('Call threw error', call, callError)
|
||||
return { call, error: swapErrorToUserReadableMessage(callError) }
|
||||
})
|
||||
})
|
||||
})
|
||||
return () =>
|
||||
libCallback().then((response) => {
|
||||
addTransaction(
|
||||
response,
|
||||
trade.tradeType === TradeType.EXACT_INPUT
|
||||
? {
|
||||
type: TransactionType.SWAP,
|
||||
tradeType: TradeType.EXACT_INPUT,
|
||||
inputCurrencyId: currencyId(trade.inputAmount.currency),
|
||||
inputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
|
||||
expectedOutputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
|
||||
outputCurrencyId: currencyId(trade.outputAmount.currency),
|
||||
minimumOutputCurrencyAmountRaw: trade.minimumAmountOut(allowedSlippage).quotient.toString(),
|
||||
}
|
||||
: {
|
||||
type: TransactionType.SWAP,
|
||||
tradeType: TradeType.EXACT_OUTPUT,
|
||||
inputCurrencyId: currencyId(trade.inputAmount.currency),
|
||||
maximumInputCurrencyAmountRaw: trade.maximumAmountIn(allowedSlippage).quotient.toString(),
|
||||
outputCurrencyId: currencyId(trade.outputAmount.currency),
|
||||
outputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
|
||||
expectedInputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
|
||||
}
|
||||
)
|
||||
return response.hash
|
||||
})
|
||||
}, [addTransaction, allowedSlippage, libCallback, trade])
|
||||
|
||||
// a successful estimation is a bignumber gas estimate and the next call is also a bignumber gas estimate
|
||||
let bestCallOption: SuccessfulCall | SwapCallEstimate | undefined = estimatedCalls.find(
|
||||
(el, ix, list): el is SuccessfulCall =>
|
||||
'gasEstimate' in el && (ix === list.length - 1 || 'gasEstimate' in list[ix + 1])
|
||||
)
|
||||
|
||||
// check if any calls errored with a recognizable error
|
||||
if (!bestCallOption) {
|
||||
const errorCalls = estimatedCalls.filter((call): call is FailedCall => 'error' in call)
|
||||
if (errorCalls.length > 0) throw errorCalls[errorCalls.length - 1].error
|
||||
const firstNoErrorCall = estimatedCalls.find<SwapCallEstimate>(
|
||||
(call): call is SwapCallEstimate => !('error' in call)
|
||||
)
|
||||
if (!firstNoErrorCall) throw new Error(t`Unexpected error. Could not estimate gas for the swap.`)
|
||||
bestCallOption = firstNoErrorCall
|
||||
}
|
||||
|
||||
const {
|
||||
call: { address, calldata, value },
|
||||
} = bestCallOption
|
||||
|
||||
return library
|
||||
.getSigner()
|
||||
.sendTransaction({
|
||||
from: account,
|
||||
to: address,
|
||||
data: calldata,
|
||||
// let the wallet try if we can't estimate the gas
|
||||
...('gasEstimate' in bestCallOption ? { gasLimit: calculateGasMargin(bestCallOption.gasEstimate) } : {}),
|
||||
...(value && !isZero(value) ? { value } : {}),
|
||||
})
|
||||
.then((response) => {
|
||||
addTransaction(
|
||||
response,
|
||||
trade.tradeType === TradeType.EXACT_INPUT
|
||||
? {
|
||||
type: TransactionType.SWAP,
|
||||
tradeType: TradeType.EXACT_INPUT,
|
||||
inputCurrencyId: currencyId(trade.inputAmount.currency),
|
||||
inputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
|
||||
expectedOutputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
|
||||
outputCurrencyId: currencyId(trade.outputAmount.currency),
|
||||
minimumOutputCurrencyAmountRaw: trade.minimumAmountOut(allowedSlippage).quotient.toString(),
|
||||
}
|
||||
: {
|
||||
type: TransactionType.SWAP,
|
||||
tradeType: TradeType.EXACT_OUTPUT,
|
||||
inputCurrencyId: currencyId(trade.inputAmount.currency),
|
||||
maximumInputCurrencyAmountRaw: trade.maximumAmountIn(allowedSlippage).quotient.toString(),
|
||||
outputCurrencyId: currencyId(trade.outputAmount.currency),
|
||||
outputCurrencyAmountRaw: trade.outputAmount.quotient.toString(),
|
||||
expectedInputCurrencyAmountRaw: trade.inputAmount.quotient.toString(),
|
||||
}
|
||||
)
|
||||
|
||||
return response.hash
|
||||
})
|
||||
.catch((error) => {
|
||||
// if the user rejected the tx, pass this along
|
||||
if (error?.code === 4001) {
|
||||
throw new Error(t`Transaction rejected.`)
|
||||
} else {
|
||||
// otherwise, the error was unexpected and we need to convey that
|
||||
console.error(`Swap failed`, error, address, calldata, value)
|
||||
|
||||
throw new Error(t`Swap failed: ${swapErrorToUserReadableMessage(error)}`)
|
||||
}
|
||||
})
|
||||
},
|
||||
error: null,
|
||||
}
|
||||
}, [trade, library, account, chainId, recipient, recipientAddressOrName, swapCalls, addTransaction, allowedSlippage])
|
||||
return {
|
||||
state,
|
||||
callback,
|
||||
error,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { useSingleCallResult } from 'lib/hooks/multicall'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||
import { useTokenContract } from './useContract'
|
||||
|
||||
export function useTokenAllowance(token?: Token, owner?: string, spender?: string): CurrencyAmount<Token> | undefined {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useMemo } from 'react'
|
||||
import { useCombinedActiveList } from 'state/lists/hooks'
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { BigNumber } from 'ethers'
|
||||
import { useSingleCallResult } from 'lib/hooks/multicall'
|
||||
|
||||
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||
import { useTokenContract } from './useContract'
|
||||
|
||||
// returns undefined if input token is undefined, or fails to get token contract,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { L2_CHAIN_IDS } from 'constants/chains'
|
||||
import { L2_DEADLINE_FROM_NOW } from 'constants/misc'
|
||||
import { BigNumber } from 'ethers'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { useMemo } from 'react'
|
||||
import { useAppSelector } from 'state/hooks'
|
||||
|
||||
import useCurrentBlockTimestamp from './useCurrentBlockTimestamp'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
// combines the block timestamp with the user setting to give the deadline that should be used for any submitted transaction
|
||||
export default function useTransactionDeadline(): BigNumber | undefined {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Currency, CurrencyAmount, Price, Token, TradeType } from '@uniswap/sdk-core'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
|
||||
import { useMemo } from 'react'
|
||||
import { tryParseAmount } from 'state/swap/hooks'
|
||||
|
||||
import { SupportedChainId } from '../constants/chains'
|
||||
import { DAI_OPTIMISM, USDC, USDC_ARBITRUM, USDC_POLYGON } from '../constants/tokens'
|
||||
import { useBestV2Trade } from './useBestV2Trade'
|
||||
import { useClientSideV3Trade } from './useClientSideV3Trade'
|
||||
import { useActiveWeb3React } from './web3'
|
||||
|
||||
// Stablecoin amounts used when calculating spot price for a given currency.
|
||||
// The amount is large enough to filter low liquidity pairs.
|
||||
@@ -87,7 +87,7 @@ export function useStablecoinAmountFromFiatValue(fiatValue: string | null | unde
|
||||
|
||||
try {
|
||||
// parse USD string into CurrencyAmount based on stablecoin decimals
|
||||
return tryParseAmount(parsedForDecimals, stablecoin)
|
||||
return tryParseCurrencyAmount(parsedForDecimals, stablecoin)
|
||||
} catch (error) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
18
src/hooks/useV2LiquidityTokenPermit.ts
Normal file
18
src/hooks/useV2LiquidityTokenPermit.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
|
||||
import { PermitInfo, PermitType, useERC20Permit } from './useERC20Permit'
|
||||
import useTransactionDeadline from './useTransactionDeadline'
|
||||
|
||||
const REMOVE_V2_LIQUIDITY_PERMIT_INFO: PermitInfo = {
|
||||
version: '1',
|
||||
name: 'Uniswap V2',
|
||||
type: PermitType.AMOUNT,
|
||||
}
|
||||
|
||||
export function useV2LiquidityTokenPermit(
|
||||
liquidityAmount: CurrencyAmount<Token> | null | undefined,
|
||||
spender: string | null | undefined
|
||||
) {
|
||||
const transactionDeadline = useTransactionDeadline()
|
||||
return useERC20Permit(liquidityAmount, spender, transactionDeadline, REMOVE_V2_LIQUIDITY_PERMIT_INFO)
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Interface } from '@ethersproject/abi'
|
||||
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
|
||||
import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.json'
|
||||
import { computePairAddress, Pair } from '@uniswap/v2-sdk'
|
||||
import { Interface } from 'ethers/lib/utils'
|
||||
import { useMultipleContractSingleData } from 'lib/hooks/multicall'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { V2_FACTORY_ADDRESSES } from '../constants/addresses'
|
||||
import { useMultipleContractSingleData } from '../state/multicall/hooks'
|
||||
|
||||
const PAIR_INTERFACE = new Interface(IUniswapV2PairABI)
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
|
||||
import { Pool } from '@uniswap/v3-sdk'
|
||||
import { BigNumber } from 'ethers'
|
||||
import { useSingleCallResult } from 'lib/hooks/multicall'
|
||||
import useBlockNumber from 'lib/hooks/useBlockNumber'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useBlockNumber } from 'state/application/hooks'
|
||||
import { useSingleCallResult } from 'state/multicall/hooks'
|
||||
import { unwrappedToken } from 'utils/unwrappedToken'
|
||||
|
||||
import { useV3NFTPositionManagerContract } from './useContract'
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user