Compare commits

...

70 Commits

Author SHA1 Message Date
Moody Salem
dbbffd17a6 fix: improve cancel/retry detection 2021-05-25 13:48:23 -05:00
Moody Salem
85d8b1e514 fix: improve privacy of the claim popup (#1698)
* fix: improve privacy of the claim popup

fixes https://github.com/Uniswap/uniswap-interface/issues/1337

* fix unhandled rejection

* clean up the claim code a bit more

* working claim fetch

* working claim fetch

* trigger some events on the claim popup
2021-05-24 18:15:12 -05:00
Moody Salem
739986ba66 chore: update README.md twitter link 2021-05-24 12:11:59 -05:00
Moody Salem
e9148b30c9 chore: bump v3-sdk 2021-05-21 20:21:41 -05:00
Moody Salem
6e46f25bd4 improvement: use the block number in receipts to update the app state (#1681) 2021-05-21 19:55:44 -05:00
Justin Domingue
02269e9376 fix: handle usdc directly in useUSDCPrice (#1683) 2021-05-21 16:45:39 -07:00
Moody Salem
9822e68d5a remove console.log accidentally committed to swap 2021-05-21 14:35:48 -05:00
Justin Domingue
9d1556b777 feat(pool): add toggle to hide inactive positions (#1676)
* feat(pool): add toggle to hide inactive positions in pools page

* cleanup code

* keep positions sorted, but move zero liquidity positions to end

* simplified sorting logic
2021-05-21 12:22:40 -07:00
Justin Domingue
858b6afb2f improvement(swap): hide swap placeholders to reduce visual complexity (#1678)
* resolve #1646: hide swap placeholders to reduce visual complexity

* use empty string over space
2021-05-21 10:44:41 -07:00
Moody Salem
da14d8fd5b chore: update links in README.md 2021-05-21 10:28:39 -05:00
Moody Salem
3aaa3fef89 fix: wrap/unwrap should not show computed trade details 2021-05-21 10:09:11 -05:00
Moody Salem
bc243e1c07 refactor: some clean up of the common bases code, add WETH 2021-05-21 10:01:58 -05:00
Moody Salem
679a58daf4 unwrapped token is not needed 2021-05-21 09:39:32 -05:00
Moody Salem
eff512deb8 bump sdk-core 2021-05-21 09:34:46 -05:00
Moody Salem
562b402293 feat: use multicall for argent wallets in swap and v3 add liquidity (#1387)
* use argent wallet contract in swap callback

* maybe working swap callback

* chore(v3): trigger a breaking release

BREAKING CHANGE: trigger a major release for the uniswap interface to indicate it now supports swapping and liquidity provision against uniswap protocol v3

* fix the value

* improve the error coverage

* retry more frequently, couple more error nits

* the is argent call was being sketchy

* get it working for add liquidity

* `0x0` for v2 swaps too

* small nits in position page

* fix import

* fix compiler error
2021-05-21 09:30:07 -05:00
Moody Salem
99ad4ae44c feat: ETH2x-FLI to ETH2X pair routing (#1644) 2021-05-20 23:53:28 -05:00
Moody Salem
6e52a43584 fix: remove the click to flip price on the position list item
fixes https://github.com/Uniswap/uniswap-interface/issues/1436
2021-05-20 23:09:14 -05:00
Moody Salem
d4c5d3e8c8 chore: run eslint 2021-05-20 20:14:55 -05:00
Moody Salem
a6e46623ad chore: bump ethers and regenerate lockfile (#1666)
* bump ethers

* regenerate lockfile

* update code style

* Fix code style issues with ESLint

* remove unused useRef

Co-authored-by: Lint Action <lint-action@samuelmeuli.com>
2021-05-20 20:11:32 -05:00
Justin Domingue
9353c7838c fix: catch errors from Pair.getLiquidityMinted (#1668) 2021-05-20 16:14:23 -07:00
TeaTwo
816249b4b1 fix: replace jazzicon to @metamask/jazzicon (#1659) 2021-05-20 15:10:39 -07:00
Moody Salem
70292a5512 do not ask for approve for argent wallet migrate 2021-05-20 16:36:51 -05:00
Justin Domingue
ecdbb4a49f revert: "fix: handle insufficient input amount error in v2 add liquidity (#1652)" (#1665)
This reverts commit 307a995a50.
2021-05-20 11:08:07 -07:00
Justin Domingue
536a5d99de fix: fix typo is getLiquidityMinted catch (#1663) 2021-05-20 10:11:42 -07:00
Noah Zinsmeister
ffc2015595 invalidate stale requests in useV3PositionFees 2021-05-20 12:31:46 -04:00
Moody Salem
5e30a4b4ad refactor: bump to latest sdk (#1657) 2021-05-20 11:21:40 -05:00
Moody Salem
b14da2844d chore: remove all tick lens stuff that is not used 2021-05-19 17:35:59 -05:00
Justin Domingue
d3c04b7246 fix: stabilize fiat value by fixing max hop and increasing USDC out to 100k (#1651)
* stablizie fiat value by fixing max hop and increasing USDC out to 100k

* move maxHops and singleHopOnly to parameter of useV2TradeExactOut

* remove single hop param
2021-05-19 13:21:27 -07:00
Justin Domingue
307a995a50 fix: handle insufficient input amount error in v2 add liquidity (#1652)
* handle insufficient input amount error in mint/hooks

* use error.isInsufficientInputAmountError

* fixed typo
2021-05-19 13:18:32 -07:00
Moody Salem
46911593e5 fix: removing and adding liquidity when the price of the pool is at the min/max price
fixes https://github.com/Uniswap/uniswap-interface/issues/1641
2021-05-18 19:17:05 -05:00
Justin Domingue
1ac36c7b6b fix: display pool shares < 10% (#1639)
* fix: display pool shares < 10%

* set bips to 1/10_000
2021-05-18 17:07:06 -05:00
Moody Salem
8e055df447 refactor: make the price computation in v2 add liquidity a little safer 2021-05-18 16:06:54 -05:00
Moody Salem
02ecd727eb fix: do not crash when user switches eth -> weth in add (#1637)
* fix: do not crash when user switches eth -> weth in add

* linting error
2021-05-18 16:52:34 -04:00
Moody Salem
adc8429bdc fix: working copy to clipboard button in walletconnect qr code modal (#1636)
fixes https://github.com/Uniswap/uniswap-interface/issues/1488
2021-05-18 15:22:53 -05:00
Moody Salem
458d34d96c give the check a name 2021-05-18 15:14:33 -05:00
Justin Domingue
6a4fa0c9bf fix: division by 0 error when both reserves are 0 (#1632)
* set pair to invalid if both reserves are 0

* consider reserve0/1=0 as no liquidity
2021-05-18 12:50:28 -07:00
Moody Salem
7fc9a655fc fix: improve rendering of fee dollar values 2021-05-18 14:35:02 -05:00
Moody Salem
8d3babd015 refactor: more separation by chain of addresses 2021-05-18 14:33:03 -05:00
Moody Salem
75ed4c5dae fix: missed a memo for fetching v2 pairs (#1635) 2021-05-18 14:21:06 -05:00
Justin Domingue
58dfadb13c handle custom token overflow (#1634) 2021-05-18 11:32:09 -07:00
Moody Salem
3d5f5f783c chore: check pr titles 2021-05-18 13:26:23 -05:00
jab416171
ecb7132843 Add etherscan links for the two tokens in the pool on the NFT page (#1593)
* Add etherscan links for the two tokens in the pool on the NFT page

* moving linked tokens to a function

added arrow to show that it's a link
added check for eth

* clean up impl

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2021-05-18 13:34:20 -04:00
Justin Domingue
980eb8a518 Leverage Trade api for USDC price with v2 & v3 support (#1610)
* calculate usdc price using trade exact out

* fix typo

* add support for non-mainnet

* use fromRawAmount instead of tryParseAmount
2021-05-18 10:07:37 -07:00
Noah Zinsmeister
a11e89c0e1 hide WETH toggle for non-WETH pairs (#1633)
* only show weth toggle for eth pairs

* revert unwrappedToken change
2021-05-18 12:58:42 -04:00
Moody Salem
3efe5268ae refactor: break down some constants files and move some stuff around 2021-05-18 10:31:22 -05:00
Moody Salem
3f43a8fbaf npx not needed 2021-05-18 09:28:30 -05:00
Ian Lapham
cccf149568 fix: update tick parsing to handle min/max prices (#1613)
* update tick parsing to handle min/max prices

* remove fee tier step
2021-05-17 20:19:15 -04:00
Justin Domingue
604ea49567 Revert "lazy load uncommon routes to reduce initial bundle size (#1597)" (#1611)
This reverts commit 4c0c6dfde6.
2021-05-17 11:24:31 -07:00
Justin Domingue
4c0c6dfde6 lazy load uncommon routes to reduce initial bundle size (#1597) 2021-05-17 11:03:49 -07:00
Noah Zinsmeister
a160bb8f02 Merge remote-tracking branch 'refs/remotes/origin/main' 2021-05-17 11:54:17 -04:00
Noah Zinsmeister
e86946b104 bump to sdk version that fixes mint slippage bug
closes #1573
2021-05-17 11:54:06 -04:00
Moody Salem
03108b981e update README.md 2021-05-17 10:14:35 -05:00
Moody Salem
ae27e245b4 replace the message when we fail to parse an error 2021-05-14 14:13:13 -05:00
Moody Salem
897f354202 chore: bump mathieudutour/github-tag-action to latest release version to fix generated changelogs
https://github.com/Uniswap/uniswap-interface/issues/1572
2021-05-14 10:56:57 -05:00
Moody Salem
4745052f0e fix: break words in swap error messages (fixes https://github.com/Uniswap/uniswap-interface/issues/1462) 2021-05-14 10:50:55 -05:00
Moody Salem
5bc5d6504e improve the text slightly 2021-05-14 08:45:17 -05:00
Moody Salem
7a0b85bf41 improvement: estimated amounts instead of minimum/maximums (#1570)
* estimated amounts instead of minimum/maximums

* missed a spot

* fix linting error
2021-05-14 07:15:53 -05:00
OMGspidertanx
534afb3278 fix: tweak element sizing/wraping in approve button (#1569) 2021-05-14 07:15:41 -05:00
Moody Salem
7d71af353e perf: debounce localStorage save 2021-05-14 07:10:25 -05:00
Moody Salem
af6098bfe5 feat: allow walletconnect on testnets (#1389)
* try walletconnect multinetwork

* clean up environment variables for multinetwork

* remove irrelevant config

* move some stuff around

Co-authored-by: Noah Zinsmeister <noahwz@gmail.com>
2021-05-13 17:46:30 -05:00
Moody Salem
fce29bb36f fix: division by 0 when computing the USDC value of tokens that do not have a v2 pair (fixes https://github.com/Uniswap/uniswap-interface/issues/1566) 2021-05-13 17:27:34 -05:00
Noah Zinsmeister
4517af39ba don't jump on mouse exit (#1565) 2021-05-13 17:06:25 -04:00
Noah Zinsmeister
b40163ce05 allow fee collection/liquidity removal in weth (#1553)
* add dummy flags for burn/collect as weth

* add toggles

* clean up toggle position

* only show weth toggle if collection is possible
2021-05-13 14:49:54 -04:00
Moody Salem
809902efec fixes https://github.com/Uniswap/uniswap-interface/issues/1386 2021-05-13 12:17:34 -05:00
Noah Zinsmeister
70be9894fa bump walletlink connector (#1563) 2021-05-13 12:35:17 -04:00
Moody Salem
698ad5bac9 bump v3 sdk 2021-05-13 11:21:07 -05:00
Moody Salem
cd37b7533d put the error reason in the message so we can differentiate between unrecognized errors 2021-05-13 11:16:37 -05:00
Justin Domingue
c0cd6a1c8d handle ape= query parameter (#1555)
* handle expert_mode= and ape= query parameters

* removed console log

* use useParsedQueryString

* only handle setting ape mode
2021-05-13 12:01:58 -04:00
Moody Salem
f6245d1093 retry more frequently, couple more error nits 2021-05-13 10:51:06 -05:00
Moody Salem
83c784f7c0 improve the error coverage 2021-05-13 10:51:06 -05:00
177 changed files with 3408 additions and 3402 deletions

3
.env
View File

@@ -1,5 +1,4 @@
REACT_APP_CHAIN_ID="1"
REACT_APP_NETWORK_URL="https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
REACT_APP_WALLETCONNECT_BRIDGE_URL="https://uniswap.bridge.walletconnect.org"
# Because we use storybook which has its own babel-loader dependency @ 8.2.2, where react-scripts uses 8.1.0
SKIP_PREFLIGHT_CHECK=true

View File

@@ -1,5 +1,4 @@
REACT_APP_CHAIN_ID="1"
REACT_APP_NETWORK_URL="https://mainnet.infura.io/v3/099fc58e0de9451d80b18d7c74caa7c1"
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"

17
.github/workflows/check-pr-title.yaml vendored Normal file
View File

@@ -0,0 +1,17 @@
name: "Check PR Title"
on:
pull_request_target:
types:
- opened
- edited
- synchronize
jobs:
check-pr-title:
name: Check PR Title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v3.4.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -19,7 +19,7 @@ jobs:
- name: Bump version and push tag
id: github_tag_action
uses: mathieudutour/github-tag-action@v4.5
uses: mathieudutour/github-tag-action@331898d5052eedac9b15fec867b5ba66ebf9b692
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
release_branches: .*
@@ -94,19 +94,13 @@ jobs:
The latest release is always accessible via our alias to the Cloudflare IPFS gateway at [app.uniswap.org](https://app.uniswap.org).
You can also access the Uniswap Interface directly from an IPFS gateway.
The Uniswap interface uses [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to store your settings.
**Beware** that other sites you access via the _same_ IPFS gateway can read and modify your settings on the Uniswap interface without your permission.
You can avoid this issue by using a subdomain IPFS gateway, or our alias to the latest release at [app.uniswap.org](https://app.uniswap.org).
The preferred URLs below are safe to use to access this specific release.
**BEWARE**: The Uniswap interface uses [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to remember your settings, such as which tokens you have imported.
**You should always use an IPFS gateway that enforces origin separation**, or our alias to the latest release at [app.uniswap.org](https://app.uniswap.org).
Your Uniswap settings are never remembered across different URLs.
Preferred URLs:
IPFS gateways:
- https://${{ steps.convert_cidv0.outputs.cidv1 }}.ipfs.dweb.link/
- https://${{ steps.convert_cidv0.outputs.cidv1 }}.ipfs.cf-ipfs.com/
- [ipfs://${{ steps.upload.outputs.hash }}/](ipfs://${{ steps.upload.outputs.hash }}/)
Other IPFS gateways:
- https://cloudflare-ipfs.com/ipfs/${{ steps.upload.outputs.hash }}/
- https://ipfs.infura.io/ipfs/${{ steps.upload.outputs.hash }}/
- https://ipfs.io/ipfs/${{ steps.upload.outputs.hash }}/
${{ needs.bump_version.outputs.changelog }}

View File

@@ -4,7 +4,7 @@ import { createWeb3ReactRoot, Web3ReactProvider } from '@web3-react/core'
import React from 'react'
import { Provider as StoreProvider } from 'react-redux'
import { ThemeProvider as SCThemeProvider } from 'styled-components'
import { NetworkContextName } from '../src/constants'
import { NetworkContextName } from '../src/constants/misc'
import store from '../src/state'
import { FixedGlobalStyle, theme, ThemedGlobalStyle } from '../src/theme'
import getLibrary from '../src/utils/getLibrary'

View File

@@ -10,11 +10,14 @@ 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/)
- Twitter: [@UniswapProtocol](https://twitter.com/UniswapProtocol)
- Twitter: [@Uniswap](https://twitter.com/Uniswap)
- Reddit: [/r/Uniswap](https://www.reddit.com/r/Uniswap/)
- Email: [contact@uniswap.org](mailto:contact@uniswap.org)
- Discord: [Uniswap](https://discord.gg/FCfyBSbCU5)
- Whitepaper: [Link](https://hackmd.io/C-DvwDSfSxuh-Gd4WKE_ig)
- Whitepapers:
- [V1](https://hackmd.io/C-DvwDSfSxuh-Gd4WKE_ig)
- [V2](https://uniswap.org/whitepaper.pdf)
- [V3](https://uniswap.org/whitepaper-v3.pdf)
## Accessing the Uniswap Interface
@@ -27,7 +30,7 @@ or visit [app.uniswap.org](https://app.uniswap.org).
### Install Dependencies
```bash
yarn
yarn install
```
### Run
@@ -36,26 +39,22 @@ yarn
yarn start
```
### Configuring the environment (optional)
To have the interface default to a different network when a wallet is not connected:
1. Make a copy of `.env` named `.env.local`
2. Change `REACT_APP_NETWORK_ID` to `"{YOUR_NETWORK_ID}"`
3. Change `REACT_APP_NETWORK_URL` to e.g. `"https://{YOUR_NETWORK_ID}.infura.io/v3/{YOUR_INFURA_KEY}"`
Note that the interface only works on testnets where both
[Uniswap V2](https://uniswap.org/docs/v2/smart-contracts/factory/) and
[multicall](https://github.com/makerdao/multicall) are deployed.
The interface will not work on other networks.
## Contributions
**Please open all pull requests against the `main` branch.**
CI checks will run against all PRs.
## Accessing Uniswap Interface V1
## Accessing Uniswap V2
The Uniswap Interface supports swapping against, and migrating or removing liquidity from Uniswap V1. However,
if you would like to use Uniswap V1, the Uniswap V1 interface for mainnet and testnets is accessible via IPFS gateways
The Uniswap Interface supports swapping, adding liquidity, removing liquidity and migrating liquidity for
Uniswap protocol V2.
- Swap on Uniswap V2: https://app.uniswap.org/#/swap?use=v2
- View V2 liquidity: https://app.uniswap.org/#/pool/v2
- Add V2 liquidity: https://app.uniswap.org/#/add/v2
- Migrate V2 liquidity to V3: https://app.uniswap.org/#/migrate/v2
## Accessing Uniswap V1
The Uniswap V1 interface for mainnet and testnets is accessible via IPFS gateways
linked from the [v1.0.0 release](https://github.com/Uniswap/uniswap-interface/releases/tag/v1.0.0).

View File

@@ -5,7 +5,8 @@
"private": true,
"devDependencies": {
"@emotion/core": "^11.0.0",
"@ethersproject/experimental": "^5.0.1",
"@ethersproject/experimental": "^5.2.0",
"@metamask/jazzicon": "^2.0.0",
"@popperjs/core": "^2.4.4",
"@reach/dialog": "^0.10.3",
"@reach/portal": "^0.10.3",
@@ -48,16 +49,16 @@
"@uniswap/token-lists": "^1.0.0-beta.19",
"@uniswap/v2-core": "1.0.0",
"@uniswap/v2-periphery": "^1.1.0-beta.0",
"@uniswap/v2-sdk": "^3.0.0-alpha.0",
"@uniswap/v2-sdk": "^3.0.0-alpha.2",
"@uniswap/v3-core": "1.0.0",
"@uniswap/v3-periphery": "1.0.0",
"@uniswap/v3-sdk": "^3.0.0-alpha.1",
"@uniswap/v3-sdk": "^3.0.0-alpha.9",
"@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": "^6.1.1",
"@web3-react/walletlink-connector": "^6.0.9",
"@web3-react/walletconnect-connector": "^6.2.0",
"@web3-react/walletlink-connector": "^6.2.0",
"ajv": "^6.12.3",
"cids": "^1.0.0",
"copy-to-clipboard": "^3.2.0",
@@ -68,12 +69,11 @@
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-react": "^7.19.0",
"eslint-plugin-react-hooks": "^4.0.0",
"ethers": "^5.0.7",
"ethers": "^5.2.0",
"i18next": "^15.0.9",
"i18next-browser-languagedetector": "^3.0.1",
"i18next-xhr-backend": "^2.0.1",
"inter-ui": "^3.13.1",
"jazzicon": "^1.5.0",
"lightweight-charts": "^3.3.0",
"lodash.flatmap": "^4.5.0",
"luxon": "^1.25.0",
@@ -116,10 +116,13 @@
"workbox-routing": "^6.1.0",
"workbox-strategies": "^6.1.0"
},
"resolutions": {
"@walletconnect/web3-provider": "1.4.2-rc.2"
},
"scripts": {
"compile-contract-types": "yarn compile-external-abi-types && yarn compile-v3-contract-types",
"compile-external-abi-types": "npx typechain --target ethers-v5 --out-dir src/abis/types './src/abis/**/*.json'",
"compile-v3-contract-types": "npx typechain --target ethers-v5 --out-dir src/types/v3 './node_modules/@uniswap/?(v3-core|v3-periphery)/artifacts/contracts/**/*.json'",
"compile-external-abi-types": "typechain --target ethers-v5 --out-dir src/abis/types './src/abis/**/*.json'",
"compile-v3-contract-types": "typechain --target ethers-v5 --out-dir src/types/v3 './node_modules/@uniswap/?(v3-core|v3-periphery)/artifacts/contracts/**/*.json'",
"build": "yarn compile-contract-types && react-scripts build",
"integration-test": "start-server-and-test 'serve build -l 3000' http://localhost:3000 'cypress run'",
"postinstall": "yarn compile-contract-types",

View File

@@ -0,0 +1,61 @@
[
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "_transactions",
"type": "tuple[]"
}
],
"name": "wc_multiCall",
"outputs": [
{
"internalType": "bytes[]",
"name": "",
"type": "bytes[]"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "_msgHash",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "_signature",
"type": "bytes"
}
],
"name": "isValidSignature",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "view",
"type": "function"
}
]

View File

@@ -1,55 +0,0 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "_factoryV1",
"type": "address"
},
{
"internalType": "address",
"name": "_router",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amountTokenMin",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "amountETHMin",
"type": "uint256"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
}
],
"name": "migrate",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]

View File

@@ -1,6 +0,0 @@
import { Interface } from '@ethersproject/abi'
import { abi as STAKING_REWARDS_ABI } from '@uniswap/liquidity-staker/build/StakingRewards.json'
const STAKING_REWARDS_INTERFACE = new Interface(STAKING_REWARDS_ABI)
export { STAKING_REWARDS_INTERFACE }

View File

@@ -2,10 +2,10 @@ import React from 'react'
import styled from 'styled-components/macro'
import { CheckCircle, Triangle } from 'react-feather'
import { useActiveWeb3React } from '../../hooks'
import { getEtherscanLink } from '../../utils'
import { useActiveWeb3React } from '../../hooks/web3'
import { ExternalLink } from '../../theme'
import { useAllTransactions } from '../../state/transactions/hooks'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { RowFixed } from '../Row'
import Loader from '../Loader'
@@ -49,7 +49,11 @@ export default function Transaction({ hash }: { hash: string }) {
return (
<TransactionWrapper>
<TransactionState href={getEtherscanLink(chainId, hash, 'transaction')} pending={pending} success={success}>
<TransactionState
href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}
pending={pending}
success={success}
>
<RowFixed>
<TransactionStatusText>{summary ?? hash} </TransactionStatusText>
</RowFixed>

View File

@@ -1,17 +1,17 @@
import React, { useCallback, useContext } from 'react'
import { useDispatch } from 'react-redux'
import styled, { ThemeContext } from 'styled-components'
import { useActiveWeb3React } from '../../hooks'
import { SUPPORTED_WALLETS } from '../../constants/wallet'
import { useActiveWeb3React } from '../../hooks/web3'
import { AppDispatch } from '../../state'
import { clearAllTransactions } from '../../state/transactions/actions'
import { shortenAddress } from '../../utils'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { AutoRow } from '../Row'
import Copy from './Copy'
import Transaction from './Transaction'
import { SUPPORTED_WALLETS } from '../../constants'
import { ReactComponent as Close } from '../../assets/images/x.svg'
import { getEtherscanLink } from '../../utils'
import { injected, walletconnect, walletlink, fortmatic, portis } from '../../connectors'
import CoinbaseWalletIcon from '../../assets/images/coinbaseWalletIcon.svg'
import WalletConnectIcon from '../../assets/images/walletConnectIcon.svg'
@@ -354,7 +354,7 @@ export default function AccountDetails({
<AddressLink
hasENS={!!ENSName}
isENS={true}
href={chainId && getEtherscanLink(chainId, ENSName, 'address')}
href={getExplorerLink(chainId, ENSName, ExplorerDataType.ADDRESS)}
>
<LinkIcon size={16} />
<span style={{ marginLeft: '4px' }}>View on Etherscan</span>
@@ -376,7 +376,7 @@ export default function AccountDetails({
<AddressLink
hasENS={!!ENSName}
isENS={false}
href={getEtherscanLink(chainId, account, 'address')}
href={getExplorerLink(chainId, account, ExplorerDataType.ADDRESS)}
>
<LinkIcon size={16} />
<span style={{ marginLeft: '4px' }}>View on Etherscan</span>

View File

@@ -1,11 +1,11 @@
import React, { useContext, useCallback } from 'react'
import styled, { ThemeContext } from 'styled-components'
import useENS from '../../hooks/useENS'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { ExternalLink, TYPE } from '../../theme'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { AutoColumn } from '../Column'
import { RowBetween } from '../Row'
import { getEtherscanLink } from '../../utils'
const InputPanel = styled.div`
${({ theme }) => theme.flexColumnNoWrap}
@@ -102,7 +102,10 @@ export default function AddressInputPanel({
Recipient
</TYPE.black>
{address && chainId && (
<ExternalLink href={getEtherscanLink(chainId, name ?? address, 'address')} style={{ fontSize: '14px' }}>
<ExternalLink
href={getExplorerLink(chainId, name ?? address, ExplorerDataType.ADDRESS)}
style={{ fontSize: '14px' }}
>
(View on Etherscan)
</ExternalLink>
)}

View File

@@ -1,6 +1,14 @@
import React, { ReactNode, useMemo } from 'react'
import { BLOCKED_ADDRESSES } from '../../constants'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
// SDN OFAC addresses
const BLOCKED_ADDRESSES: string[] = [
'0x7F367cC41522cE07553e823bf3be79A889DEbe1B',
'0xd882cFc20F52f2599D84b8e8D58C7FB62cfE344b',
'0x901bb9583b24D97e995513C6778dc6888AB6870e',
'0xA7e5d5A720f06526557c513402f2e6B5fA20b008',
'0x8576aCC5C05D6Ce88f4e49bf65BdF0C62F91353C',
]
export default function Blocklist({ children }: { children: ReactNode }) {
const { account } = useActiveWeb3React()

View File

@@ -24,8 +24,8 @@ export function FiatValue({
return (
<TYPE.body fontSize={14} color={fiatValue ? theme.text2 : theme.text4}>
{fiatValue ? '~' : ''}$
<HoverInlineText text={fiatValue ? fiatValue?.toSignificant(6, { groupSeparator: ',' }) : '-'} />{' '}
{fiatValue ? '~$' : ''}
<HoverInlineText text={fiatValue ? fiatValue?.toSignificant(6, { groupSeparator: ',' }) : ''} />{' '}
{priceImpact ? (
<span style={{ color: priceImpactColor }}> ({priceImpact.multiply(-1).toSignificant(3)}%)</span>
) : null}

View File

@@ -12,7 +12,7 @@ import { RowBetween, RowFixed } from '../Row'
import { TYPE } from '../../theme'
import { Input as NumericalInput } from '../NumericalInput'
import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { useTranslation } from 'react-i18next'
import useTheme from '../../hooks/useTheme'
import { Lock } from 'react-feather'
@@ -273,14 +273,14 @@ export default function CurrencyInputPanel({
formatTokenAmount(selectedCurrencyBalance, 4) +
' ' +
currency.symbol
: '-'}
: ''}
</TYPE.body>
{showMaxButton && selectedCurrencyBalance ? (
<StyledBalanceMax onClick={onMax}>(Max)</StyledBalanceMax>
) : null}
</RowFixed>
) : (
'-'
<span></span>
)}
<FiatValue fiatValue={fiatValue} priceImpact={priceImpact} />
</RowBetween>

View File

@@ -1,4 +1,4 @@
import { ChainId, Currency } from '@uniswap/sdk-core'
import { Currency } from '@uniswap/sdk-core'
import React, { useMemo } from 'react'
import styled from 'styled-components/macro'
import EthereumLogo from '../../assets/images/ethereum-logo.png'
@@ -37,10 +37,10 @@ export default function CurrencyLogo({
const uriLocations = useHttpLocations(currency instanceof WrappedTokenInfo ? currency.logoURI : undefined)
const srcs: string[] = useMemo(() => {
if (!currency || currency.isEther) return []
if (!currency || currency.isNative) return []
if (currency.isToken) {
const defaultUrls = currency.chainId === ChainId.MAINNET ? [getTokenLogoURL(currency.address)] : []
const defaultUrls = currency.chainId === 1 ? [getTokenLogoURL(currency.address)] : []
if (currency instanceof WrappedTokenInfo) {
return [...uriLocations, ...defaultUrls]
}
@@ -49,7 +49,7 @@ export default function CurrencyLogo({
return []
}, [currency, uriLocations])
if (currency?.isEther) {
if (currency?.isNative) {
return <StyledEthereumLogo src={EthereumLogo} size={size} style={style} {...rest} />
}

View File

@@ -1,6 +1,6 @@
import { Story } from '@storybook/react/types-6-0'
import React from 'react'
import { DAI, WBTC } from '../../constants'
import { DAI, WBTC } from '../../constants/tokens'
import Component, { DoubleCurrencyLogoProps } from './index'
export default {

View File

@@ -1,10 +1,10 @@
import React, { useState, useEffect } from 'react'
import React, { useEffect, useState } from 'react'
import styled, { keyframes } from 'styled-components'
import { TYPE, ExternalLink } from '../../theme'
import { useActiveWeb3React } from '../../hooks/web3'
import { useBlockNumber } from '../../state/application/hooks'
import { getEtherscanLink } from '../../utils'
import { useActiveWeb3React } from '../../hooks'
import { ExternalLink, TYPE } from '../../theme'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
const StyledPolling = styled.div`
position: fixed;
@@ -91,7 +91,9 @@ export default function Polling() {
)
return (
<ExternalLink href={chainId && blockNumber ? getEtherscanLink(chainId, blockNumber.toString(), 'block') : ''}>
<ExternalLink
href={chainId && blockNumber ? getExplorerLink(chainId, blockNumber.toString(), ExplorerDataType.BLOCK) : ''}
>
<StyledPolling onMouseEnter={() => setIsHover(true)} onMouseLeave={() => setIsHover(false)}>
<StyledPollingNumber breathe={isMounting} hovering={isHover}>
{blockNumber}

View File

@@ -1,11 +1,11 @@
import { ChainId, CurrencyAmount, Token } from '@uniswap/sdk-core'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import React, { 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'
import { UNI } from '../../constants/tokens'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { useMerkleDistributorContract } from '../../hooks/useContract'
import useCurrentBlockTimestamp from '../../hooks/useCurrentBlockTimestamp'
import { useTotalUniEarned } from '../../state/stake/hooks'
@@ -54,9 +54,7 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
const unclaimedUni = useTokenBalance(useMerkleDistributorContract()?.address, uni)
const circulation: CurrencyAmount<Token> | undefined = useMemo(
() =>
blockTimestamp && uni && chainId === ChainId.MAINNET
? computeUniCirculation(uni, blockTimestamp, unclaimedUni)
: totalSupply,
blockTimestamp && uni && chainId === 1 ? computeUniCirculation(uni, blockTimestamp, unclaimedUni) : totalSupply,
[blockTimestamp, chainId, totalSupply, unclaimedUni, uni]
)
@@ -116,7 +114,7 @@ export default function UniBalanceContent({ setShowUniBalanceModal }: { setShowU
<TYPE.white color="white">Total Supply</TYPE.white>
<TYPE.white color="white">{totalSupply?.toFixed(0, { groupSeparator: ',' })}</TYPE.white>
</RowBetween>
{uni && uni.chainId === ChainId.MAINNET ? (
{uni && uni.chainId === 1 ? (
<ExternalLink href={`https://info.uniswap.org/token/${uni.address}`}>View UNI Analytics</ExternalLink>
) : null}
</AutoColumn>

View File

@@ -1,4 +1,3 @@
import { ChainId } from '@uniswap/sdk-core'
import useScrollPosition from '@react-hook/window-scroll'
import React, { useState } from 'react'
import { Text } from 'rebass'
@@ -11,7 +10,7 @@ import styled from 'styled-components/macro'
import Logo from '../../assets/svg/logo.svg'
import LogoDark from '../../assets/svg/logo_white.svg'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { useDarkModeManager } from '../../state/user/hooks'
import { useETHBalances } from '../../state/wallet/hooks'
import { CardNoise } from '../earn/styled'
@@ -301,11 +300,11 @@ export const StyledMenuButton = styled.button`
}
`
const NETWORK_LABELS: { [chainId in ChainId]?: string } = {
[ChainId.RINKEBY]: 'Rinkeby',
[ChainId.ROPSTEN]: 'Ropsten',
[ChainId.GÖRLI]: 'Görli',
[ChainId.KOVAN]: 'Kovan',
const NETWORK_LABELS: { [chainId: number]: string } = {
[4]: 'Rinkeby',
[3]: 'Ropsten',
[5]: 'Görli',
[42]: 'Kovan',
}
export default function Header() {

View File

@@ -3,7 +3,6 @@ import React, { useState } from 'react'
import styled from 'styled-components/macro'
const TextWrapper = styled.span<{ margin: boolean; link?: boolean; fontSize?: string; adjustSize?: boolean }>`
cursor: auto;
margin-left: ${({ margin }) => margin && '4px'};
color: ${({ theme, link }) => (link ? theme.blue1 : theme.text1)};
font-size: ${({ fontSize }) => fontSize ?? 'inherit'};
@@ -22,7 +21,7 @@ const HoverInlineText = ({
link,
...rest
}: {
text: string
text?: string
maxCharacters?: number
margin?: boolean
adjustSize?: boolean

View File

@@ -2,8 +2,8 @@ import React, { useEffect, useRef } from 'react'
import styled from 'styled-components/macro'
import { useActiveWeb3React } from '../../hooks'
import Jazzicon from 'jazzicon'
import { useActiveWeb3React } from '../../hooks/web3'
import Jazzicon from '@metamask/jazzicon'
const StyledIdenticonContainer = styled.div`
height: 1rem;

View File

@@ -3,7 +3,7 @@ import { BookOpen, Code, Info, MessageCircle, PieChart } from 'react-feather'
import { Link } from 'react-router-dom'
import styled, { css } from 'styled-components'
import { ReactComponent as MenuIcon } from '../../assets/images/menu.svg'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { useOnClickOutside } from '../../hooks/useOnClickOutside'
import { ApplicationModal } from '../../state/application/actions'
import { useModalOpen, useToggleModal } from '../../state/application/hooks'

View File

@@ -1,5 +1,6 @@
import React, { useContext } from 'react'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { AutoColumn, ColumnCenter } from '../Column'
import styled, { ThemeContext } from 'styled-components'
@@ -8,7 +9,6 @@ import { TYPE, CloseIcon, CustomLightSpinner } from '../../theme'
import { ArrowUpCircle } from 'react-feather'
import Circle from '../../assets/images/blue-loader.svg'
import { getEtherscanLink } from '../../utils'
import { ExternalLink } from '../../theme/components'
const ConfirmOrLoadingWrapper = styled.div`
@@ -62,7 +62,10 @@ export function SubmittedView({
<AutoColumn gap="100px" justify={'center'}>
{children}
{chainId && hash && (
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')} style={{ marginLeft: '4px' }}>
<ExternalLink
href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}
style={{ marginLeft: '4px' }}
>
<TYPE.subHeader>View transaction on Etherscan</TYPE.subHeader>
</ExternalLink>
)}

View File

@@ -1,10 +1,11 @@
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import React, { useEffect } from 'react'
import React, { useCallback, useEffect } from 'react'
import ReactGA from 'react-ga'
import { X } from 'react-feather'
import styled, { keyframes } from 'styled-components'
import tokenLogo from '../../assets/images/token-logo.png'
import { ButtonPrimary } from '../../components/Button'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { ApplicationModal } from '../../state/application/actions'
import {
useModalOpen,
@@ -62,6 +63,13 @@ export default function ClaimPopup() {
// toggle for showing this modal
const showClaimModal = useModalOpen(ApplicationModal.SELF_CLAIM)
const toggleSelfClaimModal = useToggleSelfClaimModal()
const handleToggleSelfClaimModal = useCallback(() => {
ReactGA.event({
category: 'MerkleDrop',
action: 'Toggle self claim modal',
})
toggleSelfClaimModal()
}, [toggleSelfClaimModal])
// const userHasAvailableclaim = useUserHasAvailableClaim()
const userHasAvailableclaim: boolean = useUserHasAvailableClaim(account)
@@ -70,6 +78,10 @@ export default function ClaimPopup() {
// listen for available claim and show popup if needed
useEffect(() => {
if (userHasAvailableclaim) {
ReactGA.event({
category: 'MerkleDrop',
action: 'Show claim popup',
})
toggleShowClaimPopup()
}
// the toggleShowClaimPopup function changes every time the popup changes, so this will cause an infinite loop.
@@ -102,7 +114,7 @@ export default function ClaimPopup() {
</TYPE.subHeader>
</AutoColumn>
<AutoColumn style={{ zIndex: 10 }} justify="center">
<ButtonPrimary padding="8px" borderRadius="8px" width={'fit-content'} onClick={toggleSelfClaimModal}>
<ButtonPrimary padding="8px" borderRadius="8px" width={'fit-content'} onClick={handleToggleSelfClaimModal}>
Claim your UNI tokens
</ButtonPrimary>
</AutoColumn>

View File

@@ -46,7 +46,11 @@ export default function ListUpdatePopup({
removeThisPopup()
}, [auto, dispatch, listUrl, removeThisPopup])
const { added: tokensAdded, changed: tokensChanged, removed: tokensRemoved } = useMemo(() => {
const {
added: tokensAdded,
changed: tokensChanged,
removed: tokensRemoved,
} = useMemo(() => {
return diffTokenLists(oldList.tokens, newList.tokens)
}, [newList.tokens, oldList.tokens])
const numTokensChanged = useMemo(

View File

@@ -1,10 +1,10 @@
import React, { useContext } from 'react'
import { AlertCircle, CheckCircle } from 'react-feather'
import styled, { ThemeContext } from 'styled-components'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { TYPE } from '../../theme'
import { ExternalLink } from '../../theme/components'
import { getEtherscanLink } from '../../utils'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { AutoColumn } from '../Column'
import { AutoRow } from '../Row'
@@ -33,7 +33,9 @@ export default function TransactionPopup({
<AutoColumn gap="8px">
<TYPE.body fontWeight={500}>{summary ?? 'Hash: ' + hash.slice(0, 8) + '...' + hash.slice(58, 65)}</TYPE.body>
{chainId && (
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')}>View on Etherscan</ExternalLink>
<ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}>
View on Etherscan
</ExternalLink>
)}
</AutoColumn>
</RowNoFlex>

View File

@@ -4,7 +4,7 @@ import { Link } from 'react-router-dom'
import { Text } from 'rebass'
import styled from 'styled-components/macro'
import { unwrappedToken } from '../../utils/wrappedCurrency'
import { unwrappedToken } from '../../utils/unwrappedToken'
import { ButtonEmpty } from '../Button'
import { transparentize } from 'polished'
import { CardNoise } from '../earn/styled'

View File

@@ -8,10 +8,10 @@ import { Text } from 'rebass'
import styled from 'styled-components/macro'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { useTokenBalance } from '../../state/wallet/hooks'
import { currencyId } from '../../utils/currencyId'
import { unwrappedToken } from '../../utils/wrappedCurrency'
import { unwrappedToken } from '../../utils/unwrappedToken'
import { ButtonPrimary, ButtonSecondary, ButtonEmpty } from '../Button'
import { transparentize } from 'polished'
import { CardNoise } from '../earn/styled'
@@ -24,7 +24,7 @@ import CurrencyLogo from '../CurrencyLogo'
import DoubleCurrencyLogo from '../DoubleLogo'
import { RowBetween, RowFixed, AutoRow } from '../Row'
import { Dots } from '../swap/styleds'
import { BIG_INT_ZERO } from '../../constants'
import { BIG_INT_ZERO } from '../../constants/misc'
import { FixedHeightRow } from '.'
const StyledPositionCard = styled(LightCard)<{ bgColor: any }>`

View File

@@ -9,11 +9,11 @@ import { Text } from 'rebass'
import styled from 'styled-components/macro'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { useTokenBalance } from '../../state/wallet/hooks'
import { ExternalLink, TYPE } from '../../theme'
import { currencyId } from '../../utils/currencyId'
import { unwrappedToken } from '../../utils/wrappedCurrency'
import { unwrappedToken } from '../../utils/unwrappedToken'
import { ButtonPrimary, ButtonSecondary, ButtonEmpty } from '../Button'
import { transparentize } from 'polished'
import { CardNoise } from '../earn/styled'
@@ -26,7 +26,7 @@ import CurrencyLogo from '../CurrencyLogo'
import DoubleCurrencyLogo from '../DoubleLogo'
import { RowBetween, RowFixed, AutoRow } from '../Row'
import { Dots } from '../swap/styleds'
import { BIG_INT_ZERO } from '../../constants'
import { BIG_INT_ZERO } from '../../constants/misc'
export const FixedHeightRow = styled(RowBetween)`
height: 24px;

View File

@@ -1,4 +1,4 @@
import React, { useMemo, useState } from 'react'
import React, { useMemo } from 'react'
import { Position } from '@uniswap/v3-sdk'
import Badge from 'components/Badge'
import DoubleCurrencyLogo from 'components/DoubleLogo'
@@ -11,16 +11,18 @@ import { PositionDetails } from 'types/position'
import { WETH9, Price, Token, Percent } from '@uniswap/sdk-core'
import { formatPrice } from 'utils/formatTokenAmount'
import Loader from 'components/Loader'
import { unwrappedToken } from 'utils/wrappedCurrency'
import { DAI, USDC, USDT, WBTC } from '../../constants'
import { unwrappedToken } from 'utils/unwrappedToken'
import RangeBadge from 'components/Badge/RangeBadge'
import { RowFixed } from 'components/Row'
import HoverInlineText from 'components/HoverInlineText'
import { DAI, USDC, USDT, WBTC } from '../../constants/tokens'
const Row = styled(Link)`
const LinkRow = styled(Link)`
align-items: center;
border-radius: 20px;
display: flex;
cursor: pointer;
user-select: none;
justify-content: space-between;
color: ${({ theme }) => theme.text1};
margin: 8px 0;
@@ -50,6 +52,7 @@ const Row = styled(Link)`
row-gap: 24px;
`};
`
const BadgeText = styled.div`
font-weight: 500;
font-size: 14px;
@@ -65,7 +68,6 @@ const DataLineItem = styled.div`
const RangeLineItem = styled(DataLineItem)`
display: flex;
flex-direction: row;
cursor: pointer;
align-items: center;
justify-self: flex-end;
@@ -118,9 +120,7 @@ export interface PositionListItemProps {
positionDetails: PositionDetails
}
export function getPriceOrderingFromPositionForUI(
position?: Position
): {
export function getPriceOrderingFromPositionForUI(position?: Position): {
priceLower?: Price<Token, Token>
priceUpper?: Price<Token, Token>
quote?: Token
@@ -201,25 +201,20 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr
}, [liquidity, pool, tickLower, tickUpper])
// prices
let { priceLower, priceUpper, base, quote } = getPriceOrderingFromPositionForUI(position)
const inverted = token1 ? base?.equals(token1) : undefined
const currencyQuote = inverted ? currency1 : currency0
const currencyBase = inverted ? currency0 : currency1
const { priceLower, priceUpper, quote, base } = getPriceOrderingFromPositionForUI(position)
const currencyQuote = quote && unwrappedToken(quote)
const currencyBase = base && unwrappedToken(base)
// check if price is within range
const outOfRange: boolean = pool ? pool.tickCurrent < tickLower || pool.tickCurrent >= tickUpper : false
const positionSummaryLink = '/pool/' + positionDetails.tokenId
const [manuallyInverted, setManuallyInverted] = useState(true)
if (manuallyInverted) {
;[priceLower, priceUpper, base, quote] = [priceUpper?.invert(), priceLower?.invert(), quote, base]
}
const removed = liquidity?.eq(0)
return (
<Row to={positionSummaryLink}>
<LinkRow to={positionSummaryLink}>
<RowFixed>
<PrimaryPositionIdData>
<DoubleCurrencyLogo currency0={currencyBase} currency1={currencyQuote} size={18} margin />
@@ -235,42 +230,27 @@ export default function PositionListItem({ positionDetails }: PositionListItemPr
</RowFixed>
{priceLower && priceUpper ? (
<>
<RangeLineItem
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
setManuallyInverted(!manuallyInverted)
}}
>
<RangeText>
<ExtentsText>Min: </ExtentsText>
{formatPrice(priceLower, 5)}{' '}
<HoverInlineText text={manuallyInverted ? currencyQuote?.symbol ?? '' : currencyBase?.symbol ?? ''} />{' '}
{' per '}{' '}
<HoverInlineText text={manuallyInverted ? currencyBase?.symbol ?? '' : currencyQuote?.symbol ?? ''} />
</RangeText>{' '}
<HideSmall>
<DoubleArrow></DoubleArrow>{' '}
</HideSmall>
<SmallOnly>
<DoubleArrow></DoubleArrow>{' '}
</SmallOnly>
<RangeText>
<ExtentsText>Max:</ExtentsText>
{formatPrice(priceUpper, 5)}{' '}
<HoverInlineText text={manuallyInverted ? currencyQuote?.symbol ?? '' : currencyBase?.symbol ?? ''} />{' '}
{' per '}{' '}
<HoverInlineText
maxCharacters={10}
text={manuallyInverted ? currencyBase?.symbol ?? '' : currencyQuote?.symbol ?? ''}
/>
</RangeText>{' '}
</RangeLineItem>
</>
<RangeLineItem>
<RangeText>
<ExtentsText>Min: </ExtentsText>
{formatPrice(priceLower, 5)} <HoverInlineText text={currencyQuote?.symbol} /> {' per '}{' '}
<HoverInlineText text={currencyBase?.symbol ?? ''} />
</RangeText>{' '}
<HideSmall>
<DoubleArrow></DoubleArrow>{' '}
</HideSmall>
<SmallOnly>
<DoubleArrow></DoubleArrow>{' '}
</SmallOnly>
<RangeText>
<ExtentsText>Max:</ExtentsText>
{formatPrice(priceUpper, 5)} <HoverInlineText text={currencyQuote?.symbol} /> {' per '}{' '}
<HoverInlineText maxCharacters={10} text={currencyBase?.symbol} />
</RangeText>
</RangeLineItem>
) : (
<Loader />
)}
</Row>
</LinkRow>
)
}

View File

@@ -5,7 +5,7 @@ import { AutoColumn } from 'components/Column'
import { TYPE } from 'theme'
import { RowBetween, RowFixed } from 'components/Row'
import CurrencyLogo from 'components/CurrencyLogo'
import { unwrappedToken } from 'utils/wrappedCurrency'
import { unwrappedToken } from 'utils/unwrappedToken'
import { Break } from 'components/earn/styled'
import { useTranslation } from 'react-i18next'
import { Currency } from '@uniswap/sdk-core'

View File

@@ -2,8 +2,6 @@ import React from 'react'
import { Currency, Price, Token } from '@uniswap/sdk-core'
import StepCounter from 'components/InputStepCounter/InputStepCounter'
import { RowBetween } from 'components/Row'
import { useActiveWeb3React } from 'hooks'
import { wrappedCurrency } from 'utils/wrappedCurrency'
// currencyA is the base token
export default function RangeSelector({
@@ -31,9 +29,8 @@ export default function RangeSelector({
currencyB?: Currency | null
feeAmount?: number
}) {
const { chainId } = useActiveWeb3React()
const tokenA = wrappedCurrency(currencyA ?? undefined, chainId)
const tokenB = wrappedCurrency(currencyB ?? undefined, chainId)
const tokenA = (currencyA ?? undefined)?.wrapped
const tokenB = (currencyB ?? undefined)?.wrapped
const isSorted = tokenA && tokenB && tokenA.sortsBefore(tokenB)
const leftPrice = isSorted ? priceLower : priceUpper?.invert()

View File

@@ -1,8 +1,6 @@
import React from 'react'
import { Currency } from '@uniswap/sdk-core'
import { ToggleElement, ToggleWrapper } from 'components/Toggle/MultiToggle'
import { useActiveWeb3React } from 'hooks'
import { wrappedCurrency } from 'utils/wrappedCurrency'
// the order of displayed base currencies from left to right is always in sort order
// currencyA is treated as the preferred base currency
@@ -15,10 +13,8 @@ export default function RateToggle({
currencyB: Currency
handleRateToggle: () => void
}) {
const { chainId } = useActiveWeb3React()
const tokenA = wrappedCurrency(currencyA, chainId)
const tokenB = wrappedCurrency(currencyB, chainId)
const tokenA = currencyA?.wrapped
const tokenB = currencyB?.wrapped
const isSorted = tokenA && tokenB && tokenA.sortsBefore(tokenB)

View File

@@ -1,9 +1,10 @@
import React from 'react'
import { Text } from 'rebass'
import { ChainId, Currency, currencyEquals, Token, ETHER } from '@uniswap/sdk-core'
import { Currency } from '@uniswap/sdk-core'
import styled from 'styled-components/macro'
import { SUGGESTED_BASES } from '../../constants'
import { COMMON_BASES } from '../../constants/routing'
import { currencyId } from '../../utils/currencyId'
import { AutoColumn } from '../Column'
import QuestionHelper from '../QuestionHelper'
import { AutoRow } from '../Row'
@@ -30,11 +31,13 @@ export default function CommonBases({
onSelect,
selectedCurrency,
}: {
chainId?: ChainId
chainId?: number
selectedCurrency?: Currency | null
onSelect: (currency: Currency) => void
}) {
return (
const bases = typeof chainId !== 'undefined' ? COMMON_BASES[chainId] ?? [] : []
return bases.length > 0 ? (
<AutoColumn gap="md">
<AutoRow>
<Text fontWeight={500} fontSize={14}>
@@ -43,31 +46,22 @@ export default function CommonBases({
<QuestionHelper text="These tokens are commonly paired with other tokens." />
</AutoRow>
<AutoRow gap="4px">
<BaseWrapper
onClick={() => {
if (!selectedCurrency || !currencyEquals(selectedCurrency, ETHER)) {
onSelect(ETHER)
}
}}
disable={selectedCurrency?.isEther}
>
<CurrencyLogo currency={ETHER} style={{ marginRight: 8 }} />
<Text fontWeight={500} fontSize={16}>
ETH
</Text>
</BaseWrapper>
{(typeof chainId === 'number' ? SUGGESTED_BASES[chainId] ?? [] : []).map((token: Token) => {
const selected = selectedCurrency?.isToken && selectedCurrency.address === token.address
{bases.map((currency: Currency) => {
const isSelected = selectedCurrency?.equals(currency)
return (
<BaseWrapper onClick={() => !selected && onSelect(token)} disable={selected} key={token.address}>
<CurrencyLogo currency={token} style={{ marginRight: 8 }} />
<BaseWrapper
onClick={() => !isSelected && onSelect(currency)}
disable={isSelected}
key={currencyId(currency)}
>
<CurrencyLogo currency={currency} style={{ marginRight: 8 }} />
<Text fontWeight={500} fontSize={16}>
{token.symbol}
{currency.symbol}
</Text>
</BaseWrapper>
)
})}
</AutoRow>
</AutoColumn>
)
) : null
}

View File

@@ -1,9 +1,9 @@
import { Currency, CurrencyAmount, currencyEquals, Token } from '@uniswap/sdk-core'
import { Currency, CurrencyAmount, Token } from '@uniswap/sdk-core'
import React, { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react'
import { FixedSizeList } from 'react-window'
import { Text } from 'rebass'
import styled from 'styled-components/macro'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { useCombinedActiveList } from '../../state/lists/hooks'
import { WrappedTokenInfo } from '../../state/lists/wrappedTokenInfo'
import { useCurrencyBalance } from '../../state/wallet/hooks'
@@ -17,7 +17,6 @@ import { MenuItem } from './styleds'
import Loader from '../Loader'
import { isTokenOnList } from '../../utils'
import ImportRow from './ImportRow'
import { wrappedCurrency } from 'utils/wrappedCurrency'
import { LightGreyCard } from 'components/Card'
import TokenListLogo from '../../assets/svg/tokenlist.svg'
import QuestionHelper from 'components/QuestionHelper'
@@ -133,7 +132,7 @@ function CurrencyRow({
{currency.symbol}
</Text>
<TYPE.darkGray ml="0px" fontSize={'12px'} fontWeight={300}>
{currency.name} {!currency.isEther && !isOnSelectedList && customAdded && '• Added by user'}
{currency.name} {!currency.isNative && !isOnSelectedList && customAdded && '• Added by user'}
</TYPE.darkGray>
</Column>
<TokenTags currency={currency} />
@@ -197,8 +196,6 @@ export default function CurrencyList({
return currencies
}, [currencies, otherListTokens])
const { chainId } = useActiveWeb3React()
const Row = useCallback(
function TokenRow({ data, index, style }) {
const row: Currency | BreakLine = data[index]
@@ -209,11 +206,11 @@ export default function CurrencyList({
const currency = row
const isSelected = Boolean(currency && selectedCurrency && currencyEquals(selectedCurrency, currency))
const otherSelected = Boolean(currency && otherCurrency && currencyEquals(otherCurrency, currency))
const isSelected = Boolean(currency && selectedCurrency && selectedCurrency.equals(currency))
const otherSelected = Boolean(currency && otherCurrency && otherCurrency.equals(currency))
const handleSelect = () => currency && onCurrencySelect(currency)
const token = wrappedCurrency(currency, chainId)
const token = currency?.wrapped
const showImport = index > currencies.length
@@ -235,7 +232,7 @@ export default function CurrencyList({
return null
}
},
[chainId, currencies.length, onCurrencySelect, otherCurrency, selectedCurrency, setImportToken, showImportView]
[currencies.length, onCurrencySelect, otherCurrency, selectedCurrency, setImportToken, showImportView]
)
const itemKey = useCallback((index: number, data: typeof itemData) => {

View File

@@ -1,10 +1,10 @@
import { Currency, ETHER, Token } from '@uniswap/sdk-core'
import { Currency, Ether, Token } from '@uniswap/sdk-core'
import React, { KeyboardEvent, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import ReactGA from 'react-ga'
import { useTranslation } from 'react-i18next'
import { FixedSizeList } from 'react-window'
import { Text } from 'rebass'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { useAllTokens, useToken, useIsUserAddedToken, useSearchInactiveTokenLists } from '../../hooks/Tokens'
import { CloseIcon, TYPE, ButtonText, IconWrapper } from '../../theme'
import { isAddress } from '../../utils'
@@ -106,13 +106,15 @@ export function CurrencySearch({
const filteredSortedTokens = useSortedTokensByQuery(sortedTokens, debouncedQuery)
const ether = useMemo(() => chainId && Ether.onChain(chainId), [chainId])
const filteredSortedTokensWithETH: Currency[] = useMemo(() => {
const s = debouncedQuery.toLowerCase().trim()
if (s === '' || s === 'e' || s === 'et' || s === 'eth') {
return [ETHER, ...filteredSortedTokens]
return ether ? [ether, ...filteredSortedTokens] : filteredSortedTokens
}
return filteredSortedTokens
}, [debouncedQuery, filteredSortedTokens])
}, [debouncedQuery, ether, filteredSortedTokens])
const handleCurrencySelect = useCallback(
(currency: Currency) => {
@@ -140,8 +142,8 @@ export function CurrencySearch({
(e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
const s = debouncedQuery.toLowerCase().trim()
if (s === 'eth') {
handleCurrencySelect(ETHER)
if (s === 'eth' && ether) {
handleCurrencySelect(ether)
} else if (filteredSortedTokensWithETH.length > 0) {
if (
filteredSortedTokensWithETH[0].symbol?.toLowerCase() === debouncedQuery.trim().toLowerCase() ||
@@ -152,7 +154,7 @@ export function CurrencySearch({
}
}
},
[filteredSortedTokensWithETH, handleCurrencySelect, debouncedQuery]
[debouncedQuery, ether, filteredSortedTokensWithETH, handleCurrencySelect]
)
// menu ui

View File

@@ -13,10 +13,10 @@ import useTheme from 'hooks/useTheme'
import { ButtonPrimary } from 'components/Button'
import { SectionBreak } from 'components/swap/styleds'
import { useAddUserToken } from 'state/user/hooks'
import { getEtherscanLink } from 'utils'
import { useActiveWeb3React } from 'hooks'
import { useActiveWeb3React } from 'hooks/web3'
import { ExternalLink } from '../../theme/components'
import ListLogo from 'components/ListLogo'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { PaddedColumn } from './styleds'
const Wrapper = styled.div`
@@ -93,7 +93,7 @@ export function ImportToken({ tokens, list, onBack, onDismiss, handleCurrencySel
</TYPE.darkGray>
</AutoColumn>
{chainId && (
<ExternalLink href={getEtherscanLink(chainId, token.address, 'address')}>
<ExternalLink href={getExplorerLink(chainId, token.address, ExplorerDataType.ADDRESS)}>
<AddressText fontSize={12}>{token.address}</AddressText>
</ExternalLink>
)}

View File

@@ -1,5 +1,6 @@
import React, { useRef, RefObject, useCallback, useState, useMemo } from 'react'
import Column from 'components/Column'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { PaddedColumn, Separator, SearchInput } from './styleds'
import Row, { RowBetween, RowFixed } from 'components/Row'
import { TYPE, ExternalLinkIcon, TrashIcon, ButtonText, ExternalLink } from 'theme'
@@ -8,8 +9,8 @@ import styled from 'styled-components/macro'
import { useUserAddedTokens, useRemoveUserAddedToken } from 'state/user/hooks'
import { Token } from '@uniswap/sdk-core'
import CurrencyLogo from 'components/CurrencyLogo'
import { getEtherscanLink, isAddress } from 'utils'
import { useActiveWeb3React } from 'hooks'
import { isAddress } from 'utils'
import { useActiveWeb3React } from 'hooks/web3'
import Card from 'components/Card'
import ImportRow from './ImportRow'
import useTheme from '../../hooks/useTheme'
@@ -20,7 +21,7 @@ const Wrapper = styled.div`
width: 100%;
height: calc(100% - 60px);
position: relative;
padding-bottom: 60px;
padding-bottom: 80px;
`
const Footer = styled.div`
@@ -78,7 +79,7 @@ export default function ManageTokens({
<RowBetween key={token.address} width="100%">
<RowFixed>
<CurrencyLogo currency={token} size={'20px'} />
<ExternalLink href={getEtherscanLink(chainId, token.address, 'address')}>
<ExternalLink href={getExplorerLink(chainId, token.address, ExplorerDataType.ADDRESS)}>
<TYPE.main ml={'10px'} fontWeight={600}>
{token.symbol}
</TYPE.main>
@@ -86,7 +87,7 @@ export default function ManageTokens({
</RowFixed>
<RowFixed>
<TrashIcon onClick={() => removeToken(chainId, token.address)} />
<ExternalLinkIcon href={getEtherscanLink(chainId, token.address, 'address')} />
<ExternalLinkIcon href={getExplorerLink(chainId, token.address, ExplorerDataType.ADDRESS)} />
</RowFixed>
</RowBetween>
))
@@ -95,7 +96,7 @@ export default function ManageTokens({
return (
<Wrapper>
<Column style={{ width: '100%', flex: '1 1' }}>
<Column style={{ width: '100%', height: '100%', flex: '1 1' }}>
<PaddedColumn gap="14px">
<Row>
<SearchInput
@@ -121,7 +122,7 @@ export default function ManageTokens({
)}
</PaddedColumn>
<Separator />
<PaddedColumn gap="lg">
<PaddedColumn gap="lg" style={{ overflow: 'auto', marginBottom: '10px' }}>
<RowBetween>
<TYPE.main fontWeight={600}>
{userAddedTokens?.length} Custom {userAddedTokens.length === 1 ? 'Token' : 'Tokens'}

View File

@@ -1,6 +1,7 @@
import { ChainId, Currency } from '@uniswap/sdk-core'
import { Currency } from '@uniswap/sdk-core'
import React, { useContext } from 'react'
import styled, { ThemeContext } from 'styled-components'
import { getExplorerLink, ExplorerDataType } from '../../utils/getExplorerLink'
import Modal from '../Modal'
import { ExternalLink } from '../../theme'
import { Text } from 'rebass'
@@ -11,8 +12,7 @@ import { ButtonPrimary, ButtonLight } from '../Button'
import { AutoColumn, ColumnCenter } from '../Column'
import Circle from '../../assets/images/blue-loader.svg'
import MetaMaskLogo from '../../assets/images/metamask.png'
import { getEtherscanLink } from '../../utils'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import useAddTokenToMetamask from 'hooks/useAddTokenToMetamask'
const Wrapper = styled.div`
@@ -86,7 +86,7 @@ export function TransactionSubmittedContent({
}: {
onDismiss: () => void
hash: string | undefined
chainId: ChainId
chainId: number
currencyToAdd?: Currency | undefined
inline?: boolean // not in modal
}) {
@@ -113,7 +113,7 @@ export function TransactionSubmittedContent({
Transaction Submitted
</Text>
{chainId && hash && (
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')}>
<ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}>
<Text fontWeight={500} fontSize={14} color={theme.primary1}>
View on Etherscan
</Text>
@@ -184,7 +184,12 @@ export function TransactionErrorContent({ message, onDismiss }: { message: strin
</RowBetween>
<AutoColumn style={{ marginTop: 20, padding: '2rem 0' }} gap="24px" justify="center">
<AlertTriangle color={theme.red1} style={{ strokeWidth: 1.5 }} size={64} />
<Text fontWeight={500} fontSize={16} color={theme.red1} style={{ textAlign: 'center', width: '85%' }}>
<Text
fontWeight={500}
fontSize={16}
color={theme.red1}
style={{ textAlign: 'center', width: '85%', wordBreak: 'break-word' }}
>
{message}
</Text>
</AutoColumn>

View File

@@ -6,7 +6,7 @@ import QuestionHelper from '../QuestionHelper'
import { TYPE } from '../../theme'
import { AutoColumn } from '../Column'
import { RowBetween, RowFixed } from '../Row'
import { DEFAULT_DEADLINE_FROM_NOW } from 'constants/index'
import { DEFAULT_DEADLINE_FROM_NOW } from 'constants/misc'
import { darken } from 'polished'
import { useSetUserSlippageTolerance, useUserSlippageTolerance, useUserTransactionTTL } from 'state/user/hooks'

View File

@@ -1,8 +1,8 @@
import { AbstractConnector } from '@web3-react/abstract-connector'
import React from 'react'
import styled from 'styled-components/macro'
import { SUPPORTED_WALLETS } from '../../constants/wallet'
import Option from './Option'
import { SUPPORTED_WALLETS } from '../../constants'
import { injected } from '../../connectors'
import { darken } from 'polished'
import Loader from '../Loader'

View File

@@ -10,7 +10,7 @@ import MetamaskIcon from '../../assets/images/metamask.png'
import { ReactComponent as Close } from '../../assets/images/x.svg'
import { fortmatic, injected, portis } from '../../connectors'
import { OVERLAY_READY } from '../../connectors/Fortmatic'
import { SUPPORTED_WALLETS } from '../../constants'
import { SUPPORTED_WALLETS } from '../../constants/wallet'
import usePrevious from '../../hooks/usePrevious'
import { ApplicationModal } from '../../state/application/actions'
import { useModalOpen, useWalletModalToggle } from '../../state/application/hooks'

View File

@@ -4,8 +4,8 @@ import styled from 'styled-components/macro'
import { useTranslation } from 'react-i18next'
import { network } from '../../connectors'
import { useEagerConnect, useInactiveListener } from '../../hooks'
import { NetworkContextName } from '../../constants'
import { useEagerConnect, useInactiveListener } from '../../hooks/web3'
import { NetworkContextName } from '../../constants/misc'
import Loader from '../Loader'
const MessageWrapper = styled.div`

View File

@@ -10,7 +10,7 @@ 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 { NetworkContextName } from '../../constants'
import { NetworkContextName } from '../../constants/misc'
import useENSName from '../../hooks/useENSName'
import { useHasSocks } from '../../hooks/useSocksBalance'
import { useWalletModalToggle } from '../../state/application/hooks'

View File

@@ -1,4 +1,5 @@
import React, { useState } from 'react'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import Modal from '../Modal'
import { AutoColumn, ColumnCenter } from '../Column'
import styled from 'styled-components/macro'
@@ -12,13 +13,13 @@ import Circle from '../../assets/images/blue-loader.svg'
import { Text } from 'rebass'
import AddressInputPanel from '../AddressInputPanel'
import useENS from '../../hooks/useENS'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { isAddress } from 'ethers/lib/utils'
import Confetti from '../Confetti'
import { CardNoise, CardBGImage, CardBGImageSmaller } from '../earn/styled'
import { useIsTransactionPending } from '../../state/transactions/hooks'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { getEtherscanLink, shortenAddress } from '../../utils'
import { shortenAddress } from '../../utils'
const ContentWrapper = styled(AutoColumn)`
width: 100%;
@@ -181,7 +182,7 @@ export default function AddressClaimModal({ isOpen, onDismiss }: { isOpen: boole
<TYPE.subHeader color="black">Confirm this transaction in your wallet</TYPE.subHeader>
)}
{attempting && hash && !claimConfirmed && chainId && hash && (
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')} style={{ zIndex: 99 }}>
<ExternalLink href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)} style={{ zIndex: 99 }}>
View transaction on Etherscan
</ExternalLink>
)}

View File

@@ -6,13 +6,13 @@ import { Text } from 'rebass'
import styled from 'styled-components/macro'
import Circle from '../../assets/images/blue-loader.svg'
import tokenLogo from '../../assets/images/token-logo.png'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { ApplicationModal } from '../../state/application/actions'
import { useModalOpen, useToggleSelfClaimModal } from '../../state/application/hooks'
import { useClaimCallback, useUserClaimData, useUserUnclaimedAmount } from '../../state/claim/hooks'
import { useUserHasSubmittedClaim } from '../../state/transactions/hooks'
import { CloseIcon, CustomLightSpinner, ExternalLink, TYPE, UniTokenAnimated } from '../../theme'
import { getEtherscanLink } from '../../utils'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
import { ButtonPrimary } from '../Button'
import { AutoColumn, ColumnCenter } from '../Column'
import Confetti from '../Confetti'
@@ -196,7 +196,10 @@ export default function ClaimModal() {
<TYPE.subHeader color="black">Confirm this transaction in your wallet</TYPE.subHeader>
)}
{attempting && claimSubmitted && !claimConfirmed && chainId && claimTxn?.hash && (
<ExternalLink href={getEtherscanLink(chainId, claimTxn?.hash, 'transaction')} style={{ zIndex: 99 }}>
<ExternalLink
href={getExplorerLink(chainId, claimTxn?.hash, ExplorerDataType.TRANSACTION)}
style={{ zIndex: 99 }}
>
View transaction on Etherscan
</ExternalLink>
)}

View File

@@ -10,7 +10,7 @@ import { useStakingContract } from '../../hooks/useContract'
import { SubmittedView, LoadingView } from '../ModalViews'
import { TransactionResponse } from '@ethersproject/providers'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
const ContentWrapper = styled(AutoColumn)`
width: 100%;

View File

@@ -11,11 +11,11 @@ import { StakingInfo } from '../../state/stake/hooks'
import { useColor } from '../../hooks/useColor'
import { currencyId } from '../../utils/currencyId'
import { Break, CardNoise, CardBGImage } from './styled'
import { unwrappedToken } from '../../utils/wrappedCurrency'
import { unwrappedToken } from '../../utils/unwrappedToken'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useV2Pair } from '../../hooks/useV2Pairs'
import useUSDCPrice from '../../hooks/useUSDCPrice'
import { BIG_INT_SECONDS_IN_WEEK } from '../../constants'
import { BIG_INT_SECONDS_IN_WEEK } from '../../constants/misc'
const StatContainer = styled.div`
display: flex;
@@ -79,8 +79,8 @@ export default function PoolCard({ stakingInfo }: { stakingInfo: StakingInfo })
const isStaking = Boolean(stakingInfo.stakedAmount.greaterThan('0'))
// get the color of the token
const token = currency0.isEther ? token1 : token0
const WETH = currency0.isEther ? token0 : token1
const token = currency0.isNative ? token1 : token0
const WETH = currency0.isNative ? token0 : token1
const backgroundColor = useColor(token)
const totalSupplyOfStakingToken = useTotalSupply(stakingInfo.stakedAmount.currency)

View File

@@ -1,5 +1,4 @@
import React, { useState, useCallback } from 'react'
import { V2_ROUTER_ADDRESS } from '../../constants'
import { useV2LiquidityTokenPermit } from '../../hooks/useERC20Permit'
import useTransactionDeadline from '../../hooks/useTransactionDeadline'
import Modal from '../Modal'
@@ -12,12 +11,11 @@ import ProgressCircles from '../ProgressSteps'
import CurrencyInputPanel from '../CurrencyInputPanel'
import { Pair } from '@uniswap/v2-sdk'
import { Token, CurrencyAmount } from '@uniswap/sdk-core'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { maxAmountSpend } from '../../utils/maxAmountSpend'
import { usePairContract, useStakingContract } from '../../hooks/useContract'
import { usePairContract, useStakingContract, useV2RouterContract } from '../../hooks/useContract'
import { useApproveCallback, ApprovalState } from '../../hooks/useApproveCallback'
import { StakingInfo, useDerivedStakeInfo } from '../../state/stake/hooks'
import { wrappedCurrencyAmount } from '../../utils/wrappedCurrency'
import { TransactionResponse } from '@ethersproject/providers'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { LoadingView, SubmittedView } from '../ModalViews'
@@ -44,7 +42,7 @@ interface StakingModalProps {
}
export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiquidityUnstaked }: StakingModalProps) {
const { chainId, library } = useActiveWeb3React()
const { library } = useActiveWeb3React()
// track and parse user input
const [typedValue, setTypedValue] = useState('')
@@ -53,7 +51,7 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui
stakingInfo.stakedAmount.currency,
userLiquidityUnstaked
)
const parsedAmountWrapped = wrappedCurrencyAmount(parsedAmount, chainId)
const parsedAmountWrapped = parsedAmount?.wrapped
let hypotheticalRewardRate: CurrencyAmount<Token> = CurrencyAmount.fromRawAmount(stakingInfo.rewardRate.currency, '0')
if (parsedAmountWrapped?.greaterThan('0')) {
@@ -83,7 +81,8 @@ export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiqui
// approval data for stake
const deadline = useTransactionDeadline()
const { signatureData, gatherPermitSignature } = useV2LiquidityTokenPermit(parsedAmountWrapped, V2_ROUTER_ADDRESS)
const router = useV2RouterContract()
const { signatureData, gatherPermitSignature } = useV2LiquidityTokenPermit(parsedAmountWrapped, router?.address)
const [approval, approveCallback] = useApproveCallback(parsedAmount, stakingInfo.stakingRewardAddress)
const stakingContract = useStakingContract(stakingInfo.stakingRewardAddress)

View File

@@ -11,7 +11,7 @@ import { SubmittedView, LoadingView } from '../ModalViews'
import { TransactionResponse } from '@ethersproject/providers'
import { useTransactionAdder } from '../../state/transactions/hooks'
import FormattedCurrencyAmount from '../FormattedCurrencyAmount'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
const ContentWrapper = styled(AutoColumn)`
width: 100%;

View File

@@ -1,11 +1,10 @@
import { Percent, Currency, TradeType } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import React, { useContext } from 'react'
import React, { useContext, useMemo } from 'react'
import { ThemeContext } from 'styled-components'
import { TYPE } from '../../theme'
import { computePriceImpactWithMaximumSlippage } from '../../utils/computePriceImpactWithMaximumSlippage'
import { computeRealizedLPFeeAmount } from '../../utils/prices'
import { computeRealizedLPFeePercent } from '../../utils/prices'
import { AutoColumn } from '../Column'
import { RowBetween, RowFixed } from '../Row'
import FormattedPriceImpact from './FormattedPriceImpact'
@@ -19,7 +18,14 @@ export interface AdvancedSwapDetailsProps {
export function AdvancedSwapDetails({ trade, allowedSlippage }: AdvancedSwapDetailsProps) {
const theme = useContext(ThemeContext)
const realizedLPFee = computeRealizedLPFeeAmount(trade)
const { realizedLPFee, priceImpact } = useMemo(() => {
if (!trade) return { realizedLPFee: undefined, priceImpact: undefined }
const realizedLpFeePercent = computeRealizedLPFeePercent(trade)
const realizedLPFee = trade.inputAmount.multiply(realizedLpFeePercent)
const priceImpact = trade.priceImpact.subtract(realizedLpFeePercent)
return { priceImpact, realizedLPFee }
}, [trade])
return !trade ? null : (
<AutoColumn gap="8px">
@@ -30,7 +36,7 @@ export function AdvancedSwapDetails({ trade, allowedSlippage }: AdvancedSwapDeta
</TYPE.black>
</RowFixed>
<TYPE.black textAlign="right" fontSize={12} color={theme.text1}>
{realizedLPFee ? `${realizedLPFee.toSignificant(4)} ${trade.inputAmount.currency.symbol}` : '-'}
{realizedLPFee ? `${realizedLPFee.toSignificant(4)} ${realizedLPFee.currency.symbol}` : '-'}
</TYPE.black>
</RowBetween>
@@ -48,11 +54,24 @@ export function AdvancedSwapDetails({ trade, allowedSlippage }: AdvancedSwapDeta
<RowBetween>
<RowFixed>
<TYPE.black fontSize={12} fontWeight={400} color={theme.text2}>
Execution price vs. spot price
Price Impact
</TYPE.black>
</RowFixed>
<TYPE.black textAlign="right" fontSize={12} color={theme.text1}>
<FormattedPriceImpact priceImpact={computePriceImpactWithMaximumSlippage(trade, allowedSlippage)} />
<FormattedPriceImpact priceImpact={priceImpact} />
</TYPE.black>
</RowBetween>
<RowBetween>
<RowFixed>
<TYPE.black fontSize={12} fontWeight={400} color={theme.text2}>
{trade.tradeType === TradeType.EXACT_INPUT ? 'Minimum received' : 'Maximum sent'}
</TYPE.black>
</RowFixed>
<TYPE.black textAlign="right" fontSize={12} color={theme.text1}>
{trade.tradeType === TradeType.EXACT_INPUT
? `${trade.minimumAmountOut(allowedSlippage).toSignificant(6)} ${trade.outputAmount.currency.symbol}`
: `${trade.maximumAmountIn(allowedSlippage).toSignificant(6)} ${trade.inputAmount.currency.symbol}`}
</TYPE.black>
</RowBetween>

View File

@@ -1,4 +1,4 @@
import { Currency, currencyEquals, Percent, TradeType } from '@uniswap/sdk-core'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import React, { useCallback, useMemo } from 'react'
@@ -21,9 +21,9 @@ function tradeMeaningfullyDiffers(
const [tradeA, tradeB] = args
return (
tradeA.tradeType !== tradeB.tradeType ||
!currencyEquals(tradeA.inputAmount.currency, tradeB.inputAmount.currency) ||
!tradeA.inputAmount.currency.equals(tradeB.inputAmount.currency) ||
!tradeA.inputAmount.equalTo(tradeB.inputAmount) ||
!currencyEquals(tradeA.outputAmount.currency, tradeB.outputAmount.currency) ||
!tradeA.outputAmount.currency.equals(tradeB.outputAmount.currency) ||
!tradeA.outputAmount.equalTo(tradeB.outputAmount)
)
}
@@ -90,9 +90,9 @@ export default function ConfirmSwapModal({
}, [onConfirm, showAcceptChanges, swapErrorMessage, trade])
// text to show while loading
const pendingText = `Swapping ${trade?.maximumAmountIn(allowedSlippage)?.toSignificant(6)} ${
const pendingText = `Swapping ${trade?.inputAmount?.toSignificant(6)} ${
trade?.inputAmount?.currency?.symbol
} for ${trade?.minimumAmountOut(allowedSlippage)?.toSignificant(6)} ${trade?.outputAmount?.currency?.symbol}`
} for ${trade?.outputAmount?.toSignificant(6)} ${trade?.outputAmount?.currency?.symbol}`
const confirmationContent = useCallback(
() =>

View File

@@ -31,12 +31,10 @@ export const ArrowWrapper = styled.div`
margin-top: -18px;
margin-bottom: -18px;
left: calc(50% - 16px);
/* transform: rotate(90deg); */
display: flex;
justify-content: center;
align-items: center;
background-color: ${({ theme }) => theme.bg1};
/* border: 4px solid ${({ theme }) => theme.bg0}; */
z-index: 2;
`
@@ -53,15 +51,12 @@ export default function SwapModalHeader({
showAcceptChanges: boolean
onAcceptChanges: () => void
}) {
const maximumAmountIn = trade.maximumAmountIn(allowedSlippage)
const minimumAmountOut = trade.minimumAmountOut(allowedSlippage)
const theme = useContext(ThemeContext)
const [showInverted, setShowInverted] = useState<boolean>(false)
const fiatValueInput = useUSDCValue(maximumAmountIn)
const fiatValueOutput = useUSDCValue(minimumAmountOut)
const fiatValueInput = useUSDCValue(trade.inputAmount)
const fiatValueOutput = useUSDCValue(trade.outputAmount)
return (
<AutoColumn gap={'4px'} style={{ marginTop: '1rem' }}>
@@ -86,7 +81,7 @@ export default function SwapModalHeader({
fontWeight={500}
color={showAcceptChanges && trade.tradeType === TradeType.EXACT_OUTPUT ? theme.primary1 : ''}
>
{maximumAmountIn.toSignificant(6)}
{trade.inputAmount.toSignificant(6)}
</TruncatedText>
</RowFixed>
</RowBetween>
@@ -117,7 +112,7 @@ export default function SwapModalHeader({
</RowFixed>
<RowFixed gap={'0px'}>
<TruncatedText fontSize={24} fontWeight={500}>
{minimumAmountOut.toSignificant(6)}
{trade.outputAmount.toSignificant(6)}
</TruncatedText>
</RowFixed>
</RowBetween>
@@ -127,11 +122,7 @@ export default function SwapModalHeader({
<TYPE.body color={theme.text2} fontWeight={500} fontSize={14}>
{'Price:'}
</TYPE.body>
<TradePrice
price={trade.worstExecutionPrice(allowedSlippage)}
showInverted={showInverted}
setShowInverted={setShowInverted}
/>
<TradePrice price={trade.executionPrice} showInverted={showInverted} setShowInverted={setShowInverted} />
</RowBetween>
<LightCard style={{ padding: '.75rem', marginTop: '0.5rem' }}>
@@ -155,12 +146,12 @@ export default function SwapModalHeader({
</SwapShowAcceptChanges>
) : null}
{/* <AutoColumn justify="flex-start" gap="sm" style={{ padding: '.75rem 1rem' }}>
<AutoColumn justify="flex-start" gap="sm" style={{ padding: '.75rem 1rem' }}>
{trade.tradeType === TradeType.EXACT_INPUT ? (
<TYPE.italic fontWeight={400} textAlign="left" style={{ width: '100%' }}>
{`Output is estimated. You will receive at least `}
<b>
{minimumAmountOut.toSignificant(6)} {trade.outputAmount.currency.symbol}
{trade.minimumAmountOut(allowedSlippage).toSignificant(6)} {trade.outputAmount.currency.symbol}
</b>
{' or the transaction will revert.'}
</TYPE.italic>
@@ -168,12 +159,12 @@ export default function SwapModalHeader({
<TYPE.italic fontWeight={400} textAlign="left" style={{ width: '100%' }}>
{`Input is estimated. You will sell at most `}
<b>
{maximumAmountIn.toSignificant(6)} {trade.inputAmount.currency.symbol}
{trade.maximumAmountIn(allowedSlippage).toSignificant(6)} {trade.inputAmount.currency.symbol}
</b>
{' or the transaction will revert.'}
</TYPE.italic>
)}
</AutoColumn> */}
</AutoColumn>
{recipient !== null ? (
<AutoColumn justify="flex-start" gap="sm" style={{ padding: '12px 0 0 0px' }}>
<TYPE.main>

View File

@@ -6,7 +6,7 @@ import { ChevronRight } from 'react-feather'
import { Flex } from 'rebass'
import { ThemeContext } from 'styled-components'
import { TYPE } from '../../theme'
import { unwrappedToken } from 'utils/wrappedCurrency'
import { unwrappedToken } from 'utils/unwrappedToken'
function LabeledArrow({}: { fee: FeeAmount }) {
const theme = useContext(ThemeContext)

View File

@@ -7,11 +7,10 @@ import Card, { OutlineCard } from 'components/Card'
import { RowBetween, AutoRow } from 'components/Row'
import { AutoColumn } from 'components/Column'
import CurrencyLogo from 'components/CurrencyLogo'
import { useActiveWeb3React } from 'hooks'
import { getEtherscanLink } from 'utils'
import { useActiveWeb3React } from 'hooks/web3'
import { Currency, Token } from '@uniswap/sdk-core'
import { wrappedCurrency } from 'utils/wrappedCurrency'
import { useUnsupportedTokens } from '../../hooks/Tokens'
import { ExplorerDataType, getExplorerLink } from '../../utils/getExplorerLink'
const DetailsFooter = styled.div<{ show: boolean }>`
padding-top: calc(16px + 2rem);
@@ -51,7 +50,7 @@ export default function UnsupportedCurrencyFooter({
const tokens =
chainId && currencies
? currencies.map((currency) => {
return wrappedCurrency(currency, chainId)
return currency?.wrapped
})
: []
@@ -78,7 +77,7 @@ export default function UnsupportedCurrencyFooter({
<TYPE.body fontWeight={500}>{token.symbol}</TYPE.body>
</AutoRow>
{chainId && (
<ExternalLink href={getEtherscanLink(chainId, token.address, 'address')}>
<ExternalLink href={getExplorerLink(chainId, token.address, ExplorerDataType.ADDRESS)}>
<AddressText>{token.address}</AddressText>
</ExternalLink>
)}

View File

@@ -1,5 +1,5 @@
import { Percent } from '@uniswap/sdk-core'
import { ALLOWED_PRICE_IMPACT_HIGH, PRICE_IMPACT_WITHOUT_FEE_CONFIRM_MIN } from '../../constants'
import { ALLOWED_PRICE_IMPACT_HIGH, PRICE_IMPACT_WITHOUT_FEE_CONFIRM_MIN } from '../../constants/misc'
/**
* Given the price impact, get user confirmation.

View File

@@ -171,7 +171,7 @@ export function SwapCallbackError({ error }: { error: string }) {
<SwapCallbackErrorInnerAlertTriangle>
<AlertTriangle size={24} />
</SwapCallbackErrorInnerAlertTriangle>
<p>{error}</p>
<p style={{ wordBreak: 'break-word' }}>{error}</p>
</SwapCallbackErrorInner>
)
}

View File

@@ -1,4 +1,5 @@
import React, { useState } from 'react'
import { UNI } from '../../constants/tokens'
import Modal from '../Modal'
import { AutoColumn } from '../Column'
@@ -7,13 +8,12 @@ import { RowBetween } from '../Row'
import { TYPE } from '../../theme'
import { X } from 'react-feather'
import { ButtonPrimary } from '../Button'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import AddressInputPanel from '../AddressInputPanel'
import { isAddress } from 'ethers/lib/utils'
import useENS from '../../hooks/useENS'
import { useDelegateCallback } from '../../state/governance/hooks'
import { useTokenBalance } from '../../state/wallet/hooks'
import { UNI } from '../../constants'
import { LoadingView, SubmittedView } from '../ModalViews'
import { formatTokenAmount } from 'utils/formatTokenAmount'

View File

@@ -1,5 +1,6 @@
import React, { useState, useContext } from 'react'
import { useActiveWeb3React } from '../../hooks'
import { useActiveWeb3React } from '../../hooks/web3'
import { getExplorerLink, ExplorerDataType } from '../../utils/getExplorerLink'
import Modal from '../Modal'
import { AutoColumn, ColumnCenter } from '../Column'
@@ -10,7 +11,6 @@ import { X, ArrowUpCircle } from 'react-feather'
import { ButtonPrimary } from '../Button'
import Circle from '../../assets/images/blue-loader.svg'
import { useVoteCallback, useUserVotes } from '../../state/governance/hooks'
import { getEtherscanLink } from '../../utils'
import { ExternalLink } from '../../theme/components'
import { formatTokenAmount } from 'utils/formatTokenAmount'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
@@ -133,7 +133,10 @@ export default function VoteModal({ isOpen, onDismiss, proposalId, support }: Vo
<TYPE.largeHeader>Transaction Submitted</TYPE.largeHeader>
</AutoColumn>
{chainId && (
<ExternalLink href={getEtherscanLink(chainId, hash, 'transaction')} style={{ marginLeft: '4px' }}>
<ExternalLink
href={getExplorerLink(chainId, hash, ExplorerDataType.TRANSACTION)}
style={{ marginLeft: '4px' }}
>
<TYPE.subHeader>View transaction on Etherscan</TYPE.subHeader>
</ExternalLink>
)}

View File

@@ -1,15 +1,14 @@
import { ChainId } from '@uniswap/sdk-core'
import { FortmaticConnector as FortmaticConnectorCore } from '@web3-react/fortmatic-connector'
export const OVERLAY_READY = 'OVERLAY_READY'
type FormaticSupportedChains = Extract<ChainId, ChainId.MAINNET | ChainId.ROPSTEN | ChainId.RINKEBY | ChainId.KOVAN>
type FormaticSupportedChains = 1 | 3 | 4 | 42
const CHAIN_ID_NETWORK_ARGUMENT: { readonly [chainId in FormaticSupportedChains]: string | undefined } = {
[ChainId.MAINNET]: undefined,
[ChainId.ROPSTEN]: 'ropsten',
[ChainId.RINKEBY]: 'rinkeby',
[ChainId.KOVAN]: 'kovan',
[1]: undefined,
[3]: 'ropsten',
[4]: 'rinkeby',
[42]: 'kovan',
}
export class FortmaticConnector extends FortmaticConnectorCore {

View File

@@ -3,38 +3,50 @@ import { InjectedConnector } from '@web3-react/injected-connector'
import { WalletConnectConnector } from '@web3-react/walletconnect-connector'
import { WalletLinkConnector } from '@web3-react/walletlink-connector'
import { PortisConnector } from '@web3-react/portis-connector'
import getLibrary from '../utils/getLibrary'
import { FortmaticConnector } from './Fortmatic'
import { NetworkConnector } from './NetworkConnector'
import UNISWAP_LOGO_URL from '../assets/svg/logo.svg'
const NETWORK_URL = process.env.REACT_APP_NETWORK_URL
const INFURA_KEY = process.env.REACT_APP_INFURA_KEY
const FORMATIC_KEY = process.env.REACT_APP_FORTMATIC_KEY
const PORTIS_ID = process.env.REACT_APP_PORTIS_ID
const WALLETCONNECT_BRIDGE_URL = process.env.REACT_APP_WALLETCONNECT_BRIDGE_URL
export const NETWORK_CHAIN_ID: number = parseInt(process.env.REACT_APP_CHAIN_ID ?? '1')
if (typeof NETWORK_URL === 'undefined') {
throw new Error(`REACT_APP_NETWORK_URL must be a defined environment variable`)
if (typeof INFURA_KEY === 'undefined') {
throw new Error(`REACT_APP_INFURA_KEY must be a defined environment variable`)
}
const NETWORK_URLS: {
[chainId: number]: string
} = {
[1]: `https://mainnet.infura.io/v3/${INFURA_KEY}`,
[4]: `https://rinkeby.infura.io/v3/${INFURA_KEY}`,
[3]: `https://ropsten.infura.io/v3/${INFURA_KEY}`,
[5]: `https://goerli.infura.io/v3/${INFURA_KEY}`,
[42]: `https://kovan.infura.io/v3/${INFURA_KEY}`,
}
const SUPPORTED_CHAIN_IDS = [1, 4, 3, 42, 5]
export const network = new NetworkConnector({
urls: { [NETWORK_CHAIN_ID]: NETWORK_URL },
urls: NETWORK_URLS,
defaultChainId: 1,
})
let networkLibrary: Web3Provider | undefined
export function getNetworkLibrary(): Web3Provider {
return (networkLibrary = networkLibrary ?? new Web3Provider(network.provider as any))
return (networkLibrary = networkLibrary ?? getLibrary(network.provider))
}
export const injected = new InjectedConnector({
supportedChainIds: [1, 3, 4, 5, 42],
supportedChainIds: SUPPORTED_CHAIN_IDS,
})
// mainnet only
export const walletconnect = new WalletConnectConnector({
rpc: { 1: NETWORK_URL },
supportedChainIds: SUPPORTED_CHAIN_IDS,
infuraId: INFURA_KEY, // obviously a hack
bridge: WALLETCONNECT_BRIDGE_URL,
qrcode: true,
pollingInterval: 15000,
@@ -54,7 +66,7 @@ export const portis = new PortisConnector({
// mainnet only
export const walletlink = new WalletLinkConnector({
url: NETWORK_URL,
url: NETWORK_URLS[1],
appName: 'Uniswap',
appLogoUrl: UNISWAP_LOGO_URL,
})

View File

@@ -0,0 +1,30 @@
import { FACTORY_ADDRESS as V3_FACTORY_ADDRESS } from '@uniswap/v3-sdk'
import { constructSameAddressMap } from '../utils/constructSameAddressMap'
export const UNI_ADDRESS = constructSameAddressMap('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
export const MULTICALL2_ADDRESSES = constructSameAddressMap('0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696')
export const V2_ROUTER_ADDRESS = constructSameAddressMap('0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D')
export const GOVERNANCE_ADDRESS = constructSameAddressMap('0x5e4be8Bc9637f0EAA1A755019e06A68ce081D58F')
export const TIMELOCK_ADDRESS = constructSameAddressMap('0x1a9C8182C09F50C8318d769245beA52c32BE35BC')
export const MERKLE_DISTRIBUTOR_ADDRESS: { [chainId: number]: string } = {
[1]: '0x090D4613473dEE047c3f2706764f49E0821D256e',
}
export const ARGENT_WALLET_DETECTOR_ADDRESS: { [chainId: number]: string } = {
[1]: '0xeca4B0bDBf7c55E9b7925919d03CbF8Dc82537E8',
}
export const V3_CORE_FACTORY_ADDRESSES = constructSameAddressMap(V3_FACTORY_ADDRESS)
export const QUOTER_ADDRESSES = constructSameAddressMap('0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6')
export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES = constructSameAddressMap(
'0xC36442b4a4522E871399CD717aBDD847Ab11FE88'
)
export const ENS_REGISTRAR_ADDRESSES = {
[1]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
[5]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
[4]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
[3]: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
}
export const SOCKS_CONTROLLER_ADDRESSES = {
[1]: '0x65770b5283117639760beA3F867b69b3697a91dd',
}
export const SWAP_ROUTER_ADDRESSES = constructSameAddressMap('0xE592427A0AEce92De3Edee1F18E0157C05861564')
export const V3_MIGRATOR_ADDRESSES = constructSameAddressMap('0xA5644E29708357803b5A882D272c41cC0dF92B34')

View File

@@ -0,0 +1,36 @@
import { GOVERNANCE_ADDRESS, TIMELOCK_ADDRESS, UNI_ADDRESS } from './addresses'
export const COMMON_CONTRACT_NAMES: { [chainId: number]: { [address: string]: string } } = {
[1]: {
[UNI_ADDRESS[1]]: 'UNI',
[GOVERNANCE_ADDRESS[1]]: 'Governance',
[TIMELOCK_ADDRESS[1]]: 'Timelock',
},
[4]: {
[UNI_ADDRESS[4]]: 'Rinkeby UNI',
[GOVERNANCE_ADDRESS[4]]: 'Rinkeby Governance',
[TIMELOCK_ADDRESS[4]]: 'Rinkeby Timelock',
},
[3]: {
[UNI_ADDRESS[3]]: 'Ropsten UNI',
[GOVERNANCE_ADDRESS[3]]: 'Ropsten Governance',
[TIMELOCK_ADDRESS[3]]: 'Ropsten Timelock',
},
[42]: {
[UNI_ADDRESS[42]]: 'Kovan UNI',
[GOVERNANCE_ADDRESS[42]]: 'Kovan Governance',
[TIMELOCK_ADDRESS[42]]: 'Kovan Timelock',
},
[5]: {
[UNI_ADDRESS[5]]: 'Goerli UNI',
[GOVERNANCE_ADDRESS[5]]: 'Goerli Governance',
[TIMELOCK_ADDRESS[5]]: 'Goerli Timelock',
},
}
export const DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS = 13
// Block time here is slightly higher (~1s) than average in order to avoid ongoing proposals past the displayed time
export const AVERAGE_BLOCK_TIME_IN_SECS: { [chainId: number]: number } = {
[1]: DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS,
}

View File

@@ -1,279 +0,0 @@
import { ChainId, Percent, Token, WETH9 } from '@uniswap/sdk-core'
import { AbstractConnector } from '@web3-react/abstract-connector'
import JSBI from 'jsbi'
import { fortmatic, injected, portis, walletconnect, walletlink } from '../connectors'
import INJECTED_ICON_URL from '../assets/images/arrow-right.svg'
import METAMASK_ICON_URL from '../assets/images/metamask.png'
import WALLETCONNECT_ICON_URL from '../assets/images/walletConnectIcon.svg'
import COINBASE_ICON_URL from '../assets/images/coinbaseWalletIcon.svg'
import FORTMATIC_ICON_URL from '../assets/images/fortmaticIcon.png'
import PORTIS_ICON_URL from '../assets/images/portisIcon.png'
export const MULTICALL2_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696',
[ChainId.ROPSTEN]: '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696',
[ChainId.KOVAN]: '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696',
[ChainId.RINKEBY]: '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696',
[ChainId.GÖRLI]: '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696',
}
export const V2_ROUTER_ADDRESS = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D'
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
export { PRELOADED_PROPOSALS } from './proposals'
// a list of tokens by chain
type ChainTokenList = {
readonly [chainId in ChainId]: Token[]
}
export const AMPL = new Token(ChainId.MAINNET, '0xD46bA6D942050d489DBd938a2C909A5d5039A161', 9, 'AMPL', 'Ampleforth')
export const DAI = new Token(ChainId.MAINNET, '0x6B175474E89094C44Da98b954EedeAC495271d0F', 18, 'DAI', 'Dai Stablecoin')
export const USDC = new Token(ChainId.MAINNET, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'USDC', 'USD//C')
export const USDT = new Token(ChainId.MAINNET, '0xdAC17F958D2ee523a2206206994597C13D831ec7', 6, 'USDT', 'Tether USD')
export const WBTC = new Token(ChainId.MAINNET, '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 8, 'WBTC', 'Wrapped BTC')
export const FEI = new Token(ChainId.MAINNET, '0x956F47F50A910163D8BF957Cf5846D573E7f87CA', 18, 'FEI', 'Fei USD')
export const TRIBE = new Token(ChainId.MAINNET, '0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B', 18, 'TRIBE', 'Tribe')
export const FRAX = new Token(ChainId.MAINNET, '0x853d955aCEf822Db058eb8505911ED77F175b99e', 18, 'FRAX', 'Frax')
export const FXS = new Token(ChainId.MAINNET, '0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0', 18, 'FXS', 'Frax Share')
export const renBTC = new Token(ChainId.MAINNET, '0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D', 8, 'renBTC', 'renBTC')
export const UMA = new Token(
ChainId.MAINNET,
'0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828',
18,
'UMA',
'UMA Voting Token v1'
)
// Mirror Protocol compat.
export const UST = new Token(ChainId.MAINNET, '0xa47c8bf37f92abed4a126bda807a7b7498661acd', 18, 'UST', 'Wrapped UST')
export const MIR = new Token(ChainId.MAINNET, '0x09a3ecafa817268f77be1283176b946c4ff2e608', 18, 'MIR', 'Wrapped MIR')
// List of all mirror's assets addresses.
// Last pulled from : https://whitelist.mirror.finance/eth/tokenlists.json
// TODO: Generate this programaticaly ?
const mAssetsAdditionalBases: { [tokenAddress: string]: Token[] } = {
[UST.address]: [MIR],
[MIR.address]: [UST],
'0xd36932143F6eBDEDD872D5Fb0651f4B72Fd15a84': [MIR, UST], // mAAPL
'0x59A921Db27Dd6d4d974745B7FfC5c33932653442': [MIR, UST], // mGOOGL
'0x21cA39943E91d704678F5D00b6616650F066fD63': [MIR, UST], // mTSLA
'0xC8d674114bac90148d11D3C1d33C61835a0F9DCD': [MIR, UST], // mNFLX
'0x13B02c8dE71680e71F0820c996E4bE43c2F57d15': [MIR, UST], // mQQQ
'0xEdb0414627E6f1e3F082DE65cD4F9C693D78CCA9': [MIR, UST], // mTWTR
'0x41BbEDd7286dAab5910a1f15d12CBda839852BD7': [MIR, UST], // mMSFT
'0x0cae9e4d663793c2a2A0b211c1Cf4bBca2B9cAa7': [MIR, UST], // mAMZN
'0x56aA298a19C93c6801FDde870fA63EF75Cc0aF72': [MIR, UST], // mBABA
'0x1d350417d9787E000cc1b95d70E9536DcD91F373': [MIR, UST], // mIAU
'0x9d1555d8cB3C846Bb4f7D5B1B1080872c3166676': [MIR, UST], // mSLV
'0x31c63146a635EB7465e5853020b39713AC356991': [MIR, UST], // mUSO
'0xf72FCd9DCF0190923Fadd44811E240Ef4533fc86': [MIR, UST], // mVIXY
}
// Block time here is slightly higher (~1s) than average in order to avoid ongoing proposals past the displayed time
export const AVERAGE_BLOCK_TIME_IN_SECS = 13
export const PROPOSAL_LENGTH_IN_BLOCKS = 40_320
export const PROPOSAL_LENGTH_IN_SECS = AVERAGE_BLOCK_TIME_IN_SECS * PROPOSAL_LENGTH_IN_BLOCKS
export const GOVERNANCE_ADDRESS = '0x5e4be8Bc9637f0EAA1A755019e06A68ce081D58F'
export const TIMELOCK_ADDRESS = '0x1a9C8182C09F50C8318d769245beA52c32BE35BC'
const UNI_ADDRESS = '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984'
export const UNI: { [chainId in ChainId]: Token } = {
[ChainId.MAINNET]: new Token(ChainId.MAINNET, UNI_ADDRESS, 18, 'UNI', 'Uniswap'),
[ChainId.RINKEBY]: new Token(ChainId.RINKEBY, UNI_ADDRESS, 18, 'UNI', 'Uniswap'),
[ChainId.ROPSTEN]: new Token(ChainId.ROPSTEN, UNI_ADDRESS, 18, 'UNI', 'Uniswap'),
[ChainId.GÖRLI]: new Token(ChainId.GÖRLI, UNI_ADDRESS, 18, 'UNI', 'Uniswap'),
[ChainId.KOVAN]: new Token(ChainId.KOVAN, UNI_ADDRESS, 18, 'UNI', 'Uniswap'),
}
export const COMMON_CONTRACT_NAMES: { [address: string]: string } = {
[UNI_ADDRESS]: 'UNI',
[GOVERNANCE_ADDRESS]: 'Governance',
[TIMELOCK_ADDRESS]: 'Timelock',
}
export const MERKLE_DISTRIBUTOR_ADDRESS: { [chainId in ChainId]?: string } = {
[ChainId.MAINNET]: '0x090D4613473dEE047c3f2706764f49E0821D256e',
}
const WETH_ONLY: ChainTokenList = {
[ChainId.MAINNET]: [WETH9[ChainId.MAINNET]],
[ChainId.ROPSTEN]: [WETH9[ChainId.ROPSTEN]],
[ChainId.RINKEBY]: [WETH9[ChainId.RINKEBY]],
[ChainId.GÖRLI]: [WETH9[ChainId.GÖRLI]],
[ChainId.KOVAN]: [WETH9[ChainId.KOVAN]],
}
// used to construct intermediary pairs for trading
export const BASES_TO_CHECK_TRADES_AGAINST: ChainTokenList = {
...WETH_ONLY,
[ChainId.MAINNET]: [...WETH_ONLY[ChainId.MAINNET], DAI, USDC, USDT, WBTC],
}
export const ADDITIONAL_BASES: { [chainId in ChainId]?: { [tokenAddress: string]: Token[] } } = {
[ChainId.MAINNET]: {
...mAssetsAdditionalBases,
'0xA948E86885e12Fb09AfEF8C52142EBDbDf73cD18': [UNI[ChainId.MAINNET]],
'0x561a4717537ff4AF5c687328c0f7E90a319705C0': [UNI[ChainId.MAINNET]],
'0xa6e3454fec677772dd771788a079355e43910638': [UMA],
[FEI.address]: [TRIBE],
[TRIBE.address]: [FEI],
[FRAX.address]: [FXS],
[FXS.address]: [FRAX],
[WBTC.address]: [renBTC],
[renBTC.address]: [WBTC],
},
}
/**
* Some tokens can only be swapped via certain pairs, so we override the list of bases that are considered for these
* tokens.
*/
export const CUSTOM_BASES: { [chainId in ChainId]?: { [tokenAddress: string]: Token[] } } = {
[ChainId.MAINNET]: {
[AMPL.address]: [DAI, WETH9[ChainId.MAINNET]],
},
}
// used for display in the default list when adding liquidity
export const SUGGESTED_BASES: Partial<ChainTokenList> = {
[ChainId.MAINNET]: [DAI, USDC, USDT, WBTC],
}
// used to construct the list of all pairs we consider by default in the frontend
export const BASES_TO_TRACK_LIQUIDITY_FOR: ChainTokenList = {
...WETH_ONLY,
[ChainId.MAINNET]: [...WETH_ONLY[ChainId.MAINNET], DAI, USDC, USDT, WBTC],
}
export const PINNED_PAIRS: { readonly [chainId in ChainId]?: [Token, Token][] } = {
[ChainId.MAINNET]: [
[
new Token(ChainId.MAINNET, '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643', 8, 'cDAI', 'Compound Dai'),
new Token(ChainId.MAINNET, '0x39AA39c021dfbaE8faC545936693aC917d5E7563', 8, 'cUSDC', 'Compound USD Coin'),
],
[USDC, USDT],
[DAI, USDT],
],
}
export interface WalletInfo {
connector?: AbstractConnector
name: string
iconURL: string
description: string
href: string | null
color: string
primary?: true
mobile?: true
mobileOnly?: true
}
export const SUPPORTED_WALLETS: { [key: string]: WalletInfo } = {
INJECTED: {
connector: injected,
name: 'Injected',
iconURL: INJECTED_ICON_URL,
description: 'Injected web3 provider.',
href: null,
color: '#010101',
primary: true,
},
METAMASK: {
connector: injected,
name: 'MetaMask',
iconURL: METAMASK_ICON_URL,
description: 'Easy-to-use browser extension.',
href: null,
color: '#E8831D',
},
WALLET_CONNECT: {
connector: walletconnect,
name: 'WalletConnect',
iconURL: WALLETCONNECT_ICON_URL,
description: 'Connect to Trust Wallet, Rainbow Wallet and more...',
href: null,
color: '#4196FC',
mobile: true,
},
WALLET_LINK: {
connector: walletlink,
name: 'Coinbase Wallet',
iconURL: COINBASE_ICON_URL,
description: 'Use Coinbase Wallet app on mobile device',
href: null,
color: '#315CF5',
},
COINBASE_LINK: {
name: 'Open in Coinbase Wallet',
iconURL: COINBASE_ICON_URL,
description: 'Open in Coinbase Wallet app.',
href: 'https://go.cb-w.com/mtUDhEZPy1',
color: '#315CF5',
mobile: true,
mobileOnly: true,
},
FORTMATIC: {
connector: fortmatic,
name: 'Fortmatic',
iconURL: FORTMATIC_ICON_URL,
description: 'Login using Fortmatic hosted wallet',
href: null,
color: '#6748FF',
mobile: true,
},
Portis: {
connector: portis,
name: 'Portis',
iconURL: PORTIS_ICON_URL,
description: 'Login using Portis hosted wallet',
href: null,
color: '#4A6C9B',
mobile: true,
},
}
export const NetworkContextName = 'NETWORK'
// 30 minutes, denominated in seconds
export const DEFAULT_DEADLINE_FROM_NOW = 60 * 30
// used for rewards deadlines
export const BIG_INT_SECONDS_IN_WEEK = JSBI.BigInt(60 * 60 * 24 * 7)
export const BIG_INT_ZERO = JSBI.BigInt(0)
// one basis JSBI.BigInt
export const ONE_BIPS = new Percent(JSBI.BigInt(1), JSBI.BigInt(10))
export const BIPS_BASE = JSBI.BigInt(10000)
// used for warning states
export const ALLOWED_PRICE_IMPACT_LOW: Percent = new Percent(JSBI.BigInt(100), BIPS_BASE) // 1%
export const ALLOWED_PRICE_IMPACT_MEDIUM: Percent = new Percent(JSBI.BigInt(300), BIPS_BASE) // 3%
export const ALLOWED_PRICE_IMPACT_HIGH: Percent = new Percent(JSBI.BigInt(500), BIPS_BASE) // 5%
// if the price slippage exceeds this number, force the user to type 'confirm' to execute
export const PRICE_IMPACT_WITHOUT_FEE_CONFIRM_MIN: Percent = new Percent(JSBI.BigInt(1000), BIPS_BASE) // 10%
// for non expert mode disable swaps above this
export const BLOCKED_PRICE_IMPACT_NON_EXPERT: Percent = new Percent(JSBI.BigInt(1500), BIPS_BASE) // 15%
// used to ensure the user doesn't send so much ETH so they end up with <.01
export const MIN_ETH: JSBI = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(16)) // .01 ETH
export const BETTER_TRADE_LESS_HOPS_THRESHOLD = new Percent(JSBI.BigInt(50), JSBI.BigInt(10000))
export const ZERO_PERCENT = new Percent('0')
export const ONE_HUNDRED_PERCENT = new Percent('1')
// SDN OFAC addresses
export const BLOCKED_ADDRESSES: string[] = [
'0x7F367cC41522cE07553e823bf3be79A889DEbe1B',
'0xd882cFc20F52f2599D84b8e8D58C7FB62cfE344b',
'0x901bb9583b24D97e995513C6778dc6888AB6870e',
'0xA7e5d5A720f06526557c513402f2e6B5fA20b008',
'0x8576aCC5C05D6Ce88f4e49bf65BdF0C62F91353C',
]
export const ARGENT_WALLET_DETECTOR_MAINNET_ADDRESS = '0xeca4B0bDBf7c55E9b7925919d03CbF8Dc82537E8'
export const V1_MIGRATOR_ADDRESS = '0x16D4F26C15f3658ec65B1126ff27DD3dF2a2996b'

33
src/constants/misc.ts Normal file
View File

@@ -0,0 +1,33 @@
import { Percent } from '@uniswap/sdk-core'
import JSBI from 'jsbi'
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
export const NetworkContextName = 'NETWORK'
// 30 minutes, denominated in seconds
export const DEFAULT_DEADLINE_FROM_NOW = 60 * 30
// used for rewards deadlines
export const BIG_INT_SECONDS_IN_WEEK = JSBI.BigInt(60 * 60 * 24 * 7)
export const BIG_INT_ZERO = JSBI.BigInt(0)
// one basis JSBI.BigInt
export const ONE_BIPS = new Percent(JSBI.BigInt(1), JSBI.BigInt(10000))
export const BIPS_BASE = JSBI.BigInt(10000)
// used for warning states
export const ALLOWED_PRICE_IMPACT_LOW: Percent = new Percent(JSBI.BigInt(100), BIPS_BASE) // 1%
export const ALLOWED_PRICE_IMPACT_MEDIUM: Percent = new Percent(JSBI.BigInt(300), BIPS_BASE) // 3%
export const ALLOWED_PRICE_IMPACT_HIGH: Percent = new Percent(JSBI.BigInt(500), BIPS_BASE) // 5%
// if the price slippage exceeds this number, force the user to type 'confirm' to execute
export const PRICE_IMPACT_WITHOUT_FEE_CONFIRM_MIN: Percent = new Percent(JSBI.BigInt(1000), BIPS_BASE) // 10%
// for non expert mode disable swaps above this
export const BLOCKED_PRICE_IMPACT_NON_EXPERT: Percent = new Percent(JSBI.BigInt(1500), BIPS_BASE) // 15%
// used to ensure the user doesn't send so much ETH so they end up with <.01
export const MIN_ETH: JSBI = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(16)) // .01 ETH
export const BETTER_TRADE_LESS_HOPS_THRESHOLD = new Percent(JSBI.BigInt(50), JSBI.BigInt(10000))
export const ZERO_PERCENT = new Percent('0')
export const ONE_HUNDRED_PERCENT = new Percent('1')

View File

@@ -1,4 +1,6 @@
import { UNISWAP_GRANTS } from './uniswap_grants'
import { UNISWAP_GRANTS_PROPOSAL_DESCRIPTION } from './uniswap_grants_proposal_description'
// Proposals are 0-indexed
export const PRELOADED_PROPOSALS = new Map([[2, UNISWAP_GRANTS]])
export const PROPOSAL_DESCRIPTION_TEXT: { [proposalId: number]: string } = {
[2]: UNISWAP_GRANTS_PROPOSAL_DESCRIPTION,
}

View File

@@ -1,4 +1,4 @@
export const UNISWAP_GRANTS = `# Uniswap Grants Program v0.1
export const UNISWAP_GRANTS_PROPOSAL_DESCRIPTION = `# Uniswap Grants Program v0.1
*co-authored with [Ken Ng](https://twitter.com/nkennethk?lang=en)*

95
src/constants/routing.ts Normal file
View File

@@ -0,0 +1,95 @@
// a list of tokens by chain
import { Currency, Ether, Token, WETH9 } from '@uniswap/sdk-core'
import { AMPL, DAI, ETH2X_FLI, FEI, FRAX, FXS, MIR, renBTC, TRIBE, UMA, UNI, USDC, USDT, UST, WBTC } from './tokens'
type ChainTokenList = {
readonly [chainId: number]: Token[]
}
type ChainCurrencyList = {
readonly [chainId: number]: Currency[]
}
// List of all mirror's assets addresses.
// Last pulled from : https://whitelist.mirror.finance/eth/tokenlists.json
// TODO: Generate this programmatically ?
const mAssetsAdditionalBases: { [tokenAddress: string]: Token[] } = {
[UST.address]: [MIR],
[MIR.address]: [UST],
'0xd36932143F6eBDEDD872D5Fb0651f4B72Fd15a84': [MIR, UST], // mAAPL
'0x59A921Db27Dd6d4d974745B7FfC5c33932653442': [MIR, UST], // mGOOGL
'0x21cA39943E91d704678F5D00b6616650F066fD63': [MIR, UST], // mTSLA
'0xC8d674114bac90148d11D3C1d33C61835a0F9DCD': [MIR, UST], // mNFLX
'0x13B02c8dE71680e71F0820c996E4bE43c2F57d15': [MIR, UST], // mQQQ
'0xEdb0414627E6f1e3F082DE65cD4F9C693D78CCA9': [MIR, UST], // mTWTR
'0x41BbEDd7286dAab5910a1f15d12CBda839852BD7': [MIR, UST], // mMSFT
'0x0cae9e4d663793c2a2A0b211c1Cf4bBca2B9cAa7': [MIR, UST], // mAMZN
'0x56aA298a19C93c6801FDde870fA63EF75Cc0aF72': [MIR, UST], // mBABA
'0x1d350417d9787E000cc1b95d70E9536DcD91F373': [MIR, UST], // mIAU
'0x9d1555d8cB3C846Bb4f7D5B1B1080872c3166676': [MIR, UST], // mSLV
'0x31c63146a635EB7465e5853020b39713AC356991': [MIR, UST], // mUSO
'0xf72FCd9DCF0190923Fadd44811E240Ef4533fc86': [MIR, UST], // mVIXY
}
const WETH_ONLY: ChainTokenList = {
[1]: [WETH9[1]],
[3]: [WETH9[3]],
[4]: [WETH9[4]],
[5]: [WETH9[5]],
[42]: [WETH9[42]],
}
// used to construct intermediary pairs for trading
export const BASES_TO_CHECK_TRADES_AGAINST: ChainTokenList = {
...WETH_ONLY,
[1]: [...WETH_ONLY[1], DAI, USDC, USDT, WBTC],
}
export const ADDITIONAL_BASES: { [chainId: number]: { [tokenAddress: string]: Token[] } } = {
[1]: {
...mAssetsAdditionalBases,
'0xF16E4d813f4DcfDe4c5b44f305c908742De84eF0': [ETH2X_FLI],
'0xA948E86885e12Fb09AfEF8C52142EBDbDf73cD18': [UNI[1]],
'0x561a4717537ff4AF5c687328c0f7E90a319705C0': [UNI[1]],
'0xa6e3454fec677772dd771788a079355e43910638': [UMA],
[FEI.address]: [TRIBE],
[TRIBE.address]: [FEI],
[FRAX.address]: [FXS],
[FXS.address]: [FRAX],
[WBTC.address]: [renBTC],
[renBTC.address]: [WBTC],
},
}
/**
* Some tokens can only be swapped via certain pairs, so we override the list of bases that are considered for these
* tokens.
*/
export const CUSTOM_BASES: { [chainId: number]: { [tokenAddress: string]: Token[] } } = {
[1]: {
[AMPL.address]: [DAI, WETH9[1]],
},
}
/**
* Shows up in the currency select for swap and add liquidity
*/
export const COMMON_BASES: ChainCurrencyList = {
[1]: [Ether.onChain(1), DAI, USDC, USDT, WBTC, WETH9[1]],
[3]: [Ether.onChain(3), WETH9[3]],
[4]: [Ether.onChain(4), WETH9[4]],
[5]: [Ether.onChain(5), WETH9[5]],
[42]: [Ether.onChain(42), WETH9[42]],
}
// used to construct the list of all pairs we consider by default in the frontend
export const BASES_TO_TRACK_LIQUIDITY_FOR: ChainTokenList = {
...WETH_ONLY,
[1]: [...WETH_ONLY[1], DAI, USDC, USDT, WBTC],
}
export const PINNED_PAIRS: { readonly [chainId: number]: [Token, Token][] } = {
[1]: [
[
new Token(1, '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643', 8, 'cDAI', 'Compound Dai'),
new Token(1, '0x39AA39c021dfbaE8faC545936693aC917d5E7563', 8, 'cUSDC', 'Compound USD Coin'),
],
[USDC, USDT],
[DAI, USDT],
],
}

31
src/constants/tokens.ts Normal file
View File

@@ -0,0 +1,31 @@
import { Token } from '@uniswap/sdk-core'
import { UNI_ADDRESS } from './addresses'
export const AMPL = new Token(1, '0xD46bA6D942050d489DBd938a2C909A5d5039A161', 9, 'AMPL', 'Ampleforth')
export const DAI = new Token(1, '0x6B175474E89094C44Da98b954EedeAC495271d0F', 18, 'DAI', 'Dai Stablecoin')
export const USDC = new Token(1, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'USDC', 'USD//C')
export const USDT = new Token(1, '0xdAC17F958D2ee523a2206206994597C13D831ec7', 6, 'USDT', 'Tether USD')
export const WBTC = new Token(1, '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 8, 'WBTC', 'Wrapped BTC')
export const FEI = new Token(1, '0x956F47F50A910163D8BF957Cf5846D573E7f87CA', 18, 'FEI', 'Fei USD')
export const TRIBE = new Token(1, '0xc7283b66Eb1EB5FB86327f08e1B5816b0720212B', 18, 'TRIBE', 'Tribe')
export const FRAX = new Token(1, '0x853d955aCEf822Db058eb8505911ED77F175b99e', 18, 'FRAX', 'Frax')
export const FXS = new Token(1, '0x3432B6A60D23Ca0dFCa7761B7ab56459D9C964D0', 18, 'FXS', 'Frax Share')
export const renBTC = new Token(1, '0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D', 8, 'renBTC', 'renBTC')
export const UMA = new Token(1, '0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828', 18, 'UMA', 'UMA Voting Token v1')
export const ETH2X_FLI = new Token(
1,
'0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD',
18,
'ETH2x-FLI',
'ETH 2x Flexible Leverage Index'
)
// Mirror Protocol compat.
export const UST = new Token(1, '0xa47c8bf37f92abed4a126bda807a7b7498661acd', 18, 'UST', 'Wrapped UST')
export const MIR = new Token(1, '0x09a3ecafa817268f77be1283176b946c4ff2e608', 18, 'MIR', 'Wrapped MIR')
export const UNI: { [chainId: number]: Token } = {
[1]: new Token(1, UNI_ADDRESS[1], 18, 'UNI', 'Uniswap'),
[4]: new Token(4, UNI_ADDRESS[4], 18, 'UNI', 'Uniswap'),
[3]: new Token(3, UNI_ADDRESS[3], 18, 'UNI', 'Uniswap'),
[5]: new Token(5, UNI_ADDRESS[5], 18, 'UNI', 'Uniswap'),
[42]: new Token(42, UNI_ADDRESS[42], 18, 'UNI', 'Uniswap'),
}

View File

@@ -1,26 +0,0 @@
import { ChainId } from '@uniswap/sdk-core'
import { FACTORY_ADDRESS } from '@uniswap/v3-sdk'
function constructSameAddressMap(address: string): { [chainId in ChainId]: string } {
return {
[ChainId.MAINNET]: address,
[ChainId.ROPSTEN]: address,
[ChainId.KOVAN]: address,
[ChainId.RINKEBY]: address,
[ChainId.GÖRLI]: address,
}
}
export const V3_CORE_FACTORY_ADDRESSES = constructSameAddressMap(FACTORY_ADDRESS)
export const QUOTER_ADDRESSES = constructSameAddressMap('0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6')
export const TICK_LENS_ADDRESSES = constructSameAddressMap('0xbfd8137f7d1516D3ea5cA83523914859ec47F573')
export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES = constructSameAddressMap(
'0xC36442b4a4522E871399CD717aBDD847Ab11FE88'
)
export const SWAP_ROUTER_ADDRESSES = constructSameAddressMap('0xE592427A0AEce92De3Edee1F18E0157C05861564')
export const V3_MIGRATOR_ADDRESSES = constructSameAddressMap('0xA5644E29708357803b5A882D272c41cC0dF92B34')

84
src/constants/wallet.ts Normal file
View File

@@ -0,0 +1,84 @@
import { AbstractConnector } from '@web3-react/abstract-connector'
import INJECTED_ICON_URL from '../assets/images/arrow-right.svg'
import COINBASE_ICON_URL from '../assets/images/coinbaseWalletIcon.svg'
import FORTMATIC_ICON_URL from '../assets/images/fortmaticIcon.png'
import METAMASK_ICON_URL from '../assets/images/metamask.png'
import PORTIS_ICON_URL from '../assets/images/portisIcon.png'
import WALLETCONNECT_ICON_URL from '../assets/images/walletConnectIcon.svg'
import { fortmatic, injected, portis, walletconnect, walletlink } from '../connectors'
export interface WalletInfo {
connector?: AbstractConnector
name: string
iconURL: string
description: string
href: string | null
color: string
primary?: true
mobile?: true
mobileOnly?: true
}
export const SUPPORTED_WALLETS: { [key: string]: WalletInfo } = {
INJECTED: {
connector: injected,
name: 'Injected',
iconURL: INJECTED_ICON_URL,
description: 'Injected web3 provider.',
href: null,
color: '#010101',
primary: true,
},
METAMASK: {
connector: injected,
name: 'MetaMask',
iconURL: METAMASK_ICON_URL,
description: 'Easy-to-use browser extension.',
href: null,
color: '#E8831D',
},
WALLET_CONNECT: {
connector: walletconnect,
name: 'WalletConnect',
iconURL: WALLETCONNECT_ICON_URL,
description: 'Connect to Trust Wallet, Rainbow Wallet and more...',
href: null,
color: '#4196FC',
mobile: true,
},
WALLET_LINK: {
connector: walletlink,
name: 'Coinbase Wallet',
iconURL: COINBASE_ICON_URL,
description: 'Use Coinbase Wallet app on mobile device',
href: null,
color: '#315CF5',
},
COINBASE_LINK: {
name: 'Open in Coinbase Wallet',
iconURL: COINBASE_ICON_URL,
description: 'Open in Coinbase Wallet app.',
href: 'https://go.cb-w.com/mtUDhEZPy1',
color: '#315CF5',
mobile: true,
mobileOnly: true,
},
FORTMATIC: {
connector: fortmatic,
name: 'Fortmatic',
iconURL: FORTMATIC_ICON_URL,
description: 'Login using Fortmatic hosted wallet',
href: null,
color: '#6748FF',
mobile: true,
},
Portis: {
connector: portis,
name: 'Portis',
iconURL: PORTIS_ICON_URL,
description: 'Login using Portis hosted wallet',
href: null,
color: '#4A6C9B',
mobile: true,
},
}

View File

@@ -1,5 +1,5 @@
import { parseBytes32String } from '@ethersproject/strings'
import { Currency, currencyEquals, ETHER, Token } from '@uniswap/sdk-core'
import { Currency, Ether, Token } from '@uniswap/sdk-core'
import { arrayify } from 'ethers/lib/utils'
import { useMemo } from 'react'
import { createTokenFilterFunction } from '../components/SearchModal/filtering'
@@ -10,7 +10,7 @@ import { useUserAddedTokens } from '../state/user/hooks'
import { isAddress } from '../utils'
import { TokenAddressMap, useUnsupportedTokenList } from './../state/lists/hooks'
import { useActiveWeb3React } from './index'
import { useActiveWeb3React } from './web3'
import { useBytes32TokenContract, useTokenContract } from './useContract'
// reduce token map into standard address <-> Token mapping, optionally include user added tokens
@@ -103,7 +103,7 @@ export function useIsUserAddedToken(currency: Currency | undefined | null): bool
return false
}
return !!userAddedTokens.find((token) => currencyEquals(currency, token))
return !!userAddedTokens.find((token) => currency.equals(token))
}
// parse a name or symbol from a token response
@@ -172,7 +172,8 @@ export function useToken(tokenAddress?: string): Token | undefined | null {
}
export function useCurrency(currencyId: string | undefined): Currency | null | undefined {
const { chainId } = useActiveWeb3React()
const isETH = currencyId?.toUpperCase() === 'ETH'
const token = useToken(isETH ? undefined : currencyId)
return isETH ? ETHER : token
return isETH ? (chainId ? Ether.onChain(chainId) : undefined) : token
}

View File

@@ -1,15 +1,15 @@
import { getTokenLogoURL } from './../components/CurrencyLogo/index'
import { wrappedCurrency } from 'utils/wrappedCurrency'
import { Currency, Token } from '@uniswap/sdk-core'
import { useCallback, useState } from 'react'
import { useActiveWeb3React } from 'hooks'
import { useActiveWeb3React } from 'hooks/web3'
export default function useAddTokenToMetamask(
currencyToAdd: Currency | undefined
): { addToken: () => void; success: boolean | undefined } {
const { library, chainId } = useActiveWeb3React()
export default function useAddTokenToMetamask(currencyToAdd: Currency | undefined): {
addToken: () => void
success: boolean | undefined
} {
const { library } = useActiveWeb3React()
const token: Token | undefined = wrappedCurrency(currencyToAdd, chainId)
const token: Token | undefined = currencyToAdd?.wrapped
const [success, setSuccess] = useState<boolean | undefined>()

View File

@@ -1,16 +1,13 @@
import { Currency, Token } from '@uniswap/sdk-core'
import flatMap from 'lodash.flatmap'
import { useMemo } from 'react'
import { ADDITIONAL_BASES, BASES_TO_CHECK_TRADES_AGAINST, CUSTOM_BASES } from '../constants'
import { wrappedCurrency } from '../utils/wrappedCurrency'
import { useActiveWeb3React } from './index'
import { ADDITIONAL_BASES, BASES_TO_CHECK_TRADES_AGAINST, CUSTOM_BASES } from '../constants/routing'
import { useActiveWeb3React } from './web3'
export function useAllCurrencyCombinations(currencyA?: Currency, currencyB?: Currency): [Token, Token][] {
const { chainId } = useActiveWeb3React()
const [tokenA, tokenB] = chainId
? [wrappedCurrency(currencyA, chainId), wrappedCurrency(currencyB, chainId)]
: [undefined, undefined]
const [tokenA, tokenB] = chainId ? [currencyA?.wrapped, currencyB?.wrapped] : [undefined, undefined]
const bases: Token[] = useMemo(() => {
if (!chainId) return []

View File

@@ -1,23 +1,22 @@
import { ChainId, Currency } from '@uniswap/sdk-core'
import { Currency } from '@uniswap/sdk-core'
import { Pool, Route } from '@uniswap/v3-sdk'
import { useMemo } from 'react'
import { useUserSingleHopOnly } from '../state/user/hooks'
import { wrappedCurrency } from '../utils/wrappedCurrency'
import { useActiveWeb3React } from './index'
import { useActiveWeb3React } from './web3'
import { useV3SwapPools } from './useV3SwapPools'
function computeAllRoutes(
currencyIn: Currency,
currencyOut: Currency,
pools: Pool[],
chainId: ChainId,
chainId: number,
currentPath: Pool[] = [],
allPaths: Route<Currency, Currency>[] = [],
startCurrencyIn: Currency = currencyIn,
maxHops = 2
): Route<Currency, Currency>[] {
const tokenIn = wrappedCurrency(currencyIn, chainId)
const tokenOut = wrappedCurrency(currencyOut, chainId)
const tokenIn = currencyIn?.wrapped
const tokenOut = currencyOut?.wrapped
if (!tokenIn || !tokenOut) throw new Error('Missing tokenIn/tokenOut')
for (const pool of pools) {

View File

@@ -1,113 +0,0 @@
import { Token } from '@uniswap/sdk-core'
import { FeeAmount, TICK_SPACINGS } from '@uniswap/v3-sdk'
import { nearestUsableTick, TickMath } from '@uniswap/v3-sdk/dist/'
import { ZERO_ADDRESS } from '../constants'
import { useEffect, useMemo, useState } from 'react'
import { Result, useSingleCallResult, useSingleContractMultipleData } from 'state/multicall/hooks'
import { useTickLens, useV3Factory } from './useContract'
function bitmapIndex(tick: number, tickSpacing: number) {
return Math.floor(tick / tickSpacing / 256)
}
const REFRESH_FREQUENCY = { blocksPerFetch: 2 }
interface TickData {
tick: number
liquidityNet: number
liquidityGross: number
}
// for now, reconsider using this function, it consumes a lot of data and cpu to fetch all the ticks.
export function useAllV3Ticks(
token0: Token | undefined,
token1: Token | undefined,
feeAmount: FeeAmount | undefined
): {
loading: boolean
syncing: boolean
error: boolean
valid: boolean
tickData: TickData[]
} {
const tickSpacing = feeAmount && TICK_SPACINGS[feeAmount]
const minIndex = useMemo(
() => (tickSpacing ? bitmapIndex(nearestUsableTick(TickMath.MIN_TICK, tickSpacing), tickSpacing) : undefined),
[tickSpacing]
)
const maxIndex = useMemo(
() => (tickSpacing ? bitmapIndex(nearestUsableTick(TickMath.MAX_TICK, tickSpacing), tickSpacing) : undefined),
[tickSpacing]
)
const [tickDataLatestSynced, setTickDataLatestSynced] = useState<TickData[]>([])
// fetch the pool address
const factoryContract = useV3Factory()
const poolAddress = useSingleCallResult(factoryContract, 'getPool', [token0?.address, token1?.address, feeAmount])
.result?.[0]
const tickLensArgs: [string, number][] = useMemo(
() =>
maxIndex && minIndex && poolAddress && poolAddress !== ZERO_ADDRESS
? new Array(maxIndex - minIndex + 1)
.fill(0)
.map((_, i) => i + minIndex)
.map((wordIndex) => [poolAddress, wordIndex])
: [],
[minIndex, maxIndex, poolAddress]
)
const tickLens = useTickLens()
const callStates = useSingleContractMultipleData(
tickLensArgs.length > 0 ? tickLens : undefined,
'getPopulatedTicksInWord',
tickLensArgs,
REFRESH_FREQUENCY,
3_000_000
)
const error = useMemo(() => callStates.some(({ error }) => error), [callStates])
const loading = useMemo(() => callStates.some(({ loading }) => loading), [callStates])
const syncing = useMemo(() => callStates.some(({ syncing }) => syncing), [callStates])
const valid = useMemo(() => callStates.some(({ valid }) => valid), [callStates])
const tickData = useMemo(
() =>
callStates
.map(({ result }) => (result as Result)?.populatedTicks)
.reduce(
(accumulator, current) => [
...accumulator,
...(current?.map((tickData: TickData) => {
return {
tick: tickData.tick,
liquidityNet: tickData.liquidityNet,
liquidityGross: tickData.liquidityGross,
}
}) ?? []),
],
[]
),
[callStates]
)
// return the latest synced tickdata even if we are still loading the newest data
useEffect(() => {
if (!syncing && !loading && !error && valid) {
setTickDataLatestSynced(tickData)
}
}, [error, loading, syncing, tickData, valid])
return useMemo(
() => ({
loading,
syncing,
error,
valid,
tickData: tickDataLatestSynced,
}),
[loading, syncing, error, valid, tickDataLatestSynced]
)
}

View File

@@ -0,0 +1,22 @@
import { useEffect } from 'react'
import { useDispatch } from 'react-redux'
import { AppDispatch } from 'state'
import { updateUserExpertMode } from '../state/user/actions'
import useParsedQueryString from './useParsedQueryString'
export default function ApeModeQueryParamReader(): null {
useApeModeQueryParamReader()
return null
}
function useApeModeQueryParamReader() {
const dispatch = useDispatch<AppDispatch>()
const { ape } = useParsedQueryString()
useEffect(() => {
if (typeof ape !== 'string') return
if (ape === '' || ape.toLowerCase() === 'true') {
dispatch(updateUserExpertMode({ userExpertMode: true }))
}
})
}

View File

@@ -1,15 +1,14 @@
import { MaxUint256 } from '@ethersproject/constants'
import { TransactionResponse } from '@ethersproject/providers'
import { CurrencyAmount, ChainId, Percent, Currency, TradeType } from '@uniswap/sdk-core'
import { CurrencyAmount, Percent, Currency, TradeType } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import { useCallback, useMemo } from 'react'
import { V2_ROUTER_ADDRESS } from '../constants'
import { SWAP_ROUTER_ADDRESSES } from '../constants/v3'
import { SWAP_ROUTER_ADDRESSES, V2_ROUTER_ADDRESS } from '../constants/addresses'
import { useTransactionAdder, useHasPendingApproval } from '../state/transactions/hooks'
import { calculateGasMargin } from '../utils'
import { calculateGasMargin } from '../utils/calculateGasMargin'
import { useTokenContract } from './useContract'
import { useActiveWeb3React } from './index'
import { useActiveWeb3React } from './web3'
import { useTokenAllowance } from './useTokenAllowance'
export enum ApprovalState {
@@ -32,7 +31,7 @@ export function useApproveCallback(
// check the current approval status
const approvalState: ApprovalState = useMemo(() => {
if (!amountToApprove || !spender) return ApprovalState.UNKNOWN
if (amountToApprove.currency.isEther) return ApprovalState.APPROVED
if (amountToApprove.currency.isNative) return ApprovalState.APPROVED
// we might not have enough data to know whether or not we need to approve
if (!currentAllowance) return ApprovalState.UNKNOWN
@@ -104,13 +103,19 @@ export function useApproveCallbackFromTrade(
allowedSlippage: Percent
) {
const { chainId } = useActiveWeb3React()
const swapRouterAddress = SWAP_ROUTER_ADDRESSES[chainId as ChainId]
const v3SwapRouterAddress = chainId ? SWAP_ROUTER_ADDRESSES[chainId] : undefined
const amountToApprove = useMemo(
() => (trade && trade.inputAmount.currency.isToken ? trade.maximumAmountIn(allowedSlippage) : undefined),
[trade, allowedSlippage]
)
return useApproveCallback(
amountToApprove,
trade instanceof V2Trade ? V2_ROUTER_ADDRESS : trade instanceof V3Trade ? swapRouterAddress : undefined
chainId
? trade instanceof V2Trade
? V2_ROUTER_ADDRESS[chainId]
: trade instanceof V3Trade
? v3SwapRouterAddress
: undefined
: undefined
)
}

View File

@@ -0,0 +1,15 @@
import { ArgentWalletContract } from '../abis/types'
import { useActiveWeb3React } from './web3'
import { useContract } from './useContract'
import useIsArgentWallet from './useIsArgentWallet'
import ArgentWalletContractABI from '../abis/argent-wallet-contract.json'
export function useArgentWalletContract(): ArgentWalletContract | null {
const { account } = useActiveWeb3React()
const isArgentWallet = useIsArgentWallet()
return useContract(
isArgentWallet ? account ?? undefined : undefined,
ArgentWalletContractABI,
true
) as ArgentWalletContract
}

View File

@@ -2,11 +2,11 @@ import { useState, useLayoutEffect } from 'react'
import { shade } from 'polished'
import Vibrant from 'node-vibrant'
import { hex } from 'wcag-contrast'
import { Token, ChainId } from '@uniswap/sdk-core'
import { Token } from '@uniswap/sdk-core'
import uriToHttp from 'utils/uriToHttp'
async function getColorFromToken(token: Token): Promise<string | null> {
if (token.chainId !== ChainId.MAINNET) {
if (token.chainId !== 1) {
return Promise.resolve('#FAAB14')
}

View File

@@ -1,5 +1,5 @@
import { Contract } from '@ethersproject/contracts'
import { ChainId, WETH9 } from '@uniswap/sdk-core'
import { WETH9 } from '@uniswap/sdk-core'
import { abi as GOVERNANCE_ABI } from '@uniswap/governance/build/GovernorAlpha.json'
import { abi as UNI_ABI } from '@uniswap/governance/build/Uni.json'
import { abi as STAKING_REWARDS_ABI } from '@uniswap/liquidity-staker/build/StakingRewards.json'
@@ -9,7 +9,6 @@ import { abi as V3FactoryABI } from '@uniswap/v3-core/artifacts/contracts/Uniswa
import { abi as V3PoolABI } from '@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json'
import { abi as QuoterABI } from '@uniswap/v3-periphery/artifacts/contracts/lens/Quoter.sol/Quoter.json'
import { abi as V2MigratorABI } from '@uniswap/v3-periphery/artifacts/contracts/V3Migrator.sol/V3Migrator.json'
import { abi as TickLensABI } from '@uniswap/v3-periphery/artifacts/contracts/lens/TickLens.sol/TickLens.json'
import { abi as IUniswapV2Router02ABI } from '@uniswap/v2-periphery/build/IUniswapV2Router02.json'
import ARGENT_WALLET_DETECTOR_ABI from 'abis/argent-wallet-detector.json'
@@ -17,7 +16,6 @@ import ENS_PUBLIC_RESOLVER_ABI from 'abis/ens-public-resolver.json'
import ENS_ABI from 'abis/ens-registrar.json'
import ERC20_ABI from 'abis/erc20.json'
import ERC20_BYTES32_ABI from 'abis/erc20_bytes32.json'
import MIGRATOR_ABI from 'abis/migrator.json'
import MULTICALL_ABI from 'abis/multicall2.json'
import { Unisocks } from 'abis/types/Unisocks'
import UNISOCKS_ABI from 'abis/unisocks.json'
@@ -25,91 +23,74 @@ import WETH_ABI from 'abis/weth.json'
import EIP_2612 from 'abis/eip_2612.json'
import {
ARGENT_WALLET_DETECTOR_MAINNET_ADDRESS,
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
QUOTER_ADDRESSES,
V3_CORE_FACTORY_ADDRESSES,
V3_MIGRATOR_ADDRESSES,
ARGENT_WALLET_DETECTOR_ADDRESS,
GOVERNANCE_ADDRESS,
MERKLE_DISTRIBUTOR_ADDRESS,
V1_MIGRATOR_ADDRESS,
UNI,
MULTICALL2_ADDRESSES,
V2_ROUTER_ADDRESS,
} from 'constants/index'
ENS_REGISTRAR_ADDRESSES,
SOCKS_CONTROLLER_ADDRESSES,
} from 'constants/addresses'
import { abi as NFTPositionManagerABI } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
import {
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
V3_CORE_FACTORY_ADDRESSES,
TICK_LENS_ADDRESSES,
V3_MIGRATOR_ADDRESSES,
QUOTER_ADDRESSES,
} from 'constants/v3'
import { useMemo } from 'react'
import { Quoter, TickLens, UniswapV3Factory, UniswapV3Pool } from 'types/v3'
import { Quoter, UniswapV3Factory, UniswapV3Pool } from 'types/v3'
import { NonfungiblePositionManager } from 'types/v3/NonfungiblePositionManager'
import { V3Migrator } from 'types/v3/V3Migrator'
import { getContract } from 'utils'
import { Multicall2 } from '../abis/types'
import { useActiveWeb3React } from './index'
import { Erc20, ArgentWalletDetector, EnsPublicResolver, EnsRegistrar, Multicall2, Weth } from '../abis/types'
import { UNI } from '../constants/tokens'
import { useActiveWeb3React } from './web3'
// returns null on errors
export function useContract(address: string | undefined, ABI: any, withSignerIfPossible = true): Contract | null {
const { library, account } = useActiveWeb3React()
export function useContract<T extends Contract = Contract>(
addressOrAddressMap: string | { [chainId: number]: string } | undefined,
ABI: any,
withSignerIfPossible = true
): T | null {
const { library, account, chainId } = useActiveWeb3React()
return useMemo(() => {
if (!address || !ABI || !library) return null
if (!addressOrAddressMap || !ABI || !library || !chainId) return null
let address: string | undefined
if (typeof addressOrAddressMap === 'string') address = addressOrAddressMap
else address = addressOrAddressMap[chainId]
if (!address) return null
try {
return getContract(address, ABI, library, withSignerIfPossible && account ? account : undefined)
} catch (error) {
console.error('Failed to get contract', error)
return null
}
}, [address, ABI, library, withSignerIfPossible, account])
}, [addressOrAddressMap, ABI, library, chainId, withSignerIfPossible, account]) as T
}
export function useV1MigratorContract(): Contract | null {
return useContract(V1_MIGRATOR_ADDRESS, MIGRATOR_ABI, true)
export function useV2MigratorContract() {
return useContract<V3Migrator>(V3_MIGRATOR_ADDRESSES, V2MigratorABI, true)
}
export function useV2MigratorContract(): V3Migrator | null {
export function useTokenContract(tokenAddress?: string, withSignerIfPossible?: boolean) {
return useContract<Erc20>(tokenAddress, ERC20_ABI, withSignerIfPossible)
}
export function useWETHContract(withSignerIfPossible?: boolean) {
const { chainId } = useActiveWeb3React()
return useContract(chainId && V3_MIGRATOR_ADDRESSES[chainId], V2MigratorABI, true) as V3Migrator | null
return useContract<Weth>(chainId ? WETH9[chainId]?.address : undefined, WETH_ABI, withSignerIfPossible)
}
export function useTokenContract(tokenAddress?: string, withSignerIfPossible?: boolean): Contract | null {
return useContract(tokenAddress, ERC20_ABI, withSignerIfPossible)
export function useArgentWalletDetectorContract() {
return useContract<ArgentWalletDetector>(ARGENT_WALLET_DETECTOR_ADDRESS, ARGENT_WALLET_DETECTOR_ABI, false)
}
export function useWETHContract(withSignerIfPossible?: boolean): Contract | null {
const { chainId } = useActiveWeb3React()
const address = chainId && chainId in WETH9 ? WETH9[chainId].address : undefined
return useContract(address, WETH_ABI, withSignerIfPossible)
export function useENSRegistrarContract(withSignerIfPossible?: boolean) {
return useContract<EnsRegistrar>(ENS_REGISTRAR_ADDRESSES, ENS_ABI, withSignerIfPossible)
}
export function useArgentWalletDetectorContract(): Contract | null {
const { chainId } = useActiveWeb3React()
return useContract(
chainId === ChainId.MAINNET ? ARGENT_WALLET_DETECTOR_MAINNET_ADDRESS : undefined,
ARGENT_WALLET_DETECTOR_ABI,
false
)
}
export function useENSRegistrarContract(withSignerIfPossible?: boolean): Contract | null {
const { chainId } = useActiveWeb3React()
let address: string | undefined
if (chainId) {
switch (chainId) {
case ChainId.MAINNET:
case ChainId.GÖRLI:
case ChainId.ROPSTEN:
case ChainId.RINKEBY:
address = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e'
break
}
}
return useContract(address, ENS_ABI, withSignerIfPossible)
}
export function useENSResolverContract(address: string | undefined, withSignerIfPossible?: boolean): Contract | null {
return useContract(address, ENS_PUBLIC_RESOLVER_ABI, withSignerIfPossible)
export function useENSResolverContract(address: string | undefined, withSignerIfPossible?: boolean) {
return useContract<EnsPublicResolver>(address, ENS_PUBLIC_RESOLVER_ABI, withSignerIfPossible)
}
export function useBytes32TokenContract(tokenAddress?: string, withSignerIfPossible?: boolean): Contract | null {
@@ -128,61 +109,47 @@ export function useV2RouterContract(): Contract | null {
return useContract(V2_ROUTER_ADDRESS, IUniswapV2Router02ABI, true)
}
export function useMulticall2Contract(): Multicall2 | null {
const { chainId } = useActiveWeb3React()
return useContract(chainId && MULTICALL2_ADDRESSES[chainId], MULTICALL_ABI, false) as Multicall2
export function useMulticall2Contract() {
return useContract<Multicall2>(MULTICALL2_ADDRESSES, MULTICALL_ABI, false) as Multicall2
}
export function useMerkleDistributorContract(): Contract | null {
const { chainId } = useActiveWeb3React()
return useContract(chainId ? MERKLE_DISTRIBUTOR_ADDRESS[chainId] : undefined, MERKLE_DISTRIBUTOR_ABI, true)
export function useMerkleDistributorContract() {
return useContract(MERKLE_DISTRIBUTOR_ADDRESS, MERKLE_DISTRIBUTOR_ABI, true)
}
export function useGovernanceContract(): Contract | null {
export function useGovernanceContract() {
return useContract(GOVERNANCE_ADDRESS, GOVERNANCE_ABI, true)
}
export function useUniContract(): Contract | null {
export function useUniContract() {
const { chainId } = useActiveWeb3React()
return useContract(chainId ? UNI[chainId].address : undefined, UNI_ABI, true)
return useContract(chainId ? UNI[chainId]?.address : undefined, UNI_ABI, true)
}
export function useStakingContract(stakingAddress?: string, withSignerIfPossible?: boolean): Contract | null {
export function useStakingContract(stakingAddress?: string, withSignerIfPossible?: boolean) {
return useContract(stakingAddress, STAKING_REWARDS_ABI, withSignerIfPossible)
}
export function useSocksController(): Unisocks | null {
const { chainId } = useActiveWeb3React()
return useContract(
chainId === ChainId.MAINNET ? '0x65770b5283117639760beA3F867b69b3697a91dd' : undefined,
UNISOCKS_ABI,
false
) as Unisocks | null
return useContract<Unisocks>(SOCKS_CONTROLLER_ADDRESSES, UNISOCKS_ABI, false)
}
export function useV3NFTPositionManagerContract(withSignerIfPossible?: boolean): NonfungiblePositionManager | null {
const { chainId } = useActiveWeb3React()
const address = chainId ? NONFUNGIBLE_POSITION_MANAGER_ADDRESSES[chainId] : undefined
return useContract(address, NFTPositionManagerABI, withSignerIfPossible) as NonfungiblePositionManager | null
return useContract<NonfungiblePositionManager>(
NONFUNGIBLE_POSITION_MANAGER_ADDRESSES,
NFTPositionManagerABI,
withSignerIfPossible
)
}
export function useV3Factory(): UniswapV3Factory | null {
const { chainId } = useActiveWeb3React()
const address = chainId ? V3_CORE_FACTORY_ADDRESSES[chainId] : undefined
return useContract(address, V3FactoryABI) as UniswapV3Factory | null
export function useV3Factory() {
return useContract<UniswapV3Factory>(V3_CORE_FACTORY_ADDRESSES, V3FactoryABI) as UniswapV3Factory | null
}
export function useV3Pool(address: string | undefined): UniswapV3Pool | null {
return useContract(address, V3PoolABI) as UniswapV3Pool | null
export function useV3Pool(address: string | undefined) {
return useContract<UniswapV3Pool>(address, V3PoolABI)
}
export function useV3Quoter(): Quoter | null {
const { chainId } = useActiveWeb3React()
return useContract(chainId ? QUOTER_ADDRESSES[chainId] : undefined, QuoterABI) as Quoter | null
}
export function useTickLens(): TickLens | null {
const { chainId } = useActiveWeb3React()
const address = chainId ? TICK_LENS_ADDRESSES[chainId] : undefined
return useContract(address, TickLensABI) as TickLens | null
export function useV3Quoter() {
return useContract<Quoter>(QUOTER_ADDRESSES, QuoterABI)
}

View File

@@ -3,9 +3,7 @@ import { usePool } from 'hooks/usePools'
import { PositionDetails } from 'types/position'
import { useCurrency } from './Tokens'
export function useDerivedPositionInfo(
positionDetails: PositionDetails | undefined
): {
export function useDerivedPositionInfo(positionDetails: PositionDetails | undefined): {
position: Position | undefined
pool: Pool | undefined
} {

View File

@@ -6,9 +6,11 @@ import useENSName from './useENSName'
* Given a name or address, does a lookup to resolve to an address and name
* @param nameOrAddress ENS name or address
*/
export default function useENS(
nameOrAddress?: string | null
): { loading: boolean; address: string | null; name: string | null } {
export default function useENS(nameOrAddress?: string | null): {
loading: boolean
address: string | null
name: string | null
} {
const validated = isAddress(nameOrAddress)
const reverseLookup = useENSName(validated ? validated : undefined)
const lookup = useENSAddress(nameOrAddress)

View File

@@ -1,13 +1,13 @@
import JSBI from 'jsbi'
import { ChainId, Percent, CurrencyAmount, Currency, TradeType, Token } from '@uniswap/sdk-core'
import { Percent, CurrencyAmount, Currency, TradeType, Token } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import { splitSignature } from 'ethers/lib/utils'
import { useMemo, useState } from 'react'
import { UNI, USDC, DAI } from '../constants'
import { SWAP_ROUTER_ADDRESSES } from '../constants/v3'
import { SWAP_ROUTER_ADDRESSES } from '../constants/addresses'
import { DAI, UNI, USDC } from '../constants/tokens'
import { useSingleCallResult } from '../state/multicall/hooks'
import { useActiveWeb3React } from './index'
import { useActiveWeb3React } from './web3'
import { useEIP2612Contract } from './useContract'
import useIsArgentWallet from './useIsArgentWallet'
import useTransactionDeadline from './useTransactionDeadline'
@@ -29,28 +29,28 @@ interface PermitInfo {
// todo: read this information from extensions on token lists or elsewhere (permit registry?)
const PERMITTABLE_TOKENS: {
[chainId in ChainId]: {
[chainId: number]: {
[checksummedTokenAddress: string]: PermitInfo
}
} = {
[ChainId.MAINNET]: {
[1]: {
[USDC.address]: { type: PermitType.AMOUNT, name: 'USD Coin', version: '2' },
[DAI.address]: { type: PermitType.ALLOWED, name: 'Dai Stablecoin', version: '1' },
[UNI[ChainId.MAINNET].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
[UNI[1].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
},
[ChainId.RINKEBY]: {
[4]: {
['0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735']: { type: PermitType.ALLOWED, name: 'Dai Stablecoin', version: '1' },
[UNI[ChainId.RINKEBY].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
[UNI[4].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
},
[ChainId.ROPSTEN]: {
[UNI[ChainId.ROPSTEN].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
[3]: {
[UNI[3].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
['0x07865c6E87B9F70255377e024ace6630C1Eaa37F']: { type: PermitType.AMOUNT, name: 'USD Coin', version: '2' },
},
[ChainId.GÖRLI]: {
[UNI[ChainId.GÖRLI].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
[5]: {
[UNI[5].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
},
[ChainId.KOVAN]: {
[UNI[ChainId.KOVAN].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
[42]: {
[UNI[42].address]: { type: PermitType.AMOUNT, name: 'Uniswap' },
},
}
@@ -70,7 +70,7 @@ interface BaseSignatureData {
nonce: number
owner: string
spender: string
chainId: ChainId | number
chainId: number
tokenAddress: string
permitType: PermitType
}
@@ -275,11 +275,11 @@ export function useERC20PermitFromTrade(
allowedSlippage: Percent
) {
const { chainId } = useActiveWeb3React()
const swapRouterAddress = SWAP_ROUTER_ADDRESSES[chainId as ChainId]
const amountToApprove = useMemo(() => (trade ? trade.maximumAmountIn(allowedSlippage) : undefined), [
trade,
allowedSlippage,
])
const swapRouterAddress = chainId ? SWAP_ROUTER_ADDRESSES[chainId] : undefined
const amountToApprove = useMemo(
() => (trade ? trade.maximumAmountIn(allowedSlippage) : undefined),
[trade, allowedSlippage]
)
return useERC20Permit(
amountToApprove,

View File

@@ -1,27 +1,25 @@
import { nanoid } from '@reduxjs/toolkit'
import { ChainId } from '@uniswap/sdk-core'
import { TokenList } from '@uniswap/token-lists'
import { useCallback } from 'react'
import { useDispatch } from 'react-redux'
import { getNetworkLibrary, NETWORK_CHAIN_ID } from '../connectors'
import { getNetworkLibrary } from '../connectors'
import { AppDispatch } from '../state'
import { fetchTokenList } from '../state/lists/actions'
import getTokenList from '../utils/getTokenList'
import resolveENSContentHash from '../utils/resolveENSContentHash'
import { useActiveWeb3React } from './index'
import { useActiveWeb3React } from './web3'
export function useFetchListCallback(): (listUrl: string, sendDispatch?: boolean) => Promise<TokenList> {
const { chainId, library } = useActiveWeb3React()
const dispatch = useDispatch<AppDispatch>()
const ensResolver = useCallback(
(ensName: string) => {
if (!library || chainId !== ChainId.MAINNET) {
if (NETWORK_CHAIN_ID === ChainId.MAINNET) {
const networkLibrary = getNetworkLibrary()
if (networkLibrary) {
return resolveENSContentHash(ensName, networkLibrary)
}
async (ensName: string) => {
if (!library || chainId !== 1) {
const networkLibrary = getNetworkLibrary()
const network = await networkLibrary.getNetwork()
if (networkLibrary && network.chainId === 1) {
return resolveENSContentHash(ensName, networkLibrary)
}
throw new Error('Could not construct mainnet ENS resolver')
}

View File

@@ -1,10 +1,12 @@
import { useMemo } from 'react'
import { NEVER_RELOAD, useSingleCallResult } from '../state/multicall/hooks'
import { useActiveWeb3React } from './index'
import { useActiveWeb3React } from './web3'
import { useArgentWalletDetectorContract } from './useContract'
export default function useIsArgentWallet(): boolean {
const { account } = useActiveWeb3React()
const argentWalletDetector = useArgentWalletDetectorContract()
const call = useSingleCallResult(argentWalletDetector, 'isArgentWallet', [account ?? undefined], NEVER_RELOAD)
const inputs = useMemo(() => [account ?? undefined], [account])
const call = useSingleCallResult(argentWalletDetector, 'isArgentWallet', inputs, NEVER_RELOAD)
return call?.result?.[0] ?? false
}

View File

@@ -1,12 +1,12 @@
import { computePoolAddress } from '@uniswap/v3-sdk'
import { V3_CORE_FACTORY_ADDRESSES } from '../constants/addresses'
import { IUniswapV3PoolStateInterface } from '../types/v3/IUniswapV3PoolState'
import { Token, Currency } from '@uniswap/sdk-core'
import { useMemo } from 'react'
import { useActiveWeb3React } from './index'
import { useActiveWeb3React } from './web3'
import { useMultipleContractSingleData } from '../state/multicall/hooks'
import { wrappedCurrency } from '../utils/wrappedCurrency'
import { Pool, FeeAmount } from '@uniswap/v3-sdk'
import { V3_CORE_FACTORY_ADDRESSES } from 'constants/v3'
import { abi as IUniswapV3PoolStateABI } from '@uniswap/v3-core/artifacts/contracts/interfaces/pool/IUniswapV3PoolState.sol/IUniswapV3PoolState.json'
import { Interface } from '@ethersproject/abi'
@@ -28,8 +28,8 @@ export function usePools(
return poolKeys.map(([currencyA, currencyB, feeAmount]) => {
if (!chainId || !currencyA || !currencyB || !feeAmount) return null
const tokenA = wrappedCurrency(currencyA, chainId)
const tokenB = wrappedCurrency(currencyB, chainId)
const tokenA = currencyA?.wrapped
const tokenB = currencyB?.wrapped
if (!tokenA || !tokenB || tokenA.equals(tokenB)) return null
const [token0, token1] = tokenA.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA]
return [token0, token1, feeAmount]

View File

@@ -29,9 +29,10 @@ type UsePositionTokenURIResult =
export function usePositionTokenURI(tokenId: TokenId | undefined): UsePositionTokenURIResult {
const contract = useV3NFTPositionManagerContract()
const inputs = useMemo(() => [tokenId instanceof BigNumber ? tokenId.toHexString() : tokenId?.toString(16)], [
tokenId,
])
const inputs = useMemo(
() => [tokenId instanceof BigNumber ? tokenId.toHexString() : tokenId?.toString(16)],
[tokenId]
)
const { result, error, loading, valid } = useSingleCallResult(contract, 'tokenURI', inputs, NEVER_RELOAD, 1_600_000)
return useMemo(() => {

View File

@@ -1,14 +1,15 @@
import JSBI from 'jsbi'
import { useMemo } from 'react'
import { NEVER_RELOAD, useSingleCallResult } from '../state/multicall/hooks'
import { useActiveWeb3React } from './index'
import { useActiveWeb3React } from './web3'
import { useSocksController } from './useContract'
export default function useSocksBalance(): JSBI | undefined {
const { account } = useActiveWeb3React()
const socksContract = useSocksController()
const { result } = useSingleCallResult(socksContract, 'balanceOf', [account ?? undefined], NEVER_RELOAD)
const inputs = useMemo(() => [account ?? undefined], [account])
const { result } = useSingleCallResult(socksContract, 'balanceOf', inputs, NEVER_RELOAD)
const data = result?.[0]
return data ? JSBI.BigInt(data.toString()) : undefined
}

View File

@@ -1,14 +1,17 @@
import { BigNumber } from '@ethersproject/bignumber'
import { Router, Trade as V2Trade } from '@uniswap/v2-sdk'
import { SwapRouter, Trade as V3Trade } from '@uniswap/v3-sdk'
import { ChainId, Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { useMemo } from 'react'
import { SWAP_ROUTER_ADDRESSES } from '../constants/v3'
import { SWAP_ROUTER_ADDRESSES } from '../constants/addresses'
import { calculateGasMargin } from '../utils/calculateGasMargin'
import approveAmountCalldata from '../utils/approveAmountCalldata'
import { getTradeVersion } from '../utils/getTradeVersion'
import { useTransactionAdder } from '../state/transactions/hooks'
import { calculateGasMargin, isAddress, shortenAddress } from '../utils'
import { isAddress, shortenAddress } from '../utils'
import isZero from '../utils/isZero'
import { useActiveWeb3React } from './index'
import { useActiveWeb3React } from './web3'
import { useArgentWalletContract } from './useArgentWalletContract'
import { useV2RouterContract } from './useContract'
import { SignatureData } from './useERC20Permit'
import useTransactionDeadline from './useTransactionDeadline'
@@ -60,6 +63,7 @@ function useSwapCallArguments(
const recipient = recipientAddressOrName === null ? account : recipientAddress
const deadline = useTransactionDeadline()
const routerContract = useV2RouterContract()
const argentWalletContract = useArgentWalletContract()
return useMemo(() => {
if (!trade || !recipient || !library || !account || !chainId || !deadline) return []
@@ -67,6 +71,7 @@ function useSwapCallArguments(
if (trade instanceof V2Trade) {
if (!routerContract) return []
const swapMethods = []
swapMethods.push(
Router.swapCallParameters(trade, {
feeOnTransfer: false,
@@ -86,14 +91,33 @@ function useSwapCallArguments(
})
)
}
return swapMethods.map(({ methodName, args, value }) => ({
address: routerContract.address,
calldata: routerContract.interface.encodeFunctionData(methodName, args),
value,
}))
return swapMethods.map(({ methodName, args, value }) => {
if (argentWalletContract && trade.inputAmount.currency.isToken) {
return {
address: argentWalletContract.address,
calldata: argentWalletContract.interface.encodeFunctionData('wc_multiCall', [
[
approveAmountCalldata(trade.maximumAmountIn(allowedSlippage), routerContract.address),
{
to: routerContract.address,
value: value,
data: routerContract.interface.encodeFunctionData(methodName, args),
},
],
]),
value: '0x0',
}
} else {
return {
address: routerContract.address,
calldata: routerContract.interface.encodeFunctionData(methodName, args),
value,
}
}
})
} else {
// trade is V3Trade
const swapRouterAddress = SWAP_ROUTER_ADDRESSES[chainId as ChainId]
const swapRouterAddress = chainId ? SWAP_ROUTER_ADDRESSES[chainId] : undefined
if (!swapRouterAddress) return []
const { value, calldata } = SwapRouter.swapCallParameters(trade, {
@@ -121,7 +145,24 @@ function useSwapCallArguments(
}
: {}),
})
if (argentWalletContract && trade.inputAmount.currency.isToken) {
return [
{
address: argentWalletContract.address,
calldata: argentWalletContract.interface.encodeFunctionData('wc_multiCall', [
[
approveAmountCalldata(trade.maximumAmountIn(allowedSlippage), swapRouterAddress),
{
to: swapRouterAddress,
value: value,
data: calldata,
},
],
]),
value: '0x0',
},
]
}
return [
{
address: swapRouterAddress,
@@ -130,7 +171,18 @@ function useSwapCallArguments(
},
]
}
}, [account, allowedSlippage, chainId, deadline, library, recipient, routerContract, signatureData, trade])
}, [
account,
allowedSlippage,
argentWalletContract,
chainId,
deadline,
library,
recipient,
routerContract,
signatureData,
trade,
])
}
/**
@@ -153,16 +205,26 @@ export function swapErrorToUserReadableMessage(error: any): string {
case 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT':
case 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT':
return 'This transaction will not succeed either due to price movement or fee on transfer. Try increasing your slippage tolerance.'
case 'TransferHelper: TRANSFER_FROM_FAILED':
return 'The input token cannot be transferred. There may be an issue with the input token.'
case 'UniswapV2: TRANSFER_FAILED':
return 'The token could not be transferred. There may be an issue with the token.'
return 'The output token cannot be transferred. There may be an issue with the output token.'
case 'UniswapV2: K':
return 'The Uniswap invariant x*y=k was not satisfied by the swap. This usually means one of the tokens you are swapping incorporates custom behavior on transfer.'
case 'Too little received':
case 'Too much requested':
case 'STF':
return 'This transaction will not succeed due to price movement. Try increasing your slippage tolerance.'
return 'This transaction will not succeed due to price movement. Try increasing your slippage tolerance. Note fee on transfer and rebase tokens are incompatible with Uniswap V3.'
case 'TF':
return 'The output token cannot be transferred. There may be an issue with the output token. Note fee on transfer and rebase tokens are incompatible with Uniswap V3.'
default:
return 'Unknown error. Please join the Discord to get help.'
if (reason?.indexOf('undefined is not an object') !== -1) {
console.error(error, reason)
return 'An error occurred when trying to execute this swap. You may need to increase your slippage tolerance. If that does not work, there may be an incompatibility with the token you are trading. Note fee on transfer and rebase tokens are incompatible with Uniswap V3.'
}
return `Unknown error${
reason ? `: "${reason}"` : ''
}. Try increasing your slippage tolerance. Note fee on transfer and rebase tokens are incompatible with Uniswap V3.`
}
}
@@ -271,8 +333,8 @@ export function useSwapCallback(
.then((response) => {
const inputSymbol = trade.inputAmount.currency.symbol
const outputSymbol = trade.outputAmount.currency.symbol
const inputAmount = trade.maximumAmountIn(allowedSlippage).toSignificant(4)
const outputAmount = trade.minimumAmountOut(allowedSlippage).toSignificant(4)
const inputAmount = trade.inputAmount.toSignificant(4)
const outputAmount = trade.outputAmount.toSignificant(4)
const base = `Swap ${inputAmount} ${inputSymbol} for ${outputAmount} ${outputSymbol}`
const withRecipient =
@@ -302,13 +364,11 @@ export function useSwapCallback(
// otherwise, the error was unexpected and we need to convey that
console.error(`Swap failed`, error, address, calldata, value)
throw new Error(
`Swap failed: ${'reason' in error ? swapErrorToUserReadableMessage(error) : error.message}`
)
throw new Error(`Swap failed: ${swapErrorToUserReadableMessage(error)}`)
}
})
},
error: null,
}
}, [trade, library, account, chainId, recipient, recipientAddressOrName, swapCalls, allowedSlippage, addTransaction])
}, [trade, library, account, chainId, recipient, recipientAddressOrName, swapCalls, addTransaction])
}

View File

@@ -1,19 +0,0 @@
import { useActiveWeb3React } from '.'
import { useState, useEffect } from 'react'
export function useTimestampFromBlock(block: number | undefined): number | undefined {
const { library } = useActiveWeb3React()
const [timestamp, setTimestamp] = useState<number>()
useEffect(() => {
async function fetchTimestamp() {
if (block) {
const blockData = await library?.getBlock(block)
blockData && setTimestamp(blockData.timestamp)
}
}
if (!timestamp) {
fetchTimestamp()
}
}, [block, library, timestamp])
return timestamp
}

View File

@@ -9,8 +9,8 @@ export function useTokenAllowance(token?: Token, owner?: string, spender?: strin
const inputs = useMemo(() => [owner, spender], [owner, spender])
const allowance = useSingleCallResult(contract, 'allowance', inputs).result
return useMemo(() => (token && allowance ? CurrencyAmount.fromRawAmount(token, allowance.toString()) : undefined), [
token,
allowance,
])
return useMemo(
() => (token && allowance ? CurrencyAmount.fromRawAmount(token, allowance.toString()) : undefined),
[token, allowance]
)
}

View File

@@ -1,10 +1,13 @@
import { ChainId, Currency, CurrencyAmount, currencyEquals, Price, Token, WETH9 } from '@uniswap/sdk-core'
import JSBI from 'jsbi'
import { Currency, CurrencyAmount, Price, Token } from '@uniswap/sdk-core'
import { useMemo } from 'react'
import { USDC } from '../constants'
import { PairState, useV2Pairs } from './useV2Pairs'
import { useActiveWeb3React } from '../hooks'
import { wrappedCurrency } from '../utils/wrappedCurrency'
import { USDC } from '../constants/tokens'
import { useV2TradeExactOut } from './useV2Trade'
import { useBestV3TradeExactOut, V3TradeState } from './useBestV3Trade'
import { useActiveWeb3React } from './web3'
// USDC amount used when calculating spot price for a given currency.
// The amount is large enough to filter low liquidity pairs.
const usdcCurrencyAmount = CurrencyAmount.fromRawAmount(USDC, 100_000e6)
/**
* Returns the price in USDC of the input currency
@@ -12,25 +15,19 @@ import { wrappedCurrency } from '../utils/wrappedCurrency'
*/
export default function useUSDCPrice(currency?: Currency): Price<Currency, Token> | undefined {
const { chainId } = useActiveWeb3React()
const wrapped = wrappedCurrency(currency, chainId)
const weth = WETH9[chainId as ChainId]
const tokenPairs: [Currency | undefined, Currency | undefined][] = useMemo(
() => [
[chainId && wrapped && currencyEquals(weth, wrapped) ? undefined : currency, chainId ? weth : undefined],
[wrapped?.equals(USDC) ? undefined : wrapped, chainId === ChainId.MAINNET ? USDC : undefined],
[chainId ? weth : undefined, chainId === ChainId.MAINNET ? USDC : undefined],
],
[chainId, currency, weth, wrapped]
)
const [[ethPairState, ethPair], [usdcPairState, usdcPair], [usdcEthPairState, usdcEthPair]] = useV2Pairs(tokenPairs)
const v2USDCTrade = useV2TradeExactOut(currency, chainId === 1 ? usdcCurrencyAmount : undefined, {
maxHops: 2,
})
const v3USDCTrade = useBestV3TradeExactOut(currency, chainId === 1 ? usdcCurrencyAmount : undefined)
return useMemo(() => {
if (!currency || !wrapped || !chainId) {
if (!currency || !chainId) {
return undefined
}
// return some fake price data for non-mainnet
if (chainId !== ChainId.MAINNET) {
if (chainId !== 1) {
const fakeUSDC = new Token(chainId, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'fUSDC', 'Fake USDC')
return new Price(
currency,
@@ -40,40 +37,22 @@ export default function useUSDCPrice(currency?: Currency): Price<Currency, Token
)
}
// handle weth/eth
if (wrapped.equals(weth)) {
if (usdcPair) {
const price = usdcPair.priceOf(weth)
return new Price(currency, USDC, price.denominator, price.numerator)
} else {
return undefined
}
}
// handle usdc
if (wrapped.equals(USDC)) {
if (currency?.wrapped.equals(USDC)) {
return new Price(USDC, USDC, '1', '1')
}
const ethPairETHAmount = ethPair?.reserveOf(weth)
const ethPairETHUSDCValue: JSBI =
ethPairETHAmount && usdcEthPair ? usdcEthPair.priceOf(weth).quote(ethPairETHAmount).quotient : JSBI.BigInt(0)
// use v2 price if available, v3 as fallback
if (v2USDCTrade) {
const { numerator, denominator } = v2USDCTrade.route.midPrice
return new Price(currency, USDC, denominator, numerator)
} else if (v3USDCTrade.state === V3TradeState.VALID && v3USDCTrade.trade) {
const { numerator, denominator } = v3USDCTrade.trade.route.midPrice
return new Price(currency, USDC, denominator, numerator)
}
// all other tokens
// first try the usdc pair
if (usdcPairState === PairState.EXISTS && usdcPair && usdcPair.reserveOf(USDC).greaterThan(ethPairETHUSDCValue)) {
const price = usdcPair.priceOf(wrapped)
return new Price(currency, USDC, price.denominator, price.numerator)
}
if (ethPairState === PairState.EXISTS && ethPair && usdcEthPairState === PairState.EXISTS && usdcEthPair) {
if (usdcEthPair.reserveOf(USDC).greaterThan('0') && ethPair.reserveOf(weth).greaterThan('0')) {
const ethUsdcPrice = usdcEthPair.priceOf(USDC)
const currencyEthPrice = ethPair.priceOf(weth)
const usdcPrice = ethUsdcPrice.multiply(currencyEthPrice).invert()
return new Price(currency, USDC, usdcPrice.denominator, usdcPrice.numerator)
}
}
return undefined
}, [chainId, currency, ethPair, ethPairState, usdcEthPair, usdcEthPairState, usdcPair, usdcPairState, weth, wrapped])
}, [chainId, currency, v2USDCTrade, v3USDCTrade])
}
export function useUSDCValue(currencyAmount: CurrencyAmount<Currency> | undefined | null) {
@@ -81,6 +60,10 @@ export function useUSDCValue(currencyAmount: CurrencyAmount<Currency> | undefine
return useMemo(() => {
if (!price || !currencyAmount) return null
return price.quote(currencyAmount)
try {
return price.quote(currencyAmount)
} catch (error) {
return null
}
}, [currencyAmount, price])
}

View File

@@ -2,9 +2,7 @@ import { Pair } from '@uniswap/v2-sdk'
import { useMemo } from 'react'
import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.json'
import { Interface } from '@ethersproject/abi'
import { useActiveWeb3React } from './index'
import { useMultipleContractSingleData } from '../state/multicall/hooks'
import { wrappedCurrency } from '../utils/wrappedCurrency'
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
const PAIR_INTERFACE = new Interface(IUniswapV2PairABI)
@@ -17,15 +15,9 @@ export enum PairState {
}
export function useV2Pairs(currencies: [Currency | undefined, Currency | undefined][]): [PairState, Pair | null][] {
const { chainId } = useActiveWeb3React()
const tokens = useMemo(
() =>
currencies.map(([currencyA, currencyB]) => [
wrappedCurrency(currencyA, chainId),
wrappedCurrency(currencyB, chainId),
]),
[chainId, currencies]
() => currencies.map(([currencyA, currencyB]) => [currencyA?.wrapped, currencyB?.wrapped]),
[currencies]
)
const pairAddresses = useMemo(
@@ -61,5 +53,6 @@ export function useV2Pairs(currencies: [Currency | undefined, Currency | undefin
}
export function useV2Pair(tokenA?: Currency, tokenB?: Currency): [PairState, Pair | null] {
return useV2Pairs([[tokenA, tokenB]])[0]
const inputs: [[Currency | undefined, Currency | undefined]] = useMemo(() => [[tokenA, tokenB]], [tokenA, tokenB])
return useV2Pairs(inputs)[0]
}

View File

@@ -1,9 +1,8 @@
import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { Pair, Trade } from '@uniswap/v2-sdk'
import { useMemo } from 'react'
import { useUserSingleHopOnly } from 'state/user/hooks'
import { isTradeBetter } from 'utils/isTradeBetter'
import { BETTER_TRADE_LESS_HOPS_THRESHOLD } from '../constants'
import { BETTER_TRADE_LESS_HOPS_THRESHOLD } from '../constants/misc'
import { useAllCurrencyCombinations } from './useAllCurrencyCombinations'
import { PairState, useV2Pairs } from './useV2Pairs'
@@ -36,15 +35,14 @@ const MAX_HOPS = 3
*/
export function useV2TradeExactIn(
currencyAmountIn?: CurrencyAmount<Currency>,
currencyOut?: Currency
currencyOut?: Currency,
{ maxHops = MAX_HOPS } = {}
): Trade<Currency, Currency, TradeType.EXACT_INPUT> | null {
const allowedPairs = useAllCommonPairs(currencyAmountIn?.currency, currencyOut)
const [singleHopOnly] = useUserSingleHopOnly()
return useMemo(() => {
if (currencyAmountIn && currencyOut && allowedPairs.length > 0) {
if (singleHopOnly) {
if (maxHops === 1) {
return (
Trade.bestTradeExactIn(allowedPairs, currencyAmountIn, currencyOut, { maxHops: 1, maxNumResults: 1 })[0] ??
null
@@ -52,7 +50,7 @@ export function useV2TradeExactIn(
}
// search through trades with varying hops, find best trade out of them
let bestTradeSoFar: Trade<Currency, Currency, TradeType.EXACT_INPUT> | null = null
for (let i = 1; i <= MAX_HOPS; i++) {
for (let i = 1; i <= maxHops; i++) {
const currentTrade: Trade<Currency, Currency, TradeType.EXACT_INPUT> | null =
Trade.bestTradeExactIn(allowedPairs, currencyAmountIn, currencyOut, { maxHops: i, maxNumResults: 1 })[0] ??
null
@@ -65,7 +63,7 @@ export function useV2TradeExactIn(
}
return null
}, [allowedPairs, currencyAmountIn, currencyOut, singleHopOnly])
}, [allowedPairs, currencyAmountIn, currencyOut, maxHops])
}
/**
@@ -73,15 +71,14 @@ export function useV2TradeExactIn(
*/
export function useV2TradeExactOut(
currencyIn?: Currency,
currencyAmountOut?: CurrencyAmount<Currency>
currencyAmountOut?: CurrencyAmount<Currency>,
{ maxHops = MAX_HOPS } = {}
): Trade<Currency, Currency, TradeType.EXACT_OUTPUT> | null {
const allowedPairs = useAllCommonPairs(currencyIn, currencyAmountOut?.currency)
const [singleHopOnly] = useUserSingleHopOnly()
return useMemo(() => {
if (currencyIn && currencyAmountOut && allowedPairs.length > 0) {
if (singleHopOnly) {
if (maxHops === 1) {
return (
Trade.bestTradeExactOut(allowedPairs, currencyIn, currencyAmountOut, { maxHops: 1, maxNumResults: 1 })[0] ??
null
@@ -89,7 +86,7 @@ export function useV2TradeExactOut(
}
// search through trades with varying hops, find best trade out of them
let bestTradeSoFar: Trade<Currency, Currency, TradeType.EXACT_OUTPUT> | null = null
for (let i = 1; i <= MAX_HOPS; i++) {
for (let i = 1; i <= maxHops; i++) {
const currentTrade =
Trade.bestTradeExactOut(allowedPairs, currencyIn, currencyAmountOut, { maxHops: i, maxNumResults: 1 })[0] ??
null
@@ -100,5 +97,5 @@ export function useV2TradeExactOut(
return bestTradeSoFar
}
return null
}, [currencyIn, currencyAmountOut, allowedPairs, singleHopOnly])
}, [currencyIn, currencyAmountOut, allowedPairs, maxHops])
}

View File

@@ -3,27 +3,31 @@ import { useEffect, useState } from 'react'
import { useV3NFTPositionManagerContract } from './useContract'
import { BigNumber } from '@ethersproject/bignumber'
import { Pool } from '@uniswap/v3-sdk'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { CurrencyAmount, Currency } from '@uniswap/sdk-core'
import { useBlockNumber } from 'state/application/hooks'
import { unwrappedToken } from 'utils/unwrappedToken'
const MAX_UINT128 = BigNumber.from(2).pow(128).sub(1)
// compute current + counterfactual fees for a v3 position
export function useV3PositionFees(
pool?: Pool,
tokenId?: BigNumber
): [CurrencyAmount<Token>, CurrencyAmount<Token>] | [undefined, undefined] {
tokenId?: BigNumber,
asWETH = false
): [CurrencyAmount<Currency>, CurrencyAmount<Currency>] | [undefined, undefined] {
const positionManager = useV3NFTPositionManagerContract(false)
const owner = useSingleCallResult(tokenId ? positionManager : null, 'ownerOf', [tokenId]).result?.[0]
const owner: string | undefined = useSingleCallResult(tokenId ? positionManager : null, 'ownerOf', [tokenId])
.result?.[0]
const tokenIdHexString = tokenId?.toHexString()
const latestBlockNumber = useBlockNumber()
// TODO find a way to get this into multicall
// because these amounts don't ever go down, we don't actually need to clear this state
// latestBlockNumber is included to ensure data stays up-to-date every block
const [amounts, setAmounts] = useState<[BigNumber, BigNumber]>()
useEffect(() => {
let stale = false
if (positionManager && tokenIdHexString && owner && typeof latestBlockNumber === 'number') {
positionManager.callStatic
.collect(
@@ -36,15 +40,19 @@ export function useV3PositionFees(
{ from: owner } // need to simulate the call as the owner
)
.then((results) => {
setAmounts([results.amount0, results.amount1])
if (!stale) setAmounts([results.amount0, results.amount1])
})
}
return () => {
stale = true
}
}, [positionManager, tokenIdHexString, owner, latestBlockNumber])
if (pool && amounts) {
return [
CurrencyAmount.fromRawAmount(pool.token0, amounts[0].toString()),
CurrencyAmount.fromRawAmount(pool.token1, amounts[1].toString()),
CurrencyAmount.fromRawAmount(!asWETH ? unwrappedToken(pool.token0) : pool.token0, amounts[0].toString()),
CurrencyAmount.fromRawAmount(!asWETH ? unwrappedToken(pool.token1) : pool.token1, amounts[1].toString()),
]
} else {
return [undefined, undefined]

View File

@@ -1,9 +1,9 @@
import { Currency, currencyEquals, WETH9 } from '@uniswap/sdk-core'
import { Currency, WETH9 } from '@uniswap/sdk-core'
import { useMemo } from 'react'
import { tryParseAmount } from '../state/swap/hooks'
import { useTransactionAdder } from '../state/transactions/hooks'
import { useCurrencyBalance } from '../state/wallet/hooks'
import { useActiveWeb3React } from './index'
import { useActiveWeb3React } from './web3'
import { useWETHContract } from './useContract'
export enum WrapType {
@@ -33,11 +33,13 @@ export default function useWrapCallback(
return useMemo(() => {
if (!wethContract || !chainId || !inputCurrency || !outputCurrency) return NOT_APPLICABLE
const weth = WETH9[chainId]
if (!weth) return NOT_APPLICABLE
const hasInputAmount = Boolean(inputAmount?.greaterThan('0'))
const sufficientBalance = inputAmount && balance && !balance.lessThan(inputAmount)
if (inputCurrency.isEther && currencyEquals(WETH9[chainId], outputCurrency)) {
if (inputCurrency.isNative && weth.equals(outputCurrency)) {
return {
wrapType: WrapType.WRAP,
execute:
@@ -53,7 +55,7 @@ export default function useWrapCallback(
: undefined,
inputError: sufficientBalance ? undefined : hasInputAmount ? 'Insufficient ETH balance' : 'Enter ETH amount',
}
} else if (currencyEquals(WETH9[chainId], inputCurrency) && outputCurrency.isEther) {
} else if (weth.equals(inputCurrency) && outputCurrency.isNative) {
return {
wrapType: WrapType.UNWRAP,
execute:

View File

@@ -1,13 +1,12 @@
import { Web3Provider } from '@ethersproject/providers'
import { ChainId } from '@uniswap/sdk-core'
import { useWeb3React as useWeb3ReactCore } from '@web3-react/core'
import { Web3ReactContextInterface } from '@web3-react/core/dist/types'
import { useEffect, useState } from 'react'
import { isMobile } from 'react-device-detect'
import { injected } from '../connectors'
import { NetworkContextName } from '../constants'
import { NetworkContextName } from '../constants/misc'
export function useActiveWeb3React(): Web3ReactContextInterface<Web3Provider> & { chainId?: ChainId } {
export function useActiveWeb3React(): Web3ReactContextInterface<Web3Provider> {
const context = useWeb3ReactCore<Web3Provider>()
const contextNetwork = useWeb3ReactCore<Web3Provider>(NetworkContextName)
return context.active ? context : contextNetwork

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