diff --git a/packages/swap-widget/package.json b/packages/swap-widget/package.json index cff327e709d..d3e1b74ed31 100644 --- a/packages/swap-widget/package.json +++ b/packages/swap-widget/package.json @@ -1,6 +1,6 @@ { "name": "@shapeshiftoss/swap-widget", - "version": "0.6.0", + "version": "0.7.0", "description": "Embeddable swap widget using ShapeShift API", "repository": "https://github.com/shapeshift/web", "license": "MIT", diff --git a/packages/swap-widget/src/hooks/useSwapApproval.ts b/packages/swap-widget/src/hooks/useSwapApproval.ts index 7f3c9be7abb..e13d54d934d 100644 --- a/packages/swap-widget/src/hooks/useSwapApproval.ts +++ b/packages/swap-widget/src/hooks/useSwapApproval.ts @@ -1,6 +1,6 @@ import { useEffect, useRef } from 'react' import type { WalletClient } from 'viem' -import { createPublicClient, encodeFunctionData, erc20Abi, http } from 'viem' +import { createPublicClient, http } from 'viem' import { getBaseAsset } from '../constants/chains' import { switchOrAddChain, VIEM_CHAINS_BY_ID } from '../constants/viemChains' @@ -45,6 +45,18 @@ export const useSwapApproval = () => { return } + if (!sellAmountBaseUnit || sellAmountBaseUnit === '0') { + actorRef.send({ type: 'APPROVAL_ERROR', error: 'No sell amount specified' }) + return + } + + // api-supplied approvals are exact and in broadcast order (reset-then-approve for USDT-likes) + const approvalTxs = quote.approval.approvalTxs + if (!approvalTxs?.length) { + actorRef.send({ type: 'APPROVAL_ERROR', error: 'No approval transactions in quote' }) + return + } + const requiredChainId = getEvmNetworkId(sellAsset.chainId) const client = walletClient as WalletClient @@ -66,31 +78,26 @@ export const useSwapApproval = () => { rpcUrls: { default: { http: [] } }, } - if (!sellAmountBaseUnit || sellAmountBaseUnit === '0') { - actorRef.send({ type: 'APPROVAL_ERROR', error: 'No sell amount specified' }) - return - } - - const approvalData = encodeFunctionData({ - abi: erc20Abi, - functionName: 'approve', - args: [quote.approval.spender as `0x${string}`, BigInt(sellAmountBaseUnit)], - }) - - const approvalHash = await client.sendTransaction({ - to: sellAssetAddress as `0x${string}`, - data: approvalData, - value: BigInt(0), - chain, - account: walletAddress as `0x${string}`, - }) - const rpcUrl = chain.rpcUrls?.default?.http?.[0] const publicClient = createPublicClient({ chain, transport: rpcUrl ? http(rpcUrl) : http(), }) - await publicClient.waitForTransactionReceipt({ hash: approvalHash }) + + let approvalHash: `0x${string}` | undefined + for (const approvalTx of approvalTxs) { + approvalHash = await client.sendTransaction({ + to: approvalTx.to as `0x${string}`, + data: approvalTx.data as `0x${string}`, + value: BigInt(approvalTx.value), + chain, + account: walletAddress as `0x${string}`, + }) + const receipt = await publicClient.waitForTransactionReceipt({ hash: approvalHash }) + if (receipt.status !== 'success') throw new Error('Approval transaction reverted') + } + + if (!approvalHash) throw new Error('Expected at least one approval transaction') actorRef.send({ type: 'APPROVAL_SUCCESS', txHash: approvalHash }) } catch (error) {