Skip to content

Commit 43f9834

Browse files
committed
Update wyeeeh.md
1 parent 1680c94 commit 43f9834

File tree

1 file changed

+280
-14
lines changed

1 file changed

+280
-14
lines changed

wyeeeh.md

+280-14
Original file line numberDiff line numberDiff line change
@@ -1435,7 +1435,6 @@ contract interactBAYC {
14351435

14361436
// 其他函数的实现...
14371437
}
1438-
14391438
```
14401439
使用这种方法,开发者不需要重复编写接口部分,只需要关注具体实现。这样代码更加简洁、规范,并且减少了错误的可能。
14411440
##### 测验结果
@@ -2592,7 +2591,8 @@ Contract x = new Contract{salt: _salt, value: _value}(params);
25922591
- 100/100
25932592

25942593

2595-
### 2024.10.18 `SelfDestruct`自毁
2594+
### 2024.10.18
2595+
#### WTF Academy Solidity 102.26 `SelfDestruct`自毁
25962596
`selfdestruct`是智能合约中的一种操作,用于销毁合约并将合约剩余的`ETH`余额转移到指定地址。这个功能设计的初衷是应对合约发生错误时的极端情况。起初它被命名为`suicide`(自杀),但由于敏感性问题,后来改名为`selfdestruct`
25972597
坎昆升级后,[EIP-6780](https://eips.ethereum.org/EIPS/eip-6780)`selfdestruct`进行了功能限制。该提案是为了支持Verkle Tree,而限制`SELFDESTRUCT`操作码的功能,改变后它只能转移ETH,删除合约的功能只有在同一笔交易中创建和销毁合约时才会生效。
25982598

@@ -2674,33 +2674,299 @@ contract DeployContract {
26742674
##### 测验结果
26752675
- 100/100
26762676

2677-
#### WTF Academy Solidity 102.26
2677+
### 2024.10.25
2678+
#### WTF Academy Solidity 102.27 ABI编码解码
2679+
##### ABI编码
2680+
###### `abi.encode`
2681+
`abi.encode`可以对多个不同类型的数据进行编码,将其转换成动态大小的字节数组。编码后的数据包含类型信息,符合以太坊ABI标准中规定的编码规则。
2682+
适合用于复杂参数的数据传输,特别是涉及多个数据类型的情况。比如需要将数据传递给低级`call`函数或者在跨合约调用中传输大量信息时使用。
26782683

2679-
##### 笔记
2684+
**语法**
2685+
```solidity
2686+
bytes memory encodedData = abi.encode(x1, x2, ..., xn);
2687+
```
2688+
- `x1`, `x2`, ..., `xn`:需要编码的数据,可以是多种类型。
26802689

2681-
##### 测验结果
2690+
**示例**
2691+
```solidity
2692+
pragma solidity ^0.8.0;
26822693
2683-
##### 测验错题
2694+
contract EncodeExample {
2695+
function encodeData(uint256 a, string memory b) public pure returns (bytes memory) {
2696+
return abi.encode(a, b);
2697+
}
2698+
}
2699+
```
2700+
其中,`encodeData``uint256``string`类型的数据编码为字节数组。
26842701

2685-
### 2024.10.19
2686-
#### WTF Academy Solidity 102.27
2702+
###### `abi.encodePacked`
2703+
`abi.encodePacked`会将多个输入紧凑编码成最小字节大小,没有填充或分隔信息,因此比`abi.encode`生成的字节数少。
2704+
适合用于节省空间的编码方式,通常用于哈希计算(例如生成`keccak256`哈希)。由于紧凑编码可能导致哈希碰撞,`abi.encodePacked`不适合对具有可变长度的数据进行编码。
26872705

2688-
##### 笔记
2706+
**语法**
2707+
```solidity
2708+
bytes memory packedData = abi.encodePacked(x1, x2, ..., xn);
2709+
2710+
```
2711+
2712+
**示例**
2713+
```solidity
2714+
pragma solidity ^0.8.0;
2715+
2716+
contract PackedExample {
2717+
function hashData(uint256 a, string memory b) public pure returns (bytes32) {
2718+
return keccak256(abi.encodePacked(a, b));
2719+
}
2720+
}
2721+
2722+
```
2723+
在此示例中,`hashData`使用`abi.encodePacked`对数据进行紧凑编码,并计算出一个`keccak256`哈希值。
2724+
2725+
###### `abi.encodeWithSignature`
2726+
`abi.encodeWithSignature`接受一个函数签名字符串,并使用该签名和参数进行编码。签名包括函数名称和参数类型,生成的数据包含函数选择器。
2727+
适用于动态调用未知合约的函数或通过低级`call`方法直接调用其他合约。
2728+
2729+
**语法**
2730+
```solidity
2731+
bytes memory encodedWithSig = abi.encodeWithSignature("functionName(type1,type2,...)", x1, x2, ...);
2732+
2733+
```
2734+
- `functionName`:目标函数的名称。
2735+
- `type1`, `type2`, ...:参数的类型,必须按照目标函数参数顺序书写。
2736+
- `x1`, `x2`, ...:传入的实际参数。
2737+
2738+
2739+
**示例**
2740+
2741+
```solidity
2742+
pragma solidity ^0.8.0;
2743+
2744+
contract SignatureExample {
2745+
function callFunctionWithSignature(address target, uint256 a, string memory b) public returns (bytes memory) {
2746+
(bool success, bytes memory data) = target.call(abi.encodeWithSignature("targetFunction(uint256,string)", a, b));
2747+
require(success, "Call failed");
2748+
return data;
2749+
}
2750+
}
2751+
```
2752+
在此示例中,`callFunctionWithSignature`通过指定的函数签名编码数据,然后使用`call`来调用目标合约的函数。
2753+
2754+
2755+
###### `abi.encodeWithSelector`
2756+
2757+
`abi.encodeWithSelector`接受一个函数选择器和参数列表进行编码。选择器是由函数签名的前四个字节组成。
2758+
适用于在已知选择器情况下的合约调用,例如调用其他合约中的具体函数,适合低级`call`方法。
2759+
2760+
**语法**
2761+
```solidity
2762+
bytes memory encodedWithSelector = abi.encodeWithSelector(bytes4(keccak256("functionName(type1,type2,...)")), x1, x2, ...);
2763+
2764+
```
2765+
**示例**
2766+
```solidity
2767+
pragma solidity ^0.8.0;
2768+
2769+
contract SelectorExample {
2770+
function callFunctionWithSelector(address target, uint256 a, string memory b) public returns (bytes memory) {
2771+
(bool success, bytes memory data) = target.call(abi.encodeWithSelector(bytes4(keccak256("targetFunction(uint256,string)")), a, b));
2772+
require(success, "Call failed");
2773+
return data;
2774+
}
2775+
}
2776+
```
2777+
2778+
这里的`callFunctionWithSelector`通过函数选择器编码函数调用参数并调用目标合约的函数。
2779+
2780+
##### ABI解码
2781+
###### `abi.decode`
2782+
`abi.decode`用于解码编码数据,将`abi.encode``abi.encodePacked`生成的字节数据还原为原始数据类型。由于编码后不包含类型信息,解码时必须提供明确的类型。
2783+
用于接收或处理低级调用时的返回数据,将字节数据还原为具体类型。
2784+
2785+
**语法**
2786+
2787+
```solidity
2788+
(T1, T2, ..., Tn) = abi.decode(data, (T1, T2, ..., Tn));
2789+
2790+
```
2791+
2792+
- `data`:要解码的字节数据。
2793+
- `(T1, T2, ..., Tn)`:返回的类型。
2794+
2795+
2796+
**示例**
2797+
2798+
```solidity
2799+
pragma solidity ^0.8.0;
2800+
2801+
contract DecodeExample {
2802+
function decodeData(bytes memory data) public pure returns (uint256, string memory) {
2803+
return abi.decode(data, (uint256, string));
2804+
}
2805+
}
2806+
2807+
```
2808+
2809+
在这里,`decodeData`可以解码编码后的`uint256``string`数据并将其还原为实际的类型和值。
2810+
2811+
##### 总结表格
2812+
| 编码函数 | 核心功能 | 主要特点 | 使用场景 |
2813+
| --- | --- | --- | --- |
2814+
| `abi.encode` | 标准编码 | 保持原始数据的完整类型信息 | 复杂的参数传递,跨合约调用 |
2815+
| `abi.encodePacked` | 紧凑编码 | 更少的字节长度,去除填充 | 哈希计算(警惕碰撞风险) |
2816+
| `abi.encodeWithSignature` | 带函数签名的编码 | 包含函数选择器 | 直接调用其他合约的已知函数 |
2817+
| `abi.encodeWithSelector` | 带选择器的编码 | 直接使用选择器 | 在已知选择器的情况下调用函数 |
2818+
| `abi.decode` | 解码 | 将字节数据转换回原始类型 | 解码低级调用返回的数据 |
26892819

26902820
##### 测验结果
2821+
- 50/100
2822+
- 100/100
26912823

26922824
##### 测验错题
2825+
1. 下列有关ABI编码的函数中,返回值不可能当作调用智能合约的数据的是:
2826+
- `abi.encodePacked`
2827+
`abi.encodePacked`通常用于将数据紧凑编码,以节省字节数,比如用于哈希计算。由于`abi.encodePacked`去除了填充信息,因此在处理变长数据(例如字符串和字节数组)时可能会导致编码结果不唯一。这种不确定性容易引发哈希碰撞,尤其在涉及不同长度的参数时。
2828+
2. 函数foo在智能合约中定义声明为
2829+
```solidity
2830+
function foo(uint256 a) public view
2831+
```
2832+
而字符串"foo(uint256)"的keccak256哈希值为:
2833+
```solidity
2834+
0x2fbebd3821c4e005fbe0a9002cc1bd25dc266d788dba1dbcb39cc66a07e7b38b
2835+
```
2836+
那么正确且最省gas的一项是:
2837+
A. `abi.encodeWithSignature("foo(uint256)", a)`
2838+
B. `abi.encodeWithSelector("foo(uint256)", a)`
2839+
C. `abi.encodeWithSelector(bytes(keccak256("foo(uint256)")), a)`
2840+
D. `abi.encodeWithSelector(bytes4(0x2fbebd38), a)`
2841+
- D. `abi.encodeWithSelector(bytes4(0x2fbebd38), a)`
2842+
1. A. `abi.encodeWithSignature("foo(uint256)", a)` 正确但会计算签名的`keccak256`哈希值,会增加少量的gas消耗,不是最节省的方式。
2843+
2. B,C语法错误
2844+
3. 这是最简洁且节省gas的写法,直接使用已知的`keccak256`哈希前4字节(函数选择器),避免了不必要的计算。
26932845
2694-
### 2024.10.20
2695-
#### WTF Academy Solidity 102.28
2846+
#### WTF Academy Solidity 102.28 `Hash`
2847+
哈希函数(hash function)是一个密码学概念,它可以将任意长度的消息转换为一个固定长度的值,这个值也称作哈希(hash)。哈希函数有很多,其中的重要特性:
2848+
- 单向性:从输入的消息到它的哈希的正向运算简单且唯一确定,而反过来非常难,只能靠暴力枚举。
2849+
- 灵敏性:输入的消息改变一点对它的哈希改变很大。
2850+
- 高效性:从输入的消息到哈希的运算高效。
2851+
- 均一性:每个哈希值被取到的概率应该基本相等。
2852+
- 抗碰撞性:
2853+
- 弱抗碰撞性:给定一个消息`x`,找到另一个消息`x'`,使得`hash(x) = hash(x')`是困难的。
2854+
- 强抗碰撞性:找到任意`x`和`x'`,使得`hash(x) = hash(x')`是困难的。
26962855
2697-
##### 笔记
2856+
##### Keccak256
2857+
2858+
`Keccak256`函数是`Solidity`中最常用的哈希函数:
2859+
2860+
```solidity
2861+
哈希 = keccak256(数据);
2862+
```
2863+
**生成数据唯一标识**
2864+
2865+
可以利用`keccak256`来生成**一些数据**的唯一标识。
2866+
比如我们有几个不同类型的数据:`uint``string``address`,我们可以先用`abi.encodePacked`方法将他们打包编码,然后再用`keccak256`来生成唯一标识:
2867+
2868+
```solidity
2869+
function hash(
2870+
uint _num,
2871+
string memory _string,
2872+
address _addr
2873+
) public pure returns (bytes32) {
2874+
return keccak256(abi.encodePacked(_num, _string, _addr));
2875+
}
2876+
```
26982877

26992878
##### 测验结果
2879+
- 100/100
27002880

2701-
##### 测验错题
2881+
#### WTF Academy Solidity 102.29 函数选择器`Selector`
2882+
##### 函数选择器(Selector)
2883+
**函数选择器(Selector)**是一个4字节的哈希值,用于唯一标识合约中的某个函数。它通过对函数签名计算哈希后取前 4 个字节生成。它是 Solidity 中的一种标识方式,用于唯一标识智能合约中的某个函数。
2884+
2885+
**语法**
2886+
- 通过`keccak256`哈希算法对函数签名进行哈希计算,然后取前 4 个字节:
2887+
2888+
```solidity
2889+
bytes4 selector = bytes4(keccak256("mint(address)"));
2890+
2891+
```
2892+
##### `msg.data`
2893+
**`msg.data`** 是 Solidity 中的一个全局变量,它表示调用合约时传入的完整 `calldata`。这个数据包含了:
2894+
- **前 4 个字节**:函数选择器,用于告诉 EVM 要调用的目标函数。
2895+
- **后续字节**:编码后的参数数据。
2896+
2897+
假设合约中有一个函数 `mint(address to)`,调用 `mint(0x2c44b726ADF1963cA47Af88B284C06f30380fC78)` 时,生成的`msg.data`为:
2898+
2899+
```solidity
2900+
0x6a6278420000000000000000000000002c44b726adf1963ca47af88b284c06f30380fc78
2901+
```
2902+
- 前 4 字节 `0x6a627842` 是函数选择器;
2903+
- 后面 32 字节 `0x0000000000000000000000002c44b726adf1963ca47af88b284c06f30380fc78` 是参数地址。
2904+
2905+
##### 函数签名
2906+
**语法**
2907+
`函数名(参数类型1,参数类型2,...)`(例如 `mint(address)`)。
2908+
2909+
##### 不同类型参数的函数选择器
2910+
- **基础类型参数**:如`uint256``bool``address`等。
2911+
2912+
```solidity
2913+
function elementaryParamSelector(uint256 param1, bool param2) external returns(bytes4 selectorWithElementaryParam){
2914+
return bytes4(keccak256("elementaryParamSelector(uint256,bool)"));
2915+
}
2916+
```
2917+
2918+
- **固定长度类型参数**:如`uint256[3]`。
2919+
2920+
```solidity
2921+
function fixedSizeParamSelector(uint256[3] memory param1) external returns(bytes4 selectorWithFixedSizeParam){
2922+
return bytes4(keccak256("fixedSizeParamSelector(uint256[3])"));
2923+
}
2924+
```
2925+
2926+
- **可变长度类型参数**:如`address[]`、`string`。
2927+
2928+
```solidity
2929+
function nonFixedSizeParamSelector(uint256[] memory param1, string memory param2) external returns(bytes4 selectorWithNonFixedSizeParam){
2930+
return bytes4(keccak256("nonFixedSizeParamSelector(uint256[],string)"));
2931+
}
2932+
```
2933+
2934+
- **映射类型参数**:如合约、枚举、结构体等。
2935+
2936+
```solidity
2937+
function mappingParamSelector(address demo, (uint256, bytes) memory user, uint256[] memory count, uint8 mySchool) external returns(bytes4 selectorWithMappingParam){
2938+
return bytes4(keccak256("mappingParamSelector(address,(uint256,bytes),uint256[],uint8)"));
2939+
}
2940+
```
2941+
2942+
##### 使用函数选择器调用函数
2943+
可以使用 `abi.encodeWithSelector` 或 `abi.encodeWithSignature` 编码函数选择器和参数,然后通过 `call` 执行目标函数。
2944+
2945+
###### 使用`abi.encodeWithSelector`调用函数
2946+
2947+
```solidity
2948+
function callWithSelector() external {
2949+
bytes4 selector = bytes4(keccak256("elementaryParamSelector(uint256,bool)"));
2950+
(bool success, ) = address(this).call(abi.encodeWithSelector(selector, 1, true));
2951+
require(success, "Call failed");
2952+
}
2953+
```
2954+
2955+
###### 使用`abi.encodeWithSignature`调用函数
2956+
2957+
`abi.encodeWithSignature`可以直接编码函数签名,无需手动生成选择器:
2958+
2959+
```solidity
2960+
function callWithSignature() external {
2961+
(bool success, ) = address(this).call(abi.encodeWithSignature("elementaryParamSelector(uint256,bool)", 1, true));
2962+
require(success, "Call failed");
2963+
}
2964+
```
2965+
2966+
##### 测验结果
2967+
- 100/100
27022968

2703-
### 2024.10.21
2969+
### 2024.10.27
27042970
#### WTF Academy Solidity 102.29
27052971

27062972
##### 笔记

0 commit comments

Comments
 (0)