Sushiswap BentoBox报Transfer not approved错误如何解决
Sushiswap Trident兑换报错「BentoBox: Transfer not approved」排查
问题相关代码
用于与Sushiswap交互的Solidity函数如下:
function swapSingleParamswhitPool( uint256 amountIn, uint256 amountOutMinimum, address pool, address tokenIn, bytes memory data) public payable { ITridentRouter.ExactInputSingleParams memory exactInputSingleParams = ITridentRouter.ExactInputSingleParams(amountIn, amountOutMinimum, pool, tokenIn, data); tridentRouter.exactInputSingle{ value: msg.value }(exactInputSingleParams); }
报错信息
调用上述函数始终触发回滚,回滚信息如下:
{ "code": 3, "message": "execution reverted: BentoBox: Transfer not approved", "data": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001f42656e746f426f783a205472616e73666572206e6f7420617070726f76656400" }
已尝试在BentoBox中配置masterContract为授权状态、手动执行授权操作,问题仍未解决。
根因与修复方案
报错核心原因是授权链路不完整,BentoBox的授权逻辑和普通ERC20授权不同,多层授权任意一层缺失都会触发该错误,逐一排查以下点即可解决:
- 授权主体错误:Trident执行兑换时,是BentoBox直接从发起兑换的地址划扣资产,如果你是通过合约调用
exactInputSingle,那么资产持有地址是你的业务合约地址,不是你发起交易的外部钱包地址。之前手动做的授权大概率是给外部钱包地址开的权限,没有给业务合约地址开放TridentRouter的BentoBox操作权限。 - 缺少ERC20层授权:如果输入资产是ERC20而非原生链代币(如ETH/BNB),需要先给BentoBox合约开放对应数量的ERC20转账授权,注意授权对象是BentoBox地址,不是TridentRouter地址。
- 缺少BentoBox内部授权:需要调用BentoBox的
setMasterContractApproval方法,将TridentRouter设置为当前发起兑换地址(业务合约地址)的授权masterContract,允许其操作对应地址在BentoBox内的资产。
修复后的参考实现如下:
// 替换为对应链的实际部署地址 address private constant BENTO_BOX_ADDR = 0xF5BCE5077908a1b7370B9ae04AdC565EBd643966; IERC20 private constant NATIVE = IERC20(0x0000000000000000000000000000000000000000); function swapSingleParamswhitPool( uint256 amountIn, uint256 amountOutMinimum, address pool, address tokenIn, bytes memory data ) public payable { // 处理非原生币的ERC20层授权 if (tokenIn != address(NATIVE)) { // 从调用者地址将代币转入当前合约 IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn); // 给BentoBox授权对应额度的ERC20转账权限 IERC20(tokenIn).approve(BENTO_BOX_ADDR, amountIn); } // 给TridentRouter开放BentoBox内部操作权限 IBentoBox(BENTO_BOX_ADDR).setMasterContractApproval( address(this), // 授权主体:当前业务合约地址,不要填外部钱包地址 address(tridentRouter), // 被授权的masterContract:Trident路由地址 true, // 授权状态设为开启 0, // 合约自身调用不需要签名,参数填0即可 bytes32(0), bytes32(0) ); ITridentRouter.ExactInputSingleParams memory exactInputSingleParams = ITridentRouter.ExactInputSingleParams( amountIn, amountOutMinimum, pool, tokenIn, data ); // 原生币兑换时msg.value必须和amountIn严格相等 tridentRouter.exactInputSingle{ value: msg.value }(exactInputSingleParams); }
注意:如果是外部钱包直接调用Trident路由,不需要合约层的授权逻辑,只需要钱包提前完成两个授权:1. 给BentoBox开放对应ERC20的转账额度;2. 给TridentRouter开放BentoBox的masterContract操作权限即可。
内容的提问来源于stack exchange,提问作者Ivan Kral
相关产品推荐
相关产品推荐

