Solidity Assembly的基础用法
Published: 2023-07-15 02:23:06

## 什么是Assembly 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h97shde4euu7d9nrpbaj.png)
在编写Solidity代码时，我们可以使用`assembly{}`关键字开始编写Yul代码，它是一种简化且扩展了的汇编语言。通过使用assembly，我们可以直接访问堆栈，并优化代码以提高内存效率，从而减少执行交易所需的燃气量。这最终降低了用户的交易成本。

然而，在可读性方面存在一些妥协。许多前端开发人员可以阅读Solidity智能合约并理解正在执行的功能以及如何将其应用到他们的web3查询中。相比之下，汇编可能会让人感到有些困惑，如果你不熟悉低级编程，可能很难理解其逻辑和流程。

## 操作码列表
| 操作码            | 描述                                                         |
| ------------------ | ------------------------------------------------------------ |
| add(x, y)          | 将栈中的前两个值相加，并用结果替换它们                           |
| sub(x, y)          | 减法操作                                                     |
| mul(x, y)          | 乘法操作                                                     |
| div(x, y)          | 除法操作                                                     |
| mod(x, y)          | 取模操作                                                     |
| lt(x, y)           | 小于。如果 x 小于 y，则返回 1                                    |
| gt(x, y)           | 大于                                                         |
| eq(x, y)           | 等于。如果 x 等于 y，则返回 1                                    |
| not(x)             | 按位取反 (1010 > 0101)                                        |
| and(x, y)          | 按位与 (1000 AND 1100 > 1000)                                  |
| or(x, y)           | 按位或 (1000 AND 1100 > 1100)                                  |
| xor(x, y)          | 按位异或 (1000 XOR 1100 > 0100)                                |
| byte(n, x)         | X 的第 N 个字节                                               |
| keccak256(pos, n)  | 本地哈希算法                                                 |
| pop(x)             | 从栈中弹出 X                                                 |
| mload(pos)         | 加载存储在位置 pos 的内存数据                                 |
| mstore(pos, value) | 在位置 pos 的memory中存储值 value                                |
| sload(pos)         | 加载存储在位置 pos 的存储数据                                 |
| sstore(pos, value) | 在位置 pos 的storage中存储值 value                                |
| balance(address)   | 以 wei 为单位返回地址的以太币余额                              |
| call(gas, address, value, in, insize, out, outsize)               | 调用外部合约，也可用于发送资金，成功时返回 1                 |
| delegatecall(gas, address, value, in, insize, out, outsize)       | 与上述相同，但由用户而不是合约进行调用                       |
| revert(p, s)       | 撤销事务和任何状态更改                                         |
| return(p, s)       | 结束执行并返回数据                                             |

这里需要注意的是`mstore`和`sstore`的区别，主要存储的位置的不一致，一种是存储在memory中，另一种是存储在storage中。
## 使用案例

### 读写

下面我们通过一个例子展示下如何使用`Assembly`把数据存储到storage中，然后通过函数读取的方式读出来。
```solidity
    function testAssembly() public {
        //定义一个存储的值方便测试.
        uint256 x = 123;
        bool res;

        //将x存储到storage中。
        assembly {
            sstore(0, x)
        }

        //在storage中读取x的值,其中0是pos
        assembly {
            let readX := sload(0)
            //将readX的值存储到memory,pos == 0x80的位置。
            mstore(0x80, readX)
        }

        //将0x80中的值取出来.
        assembly {
            let v := mload(0x80)
            if eq(v, x) {
                res := true
            }
        }

        //判断最终取出来的v == x?
        assert(res);
    }
```
上诉的例子在实际项目中不太可能出现，只是为了展示读写的过程。

### 基础运算

```solidity
    function testAssembly() public {
        //定义一个存储的值方便测试.
        uint256 x = 123;
        uint256 r = 345;

        //将x存储到storage中。
        assembly {
            x := add(x, 300)
            x := sub(x, 78)
        }

        //判断最终取出来的r == x?
        assert(r == x);
    }
```



Follow我的推进行交流学习:https://twitter.com/nifty12111
TG:@coffiasd