Solidity 简单提取余额合约

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

我写了一份用于提取cantract余额的实体合约。有人可以告诉它是否有效。 withdrawSafe 应该可以防止重入攻击,但我不知道是否可以。只需检查一切是否正常,并请建议如何改进。可能会犯一些愚蠢的错误。

// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.19;

event Response(bool success, bytes data);


interface IVault {
    function deposit() external payable;

    function withdrawSafe(address payable holder) external;

    function withdrawUnsafe(address payable holder) external;
}

interface IAttacker {
    function depositToVault(address vault) external payable;

    function attack(address vault) external;
}

contract Vault is IVault {

    bool private _entered;

     modifier nonReentrant {
        require(!_entered, "re-entrant call");
        _entered = true;
        _;
        _entered = false;
    }

    mapping(address => uint256) public balance;

    function deposit() external payable {
        balance[msg.sender] += msg.value;
    }

    function withdrawSafe(address payable holder) external nonReentrant {
        (bool success, bytes memory data) = holder.call{value: balance[msg.sender], gas: 5000}
        (abi.encodeWithSignature("withdrawSafe(string,uint256)", "call WS", 123));
        emit Response(success, data);
    }

    function withdrawUnsafe(address payable holder) external {
        (bool success, bytes memory data) = holder.call{value: balance[msg.sender], gas: 5000}
        (abi.encodeWithSignature("withdrawSafe(string,uint256)", "call WS", 123));
        emit Response(success, data);
    }
}

此外,如果有人知道如何在没有任何测试网或主网链的情况下在 Remix 中测试合约,或者推荐另一个 IDE,我将非常高兴听到任何建议。

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

我遇到了一些可以改进的问题:

  • nonReentrant
    修饰符已正确实现,但它也应该用于保护
    withdrawSafe
    函数内的余额更新逻辑。

  • 在进行外部调用之前应更新余额,以防止重入。这样可以确保即使外部调用进行递归调用,余额也已经被设置为零,防止重入。

  • 当前的实现不会更新持有者的余额。这是防止重入所必需的。

  • 指定的 Gas 限制 (5000) 可能不足以使调用成功,具体取决于接收器的实现。您可能想确保收件人足以处理交易。

  • 简单的以太币转移并不是必需的。您可以直接使用

    holder.transfer
    holder.call{value: amount}("")
    转账。

根据建议的改进,您的合同将如下所示:

// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.19;

event Response(bool success, bytes data);

interface IVault {
    function deposit() external payable;

    function withdrawSafe(address payable holder) external;

    function withdrawUnsafe(address payable holder) external;
}

interface IAttacker {
    function depositToVault(address vault) external payable;

    function attack(address vault) external;
}

contract Vault is IVault {
    bool private _entered;

    modifier nonReentrant {
        require(!_entered, "re-entrant call");
        _entered = true;
        _;
        _entered = false;
    }

    mapping(address => uint256) public balance;

    function deposit() external payable {
        balance[msg.sender] += msg.value;
    }

    function withdrawSafe(address payable holder) external nonReentrant {
        uint256 amount = balance[msg.sender];
        require(amount > 0, "Insufficient balance");

        // Update balance before making the external call
        balance[msg.sender] = 0;

        // Use call to transfer funds and handle response
        (bool success, bytes memory data) = holder.call{value: amount}("");
        require(success, "Transfer failed");

        emit Response(success, data);
    }

    function withdrawUnsafe(address payable holder) external {
        uint256 amount = balance[msg.sender];
        require(amount > 0, "Insufficient balance");

        // Update balance before making the external call
        balance[msg.sender] = 0;

        // Use call to transfer funds and handle response
        (bool success, bytes memory data) = holder.call{value: amount}("");
        require(success, "Transfer failed");

        emit Response(success, data);
    }
}

这应该有助于确保

withdrawSafe
函数能够安全地抵御重入攻击。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.