Solidity合约测试转账失败:EvmError: OutOfFund求助
智能合约rent函数转账失败排查
错误日志
│ └─ ← () ├─ [2562] AutoChain::balanceOf(owner: [0x7c8999dC9a822c1f0Df42023113EDB4FDd543266]) [staticcall] │ └─ ← 0 ├─ [0] console::log(0) [staticcall] │ └─ ← () ├─ [0] VM::prank(user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D]) │ └─ ← () ├─ [34790] AutoChainRental::rent(0) │ ├─ [0] owner::fallback{value: 10000000000000000000}() │ │ └─ ← EvmError: OutOfFund │ ├─ emit RentTransferFailedDebug(currentCarOwner: owner: [0x7c8999dC9a822c1f0Df42023113EDB4FDd543266], rentPrice: 10000000000000000000 [1e19], sender: user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D], senderBalance: 15000000000000000000 [1.5e19]) │ └─ ← RentTransferFailed() └─ ← RentTransferFailed() Test result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 4.67ms
被测试的rent函数代码
function rent(uint256 _tokenId) external payable { uint256 rentPrice = tokenIdToRentalDetails[_tokenId].price; if (rentPrice == 0) revert NotForRent(); address currentCarOwner = _requireOwned(_tokenId); (bool success, ) = currentCarOwner.call{value: rentPrice}(""); if (!success) { emit RentTransferFailedDebug( currentCarOwner, rentPrice, msg.sender, msg.sender.balance ); revert RentTransferFailed(); } uint256 rewardAmount = (rewardPercentage * rentPrice) / 100; // autoChainERC20.transferFrom(owner(), msg.sender, rewardAmount); delete vehiclesForRent[_tokenId]; addressToRentedTokenIds[msg.sender].push(_tokenId); emit CarRented(_tokenId, msg.sender, rentPrice, rewardAmount); }
测试用例代码
function test_ShouldTransferRightAmount() public listVehicle { uint256 rentPrice = autoChainRental.carRentalDetails(tokenIdMinted).price; vm.deal(user, rentPrice+5e18); // additional for gas. uint256 beforeOwnerBalance = owner.balance; console.log(rentPrice, user.balance); console.log(autoChainERC20.balanceOf(owner)); // vm.expectEmit(); // emit AutoChainRental.CarRented(tokenIdMinted, user, rentPrice, 1e19); vm.prank(user); autoChainRental.rent(tokenIdMinted); uint256 expectedOwnerBalance = beforeOwnerBalance + rentPrice; assertEq(owner.balance, expectedOwnerBalance); }
问题原因分析
从错误日志的EvmError: OutOfFund可以看出,AutoChainRental合约账户没有足够的ETH去执行给owner的转账操作,具体原因如下:
rent函数被标记为external payable,意味着它可以接收用户发送的ETH,但测试用例中调用rent时,没有附带任何ETH(即未使用{value: rentPrice}参数),导致合约账户ETH余额为0。- 函数逻辑中,合约尝试调用
currentCarOwner.call{value: rentPrice}(""),用合约自身的资金向owner转账,但此时合约没有足够的ETH,因此触发OutOfFund错误,最终导致RentTransferFailed回滚。
注意:用户地址的余额(1.5e19)足够支付租金,但用户未将ETH发送到合约,所以合约无法完成中转转账。
解决方案
修改测试用例中的调用方式,在调用rent函数时附带对应金额的ETH:
vm.prank(user); autoChainRental.rent{value: rentPrice}(tokenIdMinted); // 添加{value: rentPrice}参数
额外优化建议:
- 可以在
rent函数中添加校验,确保用户发送的ETH金额等于租金价格,避免金额不匹配的情况:if (msg.value != rentPrice) revert InvalidPaymentAmount();
内容的提问来源于stack exchange,提问作者Masood
相关产品推荐
相关产品推荐

