feat(widgets): support convenience fee in trades (#3219)

* feat(widgets): support convenience fee in trades

* update call signature

* pr feedback
This commit is contained in:
Jordan Frankfurt 2022-02-03 09:38:42 -06:00 committed by GitHub
parent 921310ef52
commit 8064dd8ede
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 99 additions and 28 deletions

@ -2,7 +2,7 @@ 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 { FeeOptions, 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'
@ -36,7 +36,8 @@ export function useSwapCallArguments(
allowedSlippage: Percent,
recipientAddressOrName: string | null | undefined,
signatureData: SignatureData | null | undefined,
deadline: BigNumber | undefined
deadline: BigNumber | undefined,
fee: FeeOptions | undefined
): SwapCall[] {
const { account, chainId, library } = useActiveWeb3React()
@ -98,6 +99,7 @@ export function useSwapCallArguments(
} else {
// swap options shared by v3 and v2+v3 swap routers
const sharedSwapOptions = {
fee,
recipient,
slippageTolerance: allowedSlippage,
...(signatureData
@ -167,15 +169,16 @@ export function useSwapCallArguments(
]
}
}, [
trade,
recipient,
library,
account,
chainId,
deadline,
routerContract,
allowedSlippage,
argentWalletContract,
chainId,
deadline,
fee,
library,
recipient,
routerContract,
signatureData,
trade,
])
}

@ -33,7 +33,7 @@ export function useSwapCallback(
state,
callback: libCallback,
error,
} = useLibSwapCallBack(trade, allowedSlippage, recipient, signatureData, deadline)
} = useLibSwapCallBack({ trade, allowedSlippage, recipientAddressOrName: recipient, signatureData, deadline })
const callback = useMemo(() => {
if (!libCallback || !trade) {

@ -51,6 +51,7 @@ export default function SwapButton({ disabled }: SwapButtonProps) {
currencies: { [Field.INPUT]: inputCurrency },
currencyBalances: { [Field.INPUT]: inputCurrencyBalance },
currencyAmounts: { [Field.INPUT]: inputCurrencyAmount, [Field.OUTPUT]: outputCurrencyAmount },
fee,
} = useSwapInfo()
const independentField = useAtomValue(independentFieldAtom)
@ -118,13 +119,14 @@ export default function SwapButton({ disabled }: SwapButtonProps) {
const { signatureData } = useERC20PermitFromTrade(optimizedTrade, allowedSlippage, deadline)
// the callback to execute the swap
const { callback: swapCallback } = useSwapCallback(
optimizedTrade,
const { callback: swapCallback } = useSwapCallback({
trade: optimizedTrade,
allowedSlippage,
account ?? null,
recipientAddressOrName: account ?? null,
signatureData,
deadline
)
deadline,
fee,
})
//@TODO(ianlapham): add a loading state, process errors
const setDisplayTxHash = useUpdateAtom(displayTxHashAtom)

@ -1,8 +1,9 @@
import { Trans } from '@lingui/macro'
import { TokenInfo } from '@uniswap/token-lists'
import { useAtom } from 'jotai'
import useSwapDefaults from 'lib/hooks/swap/useSwapDefaults'
import { SwapInfoUpdater } from 'lib/hooks/swap/useSwapInfo'
import useSyncConvenienceFee from 'lib/hooks/swap/useSyncConvenienceFee'
import useSyncSwapDefaults from 'lib/hooks/swap/useSyncSwapDefaults'
import { usePendingTransactions } from 'lib/hooks/transactions'
import useActiveWeb3React from 'lib/hooks/useActiveWeb3React'
import useTokenList from 'lib/hooks/useTokenList'
@ -47,7 +48,8 @@ export interface SwapProps {
export default function Swap(props: SwapProps) {
useTokenList(props.tokenList)
useSwapDefaults(props)
useSyncSwapDefaults(props)
useSyncConvenienceFee(props)
const { active, account } = useActiveWeb3React()
const [boundary, setBoundary] = useState<HTMLDivElement | null>(null)

@ -3,6 +3,7 @@ import { BigNumber } from '@ethersproject/bignumber'
import { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro'
import { Percent } from '@uniswap/sdk-core'
import { FeeOptions } from '@uniswap/v3-sdk'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import useENS from 'hooks/useENS'
import { SignatureData } from 'hooks/useERC20Permit'
@ -17,18 +18,33 @@ export enum SwapCallbackState {
VALID,
}
interface UseSwapCallbackReturns {
state: SwapCallbackState
callback: null | (() => Promise<TransactionResponse>)
error: ReactNode | null
}
interface UseSwapCallbackArgs {
trade: AnyTrade | undefined // trade to execute, required
allowedSlippage: Percent // in bips
recipientAddressOrName: string | null | undefined // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender
signatureData: SignatureData | null | undefined
deadline: BigNumber | undefined
fee?: FeeOptions
}
// returns a function that will execute a swap, if the parameters are all valid
// and the user has approved the slippage adjusted input amount for the trade
export function useSwapCallback(
trade: AnyTrade | undefined, // trade to execute, required
allowedSlippage: Percent, // in bips
recipientAddressOrName: string | null | undefined, // the ENS name or address of the recipient of the trade, or null if swap should be returned to sender
signatureData: SignatureData | null | undefined,
deadline: BigNumber | undefined
): { state: SwapCallbackState; callback: null | (() => Promise<TransactionResponse>); error: ReactNode | null } {
export function useSwapCallback({
trade,
allowedSlippage,
recipientAddressOrName,
signatureData,
deadline,
fee,
}: UseSwapCallbackArgs): UseSwapCallbackReturns {
const { account, chainId, library } = useActiveWeb3React()
const swapCalls = useSwapCallArguments(trade, allowedSlippage, recipientAddressOrName, signatureData, deadline)
const swapCalls = useSwapCallArguments(trade, allowedSlippage, recipientAddressOrName, signatureData, deadline, fee)
const { callback } = useSendSwapTransaction(account, chainId, library, trade, swapCalls)
const { address: recipientAddress } = useENS(recipientAddressOrName)

@ -1,11 +1,12 @@
import { Trans } from '@lingui/macro'
import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core'
import { FeeOptions } from '@uniswap/v3-sdk'
import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance'
import { atom } from 'jotai'
import { useAtomValue, useUpdateAtom } from 'jotai/utils'
import { useCurrencyBalances } from 'lib/hooks/useCurrencyBalance'
import { maxSlippageAtom } from 'lib/state/settings'
import { Field, swapAtom } from 'lib/state/swap'
import { DEFAULT_FEE_OPTIONS, feeOptionsAtom, Field, swapAtom } from 'lib/state/swap'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { ReactNode, useEffect, useMemo } from 'react'
import { InterfaceTrade, TradeState } from 'state/routing/types'
@ -23,6 +24,7 @@ interface SwapInfo {
state: TradeState
}
allowedSlippage: Percent
fee: FeeOptions
}
const BAD_RECIPIENT_ADDRESSES: { [address: string]: true } = {
@ -42,6 +44,8 @@ function useComputeSwapInfo(): SwapInfo {
[Field.OUTPUT]: outputCurrency,
} = useAtomValue(swapAtom)
const fee = useAtomValue(feeOptionsAtom)
const to = account
const relevantTokenBalances = useCurrencyBalances(
@ -139,8 +143,9 @@ function useComputeSwapInfo(): SwapInfo {
inputError,
trade,
allowedSlippage,
fee,
}),
[currencies, currencyBalances, currencyAmounts, inputError, trade, allowedSlippage]
[currencies, currencyBalances, currencyAmounts, inputError, trade, allowedSlippage, fee]
)
}
@ -150,6 +155,7 @@ const swapInfoAtom = atom<SwapInfo>({
currencyAmounts: {},
trade: { state: TradeState.INVALID },
allowedSlippage: new Percent(0),
fee: DEFAULT_FEE_OPTIONS,
})
export function SwapInfoUpdater() {

@ -0,0 +1,35 @@
import { Percent } from '@uniswap/sdk-core'
import useActiveWeb3React from 'hooks/useActiveWeb3React'
import { useUpdateAtom } from 'jotai/utils'
import { DEFAULT_FEE_OPTIONS, feeOptionsAtom } from 'lib/state/swap'
import { useEffect } from 'react'
interface FeeOptionsArgs {
convenienceFee?: number
convenienceFeeRecipient?: string | string | { [chainId: number]: string }
}
export default function useSyncConvenienceFee({ convenienceFee, convenienceFeeRecipient }: FeeOptionsArgs) {
const { chainId } = useActiveWeb3React()
const updateFeeOptions = useUpdateAtom(feeOptionsAtom)
useEffect(() => {
if (convenienceFee && convenienceFeeRecipient) {
if (typeof convenienceFeeRecipient === 'string') {
updateFeeOptions({
fee: new Percent(convenienceFee, 10_000),
recipient: convenienceFeeRecipient,
})
return
}
if (chainId && convenienceFeeRecipient[chainId]) {
updateFeeOptions({
fee: new Percent(convenienceFee, 10_000),
recipient: convenienceFeeRecipient[chainId],
})
return
}
}
updateFeeOptions(DEFAULT_FEE_OPTIONS)
}, [chainId, convenienceFee, convenienceFeeRecipient, updateFeeOptions])
}

@ -31,7 +31,7 @@ interface UseSwapDefaultsArgs {
defaultOutputAmount?: string
}
export default function useSwapDefaults({
export default function useSyncSwapDefaults({
defaultInputAddress,
defaultInputAmount,
defaultOutputAddress,

@ -1,4 +1,5 @@
import { Currency } from '@uniswap/sdk-core'
import { Currency, Percent } from '@uniswap/sdk-core'
import { FeeOptions } from '@uniswap/v3-sdk'
import { SupportedChainId } from 'constants/chains'
import { nativeOnChain } from 'constants/tokens'
import { atom } from 'jotai'
@ -27,3 +28,9 @@ export const independentFieldAtom = pickAtom(swapAtom, 'independentField')
// If set to a transaction hash, that transaction will display in a status dialog.
export const displayTxHashAtom = atom<string | undefined>(undefined)
export const DEFAULT_FEE_OPTIONS = {
fee: new Percent(0),
recipient: '',
}
export const feeOptionsAtom = atom<FeeOptions>(DEFAULT_FEE_OPTIONS)