Add page MVP UI + v3 pool hooks and state (#35)
* WIP start usePool and useDerivedMint hooks * naming updates * Use real tick and pool math * rate updates * WIP start usePool and useDerivedMint hooks * naming updates * Use real tick and pool math * rate updates * fix warnings * fix incorrect import * clean up state, fix preview * same token check * amoutn parse update * update hard coded chain id * fix price creation in util * update 1 amount in price calculation * update comments * update tick spacing input * fix label on counter * update rate label on range select * update labels * fixing pool hook * clean pool hook * preserve working rate switching * reset values on rate switch * clean up derived hook - setup for testnet * format slippage amounts and support ETH * fix import error * fix package.json dependencies * silence warnings * silence more warnings * bump multicodec and multihashes * update migrator constants * update txn to use sdk calldata * fix txn formatting, update summary * Squashed commit of the following: commit b81ff7ca9e57bb8c3823be4c54827e5955fb3d15 Author: ianlapham <ianlapham@gmail.com> Date: Mon Apr 12 23:46:09 2021 -0400 fix txn formatting, update summary commit b9f91b0746c546602d336c8fd6f614ec9b4f3f19 Author: ianlapham <ianlapham@gmail.com> Date: Mon Apr 12 19:29:12 2021 -0400 update txn to use sdk calldata commit 20acf704c67cfd4f597494c8cb9c672c6270ae02 Merge: 4431914 2462901 Author: ianlapham <ianlapham@gmail.com> Date: Sun Apr 11 20:33:39 2021 -0400 Merge branch 'minting' of https://github.com/Uniswap/v3-interface into minting commit 44319146372e1c373b025741ae896fa2476e5765 Author: ianlapham <ianlapham@gmail.com> Date: Sun Apr 11 20:32:35 2021 -0400 update migrator constants commit 35e0618de06ba316d3a3f327075625760414ab83 Merge: 8927882 c3f65e3 Author: ianlapham <ianlapham@gmail.com> Date: Sun Apr 11 20:13:36 2021 -0400 Merge branch 'main' of https://github.com/Uniswap/v3-interface into minting commit 24629019e80c368c337a2679a51d4acb1097171c Author: Noah Zinsmeister <noahwz@gmail.com> Date: Fri Apr 9 15:56:25 2021 -0400 bump multicodec and multihashes commit 9b5dd1876a64acbf6694d208b608bb0b429e317f Author: Noah Zinsmeister <noahwz@gmail.com> Date: Fri Apr 9 14:59:09 2021 -0400 silence more warnings commit 140ddc1b54c7fbdd7ead2fa64bcc302f201d69f5 Author: Noah Zinsmeister <noahwz@gmail.com> Date: Fri Apr 9 14:57:58 2021 -0400 silence warnings commit 5a2726ebdd4ffaacfb3d8ec7903a944042c1bd9d Author: Noah Zinsmeister <noahwz@gmail.com> Date: Fri Apr 9 14:35:01 2021 -0400 fix package.json dependencies commit 7c4d0a40931338de9a6197652b82fdab773483e3 Author: Noah Zinsmeister <noahwz@gmail.com> Date: Fri Apr 9 14:21:46 2021 -0400 fix import error commit e49ef19cbef7fbdf1737787a439e7cb78ba295b4 Merge: 8927882 c3f65e3 Author: Noah Zinsmeister <noahwz@gmail.com> Date: Fri Apr 9 14:08:34 2021 -0400 Merge branch 'main' into minting commit 89278825bd798a87d6010a74f8fc1d2b34a8ece1 Author: ianlapham <ianlapham@gmail.com> Date: Thu Apr 8 15:18:40 2021 -0400 format slippage amounts and support ETH commit 9a90b19e9a759cbc0c3e903a983660730c8833ad Author: ianlapham <ianlapham@gmail.com> Date: Wed Apr 7 19:43:43 2021 -0400 clean up derived hook - setup for testnet commit dc034bc78a147f95f47b077d28a7d6e3165cedd7 Author: ianlapham <ianlapham@gmail.com> Date: Wed Apr 7 00:48:24 2021 -0400 reset values on rate switch commit bb5ccb2c853f7b2c27ec8d2f34f42a1b06f845b9 Author: ianlapham <ianlapham@gmail.com> Date: Wed Apr 7 00:38:39 2021 -0400 preserve working rate switching commit 5312d0ae7015150da48ba304de8c7a02b7d8925c Author: ianlapham <ianlapham@gmail.com> Date: Mon Apr 5 13:52:46 2021 -0400 clean pool hook commit 5222de14834e76c37755225be17214a6e798d872 Merge: b2ba466 24521f0 Author: ianlapham <ianlapham@gmail.com> Date: Mon Apr 5 12:20:34 2021 -0400 Merge branch 'main' of https://github.com/Uniswap/v3-interface into minting commit b2ba46684a7b0bd8a8362f5990f4a208bfeff2dd Author: ianlapham <ianlapham@gmail.com> Date: Mon Apr 5 12:19:20 2021 -0400 fixing pool hook commit b10742af99a725e04c1b756aa20f99e995f8cfeb Author: ianlapham <ianlapham@gmail.com> Date: Thu Apr 1 16:53:52 2021 -0400 update labels commit 05abd395949245596c95090a9d5d77c7c272dbd3 Author: ianlapham <ianlapham@gmail.com> Date: Thu Apr 1 15:34:17 2021 -0400 update rate label on range select commit f098d01b6f4dc1dcb99e0fa314dde93647a19bb6 Author: ianlapham <ianlapham@gmail.com> Date: Thu Apr 1 15:26:30 2021 -0400 fix label on counter commit 16ffe61e8ee2b677adf5d468efa9d7aa8d7e092e Author: ianlapham <ianlapham@gmail.com> Date: Thu Apr 1 15:06:50 2021 -0400 update tick spacing input commit 0fa2c8a15821dd32ec978750991a962ecb8f7344 Author: ianlapham <ianlapham@gmail.com> Date: Thu Apr 1 14:53:18 2021 -0400 update comments commit 1fccf57a1ef081ef6ba9790dc20e0ed604ac2b09 Author: ianlapham <ianlapham@gmail.com> Date: Thu Apr 1 14:52:37 2021 -0400 update 1 amount in price calculation commit b0e5d22bf8c57b3eacd75f077f68aaca4a9f975a Author: ianlapham <ianlapham@gmail.com> Date: Thu Apr 1 14:46:41 2021 -0400 fix price creation in util commit 1ce246e85372e4f120f983ca18a1eb3d16e8647e Author: ianlapham <ianlapham@gmail.com> Date: Thu Apr 1 13:55:14 2021 -0400 update hard coded chain id commit 2360b2d0a3233b604956e89de4bd7b09c0506875 Author: ianlapham <ianlapham@gmail.com> Date: Thu Apr 1 13:09:21 2021 -0400 amoutn parse update commit 6a99a7b71fe446fe77cb2741adce4c067862ca4a Author: ianlapham <ianlapham@gmail.com> Date: Thu Apr 1 13:05:41 2021 -0400 same token check commit 83a1fd5a9ff02c6a49532cb54a57770b52fc052e Author: ianlapham <ianlapham@gmail.com> Date: Thu Apr 1 12:31:21 2021 -0400 clean up state, fix preview commit 8592383b8386d7adbbaeaa2c6f9c36bb121d1c65 Author: ianlapham <ianlapham@gmail.com> Date: Wed Mar 31 22:47:56 2021 -0400 fix incorrect import commit ce526fd545e52142f847dbf3caec1ca37bb0650b Author: ianlapham <ianlapham@gmail.com> Date: Wed Mar 31 22:36:10 2021 -0400 fix warnings commit 572770fd3e000ce31cd3a6c5c5c91eac92cc8c5c Merge: a9e5b6c 2677491 Author: ianlapham <ianlapham@gmail.com> Date: Wed Mar 31 22:16:30 2021 -0400 Merge branch 'minting' of https://github.com/Uniswap/v3-interface into minting commit a9e5b6c5e5983e279a640886783f97c33b713125 Author: ianlapham <ianlapham@gmail.com> Date: Wed Mar 31 22:12:43 2021 -0400 rate updates commit b88cab6c06176eefe5cf71f7cc3e3664d9f514ab Author: ianlapham <ianlapham@gmail.com> Date: Wed Mar 31 16:15:08 2021 -0400 Use real tick and pool math commit ed933cfd17141174c03b0bcac5f41cf75ff9b258 Author: ianlapham <ianlapham@gmail.com> Date: Sun Mar 28 22:42:05 2021 -0400 naming updates commit 50c0a0ece5c6c66a603508529c5e7a28f45db632 Author: ianlapham <ianlapham@gmail.com> Date: Sun Mar 28 22:36:08 2021 -0400 WIP start usePool and useDerivedMint hooks commit 2677491e2128e1318a0dd4307e63069e0f8e1dfe Author: ianlapham <ianlapham@gmail.com> Date: Wed Mar 31 22:12:43 2021 -0400 rate updates commit c2f59d6c61068a2bf4d34d102d5d28c9863ce982 Author: ianlapham <ianlapham@gmail.com> Date: Wed Mar 31 16:15:08 2021 -0400 Use real tick and pool math commit 7d53e5c7e979be19fc5c63eb52307f302328c4eb Author: ianlapham <ianlapham@gmail.com> Date: Sun Mar 28 22:42:05 2021 -0400 naming updates commit 9022650d391682f97e71d336021c2db2e5ea5455 Author: ianlapham <ianlapham@gmail.com> Date: Sun Mar 28 22:36:08 2021 -0400 WIP start usePool and useDerivedMint hooks * remove 1337 references * clean up multicall * clean up redirects/router * cleanup * improve useAllV3Ticks * fix multicall * typo * Fix code style issues with ESLint * preserve sticky * reset to non fixed scroll * fix inputs at 1 * update tests * fix routes * sticky sidebar Co-authored-by: Noah Zinsmeister <noahwz@gmail.com> Co-authored-by: Lint Action <lint-action@samuelmeuli.com> Co-authored-by: Callil Capuozzo <callil.capuozzo@gmail.com>
This commit is contained in:
parent
c3f65e3abd
commit
0c0305a53d
1
.env
1
.env
@ -1,2 +1,3 @@
|
|||||||
REACT_APP_CHAIN_ID="1"
|
REACT_APP_CHAIN_ID="1"
|
||||||
REACT_APP_NETWORK_URL="https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
|
REACT_APP_NETWORK_URL="https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
|
||||||
|
SKIP_PREFLIGHT_CHECK=true
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
describe('Add Liquidity', () => {
|
describe('Add Liquidity', () => {
|
||||||
it('loads the two correct tokens', () => {
|
it('loads the two correct tokens', () => {
|
||||||
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85-0xc778417E063141139Fce010982780140Aa0cD5Ab')
|
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab/500')
|
||||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
|
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
|
||||||
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'ETH')
|
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'ETH')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('does not crash if ETH is duplicated', () => {
|
it('does not crash if ETH is duplicated', () => {
|
||||||
cy.visit('/add/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xc778417E063141139Fce010982780140Aa0cD5Ab')
|
cy.visit('/add/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xc778417E063141139Fce010982780140Aa0cD5Ab')
|
||||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'ETH')
|
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'ETH')
|
||||||
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('not.contain.text', 'ETH')
|
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('not.contain.text', 'ETH')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('token not in storage is loaded', () => {
|
it('token not in storage is loaded', () => {
|
||||||
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'SKL')
|
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'SKL')
|
||||||
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'MKR')
|
cy.get('#add-liquidity-input-tokenb .token-symbol-container').should('contain.text', 'MKR')
|
||||||
})
|
})
|
||||||
@ -23,28 +23,4 @@ describe('Add Liquidity', () => {
|
|||||||
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
||||||
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
|
cy.get('#add-liquidity-input-tokena .token-symbol-container').should('contain.text', 'MKR')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('redirects /add/token-token to add/token/token', () => {
|
|
||||||
cy.visit('/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
|
||||||
cy.url().should(
|
|
||||||
'contain',
|
|
||||||
'/add/0xb290b2f9f8f108d03ff2af3ac5c8de6de31cdf6d/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('redirects /add/WETH-token to /add/WETH-address/token', () => {
|
|
||||||
cy.visit('/add/0xc778417E063141139Fce010982780140Aa0cD5Ab-0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
|
|
||||||
cy.url().should(
|
|
||||||
'contain',
|
|
||||||
'/add/0xc778417E063141139Fce010982780140Aa0cD5Ab/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('redirects /add/token-WETH to /add/token/WETH-address', () => {
|
|
||||||
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85-0xc778417E063141139Fce010982780140Aa0cD5Ab')
|
|
||||||
cy.url().should(
|
|
||||||
'contain',
|
|
||||||
'/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85/0xc778417E063141139Fce010982780140Aa0cD5Ab'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
15
package.json
15
package.json
@ -37,19 +37,20 @@
|
|||||||
"@types/styled-components": "^5.1.0",
|
"@types/styled-components": "^5.1.0",
|
||||||
"@types/testing-library__cypress": "^5.0.5",
|
"@types/testing-library__cypress": "^5.0.5",
|
||||||
"@types/wcag-contrast": "^3.0.0",
|
"@types/wcag-contrast": "^3.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.20.0",
|
"@typescript-eslint/eslint-plugin": "^4.1.0",
|
||||||
"@typescript-eslint/parser": "^4.20.0",
|
"@typescript-eslint/parser": "^4.1.0",
|
||||||
"@uniswap/default-token-list": "^2.0.0",
|
"@uniswap/default-token-list": "^2.0.0",
|
||||||
"@uniswap/governance": "^1.0.2",
|
"@uniswap/governance": "^1.0.2",
|
||||||
"@uniswap/liquidity-staker": "^1.0.2",
|
"@uniswap/liquidity-staker": "^1.0.2",
|
||||||
"@uniswap/merkle-distributor": "1.0.1",
|
"@uniswap/merkle-distributor": "1.0.1",
|
||||||
"@uniswap/sdk-core": "^1.0.8",
|
"@uniswap/sdk-core": "^1.0.9",
|
||||||
"@uniswap/token-lists": "^1.0.0-beta.19",
|
"@uniswap/token-lists": "^1.0.0-beta.19",
|
||||||
"@uniswap/v2-core": "1.0.0",
|
"@uniswap/v2-core": "1.0.0",
|
||||||
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
||||||
"@uniswap/v2-sdk": "^1.0.6",
|
"@uniswap/v2-sdk": "^1.0.6",
|
||||||
"@uniswap/v3-core": "^1.0.0-rc.0",
|
"@uniswap/v3-core": "^1.0.0-rc.0",
|
||||||
"@uniswap/v3-periphery": "^1.0.0-beta.12",
|
"@uniswap/v3-periphery": "^1.0.0-beta.17",
|
||||||
|
"@uniswap/v3-sdk": "^1.0.0-alpha.11",
|
||||||
"@web3-react/core": "^6.0.9",
|
"@web3-react/core": "^6.0.9",
|
||||||
"@web3-react/fortmatic-connector": "^6.0.9",
|
"@web3-react/fortmatic-connector": "^6.0.9",
|
||||||
"@web3-react/injected-connector": "^6.0.7",
|
"@web3-react/injected-connector": "^6.0.7",
|
||||||
@ -75,8 +76,8 @@
|
|||||||
"lightweight-charts": "^3.3.0",
|
"lightweight-charts": "^3.3.0",
|
||||||
"lodash.flatmap": "^4.5.0",
|
"lodash.flatmap": "^4.5.0",
|
||||||
"luxon": "^1.25.0",
|
"luxon": "^1.25.0",
|
||||||
"multicodec": "^2.0.0",
|
"multicodec": "^3.0.1",
|
||||||
"multihashes": "^3.0.1",
|
"multihashes": "^4.0.2",
|
||||||
"node-vibrant": "^3.1.5",
|
"node-vibrant": "^3.1.5",
|
||||||
"polished": "^3.3.2",
|
"polished": "^3.3.2",
|
||||||
"prettier": "^2.2.1",
|
"prettier": "^2.2.1",
|
||||||
@ -92,7 +93,7 @@
|
|||||||
"react-popper": "^2.2.3",
|
"react-popper": "^2.2.3",
|
||||||
"react-redux": "^7.2.2",
|
"react-redux": "^7.2.2",
|
||||||
"react-router-dom": "^5.0.0",
|
"react-router-dom": "^5.0.0",
|
||||||
"react-scripts": "^4.0.2",
|
"react-scripts": "^4.0.3",
|
||||||
"react-spring": "^8.0.27",
|
"react-spring": "^8.0.27",
|
||||||
"react-use-gesture": "^6.0.14",
|
"react-use-gesture": "^6.0.14",
|
||||||
"react-virtualized-auto-sizer": "^1.0.2",
|
"react-virtualized-auto-sizer": "^1.0.2",
|
||||||
|
@ -91,14 +91,14 @@
|
|||||||
"selectFee": "Select Fee",
|
"selectFee": "Select Fee",
|
||||||
"selectLiquidityRange": "Select Liquidity Range",
|
"selectLiquidityRange": "Select Liquidity Range",
|
||||||
"selectPool": "Select Fee Tier",
|
"selectPool": "Select Fee Tier",
|
||||||
"inputTokens": "Input Tokens",
|
"depositAmounts": "Deposit Amounts",
|
||||||
"fee": "fee",
|
"fee": "fee",
|
||||||
"setLimits": "Set Limits",
|
"setLimits": "Set Limits",
|
||||||
"percent": "Percent",
|
"percent": "Percent",
|
||||||
"rate": "Rate",
|
"rate": "Rate",
|
||||||
"currentRate": "Current {{label}} Rate:",
|
"currentRate": "Current {{label}} Price:",
|
||||||
"inactiveRangeWarning": "Your position will not be active or earn fees until the selected rates come into range.",
|
"inactiveRangeWarning": "Your position will not be active or earn fees until the selected rates come into range.",
|
||||||
"invalidRangeWarning": "Invalid Range",
|
"invalidRangeWarning": "Invalid Range entered. Lower range must be lower than upper range.",
|
||||||
"connectWallet": "Connect Wallet",
|
"connectWallet": "Connect Wallet",
|
||||||
"unsupportedAsset": "Unsupported Asset",
|
"unsupportedAsset": "Unsupported Asset",
|
||||||
"feePool": "Fee Pool",
|
"feePool": "Fee Pool",
|
||||||
@ -109,6 +109,8 @@
|
|||||||
"poolType": "Select a fee tier based on your preferred liquidity provider fee.",
|
"poolType": "Select a fee tier based on your preferred liquidity provider fee.",
|
||||||
"rangeWarning": "Your liquidity will only be active and earning fees when the rate of the pool is within this price range.",
|
"rangeWarning": "Your liquidity will only be active and earning fees when the rate of the pool is within this price range.",
|
||||||
"chooseLiquidityAmount": "Choose an amount of tokens to open this liquidity position. If you don’t have enough tokens you can trade for them with a Swap.",
|
"chooseLiquidityAmount": "Choose an amount of tokens to open this liquidity position. If you don’t have enough tokens you can trade for them with a Swap.",
|
||||||
"selectPriceLimits": "Select Price Limits",
|
"selectRange": "Select Liquidity Range",
|
||||||
"inputTokenDynamic": "Input {{label}}"
|
"inputTokenDynamic": "Input {{label}}",
|
||||||
|
"selectStartingPrice": "Select Starting Price",
|
||||||
|
"newPoolPrice": "Select the market rate for the tokens being added."
|
||||||
}
|
}
|
||||||
|
165
src/abis/multicall2.json
Normal file
165
src/abis/multicall2.json
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{ "internalType": "address", "name": "target", "type": "address" },
|
||||||
|
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
|
||||||
|
],
|
||||||
|
"internalType": "struct Multicall2.Call[]",
|
||||||
|
"name": "calls",
|
||||||
|
"type": "tuple[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "aggregate",
|
||||||
|
"outputs": [
|
||||||
|
{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" },
|
||||||
|
{ "internalType": "bytes[]", "name": "returnData", "type": "bytes[]" }
|
||||||
|
],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{ "internalType": "address", "name": "target", "type": "address" },
|
||||||
|
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
|
||||||
|
],
|
||||||
|
"internalType": "struct Multicall2.Call[]",
|
||||||
|
"name": "calls",
|
||||||
|
"type": "tuple[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "blockAndAggregate",
|
||||||
|
"outputs": [
|
||||||
|
{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" },
|
||||||
|
{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" },
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{ "internalType": "bool", "name": "success", "type": "bool" },
|
||||||
|
{ "internalType": "bytes", "name": "returnData", "type": "bytes" }
|
||||||
|
],
|
||||||
|
"internalType": "struct Multicall2.Result[]",
|
||||||
|
"name": "returnData",
|
||||||
|
"type": "tuple[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" }],
|
||||||
|
"name": "getBlockHash",
|
||||||
|
"outputs": [{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "getBlockNumber",
|
||||||
|
"outputs": [{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "getCurrentBlockCoinbase",
|
||||||
|
"outputs": [{ "internalType": "address", "name": "coinbase", "type": "address" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "getCurrentBlockDifficulty",
|
||||||
|
"outputs": [{ "internalType": "uint256", "name": "difficulty", "type": "uint256" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "getCurrentBlockGasLimit",
|
||||||
|
"outputs": [{ "internalType": "uint256", "name": "gaslimit", "type": "uint256" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "getCurrentBlockTimestamp",
|
||||||
|
"outputs": [{ "internalType": "uint256", "name": "timestamp", "type": "uint256" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [{ "internalType": "address", "name": "addr", "type": "address" }],
|
||||||
|
"name": "getEthBalance",
|
||||||
|
"outputs": [{ "internalType": "uint256", "name": "balance", "type": "uint256" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "getLastBlockHash",
|
||||||
|
"outputs": [{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{ "internalType": "bool", "name": "requireSuccess", "type": "bool" },
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{ "internalType": "address", "name": "target", "type": "address" },
|
||||||
|
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
|
||||||
|
],
|
||||||
|
"internalType": "struct Multicall2.Call[]",
|
||||||
|
"name": "calls",
|
||||||
|
"type": "tuple[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "tryAggregate",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{ "internalType": "bool", "name": "success", "type": "bool" },
|
||||||
|
{ "internalType": "bytes", "name": "returnData", "type": "bytes" }
|
||||||
|
],
|
||||||
|
"internalType": "struct Multicall2.Result[]",
|
||||||
|
"name": "returnData",
|
||||||
|
"type": "tuple[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{ "internalType": "bool", "name": "requireSuccess", "type": "bool" },
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{ "internalType": "address", "name": "target", "type": "address" },
|
||||||
|
{ "internalType": "bytes", "name": "callData", "type": "bytes" }
|
||||||
|
],
|
||||||
|
"internalType": "struct Multicall2.Call[]",
|
||||||
|
"name": "calls",
|
||||||
|
"type": "tuple[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "tryBlockAndAggregate",
|
||||||
|
"outputs": [
|
||||||
|
{ "internalType": "uint256", "name": "blockNumber", "type": "uint256" },
|
||||||
|
{ "internalType": "bytes32", "name": "blockHash", "type": "bytes32" },
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{ "internalType": "bool", "name": "success", "type": "bool" },
|
||||||
|
{ "internalType": "bytes", "name": "returnData", "type": "bytes" }
|
||||||
|
],
|
||||||
|
"internalType": "struct Multicall2.Result[]",
|
||||||
|
"name": "returnData",
|
||||||
|
"type": "tuple[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
}
|
||||||
|
]
|
@ -231,7 +231,8 @@ export const ButtonText = styled(Base)`
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
text-decoration: underline;
|
// text-decoration: underline;
|
||||||
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
&:active {
|
&:active {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
@ -377,6 +378,9 @@ const Circle = styled.div`
|
|||||||
const CheckboxWrapper = styled.div`
|
const CheckboxWrapper = styled.div`
|
||||||
width: 30px;
|
width: 30px;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
`
|
`
|
||||||
|
|
||||||
export function ButtonRadioChecked({ active = false, children, ...rest }: { active?: boolean } & ButtonProps) {
|
export function ButtonRadioChecked({ active = false, children, ...rest }: { active?: boolean } & ButtonProps) {
|
||||||
|
@ -48,5 +48,4 @@ export const BlueCard = styled(Card)`
|
|||||||
background-color: ${({ theme }) => theme.primary5};
|
background-color: ${({ theme }) => theme.primary5};
|
||||||
color: ${({ theme }) => theme.blue2};
|
color: ${({ theme }) => theme.blue2};
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
width: fit-content;
|
|
||||||
`
|
`
|
||||||
|
@ -15,6 +15,8 @@ import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
|
|||||||
import { useActiveWeb3React } from '../../hooks'
|
import { useActiveWeb3React } from '../../hooks'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import useTheme from '../../hooks/useTheme'
|
import useTheme from '../../hooks/useTheme'
|
||||||
|
import { Lock } from 'react-feather'
|
||||||
|
import { AutoColumn } from 'components/Column'
|
||||||
|
|
||||||
const InputPanel = styled.div<{ hideInput?: boolean }>`
|
const InputPanel = styled.div<{ hideInput?: boolean }>`
|
||||||
${({ theme }) => theme.flexColumnNoWrap}
|
${({ theme }) => theme.flexColumnNoWrap}
|
||||||
@ -25,6 +27,19 @@ const InputPanel = styled.div<{ hideInput?: boolean }>`
|
|||||||
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')};
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const FixedContainer = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 20px;
|
||||||
|
background-color: ${({ theme }) => theme.bg1};
|
||||||
|
opacity: 0.95;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 2;
|
||||||
|
`
|
||||||
|
|
||||||
const Container = styled.div<{ hideInput: boolean }>`
|
const Container = styled.div<{ hideInput: boolean }>`
|
||||||
border-radius: ${({ hideInput }) => (hideInput ? '12px' : '20px')};
|
border-radius: ${({ hideInput }) => (hideInput ? '12px' : '20px')};
|
||||||
border: 1px solid ${({ theme }) => theme.bg2};
|
border: 1px solid ${({ theme }) => theme.bg2};
|
||||||
@ -134,6 +149,7 @@ interface CurrencyInputPanelProps {
|
|||||||
id: string
|
id: string
|
||||||
showCommonBases?: boolean
|
showCommonBases?: boolean
|
||||||
customBalanceText?: string
|
customBalanceText?: string
|
||||||
|
locked?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CurrencyInputPanel({
|
export default function CurrencyInputPanel({
|
||||||
@ -144,14 +160,15 @@ export default function CurrencyInputPanel({
|
|||||||
label = 'Input',
|
label = 'Input',
|
||||||
onCurrencySelect,
|
onCurrencySelect,
|
||||||
currency,
|
currency,
|
||||||
disableCurrencySelect = false,
|
|
||||||
hideBalance = false,
|
|
||||||
pair = null, // used for double token logo
|
|
||||||
hideInput = false,
|
|
||||||
otherCurrency,
|
otherCurrency,
|
||||||
id,
|
id,
|
||||||
showCommonBases,
|
showCommonBases,
|
||||||
customBalanceText,
|
customBalanceText,
|
||||||
|
disableCurrencySelect = false,
|
||||||
|
hideBalance = false,
|
||||||
|
pair = null, // used for double token logo
|
||||||
|
hideInput = false,
|
||||||
|
locked = false,
|
||||||
...rest
|
...rest
|
||||||
}: CurrencyInputPanelProps) {
|
}: CurrencyInputPanelProps) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@ -167,6 +184,14 @@ export default function CurrencyInputPanel({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<InputPanel id={id} hideInput={hideInput} {...rest}>
|
<InputPanel id={id} hideInput={hideInput} {...rest}>
|
||||||
|
{locked && (
|
||||||
|
<FixedContainer>
|
||||||
|
<AutoColumn gap="sm" justify="center">
|
||||||
|
<Lock />
|
||||||
|
<TYPE.label fontSize="12px">Single-asset deposit only, price out of range.</TYPE.label>
|
||||||
|
</AutoColumn>
|
||||||
|
</FixedContainer>
|
||||||
|
)}
|
||||||
<Container hideInput={hideInput}>
|
<Container hideInput={hideInput}>
|
||||||
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={disableCurrencySelect}>
|
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={disableCurrencySelect}>
|
||||||
{!hideInput && (
|
{!hideInput && (
|
||||||
|
@ -12,7 +12,7 @@ import { useTotalUniEarned } from '../../state/stake/hooks'
|
|||||||
import { useAggregateUniBalance, useTokenBalance } from '../../state/wallet/hooks'
|
import { useAggregateUniBalance, useTokenBalance } from '../../state/wallet/hooks'
|
||||||
import { ExternalLink, StyledInternalLink, TYPE, UniTokenAnimated } from '../../theme'
|
import { ExternalLink, StyledInternalLink, TYPE, UniTokenAnimated } from '../../theme'
|
||||||
import { computeUniCirculation } from '../../utils/computeUniCirculation'
|
import { computeUniCirculation } from '../../utils/computeUniCirculation'
|
||||||
import useUSDCPrice from '../../utils/useUSDCPrice'
|
import useUSDCPrice from '../../hooks/useUSDCPrice'
|
||||||
import { AutoColumn } from '../Column'
|
import { AutoColumn } from '../Column'
|
||||||
import { RowBetween } from '../Row'
|
import { RowBetween } from '../Row'
|
||||||
import { Break, CardBGImage, CardNoise, CardSection, DataCard } from '../earn/styled'
|
import { Break, CardBGImage, CardNoise, CardSection, DataCard } from '../earn/styled'
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import React, { useState, useCallback, useEffect } from 'react'
|
import React, { useState, useCallback, useEffect } from 'react'
|
||||||
import { OutlineCard } from 'components/Card'
|
import { OutlineCard } from 'components/Card'
|
||||||
import { RowBetween } from 'components/Row'
|
import { RowBetween } from 'components/Row'
|
||||||
import { ButtonGray } from 'components/Button'
|
|
||||||
import { TYPE } from 'theme'
|
|
||||||
import { Input as NumericalInput } from '../NumericalInput'
|
import { Input as NumericalInput } from '../NumericalInput'
|
||||||
import styled, { keyframes, css } from 'styled-components'
|
import styled, { keyframes, css } from 'styled-components'
|
||||||
|
import { TYPE } from 'theme'
|
||||||
|
import { AutoColumn } from 'components/Column'
|
||||||
|
|
||||||
const pulse = (color: string) => keyframes`
|
const pulse = (color: string) => keyframes`
|
||||||
0% {
|
0% {
|
||||||
@ -22,7 +22,7 @@ const pulse = (color: string) => keyframes`
|
|||||||
|
|
||||||
const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boolean }>`
|
const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boolean }>`
|
||||||
border-color: ${({ active, theme }) => active && theme.blue1};
|
border-color: ${({ active, theme }) => active && theme.blue1};
|
||||||
padding: 8px 12px;
|
padding: 12px;
|
||||||
|
|
||||||
${({ pulsing, theme }) =>
|
${({ pulsing, theme }) =>
|
||||||
pulsing &&
|
pulsing &&
|
||||||
@ -33,25 +33,22 @@ const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boo
|
|||||||
|
|
||||||
const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
|
const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
|
||||||
background-color: ${({ theme }) => theme.bg0};
|
background-color: ${({ theme }) => theme.bg0};
|
||||||
text-align: ${({ usePercent }) => (usePercent ? 'right' : 'center')};
|
text-align: left;
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
`
|
`
|
||||||
|
|
||||||
const ContentWrapper = styled(RowBetween)`
|
const ContentWrapper = styled(RowBetween)`
|
||||||
padding: 0 8px;
|
width: 92%;
|
||||||
width: 70%;
|
|
||||||
`
|
`
|
||||||
|
|
||||||
interface StepCounterProps {
|
interface StepCounterProps {
|
||||||
value: string
|
value: string
|
||||||
onUserInput: (value: string) => void
|
onUserInput: (value: string) => void
|
||||||
onIncrement?: () => void
|
label?: string
|
||||||
onDecrement?: () => void
|
width?: string
|
||||||
usePercent?: boolean
|
|
||||||
prependSymbol?: string | undefined
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const StepCounter = ({ value, onUserInput, usePercent = false, prependSymbol }: StepCounterProps) => {
|
const StepCounter = ({ value, onUserInput, label, width }: StepCounterProps) => {
|
||||||
// for focus state, styled components doesnt let you select input parent container
|
// for focus state, styled components doesnt let you select input parent container
|
||||||
const [active, setActive] = useState(false)
|
const [active, setActive] = useState(false)
|
||||||
|
|
||||||
@ -85,20 +82,9 @@ const StepCounter = ({ value, onUserInput, usePercent = false, prependSymbol }:
|
|||||||
}
|
}
|
||||||
}, [localValue, useLocalValue, value])
|
}, [localValue, useLocalValue, value])
|
||||||
|
|
||||||
const handleDeccrement = useCallback(() => {
|
|
||||||
localValue && setLocalValue((parseFloat(localValue) * 0.997).toString())
|
|
||||||
}, [localValue])
|
|
||||||
|
|
||||||
const handleIncrement = useCallback(() => {
|
|
||||||
localValue && setLocalValue((parseFloat(localValue) * 1.003).toString())
|
|
||||||
}, [localValue])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FocusedOutlineCard pulsing={pulsing} active={active} onFocus={handleOnFocus} onBlur={handleOnBlur}>
|
<FocusedOutlineCard pulsing={pulsing} active={active} onFocus={handleOnFocus} onBlur={handleOnBlur} width={width}>
|
||||||
<RowBetween>
|
<AutoColumn gap="md">
|
||||||
<ButtonGray padding="2px 0px" borderRadius="8px" onClick={handleDeccrement} width="50px">
|
|
||||||
<TYPE.label>-</TYPE.label>
|
|
||||||
</ButtonGray>
|
|
||||||
<ContentWrapper>
|
<ContentWrapper>
|
||||||
<StyledInput
|
<StyledInput
|
||||||
className="rate-input-0"
|
className="rate-input-0"
|
||||||
@ -107,15 +93,10 @@ const StepCounter = ({ value, onUserInput, usePercent = false, prependSymbol }:
|
|||||||
onUserInput={(val) => {
|
onUserInput={(val) => {
|
||||||
setLocalValue(val)
|
setLocalValue(val)
|
||||||
}}
|
}}
|
||||||
prependSymbol={prependSymbol}
|
|
||||||
usePercent={usePercent}
|
|
||||||
/>
|
/>
|
||||||
{usePercent && <TYPE.main>%</TYPE.main>}
|
|
||||||
</ContentWrapper>
|
</ContentWrapper>
|
||||||
<ButtonGray padding="2px 0px" borderRadius="8px" onClick={handleIncrement} width="50px">
|
{label && <TYPE.label fontSize="12px">{label}</TYPE.label>}
|
||||||
<TYPE.label>+</TYPE.label>
|
</AutoColumn>
|
||||||
</ButtonGray>
|
|
||||||
</RowBetween>
|
|
||||||
</FocusedOutlineCard>
|
</FocusedOutlineCard>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Text } from 'rebass'
|
import { Text } from 'rebass'
|
||||||
import { ChainId, Currency, currencyEquals, ETHER, Token } from '@uniswap/sdk-core'
|
import { ChainId, Currency, currencyEquals, Token, ETHER } from '@uniswap/sdk-core'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import { SUGGESTED_BASES } from '../../constants'
|
import { SUGGESTED_BASES } from '../../constants'
|
||||||
|
@ -79,7 +79,9 @@ export function CurrencySearch({
|
|||||||
|
|
||||||
// if they input an address, use it
|
// if they input an address, use it
|
||||||
const isAddressSearch = isAddress(debouncedQuery)
|
const isAddressSearch = isAddress(debouncedQuery)
|
||||||
|
|
||||||
const searchToken = useToken(debouncedQuery)
|
const searchToken = useToken(debouncedQuery)
|
||||||
|
|
||||||
const searchTokenIsAdded = useIsUserAddedToken(searchToken)
|
const searchTokenIsAdded = useIsUserAddedToken(searchToken)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -18,8 +18,8 @@ import useAddTokenToMetamask from 'hooks/useAddTokenToMetamask'
|
|||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
`
|
`
|
||||||
const Section = styled(AutoColumn)`
|
const Section = styled(AutoColumn)<{ inline?: boolean }>`
|
||||||
padding: 24px;
|
padding: ${({ inline }) => (inline ? '0' : '24px')};
|
||||||
`
|
`
|
||||||
|
|
||||||
const BottomSection = styled(Section)`
|
const BottomSection = styled(Section)`
|
||||||
@ -28,8 +28,8 @@ const BottomSection = styled(Section)`
|
|||||||
border-bottom-right-radius: 20px;
|
border-bottom-right-radius: 20px;
|
||||||
`
|
`
|
||||||
|
|
||||||
const ConfirmedIcon = styled(ColumnCenter)`
|
const ConfirmedIcon = styled(ColumnCenter)<{ inline?: boolean }>`
|
||||||
padding: 60px 0;
|
padding: ${({ inline }) => (inline ? '20px 0' : '60px 0;')};
|
||||||
`
|
`
|
||||||
|
|
||||||
const StyledLogo = styled.img`
|
const StyledLogo = styled.img`
|
||||||
@ -38,19 +38,29 @@ const StyledLogo = styled.img`
|
|||||||
margin-left: 6px;
|
margin-left: 6px;
|
||||||
`
|
`
|
||||||
|
|
||||||
function ConfirmationPendingContent({ onDismiss, pendingText }: { onDismiss: () => void; pendingText: string }) {
|
export function ConfirmationPendingContent({
|
||||||
|
onDismiss,
|
||||||
|
pendingText,
|
||||||
|
inline,
|
||||||
|
}: {
|
||||||
|
onDismiss: () => void
|
||||||
|
pendingText: string
|
||||||
|
inline?: boolean // not in modal
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Section>
|
<AutoColumn gap="md">
|
||||||
|
{!inline && (
|
||||||
<RowBetween>
|
<RowBetween>
|
||||||
<div />
|
<div />
|
||||||
<CloseIcon onClick={onDismiss} />
|
<CloseIcon onClick={onDismiss} />
|
||||||
</RowBetween>
|
</RowBetween>
|
||||||
<ConfirmedIcon>
|
)}
|
||||||
<CustomLightSpinner src={Circle} alt="loader" size={'90px'} />
|
<ConfirmedIcon inline={inline}>
|
||||||
|
<CustomLightSpinner src={Circle} alt="loader" size={inline ? '40px' : '90px'} />
|
||||||
</ConfirmedIcon>
|
</ConfirmedIcon>
|
||||||
<AutoColumn gap="12px" justify={'center'}>
|
<AutoColumn gap="12px" justify={'center'}>
|
||||||
<Text fontWeight={500} fontSize={20}>
|
<Text fontWeight={500} fontSize={20} textAlign="center">
|
||||||
Waiting For Confirmation
|
Waiting For Confirmation
|
||||||
</Text>
|
</Text>
|
||||||
<AutoColumn gap="12px" justify={'center'}>
|
<AutoColumn gap="12px" justify={'center'}>
|
||||||
@ -62,21 +72,23 @@ function ConfirmationPendingContent({ onDismiss, pendingText }: { onDismiss: ()
|
|||||||
Confirm this transaction in your wallet
|
Confirm this transaction in your wallet
|
||||||
</Text>
|
</Text>
|
||||||
</AutoColumn>
|
</AutoColumn>
|
||||||
</Section>
|
</AutoColumn>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function TransactionSubmittedContent({
|
export function TransactionSubmittedContent({
|
||||||
onDismiss,
|
onDismiss,
|
||||||
chainId,
|
chainId,
|
||||||
hash,
|
hash,
|
||||||
currencyToAdd,
|
currencyToAdd,
|
||||||
|
inline,
|
||||||
}: {
|
}: {
|
||||||
onDismiss: () => void
|
onDismiss: () => void
|
||||||
hash: string | undefined
|
hash: string | undefined
|
||||||
chainId: ChainId
|
chainId: ChainId
|
||||||
currencyToAdd?: Currency | undefined
|
currencyToAdd?: Currency | undefined
|
||||||
|
inline?: boolean // not in modal
|
||||||
}) {
|
}) {
|
||||||
const theme = useContext(ThemeContext)
|
const theme = useContext(ThemeContext)
|
||||||
|
|
||||||
@ -86,16 +98,18 @@ function TransactionSubmittedContent({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Section>
|
<Section inline={inline}>
|
||||||
|
{!inline && (
|
||||||
<RowBetween>
|
<RowBetween>
|
||||||
<div />
|
<div />
|
||||||
<CloseIcon onClick={onDismiss} />
|
<CloseIcon onClick={onDismiss} />
|
||||||
</RowBetween>
|
</RowBetween>
|
||||||
<ConfirmedIcon>
|
)}
|
||||||
<ArrowUpCircle strokeWidth={0.5} size={90} color={theme.primary1} />
|
<ConfirmedIcon inline={inline}>
|
||||||
|
<ArrowUpCircle strokeWidth={0.5} size={inline ? '40px' : '90px'} color={theme.primary1} />
|
||||||
</ConfirmedIcon>
|
</ConfirmedIcon>
|
||||||
<AutoColumn gap="12px" justify={'center'}>
|
<AutoColumn gap="12px" justify={'center'}>
|
||||||
<Text fontWeight={500} fontSize={20}>
|
<Text fontWeight={500} fontSize={20} textAlign="center">
|
||||||
Transaction Submitted
|
Transaction Submitted
|
||||||
</Text>
|
</Text>
|
||||||
{chainId && hash && (
|
{chainId && hash && (
|
||||||
@ -121,7 +135,7 @@ function TransactionSubmittedContent({
|
|||||||
)}
|
)}
|
||||||
<ButtonPrimary onClick={onDismiss} style={{ margin: '20px 0 0 0' }}>
|
<ButtonPrimary onClick={onDismiss} style={{ margin: '20px 0 0 0' }}>
|
||||||
<Text fontWeight={500} fontSize={20}>
|
<Text fontWeight={500} fontSize={20}>
|
||||||
Close
|
{inline ? 'Return' : 'Close'}
|
||||||
</Text>
|
</Text>
|
||||||
</ButtonPrimary>
|
</ButtonPrimary>
|
||||||
</AutoColumn>
|
</AutoColumn>
|
||||||
@ -145,7 +159,7 @@ export function ConfirmationModalContent({
|
|||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Section>
|
<Section>
|
||||||
<RowBetween>
|
<RowBetween>
|
||||||
<Text fontWeight={500} fontSize={20}>
|
<Text fontWeight={500} fontSize={16}>
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
<CloseIcon onClick={onDismiss} />
|
<CloseIcon onClick={onDismiss} />
|
||||||
|
@ -13,8 +13,8 @@ import { currencyId } from '../../utils/currencyId'
|
|||||||
import { Break, CardNoise, CardBGImage } from './styled'
|
import { Break, CardNoise, CardBGImage } from './styled'
|
||||||
import { unwrappedToken } from '../../utils/wrappedCurrency'
|
import { unwrappedToken } from '../../utils/wrappedCurrency'
|
||||||
import { useTotalSupply } from '../../data/TotalSupply'
|
import { useTotalSupply } from '../../data/TotalSupply'
|
||||||
import { usePair } from '../../data/Reserves'
|
import { usePair } from '../../data/V2'
|
||||||
import useUSDCPrice from '../../utils/useUSDCPrice'
|
import useUSDCPrice from '../../hooks/useUSDCPrice'
|
||||||
import { BIG_INT_SECONDS_IN_WEEK } from '../../constants'
|
import { BIG_INT_SECONDS_IN_WEEK } from '../../constants'
|
||||||
|
|
||||||
const StatContainer = styled.div`
|
const StatContainer = styled.div`
|
||||||
|
@ -4,6 +4,14 @@ import JSBI from 'jsbi'
|
|||||||
|
|
||||||
import { fortmatic, injected, portis, walletconnect, walletlink } from '../connectors'
|
import { fortmatic, injected, portis, walletconnect, walletlink } from '../connectors'
|
||||||
|
|
||||||
|
export const MULTICALL_ADDRESSES: { [chainId in ChainId]: string } = {
|
||||||
|
[ChainId.MAINNET]: '',
|
||||||
|
[ChainId.ROPSTEN]: '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696',
|
||||||
|
[ChainId.KOVAN]: '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696',
|
||||||
|
[ChainId.RINKEBY]: '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696',
|
||||||
|
[ChainId.GÖRLI]: '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696',
|
||||||
|
}
|
||||||
|
|
||||||
export const ROUTER_ADDRESS = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D'
|
export const ROUTER_ADDRESS = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D'
|
||||||
|
|
||||||
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
|
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
|
||||||
@ -12,7 +20,7 @@ export { PRELOADED_PROPOSALS } from './proposals'
|
|||||||
|
|
||||||
// a list of tokens by chain
|
// a list of tokens by chain
|
||||||
type ChainTokenList = {
|
type ChainTokenList = {
|
||||||
readonly [chainId in ChainId]: Token[]
|
readonly [chainId in ChainId | 1337]: Token[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DAI = new Token(ChainId.MAINNET, '0x6B175474E89094C44Da98b954EedeAC495271d0F', 18, 'DAI', 'Dai Stablecoin')
|
export const DAI = new Token(ChainId.MAINNET, '0x6B175474E89094C44Da98b954EedeAC495271d0F', 18, 'DAI', 'Dai Stablecoin')
|
||||||
@ -57,6 +65,7 @@ const WETH_ONLY: ChainTokenList = {
|
|||||||
[ChainId.RINKEBY]: [WETH9[ChainId.RINKEBY]],
|
[ChainId.RINKEBY]: [WETH9[ChainId.RINKEBY]],
|
||||||
[ChainId.GÖRLI]: [WETH9[ChainId.GÖRLI]],
|
[ChainId.GÖRLI]: [WETH9[ChainId.GÖRLI]],
|
||||||
[ChainId.KOVAN]: [WETH9[ChainId.KOVAN]],
|
[ChainId.KOVAN]: [WETH9[ChainId.KOVAN]],
|
||||||
|
[1337]: [WETH9[ChainId.KOVAN]],
|
||||||
}
|
}
|
||||||
|
|
||||||
// used to construct intermediary pairs for trading
|
// used to construct intermediary pairs for trading
|
||||||
|
@ -102,6 +102,23 @@
|
|||||||
"internalType": "bytes32",
|
"internalType": "bytes32",
|
||||||
"name": "blockHash",
|
"name": "blockHash",
|
||||||
"type": "bytes32"
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"internalType": "bool",
|
||||||
|
"name": "success",
|
||||||
|
"type": "bool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "bytes",
|
||||||
|
"name": "returnData",
|
||||||
|
"type": "bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"internalType": "struct Multicall2.Result[]",
|
||||||
|
"name": "returnData",
|
||||||
|
"type": "tuple[]"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"stateMutability": "view",
|
"stateMutability": "view",
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
import { ChainId } from '@uniswap/sdk-core'
|
|
||||||
import MULTICALL_ABI from './abi.json'
|
|
||||||
|
|
||||||
const MULTICALL_NETWORKS: { [chainId in ChainId]: string } = {
|
|
||||||
[ChainId.MAINNET]: '0xeefBa1e63905eF1D7ACbA5a8513c70307C1cE441',
|
|
||||||
[ChainId.ROPSTEN]: '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696',
|
|
||||||
[ChainId.KOVAN]: '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696',
|
|
||||||
[ChainId.RINKEBY]: '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696',
|
|
||||||
[ChainId.GÖRLI]: '0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696',
|
|
||||||
}
|
|
||||||
|
|
||||||
export { MULTICALL_ABI, MULTICALL_NETWORKS }
|
|
@ -17,6 +17,14 @@
|
|||||||
"decimals": 6,
|
"decimals": 6,
|
||||||
"chainId": 1,
|
"chainId": 1,
|
||||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4922a015c4407F87432B179bb209e125432E4a2A/logo.png"
|
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4922a015c4407F87432B179bb209e125432E4a2A/logo.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Grump Cat",
|
||||||
|
"address": "0x93B2FfF814FCaEFFB01406e80B4Ecd89Ca6A021b",
|
||||||
|
"symbol": "GRUMPY",
|
||||||
|
"decimals": 9,
|
||||||
|
"chainId": 1,
|
||||||
|
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x4922a015c4407F87432B179bb209e125432E4a2A/logo.png"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -2,40 +2,40 @@ import { ChainId } from '@uniswap/sdk-core'
|
|||||||
|
|
||||||
export const FACTORY_ADDRESSES: { [chainId in ChainId]: string } = {
|
export const FACTORY_ADDRESSES: { [chainId in ChainId]: string } = {
|
||||||
[ChainId.MAINNET]: '',
|
[ChainId.MAINNET]: '',
|
||||||
[ChainId.ROPSTEN]: '',
|
[ChainId.ROPSTEN]: '0xb31b9A7b331eA8993bdfC67c650eDbfc9256eC62',
|
||||||
[ChainId.RINKEBY]: '',
|
[ChainId.RINKEBY]: '0xD8A6adFB40Ba3B3CdA9F985BF1fdbDc0c1d7591e',
|
||||||
[ChainId.GÖRLI]: '0xb31b9A7b331eA8993bdfC67c650eDbfc9256eC62',
|
[ChainId.GÖRLI]: '0xb31b9A7b331eA8993bdfC67c650eDbfc9256eC62',
|
||||||
[ChainId.KOVAN]: '',
|
[ChainId.KOVAN]: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TICK_LENS_ADDRESSES: { [chainId in ChainId]: string } = {
|
export const TICK_LENS_ADDRESSES: { [chainId in ChainId]: string } = {
|
||||||
[ChainId.MAINNET]: '',
|
[ChainId.MAINNET]: '',
|
||||||
[ChainId.ROPSTEN]: '',
|
[ChainId.ROPSTEN]: '0x8E984b597F19E8D0FDd0b5bAfDb1d0ae4386455f',
|
||||||
[ChainId.RINKEBY]: '',
|
[ChainId.RINKEBY]: '0xB1c59e8Ae4B72f63a5a9CB9c25A9253096A4b126',
|
||||||
[ChainId.GÖRLI]: '0x8E984b597F19E8D0FDd0b5bAfDb1d0ae4386455f',
|
[ChainId.GÖRLI]: '0x8E984b597F19E8D0FDd0b5bAfDb1d0ae4386455f',
|
||||||
[ChainId.KOVAN]: '',
|
[ChainId.KOVAN]: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: { [chainId in ChainId]: string } = {
|
export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: { [chainId in ChainId]: string } = {
|
||||||
[ChainId.MAINNET]: '',
|
[ChainId.MAINNET]: '',
|
||||||
[ChainId.ROPSTEN]: '',
|
[ChainId.ROPSTEN]: '0x29e4bF3bFD649b807B4C752c01023E535094F6Bc',
|
||||||
[ChainId.RINKEBY]: '',
|
[ChainId.RINKEBY]: '0xee9e30637f84Bbf929042A9118c6E20023dab833',
|
||||||
[ChainId.GÖRLI]: '0x29e4bF3bFD649b807B4C752c01023E535094F6Bc',
|
[ChainId.GÖRLI]: '0x29e4bF3bFD649b807B4C752c01023E535094F6Bc',
|
||||||
[ChainId.KOVAN]: '',
|
[ChainId.KOVAN]: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NONFUNGIBLE_TOKEN_POSITION_DESCRIPTOR_ADDRESSES: { [chainId in ChainId]: string } = {
|
export const NONFUNGIBLE_TOKEN_POSITION_DESCRIPTOR_ADDRESSES: { [chainId in ChainId]: string } = {
|
||||||
[ChainId.MAINNET]: '',
|
[ChainId.MAINNET]: '',
|
||||||
[ChainId.ROPSTEN]: '',
|
[ChainId.ROPSTEN]: '0xa0588c89Fe967e66533aB1A0504C30989f90156f',
|
||||||
[ChainId.RINKEBY]: '',
|
[ChainId.RINKEBY]: '0x3431b9Ed12e3204bC6f7039e1c576417B70fdD67',
|
||||||
[ChainId.GÖRLI]: '0xa0588c89Fe967e66533aB1A0504C30989f90156f',
|
[ChainId.GÖRLI]: '0xa0588c89Fe967e66533aB1A0504C30989f90156f',
|
||||||
[ChainId.KOVAN]: '',
|
[ChainId.KOVAN]: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SWAP_ROUTER_ADDRESSES: { [chainId in ChainId]: string } = {
|
export const SWAP_ROUTER_ADDRESSES: { [chainId in ChainId]: string } = {
|
||||||
[ChainId.MAINNET]: '',
|
[ChainId.MAINNET]: '',
|
||||||
[ChainId.ROPSTEN]: '',
|
[ChainId.ROPSTEN]: '0x71bB3d0e63f2Fa2A5d04d54267211f4Caef7062e',
|
||||||
[ChainId.RINKEBY]: '',
|
[ChainId.RINKEBY]: '0xa0588c89Fe967e66533aB1A0504C30989f90156f',
|
||||||
[ChainId.GÖRLI]: '0x71bB3d0e63f2Fa2A5d04d54267211f4Caef7062e',
|
[ChainId.GÖRLI]: '0x71bB3d0e63f2Fa2A5d04d54267211f4Caef7062e',
|
||||||
[ChainId.KOVAN]: '',
|
[ChainId.KOVAN]: '',
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ export const SWAP_ROUTER_ADDRESSES: { [chainId in ChainId]: string } = {
|
|||||||
export const V2_MIGRATOR_ADDRESSES: { [chainId in ChainId]: string } = {
|
export const V2_MIGRATOR_ADDRESSES: { [chainId in ChainId]: string } = {
|
||||||
[ChainId.MAINNET]: '',
|
[ChainId.MAINNET]: '',
|
||||||
[ChainId.ROPSTEN]: '',
|
[ChainId.ROPSTEN]: '',
|
||||||
[ChainId.RINKEBY]: '',
|
[ChainId.RINKEBY]: '0xb31b9A7b331eA8993bdfC67c650eDbfc9256eC62',
|
||||||
[ChainId.GÖRLI]: '0xee9e30637f84Bbf929042A9118c6E20023dab833',
|
[ChainId.GÖRLI]: '0xee9e30637f84Bbf929042A9118c6E20023dab833',
|
||||||
[ChainId.KOVAN]: '',
|
[ChainId.KOVAN]: '',
|
||||||
}
|
}
|
||||||
|
98
src/data/Pools.ts
Normal file
98
src/data/Pools.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import { Tick } from '@uniswap/v3-sdk'
|
||||||
|
import { ZERO_ADDRESS } from './../constants/index'
|
||||||
|
import { Currency } from '@uniswap/sdk-core'
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import { useActiveWeb3React } from '../hooks'
|
||||||
|
import { useSingleCallResult } from '../state/multicall/hooks'
|
||||||
|
import { wrappedCurrency } from '../utils/wrappedCurrency'
|
||||||
|
import { Pool, FeeAmount, computePoolAddress } from '@uniswap/v3-sdk'
|
||||||
|
import { useV3Factory, useV3Pool } from 'hooks/useContract'
|
||||||
|
import { FACTORY_ADDRESSES } from 'constants/v3'
|
||||||
|
import { useAllV3Ticks } from 'hooks/useAllV3Ticks'
|
||||||
|
|
||||||
|
export enum PoolState {
|
||||||
|
LOADING,
|
||||||
|
NOT_EXISTS,
|
||||||
|
EXISTS,
|
||||||
|
INVALID,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function usePool(currencyA?: Currency, currencyB?: Currency, feeAmount?: FeeAmount): [PoolState, Pool | null] {
|
||||||
|
const { chainId } = useActiveWeb3React()
|
||||||
|
const factoryContract = useV3Factory()
|
||||||
|
|
||||||
|
const tokenA = wrappedCurrency(currencyA, chainId)
|
||||||
|
const tokenB = wrappedCurrency(currencyB, chainId)
|
||||||
|
|
||||||
|
// sorted version
|
||||||
|
const [token0, token1] = useMemo(
|
||||||
|
() =>
|
||||||
|
tokenA && tokenB && !tokenA.equals(tokenB)
|
||||||
|
? tokenA.sortsBefore(tokenB)
|
||||||
|
? [tokenA, tokenB]
|
||||||
|
: [tokenB, tokenA]
|
||||||
|
: [undefined, undefined],
|
||||||
|
[tokenA, tokenB]
|
||||||
|
)
|
||||||
|
|
||||||
|
// fetch all generated addresses for pools
|
||||||
|
const poolAddress = useMemo(() => {
|
||||||
|
try {
|
||||||
|
return chainId && tokenA && tokenB && feeAmount && !tokenA.equals(tokenB)
|
||||||
|
? computePoolAddress({
|
||||||
|
factoryAddress: FACTORY_ADDRESSES[chainId],
|
||||||
|
tokenA,
|
||||||
|
tokenB,
|
||||||
|
fee: feeAmount,
|
||||||
|
})
|
||||||
|
: undefined
|
||||||
|
} catch {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}, [chainId, feeAmount, tokenA, tokenB])
|
||||||
|
|
||||||
|
const poolContract = useV3Pool(poolAddress)
|
||||||
|
|
||||||
|
// check factory if pools exists
|
||||||
|
const addressParams = token0 && token1 && feeAmount ? [token0.address, token1.address, feeAmount] : undefined
|
||||||
|
const addressFromFactory = useSingleCallResult(addressParams ? factoryContract : undefined, 'getPool', addressParams)
|
||||||
|
|
||||||
|
// attempt to fetch pool metadata
|
||||||
|
const slot0Datas = useSingleCallResult(poolContract, 'slot0')
|
||||||
|
|
||||||
|
// fetch additional data to instantiate pools
|
||||||
|
const liquidityDatas = useSingleCallResult(poolContract, 'liquidity')
|
||||||
|
|
||||||
|
const { result: slot0, loading: slot0Loading } = slot0Datas
|
||||||
|
const { result: liquidityResult, loading: liquidityLoading } = liquidityDatas
|
||||||
|
const { result: addressesResult, loading: addressesLoading } = addressFromFactory
|
||||||
|
|
||||||
|
const liquidity = liquidityResult?.[0]
|
||||||
|
const poolAddressFromFactory = addressesResult?.[0]
|
||||||
|
|
||||||
|
// fetch tick data for pool
|
||||||
|
const { tickData, loading: tickLoading } = useAllV3Ticks(token0, token1, feeAmount)
|
||||||
|
|
||||||
|
// still loading data
|
||||||
|
if (slot0Loading || addressesLoading || liquidityLoading || tickLoading) return [PoolState.LOADING, null]
|
||||||
|
|
||||||
|
// invalid pool setup
|
||||||
|
if (!tokenA || !tokenB || !feeAmount || tokenA.equals(tokenB)) return [PoolState.INVALID, null]
|
||||||
|
|
||||||
|
// pool has not been created or not initialized yet
|
||||||
|
if (poolAddressFromFactory === ZERO_ADDRESS || !slot0 || !liquidity || slot0.sqrtPriceX96 === 0) {
|
||||||
|
return [PoolState.NOT_EXISTS, null]
|
||||||
|
}
|
||||||
|
|
||||||
|
const tickList: Tick[] = tickData
|
||||||
|
.map((tick) => {
|
||||||
|
return new Tick({
|
||||||
|
index: tick.tick,
|
||||||
|
liquidityGross: tick.liquidityGross,
|
||||||
|
liquidityNet: tick.liquidityNet,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.sort((tickA, tickB) => (tickA.index > tickB.index ? 1 : -1))
|
||||||
|
|
||||||
|
return [PoolState.EXISTS, new Pool(tokenA, tokenB, feeAmount, slot0.sqrtPriceX96, liquidity, slot0.tick, tickList)]
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import { Pair } from '@uniswap/v2-sdk'
|
import { Pair } from '@uniswap/v2-sdk'
|
||||||
import { TokenAmount, Currency } from '@uniswap/sdk-core'
|
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.json'
|
import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.json'
|
||||||
import { Interface } from '@ethersproject/abi'
|
import { Interface } from '@ethersproject/abi'
|
||||||
@ -7,6 +6,7 @@ import { useActiveWeb3React } from '../hooks'
|
|||||||
|
|
||||||
import { useMultipleContractSingleData } from '../state/multicall/hooks'
|
import { useMultipleContractSingleData } from '../state/multicall/hooks'
|
||||||
import { wrappedCurrency } from '../utils/wrappedCurrency'
|
import { wrappedCurrency } from '../utils/wrappedCurrency'
|
||||||
|
import { Currency, TokenAmount } from '@uniswap/sdk-core'
|
||||||
|
|
||||||
const PAIR_INTERFACE = new Interface(IUniswapV2PairABI)
|
const PAIR_INTERFACE = new Interface(IUniswapV2PairABI)
|
||||||
|
|
@ -5,7 +5,7 @@ import flatMap from 'lodash.flatmap'
|
|||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
import { BASES_TO_CHECK_TRADES_AGAINST, CUSTOM_BASES, BETTER_TRADE_LESS_HOPS_THRESHOLD } from '../constants'
|
import { BASES_TO_CHECK_TRADES_AGAINST, CUSTOM_BASES, BETTER_TRADE_LESS_HOPS_THRESHOLD } from '../constants'
|
||||||
import { PairState, usePairs } from '../data/Reserves'
|
import { PairState, usePairs } from '../data/V2'
|
||||||
import { wrappedCurrency } from '../utils/wrappedCurrency'
|
import { wrappedCurrency } from '../utils/wrappedCurrency'
|
||||||
|
|
||||||
import { useActiveWeb3React } from './index'
|
import { useActiveWeb3React } from './index'
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
import { Token } from '@uniswap/sdk-core'
|
||||||
|
import { FeeAmount, TICK_SPACINGS } from '@uniswap/v3-sdk'
|
||||||
|
import { ZERO_ADDRESS } from '../constants'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { Result, useSingleContractMultipleData } from 'state/multicall/hooks'
|
import { Result, useSingleCallResult, useSingleContractMultipleData } from 'state/multicall/hooks'
|
||||||
import { useTickLens } from './useContract'
|
import { useTickLens, useV3Factory } from './useContract'
|
||||||
|
|
||||||
// the following should probably all be from the sdk, just mocking it for now
|
// the following should probably all be from the sdk, just mocking it for now
|
||||||
function MIN_TICK(tickSpacing: number) {
|
function MIN_TICK(tickSpacing: number) {
|
||||||
@ -25,8 +28,9 @@ interface TickData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useAllV3Ticks(
|
export function useAllV3Ticks(
|
||||||
poolAddress: string,
|
token0: Token | undefined,
|
||||||
tickSpacing: number
|
token1: Token | undefined,
|
||||||
|
feeAmount: FeeAmount | undefined
|
||||||
): {
|
): {
|
||||||
loading: boolean
|
loading: boolean
|
||||||
syncing: boolean
|
syncing: boolean
|
||||||
@ -34,22 +38,35 @@ export function useAllV3Ticks(
|
|||||||
valid: boolean
|
valid: boolean
|
||||||
tickData: TickData[]
|
tickData: TickData[]
|
||||||
} {
|
} {
|
||||||
const tickLens = useTickLens()
|
const tickSpacing = useMemo(() => (feeAmount ? TICK_SPACINGS[feeAmount] : undefined), [feeAmount])
|
||||||
|
|
||||||
const minIndex = useMemo(() => bitmapIndex(MIN_TICK(tickSpacing), tickSpacing), [tickSpacing])
|
const minIndex = useMemo(() => (tickSpacing ? bitmapIndex(MIN_TICK(tickSpacing), tickSpacing) : undefined), [
|
||||||
const maxIndex = useMemo(() => bitmapIndex(MAX_TICK(tickSpacing), tickSpacing), [tickSpacing])
|
tickSpacing,
|
||||||
|
])
|
||||||
|
const maxIndex = useMemo(() => (tickSpacing ? bitmapIndex(MAX_TICK(tickSpacing), tickSpacing) : undefined), [
|
||||||
|
tickSpacing,
|
||||||
|
])
|
||||||
|
|
||||||
const tickLensArgs = useMemo(
|
// fetch the pool address
|
||||||
|
const factoryContract = useV3Factory()
|
||||||
|
const addressParams = token0 && token1 && feeAmount ? [token0.address, token1.address, feeAmount] : undefined
|
||||||
|
const poolAddress = useSingleCallResult(addressParams ? factoryContract : undefined, 'getPool', addressParams)
|
||||||
|
.result?.[0]
|
||||||
|
|
||||||
|
const tickLensArgs: [string, number][] = useMemo(
|
||||||
() =>
|
() =>
|
||||||
new Array(maxIndex - minIndex + 1)
|
maxIndex && minIndex && poolAddress && poolAddress !== ZERO_ADDRESS
|
||||||
|
? new Array(maxIndex - minIndex + 1)
|
||||||
.fill(0)
|
.fill(0)
|
||||||
.map((_, i) => i + minIndex)
|
.map((_, i) => i + minIndex)
|
||||||
.map((wordIndex) => [poolAddress, wordIndex]),
|
.map((wordIndex) => [poolAddress, wordIndex])
|
||||||
|
: [],
|
||||||
[minIndex, maxIndex, poolAddress]
|
[minIndex, maxIndex, poolAddress]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const tickLens = useTickLens()
|
||||||
const callStates = useSingleContractMultipleData(
|
const callStates = useSingleContractMultipleData(
|
||||||
tickLens,
|
tickLensArgs.length > 0 ? tickLens : undefined,
|
||||||
'getPopulatedTicksInWord',
|
'getPopulatedTicksInWord',
|
||||||
tickLensArgs,
|
tickLensArgs,
|
||||||
REFRESH_FREQUENCY,
|
REFRESH_FREQUENCY,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { UniswapV3Pool } from './../types/v3/UniswapV3Pool.d'
|
||||||
import { Contract } from '@ethersproject/contracts'
|
import { Contract } from '@ethersproject/contracts'
|
||||||
import { abi as GOVERNANCE_ABI } from '@uniswap/governance/build/GovernorAlpha.json'
|
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 UNI_ABI } from '@uniswap/governance/build/Uni.json'
|
||||||
@ -8,6 +9,7 @@ import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.
|
|||||||
import { abi as NFTPositionManagerABI } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
|
import { abi as NFTPositionManagerABI } from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
|
||||||
import { abi as V3FactoryABI } from '@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json'
|
import { abi as V3FactoryABI } from '@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json'
|
||||||
import { abi as TickLensABI } from '@uniswap/v3-periphery/artifacts/contracts/lens/TickLens.sol/TickLens.json'
|
import { abi as TickLensABI } from '@uniswap/v3-periphery/artifacts/contracts/lens/TickLens.sol/TickLens.json'
|
||||||
|
import { abi as V3PoolABI } from '@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json'
|
||||||
|
|
||||||
import ARGENT_WALLET_DETECTOR_ABI from 'abis/argent-wallet-detector.json'
|
import ARGENT_WALLET_DETECTOR_ABI from 'abis/argent-wallet-detector.json'
|
||||||
import ENS_PUBLIC_RESOLVER_ABI from 'abis/ens-public-resolver.json'
|
import ENS_PUBLIC_RESOLVER_ABI from 'abis/ens-public-resolver.json'
|
||||||
@ -24,8 +26,9 @@ import {
|
|||||||
MERKLE_DISTRIBUTOR_ADDRESS,
|
MERKLE_DISTRIBUTOR_ADDRESS,
|
||||||
V1_MIGRATOR_ADDRESS,
|
V1_MIGRATOR_ADDRESS,
|
||||||
UNI,
|
UNI,
|
||||||
|
MULTICALL_ADDRESSES,
|
||||||
} from 'constants/index'
|
} from 'constants/index'
|
||||||
import { MULTICALL_ABI, MULTICALL_NETWORKS } from 'constants/multicall'
|
import MULTICALL_ABI from 'abis/multicall2.json'
|
||||||
import { V1_EXCHANGE_ABI, V1_FACTORY_ABI, V1_FACTORY_ADDRESSES } from 'constants/v1'
|
import { V1_EXCHANGE_ABI, V1_FACTORY_ABI, V1_FACTORY_ADDRESSES } from 'constants/v1'
|
||||||
import { NONFUNGIBLE_POSITION_MANAGER_ADDRESSES, FACTORY_ADDRESSES, TICK_LENS_ADDRESSES } from 'constants/v3'
|
import { NONFUNGIBLE_POSITION_MANAGER_ADDRESSES, FACTORY_ADDRESSES, TICK_LENS_ADDRESSES } from 'constants/v3'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
@ -35,7 +38,7 @@ import { getContract } from 'utils'
|
|||||||
import { useActiveWeb3React } from './index'
|
import { useActiveWeb3React } from './index'
|
||||||
|
|
||||||
// returns null on errors
|
// returns null on errors
|
||||||
function useContract(address: string | undefined, ABI: any, withSignerIfPossible = true): Contract | null {
|
export function useContract(address: string | undefined, ABI: any, withSignerIfPossible = true): Contract | null {
|
||||||
const { library, account } = useActiveWeb3React()
|
const { library, account } = useActiveWeb3React()
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
@ -65,10 +68,10 @@ export function useV1ExchangeContract(address?: string, withSignerIfPossible?: b
|
|||||||
export function useTokenContract(tokenAddress?: string, withSignerIfPossible?: boolean): Contract | null {
|
export function useTokenContract(tokenAddress?: string, withSignerIfPossible?: boolean): Contract | null {
|
||||||
return useContract(tokenAddress, ERC20_ABI, withSignerIfPossible)
|
return useContract(tokenAddress, ERC20_ABI, withSignerIfPossible)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useWETHContract(withSignerIfPossible?: boolean): Contract | null {
|
export function useWETHContract(withSignerIfPossible?: boolean): Contract | null {
|
||||||
const { chainId } = useActiveWeb3React()
|
const { chainId } = useActiveWeb3React()
|
||||||
return useContract(chainId ? WETH9[chainId].address : undefined, WETH_ABI, withSignerIfPossible)
|
const address = chainId && chainId in WETH9 ? WETH9[chainId].address : undefined
|
||||||
|
return useContract(address, WETH_ABI, withSignerIfPossible)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useArgentWalletDetectorContract(): Contract | null {
|
export function useArgentWalletDetectorContract(): Contract | null {
|
||||||
@ -110,7 +113,7 @@ export function usePairContract(pairAddress?: string, withSignerIfPossible?: boo
|
|||||||
|
|
||||||
export function useMulticallContract(): Contract | null {
|
export function useMulticallContract(): Contract | null {
|
||||||
const { chainId } = useActiveWeb3React()
|
const { chainId } = useActiveWeb3React()
|
||||||
return useContract(chainId && MULTICALL_NETWORKS[chainId], MULTICALL_ABI, false)
|
return useContract(chainId && MULTICALL_ADDRESSES[chainId], MULTICALL_ABI, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useMerkleDistributorContract(): Contract | null {
|
export function useMerkleDistributorContract(): Contract | null {
|
||||||
@ -152,6 +155,10 @@ export function useV3Factory(): UniswapV3Factory | null {
|
|||||||
return useContract(address, V3FactoryABI) as UniswapV3Factory | null
|
return useContract(address, V3FactoryABI) as UniswapV3Factory | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useV3Pool(address: string | undefined): UniswapV3Pool | null {
|
||||||
|
return useContract(address, V3PoolABI) as UniswapV3Pool | null
|
||||||
|
}
|
||||||
|
|
||||||
export function useTickLens(): TickLens | null {
|
export function useTickLens(): TickLens | null {
|
||||||
const { chainId } = useActiveWeb3React()
|
const { chainId } = useActiveWeb3React()
|
||||||
const address = chainId ? TICK_LENS_ADDRESSES[chainId] : undefined
|
const address = chainId ? TICK_LENS_ADDRESSES[chainId] : undefined
|
||||||
|
13
src/hooks/useTickToPrice.ts
Normal file
13
src/hooks/useTickToPrice.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Token, Price } from '@uniswap/sdk-core'
|
||||||
|
import { tickToPrice } from '@uniswap/v3-sdk'
|
||||||
|
|
||||||
|
export function getTickToPrice(
|
||||||
|
baseToken: Token | undefined,
|
||||||
|
quoteToken: Token | undefined,
|
||||||
|
tick: number | undefined
|
||||||
|
): Price | undefined {
|
||||||
|
if (!baseToken || !quoteToken || !tick) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return tickToPrice(baseToken, quoteToken, tick)
|
||||||
|
}
|
@ -2,9 +2,9 @@ import { ChainId, Currency, currencyEquals, Price, WETH9 } from '@uniswap/sdk-co
|
|||||||
import { JSBI } from '@uniswap/v2-sdk'
|
import { JSBI } from '@uniswap/v2-sdk'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { USDC } from '../constants'
|
import { USDC } from '../constants'
|
||||||
import { PairState, usePairs } from '../data/Reserves'
|
import { PairState, usePairs } from '../data/V2'
|
||||||
import { useActiveWeb3React } from '../hooks'
|
import { useActiveWeb3React } from '../hooks'
|
||||||
import { wrappedCurrency } from './wrappedCurrency'
|
import { wrappedCurrency } from '../utils/wrappedCurrency'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the price in USDC of the input currency
|
* Returns the price in USDC of the input currency
|
@ -12,7 +12,7 @@ import useTheme from 'hooks/useTheme'
|
|||||||
import { AlertOctagon } from 'react-feather'
|
import { AlertOctagon } from 'react-feather'
|
||||||
import { ToggleWrapper, ToggleElement } from 'components/Toggle/MultiToggle'
|
import { ToggleWrapper, ToggleElement } from 'components/Toggle/MultiToggle'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Price, Percent, Currency, CurrencyAmount } from '@uniswap/sdk-core'
|
import { Price, Currency, CurrencyAmount } from '@uniswap/sdk-core'
|
||||||
|
|
||||||
const Wrapper = styled(AutoColumn)`
|
const Wrapper = styled(AutoColumn)`
|
||||||
padding: 1rem 0;
|
padding: 1rem 0;
|
||||||
@ -27,7 +27,6 @@ export function ConfirmContent({
|
|||||||
price?: Price
|
price?: Price
|
||||||
currencies: { [field in Field]?: Currency }
|
currencies: { [field in Field]?: Currency }
|
||||||
parsedAmounts: { [field in Field]?: CurrencyAmount }
|
parsedAmounts: { [field in Field]?: CurrencyAmount }
|
||||||
poolTokenPercentage?: Percent
|
|
||||||
onAdd: () => void
|
onAdd: () => void
|
||||||
}) {
|
}) {
|
||||||
const currencyA: Currency | undefined = currencies[Field.CURRENCY_A]
|
const currencyA: Currency | undefined = currencies[Field.CURRENCY_A]
|
||||||
|
140
src/pages/AddLiquidity/Review.tsx
Normal file
140
src/pages/AddLiquidity/Review.tsx
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
import React, { useMemo } from 'react'
|
||||||
|
import { RowBetween, RowFixed } from '../../components/Row'
|
||||||
|
import CurrencyLogo from '../../components/CurrencyLogo'
|
||||||
|
import { Field } from '../../state/mint/actions'
|
||||||
|
import { TYPE } from '../../theme'
|
||||||
|
import { AutoColumn } from 'components/Column'
|
||||||
|
import Card, { DarkGreyCard } from 'components/Card'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import { Break } from 'components/earn/styled'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Currency, CurrencyAmount, Price } from '@uniswap/sdk-core'
|
||||||
|
import { Position } from '@uniswap/v3-sdk'
|
||||||
|
import DoubleCurrencyLogo from 'components/DoubleLogo'
|
||||||
|
import { wrappedCurrency } from 'utils/wrappedCurrency'
|
||||||
|
import { useActiveWeb3React } from 'hooks'
|
||||||
|
// import QuestionHelper from 'components/QuestionHelper'
|
||||||
|
// import { WarningBadge } from 'components/Badge/Badge.stories'
|
||||||
|
// import { AlertCircle } from 'react-feather'
|
||||||
|
// import useTheme from 'hooks/useTheme'
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
padding: 20px;
|
||||||
|
min-width: 460px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const Badge = styled(Card)<{ inRange?: boolean }>`
|
||||||
|
width: fit-content;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 4px 6px;
|
||||||
|
background-color: ${({ inRange, theme }) => (inRange ? theme.green1 : theme.yellow2)};
|
||||||
|
`
|
||||||
|
|
||||||
|
export function Review({
|
||||||
|
position,
|
||||||
|
currencies,
|
||||||
|
parsedAmounts,
|
||||||
|
priceLower,
|
||||||
|
priceUpper,
|
||||||
|
outOfRange,
|
||||||
|
}: {
|
||||||
|
position?: Position
|
||||||
|
currencies: { [field in Field]?: Currency }
|
||||||
|
parsedAmounts: { [field in Field]?: CurrencyAmount }
|
||||||
|
priceLower?: Price
|
||||||
|
priceUpper?: Price
|
||||||
|
outOfRange: boolean
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { chainId } = useActiveWeb3React()
|
||||||
|
// const theme = useTheme()
|
||||||
|
|
||||||
|
const currencyA: Currency | undefined = currencies[Field.CURRENCY_A]
|
||||||
|
const currencyB: Currency | undefined = currencies[Field.CURRENCY_B]
|
||||||
|
|
||||||
|
// formatted with tokens
|
||||||
|
const [tokenA, tokenB] = useMemo(() => [wrappedCurrency(currencyA, chainId), wrappedCurrency(currencyB, chainId)], [
|
||||||
|
chainId,
|
||||||
|
currencyA,
|
||||||
|
currencyB,
|
||||||
|
])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<AutoColumn gap="lg">
|
||||||
|
<RowBetween>
|
||||||
|
<RowFixed>
|
||||||
|
<DoubleCurrencyLogo currency0={currencyA} currency1={currencyB} size={24} margin={true} />
|
||||||
|
<TYPE.label ml="10px" fontSize="24px">
|
||||||
|
{currencyA?.symbol} / {currencyB?.symbol}
|
||||||
|
</TYPE.label>
|
||||||
|
</RowFixed>
|
||||||
|
<Badge inRange={!outOfRange}>{outOfRange ? 'Out of range' : 'In Range'}</Badge>
|
||||||
|
</RowBetween>
|
||||||
|
{position && tokenA && tokenB && (
|
||||||
|
<DarkGreyCard>
|
||||||
|
<AutoColumn gap="md">
|
||||||
|
<TYPE.label>Deposit Amounts</TYPE.label>
|
||||||
|
{parsedAmounts[Field.CURRENCY_A] && (
|
||||||
|
<RowBetween>
|
||||||
|
<RowFixed>
|
||||||
|
<CurrencyLogo currency={currencyA} />
|
||||||
|
<TYPE.label ml="8px">{currencyA?.symbol}</TYPE.label>
|
||||||
|
</RowFixed>
|
||||||
|
<RowFixed>
|
||||||
|
<TYPE.label mr="8px">{parsedAmounts[Field.CURRENCY_A]?.toSignificant(6)}</TYPE.label>
|
||||||
|
</RowFixed>
|
||||||
|
</RowBetween>
|
||||||
|
)}
|
||||||
|
{parsedAmounts[Field.CURRENCY_B] && (
|
||||||
|
<RowBetween>
|
||||||
|
<RowFixed>
|
||||||
|
<CurrencyLogo currency={currencyB} />
|
||||||
|
<TYPE.label ml="8px">{currencyB?.symbol}</TYPE.label>
|
||||||
|
</RowFixed>
|
||||||
|
<RowFixed>
|
||||||
|
<TYPE.label mr="8px">{parsedAmounts[Field.CURRENCY_B]?.toSignificant(6)}</TYPE.label>
|
||||||
|
</RowFixed>
|
||||||
|
</RowBetween>
|
||||||
|
)}
|
||||||
|
<Break />
|
||||||
|
<RowBetween>
|
||||||
|
<TYPE.label>{t('feePool')}</TYPE.label>
|
||||||
|
<TYPE.label>{position?.pool?.fee / 10000}%</TYPE.label>
|
||||||
|
</RowBetween>
|
||||||
|
<RowBetween>
|
||||||
|
<TYPE.label>Current Price</TYPE.label>
|
||||||
|
<TYPE.label>{`1 ${currencyA?.symbol} = ${position?.pool
|
||||||
|
?.priceOf(position.pool?.token0.equals(tokenA) ? position.pool?.token0 : position.pool?.token1)
|
||||||
|
.toSignificant(6)} ${position.pool.token1?.symbol}`}</TYPE.label>
|
||||||
|
</RowBetween>
|
||||||
|
<RowBetween>
|
||||||
|
<TYPE.label>Active Range</TYPE.label>
|
||||||
|
<TYPE.label>{`1 ${
|
||||||
|
position.pool?.token0.equals(tokenA) ? currencyA?.symbol : currencyB?.symbol
|
||||||
|
} = ${priceLower?.toSignificant(4)} ⟷ ${priceUpper?.toSignificant(6)} ${
|
||||||
|
position.pool?.token0.equals(tokenA) ? currencyB?.symbol : currencyA?.symbol
|
||||||
|
}`}</TYPE.label>
|
||||||
|
</RowBetween>
|
||||||
|
</AutoColumn>
|
||||||
|
</DarkGreyCard>
|
||||||
|
)}
|
||||||
|
{/* <YellowCard>
|
||||||
|
<AutoColumn gap="md">
|
||||||
|
<RowBetween>
|
||||||
|
<TYPE.label color={theme.text2}>Efficiency Comparison</TYPE.label>
|
||||||
|
<AlertCircle stroke={theme.text2} />
|
||||||
|
</RowBetween>
|
||||||
|
<TYPE.label fontWeight={400} color={theme.text2}>
|
||||||
|
This liquidity position has an increased capital efficiency relative to an unbounded price limit.
|
||||||
|
</TYPE.label>
|
||||||
|
</AutoColumn>
|
||||||
|
</YellowCard> */}
|
||||||
|
</AutoColumn>
|
||||||
|
</Wrapper>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Review
|
@ -1,32 +1,29 @@
|
|||||||
import { BigNumber } from '@ethersproject/bignumber'
|
|
||||||
import { TransactionResponse } from '@ethersproject/providers'
|
import { TransactionResponse } from '@ethersproject/providers'
|
||||||
import { Currency, ETHER, TokenAmount } from '@uniswap/sdk-core'
|
import { Currency, TokenAmount, Token, Percent, ETHER } from '@uniswap/sdk-core'
|
||||||
import React, { useCallback, useContext, useState } from 'react'
|
import React, { useCallback, useContext, useState, useMemo } from 'react'
|
||||||
import { Link2, AlertTriangle, LifeBuoy, Circle } from 'react-feather'
|
import { Link2, AlertTriangle } from 'react-feather'
|
||||||
import ReactGA from 'react-ga'
|
import ReactGA from 'react-ga'
|
||||||
|
import { useV3NFTPositionManagerContract } from '../../hooks/useContract'
|
||||||
import { RouteComponentProps } from 'react-router-dom'
|
import { RouteComponentProps } from 'react-router-dom'
|
||||||
import { Text } from 'rebass'
|
import { Text } from 'rebass'
|
||||||
import { ThemeContext } from 'styled-components'
|
import { ThemeContext } from 'styled-components'
|
||||||
import { ButtonError, ButtonLight, ButtonPrimary, ButtonRadioChecked, ButtonText } from '../../components/Button'
|
import { ButtonError, ButtonLight, ButtonPrimary, ButtonRadioChecked, ButtonText } from '../../components/Button'
|
||||||
import { YellowCard } from '../../components/Card'
|
import { YellowCard, OutlineCard, BlueCard } from '../../components/Card'
|
||||||
import { AutoColumn, ColumnCenter } from '../../components/Column'
|
import { AutoColumn, ColumnCenter } from '../../components/Column'
|
||||||
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
|
import { TransactionSubmittedContent, ConfirmationPendingContent } from '../../components/TransactionConfirmationModal'
|
||||||
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
|
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
|
||||||
import { RowBetween, RowFixed, AutoRow } from '../../components/Row'
|
import { AutoRow, RowBetween } from '../../components/Row'
|
||||||
import ConfirmContent from './ConfirmContent'
|
import Review from './Review'
|
||||||
import { ROUTER_ADDRESS } from '../../constants'
|
|
||||||
import { useActiveWeb3React } from '../../hooks'
|
import { useActiveWeb3React } from '../../hooks'
|
||||||
import { useCurrency } from '../../hooks/Tokens'
|
import { useCurrency } from '../../hooks/Tokens'
|
||||||
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
|
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
|
||||||
import useTransactionDeadline from '../../hooks/useTransactionDeadline'
|
import useTransactionDeadline from '../../hooks/useTransactionDeadline'
|
||||||
import { useWalletModalToggle } from '../../state/application/hooks'
|
import { useWalletModalToggle } from '../../state/application/hooks'
|
||||||
import { Field, Bound, RangeType } from '../../state/mint/actions'
|
import { Field, Bound } from '../../state/mint/actions'
|
||||||
import { useDerivedMintInfo, useMintActionHandlers, useMintState } from '../../state/mint/hooks'
|
|
||||||
|
|
||||||
import { useTransactionAdder } from '../../state/transactions/hooks'
|
import { useTransactionAdder } from '../../state/transactions/hooks'
|
||||||
import { useIsExpertMode, useUserSlippageTolerance } from '../../state/user/hooks'
|
import { useIsExpertMode, useUserSlippageTolerance } from '../../state/user/hooks'
|
||||||
import { TYPE } from '../../theme'
|
import { TYPE } from '../../theme'
|
||||||
import { calculateGasMargin, calculateSlippageAmount, getRouterContract } from '../../utils'
|
|
||||||
import { maxAmountSpend } from '../../utils/maxAmountSpend'
|
import { maxAmountSpend } from '../../utils/maxAmountSpend'
|
||||||
import { wrappedCurrency } from '../../utils/wrappedCurrency'
|
import { wrappedCurrency } from '../../utils/wrappedCurrency'
|
||||||
import AppBody from '../AppBody'
|
import AppBody from '../AppBody'
|
||||||
@ -39,58 +36,71 @@ import StepCounter from 'components/InputStepCounter/InputStepCounter'
|
|||||||
import {
|
import {
|
||||||
DynamicSection,
|
DynamicSection,
|
||||||
CurrencyDropdown,
|
CurrencyDropdown,
|
||||||
ScrollablePage,
|
|
||||||
ScrollableContent,
|
ScrollableContent,
|
||||||
|
StyledInput,
|
||||||
FixedPreview,
|
FixedPreview,
|
||||||
PreviewCard,
|
ScrollablePage,
|
||||||
} from './styled'
|
} from './styled'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import CurrencyLogo from 'components/CurrencyLogo'
|
import { useMintState, useMintActionHandlers, useDerivedMintInfo } from 'state/mint/hooks'
|
||||||
import QuestionHelper from 'components/QuestionHelper'
|
import { FeeAmount, NonfungiblePositionManager } from '@uniswap/v3-sdk'
|
||||||
|
import { NONFUNGIBLE_POSITION_MANAGER_ADDRESSES } from 'constants/v3'
|
||||||
|
import JSBI from 'jsbi'
|
||||||
|
|
||||||
export default function AddLiquidity({
|
export default function AddLiquidity({
|
||||||
match: {
|
match: {
|
||||||
params: { currencyIdA, currencyIdB },
|
params: { currencyIdA, currencyIdB, feeAmount: feeAmountFromUrl },
|
||||||
},
|
},
|
||||||
history,
|
history,
|
||||||
}: RouteComponentProps<{ currencyIdA?: string; currencyIdB?: string }>) {
|
}: RouteComponentProps<{ currencyIdA?: string; currencyIdB?: string; feeAmount?: string }>) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const { account, chainId, library } = useActiveWeb3React()
|
const { account, chainId, library } = useActiveWeb3React()
|
||||||
const theme = useContext(ThemeContext)
|
const theme = useContext(ThemeContext)
|
||||||
|
const toggleWalletModal = useWalletModalToggle() // toggle wallet when disconnected
|
||||||
|
const expertMode = useIsExpertMode()
|
||||||
|
const addTransaction = useTransactionAdder()
|
||||||
|
const positionManager = useV3NFTPositionManagerContract()
|
||||||
|
|
||||||
|
// fee selection from url
|
||||||
|
const feeAmount =
|
||||||
|
feeAmountFromUrl && Object.values(FeeAmount).includes(parseFloat(feeAmountFromUrl))
|
||||||
|
? parseFloat(feeAmountFromUrl)
|
||||||
|
: undefined
|
||||||
|
|
||||||
const currencyA = useCurrency(currencyIdA)
|
const currencyA = useCurrency(currencyIdA)
|
||||||
const currencyB = useCurrency(currencyIdB)
|
const currencyB = useCurrency(currencyIdB)
|
||||||
|
|
||||||
// const oneCurrencyIsWETH = Boolean(
|
|
||||||
// chainId &&
|
|
||||||
// ((currencyA && currencyEquals(currencyA, WETH9[chainId])) ||
|
|
||||||
// (currencyB && currencyEquals(currencyB, WETH9[chainId])))
|
|
||||||
// )
|
|
||||||
|
|
||||||
const toggleWalletModal = useWalletModalToggle() // toggle wallet when disconnected
|
|
||||||
|
|
||||||
const expertMode = useIsExpertMode()
|
|
||||||
|
|
||||||
// mint state
|
// mint state
|
||||||
const { independentField, typedValue, otherTypedValue, rangeType } = useMintState()
|
const { independentField, typedValue, startPriceTypedValue } = useMintState()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
dependentField,
|
|
||||||
currencies,
|
|
||||||
pair,
|
|
||||||
// pairState,
|
|
||||||
currencyBalances,
|
|
||||||
parsedAmounts,
|
|
||||||
ticks,
|
ticks,
|
||||||
|
dependentField,
|
||||||
price,
|
price,
|
||||||
|
pricesAtTicks,
|
||||||
|
parsedAmounts,
|
||||||
|
currencyBalances,
|
||||||
|
position,
|
||||||
noLiquidity,
|
noLiquidity,
|
||||||
poolTokenPercentage,
|
currencies,
|
||||||
error,
|
errorMessage,
|
||||||
} = useDerivedMintInfo(currencyA ?? undefined, currencyB ?? undefined)
|
invalidPool,
|
||||||
|
invalidRange,
|
||||||
|
outOfRange,
|
||||||
|
depositADisabled,
|
||||||
|
depositBDisabled,
|
||||||
|
} = useDerivedMintInfo(currencyA ?? undefined, currencyB ?? undefined, feeAmount)
|
||||||
|
|
||||||
const { onFieldAInput, onFieldBInput, onLowerRangeInput, onUpperRangeInput } = useMintActionHandlers(noLiquidity)
|
const {
|
||||||
|
onFieldAInput,
|
||||||
|
onFieldBInput,
|
||||||
|
onLowerRangeInput,
|
||||||
|
onUpperRangeInput,
|
||||||
|
onStartPriceInput,
|
||||||
|
} = useMintActionHandlers(noLiquidity)
|
||||||
|
|
||||||
const isValid = !error
|
const isValid = !errorMessage
|
||||||
|
|
||||||
// modal and loading
|
// modal and loading
|
||||||
const [showConfirm, setShowConfirm] = useState<boolean>(false)
|
const [showConfirm, setShowConfirm] = useState<boolean>(false)
|
||||||
@ -99,12 +109,13 @@ export default function AddLiquidity({
|
|||||||
// txn values
|
// txn values
|
||||||
const deadline = useTransactionDeadline() // custom from users settings
|
const deadline = useTransactionDeadline() // custom from users settings
|
||||||
const [allowedSlippage] = useUserSlippageTolerance() // custom from users
|
const [allowedSlippage] = useUserSlippageTolerance() // custom from users
|
||||||
|
const fractionalizedTolerance = new Percent(JSBI.BigInt(allowedSlippage), JSBI.BigInt(10000))
|
||||||
const [txHash, setTxHash] = useState<string>('')
|
const [txHash, setTxHash] = useState<string>('')
|
||||||
|
|
||||||
// get formatted amounts
|
// get formatted amounts
|
||||||
const formattedAmounts = {
|
const formattedAmounts = {
|
||||||
[independentField]: typedValue,
|
[independentField]: typedValue,
|
||||||
[dependentField]: noLiquidity ? otherTypedValue : parsedAmounts[dependentField]?.toSignificant(6) ?? '',
|
[dependentField]: parsedAmounts[dependentField]?.toSignificant(6) ?? '',
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the max amounts user can add
|
// get the max amounts user can add
|
||||||
@ -129,88 +140,73 @@ export default function AddLiquidity({
|
|||||||
)
|
)
|
||||||
|
|
||||||
// check whether the user has approved the router on the tokens
|
// check whether the user has approved the router on the tokens
|
||||||
const [approvalA, approveACallback] = useApproveCallback(parsedAmounts[Field.CURRENCY_A], ROUTER_ADDRESS)
|
const [approvalA, approveACallback] = useApproveCallback(
|
||||||
const [approvalB, approveBCallback] = useApproveCallback(parsedAmounts[Field.CURRENCY_B], ROUTER_ADDRESS)
|
parsedAmounts[Field.CURRENCY_A],
|
||||||
|
chainId ? NONFUNGIBLE_POSITION_MANAGER_ADDRESSES[chainId] : undefined
|
||||||
const addTransaction = useTransactionAdder()
|
)
|
||||||
|
const [approvalB, approveBCallback] = useApproveCallback(
|
||||||
|
parsedAmounts[Field.CURRENCY_B],
|
||||||
|
chainId ? NONFUNGIBLE_POSITION_MANAGER_ADDRESSES[chainId] : undefined
|
||||||
|
)
|
||||||
|
|
||||||
async function onAdd() {
|
async function onAdd() {
|
||||||
if (!chainId || !library || !account) return
|
if (!chainId || !library || !account) return
|
||||||
const router = getRouterContract(chainId, library, account)
|
|
||||||
|
|
||||||
const { [Field.CURRENCY_A]: parsedAmountA, [Field.CURRENCY_B]: parsedAmountB } = parsedAmounts
|
if (!positionManager || !currencyA || !currencyB) {
|
||||||
if (!parsedAmountA || !parsedAmountB || !currencyA || !currencyB || !deadline) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const amountsMin = {
|
if (position && account && deadline && fractionalizedTolerance) {
|
||||||
[Field.CURRENCY_A]: calculateSlippageAmount(parsedAmountA, noLiquidity ? 0 : allowedSlippage)[0],
|
const { calldata, value } = NonfungiblePositionManager.mintCallParameters(position, {
|
||||||
[Field.CURRENCY_B]: calculateSlippageAmount(parsedAmountB, noLiquidity ? 0 : allowedSlippage)[0],
|
slippageTolerance: fractionalizedTolerance,
|
||||||
}
|
recipient: account,
|
||||||
|
deadline: deadline.toNumber(),
|
||||||
|
useEther: currencyA === ETHER || currencyB === ETHER,
|
||||||
|
createPool: noLiquidity,
|
||||||
|
})
|
||||||
|
|
||||||
let estimate,
|
const txn = {
|
||||||
method: (...args: any) => Promise<TransactionResponse>,
|
to: NONFUNGIBLE_POSITION_MANAGER_ADDRESSES[chainId],
|
||||||
args: Array<string | string[] | number>,
|
data: calldata,
|
||||||
value: BigNumber | null
|
value,
|
||||||
if (currencyA === ETHER || currencyB === ETHER) {
|
|
||||||
const tokenBIsETH = currencyB === ETHER
|
|
||||||
estimate = router.estimateGas.addLiquidityETH
|
|
||||||
method = router.addLiquidityETH
|
|
||||||
args = [
|
|
||||||
wrappedCurrency(tokenBIsETH ? currencyA : currencyB, chainId)?.address ?? '', // token
|
|
||||||
(tokenBIsETH ? parsedAmountA : parsedAmountB).raw.toString(), // token desired
|
|
||||||
amountsMin[tokenBIsETH ? Field.CURRENCY_A : Field.CURRENCY_B].toString(), // token min
|
|
||||||
amountsMin[tokenBIsETH ? Field.CURRENCY_B : Field.CURRENCY_A].toString(), // eth min
|
|
||||||
account,
|
|
||||||
deadline.toHexString(),
|
|
||||||
]
|
|
||||||
value = BigNumber.from((tokenBIsETH ? parsedAmountB : parsedAmountA).raw.toString())
|
|
||||||
} else {
|
|
||||||
estimate = router.estimateGas.addLiquidity
|
|
||||||
method = router.addLiquidity
|
|
||||||
args = [
|
|
||||||
wrappedCurrency(currencyA, chainId)?.address ?? '',
|
|
||||||
wrappedCurrency(currencyB, chainId)?.address ?? '',
|
|
||||||
parsedAmountA.raw.toString(),
|
|
||||||
parsedAmountB.raw.toString(),
|
|
||||||
amountsMin[Field.CURRENCY_A].toString(),
|
|
||||||
amountsMin[Field.CURRENCY_B].toString(),
|
|
||||||
account,
|
|
||||||
deadline.toHexString(),
|
|
||||||
]
|
|
||||||
value = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setAttemptingTxn(true)
|
setAttemptingTxn(true)
|
||||||
await estimate(...args, value ? { value } : {})
|
|
||||||
.then((estimatedGasLimit) =>
|
|
||||||
method(...args, {
|
|
||||||
...(value ? { value } : {}),
|
|
||||||
gasLimit: calculateGasMargin(estimatedGasLimit),
|
|
||||||
}).then((response) => {
|
|
||||||
setAttemptingTxn(false)
|
|
||||||
|
|
||||||
|
library
|
||||||
|
.getSigner()
|
||||||
|
.estimateGas(txn)
|
||||||
|
.then((estimate) => {
|
||||||
|
const newTxn = {
|
||||||
|
...txn,
|
||||||
|
gasLimit: estimate,
|
||||||
|
}
|
||||||
|
library
|
||||||
|
.getSigner()
|
||||||
|
.sendTransaction(newTxn)
|
||||||
|
.then((response: TransactionResponse) => {
|
||||||
|
setAttemptingTxn(false)
|
||||||
addTransaction(response, {
|
addTransaction(response, {
|
||||||
summary:
|
summary: noLiquidity
|
||||||
'Add ' +
|
? 'Create Pool + '
|
||||||
parsedAmounts[Field.CURRENCY_A]?.toSignificant(3) +
|
: '' + 'Add ' + !depositADisabled
|
||||||
|
? parsedAmounts[Field.CURRENCY_A]?.toSignificant(3) +
|
||||||
' ' +
|
' ' +
|
||||||
currencies[Field.CURRENCY_A]?.symbol +
|
currencies[Field.CURRENCY_A]?.symbol +
|
||||||
' and ' +
|
!outOfRange
|
||||||
parsedAmounts[Field.CURRENCY_B]?.toSignificant(3) +
|
: ''
|
||||||
' ' +
|
? ' and '
|
||||||
currencies[Field.CURRENCY_B]?.symbol,
|
: '' + !depositBDisabled
|
||||||
|
? parsedAmounts[Field.CURRENCY_B]?.toSignificant(3) + ' ' + currencies[Field.CURRENCY_B]?.symbol
|
||||||
|
: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
setTxHash(response.hash)
|
setTxHash(response.hash)
|
||||||
|
|
||||||
ReactGA.event({
|
ReactGA.event({
|
||||||
category: 'Liquidity',
|
category: 'Liquidity',
|
||||||
action: 'Add',
|
action: 'Add',
|
||||||
label: [currencies[Field.CURRENCY_A]?.symbol, currencies[Field.CURRENCY_B]?.symbol].join('/'),
|
label: [currencies[Field.CURRENCY_A]?.symbol, currencies[Field.CURRENCY_B]?.symbol].join('/'),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
)
|
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setAttemptingTxn(false)
|
setAttemptingTxn(false)
|
||||||
// we only care if the error is something _other_ than the user rejected the tx
|
// we only care if the error is something _other_ than the user rejected the tx
|
||||||
@ -218,24 +214,17 @@ export default function AddLiquidity({
|
|||||||
console.error(error)
|
console.error(error)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const modalContent = () => {
|
const pendingText = `Supplying ${!depositADisabled ? parsedAmounts[Field.CURRENCY_A]?.toSignificant(6) : ''} ${
|
||||||
return (
|
!depositADisabled ? currencies[Field.CURRENCY_A]?.symbol : ''
|
||||||
<ConfirmContent
|
} ${!outOfRange ? 'and' : ''} ${!depositBDisabled ? parsedAmounts[Field.CURRENCY_B]?.toSignificant(6) : ''} ${
|
||||||
price={price}
|
!depositBDisabled ? currencies[Field.CURRENCY_B]?.symbol : ''
|
||||||
currencies={currencies}
|
}`
|
||||||
parsedAmounts={parsedAmounts}
|
|
||||||
noLiquidity={noLiquidity}
|
|
||||||
onAdd={onAdd}
|
|
||||||
poolTokenPercentage={poolTokenPercentage}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const pendingText = `Supplying ${parsedAmounts[Field.CURRENCY_A]?.toSignificant(6)} ${
|
|
||||||
currencies[Field.CURRENCY_A]?.symbol
|
|
||||||
} and ${parsedAmounts[Field.CURRENCY_B]?.toSignificant(6)} ${currencies[Field.CURRENCY_B]?.symbol}`
|
|
||||||
|
|
||||||
const handleCurrencyASelect = useCallback(
|
const handleCurrencyASelect = useCallback(
|
||||||
(currencyA: Currency) => {
|
(currencyA: Currency) => {
|
||||||
@ -243,7 +232,7 @@ export default function AddLiquidity({
|
|||||||
if (newCurrencyIdA === currencyIdB) {
|
if (newCurrencyIdA === currencyIdB) {
|
||||||
history.push(`/add/${currencyIdB}/${currencyIdA}`)
|
history.push(`/add/${currencyIdB}/${currencyIdA}`)
|
||||||
} else {
|
} else {
|
||||||
history.push(`/add/${newCurrencyIdA}/${currencyIdB}`)
|
history.push(`/add/${newCurrencyIdA}/${currencyIdB ?? 'ETH'}`)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[currencyIdB, history, currencyIdA]
|
[currencyIdB, history, currencyIdA]
|
||||||
@ -258,12 +247,19 @@ export default function AddLiquidity({
|
|||||||
history.push(`/add/${newCurrencyIdB}`)
|
history.push(`/add/${newCurrencyIdB}`)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
history.push(`/add/${currencyIdA ? currencyIdA : 'ETH'}/${newCurrencyIdB}`)
|
history.push(`/add/${currencyIdA ?? 'ETH'}/${newCurrencyIdB}`)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[currencyIdA, history, currencyIdB]
|
[currencyIdA, history, currencyIdB]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const handleFeePoolSelect = useCallback(
|
||||||
|
(newFeeAmount: FeeAmount) => {
|
||||||
|
history.push(`/add/${currencyIdA}/${currencyIdB}/${newFeeAmount}`)
|
||||||
|
},
|
||||||
|
[currencyIdA, currencyIdB, history]
|
||||||
|
)
|
||||||
|
|
||||||
const handleDismissConfirmation = useCallback(() => {
|
const handleDismissConfirmation = useCallback(() => {
|
||||||
setShowConfirm(false)
|
setShowConfirm(false)
|
||||||
// if there was a tx hash, we want to clear the input
|
// if there was a tx hash, we want to clear the input
|
||||||
@ -277,13 +273,6 @@ export default function AddLiquidity({
|
|||||||
|
|
||||||
const addIsUnsupported = useIsTransactionUnsupported(currencies?.CURRENCY_A, currencies?.CURRENCY_B)
|
const addIsUnsupported = useIsTransactionUnsupported(currencies?.CURRENCY_A, currencies?.CURRENCY_B)
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* dummy values for v3 prototype
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
const [feeLevel, setFeeLevel] = useState<number>(4)
|
|
||||||
|
|
||||||
const clearAll = useCallback(() => {
|
const clearAll = useCallback(() => {
|
||||||
onFieldAInput('')
|
onFieldAInput('')
|
||||||
onFieldBInput('')
|
onFieldBInput('')
|
||||||
@ -292,38 +281,96 @@ export default function AddLiquidity({
|
|||||||
history.push(`/add/`)
|
history.push(`/add/`)
|
||||||
}, [history, onFieldAInput, onFieldBInput, onLowerRangeInput, onUpperRangeInput])
|
}, [history, onFieldAInput, onFieldBInput, onLowerRangeInput, onUpperRangeInput])
|
||||||
|
|
||||||
const currentTick = ticks[Bound.CURRENT]
|
// get value and prices at ticks
|
||||||
const lowerTick = ticks[Bound.LOWER]
|
const { [Bound.LOWER]: tickLower, [Bound.UPPER]: tickUpper } = ticks
|
||||||
const upperTick = ticks[Bound.UPPER]
|
const { [Bound.LOWER]: priceLower, [Bound.UPPER]: priceUpper } = pricesAtTicks
|
||||||
|
|
||||||
const [rateCurrencyBase, setRateCurrencyBase] = useState<Currency | null | undefined>(currencyA)
|
// used sort sorted toggle
|
||||||
|
const tokenA = wrappedCurrency(currencyA ?? undefined, chainId)
|
||||||
|
const tokenB = wrappedCurrency(currencyB ?? undefined, chainId)
|
||||||
|
const sortedTokens: Token[] | undefined = useMemo(
|
||||||
|
() =>
|
||||||
|
tokenA && tokenB && !tokenA.equals(tokenB)
|
||||||
|
? tokenA.sortsBefore(tokenB)
|
||||||
|
? [tokenA, tokenB]
|
||||||
|
: [tokenB, tokenA]
|
||||||
|
: undefined,
|
||||||
|
[tokenA, tokenB]
|
||||||
|
)
|
||||||
|
|
||||||
const invalidRange = lowerTick && upperTick && lowerTick.rate > upperTick.rate
|
const handleRateToggle = useCallback(() => {
|
||||||
const outOfRange =
|
if (currencyA && currencyB) {
|
||||||
currentTick &&
|
const currencyIdA = currencyId(currencyA)
|
||||||
lowerTick &&
|
const currencyIdB = currencyId(currencyB)
|
||||||
upperTick &&
|
// reset inputs
|
||||||
!invalidRange &&
|
onLowerRangeInput('')
|
||||||
(lowerTick.rate > currentTick.rate || upperTick.rate < currentTick.rate)
|
onUpperRangeInput('')
|
||||||
|
onStartPriceInput('')
|
||||||
|
onFieldAInput('')
|
||||||
|
onFieldBInput('')
|
||||||
|
history.push(`/add/${currencyIdB}/${currencyIdA}/${feeAmount ?? ''}`)
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
currencyA,
|
||||||
|
currencyB,
|
||||||
|
feeAmount,
|
||||||
|
history,
|
||||||
|
onFieldAInput,
|
||||||
|
onFieldBInput,
|
||||||
|
onLowerRangeInput,
|
||||||
|
onStartPriceInput,
|
||||||
|
onUpperRangeInput,
|
||||||
|
])
|
||||||
|
|
||||||
|
const RateToggle = () => {
|
||||||
|
return sortedTokens && currencyB && currencyA ? (
|
||||||
|
<ToggleWrapper width="fit-content">
|
||||||
|
<ToggleElement isActive={tokenA === sortedTokens[0]} fontSize="12px" onClick={handleRateToggle}>
|
||||||
|
{tokenA === sortedTokens[0] ? currencyB.symbol : currencyA?.symbol} {t('rate')}
|
||||||
|
</ToggleElement>
|
||||||
|
<ToggleElement isActive={tokenB === sortedTokens[0]} fontSize="12px" onClick={handleRateToggle}>
|
||||||
|
{tokenB === sortedTokens[0] ? currencyB.symbol : currencyA?.symbol} {t('rate')}
|
||||||
|
</ToggleElement>
|
||||||
|
</ToggleWrapper>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollablePage>
|
<ScrollablePage>
|
||||||
<TransactionConfirmationModal
|
|
||||||
isOpen={showConfirm}
|
|
||||||
onDismiss={handleDismissConfirmation}
|
|
||||||
attemptingTxn={attemptingTxn}
|
|
||||||
hash={txHash}
|
|
||||||
content={() => (
|
|
||||||
<ConfirmationModalContent
|
|
||||||
title={'Review Position'}
|
|
||||||
onDismiss={handleDismissConfirmation}
|
|
||||||
topContent={modalContent}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
pendingText={pendingText}
|
|
||||||
currencyToAdd={pair?.liquidityToken}
|
|
||||||
/>
|
|
||||||
<ScrollableContent>
|
<ScrollableContent>
|
||||||
|
<AutoRow marginBottom="20px">
|
||||||
|
<ButtonText opacity={'0.4'} onClick={() => history.push('/pool')}>
|
||||||
|
Pool
|
||||||
|
</ButtonText>
|
||||||
|
<TYPE.label margin="0 10px" opacity={'0.4'}>
|
||||||
|
{' > '}
|
||||||
|
</TYPE.label>
|
||||||
|
<ButtonText opacity={showConfirm ? '0.4' : '1'} onClick={() => (showConfirm ? setShowConfirm(false) : null)}>
|
||||||
|
Configure
|
||||||
|
</ButtonText>
|
||||||
|
<TYPE.label margin="0 10px" opacity={'0.4'}>
|
||||||
|
{' > '}
|
||||||
|
</TYPE.label>
|
||||||
|
<ButtonText
|
||||||
|
opacity={showConfirm ? '1' : '0.4'}
|
||||||
|
onClick={() => (!showConfirm ? setShowConfirm(true) : null)}
|
||||||
|
disabled={!isValid}
|
||||||
|
>
|
||||||
|
Review
|
||||||
|
</ButtonText>
|
||||||
|
</AutoRow>
|
||||||
|
{showConfirm ? (
|
||||||
|
<AppBody>
|
||||||
|
<Review
|
||||||
|
currencies={currencies}
|
||||||
|
parsedAmounts={parsedAmounts}
|
||||||
|
position={position}
|
||||||
|
priceLower={priceLower}
|
||||||
|
priceUpper={priceUpper}
|
||||||
|
outOfRange={outOfRange}
|
||||||
|
/>
|
||||||
|
</AppBody>
|
||||||
|
) : (
|
||||||
<AppBody>
|
<AppBody>
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<AutoColumn gap="40px">
|
<AutoColumn gap="40px">
|
||||||
@ -334,9 +381,9 @@ export default function AddLiquidity({
|
|||||||
<TYPE.blue fontSize="12px">Clear All</TYPE.blue>
|
<TYPE.blue fontSize="12px">Clear All</TYPE.blue>
|
||||||
</ButtonText>
|
</ButtonText>
|
||||||
</RowBetween>
|
</RowBetween>
|
||||||
<TYPE.main fontWeight={400} fontSize="14px">
|
{/* <TYPE.main fontWeight={400} fontSize="14px">
|
||||||
{t('selectAPool')}
|
{t('selectAPool')}
|
||||||
</TYPE.main>
|
</TYPE.main> */}
|
||||||
<RowBetween>
|
<RowBetween>
|
||||||
<CurrencyDropdown
|
<CurrencyDropdown
|
||||||
value={formattedAmounts[Field.CURRENCY_A]}
|
value={formattedAmounts[Field.CURRENCY_A]}
|
||||||
@ -367,106 +414,158 @@ export default function AddLiquidity({
|
|||||||
/>
|
/>
|
||||||
</RowBetween>
|
</RowBetween>
|
||||||
</AutoColumn>
|
</AutoColumn>
|
||||||
|
|
||||||
<AutoColumn gap="16px">
|
<AutoColumn gap="16px">
|
||||||
<DynamicSection gap="md" disabled={!currencyB || !currencyA}>
|
<DynamicSection gap="md" disabled={!currencyB || !currencyA}>
|
||||||
<TYPE.label>{t('selectPool')}</TYPE.label>
|
<TYPE.label>{t('selectPool')}</TYPE.label>
|
||||||
<TYPE.main fontWeight={400} fontSize="14px">
|
{/* <TYPE.main fontWeight={400} fontSize="14px">
|
||||||
{t('poolType')}
|
{t('poolType')}
|
||||||
</TYPE.main>
|
</TYPE.main> */}
|
||||||
<RowBetween>
|
<RowBetween>
|
||||||
<ButtonRadioChecked width="32%" active={feeLevel === 0} onClick={() => setFeeLevel(0)}>
|
<ButtonRadioChecked
|
||||||
<TYPE.label>0.1% {t('fee')}</TYPE.label>
|
width="32%"
|
||||||
|
active={feeAmount === FeeAmount.LOW}
|
||||||
|
onClick={() => handleFeePoolSelect(FeeAmount.LOW)}
|
||||||
|
>
|
||||||
|
<AutoColumn gap="sm" justify="flex-start">
|
||||||
|
<TYPE.label>0.05% {t('fee')}</TYPE.label>
|
||||||
|
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||||
|
Optimized for stable assets.
|
||||||
|
</TYPE.main>
|
||||||
|
</AutoColumn>
|
||||||
</ButtonRadioChecked>
|
</ButtonRadioChecked>
|
||||||
<ButtonRadioChecked active={feeLevel === 1} width="32%" onClick={() => setFeeLevel(1)}>
|
<ButtonRadioChecked
|
||||||
|
width="32%"
|
||||||
|
active={feeAmount === FeeAmount.MEDIUM}
|
||||||
|
onClick={() => handleFeePoolSelect(FeeAmount.MEDIUM)}
|
||||||
|
>
|
||||||
|
<AutoColumn gap="sm" justify="flex-start">
|
||||||
<TYPE.label>0.3% {t('fee')}</TYPE.label>
|
<TYPE.label>0.3% {t('fee')}</TYPE.label>
|
||||||
|
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||||
|
The classic Uniswap pool fee.
|
||||||
|
</TYPE.main>
|
||||||
|
</AutoColumn>
|
||||||
</ButtonRadioChecked>
|
</ButtonRadioChecked>
|
||||||
<ButtonRadioChecked width="32%" active={feeLevel === 2} onClick={() => setFeeLevel(2)}>
|
<ButtonRadioChecked
|
||||||
<TYPE.label>0.5% {t('fee')}</TYPE.label>
|
width="32%"
|
||||||
|
active={feeAmount === FeeAmount.HIGH}
|
||||||
|
onClick={() => handleFeePoolSelect(FeeAmount.HIGH)}
|
||||||
|
>
|
||||||
|
<AutoColumn gap="sm" justify="flex-start">
|
||||||
|
<TYPE.label>1% {t('fee')}</TYPE.label>
|
||||||
|
<TYPE.main fontWeight={400} fontSize="12px" textAlign="left">
|
||||||
|
Best for volatile assets.
|
||||||
|
</TYPE.main>
|
||||||
|
</AutoColumn>
|
||||||
</ButtonRadioChecked>
|
</ButtonRadioChecked>
|
||||||
</RowBetween>
|
</RowBetween>
|
||||||
</DynamicSection>
|
</DynamicSection>
|
||||||
</AutoColumn>
|
</AutoColumn>
|
||||||
|
|
||||||
<DynamicSection gap="md" disabled={feeLevel >= 4}>
|
{noLiquidity && (
|
||||||
|
<DynamicSection disabled={!currencyA || !currencyB}>
|
||||||
|
<AutoColumn gap="md">
|
||||||
|
<BlueCard width="100%" padding="1rem">
|
||||||
|
You are the first to provide liquidity to this pool.
|
||||||
|
</BlueCard>
|
||||||
|
<RowBetween>
|
||||||
|
<TYPE.label>{t('selectStartingPrice')}</TYPE.label>
|
||||||
|
{tokenA && tokenB && <RateToggle />}
|
||||||
|
</RowBetween>
|
||||||
|
{/* <TYPE.main fontWeight={400} fontSize="14px">
|
||||||
|
{t('newPoolPrice')}
|
||||||
|
</TYPE.main> */}
|
||||||
|
<OutlineCard padding="12px">
|
||||||
|
<StyledInput
|
||||||
|
className="start-price-input"
|
||||||
|
value={startPriceTypedValue}
|
||||||
|
onUserInput={onStartPriceInput}
|
||||||
|
/>
|
||||||
|
</OutlineCard>
|
||||||
|
<RowBetween style={{ backgroundColor: theme.bg6, padding: '12px', borderRadius: '12px' }}>
|
||||||
|
<TYPE.main>Starting Price</TYPE.main>
|
||||||
|
{price ? (
|
||||||
|
<TYPE.main>
|
||||||
|
1 {currencyA?.symbol} = {price?.toSignificant(8)} {currencyB?.symbol}
|
||||||
|
</TYPE.main>
|
||||||
|
) : (
|
||||||
|
'-'
|
||||||
|
)}
|
||||||
|
</RowBetween>
|
||||||
|
</AutoColumn>
|
||||||
|
</DynamicSection>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<DynamicSection gap="md" disabled={!feeAmount || invalidPool || (noLiquidity && !startPriceTypedValue)}>
|
||||||
<RowBetween>
|
<RowBetween>
|
||||||
<TYPE.label>{t('selectLiquidityRange')}</TYPE.label>
|
<TYPE.label>{t('selectLiquidityRange')}</TYPE.label>
|
||||||
{currencyA && currencyB && (
|
{tokenA && tokenB && !noLiquidity && <RateToggle />}
|
||||||
<ToggleWrapper width="fit-content">
|
|
||||||
<ToggleElement
|
|
||||||
isActive={rateCurrencyBase === currencyA}
|
|
||||||
fontSize="12px"
|
|
||||||
onClick={() => setRateCurrencyBase(rateCurrencyBase === currencyB ? currencyA : currencyB)}
|
|
||||||
>
|
|
||||||
{currencyA.symbol} {t('rate')}
|
|
||||||
</ToggleElement>
|
|
||||||
<ToggleElement
|
|
||||||
fontSize="12px"
|
|
||||||
isActive={currencyB === rateCurrencyBase}
|
|
||||||
onClick={() => setRateCurrencyBase(rateCurrencyBase === currencyB ? currencyA : currencyB)}
|
|
||||||
>
|
|
||||||
{currencyB.symbol} Rate
|
|
||||||
</ToggleElement>
|
|
||||||
</ToggleWrapper>
|
|
||||||
)}
|
|
||||||
</RowBetween>
|
</RowBetween>
|
||||||
<TYPE.main fontWeight={400} fontSize="14px">
|
{/* <TYPE.main fontWeight={400} fontSize="14px">
|
||||||
{t('rangeWarning')}
|
{t('rangeWarning')}
|
||||||
</TYPE.main>
|
</TYPE.main> */}
|
||||||
{price && rateCurrencyBase && (
|
{price && currencyA && !noLiquidity && (
|
||||||
<RowBetween style={{ backgroundColor: theme.bg3, padding: '8px', borderRadius: '12px' }}>
|
<RowBetween style={{ backgroundColor: theme.bg6, padding: '12px', borderRadius: '12px' }}>
|
||||||
<TYPE.main>{t('currentRate', { label: rateCurrencyBase.symbol })}</TYPE.main>
|
<TYPE.main>{t('currentRate', { label: currencyA.symbol })}</TYPE.main>
|
||||||
<TYPE.main>
|
<TYPE.main>
|
||||||
{rateCurrencyBase === currencyA ? price.toSignificant(3) : price.invert().toSignificant(3)}{' '}
|
{price.toSignificant(3)} {currencyB?.symbol}
|
||||||
{rateCurrencyBase === currencyB ? currencyA?.symbol : currencyB?.symbol}
|
|
||||||
</TYPE.main>
|
</TYPE.main>
|
||||||
</RowBetween>
|
</RowBetween>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<RowBetween>
|
<RowBetween>
|
||||||
<StepCounter
|
<StepCounter
|
||||||
value={lowerTick?.rate?.toString() ?? ''}
|
value={priceLower?.toSignificant(5) ?? ''}
|
||||||
onUserInput={onLowerRangeInput}
|
onUserInput={onLowerRangeInput}
|
||||||
usePercent={rangeType === RangeType.PERCENT}
|
width="48%"
|
||||||
prependSymbol={rangeType === RangeType.PERCENT ? '-' : undefined}
|
label={
|
||||||
|
priceLower && currencyA && currencyB
|
||||||
|
? '1 ' + currencyA?.symbol + ' / ' + priceLower?.toSignificant(4) + ' ' + currencyB?.symbol
|
||||||
|
: '-'
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Link2 style={{ margin: '0 10px' }} size={40} />
|
|
||||||
<StepCounter
|
<StepCounter
|
||||||
value={upperTick?.rate?.toString() ?? ''}
|
value={priceUpper?.toSignificant(5) ?? ''}
|
||||||
onUserInput={onUpperRangeInput}
|
onUserInput={onUpperRangeInput}
|
||||||
usePercent={rangeType === RangeType.PERCENT}
|
width="48%"
|
||||||
prependSymbol={rangeType === RangeType.PERCENT ? '+' : undefined}
|
label={
|
||||||
|
priceUpper && currencyA && currencyB
|
||||||
|
? '1 ' + currencyA?.symbol + ' / ' + priceUpper?.toSignificant(4) + ' ' + currencyB?.symbol
|
||||||
|
: '-'
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</RowBetween>
|
</RowBetween>
|
||||||
|
|
||||||
{outOfRange && (
|
{outOfRange ? (
|
||||||
<YellowCard>
|
<YellowCard padding="8px 12px" borderRadius="12px">
|
||||||
<RowBetween>
|
<RowBetween>
|
||||||
<AlertTriangle stroke={theme.yellow3} size="24px" />
|
<AlertTriangle stroke={theme.yellow3} size="16px" />
|
||||||
<TYPE.yellow ml="12px" fontSize="12px">
|
<TYPE.yellow ml="12px" fontSize="12px">
|
||||||
{t('inactiveRangeWarning')}
|
{t('inactiveRangeWarning')}
|
||||||
</TYPE.yellow>
|
</TYPE.yellow>
|
||||||
</RowBetween>
|
</RowBetween>
|
||||||
</YellowCard>
|
</YellowCard>
|
||||||
)}
|
) : null}
|
||||||
|
|
||||||
{invalidRange && (
|
{invalidRange ? (
|
||||||
<YellowCard>
|
<YellowCard padding="8px 12px" borderRadius="12px">
|
||||||
<RowBetween>
|
<RowBetween>
|
||||||
<AlertTriangle stroke={theme.yellow3} size="24px" />
|
<AlertTriangle stroke={theme.yellow3} size="16px" />
|
||||||
<TYPE.yellow ml="12px" fontSize="12px">
|
<TYPE.yellow ml="12px" fontSize="12px">
|
||||||
{t('invalidRangeWarning')}
|
{t('invalidRangeWarning')}
|
||||||
</TYPE.yellow>
|
</TYPE.yellow>
|
||||||
</RowBetween>
|
</RowBetween>
|
||||||
</YellowCard>
|
</YellowCard>
|
||||||
)}
|
) : null}
|
||||||
</DynamicSection>
|
</DynamicSection>
|
||||||
|
|
||||||
<DynamicSection disabled={!lowerTick || !upperTick}>
|
<DynamicSection
|
||||||
|
disabled={tickLower === undefined || tickUpper === undefined || invalidPool || invalidRange}
|
||||||
|
>
|
||||||
<AutoColumn gap="md">
|
<AutoColumn gap="md">
|
||||||
<TYPE.label>{t('inputTokens')}</TYPE.label>
|
<TYPE.label>{t('depositAmounts')}</TYPE.label>
|
||||||
<TYPE.main fontWeight={400} fontSize="14px">
|
{/* <TYPE.main fontWeight={400} fontSize="14px">
|
||||||
{t('chooseLiquidityAmount')}
|
{t('chooseLiquidityAmount')}
|
||||||
</TYPE.main>
|
</TYPE.main> */}
|
||||||
<CurrencyInputPanel
|
<CurrencyInputPanel
|
||||||
value={formattedAmounts[Field.CURRENCY_A]}
|
value={formattedAmounts[Field.CURRENCY_A]}
|
||||||
onUserInput={onFieldAInput}
|
onUserInput={onFieldAInput}
|
||||||
@ -479,11 +578,11 @@ export default function AddLiquidity({
|
|||||||
currency={currencies[Field.CURRENCY_A]}
|
currency={currencies[Field.CURRENCY_A]}
|
||||||
id="add-liquidity-input-tokena"
|
id="add-liquidity-input-tokena"
|
||||||
showCommonBases
|
showCommonBases
|
||||||
|
locked={depositADisabled}
|
||||||
/>
|
/>
|
||||||
<ColumnCenter>
|
<ColumnCenter>
|
||||||
<Link2 stroke={theme.text2} size={'24px'} />
|
<Link2 stroke={theme.text2} size={'24px'} />
|
||||||
</ColumnCenter>
|
</ColumnCenter>
|
||||||
|
|
||||||
<CurrencyInputPanel
|
<CurrencyInputPanel
|
||||||
value={formattedAmounts[Field.CURRENCY_B]}
|
value={formattedAmounts[Field.CURRENCY_B]}
|
||||||
disableCurrencySelect={true}
|
disableCurrencySelect={true}
|
||||||
@ -496,12 +595,14 @@ export default function AddLiquidity({
|
|||||||
currency={currencies[Field.CURRENCY_B]}
|
currency={currencies[Field.CURRENCY_B]}
|
||||||
id="add-liquidity-input-tokenb"
|
id="add-liquidity-input-tokenb"
|
||||||
showCommonBases
|
showCommonBases
|
||||||
|
locked={depositBDisabled}
|
||||||
/>
|
/>
|
||||||
</AutoColumn>
|
</AutoColumn>
|
||||||
</DynamicSection>
|
</DynamicSection>
|
||||||
</AutoColumn>
|
</AutoColumn>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
</AppBody>
|
</AppBody>
|
||||||
|
)}
|
||||||
</ScrollableContent>
|
</ScrollableContent>
|
||||||
{addIsUnsupported && (
|
{addIsUnsupported && (
|
||||||
<UnsupportedCurrencyFooter
|
<UnsupportedCurrencyFooter
|
||||||
@ -510,103 +611,23 @@ export default function AddLiquidity({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<FixedPreview>
|
<FixedPreview>
|
||||||
|
{attemptingTxn ? (
|
||||||
|
<ConfirmationPendingContent onDismiss={handleDismissConfirmation} pendingText={pendingText} inline={true} />
|
||||||
|
) : txHash && chainId ? (
|
||||||
|
<TransactionSubmittedContent
|
||||||
|
chainId={chainId}
|
||||||
|
hash={txHash}
|
||||||
|
onDismiss={handleDismissConfirmation}
|
||||||
|
inline={true}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<AutoColumn gap="md">
|
<AutoColumn gap="md">
|
||||||
<TYPE.main fontSize="12px">Position Preview</TYPE.main>
|
<TYPE.label fontSize="16px">{showConfirm ? 'Review and submit' : 'Configure Position'}</TYPE.label>
|
||||||
<PreviewCard>
|
<TYPE.main fontWeight={400} fontSize="14px">
|
||||||
{!currencyA || !currencyB ? (
|
Learn more about Uniswap V3 liquidity pools.
|
||||||
<RowBetween>
|
</TYPE.main>
|
||||||
<TYPE.label>Select a pair to begin</TYPE.label>
|
{showConfirm ? (
|
||||||
<QuestionHelper text="Select a pair to begin" size={20} />
|
<div>
|
||||||
</RowBetween>
|
|
||||||
) : (
|
|
||||||
<AutoRow gap="4px">
|
|
||||||
<CurrencyLogo currency={currencyA} size={'20px'} />
|
|
||||||
<TYPE.label ml="4px">{currencyA.symbol}</TYPE.label>
|
|
||||||
<TYPE.main>/</TYPE.main>
|
|
||||||
<CurrencyLogo currency={currencyB} size={'20px'} />
|
|
||||||
<TYPE.label ml="4px">{currencyB.symbol}</TYPE.label>
|
|
||||||
</AutoRow>
|
|
||||||
)}
|
|
||||||
</PreviewCard>
|
|
||||||
<PreviewCard disabled={!currencyA || !currencyB}>
|
|
||||||
<RowBetween>
|
|
||||||
<TYPE.label>
|
|
||||||
{feeLevel >= 4
|
|
||||||
? t('selectPool')
|
|
||||||
: `${feeLevel === 0 ? '0.1' : feeLevel === 1 ? '0.3' : '0.5'}% fee pool`}
|
|
||||||
</TYPE.label>
|
|
||||||
<LifeBuoy size="20px" />
|
|
||||||
</RowBetween>
|
|
||||||
</PreviewCard>
|
|
||||||
<PreviewCard disabled={feeLevel >= 4}>
|
|
||||||
{!lowerTick || !upperTick ? (
|
|
||||||
<RowBetween>
|
|
||||||
<TYPE.label>{t('selectPriceLimits')}</TYPE.label>
|
|
||||||
<Circle size="20px" />
|
|
||||||
</RowBetween>
|
|
||||||
) : (
|
|
||||||
currencyA &&
|
|
||||||
currencyB && (
|
|
||||||
<AutoColumn gap="sm" style={{ width: '100%' }}>
|
|
||||||
<ToggleWrapper width="100%">
|
|
||||||
<ToggleElement
|
|
||||||
isActive={rateCurrencyBase === currencyA}
|
|
||||||
fontSize="12px"
|
|
||||||
onClick={() => setRateCurrencyBase(rateCurrencyBase === currencyB ? currencyA : currencyB)}
|
|
||||||
>
|
|
||||||
{currencyA.symbol} {t('rate')}
|
|
||||||
</ToggleElement>
|
|
||||||
<ToggleElement
|
|
||||||
fontSize="12px"
|
|
||||||
isActive={currencyB === rateCurrencyBase}
|
|
||||||
onClick={() => setRateCurrencyBase(rateCurrencyBase === currencyB ? currencyA : currencyB)}
|
|
||||||
>
|
|
||||||
{currencyB.symbol} Rate
|
|
||||||
</ToggleElement>
|
|
||||||
</ToggleWrapper>
|
|
||||||
<RowBetween padding="0 32px">
|
|
||||||
<TYPE.label>{lowerTick.rate}</TYPE.label>
|
|
||||||
<TYPE.main>⟷</TYPE.main>
|
|
||||||
<TYPE.label>{upperTick.rate}</TYPE.label>
|
|
||||||
</RowBetween>
|
|
||||||
</AutoColumn>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</PreviewCard>
|
|
||||||
<PreviewCard disabled={!lowerTick || !upperTick}>
|
|
||||||
{!formattedAmounts[Field.CURRENCY_A] || !currencyA || !currencyB ? (
|
|
||||||
<RowBetween>
|
|
||||||
<TYPE.label>{t('inputTokenDynamic', { label: currencyA ? currencyA.symbol : 'Token' })}</TYPE.label>
|
|
||||||
<Circle size="20px" />
|
|
||||||
</RowBetween>
|
|
||||||
) : (
|
|
||||||
<RowBetween>
|
|
||||||
<RowFixed>
|
|
||||||
<CurrencyLogo currency={currencyA} />
|
|
||||||
<TYPE.label ml="8px">{formattedAmounts[Field.CURRENCY_A]}</TYPE.label>
|
|
||||||
<TYPE.label ml="8px">{currencyA.symbol}</TYPE.label>
|
|
||||||
</RowFixed>
|
|
||||||
<TYPE.main>50%</TYPE.main>
|
|
||||||
</RowBetween>
|
|
||||||
)}
|
|
||||||
</PreviewCard>
|
|
||||||
<PreviewCard disabled={!lowerTick || !upperTick}>
|
|
||||||
{!formattedAmounts[Field.CURRENCY_B] || !currencyA || !currencyB ? (
|
|
||||||
<RowBetween>
|
|
||||||
<TYPE.label>{t('inputTokenDynamic', { label: currencyB ? currencyB.symbol : 'Token' })}</TYPE.label>
|
|
||||||
<Circle size="20px" />
|
|
||||||
</RowBetween>
|
|
||||||
) : (
|
|
||||||
<RowBetween>
|
|
||||||
<RowFixed>
|
|
||||||
<CurrencyLogo currency={currencyB} />
|
|
||||||
<TYPE.label ml="8px">{formattedAmounts[Field.CURRENCY_B]}</TYPE.label>
|
|
||||||
<TYPE.label ml="8px">{currencyB.symbol}</TYPE.label>
|
|
||||||
</RowFixed>
|
|
||||||
<TYPE.main>50%</TYPE.main>
|
|
||||||
</RowBetween>
|
|
||||||
)}
|
|
||||||
</PreviewCard>
|
|
||||||
{addIsUnsupported ? (
|
{addIsUnsupported ? (
|
||||||
<ButtonPrimary disabled={true} borderRadius="12px" padding={'12px'}>
|
<ButtonPrimary disabled={true} borderRadius="12px" padding={'12px'}>
|
||||||
<TYPE.main mb="4px">{t('unsupportedAsset')}</TYPE.main>
|
<TYPE.main mb="4px">{t('unsupportedAsset')}</TYPE.main>
|
||||||
@ -655,20 +676,41 @@ export default function AddLiquidity({
|
|||||||
)}
|
)}
|
||||||
</RowBetween>
|
</RowBetween>
|
||||||
)}
|
)}
|
||||||
|
<ButtonError
|
||||||
|
onClick={() => {
|
||||||
|
onAdd()
|
||||||
|
}}
|
||||||
|
style={{ borderRadius: '12px' }}
|
||||||
|
padding={'12px'}
|
||||||
|
disabled={
|
||||||
|
!isValid ||
|
||||||
|
(approvalA !== ApprovalState.APPROVED && !depositADisabled) ||
|
||||||
|
(approvalB !== ApprovalState.APPROVED && !depositBDisabled)
|
||||||
|
}
|
||||||
|
error={!isValid && !!parsedAmounts[Field.CURRENCY_A] && !!parsedAmounts[Field.CURRENCY_B]}
|
||||||
|
>
|
||||||
|
<Text fontWeight={500}>
|
||||||
|
{errorMessage ? errorMessage : noLiquidity ? 'Create Pool and Add' : 'Add'}
|
||||||
|
</Text>
|
||||||
|
</ButtonError>
|
||||||
|
</AutoColumn>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<ButtonError
|
<ButtonError
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
expertMode ? onAdd() : setShowConfirm(true)
|
expertMode ? onAdd() : setShowConfirm(true)
|
||||||
}}
|
}}
|
||||||
style={{ borderRadius: '12px' }}
|
style={{ borderRadius: '12px' }}
|
||||||
padding={'12px'}
|
padding={'12px'}
|
||||||
disabled={!isValid || approvalA !== ApprovalState.APPROVED || approvalB !== ApprovalState.APPROVED}
|
disabled={!isValid}
|
||||||
error={!isValid && !!parsedAmounts[Field.CURRENCY_A] && !!parsedAmounts[Field.CURRENCY_B]}
|
error={!isValid && !!parsedAmounts[Field.CURRENCY_A] && !!parsedAmounts[Field.CURRENCY_B]}
|
||||||
>
|
>
|
||||||
<Text fontWeight={500}>{error ?? 'Next'}</Text>
|
<Text fontWeight={500}>{errorMessage ?? 'Review'}</Text>
|
||||||
</ButtonError>
|
</ButtonError>
|
||||||
</AutoColumn>
|
|
||||||
)}
|
)}
|
||||||
</AutoColumn>
|
</AutoColumn>
|
||||||
|
)}
|
||||||
</FixedPreview>
|
</FixedPreview>
|
||||||
</ScrollablePage>
|
</ScrollablePage>
|
||||||
)
|
)
|
||||||
|
@ -2,32 +2,16 @@ import React from 'react'
|
|||||||
import { Redirect, RouteComponentProps } from 'react-router-dom'
|
import { Redirect, RouteComponentProps } from 'react-router-dom'
|
||||||
import AddLiquidity from './index'
|
import AddLiquidity from './index'
|
||||||
|
|
||||||
export function RedirectToAddLiquidity() {
|
export function RedirectDuplicateTokenIds(
|
||||||
return <Redirect to="/add/" />
|
props: RouteComponentProps<{ currencyIdA: string; currencyIdB: string; feeAmount?: string }>
|
||||||
}
|
) {
|
||||||
|
|
||||||
const OLD_PATH_STRUCTURE = /^(0x[a-fA-F0-9]{40})-(0x[a-fA-F0-9]{40})$/
|
|
||||||
export function RedirectOldAddLiquidityPathStructure(props: RouteComponentProps<{ currencyIdA: string }>) {
|
|
||||||
const {
|
|
||||||
match: {
|
|
||||||
params: { currencyIdA },
|
|
||||||
},
|
|
||||||
} = props
|
|
||||||
const match = currencyIdA.match(OLD_PATH_STRUCTURE)
|
|
||||||
if (match?.length) {
|
|
||||||
return <Redirect to={`/add/${match[1]}/${match[2]}`} />
|
|
||||||
}
|
|
||||||
|
|
||||||
return <AddLiquidity {...props} />
|
|
||||||
}
|
|
||||||
|
|
||||||
export function RedirectDuplicateTokenIds(props: RouteComponentProps<{ currencyIdA: string; currencyIdB: string }>) {
|
|
||||||
const {
|
const {
|
||||||
match: {
|
match: {
|
||||||
params: { currencyIdA, currencyIdB },
|
params: { currencyIdA, currencyIdB },
|
||||||
},
|
},
|
||||||
} = props
|
} = props
|
||||||
if (currencyIdA.toLowerCase() === currencyIdB.toLowerCase()) {
|
|
||||||
|
if (currencyIdA && currencyIdB && currencyIdA.toLowerCase() === currencyIdB.toLowerCase()) {
|
||||||
return <Redirect to={`/add/${currencyIdA}`} />
|
return <Redirect to={`/add/${currencyIdA}`} />
|
||||||
}
|
}
|
||||||
return <AddLiquidity {...props} />
|
return <AddLiquidity {...props} />
|
||||||
|
@ -2,6 +2,7 @@ import styled from 'styled-components'
|
|||||||
import { AutoColumn } from 'components/Column'
|
import { AutoColumn } from 'components/Column'
|
||||||
import CurrencyInputPanel from 'components/CurrencyInputPanel'
|
import CurrencyInputPanel from 'components/CurrencyInputPanel'
|
||||||
import { DarkGreyCard } from 'components/Card'
|
import { DarkGreyCard } from 'components/Card'
|
||||||
|
import Input from 'components/NumericalInput'
|
||||||
|
|
||||||
export const ScrollablePage = styled.div`
|
export const ScrollablePage = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -10,18 +11,21 @@ export const ScrollablePage = styled.div`
|
|||||||
`
|
`
|
||||||
|
|
||||||
export const ScrollableContent = styled.div`
|
export const ScrollableContent = styled.div`
|
||||||
margin-right: 24px;
|
margin-right: 16px;
|
||||||
`
|
`
|
||||||
|
|
||||||
export const FixedPreview = styled.div`
|
export const FixedPreview = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 8px;
|
padding: 16px;
|
||||||
width: 260px;
|
width: 260px;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
|
margin-top: 42px;
|
||||||
background: ${({ theme }) => theme.bg0};
|
background: ${({ theme }) => theme.bg0};
|
||||||
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
|
box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
|
||||||
0px 24px 32px rgba(0, 0, 0, 0.01);
|
0px 24px 32px rgba(0, 0, 0, 0.01);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
|
position: sticky;
|
||||||
|
top: 120px;
|
||||||
`
|
`
|
||||||
|
|
||||||
export const DynamicSection = styled(AutoColumn)<{ disabled?: boolean }>`
|
export const DynamicSection = styled(AutoColumn)<{ disabled?: boolean }>`
|
||||||
@ -30,7 +34,7 @@ export const DynamicSection = styled(AutoColumn)<{ disabled?: boolean }>`
|
|||||||
`
|
`
|
||||||
|
|
||||||
export const CurrencyDropdown = styled(CurrencyInputPanel)`
|
export const CurrencyDropdown = styled(CurrencyInputPanel)`
|
||||||
width: 49%;
|
width: 48.5%;
|
||||||
`
|
`
|
||||||
|
|
||||||
export const PreviewCard = styled(DarkGreyCard)<{ disabled?: boolean }>`
|
export const PreviewCard = styled(DarkGreyCard)<{ disabled?: boolean }>`
|
||||||
@ -42,3 +46,10 @@ export const PreviewCard = styled(DarkGreyCard)<{ disabled?: boolean }>`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export const StyledInput = styled(Input)`
|
||||||
|
background-color: ${({ theme }) => theme.bg0};
|
||||||
|
text-align: left;
|
||||||
|
font-size: 18px;
|
||||||
|
width: 100%;
|
||||||
|
`
|
||||||
|
67
src/pages/AddLiquidityV2/ConfirmAddModalBottom.tsx
Normal file
67
src/pages/AddLiquidityV2/ConfirmAddModalBottom.tsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { Currency, CurrencyAmount, Fraction, Percent } from '@uniswap/sdk-core'
|
||||||
|
import React from 'react'
|
||||||
|
import { Text } from 'rebass'
|
||||||
|
import { ButtonPrimary } from '../../components/Button'
|
||||||
|
import { RowBetween, RowFixed } from '../../components/Row'
|
||||||
|
import CurrencyLogo from '../../components/CurrencyLogo'
|
||||||
|
import { Field } from '../../state/mint/actions'
|
||||||
|
import { TYPE } from '../../theme'
|
||||||
|
|
||||||
|
export function ConfirmAddModalBottom({
|
||||||
|
noLiquidity,
|
||||||
|
price,
|
||||||
|
currencies,
|
||||||
|
parsedAmounts,
|
||||||
|
poolTokenPercentage,
|
||||||
|
onAdd,
|
||||||
|
}: {
|
||||||
|
noLiquidity?: boolean
|
||||||
|
price?: Fraction
|
||||||
|
currencies: { [field in Field]?: Currency }
|
||||||
|
parsedAmounts: { [field in Field]?: CurrencyAmount }
|
||||||
|
poolTokenPercentage?: Percent
|
||||||
|
onAdd: () => void
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<RowBetween>
|
||||||
|
<TYPE.body>{currencies[Field.CURRENCY_A]?.symbol} Deposited</TYPE.body>
|
||||||
|
<RowFixed>
|
||||||
|
<CurrencyLogo currency={currencies[Field.CURRENCY_A]} style={{ marginRight: '8px' }} />
|
||||||
|
<TYPE.body>{parsedAmounts[Field.CURRENCY_A]?.toSignificant(6)}</TYPE.body>
|
||||||
|
</RowFixed>
|
||||||
|
</RowBetween>
|
||||||
|
<RowBetween>
|
||||||
|
<TYPE.body>{currencies[Field.CURRENCY_B]?.symbol} Deposited</TYPE.body>
|
||||||
|
<RowFixed>
|
||||||
|
<CurrencyLogo currency={currencies[Field.CURRENCY_B]} style={{ marginRight: '8px' }} />
|
||||||
|
<TYPE.body>{parsedAmounts[Field.CURRENCY_B]?.toSignificant(6)}</TYPE.body>
|
||||||
|
</RowFixed>
|
||||||
|
</RowBetween>
|
||||||
|
<RowBetween>
|
||||||
|
<TYPE.body>Rates</TYPE.body>
|
||||||
|
<TYPE.body>
|
||||||
|
{`1 ${currencies[Field.CURRENCY_A]?.symbol} = ${price?.toSignificant(4)} ${
|
||||||
|
currencies[Field.CURRENCY_B]?.symbol
|
||||||
|
}`}
|
||||||
|
</TYPE.body>
|
||||||
|
</RowBetween>
|
||||||
|
<RowBetween style={{ justifyContent: 'flex-end' }}>
|
||||||
|
<TYPE.body>
|
||||||
|
{`1 ${currencies[Field.CURRENCY_B]?.symbol} = ${price?.invert().toSignificant(4)} ${
|
||||||
|
currencies[Field.CURRENCY_A]?.symbol
|
||||||
|
}`}
|
||||||
|
</TYPE.body>
|
||||||
|
</RowBetween>
|
||||||
|
<RowBetween>
|
||||||
|
<TYPE.body>Share of Pool:</TYPE.body>
|
||||||
|
<TYPE.body>{noLiquidity ? '100' : poolTokenPercentage?.toSignificant(4)}%</TYPE.body>
|
||||||
|
</RowBetween>
|
||||||
|
<ButtonPrimary style={{ margin: '20px 0 0 0' }} onClick={onAdd}>
|
||||||
|
<Text fontWeight={500} fontSize={20}>
|
||||||
|
{noLiquidity ? 'Create Pool & Supply' : 'Confirm Supply'}
|
||||||
|
</Text>
|
||||||
|
</ButtonPrimary>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
52
src/pages/AddLiquidityV2/PoolPriceBar.tsx
Normal file
52
src/pages/AddLiquidityV2/PoolPriceBar.tsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { Currency, Percent, Price } from '@uniswap/sdk-core'
|
||||||
|
import React, { useContext } from 'react'
|
||||||
|
import { Text } from 'rebass'
|
||||||
|
import { ThemeContext } from 'styled-components'
|
||||||
|
import { AutoColumn } from '../../components/Column'
|
||||||
|
import { AutoRow } from '../../components/Row'
|
||||||
|
import { ONE_BIPS } from '../../constants'
|
||||||
|
import { Field } from '../../state/mint/actions'
|
||||||
|
import { TYPE } from '../../theme'
|
||||||
|
|
||||||
|
export function PoolPriceBar({
|
||||||
|
currencies,
|
||||||
|
noLiquidity,
|
||||||
|
poolTokenPercentage,
|
||||||
|
price,
|
||||||
|
}: {
|
||||||
|
currencies: { [field in Field]?: Currency }
|
||||||
|
noLiquidity?: boolean
|
||||||
|
poolTokenPercentage?: Percent
|
||||||
|
price?: Price
|
||||||
|
}) {
|
||||||
|
const theme = useContext(ThemeContext)
|
||||||
|
return (
|
||||||
|
<AutoColumn gap="md">
|
||||||
|
<AutoRow justify="space-around" gap="4px">
|
||||||
|
<AutoColumn justify="center">
|
||||||
|
<TYPE.black>{price?.toSignificant(6) ?? '-'}</TYPE.black>
|
||||||
|
<Text fontWeight={500} fontSize={14} color={theme.text2} pt={1}>
|
||||||
|
{currencies[Field.CURRENCY_B]?.symbol} per {currencies[Field.CURRENCY_A]?.symbol}
|
||||||
|
</Text>
|
||||||
|
</AutoColumn>
|
||||||
|
<AutoColumn justify="center">
|
||||||
|
<TYPE.black>{price?.invert()?.toSignificant(6) ?? '-'}</TYPE.black>
|
||||||
|
<Text fontWeight={500} fontSize={14} color={theme.text2} pt={1}>
|
||||||
|
{currencies[Field.CURRENCY_A]?.symbol} per {currencies[Field.CURRENCY_B]?.symbol}
|
||||||
|
</Text>
|
||||||
|
</AutoColumn>
|
||||||
|
<AutoColumn justify="center">
|
||||||
|
<TYPE.black>
|
||||||
|
{noLiquidity && price
|
||||||
|
? '100'
|
||||||
|
: (poolTokenPercentage?.lessThan(ONE_BIPS) ? '<0.01' : poolTokenPercentage?.toFixed(2)) ?? '0'}
|
||||||
|
%
|
||||||
|
</TYPE.black>
|
||||||
|
<Text fontWeight={500} fontSize={14} color={theme.text2} pt={1}>
|
||||||
|
Share of Pool
|
||||||
|
</Text>
|
||||||
|
</AutoColumn>
|
||||||
|
</AutoRow>
|
||||||
|
</AutoColumn>
|
||||||
|
)
|
||||||
|
}
|
485
src/pages/AddLiquidityV2/index.tsx
Normal file
485
src/pages/AddLiquidityV2/index.tsx
Normal file
@ -0,0 +1,485 @@
|
|||||||
|
import { BigNumber } from '@ethersproject/bignumber'
|
||||||
|
import { TransactionResponse } from '@ethersproject/providers'
|
||||||
|
import { Currency, currencyEquals, ETHER, TokenAmount, WETH9 } from '@uniswap/sdk-core'
|
||||||
|
import React, { useCallback, useContext, useState } from 'react'
|
||||||
|
import { Plus } from 'react-feather'
|
||||||
|
import ReactGA from 'react-ga'
|
||||||
|
import { RouteComponentProps } from 'react-router-dom'
|
||||||
|
import { Text } from 'rebass'
|
||||||
|
import { ThemeContext } from 'styled-components'
|
||||||
|
import { ButtonError, ButtonLight, ButtonPrimary } from '../../components/Button'
|
||||||
|
import { BlueCard, LightCard } from '../../components/Card'
|
||||||
|
import { AutoColumn, ColumnCenter } from '../../components/Column'
|
||||||
|
import TransactionConfirmationModal, { ConfirmationModalContent } from '../../components/TransactionConfirmationModal'
|
||||||
|
import CurrencyInputPanel from '../../components/CurrencyInputPanel'
|
||||||
|
import DoubleCurrencyLogo from '../../components/DoubleLogo'
|
||||||
|
import { AddRemoveTabs } from '../../components/NavigationTabs'
|
||||||
|
import { MinimalPositionCard } from '../../components/PositionCard'
|
||||||
|
import Row, { RowBetween, RowFlat } from '../../components/Row'
|
||||||
|
|
||||||
|
import { ROUTER_ADDRESS } from '../../constants'
|
||||||
|
import { PairState } from '../../data/V2'
|
||||||
|
import { useActiveWeb3React } from '../../hooks'
|
||||||
|
import { useCurrency } from '../../hooks/Tokens'
|
||||||
|
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
|
||||||
|
import useTransactionDeadline from '../../hooks/useTransactionDeadline'
|
||||||
|
import { useWalletModalToggle } from '../../state/application/hooks'
|
||||||
|
import { Field } from '../../state/mint/actions'
|
||||||
|
import { useMintActionHandlers, useMintState } from '../../state/mint/hooks'
|
||||||
|
|
||||||
|
import { useTransactionAdder } from '../../state/transactions/hooks'
|
||||||
|
import { useIsExpertMode, useUserSlippageTolerance } from '../../state/user/hooks'
|
||||||
|
import { TYPE } from '../../theme'
|
||||||
|
import { calculateGasMargin, calculateSlippageAmount, getRouterContract } from '../../utils'
|
||||||
|
import { maxAmountSpend } from '../../utils/maxAmountSpend'
|
||||||
|
import { wrappedCurrency } from '../../utils/wrappedCurrency'
|
||||||
|
import AppBody from '../AppBody'
|
||||||
|
import { Dots, Wrapper } from '../Pool/styleds'
|
||||||
|
import { ConfirmAddModalBottom } from './ConfirmAddModalBottom'
|
||||||
|
import { currencyId } from '../../utils/currencyId'
|
||||||
|
import { PoolPriceBar } from './PoolPriceBar'
|
||||||
|
import { useIsTransactionUnsupported } from 'hooks/Trades'
|
||||||
|
import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter'
|
||||||
|
import { useV2DerivedMintInfo } from 'state/mint/v2'
|
||||||
|
|
||||||
|
export default function AddLiquidity({
|
||||||
|
match: {
|
||||||
|
params: { currencyIdA, currencyIdB },
|
||||||
|
},
|
||||||
|
history,
|
||||||
|
}: RouteComponentProps<{ currencyIdA?: string; currencyIdB?: string }>) {
|
||||||
|
const { account, chainId, library } = useActiveWeb3React()
|
||||||
|
const theme = useContext(ThemeContext)
|
||||||
|
|
||||||
|
const currencyA = useCurrency(currencyIdA)
|
||||||
|
const currencyB = useCurrency(currencyIdB)
|
||||||
|
|
||||||
|
const oneCurrencyIsWETH = Boolean(
|
||||||
|
chainId &&
|
||||||
|
((currencyA && currencyEquals(currencyA, WETH9[chainId])) ||
|
||||||
|
(currencyB && currencyEquals(currencyB, WETH9[chainId])))
|
||||||
|
)
|
||||||
|
|
||||||
|
const toggleWalletModal = useWalletModalToggle() // toggle wallet when disconnected
|
||||||
|
|
||||||
|
const expertMode = useIsExpertMode()
|
||||||
|
|
||||||
|
// mint state
|
||||||
|
const { independentField, typedValue, otherTypedValue } = useMintState()
|
||||||
|
const {
|
||||||
|
dependentField,
|
||||||
|
currencies,
|
||||||
|
pair,
|
||||||
|
pairState,
|
||||||
|
currencyBalances,
|
||||||
|
parsedAmounts,
|
||||||
|
price,
|
||||||
|
noLiquidity,
|
||||||
|
liquidityMinted,
|
||||||
|
poolTokenPercentage,
|
||||||
|
error,
|
||||||
|
} = useV2DerivedMintInfo(currencyA ?? undefined, currencyB ?? undefined)
|
||||||
|
|
||||||
|
const { onFieldAInput, onFieldBInput } = useMintActionHandlers(noLiquidity)
|
||||||
|
|
||||||
|
const isValid = !error
|
||||||
|
|
||||||
|
// modal and loading
|
||||||
|
const [showConfirm, setShowConfirm] = useState<boolean>(false)
|
||||||
|
const [attemptingTxn, setAttemptingTxn] = useState<boolean>(false) // clicked confirm
|
||||||
|
|
||||||
|
// txn values
|
||||||
|
const deadline = useTransactionDeadline() // custom from users settings
|
||||||
|
const [allowedSlippage] = useUserSlippageTolerance() // custom from users
|
||||||
|
const [txHash, setTxHash] = useState<string>('')
|
||||||
|
|
||||||
|
// get formatted amounts
|
||||||
|
const formattedAmounts = {
|
||||||
|
[independentField]: typedValue,
|
||||||
|
[dependentField]: noLiquidity ? otherTypedValue : parsedAmounts[dependentField]?.toSignificant(6) ?? '',
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the max amounts user can add
|
||||||
|
const maxAmounts: { [field in Field]?: TokenAmount } = [Field.CURRENCY_A, Field.CURRENCY_B].reduce(
|
||||||
|
(accumulator, field) => {
|
||||||
|
return {
|
||||||
|
...accumulator,
|
||||||
|
[field]: maxAmountSpend(currencyBalances[field]),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
|
const atMaxAmounts: { [field in Field]?: TokenAmount } = [Field.CURRENCY_A, Field.CURRENCY_B].reduce(
|
||||||
|
(accumulator, field) => {
|
||||||
|
return {
|
||||||
|
...accumulator,
|
||||||
|
[field]: maxAmounts[field]?.equalTo(parsedAmounts[field] ?? '0'),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// check whether the user has approved the router on the tokens
|
||||||
|
const [approvalA, approveACallback] = useApproveCallback(parsedAmounts[Field.CURRENCY_A], ROUTER_ADDRESS)
|
||||||
|
const [approvalB, approveBCallback] = useApproveCallback(parsedAmounts[Field.CURRENCY_B], ROUTER_ADDRESS)
|
||||||
|
|
||||||
|
const addTransaction = useTransactionAdder()
|
||||||
|
|
||||||
|
async function onAdd() {
|
||||||
|
if (!chainId || !library || !account) return
|
||||||
|
const router = getRouterContract(chainId, library, account)
|
||||||
|
|
||||||
|
const { [Field.CURRENCY_A]: parsedAmountA, [Field.CURRENCY_B]: parsedAmountB } = parsedAmounts
|
||||||
|
if (!parsedAmountA || !parsedAmountB || !currencyA || !currencyB || !deadline) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const amountsMin = {
|
||||||
|
[Field.CURRENCY_A]: calculateSlippageAmount(parsedAmountA, noLiquidity ? 0 : allowedSlippage)[0],
|
||||||
|
[Field.CURRENCY_B]: calculateSlippageAmount(parsedAmountB, noLiquidity ? 0 : allowedSlippage)[0],
|
||||||
|
}
|
||||||
|
|
||||||
|
let estimate,
|
||||||
|
method: (...args: any) => Promise<TransactionResponse>,
|
||||||
|
args: Array<string | string[] | number>,
|
||||||
|
value: BigNumber | null
|
||||||
|
if (currencyA === ETHER || currencyB === ETHER) {
|
||||||
|
const tokenBIsETH = currencyB === ETHER
|
||||||
|
estimate = router.estimateGas.addLiquidityETH
|
||||||
|
method = router.addLiquidityETH
|
||||||
|
args = [
|
||||||
|
wrappedCurrency(tokenBIsETH ? currencyA : currencyB, chainId)?.address ?? '', // token
|
||||||
|
(tokenBIsETH ? parsedAmountA : parsedAmountB).raw.toString(), // token desired
|
||||||
|
amountsMin[tokenBIsETH ? Field.CURRENCY_A : Field.CURRENCY_B].toString(), // token min
|
||||||
|
amountsMin[tokenBIsETH ? Field.CURRENCY_B : Field.CURRENCY_A].toString(), // eth min
|
||||||
|
account,
|
||||||
|
deadline.toHexString(),
|
||||||
|
]
|
||||||
|
value = BigNumber.from((tokenBIsETH ? parsedAmountB : parsedAmountA).raw.toString())
|
||||||
|
} else {
|
||||||
|
estimate = router.estimateGas.addLiquidity
|
||||||
|
method = router.addLiquidity
|
||||||
|
args = [
|
||||||
|
wrappedCurrency(currencyA, chainId)?.address ?? '',
|
||||||
|
wrappedCurrency(currencyB, chainId)?.address ?? '',
|
||||||
|
parsedAmountA.raw.toString(),
|
||||||
|
parsedAmountB.raw.toString(),
|
||||||
|
amountsMin[Field.CURRENCY_A].toString(),
|
||||||
|
amountsMin[Field.CURRENCY_B].toString(),
|
||||||
|
account,
|
||||||
|
deadline.toHexString(),
|
||||||
|
]
|
||||||
|
value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
setAttemptingTxn(true)
|
||||||
|
await estimate(...args, value ? { value } : {})
|
||||||
|
.then((estimatedGasLimit) =>
|
||||||
|
method(...args, {
|
||||||
|
...(value ? { value } : {}),
|
||||||
|
gasLimit: calculateGasMargin(estimatedGasLimit),
|
||||||
|
}).then((response) => {
|
||||||
|
setAttemptingTxn(false)
|
||||||
|
|
||||||
|
addTransaction(response, {
|
||||||
|
summary:
|
||||||
|
'Add ' +
|
||||||
|
parsedAmounts[Field.CURRENCY_A]?.toSignificant(3) +
|
||||||
|
' ' +
|
||||||
|
currencies[Field.CURRENCY_A]?.symbol +
|
||||||
|
' and ' +
|
||||||
|
parsedAmounts[Field.CURRENCY_B]?.toSignificant(3) +
|
||||||
|
' ' +
|
||||||
|
currencies[Field.CURRENCY_B]?.symbol,
|
||||||
|
})
|
||||||
|
|
||||||
|
setTxHash(response.hash)
|
||||||
|
|
||||||
|
ReactGA.event({
|
||||||
|
category: 'Liquidity',
|
||||||
|
action: 'Add',
|
||||||
|
label: [currencies[Field.CURRENCY_A]?.symbol, currencies[Field.CURRENCY_B]?.symbol].join('/'),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.catch((error) => {
|
||||||
|
setAttemptingTxn(false)
|
||||||
|
// we only care if the error is something _other_ than the user rejected the tx
|
||||||
|
if (error?.code !== 4001) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const modalHeader = () => {
|
||||||
|
return noLiquidity ? (
|
||||||
|
<AutoColumn gap="20px">
|
||||||
|
<LightCard mt="20px" borderRadius="20px">
|
||||||
|
<RowFlat>
|
||||||
|
<Text fontSize="48px" fontWeight={500} lineHeight="42px" marginRight={10}>
|
||||||
|
{currencies[Field.CURRENCY_A]?.symbol + '/' + currencies[Field.CURRENCY_B]?.symbol}
|
||||||
|
</Text>
|
||||||
|
<DoubleCurrencyLogo
|
||||||
|
currency0={currencies[Field.CURRENCY_A]}
|
||||||
|
currency1={currencies[Field.CURRENCY_B]}
|
||||||
|
size={30}
|
||||||
|
/>
|
||||||
|
</RowFlat>
|
||||||
|
</LightCard>
|
||||||
|
</AutoColumn>
|
||||||
|
) : (
|
||||||
|
<AutoColumn gap="20px">
|
||||||
|
<RowFlat style={{ marginTop: '20px' }}>
|
||||||
|
<Text fontSize="48px" fontWeight={500} lineHeight="42px" marginRight={10}>
|
||||||
|
{liquidityMinted?.toSignificant(6)}
|
||||||
|
</Text>
|
||||||
|
<DoubleCurrencyLogo
|
||||||
|
currency0={currencies[Field.CURRENCY_A]}
|
||||||
|
currency1={currencies[Field.CURRENCY_B]}
|
||||||
|
size={30}
|
||||||
|
/>
|
||||||
|
</RowFlat>
|
||||||
|
<Row>
|
||||||
|
<Text fontSize="24px">
|
||||||
|
{currencies[Field.CURRENCY_A]?.symbol + '/' + currencies[Field.CURRENCY_B]?.symbol + ' Pool Tokens'}
|
||||||
|
</Text>
|
||||||
|
</Row>
|
||||||
|
<TYPE.italic fontSize={12} textAlign="left" padding={'8px 0 0 0 '}>
|
||||||
|
{`Output is estimated. If the price changes by more than ${
|
||||||
|
allowedSlippage / 100
|
||||||
|
}% your transaction will revert.`}
|
||||||
|
</TYPE.italic>
|
||||||
|
</AutoColumn>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const modalBottom = () => {
|
||||||
|
return (
|
||||||
|
<ConfirmAddModalBottom
|
||||||
|
price={price}
|
||||||
|
currencies={currencies}
|
||||||
|
parsedAmounts={parsedAmounts}
|
||||||
|
noLiquidity={noLiquidity}
|
||||||
|
onAdd={onAdd}
|
||||||
|
poolTokenPercentage={poolTokenPercentage}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const pendingText = `Supplying ${parsedAmounts[Field.CURRENCY_A]?.toSignificant(6)} ${
|
||||||
|
currencies[Field.CURRENCY_A]?.symbol
|
||||||
|
} and ${parsedAmounts[Field.CURRENCY_B]?.toSignificant(6)} ${currencies[Field.CURRENCY_B]?.symbol}`
|
||||||
|
|
||||||
|
const handleCurrencyASelect = useCallback(
|
||||||
|
(currencyA: Currency) => {
|
||||||
|
const newCurrencyIdA = currencyId(currencyA)
|
||||||
|
if (newCurrencyIdA === currencyIdB) {
|
||||||
|
history.push(`/add/${currencyIdB}/${currencyIdA}`)
|
||||||
|
} else {
|
||||||
|
history.push(`/add/${newCurrencyIdA}/${currencyIdB}`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[currencyIdB, history, currencyIdA]
|
||||||
|
)
|
||||||
|
const handleCurrencyBSelect = useCallback(
|
||||||
|
(currencyB: Currency) => {
|
||||||
|
const newCurrencyIdB = currencyId(currencyB)
|
||||||
|
if (currencyIdA === newCurrencyIdB) {
|
||||||
|
if (currencyIdB) {
|
||||||
|
history.push(`/add/${currencyIdB}/${newCurrencyIdB}`)
|
||||||
|
} else {
|
||||||
|
history.push(`/add/${newCurrencyIdB}`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
history.push(`/add/${currencyIdA ? currencyIdA : 'ETH'}/${newCurrencyIdB}`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[currencyIdA, history, currencyIdB]
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleDismissConfirmation = useCallback(() => {
|
||||||
|
setShowConfirm(false)
|
||||||
|
// if there was a tx hash, we want to clear the input
|
||||||
|
if (txHash) {
|
||||||
|
onFieldAInput('')
|
||||||
|
}
|
||||||
|
setTxHash('')
|
||||||
|
}, [onFieldAInput, txHash])
|
||||||
|
|
||||||
|
const isCreate = history.location.pathname.includes('/create')
|
||||||
|
|
||||||
|
const addIsUnsupported = useIsTransactionUnsupported(currencies?.CURRENCY_A, currencies?.CURRENCY_B)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AppBody>
|
||||||
|
<AddRemoveTabs creating={isCreate} adding={true} />
|
||||||
|
<Wrapper>
|
||||||
|
<TransactionConfirmationModal
|
||||||
|
isOpen={showConfirm}
|
||||||
|
onDismiss={handleDismissConfirmation}
|
||||||
|
attemptingTxn={attemptingTxn}
|
||||||
|
hash={txHash}
|
||||||
|
content={() => (
|
||||||
|
<ConfirmationModalContent
|
||||||
|
title={noLiquidity ? 'You are creating a pool' : 'You will receive'}
|
||||||
|
onDismiss={handleDismissConfirmation}
|
||||||
|
topContent={modalHeader}
|
||||||
|
bottomContent={modalBottom}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
pendingText={pendingText}
|
||||||
|
currencyToAdd={pair?.liquidityToken}
|
||||||
|
/>
|
||||||
|
<AutoColumn gap="20px">
|
||||||
|
{noLiquidity ||
|
||||||
|
(isCreate ? (
|
||||||
|
<ColumnCenter>
|
||||||
|
<BlueCard>
|
||||||
|
<AutoColumn gap="10px">
|
||||||
|
<TYPE.link fontWeight={600} color={'primaryText1'}>
|
||||||
|
You are the first liquidity provider.
|
||||||
|
</TYPE.link>
|
||||||
|
<TYPE.link fontWeight={400} color={'primaryText1'}>
|
||||||
|
The ratio of tokens you add will set the price of this pool.
|
||||||
|
</TYPE.link>
|
||||||
|
<TYPE.link fontWeight={400} color={'primaryText1'}>
|
||||||
|
Once you are happy with the rate click supply to review.
|
||||||
|
</TYPE.link>
|
||||||
|
</AutoColumn>
|
||||||
|
</BlueCard>
|
||||||
|
</ColumnCenter>
|
||||||
|
) : (
|
||||||
|
<ColumnCenter>
|
||||||
|
<BlueCard>
|
||||||
|
<AutoColumn gap="10px">
|
||||||
|
<TYPE.link fontWeight={400} color={'primaryText1'}>
|
||||||
|
<b>Tip:</b> When you add liquidity, you will receive pool tokens representing your position.
|
||||||
|
These tokens automatically earn fees proportional to your share of the pool, and can be redeemed
|
||||||
|
at any time.
|
||||||
|
</TYPE.link>
|
||||||
|
</AutoColumn>
|
||||||
|
</BlueCard>
|
||||||
|
</ColumnCenter>
|
||||||
|
))}
|
||||||
|
<CurrencyInputPanel
|
||||||
|
value={formattedAmounts[Field.CURRENCY_A]}
|
||||||
|
onUserInput={onFieldAInput}
|
||||||
|
onMax={() => {
|
||||||
|
onFieldAInput(maxAmounts[Field.CURRENCY_A]?.toExact() ?? '')
|
||||||
|
}}
|
||||||
|
onCurrencySelect={handleCurrencyASelect}
|
||||||
|
showMaxButton={!atMaxAmounts[Field.CURRENCY_A]}
|
||||||
|
currency={currencies[Field.CURRENCY_A]}
|
||||||
|
id="add-liquidity-input-tokena"
|
||||||
|
showCommonBases
|
||||||
|
/>
|
||||||
|
<ColumnCenter>
|
||||||
|
<Plus size="16" color={theme.text2} />
|
||||||
|
</ColumnCenter>
|
||||||
|
<CurrencyInputPanel
|
||||||
|
value={formattedAmounts[Field.CURRENCY_B]}
|
||||||
|
onUserInput={onFieldBInput}
|
||||||
|
onCurrencySelect={handleCurrencyBSelect}
|
||||||
|
onMax={() => {
|
||||||
|
onFieldBInput(maxAmounts[Field.CURRENCY_B]?.toExact() ?? '')
|
||||||
|
}}
|
||||||
|
showMaxButton={!atMaxAmounts[Field.CURRENCY_B]}
|
||||||
|
currency={currencies[Field.CURRENCY_B]}
|
||||||
|
id="add-liquidity-input-tokenb"
|
||||||
|
showCommonBases
|
||||||
|
/>
|
||||||
|
{currencies[Field.CURRENCY_A] && currencies[Field.CURRENCY_B] && pairState !== PairState.INVALID && (
|
||||||
|
<>
|
||||||
|
<LightCard padding="0px" borderRadius={'20px'}>
|
||||||
|
<RowBetween padding="1rem">
|
||||||
|
<TYPE.subHeader fontWeight={500} fontSize={14}>
|
||||||
|
{noLiquidity ? 'Initial prices' : 'Prices'} and pool share
|
||||||
|
</TYPE.subHeader>
|
||||||
|
</RowBetween>{' '}
|
||||||
|
<LightCard padding="1rem" borderRadius={'20px'}>
|
||||||
|
<PoolPriceBar
|
||||||
|
currencies={currencies}
|
||||||
|
poolTokenPercentage={poolTokenPercentage}
|
||||||
|
noLiquidity={noLiquidity}
|
||||||
|
price={price}
|
||||||
|
/>
|
||||||
|
</LightCard>
|
||||||
|
</LightCard>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{addIsUnsupported ? (
|
||||||
|
<ButtonPrimary disabled={true}>
|
||||||
|
<TYPE.main mb="4px">Unsupported Asset</TYPE.main>
|
||||||
|
</ButtonPrimary>
|
||||||
|
) : !account ? (
|
||||||
|
<ButtonLight onClick={toggleWalletModal}>Connect Wallet</ButtonLight>
|
||||||
|
) : (
|
||||||
|
<AutoColumn gap={'md'}>
|
||||||
|
{(approvalA === ApprovalState.NOT_APPROVED ||
|
||||||
|
approvalA === ApprovalState.PENDING ||
|
||||||
|
approvalB === ApprovalState.NOT_APPROVED ||
|
||||||
|
approvalB === ApprovalState.PENDING) &&
|
||||||
|
isValid && (
|
||||||
|
<RowBetween>
|
||||||
|
{approvalA !== ApprovalState.APPROVED && (
|
||||||
|
<ButtonPrimary
|
||||||
|
onClick={approveACallback}
|
||||||
|
disabled={approvalA === ApprovalState.PENDING}
|
||||||
|
width={approvalB !== ApprovalState.APPROVED ? '48%' : '100%'}
|
||||||
|
>
|
||||||
|
{approvalA === ApprovalState.PENDING ? (
|
||||||
|
<Dots>Approving {currencies[Field.CURRENCY_A]?.symbol}</Dots>
|
||||||
|
) : (
|
||||||
|
'Approve ' + currencies[Field.CURRENCY_A]?.symbol
|
||||||
|
)}
|
||||||
|
</ButtonPrimary>
|
||||||
|
)}
|
||||||
|
{approvalB !== ApprovalState.APPROVED && (
|
||||||
|
<ButtonPrimary
|
||||||
|
onClick={approveBCallback}
|
||||||
|
disabled={approvalB === ApprovalState.PENDING}
|
||||||
|
width={approvalA !== ApprovalState.APPROVED ? '48%' : '100%'}
|
||||||
|
>
|
||||||
|
{approvalB === ApprovalState.PENDING ? (
|
||||||
|
<Dots>Approving {currencies[Field.CURRENCY_B]?.symbol}</Dots>
|
||||||
|
) : (
|
||||||
|
'Approve ' + currencies[Field.CURRENCY_B]?.symbol
|
||||||
|
)}
|
||||||
|
</ButtonPrimary>
|
||||||
|
)}
|
||||||
|
</RowBetween>
|
||||||
|
)}
|
||||||
|
<ButtonError
|
||||||
|
onClick={() => {
|
||||||
|
expertMode ? onAdd() : setShowConfirm(true)
|
||||||
|
}}
|
||||||
|
disabled={!isValid || approvalA !== ApprovalState.APPROVED || approvalB !== ApprovalState.APPROVED}
|
||||||
|
error={!isValid && !!parsedAmounts[Field.CURRENCY_A] && !!parsedAmounts[Field.CURRENCY_B]}
|
||||||
|
>
|
||||||
|
<Text fontSize={20} fontWeight={500}>
|
||||||
|
{error ?? 'Supply'}
|
||||||
|
</Text>
|
||||||
|
</ButtonError>
|
||||||
|
</AutoColumn>
|
||||||
|
)}
|
||||||
|
</AutoColumn>
|
||||||
|
</Wrapper>
|
||||||
|
</AppBody>
|
||||||
|
{!addIsUnsupported ? (
|
||||||
|
pair && !noLiquidity && pairState !== PairState.INVALID ? (
|
||||||
|
<AutoColumn style={{ minWidth: '20rem', width: '100%', maxWidth: '400px', marginTop: '1rem' }}>
|
||||||
|
<MinimalPositionCard showUnwrapped={oneCurrencyIsWETH} pair={pair} />
|
||||||
|
</AutoColumn>
|
||||||
|
) : null
|
||||||
|
) : (
|
||||||
|
<UnsupportedCurrencyFooter
|
||||||
|
show={addIsUnsupported}
|
||||||
|
currencies={[currencies.CURRENCY_A, currencies.CURRENCY_B]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
20
src/pages/AddLiquidityV2/redirects.tsx
Normal file
20
src/pages/AddLiquidityV2/redirects.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Redirect, RouteComponentProps } from 'react-router-dom'
|
||||||
|
import AddLiquidityV2 from './index'
|
||||||
|
|
||||||
|
export function RedirectToAddLiquidityV2() {
|
||||||
|
return <Redirect to="/add/v2/ETH" />
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RedirectDuplicateTokenIdsV2(props: RouteComponentProps<{ currencyIdA: string; currencyIdB: string }>) {
|
||||||
|
const {
|
||||||
|
match: {
|
||||||
|
params: { currencyIdA, currencyIdB },
|
||||||
|
},
|
||||||
|
} = props
|
||||||
|
|
||||||
|
if (currencyIdA.toLowerCase() === currencyIdB.toLowerCase()) {
|
||||||
|
return <Redirect to={`/add/V2/${currencyIdA}`} />
|
||||||
|
}
|
||||||
|
return <AddLiquidityV2 {...props} />
|
||||||
|
}
|
@ -11,12 +11,7 @@ import Web3ReactManager from '../components/Web3ReactManager'
|
|||||||
import { ApplicationModal } from '../state/application/actions'
|
import { ApplicationModal } from '../state/application/actions'
|
||||||
import { useModalOpen, useToggleModal } from '../state/application/hooks'
|
import { useModalOpen, useToggleModal } from '../state/application/hooks'
|
||||||
import DarkModeQueryParamReader from '../theme/DarkModeQueryParamReader'
|
import DarkModeQueryParamReader from '../theme/DarkModeQueryParamReader'
|
||||||
import AddLiquidity from './AddLiquidity'
|
import { RedirectDuplicateTokenIds } from './AddLiquidity/redirects'
|
||||||
import {
|
|
||||||
RedirectDuplicateTokenIds,
|
|
||||||
RedirectOldAddLiquidityPathStructure,
|
|
||||||
RedirectToAddLiquidity,
|
|
||||||
} from './AddLiquidity/redirects'
|
|
||||||
import Earn from './Earn'
|
import Earn from './Earn'
|
||||||
import Manage from './Earn/Manage'
|
import Manage from './Earn/Manage'
|
||||||
import MigrateV1 from './MigrateV1'
|
import MigrateV1 from './MigrateV1'
|
||||||
@ -34,12 +29,15 @@ import Swap from './Swap'
|
|||||||
import { OpenClaimAddressModalAndRedirectToSwap, RedirectPathToSwapOnly, RedirectToSwap } from './Swap/redirects'
|
import { OpenClaimAddressModalAndRedirectToSwap, RedirectPathToSwapOnly, RedirectToSwap } from './Swap/redirects'
|
||||||
import Vote from './Vote'
|
import Vote from './Vote'
|
||||||
import VotePage from './Vote/VotePage'
|
import VotePage from './Vote/VotePage'
|
||||||
|
import { RedirectDuplicateTokenIdsV2 } from './AddLiquidityV2/redirects'
|
||||||
|
import AddLiquidity from './AddLiquidity'
|
||||||
|
import AddLiquidityV2 from './AddLiquidityV2'
|
||||||
|
|
||||||
const AppWrapper = styled.div`
|
const AppWrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
overflow-x: hidden;
|
/* overflow-x: hidden; */
|
||||||
`
|
`
|
||||||
|
|
||||||
const BodyWrapper = styled.div`
|
const BodyWrapper = styled.div`
|
||||||
@ -49,8 +47,8 @@ const BodyWrapper = styled.div`
|
|||||||
padding-top: 160px;
|
padding-top: 160px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
/* overflow-y: auto; */
|
||||||
overflow-x: hidden;
|
/* overflow-x: hidden; */
|
||||||
|
|
||||||
${({ theme }) => theme.mediaWidth.upToSmall`
|
${({ theme }) => theme.mediaWidth.upToSmall`
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
@ -108,13 +106,20 @@ export default function App() {
|
|||||||
<Route exact strict path="/find" component={PoolFinder} />
|
<Route exact strict path="/find" component={PoolFinder} />
|
||||||
<Route exact strict path="/pool/v2" component={PoolV2} />
|
<Route exact strict path="/pool/v2" component={PoolV2} />
|
||||||
<Route exact strict path="/pool" component={Pool} />
|
<Route exact strict path="/pool" component={Pool} />
|
||||||
<Route exact strict path="/create" component={RedirectToAddLiquidity} />
|
|
||||||
<Route exact path="/add" component={AddLiquidity} />
|
<Route exact path="/add" component={AddLiquidity} />
|
||||||
<Route exact path="/add/:currencyIdA" component={RedirectOldAddLiquidityPathStructure} />
|
<Route exact path="/add/v2/" component={AddLiquidityV2} />
|
||||||
<Route exact path="/add/:currencyIdA/:currencyIdB" component={RedirectDuplicateTokenIds} />
|
<Route exact path="/add/v2/:currencyIdA" component={AddLiquidityV2} />
|
||||||
<Route exact path="/create" component={AddLiquidity} />
|
|
||||||
<Route exact path="/create/:currencyIdA" component={RedirectOldAddLiquidityPathStructure} />
|
<Route exact path="/add/:currencyIdA" component={AddLiquidity} />
|
||||||
<Route exact path="/create/:currencyIdA/:currencyIdB" component={RedirectDuplicateTokenIds} />
|
<Route exact strict path="/add/v2/:currencyIdA?/:currencyIdB?" component={RedirectDuplicateTokenIdsV2} />
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
strict
|
||||||
|
path="/add/:currencyIdA?/:currencyIdB?/:feeAmount?"
|
||||||
|
component={RedirectDuplicateTokenIds}
|
||||||
|
/>
|
||||||
|
|
||||||
<Route exact strict path="/remove/v1/:address" component={RemoveV1Exchange} />
|
<Route exact strict path="/remove/v1/:address" component={RemoveV1Exchange} />
|
||||||
<Route exact strict path="/remove/v2/:tokens" component={RedirectOldRemoveLiquidityPathStructure} />
|
<Route exact strict path="/remove/v2/:tokens" component={RedirectOldRemoveLiquidityPathStructure} />
|
||||||
<Route exact strict path="/remove/v2/:currencyIdA/:currencyIdB" component={RemoveLiquidity} />
|
<Route exact strict path="/remove/v2/:currencyIdA/:currencyIdB" component={RemoveLiquidity} />
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
export const BodyWrapper = styled.div`
|
export const BodyWrapper = styled.div<{ margin?: string }>`
|
||||||
position: relative;
|
position: relative;
|
||||||
|
margin-top: ${({ margin }) => margin ?? '0px'};
|
||||||
max-width: 460px;
|
max-width: 460px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: ${({ theme }) => theme.bg0};
|
background: ${({ theme }) => theme.bg0};
|
||||||
@ -14,6 +15,6 @@ export const BodyWrapper = styled.div`
|
|||||||
/**
|
/**
|
||||||
* The styled container element that wraps the content of most pages and the tabs.
|
* The styled container element that wraps the content of most pages and the tabs.
|
||||||
*/
|
*/
|
||||||
export default function AppBody({ children }: { children: React.ReactNode }) {
|
export default function AppBody({ children, ...rest }: { children: React.ReactNode }) {
|
||||||
return <BodyWrapper>{children}</BodyWrapper>
|
return <BodyWrapper {...rest}>{children}</BodyWrapper>
|
||||||
}
|
}
|
||||||
|
@ -26,9 +26,9 @@ import { CountUp } from 'use-count-up'
|
|||||||
import { wrappedCurrency } from '../../utils/wrappedCurrency'
|
import { wrappedCurrency } from '../../utils/wrappedCurrency'
|
||||||
import { currencyId } from '../../utils/currencyId'
|
import { currencyId } from '../../utils/currencyId'
|
||||||
import { useTotalSupply } from '../../data/TotalSupply'
|
import { useTotalSupply } from '../../data/TotalSupply'
|
||||||
import { usePair } from '../../data/Reserves'
|
import { usePair } from '../../data/V2'
|
||||||
import usePrevious from '../../hooks/usePrevious'
|
import usePrevious from '../../hooks/usePrevious'
|
||||||
import useUSDCPrice from '../../utils/useUSDCPrice'
|
import useUSDCPrice from '../../hooks/useUSDCPrice'
|
||||||
import { BIG_INT_ZERO, BIG_INT_SECONDS_IN_WEEK } from '../../constants'
|
import { BIG_INT_ZERO, BIG_INT_SECONDS_IN_WEEK } from '../../constants'
|
||||||
|
|
||||||
const PageWrapper = styled(AutoColumn)`
|
const PageWrapper = styled(AutoColumn)`
|
||||||
|
@ -14,13 +14,12 @@ import FormattedCurrencyAmount from '../../components/FormattedCurrencyAmount'
|
|||||||
import QuestionHelper from '../../components/QuestionHelper'
|
import QuestionHelper from '../../components/QuestionHelper'
|
||||||
import { AutoRow, RowBetween, RowFixed } from '../../components/Row'
|
import { AutoRow, RowBetween, RowFixed } from '../../components/Row'
|
||||||
import { Dots } from '../../components/swap/styleds'
|
import { Dots } from '../../components/swap/styleds'
|
||||||
import { DEFAULT_DEADLINE_FROM_NOW, INITIAL_ALLOWED_SLIPPAGE, V1_MIGRATOR_ADDRESS } from 'constants/index'
|
import { DEFAULT_DEADLINE_FROM_NOW, INITIAL_ALLOWED_SLIPPAGE, V1_MIGRATOR_ADDRESS } from '../../constants'
|
||||||
import { PairState, usePair } from '../../data/Reserves'
|
import { PairState, usePair } from '../../data/V2'
|
||||||
import { useTotalSupply } from '../../data/TotalSupply'
|
import { useTotalSupply } from '../../data/TotalSupply'
|
||||||
import { useActiveWeb3React } from '../../hooks'
|
import { useActiveWeb3React } from '../../hooks'
|
||||||
import { useToken } from '../../hooks/Tokens'
|
import { useToken } from '../../hooks/Tokens'
|
||||||
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
|
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
|
||||||
import { useV1ExchangeContract, useV1MigratorContract } from '../../hooks/useContract'
|
|
||||||
import { NEVER_RELOAD, useSingleCallResult } from '../../state/multicall/hooks'
|
import { NEVER_RELOAD, useSingleCallResult } from '../../state/multicall/hooks'
|
||||||
import { useIsTransactionPending, useTransactionAdder } from '../../state/transactions/hooks'
|
import { useIsTransactionPending, useTransactionAdder } from '../../state/transactions/hooks'
|
||||||
import { useETHBalances, useTokenBalance } from '../../state/wallet/hooks'
|
import { useETHBalances, useTokenBalance } from '../../state/wallet/hooks'
|
||||||
@ -28,6 +27,7 @@ import { BackArrow, ExternalLink, TYPE } from '../../theme'
|
|||||||
import { getEtherscanLink, isAddress } from '../../utils'
|
import { getEtherscanLink, isAddress } from '../../utils'
|
||||||
import { BodyWrapper } from '../AppBody'
|
import { BodyWrapper } from '../AppBody'
|
||||||
import { EmptyState } from './EmptyState'
|
import { EmptyState } from './EmptyState'
|
||||||
|
import { useV1ExchangeContract, useV1MigratorContract } from 'hooks/useContract'
|
||||||
|
|
||||||
const WEI_DENOM = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18))
|
const WEI_DENOM = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18))
|
||||||
const ZERO = JSBI.BigInt(0)
|
const ZERO = JSBI.BigInt(0)
|
||||||
|
@ -1,43 +1,32 @@
|
|||||||
import { TransactionResponse } from '@ethersproject/abstract-provider'
|
import React, { useMemo } from 'react'
|
||||||
import { AddressZero } from '@ethersproject/constants'
|
import { Currency, CurrencyAmount, Price, Token, TokenAmount, WETH9 } from '@uniswap/sdk-core'
|
||||||
import { Currency, CurrencyAmount, Fraction, Percent, Price, Token, TokenAmount, WETH9 } from '@uniswap/sdk-core'
|
|
||||||
import { JSBI } from '@uniswap/v2-sdk'
|
import { JSBI } from '@uniswap/v2-sdk'
|
||||||
import React, { useCallback, useMemo, useState } from 'react'
|
|
||||||
import ReactGA from 'react-ga'
|
|
||||||
import { Redirect, RouteComponentProps } from 'react-router'
|
import { Redirect, RouteComponentProps } from 'react-router'
|
||||||
import { Text } from 'rebass'
|
import { Text } from 'rebass'
|
||||||
import { ButtonConfirmed } from '../../components/Button'
|
|
||||||
import { LightCard, PinkCard, YellowCard } from '../../components/Card'
|
|
||||||
import { AutoColumn } from '../../components/Column'
|
import { AutoColumn } from '../../components/Column'
|
||||||
import CurrencyLogo from '../../components/CurrencyLogo'
|
import CurrencyLogo from '../../components/CurrencyLogo'
|
||||||
import FormattedCurrencyAmount from '../../components/FormattedCurrencyAmount'
|
import FormattedCurrencyAmount from '../../components/FormattedCurrencyAmount'
|
||||||
import QuestionHelper from '../../components/QuestionHelper'
|
import QuestionHelper from '../../components/QuestionHelper'
|
||||||
import { AutoRow, RowBetween, RowFixed } from '../../components/Row'
|
import { AutoRow, RowBetween, RowFixed } from '../../components/Row'
|
||||||
import { Dots } from '../../components/swap/styleds'
|
|
||||||
import { DEFAULT_DEADLINE_FROM_NOW, INITIAL_ALLOWED_SLIPPAGE, V1_MIGRATOR_ADDRESS } from 'constants/index'
|
|
||||||
import { PairState, usePair } from '../../data/Reserves'
|
|
||||||
import { useTotalSupply } from '../../data/TotalSupply'
|
import { useTotalSupply } from '../../data/TotalSupply'
|
||||||
import { useActiveWeb3React } from '../../hooks'
|
import { useActiveWeb3React } from '../../hooks'
|
||||||
import { useToken } from '../../hooks/Tokens'
|
import { useToken } from '../../hooks/Tokens'
|
||||||
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
|
import { usePairContract } from '../../hooks/useContract'
|
||||||
import { usePairContract, useV1MigratorContract } from '../../hooks/useContract'
|
|
||||||
import { NEVER_RELOAD, useSingleCallResult } from '../../state/multicall/hooks'
|
import { NEVER_RELOAD, useSingleCallResult } from '../../state/multicall/hooks'
|
||||||
import { useIsTransactionPending, useTransactionAdder } from '../../state/transactions/hooks'
|
import { useTokenBalance } from '../../state/wallet/hooks'
|
||||||
import { useETHBalances, useTokenBalance } from '../../state/wallet/hooks'
|
|
||||||
import { BackArrow, ExternalLink, TYPE } from '../../theme'
|
import { BackArrow, ExternalLink, TYPE } from '../../theme'
|
||||||
import { getEtherscanLink, isAddress } from '../../utils'
|
import { getEtherscanLink, isAddress } from '../../utils'
|
||||||
import { BodyWrapper } from '../AppBody'
|
import { BodyWrapper } from '../AppBody'
|
||||||
import { EmptyState } from '../MigrateV1/EmptyState'
|
import { EmptyState } from '../MigrateV1/EmptyState'
|
||||||
import { toV2LiquidityToken } from 'state/user/hooks'
|
|
||||||
import { V2_MIGRATOR_ADDRESSES } from 'constants/v3'
|
import { V2_MIGRATOR_ADDRESSES } from 'constants/v3'
|
||||||
|
|
||||||
// TODO the whole file
|
// TODO the whole file
|
||||||
|
|
||||||
const WEI_DENOM = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18))
|
// const WEI_DENOM = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18))
|
||||||
const ZERO = JSBI.BigInt(0)
|
// const ZERO = JSBI.BigInt(0)
|
||||||
const ONE = JSBI.BigInt(1)
|
// const ONE = JSBI.BigInt(1)
|
||||||
const ZERO_FRACTION = new Fraction(ZERO, ONE)
|
// const ZERO_FRACTION = new Fraction(ZERO, ONE)
|
||||||
const ALLOWED_OUTPUT_MIN_PERCENT = new Percent(JSBI.BigInt(10000 - INITIAL_ALLOWED_SLIPPAGE), JSBI.BigInt(10000))
|
// const ALLOWED_OUTPUT_MIN_PERCENT = new Percent(JSBI.BigInt(10000 - INITIAL_ALLOWED_SLIPPAGE), JSBI.BigInt(10000))
|
||||||
|
|
||||||
export function V2LiquidityInfo({
|
export function V2LiquidityInfo({
|
||||||
token,
|
token,
|
||||||
@ -105,7 +94,7 @@ function V2PairMigration({
|
|||||||
token0: Token
|
token0: Token
|
||||||
token1: Token
|
token1: Token
|
||||||
}) {
|
}) {
|
||||||
const { account, chainId } = useActiveWeb3React()
|
const { chainId } = useActiveWeb3React()
|
||||||
|
|
||||||
// this is just getLiquidityValue with the fee off, but for the passed pair
|
// this is just getLiquidityValue with the fee off, but for the passed pair
|
||||||
const token0Value = useMemo(
|
const token0Value = useMemo(
|
||||||
@ -119,6 +108,8 @@ function V2PairMigration({
|
|||||||
|
|
||||||
const v2SpotPrice = new Price(token0, token1, reserve0.raw, reserve1.raw)
|
const v2SpotPrice = new Price(token0, token1, reserve0.raw, reserve1.raw)
|
||||||
|
|
||||||
|
console.log(token0Value, token1Value, v2SpotPrice)
|
||||||
|
|
||||||
// const isFirstLiquidityProvider: boolean = false // check for v3 pair existence
|
// const isFirstLiquidityProvider: boolean = false // check for v3 pair existence
|
||||||
|
|
||||||
// const [confirmingMigration, setConfirmingMigration] = useState<boolean>(false)
|
// const [confirmingMigration, setConfirmingMigration] = useState<boolean>(false)
|
||||||
|
@ -13,8 +13,8 @@ import { EmptyState } from '../MigrateV1/EmptyState'
|
|||||||
import QuestionHelper from '../../components/QuestionHelper'
|
import QuestionHelper from '../../components/QuestionHelper'
|
||||||
import { Dots } from '../../components/swap/styleds'
|
import { Dots } from '../../components/swap/styleds'
|
||||||
import { toV2LiquidityToken, useTrackedTokenPairs } from '../../state/user/hooks'
|
import { toV2LiquidityToken, useTrackedTokenPairs } from '../../state/user/hooks'
|
||||||
import { usePairs } from 'data/Reserves'
|
|
||||||
import MigrateV2PositionCard from 'components/PositionCard/V2'
|
import MigrateV2PositionCard from 'components/PositionCard/V2'
|
||||||
|
import { usePairs } from 'data/V2'
|
||||||
|
|
||||||
// TODO there's a bug in loading where "No V2 Liquidity found" flashes
|
// TODO there's a bug in loading where "No V2 Liquidity found" flashes
|
||||||
// TODO add support for more pairs
|
// TODO add support for more pairs
|
||||||
|
@ -15,7 +15,7 @@ import { ButtonPrimary, ButtonSecondary } from '../../components/Button'
|
|||||||
import { AutoColumn } from '../../components/Column'
|
import { AutoColumn } from '../../components/Column'
|
||||||
|
|
||||||
import { useActiveWeb3React } from '../../hooks'
|
import { useActiveWeb3React } from '../../hooks'
|
||||||
import { usePairs } from '../../data/Reserves'
|
import { usePairs } from '../../data/V2'
|
||||||
import { toV2LiquidityToken, useTrackedTokenPairs } from '../../state/user/hooks'
|
import { toV2LiquidityToken, useTrackedTokenPairs } from '../../state/user/hooks'
|
||||||
import { Dots } from '../../components/swap/styleds'
|
import { Dots } from '../../components/swap/styleds'
|
||||||
import { CardSection, DataCard, CardNoise, CardBGImage } from '../../components/earn/styled'
|
import { CardSection, DataCard, CardNoise, CardBGImage } from '../../components/earn/styled'
|
||||||
|
@ -11,7 +11,7 @@ import { FindPoolTabs } from '../../components/NavigationTabs'
|
|||||||
import { MinimalPositionCard } from '../../components/PositionCard'
|
import { MinimalPositionCard } from '../../components/PositionCard'
|
||||||
import Row from '../../components/Row'
|
import Row from '../../components/Row'
|
||||||
import CurrencySearchModal from '../../components/SearchModal/CurrencySearchModal'
|
import CurrencySearchModal from '../../components/SearchModal/CurrencySearchModal'
|
||||||
import { PairState, usePair } from '../../data/Reserves'
|
import { PairState, usePair } from '../../data/V2'
|
||||||
import { useActiveWeb3React } from '../../hooks'
|
import { useActiveWeb3React } from '../../hooks'
|
||||||
import { usePairAdder } from '../../state/user/hooks'
|
import { usePairAdder } from '../../state/user/hooks'
|
||||||
import { useTokenBalance } from '../../state/wallet/hooks'
|
import { useTokenBalance } from '../../state/wallet/hooks'
|
||||||
|
@ -8,5 +8,6 @@ export default function RemoveLiquidityV3({
|
|||||||
params: { currencyIdA, currencyIdB, fee },
|
params: { currencyIdA, currencyIdB, fee },
|
||||||
},
|
},
|
||||||
}: RouteComponentProps<{ currencyIdA: string; currencyIdB: string; fee: string }>) {
|
}: RouteComponentProps<{ currencyIdA: string; currencyIdB: string; fee: string }>) {
|
||||||
|
console.log(currencyIdA, currencyIdB, fee)
|
||||||
return <AppBody>TODO</AppBody>
|
return <AppBody>TODO</AppBody>
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ import { useTransactionAdder } from '../../state/transactions/hooks'
|
|||||||
import { StyledInternalLink, TYPE } from '../../theme'
|
import { StyledInternalLink, TYPE } from '../../theme'
|
||||||
import { calculateGasMargin, calculateSlippageAmount, getRouterContract } from '../../utils'
|
import { calculateGasMargin, calculateSlippageAmount, getRouterContract } from '../../utils'
|
||||||
import { currencyId } from '../../utils/currencyId'
|
import { currencyId } from '../../utils/currencyId'
|
||||||
import useDebouncedChangeHandler from '../../utils/useDebouncedChangeHandler'
|
import useDebouncedChangeHandler from '../../hooks/useDebouncedChangeHandler'
|
||||||
import { wrappedCurrency } from '../../utils/wrappedCurrency'
|
import { wrappedCurrency } from '../../utils/wrappedCurrency'
|
||||||
import AppBody from '../AppBody'
|
import AppBody from '../AppBody'
|
||||||
import { ClickableText, MaxButton, Wrapper } from '../Pool/styleds'
|
import { ClickableText, MaxButton, Wrapper } from '../Pool/styleds'
|
||||||
|
6
src/pages/styled.tsx
Normal file
6
src/pages/styled.tsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
export const StandardPageWrapper = styled.div`
|
||||||
|
padding-top: 160px;
|
||||||
|
width: 100%;
|
||||||
|
`
|
@ -2,7 +2,7 @@ import { Currency, CurrencyAmount, Percent, TokenAmount } from '@uniswap/sdk-cor
|
|||||||
import { JSBI, Pair } from '@uniswap/v2-sdk'
|
import { JSBI, Pair } from '@uniswap/v2-sdk'
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
import { usePair } from '../../data/Reserves'
|
import { usePair } from '../../data/V2'
|
||||||
import { useTotalSupply } from '../../data/TotalSupply'
|
import { useTotalSupply } from '../../data/TotalSupply'
|
||||||
|
|
||||||
import { useActiveWeb3React } from '../../hooks'
|
import { useActiveWeb3React } from '../../hooks'
|
||||||
|
@ -42,7 +42,6 @@ const EMPTY_LIST: TokenAddressMap = {
|
|||||||
[ChainId.ROPSTEN]: {},
|
[ChainId.ROPSTEN]: {},
|
||||||
[ChainId.GÖRLI]: {},
|
[ChainId.GÖRLI]: {},
|
||||||
[ChainId.MAINNET]: {},
|
[ChainId.MAINNET]: {},
|
||||||
[1337]: {},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const listCache: WeakMap<TokenList, TokenAddressMap> | null =
|
const listCache: WeakMap<TokenList, TokenAddressMap> | null =
|
||||||
@ -99,7 +98,6 @@ function combineMaps(map1: TokenAddressMap, map2: TokenAddressMap): TokenAddress
|
|||||||
4: { ...map1[4], ...map2[4] },
|
4: { ...map1[4], ...map2[4] },
|
||||||
5: { ...map1[5], ...map2[5] },
|
5: { ...map1[5], ...map2[5] },
|
||||||
42: { ...map1[42], ...map2[42] },
|
42: { ...map1[42], ...map2[42] },
|
||||||
1337: { ...map1[1337], ...map2[1337] },
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,11 +6,11 @@ export enum Field {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum Bound {
|
export enum Bound {
|
||||||
CURRENT = 'CURRENT',
|
|
||||||
LOWER = 'LOWER',
|
LOWER = 'LOWER',
|
||||||
UPPER = 'UPPER',
|
UPPER = 'UPPER',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// save for % inputs
|
||||||
export enum RangeType {
|
export enum RangeType {
|
||||||
PERCENT = 'PERCENT',
|
PERCENT = 'PERCENT',
|
||||||
RATE = 'RATE',
|
RATE = 'RATE',
|
||||||
@ -19,5 +19,5 @@ export enum RangeType {
|
|||||||
export const typeInput = createAction<{ field: Field; typedValue: string; noLiquidity: boolean }>('mint/typeInputMint')
|
export const typeInput = createAction<{ field: Field; typedValue: string; noLiquidity: boolean }>('mint/typeInputMint')
|
||||||
export const typeLowerRangeInput = createAction<{ typedValue: string }>('mint/typeLowerRangeInput')
|
export const typeLowerRangeInput = createAction<{ typedValue: string }>('mint/typeLowerRangeInput')
|
||||||
export const typeUpperRangeInput = createAction<{ typedValue: string }>('mint/typeUpperRangeInput')
|
export const typeUpperRangeInput = createAction<{ typedValue: string }>('mint/typeUpperRangeInput')
|
||||||
export const updateRangeType = createAction<{ rangeType: RangeType }>('mint/updateRangeType')
|
export const typeStartPriceInput = createAction<{ typedValue: string }>('mint/typeStartPriceInput')
|
||||||
export const resetMintState = createAction<void>('mint/resetMintState')
|
export const resetMintState = createAction<void>('mint/resetMintState')
|
||||||
|
@ -1,27 +1,19 @@
|
|||||||
import { Currency, CurrencyAmount, ETHER, Percent, Price, TokenAmount } from '@uniswap/sdk-core'
|
import { BIG_INT_ZERO } from './../../constants/index'
|
||||||
import { JSBI, Pair } from '@uniswap/v2-sdk'
|
import { getTickToPrice } from 'utils/getTickToPrice'
|
||||||
|
import JSBI from 'jsbi'
|
||||||
|
import { PoolState } from './../../data/Pools'
|
||||||
|
import { Pool, FeeAmount, Position, priceToClosestTick, TickMath } from '@uniswap/v3-sdk/dist/'
|
||||||
|
import { Currency, CurrencyAmount, ETHER, Price } from '@uniswap/sdk-core'
|
||||||
import { useCallback, useMemo } from 'react'
|
import { useCallback, useMemo } from 'react'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useDispatch, useSelector } from 'react-redux'
|
||||||
import { PairState, usePair } from '../../data/Reserves'
|
|
||||||
import { useTotalSupply } from '../../data/TotalSupply'
|
|
||||||
|
|
||||||
import { useActiveWeb3React } from '../../hooks'
|
import { useActiveWeb3React } from '../../hooks'
|
||||||
import { wrappedCurrency, wrappedCurrencyAmount } from '../../utils/wrappedCurrency'
|
import { wrappedCurrency, wrappedCurrencyAmount } from '../../utils/wrappedCurrency'
|
||||||
import { AppDispatch, AppState } from '../index'
|
import { AppDispatch, AppState } from '../index'
|
||||||
import { tryParseAmount } from '../swap/hooks'
|
import { tryParseAmount } from '../swap/hooks'
|
||||||
import { useCurrencyBalances } from '../wallet/hooks'
|
import { useCurrencyBalances } from '../wallet/hooks'
|
||||||
import {
|
import { Field, Bound, typeInput, typeLowerRangeInput, typeUpperRangeInput, typeStartPriceInput } from './actions'
|
||||||
Field,
|
|
||||||
Bound,
|
|
||||||
typeInput,
|
|
||||||
typeLowerRangeInput,
|
|
||||||
typeUpperRangeInput,
|
|
||||||
RangeType,
|
|
||||||
updateRangeType as updateRangeTypeAction,
|
|
||||||
} from './actions'
|
|
||||||
import { tryParseTick } from './utils'
|
import { tryParseTick } from './utils'
|
||||||
|
import { usePool } from 'data/Pools'
|
||||||
const ZERO = JSBI.BigInt(0)
|
|
||||||
|
|
||||||
export function useMintState(): AppState['mint'] {
|
export function useMintState(): AppState['mint'] {
|
||||||
return useSelector<AppState, AppState['mint']>((state) => state.mint)
|
return useSelector<AppState, AppState['mint']>((state) => state.mint)
|
||||||
@ -34,7 +26,7 @@ export function useMintActionHandlers(
|
|||||||
onFieldBInput: (typedValue: string) => void
|
onFieldBInput: (typedValue: string) => void
|
||||||
onLowerRangeInput: (typedValue: string) => void
|
onLowerRangeInput: (typedValue: string) => void
|
||||||
onUpperRangeInput: (typedValue: string) => void
|
onUpperRangeInput: (typedValue: string) => void
|
||||||
updateRangeType: (rangetype: RangeType) => void
|
onStartPriceInput: (typedValue: string) => void
|
||||||
} {
|
} {
|
||||||
const dispatch = useDispatch<AppDispatch>()
|
const dispatch = useDispatch<AppDispatch>()
|
||||||
|
|
||||||
@ -66,9 +58,9 @@ export function useMintActionHandlers(
|
|||||||
[dispatch]
|
[dispatch]
|
||||||
)
|
)
|
||||||
|
|
||||||
const updateRangeType = useCallback(
|
const onStartPriceInput = useCallback(
|
||||||
(rangeType: RangeType) => {
|
(typedValue: string) => {
|
||||||
dispatch(updateRangeTypeAction({ rangeType }))
|
dispatch(typeStartPriceInput({ typedValue }))
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
)
|
)
|
||||||
@ -78,39 +70,48 @@ export function useMintActionHandlers(
|
|||||||
onFieldBInput,
|
onFieldBInput,
|
||||||
onLowerRangeInput,
|
onLowerRangeInput,
|
||||||
onUpperRangeInput,
|
onUpperRangeInput,
|
||||||
updateRangeType,
|
onStartPriceInput,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// dummy entity
|
|
||||||
export interface Tick {
|
|
||||||
rate: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useDerivedMintInfo(
|
export function useDerivedMintInfo(
|
||||||
currencyA: Currency | undefined,
|
currencyA: Currency | undefined,
|
||||||
currencyB: Currency | undefined
|
currencyB: Currency | undefined,
|
||||||
|
feeAmount: FeeAmount | undefined
|
||||||
): {
|
): {
|
||||||
dependentField: Field
|
pool?: Pool | null
|
||||||
currencies: { [field in Field]?: Currency }
|
poolState: PoolState
|
||||||
pair?: Pair | null
|
ticks: { [bound in Bound]?: number | undefined }
|
||||||
pairState: PairState
|
|
||||||
currencyBalances: { [field in Field]?: CurrencyAmount }
|
|
||||||
parsedAmounts: { [field in Field]?: CurrencyAmount }
|
|
||||||
ticks: { [bound in Bound]?: Tick }
|
|
||||||
price?: Price
|
price?: Price
|
||||||
|
pricesAtTicks: {
|
||||||
|
[bound in Bound]?: Price | undefined
|
||||||
|
}
|
||||||
|
currencies: { [field in Field]?: Currency }
|
||||||
|
currencyBalances: { [field in Field]?: CurrencyAmount }
|
||||||
|
dependentField: Field
|
||||||
|
parsedAmounts: { [field in Field]?: CurrencyAmount }
|
||||||
|
position: Position | undefined
|
||||||
noLiquidity?: boolean
|
noLiquidity?: boolean
|
||||||
liquidityMinted?: TokenAmount
|
errorMessage?: string
|
||||||
poolTokenPercentage?: Percent
|
invalidPool: boolean
|
||||||
error?: string
|
outOfRange: boolean
|
||||||
|
invalidRange: boolean
|
||||||
|
depositADisabled: boolean
|
||||||
|
depositBDisabled: boolean
|
||||||
} {
|
} {
|
||||||
const { account, chainId } = useActiveWeb3React()
|
const { account, chainId } = useActiveWeb3React()
|
||||||
|
|
||||||
const { independentField, typedValue, otherTypedValue, lowerRangeTypedValue, upperRangeTypedValue } = useMintState()
|
const {
|
||||||
|
independentField,
|
||||||
|
typedValue,
|
||||||
|
lowerRangeTypedValue,
|
||||||
|
upperRangeTypedValue,
|
||||||
|
startPriceTypedValue,
|
||||||
|
} = useMintState()
|
||||||
|
|
||||||
const dependentField = independentField === Field.CURRENCY_A ? Field.CURRENCY_B : Field.CURRENCY_A
|
const dependentField = independentField === Field.CURRENCY_A ? Field.CURRENCY_B : Field.CURRENCY_A
|
||||||
|
|
||||||
// tokens
|
// currencies
|
||||||
const currencies: { [field in Field]?: Currency } = useMemo(
|
const currencies: { [field in Field]?: Currency } = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
[Field.CURRENCY_A]: currencyA ?? undefined,
|
[Field.CURRENCY_A]: currencyA ?? undefined,
|
||||||
@ -119,48 +120,152 @@ export function useDerivedMintInfo(
|
|||||||
[currencyA, currencyB]
|
[currencyA, currencyB]
|
||||||
)
|
)
|
||||||
|
|
||||||
// pair
|
// formatted with tokens
|
||||||
const [pairState, pair] = usePair(currencies[Field.CURRENCY_A], currencies[Field.CURRENCY_B])
|
const [tokenA, tokenB] = useMemo(() => [wrappedCurrency(currencyA, chainId), wrappedCurrency(currencyB, chainId)], [
|
||||||
const totalSupply = useTotalSupply(pair?.liquidityToken)
|
chainId,
|
||||||
|
currencyA,
|
||||||
const noLiquidity: boolean =
|
currencyB,
|
||||||
pairState === PairState.NOT_EXISTS || Boolean(totalSupply && JSBI.equal(totalSupply.raw, ZERO))
|
])
|
||||||
|
|
||||||
// balances
|
// balances
|
||||||
const balances = useCurrencyBalances(account ?? undefined, [
|
const balances = useCurrencyBalances(account ?? undefined, [
|
||||||
currencies[Field.CURRENCY_A],
|
currencies[Field.CURRENCY_A],
|
||||||
currencies[Field.CURRENCY_B],
|
currencies[Field.CURRENCY_B],
|
||||||
])
|
])
|
||||||
|
|
||||||
const currencyBalances: { [field in Field]?: CurrencyAmount } = {
|
const currencyBalances: { [field in Field]?: CurrencyAmount } = {
|
||||||
[Field.CURRENCY_A]: balances[0],
|
[Field.CURRENCY_A]: balances[0],
|
||||||
[Field.CURRENCY_B]: balances[1],
|
[Field.CURRENCY_B]: balances[1],
|
||||||
}
|
}
|
||||||
|
|
||||||
// amounts
|
// pool
|
||||||
const independentAmount: CurrencyAmount | undefined = tryParseAmount(typedValue, currencies[independentField])
|
const [poolState, pool] = usePool(currencies[Field.CURRENCY_A], currencies[Field.CURRENCY_B], feeAmount)
|
||||||
const dependentAmount: CurrencyAmount | undefined = useMemo(() => {
|
const noLiquidity = poolState === PoolState.NOT_EXISTS
|
||||||
|
|
||||||
|
const price = useMemo(() => {
|
||||||
|
// if no liquidity use typed value
|
||||||
if (noLiquidity) {
|
if (noLiquidity) {
|
||||||
if (otherTypedValue && currencies[dependentField]) {
|
const parsedAmount = tryParseAmount(startPriceTypedValue, tokenA)
|
||||||
return tryParseAmount(otherTypedValue, currencies[dependentField])
|
if (parsedAmount && tokenA && tokenB) {
|
||||||
}
|
const amountOne = tryParseAmount('1', tokenA)
|
||||||
return undefined
|
return amountOne ? new Price(tokenA, tokenB, amountOne.raw, parsedAmount.raw) : undefined
|
||||||
} else if (independentAmount) {
|
|
||||||
// we wrap the currencies just to get the price in terms of the other token
|
|
||||||
const wrappedIndependentAmount = wrappedCurrencyAmount(independentAmount, chainId)
|
|
||||||
const [tokenA, tokenB] = [wrappedCurrency(currencyA, chainId), wrappedCurrency(currencyB, chainId)]
|
|
||||||
if (tokenA && tokenB && wrappedIndependentAmount && pair) {
|
|
||||||
const dependentCurrency = dependentField === Field.CURRENCY_B ? currencyB : currencyA
|
|
||||||
const dependentTokenAmount =
|
|
||||||
dependentField === Field.CURRENCY_B
|
|
||||||
? pair.priceOf(tokenA).quote(wrappedIndependentAmount)
|
|
||||||
: pair.priceOf(tokenB).quote(wrappedIndependentAmount)
|
|
||||||
return dependentCurrency === ETHER ? CurrencyAmount.ether(dependentTokenAmount.raw) : dependentTokenAmount
|
|
||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
|
} else {
|
||||||
|
// get the amount of quote currency
|
||||||
|
return pool && tokenA ? pool.priceOf(tokenA) : undefined
|
||||||
|
}
|
||||||
|
}, [noLiquidity, startPriceTypedValue, tokenA, tokenB, pool])
|
||||||
|
|
||||||
|
// used for ratio calculation when pool not initialized
|
||||||
|
const mockPool = useMemo(() => {
|
||||||
|
if (tokenA && tokenB && feeAmount && price) {
|
||||||
|
const currentTick = priceToClosestTick(price)
|
||||||
|
const currentSqrt = TickMath.getSqrtRatioAtTick(currentTick)
|
||||||
|
return new Pool(tokenA, tokenB, feeAmount, currentSqrt, JSBI.BigInt(0), currentTick, [])
|
||||||
} else {
|
} else {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
}, [noLiquidity, otherTypedValue, currencies, dependentField, independentAmount, currencyA, chainId, currencyB, pair])
|
}, [feeAmount, price, tokenA, tokenB])
|
||||||
|
|
||||||
|
// if pool exists use it, if not use the mock pool
|
||||||
|
const poolForPosition: Pool | undefined = pool ?? mockPool
|
||||||
|
|
||||||
|
// parse typed range values and determine closest ticks
|
||||||
|
const ticks: {
|
||||||
|
[key: string]: number | undefined
|
||||||
|
} = useMemo(() => {
|
||||||
|
return {
|
||||||
|
[Bound.LOWER]: poolForPosition ? tryParseTick(tokenA, tokenB, poolForPosition, lowerRangeTypedValue) : undefined,
|
||||||
|
[Bound.UPPER]: poolForPosition ? tryParseTick(tokenA, tokenB, poolForPosition, upperRangeTypedValue) : undefined,
|
||||||
|
}
|
||||||
|
}, [lowerRangeTypedValue, poolForPosition, tokenA, tokenB, upperRangeTypedValue])
|
||||||
|
|
||||||
|
const { [Bound.LOWER]: tickLower, [Bound.UPPER]: tickUpper } = ticks || {}
|
||||||
|
const sortedTicks = useMemo(
|
||||||
|
() =>
|
||||||
|
tickLower !== undefined && tickUpper !== undefined
|
||||||
|
? tickLower < tickUpper
|
||||||
|
? [tickLower, tickUpper]
|
||||||
|
: [tickUpper, tickLower]
|
||||||
|
: undefined,
|
||||||
|
[tickLower, tickUpper]
|
||||||
|
)
|
||||||
|
|
||||||
|
const pricesAtTicks = useMemo(() => {
|
||||||
|
return {
|
||||||
|
[Bound.LOWER]: poolForPosition ? getTickToPrice(tokenA, tokenB, ticks[Bound.LOWER]) : undefined,
|
||||||
|
[Bound.UPPER]: poolForPosition ? getTickToPrice(tokenA, tokenB, ticks[Bound.UPPER]) : undefined,
|
||||||
|
}
|
||||||
|
}, [poolForPosition, ticks, tokenA, tokenB])
|
||||||
|
const { [Bound.LOWER]: lowerPrice, [Bound.UPPER]: upperPrice } = pricesAtTicks
|
||||||
|
|
||||||
|
// mark invalid range
|
||||||
|
const invalidRange = Boolean(lowerPrice && upperPrice && lowerPrice.greaterThan(upperPrice))
|
||||||
|
|
||||||
|
// amounts
|
||||||
|
const independentAmount: CurrencyAmount | undefined = tryParseAmount(typedValue, currencies[independentField])
|
||||||
|
|
||||||
|
const dependentAmount: CurrencyAmount | undefined = useMemo(() => {
|
||||||
|
// we wrap the currencies just to get the price in terms of the other token
|
||||||
|
const wrappedIndependentAmount = wrappedCurrencyAmount(independentAmount, chainId)
|
||||||
|
|
||||||
|
const dependentCurrency = dependentField === Field.CURRENCY_B ? currencyB : currencyA
|
||||||
|
|
||||||
|
if (
|
||||||
|
feeAmount &&
|
||||||
|
independentAmount &&
|
||||||
|
tokenA &&
|
||||||
|
tokenB &&
|
||||||
|
wrappedIndependentAmount &&
|
||||||
|
price &&
|
||||||
|
lowerPrice &&
|
||||||
|
upperPrice &&
|
||||||
|
sortedTicks &&
|
||||||
|
poolForPosition
|
||||||
|
) {
|
||||||
|
// if price is out of range or invalid range - retun 0 (single deposit with be independent)
|
||||||
|
if (price.lessThan(lowerPrice) || price.greaterThan(upperPrice) || invalidRange) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const position: Position | undefined = wrappedIndependentAmount.token.equals(poolForPosition.token0)
|
||||||
|
? Position.fromAmount0({
|
||||||
|
pool: poolForPosition,
|
||||||
|
tickLower: sortedTicks[0],
|
||||||
|
tickUpper: sortedTicks[1],
|
||||||
|
amount0: independentAmount.raw,
|
||||||
|
})
|
||||||
|
: Position.fromAmount1({
|
||||||
|
pool: poolForPosition,
|
||||||
|
tickLower: sortedTicks[0],
|
||||||
|
tickUpper: sortedTicks[1],
|
||||||
|
amount1: independentAmount.raw,
|
||||||
|
})
|
||||||
|
|
||||||
|
const dependentTokenAmount = wrappedIndependentAmount.token.equals(poolForPosition.token0)
|
||||||
|
? position.amount1
|
||||||
|
: position.amount0
|
||||||
|
return dependentCurrency === ETHER ? CurrencyAmount.ether(dependentTokenAmount.raw) : dependentTokenAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}, [
|
||||||
|
independentAmount,
|
||||||
|
chainId,
|
||||||
|
dependentField,
|
||||||
|
currencyB,
|
||||||
|
currencyA,
|
||||||
|
feeAmount,
|
||||||
|
tokenA,
|
||||||
|
tokenB,
|
||||||
|
price,
|
||||||
|
sortedTicks,
|
||||||
|
lowerPrice,
|
||||||
|
upperPrice,
|
||||||
|
poolForPosition,
|
||||||
|
invalidRange,
|
||||||
|
])
|
||||||
|
|
||||||
const parsedAmounts: { [field in Field]: CurrencyAmount | undefined } = useMemo(() => {
|
const parsedAmounts: { [field in Field]: CurrencyAmount | undefined } = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
@ -169,87 +274,101 @@ export function useDerivedMintInfo(
|
|||||||
}
|
}
|
||||||
}, [dependentAmount, independentAmount, independentField])
|
}, [dependentAmount, independentAmount, independentField])
|
||||||
|
|
||||||
const price = useMemo(() => {
|
// single deposit only if price is out of range
|
||||||
if (noLiquidity) {
|
const deposit1Disabled = Boolean(sortedTicks && poolForPosition && poolForPosition.tickCurrent <= sortedTicks[0])
|
||||||
const { [Field.CURRENCY_A]: currencyAAmount, [Field.CURRENCY_B]: currencyBAmount } = parsedAmounts
|
const deposit0Disabled = Boolean(sortedTicks && poolForPosition && poolForPosition.tickCurrent >= sortedTicks[1])
|
||||||
if (currencyAAmount && currencyBAmount) {
|
|
||||||
return new Price(currencyAAmount.currency, currencyBAmount.currency, currencyAAmount.raw, currencyBAmount.raw)
|
// sorted for token order
|
||||||
}
|
const depositADisabled = Boolean(
|
||||||
|
(deposit0Disabled && poolForPosition && tokenA && poolForPosition.token0.equals(tokenA)) ||
|
||||||
|
(deposit1Disabled && poolForPosition && tokenA && poolForPosition.token1.equals(tokenA))
|
||||||
|
)
|
||||||
|
const depositBDisabled = Boolean(
|
||||||
|
(deposit0Disabled && poolForPosition && tokenB && poolForPosition.token0.equals(tokenB)) ||
|
||||||
|
(deposit1Disabled && poolForPosition && tokenB && poolForPosition.token1.equals(tokenB))
|
||||||
|
)
|
||||||
|
|
||||||
|
// create position entity based on users selection
|
||||||
|
const position: Position | undefined = useMemo(() => {
|
||||||
|
if (!poolForPosition || !tokenA || !tokenB || !sortedTicks) {
|
||||||
return undefined
|
return undefined
|
||||||
} else {
|
|
||||||
const wrappedCurrencyA = wrappedCurrency(currencyA, chainId)
|
|
||||||
return pair && wrappedCurrencyA ? pair.priceOf(wrappedCurrencyA) : undefined
|
|
||||||
}
|
|
||||||
}, [chainId, currencyA, noLiquidity, pair, parsedAmounts])
|
|
||||||
|
|
||||||
// parse typed range values and determine closest ticks, dummy rn
|
|
||||||
const ticks = {
|
|
||||||
[Bound.CURRENT]: {
|
|
||||||
rate: parseFloat(
|
|
||||||
independentField === Field.CURRENCY_A ? price?.toFixed(6) ?? '0' : price?.invert().toFixed(6) ?? '0'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
[Bound.LOWER]: tryParseTick(lowerRangeTypedValue),
|
|
||||||
[Bound.UPPER]: tryParseTick(upperRangeTypedValue),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// liquidity minted
|
// mark as 0 if disbaled because out of range
|
||||||
const liquidityMinted = useMemo(() => {
|
const amount0 = !deposit0Disabled
|
||||||
const { [Field.CURRENCY_A]: currencyAAmount, [Field.CURRENCY_B]: currencyBAmount } = parsedAmounts
|
? parsedAmounts?.[tokenA.equals(poolForPosition.token0) ? Field.CURRENCY_A : Field.CURRENCY_B]?.raw
|
||||||
const [tokenAmountA, tokenAmountB] = [
|
: BIG_INT_ZERO
|
||||||
wrappedCurrencyAmount(currencyAAmount, chainId),
|
const amount1 = !deposit1Disabled
|
||||||
wrappedCurrencyAmount(currencyBAmount, chainId),
|
? parsedAmounts?.[tokenA.equals(poolForPosition.token0) ? Field.CURRENCY_B : Field.CURRENCY_A]?.raw
|
||||||
]
|
: BIG_INT_ZERO
|
||||||
if (pair && totalSupply && tokenAmountA && tokenAmountB) {
|
|
||||||
return pair.getLiquidityMinted(totalSupply, tokenAmountA, tokenAmountB)
|
if (amount0 !== undefined && amount1 !== undefined) {
|
||||||
|
return Position.fromAmounts({
|
||||||
|
pool: poolForPosition,
|
||||||
|
tickLower: sortedTicks[0],
|
||||||
|
tickUpper: sortedTicks[1],
|
||||||
|
amount0: amount0,
|
||||||
|
amount1: amount1,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
}, [parsedAmounts, chainId, pair, totalSupply])
|
}, [parsedAmounts, poolForPosition, sortedTicks, tokenA, tokenB, deposit0Disabled, deposit1Disabled])
|
||||||
|
|
||||||
const poolTokenPercentage = useMemo(() => {
|
// liquiidty range warning
|
||||||
if (liquidityMinted && totalSupply) {
|
const outOfRange = Boolean(
|
||||||
return new Percent(liquidityMinted.raw, totalSupply.add(liquidityMinted).raw)
|
price &&
|
||||||
} else {
|
lowerPrice &&
|
||||||
return undefined
|
upperPrice &&
|
||||||
}
|
!invalidRange &&
|
||||||
}, [liquidityMinted, totalSupply])
|
(lowerPrice.greaterThan(price) || price.greaterThan(upperPrice))
|
||||||
|
)
|
||||||
|
|
||||||
let error: string | undefined
|
let errorMessage: string | undefined
|
||||||
if (!account) {
|
if (!account) {
|
||||||
error = 'Connect Wallet'
|
errorMessage = 'Connect Wallet'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pairState === PairState.INVALID) {
|
if (poolState === PoolState.INVALID) {
|
||||||
error = error ?? 'Invalid pair'
|
errorMessage = errorMessage ?? 'Invalid pair'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!parsedAmounts[Field.CURRENCY_A] || !parsedAmounts[Field.CURRENCY_B]) {
|
if (
|
||||||
error = error ?? 'Enter an amount'
|
(!parsedAmounts[Field.CURRENCY_A] && !depositADisabled) ||
|
||||||
|
(!parsedAmounts[Field.CURRENCY_B] && !depositBDisabled)
|
||||||
|
) {
|
||||||
|
errorMessage = errorMessage ?? 'Enter an amount'
|
||||||
}
|
}
|
||||||
|
|
||||||
const { [Field.CURRENCY_A]: currencyAAmount, [Field.CURRENCY_B]: currencyBAmount } = parsedAmounts
|
const { [Field.CURRENCY_A]: currencyAAmount, [Field.CURRENCY_B]: currencyBAmount } = parsedAmounts
|
||||||
|
|
||||||
if (currencyAAmount && currencyBalances?.[Field.CURRENCY_A]?.lessThan(currencyAAmount)) {
|
if (currencyAAmount && currencyBalances?.[Field.CURRENCY_A]?.lessThan(currencyAAmount)) {
|
||||||
error = 'Insufficient ' + currencies[Field.CURRENCY_A]?.symbol + ' balance'
|
errorMessage = 'Insufficient ' + currencies[Field.CURRENCY_A]?.symbol + ' balance'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currencyBAmount && currencyBalances?.[Field.CURRENCY_B]?.lessThan(currencyBAmount)) {
|
if (currencyBAmount && currencyBalances?.[Field.CURRENCY_B]?.lessThan(currencyBAmount)) {
|
||||||
error = 'Insufficient ' + currencies[Field.CURRENCY_B]?.symbol + ' balance'
|
errorMessage = 'Insufficient ' + currencies[Field.CURRENCY_B]?.symbol + ' balance'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const invalidPool = poolState === PoolState.INVALID
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dependentField,
|
dependentField,
|
||||||
currencies,
|
currencies,
|
||||||
pair,
|
pool,
|
||||||
pairState,
|
poolState,
|
||||||
currencyBalances,
|
currencyBalances,
|
||||||
parsedAmounts,
|
parsedAmounts,
|
||||||
ticks,
|
ticks,
|
||||||
price,
|
price,
|
||||||
|
pricesAtTicks,
|
||||||
|
position,
|
||||||
noLiquidity,
|
noLiquidity,
|
||||||
liquidityMinted,
|
errorMessage,
|
||||||
poolTokenPercentage,
|
invalidPool,
|
||||||
error,
|
invalidRange,
|
||||||
|
outOfRange,
|
||||||
|
depositADisabled,
|
||||||
|
depositBDisabled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,37 +5,30 @@ import {
|
|||||||
typeInput,
|
typeInput,
|
||||||
typeLowerRangeInput,
|
typeLowerRangeInput,
|
||||||
typeUpperRangeInput,
|
typeUpperRangeInput,
|
||||||
RangeType,
|
typeStartPriceInput,
|
||||||
updateRangeType,
|
|
||||||
} from './actions'
|
} from './actions'
|
||||||
|
|
||||||
export interface MintState {
|
export interface MintState {
|
||||||
readonly independentField: Field
|
readonly independentField: Field
|
||||||
readonly typedValue: string
|
readonly typedValue: string
|
||||||
readonly otherTypedValue: string // for the case when there's no liquidity
|
readonly otherTypedValue: string // for the case when there's no liquidity
|
||||||
|
readonly startPriceTypedValue: string // for the case when there's no liquidity
|
||||||
readonly lowerRangeTypedValue: string
|
readonly lowerRangeTypedValue: string
|
||||||
readonly upperRangeTypedValue: string
|
readonly upperRangeTypedValue: string
|
||||||
readonly rangeType: RangeType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialState: MintState = {
|
export const initialState: MintState = {
|
||||||
independentField: Field.CURRENCY_A,
|
independentField: Field.CURRENCY_A,
|
||||||
typedValue: '',
|
typedValue: '',
|
||||||
otherTypedValue: '',
|
otherTypedValue: '',
|
||||||
|
startPriceTypedValue: '',
|
||||||
lowerRangeTypedValue: '',
|
lowerRangeTypedValue: '',
|
||||||
upperRangeTypedValue: '',
|
upperRangeTypedValue: '',
|
||||||
rangeType: RangeType.RATE,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default createReducer<MintState>(initialState, (builder) =>
|
export default createReducer<MintState>(initialState, (builder) =>
|
||||||
builder
|
builder
|
||||||
.addCase(resetMintState, () => initialState)
|
.addCase(resetMintState, () => initialState)
|
||||||
.addCase(updateRangeType, (state, { payload: { rangeType } }) => {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
rangeType,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.addCase(typeLowerRangeInput, (state, { payload: { typedValue } }) => {
|
.addCase(typeLowerRangeInput, (state, { payload: { typedValue } }) => {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@ -48,6 +41,12 @@ export default createReducer<MintState>(initialState, (builder) =>
|
|||||||
upperRangeTypedValue: typedValue,
|
upperRangeTypedValue: typedValue,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.addCase(typeStartPriceInput, (state, { payload: { typedValue } }) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
startPriceTypedValue: typedValue,
|
||||||
|
}
|
||||||
|
})
|
||||||
.addCase(typeInput, (state, { payload: { field, typedValue, noLiquidity } }) => {
|
.addCase(typeInput, (state, { payload: { field, typedValue, noLiquidity } }) => {
|
||||||
if (noLiquidity) {
|
if (noLiquidity) {
|
||||||
// they're typing into the field they've last typed in
|
// they're typing into the field they've last typed in
|
||||||
|
@ -1,18 +1,27 @@
|
|||||||
import { Tick } from './hooks'
|
import { Pool, priceToClosestTick, nearestUsableTick } from '@uniswap/v3-sdk/dist/'
|
||||||
/**
|
import { Price, Token } from '@uniswap/sdk-core'
|
||||||
* @todo
|
import { tryParseAmount } from 'state/swap/hooks'
|
||||||
* udpate to actually parse input and calculate next tick
|
|
||||||
*/
|
export function tryParseTick(
|
||||||
export function tryParseTick(value?: string): Tick | undefined {
|
baseToken: Token | undefined,
|
||||||
if (!value) {
|
quoteToken: Token | undefined,
|
||||||
|
pool: Pool | undefined,
|
||||||
|
value?: string
|
||||||
|
): number | undefined {
|
||||||
|
if (!value || !baseToken || !quoteToken || !pool) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const amount = tryParseAmount(value, quoteToken)
|
||||||
return { rate: parseFloat(value) * 0.999 }
|
|
||||||
} catch (error) {
|
|
||||||
console.debug(`Failed to parse range amount: "${value}"`, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined
|
const amountOne = tryParseAmount('1', baseToken)
|
||||||
|
|
||||||
|
if (!amount || !amountOne) return undefined
|
||||||
|
|
||||||
|
// parse the typed value into a price, token0 should always be base currency based on url
|
||||||
|
const price = new Price(baseToken, quoteToken, amountOne.raw, amount.raw)
|
||||||
|
|
||||||
|
const tick = priceToClosestTick(price)
|
||||||
|
|
||||||
|
return nearestUsableTick(tick, pool.tickSpacing)
|
||||||
}
|
}
|
||||||
|
169
src/state/mint/v2.ts
Normal file
169
src/state/mint/v2.ts
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
import { useMemo } from 'react'
|
||||||
|
import { Pair } from '@uniswap/v2-sdk'
|
||||||
|
import { Currency, CurrencyAmount, ETHER, Percent, Price, TokenAmount } from '@uniswap/sdk-core'
|
||||||
|
import JSBI from 'jsbi'
|
||||||
|
import { PairState, usePair } from '../../data/V2'
|
||||||
|
import { useTotalSupply } from '../../data/TotalSupply'
|
||||||
|
|
||||||
|
import { useActiveWeb3React } from '../../hooks'
|
||||||
|
import { wrappedCurrency, wrappedCurrencyAmount } from '../../utils/wrappedCurrency'
|
||||||
|
import { tryParseAmount } from '../swap/hooks'
|
||||||
|
import { useCurrencyBalances } from '../wallet/hooks'
|
||||||
|
import { Field } from './actions'
|
||||||
|
import { useMintState } from './hooks'
|
||||||
|
|
||||||
|
const ZERO = JSBI.BigInt(0)
|
||||||
|
|
||||||
|
export function useV2DerivedMintInfo(
|
||||||
|
currencyA: Currency | undefined,
|
||||||
|
currencyB: Currency | undefined
|
||||||
|
): {
|
||||||
|
dependentField: Field
|
||||||
|
currencies: { [field in Field]?: Currency }
|
||||||
|
pair?: Pair | null
|
||||||
|
pairState: PairState
|
||||||
|
currencyBalances: { [field in Field]?: CurrencyAmount }
|
||||||
|
parsedAmounts: { [field in Field]?: CurrencyAmount }
|
||||||
|
price?: Price
|
||||||
|
noLiquidity?: boolean
|
||||||
|
liquidityMinted?: TokenAmount
|
||||||
|
poolTokenPercentage?: Percent
|
||||||
|
error?: string
|
||||||
|
} {
|
||||||
|
const { account, chainId } = useActiveWeb3React()
|
||||||
|
|
||||||
|
const { independentField, typedValue, otherTypedValue } = useMintState()
|
||||||
|
|
||||||
|
const dependentField = independentField === Field.CURRENCY_A ? Field.CURRENCY_B : Field.CURRENCY_A
|
||||||
|
|
||||||
|
// tokens
|
||||||
|
const currencies: { [field in Field]?: Currency } = useMemo(
|
||||||
|
() => ({
|
||||||
|
[Field.CURRENCY_A]: currencyA ?? undefined,
|
||||||
|
[Field.CURRENCY_B]: currencyB ?? undefined,
|
||||||
|
}),
|
||||||
|
[currencyA, currencyB]
|
||||||
|
)
|
||||||
|
|
||||||
|
// pair
|
||||||
|
const [pairState, pair] = usePair(currencies[Field.CURRENCY_A], currencies[Field.CURRENCY_B])
|
||||||
|
const totalSupply = useTotalSupply(pair?.liquidityToken)
|
||||||
|
|
||||||
|
const noLiquidity: boolean =
|
||||||
|
pairState === PairState.NOT_EXISTS || Boolean(totalSupply && JSBI.equal(totalSupply.raw, ZERO))
|
||||||
|
|
||||||
|
// balances
|
||||||
|
const balances = useCurrencyBalances(account ?? undefined, [
|
||||||
|
currencies[Field.CURRENCY_A],
|
||||||
|
currencies[Field.CURRENCY_B],
|
||||||
|
])
|
||||||
|
const currencyBalances: { [field in Field]?: CurrencyAmount } = {
|
||||||
|
[Field.CURRENCY_A]: balances[0],
|
||||||
|
[Field.CURRENCY_B]: balances[1],
|
||||||
|
}
|
||||||
|
|
||||||
|
// amounts
|
||||||
|
const independentAmount: CurrencyAmount | undefined = tryParseAmount(typedValue, currencies[independentField])
|
||||||
|
const dependentAmount: CurrencyAmount | undefined = useMemo(() => {
|
||||||
|
if (noLiquidity) {
|
||||||
|
if (otherTypedValue && currencies[dependentField]) {
|
||||||
|
return tryParseAmount(otherTypedValue, currencies[dependentField])
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
} else if (independentAmount) {
|
||||||
|
// we wrap the currencies just to get the price in terms of the other token
|
||||||
|
const wrappedIndependentAmount = wrappedCurrencyAmount(independentAmount, chainId)
|
||||||
|
const [tokenA, tokenB] = [wrappedCurrency(currencyA, chainId), wrappedCurrency(currencyB, chainId)]
|
||||||
|
if (tokenA && tokenB && wrappedIndependentAmount && pair) {
|
||||||
|
const dependentCurrency = dependentField === Field.CURRENCY_B ? currencyB : currencyA
|
||||||
|
const dependentTokenAmount =
|
||||||
|
dependentField === Field.CURRENCY_B
|
||||||
|
? pair.priceOf(tokenA).quote(wrappedIndependentAmount)
|
||||||
|
: pair.priceOf(tokenB).quote(wrappedIndependentAmount)
|
||||||
|
return dependentCurrency === ETHER ? CurrencyAmount.ether(dependentTokenAmount.raw) : dependentTokenAmount
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
} else {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}, [noLiquidity, otherTypedValue, currencies, dependentField, independentAmount, currencyA, chainId, currencyB, pair])
|
||||||
|
|
||||||
|
const parsedAmounts: { [field in Field]: CurrencyAmount | undefined } = useMemo(() => {
|
||||||
|
return {
|
||||||
|
[Field.CURRENCY_A]: independentField === Field.CURRENCY_A ? independentAmount : dependentAmount,
|
||||||
|
[Field.CURRENCY_B]: independentField === Field.CURRENCY_A ? dependentAmount : independentAmount,
|
||||||
|
}
|
||||||
|
}, [dependentAmount, independentAmount, independentField])
|
||||||
|
|
||||||
|
const price = useMemo(() => {
|
||||||
|
if (noLiquidity) {
|
||||||
|
const { [Field.CURRENCY_A]: currencyAAmount, [Field.CURRENCY_B]: currencyBAmount } = parsedAmounts
|
||||||
|
if (currencyAAmount && currencyBAmount) {
|
||||||
|
return new Price(currencyAAmount.currency, currencyBAmount.currency, currencyAAmount.raw, currencyBAmount.raw)
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
} else {
|
||||||
|
const wrappedCurrencyA = wrappedCurrency(currencyA, chainId)
|
||||||
|
return pair && wrappedCurrencyA ? pair.priceOf(wrappedCurrencyA) : undefined
|
||||||
|
}
|
||||||
|
}, [chainId, currencyA, noLiquidity, pair, parsedAmounts])
|
||||||
|
|
||||||
|
// liquidity minted
|
||||||
|
const liquidityMinted = useMemo(() => {
|
||||||
|
const { [Field.CURRENCY_A]: currencyAAmount, [Field.CURRENCY_B]: currencyBAmount } = parsedAmounts
|
||||||
|
const [tokenAmountA, tokenAmountB] = [
|
||||||
|
wrappedCurrencyAmount(currencyAAmount, chainId),
|
||||||
|
wrappedCurrencyAmount(currencyBAmount, chainId),
|
||||||
|
]
|
||||||
|
if (pair && totalSupply && tokenAmountA && tokenAmountB) {
|
||||||
|
return pair.getLiquidityMinted(totalSupply, tokenAmountA, tokenAmountB)
|
||||||
|
} else {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}, [parsedAmounts, chainId, pair, totalSupply])
|
||||||
|
|
||||||
|
const poolTokenPercentage = useMemo(() => {
|
||||||
|
if (liquidityMinted && totalSupply) {
|
||||||
|
return new Percent(liquidityMinted.raw, totalSupply.add(liquidityMinted).raw)
|
||||||
|
} else {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}, [liquidityMinted, totalSupply])
|
||||||
|
|
||||||
|
let error: string | undefined
|
||||||
|
if (!account) {
|
||||||
|
error = 'Connect Wallet'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pairState === PairState.INVALID) {
|
||||||
|
error = error ?? 'Invalid pair'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parsedAmounts[Field.CURRENCY_A] || !parsedAmounts[Field.CURRENCY_B]) {
|
||||||
|
error = error ?? 'Enter an amount'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { [Field.CURRENCY_A]: currencyAAmount, [Field.CURRENCY_B]: currencyBAmount } = parsedAmounts
|
||||||
|
|
||||||
|
if (currencyAAmount && currencyBalances?.[Field.CURRENCY_A]?.lessThan(currencyAAmount)) {
|
||||||
|
error = 'Insufficient ' + currencies[Field.CURRENCY_A]?.symbol + ' balance'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currencyBAmount && currencyBalances?.[Field.CURRENCY_B]?.lessThan(currencyBAmount)) {
|
||||||
|
error = 'Insufficient ' + currencies[Field.CURRENCY_B]?.symbol + ' balance'
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
dependentField,
|
||||||
|
currencies,
|
||||||
|
pair,
|
||||||
|
pairState,
|
||||||
|
currencyBalances,
|
||||||
|
parsedAmounts,
|
||||||
|
price,
|
||||||
|
noLiquidity,
|
||||||
|
liquidityMinted,
|
||||||
|
poolTokenPercentage,
|
||||||
|
error,
|
||||||
|
}
|
||||||
|
}
|
@ -171,7 +171,7 @@ export function useSingleContractMultipleData(
|
|||||||
|
|
||||||
const calls = useMemo(
|
const calls = useMemo(
|
||||||
() =>
|
() =>
|
||||||
contract && fragment && callInputs && callInputs.length > 0
|
contract && fragment && callInputs?.length > 0 && callInputs.every((inputs) => isValidMethodArgs(inputs))
|
||||||
? callInputs.map<Call>((inputs) => {
|
? callInputs.map<Call>((inputs) => {
|
||||||
return {
|
return {
|
||||||
address: contract.address,
|
address: contract.address,
|
||||||
@ -180,7 +180,7 @@ export function useSingleContractMultipleData(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
: [],
|
: [],
|
||||||
[callInputs, contract, fragment, gasRequired]
|
[contract, fragment, callInputs, gasRequired]
|
||||||
)
|
)
|
||||||
|
|
||||||
const results = useCallsData(calls, options)
|
const results = useCallsData(calls, options)
|
||||||
|
@ -53,6 +53,7 @@ export function colors(darkMode: boolean): Colors {
|
|||||||
bg3: darkMode ? '#40444F' : '#EDEEF2',
|
bg3: darkMode ? '#40444F' : '#EDEEF2',
|
||||||
bg4: darkMode ? '#565A69' : '#CED0D9',
|
bg4: darkMode ? '#565A69' : '#CED0D9',
|
||||||
bg5: darkMode ? '#6C7284' : '#888D9B',
|
bg5: darkMode ? '#6C7284' : '#888D9B',
|
||||||
|
bg6: darkMode ? '#1A2028' : '#888D9B',
|
||||||
|
|
||||||
//specialty colors
|
//specialty colors
|
||||||
modalBG: darkMode ? 'rgba(0,0,0,.425)' : 'rgba(0,0,0,0.3)',
|
modalBG: darkMode ? 'rgba(0,0,0,.425)' : 'rgba(0,0,0,0.3)',
|
||||||
|
1
src/theme/styled.d.ts
vendored
1
src/theme/styled.d.ts
vendored
@ -20,6 +20,7 @@ export interface Colors {
|
|||||||
bg3: Color
|
bg3: Color
|
||||||
bg4: Color
|
bg4: Color
|
||||||
bg5: Color
|
bg5: Color
|
||||||
|
bg6: Color
|
||||||
|
|
||||||
modalBG: Color
|
modalBG: Color
|
||||||
advancedBG: Color
|
advancedBG: Color
|
||||||
|
7
src/types/position.d.ts
vendored
7
src/types/position.d.ts
vendored
@ -1,11 +1,4 @@
|
|||||||
import { BigNumberish } from '@ethersproject/bignumber'
|
import { BigNumberish } from '@ethersproject/bignumber'
|
||||||
import { basisPointsToPercent } from 'utils'
|
|
||||||
|
|
||||||
const FEE_BIPS = {
|
|
||||||
FIVE: basisPointsToPercent(5),
|
|
||||||
THIRTY: basisPointsToPercent(30),
|
|
||||||
ONE_HUNDRED: basisPointsToPercent(100),
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Position {
|
export interface Position {
|
||||||
feesEarned: Record<string, BigNumberish>
|
feesEarned: Record<string, BigNumberish>
|
||||||
|
@ -12,7 +12,7 @@ export function hexToUint8Array(hex: string): Uint8Array {
|
|||||||
return arr
|
return arr
|
||||||
}
|
}
|
||||||
|
|
||||||
const UTF_8_DECODER = new TextDecoder()
|
const UTF_8_DECODER = new TextDecoder('utf-8')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the URI representation of the content hash for supported codecs
|
* Returns the URI representation of the content hash for supported codecs
|
||||||
|
13
src/utils/getTickToPrice.ts
Normal file
13
src/utils/getTickToPrice.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Token, Price } from '@uniswap/sdk-core'
|
||||||
|
import { tickToPrice } from '@uniswap/v3-sdk'
|
||||||
|
|
||||||
|
export function getTickToPrice(
|
||||||
|
baseToken: Token | undefined,
|
||||||
|
quoteToken: Token | undefined,
|
||||||
|
tick: number | undefined
|
||||||
|
): Price | undefined {
|
||||||
|
if (!baseToken || !quoteToken || tick === undefined) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return tickToPrice(baseToken, quoteToken, tick)
|
||||||
|
}
|
@ -1,9 +1,11 @@
|
|||||||
|
import { FeeAmount } from '@uniswap/v3-sdk'
|
||||||
import { Contract } from '@ethersproject/contracts'
|
import { Contract } from '@ethersproject/contracts'
|
||||||
import { getAddress } from '@ethersproject/address'
|
import { getAddress } from '@ethersproject/address'
|
||||||
import { AddressZero } from '@ethersproject/constants'
|
import { AddressZero } from '@ethersproject/constants'
|
||||||
import { JsonRpcSigner, Web3Provider } from '@ethersproject/providers'
|
import { JsonRpcSigner, Web3Provider } from '@ethersproject/providers'
|
||||||
import { BigNumber } from '@ethersproject/bignumber'
|
import { BigNumber } from '@ethersproject/bignumber'
|
||||||
import { abi as IUniswapV2Router02ABI } from '@uniswap/v2-periphery/build/IUniswapV2Router02.json'
|
import { abi as IUniswapV2Router02ABI } from '@uniswap/v2-periphery/build/IUniswapV2Router02.json'
|
||||||
|
|
||||||
import { ROUTER_ADDRESS } from '../constants'
|
import { ROUTER_ADDRESS } from '../constants'
|
||||||
import { ChainId, Percent, Token, CurrencyAmount, Currency, ETHER } from '@uniswap/sdk-core'
|
import { ChainId, Percent, Token, CurrencyAmount, Currency, ETHER } from '@uniswap/sdk-core'
|
||||||
import { JSBI } from '@uniswap/v2-sdk'
|
import { JSBI } from '@uniswap/v2-sdk'
|
||||||
@ -123,3 +125,7 @@ export function supportedChainId(chainId: number): ChainId | undefined {
|
|||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const formattedFeeAmount = (feeAmount: FeeAmount): number => {
|
||||||
|
return feeAmount / 10000
|
||||||
|
}
|
||||||
|
@ -2,7 +2,13 @@ import { supportedChainId } from 'utils'
|
|||||||
import { ChainId, Currency, CurrencyAmount, ETHER, Token, TokenAmount, WETH9 } from '@uniswap/sdk-core'
|
import { ChainId, Currency, CurrencyAmount, ETHER, Token, TokenAmount, WETH9 } from '@uniswap/sdk-core'
|
||||||
|
|
||||||
export function wrappedCurrency(currency: Currency | undefined, chainId: ChainId | undefined): Token | undefined {
|
export function wrappedCurrency(currency: Currency | undefined, chainId: ChainId | undefined): Token | undefined {
|
||||||
return chainId && currency === ETHER ? WETH9[chainId] : currency instanceof Token ? currency : undefined
|
return chainId && currency === ETHER
|
||||||
|
? chainId === 1337
|
||||||
|
? new Token(1337, '0xbBca0fFBFE60F60071630A8c80bb6253dC9D6023', 18, 'WETH', 'WETH9')
|
||||||
|
: WETH9[chainId]
|
||||||
|
: currency instanceof Token
|
||||||
|
? currency
|
||||||
|
: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wrappedCurrencyAmount(
|
export function wrappedCurrencyAmount(
|
||||||
|
Loading…
Reference in New Issue
Block a user