Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Selfdestruct after Cancun Upgrade + Fix typos #711

Merged
merged 8 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions 17_Library/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ tags:
3. 不能接收以太币
4. 不可以被销毁

需要注意的是,库合约重的函数可见性如果被设置为`public`或者`external`,则在调用函数时会触发一次`delegatecall`。而如果被设置为`internal`,则不会引起。对于设置为`private`可见性的函数来说,其仅能在库合约中可见,在其他合约中不可用。


## Strings库合约

`Strings库合约`是将`uint256`类型转换为相应的`string`类型的代码库,样例代码如下:
Expand Down
30 changes: 30 additions & 0 deletions 26_DeleteContract/DeployContract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import "./DeleteContract.sol";

contract DeployContract {

struct DemoResult {
address addr;
uint balance;
uint value;
}

constructor() payable {}

function getBalance() external view returns(uint balance){
balance = address(this).balance;
}

function demo() public payable returns (DemoResult memory){
DeleteContract del = new DeleteContract{value:msg.value}();
DemoResult memory res = DemoResult({
addr: address(del),
balance: del.getBalance(),
value: del.value()
});
del.deleteContract();
return res;
}
}
Binary file added 26_DeleteContract/img/26-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added 26_DeleteContract/img/26-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added 26_DeleteContract/img/26-5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added 26_DeleteContract/img/26-6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
80 changes: 70 additions & 10 deletions 26_DeleteContract/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ tags:

