You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的转账操作,具体原因如下:

  1. rent函数被标记为external payable,意味着它可以接收用户发送的ETH,但测试用例中调用rent时,没有附带任何ETH(即未使用{value: rentPrice}参数),导致合约账户ETH余额为0。
  2. 函数逻辑中,合约尝试调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 10:52:08