首页 > 解决方案 > Solidity 中的 EndSale() 函数失败

问题描述

我有以下可靠代码:

function endSale() public {
    require(msg.sender == admin);
    require(tokenContract.transfer(admin, tokenContract.balanceOf(address(this))));

    // UPDATE: Let's not destroy the contract here
    // Just transfer the balance to the admin
    msg.sender.transfer(address(this).balance);
}

由于某种原因,当我调用该函数时它失败了。我正在使用 pragma solidity 0.6.0;能否请你帮忙?谢谢!

标签: tokensolidity

解决方案


首先,这行代码

require(tokenContract.transfer(admin, tokenContract.balanceOf(address(this))));

告诉你的合约将其全部余额转移到管理员地址,要求调用 transfer 的结果为 true,否则函数失败并且状态恢复,阻止进一步执行。

然后,调用:

msg.sender.transfer(address(this).balance);

再次尝试转移该合约的总余额。

需要注意的一点是,当声明并用作合约中声明的修饰符语句时, require语句可以确保在函数体执行之前满足合约状态的条件。

另一件事是,对.transfer的调用不会返回任何内容,它们只会传播发生的错误。使用函数.send()有一个替代工作,它确实返回一个布尔值,但它在语法上不是很吸引人,并且不建议使用它,除非有必要或者你知道你在做什么,因为程序员需要明确要求在调用返回 false 的情况下调用revert()并且如果忽略可能会导致严重的错误。

所以实际上,调用

require(tokenContract.transfer(admin, tokenContract.balanceOf(address(this))));

应该总是以异常终止,因为它既不评估为真也不评估为假,并且在大多数其他编程语言中,solidity 没有普遍存在的 NULL 值的概念。

您可以通过将字符串作为第二个参数添加到require调用来确定是否是这种情况, 当条件评估为false时,它​​将输出一条日志消息。

然后,如果由于某种原因不是终止执行的原因,您还试图转移合同的余额,然后再调用您的方法转移其余额。

这是一个重大的逻辑错误/错误,并会尝试再次(在合同已经这样做之后)导致相同金额的转移,这被称为双重支付,但它不应再有余额,因为它有已通过require()中的先前调用转移给管理员;.

一个合理的解决方案可能看起来类似于:

modifier onlyAdmin(){
    require(msg.sender == admin, "only an admin may access this method");
    _;
}

modifier nonZeroBalance(){
    require(tokenContract.balanceOf(address(this)) > 0 wei, "contract requires a non-zero balance to execute");
    _;
}

function endSale() public onlyAdmin nonZeroBalance {
    //
    // require this contract to have a non-zero balance (or any other appropriate value as required) before accepting transfers since,
    // it's a waste of gas to transfer nothing
    //
    // now transfer should work alright and will throw and exception,
    // should .transfer fail in any way
    uint256 previousBalance = address(this).balance;
    //
    msg.sender.transfer(previousBalance);
    //
    //transaction complete, this contract's balance is now 0
    //
    //perform additional sanity checks to ensure this contract and the admin's
    //state are what should be expected, otherwise call revert() or throw()
}

推荐阅读