Skip to content

Commit

Permalink
feat: update swap hooks and add swap txn submission (#3187)
Browse files Browse the repository at this point in the history
* update swap hooks to add swap txn confirmations

* fix: remove uneeded comments

* update with latest

* update utils to separate swap callback hooks

* create generic swap callabck to be used by both app and widget

* update app swap callback to use logic from lib

* update big number import
  • Loading branch information
ianlapham authored Jan 27, 2022
1 parent ce96873 commit b50d10c
Show file tree
Hide file tree
Showing 13 changed files with 574 additions and 430 deletions.
2 changes: 1 addition & 1 deletion src/components/earn/StakingModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { Trans } from '@lingui/macro'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useV2LiquidityTokenPermit } from 'hooks/useV2LiquidityTokenPermit'
import { useCallback, useState } from 'react'
import styled from 'styled-components/macro'

import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
import { usePairContract, useStakingContract, useV2RouterContract } from '../../hooks/useContract'
import { useV2LiquidityTokenPermit } from '../../hooks/useERC20Permit'
import useTransactionDeadline from '../../hooks/useTransactionDeadline'
import { StakingInfo, useDerivedStakeInfo } from '../../state/stake/hooks'
import { TransactionType } from '../../state/transactions/actions'
Expand Down
30 changes: 9 additions & 21 deletions src/hooks/useERC20Permit.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BigNumber } from '@ethersproject/bignumber'
import { splitSignature } from '@ethersproject/bytes'
import { Trade } from '@uniswap/router-sdk'
import { Currency, CurrencyAmount, Percent, Token, TradeType } from '@uniswap/sdk-core'
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
import { Trade as V2Trade } from '@uniswap/v2-sdk'
import { Trade as V3Trade } from '@uniswap/v3-sdk'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
Expand All @@ -12,17 +13,16 @@ import { SWAP_ROUTER_ADDRESSES, V3_ROUTER_ADDRESS } from '../constants/addresses
import { DAI, UNI, USDC } from '../constants/tokens'
import { useEIP2612Contract } from './useContract'
import useIsArgentWallet from './useIsArgentWallet'
import useTransactionDeadline from './useTransactionDeadline'

