withdraw() 中调用函数返回 false

问题描述 投票:0回答:1

withdraw() 中的调用函数返回 false,并且其下面的 require 语句记录“转账失败”。我问过 ChatGPT,它说“检查你的 Gas 限制”。(我这个合约的 Gas 限制是 3000000)。我不知道如何解决这个错误。

智能合约

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract Thrive {

    error InvalidDeadline(string message);

    enum campaignStatus {
        OPEN, 
        CLOSED
    }

    struct Campaign {
        address payable owner;
        string title;
        string description;
        uint256 opening;
        uint256 deadline;
        uint256 amountCollected;
        string image;
        campaignStatus status;
        address[] donators;
        uint256[] donations;
    }

    event Action (uint256 id, string actionType, address executor, uint256 timestamp);

    mapping(uint256 => Campaign) public campaigns;

    uint256 public numberOfCampaigns = 0;

    function createCampaign(address _owner, string memory _title, string memory _description, uint256 _deadline, string memory _image) public returns (uint256) {
        Campaign storage campaign = campaigns[numberOfCampaigns];
    
        // require(campaign.deadline > block.timestamp, "The deadline should be a date in the future.");
        if(_deadline < block.timestamp) {
            campaign.status = campaignStatus.CLOSED;
            revert InvalidDeadline("The deadline should be a date in the future");
        }
        campaign.owner = payable(_owner);
        campaign.title = _title;
        campaign.description = _description;
        campaign.opening = block.timestamp;
        campaign.deadline = _deadline;
        campaign.amountCollected = 0;
        campaign.image = _image;
        campaign.status = campaignStatus.OPEN;

        numberOfCampaigns++;

        emit Action (numberOfCampaigns - 1, "Campaign created", _owner, block.timestamp);

        return numberOfCampaigns - 1;
    }

    function donateToCampaign(uint256 _id) public payable {
        Campaign storage campaign = campaigns[_id];
        require(campaign.status == campaignStatus.OPEN, "The campaign is closed");

        uint256 amount = msg.value;

        campaign.donators.push(msg.sender);
        campaign.donations.push(amount);

        (bool sent,) = payable(campaign.owner).call{value: amount}("");
        require(sent, "transaction failed");

        if(sent) {
            campaign.amountCollected = campaign.amountCollected + amount;
        }

        emit Action (_id, "Fund donated to campaign", msg.sender, block.timestamp);
    }

    function withdraw(uint256 _id) public {
        Campaign storage campaign = campaigns[_id];

        require(campaign.status == campaignStatus.OPEN, "The campaign is closed");
        require(msg.sender == campaign.owner, "Withdrawer should be the owner of the campaign");
        require(campaign.amountCollected > 0, "Not enough fund generated");

        uint256 amount = campaign.amountCollected;
        require(amount > 0, "Amount not collected yet");

        (bool success, ) = payable(campaign.owner).call{value: amount}("");
        require(success, "Transfer failed");
 
        campaign.amountCollected = 0;
        campaign.status = campaignStatus.CLOSED;
        
        emit Action (_id, "Fund withdrawed", msg.sender, block.timestamp);
    }

    function getDonators(uint256 _id) view public returns (address[] memory, uint256[] memory) {
        return (campaigns[_id].donators, campaigns[_id].donations);
    }

    function getCampaigns() public view returns (Campaign[] memory) {
        Campaign[] memory allCampaigns = new Campaign[](numberOfCampaigns);

        for(uint i = 0; i < numberOfCampaigns; i++) {
            Campaign storage item = campaigns[i];

            allCampaigns[i] = item;
        }

        return allCampaigns;
    }

    function deleteCampaign(uint256 _id) public returns (uint256) {
        Campaign storage campaign = campaigns[_id];

        require(campaign.status == campaignStatus.CLOSED, "The campaign is open");

        campaign.owner = payable(address(0));
        campaign.title = "";
        campaign.description = "";
        campaign.opening = 0;
        campaign.deadline = 0;
        campaign.amountCollected = 0;
        campaign.image = "";

        numberOfCampaigns--;

        emit Action (_id, "Campaign deleted", msg.sender, block.timestamp);

        return numberOfCampaigns;
    }

    function getOwner() public view returns (address) {
        return address(this);
    }

    function getCampaignOwner(uint256 _id) public view returns (address) {
        return campaigns[_id].owner;
    }

    function getAmountCollected(uint256 _id) public view returns (uint256) {
        return campaigns[_id].amountCollected;
    }

}

错误:

status  0x0 Transaction mined but execution failed
transaction hash    0xe3e3e02272ee5393d3d7d48e94972a83fee50b37d0d3f58283b3ece5507cf838
block hash  0x2ac76a4b78da8572c51720eb198912d60d00b27b537d7145424ab679f51f8965
block number    198
from    0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
to  Thrive.withdraw(uint256) 0x45373635641f5C51bf1029FdF2A225674D61AD45
gas 3000000 gas
transaction cost    35923 gas 
execution cost  14731 gas 
input   0x2e1...00000
decoded input   {
    "uint256 _id": "0"
}
decoded output  {}
logs    []
transact to Thrive.withdraw errored: Error occurred: revert.

revert
    The transaction has been reverted to the initial state.
Reason provided by the contract: "Transfer failed".
You may want to cautiously increase the gas limit if the transaction went out of gas.

我尝试从特定活动中撤回以太币,并期望它能够正常工作,因为所有 require 语句都在调用函数之上传递。但交易失败并恢复到之前的状态。

ethereum blockchain solidity smartcontracts remix-ide
1个回答
0
投票

您提供的代码正在

donateToCampaign
函数中将代币转移给活动所有者,之后您尝试重新提取合同中不存在且已转移给所有者的一定数量的代币.

下面的代码删除了

donateToCampaign
函数上的转账操作,并将代币保留在合约内,提现函数应该可以正常工作:

 function donateToCampaign(uint256 _id) public payable {
        Campaign storage campaign = campaigns[_id];
        require(campaign.status == campaignStatus.OPEN, "The campaign is closed");

        uint256 amount = msg.value;

        campaign.donators.push(msg.sender);
        campaign.donations.push(amount);

        campaign.amountCollected = campaign.amountCollected + amount;

        emit Action (_id, "Fund donated to campaign", msg.sender, block.timestamp);
    }

加上提现功能也受到重入攻击的污染,必须在转账操作之前设置合约的状态,这里是固定的提现功能:

    function withdraw(uint256 _id) public {
        Campaign storage campaign = campaigns[_id];

        require(campaign.status == campaignStatus.OPEN, "The campaign is closed");
        require(msg.sender == campaign.owner, "Withdrawer should be the owner of the campaign");
        require(campaign.amountCollected > 0, "Not enough fund generated");

        uint256 amount = campaign.amountCollected;
        require(amount > 0, "Amount not collected yet");

        campaign.amountCollected = 0;
        campaign.status = campaignStatus.CLOSED;

        (bool success, ) = payable(campaign.owner).call{value: amount}("");
        require(success, "Transfer failed");
 
        emit Action (_id, "Fund withdrawed", msg.sender, block.timestamp);
    }

© www.soinside.com 2019 - 2024. All rights reserved.