`selfdestruct`命令可以用来删除智能合约,并将该合约剩余`ETH`转到指定地址。`selfdestruct`是为了应对合约出错的极端情况而设计的。它最早被命名为`suicide`(自杀),但是这个词太敏感。为了保护抑郁的程序员,改名为`selfdestruct`;在 [v0.8.18](https://blog.soliditylang.org/2023/02/01/solidity-0.8.18-release-announcement/) 版本中,`selfdestruct` 关键字被标记为「不再建议使用」,在一些情况下它会导致预期之外的合约语义,但由于目前还没有代替方案,目前只是对开发者做了编译阶段的警告,相关内容可以查看 [EIP-6049](https://eips.ethereum.org/EIPS/eip-6049)。

然而,在以太坊坎昆(Cancun)升级中,[EIP-6780](https://eips.ethereum.org/EIPS/eip-6780)被纳入升级以实现对`Verkle Tree`更好的支持。EIP-6780减少了`SELFDESTRUCT`操作码的功能。根据提案描述,当前`SELFDESTRUCT`仅会被用来将合约中的ETH转移到指定地址,而原先的删除功能只有在`合约创建-自毁`这两个操作处在同一笔交易时才能生效。所以目前来说:

1. 已经部署的合约无法被`SELFDESTRUCT`了。
2. 如果要使用原先的`SELFDESTRUCT`功能,必须在同一笔交易中创建并`SELFDESTRUCT`。

### 如何使用`selfdestruct`

`selfdestruct`使用起来非常简单:
Expand All @@ -34,7 +39,9 @@ selfdestruct(_addr);

其中`_addr`是接收合约中剩余`ETH`的地址。`_addr` 地址不需要有`receive()`或`fallback()`也能接收`ETH`。

### 例子
### Demo-转移ETH功能

以下合约在坎昆升级前可以完成合约的自毁,在坎昆升级后仅能实现内部ETH余额的转移。

```solidity
contract DeleteContract {
Expand All @@ -60,16 +67,11 @@ contract DeleteContract {

部署好合约后,我们向`DeleteContract`合约转入1 `ETH`。这时,`getBalance()`会返回1 `ETH`,`value`变量是10。

当我们调用`deleteContract()`函数,合约将自毁,此时再次调用合约函数交互会失败。

### 注意事项

1. 对外提供合约销毁接口时,最好设置为只有合约所有者可以调用,可以使用函数修饰符`onlyOwner`进行函数声明。
2. 当合约被销毁后再次与合约函数交互会报error。
3. 当合约中有`selfdestruct`功能时常常会带来安全问题和信任问题,合约中的selfdestruct功能会为攻击者打开攻击向量(例如使用`selfdestruct`向一个合约频繁转入token进行攻击,这将大大节省了GAS的费用,虽然很少人这么做),此外,此功能还会降低用户对合约的信心。
当我们调用`deleteContract()`函数,合约将触发`selfdestruct`操作。**在坎昆升级前,合约会被自毁。但是在升级后,合约依然存在,只是将合约包含的ETH转移到指定地址,而合约依然能够调用。**

### 在remix上验证
#### 在remix上验证

##### 坎昆升级之前
1. 部署合约并且转入1ETH,查看合约状态

![deployContract.png](./img/26-1.png)
Expand All @@ -78,6 +80,64 @@ contract DeleteContract {
![deleteContract.png](./img/26-2.png)
从测试中观察合约状态可以发现合约销毁后的ETH返回给了指定的地址,在合约销毁后再次调用合约函数进行交互则会失败。

##### 坎昆升级之后
1. 部署合约并且转入1ETH,查看合约状态

![deployContract2.png](./img/26-3.png)
2. 销毁合约,查看合约状态

![deleteContract2.png](./img/26-4.png)
从测试中观察合约状态可以发现合约包含的ETH已经清零(返回给了指定的地址),再次调用合约函数进行交互依然可以成功。




### Demo-同笔交易内实现合约创建-自毁

根据提案,原先的删除功能只有在`合约创建-自毁`这两个操作处在同一笔交易时才能生效。所以我们需要通过另一个合约进行控制。

```solidity
contract DeployContract {

struct DemoResult {
address addr;
uint balance;
uint value;
}

constructor() payable {}

function getBalance() external view returns(uint balance){
balance = address(this).balance;
}

function demo() public payable returns (DemoResult memory){
DeleteContract del = new DeleteContract{value:msg.value}();
DemoResult memory res = DemoResult({
addr: address(del),
balance: del.getBalance(),
value: del.value()
});
del.deleteContract();
return res;
}
}
```
#### 在remix上验证
1. 部署`DeployContract`合约并且转入1ETH调用`demo`方法,查看合约状态,显示`DeleteContract`已被正确部署,且在`selfdestruct`后ETH已转移到`DeployContract`。

![deployContract3.png](./img/26-5.png)
2. 选择导入返回值中的地址为`DeleteContract`。显示该地址不存有ETH,且调用合约函数进行交互均失败。

![deleteContract3.png](./img/26-6.png)


### 注意事项

1. 对外提供合约销毁接口时,最好设置为只有合约所有者可以调用,可以使用函数修饰符`onlyOwner`进行函数声明。
2. 当合约中有`selfdestruct`功能时常常会带来安全问题和信任问题,合约中的selfdestruct功能会为攻击者打开攻击向量(例如使用`selfdestruct`向一个合约频繁转入token进行攻击,这将大大节省了GAS的费用,虽然很少人这么做),此外,此功能还会降低用户对合约的信心。


## 总结

`selfdestruct`是智能合约的紧急按钮,销毁合约并将剩余`ETH`转移到指定账户。当著名的`The DAO`攻击发生时,以太坊的创始人们一定后悔过没有在合约里加入`selfdestruct`来停止黑客的攻击吧。
`selfdestruct`是智能合约的紧急按钮,销毁合约并将剩余`ETH`转移到指定账户。当著名的`The DAO`攻击发生时,以太坊的创始人们一定后悔过没有在合约里加入`selfdestruct`来停止黑客的攻击吧。在坎昆升级后,`selfdestruct`的作用也逐渐发生了改变,什么都不是一成不变的,还是要保持学习。
4 changes: 3 additions & 1 deletion 53_ERC20Permit/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,14 +198,16 @@ contract ERC20Permit is ERC20, IERC20Permit, EIP712 {

3. 调用合约的 `permit()` 方法,输入相应参数,进行授权。

4. 调用合约的 `allance()` 方法,输入相应的 `owner` 和 `spender`,可以看到授权成功。
4. 调用合约的 `allowance()` 方法,输入相应的 `owner` 和 `spender`,可以看到授权成功。

## 安全注意

ERC20Permit 利用链下签名进行授权给用户带来了便利,同时带来了风险。一些黑客会利用这一特性进行钓鱼攻击,骗取用户签名并盗取资产。2023年4月的一起针对 USDC 的签名[钓鱼攻击](https://twitter.com/0xAA_Science/status/1652880488095440897?s=20)让一位用户损失了 228w u 的资产。

**签名时,一定要谨慎的阅读签名内容!**

同时,一些合约在集成`permit`时,也会带来DoS(拒绝服务)的风险。因为`permit`在执行时会用掉当前的`nonce`值,如果合约的函数中包含`permit`操作,则攻击者可以通过抢跑执行`permit`从而使得目标交易因为`nonce`被占用而回滚。

## 总结

这一讲,我们介绍了 ERC20Permit,一个 ERC20 代币标准的拓展,支持用户使用链下签名进行授权操作,改善了用户体验,被很多项目采用。但同时,它也带来了更大的风险,一个签名就能将你的资产卷走。大家在签名时一定要更加谨慎。
1 change: 1 addition & 0 deletions S11_Frontrun/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ main()

- 使用预提交方案(commit-reveal scheme)。
- 使用暗池,用户发出的交易将不进入公开的`mempool`,而是直接到矿工手里。例如 flashbots 和 TaiChi。
- 在调用参数中加上保护性参数,如[滑点保护](https://uniswapv3book.com/milestone_3/slippage-protection.html),从而减少抢跑者的潜在收益。

## 总结

Expand Down
1 change: 1 addition & 0 deletions S17_CrossReentrancy/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ contract VulnerableBank {

*“我不杀伯仁,伯仁却因我而死...” -- 戴锁婆婆*

针对`Read-Only Reentrancy`, [Euler Finance](https://github.com/euler-xyz/euler-contracts/commit/91adeee39daf8ece00584b6f7ec3e60a1d226bc9#diff-05f47d885ccf959493d5c53203672966544d73232f5410184d5484a7aedf0c5eR260)采用`read-only reentrancy guard`,仅当未加锁时才能进行读取。同时,锁的可见性可以设置为`public`以供其他项目使用。

## 4. ERC721 & ERC777 Reentrancy

Expand Down
Loading