如何将合约余额从内部函数转移到外部地址?
How to transfer a contract balance to an external address from an internal function?
在这个合约中,一旦第三个参与者买了票,我就调用 payWinner()
。 payWinner()
是内部的,以避免任何外部调用,但是当它执行 winnerAddress.transfer(winnableBalance);
时,合约余额被发送到 internal address 而不是外部地址 winnerAddress
。 burnLeftBalance()
显然也是如此。
我想要么无法将部分合约持有量从内部函数转移到外部地址,我需要为此创建一个 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:这不是错误,而是一项功能。 :)
在这个合约中,一旦第三个参与者买了票,我就调用 payWinner()
。 payWinner()
是内部的,以避免任何外部调用,但是当它执行 winnerAddress.transfer(winnableBalance);
时,合约余额被发送到 internal address 而不是外部地址 winnerAddress
。 burnLeftBalance()
显然也是如此。
我想要么无法将部分合约持有量从内部函数转移到外部地址,我需要为此创建一个 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:这不是错误,而是一项功能。 :)