Skip to content

Commit

Permalink
Merge pull request #1576 from sushiswap/chore/trade-txdata
Browse files Browse the repository at this point in the history
chore: useCall & useSendTransaction when swapping
  • Loading branch information
matthewlilley authored Aug 2, 2024
2 parents dc1687b + 32d346c commit 96dea53
Show file tree
Hide file tree
Showing 33 changed files with 608 additions and 125 deletions.
2 changes: 2 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"check": "tsc --pretty --noEmit",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next && rm -rf .swc",
"dev": "next dev",
"generate-swaps": "tsx test/swap/scripts/generate-swaps.ts",
"lint": "TIMING=1 next lint",
"lint:fix": "TIMING=1 next lint --fix",
"start": "next start",
Expand Down Expand Up @@ -129,6 +130,7 @@
"postcss": "8.4.23",
"schema-dts": "^1.1.2",
"tailwindcss": "3.3.2",
"tsx": "^4.16.5",
"typescript": "5.4.5",
"unimported": "1.30.0"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function ArticleCard({ article }: ArticleCard) {
className={classNames(
isMediaVideo(article.cover.provider_metadata)
? ''
: 'group-hover:scale-[1.06] scale-[1.01] transition duration-[400ms]',
: 'group-hover:scale-[1.06] scale-[1.01] transition [animation-duration:400ms]',
)}
image={article.cover}
quality={100}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ interface ArticleCard {
export function ArticleCard({ article }: ArticleCard) {
return (
<Link href={`/blog/${article.slug}`}>
<div className="transition relative duration-[400ms] min-h-[400px] h-full w-full rounded-xl shadow-md bg-slate-800 overflow-hidden hover:ring-2 ring-slate-700 ring-offset-2 ring-offset-slate-900 border border-accent z-10">
<div className="transition relative [animation-duration:400ms] min-h-[400px] h-full w-full rounded-xl shadow-md bg-slate-800 overflow-hidden hover:ring-2 ring-slate-700 ring-offset-2 ring-offset-slate-900 border border-accent z-10">
<div className="relative">
{article.cover ? (
<Media
className={classNames(
isMediaVideo(article.cover.provider_metadata)
? ''
: 'group-hover:scale-[1.06] scale-[1.01] transition duration-[400ms]',
: 'group-hover:scale-[1.06] scale-[1.01] transition [animation-duration:400ms]',
)}
image={article.cover}
layout="responsive"
Expand Down
70 changes: 45 additions & 25 deletions apps/web/src/lib/hooks/useSimulateTrade.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import { UseTradeReturn } from '@sushiswap/react-query'
import { SimulateContractErrorType } from '@wagmi/core'
import { useEffect, useMemo } from 'react'
import { useEffect, useMemo, useRef } from 'react'
import { useDerivedStateSimpleSwap } from 'src/ui/swap/simple/derivedstate-simple-swap-provider'
import { routeProcessor4Abi } from 'sushi/abi'
import {
ROUTE_PROCESSOR_4_ADDRESS,
isRouteProcessor4ChainId,
} from 'sushi/config'
import { BaseError } from 'viem'
import { useSimulateContract } from 'wagmi'
import { CallErrorType, CallReturnType, Hex, RawContractError } from 'viem'
import { useAccount, useCall } from 'wagmi'
import { getTokenTax } from '../swap/getTokenTax'

const isMinOutError = (error: SimulateContractErrorType | null) =>
error instanceof BaseError &&
(error.message.includes('MinimalOutputBalanceViolation') ||
error.message.includes('0x963b34a5'))
const isMinOutError = (_error: CallErrorType): Hex | false => {
const error = _error.walk() as RawContractError
const data = typeof error?.data === 'object' ? error.data?.data : error.data
return data?.includes('0x963b34a5') ? data : false
}

export function useSimulateTrade({
trade,
Expand All @@ -28,14 +27,15 @@ export function useSimulateTrade({
mutate: { setTokenTax },
} = useDerivedStateSimpleSwap()

const simulateTrade = useSimulateContract({
const { address } = useAccount()

const simulateTrade = useCall({
chainId: chainId,
address: isRouteProcessor4ChainId(chainId)
to: isRouteProcessor4ChainId(chainId)
? ROUTE_PROCESSOR_4_ADDRESS[chainId]
: undefined,
abi: routeProcessor4Abi,
functionName: trade?.functionName || 'processRoute', // To make typescript happy
args: trade?.writeArgs as any,
data: trade?.txdata as Hex | undefined,
account: address,
value: trade?.value || 0n,
query: {
retry: (i, error) => {
Expand All @@ -50,35 +50,40 @@ export function useSimulateTrade({
enabled:
enabled &&
Boolean(
trade?.writeArgs &&
trade?.functionName &&
address &&
trade?.txdata &&
isRouteProcessor4ChainId(chainId) &&
trade?.route?.status !== 'NoWay',
),
},
})

const prevErrorRef = useRef<CallErrorType>()
const prevDataRef = useRef<CallReturnType>()

// onSuccess
useEffect(() => {
if (simulateTrade.data) {
if (typeof trade?.tokenTax === 'undefined') {
if (simulateTrade.data && simulateTrade.data !== prevDataRef.current) {
prevDataRef.current = simulateTrade.data

if (trade && typeof trade.tokenTax === 'undefined') {
setTokenTax(false)
}
}
}, [setTokenTax, simulateTrade.data, trade?.tokenTax])
}, [simulateTrade.data, trade, setTokenTax])

// onError
useEffect(() => {
const error = simulateTrade.error
if (simulateTrade.error && simulateTrade.error !== prevErrorRef.current) {
prevErrorRef.current = simulateTrade.error

if (error) {
if (isMinOutError(error)) {
const errorData = isMinOutError(simulateTrade.error)
if (errorData) {
if (trade?.amountOut && typeof trade.tokenTax === 'undefined') {
const _tokenTax = getTokenTax({
error,
data: errorData,
expectedAmountOut: trade.amountOut,
})

setTokenTax(_tokenTax)
} else if (trade?.tokenTax !== false) {
setTokenTax(false)
Expand All @@ -90,19 +95,34 @@ export function useSimulateTrade({
return useMemo(
() => ({
...simulateTrade,
data: simulateTrade.data
? {
...simulateTrade.data,
request: {
to: isRouteProcessor4ChainId(chainId)
? ROUTE_PROCESSOR_4_ADDRESS[chainId]
: undefined,
data: trade?.txdata as Hex | undefined,
value: trade?.value || 0n,
account: address,
},
}
: undefined,
isError:
trade &&
typeof trade.tokenTax === 'undefined' &&
simulateTrade.error &&
isMinOutError(simulateTrade.error)
? false
: simulateTrade.isError,
error:
trade &&
typeof trade.tokenTax === 'undefined' &&
simulateTrade.error &&
isMinOutError(simulateTrade.error)
? null
: simulateTrade.error,
}),
[simulateTrade, trade],
[simulateTrade, trade, chainId, address],
)
}
47 changes: 16 additions & 31 deletions apps/web/src/lib/swap/getTokenTax.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,35 @@
import { SimulateContractErrorType } from '@wagmi/core'
import { Fraction, Percent } from 'sushi'
import { Amount, Type } from 'sushi/currency'
import { decodeErrorResult } from 'viem'
import { Hex, decodeErrorResult } from 'viem'

const MAX_TOKEN_TAX = new Percent(1_000, 10_000) // 10%

export const getTokenTax = ({
error,
data,
expectedAmountOut,
}: {
error: SimulateContractErrorType
data: Hex
expectedAmountOut: Amount<Type>
}) => {
let amountOut: bigint | undefined = undefined

try {
const decodedErrors = error.message.match(
/Error: MinimalOutputBalanceViolation\(uint256 amountOut\)\s+\(\d+\)/g,
)

if (decodedErrors) {
const parsedAmountOut = decodedErrors[0].match(/\(\d+\)/g)
if (parsedAmountOut) {
amountOut = BigInt(parsedAmountOut[0].slice(1, -1))
}
} else {
const encodedErrors = error.message.match(/0x963b34a5[a-fA-F0-9]{64}/g)
if (encodedErrors) {
amountOut = decodeErrorResult({
abi: [
amountOut = decodeErrorResult({
abi: [
{
inputs: [
{
inputs: [
{
internalType: 'uint256',
name: 'amountOut',
type: 'uint256',
},
],
name: 'MinimalOutputBalanceViolation',
type: 'error',
internalType: 'uint256',
name: 'amountOut',
type: 'uint256',
},
],
data: encodedErrors[0] as `0x${string}`,
}).args[0]
}
}
name: 'MinimalOutputBalanceViolation',
type: 'error',
},
],
data,
}).args[0]
} catch (_) {}

if (amountOut) {
Expand Down
24 changes: 12 additions & 12 deletions apps/web/src/lib/wagmi/hooks/trade/use-client-trade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,23 +146,22 @@ export const useClientTrade = (variables: UseTradeParams) => {
}

if (route) {
const amountIn = Amount.fromRawAmount(
fromToken,
route.amountInBI.toString(),
)
const amountIn = Amount.fromRawAmount(fromToken, route.amountInBI)
const amountOut = Amount.fromRawAmount(
toToken,
new Fraction(route.amountOutBI).multiply(
tokenTax ? new Percent(1).subtract(tokenTax) : 1,
).quotient,
)
const minAmountOut = Amount.fromRawAmount(
toToken,
slippageAmount(
amountOut,
new Percent(Math.floor(+slippagePercentage * 100), 10_000),
)[0],
)
const minAmountOut = args?.amountOutMin
? Amount.fromRawAmount(toToken, args.amountOutMin)
: Amount.fromRawAmount(
toToken,
slippageAmount(
Amount.fromRawAmount(toToken, route.amountOutBI),
new Percent(Math.floor(+slippagePercentage * 100), 10_000),
)[0],
)
const isOffset = chainId === ChainId.POLYGON && carbonOffset

// let writeArgs: UseTradeReturnWriteArgs = args
Expand All @@ -171,7 +170,7 @@ export const useClientTrade = (variables: UseTradeParams) => {
args.tokenIn as Address,
args.amountIn,
args.tokenOut as Address,
minAmountOut.quotient,
args.amountOutMin,
args.to as Address,
args.routeCode as Hex,
]
Expand Down Expand Up @@ -233,6 +232,7 @@ export const useClientTrade = (variables: UseTradeParams) => {
: 'processRoute',
writeArgs,
value,
txdata: args?.data,
tokenTax,
}),
250,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,12 +420,17 @@ const useSimpleSwapTrade = () => {

const useSwapApi = !isFallback && !forceClient

const adjustedSlippage = useMemo(
() => (tokenTax ? slippagePercent.add(tokenTax) : slippagePercent),
[slippagePercent, tokenTax],
)

const apiTrade = useApiTrade({
chainId,
fromToken: token0,
toToken: token1,
amount: swapAmount,
slippagePercentage: slippagePercent.toFixed(2),
slippagePercentage: adjustedSlippage.toFixed(2),
gasPrice,
recipient: recipient as Address,
enabled: Boolean(useSwapApi && swapAmount?.greaterThan(ZERO)),
Expand All @@ -442,7 +447,7 @@ const useSimpleSwapTrade = () => {
fromToken: token0,
toToken: token1,
amount: swapAmount,
slippagePercentage: slippagePercent.toFixed(2),
slippagePercentage: adjustedSlippage.toFixed(2),
gasPrice,
recipient: recipient as Address,
enabled: Boolean(!useSwapApi && swapAmount?.greaterThan(ZERO)),
Expand Down
14 changes: 7 additions & 7 deletions apps/web/src/ui/swap/simple/simple-swap-trade-review-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ import {
import {
useAccount,
usePublicClient,
useSendTransaction,
useWaitForTransactionReceipt,
useWriteContract,
} from 'wagmi'
import { APPROVE_TAG_SWAP } from '../../../lib/constants'
import {
Expand Down Expand Up @@ -353,10 +353,10 @@ export const SimpleSwapTradeReviewDialog: FC<{
)

const {
writeContractAsync,
sendTransactionAsync,
isPending: isWritePending,
data,
} = useWriteContract({
} = useSendTransaction({
mutation: {
onMutate: () => {
// Set reference of current trade
Expand All @@ -370,15 +370,15 @@ export const SimpleSwapTradeReviewDialog: FC<{
})

const write = useMemo(() => {
if (!writeContractAsync || !simulation) return undefined
if (!sendTransactionAsync || !simulation) return undefined

return async (confirm: () => void) => {
try {
await writeContractAsync(simulation.request)
await sendTransactionAsync(simulation.request)
confirm()
} catch {}
}
}, [simulation, writeContractAsync])
}, [simulation, sendTransactionAsync])

const { status } = useWaitForTransactionReceipt({
chainId: chainId,
Expand Down Expand Up @@ -542,7 +542,7 @@ export const SimpleSwapTradeReviewDialog: FC<{
!!error ||
isWritePending ||
Boolean(
!writeContractAsync &&
!sendTransactionAsync &&
swapAmount?.greaterThan(ZERO),
) ||
isError,
Expand Down
2 changes: 1 addition & 1 deletion apps/web/test/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const baseURL = `http://localhost:${PORT}`
const config: PlaywrightTestConfig = {
quiet: !!process.env.CI,
testMatch: [
'pool.test.ts',
// 'pool.test.ts',
'simple.test.ts',
// 'smart.test.ts',
// 'cross-chain.test.ts',
Expand Down
3 changes: 2 additions & 1 deletion apps/web/test/swap/mock/137-native-to-usdc.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"amountOutMin": "89028230",
"to": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"routeCode": "0x0301ffff020146B3fDF7b5CDe91Ac049936bF0bDb12c5d22202e0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270010d500B1d8E8eF31E21C99d1Db9A6444d3ADf127001ffff0121988C9CFD08db3b5793c2C6782271dC9474925101f39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"value": "100000000000000000000"
"value": "100000000000000000000",
"txdata": "0x2646478b000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000056bc75e2d631000000000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa8417400000000000000000000000000000000000000000000000000000000054e7686000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000700301ffff020146b3fdf7b5cde91ac049936bf0bdb12c5d22202e0d500b1d8e8ef31e21c99d1db9a6444d3adf1270010d500b1d8e8ef31e21c99d1db9a6444d3adf127001ffff0121988c9cfd08db3b5793c2c6782271dc9474925101f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000"
}
}
Loading

0 comments on commit 96dea53

Please sign in to comment.