Commit 0c0305a5 authored by Ian Lapham's avatar Ian Lapham Committed by GitHub

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 c3f65e3a
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 c3f65e3a
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 24521f0c
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: default avatarNoah Zinsmeister <noahwz@gmail.com>
Co-authored-by: default avatarLint Action <lint-action@samuelmeuli.com>
Co-authored-by: default avatarCallil Capuozzo <callil.capuozzo@gmail.com>
parent c3f65e3a
REACT_APP_CHAIN_ID="1"
REACT_APP_NETWORK_URL="https://mainnet.infura.io/v3/4bf032f2d38a4ed6bb975b80d6340847"
SKIP_PREFLIGHT_CHECK=true
describe('Add Liquidity', () => {
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-tokenb .token-symbol-container').should('contain.text', 'ETH')
})
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-tokenb .token-symbol-container').should('not.contain.text', 'ETH')
})
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-tokenb .token-symbol-container').should('contain.text', 'MKR')
})
......@@ -23,28 +23,4 @@ describe('Add Liquidity', () => {
cy.visit('/add/0xF9bA5210F91D0474bd1e1DcDAeC4C58E359AaD85')
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'
)
})
})
......@@ -91,14 +91,14 @@
"selectFee": "Select Fee",
"selectLiquidityRange": "Select Liquidity Range",
"selectPool": "Select Fee Tier",
"inputTokens": "Input Tokens",
"depositAmounts": "Deposit Amounts",
"fee": "fee",
"setLimits": "Set Limits",
"percent": "Percent",
"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.",
"invalidRangeWarning": "Invalid Range",
"invalidRangeWarning": "Invalid Range entered. Lower range must be lower than upper range.",
"connectWallet": "Connect Wallet",
"unsupportedAsset": "Unsupported Asset",
"feePool": "Fee Pool",
......@@ -109,6 +109,8 @@
"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.",
"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",
"inputTokenDynamic": "Input {{label}}"
"selectRange": "Select Liquidity Range",
"inputTokenDynamic": "Input {{label}}",
"selectStartingPrice": "Select Starting Price",
"newPoolPrice": "Select the market rate for the tokens being added."
}
[
{
"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;
}
&:hover {
text-decoration: underline;
// text-decoration: underline;
opacity: 0.9;
}
&:active {
text-decoration: underline;
......@@ -377,6 +378,9 @@ const Circle = styled.div`
const CheckboxWrapper = styled.div`
width: 30px;
padding: 0 10px;
position: absolute;
top: 10px;
right: 10px;
`
export function ButtonRadioChecked({ active = false, children, ...rest }: { active?: boolean } & ButtonProps) {
......
......@@ -48,5 +48,4 @@ export const BlueCard = styled(Card)`
background-color: ${({ theme }) => theme.primary5};
color: ${({ theme }) => theme.blue2};
border-radius: 12px;
width: fit-content;
`
......@@ -15,6 +15,8 @@ import { ReactComponent as DropDown } from '../../assets/images/dropdown.svg'
import { useActiveWeb3React } from '../../hooks'
import { useTranslation } from 'react-i18next'
import useTheme from '../../hooks/useTheme'
import { Lock } from 'react-feather'
import { AutoColumn } from 'components/Column'
const InputPanel = styled.div<{ hideInput?: boolean }>`
${({ theme }) => theme.flexColumnNoWrap}
......@@ -25,6 +27,19 @@ const InputPanel = styled.div<{ hideInput?: boolean }>`
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 }>`
border-radius: ${({ hideInput }) => (hideInput ? '12px' : '20px')};
border: 1px solid ${({ theme }) => theme.bg2};
......@@ -134,6 +149,7 @@ interface CurrencyInputPanelProps {
id: string
showCommonBases?: boolean
customBalanceText?: string
locked?: boolean
}
export default function CurrencyInputPanel({
......@@ -144,14 +160,15 @@ export default function CurrencyInputPanel({
label = 'Input',
onCurrencySelect,
currency,
disableCurrencySelect = false,
hideBalance = false,
pair = null, // used for double token logo
hideInput = false,
otherCurrency,
id,
showCommonBases,
customBalanceText,
disableCurrencySelect = false,
hideBalance = false,
pair = null, // used for double token logo
hideInput = false,
locked = false,
...rest
}: CurrencyInputPanelProps) {
const { t } = useTranslation()
......@@ -167,6 +184,14 @@ export default function CurrencyInputPanel({
return (
<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}>
<InputRow style={hideInput ? { padding: '0', borderRadius: '8px' } : {}} selected={disableCurrencySelect}>
{!hideInput && (
......
......@@ -12,7 +12,7 @@ import { useTotalUniEarned } from '../../state/stake/hooks'
import { useAggregateUniBalance, useTokenBalance } from '../../state/wallet/hooks'
import { ExternalLink, StyledInternalLink, TYPE, UniTokenAnimated } from '../../theme'
import { computeUniCirculation } from '../../utils/computeUniCirculation'
import useUSDCPrice from '../../utils/useUSDCPrice'
import useUSDCPrice from '../../hooks/useUSDCPrice'
import { AutoColumn } from '../Column'
import { RowBetween } from '../Row'
import { Break, CardBGImage, CardNoise, CardSection, DataCard } from '../earn/styled'
......
import React, { useState, useCallback, useEffect } from 'react'
import { OutlineCard } from 'components/Card'
import { RowBetween } from 'components/Row'
import { ButtonGray } from 'components/Button'
import { TYPE } from 'theme'
import { Input as NumericalInput } from '../NumericalInput'
import styled, { keyframes, css } from 'styled-components'
import { TYPE } from 'theme'
import { AutoColumn } from 'components/Column'
const pulse = (color: string) => keyframes`
0% {
......@@ -22,7 +22,7 @@ const pulse = (color: string) => keyframes`
const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boolean }>`
border-color: ${({ active, theme }) => active && theme.blue1};
padding: 8px 12px;
padding: 12px;
${({ pulsing, theme }) =>
pulsing &&
......@@ -33,25 +33,22 @@ const FocusedOutlineCard = styled(OutlineCard)<{ active?: boolean; pulsing?: boo
const StyledInput = styled(NumericalInput)<{ usePercent?: boolean }>`
background-color: ${({ theme }) => theme.bg0};
text-align: ${({ usePercent }) => (usePercent ? 'right' : 'center')};
text-align: left;
margin-right: 2px;
`
const ContentWrapper = styled(RowBetween)`
padding: 0 8px;
width: 70%;
width: 92%;
`
interface StepCounterProps {
value: string
onUserInput: (value: string) => void
onIncrement?: () => void
onDecrement?: () => void
usePercent?: boolean
prependSymbol?: string | undefined
label?: string
width?: string
}
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
const [active, setActive] = useState(false)
......@@ -85,20 +82,9 @@ const StepCounter = ({ value, onUserInput, usePercent = false, prependSymbol }:
}
}, [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 (
<FocusedOutlineCard pulsing={pulsing} active={active} onFocus={handleOnFocus} onBlur={handleOnBlur}>
<RowBetween>
<ButtonGray padding="2px 0px" borderRadius="8px" onClick={handleDeccrement} width="50px">
<TYPE.label>-</TYPE.label>
</ButtonGray>
<FocusedOutlineCard pulsing={pulsing} active={active} onFocus={handleOnFocus} onBlur={handleOnBlur} width={width}>
<AutoColumn gap="md">
<ContentWrapper>
<StyledInput
className="rate-input-0"
......@@ -107,15 +93,10 @@ const StepCounter = ({ value, onUserInput, usePercent = false, prependSymbol }:
onUserInput={(val) => {
setLocalValue(val)
}}
prependSymbol={prependSymbol}
usePercent={usePercent}
/>
{usePercent && <TYPE.main>%</TYPE.main>}
</ContentWrapper>
<ButtonGray padding="2px 0px" borderRadius="8px" onClick={handleIncrement} width="50px">
<TYPE.label>+</TYPE.label>
</ButtonGray>
</RowBetween>
{label && <TYPE.label fontSize="12px">{label}</TYPE.label>}
</AutoColumn>
</FocusedOutlineCard>
)
}
......
import React from 'react'
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 { SUGGESTED_BASES } from '../../constants'
......
......@@ -79,7 +79,9 @@ export function CurrencySearch({
// if they input an address, use it
const isAddressSearch = isAddress(debouncedQuery)
const searchToken = useToken(debouncedQuery)
const searchTokenIsAdded = useIsUserAddedToken(searchToken)
useEffect(() => {
......
......@@ -18,8 +18,8 @@ import useAddTokenToMetamask from 'hooks/useAddTokenToMetamask'
const Wrapper = styled.div`
width: 100%;
`
const Section = styled(AutoColumn)`
padding: 24px;
const Section = styled(AutoColumn)<{ inline?: boolean }>`
padding: ${({ inline }) => (inline ? '0' : '24px')};
`
const BottomSection = styled(Section)`
......@@ -28,8 +28,8 @@ const BottomSection = styled(Section)`
border-bottom-right-radius: 20px;
`
const ConfirmedIcon = styled(ColumnCenter)`
padding: 60px 0;
const ConfirmedIcon = styled(ColumnCenter)<{ inline?: boolean }>`
padding: ${({ inline }) => (inline ? '20px 0' : '60px 0;')};
`
const StyledLogo = styled.img`
......@@ -38,19 +38,29 @@ const StyledLogo = styled.img`
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 (
<Wrapper>
<Section>
<AutoColumn gap="md">
{!inline && (
<RowBetween>
<div />
<CloseIcon onClick={onDismiss} />
</RowBetween>
<ConfirmedIcon>
<CustomLightSpinner src={Circle} alt="loader" size={'90px'} />
)}
<ConfirmedIcon inline={inline}>
<CustomLightSpinner src={Circle} alt="loader" size={inline ? '40px' : '90px'} />
</ConfirmedIcon>
<AutoColumn gap="12px" justify={'center'}>
<Text fontWeight={500} fontSize={20}>
<Text fontWeight={500} fontSize={20} textAlign="center">
Waiting For Confirmation
</Text>
<AutoColumn gap="12px" justify={'center'}>
......@@ -62,21 +72,23 @@ function ConfirmationPendingContent({ onDismiss, pendingText }: { onDismiss: ()
Confirm this transaction in your wallet
</Text>
</AutoColumn>
</Section>
</AutoColumn>
</Wrapper>
)
}
function TransactionSubmittedContent({
export function TransactionSubmittedContent({
onDismiss,
chainId,
hash,
currencyToAdd,
inline,
}: {
onDismiss: () => void
hash: string | undefined
chainId: ChainId
currencyToAdd?: Currency | undefined
inline?: boolean // not in modal
}) {
const theme = useContext(ThemeContext)
......@@ -86,16 +98,18 @@ function TransactionSubmittedContent({
return (
<Wrapper>
<Section>
<Section inline={inline}>
{!inline && (
<RowBetween>
<div />
<CloseIcon onClick={onDismiss} />
</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>
<AutoColumn gap="12px" justify={'center'}>
<Text fontWeight={500} fontSize={20}>
<Text fontWeight={500} fontSize={20} textAlign="center">
Transaction Submitted
</Text>
{chainId && hash && (
......@@ -121,7 +135,7 @@ function TransactionSubmittedContent({
)}
<ButtonPrimary onClick={onDismiss} style={{ margin: '20px 0 0 0' }}>
<Text fontWeight={500} fontSize={20}>
Close
{inline ? 'Return' : 'Close'}
</Text>
</ButtonPrimary>
</AutoColumn>
......@@ -145,7 +159,7 @@ export function ConfirmationModalContent({
<Wrapper>
<Section>
<RowBetween>
<Text fontWeight={500} fontSize={20}>
<Text fontWeight={500} fontSize={16}>
{title}
</Text>
<CloseIcon onClick={onDismiss} />
......
......@@ -13,8 +13,8 @@ import { currencyId } from '../../utils/currencyId'
import { Break, CardNoise, CardBGImage } from './styled'
import { unwrappedToken } from '../../utils/wrappedCurrency'
import { useTotalSupply } from '../../data/TotalSupply'
import { usePair } from '../../data/Reserves'
import useUSDCPrice from '../../utils/useUSDCPrice'
import { usePair } from '../../data/V2'
import useUSDCPrice from '../../hooks/useUSDCPrice'
import { BIG_INT_SECONDS_IN_WEEK } from '../../constants'
const StatContainer = styled.div`
......
......@@ -4,6 +4,14 @@ import JSBI from 'jsbi'
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 ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
......@@ -12,7 +20,7 @@ export { PRELOADED_PROPOSALS } from './proposals'
// a list of tokens by chain
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')
......@@ -57,6 +65,7 @@ const WETH_ONLY: ChainTokenList = {
[ChainId.RINKEBY]: [WETH9[ChainId.RINKEBY]],
[ChainId.GÖRLI]: [WETH9[ChainId.GÖRLI]],
[ChainId.KOVAN]: [WETH9[ChainId.KOVAN]],
[1337]: [WETH9[ChainId.KOVAN]],
}
// used to construct intermediary pairs for trading
......
......@@ -102,6 +102,23 @@
"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": "view",
......
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,
"chainId": 1,
"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 } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '',
[ChainId.RINKEBY]: '',
[ChainId.ROPSTEN]: '0xb31b9A7b331eA8993bdfC67c650eDbfc9256eC62',
[ChainId.RINKEBY]: '0xD8A6adFB40Ba3B3CdA9F985BF1fdbDc0c1d7591e',
[ChainId.GÖRLI]: '0xb31b9A7b331eA8993bdfC67c650eDbfc9256eC62',
[ChainId.KOVAN]: '',
}
export const TICK_LENS_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '',
[ChainId.RINKEBY]: '',
[ChainId.ROPSTEN]: '0x8E984b597F19E8D0FDd0b5bAfDb1d0ae4386455f',
[ChainId.RINKEBY]: '0xB1c59e8Ae4B72f63a5a9CB9c25A9253096A4b126',
[ChainId.GÖRLI]: '0x8E984b597F19E8D0FDd0b5bAfDb1d0ae4386455f',
[ChainId.KOVAN]: '',
}
export const NONFUNGIBLE_POSITION_MANAGER_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '',
[ChainId.RINKEBY]: '',
[ChainId.ROPSTEN]: '0x29e4bF3bFD649b807B4C752c01023E535094F6Bc',
[ChainId.RINKEBY]: '0xee9e30637f84Bbf929042A9118c6E20023dab833',
[ChainId.GÖRLI]: '0x29e4bF3bFD649b807B4C752c01023E535094F6Bc',
[ChainId.KOVAN]: '',
}
export const NONFUNGIBLE_TOKEN_POSITION_DESCRIPTOR_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '',
[ChainId.RINKEBY]: '',
[ChainId.ROPSTEN]: '0xa0588c89Fe967e66533aB1A0504C30989f90156f',
[ChainId.RINKEBY]: '0x3431b9Ed12e3204bC6f7039e1c576417B70fdD67',
[ChainId.GÖRLI]: '0xa0588c89Fe967e66533aB1A0504C30989f90156f',
[ChainId.KOVAN]: '',
}
export const SWAP_ROUTER_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '',
[ChainId.RINKEBY]: '',
[ChainId.ROPSTEN]: '0x71bB3d0e63f2Fa2A5d04d54267211f4Caef7062e',
[ChainId.RINKEBY]: '0xa0588c89Fe967e66533aB1A0504C30989f90156f',
[ChainId.GÖRLI]: '0x71bB3d0e63f2Fa2A5d04d54267211f4Caef7062e',
[ChainId.KOVAN]: '',
}
......@@ -43,7 +43,7 @@ export const SWAP_ROUTER_ADDRESSES: { [chainId in ChainId]: string } = {
export const V2_MIGRATOR_ADDRESSES: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: '',
[ChainId.ROPSTEN]: '',
[ChainId.RINKEBY]: '',
[ChainId.RINKEBY]: '0xb31b9A7b331eA8993bdfC67c650eDbfc9256eC62',
[ChainId.GÖRLI]: '0xee9e30637f84Bbf929042A9118c6E20023dab833',
[ChainId.KOVAN]: '',
}
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)]
}
import { Pair } from '@uniswap/v2-sdk'
import { TokenAmount, Currency } from '@uniswap/sdk-core'
import { useMemo } from 'react'
import { abi as IUniswapV2PairABI } from '@uniswap/v2-core/build/IUniswapV2Pair.json'
import { Interface } from '@ethersproject/abi'
......@@ -7,6 +6,7 @@ import { useActiveWeb3React } from '../hooks'
import { useMultipleContractSingleData } from '../state/multicall/hooks'
import { wrappedCurrency } from '../utils/wrappedCurrency'
import { Currency, TokenAmount } from '@uniswap/sdk-core'
const PAIR_INTERFACE = new Interface(IUniswapV2PairABI)
......
......@@ -5,7 +5,7 @@ import flatMap from 'lodash.flatmap'
import { useMemo } from 'react'
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 { useActiveWeb3React } from './index'
......
import { Token } from '@uniswap/sdk-core'
import { FeeAmount, TICK_SPACINGS } from '@uniswap/v3-sdk'
import { ZERO_ADDRESS } from '../constants'
import { useMemo } from 'react'
import { Result, useSingleContractMultipleData } from 'state/multicall/hooks'
import { useTickLens } from './useContract'
import { Result, useSingleCallResult, useSingleContractMultipleData } from 'state/multicall/hooks'
import { useTickLens, useV3Factory } from './useContract'
// the following should probably all be from the sdk, just mocking it for now
function MIN_TICK(tickSpacing: number) {
......@@ -25,8 +28,9 @@ interface TickData {
}
export function useAllV3Ticks(
poolAddress: string,
tickSpacing: number
token0: Token | undefined,
token1: Token | undefined,
feeAmount: FeeAmount | undefined
): {
loading: boolean
syncing: boolean
......@@ -34,22 +38,35 @@ export function useAllV3Ticks(
valid: boolean
tickData: TickData[]
} {
const tickLens = useTickLens()
const tickSpacing = useMemo(() => (feeAmount ? TICK_SPACINGS[feeAmount] : undefined), [feeAmount])
const minIndex = useMemo(() => (tickSpacing ? bitmapIndex(MIN_TICK(tickSpacing), tickSpacing) : undefined), [
tickSpacing,
])
const maxIndex = useMemo(() => (tickSpacing ? bitmapIndex(MAX_TICK(tickSpacing), tickSpacing) : undefined), [
tickSpacing,
])
const minIndex = useMemo(() => bitmapIndex(MIN_TICK(tickSpacing), tickSpacing), [tickSpacing])
const maxIndex = useMemo(() => bitmapIndex(MAX_TICK(tickSpacing), tickSpacing), [tickSpacing])
// 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 = useMemo(
const tickLensArgs: [string, number][] = useMemo(
() =>
new Array(maxIndex - minIndex + 1)
maxIndex && minIndex && poolAddress && poolAddress !== ZERO_ADDRESS
? new Array(maxIndex - minIndex + 1)
.fill(0)
.map((_, i) => i + minIndex)
.map((wordIndex) => [poolAddress, wordIndex]),
.map((wordIndex) => [poolAddress, wordIndex])
: [],
[minIndex, maxIndex, poolAddress]
)
const tickLens = useTickLens()
const callStates = useSingleContractMultipleData(
tickLens,
tickLensArgs.length > 0 ? tickLens : undefined,
'getPopulatedTicksInWord',
tickLensArgs,
REFRESH_FREQUENCY,
......
import { UniswapV3Pool } from './../types/v3/UniswapV3Pool.d'
import { Contract } from '@ethersproject/contracts'
import { abi as GOVERNANCE_ABI } from '@uniswap/governance/build/GovernorAlpha.json'
import { abi as UNI_ABI } from '@uniswap/governance/build/Uni.json'
......@@ -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 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 V3PoolABI } from '@uniswap/v3-core/artifacts/contracts/UniswapV3Pool.sol/UniswapV3Pool.json'
import ARGENT_WALLET_DETECTOR_ABI from 'abis/argent-wallet-detector.json'
import ENS_PUBLIC_RESOLVER_ABI from 'abis/ens-public-resolver.json'
......@@ -24,8 +26,9 @@ import {
MERKLE_DISTRIBUTOR_ADDRESS,
V1_MIGRATOR_ADDRESS,
UNI,
MULTICALL_ADDRESSES,
} 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 { NONFUNGIBLE_POSITION_MANAGER_ADDRESSES, FACTORY_ADDRESSES, TICK_LENS_ADDRESSES } from 'constants/v3'
import { useMemo } from 'react'
......@@ -35,7 +38,7 @@ import { getContract } from 'utils'
import { useActiveWeb3React } from './index'
// 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()
return useMemo(() => {
......@@ -65,10 +68,10 @@ export function useV1ExchangeContract(address?: string, withSignerIfPossible?: b
export function useTokenContract(tokenAddress?: string, withSignerIfPossible?: boolean): Contract | null {
return useContract(tokenAddress, ERC20_ABI, withSignerIfPossible)
}
export function useWETHContract(withSignerIfPossible?: boolean): Contract | null {
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 {
......@@ -110,7 +113,7 @@ export function usePairContract(pairAddress?: string, withSignerIfPossible?: boo
export function useMulticallContract(): Contract | null {
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 {
......@@ -152,6 +155,10 @@ export function useV3Factory(): 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 {
const { chainId } = useActiveWeb3React()
const address = chainId ? TICK_LENS_ADDRESSES[chainId] : undefined
......
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 { useMemo } from 'react'
import { USDC } from '../constants'
import { PairState, usePairs } from '../data/Reserves'
import { PairState, usePairs } from '../data/V2'
import { useActiveWeb3React } from '../hooks'
import { wrappedCurrency } from './wrappedCurrency'
import { wrappedCurrency } from '../utils/wrappedCurrency'
/**
* Returns the price in USDC of the input currency
......
......@@ -12,7 +12,7 @@ import useTheme from 'hooks/useTheme'
import { AlertOctagon } from 'react-feather'
import { ToggleWrapper, ToggleElement } from 'components/Toggle/MultiToggle'
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)`
padding: 1rem 0;
......@@ -27,7 +27,6 @@ export function ConfirmContent({
price?: Price
currencies: { [field in Field]?: Currency }
parsedAmounts: { [field in Field]?: CurrencyAmount }
poolTokenPercentage?: Percent
onAdd: () => void
}) {
const currencyA: Currency | undefined = currencies[Field.CURRENCY_A]
......
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
This diff is collapsed.
......@@ -2,32 +2,16 @@ import React from 'react'
import { Redirect, RouteComponentProps } from 'react-router-dom'
import AddLiquidity from './index'
export function RedirectToAddLiquidity() {
return <Redirect to="/add/" />
}
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 }>) {
export function RedirectDuplicateTokenIds(
props: RouteComponentProps<{ currencyIdA: string; currencyIdB: string; feeAmount?: string }>
) {
const {
match: {
params: { currencyIdA, currencyIdB },
},
} = props
if (currencyIdA.toLowerCase() === currencyIdB.toLowerCase()) {
if (currencyIdA && currencyIdB && currencyIdA.toLowerCase() === currencyIdB.toLowerCase()) {
return <Redirect to={`/add/${currencyIdA}`} />
}
return <AddLiquidity {...props} />
......
......@@ -2,6 +2,7 @@ import styled from 'styled-components'
import { AutoColumn } from 'components/Column'
import CurrencyInputPanel from 'components/CurrencyInputPanel'
import { DarkGreyCard } from 'components/Card'
import Input from 'components/NumericalInput'
export const ScrollablePage = styled.div`
position: relative;
......@@ -10,18 +11,21 @@ export const ScrollablePage = styled.div`
`
export const ScrollableContent = styled.div`
margin-right: 24px;
margin-right: 16px;
`
export const FixedPreview = styled.div`
position: relative;
padding: 8px;
padding: 16px;
width: 260px;
height: fit-content;
margin-top: 42px;
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),
0px 24px 32px rgba(0, 0, 0, 0.01);
border-radius: 12px;
position: sticky;
top: 120px;
`
export const DynamicSection = styled(AutoColumn)<{ disabled?: boolean }>`
......@@ -30,7 +34,7 @@ export const DynamicSection = styled(AutoColumn)<{ disabled?: boolean }>`
`
export const CurrencyDropdown = styled(CurrencyInputPanel)`
width: 49%;
width: 48.5%;
`
export const PreviewCard = styled(DarkGreyCard)<{ disabled?: boolean }>`
......@@ -42,3 +46,10 @@ export const PreviewCard = styled(DarkGreyCard)<{ disabled?: boolean }>`
align-items: center;
justify-content: center;
`
export const StyledInput = styled(Input)`
background-color: ${({ theme }) => theme.bg0};
text-align: left;
font-size: 18px;
width: 100%;
`
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>
</>
)
}
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>
)
}
This diff is collapsed.
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 { useModalOpen, useToggleModal } from '../state/application/hooks'
import DarkModeQueryParamReader from '../theme/DarkModeQueryParamReader'
import AddLiquidity from './AddLiquidity'
import {
RedirectDuplicateTokenIds,
RedirectOldAddLiquidityPathStructure,
RedirectToAddLiquidity,
} from './AddLiquidity/redirects'
import { RedirectDuplicateTokenIds } from './AddLiquidity/redirects'
import Earn from './Earn'
import Manage from './Earn/Manage'
import MigrateV1 from './MigrateV1'
......@@ -34,12 +29,15 @@ import Swap from './Swap'
import { OpenClaimAddressModalAndRedirectToSwap, RedirectPathToSwapOnly, RedirectToSwap } from './Swap/redirects'
import Vote from './Vote'
import VotePage from './Vote/VotePage'
import { RedirectDuplicateTokenIdsV2 } from './AddLiquidityV2/redirects'
import AddLiquidity from './AddLiquidity'
import AddLiquidityV2 from './AddLiquidityV2'
const AppWrapper = styled.div`
display: flex;
flex-flow: column;
align-items: flex-start;
overflow-x: hidden;
/* overflow-x: hidden; */
`
const BodyWrapper = styled.div`
......@@ -49,8 +47,8 @@ const BodyWrapper = styled.div`
padding-top: 160px;
align-items: center;
flex: 1;
overflow-y: auto;
overflow-x: hidden;
/* overflow-y: auto; */
/* overflow-x: hidden; */
${({ theme }) => theme.mediaWidth.upToSmall`
padding: 16px;
......@@ -108,13 +106,20 @@ export default function App() {
<Route exact strict path="/find" component={PoolFinder} />
<Route exact strict path="/pool/v2" component={PoolV2} />
<Route exact strict path="/pool" component={Pool} />
<Route exact strict path="/create" component={RedirectToAddLiquidity} />
<Route exact path="/add" component={AddLiquidity} />
<Route exact path="/add/:currencyIdA" component={RedirectOldAddLiquidityPathStructure} />
<Route exact path="/add/:currencyIdA/:currencyIdB" component={RedirectDuplicateTokenIds} />
<Route exact path="/create" component={AddLiquidity} />
<Route exact path="/create/:currencyIdA" component={RedirectOldAddLiquidityPathStructure} />
<Route exact path="/create/:currencyIdA/:currencyIdB" component={RedirectDuplicateTokenIds} />
<Route exact path="/add/v2/" component={AddLiquidityV2} />
<Route exact path="/add/v2/:currencyIdA" component={AddLiquidityV2} />
<Route exact path="/add/:currencyIdA" component={AddLiquidity} />
<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/v2/:tokens" component={RedirectOldRemoveLiquidityPathStructure} />
<Route exact strict path="/remove/v2/:currencyIdA/:currencyIdB" component={RemoveLiquidity} />
......
import React from 'react'
import styled from 'styled-components'
export const BodyWrapper = styled.div`
export const BodyWrapper = styled.div<{ margin?: string }>`
position: relative;
margin-top: ${({ margin }) => margin ?? '0px'};
max-width: 460px;
width: 100%;
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.
*/
export default function AppBody({ children }: { children: React.ReactNode }) {
return <BodyWrapper>{children}</BodyWrapper>
export default function AppBody({ children, ...rest }: { children: React.ReactNode }) {
return <BodyWrapper {...rest}>{children}</BodyWrapper>
}
......@@ -26,9 +26,9 @@ import { CountUp } from 'use-count-up'
import { wrappedCurrency } from '../../utils/wrappedCurrency'
import { currencyId } from '../../utils/currencyId'
import { useTotalSupply } from '../../data/TotalSupply'
import { usePair } from '../../data/Reserves'
import { usePair } from '../../data/V2'
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'
const PageWrapper = styled(AutoColumn)`
......
......@@ -14,13 +14,12 @@ import FormattedCurrencyAmount from '../../components/FormattedCurrencyAmount'
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, V1_MIGRATOR_ADDRESS } from 'constants/index'
import { PairState, usePair } from '../../data/Reserves'
import { DEFAULT_DEADLINE_FROM_NOW, INITIAL_ALLOWED_SLIPPAGE, V1_MIGRATOR_ADDRESS } from '../../constants'
import { PairState, usePair } from '../../data/V2'
import { useTotalSupply } from '../../data/TotalSupply'
import { useActiveWeb3React } from '../../hooks'
import { useToken } from '../../hooks/Tokens'
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
import { useV1ExchangeContract, useV1MigratorContract } from '../../hooks/useContract'
import { NEVER_RELOAD, useSingleCallResult } from '../../state/multicall/hooks'
import { useIsTransactionPending, useTransactionAdder } from '../../state/transactions/hooks'
import { useETHBalances, useTokenBalance } from '../../state/wallet/hooks'
......@@ -28,6 +27,7 @@ import { BackArrow, ExternalLink, TYPE } from '../../theme'
import { getEtherscanLink, isAddress } from '../../utils'
import { BodyWrapper } from '../AppBody'
import { EmptyState } from './EmptyState'
import { useV1ExchangeContract, useV1MigratorContract } from 'hooks/useContract'
const WEI_DENOM = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18))
const ZERO = JSBI.BigInt(0)
......
import { TransactionResponse } from '@ethersproject/abstract-provider'
import { AddressZero } from '@ethersproject/constants'
import { Currency, CurrencyAmount, Fraction, Percent, Price, Token, TokenAmount, WETH9 } from '@uniswap/sdk-core'
import React, { useMemo } from 'react'
import { Currency, CurrencyAmount, Price, Token, TokenAmount, WETH9 } from '@uniswap/sdk-core'
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 { Text } from 'rebass'
import { ButtonConfirmed } from '../../components/Button'
import { LightCard, PinkCard, YellowCard } from '../../components/Card'
import { AutoColumn } from '../../components/Column'
import CurrencyLogo from '../../components/CurrencyLogo'
import FormattedCurrencyAmount from '../../components/FormattedCurrencyAmount'
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, V1_MIGRATOR_ADDRESS } from 'constants/index'
import { PairState, usePair } from '../../data/Reserves'
import { useTotalSupply } from '../../data/TotalSupply'
import { useActiveWeb3React } from '../../hooks'
import { useToken } from '../../hooks/Tokens'
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
import { usePairContract, useV1MigratorContract } from '../../hooks/useContract'
import { usePairContract } from '../../hooks/useContract'
import { NEVER_RELOAD, useSingleCallResult } from '../../state/multicall/hooks'
import { useIsTransactionPending, useTransactionAdder } from '../../state/transactions/hooks'
import { useETHBalances, useTokenBalance } from '../../state/wallet/hooks'
import { useTokenBalance } from '../../state/wallet/hooks'
import { BackArrow, ExternalLink, TYPE } from '../../theme'
import { getEtherscanLink, isAddress } from '../../utils'
import { BodyWrapper } from '../AppBody'
import { EmptyState } from '../MigrateV1/EmptyState'
import { toV2LiquidityToken } from 'state/user/hooks'
import { V2_MIGRATOR_ADDRESSES } from 'constants/v3'
// TODO the whole file
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 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))
export function V2LiquidityInfo({
token,
......@@ -105,7 +94,7 @@ function V2PairMigration({
token0: Token
token1: Token
}) {
const { account, chainId } = useActiveWeb3React()
const { chainId } = useActiveWeb3React()
// this is just getLiquidityValue with the fee off, but for the passed pair
const token0Value = useMemo(
......@@ -119,6 +108,8 @@ function V2PairMigration({
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 [confirmingMigration, setConfirmingMigration] = useState<boolean>(false)
......
......@@ -13,8 +13,8 @@ import { EmptyState } from '../MigrateV1/EmptyState'
import QuestionHelper from '../../components/QuestionHelper'
import { Dots } from '../../components/swap/styleds'
import { toV2LiquidityToken, useTrackedTokenPairs } from '../../state/user/hooks'
import { usePairs } from 'data/Reserves'
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 add support for more pairs
......
......@@ -15,7 +15,7 @@ import { ButtonPrimary, ButtonSecondary } from '../../components/Button'
import { AutoColumn } from '../../components/Column'
import { useActiveWeb3React } from '../../hooks'
import { usePairs } from '../../data/Reserves'
import { usePairs } from '../../data/V2'
import { toV2LiquidityToken, useTrackedTokenPairs } from '../../state/user/hooks'
import { Dots } from '../../components/swap/styleds'
import { CardSection, DataCard, CardNoise, CardBGImage } from '../../components/earn/styled'
......
......@@ -11,7 +11,7 @@ import { FindPoolTabs } from '../../components/NavigationTabs'
import { MinimalPositionCard } from '../../components/PositionCard'
import Row from '../../components/Row'
import CurrencySearchModal from '../../components/SearchModal/CurrencySearchModal'
import { PairState, usePair } from '../../data/Reserves'
import { PairState, usePair } from '../../data/V2'
import { useActiveWeb3React } from '../../hooks'
import { usePairAdder } from '../../state/user/hooks'
import { useTokenBalance } from '../../state/wallet/hooks'
......
......@@ -8,5 +8,6 @@ export default function RemoveLiquidityV3({
params: { currencyIdA, currencyIdB, fee },
},
}: RouteComponentProps<{ currencyIdA: string; currencyIdB: string; fee: string }>) {
console.log(currencyIdA, currencyIdB, fee)
return <AppBody>TODO</AppBody>
}
......@@ -31,7 +31,7 @@ import { useTransactionAdder } from '../../state/transactions/hooks'
import { StyledInternalLink, TYPE } from '../../theme'
import { calculateGasMargin, calculateSlippageAmount, getRouterContract } from '../../utils'
import { currencyId } from '../../utils/currencyId'
import useDebouncedChangeHandler from '../../utils/useDebouncedChangeHandler'
import useDebouncedChangeHandler from '../../hooks/useDebouncedChangeHandler'
import { wrappedCurrency } from '../../utils/wrappedCurrency'
import AppBody from '../AppBody'
import { ClickableText, MaxButton, Wrapper } from '../Pool/styleds'
......
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 { useCallback } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { usePair } from '../../data/Reserves'
import { usePair } from '../../data/V2'
import { useTotalSupply } from '../../data/TotalSupply'
import { useActiveWeb3React } from '../../hooks'
......
......@@ -42,7 +42,6 @@ const EMPTY_LIST: TokenAddressMap = {
[ChainId.ROPSTEN]: {},
[ChainId.GÖRLI]: {},
[ChainId.MAINNET]: {},
[1337]: {},
}
const listCache: WeakMap<TokenList, TokenAddressMap> | null =
......@@ -99,7 +98,6 @@ function combineMaps(map1: TokenAddressMap, map2: TokenAddressMap): TokenAddress
4: { ...map1[4], ...map2[4] },
5: { ...map1[5], ...map2[5] },
42: { ...map1[42], ...map2[42] },
1337: { ...map1[1337], ...map2[1337] },
}
}
......
......@@ -6,11 +6,11 @@ export enum Field {
}
export enum Bound {
CURRENT = 'CURRENT',
LOWER = 'LOWER',
UPPER = 'UPPER',
}
// save for % inputs
export enum RangeType {
PERCENT = 'PERCENT',
RATE = 'RATE',
......@@ -19,5 +19,5 @@ export enum RangeType {
export const typeInput = createAction<{ field: Field; typedValue: string; noLiquidity: boolean }>('mint/typeInputMint')
export const typeLowerRangeInput = createAction<{ typedValue: string }>('mint/typeLowerRangeInput')
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')
This diff is collapsed.
......@@ -5,37 +5,30 @@ import {
typeInput,
typeLowerRangeInput,
typeUpperRangeInput,
RangeType,
updateRangeType,
typeStartPriceInput,
} from './actions'
export interface MintState {
readonly independentField: Field
readonly typedValue: string
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 upperRangeTypedValue: string
readonly rangeType: RangeType
}
export const initialState: MintState = {
independentField: Field.CURRENCY_A,
typedValue: '',
otherTypedValue: '',
startPriceTypedValue: '',
lowerRangeTypedValue: '',
upperRangeTypedValue: '',
rangeType: RangeType.RATE,
}
export default createReducer<MintState>(initialState, (builder) =>
builder
.addCase(resetMintState, () => initialState)
.addCase(updateRangeType, (state, { payload: { rangeType } }) => {
return {
...state,
rangeType,
}
})
.addCase(typeLowerRangeInput, (state, { payload: { typedValue } }) => {
return {
...state,
......@@ -48,6 +41,12 @@ export default createReducer<MintState>(initialState, (builder) =>
upperRangeTypedValue: typedValue,
}
})
.addCase(typeStartPriceInput, (state, { payload: { typedValue } }) => {
return {
...state,
startPriceTypedValue: typedValue,
}
})
.addCase(typeInput, (state, { payload: { field, typedValue, noLiquidity } }) => {
if (noLiquidity) {
// they're typing into the field they've last typed in
......
import { Tick } from './hooks'
/**
* @todo
* udpate to actually parse input and calculate next tick
*/
export function tryParseTick(value?: string): Tick | undefined {
if (!value) {
import { Pool, priceToClosestTick, nearestUsableTick } from '@uniswap/v3-sdk/dist/'
import { Price, Token } from '@uniswap/sdk-core'
import { tryParseAmount } from 'state/swap/hooks'
export function tryParseTick(
baseToken: Token | undefined,
quoteToken: Token | undefined,
pool: Pool | undefined,
value?: string
): number | undefined {
if (!value || !baseToken || !quoteToken || !pool) {
return undefined
}
try {
return { rate: parseFloat(value) * 0.999 }
} catch (error) {
console.debug(`Failed to parse range amount: "${value}"`, error)
}
const amount = tryParseAmount(value, quoteToken)
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)
}
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(
() =>
contract && fragment && callInputs && callInputs.length > 0
contract && fragment && callInputs?.length > 0 && callInputs.every((inputs) => isValidMethodArgs(inputs))
? callInputs.map<Call>((inputs) => {
return {
address: contract.address,
......@@ -180,7 +180,7 @@ export function useSingleContractMultipleData(
}
})
: [],
[callInputs, contract, fragment, gasRequired]
[contract, fragment, callInputs, gasRequired]
)
const results = useCallsData(calls, options)
......
......@@ -53,6 +53,7 @@ export function colors(darkMode: boolean): Colors {
bg3: darkMode ? '#40444F' : '#EDEEF2',
bg4: darkMode ? '#565A69' : '#CED0D9',
bg5: darkMode ? '#6C7284' : '#888D9B',
bg6: darkMode ? '#1A2028' : '#888D9B',
//specialty colors
modalBG: darkMode ? 'rgba(0,0,0,.425)' : 'rgba(0,0,0,0.3)',
......
......@@ -20,6 +20,7 @@ export interface Colors {
bg3: Color
bg4: Color
bg5: Color
bg6: Color
modalBG: Color
advancedBG: Color
......
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 {
feesEarned: Record<string, BigNumberish>
......
......@@ -12,7 +12,7 @@ export function hexToUint8Array(hex: string): Uint8Array {
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
......
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)
}
import { FeeAmount } from '@uniswap/v3-sdk'
import { Contract } from '@ethersproject/contracts'
import { getAddress } from '@ethersproject/address'
import { AddressZero } from '@ethersproject/constants'
import { JsonRpcSigner, Web3Provider } from '@ethersproject/providers'
import { BigNumber } from '@ethersproject/bignumber'
import { abi as IUniswapV2Router02ABI } from '@uniswap/v2-periphery/build/IUniswapV2Router02.json'
import { ROUTER_ADDRESS } from '../constants'
import { ChainId, Percent, Token, CurrencyAmount, Currency, ETHER } from '@uniswap/sdk-core'
import { JSBI } from '@uniswap/v2-sdk'
......@@ -123,3 +125,7 @@ export function supportedChainId(chainId: number): ChainId | 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'
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(
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment