智能合约提币故障:Goerli ETH转入黑洞地址求助
合约提币异常:资金转入黑洞地址问题
我向合约转入Goerli ETH后,在Goerli Etherscan上能查到合约余额,但调用withdraw函数后,合约余额变为0,资金并未转入我的钱包地址,反而在内部交易中显示资金被转入了Goerli黑洞地址。提币交易有记录但无转账金额,求解决办法。
相关合约代码
基础合约 TimeControlled
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract TimeControlled { address public owner; address[] private ownerArr; uint256 dateForWithdrawal; address public contractAddress; mapping(address => bool) private addressToBool; constructor(bool isAllowed) public { owner == msg.sender; contractAddress == address(this); addressToBool[msg.sender] = isAllowed; addressToBool[address(this)] = isAllowed; ownerArr.push(owner); ownerArr.push(contractAddress); } // 向合约转入资金并返回true function fund() public payable returns (bool) { return true; } // 检查调用者是否为授权地址 modifier owners() { require(addressToBool[msg.sender], "You are not allowed!"); _; } // 提币函数 function withdraw() public payable owners returns (bool) { require(address(this).balance > 0); payable(owner).transfer(address(this).balance); return true; /// @notice 从合约提取资金 /// @dev 使用owners修饰符,返回布尔值 } }
继承合约 CallWithdraw
注:通过该合约的构造函数完成部署
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol"; import "./Timecontrolled.sol"; contract CallWithdraw is ChainlinkClient, TimeControlled { using Chainlink for Chainlink.Request; address public linkToken; uint256 public oraclePayment; address public oracle; bytes32 public jobId; uint256 public fundingPeriod = 5; constructor( address _linkToken, uint256 _oraclepayment, address _oracle, bytes32 _jobId ) public TimeControlled(true) { owner == msg.sender; oraclePayment = _oraclepayment; linkToken = _linkToken; oracle = _oracle; jobId = _jobId; } function callWithdraw() external { // 如果所有者未在设定时间调用提币函数,合约自动调用withdraw require(address(this).balance > 0); Chainlink.Request memory req = buildChainlinkRequest( jobId, address(this), this.fulfill.selector ); req.addUint("until", block.timestamp + fundingPeriod * 1 minutes); withdraw(); sendChainlinkRequestTo(oracle, req, oraclePayment); } function fulfill(bytes32 _requestId) public recordChainlinkFulfillment(_requestId) { address(this).balance == 0; } }
部署脚本
from brownie import CallWithdraw, config from scripts.helpful_scripts import get_account, network, get_contract from web3 import Web3 oracle_payment = config["networks"][network.show_active()]["payment"] oracle = config["networks"][network.show_active()]["oracle"] job_Id = config["networks"][network.show_active()]["jobId"] def deploy_call_withdraw_and_time_controlled(): print(f"The network is {network.show_active()}") account = get_account() link_token = get_contract("link_token") print("Deploying contract...") call_withdraw = CallWithdraw.deploy( link_token.address, oracle_payment, oracle, job_Id, {"from": account}, ) """ print("Transferring linktoken...") link_token.transfer( call_withdraw.address, Web3.toWei(1, "ether"), {"from": account} ) print("Linktoken transferred successfully!") """ print(account.balance()) return call_withdraw def fund(): call_withdraw = CallWithdraw[-1] account = get_account() call_withdraw.fund({"value": Web3.toWei(0.03, "ether"), "from": account}) print(f"{account.balance()} is the current balance") call_withdraw.withdraw({"from": account}) print(f"{account.balance()} is the current balance") def get_contract_balance(): call_withdraw = CallWithdraw[-1] print(f"{call_withdraw.balance()} is {call_withdraw.address} balance") def main(): deploy_call_withdraw_and_time_controlled() fund() get_contract_balance()
问题根源与修复方案
核心错误:赋值运算符误用
合约中多处将赋值运算符=写成了相等判断符==,导致关键变量未被正确初始化:
TimeControlled构造函数中,owner == msg.sender只是判断所有者是否等于调用者,并没有给owner变量赋值,owner保留默认值address(0)(即黑洞地址);CallWithdraw构造函数中同样存在owner == msg.sender的错误,没有覆盖父类的owner变量;- 当
withdraw函数执行payable(owner).transfer(address(this).balance)时,实际是把资金转到了address(0),也就是你看到的黑洞地址。
修复步骤
将所有错误的
==替换为=:- 修正
TimeControlled构造函数:constructor(bool isAllowed) public { owner = msg.sender; contractAddress = address(this); addressToBool[msg.sender] = isAllowed; addressToBool[address(this)] = isAllowed; ownerArr.push(owner); ownerArr.push(contractAddress); } - 修正
CallWithdraw构造函数:constructor( address _linkToken, uint256 _oraclepayment, address _oracle, bytes32 _jobId ) public TimeControlled(true) { owner = msg.sender; oraclePayment = _oraclepayment; linkToken = _linkToken; oracle = _oracle; jobId = _jobId; }
- 修正
额外优化:
withdraw函数不需要payable修饰,因为该函数是转出资金,无需接收ETH;- 删除
fulfill函数中无效的address(this).balance == 0;代码,该判断无实际作用。
重要提示
已经转入合约的资金无法找回,因为address(0)是黑洞地址,没有任何私钥可以控制该地址的资产。后续部署修复后的合约时,务必仔细检查变量赋值逻辑。
内容的提问来源于stack exchange,提问作者christian olori
相关产品推荐
相关产品推荐

