Skip to content

Commit

Permalink
feat(widgets): support convenience fee in trades (#3219)
Browse files Browse the repository at this point in the history
* feat(widgets): support convenience fee in trades

* update call signature

* pr feedback
  • Loading branch information
JFrankfurt authored Feb 3, 2022
1 parent 921310e commit 8064dd8
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 27 deletions.
17 changes: 10 additions & 7 deletions src/hooks/useSwapCallArguments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -167,15 +169,16 @@ export function useSwapCallArguments(
]
}
}, [
trade,
recipient,
library,
account,
allowedSlippage,
argentWalletContract,
chainId,
deadline,
fee,
library,
recipient,
routerContract,
allowedSlippage,
argentWalletContract,
signatureData,
trade,
])
}
2 changes: 1 addition & 1 deletion src/hooks/useSwapCallback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
12 changes: 7 additions & 5 deletions src/lib/components/Swap/SwapButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 4 additions & 2 deletions src/lib/components/Swap/index.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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)
Expand Down
32 changes: 24 additions & 8 deletions src/lib/hooks/swap/useSwapCallback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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)
Expand Down
10 changes: 8 additions & 2 deletions src/lib/hooks/swap/useSwapInfo.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -23,6 +24,7 @@ interface SwapInfo {
state: TradeState
}
allowedSlippage: Percent
fee: FeeOptions
}

const BAD_RECIPIENT_ADDRESSES: { [address: string]: true } = {
Expand All @@ -42,6 +44,8 @@ function useComputeSwapInfo(): SwapInfo {
[Field.OUTPUT]: outputCurrency,
} = useAtomValue(swapAtom)

const fee = useAtomValue(feeOptionsAtom)

const to = account

const relevantTokenBalances = useCurrencyBalances(
Expand Down Expand Up @@ -139,8 +143,9 @@ function useComputeSwapInfo(): SwapInfo {
inputError,
trade,
allowedSlippage,
fee,
}),
[currencies, currencyBalances, currencyAmounts, inputError, trade, allowedSlippage]
[currencies, currencyBalances, currencyAmounts, inputError, trade, allowedSlippage, fee]
)
}

Expand All @@ -150,6 +155,7 @@ const swapInfoAtom = atom<SwapInfo>({
currencyAmounts: {},
trade: { state: TradeState.INVALID },
allowedSlippage: new Percent(0),
fee: DEFAULT_FEE_OPTIONS,
})

export function SwapInfoUpdater() {
Expand Down
35 changes: 35 additions & 0 deletions src/lib/hooks/swap/useSyncConvenienceFee.ts
Original file line number Diff line number Diff line change
@@ -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])
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ interface UseSwapDefaultsArgs {
defaultOutputAmount?: string
}

export default function useSwapDefaults({
export default function useSyncSwapDefaults({
defaultInputAddress,
defaultInputAmount,
defaultOutputAddress,
Expand Down
9 changes: 8 additions & 1 deletion src/lib/state/swap.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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)

0 comments on commit 8064dd8

Please sign in to comment.