如何用wagmi.js实现非原生代币授权与跨代币兑换?
解决ERC20代币授权与兑换流程问题
你遇到的核心问题是:非MATIC的ERC20代币兑换时,必须先授权DODO Router合约访问你的代币,合约才有权限转移你的代币完成兑换。MATIC是原生代币,不需要授权,所以之前的兑换能成功,而反向/跨代币兑换因为缺少授权步骤失败。
以下是基于wagmi.js实现完整授权+兑换流程的方案:
一、核心逻辑步骤
- 检查授权额度:查询用户已授权给DODO Router的代币数量,判断是否足够本次兑换使用
- 发起授权交易:如果授权不足,调用ERC20代币的
approve方法完成授权 - 执行兑换交易:授权成功后,再执行之前的兑换逻辑
二、代码实现调整
1. 定义ERC20核心ABI片段
首先需要ERC20合约的approve和allowance方法的ABI,用来与代币合约交互:
const ERC20_ABI = [ { constant: true, inputs: [ { name: "owner", type: "address" }, { name: "spender", type: "address" }, ], name: "allowance", outputs: [{ name: "", type: "uint256" }], payable: false, stateMutability: "view", type: "function", }, { constant: false, inputs: [ { name: "spender", type: "address" }, { name: "value", type: "uint256" }, ], name: "approve", outputs: [{ name: "", type: "bool" }], payable: false, stateMutability: "nonpayable", type: "function", }, ] as const;
2. 在DexPage.tsx中添加授权相关逻辑
修改现有代码,整合授权检查与交易流程:
import { useAllowance, useContractWrite, useWaitForTransaction, useAccount } from 'wagmi'; import { parseUnits } from 'viem'; // Polygon网络DODO Router合约地址(替换为实际地址) const DODO_ROUTER_ADDRESS = "0x..." const DexPage = () => { const [transactionData, setTransactionData] = useState<DodoAPIData | undefined>(); const [needsApproval, setNeedsApproval] = useState(false); const { address: userAddress } = useAccount(); // 1. 检查授权额度(仅当fromToken不是MATIC时生效) const { data: allowance } = useAllowance({ address: fromTokenAddress, owner: userAddress, spender: DODO_ROUTER_ADDRESS, enabled: !!userAddress && fromTokenAddress !== chain.polygon.nativeCurrency.address, }); // 2. 发起授权交易的hooks const { write: approveWrite, data: approveTxHash } = useContractWrite({ address: fromTokenAddress, abi: ERC20_ABI, functionName: 'approve', // 可选择授权本次兑换额度,或用最大额度避免重复授权:BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') args: [DODO_ROUTER_ADDRESS, parseUnits(fromTokenAmount, fromTokenDecimals)], }); // 等待授权交易完成 const { isSuccess: isApproveSuccess } = useWaitForTransaction({ hash: approveTxHash, }); // 3. 兑换交易的hooks(调整原有逻辑) const { data, isLoading, isSuccess, sendTransaction, error, status } = useSendTransaction({ request: { from: userAddress, to: transactionData?.to || "", // 非MATIC代币兑换时,value设为0,代币转移由合约通过授权执行 value: fromTokenAddress === chain.polygon.nativeCurrency.address ? parseUnits(fromTokenAmount, fromTokenDecimals) : BigInt(0), gasLimit: 500000, data: transactionData?.data || "", }, mode: "prepared", enabled: !needsApproval || isApproveSuccess, // 授权完成后才允许执行兑换 }); // 获取DODO交易参数后,判断是否需要授权 const fetchDodoParams = async () => { try { const price = await axios.get(dodoAPI, { params: { fromTokenAddress, fromTokenDecimals, toTokenAddress, toTokenDecimals, fromAmount, slippage: 1, userAddr: userAddress, chainId: 137, rpc: rpcUrl, } }); if (price.status !== 200) return; setTransactionData(price.data.data); // 判断是否需要授权:非MATIC代币且授权额度不足 if (fromTokenAddress !== chain.polygon.nativeCurrency.address) { const requiredAmount = parseUnits(fromTokenAmount, fromTokenDecimals); setNeedsApproval(!allowance || allowance < requiredAmount); } else { setNeedsApproval(false); } } catch (e) { console.error(e); } }; // 触发流程:先授权(如果需要),再兑换 const handleSwap = () => { if (needsApproval) { approveWrite?.(); } else { sendTransaction?.(); } }; return ( // 你的UI组件示例:按钮点击触发handleSwap <button onClick={handleSwap} disabled={isLoading || !transactionData}> {needsApproval ? '授权代币' : '立即兑换'} </button> ); };
三、关键细节说明
- 授权额度选择:可以授权本次兑换的精确额度,或直接授权最大额度(适合高频兑换场景,避免重复授权)
- MATIC判断:通过
chain.polygon.nativeCurrency.address区分原生代币与ERC20代币,原生代币无需授权 - 交易依赖:用
useWaitForTransaction监听授权交易的成功状态,确保授权完成后再执行兑换 - 错误处理:可在
useContractWrite和useSendTransaction中添加错误状态提示,提升用户体验
内容的提问来源于stack exchange,提问作者Abobker Elaghel
相关产品推荐
相关产品推荐

