简介Solidity0.8.18(2023年2月):根据EIP-6049,selfdestruct被标记为弃用。编译器会对其使用发出警告,建议开发者避免使用该函数。以太坊Cancun升级(计划中):依据EIP-6780,selfdestruct的行为将被修改。除了在合约部署交
即使你是低版本的合约,一般不建议使用合约自毁,建议在合约里面加开关,可以通过开关来暂停合约的业务功能。
执行 selfdestruct
时,EVM 触发 SELFDESTRUCT
操作码(旧称 SUICIDE
),完成两个关键动作:
recipient
(即使接收地址是合约且未定义 receive()
或 fallback
函数)。selfdestruct(address payable recipient);
合约完成所有功能或达到预期目的时,销毁以释放存储资源
function terminateContract() public onlyOwner {
selfdestruct(payable(owner));
}
合约需要升级或替换 在需要替换合约逻辑时,可以销毁旧合约,部署新合约。
contract OldVersion {
address public immutable newContract;
constructor(address _newContract) {
newContract = _newContract;
}
function upgrade() public {
require(msg.sender == owner, "Unauthorized");
// 将资金转移到新合约后自毁
selfdestruct(payable(newContract));
}
}
合约遭受攻击或存在安全问题
合约存在漏洞或私钥丢失时,通过预置的自毁函数转移资金。比如多签钱包的 emergencyExit 功能。
临时合约(如空投领取)完成任务后自毁,清除链上数据。
recipient
是恶意合约,可通过 receive()
函数发起重入攻击。isActive
状态变量)。原理:攻击者在相同地址重新部署恶意合约(通过 CREATE2
)。
// 错误示例:自毁后未清理权限
contract Vulnerable {
address owner;
constructor() { owner = msg.sender; }
function kill() public {
require(msg.sender == owner);
selfdestruct(payable(owner));
}
}
安全自毁示例:
function safeDestruct() external {
// 1. 权限检查
require(msg.sender == owner, "Unauthorized");
// 2. 转移剩余资产(ERC20/ERC721)
_withdrawTokens();
// 3. 清理授权(避免复活攻击)
approvals[spender] = 0;
// 4. 发出事件通知
emit ContractDestroyed(block.timestamp);
// 5. 自毁(转ETH给可信地址)
selfdestruct(payable(treasury));
}
问题:未授权调用可导致任意资金转移
解决方案:
function destroy() external {
require(msg.sender == owner, "Unauthorized"); // 强权限校验
selfdestruct(payable(owner));
}
onlyOwner
修饰器风险:
防御措施:
address payable immutable public safeReceiver; // 固定为可信地址
constructor(address payable _receiver) {
require(_receiver != address(0), "Invalid address");
safeReceiver = _receiver;
}
function destroy() external onlyOwner {
selfdestruct(safeReceiver); // 仅允许转给预设地址
}
问题:自毁前未转移 ERC20/NFT 等资产将永久锁定
标准化流程:
function safeDestruct() external onlyOwner {
// 1. 转移原生代币 (ETH)
uint256 ethBalance = address(this).balance;
(bool sent, ) = safeReceiver.call{value: ethBalance}("");
require(sent, "ETH transfer failed");
// 2. 转移 ERC20 代币
IERC20 token = IERC20(tokenAddress);
uint256 tokenBalance = token.balanceOf(address(this));
token.transfer(safeReceiver, tokenBalance);
// 3. 转移 NFT (示例 ERC721)
IERC721 nft = IERC721(nftAddress);
uint256 nftId = storedNftId;
nft.safeTransferFrom(address(this), safeReceiver, nftId);
selfdestruct(safeReceiver);
}
在 2023 年后,避免使用 selfdestruct
。优先选择代理升级(OpenZeppelin Upgradeable Contracts)或状态冻结模式。如必须使用,需进行:
onlyOwner
)如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!