首先,这行代码
require(tokenContract.transfer(admin, tokenContract.balanceOf(address(this))));
告诉你的合约将其全部余额转移到管理员地址,要求调用 transfer 的结果为 true,否则函数失败并且状态恢复,阻止进一步执行。
然后,调用:
msg.sender.transfer(address(this).balance);
再次尝试转移此合约的总余额。
需要注意的一点是,require 语句可以确保在函数体被声明并用作函数体执行之前满足合约状态的条件modifier 声明在合同中。
另一件事是,对 .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()
}