From 691f3a5e8b50c12f28c3412d77ff51119890f4f2 Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 25 Aug 2020 05:53:59 +0300 Subject: [PATCH 1/4] migration --- package.json | 2 +- src/components/PositionCard/V1.tsx | 33 +- src/constants/abis/migrator.json | 271 +++++++- src/constants/abis/migrator.ts | 2 +- src/constants/abis/uniswap-helper.json | 45 ++ src/constants/abis/uniswap-v2-helper.ts | 5 + src/constants/abis/uniswap-v2-pair.json | 713 ++++++++++++++++++++++ src/constants/abis/uniswap-v2-pair.ts | 3 + src/data-mooniswap/UniswapV2.ts | 52 ++ src/data-mooniswap/tsconfig.json | 7 +- src/hooks/useContract.ts | 19 +- src/pages/App.tsx | 6 +- src/pages/MigrateV1/MigrateV1Exchange.tsx | 243 ++++---- src/pages/MigrateV1/RemoveV1Exchange.tsx | 181 ------ src/pages/MigrateV1/index.tsx | 85 +-- src/pages/Pool/index.tsx | 9 +- 16 files changed, 1294 insertions(+), 382 deletions(-) create mode 100644 src/constants/abis/uniswap-helper.json create mode 100644 src/constants/abis/uniswap-v2-helper.ts create mode 100644 src/constants/abis/uniswap-v2-pair.json create mode 100644 src/constants/abis/uniswap-v2-pair.ts create mode 100644 src/data-mooniswap/UniswapV2.ts delete mode 100644 src/pages/MigrateV1/RemoveV1Exchange.tsx diff --git a/package.json b/package.json index d96bb3770f2..d5caf88f25f 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "use-media": "^1.4.0" }, "scripts": { - "start": "PORT=4200 react-scripts start", + "start": "PORT=4202 react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject", diff --git a/src/components/PositionCard/V1.tsx b/src/components/PositionCard/V1.tsx index b024c3c3fcf..54f8bfd9da7 100644 --- a/src/components/PositionCard/V1.tsx +++ b/src/components/PositionCard/V1.tsx @@ -12,11 +12,12 @@ import { useActiveWeb3React } from '../../hooks' import { ThemeContext } from 'styled-components' interface PositionCardProps extends RouteComponentProps<{}> { - token: Token + token0: Token + token1: Token V1LiquidityBalance: TokenAmount } -function V1PositionCard({ token, V1LiquidityBalance }: PositionCardProps) { +function UniV2PositionCard({ token0, token1, V1LiquidityBalance }: PositionCardProps) { const theme = useContext(ThemeContext) const { chainId } = useActiveWeb3React() @@ -25,10 +26,10 @@ function V1PositionCard({ token, V1LiquidityBalance }: PositionCardProps) { - - + + - {`${token.symbol}/ETH`} + {`${token0.symbol}/${token1.symbol}`} - V1 + UniV2 - + Migrate - - Remove - + {/**/} + {/* Remove*/} + {/**/} @@ -66,4 +67,4 @@ function V1PositionCard({ token, V1LiquidityBalance }: PositionCardProps) { ) } -export default withRouter(V1PositionCard) +export default withRouter(UniV2PositionCard) diff --git a/src/constants/abis/migrator.json b/src/constants/abis/migrator.json index e5ae58d1e72..cc181207a37 100644 --- a/src/constants/abis/migrator.json +++ b/src/constants/abis/migrator.json @@ -2,54 +2,293 @@ { "inputs": [ { - "internalType": "address", - "name": "_factoryV1", + "internalType": "contract IOneSplitView", + "name": "_oneSplitView", "type": "address" }, { - "internalType": "address", - "name": "_router", + "internalType": "contract IOneSplit", + "name": "_oneSplit", "type": "address" } ], + "payable": false, "stateMutability": "nonpayable", "type": "constructor" }, { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "constant": true, "inputs": [ { - "internalType": "address", - "name": "token", + "internalType": "contract IERC20", + "name": "fromToken", "type": "address" }, + { + "internalType": "contract IERC20", + "name": "destToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, { "internalType": "uint256", - "name": "amountTokenMin", + "name": "parts", "type": "uint256" }, { "internalType": "uint256", - "name": "amountETHMin", + "name": "flags", "type": "uint256" + } + ], + "name": "getExpectedReturn", + "outputs": [ + { + "internalType": "uint256", + "name": "returnAmount", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "distribution", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "contract IERC20", + "name": "fromToken", + "type": "address" }, { - "internalType": "address", - "name": "to", + "internalType": "contract IERC20", + "name": "destToken", "type": "address" }, { "internalType": "uint256", - "name": "deadline", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "parts", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "flags", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "destTokenEthPriceTimesGasPrice", "type": "uint256" } ], - "name": "migrate", - "outputs": [], - "stateMutability": "nonpayable", + "name": "getExpectedReturnWithGas", + "outputs": [ + { + "internalType": "uint256", + "name": "returnAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "estimateGasAmount", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "distribution", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "parts", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "flags", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "destTokenEthPriceTimesGasPrices", + "type": "uint256[]" + } + ], + "name": "getExpectedReturnWithGasMulti", + "outputs": [ + { + "internalType": "uint256[]", + "name": "returnAmounts", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "estimateGasAmount", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "distribution", + "type": "uint256[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "oneSplit", + "outputs": [ + { + "internalType": "contract IOneSplit", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "oneSplitView", + "outputs": [ + { + "internalType": "contract IOneSplitView", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", "type": "function" }, { + "constant": false, + "inputs": [ + { + "internalType": "contract IERC20", + "name": "fromToken", + "type": "address" + }, + { + "internalType": "contract IERC20", + "name": "destToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minReturn", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "distribution", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "flags", + "type": "uint256" + } + ], + "name": "swap", + "outputs": [ + { + "internalType": "uint256", + "name": "returnAmount", + "type": "uint256" + } + ], + "payable": true, "stateMutability": "payable", - "type": "receive" + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minReturn", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "distribution", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "flags", + "type": "uint256[]" + } + ], + "name": "swapMulti", + "outputs": [ + { + "internalType": "uint256", + "name": "returnAmount", + "type": "uint256" + } + ], + "payable": true, + "stateMutability": "payable", + "type": "function" } -] \ No newline at end of file +] diff --git a/src/constants/abis/migrator.ts b/src/constants/abis/migrator.ts index a1230156c37..a3c012822c0 100644 --- a/src/constants/abis/migrator.ts +++ b/src/constants/abis/migrator.ts @@ -1,5 +1,5 @@ import MIGRATOR_ABI from './migrator.json' -const MIGRATOR_ADDRESS = '0x16D4F26C15f3658ec65B1126ff27DD3dF2a2996b' +const MIGRATOR_ADDRESS = '0xC0414ba10d554B3D2F7Be02b0C5E992D653Ffef3' export { MIGRATOR_ADDRESS, MIGRATOR_ABI } diff --git a/src/constants/abis/uniswap-helper.json b/src/constants/abis/uniswap-helper.json new file mode 100644 index 00000000000..d63fec07cf0 --- /dev/null +++ b/src/constants/abis/uniswap-helper.json @@ -0,0 +1,45 @@ +[ + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + } + ], + "name": "getAllPairsWithBalances", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "id", + "type": "address" + }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "walletBalance", + "type": "uint256" + } + ], + "internalType": "struct UniswapHelper.Pair[]", + "name": "pairsWithBalances", + "type": "tuple[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/constants/abis/uniswap-v2-helper.ts b/src/constants/abis/uniswap-v2-helper.ts new file mode 100644 index 00000000000..bd45f81ad46 --- /dev/null +++ b/src/constants/abis/uniswap-v2-helper.ts @@ -0,0 +1,5 @@ +import UNISWAP_V2_HELPER_ABI from './uniswap-helper.json' + +const UNISWAP_V2_HELPER_ADDRESS = '0x98E722098CA0cC28a26f2F0D05BEe4Fc18B170D7' + +export { UNISWAP_V2_HELPER_ADDRESS, UNISWAP_V2_HELPER_ABI } diff --git a/src/constants/abis/uniswap-v2-pair.json b/src/constants/abis/uniswap-v2-pair.json new file mode 100644 index 00000000000..53582c1ed68 --- /dev/null +++ b/src/constants/abis/uniswap-v2-pair.json @@ -0,0 +1,713 @@ +[ + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0In", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1In", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0Out", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1Out", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "Swap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint112", + "name": "reserve0", + "type": "uint112" + }, + { + "indexed": false, + "internalType": "uint112", + "name": "reserve1", + "type": "uint112" + } + ], + "name": "Sync", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "constant": true, + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MINIMUM_LIQUIDITY", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "PERMIT_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getReserves", + "outputs": [ + { + "internalType": "uint112", + "name": "_reserve0", + "type": "uint112" + }, + { + "internalType": "uint112", + "name": "_reserve1", + "type": "uint112" + }, + { + "internalType": "uint32", + "name": "_blockTimestampLast", + "type": "uint32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_token0", + "type": "address" + }, + { + "internalType": "address", + "name": "_token1", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "kLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "price0CumulativeLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "price1CumulativeLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "skim", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "amount0Out", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Out", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "swap", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "sync", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "token0", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "token1", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/src/constants/abis/uniswap-v2-pair.ts b/src/constants/abis/uniswap-v2-pair.ts new file mode 100644 index 00000000000..567606d3235 --- /dev/null +++ b/src/constants/abis/uniswap-v2-pair.ts @@ -0,0 +1,3 @@ +import UNISWAP_V2_PAIR from './uniswap-v2-pair.json' + +export { UNISWAP_V2_PAIR } diff --git a/src/data-mooniswap/UniswapV2.ts b/src/data-mooniswap/UniswapV2.ts new file mode 100644 index 00000000000..eb22553cc94 --- /dev/null +++ b/src/data-mooniswap/UniswapV2.ts @@ -0,0 +1,52 @@ +import { useUniswapV2HelperContract, useUniswapV2PairContract } from '../hooks/useContract' +import { useMemo } from 'react' +import { NEVER_RELOAD, useSingleCallResult, useSingleContractMultipleData } from '../state/multicall/hooks' +import { BigNumber } from '@ethersproject/bignumber' +import { Token } from '@uniswap/sdk' + +export interface UniswapV2Pair { + pair: string + token0: string, + token1: string, + balance: BigNumber +} + +// returns all Uniswapv2 exchange addresses in the user's token list +export function useAllUniswapV2Pairs(account: string): [UniswapV2Pair[], boolean] { + const helper = useUniswapV2HelperContract() + + const res = useSingleCallResult(helper, 'getAllPairsWithBalances', [account], NEVER_RELOAD) + + return useMemo( + () => { + const data = res?.result?.pairsWithBalances?.reduce((acc: any, pair: any) => { + const p = { + pair: pair.id, + token0: pair.token0, + token1: pair.token1, + balance: pair.walletBalance + }; + acc.push(p) + return acc + }, []) + + return [data || [], res?.loading] + }, + [account, res] + ) +} + +export function usePairTokens(pairAddress: string | undefined): string[] { + const contract = useUniswapV2PairContract(pairAddress) + + const res0 = useSingleCallResult(contract, 'token0', [], NEVER_RELOAD) + const res1 = useSingleCallResult(contract, 'token1', [], NEVER_RELOAD) + + return useMemo(() => { + if (res0?.result?.[0] && res1?.result?.[0]) { + return [res0.result[0], res1.result[0]] + } + return [] + }, + [contract, res0, res1]) +} diff --git a/src/data-mooniswap/tsconfig.json b/src/data-mooniswap/tsconfig.json index 3500deb3a9b..d5a6cc127e1 100644 --- a/src/data-mooniswap/tsconfig.json +++ b/src/data-mooniswap/tsconfig.json @@ -1,4 +1,7 @@ { "extends": "../../tsconfig.strict.json", - "include": ["**/*"] -} \ No newline at end of file + "include": [ + "**/*", + "../../node_modules/eslint-plugin-react/lib/types.d.ts" + ] +} diff --git a/src/hooks/useContract.ts b/src/hooks/useContract.ts index e6780bee5f1..b543d7b0c93 100644 --- a/src/hooks/useContract.ts +++ b/src/hooks/useContract.ts @@ -15,6 +15,8 @@ import { V1_MOONISWAP_FACTORY_ADDRESSES, V1_MOONISWAP_HELPER_ADDRESSES } from '. import { getContract } from '../utils' import { useActiveWeb3React } from './index' import { ONE_SPLIT_ABI, ONE_SPLIT_ADDRESSES } from '../constants/one-split' +import { UNISWAP_V2_HELPER_ABI, UNISWAP_V2_HELPER_ADDRESS } from '../constants/abis/uniswap-v2-helper' +import { UNISWAP_V2_PAIR } from '../constants/abis/uniswap-v2-pair' // returns null on errors function useContract(address?: string, ABI?: any, withSignerIfPossible = true): Contract | null { @@ -36,7 +38,22 @@ export function useV1FactoryContract(): Contract | null { return useContract(chainId && V1_FACTORY_ADDRESSES[chainId], V1_FACTORY_ABI, false) } -export function useV2MigratorContract(): Contract | null { +export function useUniswapV2HelperContract(): Contract | null { + const { chainId } = useActiveWeb3React() + return useContract(chainId && UNISWAP_V2_HELPER_ADDRESS, UNISWAP_V2_HELPER_ABI, false) +} + +export function useUniswapV2PairContract(pairAddress: string | undefined): Contract | null { + const { chainId } = useActiveWeb3React() + return useContract(chainId && pairAddress, UNISWAP_V2_PAIR, false) +} + +export function useUniswapV2FactoryContract(): Contract | null { + const { chainId } = useActiveWeb3React() + return useContract(chainId && V1_FACTORY_ADDRESSES[chainId], V1_FACTORY_ABI, false) +} + +export function useMooniswapMigratorContract(): Contract | null { return useContract(MIGRATOR_ADDRESS, MIGRATOR_ABI, true) } diff --git a/src/pages/App.tsx b/src/pages/App.tsx index 09285ad004e..5cf90ab80aa 100644 --- a/src/pages/App.tsx +++ b/src/pages/App.tsx @@ -14,7 +14,6 @@ import { RedirectOldAddLiquidityPathStructure, RedirectToAddLiquidity } from './AddLiquidity/redirects' -import RemoveV1Exchange from './MigrateV1/RemoveV1Exchange' import Pool from './Pool' import PoolFinder from './PoolFinder' import RemoveLiquidity from './RemoveLiquidity' @@ -22,6 +21,8 @@ import { RedirectOldRemoveLiquidityPathStructure } from './RemoveLiquidity/redir import Swap from './Swap' import { RedirectPathToSwapOnly, RedirectToSwap } from './Swap/redirects' import ReferralUrlParser from '../referral-url-parser' +import MigrateV1 from './MigrateV1' +import MigrateV1Exchange from './MigrateV1/MigrateV1Exchange' const AppWrapper = styled.div` display: flex; @@ -94,9 +95,10 @@ export default function App() { - + + diff --git a/src/pages/MigrateV1/MigrateV1Exchange.tsx b/src/pages/MigrateV1/MigrateV1Exchange.tsx index 05023665484..023399cf843 100644 --- a/src/pages/MigrateV1/MigrateV1Exchange.tsx +++ b/src/pages/MigrateV1/MigrateV1Exchange.tsx @@ -5,7 +5,7 @@ import React, { useCallback, useMemo, useState } from 'react' import ReactGA from 'react-ga' import { Redirect, RouteComponentProps } from 'react-router' import { Text } from 'rebass' -import { ButtonConfirmed } from '../../components/Button' +import { ButtonConfirmed, ButtonPrimary, ButtonSecondary } from '../../components/Button' import { LightCard, PinkCard, YellowCard } from '../../components/Card' import { AutoColumn } from '../../components/Column' import CurrencyLogo from '../../components/CurrencyLogo' @@ -14,19 +14,21 @@ import { AutoRow, RowBetween, RowFixed } from '../../components/Row' import { Dots } from '../../components/swap/styleds' import { DEFAULT_DEADLINE_FROM_NOW, INITIAL_ALLOWED_SLIPPAGE } from '../../constants' import { MIGRATOR_ADDRESS } from '../../constants/abis/migrator' -import { PairState, usePair } from '../../data/Reserves' -import { useTotalSupply } from '../../data/TotalSupply' +import { PairState, usePair } from '../../data-mooniswap/Reserves' +import { useTotalSupply } from '../../data-mooniswap/TotalSupply' import { useActiveWeb3React } from '../../hooks' import { useToken } from '../../hooks/Tokens' import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback' -import { useV1ExchangeContract, useV2MigratorContract } from '../../hooks/useContract' -import { NEVER_RELOAD, useSingleCallResult } from '../../state/multicall/hooks' +import { useMooniswapMigratorContract } from '../../hooks/useContract' import { useIsTransactionPending, useTransactionAdder } from '../../state/transactions/hooks' -import { useETHBalances, useTokenBalance } from '../../state/wallet/hooks' +import { useETHBalances, useTokenBalance, useTokenBalances } from '../../state/wallet/hooks' import { BackArrow, ExternalLink, TYPE } from '../../theme' import { getEtherscanLink, isAddress } from '../../utils' import { BodyWrapper } from '../AppBody' import { EmptyState } from './EmptyState' +import { usePairTokens } from '../../data-mooniswap/UniswapV2' +import DoubleCurrencyLogo from '../../components/DoubleLogo' +import { Link } from 'react-router-dom' const POOL_CURRENCY_AMOUNT_MIN = new Fraction(JSBI.BigInt(1), JSBI.BigInt(1000000)) const WEI_DENOM = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18)) @@ -34,6 +36,11 @@ const ZERO = JSBI.BigInt(0) const ONE = JSBI.BigInt(1) const ZERO_FRACTION = new Fraction(ZERO, ONE) const ALLOWED_OUTPUT_MIN_PERCENT = new Percent(JSBI.BigInt(10000 - INITIAL_ALLOWED_SLIPPAGE), JSBI.BigInt(10000)) +const weth = isAddress('0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2') + +function getDenom(decimals: number): JSBI { + return JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(decimals)) +} function FormattedPoolCurrencyAmount({ currencyAmount }: { currencyAmount: TokenAmount }) { return ( @@ -48,39 +55,41 @@ function FormattedPoolCurrencyAmount({ currencyAmount }: { currencyAmount: Token } export function V1LiquidityInfo({ - token, + token0, + token1, liquidityTokenAmount, - tokenWorth, - ethWorth + token0Worth, + token1Worth }: { - token: Token + token0: Token + token1: Token liquidityTokenAmount: TokenAmount - tokenWorth: TokenAmount - ethWorth: TokenAmount + token0Worth: TokenAmount + token1Worth: TokenAmount }) { // const { chainId } = useActiveWeb3React() return ( <> - +
{}{' '} - {token.symbol}/ETH + {token0.symbol}/{ token1.symbol }
- Pooled {token.symbol}: + Pooled {token0.symbol}: - {tokenWorth.toSignificant(4)} + {token0Worth.toSignificant(4)} - + @@ -89,50 +98,60 @@ export function V1LiquidityInfo({ - + - + ) } -function V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount: TokenAmount; token: Token }) { +function V1PairMigration({ liquidityTokenAmount, token0, token1 }: { liquidityTokenAmount: TokenAmount; token0: Token, token1: Token }) { const { account, chainId } = useActiveWeb3React() const totalSupply = useTotalSupply(liquidityTokenAmount.token) - const exchangeETHBalance = useETHBalances([liquidityTokenAmount.token.address])?.[liquidityTokenAmount.token.address] - const exchangeTokenBalance = useTokenBalance(liquidityTokenAmount.token.address, token) - - const [v2PairState, v2Pair] = usePair(token) - const isFirstLiquidityProvider: boolean = v2PairState === PairState.NOT_EXISTS + const pairTokenBalances = useTokenBalances(liquidityTokenAmount.token.address, [token0, token1]) + + let mooniswapTokens = [] + if (token0.address === weth) { + mooniswapTokens = [ETHER, token1] + } else if (token1.address === weth) { + mooniswapTokens = [ETHER, token0] + } else { + mooniswapTokens = [token0, token1] + } + const [mooniswapPairState, mooniswapPair] = usePair(mooniswapTokens[0], mooniswapTokens[1]) + const isFirstLiquidityProvider: boolean = mooniswapPairState === PairState.NOT_EXISTS - const v2SpotPrice = v2Pair?.reserveOf(token)?.divide(v2Pair?.reserveOf(ETHER)) + const mooniswapSpotPrice = mooniswapPair?.reserveOf(mooniswapTokens[1])?.divide(mooniswapPair?.reserveOf(mooniswapTokens[0])) const [confirmingMigration, setConfirmingMigration] = useState(false) const [pendingMigrationHash, setPendingMigrationHash] = useState(null) const shareFraction: Fraction = totalSupply ? new Percent(liquidityTokenAmount.raw, totalSupply.raw) : ZERO_FRACTION - const ethWorth: TokenAmount = exchangeETHBalance - ? new TokenAmount(ETHER, exchangeETHBalance.multiply(shareFraction).multiply(WEI_DENOM).quotient) - : new TokenAmount(ETHER, ZERO) + const token0Worth: TokenAmount = pairTokenBalances?.[token0.address] + ? new TokenAmount( + token0, + pairTokenBalances[token0.address].multiply(shareFraction).multiply(getDenom(token0.decimals)).quotient + ) + : new TokenAmount(token0, ZERO) - const tokenWorth: TokenAmount = exchangeTokenBalance - ? new TokenAmount(token, shareFraction.multiply(exchangeTokenBalance.raw).quotient) - : new TokenAmount(token, ZERO) + const token1Worth: TokenAmount = pairTokenBalances?.[token1.address] + ? new TokenAmount(token1, shareFraction.multiply(pairTokenBalances[token1.address].raw).quotient) + : new TokenAmount(token1, ZERO) const [approval, approve] = useApproveCallback(liquidityTokenAmount, MIGRATOR_ADDRESS) - const v1SpotPrice = - exchangeTokenBalance && exchangeETHBalance - ? exchangeTokenBalance.divide(new Fraction(exchangeETHBalance.raw, WEI_DENOM)) + const uniswapSpotPrice = + pairTokenBalances?.[token0.address] && pairTokenBalances?.[token1.address] + ? token0Worth.divide(new Fraction(token1Worth.raw, getDenom(token1.decimals))) : null const priceDifferenceFraction: Fraction | undefined = - v1SpotPrice && v2SpotPrice - ? v1SpotPrice - .divide(v2SpotPrice) + uniswapSpotPrice && mooniswapSpotPrice + ? uniswapSpotPrice + .divide(mooniswapSpotPrice) .multiply('100') .subtract('100') : undefined @@ -141,54 +160,45 @@ function V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount ? priceDifferenceFraction?.multiply('-1') : priceDifferenceFraction - const minAmountETH: JSBI | undefined = - v2SpotPrice && tokenWorth - ? tokenWorth - .divide(v2SpotPrice) - .multiply(WEI_DENOM) - .multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient - : ethWorth?.numerator - - const minAmountToken: JSBI | undefined = - v2SpotPrice && ethWorth - ? ethWorth - .multiply(v2SpotPrice) - .multiply(JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(token.decimals))) - .multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient - : tokenWorth?.numerator - const addTransaction = useTransactionAdder() const isMigrationPending = useIsTransactionPending(pendingMigrationHash) - const migrator = useV2MigratorContract() + const migrator = useMooniswapMigratorContract() + + let toPairAddress = mooniswapPair?.poolAddress const migrate = useCallback(() => { - if (!minAmountToken || !minAmountETH) return + + // if (!mooniswapPair || !minAmountToken || !minAmountETH) return + if (!toPairAddress) return setConfirmingMigration(true) migrator - .migrate( - token.address, - minAmountToken.toString(), - minAmountETH.toString(), - account, - Math.floor(new Date().getTime() / 1000) + DEFAULT_DEADLINE_FROM_NOW + .swap( + liquidityTokenAmount.token.address, + toPairAddress, + '0x' + liquidityTokenAmount.raw.toString(16), + '0x0', // set normal amount + new Array(34).fill(0), + '0x0' ) .then((response: TransactionResponse) => { + ReactGA.event({ category: 'Migrate', - action: 'V1->V2', - label: token?.symbol + action: 'V2->Mooniswap', + label: token0?.symbol + '/' + token1?.symbol }) addTransaction(response, { - summary: `Migrate ${token.symbol} liquidity to V2` + summary: `Migrate UNI-V2-${token0.symbol}-${token0.symbol} liquidity to Mooniswap` }) setPendingMigrationHash(response.hash) }) - .catch(() => { + .catch((e) => { + console.log(e) setConfirmingMigration(false) }) - }, [minAmountToken, minAmountETH, migrator, token, account, addTransaction]) + }, [toPairAddress, migrator, token0, token1, account, addTransaction]) const noLiquidityTokens = !!liquidityTokenAmount && liquidityTokenAmount.equalTo(ZERO) @@ -199,49 +209,42 @@ function V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount return ( - This tool will safely migrate your V1 liquidity to V2 with minimal price risk. The process is completely - trustless thanks to the{' '} - - Uniswap migration contract↗ - - . + This tool will safely migrate your Uniswap V2 liquidity to Mooniswap with minimal price risk . {!isFirstLiquidityProvider && largePriceDifference ? ( - - It{"'"}s best to deposit liquidity into Uniswap V2 at a price you believe is correct. If the V2 price seems - incorrect, you can either make a swap to move the price or wait for someone else to do so. - - V1 Price: + Uniswap V2 Price: - {v1SpotPrice?.toSignificant(6)} {token.symbol}/ETH + {uniswapSpotPrice?.toSignificant(6)} {token0.symbol}/{token1.symbol}
- {v1SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol} + {uniswapSpotPrice?.invert()?.toSignificant(6)} {token1.symbol}/{token0.symbol} - V2 Price: + Mooniswap Price: - {v2SpotPrice?.toSignificant(6)} {token.symbol}/ETH + {mooniswapSpotPrice?.toSignificant(6)} {token0.symbol.replace('WETH', 'ETH')}/{token1.symbol.replace('WETH', 'ETH')}
- {v2SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol} + {mooniswapSpotPrice?.invert()?.toSignificant(6)} {token1.symbol.replace('WETH', 'ETH')}/{token0.symbol.replace('WETH', 'ETH')} - Price Difference: + Price Difference: {priceDifferenceAbs.toSignificant(4)}% @@ -251,21 +254,20 @@ function V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount {isFirstLiquidityProvider && ( - You are the first liquidity provider for this pair on Uniswap V2. Your liquidity will be migrated at the - current V1 price. Your transaction cost also includes the gas to create the pool. + Mooniswap POOL for {mooniswapTokens[0].symbol}/{mooniswapTokens[1].symbol} hasn't created yet. First you need to create a pool - V1 Price: + Uniswap V2 Price: - {v1SpotPrice?.toSignificant(6)} {token.symbol}/ETH + {uniswapSpotPrice?.toSignificant(6)} {token0.symbol}/{token1.symbol}
- {v1SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol} + {uniswapSpotPrice?.invert()?.toSignificant(6)} {token1.symbol}/{token0.symbol} @@ -274,13 +276,23 @@ function V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount -
+ {isFirstLiquidityProvider && (
+ + Create Pool + +
)} + + {!isFirstLiquidityProvider && (
Migrating : 'Migrate'} -
+
)}
- {`Your Uniswap V1 ${token.symbol}/ETH liquidity will become Uniswap V2 ${token.symbol}/ETH liquidity.`} + {`Your Uniswap V2 ${token0.symbol}/${token1.symbol} liquidity will become Mooniswap ${mooniswapTokens[0].symbol}/${mooniswapTokens[1].symbol} liquidity.`} ) @@ -329,56 +341,41 @@ export default function MigrateV1Exchange({ const validatedAddress = isAddress(address) const { chainId, account } = useActiveWeb3React() - const exchangeContract = useV1ExchangeContract(validatedAddress ? validatedAddress : undefined) - const tokenAddress = useSingleCallResult(exchangeContract, 'tokenAddress', undefined, NEVER_RELOAD)?.result?.[0] + const tokenAddresses = usePairTokens(validatedAddress ? validatedAddress : undefined) - const token = useToken(tokenAddress) + const token0 = useToken(tokenAddresses[0]) + const token1 = useToken(tokenAddresses[1]) const liquidityToken: Token | undefined = useMemo( () => - validatedAddress && token - ? new Token(chainId, validatedAddress, 18, `UNI-V1-${token.symbol}`, 'Uniswap V1') + validatedAddress && token0 && token1 + ? new Token(chainId, validatedAddress, 18, `UNI-V2-${token0.symbol}-${token1.symbol}`, 'Uniswap V2') : undefined, - [chainId, validatedAddress, token] + [chainId, validatedAddress, token0, token1] ) const userLiquidityBalance = useTokenBalance(account, liquidityToken) // redirect for invalid url params - if (!validatedAddress || tokenAddress === AddressZero) { + if (!validatedAddress || tokenAddresses[0] === AddressZero || tokenAddresses[1] === AddressZero) { console.error('Invalid address in path', address) - return + return } return ( - - Migrate V1 Liquidity + + Migrate Liquidity
- +
{!account ? ( You must connect an account. - ) : validatedAddress && token?.equals(ETHER) ? ( - <> - - Because Uniswap V2 uses WETH under the hood, your Uniswap V1 WETH/ETH liquidity cannot be migrated. You - may want to remove your liquidity instead. - - - { - history.push(`/remove/v1/${validatedAddress}`) - }} - > - Remove - - - ) : userLiquidityBalance && token ? ( - + ) : userLiquidityBalance && token0 && token1 ? ( + ) : ( )} diff --git a/src/pages/MigrateV1/RemoveV1Exchange.tsx b/src/pages/MigrateV1/RemoveV1Exchange.tsx deleted file mode 100644 index bee7b1a7cbe..00000000000 --- a/src/pages/MigrateV1/RemoveV1Exchange.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import { TransactionResponse } from '@ethersproject/abstract-provider' -import { JSBI, Token, TokenAmount, Fraction, Percent, ETHER } from '@uniswap/sdk' -import React, { useCallback, useMemo, useState } from 'react' -import ReactGA from 'react-ga' -import { Redirect, RouteComponentProps } from 'react-router' -import { ButtonConfirmed } from '../../components/Button' -import { LightCard } from '../../components/Card' -import { AutoColumn } from '../../components/Column' -import QuestionHelper from '../../components/QuestionHelper' -import { AutoRow } from '../../components/Row' -import { DEFAULT_DEADLINE_FROM_NOW } from '../../constants' -import { useActiveWeb3React } from '../../hooks' -import { useToken } from '../../hooks/Tokens' -import { useV1ExchangeContract } from '../../hooks/useContract' -import { NEVER_RELOAD, useSingleCallResult } from '../../state/multicall/hooks' -import { useIsTransactionPending, useTransactionAdder } from '../../state/transactions/hooks' -import { useTokenBalance, useETHBalances } from '../../state/wallet/hooks' -import { BackArrow, TYPE } from '../../theme' -import { isAddress } from '../../utils' -import { BodyWrapper } from '../AppBody' -import { EmptyState } from './EmptyState' -import { V1LiquidityInfo } from './MigrateV1Exchange' -import { AddressZero } from '@ethersproject/constants' -import { Dots } from '../../components/swap/styleds' -import { Contract } from '@ethersproject/contracts' -import { useTotalSupply } from '../../data/TotalSupply' - -const WEI_DENOM = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18)) -const ZERO = JSBI.BigInt(0) -const ONE = JSBI.BigInt(1) -const ZERO_FRACTION = new Fraction(ZERO, ONE) - -function V1PairRemoval({ - exchangeContract, - liquidityTokenAmount, - token -}: { - exchangeContract: Contract - liquidityTokenAmount: TokenAmount - token: Token -}) { - // const { chainId } = useActiveWeb3React() - const totalSupply = useTotalSupply(liquidityTokenAmount.token) - const exchangeETHBalance = useETHBalances([liquidityTokenAmount.token.address])?.[liquidityTokenAmount.token.address] - const exchangeTokenBalance = useTokenBalance(liquidityTokenAmount.token.address, token) - - const [confirmingRemoval, setConfirmingRemoval] = useState(false) - const [pendingRemovalHash, setPendingRemovalHash] = useState(null) - - const shareFraction: Fraction = totalSupply ? new Percent(liquidityTokenAmount.raw, totalSupply.raw) : ZERO_FRACTION - - const ethWorth: TokenAmount = exchangeETHBalance - ? new TokenAmount(ETHER, exchangeETHBalance.multiply(shareFraction).multiply(WEI_DENOM).quotient) - : new TokenAmount(ETHER, ZERO) - - const tokenWorth: TokenAmount = exchangeTokenBalance - ? new TokenAmount(token, shareFraction.multiply(exchangeTokenBalance.raw).quotient) - : new TokenAmount(token, ZERO) - - const addTransaction = useTransactionAdder() - const isRemovalPending = useIsTransactionPending(pendingRemovalHash) - - const remove = useCallback(() => { - if (!liquidityTokenAmount) return - - setConfirmingRemoval(true) - exchangeContract - .removeLiquidity( - liquidityTokenAmount.raw.toString(), - 1, // min_eth, this is safe because we're removing liquidity - 1, // min_tokens, this is safe because we're removing liquidity - Math.floor(new Date().getTime() / 1000) + DEFAULT_DEADLINE_FROM_NOW - ) - .then((response: TransactionResponse) => { - ReactGA.event({ - category: 'Remove', - action: 'V1', - label: token?.symbol - }) - - addTransaction(response, { - summary: `Remove ${token.symbol}/ETH V1 liquidity` - }) - setPendingRemovalHash(response.hash) - }) - .catch(error => { - console.error(error) - setConfirmingRemoval(false) - }) - }, [exchangeContract, liquidityTokenAmount, token, addTransaction]) - - const noLiquidityTokens = !!liquidityTokenAmount && liquidityTokenAmount.equalTo(ZERO) - - const isSuccessfullyRemoved = !!pendingRemovalHash && !!noLiquidityTokens - - return ( - - - This tool will remove your V1 liquidity and send the underlying assets to your wallet. - - - - - -
- - {isSuccessfullyRemoved ? 'Success' : isRemovalPending ? Removing : 'Remove'} - -
-
- - {`Your Uniswap V1 ${ - token.symbol - }/ETH liquidity will be redeemed for underlying assets.`} - -
- ) -} - -export default function RemoveV1Exchange({ - match: { - params: { address } - } -}: RouteComponentProps<{ address: string }>) { - const validatedAddress = isAddress(address) - const { chainId, account } = useActiveWeb3React() - - const exchangeContract = useV1ExchangeContract(validatedAddress ? validatedAddress : undefined, true) - const tokenAddress = useSingleCallResult(exchangeContract, 'tokenAddress', undefined, NEVER_RELOAD)?.result?.[0] - const token = useToken(tokenAddress) - - const liquidityToken: Token | undefined = useMemo( - () => - validatedAddress && token - ? new Token(chainId, validatedAddress, 18, `UNI-V1-${token.symbol}`, 'Uniswap V1') - : undefined, - [chainId, validatedAddress, token] - ) - const userLiquidityBalance = useTokenBalance(account, liquidityToken) - - // redirect for invalid url params - if (!validatedAddress || tokenAddress === AddressZero) { - console.error('Invalid address in path', address) - return - } - - return ( - - - - - Remove V1 Liquidity -
- -
-
- - {!account ? ( - You must connect an account. - ) : userLiquidityBalance && token ? ( - - ) : ( - - )} -
-
- ) -} diff --git a/src/pages/MigrateV1/index.tsx b/src/pages/MigrateV1/index.tsx index 3c1406c9244..194f886317b 100644 --- a/src/pages/MigrateV1/index.tsx +++ b/src/pages/MigrateV1/index.tsx @@ -1,23 +1,22 @@ -import { JSBI, Token } from '@uniswap/sdk' -import React, { useCallback, useContext, useMemo, useState, useEffect } from 'react' +import { Token, TokenAmount } from '@uniswap/sdk' +import React, { useCallback, useContext, useState, useEffect } from 'react' import { ThemeContext } from 'styled-components' import { AutoColumn } from '../../components/Column' import { AutoRow } from '../../components/Row' import { SearchInput } from '../../components/SearchModal/styleds' -import { useAllTokenV1Exchanges } from '../../data/V1' import { useActiveWeb3React } from '../../hooks' import { useAllTokens, useToken } from '../../hooks/Tokens' import { useDefaultTokenList } from '../../state/lists/hooks' -import { useTokenBalancesWithLoadingIndicator } from '../../state/wallet/hooks' import { BackArrow, TYPE } from '../../theme' import { LightCard } from '../../components/Card' import { BodyWrapper } from '../AppBody' import { EmptyState } from './EmptyState' -import V1PositionCard from '../../components/PositionCard/V1' +import UniV2PositionCard from '../../components/PositionCard/V1' import QuestionHelper from '../../components/QuestionHelper' import { Dots } from '../../components/swap/styleds' import { useAddUserToken } from '../../state/user/hooks' import { isDefaultToken } from '../../utils' +import { useAllUniswapV2Pairs } from '../../data-mooniswap/UniswapV2' export default function MigrateV1() { const theme = useContext(ThemeContext) @@ -31,62 +30,74 @@ export default function MigrateV1() { const defaultTokens = useDefaultTokenList() const isDefault = isDefaultToken(defaultTokens, token) const allTokens = useAllTokens() + const addToken = useAddUserToken() + useEffect(() => { if (token && !isDefault && !allTokens[token.address]) { addToken(token) } }, [token, isDefault, addToken, allTokens]) - // get V1 LP balances - const V1Exchanges = useAllTokenV1Exchanges() - const V1LiquidityTokens: Token[] = useMemo(() => { - return Object.keys(V1Exchanges).map( - exchangeAddress => new Token(chainId, exchangeAddress, 18, 'UNI-V1', 'Uniswap V1') - ) - }, [chainId, V1Exchanges]) - const [V1LiquidityBalances, V1LiquidityBalancesLoading] = useTokenBalancesWithLoadingIndicator( - account, - V1LiquidityTokens - ) - const allV1PairsWithLiquidity = V1LiquidityTokens.filter(V1LiquidityToken => { - return ( - V1LiquidityBalances?.[V1LiquidityToken.address] && - JSBI.greaterThan(V1LiquidityBalances[V1LiquidityToken.address].raw, JSBI.BigInt(0)) - ) - }).map(V1LiquidityToken => { - return ( - ) - }) + allUniV2WithLiquidity.push(card); + } - // should never always be false, because a V1 exhchange exists for WETH on all testnets - const isLoading = Object.keys(V1Exchanges)?.length === 0 || V1LiquidityBalancesLoading + const isLoading = uniswapV2Pairs?.length === 0 || pairLoading return ( - Migrate V1 Liquidity + Migrate Liquidity
- For each pool shown below, click migrate to remove your liquidity from Uniswap V1 and deposit it into Uniswap - V2. + For each pool shown below, click migrate to remove your liquidity from Uniswap V2 and deposit it into Mooniswap. {!account ? ( - Connect to a wallet to view your V1 liquidity. + Connect to a wallet to view your Uniswap V2 liquidity. ) : isLoading ? ( @@ -104,10 +115,10 @@ export default function MigrateV1() { placeholder="Enter a token address to find liquidity" /> - {allV1PairsWithLiquidity?.length > 0 ? ( - <>{allV1PairsWithLiquidity} + {allUniV2WithLiquidity?.length > 0 ? ( + <>{allUniV2WithLiquidity} ) : ( - + )} )} diff --git a/src/pages/Pool/index.tsx b/src/pages/Pool/index.tsx index 4eadf0147cd..047692d333a 100644 --- a/src/pages/Pool/index.tsx +++ b/src/pages/Pool/index.tsx @@ -11,7 +11,7 @@ import { StyledInternalLink, TYPE } from '../../theme' import { Text } from 'rebass' import { LightCard } from '../../components/Card' import { RowBetween } from '../../components/Row' -import { ButtonPrimary } from '../../components/Button' +import { ButtonPrimary, ButtonSecondary } from '../../components/Button' import { AutoColumn } from '../../components/Column' import { useActiveWeb3React } from '../../hooks' @@ -118,7 +118,7 @@ export default function Pool() {
{'Don\'t see a pool you joined?'}{' '} - + {'Import it.'} @@ -129,6 +129,11 @@ export default function Pool() { +
+ + Migrate Liquidity to Mooniswap + +
) } From 1995e87adad0c905b86ad3c3335ac694e3049618 Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 25 Aug 2020 06:04:36 +0300 Subject: [PATCH 2/4] slippage --- src/pages/MigrateV1/MigrateV1Exchange.tsx | 32 +++++++++++++++++++---- src/utils/index.ts | 5 ++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/pages/MigrateV1/MigrateV1Exchange.tsx b/src/pages/MigrateV1/MigrateV1Exchange.tsx index 023399cf843..d908aadbff5 100644 --- a/src/pages/MigrateV1/MigrateV1Exchange.tsx +++ b/src/pages/MigrateV1/MigrateV1Exchange.tsx @@ -23,12 +23,19 @@ import { useMooniswapMigratorContract } from '../../hooks/useContract' import { useIsTransactionPending, useTransactionAdder } from '../../state/transactions/hooks' import { useETHBalances, useTokenBalance, useTokenBalances } from '../../state/wallet/hooks' import { BackArrow, ExternalLink, TYPE } from '../../theme' -import { getEtherscanLink, isAddress } from '../../utils' +import { + calculateSlippageAmount, + getContract, + getEtherscanLink, + getMooniswapMigratorContract, + isAddress +} from '../../utils' import { BodyWrapper } from '../AppBody' import { EmptyState } from './EmptyState' import { usePairTokens } from '../../data-mooniswap/UniswapV2' import DoubleCurrencyLogo from '../../components/DoubleLogo' import { Link } from 'react-router-dom' +import { useUserSlippageTolerance } from '../../state/user/hooks' const POOL_CURRENCY_AMOUNT_MIN = new Fraction(JSBI.BigInt(1), JSBI.BigInt(1000000)) const WEI_DENOM = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18)) @@ -108,9 +115,10 @@ export function V1LiquidityInfo({ } function V1PairMigration({ liquidityTokenAmount, token0, token1 }: { liquidityTokenAmount: TokenAmount; token0: Token, token1: Token }) { - const { account, chainId } = useActiveWeb3React() + const { account, chainId, library } = useActiveWeb3React() const totalSupply = useTotalSupply(liquidityTokenAmount.token) const pairTokenBalances = useTokenBalances(liquidityTokenAmount.token.address, [token0, token1]) + const [allowedSlippage] = useUserSlippageTolerance() let mooniswapTokens = [] if (token0.address === weth) { @@ -166,10 +174,24 @@ function V1PairMigration({ liquidityTokenAmount, token0, token1 }: { liquidityTo const migrator = useMooniswapMigratorContract() let toPairAddress = mooniswapPair?.poolAddress - const migrate = useCallback(() => { + const migrate = useCallback(async () => { // if (!mooniswapPair || !minAmountToken || !minAmountETH) return - if (!toPairAddress) return + if (!toPairAddress || !allowedSlippage) return + + const contract = getMooniswapMigratorContract(chainId, library, account) + const res = await contract.getExpectedReturn( + liquidityTokenAmount.token.address, + toPairAddress, + '0x' + liquidityTokenAmount.raw.toString(16), + '1', // set normal amount + '0x0' + ) + + const minReturn = calculateSlippageAmount( + new TokenAmount(mooniswapPair.liquidityToken, res.returnAmount), + allowedSlippage + )[0] setConfirmingMigration(true) migrator @@ -177,7 +199,7 @@ function V1PairMigration({ liquidityTokenAmount, token0, token1 }: { liquidityTo liquidityTokenAmount.token.address, toPairAddress, '0x' + liquidityTokenAmount.raw.toString(16), - '0x0', // set normal amount + '0x' + minReturn.toString(16), // set normal amount new Array(34).fill(0), '0x0' ) diff --git a/src/utils/index.ts b/src/utils/index.ts index acfa8cae9fd..e66c5bade39 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -11,6 +11,7 @@ import { ChainId, JSBI, Percent, Token, TokenAmount, ETHER } from '@uniswap/sdk' import { TokenAddressMap } from '../state/lists/hooks' import { V1_MOONISWAP_FACTORY_ADDRESSES } from '../constants/v1-mooniswap' import { ONE_SPLIT_ABI, ONE_SPLIT_ADDRESSES } from '../constants/one-split' +import { MIGRATOR_ABI, MIGRATOR_ADDRESS } from '../constants/abis/migrator' // returns the checksummed address if the address is valid, otherwise returns false export function isAddress(value: any): string | false { @@ -115,6 +116,10 @@ export function getOneSplit(chainId: ChainId, library: Web3Provider, account?: s return getContract(ONE_SPLIT_ADDRESSES[chainId], ONE_SPLIT_ABI, library, account) } +export function getMooniswapMigratorContract(chainId: ChainId, library: Web3Provider, account?: string) { + return getContract(MIGRATOR_ADDRESS, MIGRATOR_ABI, library, account) +} + export function getMooniswapContract(_: number, library: Web3Provider, pairAddress: string, account?: string) { return getContract(pairAddress, MooniswapABI, library, account) } From a32ff48a74bab99848ec237fa24657a525bf0fab Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 25 Aug 2020 06:08:54 +0300 Subject: [PATCH 3/4] refactor --- src/components/PositionCard/V1.tsx | 3 --- src/data-mooniswap/UniswapV2.ts | 7 +++---- src/pages/MigrateV1/MigrateV1Exchange.tsx | 22 ++++++++-------------- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/components/PositionCard/V1.tsx b/src/components/PositionCard/V1.tsx index 54f8bfd9da7..75377a65ba4 100644 --- a/src/components/PositionCard/V1.tsx +++ b/src/components/PositionCard/V1.tsx @@ -8,7 +8,6 @@ import { ButtonSecondary } from '../Button' import { RowBetween, RowFixed } from '../Row' import { FixedHeightRow, HoverCard } from './index' import DoubleCurrencyLogo from '../DoubleLogo' -import { useActiveWeb3React } from '../../hooks' import { ThemeContext } from 'styled-components' interface PositionCardProps extends RouteComponentProps<{}> { @@ -20,8 +19,6 @@ interface PositionCardProps extends RouteComponentProps<{}> { function UniV2PositionCard({ token0, token1, V1LiquidityBalance }: PositionCardProps) { const theme = useContext(ThemeContext) - const { chainId } = useActiveWeb3React() - return ( diff --git a/src/data-mooniswap/UniswapV2.ts b/src/data-mooniswap/UniswapV2.ts index eb22553cc94..ae719bc4a7b 100644 --- a/src/data-mooniswap/UniswapV2.ts +++ b/src/data-mooniswap/UniswapV2.ts @@ -1,8 +1,7 @@ import { useUniswapV2HelperContract, useUniswapV2PairContract } from '../hooks/useContract' import { useMemo } from 'react' -import { NEVER_RELOAD, useSingleCallResult, useSingleContractMultipleData } from '../state/multicall/hooks' +import { NEVER_RELOAD, useSingleCallResult } from '../state/multicall/hooks' import { BigNumber } from '@ethersproject/bignumber' -import { Token } from '@uniswap/sdk' export interface UniswapV2Pair { pair: string @@ -32,7 +31,7 @@ export function useAllUniswapV2Pairs(account: string): [UniswapV2Pair[], boolean return [data || [], res?.loading] }, - [account, res] + [res] ) } @@ -48,5 +47,5 @@ export function usePairTokens(pairAddress: string | undefined): string[] { } return [] }, - [contract, res0, res1]) + [res0, res1]) } diff --git a/src/pages/MigrateV1/MigrateV1Exchange.tsx b/src/pages/MigrateV1/MigrateV1Exchange.tsx index d908aadbff5..eb6a27d5282 100644 --- a/src/pages/MigrateV1/MigrateV1Exchange.tsx +++ b/src/pages/MigrateV1/MigrateV1Exchange.tsx @@ -5,14 +5,13 @@ import React, { useCallback, useMemo, useState } from 'react' import ReactGA from 'react-ga' import { Redirect, RouteComponentProps } from 'react-router' import { Text } from 'rebass' -import { ButtonConfirmed, ButtonPrimary, ButtonSecondary } from '../../components/Button' +import { ButtonConfirmed, ButtonPrimary } from '../../components/Button' import { LightCard, PinkCard, YellowCard } from '../../components/Card' import { AutoColumn } from '../../components/Column' import CurrencyLogo from '../../components/CurrencyLogo' import QuestionHelper from '../../components/QuestionHelper' import { AutoRow, RowBetween, RowFixed } from '../../components/Row' import { Dots } from '../../components/swap/styleds' -import { DEFAULT_DEADLINE_FROM_NOW, INITIAL_ALLOWED_SLIPPAGE } from '../../constants' import { MIGRATOR_ADDRESS } from '../../constants/abis/migrator' import { PairState, usePair } from '../../data-mooniswap/Reserves' import { useTotalSupply } from '../../data-mooniswap/TotalSupply' @@ -21,12 +20,10 @@ import { useToken } from '../../hooks/Tokens' import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback' import { useMooniswapMigratorContract } from '../../hooks/useContract' import { useIsTransactionPending, useTransactionAdder } from '../../state/transactions/hooks' -import { useETHBalances, useTokenBalance, useTokenBalances } from '../../state/wallet/hooks' -import { BackArrow, ExternalLink, TYPE } from '../../theme' +import { useTokenBalance, useTokenBalances } from '../../state/wallet/hooks' +import { BackArrow, TYPE } from '../../theme' import { calculateSlippageAmount, - getContract, - getEtherscanLink, getMooniswapMigratorContract, isAddress } from '../../utils' @@ -38,11 +35,9 @@ import { Link } from 'react-router-dom' import { useUserSlippageTolerance } from '../../state/user/hooks' const POOL_CURRENCY_AMOUNT_MIN = new Fraction(JSBI.BigInt(1), JSBI.BigInt(1000000)) -const WEI_DENOM = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18)) const ZERO = JSBI.BigInt(0) const ONE = JSBI.BigInt(1) const ZERO_FRACTION = new Fraction(ZERO, ONE) -const ALLOWED_OUTPUT_MIN_PERCENT = new Percent(JSBI.BigInt(10000 - INITIAL_ALLOWED_SLIPPAGE), JSBI.BigInt(10000)) const weth = isAddress('0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2') function getDenom(decimals: number): JSBI { @@ -173,16 +168,15 @@ function V1PairMigration({ liquidityTokenAmount, token0, token1 }: { liquidityTo const migrator = useMooniswapMigratorContract() - let toPairAddress = mooniswapPair?.poolAddress const migrate = useCallback(async () => { // if (!mooniswapPair || !minAmountToken || !minAmountETH) return - if (!toPairAddress || !allowedSlippage) return + if (!mooniswapPair || !allowedSlippage) return const contract = getMooniswapMigratorContract(chainId, library, account) const res = await contract.getExpectedReturn( liquidityTokenAmount.token.address, - toPairAddress, + mooniswapPair.poolAddress, '0x' + liquidityTokenAmount.raw.toString(16), '1', // set normal amount '0x0' @@ -197,9 +191,9 @@ function V1PairMigration({ liquidityTokenAmount, token0, token1 }: { liquidityTo migrator .swap( liquidityTokenAmount.token.address, - toPairAddress, + mooniswapPair.poolAddress, '0x' + liquidityTokenAmount.raw.toString(16), - '0x' + minReturn.toString(16), // set normal amount + '0x' + minReturn.toString(16), new Array(34).fill(0), '0x0' ) @@ -220,7 +214,7 @@ function V1PairMigration({ liquidityTokenAmount, token0, token1 }: { liquidityTo console.log(e) setConfirmingMigration(false) }) - }, [toPairAddress, migrator, token0, token1, account, addTransaction]) + }, [allowedSlippage, chainId, library, liquidityTokenAmount, mooniswapPair, migrator, token0, token1, account, addTransaction]) const noLiquidityTokens = !!liquidityTokenAmount && liquidityTokenAmount.equalTo(ZERO) From ba2bbf53eeaccd9c001607579218bee1f4342cdf Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 25 Aug 2020 14:07:00 +0300 Subject: [PATCH 4/4] update tokens --- src/constants/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants/index.ts b/src/constants/index.ts index ab183f43333..591fa2bf55d 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -157,6 +157,6 @@ export const BETTER_TRADE_LINK_THRESHOLD = new Percent(JSBI.BigInt(75), JSBI.Big // the Uniswap Default token list lives here export const DEFAULT_TOKEN_LIST_URL = - 'https://gateway.ipfs.io/ipfs/QmPgEqyV3m8SB52BS2j2mJpu9zGprhj2BGCHtRiiw2fdM1' + 'https://gateway.ipfs.io/ipfs/QmRZ5omgSSQrjzTXV59khwE2gf7buynx9GBQgegFK3zdUb' export const REFERRAL_ADDRESS_STORAGE_KEY = 'referral-address'