如何使用web3设置代币无限授权,避免DEX兑换每次都要重复授权?
问题原因
你当前的代码没有预先查询用户已授权额度,每次兑换操作都会无条件发起approve交易请求,所以每次都需要用户确认授权。PancakeSwap等成熟DEX的逻辑是先校验授权额度,只有额度不足时才会发起授权请求,额度足够时直接走兑换流程。
修复步骤
1. 新增授权额度查询逻辑
在发起授权前,先调用ERC20合约的allowance方法查询当前用户对0x授权目标合约的可用额度,额度大于等于本次兑换的卖出代币数量时,直接跳过授权步骤。
2. 兼容特殊代币逻辑
部分非标准ERC20代币(如USDT、USDC等)不允许直接修改非零的授权额度,需要先将授权额度设为0,再提交新的授权额度申请。
3. 确认授权目标地址一致性
确认每次调用0x API获取的allowanceTarget为同一固定地址,避免因授权目标不同导致之前的授权失效。
修改后的参考代码
const BscABI = await axios.get(`https://api.bscscan.com/api`, { params: { module: 'contract', action: 'getabi', address: receipt.sellTokenAddress, } }) const TokenAbi = JSON.parse(BscABI.data.result); const Tokencontract = new web3.eth.Contract(TokenAbi, receipt.sellTokenAddress); // 新增:查询当前授权额度 const currentAllowance = await Tokencontract.methods.allowance(metaMaskAddress, receipt.allowanceTarget).call(); const sellAmount = new BigNumber(receipt.sellAmount); // 本次兑换要卖出的代币数量,从0x quote返回结果中取 // 额度足够直接走兑换 if (new BigNumber(currentAllowance).gte(sellAmount)) { const txHash = await web3.eth.sendTransaction({ from: metaMaskAddress, data: receipt.data, gas: receipt.gas, to: receipt.allowanceTarget, chainId: 56 }); } else { // 额度不足才发起授权 const maxApproval = new BigNumber(2).pow(256).minus(1); await Tokencontract.methods.approve(receipt.allowanceTarget, maxApproval).send({from: metaMaskAddress, chainId: 56}) .then(async () => { alert("Token Spend Allowed"); const txHash = await web3.eth.sendTransaction({ from: metaMaskAddress, data: receipt.data, gas: receipt.gas, to: receipt.allowanceTarget, chainId: 56 }); }) .catch(() => { setMessage("Token Spend Allowance Failed"); setError(true); }); }
额外优化建议
- 可以把用户的授权状态缓存在本地,避免每次兑换都重复查询链上额度,减少请求耗时,缓存时要注意绑定链ID、用户地址、代币地址、授权目标地址四个维度,避免缓存出错。
- 针对大额授权的风险,可以给用户提供自定义授权额度的选项,支持仅授权本次兑换所需额度。
内容的提问来源于stack exchange,提问作者Mohit Chandel
相关产品推荐
相关产品推荐

