Compare commits
242 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27843f6189 | ||
|
|
1b10c88c51 | ||
|
|
5d97cbf6ad | ||
|
|
064a73ca1b | ||
|
|
e5a1cb4276 | ||
|
|
e68e1afd9d | ||
|
|
8784a761d6 | ||
|
|
7aa0f500d6 | ||
|
|
06a8151ede | ||
|
|
ac2642fedc | ||
|
|
ac962fb00d | ||
|
|
f3bcf64144 | ||
|
|
711b2ca85c | ||
|
|
aa97ec01d3 | ||
|
|
83b70f3aa6 | ||
|
|
e37dd77680 | ||
|
|
aa37b23126 | ||
|
|
5a90f13a03 | ||
|
|
8c213f9001 | ||
|
|
5722902f96 | ||
|
|
27412e49d5 | ||
|
|
90dfdc6bef | ||
|
|
4f896361be | ||
|
|
c9bc166c1a | ||
|
|
cecbf770c6 | ||
|
|
a7041ea700 | ||
|
|
5413303d24 | ||
|
|
103e18496f | ||
|
|
0e678465cb | ||
|
|
6b68baaa4d | ||
|
|
3c5e3744f1 | ||
|
|
d93b6f795e | ||
|
|
f145a56f4a | ||
|
|
7622290557 | ||
|
|
226544402a | ||
|
|
49fee909bc | ||
|
|
f458ec8e12 | ||
|
|
f9fc506db4 | ||
|
|
5b7a80d10d | ||
|
|
db8dab4559 | ||
|
|
7cc1b899ea | ||
|
|
339a35cb1b | ||
|
|
e2bd8020f3 | ||
|
|
571a932d6e | ||
|
|
3923438011 | ||
|
|
4ed9c7ba94 | ||
|
|
1ead35e782 | ||
|
|
0af516b884 | ||
|
|
13c42a384b | ||
|
|
458e04f94c | ||
|
|
587edf46ea | ||
|
|
768f7659d2 | ||
|
|
96db27722b | ||
|
|
f047f0d196 | ||
|
|
7ac1ed3f52 | ||
|
|
205412fe1e | ||
|
|
43e1fe9764 | ||
|
|
e9a9dd9779 | ||
|
|
fa3325b7e4 | ||
|
|
19aa7173ab | ||
|
|
a3238c701a | ||
|
|
82a763f905 | ||
|
|
61e0ce096b | ||
|
|
9e1a775c13 | ||
|
|
642a4177d8 | ||
|
|
37e085763c | ||
|
|
e2baa051c5 | ||
|
|
a5b152da06 | ||
|
|
38cf4f46d7 | ||
|
|
744c313ecf | ||
|
|
b967b1b236 | ||
|
|
596ea03043 | ||
|
|
e81e8a8f71 | ||
|
|
7d343dcfbd | ||
|
|
bf7a40be7a | ||
|
|
097b8361d4 | ||
|
|
82843ff16a | ||
|
|
890471f590 | ||
|
|
478ee7ba14 | ||
|
|
745be977ef | ||
|
|
0e25c055fb | ||
|
|
82a079935e | ||
|
|
876c1539d4 | ||
|
|
f43fd89884 | ||
|
|
efdfdc9083 | ||
|
|
709f0299e2 | ||
|
|
6b57ffe311 | ||
|
|
f7ecdc4332 | ||
|
|
b1009b0e03 | ||
|
|
5a4c7890c6 | ||
|
|
5a3c91f19f | ||
|
|
d5c4ee0342 | ||
|
|
262d984f92 | ||
|
|
7781f5112e | ||
|
|
204e44ac40 | ||
|
|
7c9d9bdb03 | ||
|
|
cb0ea3f14d | ||
|
|
e54ffcc483 | ||
|
|
8a72b374a8 | ||
|
|
3f64415906 | ||
|
|
41a4500f41 | ||
|
|
093dc66cfe | ||
|
|
b10729d217 | ||
|
|
9b0fa8a2c4 | ||
|
|
82c026872f | ||
|
|
222a6d53bc | ||
|
|
0f35f6ee93 | ||
|
|
7b83e3968f | ||
|
|
7938273c0c | ||
|
|
51a4504c75 | ||
|
|
b2697f0077 | ||
|
|
34a58851f7 | ||
|
|
5a20dc82cd | ||
|
|
d1627a6c36 | ||
|
|
55c971892c | ||
|
|
68f8576499 | ||
|
|
d0be3bf222 | ||
|
|
b79fe4b833 | ||
|
|
408c907870 | ||
|
|
8a99bad736 | ||
|
|
5ba3d2f679 | ||
|
|
ec4cd57dc0 | ||
|
|
ccad45d24e | ||
|
|
1903a16097 | ||
|
|
0ea029db4f | ||
|
|
cb41df4cd5 | ||
|
|
659a564db8 | ||
|
|
74c61c0213 | ||
|
|
dc55a21285 | ||
|
|
48cc6811c9 | ||
|
|
ab93d512d3 | ||
|
|
cf4c26a77c | ||
|
|
02296c686f | ||
|
|
60bd0eb86c | ||
|
|
188b321cc9 | ||
|
|
377331c44e | ||
|
|
dfd442cdac | ||
|
|
9561cf54e4 | ||
|
|
714953b50e | ||
|
|
b3844e38d1 | ||
|
|
5dac6a03eb | ||
|
|
d18974480a | ||
|
|
6a90bf3b9d | ||
|
|
5026ebded8 | ||
|
|
02dbed7a75 | ||
|
|
20f462f5a4 | ||
|
|
cf9c6e4b4c | ||
|
|
c0201206cc | ||
|
|
2d2508f681 | ||
|
|
609542c49b | ||
|
|
9a0294f469 | ||
|
|
68e6bc1ba8 | ||
|
|
bbc64f12bb | ||
|
|
06f5fdc6ad | ||
|
|
b4e756ebba | ||
|
|
17439d6fbb | ||
|
|
79d582c72f | ||
|
|
245f8d7279 | ||
|
|
3bf36eade4 | ||
|
|
8eb864426f | ||
|
|
af83399606 | ||
|
|
1d5be31621 | ||
|
|
abe6bf500c | ||
|
|
36cfe627f1 | ||
|
|
aef5d0513a | ||
|
|
5e09a0ced7 | ||
|
|
f768428b9d | ||
|
|
76c9bf84cd | ||
|
|
f26a3306ec | ||
|
|
1846883e56 | ||
|
|
9cacd577e7 | ||
|
|
5e8d725e0e | ||
|
|
c63482b6f7 | ||
|
|
e15a8ddf59 | ||
|
|
1e7dff060f | ||
|
|
244ed38b72 | ||
|
|
87d547ab2b | ||
|
|
0c213be5d9 | ||
|
|
f62d301891 | ||
|
|
902cd343d2 | ||
|
|
a26db9a51d | ||
|
|
b7cb2d24c7 | ||
|
|
aea2bca775 | ||
|
|
93b79be1b6 | ||
|
|
54531b53c6 | ||
|
|
b286c1ba09 | ||
|
|
8dd1be3b47 | ||
|
|
0b8afcff41 | ||
|
|
e4b727f6f0 | ||
|
|
6e0b24cbb1 | ||
|
|
442879ccd9 | ||
|
|
50386f65e7 | ||
|
|
eba8170c46 | ||
|
|
095dbffbca | ||
|
|
4df2824984 | ||
|
|
15345690e3 | ||
|
|
3e36281f77 | ||
|
|
27a868f0cb | ||
|
|
03c3fdef34 | ||
|
|
d37e963f26 | ||
|
|
2e40bef7f0 | ||
|
|
dac87e2299 | ||
|
|
be15604244 | ||
|
|
077570efb4 | ||
|
|
a9ea5c9a1d | ||
|
|
f5b91c9cda | ||
|
|
c0e6388bf8 | ||
|
|
d89d5598a0 | ||
|
|
6f66f5ee77 | ||
|
|
4b8d942da6 | ||
|
|
ef2cb75f6f | ||
|
|
5e0e9be955 | ||
|
|
72a914b0d1 | ||
|
|
c4ce5caf0c | ||
|
|
b87a45b0ba | ||
|
|
80eb394877 | ||
|
|
b47808fcd9 | ||
|
|
e56fdf3eff | ||
|
|
bca70dd46a | ||
|
|
78581d5420 | ||
|
|
896f2fc3c0 | ||
|
|
66e9092d11 | ||
|
|
df397a8a15 | ||
|
|
a7b945a812 | ||
|
|
b71708faee | ||
|
|
7715294bea | ||
|
|
7a57fbe6d9 | ||
|
|
dddd24f1f4 | ||
|
|
13d9f6c29d | ||
|
|
16d8f958b1 | ||
|
|
868242a6b2 | ||
|
|
aa1db580ee | ||
|
|
496a963dcf | ||
|
|
5cb37dc098 | ||
|
|
b58e200eac | ||
|
|
be8b6ccec7 | ||
|
|
d3700882b2 | ||
|
|
31d286c8fa | ||
|
|
77c7dab024 | ||
|
|
ffab1c56c7 | ||
|
|
cd4a2313ac | ||
|
|
81b8afdd3d |
2
.env
@@ -1 +1 @@
|
||||
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
|
||||
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
|
||||
@@ -2,3 +2,4 @@ REACT_APP_INFURA_KEY="099fc58e0de9451d80b18d7c74caa7c1"
|
||||
REACT_APP_PORTIS_ID="c0e2bf01-4b08-4fd5-ac7b-8e26b58cd236"
|
||||
REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
|
||||
REACT_APP_GOOGLE_ANALYTICS_ID="UA-128182339-4"
|
||||
REACT_APP_FIREBASE_KEY="AIzaSyBcZWwTcTJHj_R6ipZcrJkXdq05PuX0Rs0"
|
||||
|
||||
@@ -8,21 +8,42 @@
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"ignorePatterns": ["node_modules/**/*"],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"src/types/v3",
|
||||
"src/abis/types",
|
||||
"src/locales/**/*.js",
|
||||
"src/locales/**/en-US.po",
|
||||
"src/state/data/generated.ts",
|
||||
"node_modules",
|
||||
"coverage",
|
||||
"build",
|
||||
"dist",
|
||||
".DS_Store",
|
||||
".env.local",
|
||||
".env.development.local",
|
||||
".env.test.local",
|
||||
".env.production.local",
|
||||
".idea/",
|
||||
".vscode/",
|
||||
"package-lock.json",
|
||||
"yarn.lock"
|
||||
],
|
||||
"extends": [
|
||||
"react-app",
|
||||
"plugin:react/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"prettier/@typescript-eslint",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"plugins": ["simple-import-sort"],
|
||||
"plugins": ["simple-import-sort", "unused-imports"],
|
||||
"rules": {
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"simple-import-sort/imports": "error",
|
||||
"simple-import-sort/exports": "error",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
@@ -37,10 +58,6 @@
|
||||
"error",
|
||||
{
|
||||
"paths": [
|
||||
{
|
||||
"name": "lodash",
|
||||
"message": "Please import from 'lodash/module' directly to support tree-shaking."
|
||||
},
|
||||
{
|
||||
"name": "ethers",
|
||||
"message": "Please import from '@ethersproject/module' directly to support tree-shaking."
|
||||
@@ -48,6 +65,11 @@
|
||||
{
|
||||
"name": "styled-components",
|
||||
"message": "Please import from styled-components/macro."
|
||||
},
|
||||
{
|
||||
"name": "@lingui/macro",
|
||||
"importNames": ["t"],
|
||||
"message": "Please use <Trans> instead of t."
|
||||
}
|
||||
],
|
||||
"patterns": [
|
||||
|
||||
1
.github/dependabot.yml
vendored
@@ -7,3 +7,4 @@ updates:
|
||||
interval: "daily"
|
||||
allow:
|
||||
- dependency-name: "@uniswap/token-lists"
|
||||
- dependency-name: "@uniswap/default-token-list"
|
||||
|
||||
40
.github/workflows/bundle.yaml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: Widgets
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
registry-url: https://registry.npmjs.org
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
- uses: actions/cache@v2
|
||||
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: yarn widgets:build
|
||||
10
.github/workflows/lint.yml
vendored
@@ -11,7 +11,6 @@ on:
|
||||
jobs:
|
||||
run-linters:
|
||||
name: Run linters
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login == github.repository_owner }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -39,10 +38,15 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Run linters
|
||||
- name: Run eslint w/ autofix
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.owner.login == github.repository_owner }}
|
||||
uses: wearerequired/lint-action@36c7e6689e80d785d27a22f71d970f3a3b4fcb70
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
eslint: true
|
||||
eslint_extensions: js,jsx,ts,tsx,json
|
||||
eslint_args: "-c .eslintrc.json"
|
||||
auto_fix: true
|
||||
|
||||
- name: Run eslint
|
||||
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.owner.login != github.repository_owner }}
|
||||
run: yarn eslint .
|
||||
|
||||
7
.gitignore
vendored
@@ -3,8 +3,12 @@
|
||||
# generated contract types
|
||||
/src/types/v3
|
||||
/src/abis/types
|
||||
/src/lib/locales/**/*.js
|
||||
/src/lib/locales/**/en-US.po
|
||||
/src/lib/locales/**/pseudo.po
|
||||
/src/locales/**/*.js
|
||||
/src/locales/**/en-US.po
|
||||
/src/locales/**/pseudo.po
|
||||
/src/state/data/generated.ts
|
||||
|
||||
# dependencies
|
||||
@@ -16,6 +20,9 @@
|
||||
# production
|
||||
/build
|
||||
|
||||
# widgets
|
||||
/dist
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
|
||||
@@ -27,7 +27,7 @@ makes large architectural changes, consider following all the standards.
|
||||
- If something breaks, add automated tests so it doesn't break again
|
||||
- Add integration tests for new pages or flows
|
||||
- Verify that all CI checks pass before merging
|
||||
- Have at least one product manager or designer approve of significant UX changes
|
||||
- Have at least one product manager or designer approve of any significant UX changes
|
||||
|
||||
## Guidelines
|
||||
|
||||
@@ -42,7 +42,7 @@ The following points should help guide your development:
|
||||
- An Ethereum node should be the only critical dependency
|
||||
- All other external dependencies should only enhance the UX ([graceful degradation](https://developer.mozilla.org/en-US/docs/Glossary/Graceful_degradation))
|
||||
- Accessibility: anyone can use the interface
|
||||
- The interface should be responsive, small and run well on low performance devices (majority of swaps on mobile!)
|
||||
- The interface should be responsive, small and also run well on low performance devices (majority of swaps on mobile!)
|
||||
|
||||
## Release process
|
||||
|
||||
@@ -73,4 +73,4 @@ We sync to the repository on a schedule, rather than download translations at bu
|
||||
|
||||
You can contribute by joining Crowdin to proofread existing translations [here](https://crowdin.com/project/uniswap-interface/invite?d=93i5n413q403t4g473p443o4c3t2g3s21343u2c3n403l4b3v2735353i4g4k4l4g453j4g4o4j4e4k4b323l4a3h463s4g453q443m4e3t2b303s2a35353l403o443v293e303k4g4n4r4g483i4g4r4j4e4o473i5n4a3t463t4o4)
|
||||
|
||||
Or, ask to join us as a translator in the Discord!
|
||||
Or, ask to join us as a translator in the Discord!!
|
||||
|
||||
@@ -10,7 +10,7 @@ An open source interface for Uniswap -- a protocol for decentralized exchange of
|
||||
|
||||
- Website: [uniswap.org](https://uniswap.org/)
|
||||
- Interface: [app.uniswap.org](https://app.uniswap.org)
|
||||
- Docs: [uniswap.org/docs/](https://uniswap.org/docs/)
|
||||
- Docs: [uniswap.org/docs/](https://docs.uniswap.org/)
|
||||
- Twitter: [@Uniswap](https://twitter.com/Uniswap)
|
||||
- Reddit: [/r/Uniswap](https://www.reddit.com/r/Uniswap/)
|
||||
- Email: [contact@uniswap.org](mailto:contact@uniswap.org)
|
||||
|
||||
9
cosmos.config.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"watchDirs": [
|
||||
"src"
|
||||
],
|
||||
"webpack": {
|
||||
"configPath": "react-scripts/config/webpack.config",
|
||||
"overridePath": "cosmos.override.js"
|
||||
}
|
||||
}
|
||||
14
cosmos.override.js
Normal file
@@ -0,0 +1,14 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
|
||||
// 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
|
||||
),
|
||||
})
|
||||
@@ -5,6 +5,11 @@
|
||||
}
|
||||
},
|
||||
"asToken0": [
|
||||
{
|
||||
"feeTier": "100",
|
||||
"totalValueLockedToken0": "0",
|
||||
"totalValueLockedToken1": "3"
|
||||
},
|
||||
{
|
||||
"feeTier": "500",
|
||||
"totalValueLockedToken0": "0",
|
||||
@@ -13,7 +18,7 @@
|
||||
{
|
||||
"feeTier": "3000",
|
||||
"totalValueLockedToken0": "0",
|
||||
"totalValueLockedToken1": "7"
|
||||
"totalValueLockedToken1": "4"
|
||||
},
|
||||
{
|
||||
"feeTier": "10000",
|
||||
|
||||
@@ -58,7 +58,7 @@ describe('Add Liquidity', () => {
|
||||
cy.wait('@feeTierDistributionQuery')
|
||||
|
||||
cy.get('#add-liquidity-selected-fee .selected-fee-label').should('contain.text', '0.3% fee tier')
|
||||
cy.get('#add-liquidity-selected-fee .selected-fee-percentage').should('contain.text', '70%')
|
||||
cy.get('#add-liquidity-selected-fee .selected-fee-percentage').should('contain.text', '40%')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -74,6 +74,7 @@ class CustomizedBridge extends Eip1193Bridge {
|
||||
}
|
||||
|
||||
// sets up the injected provider to be a mock ethereum provider with the given mnemonic/index
|
||||
// eslint-disable-next-line no-undef
|
||||
Cypress.Commands.overwrite('visit', (original, url, options) => {
|
||||
return original(url.startsWith('/') && url.length > 2 && !url.startsWith('/#') ? `/#${url}` : url, {
|
||||
...options,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export default {
|
||||
const linguiConfig = {
|
||||
catalogs: [
|
||||
{
|
||||
path: '<rootDir>/src/locales/{locale}',
|
||||
@@ -46,9 +46,13 @@ export default {
|
||||
'vi-VN',
|
||||
'zh-CN',
|
||||
'zh-TW',
|
||||
'pseudo',
|
||||
],
|
||||
orderBy: 'messageId',
|
||||
rootDir: '.',
|
||||
runtimeConfigModule: ['@lingui/core', 'i18n'],
|
||||
sourceLocale: 'en-US',
|
||||
pseudoLocale: 'pseudo',
|
||||
}
|
||||
|
||||
export default linguiConfig
|
||||
|
||||
131
package.json
@@ -2,9 +2,15 @@
|
||||
"name": "@uniswap/interface",
|
||||
"description": "Uniswap Interface",
|
||||
"homepage": ".",
|
||||
"main": "dist/interface.js",
|
||||
"module": "dist/interface.esm.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"lib",
|
||||
"dist"
|
||||
],
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@davatar/react": "1.6.0",
|
||||
"@ethersproject/experimental": "^5.4.0",
|
||||
"@gnosis.pm/safe-apps-web3-react": "^0.6.0",
|
||||
"@graphql-codegen/cli": "1.21.5",
|
||||
@@ -12,13 +18,16 @@
|
||||
"@graphql-codegen/typescript-operations": "^1.18.2",
|
||||
"@graphql-codegen/typescript-rtk-query": "^1.1.1",
|
||||
"@lingui/cli": "^3.9.0",
|
||||
"@lingui/macro": "^3.9.0",
|
||||
"@lingui/react": "^3.9.0",
|
||||
"@popperjs/core": "^2.4.4",
|
||||
"@metamask/jazzicon": "^2.0.0",
|
||||
"@reach/dialog": "^0.10.3",
|
||||
"@reach/portal": "^0.10.3",
|
||||
"@react-hook/window-scroll": "^1.3.0",
|
||||
"@reduxjs/toolkit": "^1.6.1",
|
||||
"@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",
|
||||
@@ -30,7 +39,6 @@
|
||||
"@types/lingui__core": "^2.7.1",
|
||||
"@types/lingui__macro": "^2.7.4",
|
||||
"@types/lingui__react": "^2.8.3",
|
||||
"@types/luxon": "^1.24.4",
|
||||
"@types/ms.macro": "^2.0.0",
|
||||
"@types/multicodec": "^1.0.0",
|
||||
"@types/node": "^13.13.5",
|
||||
@@ -48,75 +56,65 @@
|
||||
"@types/wcag-contrast": "^3.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.1.0",
|
||||
"@typescript-eslint/parser": "^4.1.0",
|
||||
"@uniswap/default-token-list": "^2.0.0",
|
||||
"@uniswap/governance": "^1.0.2",
|
||||
"@uniswap/liquidity-staker": "^1.0.2",
|
||||
"@uniswap/merkle-distributor": "1.0.1",
|
||||
"@uniswap/sdk-core": "^3.0.1",
|
||||
"@uniswap/token-lists": "^1.0.0-beta.26",
|
||||
"@uniswap/router-sdk": "^1.0.3",
|
||||
"@uniswap/smart-order-router": "^2.5.10",
|
||||
"@uniswap/v2-core": "1.0.0",
|
||||
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
||||
"@uniswap/v2-sdk": "^3.0.0-alpha.2",
|
||||
"@uniswap/v3-core": "1.0.0",
|
||||
"@uniswap/v3-periphery": "^1.1.1",
|
||||
"@uniswap/v3-sdk": "^3.4.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.3",
|
||||
"ajv": "^6.12.3",
|
||||
"@web3-react/walletlink-connector": "^6.2.8",
|
||||
"array.prototype.flat": "^1.2.4",
|
||||
"array.prototype.flatmap": "^1.2.4",
|
||||
"cids": "^1.0.0",
|
||||
"copy-to-clipboard": "^3.2.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"cypress": "^7.7.0",
|
||||
"d3": "^7.0.0",
|
||||
"eslint": "^7.11.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-better-styled-components": "^1.1.2",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"eslint-plugin-react": "^7.19.0",
|
||||
"eslint-plugin-react-hooks": "^4.0.0",
|
||||
"eslint-plugin-simple-import-sort": "^7.0.0",
|
||||
"ethers": "^5.4.6",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"firebase": "^9.1.3",
|
||||
"graphql": "^15.5.0",
|
||||
"graphql-request": "^3.4.0",
|
||||
"inter-ui": "^3.13.1",
|
||||
"jest-styled-components": "^7.0.5",
|
||||
"luxon": "^1.25.0",
|
||||
"ms.macro": "^2.0.0",
|
||||
"multicodec": "^3.0.1",
|
||||
"multihashes": "^4.0.2",
|
||||
"node-vibrant": "^3.1.5",
|
||||
"polished": "^3.3.2",
|
||||
"polyfill-object.fromentries": "^1.0.1",
|
||||
"prettier": "^2.2.1",
|
||||
"qs": "^6.9.4",
|
||||
"react": "^17.0.1",
|
||||
"react-confetti": "^6.0.0",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-feather": "^2.0.8",
|
||||
"react-cosmos": "^5.6.3",
|
||||
"react-ga": "^2.5.7",
|
||||
"react-is": "^17.0.2",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-popper": "^2.2.3",
|
||||
"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",
|
||||
"react-virtualized-auto-sizer": "^1.0.2",
|
||||
"react-window": "^1.8.5",
|
||||
"rebass": "^4.0.7",
|
||||
"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",
|
||||
"styled-components": "^5.3.0",
|
||||
"typechain": "^5.0.0",
|
||||
"typescript": "^4.2.3",
|
||||
"ua-parser-js": "^0.7.28",
|
||||
"use-count-up": "^2.2.5",
|
||||
"use-resize-observer": "^8.0.0",
|
||||
"wcag-contrast": "^3.0.0",
|
||||
"web-vitals": "^2.1.0",
|
||||
"workbox-core": "^6.1.0",
|
||||
@@ -124,27 +122,24 @@
|
||||
"workbox-routing": "^6.1.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"@walletconnect/ethereum-provider": "1.6.5"
|
||||
"@walletconnect/ethereum-provider": "1.7.1"
|
||||
},
|
||||
"scripts": {
|
||||
"contracts:compile:abi": "typechain --target ethers-v5 --out-dir src/abis/types './src/abis/**/*.json'",
|
||||
"contracts:compile:v3": "typechain --target ethers-v5 --out-dir src/types/v3 './node_modules/@uniswap/?(v3-core|v3-periphery)/artifacts/contracts/**/*.json'",
|
||||
"contracts:compile:abi": "typechain --target ethers-v5 --out-dir src/abis/types \"./src/abis/**/*.json\"",
|
||||
"contracts:compile:v3": "typechain --target ethers-v5 --out-dir src/types/v3 \"./node_modules/@uniswap/**/artifacts/contracts/**/*.json\"",
|
||||
"contracts:compile": "yarn contracts:compile:abi && yarn contracts:compile:v3",
|
||||
"graphql:generate": "graphql-codegen --config codegen.yml",
|
||||
"prei18n:extract": "touch src/locales/en-US.po",
|
||||
"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",
|
||||
"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 --record'"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app",
|
||||
"ignorePatterns": [
|
||||
"node_modules"
|
||||
]
|
||||
"test:e2e": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run --record'",
|
||||
"widgets:start": "cross-env FAST_REFRESH=false REACT_APP_IS_WIDGET=true cosmos",
|
||||
"widgets:build": "rollup --config --failAfterWarnings --configPlugin typescript2"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
@@ -159,5 +154,61 @@
|
||||
]
|
||||
},
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {}
|
||||
"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.5",
|
||||
"@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",
|
||||
"@lingui/macro": "^3.9.0",
|
||||
"@lingui/react": "^3.9.0",
|
||||
"@popperjs/core": "^2.4.4",
|
||||
"@uniswap/redux-multicall": "^1.0.0",
|
||||
"@uniswap/sdk-core": "^3.0.1",
|
||||
"@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",
|
||||
"immer": "^9.0.6",
|
||||
"jotai": "^1.3.7",
|
||||
"jsbi": "^3.1.4",
|
||||
"make-plural": "^7.0.0",
|
||||
"multicodec": "^3.0.1",
|
||||
"multihashes": "^4.0.2",
|
||||
"node-vibrant": "^3.2.1-alpha.1",
|
||||
"polished": "^3.3.2",
|
||||
"popper-max-size-modifier": "^0.2.0",
|
||||
"react": "^17.0.1",
|
||||
"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",
|
||||
"redux": "^4.1.2",
|
||||
"styled-components": "^5.3.0",
|
||||
"tiny-invariant": "^1.2.0",
|
||||
"wcag-contrast": "^3.0.0",
|
||||
"wicg-inert": "^3.1.1",
|
||||
"widgets-web3-react/core": "npm:@web3-react/core@8.0.16-alpha.0",
|
||||
"widgets-web3-react/eip1193": "npm:@web3-react/eip1193@8.0.16-alpha.0",
|
||||
"widgets-web3-react/empty": "npm:@web3-react/empty@8.0.17-alpha.0",
|
||||
"widgets-web3-react/metamask": "npm:@web3-react/metamask@8.0.16-alpha.0",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
html {
|
||||
font-size: 16px;
|
||||
font-variant: none;
|
||||
font-smooth: always;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
|
||||
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
|
||||
49
src/abis/erc1155.json
Normal file
@@ -0,0 +1,49 @@
|
||||
[
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "_owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "balanceOf",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_id",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "uri",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
40
src/abis/erc721.json
Normal file
@@ -0,0 +1,40 @@
|
||||
[
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "tokenId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "ownerOf",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "tokenId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "tokenURI",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
6
src/assets/images/gas-icon.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.0047 9.26921H10.2714C11.0078 9.26921 11.6047 9.86617 11.6047 10.6025V12.1359C11.6047 12.7987 12.142 13.3359 12.8047 13.3359C13.4675 13.3359 14.0047 12.7995 14.0047 12.1367V5.22059C14.0047 4.86697 13.7758 4.56227 13.5258 4.31223L10.6714 1.33594M4.00472 2.00254H8.00472C8.7411 2.00254 9.33805 2.59949 9.33805 3.33587V14.0015H2.67139V3.33587C2.67139 2.59949 3.26834 2.00254 4.00472 2.00254ZM14.0047 5.33587C14.0047 6.07225 13.4078 6.66921 12.6714 6.66921C11.935 6.66921 11.3381 6.07225 11.3381 5.33587C11.3381 4.59949 11.935 4.00254 12.6714 4.00254C13.4078 4.00254 14.0047 4.59949 14.0047 5.33587Z" stroke="white"/>
|
||||
<line x1="4" y1="9.99414" x2="8" y2="9.99414" stroke="white"/>
|
||||
<line x1="4" y1="11.9941" x2="8" y2="11.9941" stroke="white"/>
|
||||
<path d="M4 8.16113H8" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 895 B |
12
src/assets/images/router-icon-grey.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_988_5781)">
|
||||
<path d="M11.3333 12.5C7.33329 12.5 6.66663 8.5 3.99996 8.5M3.99996 8.5C6.66663 8.5 7.33329 4.5 11.3333 4.5M3.99996 8.5H1.66663" stroke="#888D9B" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="13.3334" cy="4.5" r="2" stroke="#888D9B" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="13.3334" cy="12.5" r="2" stroke="#888D9B" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_988_5781">
|
||||
<rect width="16" height="16" fill="white" transform="translate(0 0.5)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 733 B |
BIN
src/assets/images/santa-hat.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
9
src/assets/images/survey-orb.svg
Normal file
|
After Width: | Height: | Size: 572 KiB |
3
src/assets/svg/dot_line.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="100%" height="35" viewBox="800 0 300 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<line x1="0" x2="2000" y1="100" y2="100" stroke="currentColor" stroke-width="20" stroke-linecap="round" stroke-dasharray="1, 45"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 233 B |
@@ -1,13 +1,13 @@
|
||||
<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="14" height="15" viewBox="0 0 14 15" fill="black" xmlns="http://www.w3.org/2000/svg">
|
||||
<g style="mix-blend-mode:darken">
|
||||
<path d="M4.15217 1.55141C3.96412 1.52242 3.95619 1.51902 4.04468 1.5055C4.21427 1.47958 4.61472 1.51491 4.89067 1.58012C5.53489 1.73232 6.12109 2.12221 6.74683 2.81466L6.91307 2.99862L7.15088 2.96062C8.15274 2.8006 9.17194 2.92778 10.0244 3.31918C10.2589 3.42686 10.6287 3.64121 10.6749 3.69629C10.6896 3.71384 10.7166 3.82684 10.7349 3.94742C10.7982 4.36458 10.7665 4.68434 10.6382 4.92317C10.5683 5.05313 10.5644 5.09432 10.6114 5.20554C10.6489 5.2943 10.7534 5.35999 10.8569 5.35985C11.0687 5.35956 11.2968 5.0192 11.4024 4.54561L11.4444 4.3575L11.5275 4.45109C11.9835 4.96459 12.3417 5.66488 12.4032 6.16335L12.4192 6.29332L12.3426 6.17517C12.2107 5.97186 12.0781 5.83346 11.9084 5.72183C11.6024 5.52062 11.2789 5.45215 10.4222 5.40727C9.64839 5.36675 9.21045 5.30106 8.77621 5.16032C8.03738 4.9209 7.66493 4.60204 6.78729 3.4576C6.39748 2.94928 6.15654 2.66804 5.91687 2.44155C5.37228 1.92691 4.83716 1.65701 4.15217 1.55141Z" fill="black"/>
|
||||
<path d="M10.8494 2.68637C10.8689 2.34575 10.9153 2.12108 11.0088 1.9159C11.0458 1.83469 11.0804 1.76822 11.0858 1.76822C11.0911 1.76822 11.075 1.82816 11.05 1.90142C10.9821 2.10054 10.9709 2.3729 11.0177 2.68978C11.0771 3.09184 11.1109 3.14985 11.5385 3.58416C11.739 3.78788 11.9723 4.0448 12.0568 4.15511L12.2106 4.35568L12.0568 4.21234C11.8688 4.03705 11.4364 3.6952 11.3409 3.64633C11.2768 3.61356 11.2673 3.61413 11.2278 3.65321C11.1914 3.68922 11.1837 3.74333 11.1787 3.99915C11.1708 4.39786 11.1161 4.65377 10.9842 4.90965C10.9128 5.04805 10.9015 5.01851 10.9661 4.8623C11.0143 4.74566 11.0192 4.69439 11.0189 4.30842C11.0181 3.53291 10.9255 3.34647 10.3823 3.02709C10.2447 2.94618 10.0179 2.8295 9.87839 2.76778C9.73887 2.70606 9.62805 2.6523 9.63208 2.64828C9.64746 2.63307 10.1772 2.78675 10.3905 2.86828C10.7077 2.98954 10.76 3.00526 10.7985 2.99063C10.8244 2.98082 10.8369 2.90608 10.8494 2.68637Z" fill="black"/>
|
||||
<path d="M4.51745 4.01304C4.13569 3.49066 3.89948 2.68973 3.95062 2.091L3.96643 1.90572L4.05333 1.92148C4.21652 1.95106 4.49789 2.05515 4.62964 2.13469C4.9912 2.35293 5.14773 2.64027 5.30697 3.37811C5.35362 3.59423 5.41482 3.8388 5.44298 3.9216C5.48831 4.05487 5.65962 4.36617 5.7989 4.56834C5.89922 4.71395 5.83258 4.78295 5.61082 4.76305C5.27215 4.73267 4.8134 4.41799 4.51745 4.01304Z" fill="black"/>
|
||||
<path d="M10.3863 7.90088C8.60224 7.18693 7.97389 6.56721 7.97389 5.52157C7.97389 5.36769 7.97922 5.24179 7.98571 5.24179C7.99221 5.24179 8.06124 5.29257 8.1391 5.35465C8.50088 5.64305 8.906 5.76623 10.0275 5.92885C10.6875 6.02455 11.0589 6.10185 11.4015 6.21477C12.4904 6.57371 13.1641 7.30212 13.3248 8.29426C13.3715 8.58255 13.3441 9.12317 13.2684 9.4081C13.2087 9.63315 13.0263 10.0388 12.9779 10.0544C12.9645 10.0587 12.9514 10.0076 12.9479 9.93809C12.9296 9.56554 12.7402 9.20285 12.4221 8.93116C12.0604 8.62227 11.5745 8.37633 10.3863 7.90088Z" fill="black"/>
|
||||
<path d="M9.13385 8.19748C9.11149 8.06527 9.07272 7.89643 9.04769 7.82228L9.00217 7.68748L9.08672 7.7818C9.20374 7.91233 9.2962 8.07937 9.37457 8.30185C9.43438 8.47165 9.44111 8.52215 9.44066 8.79807C9.4402 9.06896 9.43273 9.12575 9.3775 9.27858C9.29042 9.51959 9.18233 9.69048 9.00097 9.87391C8.67507 10.2036 8.25607 10.3861 7.65143 10.4618C7.54633 10.4749 7.24 10.4971 6.97069 10.511C6.292 10.5461 5.84531 10.6186 5.44393 10.7587C5.38623 10.7788 5.3347 10.7911 5.32947 10.7859C5.31323 10.7698 5.58651 10.6079 5.81223 10.4998C6.1305 10.3474 6.44733 10.2643 7.15719 10.1468C7.50785 10.0887 7.86998 10.0183 7.96194 9.99029C8.83033 9.72566 9.27671 9.04276 9.13385 8.19748Z" fill="black"/>
|
||||
<path d="M9.95169 9.64109C9.71465 9.13463 9.66022 8.64564 9.79009 8.18961C9.80399 8.14088 9.82632 8.101 9.83976 8.101C9.85319 8.101 9.90913 8.13105 9.96404 8.16777C10.0733 8.24086 10.2924 8.36395 10.876 8.68023C11.6043 9.0749 12.0196 9.3805 12.302 9.72965C12.5493 10.0354 12.7023 10.3837 12.776 10.8084C12.8177 11.0489 12.7932 11.6277 12.7311 11.8699C12.5353 12.6337 12.0802 13.2336 11.4311 13.5837C11.336 13.635 11.2506 13.6771 11.2414 13.6773C11.2321 13.6775 11.2668 13.5899 11.3184 13.4827C11.5367 13.029 11.5616 12.5877 11.3965 12.0965C11.2954 11.7957 11.0893 11.4287 10.6732 10.8084C10.1893 10.0873 10.0707 9.89539 9.95169 9.64109Z" fill="black"/>
|
||||
<path d="M3.25046 12.3737C3.91252 11.8181 4.73629 11.4234 5.48666 11.3022C5.81005 11.25 6.34877 11.2707 6.64823 11.3469C7.12824 11.469 7.55763 11.7425 7.78094 12.0683C7.99918 12.3867 8.09281 12.6642 8.19029 13.2816C8.22875 13.5252 8.27057 13.7697 8.28323 13.8251C8.35644 14.1451 8.4989 14.4008 8.67544 14.5293C8.95583 14.7333 9.43865 14.7459 9.91362 14.5618C9.99423 14.5305 10.0642 14.5089 10.0691 14.5138C10.0864 14.5308 9.84719 14.6899 9.67847 14.7737C9.45143 14.8864 9.2709 14.93 9.03102 14.93C8.59601 14.93 8.23486 14.7101 7.9335 14.2616C7.87419 14.1733 7.7409 13.909 7.63729 13.6741C7.3191 12.9528 7.16199 12.7331 6.79255 12.4926C6.47104 12.2834 6.05641 12.2459 5.74449 12.3979C5.33475 12.5976 5.22043 13.118 5.51389 13.4478C5.63053 13.5789 5.84803 13.6919 6.02588 13.7139C6.35861 13.7551 6.64455 13.5035 6.64455 13.1696C6.64455 12.9528 6.56071 12.8291 6.34966 12.7344C6.0614 12.6051 5.75156 12.7562 5.75304 13.0254C5.75368 13.1402 5.80396 13.2122 5.91971 13.2643C5.99397 13.2977 5.99569 13.3003 5.93514 13.2878C5.67066 13.2333 5.6087 12.9164 5.82135 12.706C6.07667 12.4535 6.60461 12.5649 6.78591 12.9097C6.86208 13.0545 6.87092 13.3429 6.80451 13.517C6.6559 13.9068 6.22256 14.1117 5.78297 14.0002C5.48368 13.9242 5.36181 13.842 5.00097 13.4726C4.37395 12.8306 4.13053 12.7062 3.22657 12.566L3.05335 12.5391L3.25046 12.3737Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.308383 0.883984C2.40235 3.40996 3.84457 4.45213 4.00484 4.67231C4.13717 4.85412 4.08737 5.01757 3.86067 5.14567C3.7346 5.21689 3.47541 5.28905 3.34564 5.28905C3.19887 5.28905 3.14847 5.23278 3.14847 5.23278C3.06337 5.15255 3.01544 5.16658 2.5784 4.39555C1.97166 3.45981 1.46389 2.68357 1.45004 2.67057C1.41801 2.64052 1.41856 2.64153 2.51654 4.59413C2.69394 5.0011 2.55182 5.15049 2.55182 5.20845C2.55182 5.32636 2.51946 5.38834 2.37311 5.55059C2.12914 5.8211 2.02008 6.12505 1.94135 6.7541C1.8531 7.45926 1.60492 7.95737 0.917156 8.80989C0.514562 9.30893 0.448686 9.4004 0.3471 9.60153C0.219144 9.85482 0.183961 9.99669 0.169701 10.3165C0.154629 10.6547 0.183983 10.8732 0.287934 11.1965C0.378939 11.4796 0.473932 11.6665 0.716778 12.0403C0.926351 12.3629 1.04702 12.6027 1.04702 12.6965C1.04702 12.7711 1.06136 12.7712 1.38611 12.6983C2.16328 12.5239 2.79434 12.2171 3.14925 11.8411C3.36891 11.6084 3.42048 11.4799 3.42215 11.1611C3.42325 10.9525 3.41587 10.9088 3.35914 10.7888C3.2668 10.5935 3.09869 10.4311 2.72817 10.1794C2.2427 9.84953 2.03534 9.58398 1.97807 9.21878C1.93108 8.91913 1.98559 8.70771 2.25416 8.14825C2.53214 7.56916 2.60103 7.32239 2.64763 6.73869C2.67773 6.36158 2.71941 6.21286 2.82842 6.09348C2.94212 5.969 3.04447 5.92684 3.32584 5.88863C3.78457 5.82635 4.07667 5.70839 4.31677 5.48849C4.52505 5.29772 4.61221 5.11391 4.62558 4.8372L4.63574 4.62747L4.51934 4.49259C4.09783 4.00411 0.0261003 0.5 0.000160437 0.5C-0.00538105 0.5 0.133325 0.672804 0.308383 0.883984ZM1.28364 10.6992C1.37894 10.5314 1.3283 10.3158 1.16889 10.2104C1.01827 10.1109 0.78428 10.1578 0.78428 10.2875C0.78428 10.3271 0.806303 10.3559 0.855937 10.3813C0.939514 10.424 0.945581 10.4721 0.879823 10.5703C0.81323 10.6698 0.818604 10.7573 0.894991 10.8167C1.0181 10.9125 1.19237 10.8598 1.28364 10.6992Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.92523 5.99865C4.70988 6.06439 4.50054 6.29124 4.43574 6.5291C4.39621 6.67421 4.41864 6.92875 4.47785 7.00736C4.57351 7.13433 4.66602 7.16778 4.91651 7.16603C5.40693 7.16263 5.83327 6.95358 5.88284 6.69224C5.92347 6.47801 5.73622 6.18112 5.4783 6.05078C5.34521 5.98355 5.06217 5.95688 4.92523 5.99865ZM5.49853 6.44422C5.57416 6.33741 5.54107 6.22198 5.41245 6.14391C5.1675 5.99525 4.79708 6.11827 4.79708 6.34826C4.79708 6.46274 4.99025 6.58765 5.16731 6.58765C5.28516 6.58765 5.44644 6.5178 5.49853 6.44422Z" fill="black"/>
|
||||
<path d="M4.15217 1.55141C3.96412 1.52242 3.95619 1.51902 4.04468 1.5055C4.21427 1.47958 4.61472 1.51491 4.89067 1.58012C5.53489 1.73232 6.12109 2.12221 6.74683 2.81466L6.91307 2.99862L7.15088 2.96062C8.15274 2.8006 9.17194 2.92778 10.0244 3.31918C10.2589 3.42686 10.6287 3.64121 10.6749 3.69629C10.6896 3.71384 10.7166 3.82684 10.7349 3.94742C10.7982 4.36458 10.7665 4.68434 10.6382 4.92317C10.5683 5.05313 10.5644 5.09432 10.6114 5.20554C10.6489 5.2943 10.7534 5.35999 10.8569 5.35985C11.0687 5.35956 11.2968 5.0192 11.4024 4.54561L11.4444 4.3575L11.5275 4.45109C11.9835 4.96459 12.3417 5.66488 12.4032 6.16335L12.4192 6.29332L12.3426 6.17517C12.2107 5.97186 12.0781 5.83346 11.9084 5.72183C11.6024 5.52062 11.2789 5.45215 10.4222 5.40727C9.64839 5.36675 9.21045 5.30106 8.77621 5.16032C8.03738 4.9209 7.66493 4.60204 6.78729 3.4576C6.39748 2.94928 6.15654 2.66804 5.91687 2.44155C5.37228 1.92691 4.83716 1.65701 4.15217 1.55141Z"/>
|
||||
<path d="M10.8494 2.68637C10.8689 2.34575 10.9153 2.12108 11.0088 1.9159C11.0458 1.83469 11.0804 1.76822 11.0858 1.76822C11.0911 1.76822 11.075 1.82816 11.05 1.90142C10.9821 2.10054 10.9709 2.3729 11.0177 2.68978C11.0771 3.09184 11.1109 3.14985 11.5385 3.58416C11.739 3.78788 11.9723 4.0448 12.0568 4.15511L12.2106 4.35568L12.0568 4.21234C11.8688 4.03705 11.4364 3.6952 11.3409 3.64633C11.2768 3.61356 11.2673 3.61413 11.2278 3.65321C11.1914 3.68922 11.1837 3.74333 11.1787 3.99915C11.1708 4.39786 11.1161 4.65377 10.9842 4.90965C10.9128 5.04805 10.9015 5.01851 10.9661 4.8623C11.0143 4.74566 11.0192 4.69439 11.0189 4.30842C11.0181 3.53291 10.9255 3.34647 10.3823 3.02709C10.2447 2.94618 10.0179 2.8295 9.87839 2.76778C9.73887 2.70606 9.62805 2.6523 9.63208 2.64828C9.64746 2.63307 10.1772 2.78675 10.3905 2.86828C10.7077 2.98954 10.76 3.00526 10.7985 2.99063C10.8244 2.98082 10.8369 2.90608 10.8494 2.68637Z"/>
|
||||
<path d="M4.51745 4.01304C4.13569 3.49066 3.89948 2.68973 3.95062 2.091L3.96643 1.90572L4.05333 1.92148C4.21652 1.95106 4.49789 2.05515 4.62964 2.13469C4.9912 2.35293 5.14773 2.64027 5.30697 3.37811C5.35362 3.59423 5.41482 3.8388 5.44298 3.9216C5.48831 4.05487 5.65962 4.36617 5.7989 4.56834C5.89922 4.71395 5.83258 4.78295 5.61082 4.76305C5.27215 4.73267 4.8134 4.41799 4.51745 4.01304Z"/>
|
||||
<path d="M10.3863 7.90088C8.60224 7.18693 7.97389 6.56721 7.97389 5.52157C7.97389 5.36769 7.97922 5.24179 7.98571 5.24179C7.99221 5.24179 8.06124 5.29257 8.1391 5.35465C8.50088 5.64305 8.906 5.76623 10.0275 5.92885C10.6875 6.02455 11.0589 6.10185 11.4015 6.21477C12.4904 6.57371 13.1641 7.30212 13.3248 8.29426C13.3715 8.58255 13.3441 9.12317 13.2684 9.4081C13.2087 9.63315 13.0263 10.0388 12.9779 10.0544C12.9645 10.0587 12.9514 10.0076 12.9479 9.93809C12.9296 9.56554 12.7402 9.20285 12.4221 8.93116C12.0604 8.62227 11.5745 8.37633 10.3863 7.90088Z"/>
|
||||
<path d="M9.13385 8.19748C9.11149 8.06527 9.07272 7.89643 9.04769 7.82228L9.00217 7.68748L9.08672 7.7818C9.20374 7.91233 9.2962 8.07937 9.37457 8.30185C9.43438 8.47165 9.44111 8.52215 9.44066 8.79807C9.4402 9.06896 9.43273 9.12575 9.3775 9.27858C9.29042 9.51959 9.18233 9.69048 9.00097 9.87391C8.67507 10.2036 8.25607 10.3861 7.65143 10.4618C7.54633 10.4749 7.24 10.4971 6.97069 10.511C6.292 10.5461 5.84531 10.6186 5.44393 10.7587C5.38623 10.7788 5.3347 10.7911 5.32947 10.7859C5.31323 10.7698 5.58651 10.6079 5.81223 10.4998C6.1305 10.3474 6.44733 10.2643 7.15719 10.1468C7.50785 10.0887 7.86998 10.0183 7.96194 9.99029C8.83033 9.72566 9.27671 9.04276 9.13385 8.19748Z"/>
|
||||
<path d="M9.95169 9.64109C9.71465 9.13463 9.66022 8.64564 9.79009 8.18961C9.80399 8.14088 9.82632 8.101 9.83976 8.101C9.85319 8.101 9.90913 8.13105 9.96404 8.16777C10.0733 8.24086 10.2924 8.36395 10.876 8.68023C11.6043 9.0749 12.0196 9.3805 12.302 9.72965C12.5493 10.0354 12.7023 10.3837 12.776 10.8084C12.8177 11.0489 12.7932 11.6277 12.7311 11.8699C12.5353 12.6337 12.0802 13.2336 11.4311 13.5837C11.336 13.635 11.2506 13.6771 11.2414 13.6773C11.2321 13.6775 11.2668 13.5899 11.3184 13.4827C11.5367 13.029 11.5616 12.5877 11.3965 12.0965C11.2954 11.7957 11.0893 11.4287 10.6732 10.8084C10.1893 10.0873 10.0707 9.89539 9.95169 9.64109Z"/>
|
||||
<path d="M3.25046 12.3737C3.91252 11.8181 4.73629 11.4234 5.48666 11.3022C5.81005 11.25 6.34877 11.2707 6.64823 11.3469C7.12824 11.469 7.55763 11.7425 7.78094 12.0683C7.99918 12.3867 8.09281 12.6642 8.19029 13.2816C8.22875 13.5252 8.27057 13.7697 8.28323 13.8251C8.35644 14.1451 8.4989 14.4008 8.67544 14.5293C8.95583 14.7333 9.43865 14.7459 9.91362 14.5618C9.99423 14.5305 10.0642 14.5089 10.0691 14.5138C10.0864 14.5308 9.84719 14.6899 9.67847 14.7737C9.45143 14.8864 9.2709 14.93 9.03102 14.93C8.59601 14.93 8.23486 14.7101 7.9335 14.2616C7.87419 14.1733 7.7409 13.909 7.63729 13.6741C7.3191 12.9528 7.16199 12.7331 6.79255 12.4926C6.47104 12.2834 6.05641 12.2459 5.74449 12.3979C5.33475 12.5976 5.22043 13.118 5.51389 13.4478C5.63053 13.5789 5.84803 13.6919 6.02588 13.7139C6.35861 13.7551 6.64455 13.5035 6.64455 13.1696C6.64455 12.9528 6.56071 12.8291 6.34966 12.7344C6.0614 12.6051 5.75156 12.7562 5.75304 13.0254C5.75368 13.1402 5.80396 13.2122 5.91971 13.2643C5.99397 13.2977 5.99569 13.3003 5.93514 13.2878C5.67066 13.2333 5.6087 12.9164 5.82135 12.706C6.07667 12.4535 6.60461 12.5649 6.78591 12.9097C6.86208 13.0545 6.87092 13.3429 6.80451 13.517C6.6559 13.9068 6.22256 14.1117 5.78297 14.0002C5.48368 13.9242 5.36181 13.842 5.00097 13.4726C4.37395 12.8306 4.13053 12.7062 3.22657 12.566L3.05335 12.5391L3.25046 12.3737Z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.308383 0.883984C2.40235 3.40996 3.84457 4.45213 4.00484 4.67231C4.13717 4.85412 4.08737 5.01757 3.86067 5.14567C3.7346 5.21689 3.47541 5.28905 3.34564 5.28905C3.19887 5.28905 3.14847 5.23278 3.14847 5.23278C3.06337 5.15255 3.01544 5.16658 2.5784 4.39555C1.97166 3.45981 1.46389 2.68357 1.45004 2.67057C1.41801 2.64052 1.41856 2.64153 2.51654 4.59413C2.69394 5.0011 2.55182 5.15049 2.55182 5.20845C2.55182 5.32636 2.51946 5.38834 2.37311 5.55059C2.12914 5.8211 2.02008 6.12505 1.94135 6.7541C1.8531 7.45926 1.60492 7.95737 0.917156 8.80989C0.514562 9.30893 0.448686 9.4004 0.3471 9.60153C0.219144 9.85482 0.183961 9.99669 0.169701 10.3165C0.154629 10.6547 0.183983 10.8732 0.287934 11.1965C0.378939 11.4796 0.473932 11.6665 0.716778 12.0403C0.926351 12.3629 1.04702 12.6027 1.04702 12.6965C1.04702 12.7711 1.06136 12.7712 1.38611 12.6983C2.16328 12.5239 2.79434 12.2171 3.14925 11.8411C3.36891 11.6084 3.42048 11.4799 3.42215 11.1611C3.42325 10.9525 3.41587 10.9088 3.35914 10.7888C3.2668 10.5935 3.09869 10.4311 2.72817 10.1794C2.2427 9.84953 2.03534 9.58398 1.97807 9.21878C1.93108 8.91913 1.98559 8.70771 2.25416 8.14825C2.53214 7.56916 2.60103 7.32239 2.64763 6.73869C2.67773 6.36158 2.71941 6.21286 2.82842 6.09348C2.94212 5.969 3.04447 5.92684 3.32584 5.88863C3.78457 5.82635 4.07667 5.70839 4.31677 5.48849C4.52505 5.29772 4.61221 5.11391 4.62558 4.8372L4.63574 4.62747L4.51934 4.49259C4.09783 4.00411 0.0261003 0.5 0.000160437 0.5C-0.00538105 0.5 0.133325 0.672804 0.308383 0.883984ZM1.28364 10.6992C1.37894 10.5314 1.3283 10.3158 1.16889 10.2104C1.01827 10.1109 0.78428 10.1578 0.78428 10.2875C0.78428 10.3271 0.806303 10.3559 0.855937 10.3813C0.939514 10.424 0.945581 10.4721 0.879823 10.5703C0.81323 10.6698 0.818604 10.7573 0.894991 10.8167C1.0181 10.9125 1.19237 10.8598 1.28364 10.6992Z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.92523 5.99865C4.70988 6.06439 4.50054 6.29124 4.43574 6.5291C4.39621 6.67421 4.41864 6.92875 4.47785 7.00736C4.57351 7.13433 4.66602 7.16778 4.91651 7.16603C5.40693 7.16263 5.83327 6.95358 5.88284 6.69224C5.92347 6.47801 5.73622 6.18112 5.4783 6.05078C5.34521 5.98355 5.06217 5.95688 4.92523 5.99865ZM5.49853 6.44422C5.57416 6.33741 5.54107 6.22198 5.41245 6.14391C5.1675 5.99525 4.79708 6.11827 4.79708 6.34826C4.79708 6.46274 4.99025 6.58765 5.16731 6.58765C5.28516 6.58765 5.44644 6.5178 5.49853 6.44422Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.8 KiB |
@@ -1,11 +0,0 @@
|
||||
<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.15217 1.55141C3.96412 1.52242 3.95619 1.51902 4.04468 1.5055C4.21427 1.47958 4.61472 1.51491 4.89067 1.58012C5.53489 1.73232 6.12109 2.12221 6.74683 2.81466L6.91307 2.99862L7.15088 2.96062C8.15274 2.8006 9.17194 2.92778 10.0244 3.31918C10.2589 3.42686 10.6287 3.64121 10.6749 3.69629C10.6896 3.71384 10.7166 3.82684 10.7349 3.94742C10.7982 4.36458 10.7665 4.68434 10.6382 4.92317C10.5683 5.05313 10.5644 5.09432 10.6114 5.20554C10.6489 5.2943 10.7534 5.35999 10.8569 5.35985C11.0687 5.35956 11.2968 5.0192 11.4024 4.54561L11.4444 4.3575L11.5275 4.45109C11.9835 4.96459 12.3417 5.66488 12.4032 6.16335L12.4192 6.29332L12.3426 6.17517C12.2107 5.97186 12.0781 5.83346 11.9084 5.72183C11.6024 5.52062 11.2789 5.45215 10.4222 5.40727C9.64839 5.36675 9.21045 5.30106 8.77621 5.16032C8.03738 4.9209 7.66493 4.60204 6.78729 3.4576C6.39748 2.94928 6.15654 2.66804 5.91687 2.44155C5.37228 1.92691 4.83716 1.65701 4.15217 1.55141Z" fill="white"/>
|
||||
<path d="M10.8494 2.68637C10.8689 2.34575 10.9153 2.12108 11.0088 1.9159C11.0458 1.83469 11.0804 1.76822 11.0858 1.76822C11.0911 1.76822 11.075 1.82816 11.05 1.90142C10.9821 2.10054 10.9709 2.3729 11.0177 2.68978C11.0771 3.09184 11.1109 3.14985 11.5385 3.58416C11.739 3.78788 11.9723 4.0448 12.0568 4.15511L12.2106 4.35568L12.0568 4.21234C11.8688 4.03705 11.4364 3.6952 11.3409 3.64633C11.2768 3.61356 11.2673 3.61413 11.2278 3.65321C11.1914 3.68922 11.1837 3.74333 11.1787 3.99915C11.1708 4.39786 11.1161 4.65377 10.9842 4.90965C10.9128 5.04805 10.9015 5.01851 10.9661 4.8623C11.0143 4.74566 11.0192 4.69439 11.0189 4.30842C11.0181 3.53291 10.9255 3.34647 10.3823 3.02709C10.2447 2.94618 10.0179 2.8295 9.87839 2.76778C9.73887 2.70606 9.62805 2.6523 9.63208 2.64828C9.64746 2.63307 10.1772 2.78675 10.3905 2.86828C10.7077 2.98954 10.76 3.00526 10.7985 2.99063C10.8244 2.98082 10.8369 2.90608 10.8494 2.68637Z" fill="white"/>
|
||||
<path d="M4.51745 4.01304C4.13569 3.49066 3.89948 2.68973 3.95062 2.091L3.96643 1.90572L4.05333 1.92148C4.21652 1.95106 4.49789 2.05515 4.62964 2.13469C4.9912 2.35293 5.14773 2.64027 5.30697 3.37811C5.35362 3.59423 5.41482 3.8388 5.44298 3.9216C5.48831 4.05487 5.65962 4.36617 5.7989 4.56834C5.89922 4.71395 5.83258 4.78295 5.61082 4.76305C5.27215 4.73267 4.8134 4.41799 4.51745 4.01304Z" fill="white"/>
|
||||
<path d="M10.3863 7.90088C8.60224 7.18693 7.97389 6.56721 7.97389 5.52157C7.97389 5.36769 7.97922 5.24179 7.98571 5.24179C7.99221 5.24179 8.06124 5.29257 8.1391 5.35465C8.50088 5.64305 8.906 5.76623 10.0275 5.92885C10.6875 6.02455 11.0589 6.10185 11.4015 6.21477C12.4904 6.57371 13.1641 7.30212 13.3248 8.29426C13.3715 8.58255 13.3441 9.12317 13.2684 9.4081C13.2087 9.63315 13.0263 10.0388 12.9779 10.0544C12.9645 10.0587 12.9514 10.0076 12.9479 9.93809C12.9296 9.56554 12.7402 9.20285 12.4221 8.93116C12.0604 8.62227 11.5745 8.37633 10.3863 7.90088Z" fill="white"/>
|
||||
<path d="M9.13385 8.19748C9.11149 8.06527 9.07272 7.89643 9.04769 7.82228L9.00217 7.68748L9.08672 7.7818C9.20374 7.91233 9.2962 8.07937 9.37457 8.30185C9.43438 8.47165 9.44111 8.52215 9.44066 8.79807C9.4402 9.06896 9.43273 9.12575 9.3775 9.27858C9.29042 9.51959 9.18233 9.69048 9.00097 9.87391C8.67507 10.2036 8.25607 10.3861 7.65143 10.4618C7.54633 10.4749 7.24 10.4971 6.97069 10.511C6.292 10.5461 5.84531 10.6186 5.44393 10.7587C5.38623 10.7788 5.3347 10.7911 5.32947 10.7859C5.31323 10.7698 5.58651 10.6079 5.81223 10.4998C6.1305 10.3474 6.44733 10.2643 7.15719 10.1468C7.50785 10.0887 7.86998 10.0183 7.96194 9.99029C8.83033 9.72566 9.27671 9.04276 9.13385 8.19748Z" fill="white"/>
|
||||
<path d="M9.95169 9.64109C9.71465 9.13463 9.66022 8.64564 9.79009 8.18961C9.80399 8.14088 9.82632 8.101 9.83976 8.101C9.85319 8.101 9.90913 8.13105 9.96404 8.16777C10.0733 8.24086 10.2924 8.36395 10.876 8.68023C11.6043 9.0749 12.0196 9.3805 12.302 9.72965C12.5493 10.0354 12.7023 10.3837 12.776 10.8084C12.8177 11.0489 12.7932 11.6277 12.7311 11.8699C12.5353 12.6337 12.0802 13.2336 11.4311 13.5837C11.336 13.635 11.2506 13.6771 11.2414 13.6773C11.2321 13.6775 11.2668 13.5899 11.3184 13.4827C11.5367 13.029 11.5616 12.5877 11.3965 12.0965C11.2954 11.7957 11.0893 11.4287 10.6732 10.8084C10.1893 10.0873 10.0707 9.89539 9.95169 9.64109Z" fill="white"/>
|
||||
<path d="M3.25046 12.3737C3.91252 11.8181 4.73629 11.4234 5.48666 11.3022C5.81005 11.25 6.34877 11.2707 6.64823 11.3469C7.12824 11.469 7.55763 11.7425 7.78094 12.0683C7.99918 12.3867 8.09281 12.6642 8.19029 13.2816C8.22875 13.5252 8.27057 13.7697 8.28323 13.8251C8.35644 14.1451 8.4989 14.4008 8.67544 14.5293C8.95583 14.7333 9.43865 14.7459 9.91362 14.5618C9.99423 14.5305 10.0642 14.5089 10.0691 14.5138C10.0864 14.5308 9.84719 14.6899 9.67847 14.7737C9.45143 14.8864 9.2709 14.93 9.03102 14.93C8.59601 14.93 8.23486 14.7101 7.9335 14.2616C7.87419 14.1733 7.7409 13.909 7.63729 13.6741C7.3191 12.9528 7.16199 12.7331 6.79255 12.4926C6.47104 12.2834 6.05641 12.2459 5.74449 12.3979C5.33475 12.5976 5.22043 13.118 5.51389 13.4478C5.63053 13.5789 5.84803 13.6919 6.02588 13.7139C6.35861 13.7551 6.64455 13.5035 6.64455 13.1696C6.64455 12.9528 6.56071 12.8291 6.34966 12.7344C6.0614 12.6051 5.75156 12.7562 5.75304 13.0254C5.75368 13.1402 5.80396 13.2122 5.91971 13.2643C5.99397 13.2977 5.99569 13.3003 5.93514 13.2878C5.67066 13.2333 5.6087 12.9164 5.82135 12.706C6.07667 12.4535 6.60461 12.5649 6.78591 12.9097C6.86208 13.0545 6.87092 13.3429 6.80451 13.517C6.6559 13.9068 6.22256 14.1117 5.78297 14.0002C5.48368 13.9242 5.36181 13.842 5.00097 13.4726C4.37395 12.8306 4.13053 12.7062 3.22657 12.566L3.05335 12.5391L3.25046 12.3737Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.308383 0.883984C2.40235 3.40996 3.84457 4.45213 4.00484 4.67231C4.13717 4.85412 4.08737 5.01757 3.86067 5.14567C3.7346 5.21689 3.47541 5.28905 3.34564 5.28905C3.19887 5.28905 3.14847 5.23278 3.14847 5.23278C3.06337 5.15255 3.01544 5.16658 2.5784 4.39555C1.97166 3.45981 1.46389 2.68357 1.45004 2.67057C1.41801 2.64052 1.41856 2.64153 2.51654 4.59413C2.69394 5.0011 2.55182 5.15049 2.55182 5.20845C2.55182 5.32636 2.51946 5.38834 2.37311 5.55059C2.12914 5.8211 2.02008 6.12505 1.94135 6.7541C1.8531 7.45926 1.60492 7.95737 0.917156 8.80989C0.514562 9.30893 0.448686 9.4004 0.3471 9.60153C0.219144 9.85482 0.183961 9.99669 0.169701 10.3165C0.154629 10.6547 0.183983 10.8732 0.287934 11.1965C0.378939 11.4796 0.473932 11.6665 0.716778 12.0403C0.926351 12.3629 1.04702 12.6027 1.04702 12.6965C1.04702 12.7711 1.06136 12.7712 1.38611 12.6983C2.16328 12.5239 2.79434 12.2171 3.14925 11.8411C3.36891 11.6084 3.42048 11.4799 3.42215 11.1611C3.42325 10.9525 3.41587 10.9088 3.35914 10.7888C3.2668 10.5935 3.09869 10.4311 2.72817 10.1794C2.2427 9.84953 2.03534 9.58398 1.97807 9.21878C1.93108 8.91913 1.98559 8.70771 2.25416 8.14825C2.53214 7.56916 2.60103 7.32239 2.64763 6.73869C2.67773 6.36158 2.71941 6.21286 2.82842 6.09348C2.94212 5.969 3.04447 5.92684 3.32584 5.88863C3.78457 5.82635 4.07667 5.70839 4.31677 5.48849C4.52505 5.29772 4.61221 5.11391 4.62558 4.8372L4.63574 4.62747L4.51934 4.49259C4.09783 4.00411 0.0261003 0.5 0.000160437 0.5C-0.00538105 0.5 0.133325 0.672804 0.308383 0.883984ZM1.28364 10.6992C1.37894 10.5314 1.3283 10.3158 1.16889 10.2104C1.01827 10.1109 0.78428 10.1578 0.78428 10.2875C0.78428 10.3271 0.806303 10.3559 0.855937 10.3813C0.939514 10.424 0.945581 10.4721 0.879823 10.5703C0.81323 10.6698 0.818604 10.7573 0.894991 10.8167C1.0181 10.9125 1.19237 10.8598 1.28364 10.6992Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.92523 5.99865C4.70988 6.06439 4.50054 6.29124 4.43574 6.5291C4.39621 6.67421 4.41864 6.92875 4.47785 7.00736C4.57351 7.13433 4.66602 7.16778 4.91651 7.16603C5.40693 7.16263 5.83327 6.95358 5.88284 6.69224C5.92347 6.47801 5.73622 6.18112 5.4783 6.05078C5.34521 5.98355 5.06217 5.95688 4.92523 5.99865ZM5.49853 6.44422C5.57416 6.33741 5.54107 6.22198 5.41245 6.14391C5.1675 5.99525 4.79708 6.11827 4.79708 6.34826C4.79708 6.46274 4.99025 6.58765 5.16731 6.58765C5.28516 6.58765 5.44644 6.5178 5.49853 6.44422Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 7.9 KiB |
4
src/assets/svg/matic-token-icon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="512" cy="512" r="512" fill="#8247E5"/>
|
||||
<path d="M681.469 402.456C669.189 395.312 653.224 395.312 639.716 402.456L543.928 457.228L478.842 492.949L383.055 547.721C370.774 554.865 354.81 554.865 341.301 547.721L265.162 504.856C252.882 497.712 244.286 484.614 244.286 470.325V385.786C244.286 371.498 251.654 358.4 265.162 351.256L340.073 309.581C352.353 302.437 368.318 302.437 381.827 309.581L456.737 351.256C469.018 358.4 477.614 371.498 477.614 385.786V440.558L542.7 403.646V348.874C542.7 334.586 535.332 321.488 521.824 314.344L383.055 235.758C370.774 228.614 354.81 228.614 341.301 235.758L200.076 314.344C186.567 321.488 179.199 334.586 179.199 348.874V507.237C179.199 521.525 186.567 534.623 200.076 541.767L341.301 620.353C353.582 627.498 369.546 627.498 383.055 620.353L478.842 566.772L543.928 529.86L639.716 476.279C651.996 469.135 667.961 469.135 681.469 476.279L756.38 517.953C768.66 525.098 777.257 538.195 777.257 552.484V637.023C777.257 651.312 769.888 664.409 756.38 671.553L681.469 714.419C669.189 721.563 653.224 721.563 639.716 714.419L564.805 672.744C552.525 665.6 543.928 652.502 543.928 638.214V583.442L478.842 620.353V675.125C478.842 689.414 486.21 702.512 499.719 709.656L640.944 788.242C653.224 795.386 669.189 795.386 682.697 788.242L823.922 709.656C836.203 702.512 844.799 689.414 844.799 675.125V516.763C844.799 502.474 837.431 489.377 823.922 482.232L681.469 402.456Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
16
src/assets/svg/polygon-matic-logo.svg
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 38.4 33.5" style="enable-background:new 0 0 38.4 33.5;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#8247E5;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M29,10.2c-0.7-0.4-1.6-0.4-2.4,0L21,13.5l-3.8,2.1l-5.5,3.3c-0.7,0.4-1.6,0.4-2.4,0L5,16.3
|
||||
c-0.7-0.4-1.2-1.2-1.2-2.1v-5c0-0.8,0.4-1.6,1.2-2.1l4.3-2.5c0.7-0.4,1.6-0.4,2.4,0L16,7.2c0.7,0.4,1.2,1.2,1.2,2.1v3.3l3.8-2.2V7
|
||||
c0-0.8-0.4-1.6-1.2-2.1l-8-4.7c-0.7-0.4-1.6-0.4-2.4,0L1.2,5C0.4,5.4,0,6.2,0,7v9.4c0,0.8,0.4,1.6,1.2,2.1l8.1,4.7
|
||||
c0.7,0.4,1.6,0.4,2.4,0l5.5-3.2l3.8-2.2l5.5-3.2c0.7-0.4,1.6-0.4,2.4,0l4.3,2.5c0.7,0.4,1.2,1.2,1.2,2.1v5c0,0.8-0.4,1.6-1.2,2.1
|
||||
L29,28.8c-0.7,0.4-1.6,0.4-2.4,0l-4.3-2.5c-0.7-0.4-1.2-1.2-1.2-2.1V21l-3.8,2.2v3.3c0,0.8,0.4,1.6,1.2,2.1l8.1,4.7
|
||||
c0.7,0.4,1.6,0.4,2.4,0l8.1-4.7c0.7-0.4,1.2-1.2,1.2-2.1V17c0-0.8-0.4-1.6-1.2-2.1L29,10.2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 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'
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Trans } from '@lingui/macro'
|
||||
import { Fraction, TradeType } from '@uniswap/sdk-core'
|
||||
import JSBI from 'jsbi'
|
||||
|
||||
import { nativeOnChain } from '../../constants/tokens'
|
||||
import { useCurrency, useToken } from '../../hooks/Tokens'
|
||||
import useENSName from '../../hooks/useENSName'
|
||||
import { VoteOption } from '../../state/governance/types'
|
||||
@@ -80,7 +81,7 @@ function ClaimSummary({ info: { recipient, uniAmountRaw } }: { info: ClaimTransa
|
||||
)
|
||||
}
|
||||
|
||||
function SubmitProposalTransactionSummary({}: { info: SubmitProposalTransactionInfo }) {
|
||||
function SubmitProposalTransactionSummary(_: { info: SubmitProposalTransactionInfo }) {
|
||||
return <Trans>Submit new proposal</Trans>
|
||||
}
|
||||
|
||||
@@ -130,30 +131,45 @@ function DelegateSummary({ info: { delegatee } }: { info: DelegateTransactionInf
|
||||
return <Trans>Delegate voting power to {ENSName ?? delegatee}</Trans>
|
||||
}
|
||||
|
||||
function WrapSummary({ info: { currencyAmountRaw, unwrapped } }: { info: WrapTransactionInfo }) {
|
||||
function WrapSummary({ info: { chainId, currencyAmountRaw, unwrapped } }: { info: WrapTransactionInfo }) {
|
||||
const native = chainId ? nativeOnChain(chainId) : undefined
|
||||
|
||||
if (unwrapped) {
|
||||
return (
|
||||
<Trans>
|
||||
Unwrap <FormattedCurrencyAmount rawAmount={currencyAmountRaw} symbol={'WETH'} decimals={18} sigFigs={6} /> to
|
||||
ETH
|
||||
Unwrap{' '}
|
||||
<FormattedCurrencyAmount
|
||||
rawAmount={currencyAmountRaw}
|
||||
symbol={native?.wrapped?.symbol ?? 'WETH'}
|
||||
decimals={18}
|
||||
sigFigs={6}
|
||||
/>{' '}
|
||||
to {native?.symbol ?? 'ETH'}
|
||||
</Trans>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Trans>
|
||||
Wrap <FormattedCurrencyAmount rawAmount={currencyAmountRaw} symbol={'ETH'} decimals={18} sigFigs={6} /> to WETH
|
||||
Wrap{' '}
|
||||
<FormattedCurrencyAmount
|
||||
rawAmount={currencyAmountRaw}
|
||||
symbol={native?.symbol ?? 'ETH'}
|
||||
decimals={18}
|
||||
sigFigs={6}
|
||||
/>{' '}
|
||||
to {native?.wrapped?.symbol ?? 'WETH'}
|
||||
</Trans>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function DepositLiquidityStakingSummary({}: { info: DepositLiquidityStakingTransactionInfo }) {
|
||||
function DepositLiquidityStakingSummary(_: { info: DepositLiquidityStakingTransactionInfo }) {
|
||||
// not worth rendering the tokens since you can should no longer deposit liquidity in the staking contracts
|
||||
// todo: deprecate and delete the code paths that allow this, show user more information
|
||||
return <Trans>Deposit liquidity</Trans>
|
||||
}
|
||||
|
||||
function WithdrawLiquidityStakingSummary({}: { info: WithdrawLiquidityStakingTransactionInfo }) {
|
||||
function WithdrawLiquidityStakingSummary(_: { info: WithdrawLiquidityStakingTransactionInfo }) {
|
||||
return <Trans>Withdraw deposited liquidity</Trans>
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
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'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
import { Connector } from 'widgets-web3-react/types'
|
||||
|
||||
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
|
||||
import FortmaticIcon from '../../assets/images/fortmaticIcon.png'
|
||||
import PortisIcon from '../../assets/images/portisIcon.png'
|
||||
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
|
||||
import { ReactComponent as Close } from '../../assets/images/x.svg'
|
||||
import { fortmatic, injected, portis, walletconnect, walletlink } from '../../connectors'
|
||||
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, TYPE } from '../../theme'
|
||||
import { ExternalLink, LinkStyledButton, ThemedText } from '../../theme'
|
||||
import { shortenAddress } from '../../utils'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { ButtonSecondary } from '../Button'
|
||||
import Identicon from '../Identicon'
|
||||
import StatusIcon from '../Identicon/StatusIcon'
|
||||
import { AutoRow } from '../Row'
|
||||
import Copy from './Copy'
|
||||
import Transaction from './Transaction'
|
||||
@@ -179,6 +177,23 @@ const IconWrapper = styled.div<{ size?: number }>`
|
||||
`};
|
||||
`
|
||||
|
||||
function WrappedStatusIcon({ connector }: { connector: AbstractConnector | Connector }) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<StatusIcon connector={connector} />
|
||||
{connector === portis && (
|
||||
<MainWalletAction
|
||||
onClick={() => {
|
||||
portis.portis.showPortis()
|
||||
}}
|
||||
>
|
||||
<Trans>Show Portis</Trans>
|
||||
</MainWalletAction>
|
||||
)}
|
||||
</IconWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
const TransactionListWrapper = styled.div`
|
||||
${({ theme }) => theme.flexColumnNoWrap};
|
||||
`
|
||||
@@ -244,50 +259,6 @@ export default function AccountDetails({
|
||||
)
|
||||
}
|
||||
|
||||
function getStatusIcon() {
|
||||
if (connector === injected) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<Identicon />
|
||||
</IconWrapper>
|
||||
)
|
||||
} else if (connector === walletconnect) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<img src={WalletConnectIcon} alt={'WalletConnect logo'} />
|
||||
</IconWrapper>
|
||||
)
|
||||
} else if (connector === walletlink) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<img src={CoinbaseWalletIcon} alt={'Coinbase Wallet logo'} />
|
||||
</IconWrapper>
|
||||
)
|
||||
} else if (connector === fortmatic) {
|
||||
return (
|
||||
<IconWrapper size={16}>
|
||||
<img src={FortmaticIcon} alt={'Fortmatic logo'} />
|
||||
</IconWrapper>
|
||||
)
|
||||
} else if (connector === portis) {
|
||||
return (
|
||||
<>
|
||||
<IconWrapper size={16}>
|
||||
<img src={PortisIcon} alt={'Portis logo'} />
|
||||
<MainWalletAction
|
||||
onClick={() => {
|
||||
portis.portis.showPortis()
|
||||
}}
|
||||
>
|
||||
<Trans>Show Portis</Trans>
|
||||
</MainWalletAction>
|
||||
</IconWrapper>
|
||||
</>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const clearAllTransactionsCallback = useCallback(() => {
|
||||
if (chainId) dispatch(clearAllTransactions({ chainId }))
|
||||
}, [dispatch, chainId])
|
||||
@@ -332,14 +303,14 @@ export default function AccountDetails({
|
||||
{ENSName ? (
|
||||
<>
|
||||
<div>
|
||||
{getStatusIcon()}
|
||||
{connector && <WrappedStatusIcon connector={connector} />}
|
||||
<p> {ENSName}</p>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div>
|
||||
{getStatusIcon()}
|
||||
{connector && <WrappedStatusIcon connector={connector} />}
|
||||
<p> {account && shortenAddress(account)}</p>
|
||||
</div>
|
||||
</>
|
||||
@@ -408,9 +379,9 @@ export default function AccountDetails({
|
||||
{!!pendingTransactions.length || !!confirmedTransactions.length ? (
|
||||
<LowerSection>
|
||||
<AutoRow mb={'1rem'} style={{ justifyContent: 'space-between' }}>
|
||||
<TYPE.body>
|
||||
<ThemedText.Body>
|
||||
<Trans>Recent Transactions</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
<LinkStyledButton onClick={clearAllTransactionsCallback}>
|
||||
<Trans>(clear all)</Trans>
|
||||
</LinkStyledButton>
|
||||
@@ -420,9 +391,9 @@ export default function AccountDetails({
|
||||
</LowerSection>
|
||||
) : (
|
||||
<LowerSection>
|
||||
<TYPE.body color={theme.text1}>
|
||||
<ThemedText.Body color={theme.text1}>
|
||||
<Trans>Your transactions will appear here...</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</LowerSection>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
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, TYPE } from '../../theme'
|
||||
import { ExternalLink, ThemedText } from '../../theme'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { RowBetween } from '../Row'
|
||||
@@ -106,9 +108,9 @@ export default function AddressInputPanel({
|
||||
<InputContainer>
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<TYPE.black color={theme.text2} fontWeight={500} fontSize={14}>
|
||||
<ThemedText.Black color={theme.text2} fontWeight={500} fontSize={14}>
|
||||
{label ?? <Trans>Recipient</Trans>}
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
{address && chainId && (
|
||||
<ExternalLink
|
||||
href={getExplorerLink(chainId, name ?? address, ExplorerDataType.ADDRESS)}
|
||||
|
||||
34
src/components/AnimatedDropdown/index.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { animated, useSpring } from 'react-spring'
|
||||
import useResizeObserver from 'use-resize-observer'
|
||||
|
||||
/**
|
||||
* @param open conditional to show content or hide
|
||||
* @returns Wrapper to smoothly hide and expand content
|
||||
*/
|
||||
export default function AnimatedDropdown({ open, children }: React.PropsWithChildren<{ open: boolean }>) {
|
||||
const { ref, height } = useResizeObserver()
|
||||
|
||||
const props = useSpring({
|
||||
height: open ? height ?? 0 : 0,
|
||||
config: {
|
||||
mass: 1.2,
|
||||
tension: 300,
|
||||
friction: 20,
|
||||
clamp: true,
|
||||
velocity: 0.01,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<animated.div
|
||||
style={{
|
||||
...props,
|
||||
overflow: 'hidden',
|
||||
width: '100%',
|
||||
willChange: 'height',
|
||||
}}
|
||||
>
|
||||
<div ref={ref}>{children}</div>
|
||||
</animated.div>
|
||||
)
|
||||
}
|
||||
@@ -1,17 +1,36 @@
|
||||
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',
|
||||
'0x72a5843cc08275C8171E582972Aa4fDa8C397B2A',
|
||||
'0x7F19720A857F834887FC9A7bC0a0fBe7Fc7f8102',
|
||||
'0xA7e5d5A720f06526557c513402f2e6B5fA20b008',
|
||||
'0x1da5821544e25c636c1417Ba96Ade4Cf6D2f9B5A',
|
||||
'0x9F4cda013E354b8fC285BF4b9A60460cEe7f7Ea9',
|
||||
'0x19Aa5Fe80D33a56D56c78e82eA5E50E5d80b4Dff',
|
||||
'0x2f389cE8bD8ff92De3402FFCe4691d17fC4f6535',
|
||||
'0xe7aa314c77F4233C18C6CC84384A9247c0cf367B',
|
||||
'0x7F367cC41522cE07553e823bf3be79A889DEbe1B',
|
||||
'0xd882cFc20F52f2599D84b8e8D58C7FB62cfE344b',
|
||||
'0x901bb9583b24D97e995513C6778dc6888AB6870e',
|
||||
'0xA7e5d5A720f06526557c513402f2e6B5fA20b008',
|
||||
'0x8576aCC5C05D6Ce88f4e49bf65BdF0C62F91353C',
|
||||
'0xC8a65Fadf0e0dDAf421F28FEAb69Bf6E2E589963',
|
||||
'0x9f4cda013e354b8fc285bf4b9a60460cee7f7ea9',
|
||||
'0x308eD4B7b49797e1A98D3818bFF6fe5385410370',
|
||||
'0x67d40EE1A85bf4a4Bb7Ffae16De985e8427B',
|
||||
'0x6f1ca141a28907f78ebaa64fb83a9088b02a83',
|
||||
'0x6acdfba02d390b97ac2b2d42a63e85293bcc1',
|
||||
'0x48549a34ae37b12f6a30566245176994e17c6',
|
||||
'0x5512d943ed1f7c8a43f3435c85f7ab68b30121',
|
||||
'0xc455f7fd3e0e12afd51fba5c106909934d8a0e',
|
||||
'0x3cbded43efdaf0fc77b9c55f6fc9988fcc9b757d',
|
||||
'0x67d40EE1A85bf4a4Bb7Ffae16De985e8427B6b45',
|
||||
'0x6f1ca141a28907f78ebaa64fb83a9088b02a8352',
|
||||
'0x6acdfba02d390b97ac2b2d42a63e85293bcc160e',
|
||||
'0x48549a34ae37b12f6a30566245176994e17c6b4a',
|
||||
'0x5512d943ed1f7c8a43f3435c85f7ab68b30121b0',
|
||||
]
|
||||
|
||||
export default function Blocklist({ children }: { children: ReactNode }) {
|
||||
|
||||
@@ -23,7 +23,7 @@ export const BaseButton = styled(RebassButton)<
|
||||
border-radius: ${({ $borderRadius }) => $borderRadius ?? '20px'};
|
||||
outline: none;
|
||||
border: 1px solid transparent;
|
||||
color: white;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -33,6 +33,7 @@ export const BaseButton = styled(RebassButton)<
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
&:disabled {
|
||||
opacity: 50%;
|
||||
cursor: auto;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -236,7 +237,7 @@ const ButtonConfirmedStyle = styled(BaseButton)`
|
||||
/* border: 1px solid ${({ theme }) => theme.green1}; */
|
||||
|
||||
&:disabled {
|
||||
/* opacity: 50%; */
|
||||
opacity: 50%;
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
color: ${({ theme }) => theme.text2};
|
||||
cursor: auto;
|
||||
@@ -315,8 +316,8 @@ const ActiveOutlined = styled(ButtonOutlined)`
|
||||
`
|
||||
|
||||
const Circle = styled.div`
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
height: 17px;
|
||||
width: 17px;
|
||||
border-radius: 50%;
|
||||
background-color: ${({ theme }) => theme.primary1};
|
||||
display: flex;
|
||||
@@ -325,11 +326,11 @@ const Circle = styled.div`
|
||||
`
|
||||
|
||||
const CheckboxWrapper = styled.div`
|
||||
width: 30px;
|
||||
width: 20px;
|
||||
padding: 0 10px;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
top: 11px;
|
||||
right: 15px;
|
||||
`
|
||||
|
||||
const ResponsiveCheck = styled(Check)`
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t } from '@lingui/macro'
|
||||
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
|
||||
import HoverInlineText from 'components/HoverInlineText'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import useTheme from '../../hooks/useTheme'
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { warningSeverity } from '../../utils/prices'
|
||||
import { MouseoverTooltip } from '../Tooltip'
|
||||
|
||||
export function FiatValue({
|
||||
fiatValue,
|
||||
@@ -25,10 +28,14 @@ export function FiatValue({
|
||||
}, [priceImpact, theme.green1, theme.red1, theme.text3, theme.yellow1])
|
||||
|
||||
return (
|
||||
<TYPE.body fontSize={14} color={fiatValue ? theme.text2 : theme.text4}>
|
||||
<ThemedText.Body fontSize={14} color={fiatValue ? theme.text3 : theme.text4}>
|
||||
{fiatValue ? (
|
||||
<Trans>
|
||||
~$ <HoverInlineText text={fiatValue?.toSignificant(6, { groupSeparator: ',' })} />
|
||||
$
|
||||
<HoverInlineText
|
||||
text={fiatValue?.toSignificant(6, { groupSeparator: ',' })}
|
||||
textColor={fiatValue ? theme.text3 : theme.text4}
|
||||
/>
|
||||
</Trans>
|
||||
) : (
|
||||
''
|
||||
@@ -36,9 +43,11 @@ export function FiatValue({
|
||||
{priceImpact ? (
|
||||
<span style={{ color: priceImpactColor }}>
|
||||
{' '}
|
||||
(<Trans>{priceImpact.multiply(-1).toSignificant(3)}%</Trans>)
|
||||
<MouseoverTooltip text={t`The estimated difference between the USD values of input and output amounts.`}>
|
||||
(<Trans>{priceImpact.multiply(-1).toSignificant(3)}%</Trans>)
|
||||
</MouseoverTooltip>
|
||||
</span>
|
||||
) : null}
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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,9 +12,8 @@ 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 { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { ButtonGray } from '../Button'
|
||||
import CurrencyLogo from '../CurrencyLogo'
|
||||
import DoubleCurrencyLogo from '../DoubleLogo'
|
||||
@@ -29,6 +29,8 @@ const InputPanel = styled.div<{ hideInput?: boolean }>`
|
||||
background-color: ${({ theme, hideInput }) => (hideInput ? 'transparent' : theme.bg2)};
|
||||
z-index: 1;
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
transition: height 1s ease;
|
||||
will-change: height;
|
||||
`
|
||||
|
||||
const FixedContainer = styled.div`
|
||||
@@ -36,8 +38,7 @@ const FixedContainer = styled.div`
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
border-radius: 20px;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
opacity: 0.95;
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -46,7 +47,7 @@ const FixedContainer = styled.div`
|
||||
|
||||
const Container = styled.div<{ hideInput: boolean }>`
|
||||
border-radius: ${({ hideInput }) => (hideInput ? '16px' : '20px')};
|
||||
border: 1px solid ${({ theme, hideInput }) => (hideInput ? ' transparent' : theme.bg2)};
|
||||
border: 1px solid ${({ theme }) => theme.bg0};
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
:focus,
|
||||
@@ -56,35 +57,35 @@ const Container = styled.div<{ hideInput: boolean }>`
|
||||
`
|
||||
|
||||
const CurrencySelect = styled(ButtonGray)<{ visible: boolean; selected: boolean; hideInput?: boolean }>`
|
||||
visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')};
|
||||
align-items: center;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.bg0 : theme.primary1)};
|
||||
color: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)};
|
||||
border-radius: 16px;
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.bg2 : theme.primary1)};
|
||||
box-shadow: ${({ selected }) => (selected ? 'none' : '0px 6px 10px rgba(0, 0, 0, 0.075)')};
|
||||
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
|
||||
outline: none;
|
||||
color: ${({ selected, theme }) => (selected ? theme.text1 : theme.white)};
|
||||
cursor: pointer;
|
||||
border-radius: 16px;
|
||||
outline: none;
|
||||
user-select: none;
|
||||
border: none;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
height: ${({ hideInput }) => (hideInput ? '2.8rem' : '2.4rem')};
|
||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||
padding: 0 8px;
|
||||
justify-content: space-between;
|
||||
margin-right: ${({ hideInput }) => (hideInput ? '0' : '12px')};
|
||||
margin-left: ${({ hideInput }) => (hideInput ? '0' : '12px')};
|
||||
:focus,
|
||||
:hover {
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.bg2 : darken(0.05, theme.primary1))};
|
||||
background-color: ${({ selected, theme }) => (selected ? theme.bg3 : darken(0.05, theme.primary1))};
|
||||
}
|
||||
visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')};
|
||||
`
|
||||
|
||||
const InputRow = styled.div<{ selected: boolean }>`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: ${({ selected }) => (selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 0.75rem 1rem')};
|
||||
padding: ${({ selected }) => (selected ? ' 1rem 1rem 0.75rem 1rem' : '1rem 1rem 1rem 1rem')};
|
||||
`
|
||||
|
||||
const LabelRow = styled.div`
|
||||
@@ -128,28 +129,30 @@ const StyledTokenName = styled.span<{ active?: boolean }>`
|
||||
|
||||
const StyledBalanceMax = styled.button<{ disabled?: boolean }>`
|
||||
background-color: transparent;
|
||||
background-color: ${({ theme }) => theme.primary5};
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: ${({ theme }) => theme.primary1};
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
color: ${({ theme }) => theme.primaryText1};
|
||||
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
|
||||
pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')};
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
margin-left: 0.25rem;
|
||||
opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)};
|
||||
padding: 4px 6px;
|
||||
pointer-events: ${({ disabled }) => (!disabled ? 'initial' : 'none')};
|
||||
|
||||
:hover {
|
||||
opacity: ${({ disabled }) => (!disabled ? 0.8 : 0.4)};
|
||||
}
|
||||
|
||||
:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToExtraSmall`
|
||||
margin-right: 0.5rem;
|
||||
`};
|
||||
`
|
||||
|
||||
const StyledNumericalInput = styled(NumericalInput)<{ $loading: boolean }>`
|
||||
${loadingOpacityMixin}
|
||||
${loadingOpacityMixin};
|
||||
text-align: left;
|
||||
`
|
||||
|
||||
interface CurrencyInputPanelProps {
|
||||
@@ -212,14 +215,23 @@ export default function CurrencyInputPanel({
|
||||
<FixedContainer>
|
||||
<AutoColumn gap="sm" justify="center">
|
||||
<Lock />
|
||||
<TYPE.label fontSize="12px" textAlign="center" padding="0 12px">
|
||||
<ThemedText.Label fontSize="12px" textAlign="center" padding="0 12px">
|
||||
<Trans>The market price is outside your specified price range. Single-asset deposit only.</Trans>
|
||||
</TYPE.label>
|
||||
</ThemedText.Label>
|
||||
</AutoColumn>
|
||||
</FixedContainer>
|
||||
)}
|
||||
<Container hideInput={hideInput}>
|
||||
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={!onCurrencySelect}>
|
||||
{!hideInput && (
|
||||
<StyledNumericalInput
|
||||
className="token-amount-input"
|
||||
value={value}
|
||||
onUserInput={onUserInput}
|
||||
$loading={loading}
|
||||
/>
|
||||
)}
|
||||
|
||||
<CurrencySelect
|
||||
visible={currency !== undefined}
|
||||
selected={!!currency}
|
||||
@@ -257,24 +269,19 @@ export default function CurrencyInputPanel({
|
||||
{onCurrencySelect && <StyledDropDown selected={!!currency} />}
|
||||
</Aligner>
|
||||
</CurrencySelect>
|
||||
{!hideInput && (
|
||||
<StyledNumericalInput
|
||||
className="token-amount-input"
|
||||
value={value}
|
||||
onUserInput={onUserInput}
|
||||
$loading={loading}
|
||||
/>
|
||||
)}
|
||||
</InputRow>
|
||||
{!hideInput && !hideBalance && (
|
||||
{!hideInput && !hideBalance && currency && (
|
||||
<FiatRow>
|
||||
<RowBetween>
|
||||
<LoadingOpacityContainer $loading={loading}>
|
||||
<FiatValue fiatValue={fiatValue} priceImpact={priceImpact} />
|
||||
</LoadingOpacityContainer>
|
||||
{account ? (
|
||||
<RowFixed style={{ height: '17px' }}>
|
||||
<TYPE.body
|
||||
<ThemedText.Body
|
||||
onClick={onMax}
|
||||
color={theme.text2}
|
||||
fontWeight={400}
|
||||
color={theme.text3}
|
||||
fontWeight={500}
|
||||
fontSize={14}
|
||||
style={{ display: 'inline', cursor: 'pointer' }}
|
||||
>
|
||||
@@ -282,24 +289,19 @@ export default function CurrencyInputPanel({
|
||||
renderBalance ? (
|
||||
renderBalance(selectedCurrencyBalance)
|
||||
) : (
|
||||
<Trans>
|
||||
Balance: {formatCurrencyAmount(selectedCurrencyBalance, 4)} {currency.symbol}
|
||||
</Trans>
|
||||
<Trans>Balance: {formatCurrencyAmount(selectedCurrencyBalance, 4)}</Trans>
|
||||
)
|
||||
) : null}
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
{showMaxButton && selectedCurrencyBalance ? (
|
||||
<StyledBalanceMax onClick={onMax}>
|
||||
<Trans>(Max)</Trans>
|
||||
<Trans>MAX</Trans>
|
||||
</StyledBalanceMax>
|
||||
) : null}
|
||||
</RowFixed>
|
||||
) : (
|
||||
<span />
|
||||
)}
|
||||
<LoadingOpacityContainer $loading={loading}>
|
||||
<FiatValue fiatValue={fiatValue} priceImpact={priceImpact} />
|
||||
</LoadingOpacityContainer>
|
||||
</RowBetween>
|
||||
</FiatRow>
|
||||
)}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
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 styled from 'styled-components/macro'
|
||||
|
||||
import EthereumLogo from '../../assets/images/ethereum-logo.png'
|
||||
import useHttpLocations from '../../hooks/useHttpLocations'
|
||||
import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
|
||||
import Logo from '../Logo'
|
||||
|
||||
type Network = 'ethereum' | 'arbitrum' | 'optimism'
|
||||
@@ -34,19 +35,27 @@ export const getTokenLogoURL = (
|
||||
}
|
||||
}
|
||||
|
||||
const StyledEthereumLogo = styled.img<{ size: string }>`
|
||||
const StyledNativeLogo = styled.img<{ size: string }>`
|
||||
width: ${({ size }) => size};
|
||||
height: ${({ size }) => size};
|
||||
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
|
||||
border-radius: 24px;
|
||||
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 }>`
|
||||
width: ${({ size }) => size};
|
||||
height: ${({ size }) => size};
|
||||
border-radius: ${({ size }) => size};
|
||||
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.075);
|
||||
background-color: ${({ theme }) => theme.white};
|
||||
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;
|
||||
border: 0px solid rgba(255, 255, 255, 0);
|
||||
`
|
||||
|
||||
export default function CurrencyLogo({
|
||||
@@ -79,7 +88,17 @@ export default function CurrencyLogo({
|
||||
}, [currency, uriLocations])
|
||||
|
||||
if (currency?.isNative) {
|
||||
return <StyledEthereumLogo src={EthereumLogo} alt="ethereum logo" size={size} style={style} {...rest} />
|
||||
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} />
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { L2_CHAIN_IDS, SupportedChainId } from 'constants/chains'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { AlertOctagon } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink } from 'theme'
|
||||
|
||||
import { isL2ChainId } from '../../utils/chains'
|
||||
|
||||
const Root = styled.div`
|
||||
background-color: ${({ theme }) => (theme.darkMode ? '#888D9B' : '#CED0D9')};
|
||||
border-radius: 18px;
|
||||
@@ -18,7 +20,6 @@ const Root = styled.div`
|
||||
max-width: 880px;
|
||||
`
|
||||
const WarningIcon = styled(AlertOctagon)`
|
||||
display: block;
|
||||
margin: auto 16px auto 0;
|
||||
min-height: 22px;
|
||||
min-width: 22px;
|
||||
@@ -28,50 +29,54 @@ const ReadMoreLink = styled(ExternalLink)`
|
||||
text-decoration: underline;
|
||||
`
|
||||
|
||||
export default function DowntimeWarning() {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
if (!chainId || !L2_CHAIN_IDS.includes(chainId)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const Content = () => {
|
||||
switch (chainId) {
|
||||
case SupportedChainId.OPTIMISM:
|
||||
case SupportedChainId.OPTIMISTIC_KOVAN:
|
||||
return (
|
||||
<div>
|
||||
<Trans>
|
||||
Optimistic Ethereum is in Beta and may experience downtime. Optimism expects planned downtime to upgrade
|
||||
the network in the near future. During downtime, your position will not earn fees and you will be unable
|
||||
to remove liquidity.{' '}
|
||||
<ReadMoreLink href="https://help.uniswap.org/en/articles/5406082-what-happens-if-the-optimistic-ethereum-network-experiences-downtime">
|
||||
Read more.
|
||||
</ReadMoreLink>
|
||||
</Trans>
|
||||
</div>
|
||||
)
|
||||
case SupportedChainId.ARBITRUM_ONE:
|
||||
case SupportedChainId.ARBITRUM_RINKEBY:
|
||||
return (
|
||||
<div>
|
||||
<Trans>
|
||||
Arbitrum is in Beta and may experience downtime. During downtime, your position will not earn fees and you
|
||||
will be unable to remove liquidity.{' '}
|
||||
<ReadMoreLink href="https://help.uniswap.org/en/articles/5576122-arbitrum-network-downtime">
|
||||
Read more.
|
||||
</ReadMoreLink>
|
||||
</Trans>
|
||||
</div>
|
||||
)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function Wrapper({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<Root>
|
||||
<WarningIcon />
|
||||
<Content />
|
||||
<div>{children}</div>
|
||||
</Root>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a downtime warning for the network if it's relevant
|
||||
*/
|
||||
export default function DowntimeWarning() {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
if (!isL2ChainId(chainId)) {
|
||||
return null
|
||||
}
|
||||
|
||||
switch (chainId) {
|
||||
case SupportedChainId.OPTIMISM:
|
||||
case SupportedChainId.OPTIMISTIC_KOVAN:
|
||||
return (
|
||||
<Wrapper>
|
||||
<Trans>
|
||||
Optimism is in Beta and may experience downtime. Optimism expects planned downtime to upgrade the network in
|
||||
the near future. During downtime, your position will not earn fees and you will be unable to remove
|
||||
liquidity.{' '}
|
||||
<ReadMoreLink href="https://help.uniswap.org/en/articles/5406082-what-happens-if-the-optimistic-ethereum-network-experiences-downtime">
|
||||
Read more.
|
||||
</ReadMoreLink>
|
||||
</Trans>
|
||||
</Wrapper>
|
||||
)
|
||||
case SupportedChainId.ARBITRUM_ONE:
|
||||
case SupportedChainId.ARBITRUM_RINKEBY:
|
||||
return (
|
||||
<Wrapper>
|
||||
<Trans>
|
||||
Arbitrum is in Beta and may experience downtime. During downtime, your position will not earn fees and you
|
||||
will be unable to remove liquidity.{' '}
|
||||
<ReadMoreLink href="https://help.uniswap.org/en/articles/5576122-arbitrum-network-downtime">
|
||||
Read more.
|
||||
</ReadMoreLink>
|
||||
</Trans>
|
||||
</Wrapper>
|
||||
)
|
||||
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import ReactGA from 'react-ga'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import store, { AppState } from '../../state'
|
||||
import { ExternalLink, TYPE } from '../../theme'
|
||||
import { ExternalLink, ThemedText } from '../../theme'
|
||||
import { userAgent } from '../../utils/userAgent'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { AutoRow } from '../Row'
|
||||
@@ -47,6 +47,8 @@ type ErrorBoundaryState = {
|
||||
error: Error | null
|
||||
}
|
||||
|
||||
const IS_UNISWAP = window.location.hostname === 'app.uniswap.org'
|
||||
|
||||
export default class ErrorBoundary extends React.Component<unknown, ErrorBoundaryState> {
|
||||
constructor(props: unknown) {
|
||||
super(props)
|
||||
@@ -67,6 +69,7 @@ export default class ErrorBoundary extends React.Component<unknown, ErrorBoundar
|
||||
|
||||
render() {
|
||||
const { error } = this.state
|
||||
|
||||
if (error !== null) {
|
||||
const encodedBody = encodeURIComponent(issueBody(error))
|
||||
return (
|
||||
@@ -74,39 +77,41 @@ export default class ErrorBoundary extends React.Component<unknown, ErrorBoundar
|
||||
<BodyWrapper>
|
||||
<AutoColumn gap={'md'}>
|
||||
<SomethingWentWrongWrapper>
|
||||
<TYPE.label fontSize={24} fontWeight={600}>
|
||||
<ThemedText.Label fontSize={24} fontWeight={600}>
|
||||
<Trans>Something went wrong</Trans>
|
||||
</TYPE.label>
|
||||
</ThemedText.Label>
|
||||
</SomethingWentWrongWrapper>
|
||||
<CodeBlockWrapper>
|
||||
<code>
|
||||
<TYPE.main fontSize={10}>{error.stack}</TYPE.main>
|
||||
<ThemedText.Main fontSize={10}>{error.stack}</ThemedText.Main>
|
||||
</code>
|
||||
</CodeBlockWrapper>
|
||||
<AutoRow>
|
||||
<LinkWrapper>
|
||||
<ExternalLink
|
||||
id="create-github-issue-link"
|
||||
href={`https://github.com/Uniswap/uniswap-interface/issues/new?assignees=&labels=bug&body=${encodedBody}&title=${encodeURIComponent(
|
||||
`Crash report: \`${error.name}${error.message && `: ${error.message}`}\``
|
||||
)}`}
|
||||
target="_blank"
|
||||
>
|
||||
<TYPE.link fontSize={16}>
|
||||
<Trans>Create an issue on GitHub</Trans>
|
||||
<span>↗</span>
|
||||
</TYPE.link>
|
||||
</ExternalLink>
|
||||
</LinkWrapper>
|
||||
<LinkWrapper>
|
||||
<ExternalLink id="get-support-on-discord" href="https://discord.gg/FCfyBSbCU5" target="_blank">
|
||||
<TYPE.link fontSize={16}>
|
||||
<Trans>Get support on Discord</Trans>
|
||||
<span>↗</span>
|
||||
</TYPE.link>
|
||||
</ExternalLink>
|
||||
</LinkWrapper>
|
||||
</AutoRow>
|
||||
{IS_UNISWAP ? (
|
||||
<AutoRow>
|
||||
<LinkWrapper>
|
||||
<ExternalLink
|
||||
id="create-github-issue-link"
|
||||
href={`https://github.com/Uniswap/uniswap-interface/issues/new?assignees=&labels=bug&body=${encodedBody}&title=${encodeURIComponent(
|
||||
`Crash report: \`${error.name}${error.message && `: ${error.message}`}\``
|
||||
)}`}
|
||||
target="_blank"
|
||||
>
|
||||
<ThemedText.Link fontSize={16}>
|
||||
<Trans>Create an issue on GitHub</Trans>
|
||||
<span>↗</span>
|
||||
</ThemedText.Link>
|
||||
</ExternalLink>
|
||||
</LinkWrapper>
|
||||
<LinkWrapper>
|
||||
<ExternalLink id="get-support-on-discord" href="https://discord.gg/FCfyBSbCU5" target="_blank">
|
||||
<ThemedText.Link fontSize={16}>
|
||||
<Trans>Get support on Discord</Trans>
|
||||
<span>↗</span>
|
||||
</ThemedText.Link>
|
||||
</ExternalLink>
|
||||
</LinkWrapper>
|
||||
</AutoRow>
|
||||
) : null}
|
||||
</AutoColumn>
|
||||
</BodyWrapper>
|
||||
</FallbackWrapper>
|
||||
@@ -121,7 +126,7 @@ function getRelevantState(): null | keyof AppState {
|
||||
if (!path.startsWith('#/')) {
|
||||
return null
|
||||
}
|
||||
const pieces = path.substring(2).split(/[\/\\?]/)
|
||||
const pieces = path.substring(2).split(/[/\\?]/)
|
||||
switch (pieces[0]) {
|
||||
case 'swap':
|
||||
return 'swap'
|
||||
|
||||
51
src/components/FeeSelector/FeeOption.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import { ButtonRadioChecked } from 'components/Button'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import { useFeeTierDistribution } from 'hooks/useFeeTierDistribution'
|
||||
import { PoolState } from 'hooks/usePools'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import { FeeTierPercentageBadge } from './FeeTierPercentageBadge'
|
||||
import { FEE_AMOUNT_DETAIL } from './shared'
|
||||
|
||||
const ResponsiveText = styled(ThemedText.Label)`
|
||||
line-height: 16px;
|
||||
font-size: 14px;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
`};
|
||||
`
|
||||
|
||||
interface FeeOptionProps {
|
||||
feeAmount: FeeAmount
|
||||
active: boolean
|
||||
distributions: ReturnType<typeof useFeeTierDistribution>['distributions']
|
||||
poolState: PoolState
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
export function FeeOption({ feeAmount, active, poolState, distributions, onClick }: FeeOptionProps) {
|
||||
return (
|
||||
<ButtonRadioChecked active={active} onClick={onClick}>
|
||||
<AutoColumn gap="sm" justify="flex-start">
|
||||
<AutoColumn justify="flex-start" gap="6px">
|
||||
<ResponsiveText>
|
||||
<Trans>{FEE_AMOUNT_DETAIL[feeAmount].label}%</Trans>
|
||||
</ResponsiveText>
|
||||
<ThemedText.Main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
{FEE_AMOUNT_DETAIL[feeAmount].description}
|
||||
</ThemedText.Main>
|
||||
</AutoColumn>
|
||||
|
||||
{distributions && (
|
||||
<FeeTierPercentageBadge distributions={distributions} feeAmount={feeAmount} poolState={poolState} />
|
||||
)}
|
||||
</AutoColumn>
|
||||
</ButtonRadioChecked>
|
||||
)
|
||||
}
|
||||
31
src/components/FeeSelector/FeeTierPercentageBadge.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import Badge from 'components/Badge'
|
||||
import { useFeeTierDistribution } from 'hooks/useFeeTierDistribution'
|
||||
import { PoolState } from 'hooks/usePools'
|
||||
import React from 'react'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
export function FeeTierPercentageBadge({
|
||||
feeAmount,
|
||||
distributions,
|
||||
poolState,
|
||||
}: {
|
||||
feeAmount: FeeAmount
|
||||
distributions: ReturnType<typeof useFeeTierDistribution>['distributions']
|
||||
poolState: PoolState
|
||||
}) {
|
||||
return (
|
||||
<Badge>
|
||||
<ThemedText.Label fontSize={10}>
|
||||
{!distributions || poolState === PoolState.NOT_EXISTS || poolState === PoolState.INVALID ? (
|
||||
<Trans>Not created</Trans>
|
||||
) : distributions[feeAmount] !== undefined ? (
|
||||
<Trans>{distributions[feeAmount]?.toFixed(0)}% select</Trans>
|
||||
) : (
|
||||
<Trans>No data</Trans>
|
||||
)}
|
||||
</ThemedText.Label>
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
@@ -1,19 +1,24 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import Badge from 'components/Badge'
|
||||
import { ButtonGray, ButtonRadioChecked } from 'components/Button'
|
||||
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 { DynamicSection } from 'pages/AddLiquidity/styled'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import ReactGA from 'react-ga'
|
||||
import { Box } from 'rebass'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import { FeeOption } from './FeeOption'
|
||||
import { FeeTierPercentageBadge } from './FeeTierPercentageBadge'
|
||||
import { FEE_AMOUNT_DETAIL } from './shared'
|
||||
|
||||
const pulse = (color: string) => keyframes`
|
||||
0% {
|
||||
@@ -28,45 +33,18 @@ const pulse = (color: string) => keyframes`
|
||||
box-shadow: 0 0 0 0 ${color};
|
||||
}
|
||||
`
|
||||
|
||||
const ResponsiveText = styled(TYPE.label)`
|
||||
line-height: 16px;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
`};
|
||||
`
|
||||
|
||||
const FocusedOutlineCard = styled(Card)<{ pulsing: boolean }>`
|
||||
border: 1px solid ${({ theme }) => theme.bg2};
|
||||
animation: ${({ pulsing, theme }) => pulsing && pulse(theme.primary1)} 0.6s linear;
|
||||
align-self: center;
|
||||
`
|
||||
|
||||
const FeeAmountLabel = {
|
||||
[FeeAmount.LOW]: {
|
||||
label: '0.05',
|
||||
description: <Trans>Best for stable pairs.</Trans>,
|
||||
},
|
||||
[FeeAmount.MEDIUM]: {
|
||||
label: '0.3',
|
||||
description: <Trans>Best for most pairs.</Trans>,
|
||||
},
|
||||
[FeeAmount.HIGH]: {
|
||||
label: '1',
|
||||
description: <Trans>Best for exotic pairs.</Trans>,
|
||||
},
|
||||
}
|
||||
|
||||
const FeeTierPercentageBadge = ({ percentage }: { percentage: number | undefined }) => {
|
||||
return (
|
||||
<Badge>
|
||||
<TYPE.label fontSize={12}>
|
||||
{percentage !== undefined ? <Trans>{percentage?.toFixed(0)}% select</Trans> : <Trans>Not created</Trans>}
|
||||
</TYPE.label>
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
const Select = styled.div`
|
||||
align-items: flex-start;
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-gap: 8px;
|
||||
`
|
||||
|
||||
export default function FeeSelector({
|
||||
disabled = false,
|
||||
@@ -81,8 +59,39 @@ export default function FeeSelector({
|
||||
currencyA?: Currency | undefined
|
||||
currencyB?: Currency | undefined
|
||||
}) {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
|
||||
const { isLoading, isError, largestUsageFeeTier, distributions } = useFeeTierDistribution(currencyA, currencyB)
|
||||
|
||||
// get pool data on-chain for latest states
|
||||
const pools = usePools([
|
||||
[currencyA, currencyB, FeeAmount.LOWEST],
|
||||
[currencyA, currencyB, FeeAmount.LOW],
|
||||
[currencyA, currencyB, FeeAmount.MEDIUM],
|
||||
[currencyA, currencyB, FeeAmount.HIGH],
|
||||
])
|
||||
|
||||
const poolsByFeeTier: Record<FeeAmount, PoolState> = useMemo(
|
||||
() =>
|
||||
pools.reduce(
|
||||
(acc, [curPoolState, curPool]) => {
|
||||
acc = {
|
||||
...acc,
|
||||
...{ [curPool?.fee as FeeAmount]: curPoolState },
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{
|
||||
// default all states to NOT_EXISTS
|
||||
[FeeAmount.LOWEST]: PoolState.NOT_EXISTS,
|
||||
[FeeAmount.LOW]: PoolState.NOT_EXISTS,
|
||||
[FeeAmount.MEDIUM]: PoolState.NOT_EXISTS,
|
||||
[FeeAmount.HIGH]: PoolState.NOT_EXISTS,
|
||||
}
|
||||
),
|
||||
[pools]
|
||||
)
|
||||
|
||||
const [showOptions, setShowOptions] = useState(false)
|
||||
const [pulsing, setPulsing] = useState(false)
|
||||
|
||||
@@ -91,7 +100,7 @@ export default function FeeSelector({
|
||||
const recommended = useRef(false)
|
||||
|
||||
const handleFeePoolSelectWithEvent = useCallback(
|
||||
(fee) => {
|
||||
(fee: FeeAmount) => {
|
||||
ReactGA.event({
|
||||
category: 'FeePoolSelect',
|
||||
action: 'Manual',
|
||||
@@ -140,20 +149,26 @@ export default function FeeSelector({
|
||||
<AutoColumn id="add-liquidity-selected-fee">
|
||||
{!feeAmount ? (
|
||||
<>
|
||||
<TYPE.label>
|
||||
<ThemedText.Label>
|
||||
<Trans>Fee tier</Trans>
|
||||
</TYPE.label>
|
||||
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
</ThemedText.Label>
|
||||
<ThemedText.Main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
<Trans>The % you will earn in fees.</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<TYPE.label className="selected-fee-label">
|
||||
<Trans>{FeeAmountLabel[feeAmount].label}% fee tier</Trans>
|
||||
</TYPE.label>
|
||||
<ThemedText.Label className="selected-fee-label">
|
||||
<Trans>{FEE_AMOUNT_DETAIL[feeAmount].label}% fee tier</Trans>
|
||||
</ThemedText.Label>
|
||||
<Box style={{ width: 'fit-content', marginTop: '8px' }} className="selected-fee-percentage">
|
||||
{distributions && feeAmount && <FeeTierPercentageBadge percentage={distributions[feeAmount]} />}
|
||||
{distributions && (
|
||||
<FeeTierPercentageBadge
|
||||
distributions={distributions}
|
||||
feeAmount={feeAmount}
|
||||
poolState={poolsByFeeTier[feeAmount]}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
@@ -165,63 +180,25 @@ export default function FeeSelector({
|
||||
</RowBetween>
|
||||
</FocusedOutlineCard>
|
||||
|
||||
{showOptions && (
|
||||
<RowBetween>
|
||||
<ButtonRadioChecked
|
||||
width="32%"
|
||||
active={feeAmount === FeeAmount.LOW}
|
||||
onClick={() => handleFeePoolSelectWithEvent(FeeAmount.LOW)}
|
||||
>
|
||||
<AutoColumn gap="sm" justify="flex-start">
|
||||
<AutoColumn justify="flex-start" gap="6px">
|
||||
<ResponsiveText>
|
||||
<Trans>0.05% fee</Trans>
|
||||
</ResponsiveText>
|
||||
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
<Trans>Best for stable pairs.</Trans>
|
||||
</TYPE.main>
|
||||
</AutoColumn>
|
||||
|
||||
{distributions && <FeeTierPercentageBadge percentage={distributions[FeeAmount.LOW]} />}
|
||||
</AutoColumn>
|
||||
</ButtonRadioChecked>
|
||||
<ButtonRadioChecked
|
||||
width="32%"
|
||||
active={feeAmount === FeeAmount.MEDIUM}
|
||||
onClick={() => handleFeePoolSelectWithEvent(FeeAmount.MEDIUM)}
|
||||
>
|
||||
<AutoColumn gap="sm" justify="flex-start">
|
||||
<AutoColumn justify="flex-start" gap="4px">
|
||||
<ResponsiveText>
|
||||
<Trans>0.3% fee</Trans>
|
||||
</ResponsiveText>
|
||||
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
<Trans>Best for most pairs.</Trans>
|
||||
</TYPE.main>
|
||||
</AutoColumn>
|
||||
|
||||
{distributions && <FeeTierPercentageBadge percentage={distributions[FeeAmount.MEDIUM]} />}
|
||||
</AutoColumn>
|
||||
</ButtonRadioChecked>
|
||||
<ButtonRadioChecked
|
||||
width="32%"
|
||||
active={feeAmount === FeeAmount.HIGH}
|
||||
onClick={() => handleFeePoolSelectWithEvent(FeeAmount.HIGH)}
|
||||
>
|
||||
<AutoColumn gap="sm" justify="flex-start">
|
||||
<AutoColumn justify="flex-start" gap="4px">
|
||||
<ResponsiveText>
|
||||
<Trans>1% fee</Trans>
|
||||
</ResponsiveText>
|
||||
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||
<Trans>Best for exotic pairs.</Trans>
|
||||
</TYPE.main>
|
||||
</AutoColumn>
|
||||
|
||||
{distributions && <FeeTierPercentageBadge percentage={distributions[FeeAmount.HIGH]} />}
|
||||
</AutoColumn>
|
||||
</ButtonRadioChecked>
|
||||
</RowBetween>
|
||||
{chainId && showOptions && (
|
||||
<Select>
|
||||
{[FeeAmount.LOWEST, FeeAmount.LOW, FeeAmount.MEDIUM, FeeAmount.HIGH].map((_feeAmount, i) => {
|
||||
const { supportedChains } = FEE_AMOUNT_DETAIL[_feeAmount]
|
||||
if (supportedChains.includes(chainId)) {
|
||||
return (
|
||||
<FeeOption
|
||||
feeAmount={_feeAmount}
|
||||
active={feeAmount === _feeAmount}
|
||||
onClick={() => handleFeePoolSelectWithEvent(_feeAmount)}
|
||||
distributions={distributions}
|
||||
poolState={poolsByFeeTier[_feeAmount]}
|
||||
key={i}
|
||||
/>
|
||||
)
|
||||
}
|
||||
return null
|
||||
})}
|
||||
</Select>
|
||||
)}
|
||||
</DynamicSection>
|
||||
</AutoColumn>
|
||||
|
||||
30
src/components/FeeSelector/shared.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from 'constants/chains'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
export const FEE_AMOUNT_DETAIL: Record<
|
||||
FeeAmount,
|
||||
{ label: string; description: ReactNode; supportedChains: SupportedChainId[] }
|
||||
> = {
|
||||
[FeeAmount.LOWEST]: {
|
||||
label: '0.01',
|
||||
description: <Trans>Best for very stable pairs.</Trans>,
|
||||
supportedChains: [SupportedChainId.MAINNET],
|
||||
},
|
||||
[FeeAmount.LOW]: {
|
||||
label: '0.05',
|
||||
description: <Trans>Best for stable pairs.</Trans>,
|
||||
supportedChains: ALL_SUPPORTED_CHAIN_IDS,
|
||||
},
|
||||
[FeeAmount.MEDIUM]: {
|
||||
label: '0.3',
|
||||
description: <Trans>Best for most pairs.</Trans>,
|
||||
supportedChains: ALL_SUPPORTED_CHAIN_IDS,
|
||||
},
|
||||
[FeeAmount.HIGH]: {
|
||||
label: '1',
|
||||
description: <Trans>Best for exotic pairs.</Trans>,
|
||||
supportedChains: ALL_SUPPORTED_CHAIN_IDS,
|
||||
},
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
26
src/components/Header/HolidayOrnament.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { ReactElement } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import SantaHat from '../../assets/images/santa-hat.png'
|
||||
|
||||
const SantaHatImage = styled.img`
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
right: -4px;
|
||||
height: 18px;
|
||||
`
|
||||
|
||||
const Christmas = <SantaHatImage src={SantaHat} alt="Santa hat" />
|
||||
|
||||
const DATE_TO_ORNAMENT: { [date: string]: ReactElement } = {
|
||||
'12-24': Christmas,
|
||||
'12-25': Christmas,
|
||||
}
|
||||
|
||||
const HolidayOrnament = () => {
|
||||
// months in javascript are 0 indexed...
|
||||
const today = `${new Date().getMonth() + 1}-${new Date().getDate()}`
|
||||
return DATE_TO_ORNAMENT[today] || null
|
||||
}
|
||||
|
||||
export default HolidayOrnament
|
||||
@@ -1,22 +1,17 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import {
|
||||
ARBITRUM_HELP_CENTER_LINK,
|
||||
CHAIN_INFO,
|
||||
L2_CHAIN_IDS,
|
||||
OPTIMISM_HELP_CENTER_LINK,
|
||||
SupportedChainId,
|
||||
SupportedL2ChainId,
|
||||
} from 'constants/chains'
|
||||
import { CHAIN_INFO } from 'constants/chainInfo'
|
||||
import { 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 { ArrowDownCircle, ChevronDown } from 'react-feather'
|
||||
import { ApplicationModal } from 'state/application/actions'
|
||||
import { useModalOpen, useToggleModal } from 'state/application/hooks'
|
||||
import { useAppSelector } from 'state/hooks'
|
||||
import { addPopup, ApplicationModal } from 'state/application/reducer'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
|
||||
import { switchToNetwork } from 'utils/switchToNetwork'
|
||||
|
||||
import { useAppDispatch } from '../../state/hooks'
|
||||
import { switchToNetwork } from '../../utils/switchToNetwork'
|
||||
|
||||
const ActiveRowLinkList = styled.div`
|
||||
display: flex;
|
||||
@@ -34,17 +29,16 @@ const ActiveRowLinkList = styled.div`
|
||||
text-decoration: none;
|
||||
}
|
||||
& > a:first-child {
|
||||
border-top: 1px solid ${({ theme }) => theme.text2};
|
||||
margin: 0;
|
||||
margin-top: 6px;
|
||||
margin-top: 0px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
`
|
||||
const ActiveRowWrapper = styled.div`
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
padding: 8px 0 8px 0;
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
`
|
||||
const FlyoutHeader = styled.div`
|
||||
@@ -53,7 +47,7 @@ const FlyoutHeader = styled.div`
|
||||
`
|
||||
const FlyoutMenu = styled.div`
|
||||
align-items: flex-start;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
|
||||
0px 24px 32px rgba(0, 0, 0, 0.01);
|
||||
border-radius: 20px;
|
||||
@@ -75,7 +69,7 @@ const FlyoutMenu = styled.div`
|
||||
`
|
||||
const FlyoutRow = styled.div<{ active: boolean }>`
|
||||
align-items: center;
|
||||
background-color: ${({ active, theme }) => (active ? theme.bg2 : 'transparent')};
|
||||
background-color: ${({ active, theme }) => (active ? theme.bg1 : 'transparent')};
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
@@ -113,9 +107,9 @@ const SelectorLabel = styled(NetworkLabel)`
|
||||
`
|
||||
const SelectorControls = styled.div<{ interactive: boolean }>`
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
border: 2px solid ${({ theme }) => theme.bg1};
|
||||
border-radius: 12px;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
border: 2px solid ${({ theme }) => theme.bg0};
|
||||
border-radius: 16px;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
cursor: ${({ interactive }) => (interactive ? 'pointer' : 'auto')};
|
||||
display: flex;
|
||||
@@ -135,9 +129,9 @@ const SelectorWrapper = styled.div`
|
||||
}
|
||||
`
|
||||
const StyledChevronDown = styled(ChevronDown)`
|
||||
width: 12px;
|
||||
width: 16px;
|
||||
`
|
||||
const BridgeText = ({ chainId }: { chainId: SupportedL2ChainId }) => {
|
||||
const BridgeLabel = ({ chainId }: { chainId: SupportedChainId }) => {
|
||||
switch (chainId) {
|
||||
case SupportedChainId.ARBITRUM_ONE:
|
||||
case SupportedChainId.ARBITRUM_RINKEBY:
|
||||
@@ -145,11 +139,14 @@ const BridgeText = ({ chainId }: { chainId: SupportedL2ChainId }) => {
|
||||
case SupportedChainId.OPTIMISM:
|
||||
case SupportedChainId.OPTIMISTIC_KOVAN:
|
||||
return <Trans>Optimism Gateway</Trans>
|
||||
case SupportedChainId.POLYGON:
|
||||
case SupportedChainId.POLYGON_MUMBAI:
|
||||
return <Trans>Polygon Bridge</Trans>
|
||||
default:
|
||||
return <Trans>Bridge</Trans>
|
||||
}
|
||||
}
|
||||
const ExplorerText = ({ chainId }: { chainId: SupportedL2ChainId }) => {
|
||||
const ExplorerLabel = ({ chainId }: { chainId: SupportedChainId }) => {
|
||||
switch (chainId) {
|
||||
case SupportedChainId.ARBITRUM_ONE:
|
||||
case SupportedChainId.ARBITRUM_RINKEBY:
|
||||
@@ -157,91 +154,108 @@ const ExplorerText = ({ chainId }: { chainId: SupportedL2ChainId }) => {
|
||||
case SupportedChainId.OPTIMISM:
|
||||
case SupportedChainId.OPTIMISTIC_KOVAN:
|
||||
return <Trans>Optimistic Etherscan</Trans>
|
||||
case SupportedChainId.POLYGON:
|
||||
case SupportedChainId.POLYGON_MUMBAI:
|
||||
return <Trans>Polygonscan</Trans>
|
||||
default:
|
||||
return <Trans>Explorer</Trans>
|
||||
return <Trans>Etherscan</Trans>
|
||||
}
|
||||
}
|
||||
|
||||
function Row({
|
||||
targetChain,
|
||||
onSelectChain,
|
||||
}: {
|
||||
targetChain: SupportedChainId
|
||||
onSelectChain: (targetChain: number) => void
|
||||
}) {
|
||||
const { library, chainId } = useActiveWeb3React()
|
||||
if (!library || !chainId) {
|
||||
return null
|
||||
}
|
||||
const active = chainId === targetChain
|
||||
const { helpCenterUrl, explorer, bridge, label, logoUrl } = CHAIN_INFO[targetChain]
|
||||
|
||||
const rowContent = (
|
||||
<FlyoutRow onClick={() => onSelectChain(targetChain)} active={active}>
|
||||
<Logo src={logoUrl} />
|
||||
<NetworkLabel>{label}</NetworkLabel>
|
||||
{chainId === targetChain && <FlyoutRowActiveIndicator />}
|
||||
</FlyoutRow>
|
||||
)
|
||||
|
||||
if (active) {
|
||||
return (
|
||||
<ActiveRowWrapper>
|
||||
{rowContent}
|
||||
<ActiveRowLinkList>
|
||||
{bridge ? (
|
||||
<ExternalLink href={bridge}>
|
||||
<BridgeLabel chainId={chainId} /> <LinkOutCircle />
|
||||
</ExternalLink>
|
||||
) : null}
|
||||
{explorer ? (
|
||||
<ExternalLink href={explorer}>
|
||||
<ExplorerLabel chainId={chainId} /> <LinkOutCircle />
|
||||
</ExternalLink>
|
||||
) : null}
|
||||
{helpCenterUrl ? (
|
||||
<ExternalLink href={helpCenterUrl}>
|
||||
<Trans>Help Center</Trans> <LinkOutCircle />
|
||||
</ExternalLink>
|
||||
) : null}
|
||||
</ActiveRowLinkList>
|
||||
</ActiveRowWrapper>
|
||||
)
|
||||
}
|
||||
return rowContent
|
||||
}
|
||||
|
||||
export default function NetworkSelector() {
|
||||
const { chainId, library } = useActiveWeb3React()
|
||||
const node = useRef<HTMLDivElement>()
|
||||
const open = useModalOpen(ApplicationModal.NETWORK_SELECTOR)
|
||||
const toggle = useToggleModal(ApplicationModal.NETWORK_SELECTOR)
|
||||
useOnClickOutside(node, open ? toggle : undefined)
|
||||
const implements3085 = useAppSelector((state) => state.application.implements3085)
|
||||
|
||||
const info = chainId ? CHAIN_INFO[chainId] : undefined
|
||||
|
||||
const isOnL2 = chainId ? L2_CHAIN_IDS.includes(chainId) : false
|
||||
const showSelector = Boolean(implements3085 || isOnL2)
|
||||
const mainnetInfo = CHAIN_INFO[SupportedChainId.MAINNET]
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const conditionalToggle = useCallback(() => {
|
||||
if (showSelector) {
|
||||
toggle()
|
||||
}
|
||||
}, [showSelector, toggle])
|
||||
const handleRowClick = useCallback(
|
||||
(targetChain: number) => {
|
||||
if (!library) return
|
||||
switchToNetwork({ library, chainId: targetChain })
|
||||
.then(() => toggle())
|
||||
.catch((error) => {
|
||||
console.error('Failed to switch networks', error)
|
||||
toggle()
|
||||
dispatch(addPopup({ content: { failedSwitchNetwork: targetChain }, key: `failed-network-switch` }))
|
||||
})
|
||||
},
|
||||
[dispatch, library, toggle]
|
||||
)
|
||||
|
||||
if (!chainId || !info || !library) {
|
||||
return null
|
||||
}
|
||||
|
||||
function Row({ targetChain }: { targetChain: number }) {
|
||||
if (!library || !chainId || (!implements3085 && targetChain !== chainId)) {
|
||||
return null
|
||||
}
|
||||
const handleRowClick = () => {
|
||||
switchToNetwork({ library, chainId: targetChain })
|
||||
toggle()
|
||||
}
|
||||
const active = chainId === targetChain
|
||||
const hasExtendedInfo = L2_CHAIN_IDS.includes(targetChain)
|
||||
const isOptimism = targetChain === SupportedChainId.OPTIMISM
|
||||
const rowText = `${CHAIN_INFO[targetChain].label}${isOptimism ? ' (Optimism)' : ''}`
|
||||
const RowContent = () => (
|
||||
<FlyoutRow onClick={handleRowClick} active={active}>
|
||||
<Logo src={CHAIN_INFO[targetChain].logoUrl} />
|
||||
<NetworkLabel>{rowText}</NetworkLabel>
|
||||
{chainId === targetChain && <FlyoutRowActiveIndicator />}
|
||||
</FlyoutRow>
|
||||
)
|
||||
const helpCenterLink = isOptimism ? OPTIMISM_HELP_CENTER_LINK : ARBITRUM_HELP_CENTER_LINK
|
||||
if (active && hasExtendedInfo) {
|
||||
return (
|
||||
<ActiveRowWrapper>
|
||||
<RowContent />
|
||||
<ActiveRowLinkList>
|
||||
<ExternalLink href={CHAIN_INFO[targetChain as SupportedL2ChainId].bridge}>
|
||||
<BridgeText chainId={chainId} /> <LinkOutCircle />
|
||||
</ExternalLink>
|
||||
<ExternalLink href={CHAIN_INFO[targetChain].explorer}>
|
||||
<ExplorerText chainId={chainId} /> <LinkOutCircle />
|
||||
</ExternalLink>
|
||||
<ExternalLink href={helpCenterLink}>
|
||||
<Trans>Help Center</Trans> <LinkOutCircle />
|
||||
</ExternalLink>
|
||||
</ActiveRowLinkList>
|
||||
</ActiveRowWrapper>
|
||||
)
|
||||
}
|
||||
return <RowContent />
|
||||
}
|
||||
|
||||
return (
|
||||
<SelectorWrapper ref={node as any}>
|
||||
<SelectorControls onClick={conditionalToggle} interactive={showSelector}>
|
||||
<SelectorLogo interactive={showSelector} src={info.logoUrl || mainnetInfo.logoUrl} />
|
||||
<SelectorControls onClick={toggle} interactive>
|
||||
<SelectorLogo interactive src={info.logoUrl} />
|
||||
<SelectorLabel>{info.label}</SelectorLabel>
|
||||
{showSelector && <StyledChevronDown />}
|
||||
<StyledChevronDown />
|
||||
</SelectorControls>
|
||||
{open && (
|
||||
<FlyoutMenu>
|
||||
<FlyoutMenu onMouseLeave={toggle}>
|
||||
<FlyoutHeader>
|
||||
<Trans>Select a network</Trans>
|
||||
</FlyoutHeader>
|
||||
<Row targetChain={SupportedChainId.MAINNET} />
|
||||
<Row targetChain={SupportedChainId.OPTIMISM} />
|
||||
<Row targetChain={SupportedChainId.ARBITRUM_ONE} />
|
||||
<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} />
|
||||
</FlyoutMenu>
|
||||
)}
|
||||
</SelectorWrapper>
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { RowFixed } from 'components/Row'
|
||||
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 JSBI from 'jsbi'
|
||||
import useBlockNumber from 'lib/hooks/useBlockNumber'
|
||||
import ms from 'ms.macro'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useAppSelector } from 'state/hooks'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
import { ExternalLink, ThemedText } from 'theme'
|
||||
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
|
||||
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useBlockNumber } from '../../state/application/hooks'
|
||||
import { ExternalLink, TYPE } from '../../theme'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { MouseoverTooltip } from '../Tooltip'
|
||||
import { ChainConnectivityWarning } from './ChainConnectivityWarning'
|
||||
|
||||
const StyledPolling = styled.div<{ warning: boolean }>`
|
||||
@@ -22,12 +31,20 @@ const StyledPolling = styled.div<{ warning: boolean }>`
|
||||
display: none;
|
||||
`}
|
||||
`
|
||||
const StyledPollingNumber = styled(TYPE.small)<{ breathe: boolean; hovering: boolean }>`
|
||||
const StyledPollingNumber = styled(ThemedText.Small)<{ breathe: boolean; hovering: boolean }>`
|
||||
transition: opacity 0.25s ease;
|
||||
opacity: ${({ breathe, hovering }) => (hovering ? 0.7 : breathe ? 1 : 0.5)};
|
||||
:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
a {
|
||||
color: unset;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
color: unset;
|
||||
}
|
||||
`
|
||||
const StyledPollingDot = styled.div<{ warning: boolean }>`
|
||||
width: 8px;
|
||||
@@ -40,6 +57,17 @@ const StyledPollingDot = styled.div<{ warning: boolean }>`
|
||||
transition: 250ms ease background-color;
|
||||
`
|
||||
|
||||
const StyledGasDot = styled.div`
|
||||
background-color: ${({ theme }) => theme.text3};
|
||||
border-radius: 50%;
|
||||
height: 4px;
|
||||
min-height: 4px;
|
||||
min-width: 4px;
|
||||
position: relative;
|
||||
transition: 250ms ease background-color;
|
||||
width: 4px;
|
||||
`
|
||||
|
||||
const rotate360 = keyframes`
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
@@ -68,12 +96,25 @@ const Spinner = styled.div<{ warning: boolean }>`
|
||||
top: -3px;
|
||||
`
|
||||
|
||||
const DEFAULT_MS_BEFORE_WARNING = ms`10m`
|
||||
const NETWORK_HEALTH_CHECK_MS = ms`10s`
|
||||
|
||||
export default function Polling() {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const blockNumber = useBlockNumber()
|
||||
const [isMounting, setIsMounting] = useState(false)
|
||||
const [isHover, setIsHover] = useState(false)
|
||||
const chainConnectivityWarning = useAppSelector((state) => state.application.chainConnectivityWarning)
|
||||
const machineTime = useMachineTimeMs(NETWORK_HEALTH_CHECK_MS)
|
||||
const blockTime = useCurrentBlockTimestamp()
|
||||
const theme = useTheme()
|
||||
|
||||
const ethGasPrice = useGasPrice()
|
||||
const priceGwei = ethGasPrice ? JSBI.divide(ethGasPrice, JSBI.BigInt(1000000000)) : undefined
|
||||
|
||||
const waitMsBeforeWarning =
|
||||
(chainId ? CHAIN_INFO[chainId]?.blockWaitMsBeforeWarning : DEFAULT_MS_BEFORE_WARNING) ?? DEFAULT_MS_BEFORE_WARNING
|
||||
|
||||
const warning = Boolean(!!blockTime && machineTime - blockTime.mul(1000).toNumber() > waitMsBeforeWarning)
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
@@ -93,25 +134,48 @@ export default function Polling() {
|
||||
//if you pass a value to array, like this [data] than clearTimeout will run every time this value changes (useEffect re-run)
|
||||
)
|
||||
|
||||
//TODO - chainlink gas oracle is really slow. Can we get a better data source?
|
||||
|
||||
return (
|
||||
<>
|
||||
<ExternalLink
|
||||
href={chainId && blockNumber ? getExplorerLink(chainId, blockNumber.toString(), ExplorerDataType.BLOCK) : ''}
|
||||
>
|
||||
<StyledPolling
|
||||
onMouseEnter={() => setIsHover(true)}
|
||||
onMouseLeave={() => setIsHover(false)}
|
||||
warning={chainConnectivityWarning}
|
||||
>
|
||||
<RowFixed>
|
||||
<StyledPolling onMouseEnter={() => setIsHover(true)} onMouseLeave={() => setIsHover(false)} warning={warning}>
|
||||
<ExternalLink href={'https://etherscan.io/gastracker'}>
|
||||
{priceGwei ? (
|
||||
<RowFixed style={{ marginRight: '8px' }}>
|
||||
<ThemedText.Main fontSize="11px" mr="8px" color={theme.text3}>
|
||||
<MouseoverTooltip
|
||||
text={
|
||||
<Trans>
|
||||
The current fast gas amount for sending a transaction on L1. Gas fees are paid in
|
||||
Ethereum's native currency Ether (ETH) and denominated in GWEI.
|
||||
</Trans>
|
||||
}
|
||||
>
|
||||
{priceGwei.toString()} <Trans>gwei</Trans>
|
||||
</MouseoverTooltip>
|
||||
</ThemedText.Main>
|
||||
<StyledGasDot />
|
||||
</RowFixed>
|
||||
) : null}
|
||||
</ExternalLink>
|
||||
<StyledPollingNumber breathe={isMounting} hovering={isHover}>
|
||||
{blockNumber} 
|
||||
<ExternalLink
|
||||
href={
|
||||
chainId && blockNumber ? getExplorerLink(chainId, blockNumber.toString(), ExplorerDataType.BLOCK) : ''
|
||||
}
|
||||
>
|
||||
<MouseoverTooltip
|
||||
text={<Trans>The most recent block number on this network. Prices update on every block.</Trans>}
|
||||
>
|
||||
{blockNumber} 
|
||||
</MouseoverTooltip>
|
||||
</ExternalLink>
|
||||
</StyledPollingNumber>
|
||||
<StyledPollingDot warning={chainConnectivityWarning}>
|
||||
{isMounting && <Spinner warning={chainConnectivityWarning} />}
|
||||
</StyledPollingDot>{' '}
|
||||
<StyledPollingDot warning={warning}>{isMounting && <Spinner warning={warning} />}</StyledPollingDot>{' '}
|
||||
</StyledPolling>
|
||||
</ExternalLink>
|
||||
{chainConnectivityWarning && <ChainConnectivityWarning />}
|
||||
{warning && <ChainConnectivityWarning />}
|
||||
</RowFixed>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
|
||||
import { CHAIN_INFO, SupportedChainId } from 'constants/chains'
|
||||
import { useMemo } from 'react'
|
||||
import { X } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import tokenLogo from '../../assets/images/token-logo.png'
|
||||
import { UNI } from '../../constants/tokens'
|
||||
import { useMerkleDistributorContract } from '../../hooks/useContract'
|
||||
import useCurrentBlockTimestamp from '../../hooks/useCurrentBlockTimestamp'
|
||||
import { useTotalSupply } from '../../hooks/useTotalSupply'
|
||||
import useUSDCPrice from '../../hooks/useUSDCPrice'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { useTotalUniEarned } from '../../state/stake/hooks'
|
||||
import { useAggregateUniBalance, useTokenBalance } from '../../state/wallet/hooks'
|
||||
import { ExternalLink, StyledInternalLink, TYPE, UniTokenAnimated } from '../../theme'
|
||||
import { computeUniCirculation } from '../../utils/computeUniCirculation'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { Break, CardBGImage, CardNoise, CardSection, DataCard } from '../earn/styled'
|
||||
import { RowBetween } from '../Row'
|
||||
|
||||
const ContentWrapper = styled(AutoColumn)`
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const ModalUpper = styled(DataCard)`
|
||||
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
|
||||
background: radial-gradient(76.02% 75.41% at 1.84% 0%, #ff007a 0%, #021d43 100%);
|
||||
padding: 0.5rem;
|
||||
`
|
||||
|
||||
const StyledClose = styled(X)`
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`
|
||||
|
||||
/**
|
||||
* Content for balance stats modal
|
||||
*/
|
||||
export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowUniBalanceModal: any }) {
|
||||
const { account, chainId } = useActiveWeb3React()
|
||||
const uni = chainId ? UNI[chainId] : undefined
|
||||
|
||||
const total = useAggregateUniBalance()
|
||||
const uniBalance: CurrencyAmount<Token> | undefined = useTokenBalance(account ?? undefined, uni)
|
||||
const uniToClaim: CurrencyAmount<Token> | undefined = useTotalUniEarned()
|
||||
|
||||
const totalSupply: CurrencyAmount<Token> | undefined = useTotalSupply(uni)
|
||||
const uniPrice = useUSDCPrice(uni)
|
||||
const blockTimestamp = useCurrentBlockTimestamp()
|
||||
const unclaimedUni = useTokenBalance(useMerkleDistributorContract()?.address, uni)
|
||||
const circulation: CurrencyAmount<Token> | undefined = useMemo(
|
||||
() =>
|
||||
blockTimestamp && uni && chainId === 1 ? computeUniCirculation(uni, blockTimestamp, unclaimedUni) : totalSupply,
|
||||
[blockTimestamp, chainId, totalSupply, unclaimedUni, uni]
|
||||
)
|
||||
|
||||
const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET]
|
||||
|
||||
return (
|
||||
<ContentWrapper gap="lg">
|
||||
<ModalUpper>
|
||||
<CardBGImage />
|
||||
<CardNoise />
|
||||
<CardSection gap="md">
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">
|
||||
<Trans>Your UNI Breakdown</Trans>
|
||||
</TYPE.white>
|
||||
<StyledClose stroke="white" onClick={() => setShowUniBalanceModal(false)} />
|
||||
</RowBetween>
|
||||
</CardSection>
|
||||
<Break />
|
||||
{account && (
|
||||
<>
|
||||
<CardSection gap="sm">
|
||||
<AutoColumn gap="md" justify="center">
|
||||
<UniTokenAnimated width="48px" src={tokenLogo} />{' '}
|
||||
<TYPE.white fontSize={48} fontWeight={600} color="white">
|
||||
{total?.toFixed(2, { groupSeparator: ',' })}
|
||||
</TYPE.white>
|
||||
</AutoColumn>
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">
|
||||
<Trans>Balance:</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white color="white">{uniBalance?.toFixed(2, { groupSeparator: ',' })}</TYPE.white>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">
|
||||
<Trans>Unclaimed:</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white color="white">
|
||||
{uniToClaim?.toFixed(4, { groupSeparator: ',' })}{' '}
|
||||
{uniToClaim && uniToClaim.greaterThan('0') && (
|
||||
<StyledInternalLink onClick={() => setShowUniBalanceModal(false)} to="/uni">
|
||||
<Trans>(claim)</Trans>
|
||||
</StyledInternalLink>
|
||||
)}
|
||||
</TYPE.white>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
</CardSection>
|
||||
<Break />
|
||||
</>
|
||||
)}
|
||||
<CardSection gap="sm">
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">
|
||||
<Trans>UNI price:</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white color="white">${uniPrice?.toFixed(2) ?? '-'}</TYPE.white>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">
|
||||
<Trans>UNI in circulation:</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white color="white">{circulation?.toFixed(0, { groupSeparator: ',' })}</TYPE.white>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<TYPE.white color="white">
|
||||
<Trans>Total Supply</Trans>
|
||||
</TYPE.white>
|
||||
<TYPE.white color="white">{totalSupply?.toFixed(0, { groupSeparator: ',' })}</TYPE.white>
|
||||
</RowBetween>
|
||||
{uni && uni.chainId === 1 ? (
|
||||
<ExternalLink href={`${infoLink}/token/${uni.address}`}>
|
||||
<Trans>View UNI Analytics</Trans>
|
||||
</ExternalLink>
|
||||
) : null}
|
||||
</AutoColumn>
|
||||
</CardSection>
|
||||
</ModalUpper>
|
||||
</ContentWrapper>
|
||||
)
|
||||
}
|
||||
@@ -1,30 +1,29 @@
|
||||
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 { useState } from 'react'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { Text } from 'rebass'
|
||||
import { useShowClaimPopup, useToggleSelfClaimModal } from 'state/application/hooks'
|
||||
import { useUserHasAvailableClaim } from 'state/claim/hooks'
|
||||
import { useUserHasSubmittedClaim } from 'state/transactions/hooks'
|
||||
import { useDarkModeManager } from 'state/user/hooks'
|
||||
import { useETHBalances } from 'state/wallet/hooks'
|
||||
import { useNativeCurrencyBalances } from 'state/wallet/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { ReactComponent as Logo } from '../../assets/svg/logo.svg'
|
||||
import { ReactComponent as LogoDark } from '../../assets/svg/logo_white.svg'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { ExternalLink, TYPE } from '../../theme'
|
||||
import { ExternalLink, ThemedText } from '../../theme'
|
||||
import ClaimModal from '../claim/ClaimModal'
|
||||
import { CardNoise } from '../earn/styled'
|
||||
import Menu from '../Menu'
|
||||
import Modal from '../Modal'
|
||||
import Row from '../Row'
|
||||
import { Dots } from '../swap/styleds'
|
||||
import Web3Status from '../Web3Status'
|
||||
import HolidayOrnament from './HolidayOrnament'
|
||||
import NetworkSelector from './NetworkSelector'
|
||||
import UniBalanceContent from './UniBalanceContent'
|
||||
|
||||
const HeaderFrame = styled.div<{ showBackground: boolean }>`
|
||||
display: grid;
|
||||
@@ -91,7 +90,7 @@ const HeaderLinks = styled(Row)`
|
||||
justify-self: center;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
width: fit-content;
|
||||
padding: 4px;
|
||||
padding: 2px;
|
||||
border-radius: 16px;
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
@@ -123,10 +122,11 @@ const AccountElement = styled.div<{ active: boolean }>`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
background-color: ${({ theme, active }) => (!active ? theme.bg1 : theme.bg1)};
|
||||
border-radius: 12px;
|
||||
background-color: ${({ theme, active }) => (!active ? theme.bg0 : theme.bg0)};
|
||||
border-radius: 16px;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
|
||||
:focus {
|
||||
border: 1px solid blue;
|
||||
@@ -181,6 +181,8 @@ const UniIcon = styled.div`
|
||||
:hover {
|
||||
transform: rotate(-5deg);
|
||||
}
|
||||
|
||||
position: relative;
|
||||
`
|
||||
|
||||
const activeClassName = 'ACTIVE'
|
||||
@@ -202,11 +204,11 @@ const StyledNavLink = styled(NavLink).attrs({
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
&.${activeClassName} {
|
||||
border-radius: 12px;
|
||||
border-radius: 14px;
|
||||
font-weight: 600;
|
||||
justify-content: center;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
}
|
||||
|
||||
:hover,
|
||||
@@ -231,7 +233,7 @@ const StyledExternalLink = styled(ExternalLink).attrs({
|
||||
font-weight: 500;
|
||||
|
||||
&.${activeClassName} {
|
||||
border-radius: 12px;
|
||||
border-radius: 14px;
|
||||
font-weight: 600;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
}
|
||||
@@ -246,8 +248,9 @@ const StyledExternalLink = styled(ExternalLink).attrs({
|
||||
export default function Header() {
|
||||
const { account, chainId } = useActiveWeb3React()
|
||||
|
||||
const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? '']
|
||||
const userEthBalance = useNativeCurrencyBalances(account ? [account] : [])?.[account ?? '']
|
||||
const [darkMode] = useDarkModeManager()
|
||||
const { white, black } = useTheme()
|
||||
|
||||
const toggleClaimModal = useToggleSelfClaimModal()
|
||||
|
||||
@@ -255,25 +258,24 @@ export default function Header() {
|
||||
|
||||
const { claimTxn } = useUserHasSubmittedClaim(account ?? undefined)
|
||||
|
||||
const [showUniBalanceModal, setShowUniBalanceModal] = useState(false)
|
||||
const showClaimPopup = useShowClaimPopup()
|
||||
|
||||
const scrollY = useScrollPosition()
|
||||
|
||||
const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET]
|
||||
const {
|
||||
infoLink,
|
||||
addNetworkInfo: {
|
||||
nativeCurrency: { symbol: nativeCurrencySymbol },
|
||||
},
|
||||
} = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET]
|
||||
|
||||
return (
|
||||
<HeaderFrame showBackground={scrollY > 45}>
|
||||
<ClaimModal />
|
||||
<Modal isOpen={showUniBalanceModal} onDismiss={() => setShowUniBalanceModal(false)}>
|
||||
<UniBalanceContent setShowUniBalanceModal={setShowUniBalanceModal} />
|
||||
</Modal>
|
||||
<Title href=".">
|
||||
<UniIcon>
|
||||
{darkMode ? (
|
||||
<LogoDark width="24px" height="100%" title="logo" />
|
||||
) : (
|
||||
<Logo width="24px" height="100%" title="logo" />
|
||||
)}
|
||||
<Logo fill={darkMode ? white : black} width="24px" height="100%" title="logo" />
|
||||
<HolidayOrnament />
|
||||
</UniIcon>
|
||||
</Title>
|
||||
<HeaderLinks>
|
||||
@@ -312,7 +314,7 @@ export default function Header() {
|
||||
{availableClaim && !showClaimPopup && (
|
||||
<UNIWrapper onClick={toggleClaimModal}>
|
||||
<UNIAmount active={!!account && !availableClaim} style={{ pointerEvents: 'auto' }}>
|
||||
<TYPE.white padding="0 2px">
|
||||
<ThemedText.White padding="0 2px">
|
||||
{claimTxn && !claimTxn?.receipt ? (
|
||||
<Dots>
|
||||
<Trans>Claiming UNI</Trans>
|
||||
@@ -320,7 +322,7 @@ export default function Header() {
|
||||
) : (
|
||||
<Trans>Claim UNI</Trans>
|
||||
)}
|
||||
</TYPE.white>
|
||||
</ThemedText.White>
|
||||
</UNIAmount>
|
||||
<CardNoise />
|
||||
</UNIWrapper>
|
||||
@@ -328,7 +330,9 @@ export default function Header() {
|
||||
<AccountElement active={!!account}>
|
||||
{account && userEthBalance ? (
|
||||
<BalanceText style={{ flexShrink: 0, userSelect: 'none' }} pl="0.75rem" pr="0.5rem" fontWeight={500}>
|
||||
<Trans>{userEthBalance?.toSignificant(3)} ETH</Trans>
|
||||
<Trans>
|
||||
{userEthBalance?.toSignificant(3)} {nativeCurrencySymbol}
|
||||
</Trans>
|
||||
</BalanceText>
|
||||
) : null}
|
||||
<Web3Status />
|
||||
|
||||
@@ -2,9 +2,15 @@ import Tooltip from 'components/Tooltip'
|
||||
import { useState } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
const TextWrapper = styled.span<{ margin: boolean; link?: boolean; fontSize?: string; adjustSize?: boolean }>`
|
||||
const TextWrapper = styled.span<{
|
||||
margin: boolean
|
||||
link?: boolean
|
||||
fontSize?: string
|
||||
adjustSize?: boolean
|
||||
textColor?: string
|
||||
}>`
|
||||
margin-left: ${({ margin }) => margin && '4px'};
|
||||
color: ${({ theme, link }) => (link ? theme.blue1 : theme.text1)};
|
||||
color: ${({ theme, link, textColor }) => (link ? theme.blue1 : textColor ?? theme.text1)};
|
||||
font-size: ${({ fontSize }) => fontSize ?? 'inherit'};
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
@@ -18,6 +24,7 @@ const HoverInlineText = ({
|
||||
margin = false,
|
||||
adjustSize = false,
|
||||
fontSize,
|
||||
textColor,
|
||||
link,
|
||||
...rest
|
||||
}: {
|
||||
@@ -26,6 +33,7 @@ const HoverInlineText = ({
|
||||
margin?: boolean
|
||||
adjustSize?: boolean
|
||||
fontSize?: string
|
||||
textColor?: string
|
||||
link?: boolean
|
||||
}) => {
|
||||
const [showHover, setShowHover] = useState(false)
|
||||
@@ -42,6 +50,7 @@ const HoverInlineText = ({
|
||||
onMouseLeave={() => setShowHover(false)}
|
||||
margin={margin}
|
||||
adjustSize={adjustSize}
|
||||
textColor={textColor}
|
||||
link={link}
|
||||
fontSize={fontSize}
|
||||
{...rest}
|
||||
@@ -53,7 +62,14 @@ const HoverInlineText = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<TextWrapper margin={margin} adjustSize={adjustSize} link={link} fontSize={fontSize} {...rest}>
|
||||
<TextWrapper
|
||||
margin={margin}
|
||||
adjustSize={adjustSize}
|
||||
link={link}
|
||||
fontSize={fontSize}
|
||||
textColor={textColor}
|
||||
{...rest}
|
||||
>
|
||||
{text}
|
||||
</TextWrapper>
|
||||
)
|
||||
|
||||
26
src/components/Identicon/StatusIcon.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { AbstractConnector } from '@web3-react/abstract-connector'
|
||||
import { Connector } from 'widgets-web3-react/types'
|
||||
|
||||
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
|
||||
import FortmaticIcon from '../../assets/images/fortmaticIcon.png'
|
||||
import PortisIcon from '../../assets/images/portisIcon.png'
|
||||
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
|
||||
import { fortmatic, injected, portis, walletconnect, walletlink } from '../../connectors'
|
||||
import Identicon from '../Identicon'
|
||||
|
||||
export default function StatusIcon({ connector }: { connector: AbstractConnector | Connector }) {
|
||||
switch (connector) {
|
||||
case injected:
|
||||
return <Identicon />
|
||||
case walletconnect:
|
||||
return <img src={WalletConnectIcon} alt={'WalletConnect'} />
|
||||
case walletlink:
|
||||
return <img src={CoinbaseWalletIcon} alt={'Coinbase Wallet'} />
|
||||
case fortmatic:
|
||||
return <img src={FortmaticIcon} alt={'Fortmatic'} />
|
||||
case portis:
|
||||
return <img src={PortisIcon} alt={'Portis'} />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,52 @@
|
||||
import Davatar from '@davatar/react'
|
||||
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 StyledIdenticonContainer = styled.div`
|
||||
const StyledIdenticon = styled.div`
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
border-radius: 1.125rem;
|
||||
background-color: ${({ theme }) => theme.bg4};
|
||||
font-size: initial;
|
||||
`
|
||||
|
||||
const StyledAvatar = styled.img`
|
||||
height: inherit;
|
||||
width: inherit;
|
||||
border-radius: inherit;
|
||||
`
|
||||
|
||||
export default function Identicon() {
|
||||
const { account, library } = useActiveWeb3React()
|
||||
const { account } = useActiveWeb3React()
|
||||
const { avatar } = useENSAvatar(account ?? undefined)
|
||||
const [fetchable, setFetchable] = useState(true)
|
||||
|
||||
const icon = useMemo(() => account && jazzicon(16, parseInt(account.slice(2, 10), 16)), [account])
|
||||
const iconRef = useRef<HTMLDivElement>(null)
|
||||
useLayoutEffect(() => {
|
||||
const current = iconRef.current
|
||||
if (icon) {
|
||||
current?.appendChild(icon)
|
||||
return () => {
|
||||
try {
|
||||
current?.removeChild(icon)
|
||||
} catch (e) {
|
||||
console.error('Avatar icon not found')
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}, [icon, iconRef])
|
||||
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451
|
||||
return (
|
||||
<StyledIdenticonContainer>
|
||||
{account && library?.provider && <Davatar address={account} size={16} provider={library.provider} />}
|
||||
</StyledIdenticonContainer>
|
||||
<StyledIdenticon>
|
||||
{avatar && fetchable ? (
|
||||
<StyledAvatar alt="avatar" src={avatar} onError={() => setFetchable(false)}></StyledAvatar>
|
||||
) : (
|
||||
<span ref={iconRef} />
|
||||
)}
|
||||
</StyledIdenticon>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { AutoColumn } from 'components/Column'
|
||||
import { ReactNode, useCallback, useEffect, useState } from 'react'
|
||||
import { Minus, Plus } from 'react-feather'
|
||||
import styled, { keyframes } from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import { Input as NumericalInput } from '../NumericalInput'
|
||||
|
||||
@@ -57,13 +57,13 @@ const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
|
||||
`};
|
||||
`
|
||||
|
||||
const InputTitle = styled(TYPE.small)`
|
||||
const InputTitle = styled(ThemedText.Small)`
|
||||
color: ${({ theme }) => theme.text2};
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
const ButtonLabel = styled(TYPE.white)<{ disabled: boolean }>`
|
||||
const ButtonLabel = styled(ThemedText.White)<{ disabled: boolean }>`
|
||||
color: ${({ theme, disabled }) => (disabled ? theme.text2 : theme.text1)} !important;
|
||||
`
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { area, curveStepAfter, ScaleLinear } from 'd3'
|
||||
import inRange from 'lodash/inRange'
|
||||
import React, { useMemo } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
@@ -36,7 +35,10 @@ export const Area = ({
|
||||
.x((d: unknown) => xScale(xValue(d as ChartEntry)))
|
||||
.y1((d: unknown) => yScale(yValue(d as ChartEntry)))
|
||||
.y0(yScale(0))(
|
||||
series.filter((d) => inRange(xScale(xValue(d)), 0, innerWidth)) as Iterable<[number, number]>
|
||||
series.filter((d) => {
|
||||
const value = xScale(xValue(d))
|
||||
return value > 0 && value <= window.innerWidth
|
||||
}) as Iterable<[number, number]>
|
||||
) ?? undefined
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -14,12 +14,18 @@ import { batch } from 'react-redux'
|
||||
import { Bound } from 'state/mint/v3/actions'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { Chart } from './Chart'
|
||||
import { useDensityChartData } from './hooks'
|
||||
import { ZoomLevels } from './types'
|
||||
|
||||
const ZOOM_LEVELS: Record<FeeAmount, ZoomLevels> = {
|
||||
[FeeAmount.LOWEST]: {
|
||||
initialMin: 0.999,
|
||||
initialMax: 1.001,
|
||||
min: 0.00001,
|
||||
max: 1.5,
|
||||
},
|
||||
[FeeAmount.LOW]: {
|
||||
initialMin: 0.999,
|
||||
initialMax: 1.001,
|
||||
@@ -52,9 +58,9 @@ function InfoBox({ message, icon }: { message?: ReactNode; icon: ReactNode }) {
|
||||
<ColumnCenter style={{ height: '100%', justifyContent: 'center' }}>
|
||||
{icon}
|
||||
{message && (
|
||||
<TYPE.mediumHeader padding={10} marginTop="20px" textAlign="center">
|
||||
<ThemedText.MediumHeader padding={10} marginTop="20px" textAlign="center">
|
||||
{message}
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
)}
|
||||
</ColumnCenter>
|
||||
)
|
||||
|
||||
@@ -1,20 +1,33 @@
|
||||
import { t } from '@lingui/macro'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { CHAIN_INFO, L2_CHAIN_IDS, SupportedChainId } from 'constants/chains'
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
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 { BookOpen, Check, ChevronLeft, Code, Globe, Info, MessageCircle, Moon, PieChart, Sun } from 'react-feather'
|
||||
import {
|
||||
BookOpen,
|
||||
Check,
|
||||
ChevronLeft,
|
||||
Coffee,
|
||||
FileText,
|
||||
Globe,
|
||||
HelpCircle,
|
||||
Info,
|
||||
MessageCircle,
|
||||
Moon,
|
||||
Sun,
|
||||
} from 'react-feather'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { useDarkModeManager } from 'state/user/hooks'
|
||||
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 { ApplicationModal } from '../../state/application/actions'
|
||||
import { useModalOpen, useToggleModal } from '../../state/application/hooks'
|
||||
import { ApplicationModal } from '../../state/application/reducer'
|
||||
import { ExternalLink } from '../../theme'
|
||||
import { ButtonPrimary } from '../Button'
|
||||
|
||||
@@ -36,12 +49,11 @@ const StyledMenuButton = styled.button`
|
||||
background-color: transparent;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 38px;
|
||||
height: 40px;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
border: 1px solid ${({ theme }) => theme.bg0};
|
||||
|
||||
padding: 0.15rem 0.5rem;
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
|
||||
:hover,
|
||||
:focus {
|
||||
@@ -166,8 +178,6 @@ const ToggleMenuItem = styled.button`
|
||||
}
|
||||
`
|
||||
|
||||
const CODE_LINK = 'https://github.com/Uniswap/uniswap-interface'
|
||||
|
||||
function LanguageMenuItem({ locale, active, key }: { locale: SupportedLocale; active: boolean; key: string }) {
|
||||
const { to, onClick } = useLocationLinkProps(locale)
|
||||
|
||||
@@ -201,11 +211,11 @@ export default function Menu() {
|
||||
|
||||
const node = useRef<HTMLDivElement>()
|
||||
const open = useModalOpen(ApplicationModal.MENU)
|
||||
const toggle = useToggleModal(ApplicationModal.MENU)
|
||||
useOnClickOutside(node, open ? toggle : undefined)
|
||||
const toggleMenu = useToggleModal(ApplicationModal.MENU)
|
||||
useOnClickOutside(node, open ? toggleMenu : undefined)
|
||||
const togglePrivacyPolicy = useToggleModal(ApplicationModal.PRIVACY_POLICY)
|
||||
const openClaimModal = useToggleModal(ApplicationModal.ADDRESS_CLAIM)
|
||||
const showUNIClaimOption = Boolean(!!account && !!chainId && !L2_CHAIN_IDS.includes(chainId))
|
||||
const { infoLink } = CHAIN_INFO[chainId ? chainId : SupportedChainId.MAINNET]
|
||||
|
||||
const [darkMode, toggleDarkMode] = useDarkModeManager()
|
||||
|
||||
@@ -216,77 +226,86 @@ export default function Menu() {
|
||||
}, [open])
|
||||
|
||||
return (
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451
|
||||
<StyledMenu ref={node as any}>
|
||||
<StyledMenuButton onClick={toggle} aria-label={t`Menu`}>
|
||||
<StyledMenuIcon />
|
||||
</StyledMenuButton>
|
||||
<>
|
||||
{/* // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30451 */}
|
||||
<StyledMenu ref={node as any}>
|
||||
<StyledMenuButton onClick={toggleMenu} aria-label={t`Menu`}>
|
||||
<StyledMenuIcon />
|
||||
</StyledMenuButton>
|
||||
|
||||
{open &&
|
||||
(() => {
|
||||
switch (menu) {
|
||||
case 'lang':
|
||||
return <LanguageMenu close={() => setMenu('main')} />
|
||||
case 'main':
|
||||
default:
|
||||
return (
|
||||
<MenuFlyout>
|
||||
<MenuItem href="https://uniswap.org/">
|
||||
<div>
|
||||
<Trans>About</Trans>
|
||||
</div>
|
||||
<Info opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<MenuItem href="https://docs.uniswap.org/">
|
||||
<div>
|
||||
<Trans>Docs</Trans>
|
||||
</div>
|
||||
<BookOpen opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<MenuItem href={CODE_LINK}>
|
||||
<div>
|
||||
<Trans>Code</Trans>
|
||||
</div>
|
||||
<Code opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<MenuItem href="https://discord.gg/FCfyBSbCU5">
|
||||
<div>
|
||||
<Trans>Discord</Trans>
|
||||
</div>
|
||||
<MessageCircle opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<MenuItem href={infoLink}>
|
||||
<div>
|
||||
<Trans>Analytics</Trans>
|
||||
</div>
|
||||
<PieChart opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<ToggleMenuItem onClick={() => setMenu('lang')}>
|
||||
<div>
|
||||
<Trans>Language</Trans>
|
||||
</div>
|
||||
<Globe opacity={0.6} size={16} />
|
||||
</ToggleMenuItem>
|
||||
<ToggleMenuItem onClick={() => toggleDarkMode()}>
|
||||
<div>{darkMode ? <Trans>Light Theme</Trans> : <Trans>Dark Theme</Trans>}</div>
|
||||
{darkMode ? <Moon opacity={0.6} size={16} /> : <Sun opacity={0.6} size={16} />}
|
||||
</ToggleMenuItem>
|
||||
{showUNIClaimOption && (
|
||||
<UNIbutton
|
||||
onClick={openClaimModal}
|
||||
padding="8px 16px"
|
||||
width="100%"
|
||||
$borderRadius="12px"
|
||||
mt="0.5rem"
|
||||
>
|
||||
<Trans>Claim UNI</Trans>
|
||||
</UNIbutton>
|
||||
)}
|
||||
</MenuFlyout>
|
||||
)
|
||||
}
|
||||
})()}
|
||||
</StyledMenu>
|
||||
{open &&
|
||||
(() => {
|
||||
switch (menu) {
|
||||
case 'lang':
|
||||
return <LanguageMenu close={() => setMenu('main')} />
|
||||
case 'main':
|
||||
default:
|
||||
return (
|
||||
<MenuFlyout>
|
||||
<MenuItem href="https://uniswap.org/">
|
||||
<div>
|
||||
<Trans>About</Trans>
|
||||
</div>
|
||||
<Info opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<MenuItem href="https://help.uniswap.org/">
|
||||
<div>
|
||||
<Trans>Help Center</Trans>
|
||||
</div>
|
||||
<HelpCircle opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<MenuItem href="https://uniswap.canny.io/feature-requests">
|
||||
<div>
|
||||
<Trans>Request Features</Trans>
|
||||
</div>
|
||||
<Coffee opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<MenuItem href="https://discord.gg/FCfyBSbCU5">
|
||||
<div>
|
||||
<Trans>Discord</Trans>
|
||||
</div>
|
||||
<MessageCircle opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<ToggleMenuItem onClick={() => setMenu('lang')}>
|
||||
<div>
|
||||
<Trans>Language</Trans>
|
||||
</div>
|
||||
<Globe opacity={0.6} size={16} />
|
||||
</ToggleMenuItem>
|
||||
<ToggleMenuItem onClick={() => toggleDarkMode()}>
|
||||
<div>{darkMode ? <Trans>Light Theme</Trans> : <Trans>Dark Theme</Trans>}</div>
|
||||
{darkMode ? <Moon opacity={0.6} size={16} /> : <Sun opacity={0.6} size={16} />}
|
||||
</ToggleMenuItem>
|
||||
<MenuItem href="https://docs.uniswap.org/">
|
||||
<div>
|
||||
<Trans>Docs</Trans>
|
||||
</div>
|
||||
<BookOpen opacity={0.6} size={16} />
|
||||
</MenuItem>
|
||||
<ToggleMenuItem onClick={() => togglePrivacyPolicy()}>
|
||||
<div>
|
||||
<Trans>Legal & Privacy</Trans>
|
||||
</div>
|
||||
<FileText opacity={0.6} size={16} />
|
||||
</ToggleMenuItem>
|
||||
{showUNIClaimOption && (
|
||||
<UNIbutton
|
||||
onClick={openClaimModal}
|
||||
padding="8px 16px"
|
||||
width="100%"
|
||||
$borderRadius="12px"
|
||||
mt="0.5rem"
|
||||
>
|
||||
<Trans>Claim UNI</Trans>
|
||||
</UNIbutton>
|
||||
)}
|
||||
</MenuFlyout>
|
||||
)
|
||||
}
|
||||
})()}
|
||||
</StyledMenu>
|
||||
<PrivacyPolicyModal />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
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, TYPE } from '../../theme'
|
||||
import { CloseIcon, CustomLightSpinner, ThemedText } from '../../theme'
|
||||
import { ExternalLink } from '../../theme/components'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { AutoColumn, ColumnCenter } from '../Column'
|
||||
@@ -32,9 +32,9 @@ export function LoadingView({ children, onDismiss }: { children: any; onDismiss:
|
||||
</ConfirmedIcon>
|
||||
<AutoColumn gap="100px" justify={'center'}>
|
||||
{children}
|
||||
<TYPE.subHeader>
|
||||
<ThemedText.SubHeader>
|
||||
<Trans>Confirm this transaction in your wallet</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</AutoColumn>
|
||||
</ConfirmOrLoadingWrapper>
|
||||
)
|
||||
@@ -68,9 +68,9 @@ export function SubmittedView({
|
||||
href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}
|
||||
style={{ marginLeft: '4px' }}
|
||||
>
|
||||
<TYPE.subHeader>
|
||||
<ThemedText.SubHeader>
|
||||
<Trans>View transaction on Explorer</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</ExternalLink>
|
||||
)}
|
||||
</AutoColumn>
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useAppDispatch } from 'state/hooks'
|
||||
import { resetMintState } from 'state/mint/actions'
|
||||
import { resetMintState as resetMintV3State } from 'state/mint/v3/actions'
|
||||
import styled from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import Row, { RowBetween } from '../Row'
|
||||
import SettingsTab from '../Settings'
|
||||
@@ -136,7 +136,7 @@ export function AddRemoveTabs({
|
||||
>
|
||||
<StyledArrowLeft stroke={theme.text2} />
|
||||
</StyledHistoryLink>
|
||||
<TYPE.mediumHeader
|
||||
<ThemedText.MediumHeader
|
||||
fontWeight={500}
|
||||
fontSize={20}
|
||||
style={{ flex: '1', margin: 'auto', textAlign: children ? 'start' : 'center' }}
|
||||
@@ -148,7 +148,7 @@ export function AddRemoveTabs({
|
||||
) : (
|
||||
<Trans>Remove Liquidity</Trans>
|
||||
)}
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
<Box style={{ marginRight: '.5rem' }}>{children}</Box>
|
||||
<SettingsTab placeholderSlippage={defaultSlippage} />
|
||||
</RowBetween>
|
||||
|
||||
@@ -1,157 +1,93 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import {
|
||||
ARBITRUM_HELP_CENTER_LINK,
|
||||
L2_CHAIN_IDS,
|
||||
OPTIMISM_HELP_CENTER_LINK,
|
||||
SupportedChainId,
|
||||
SupportedL2ChainId,
|
||||
} from 'constants/chains'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { ArrowDownCircle, X } from 'react-feather'
|
||||
import { useArbitrumAlphaAlert, useDarkModeManager, useOptimismAlphaAlert } from 'state/user/hooks'
|
||||
import { useETHBalances } from 'state/wallet/hooks'
|
||||
import styled, { css } from 'styled-components/macro'
|
||||
import { ExternalLink, MEDIA_WIDTHS } from 'theme'
|
||||
import { CHAIN_INFO } from 'constants/chainInfo'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
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'
|
||||
|
||||
export const DesktopTextBreak = styled.div`
|
||||
display: none;
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToMedium}px) {
|
||||
display: block;
|
||||
}
|
||||
`
|
||||
import { AutoRow } from '../Row'
|
||||
|
||||
const L2Icon = styled.img`
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
justify-self: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 16px;
|
||||
`
|
||||
const BetaTag = styled.span<{ color: string }>`
|
||||
align-items: center;
|
||||
background-color: ${({ color }) => color};
|
||||
border-radius: 6px;
|
||||
color: ${({ theme }) => theme.white};
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
height: 28px;
|
||||
justify-content: center;
|
||||
left: -16px;
|
||||
position: absolute;
|
||||
transform: rotate(-15deg);
|
||||
top: -16px;
|
||||
width: 60px;
|
||||
z-index: 1;
|
||||
`
|
||||
const Body = styled.p`
|
||||
font-size: 12px;
|
||||
grid-column: 1 / 3;
|
||||
line-height: 143%;
|
||||
margin: 0;
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
grid-column: 2 / 3;
|
||||
}
|
||||
`
|
||||
export const Controls = styled.div<{ thin?: boolean }>`
|
||||
|
||||
export const Controls = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
${({ thin }) =>
|
||||
thin &&
|
||||
css`
|
||||
margin: auto 32px auto 0;
|
||||
`}
|
||||
`
|
||||
const CloseIcon = styled(X)`
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
padding: 0 20px 20px 20px;
|
||||
`
|
||||
|
||||
const BodyText = styled.div`
|
||||
align-items: center;
|
||||
display: grid;
|
||||
grid-gap: 4px;
|
||||
grid-template-columns: 40px 4fr;
|
||||
grid-template-rows: auto auto;
|
||||
margin: 20px 16px;
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
grid-template-columns: 42px 4fr;
|
||||
grid-gap: 8px;
|
||||
}
|
||||
`
|
||||
const LearnMoreLink = styled(ExternalLink)<{ thin?: boolean }>`
|
||||
align-items: center;
|
||||
background-color: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||
border-radius: 8px;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
color: ${({ color }) => color};
|
||||
display: flex;
|
||||
font-size: 16px;
|
||||
height: 44px;
|
||||
justify-content: space-between;
|
||||
margin: 0 0 20px 0;
|
||||
padding: 12px 16px;
|
||||
text-decoration: none;
|
||||
width: auto;
|
||||
:hover,
|
||||
:focus,
|
||||
:active {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
transition: background-color 150ms ease-in-out;
|
||||
${({ thin }) =>
|
||||
thin &&
|
||||
css`
|
||||
font-size: 14px;
|
||||
margin: auto;
|
||||
width: 112px;
|
||||
`}
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
margin: 8px;
|
||||
font-size: 14px;
|
||||
`
|
||||
const RootWrapper = styled.div`
|
||||
position: relative;
|
||||
margin-top: 16px;
|
||||
`
|
||||
export const ArbitrumWrapperBackgroundDarkMode = css`
|
||||
background: radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),
|
||||
radial-gradient(75% 75% at 0% 0%, rgba(150, 190, 220, 0.3) 0%, rgba(33, 114, 229, 0.3) 100%), hsla(0, 0%, 100%, 0.1);
|
||||
`
|
||||
export const ArbitrumWrapperBackgroundLightMode = css`
|
||||
background: radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),
|
||||
radial-gradient(circle at top left, hsla(206, 50%, 75%, 0.01), hsla(215, 79%, 51%, 0.12)), hsla(0, 0%, 100%, 0.1);
|
||||
`
|
||||
export const OptimismWrapperBackgroundDarkMode = css`
|
||||
background: radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.2) 0%, rgba(255, 255, 255, 0.1) 100%),
|
||||
radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.5) 0%, rgba(235, 0, 255, 0.345) 96%);
|
||||
`
|
||||
export const OptimismWrapperBackgroundLightMode = css`
|
||||
background: radial-gradient(92% 105% at 50% 7%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.03) 100%),
|
||||
radial-gradient(100% 97% at 0% 12%, rgba(235, 0, 255, 0.1) 0%, rgba(243, 19, 19, 0.1) 100%), hsla(0, 0%, 100%, 0.5);
|
||||
`
|
||||
const ContentWrapper = styled.div<{ chainId: SupportedChainId; darkMode: boolean; logoUrl: string; thin?: boolean }>`
|
||||
${({ chainId, darkMode }) =>
|
||||
[SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId)
|
||||
? darkMode
|
||||
? OptimismWrapperBackgroundDarkMode
|
||||
: OptimismWrapperBackgroundLightMode
|
||||
: darkMode
|
||||
? ArbitrumWrapperBackgroundDarkMode
|
||||
: ArbitrumWrapperBackgroundLightMode};
|
||||
|
||||
const SHOULD_SHOW_ALERT = {
|
||||
[SupportedChainId.OPTIMISM]: true,
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: true,
|
||||
[SupportedChainId.ARBITRUM_ONE]: true,
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: true,
|
||||
[SupportedChainId.POLYGON]: true,
|
||||
[SupportedChainId.POLYGON_MUMBAI]: true,
|
||||
}
|
||||
|
||||
type NetworkAlertChains = keyof typeof SHOULD_SHOW_ALERT
|
||||
|
||||
const BG_COLORS_BY_DARK_MODE_AND_CHAIN_ID: {
|
||||
[darkMode in 'dark' | 'light']: { [chainId in NetworkAlertChains]: string }
|
||||
} = {
|
||||
dark: {
|
||||
[SupportedChainId.POLYGON]:
|
||||
'radial-gradient(100% 93.36% at 0% 6.64%, rgba(160, 108, 247, 0.1) 0%, rgba(82, 32, 166, 0.1) 100%)',
|
||||
[SupportedChainId.POLYGON_MUMBAI]:
|
||||
'radial-gradient(100% 93.36% at 0% 6.64%, rgba(160, 108, 247, 0.1) 0%, rgba(82, 32, 166, 0.1) 100%)',
|
||||
[SupportedChainId.OPTIMISM]:
|
||||
'radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.01) 0%, rgba(255, 255, 255, 0.04) 100%),radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.01) 0%, rgba(235, 0, 255, 0.01) 96%)',
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]:
|
||||
'radial-gradient(948% 292% at 42% 0%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.04) 100%),radial-gradient(98% 96% at 2% 0%, rgba(255, 39, 39, 0.04) 0%, rgba(235, 0, 255, 0.01 96%)',
|
||||
[SupportedChainId.ARBITRUM_ONE]:
|
||||
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.01) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(75% 75% at 0% 0%, rgba(150, 190, 220, 0.05) 0%, rgba(33, 114, 229, 0.05) 100%), hsla(0, 0%, 100%, 0.05)',
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]:
|
||||
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.05) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(75% 75% at 0% 0%, rgba(150, 190, 220, 0.05) 0%, rgba(33, 114, 229, 0.1) 100%), hsla(0, 0%, 100%, 0.05)',
|
||||
},
|
||||
light: {
|
||||
[SupportedChainId.POLYGON]:
|
||||
'radial-gradient(182.71% 205.59% at 2.81% 7.69%, rgba(130, 71, 229, 0.2) 0%, rgba(167, 202, 255, 0.2) 100%)',
|
||||
[SupportedChainId.POLYGON_MUMBAI]:
|
||||
'radial-gradient(182.71% 205.59% at 2.81% 7.69%, rgba(130, 71, 229, 0.2) 0%, rgba(167, 202, 255, 0.2) 100%)',
|
||||
[SupportedChainId.OPTIMISM]:
|
||||
'radial-gradient(92% 105% at 50% 7%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.03) 100%),radial-gradient(100% 97% at 0% 12%, rgba(235, 0, 255, 0.1) 0%, rgba(243, 19, 19, 0.1) 100%), hsla(0, 0%, 100%, 0.1)',
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]:
|
||||
'radial-gradient(92% 105% at 50% 7%, rgba(255, 58, 212, 0.04) 0%, rgba(255, 255, 255, 0.03) 100%),radial-gradient(100% 97% at 0% 12%, rgba(235, 0, 255, 0.1) 0%, rgba(243, 19, 19, 0.1) 100%), hsla(0, 0%, 100%, 0.1)',
|
||||
[SupportedChainId.ARBITRUM_ONE]:
|
||||
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(circle at top left, hsla(206, 50%, 75%, 0.01), hsla(215, 79%, 51%, 0.12)), hsla(0, 0%, 100%, 0.1)',
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]:
|
||||
'radial-gradient(285% 8200% at 30% 50%, rgba(40, 160, 240, 0.1) 0%, rgba(219, 255, 0, 0) 100%),radial-gradient(circle at top left, hsla(206, 50%, 75%, 0.01), hsla(215, 79%, 51%, 0.12)), hsla(0, 0%, 100%, 0.1)',
|
||||
},
|
||||
}
|
||||
|
||||
const ContentWrapper = styled.div<{ chainId: NetworkAlertChains; darkMode: boolean; logoUrl: string }>`
|
||||
background: ${({ chainId, darkMode }) => BG_COLORS_BY_DARK_MODE_AND_CHAIN_ID[darkMode ? 'dark' : 'light'][chainId]};
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 480px;
|
||||
min-height: 174px;
|
||||
flex-direction: row;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
${({ thin }) =>
|
||||
thin &&
|
||||
css`
|
||||
flex-direction: row;
|
||||
max-width: max-content;
|
||||
min-height: min-content;
|
||||
`}
|
||||
|
||||
:before {
|
||||
background-image: url(${({ logoUrl }) => logoUrl});
|
||||
background-repeat: no-repeat;
|
||||
@@ -165,116 +101,73 @@ const ContentWrapper = styled.div<{ chainId: SupportedChainId; darkMode: boolean
|
||||
z-index: -1;
|
||||
}
|
||||
`
|
||||
const Header = styled.h2<{ thin?: boolean }>`
|
||||
const Header = styled.h2`
|
||||
font-weight: 600;
|
||||
font-size: 20px;
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
padding-right: 30px;
|
||||
display: ${({ thin }) => (thin ? 'none' : 'block')};
|
||||
`
|
||||
const LinkOutCircle = styled(ArrowDownCircle)`
|
||||
margin-left: 12px;
|
||||
transform: rotate(230deg);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
`
|
||||
const LinkOutToBridge = styled(ExternalLink)<{ thin?: boolean }>`
|
||||
|
||||
const LinkOutToBridge = styled(ExternalLink)`
|
||||
align-items: center;
|
||||
background-color: black;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
display: flex;
|
||||
font-size: 16px;
|
||||
height: 44px;
|
||||
justify-content: space-between;
|
||||
margin: 0 12px 20px 18px;
|
||||
padding: 12px 16px;
|
||||
text-decoration: none;
|
||||
width: auto;
|
||||
:hover,
|
||||
:focus,
|
||||
:active {
|
||||
background-color: black;
|
||||
}
|
||||
${({ thin }) =>
|
||||
thin &&
|
||||
css`
|
||||
font-size: 14px;
|
||||
margin: auto 10px;
|
||||
width: 168px;
|
||||
`}
|
||||
padding: 6px 8px;
|
||||
margin-right: 12px;
|
||||
text-decoration: none !important;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
interface NetworkAlertProps {
|
||||
thin?: boolean
|
||||
const StyledArrowUpRight = styled(ArrowUpRight)`
|
||||
margin-left: 12px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
`
|
||||
|
||||
const TEXT_COLORS: { [chainId in NetworkAlertChains]: string } = {
|
||||
[SupportedChainId.POLYGON]: 'rgba(130, 71, 229)',
|
||||
[SupportedChainId.POLYGON_MUMBAI]: 'rgba(130, 71, 229)',
|
||||
[SupportedChainId.OPTIMISM]: '#ff3856',
|
||||
[SupportedChainId.OPTIMISTIC_KOVAN]: '#ff3856',
|
||||
[SupportedChainId.ARBITRUM_ONE]: '#0490ed',
|
||||
[SupportedChainId.ARBITRUM_RINKEBY]: '#0490ed',
|
||||
}
|
||||
|
||||
export function NetworkAlert(props: NetworkAlertProps) {
|
||||
const { account, chainId } = useActiveWeb3React()
|
||||
function shouldShowAlert(chainId: number | undefined): chainId is NetworkAlertChains {
|
||||
return Boolean(chainId && SHOULD_SHOW_ALERT[chainId as unknown as NetworkAlertChains])
|
||||
}
|
||||
|
||||
export function NetworkAlert() {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const [darkMode] = useDarkModeManager()
|
||||
const [arbitrumAlphaAcknowledged, setArbitrumAlphaAcknowledged] = useArbitrumAlphaAlert()
|
||||
const [optimismAlphaAcknowledged, setOptimismAlphaAcknowledged] = useOptimismAlphaAlert()
|
||||
const [locallyDismissed, setLocallyDimissed] = useState(false)
|
||||
const userEthBalance = useETHBalances(account ? [account] : [])?.[account ?? '']
|
||||
|
||||
const dismiss = useCallback(() => {
|
||||
if (userEthBalance?.greaterThan(0)) {
|
||||
switch (chainId) {
|
||||
case SupportedChainId.OPTIMISM:
|
||||
setOptimismAlphaAcknowledged(true)
|
||||
break
|
||||
case SupportedChainId.ARBITRUM_ONE:
|
||||
setArbitrumAlphaAcknowledged(true)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
setLocallyDimissed(true)
|
||||
}
|
||||
}, [chainId, setArbitrumAlphaAcknowledged, setOptimismAlphaAcknowledged, userEthBalance])
|
||||
|
||||
const onOptimismAndOptimismAcknowledged = SupportedChainId.OPTIMISM === chainId && optimismAlphaAcknowledged
|
||||
const onArbitrumAndArbitrumAcknowledged = SupportedChainId.ARBITRUM_ONE === chainId && arbitrumAlphaAcknowledged
|
||||
if (
|
||||
!chainId ||
|
||||
!L2_CHAIN_IDS.includes(chainId) ||
|
||||
onArbitrumAndArbitrumAcknowledged ||
|
||||
onOptimismAndOptimismAcknowledged ||
|
||||
locallyDismissed
|
||||
) {
|
||||
if (!shouldShowAlert(chainId)) {
|
||||
return null
|
||||
}
|
||||
const info = CHAIN_INFO[chainId as SupportedL2ChainId]
|
||||
const isOptimism = [SupportedChainId.OPTIMISM, SupportedChainId.OPTIMISTIC_KOVAN].includes(chainId)
|
||||
const depositUrl = isOptimism ? `${info.bridge}?chainId=1` : info.bridge
|
||||
const helpCenterLink = isOptimism ? OPTIMISM_HELP_CENTER_LINK : ARBITRUM_HELP_CENTER_LINK
|
||||
const showCloseIcon = Boolean(userEthBalance?.greaterThan(0) && !props.thin)
|
||||
return (
|
||||
|
||||
const { label, logoUrl, bridge } = CHAIN_INFO[chainId]
|
||||
const textColor = TEXT_COLORS[chainId]
|
||||
|
||||
return bridge ? (
|
||||
<RootWrapper>
|
||||
<BetaTag color={isOptimism ? '#ff0420' : '#0490ed'}>Beta</BetaTag>
|
||||
<ContentWrapper chainId={chainId} darkMode={darkMode} logoUrl={info.logoUrl} thin={props.thin}>
|
||||
{showCloseIcon && <CloseIcon onClick={dismiss} />}
|
||||
<BodyText>
|
||||
<L2Icon src={info.logoUrl} />
|
||||
<Header thin={props.thin}>
|
||||
<Trans>Uniswap on {info.label}</Trans>
|
||||
</Header>
|
||||
<Body>
|
||||
<Trans>
|
||||
To starting trading on {info.label}, first bridge your assets from L1 to L2. Please treat this as a beta
|
||||
release and learn about the risks before using {info.label}.
|
||||
</Trans>
|
||||
</Body>
|
||||
</BodyText>
|
||||
<Controls thin={props.thin}>
|
||||
<LinkOutToBridge href={depositUrl} thin={props.thin}>
|
||||
<Trans>Deposit Assets</Trans>
|
||||
<LinkOutCircle />
|
||||
</LinkOutToBridge>
|
||||
<LearnMoreLink href={helpCenterLink} thin={props.thin}>
|
||||
<Trans>Learn More</Trans>
|
||||
</LearnMoreLink>
|
||||
</Controls>
|
||||
<ContentWrapper chainId={chainId} darkMode={darkMode} logoUrl={logoUrl}>
|
||||
<LinkOutToBridge href={bridge}>
|
||||
<BodyText color={textColor}>
|
||||
<L2Icon src={logoUrl} />
|
||||
<AutoRow>
|
||||
<Header>
|
||||
<Trans>{label} token bridge</Trans>
|
||||
</Header>
|
||||
<HideSmall>
|
||||
<Trans>Deposit tokens to the {label} network.</Trans>
|
||||
</HideSmall>
|
||||
</AutoRow>
|
||||
</BodyText>
|
||||
<StyledArrowUpRight color={textColor} />
|
||||
</LinkOutToBridge>
|
||||
</ContentWrapper>
|
||||
</RootWrapper>
|
||||
)
|
||||
) : null
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ const StyledInput = styled.input<{ error?: boolean; fontSize?: string; align?: s
|
||||
border: none;
|
||||
flex: 1 1 auto;
|
||||
background-color: ${({ theme }) => theme.bg1};
|
||||
font-size: ${({ fontSize }) => fontSize ?? '24px'};
|
||||
font-size: ${({ fontSize }) => fontSize ?? '28px'};
|
||||
text-align: ${({ align }) => align && align};
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Options, Placement } from '@popperjs/core'
|
||||
import Portal from '@reach/portal'
|
||||
import useInterval from 'lib/hooks/useInterval'
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { usePopper } from 'react-popper'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import useInterval from '../../hooks/useInterval'
|
||||
|
||||
const PopoverContainer = styled.div<{ show: boolean }>`
|
||||
z-index: 9999;
|
||||
visibility: ${(props) => (props.show ? 'visible' : 'hidden')};
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
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 { ButtonPrimary } from '../../components/Button'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { ApplicationModal } from '../../state/application/actions'
|
||||
import {
|
||||
useModalOpen,
|
||||
useShowClaimPopup,
|
||||
useToggleSelfClaimModal,
|
||||
useToggleShowClaimPopup,
|
||||
} from '../../state/application/hooks'
|
||||
import { ApplicationModal } from '../../state/application/reducer'
|
||||
import { useUserHasAvailableClaim, useUserUnclaimedAmount } from '../../state/claim/hooks'
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { ButtonPrimary } from '../Button'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { CardBGImage, CardNoise } from '../earn/styled'
|
||||
|
||||
@@ -98,10 +98,10 @@ export default function ClaimPopup() {
|
||||
<StyledClose stroke="white" onClick={toggleShowClaimPopup} />
|
||||
<AutoColumn style={{ padding: '2rem 0', zIndex: 10 }} justify="center">
|
||||
<UniToken width="48px" src={tokenLogo} />{' '}
|
||||
<TYPE.white style={{ marginTop: '1rem' }} fontSize={36} fontWeight={600}>
|
||||
<ThemedText.White style={{ marginTop: '1rem' }} fontSize={36} fontWeight={600}>
|
||||
{unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')} UNI
|
||||
</TYPE.white>
|
||||
<TYPE.white style={{ paddingTop: '1.25rem', textAlign: 'center' }} fontWeight={600} color="white">
|
||||
</ThemedText.White>
|
||||
<ThemedText.White style={{ paddingTop: '1.25rem', textAlign: 'center' }} fontWeight={600} color="white">
|
||||
<span role="img" aria-label="party">
|
||||
🎉
|
||||
</span>{' '}
|
||||
@@ -109,12 +109,12 @@ export default function ClaimPopup() {
|
||||
<span role="img" aria-label="party">
|
||||
🎉
|
||||
</span>
|
||||
</TYPE.white>
|
||||
<TYPE.subHeader style={{ paddingTop: '0.5rem', textAlign: 'center' }} color="white">
|
||||
</ThemedText.White>
|
||||
<ThemedText.SubHeader style={{ paddingTop: '0.5rem', textAlign: 'center' }} color="white">
|
||||
<Trans>
|
||||
Thanks for being part of the Uniswap community <Heart size={12} />
|
||||
</Trans>
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</AutoColumn>
|
||||
<AutoColumn style={{ zIndex: 10 }} justify="center">
|
||||
<ButtonPrimary padding="8px" $borderRadius="8px" width={'fit-content'} onClick={handleToggleSelfClaimModal}>
|
||||
|
||||
35
src/components/Popups/FailedNetworkSwitchPopup.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
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 { ThemedText } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
import { AutoRow } from '../Row'
|
||||
|
||||
const RowNoFlex = styled(AutoRow)`
|
||||
flex-wrap: nowrap;
|
||||
`
|
||||
|
||||
export default function FailedNetworkSwitchPopup({ chainId }: { chainId: SupportedChainId }) {
|
||||
const chainInfo = CHAIN_INFO[chainId]
|
||||
const theme = useContext(ThemeContext)
|
||||
|
||||
return (
|
||||
<RowNoFlex>
|
||||
<div style={{ paddingRight: 16 }}>
|
||||
<AlertCircle color={theme.red1} size={24} />
|
||||
</div>
|
||||
<AutoColumn gap="8px">
|
||||
<ThemedText.Body fontWeight={500}>
|
||||
<Trans>
|
||||
Failed to switch networks from the Uniswap Interface. In order to use Uniswap on {chainInfo.label}, you must
|
||||
change the network in your wallet.
|
||||
</Trans>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
</RowNoFlex>
|
||||
)
|
||||
}
|
||||
@@ -4,8 +4,9 @@ import { animated } from 'react-spring'
|
||||
import { useSpring } from 'react-spring/web'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import { PopupContent } from '../../state/application/actions'
|
||||
import { useRemovePopup } from '../../state/application/hooks'
|
||||
import { PopupContent } from '../../state/application/reducer'
|
||||
import FailedNetworkSwitchPopup from './FailedNetworkSwitchPopup'
|
||||
import TransactionPopup from './TransactionPopup'
|
||||
|
||||
const StyledClose = styled(X)`
|
||||
@@ -77,6 +78,8 @@ export default function PopupItem({
|
||||
txn: { hash },
|
||||
} = content
|
||||
popupContent = <TransactionPopup hash={hash} />
|
||||
} else if ('failedSwitchNetwork' in content) {
|
||||
popupContent = <FailedNetworkSwitchPopup chainId={content.failedSwitchNetwork} />
|
||||
}
|
||||
|
||||
const faderStyle = useSpring({
|
||||
|
||||
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,11 +1,11 @@
|
||||
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 { TYPE } from '../../theme'
|
||||
import { ExternalLink } from '../../theme/components'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { ExternalLink } from '../../theme'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { TransactionSummary } from '../AccountDetails/TransactionSummary'
|
||||
import { AutoColumn } from '../Column'
|
||||
@@ -30,9 +30,9 @@ export default function TransactionPopup({ hash }: { hash: string }) {
|
||||
{success ? <CheckCircle color={theme.green1} size={24} /> : <AlertCircle color={theme.red1} size={24} />}
|
||||
</div>
|
||||
<AutoColumn gap="8px">
|
||||
<TYPE.body fontWeight={500}>
|
||||
<ThemedText.Body fontWeight={500}>
|
||||
<TransactionSummary info={tx.info} />
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
{chainId && (
|
||||
<ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}>
|
||||
View on Explorer
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
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'
|
||||
|
||||
import { useActivePopups } from '../../state/application/hooks'
|
||||
import { useURLWarningVisible } from '../../state/user/hooks'
|
||||
import { useShowSurveyPopup, useURLWarningVisible } from '../../state/user/hooks'
|
||||
import { AutoColumn } from '../Column'
|
||||
import ClaimPopup from './ClaimPopup'
|
||||
import PopupItem from './PopupItem'
|
||||
import SurveyPopup from './SurveyPopup'
|
||||
|
||||
const MobilePopupWrapper = styled.div<{ height: string | number }>`
|
||||
position: relative;
|
||||
@@ -59,6 +60,9 @@ export default function Popups() {
|
||||
// get all popups
|
||||
const activePopups = useActivePopups()
|
||||
|
||||
// show survey popup if user has not closed
|
||||
const [showSurveyPopup] = useShowSurveyPopup()
|
||||
|
||||
const urlWarningActive = useURLWarningVisible()
|
||||
|
||||
// need extra padding if network is not L1 Ethereum
|
||||
@@ -69,12 +73,14 @@ export default function Popups() {
|
||||
<>
|
||||
<FixedPopupColumn gap="20px" extraPadding={urlWarningActive} xlPadding={isNotOnMainnet}>
|
||||
<ClaimPopup />
|
||||
<SurveyPopup />
|
||||
{activePopups.map((item) => (
|
||||
<PopupItem key={item.key} content={item.content} popKey={item.key} removeAfterMs={item.removeAfterMs} />
|
||||
))}
|
||||
</FixedPopupColumn>
|
||||
<MobilePopupWrapper height={activePopups?.length > 0 ? 'fit-content' : 0}>
|
||||
<MobilePopupWrapper height={activePopups?.length > 0 || showSurveyPopup ? 'fit-content' : 0}>
|
||||
<MobilePopupInner>
|
||||
<SurveyPopup />
|
||||
{activePopups // reverse so new items up front
|
||||
.slice(0)
|
||||
.reverse()
|
||||
|
||||
@@ -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,9 +13,8 @@ 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, TYPE } from '../../theme'
|
||||
import { ExternalLink, ThemedText } from '../../theme'
|
||||
import { currencyId } from '../../utils/currencyId'
|
||||
import { unwrappedToken } from '../../utils/unwrappedToken'
|
||||
import { ButtonEmpty, ButtonPrimary, ButtonSecondary } from '../Button'
|
||||
@@ -142,7 +142,7 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
|
||||
</GreyCard>
|
||||
) : (
|
||||
<LightCard>
|
||||
<TYPE.subHeader style={{ textAlign: 'center' }}>
|
||||
<ThemedText.SubHeader style={{ textAlign: 'center' }}>
|
||||
<span role="img" aria-label="wizard-icon">
|
||||
⭐️
|
||||
</span>{' '}
|
||||
@@ -150,7 +150,7 @@ export function MinimalPositionCard({ pair, showUnwrapped = false, border }: Pos
|
||||
By adding liquidity you'll earn 0.3% of all trades on this pair proportional to your share of the
|
||||
pool. Fees are added to the pool, accrue in real time and can be claimed by withdrawing your liquidity.
|
||||
</Trans>{' '}
|
||||
</TYPE.subHeader>
|
||||
</ThemedText.SubHeader>
|
||||
</LightCard>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { ButtonText } from 'components/Button'
|
||||
import PositionListItem from 'components/PositionListItem'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
@@ -14,9 +15,7 @@ const DesktopHeader = styled.div`
|
||||
@media screen and (min-width: ${MEDIA_WIDTHS.upToSmall}px) {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
justify-content: space-between;
|
||||
& > div:last-child {
|
||||
text-align: right;
|
||||
margin-right: 12px;
|
||||
@@ -36,9 +35,15 @@ const MobileHeader = styled.div`
|
||||
|
||||
type PositionListProps = React.PropsWithChildren<{
|
||||
positions: PositionDetails[]
|
||||
setUserHideClosedPositions: any
|
||||
userHideClosedPositions: boolean
|
||||
}>
|
||||
|
||||
export default function PositionList({ positions }: PositionListProps) {
|
||||
export default function PositionList({
|
||||
positions,
|
||||
setUserHideClosedPositions,
|
||||
userHideClosedPositions,
|
||||
}: PositionListProps) {
|
||||
return (
|
||||
<>
|
||||
<DesktopHeader>
|
||||
@@ -46,9 +51,9 @@ export default function PositionList({ positions }: PositionListProps) {
|
||||
<Trans>Your positions</Trans>
|
||||
{positions && ' (' + positions.length + ')'}
|
||||
</div>
|
||||
<div>
|
||||
<Trans>Status</Trans>
|
||||
</div>
|
||||
<ButtonText style={{ opacity: 0.6 }} onClick={() => setUserHideClosedPositions(!userHideClosedPositions)}>
|
||||
<Trans>Hide closed positions</Trans>
|
||||
</ButtonText>
|
||||
</DesktopHeader>
|
||||
<MobileHeader>
|
||||
<Trans>Your positions</Trans>
|
||||
|
||||
@@ -19,7 +19,7 @@ import { PositionDetails } from 'types/position'
|
||||
import { formatTickPrice } from 'utils/formatTickPrice'
|
||||
import { unwrappedToken } from 'utils/unwrappedToken'
|
||||
|
||||
import { DAI, USDC, USDT, WBTC, WETH9_EXTENDED } from '../../constants/tokens'
|
||||
import { DAI, USDC, USDT, WBTC, WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens'
|
||||
|
||||
const LinkRow = styled(Link)`
|
||||
align-items: center;
|
||||
@@ -156,7 +156,7 @@ export function getPriceOrderingFromPositionForUI(position?: Position): {
|
||||
}
|
||||
|
||||
// if token1 is an ETH-/BTC-stable asset, set it as the base token
|
||||
const bases = [...Object.values(WETH9_EXTENDED), WBTC]
|
||||
const bases = [...Object.values(WRAPPED_NATIVE_CURRENCY), WBTC]
|
||||
if (bases.some((base) => base.equals(token1))) {
|
||||
return {
|
||||
priceLower: position.token0PriceUpper.invert(),
|
||||
|
||||
@@ -13,7 +13,7 @@ import JSBI from 'jsbi'
|
||||
import { ReactNode, useCallback, useContext, useState } from 'react'
|
||||
import { Bound } from 'state/mint/v3/actions'
|
||||
import { ThemeContext } from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
import { formatTickPrice } from 'utils/formatTickPrice'
|
||||
import { unwrappedToken } from 'utils/unwrappedToken'
|
||||
|
||||
@@ -70,9 +70,9 @@ export const PositionPreview = ({
|
||||
size={24}
|
||||
margin={true}
|
||||
/>
|
||||
<TYPE.label ml="10px" fontSize="24px">
|
||||
<ThemedText.Label ml="10px" fontSize="24px">
|
||||
{currency0?.symbol} / {currency1?.symbol}
|
||||
</TYPE.label>
|
||||
</ThemedText.Label>
|
||||
</RowFixed>
|
||||
<RangeBadge removed={removed} inRange={inRange} />
|
||||
</RowBetween>
|
||||
@@ -82,36 +82,36 @@ export const PositionPreview = ({
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<CurrencyLogo currency={currency0} />
|
||||
<TYPE.label ml="8px">{currency0?.symbol}</TYPE.label>
|
||||
<ThemedText.Label ml="8px">{currency0?.symbol}</ThemedText.Label>
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
<TYPE.label mr="8px">{position.amount0.toSignificant(4)}</TYPE.label>
|
||||
<ThemedText.Label mr="8px">{position.amount0.toSignificant(4)}</ThemedText.Label>
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<CurrencyLogo currency={currency1} />
|
||||
<TYPE.label ml="8px">{currency1?.symbol}</TYPE.label>
|
||||
<ThemedText.Label ml="8px">{currency1?.symbol}</ThemedText.Label>
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
<TYPE.label mr="8px">{position.amount1.toSignificant(4)}</TYPE.label>
|
||||
<ThemedText.Label mr="8px">{position.amount1.toSignificant(4)}</ThemedText.Label>
|
||||
</RowFixed>
|
||||
</RowBetween>
|
||||
<Break />
|
||||
<RowBetween>
|
||||
<TYPE.label>
|
||||
<ThemedText.Label>
|
||||
<Trans>Fee Tier</Trans>
|
||||
</TYPE.label>
|
||||
<TYPE.label>
|
||||
</ThemedText.Label>
|
||||
<ThemedText.Label>
|
||||
<Trans>{position?.pool?.fee / 10000}%</Trans>
|
||||
</TYPE.label>
|
||||
</ThemedText.Label>
|
||||
</RowBetween>
|
||||
</AutoColumn>
|
||||
</LightCard>
|
||||
|
||||
<AutoColumn gap="md">
|
||||
<RowBetween>
|
||||
{title ? <TYPE.main>{title}</TYPE.main> : <div />}
|
||||
{title ? <ThemedText.Main>{title}</ThemedText.Main> : <div />}
|
||||
<RateToggle
|
||||
currencyA={sorted ? currency0 : currency1}
|
||||
currencyB={sorted ? currency1 : currency0}
|
||||
@@ -122,57 +122,57 @@ export const PositionPreview = ({
|
||||
<RowBetween>
|
||||
<LightCard width="48%" padding="8px">
|
||||
<AutoColumn gap="4px" justify="center">
|
||||
<TYPE.main fontSize="12px">
|
||||
<ThemedText.Main fontSize="12px">
|
||||
<Trans>Min Price</Trans>
|
||||
</TYPE.main>
|
||||
<TYPE.mediumHeader textAlign="center">{`${formatTickPrice(
|
||||
</ThemedText.Main>
|
||||
<ThemedText.MediumHeader textAlign="center">{`${formatTickPrice(
|
||||
priceLower,
|
||||
ticksAtLimit,
|
||||
Bound.LOWER
|
||||
)}`}</TYPE.mediumHeader>
|
||||
<TYPE.main textAlign="center" fontSize="12px">
|
||||
)}`}</ThemedText.MediumHeader>
|
||||
<ThemedText.Main textAlign="center" fontSize="12px">
|
||||
<Trans>
|
||||
{quoteCurrency.symbol} per {baseCurrency.symbol}
|
||||
</Trans>
|
||||
</TYPE.main>
|
||||
<TYPE.small textAlign="center" color={theme.text3} style={{ marginTop: '4px' }}>
|
||||
</ThemedText.Main>
|
||||
<ThemedText.Small textAlign="center" color={theme.text3} style={{ marginTop: '4px' }}>
|
||||
<Trans>Your position will be 100% composed of {baseCurrency?.symbol} at this price</Trans>
|
||||
</TYPE.small>
|
||||
</ThemedText.Small>
|
||||
</AutoColumn>
|
||||
</LightCard>
|
||||
|
||||
<LightCard width="48%" padding="8px">
|
||||
<AutoColumn gap="4px" justify="center">
|
||||
<TYPE.main fontSize="12px">
|
||||
<ThemedText.Main fontSize="12px">
|
||||
<Trans>Max Price</Trans>
|
||||
</TYPE.main>
|
||||
<TYPE.mediumHeader textAlign="center">{`${formatTickPrice(
|
||||
</ThemedText.Main>
|
||||
<ThemedText.MediumHeader textAlign="center">{`${formatTickPrice(
|
||||
priceUpper,
|
||||
ticksAtLimit,
|
||||
Bound.UPPER
|
||||
)}`}</TYPE.mediumHeader>
|
||||
<TYPE.main textAlign="center" fontSize="12px">
|
||||
)}`}</ThemedText.MediumHeader>
|
||||
<ThemedText.Main textAlign="center" fontSize="12px">
|
||||
<Trans>
|
||||
{quoteCurrency.symbol} per {baseCurrency.symbol}
|
||||
</Trans>
|
||||
</TYPE.main>
|
||||
<TYPE.small textAlign="center" color={theme.text3} style={{ marginTop: '4px' }}>
|
||||
</ThemedText.Main>
|
||||
<ThemedText.Small textAlign="center" color={theme.text3} style={{ marginTop: '4px' }}>
|
||||
<Trans>Your position will be 100% composed of {quoteCurrency?.symbol} at this price</Trans>
|
||||
</TYPE.small>
|
||||
</ThemedText.Small>
|
||||
</AutoColumn>
|
||||
</LightCard>
|
||||
</RowBetween>
|
||||
<LightCard padding="12px ">
|
||||
<AutoColumn gap="4px" justify="center">
|
||||
<TYPE.main fontSize="12px">
|
||||
<ThemedText.Main fontSize="12px">
|
||||
<Trans>Current price</Trans>
|
||||
</TYPE.main>
|
||||
<TYPE.mediumHeader>{`${price.toSignificant(5)} `}</TYPE.mediumHeader>
|
||||
<TYPE.main textAlign="center" fontSize="12px">
|
||||
</ThemedText.Main>
|
||||
<ThemedText.MediumHeader>{`${price.toSignificant(5)} `}</ThemedText.MediumHeader>
|
||||
<ThemedText.Main textAlign="center" fontSize="12px">
|
||||
<Trans>
|
||||
{quoteCurrency.symbol} per {baseCurrency.symbol}
|
||||
</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</AutoColumn>
|
||||
</LightCard>
|
||||
</AutoColumn>
|
||||
|
||||
179
src/components/PrivacyPolicy/index.tsx
Normal file
@@ -0,0 +1,179 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import Card, { DarkGreyCard } from 'components/Card'
|
||||
import Row, { AutoRow, RowBetween } from 'components/Row'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { ArrowDown, Info, X } from 'react-feather'
|
||||
import ReactGA from 'react-ga'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ExternalLink, ThemedText } from 'theme'
|
||||
import { isMobile } from 'utils/userAgent'
|
||||
|
||||
import { useModalOpen, useTogglePrivacyPolicy } from '../../state/application/hooks'
|
||||
import { ApplicationModal } from '../../state/application/reducer'
|
||||
import { AutoColumn } from '../Column'
|
||||
import Modal from '../Modal'
|
||||
|
||||
const Wrapper = styled.div`
|
||||
max-height: 70vh;
|
||||
overflow: auto;
|
||||
padding: 0 1rem;
|
||||
`
|
||||
|
||||
const StyledExternalCard = styled(Card)`
|
||||
background-color: ${({ theme }) => theme.primary5};
|
||||
padding: 0.5rem;
|
||||
width: 100%;
|
||||
|
||||
:hover,
|
||||
:focus,
|
||||
:active {
|
||||
background-color: ${({ theme }) => theme.primary4};
|
||||
}
|
||||
`
|
||||
|
||||
const HoverText = styled.div`
|
||||
text-decoration: none;
|
||||
color: ${({ theme }) => theme.text1};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`
|
||||
|
||||
const StyledLinkOut = styled(ArrowDown)`
|
||||
transform: rotate(230deg);
|
||||
`
|
||||
|
||||
const EXTERNAL_APIS = [
|
||||
{
|
||||
name: 'Auto Router',
|
||||
description: <Trans>The app fetches the optimal trade route from a Uniswap Labs server.</Trans>,
|
||||
},
|
||||
{
|
||||
name: 'Infura',
|
||||
description: <Trans>The app fetches on-chain data and constructs contract calls with an Infura API.</Trans>,
|
||||
},
|
||||
{
|
||||
name: 'TRM Labs',
|
||||
description: (
|
||||
<>
|
||||
<Trans>
|
||||
The app securely collects your wallet address and shares it with TRM Labs Inc. for risk and compliance
|
||||
reasons.
|
||||
</Trans>{' '}
|
||||
<ExternalLink href="https://help.uniswap.org/en/articles/5675203-terms-of-service-faq">
|
||||
<Trans>Learn more</Trans>
|
||||
</ExternalLink>
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: 'Google Analytics',
|
||||
description: <Trans>The app logs anonymized usage statistics in order to improve over time.</Trans>,
|
||||
},
|
||||
{
|
||||
name: 'The Graph',
|
||||
description: <Trans>The app fetches blockchain data from The Graph’s hosted service.</Trans>,
|
||||
},
|
||||
]
|
||||
|
||||
export function PrivacyPolicyModal() {
|
||||
const node = useRef<HTMLDivElement>()
|
||||
const open = useModalOpen(ApplicationModal.PRIVACY_POLICY)
|
||||
const toggle = useTogglePrivacyPolicy()
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
|
||||
ReactGA.event({
|
||||
category: 'Modal',
|
||||
action: 'Show Legal',
|
||||
})
|
||||
}, [open])
|
||||
|
||||
return (
|
||||
<Modal isOpen={open} onDismiss={() => toggle()}>
|
||||
<AutoColumn gap="12px" ref={node as any}>
|
||||
<RowBetween padding="1rem 1rem 0.5rem 1rem">
|
||||
<ThemedText.MediumHeader>
|
||||
<Trans>Legal & Privacy</Trans>
|
||||
</ThemedText.MediumHeader>
|
||||
<HoverText onClick={() => toggle()}>
|
||||
<X size={24} />
|
||||
</HoverText>
|
||||
</RowBetween>
|
||||
<PrivacyPolicy />
|
||||
</AutoColumn>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export function PrivacyPolicy() {
|
||||
return (
|
||||
<Wrapper
|
||||
draggable="true"
|
||||
onTouchMove={(e) => {
|
||||
// prevent modal gesture handler from dismissing modal when content is scrolling
|
||||
if (isMobile) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
}}
|
||||
>
|
||||
<AutoColumn gap="16px">
|
||||
<AutoColumn gap="8px" style={{ width: '100%' }}>
|
||||
<StyledExternalCard>
|
||||
<ExternalLink href={'https://uniswap.org/terms-of-service'}>
|
||||
<RowBetween>
|
||||
<AutoRow gap="4px">
|
||||
<Info size={20} />
|
||||
<ThemedText.Main fontSize={14} color={'primaryText1'}>
|
||||
<Trans>Uniswap Labs' Terms of Service</Trans>
|
||||
</ThemedText.Main>
|
||||
</AutoRow>
|
||||
<StyledLinkOut size={20} />
|
||||
</RowBetween>
|
||||
</ExternalLink>
|
||||
</StyledExternalCard>
|
||||
<StyledExternalCard>
|
||||
<ExternalLink href={'https://uniswap.org/disclaimer/'}>
|
||||
<RowBetween>
|
||||
<AutoRow gap="4px">
|
||||
<Info size={20} />
|
||||
<ThemedText.Main fontSize={14} color={'primaryText1'}>
|
||||
<Trans>Protocol Disclaimer</Trans>
|
||||
</ThemedText.Main>
|
||||
</AutoRow>
|
||||
<StyledLinkOut size={20} />
|
||||
</RowBetween>
|
||||
</ExternalLink>
|
||||
</StyledExternalCard>
|
||||
</AutoColumn>
|
||||
<ThemedText.Main fontSize={14}>
|
||||
<Trans>This app uses the following third-party APIs:</Trans>
|
||||
</ThemedText.Main>
|
||||
<AutoColumn gap="12px">
|
||||
{EXTERNAL_APIS.map(({ name, description }, i) => (
|
||||
<DarkGreyCard key={i}>
|
||||
<AutoColumn gap="8px">
|
||||
<AutoRow gap="4px">
|
||||
<Info size={18} />
|
||||
<ThemedText.Main fontSize={14} color={'text1'}>
|
||||
{name}
|
||||
</ThemedText.Main>
|
||||
</AutoRow>
|
||||
<ThemedText.Main fontSize={14}>{description}</ThemedText.Main>
|
||||
</AutoColumn>
|
||||
</DarkGreyCard>
|
||||
))}
|
||||
<Row justify="center" marginBottom="1rem">
|
||||
<ExternalLink href="https://help.uniswap.org/en/articles/5675203-terms-of-service-faq">
|
||||
<Trans>Learn more</Trans>
|
||||
</ExternalLink>
|
||||
</Row>
|
||||
</AutoColumn>
|
||||
</AutoColumn>
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { useContext } from 'react'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
|
||||
const Wrapper = styled(AutoColumn)`
|
||||
@@ -65,7 +65,7 @@ export default function ProgressCircles({ steps, disabled = false, ...rest }: Pr
|
||||
<Circle confirmed={step} disabled={disabled || (!steps[i - 1] && i !== 0)}>
|
||||
{step ? '✓' : i + 1 + '.'}
|
||||
</Circle>
|
||||
<TYPE.main color={theme.text4}>|</TYPE.main>
|
||||
<ThemedText.Main color={theme.text4}>|</ThemedText.Main>
|
||||
</CircleRow>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { AutoRow } from 'components/Row'
|
||||
import React from 'react'
|
||||
import ReactGA from 'react-ga'
|
||||
import styled from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
const Button = styled(ButtonOutlined).attrs(() => ({
|
||||
padding: '8px',
|
||||
@@ -26,9 +26,9 @@ export default function PresetsButtons({ setFullRange }: { setFullRange: () => v
|
||||
})
|
||||
}}
|
||||
>
|
||||
<TYPE.body fontSize={12}>
|
||||
<ThemedText.Body fontSize={12}>
|
||||
<Trans>Full Range</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</Button>
|
||||
</AutoRow>
|
||||
)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Protocol } from '@uniswap/router-sdk'
|
||||
import { Currency, Percent } from '@uniswap/sdk-core'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import { DAI, USDC, WBTC } from 'constants/tokens'
|
||||
@@ -7,16 +8,21 @@ import RoutingDiagram, { RoutingDiagramEntry } from './RoutingDiagram'
|
||||
|
||||
const percent = (strings: TemplateStringsArray) => new Percent(parseInt(strings[0]), 100)
|
||||
|
||||
const singleRoute: RoutingDiagramEntry = { percent: percent`100`, path: [[USDC, DAI, FeeAmount.LOW]] }
|
||||
const singleRoute: RoutingDiagramEntry = {
|
||||
percent: percent`100`,
|
||||
path: [[USDC, DAI, FeeAmount.LOW]],
|
||||
protocol: Protocol.V3,
|
||||
}
|
||||
|
||||
const multiRoute: RoutingDiagramEntry[] = [
|
||||
{ percent: percent`75`, path: [[USDC, DAI, FeeAmount.LOW]] },
|
||||
{ percent: percent`75`, path: [[USDC, DAI, FeeAmount.LOWEST]], protocol: Protocol.V2 },
|
||||
{
|
||||
percent: percent`25`,
|
||||
path: [
|
||||
[USDC, WBTC, FeeAmount.MEDIUM],
|
||||
[WBTC, DAI, FeeAmount.HIGH],
|
||||
],
|
||||
protocol: Protocol.V3,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Protocol } from '@uniswap/router-sdk'
|
||||
import { Currency, Percent } from '@uniswap/sdk-core'
|
||||
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||
import Badge from 'components/Badge'
|
||||
@@ -7,22 +9,24 @@ import Row, { AutoRow } from 'components/Row'
|
||||
import { useTokenInfoFromActiveList } from 'hooks/useTokenInfoFromActiveList'
|
||||
import { Box } from 'rebass'
|
||||
import styled from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText, Z_INDEX } from 'theme'
|
||||
|
||||
import { ReactComponent as DotLine } from '../../assets/svg/dot_line.svg'
|
||||
import { MouseoverTooltip } from '../Tooltip'
|
||||
|
||||
export interface RoutingDiagramEntry {
|
||||
percent: Percent
|
||||
path: [Currency, Currency, FeeAmount][]
|
||||
protocol: Protocol
|
||||
}
|
||||
|
||||
const Wrapper = styled(Box)`
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.bg0};
|
||||
width: 400px;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const RouteContainerRow = styled(Row)`
|
||||
display: grid;
|
||||
grid-gap: 8px;
|
||||
grid-template-columns: 24px 1fr 24px;
|
||||
`
|
||||
|
||||
@@ -36,22 +40,47 @@ const RouteRow = styled(Row)`
|
||||
|
||||
const PoolBadge = styled(Badge)`
|
||||
display: flex;
|
||||
padding: 0.25rem 0.5rem;
|
||||
padding: 4px 4px;
|
||||
`
|
||||
|
||||
const DottedLine = styled.div`
|
||||
border-color: ${({ theme }) => theme.bg4};
|
||||
border-top-style: dotted;
|
||||
border-width: 4px;
|
||||
height: 0px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
width: calc(100%);
|
||||
z-index: 1;
|
||||
opacity: 0.5;
|
||||
`
|
||||
|
||||
const DotColor = styled(DotLine)`
|
||||
path {
|
||||
stroke: ${({ theme }) => theme.bg4};
|
||||
}
|
||||
`
|
||||
|
||||
const OpaqueBadge = styled(Badge)`
|
||||
background-color: ${({ theme }) => theme.bg2};
|
||||
z-index: 2;
|
||||
border-radius: 8px;
|
||||
display: grid;
|
||||
font-size: 12px;
|
||||
grid-gap: 4px;
|
||||
grid-auto-flow: column;
|
||||
justify-content: start;
|
||||
padding: 4px 6px 4px 4px;
|
||||
z-index: ${Z_INDEX.sticky};
|
||||
`
|
||||
|
||||
const ProtocolBadge = styled(Badge)`
|
||||
background-color: ${({ theme }) => theme.bg3};
|
||||
border-radius: 4px;
|
||||
color: ${({ theme }) => theme.text2};
|
||||
font-size: 10px;
|
||||
padding: 2px 4px;
|
||||
z-index: ${Z_INDEX.sticky + 1};
|
||||
`
|
||||
|
||||
const BadgeText = styled(ThemedText.Small)`
|
||||
word-break: normal;
|
||||
`
|
||||
|
||||
export default function RoutingDiagram({
|
||||
@@ -68,27 +97,31 @@ export default function RoutingDiagram({
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
{routes.map(({ percent, path }, index) => (
|
||||
{routes.map((entry, index) => (
|
||||
<RouteContainerRow key={index}>
|
||||
<CurrencyLogo currency={tokenIn} />
|
||||
<Route percent={percent} path={path} />
|
||||
<CurrencyLogo currency={tokenOut} />
|
||||
<CurrencyLogo currency={tokenIn} size={'20px'} />
|
||||
<Route entry={entry} />
|
||||
<CurrencyLogo currency={tokenOut} size={'20px'} />
|
||||
</RouteContainerRow>
|
||||
))}
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
||||
function Route({ percent, path }: { percent: RoutingDiagramEntry['percent']; path: RoutingDiagramEntry['path'] }) {
|
||||
function Route({ entry: { percent, path, protocol } }: { entry: RoutingDiagramEntry }) {
|
||||
return (
|
||||
<RouteRow>
|
||||
<DottedLine />
|
||||
<DottedLine>
|
||||
<DotColor />
|
||||
</DottedLine>
|
||||
<OpaqueBadge>
|
||||
<TYPE.small fontSize={12} style={{ wordBreak: 'normal' }}>
|
||||
<ProtocolBadge>
|
||||
<BadgeText fontSize={12}>{protocol.toUpperCase()}</BadgeText>
|
||||
</ProtocolBadge>
|
||||
<BadgeText fontSize={14} style={{ minWidth: 'auto' }}>
|
||||
{percent.toSignificant(2)}%
|
||||
</TYPE.small>
|
||||
</BadgeText>
|
||||
</OpaqueBadge>
|
||||
|
||||
<AutoRow gap="1px" width="100%" style={{ justifyContent: 'space-evenly', zIndex: 2 }}>
|
||||
{path.map(([currency0, currency1, feeAmount], index) => (
|
||||
<Pool key={index} currency0={currency0} currency1={currency1} feeAmount={feeAmount} />
|
||||
@@ -102,12 +135,17 @@ function Pool({ currency0, currency1, feeAmount }: { currency0: Currency; curren
|
||||
const tokenInfo0 = useTokenInfoFromActiveList(currency0)
|
||||
const tokenInfo1 = useTokenInfoFromActiveList(currency1)
|
||||
|
||||
// TODO - link pool icon to info.uniswap.org via query params
|
||||
return (
|
||||
<PoolBadge>
|
||||
<Box margin="0 5px 0 10px">
|
||||
<DoubleCurrencyLogo currency0={tokenInfo1} currency1={tokenInfo0} size={20} />
|
||||
</Box>
|
||||
<TYPE.small fontSize={12}>{feeAmount / 10000}%</TYPE.small>
|
||||
</PoolBadge>
|
||||
<MouseoverTooltip
|
||||
text={<Trans>{tokenInfo0?.symbol + '/' + tokenInfo1?.symbol + ' ' + feeAmount / 10000}% pool</Trans>}
|
||||
>
|
||||
<PoolBadge>
|
||||
<Box margin="0 4px 0 12px">
|
||||
<DoubleCurrencyLogo currency0={tokenInfo1} currency1={tokenInfo0} size={20} />
|
||||
</Box>
|
||||
<ThemedText.Small fontSize={14}>{feeAmount / 10000}%</ThemedText.Small>
|
||||
</PoolBadge>
|
||||
</MouseoverTooltip>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,24 +3,39 @@
|
||||
exports[`renders multi route 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="RoutingDiagram__Wrapper-sc-o1ook0-0 fUoVYh css-vurnku"
|
||||
class="RoutingDiagram__Wrapper-sc-o1ook0-0 ePDWDk css-vurnku"
|
||||
>
|
||||
<div
|
||||
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteContainerRow-sc-o1ook0-1 lmTMKd itvFNV iiQQUx"
|
||||
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteContainerRow-sc-o1ook0-1 lmTMKd itvFNV ibRCpr"
|
||||
>
|
||||
CurrencyLogo currency=USDC
|
||||
<div
|
||||
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteRow-sc-o1ook0-2 lmTMKd itvFNV fzMiot"
|
||||
>
|
||||
<div
|
||||
class="RoutingDiagram__DottedLine-sc-o1ook0-4 gwivhA"
|
||||
/>
|
||||
class="RoutingDiagram__DottedLine-sc-o1ook0-4 kkXINS"
|
||||
>
|
||||
<svg
|
||||
class="RoutingDiagram__DotColor-sc-o1ook0-5 kgYqrO"
|
||||
>
|
||||
dot_line.svg
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="Badge-sc-1mhw5si-0 RoutingDiagram__OpaqueBadge-sc-o1ook0-5 gayll cvyxdH"
|
||||
class="Badge-sc-1mhw5si-0 RoutingDiagram__OpaqueBadge-sc-o1ook0-6 gayll OurGh"
|
||||
>
|
||||
<div
|
||||
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab css-15li2d9"
|
||||
style="word-break: normal;"
|
||||
class="Badge-sc-1mhw5si-0 RoutingDiagram__ProtocolBadge-sc-o1ook0-7 gayll bNVqMw"
|
||||
>
|
||||
<div
|
||||
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab RoutingDiagram__BadgeText-sc-o1ook0-8 dYpdfO css-15li2d9"
|
||||
>
|
||||
V2
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab RoutingDiagram__BadgeText-sc-o1ook0-8 dYpdfO css-1aekuku"
|
||||
style="min-width: auto;"
|
||||
>
|
||||
75%
|
||||
</div>
|
||||
@@ -30,40 +45,42 @@ exports[`renders multi route 1`] = `
|
||||
style="justify-content: space-evenly; z-index: 2;"
|
||||
width="100%"
|
||||
>
|
||||
<div
|
||||
class="Badge-sc-1mhw5si-0 RoutingDiagram__PoolBadge-sc-o1ook0-3 gayll bRJvWg"
|
||||
>
|
||||
<div
|
||||
class="css-1t7xebc"
|
||||
>
|
||||
DoubleCurrencyLogo currency0=DAI currency1=USDC
|
||||
</div>
|
||||
<div
|
||||
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab css-15li2d9"
|
||||
>
|
||||
0.05%
|
||||
</div>
|
||||
</div>
|
||||
Popover
|
||||
</div>
|
||||
</div>
|
||||
CurrencyLogo currency=DAI
|
||||
</div>
|
||||
<div
|
||||
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteContainerRow-sc-o1ook0-1 lmTMKd itvFNV iiQQUx"
|
||||
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteContainerRow-sc-o1ook0-1 lmTMKd itvFNV ibRCpr"
|
||||
>
|
||||
CurrencyLogo currency=USDC
|
||||
<div
|
||||
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteRow-sc-o1ook0-2 lmTMKd itvFNV fzMiot"
|
||||
>
|
||||
<div
|
||||
class="RoutingDiagram__DottedLine-sc-o1ook0-4 gwivhA"
|
||||
/>
|
||||
class="RoutingDiagram__DottedLine-sc-o1ook0-4 kkXINS"
|
||||
>
|
||||
<svg
|
||||
class="RoutingDiagram__DotColor-sc-o1ook0-5 kgYqrO"
|
||||
>
|
||||
dot_line.svg
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="Badge-sc-1mhw5si-0 RoutingDiagram__OpaqueBadge-sc-o1ook0-5 gayll cvyxdH"
|
||||
class="Badge-sc-1mhw5si-0 RoutingDiagram__OpaqueBadge-sc-o1ook0-6 gayll OurGh"
|
||||
>
|
||||
<div
|
||||
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab css-15li2d9"
|
||||
style="word-break: normal;"
|
||||
class="Badge-sc-1mhw5si-0 RoutingDiagram__ProtocolBadge-sc-o1ook0-7 gayll bNVqMw"
|
||||
>
|
||||
<div
|
||||
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab RoutingDiagram__BadgeText-sc-o1ook0-8 dYpdfO css-15li2d9"
|
||||
>
|
||||
V3
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab RoutingDiagram__BadgeText-sc-o1ook0-8 dYpdfO css-1aekuku"
|
||||
style="min-width: auto;"
|
||||
>
|
||||
25%
|
||||
</div>
|
||||
@@ -73,34 +90,7 @@ exports[`renders multi route 1`] = `
|
||||
style="justify-content: space-evenly; z-index: 2;"
|
||||
width="100%"
|
||||
>
|
||||
<div
|
||||
class="Badge-sc-1mhw5si-0 RoutingDiagram__PoolBadge-sc-o1ook0-3 gayll bRJvWg"
|
||||
>
|
||||
<div
|
||||
class="css-1t7xebc"
|
||||
>
|
||||
DoubleCurrencyLogo currency0=WBTC currency1=USDC
|
||||
</div>
|
||||
<div
|
||||
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab css-15li2d9"
|
||||
>
|
||||
0.3%
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="Badge-sc-1mhw5si-0 RoutingDiagram__PoolBadge-sc-o1ook0-3 gayll bRJvWg"
|
||||
>
|
||||
<div
|
||||
class="css-1t7xebc"
|
||||
>
|
||||
DoubleCurrencyLogo currency0=DAI currency1=WBTC
|
||||
</div>
|
||||
<div
|
||||
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab css-15li2d9"
|
||||
>
|
||||
1%
|
||||
</div>
|
||||
</div>
|
||||
PopoverPopover
|
||||
</div>
|
||||
</div>
|
||||
CurrencyLogo currency=DAI
|
||||
@@ -112,24 +102,39 @@ exports[`renders multi route 1`] = `
|
||||
exports[`renders single route 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="RoutingDiagram__Wrapper-sc-o1ook0-0 fUoVYh css-vurnku"
|
||||
class="RoutingDiagram__Wrapper-sc-o1ook0-0 ePDWDk css-vurnku"
|
||||
>
|
||||
<div
|
||||
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteContainerRow-sc-o1ook0-1 lmTMKd itvFNV iiQQUx"
|
||||
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteContainerRow-sc-o1ook0-1 lmTMKd itvFNV ibRCpr"
|
||||
>
|
||||
CurrencyLogo currency=USDC
|
||||
<div
|
||||
class="sc-bdnxRM Row-sc-nrd8cx-0 RoutingDiagram__RouteRow-sc-o1ook0-2 lmTMKd itvFNV fzMiot"
|
||||
>
|
||||
<div
|
||||
class="RoutingDiagram__DottedLine-sc-o1ook0-4 gwivhA"
|
||||
/>
|
||||
class="RoutingDiagram__DottedLine-sc-o1ook0-4 kkXINS"
|
||||
>
|
||||
<svg
|
||||
class="RoutingDiagram__DotColor-sc-o1ook0-5 kgYqrO"
|
||||
>
|
||||
dot_line.svg
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="Badge-sc-1mhw5si-0 RoutingDiagram__OpaqueBadge-sc-o1ook0-5 gayll cvyxdH"
|
||||
class="Badge-sc-1mhw5si-0 RoutingDiagram__OpaqueBadge-sc-o1ook0-6 gayll OurGh"
|
||||
>
|
||||
<div
|
||||
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab css-15li2d9"
|
||||
style="word-break: normal;"
|
||||
class="Badge-sc-1mhw5si-0 RoutingDiagram__ProtocolBadge-sc-o1ook0-7 gayll bNVqMw"
|
||||
>
|
||||
<div
|
||||
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab RoutingDiagram__BadgeText-sc-o1ook0-8 dYpdfO css-15li2d9"
|
||||
>
|
||||
V3
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab RoutingDiagram__BadgeText-sc-o1ook0-8 dYpdfO css-1aekuku"
|
||||
style="min-width: auto;"
|
||||
>
|
||||
100%
|
||||
</div>
|
||||
@@ -139,20 +144,7 @@ exports[`renders single route 1`] = `
|
||||
style="justify-content: space-evenly; z-index: 2;"
|
||||
width="100%"
|
||||
>
|
||||
<div
|
||||
class="Badge-sc-1mhw5si-0 RoutingDiagram__PoolBadge-sc-o1ook0-3 gayll bRJvWg"
|
||||
>
|
||||
<div
|
||||
class="css-1t7xebc"
|
||||
>
|
||||
DoubleCurrencyLogo currency0=DAI currency1=USDC
|
||||
</div>
|
||||
<div
|
||||
class="theme__TextWrapper-sc-18nh1jk-0 cWOfab css-15li2d9"
|
||||
>
|
||||
0.05%
|
||||
</div>
|
||||
</div>
|
||||
Popover
|
||||
</div>
|
||||
</div>
|
||||
CurrencyLogo currency=DAI
|
||||
@@ -164,7 +156,7 @@ exports[`renders single route 1`] = `
|
||||
exports[`renders when no routes are provided 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="RoutingDiagram__Wrapper-sc-o1ook0-0 fUoVYh css-vurnku"
|
||||
class="RoutingDiagram__Wrapper-sc-o1ook0-0 ePDWDk css-vurnku"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
71
src/components/SearchModal/BlockedToken.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Token } from '@uniswap/sdk-core'
|
||||
import { ButtonPrimary } from 'components/Button'
|
||||
import { AlertCircle, ArrowLeft } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { CloseIcon, ThemedText } from 'theme'
|
||||
|
||||
import TokenImportCard from './TokenImportCard'
|
||||
|
||||
const Wrapper = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
`
|
||||
const Button = styled(ButtonPrimary)`
|
||||
margin-top: 1em;
|
||||
padding: 10px 1em;
|
||||
`
|
||||
const Content = styled.div`
|
||||
padding: 1em;
|
||||
`
|
||||
const Copy = styled(ThemedText.Body)`
|
||||
text-align: center;
|
||||
margin: 0 2em 1em !important;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
`
|
||||
const Header = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
justify-content: space-between;
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
`
|
||||
const Icon = styled(AlertCircle)`
|
||||
stroke: ${({ theme }) => theme.text2};
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
`
|
||||
interface BlockedTokenProps {
|
||||
onBack: (() => void) | undefined
|
||||
onDismiss: (() => void) | undefined
|
||||
blockedTokens: Token[]
|
||||
}
|
||||
|
||||
const BlockedToken = ({ onBack, onDismiss, blockedTokens }: BlockedTokenProps) => (
|
||||
<Wrapper>
|
||||
<Header>
|
||||
{onBack ? <ArrowLeft style={{ cursor: 'pointer' }} onClick={onBack} /> : <div />}
|
||||
<ThemedText.MediumHeader>
|
||||
<Trans>Token not supported</Trans>
|
||||
</ThemedText.MediumHeader>
|
||||
{onDismiss ? <CloseIcon onClick={onDismiss} /> : <div />}
|
||||
</Header>
|
||||
<Icon />
|
||||
<Content>
|
||||
<Copy>
|
||||
<Trans>This token is not supported in the Uniswap Labs app</Trans>
|
||||
</Copy>
|
||||
<TokenImportCard token={blockedTokens[0]} />
|
||||
<Button disabled>
|
||||
<Trans>Import</Trans>
|
||||
</Button>
|
||||
</Content>
|
||||
</Wrapper>
|
||||
)
|
||||
export default BlockedToken
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Currency } from '@uniswap/sdk-core'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import CurrencyLogo from 'components/CurrencyLogo'
|
||||
import QuestionHelper from 'components/QuestionHelper'
|
||||
import { AutoRow } from 'components/Row'
|
||||
import { COMMON_BASES } from 'constants/routing'
|
||||
import { useTokenInfoFromActiveList } from 'hooks/useTokenInfoFromActiveList'
|
||||
import { Text } from 'rebass'
|
||||
import styled from 'styled-components/macro'
|
||||
import { currencyId } from 'utils/currencyId'
|
||||
@@ -45,12 +44,6 @@ export default function CommonBases({
|
||||
|
||||
return bases.length > 0 ? (
|
||||
<MobileWrapper gap="md">
|
||||
<AutoRow>
|
||||
<Text fontWeight={500} fontSize={14}>
|
||||
<Trans>Common bases</Trans>
|
||||
</Text>
|
||||
<QuestionHelper text={<Trans>These tokens are commonly paired with other tokens.</Trans>} />
|
||||
</AutoRow>
|
||||
<AutoRow gap="4px">
|
||||
{bases.map((currency: Currency) => {
|
||||
const isSelected = selectedCurrency?.equals(currency)
|
||||
@@ -60,7 +53,7 @@ export default function CommonBases({
|
||||
disable={isSelected}
|
||||
key={currencyId(currency)}
|
||||
>
|
||||
<CurrencyLogo currency={currency} style={{ marginRight: 8 }} />
|
||||
<CurrencyLogoFromList currency={currency} />
|
||||
<Text fontWeight={500} fontSize={16}>
|
||||
{currency.symbol}
|
||||
</Text>
|
||||
@@ -71,3 +64,10 @@ export default function CommonBases({
|
||||
</MobileWrapper>
|
||||
) : null
|
||||
}
|
||||
|
||||
/** helper component to retrieve a base currency from the active token lists */
|
||||
function CurrencyLogoFromList({ currency }: { currency: Currency }) {
|
||||
const token = useTokenInfoFromActiveList(currency)
|
||||
|
||||
return <CurrencyLogo currency={token} style={{ marginRight: 8 }} />
|
||||
}
|
||||
|
||||
@@ -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,11 +11,10 @@ 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'
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { isTokenOnList } from '../../utils'
|
||||
import Column from '../Column'
|
||||
import CurrencyLogo from '../CurrencyLogo'
|
||||
@@ -135,13 +135,13 @@ function CurrencyRow({
|
||||
<Text title={currency.name} fontWeight={500}>
|
||||
{currency.symbol}
|
||||
</Text>
|
||||
<TYPE.darkGray ml="0px" fontSize={'12px'} fontWeight={300}>
|
||||
<ThemedText.DarkGray ml="0px" fontSize={'12px'} fontWeight={300}>
|
||||
{!currency.isNative && !isOnSelectedList && customAdded ? (
|
||||
<Trans>{currency.name} • Added by user</Trans>
|
||||
) : (
|
||||
currency.name
|
||||
)}
|
||||
</TYPE.darkGray>
|
||||
</ThemedText.DarkGray>
|
||||
</Column>
|
||||
<TokenTags currency={currency} />
|
||||
{showCurrencyAmount && (
|
||||
@@ -167,9 +167,9 @@ function BreakLineComponent({ style }: { style: CSSProperties }) {
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TokenListLogoWrapper src={TokenListLogo} />
|
||||
<TYPE.main ml="6px" fontSize="12px" color={theme.text1}>
|
||||
<ThemedText.Main ml="6px" fontSize="12px" color={theme.text1}>
|
||||
<Trans>Expanded results from inactive Token Lists</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</RowFixed>
|
||||
<QuestionHelper
|
||||
text={
|
||||
|
||||
@@ -1,29 +1,36 @@
|
||||
// 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 { 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 { ExtendedEther } from '../../constants/tokens'
|
||||
import { useAllTokens, useIsUserAddedToken, useSearchInactiveTokenLists, useToken } from '../../hooks/Tokens'
|
||||
import { useActiveWeb3React } from '../../hooks/web3'
|
||||
import { ButtonText, CloseIcon, IconWrapper, TYPE } from '../../theme'
|
||||
import {
|
||||
useAllTokens,
|
||||
useIsUserAddedToken,
|
||||
useNativeCurrency,
|
||||
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)`
|
||||
@@ -78,8 +85,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
|
||||
@@ -99,27 +104,28 @@ 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 ether = useMemo(() => chainId && ExtendedEther.onChain(chainId), [chainId])
|
||||
const native = useNativeCurrency()
|
||||
|
||||
const filteredSortedTokensWithETH: Currency[] = useMemo(() => {
|
||||
if (!native) return filteredSortedTokens
|
||||
|
||||
const s = debouncedQuery.toLowerCase().trim()
|
||||
if (s === '' || s === 'e' || s === 'et' || s === 'eth') {
|
||||
return ether ? [ether, ...filteredSortedTokens] : filteredSortedTokens
|
||||
if (native.symbol?.toLowerCase()?.indexOf(s) !== -1) {
|
||||
return native ? [native, ...filteredSortedTokens] : filteredSortedTokens
|
||||
}
|
||||
return filteredSortedTokens
|
||||
}, [debouncedQuery, ether, filteredSortedTokens])
|
||||
}, [debouncedQuery, native, filteredSortedTokens])
|
||||
|
||||
const handleCurrencySelect = useCallback(
|
||||
(currency: Currency) => {
|
||||
@@ -147,8 +153,8 @@ export function CurrencySearch({
|
||||
(e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
const s = debouncedQuery.toLowerCase().trim()
|
||||
if (s === 'eth' && ether) {
|
||||
handleCurrencySelect(ether)
|
||||
if (s === native?.symbol?.toLowerCase()) {
|
||||
handleCurrencySelect(native)
|
||||
} else if (filteredSortedTokensWithETH.length > 0) {
|
||||
if (
|
||||
filteredSortedTokensWithETH[0].symbol?.toLowerCase() === debouncedQuery.trim().toLowerCase() ||
|
||||
@@ -159,7 +165,7 @@ export function CurrencySearch({
|
||||
}
|
||||
}
|
||||
},
|
||||
[debouncedQuery, ether, filteredSortedTokensWithETH, handleCurrencySelect]
|
||||
[debouncedQuery, native, filteredSortedTokensWithETH, handleCurrencySelect]
|
||||
)
|
||||
|
||||
// menu ui
|
||||
@@ -223,9 +229,9 @@ export function CurrencySearch({
|
||||
</div>
|
||||
) : (
|
||||
<Column style={{ padding: '20px', height: '100%' }}>
|
||||
<TYPE.main color={theme.text3} textAlign="center" mb="20px">
|
||||
<ThemedText.Main color={theme.text3} textAlign="center" mb="20px">
|
||||
<Trans>No results found.</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</Column>
|
||||
)}
|
||||
<Footer>
|
||||
@@ -235,9 +241,9 @@ export function CurrencySearch({
|
||||
<IconWrapper size="16px" marginRight="6px" stroke={theme.primaryText1}>
|
||||
<Edit />
|
||||
</IconWrapper>
|
||||
<TYPE.main color={theme.primaryText1}>
|
||||
<ThemedText.Main color={theme.primaryText1}>
|
||||
<Trans>Manage Token Lists</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</RowFixed>
|
||||
</ButtonText>
|
||||
</Row>
|
||||
|
||||
@@ -66,12 +66,19 @@ export default function CurrencySearchModal({
|
||||
const [importList, setImportList] = useState<TokenList | undefined>()
|
||||
const [listURL, setListUrl] = useState<string | undefined>()
|
||||
|
||||
const showImportView = useCallback(() => setModalView(CurrencyModalView.importToken), [setModalView])
|
||||
const showManageView = useCallback(() => setModalView(CurrencyModalView.manage), [setModalView])
|
||||
const handleBackImport = useCallback(
|
||||
() => setModalView(prevView && prevView !== CurrencyModalView.importToken ? prevView : CurrencyModalView.search),
|
||||
[setModalView, prevView]
|
||||
)
|
||||
|
||||
// change min height if not searching
|
||||
const minHeight = modalView === CurrencyModalView.importToken || modalView === CurrencyModalView.importList ? 40 : 80
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onDismiss={onDismiss} maxHeight={80} minHeight={minHeight}>
|
||||
{modalView === CurrencyModalView.search ? (
|
||||
let content = null
|
||||
switch (modalView) {
|
||||
case CurrencyModalView.search:
|
||||
content = (
|
||||
<CurrencySearch
|
||||
isOpen={isOpen}
|
||||
onDismiss={onDismiss}
|
||||
@@ -81,23 +88,32 @@ export default function CurrencySearchModal({
|
||||
showCommonBases={showCommonBases}
|
||||
showCurrencyAmount={showCurrencyAmount}
|
||||
disableNonToken={disableNonToken}
|
||||
showImportView={() => setModalView(CurrencyModalView.importToken)}
|
||||
showImportView={showImportView}
|
||||
setImportToken={setImportToken}
|
||||
showManageView={() => setModalView(CurrencyModalView.manage)}
|
||||
showManageView={showManageView}
|
||||
/>
|
||||
) : modalView === CurrencyModalView.importToken && importToken ? (
|
||||
<ImportToken
|
||||
tokens={[importToken]}
|
||||
onDismiss={onDismiss}
|
||||
list={importToken instanceof WrappedTokenInfo ? importToken.list : undefined}
|
||||
onBack={() =>
|
||||
setModalView(prevView && prevView !== CurrencyModalView.importToken ? prevView : CurrencyModalView.search)
|
||||
}
|
||||
handleCurrencySelect={handleCurrencySelect}
|
||||
/>
|
||||
) : modalView === CurrencyModalView.importList && importList && listURL ? (
|
||||
<ImportList list={importList} listURL={listURL} onDismiss={onDismiss} setModalView={setModalView} />
|
||||
) : modalView === CurrencyModalView.manage ? (
|
||||
)
|
||||
break
|
||||
case CurrencyModalView.importToken:
|
||||
if (importToken) {
|
||||
content = (
|
||||
<ImportToken
|
||||
tokens={[importToken]}
|
||||
onDismiss={onDismiss}
|
||||
list={importToken instanceof WrappedTokenInfo ? importToken.list : undefined}
|
||||
onBack={handleBackImport}
|
||||
handleCurrencySelect={handleCurrencySelect}
|
||||
/>
|
||||
)
|
||||
}
|
||||
break
|
||||
case CurrencyModalView.importList:
|
||||
if (importList && listURL) {
|
||||
content = <ImportList list={importList} listURL={listURL} onDismiss={onDismiss} setModalView={setModalView} />
|
||||
}
|
||||
break
|
||||
case CurrencyModalView.manage:
|
||||
content = (
|
||||
<Manage
|
||||
onDismiss={onDismiss}
|
||||
setModalView={setModalView}
|
||||
@@ -105,9 +121,12 @@ export default function CurrencySearchModal({
|
||||
setImportList={setImportList}
|
||||
setListUrl={setListUrl}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
)
|
||||
break
|
||||
}
|
||||
return (
|
||||
<Modal isOpen={isOpen} onDismiss={onDismiss} maxHeight={80} minHeight={minHeight}>
|
||||
{content}
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@ import { useAppDispatch } from 'state/hooks'
|
||||
import { enableList, removeList } from 'state/lists/actions'
|
||||
import { useAllLists } from 'state/lists/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
import { CloseIcon, TYPE } from 'theme'
|
||||
import { CloseIcon, ThemedText } from 'theme'
|
||||
|
||||
import { ExternalLink } from '../../theme/components'
|
||||
import { ExternalLink } from '../../theme'
|
||||
import { CurrencyModalView } from './CurrencySearchModal'
|
||||
import { Checkbox, PaddedColumn, TextDot } from './styleds'
|
||||
|
||||
@@ -81,9 +81,9 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
|
||||
<PaddedColumn gap="14px" style={{ width: '100%', flex: '1 1' }}>
|
||||
<RowBetween>
|
||||
<ArrowLeft style={{ cursor: 'pointer' }} onClick={() => setModalView(CurrencyModalView.manage)} />
|
||||
<TYPE.mediumHeader>
|
||||
<ThemedText.MediumHeader>
|
||||
<Trans>Import List</Trans>
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
<CloseIcon onClick={onDismiss} />
|
||||
</RowBetween>
|
||||
</PaddedColumn>
|
||||
@@ -96,18 +96,18 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
|
||||
{list.logoURI && <ListLogo logoURI={list.logoURI} size="40px" />}
|
||||
<AutoColumn gap="sm" style={{ marginLeft: '20px' }}>
|
||||
<RowFixed>
|
||||
<TYPE.body fontWeight={600} mr="6px">
|
||||
<ThemedText.Body fontWeight={600} mr="6px">
|
||||
{list.name}
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
<TextDot />
|
||||
<TYPE.main fontSize={'16px'} ml="6px">
|
||||
<ThemedText.Main fontSize={'16px'} ml="6px">
|
||||
<Trans>{list.tokens.length} tokens</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</RowFixed>
|
||||
<ExternalLink href={`https://tokenlists.org/token-list?url=${listURL}`}>
|
||||
<TYPE.main fontSize={'12px'} color={theme.blue1}>
|
||||
<ThemedText.Main fontSize={'12px'} color={theme.blue1}>
|
||||
{listURL}
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</ExternalLink>
|
||||
</AutoColumn>
|
||||
</RowFixed>
|
||||
@@ -116,22 +116,22 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
|
||||
<Card style={{ backgroundColor: transparentize(0.8, theme.red1) }}>
|
||||
<AutoColumn justify="center" style={{ textAlign: 'center', gap: '16px', marginBottom: '12px' }}>
|
||||
<AlertTriangle stroke={theme.red1} size={32} />
|
||||
<TYPE.body fontWeight={500} fontSize={20} color={theme.red1}>
|
||||
<ThemedText.Body fontWeight={500} fontSize={20} color={theme.red1}>
|
||||
<Trans>Import at your own risk</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
|
||||
<AutoColumn style={{ textAlign: 'center', gap: '16px', marginBottom: '12px' }}>
|
||||
<TYPE.body fontWeight={500} color={theme.red1}>
|
||||
<ThemedText.Body fontWeight={500} color={theme.red1}>
|
||||
<Trans>
|
||||
By adding this list you are implicitly trusting that the data is correct. Anyone can create a list,
|
||||
including creating fake versions of existing lists and lists that claim to represent projects that do
|
||||
not have one.
|
||||
</Trans>
|
||||
</TYPE.body>
|
||||
<TYPE.body fontWeight={600} color={theme.red1}>
|
||||
</ThemedText.Body>
|
||||
<ThemedText.Body fontWeight={600} color={theme.red1}>
|
||||
<Trans>If you purchase a token from this list, you may not be able to sell it back.</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
<AutoRow justify="center" style={{ cursor: 'pointer' }} onClick={() => setConfirmed(!confirmed)}>
|
||||
<Checkbox
|
||||
@@ -140,9 +140,9 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
|
||||
checked={confirmed}
|
||||
onChange={() => setConfirmed(!confirmed)}
|
||||
/>
|
||||
<TYPE.body ml="10px" fontSize="16px" color={theme.red1} fontWeight={500}>
|
||||
<ThemedText.Body ml="10px" fontSize="16px" color={theme.red1} fontWeight={500}>
|
||||
<Trans>I understand</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoRow>
|
||||
</Card>
|
||||
|
||||
@@ -156,9 +156,9 @@ export function ImportList({ listURL, list, setModalView, onDismiss }: ImportPro
|
||||
<Trans>Import</Trans>
|
||||
</ButtonPrimary>
|
||||
{addError ? (
|
||||
<TYPE.error title={addError} style={{ textOverflow: 'ellipsis', overflow: 'hidden' }} error>
|
||||
<ThemedText.Error title={addError} style={{ textOverflow: 'ellipsis', overflow: 'hidden' }} error>
|
||||
{addError}
|
||||
</TYPE.error>
|
||||
</ThemedText.Error>
|
||||
) : null}
|
||||
</AutoColumn>
|
||||
{/* </Card> */}
|
||||
|
||||
@@ -10,7 +10,7 @@ import useTheme from 'hooks/useTheme'
|
||||
import { CSSProperties } from 'react'
|
||||
import { CheckCircle } from 'react-feather'
|
||||
import styled from 'styled-components/macro'
|
||||
import { TYPE } from 'theme'
|
||||
import { ThemedText } from 'theme'
|
||||
|
||||
import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
|
||||
|
||||
@@ -67,16 +67,16 @@ export default function ImportRow({
|
||||
<CurrencyLogo currency={token} size={'24px'} style={{ opacity: dim ? '0.6' : '1' }} />
|
||||
<AutoColumn gap="4px" style={{ opacity: dim ? '0.6' : '1' }}>
|
||||
<AutoRow>
|
||||
<TYPE.body fontWeight={500}>{token.symbol}</TYPE.body>
|
||||
<TYPE.darkGray ml="8px" fontWeight={300}>
|
||||
<ThemedText.Body fontWeight={500}>{token.symbol}</ThemedText.Body>
|
||||
<ThemedText.DarkGray ml="8px" fontWeight={300}>
|
||||
<NameOverflow title={token.name}>{token.name}</NameOverflow>
|
||||
</TYPE.darkGray>
|
||||
</ThemedText.DarkGray>
|
||||
</AutoRow>
|
||||
{list && list.logoURI && (
|
||||
<RowFixed>
|
||||
<TYPE.small mr="4px" color={theme.text3}>
|
||||
<ThemedText.Small mr="4px" color={theme.text3}>
|
||||
<Trans>via {list.name} </Trans>
|
||||
</TYPE.small>
|
||||
</ThemedText.Small>
|
||||
<ListLogo logoURI={list.logoURI} size="12px" />
|
||||
</RowFixed>
|
||||
)}
|
||||
@@ -97,9 +97,9 @@ export default function ImportRow({
|
||||
) : (
|
||||
<RowFixed style={{ minWidth: 'fit-content' }}>
|
||||
<CheckIcon />
|
||||
<TYPE.main color={theme.green1}>
|
||||
<ThemedText.Main color={theme.green1}>
|
||||
<Trans>Active</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</RowFixed>
|
||||
)}
|
||||
</TokenSection>
|
||||
|
||||
@@ -2,23 +2,19 @@ import { Plural, Trans } from '@lingui/macro'
|
||||
import { Currency, Token } from '@uniswap/sdk-core'
|
||||
import { TokenList } from '@uniswap/token-lists'
|
||||
import { ButtonPrimary } from 'components/Button'
|
||||
import Card from 'components/Card'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import CurrencyLogo from 'components/CurrencyLogo'
|
||||
import ListLogo from 'components/ListLogo'
|
||||
import { RowBetween, RowFixed } from 'components/Row'
|
||||
import { RowBetween } from 'components/Row'
|
||||
import { SectionBreak } from 'components/swap/styleds'
|
||||
import { useUnsupportedTokens } from 'hooks/Tokens'
|
||||
import useTheme from 'hooks/useTheme'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import { transparentize } from 'polished'
|
||||
import { AlertCircle, ArrowLeft } from 'react-feather'
|
||||
import { useAddUserToken } from 'state/user/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
import { CloseIcon, TYPE } from 'theme'
|
||||
import { CloseIcon, ThemedText } from 'theme'
|
||||
|
||||
import { ExternalLink } from '../../theme/components'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import BlockedToken from './BlockedToken'
|
||||
import { PaddedColumn } from './styleds'
|
||||
import TokenImportCard from './TokenImportCard'
|
||||
|
||||
const Wrapper = styled.div`
|
||||
position: relative;
|
||||
@@ -26,21 +22,6 @@ const Wrapper = styled.div`
|
||||
overflow: auto;
|
||||
`
|
||||
|
||||
const WarningWrapper = styled(Card)<{ highWarning: boolean }>`
|
||||
background-color: ${({ theme, highWarning }) =>
|
||||
highWarning ? transparentize(0.8, theme.red1) : transparentize(0.8, theme.yellow2)};
|
||||
width: fit-content;
|
||||
`
|
||||
|
||||
const AddressText = styled(TYPE.blue)`
|
||||
font-size: 12px;
|
||||
word-break: break-all;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
font-size: 10px;
|
||||
`}
|
||||
`
|
||||
|
||||
interface ImportProps {
|
||||
tokens: Token[]
|
||||
list?: TokenList
|
||||
@@ -49,21 +30,26 @@ interface ImportProps {
|
||||
handleCurrencySelect?: (currency: Currency) => void
|
||||
}
|
||||
|
||||
export function ImportToken({ tokens, list, onBack, onDismiss, handleCurrencySelect }: ImportProps) {
|
||||
export function ImportToken(props: ImportProps) {
|
||||
const { tokens, list, onBack, onDismiss, handleCurrencySelect } = props
|
||||
const theme = useTheme()
|
||||
|
||||
const { chainId } = useActiveWeb3React()
|
||||
|
||||
const addToken = useAddUserToken()
|
||||
|
||||
const unsupportedTokens = useUnsupportedTokens()
|
||||
const unsupportedSet = new Set(Object.keys(unsupportedTokens))
|
||||
const intersection = new Set(tokens.filter((token) => unsupportedSet.has(token.address)))
|
||||
if (intersection.size > 0) {
|
||||
return <BlockedToken onBack={onBack} onDismiss={onDismiss} blockedTokens={Array.from(intersection)} />
|
||||
}
|
||||
return (
|
||||
<Wrapper>
|
||||
<PaddedColumn gap="14px" style={{ width: '100%', flex: '1 1' }}>
|
||||
<RowBetween>
|
||||
{onBack ? <ArrowLeft style={{ cursor: 'pointer' }} onClick={onBack} /> : <div />}
|
||||
<TYPE.mediumHeader>
|
||||
<ThemedText.MediumHeader>
|
||||
<Plural value={tokens.length} one="Import token" other="Import tokens" />
|
||||
</TYPE.mediumHeader>
|
||||
</ThemedText.MediumHeader>
|
||||
{onDismiss ? <CloseIcon onClick={onDismiss} /> : <div />}
|
||||
</RowBetween>
|
||||
</PaddedColumn>
|
||||
@@ -71,59 +57,16 @@ export function ImportToken({ tokens, list, onBack, onDismiss, handleCurrencySel
|
||||
<AutoColumn gap="md" style={{ marginBottom: '32px', padding: '1rem' }}>
|
||||
<AutoColumn justify="center" style={{ textAlign: 'center', gap: '16px', padding: '1rem' }}>
|
||||
<AlertCircle size={48} stroke={theme.text2} strokeWidth={1} />
|
||||
<TYPE.body fontWeight={400} fontSize={16}>
|
||||
<ThemedText.Body fontWeight={400} fontSize={16}>
|
||||
<Trans>
|
||||
This token doesn't appear on the active token list(s). Make sure this is the token that you want to
|
||||
trade.
|
||||
</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</AutoColumn>
|
||||
{tokens.map((token) => {
|
||||
return (
|
||||
<Card
|
||||
backgroundColor={theme.bg2}
|
||||
key={'import' + token.address}
|
||||
className=".token-warning-container"
|
||||
padding="2rem"
|
||||
>
|
||||
<AutoColumn gap="10px" justify="center">
|
||||
<CurrencyLogo currency={token} size={'32px'} />
|
||||
|
||||
<AutoColumn gap="4px" justify="center">
|
||||
<TYPE.body ml="8px" mr="8px" fontWeight={500} fontSize={20}>
|
||||
{token.symbol}
|
||||
</TYPE.body>
|
||||
<TYPE.darkGray fontWeight={400} fontSize={14}>
|
||||
{token.name}
|
||||
</TYPE.darkGray>
|
||||
</AutoColumn>
|
||||
{chainId && (
|
||||
<ExternalLink href={getExplorerLink(chainId, token.address, ExplorerDataType.ADDRESS)}>
|
||||
<AddressText fontSize={12}>{token.address}</AddressText>
|
||||
</ExternalLink>
|
||||
)}
|
||||
{list !== undefined ? (
|
||||
<RowFixed>
|
||||
{list.logoURI && <ListLogo logoURI={list.logoURI} size="16px" />}
|
||||
<TYPE.small ml="6px" fontSize={14} color={theme.text3}>
|
||||
<Trans>via {list.name} token list</Trans>
|
||||
</TYPE.small>
|
||||
</RowFixed>
|
||||
) : (
|
||||
<WarningWrapper $borderRadius="4px" padding="4px" highWarning={true}>
|
||||
<RowFixed>
|
||||
<AlertCircle stroke={theme.red1} size="10px" />
|
||||
<TYPE.body color={theme.red1} ml="4px" fontSize="10px" fontWeight={500}>
|
||||
<Trans>Unknown Source</Trans>
|
||||
</TYPE.body>
|
||||
</RowFixed>
|
||||
</WarningWrapper>
|
||||
)}
|
||||
</AutoColumn>
|
||||
</Card>
|
||||
)
|
||||
})}
|
||||
|
||||
{tokens.map((token) => (
|
||||
<TokenImportCard token={token} list={list} key={'import' + token.address} />
|
||||
))}
|
||||
<ButtonPrimary
|
||||
altDisabledStyle={true}
|
||||
$borderRadius="20px"
|
||||
|
||||
@@ -16,7 +16,8 @@ import { PaddedColumn, Separator } from './styleds'
|
||||
const Wrapper = styled.div`
|
||||
width: 100%;
|
||||
position: relative;
|
||||
padding-bottom: 80px;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
`
|
||||
|
||||
const ToggleWrapper = styled(RowBetween)`
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
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'
|
||||
import { CheckCircle, Settings } from 'react-feather'
|
||||
import ReactGA from 'react-ga'
|
||||
@@ -17,10 +20,8 @@ import useTheme from '../../hooks/useTheme'
|
||||
import useToggle from '../../hooks/useToggle'
|
||||
import { acceptListUpdate, disableList, enableList, removeList } from '../../state/lists/actions'
|
||||
import { useActiveListUrls, useAllLists, useIsListActive } from '../../state/lists/hooks'
|
||||
import { ExternalLink, IconWrapper, LinkStyledButton, TYPE } from '../../theme'
|
||||
import { ExternalLink, IconWrapper, LinkStyledButton, ThemedText } from '../../theme'
|
||||
import listVersionLabel from '../../utils/listVersionLabel'
|
||||
import { parseENSAddress } from '../../utils/parseENSAddress'
|
||||
import uriToHttp from '../../utils/uriToHttp'
|
||||
import { ButtonEmpty, ButtonPrimary } from '../Button'
|
||||
import Column, { AutoColumn } from '../Column'
|
||||
import ListLogo from '../ListLogo'
|
||||
@@ -30,7 +31,8 @@ import { CurrencyModalView } from './CurrencySearchModal'
|
||||
import { PaddedColumn, SearchInput, Separator, SeparatorDark } from './styleds'
|
||||
|
||||
const Wrapper = styled(Column)`
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
overflow-y: hidden;
|
||||
`
|
||||
|
||||
const UnpaddedLinkStyledButton = styled(LinkStyledButton)`
|
||||
@@ -74,7 +76,7 @@ const StyledTitleText = styled.div<{ active: boolean }>`
|
||||
color: ${({ theme, active }) => (active ? theme.white : theme.text2)};
|
||||
`
|
||||
|
||||
const StyledListUrlText = styled(TYPE.main)<{ active: boolean }>`
|
||||
const StyledListUrlText = styled(ThemedText.Main)<{ active: boolean }>`
|
||||
font-size: 12px;
|
||||
color: ${({ theme, active }) => (active ? theme.white : theme.text2)};
|
||||
`
|
||||
@@ -228,7 +230,7 @@ const ListContainer = styled.div`
|
||||
padding: 1rem;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
padding-bottom: 80px;
|
||||
flex: 1;
|
||||
`
|
||||
|
||||
export function ManageLists({
|
||||
@@ -360,9 +362,9 @@ export function ManageLists({
|
||||
/>
|
||||
</Row>
|
||||
{addError ? (
|
||||
<TYPE.error title={addError} style={{ textOverflow: 'ellipsis', overflow: 'hidden' }} error>
|
||||
<ThemedText.Error title={addError} style={{ textOverflow: 'ellipsis', overflow: 'hidden' }} error>
|
||||
{addError}
|
||||
</TYPE.error>
|
||||
</ThemedText.Error>
|
||||
) : null}
|
||||
</PaddedColumn>
|
||||
{tempList && (
|
||||
@@ -372,10 +374,10 @@ export function ManageLists({
|
||||
<RowFixed>
|
||||
{tempList.logoURI && <ListLogo logoURI={tempList.logoURI} size="40px" />}
|
||||
<AutoColumn gap="4px" style={{ marginLeft: '20px' }}>
|
||||
<TYPE.body fontWeight={600}>{tempList.name}</TYPE.body>
|
||||
<TYPE.main fontSize={'12px'}>
|
||||
<ThemedText.Body fontWeight={600}>{tempList.name}</ThemedText.Body>
|
||||
<ThemedText.Main fontSize={'12px'}>
|
||||
<Trans>{tempList.tokens.length} tokens</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</AutoColumn>
|
||||
</RowFixed>
|
||||
{isImported ? (
|
||||
@@ -383,9 +385,9 @@ export function ManageLists({
|
||||
<IconWrapper stroke={theme.text2} size="16px" marginRight={'10px'}>
|
||||
<CheckCircle />
|
||||
</IconWrapper>
|
||||
<TYPE.body color={theme.text2}>
|
||||
<ThemedText.Body color={theme.text2}>
|
||||
<Trans>Loaded</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</RowFixed>
|
||||
) : (
|
||||
<ButtonPrimary
|
||||
|
||||
@@ -5,11 +5,11 @@ 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'
|
||||
import { ButtonText, ExternalLink, ExternalLinkIcon, TrashIcon, TYPE } from 'theme'
|
||||
import { ButtonText, ExternalLink, ExternalLinkIcon, ThemedText, TrashIcon } from 'theme'
|
||||
import { isAddress } from 'utils'
|
||||
|
||||
import useTheme from '../../hooks/useTheme'
|
||||
@@ -81,9 +81,9 @@ export default function ManageTokens({
|
||||
<RowFixed>
|
||||
<CurrencyLogo currency={token} size={'20px'} />
|
||||
<ExternalLink href={getExplorerLink(chainId, token.address, ExplorerDataType.ADDRESS)}>
|
||||
<TYPE.main ml={'10px'} fontWeight={600}>
|
||||
<ThemedText.Main ml={'10px'} fontWeight={600}>
|
||||
{token.symbol}
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
</ExternalLink>
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
@@ -111,9 +111,9 @@ export default function ManageTokens({
|
||||
/>
|
||||
</Row>
|
||||
{searchQuery !== '' && !isAddressSearch && (
|
||||
<TYPE.error error={true}>
|
||||
<ThemedText.Error error={true}>
|
||||
<Trans>Enter valid token address</Trans>
|
||||
</TYPE.error>
|
||||
</ThemedText.Error>
|
||||
)}
|
||||
{searchToken && (
|
||||
<Card backgroundColor={theme.bg2} padding="10px 0">
|
||||
@@ -129,14 +129,14 @@ export default function ManageTokens({
|
||||
<Separator />
|
||||
<PaddedColumn gap="lg" style={{ overflow: 'auto', marginBottom: '10px' }}>
|
||||
<RowBetween>
|
||||
<TYPE.main fontWeight={600}>
|
||||
<ThemedText.Main fontWeight={600}>
|
||||
<Trans>{userAddedTokens?.length} Custom Tokens</Trans>
|
||||
</TYPE.main>
|
||||
</ThemedText.Main>
|
||||
{userAddedTokens.length > 0 && (
|
||||
<ButtonText onClick={handleRemoveAll}>
|
||||
<TYPE.blue>
|
||||
<ThemedText.Blue>
|
||||
<Trans>Clear all</Trans>
|
||||
</TYPE.blue>
|
||||
</ThemedText.Blue>
|
||||
</ButtonText>
|
||||
)}
|
||||
</RowBetween>
|
||||
@@ -144,9 +144,9 @@ export default function ManageTokens({
|
||||
</PaddedColumn>
|
||||
</Column>
|
||||
<Footer>
|
||||
<TYPE.darkGray>
|
||||
<ThemedText.DarkGray>
|
||||
<Trans>Tip: Custom tokens are stored locally in your browser</Trans>
|
||||
</TYPE.darkGray>
|
||||
</ThemedText.DarkGray>
|
||||
</Footer>
|
||||
</Wrapper>
|
||||
)
|
||||
|
||||
76
src/components/SearchModal/TokenImportCard.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { Token } from '@uniswap/sdk-core'
|
||||
import { TokenList } from '@uniswap/token-lists'
|
||||
import Card from 'components/Card'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
import CurrencyLogo from 'components/CurrencyLogo'
|
||||
import ListLogo from 'components/ListLogo'
|
||||
import { RowFixed } from 'components/Row'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
import { transparentize } from 'polished'
|
||||
import { AlertCircle } from 'react-feather'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
import { ExternalLink, ThemedText } from 'theme'
|
||||
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
|
||||
|
||||
const WarningWrapper = styled(Card)<{ highWarning: boolean }>`
|
||||
background-color: ${({ theme, highWarning }) =>
|
||||
highWarning ? transparentize(0.8, theme.red1) : transparentize(0.8, theme.yellow2)};
|
||||
width: fit-content;
|
||||
`
|
||||
|
||||
const AddressText = styled(ThemedText.Blue)`
|
||||
font-size: 12px;
|
||||
word-break: break-all;
|
||||
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
font-size: 10px;
|
||||
`}
|
||||
`
|
||||
interface TokenImportCardProps {
|
||||
list?: TokenList
|
||||
token: Token
|
||||
}
|
||||
const TokenImportCard = ({ list, token }: TokenImportCardProps) => {
|
||||
const theme = useTheme()
|
||||
const { chainId } = useActiveWeb3React()
|
||||
return (
|
||||
<Card backgroundColor={theme.bg2} padding="2rem">
|
||||
<AutoColumn gap="10px" justify="center">
|
||||
<CurrencyLogo currency={token} size={'32px'} />
|
||||
<AutoColumn gap="4px" justify="center">
|
||||
<ThemedText.Body ml="8px" mr="8px" fontWeight={500} fontSize={20}>
|
||||
{token.symbol}
|
||||
</ThemedText.Body>
|
||||
<ThemedText.DarkGray fontWeight={400} fontSize={14}>
|
||||
{token.name}
|
||||
</ThemedText.DarkGray>
|
||||
</AutoColumn>
|
||||
{chainId && (
|
||||
<ExternalLink href={getExplorerLink(chainId, token.address, ExplorerDataType.ADDRESS)}>
|
||||
<AddressText fontSize={12}>{token.address}</AddressText>
|
||||
</ExternalLink>
|
||||
)}
|
||||
{list !== undefined ? (
|
||||
<RowFixed>
|
||||
{list.logoURI && <ListLogo logoURI={list.logoURI} size="16px" />}
|
||||
<ThemedText.Small ml="6px" fontSize={14} color={theme.text3}>
|
||||
<Trans>via {list.name} token list</Trans>
|
||||
</ThemedText.Small>
|
||||
</RowFixed>
|
||||
) : (
|
||||
<WarningWrapper $borderRadius="4px" padding="4px" highWarning={true}>
|
||||
<RowFixed>
|
||||
<AlertCircle stroke={theme.red1} size="10px" />
|
||||
<ThemedText.Body color={theme.red1} ml="4px" fontSize="10px" fontWeight={500}>
|
||||
<Trans>Unknown Source</Trans>
|
||||
</ThemedText.Body>
|
||||
</RowFixed>
|
||||
</WarningWrapper>
|
||||
)}
|
||||
</AutoColumn>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default TokenImportCard
|
||||
@@ -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,18 +1,19 @@
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
import { SupportedChainId } from 'constants/chains'
|
||||
import { useActiveWeb3React } from 'hooks/web3'
|
||||
import useActiveWeb3React from 'hooks/useActiveWeb3React'
|
||||
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'
|
||||
import { ApplicationModal } from '../../state/application/actions'
|
||||
import { useModalOpen, useToggleSettingsMenu } from '../../state/application/hooks'
|
||||
import { ApplicationModal } from '../../state/application/reducer'
|
||||
import { useClientSideRouter, useExpertModeManager } from '../../state/user/hooks'
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { ButtonError } from '../Button'
|
||||
import { AutoColumn } from '../Column'
|
||||
import Modal from '../Modal'
|
||||
@@ -26,7 +27,7 @@ const StyledMenuIcon = styled(Settings)`
|
||||
width: 20px;
|
||||
|
||||
> * {
|
||||
stroke: ${({ theme }) => theme.text2};
|
||||
stroke: ${({ theme }) => theme.text1};
|
||||
}
|
||||
|
||||
:hover {
|
||||
@@ -198,16 +199,13 @@ export default function SettingsTab({ placeholderSlippage }: { placeholderSlippa
|
||||
<Text fontWeight={600} fontSize={14}>
|
||||
<Trans>Interface Settings</Trans>
|
||||
</Text>
|
||||
|
||||
{chainId === SupportedChainId.MAINNET && (
|
||||
{chainId && AUTO_ROUTER_SUPPORTED_CHAINS.includes(chainId) && (
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.black fontWeight={400} fontSize={14} color={theme.text2}>
|
||||
<Trans>Auto Router</Trans>
|
||||
</TYPE.black>
|
||||
<QuestionHelper
|
||||
text={<Trans>Use the Uniswap Labs API to get better pricing through a more efficient route.</Trans>}
|
||||
/>
|
||||
<ThemedText.Black fontWeight={400} fontSize={14} color={theme.text2}>
|
||||
<Trans>Auto Router API</Trans>
|
||||
</ThemedText.Black>
|
||||
<QuestionHelper text={<Trans>Use the Uniswap Labs API to get faster quotes.</Trans>} />
|
||||
</RowFixed>
|
||||
<Toggle
|
||||
id="toggle-optimized-router-button"
|
||||
@@ -222,12 +220,11 @@ export default function SettingsTab({ placeholderSlippage }: { placeholderSlippa
|
||||
/>
|
||||
</RowBetween>
|
||||
)}
|
||||
|
||||
<RowBetween>
|
||||
<RowFixed>
|
||||
<TYPE.black fontWeight={400} fontSize={14} color={theme.text2}>
|
||||
<ThemedText.Black fontWeight={400} fontSize={14} color={theme.text2}>
|
||||
<Trans>Expert Mode</Trans>
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
<QuestionHelper
|
||||
text={
|
||||
<Trans>Allow high price impact trades and skip the confirm screen. Use at your own risk.</Trans>
|
||||
|
||||
@@ -5,9 +5,9 @@ import styled from 'styled-components/macro'
|
||||
|
||||
import { DEFAULT_LOCALE, LOCALE_LABEL, SupportedLocale } from '../../constants/locales'
|
||||
import { navigatorLocale, useActiveLocale } from '../../hooks/useActiveLocale'
|
||||
import { StyledInternalLink, TYPE } from '../../theme'
|
||||
import { StyledInternalLink, ThemedText } from '../../theme'
|
||||
|
||||
const Container = styled(TYPE.small)`
|
||||
const Container = styled(ThemedText.Small)`
|
||||
opacity: 0.6;
|
||||
:hover {
|
||||
opacity: 1;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import styled from 'styled-components/macro'
|
||||
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
|
||||
const Wrapper = styled.button<{ isActive?: boolean; activeElement?: boolean }>`
|
||||
border-radius: 20px;
|
||||
@@ -25,7 +25,7 @@ const ToggleElement = styled.span<{ isActive?: boolean; bgColor?: string }>`
|
||||
}
|
||||
`
|
||||
|
||||
const StatusText = styled(TYPE.main)<{ isActive?: boolean }>`
|
||||
const StatusText = styled(ThemedText.Main)<{ isActive?: boolean }>`
|
||||
margin: 0 10px;
|
||||
width: 24px;
|
||||
color: ${({ theme, isActive }) => (isActive ? theme.text1 : theme.text3)};
|
||||
|
||||
@@ -5,7 +5,7 @@ import styled from 'styled-components/macro'
|
||||
import Popover, { PopoverProps } from '../Popover'
|
||||
|
||||
export const TooltipContainer = styled.div`
|
||||
width: 256px;
|
||||
max-width: 256px;
|
||||
padding: 0.6rem 1rem;
|
||||
font-weight: 400;
|
||||
word-break: break-word;
|
||||
@@ -25,6 +25,7 @@ interface TooltipContentProps extends Omit<PopoverProps, 'content'> {
|
||||
onOpen?: () => void
|
||||
// whether to wrap the content in a `TooltipContainer`
|
||||
wrap?: boolean
|
||||
disableHover?: boolean // disable the hover and content display
|
||||
}
|
||||
|
||||
export default function Tooltip({ text, ...rest }: TooltipProps) {
|
||||
@@ -52,6 +53,7 @@ export function MouseoverTooltipContent({
|
||||
content,
|
||||
children,
|
||||
onOpen: openCallback = undefined,
|
||||
disableHover,
|
||||
...rest
|
||||
}: Omit<TooltipContentProps, 'show'>) {
|
||||
const [show, setShow] = useState(false)
|
||||
@@ -61,7 +63,7 @@ export function MouseoverTooltipContent({
|
||||
}, [openCallback])
|
||||
const close = useCallback(() => setShow(false), [setShow])
|
||||
return (
|
||||
<TooltipContent {...rest} show={show} content={content}>
|
||||
<TooltipContent {...rest} show={show} content={disableHover ? null : content}>
|
||||
<div
|
||||
style={{ display: 'inline-block', lineHeight: 0, padding: '0.25rem' }}
|
||||
onMouseEnter={open}
|
||||
|
||||
@@ -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,9 +13,8 @@ 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/components'
|
||||
import { CloseIcon, CustomLightSpinner } from '../../theme'
|
||||
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
|
||||
import { TransactionSummary } from '../AccountDetails/TransactionSummary'
|
||||
import { ButtonLight, ButtonPrimary } from '../Button'
|
||||
@@ -284,11 +285,11 @@ function L2Content({
|
||||
</Text>
|
||||
</ExternalLink>
|
||||
) : (
|
||||
<div style={{ height: '17px' }}></div>
|
||||
<div style={{ height: '17px' }} />
|
||||
)}
|
||||
<Text color={theme.text3} style={{ margin: '20px 0 0 0' }} fontSize={'14px'}>
|
||||
{!secondsToConfirm ? (
|
||||
<div style={{ height: '24px' }}></div>
|
||||
<div style={{ height: '24px' }} />
|
||||
) : (
|
||||
<div>
|
||||
<Trans>Transaction completed in </Trans>
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { t, Trans } from '@lingui/macro'
|
||||
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'
|
||||
import { useSetUserSlippageTolerance, useUserSlippageTolerance, useUserTransactionTTL } from 'state/user/hooks'
|
||||
import styled, { ThemeContext } from 'styled-components/macro'
|
||||
|
||||
import { TYPE } from '../../theme'
|
||||
import { ThemedText } from '../../theme'
|
||||
import { AutoColumn } from '../Column'
|
||||
import QuestionHelper from '../QuestionHelper'
|
||||
import { RowBetween, RowFixed } from '../Row'
|
||||
@@ -85,7 +86,7 @@ const OptionCustom = styled(FancyButton)<{ active?: boolean; warning?: boolean }
|
||||
const SlippageEmojiContainer = styled.span`
|
||||
color: #f3841e;
|
||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||
display: none;
|
||||
display: none;
|
||||
`}
|
||||
`
|
||||
|
||||
@@ -93,6 +94,8 @@ interface TransactionSettingsProps {
|
||||
placeholderSlippage: Percent // varies according to the context in which the settings dialog is placed
|
||||
}
|
||||
|
||||
const THREE_DAYS_IN_SECONDS = ms`3 days` / 1000
|
||||
|
||||
export default function TransactionSettings({ placeholderSlippage }: TransactionSettingsProps) {
|
||||
const { chainId } = useActiveWeb3React()
|
||||
const theme = useContext(ThemeContext)
|
||||
@@ -142,7 +145,7 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction
|
||||
} else {
|
||||
try {
|
||||
const parsed: number = Math.floor(Number.parseFloat(value) * 60)
|
||||
if (!Number.isInteger(parsed) || parsed < 60 || parsed > 180 * 60) {
|
||||
if (!Number.isInteger(parsed) || parsed < 60 || parsed > THREE_DAYS_IN_SECONDS) {
|
||||
setDeadlineError(DeadlineError.InvalidInput)
|
||||
} else {
|
||||
setDeadline(parsed)
|
||||
@@ -160,9 +163,9 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction
|
||||
<AutoColumn gap="md">
|
||||
<AutoColumn gap="sm">
|
||||
<RowFixed>
|
||||
<TYPE.black fontWeight={400} fontSize={14} color={theme.text2}>
|
||||
<ThemedText.Black fontWeight={400} fontSize={14} color={theme.text2}>
|
||||
<Trans>Slippage tolerance</Trans>
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
<QuestionHelper
|
||||
text={
|
||||
<Trans>Your transaction will revert if the price changes unfavorably by more than this percentage.</Trans>
|
||||
@@ -229,11 +232,11 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction
|
||||
{showCustomDeadlineRow && (
|
||||
<AutoColumn gap="sm">
|
||||
<RowFixed>
|
||||
<TYPE.black fontSize={14} fontWeight={400} color={theme.text2}>
|
||||
<ThemedText.Black fontSize={14} fontWeight={400} color={theme.text2}>
|
||||
<Trans>Transaction deadline</Trans>
|
||||
</TYPE.black>
|
||||
</ThemedText.Black>
|
||||
<QuestionHelper
|
||||
text={t`Your transaction will revert if it is pending for more than this period of time.`}
|
||||
text={<Trans>Your transaction will revert if it is pending for more than this period of time.</Trans>}
|
||||
/>
|
||||
</RowFixed>
|
||||
<RowFixed>
|
||||
@@ -255,9 +258,9 @@ export default function TransactionSettings({ placeholderSlippage }: Transaction
|
||||
color={deadlineError ? 'red' : ''}
|
||||
/>
|
||||
</OptionCustom>
|
||||
<TYPE.body style={{ paddingLeft: '8px' }} fontSize={14}>
|
||||
<ThemedText.Body style={{ paddingLeft: '8px' }} fontSize={14}>
|
||||
<Trans>minutes</Trans>
|
||||
</TYPE.body>
|
||||
</ThemedText.Body>
|
||||
</RowFixed>
|
||||
</AutoColumn>
|
||||
)}
|
||||
|
||||