Solidity ABI encoding test (abi.encode 可変長変数、固定長変数の比較)

Solidty ABIのエンコード関数の挙動を見ていきます。
Foundry のテストを使用します。

```
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test, console2} from "forge-std/Test.sol";
import {Counter} from "../src/Counter.sol";



contract Test_abiEncode is Test {

```

#### `abi.encodePacked` の動作

まずは一番簡単な`abi.encodePacked` から。こちらは単純にバイト列を連結していきます。
```
       bytes memory str1 = "ABC";
       bytes memory str2 = "DEF";
       bytes memory str3 = "GHI";
       bytes memory str4 = "ABCDEFGHI"
       
       // encodePacked Test
       assertEq(abi.encodePacked(str1, str2, str3),str4);
```
##### padding　
次に`abi.encode`に入る前にパディングの挙動を見ていきます。なぜパディングの挙動をみるかというと`abi.encode`は32バイトにパディングするからです。
 - `bytes, bytes4`は右パディング
-  `uint32, address`は左パディング
-  (注）EVMはBigEndianです。たとえばuint32の0x22は　0x00000022となります。　(x86ではlittile endian なので uint32は 0x220000000 となる。)

```
      // padding test
      //
       bytes memory pad28= hex"00000000000000000000000000000000000000000000000000000000";
       bytes memory pad29= hex"0000000000000000000000000000000000000000000000000000000000";
       bytes memory pad12= hex"000000000000000000000000";
       assertEq(bytes32(str1), bytes32(abi.encodePacked(str1, pad29)));  // right padding
       bytes4 str5 = "abcd";
       bytes4 str6 = "efgh";
       assertEq(bytes32(abi.encodePacked(str5,pad28)), bytes32(str5)); // right padding
       
       uint32 num1 = 0xC0FFEE;
       assertEq(bytes32(uint256(num1)), bytes32(abi.encodePacked(pad28, num1))); // left padding
       address addr1 = address(0xC0FFEE);
       assertEq(bytes32(abi.encodePacked(pad12, addr1)), bytes32(abi.encode(addr1))); // left padding
```

#### `abi.encode`の引数がすべて固定サイズの場合。
- 各変数を32バイトでパディングしてから連結
```
       // static encoding　
       // byte4 
       assertEq(abi.encode(str5, str6), abi.encodePacked(bytes32(str5), bytes32(str6)));
       // byte4 + uint32
       assertEq(abi.encode(num1, str6), abi.encodePacked(bytes32(uint256(num1)), bytes32(str6)));

```
#### `abi.encode`の引数に可変長のデータがある場合
- 可変長データの開始位置もしくは固定長データの値を32byteにパディングする。
- 開始位置からそのデータのサイズ32bytes, 次にその可変長データが入る。可変長データは32bytesの倍数になるようにパディングされる。

```

       // dynamic encoding (including dynamic size varriable)
       assertEq(abi.encode(str1, num1, str2),
                abi.encodePacked(
                    bytes32(uint256(0x60)),   // offset of the fist varriable
                    bytes32(uint256(num1)),   // value of static varriable
                    bytes32(uint256(0xa0)),   // offset of the 3rd varriable
                    bytes32(str1.length), //  start from 0x60
                    bytes32((str1)),
                    bytes32(str2.length), // start from 0xa0
                    bytes32((str2))
                    ));
```
実際のデータ
```
0000000000000000000000000000000000000000000000000000000000000060　 ... str1の開始位置
0000000000000000000000000000000000000000000000000000000000c0ffee　 .... num1の値
00000000000000000000000000000000000000000000000000000000000000a0　　....str2の開始位置
0000000000000000000000000000000000000000000000000000000000000003   .... str1のサイズ (開始位置として指定されている所はここ）
4142430000000000000000000000000000000000000000000000000000000000　 .... str1の値
0000000000000000000000000000000000000000000000000000000000000003　　.... str2のサイズ (開始位置として指定されている所はここ）
4445460000000000000000000000000000000000000000000000000000000000　 ... str2の値
```


####  `abi.encodeWithSelector` の動作
- 第2引数以降で可変長のデータを引数にとると 第２引数以降はabi.encodeでまとめたものと同じ。
- [4 bytes selector] [abi.encode param data] の形になる。通常これがcalldataとしてcall先にわたる。

```
       // encode with selector
        bytes4 mySelector = 0x11223344;
        assertEq(abi.encodeWithSelector(mySelector,str1,str2),
                 abi.encodePacked(mySelector, abi.encode(str1,str2)));
```

##### abi.encodeWithSignature の動作
- selector 部分を関数の文字列から求めなくても、そのまま文字列を引数にとる。

```
        bytes memory funcStr = "balanceOf(address)";
        bytes4 funcId = bytes4(keccak256(funcStr));  // called selector
        assertEq(abi.encodeWithSelector(funcId,addr1),
                 abi.encodePacked(funcId, abi.encode(addr1)));
        assertEq(abi.encodeWithSelector(funcId, addr1),
                 abi.encodeWithSignature(string(funcStr),addr1));
```


- 追記
このテストコードで以下のエラーがでたので`forge test -vvvv  --via-ir` で実行しています。 
```
Error:
Compiler run failed:
Error: Compiler error (/solidity/libsolidity/codegen/LValue.cpp:51):Stack too deep. Try compiling with `--via-ir` (cli) or the equivalent `viaIR: true` (standard JSON) while enabling the optimizer. Otherwise, try removing local variables.
  --> test/AbiEncode.t.sol:67:30:
   |
67 |                     bytes32((str1)),
   |                              ^^^^

```