如何将合约余额从内部函数转移到外部地址?

How to transfer a contract balance to an external address from an internal function?

在这个合约中,一旦第三个参与者买了票,我就调用 payWinner()payWinner() 是内部的,以避免任何外部调用,但是当它执行 winnerAddress.transfer(winnableBalance); 时,合约余额被发送到 internal address 而不是外部地址 winnerAddressburnLeftBalance() 显然也是如此。

我想要么无法将部分合约持有量从内部函数转移到外部地址,我需要为此创建一个 public 函数(但我不知道我该怎么做确定是合约本身调用了函数),或者我遗漏了什么。

pragma solidity ^0.8.14;

contract Lottery {
  address payable public burnAddress = payable(0x0000000000000000000000000000000000000000);
  uint256 public ticketsPerRound = 3;
  uint256 public ticketPrice = 0.001 * 10e8 * 10e8;
  address[] public participantAddresses;

  error WrongTicketPrice(uint256 expectedRole, uint256 actualRole);

  function buyTicket() external payable {
    if (msg.value != ticketPrice) {
      revert WrongTicketPrice(ticketPrice, msg.value);
    }

    participantAddresses.push(msg.sender);

    if (participantAddresses.length >= ticketsPerRound) {
      payWinner();
    }
  }

  function getBalance() external view returns (uint256) {
    return address(this).balance;
  }

  function getParticipantAddresses() external view returns (address[] memory) {
    return participantAddresses;
  }

  function getWinnableBalance() public view returns (uint256) {
    return address(this).balance / 2;
  }

  function getWinner() internal view returns (address payable winnerAddress) {
    uint256 unsafeRandomNumber = uint256(
      keccak256(abi.encodePacked(block.difficulty, block.timestamp, participantAddresses))
    );
    uint256 winnerIndex = unsafeRandomNumber % participantAddresses.length;
    winnerAddress = payable(participantAddresses[winnerIndex]);
  }

  function burnLeftBalance() internal {
    uint256 leftBalance = address(this).balance;

    burnAddress.transfer(leftBalance);

    reset();
  }

  function payWinner() internal {
    uint256 winnableBalance = getWinnableBalance();
    address payable winnerAddress = getWinner();

    winnerAddress.transfer(winnableBalance);

    burnLeftBalance();
  }

  function reset() internal {
    delete participantAddresses;
  }
}

没有“内部地址和外部地址”这样的东西。我能想到的最接近这个术语的是Externally Owned Account,这是一个表示从私钥派生的地址的术语,没有合约字节码,主要由最终用户钱包使用.但这似乎与你的问题无关。

Etherscan 使用“内部事务”作为“message 虚拟机内调用”的更 user-friedly 术语。这些“内部交易”细节(例如从什么发件人,到什么收件人,传递了什么数据,什么 ETH 值,...)不是 public 区块链数据的一部分 - 只有“父”交易存储在区块链中。因此 Etherscan 通过模拟交易来检索这些详细信息,记录其消息调用并将它们存储在 Etherscan 自己的数据库中,然后在单独的选项卡中显示它们。

因此,您发送的交易是from您的地址,to合约地址,value为0.01 ETH,其data字段包含选择器您要执行的 buyTicket() 函数...这些都是您发送的交易的参数。而“内部交易”只是一种显示您的交易产生的状态变化及其踪迹的方式。但是只要产生了状态变化(例如 winnerAddress 的余额增加),它们是由“父”交易还是“内部交易”产生的都没有区别。

TLDR:这不是错误,而是一项功能。 :)