Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/swap-widget/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
49 changes: 28 additions & 21 deletions packages/swap-widget/src/hooks/useSwapApproval.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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

Expand All @@ -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) {
Expand Down
Loading