Solidity 中的 EndSale() 函数失败

EndSale() function in Solidity fails

我有以下代码:

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; 能否请你帮忙?谢谢!

首先,这行代码

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

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

然后,调用:

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

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

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

另一件事是,调用 .transfer 不会 return 任何东西,它们只会传播发生的错误。有一种替代方法可以使用函数 .send() ,它 return 一个 bool 但它在语法上不是很吸引人并且不推荐使用它,除非有必要或者你知道自己在做什么,因为程序员需要明确要求在调用 returns 时调用 revert() false,如果忽视可能会导致严重的错误。

所以实际上,调用

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

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

您可以通过将字符串作为第二个参数添加到 require 调用来确定是否属于这种情况,当条件计算为 [=38 时,它将输出一条日志消息=]假.

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

这是一个主要的逻辑 error/bug 并且会尝试再次转移相同金额(在合同已经这样做之后),这被称为 双重支付,但它应该不再有余额,因为它已通过之前在 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()
}