Commit 361d17c9 authored by Noah Zinsmeister's avatar Noah Zinsmeister

add permit support to migration

parent 9269f15f
...@@ -35,6 +35,11 @@ import { useDerivedMintInfo, useMintActionHandlers } from 'state/mint/hooks' ...@@ -35,6 +35,11 @@ import { useDerivedMintInfo, useMintActionHandlers } from 'state/mint/hooks'
import { Bound } from 'state/mint/actions' import { Bound } from 'state/mint/actions'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { ChevronDown } from 'react-feather' import { ChevronDown } from 'react-feather'
import useIsArgentWallet from 'hooks/useIsArgentWallet'
import { Contract } from '@ethersproject/contracts'
import { splitSignature } from '@ethersproject/bytes'
import { BigNumber } from '@ethersproject/bignumber'
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
const ZERO = JSBI.BigInt(0) const ZERO = JSBI.BigInt(0)
...@@ -71,6 +76,7 @@ function LiquidityInfo({ token0Amount, token1Amount }: { token0Amount: TokenAmou ...@@ -71,6 +76,7 @@ function LiquidityInfo({ token0Amount, token1Amount }: { token0Amount: TokenAmou
const percentageToMigrate = 100 const percentageToMigrate = 100
function V2PairMigration({ function V2PairMigration({
pair,
pairBalance, pairBalance,
totalSupply, totalSupply,
reserve0, reserve0,
...@@ -78,6 +84,7 @@ function V2PairMigration({ ...@@ -78,6 +84,7 @@ function V2PairMigration({
token0, token0,
token1, token1,
}: { }: {
pair: Contract
pairBalance: TokenAmount pairBalance: TokenAmount
totalSupply: TokenAmount totalSupply: TokenAmount
reserve0: TokenAmount reserve0: TokenAmount
...@@ -86,9 +93,10 @@ function V2PairMigration({ ...@@ -86,9 +93,10 @@ function V2PairMigration({
token1: Token token1: Token
}) { }) {
const { t } = useTranslation() const { t } = useTranslation()
const { chainId, account } = useActiveWeb3React() const { chainId, account, library } = useActiveWeb3React()
const deadline = useTransactionDeadline() // custom from users settings const deadline = useTransactionDeadline() // custom from users settings
const blockTimestamp = useCurrentBlockTimestamp()
const [allowedSlippage] = useUserSlippageTolerance() // custom from users const [allowedSlippage] = useUserSlippageTolerance() // custom from users
// this is just getLiquidityValue with the fee off, but for the passed pair // this is just getLiquidityValue with the fee off, but for the passed pair
...@@ -184,18 +192,93 @@ function V2PairMigration({ ...@@ -184,18 +192,93 @@ function V2PairMigration({
const [confirmingMigration, setConfirmingMigration] = useState<boolean>(false) const [confirmingMigration, setConfirmingMigration] = useState<boolean>(false)
const [pendingMigrationHash, setPendingMigrationHash] = useState<string | null>(null) const [pendingMigrationHash, setPendingMigrationHash] = useState<string | null>(null)
// TODO add permit approval const migrator = useV2MigratorContract()
const [approval, approve] = useApproveCallback(pairBalance, chainId ? V2_MIGRATOR_ADDRESSES[chainId] : undefined)
// approvals
const [signatureData, setSignatureData] = useState<{ v: number; r: string; s: string; deadline: BigNumber } | null>(
null
)
const [approval, approveManually] = useApproveCallback(
pairBalance,
chainId ? V2_MIGRATOR_ADDRESSES[chainId] : undefined
)
const isArgentWallet = useIsArgentWallet()
const approve = useCallback(async () => {
if (!account || !migrator || !deadline || !chainId || !library) return
if (isArgentWallet) {
return approveManually()
}
// try to gather a signature for permission
const nonce = await pair.nonces(account)
const EIP712Domain = [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
]
const domain = {
name: 'Uniswap V2',
version: '1',
chainId: chainId,
verifyingContract: pair.address,
}
const Permit = [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
]
const message = {
owner: account,
spender: migrator.address,
value: `0x${pairBalance.raw.toString(16)}`,
nonce: nonce.toHexString(),
deadline: deadline.toHexString(),
}
const data = JSON.stringify({
types: {
EIP712Domain,
Permit,
},
domain,
primaryType: 'Permit',
message,
})
library
.send('eth_signTypedData_v4', [account, data])
.then(splitSignature)
.then((signature) => {
setSignatureData({
v: signature.v,
r: signature.r,
s: signature.s,
deadline,
})
})
.catch((error) => {
// for all errors other than 4001 (EIP-1193 user rejected request), fall back to manual approve
if (error?.code !== 4001) {
console.log('here?')
approveManually()
}
})
}, [account, isArgentWallet, approveManually, pair, pairBalance, migrator, deadline, chainId, library])
const addTransaction = useTransactionAdder() const addTransaction = useTransactionAdder()
const isMigrationPending = useIsTransactionPending(pendingMigrationHash ?? undefined) const isMigrationPending = useIsTransactionPending(pendingMigrationHash ?? undefined)
const migrator = useV2MigratorContract()
const migrate = useCallback(() => { const migrate = useCallback(() => {
if ( if (
!migrator || !migrator ||
!account || !account ||
!deadline || !deadline ||
!blockTimestamp ||
typeof tickLower !== 'number' || typeof tickLower !== 'number' ||
typeof tickUpper !== 'number' || typeof tickUpper !== 'number' ||
!v3Amount0Min || !v3Amount0Min ||
...@@ -203,8 +286,29 @@ function V2PairMigration({ ...@@ -203,8 +286,29 @@ function V2PairMigration({
) )
return return
const deadlineToUse = signatureData?.deadline ?? deadline
// janky way to ensure that stale-ish sigs are cleared
if (deadline.sub(deadlineToUse).lt(deadline.sub(blockTimestamp).div(2))) {
setSignatureData(null)
}
const data = [] const data = []
// permit if necessary
if (signatureData) {
data.push(
migrator.interface.encodeFunctionData('selfPermit', [
pair.address,
`0x${pairBalance.raw.toString(16)}`,
deadlineToUse,
signatureData.v,
signatureData.r,
signatureData.s,
])
)
}
// create/initialize pool if necessary // create/initialize pool if necessary
if (noLiquidity) { if (noLiquidity) {
data.push( data.push(
...@@ -221,7 +325,7 @@ function V2PairMigration({ ...@@ -221,7 +325,7 @@ function V2PairMigration({
data.push( data.push(
migrator.interface.encodeFunctionData('migrate', [ migrator.interface.encodeFunctionData('migrate', [
{ {
pair: pairBalance.token.address, pair: pair.address,
liquidityToMigrate: `0x${pairBalance.raw.toString(16)}`, liquidityToMigrate: `0x${pairBalance.raw.toString(16)}`,
percentageToMigrate, percentageToMigrate,
token0: token0.address, token0: token0.address,
...@@ -232,7 +336,7 @@ function V2PairMigration({ ...@@ -232,7 +336,7 @@ function V2PairMigration({
amount0Min: `0x${v3Amount0Min.raw.toString(16)}`, amount0Min: `0x${v3Amount0Min.raw.toString(16)}`,
amount1Min: `0x${v3Amount1Min.raw.toString(16)}`, amount1Min: `0x${v3Amount1Min.raw.toString(16)}`,
recipient: account, recipient: account,
deadline, deadline: deadlineToUse,
refundAsETH: true, // hard-code this for now refundAsETH: true, // hard-code this for now
}, },
]) ])
...@@ -242,6 +346,8 @@ function V2PairMigration({ ...@@ -242,6 +346,8 @@ function V2PairMigration({
migrator migrator
.multicall(data) .multicall(data)
.then((response: TransactionResponse) => { .then((response: TransactionResponse) => {
setSignatureData(null) // clear sig data
ReactGA.event({ ReactGA.event({
category: 'Migrate', category: 'Migrate',
action: 'V2->V3', action: 'V2->V3',
...@@ -259,6 +365,7 @@ function V2PairMigration({ ...@@ -259,6 +365,7 @@ function V2PairMigration({
}, [ }, [
migrator, migrator,
noLiquidity, noLiquidity,
blockTimestamp,
token0, token0,
token1, token1,
feeAmount, feeAmount,
...@@ -271,7 +378,9 @@ function V2PairMigration({ ...@@ -271,7 +378,9 @@ function V2PairMigration({
v3Amount1Min, v3Amount1Min,
account, account,
deadline, deadline,
signatureData,
addTransaction, addTransaction,
pair,
]) ])
const isSuccessfullyMigrated = !!pendingMigrationHash && JSBI.equal(pairBalance.raw, ZERO) const isSuccessfullyMigrated = !!pendingMigrationHash && JSBI.equal(pairBalance.raw, ZERO)
...@@ -379,28 +488,36 @@ function V2PairMigration({ ...@@ -379,28 +488,36 @@ function V2PairMigration({
) : null} ) : null}
<div style={{ display: 'flex', marginTop: '1rem' }}> <div style={{ display: 'flex', marginTop: '1rem' }}>
<AutoColumn gap="12px" style={{ flex: '1', marginRight: 12 }}> {!isSuccessfullyMigrated && !isMigrationPending ? (
<ButtonConfirmed <AutoColumn gap="12px" style={{ flex: '1', marginRight: 12 }}>
confirmed={approval === ApprovalState.APPROVED} <ButtonConfirmed
disabled={approval !== ApprovalState.NOT_APPROVED} confirmed={approval === ApprovalState.APPROVED || signatureData !== null}
onClick={approve} disabled={
> approval !== ApprovalState.NOT_APPROVED ||
{approval === ApprovalState.PENDING ? ( signatureData !== null ||
<Dots>Approving</Dots> !v3Amount0Min ||
) : approval === ApprovalState.APPROVED ? ( !v3Amount1Min ||
'Approved' confirmingMigration
) : ( }
'Approve' onClick={approve}
)} >
</ButtonConfirmed> {approval === ApprovalState.PENDING ? (
</AutoColumn> <Dots>Approving</Dots>
) : approval === ApprovalState.APPROVED || signatureData !== null ? (
'Approved'
) : (
'Approve'
)}
</ButtonConfirmed>
</AutoColumn>
) : null}
<AutoColumn gap="12px" style={{ flex: '1' }}> <AutoColumn gap="12px" style={{ flex: '1' }}>
<ButtonConfirmed <ButtonConfirmed
confirmed={isSuccessfullyMigrated} confirmed={isSuccessfullyMigrated}
disabled={ disabled={
!v3Amount0Min || !v3Amount0Min ||
!v3Amount1Min || !v3Amount1Min ||
approval !== ApprovalState.APPROVED || (approval !== ApprovalState.APPROVED && signatureData === null) ||
confirmingMigration || confirmingMigration ||
isMigrationPending || isMigrationPending ||
isSuccessfullyMigrated isSuccessfullyMigrated
...@@ -475,6 +592,7 @@ export default function MigrateV2Pair({ ...@@ -475,6 +592,7 @@ export default function MigrateV2Pair({
// redirect for invalid url params // redirect for invalid url params
if ( if (
!validatedAddress || !validatedAddress ||
!pair ||
(pair && (pair &&
token0AddressCallState?.valid && token0AddressCallState?.valid &&
!token0AddressCallState?.loading && !token0AddressCallState?.loading &&
...@@ -500,6 +618,7 @@ export default function MigrateV2Pair({ ...@@ -500,6 +618,7 @@ export default function MigrateV2Pair({
<TYPE.largeHeader>You must connect an account.</TYPE.largeHeader> <TYPE.largeHeader>You must connect an account.</TYPE.largeHeader>
) : pairBalance && totalSupply && reserve0 && reserve1 && token0 && token1 ? ( ) : pairBalance && totalSupply && reserve0 && reserve1 && token0 && token1 ? (
<V2PairMigration <V2PairMigration
pair={pair}
pairBalance={pairBalance} pairBalance={pairBalance}
totalSupply={totalSupply} totalSupply={totalSupply}
reserve0={reserve0} reserve0={reserve0}
......
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