enum PermitType {
export enum PermitType {
AMOUNT = 1,
ALLOWED = 2,
}

// 20 minutes to submit after signing
const PERMIT_VALIDITY_BUFFER = 20 * 60

interface PermitInfo {
export interface PermitInfo {
type: PermitType
name: string
// version is optional, and if omitted, will not be included in the domain
Expand Down Expand Up @@ -116,17 +116,17 @@ const PERMIT_ALLOWED_TYPE = [
{ name: 'allowed', type: 'bool' },
]

function useERC20Permit(
export function useERC20Permit(
currencyAmount: CurrencyAmount<Currency> | null | undefined,
spender: string | null | undefined,
transactionDeadline: BigNumber | undefined,
overridePermitInfo: PermitInfo | undefined | null
): {
signatureData: SignatureData | null
state: UseERC20PermitState
gatherPermitSignature: null | (() => Promise<void>)
} {
const { account, chainId, library } = useActiveWeb3React()
const transactionDeadline = useTransactionDeadline()
const tokenAddress = currencyAmount?.currency?.isToken ? currencyAmount.currency.address : undefined
const eip2612Contract = useEIP2612Contract(tokenAddress)
const isArgentWallet = useIsArgentWallet()
Expand Down Expand Up @@ -259,26 +259,14 @@ function useERC20Permit(
])
}

const REMOVE_V2_LIQUIDITY_PERMIT_INFO: PermitInfo = {
version: '1',
name: 'Uniswap V2',
type: PermitType.AMOUNT,
}

export function useV2LiquidityTokenPermit(
liquidityAmount: CurrencyAmount<Token> | null | undefined,
spender: string | null | undefined
) {
return useERC20Permit(liquidityAmount, spender, REMOVE_V2_LIQUIDITY_PERMIT_INFO)
}

export function useERC20PermitFromTrade(
trade:
| V2Trade<Currency, Currency, TradeType>
| V3Trade<Currency, Currency, TradeType>
| Trade<Currency, Currency, TradeType>
| undefined,
allowedSlippage: Percent
allowedSlippage: Percent,
transactionDeadline: BigNumber | undefined
) {
const { chainId } = useActiveWeb3React()
const swapRouterAddress = chainId
Expand All @@ -294,5 +282,5 @@ export function useERC20PermitFromTrade(
[trade, allowedSlippage]
)

return useERC20Permit(amountToApprove, swapRouterAddress, null)
return useERC20Permit(amountToApprove, swapRouterAddress, transactionDeadline, null)
}
181 changes: 181 additions & 0 deletions src/hooks/useSwapCallArguments.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import { BigNumber } from '@ethersproject/bignumber'
import { SwapRouter, Trade } from '@uniswap/router-sdk'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core'
import { Router as V2SwapRouter, Trade as V2Trade } from '@uniswap/v2-sdk'
import { SwapRouter as V3SwapRouter, Trade as V3Trade } from '@uniswap/v3-sdk'
import { SWAP_ROUTER_ADDRESSES, V3_ROUTER_ADDRESS } from 'constants/addresses'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useMemo } from 'react'
import approveAmountCalldata from 'utils/approveAmountCalldata'

import { useArgentWalletContract } from './useArgentWalletContract'
import { useV2RouterContract } from './useContract'
import useENS from './useENS'
import { SignatureData } from './useERC20Permit'

export type AnyTrade =
| V2Trade<Currency, Currency, TradeType>
| V3Trade<Currency, Currency, TradeType>
| Trade<Currency, Currency, TradeType>

interface SwapCall {
address: string
calldata: string
value: string
}

/**
* Returns the swap calls that can be used to make the trade
* @param trade trade to execute
* @param allowedSlippage user allowed slippage
* @param recipientAddressOrName the ENS name or address of the recipient of the swap output
* @param signatureData the signature data of the permit of the input token amount, if available
*/
export function useSwapCallArguments(
trade: AnyTrade | undefined,
allowedSlippage: Percent,
recipientAddressOrName: string | null | undefined,
signatureData: SignatureData | null | undefined,
deadline: BigNumber | undefined
): SwapCall[] {
const { account, chainId, library } = useActiveWeb3React()

const { address: recipientAddress } = useENS(recipientAddressOrName)
const recipient = recipientAddressOrName === null ? account : recipientAddress
const routerContract = useV2RouterContract()
const argentWalletContract = useArgentWalletContract()

return useMemo(() => {
if (!trade || !recipient || !library || !account || !chainId || !deadline) return []

if (trade instanceof V2Trade) {
if (!routerContract) return []
const swapMethods = []

swapMethods.push(
V2SwapRouter.swapCallParameters(trade, {
feeOnTransfer: false,
allowedSlippage,
recipient,
deadline: deadline.toNumber(),
})
)

if (trade.tradeType === TradeType.EXACT_INPUT) {
swapMethods.push(
V2SwapRouter.swapCallParameters(trade, {
feeOnTransfer: true,
allowedSlippage,
recipient,
deadline: deadline.toNumber(),
})
)
}
return swapMethods.map(({ methodName, args, value }) => {
if (argentWalletContract && trade.inputAmount.currency.isToken) {
return {
address: argentWalletContract.address,
calldata: argentWalletContract.interface.encodeFunctionData('wc_multiCall', [
[
approveAmountCalldata(trade.maximumAmountIn(allowedSlippage), routerContract.address),
{
to: routerContract.address,
value,
data: routerContract.interface.encodeFunctionData(methodName, args),
},
],
]),
value: '0x0',
}
} else {
return {
address: routerContract.address,
calldata: routerContract.interface.encodeFunctionData(methodName, args),
value,
}
}
})
} else {
// swap options shared by v3 and v2+v3 swap routers
const sharedSwapOptions = {
recipient,
slippageTolerance: allowedSlippage,
...(signatureData
? {
inputTokenPermit:
'allowed' in signatureData
? {
expiry: signatureData.deadline,
nonce: signatureData.nonce,
s: signatureData.s,
r: signatureData.r,
v: signatureData.v as any,
}
: {
deadline: signatureData.deadline,
amount: signatureData.amount,
s: signatureData.s,
r: signatureData.r,
v: signatureData.v as any,
},
}
: {}),
}

const swapRouterAddress = chainId
? trade instanceof V3Trade
? V3_ROUTER_ADDRESS[chainId]
: SWAP_ROUTER_ADDRESSES[chainId]
: undefined
if (!swapRouterAddress) return []

const { value, calldata } =
trade instanceof V3Trade
? V3SwapRouter.swapCallParameters(trade, {
...sharedSwapOptions,
deadline: deadline.toString(),
})
: SwapRouter.swapCallParameters(trade, {
...sharedSwapOptions,
deadlineOrPreviousBlockhash: deadline.toString(),
})

if (argentWalletContract && trade.inputAmount.currency.isToken) {
return [
{
address: argentWalletContract.address,
calldata: argentWalletContract.interface.encodeFunctionData('wc_multiCall', [
[
approveAmountCalldata(trade.maximumAmountIn(allowedSlippage), swapRouterAddress),
{
to: swapRouterAddress,
value,
data: calldata,
},
],
]),
value: '0x0',
},
]
}
return [
{
address: swapRouterAddress,
calldata,
value,
},
]
}
}, [
trade,
recipient,
library,
account,
chainId,
deadline,
routerContract,
allowedSlippage,
argentWalletContract,
signatureData,
])
}
Loading

0 comments on commit b50d10c

Please sign in to comment.