# EVM学习——合约创建

By [rbtree](https://paragraph.com/@rbtree) · 2022-09-20

---

本文从EVM操作码的角度，研究合约创建的详细过程。

solidity version = 0.8.15，optimizer = true，optimizer\_runs = 200，evm\_version = "london"

在线反汇编 [https://ethervm.io/decompile](https://ethervm.io/decompile)

evm执行模拟使用foundry环境[https://book.getfoundry.sh/reference/forge/forge-debug](https://book.getfoundry.sh/reference/forge/forge-debug)

合约代码如下

    pragma solidity 0.8.15;
    
    contract Test {
        uint256 public val;
        uint256 immutable c;
    
        constructor(uint256 _val, uint256 _c) {
            val = _val;
            c = _c;
        }
    
        function setNumber(uint256 newNumber) public {
            val = newNumber + c;
        }
    }
    

我们在在foundry环境中，在script文件里，使用new进行部署。

new的部署方式和外部账户通过交易部署合约其实是很类似的，都是message call的过程。

    contract TestScript is Script {
        function setUp() public view {}
    
        function run() public {
           vm.startBroadcast(); // record call information so deploy to chain
    
           Test t = new Test(0x1234, 0x5678);
    
           vm.stopBroadcast();
        }
    }
    

在TestScript执行new Test的时候，

我们可以观察一下memory的情况，这里存储的是calldata内容。

0x080～0x1fe: 这一段是deploy code（60a0开始 一直到 0033为之）

接在后面的还有两个uint256的数字，0x1234和0x5678，这两个是我们即将部署的合约的构造函数实参。

![](https://storage.googleapis.com/papyrus_images/3bf976eed174729a2ff72c6c2f25bfec38bc2dcf93c2c74a1961a110263297dd.png)

继续往下走，我们就会进入真正的deploy阶段。

把deploy code进行反汇编。

    contract Contract {
        function main() {
            memory[0x40:0x60] = 0xa0;
            var var0 = msg.value;
        
            if (var0) { revert(memory[0x00:0x00]); }
        
            var temp0 = memory[0x40:0x60];
            var temp1 = code.length - 0x017e;
            memory[temp0:temp0 + temp1] = code[0x017e:0x017e + temp1];
            var var1 = temp0 + temp1;
            memory[0x40:0x60] = var1;
            var0 = 0x002f;
            var var2 = temp0;
            var0, var1 = func_003D(var1, var2);
            storage[0x00] = var0;
            memory[0x80:0xa0] = var1;
            var temp2 = memory[0x80:0xa0];
            memory[0x00:0x0103] = code[0x7b:0x017e];
            memory[0x66:0x86] = temp2;
            return memory[0x00:0x0103];
        }
        
        function func_003D(var arg0, var arg1) returns (var r0, var arg0) {
            var var0 = 0x00;
            var var1 = var0;
        
            if (arg0 - arg1 i< 0x40) { revert(memory[0x00:0x00]); }
        
            var temp0 = arg1;
            r0 = memory[temp0:temp0 + 0x20];
            arg0 = memory[temp0 + 0x20:temp0 + 0x20 + 0x20];
            return r0, arg0;
        }
    }
    

代码的第1个关键点在storage\[0x00\] = var0;

这里实际上对应构造函数中的val = \_val;

代码的第2个关键点是memory\[0x00:0x0103\] = code\[0x7b:0x017e\];

这里是把deploy code中的一部分代码拷贝到memory中。

deploy code其实有两部分，第一部分是用于deploy的代码部分，这些对应上面的反汇编代码。

60a060405234801561001057600080fd5b5060405161017e38038061017e8339 8101604081905261002f9161003d565b600091909155608052610061565b6000 806040838503121561005057600080fd5b505080516020909101519092909150 565b60805161010361007b6000396000606601526101036000f3fe

另一部分是将会被部署上链的代码，被叫做deployed code。这些代码就是在合约上链之后，后面与之交互时所使用的部分。

6080604052348015600f57600080fd5b506004361060325760003560e01c8063 3c6bb4361460375780633fb5c1cb146051575b600080fd5b603f60005481565b 60405190815260200160405180910390f35b6060605c3660046090565b606256 5b005b608a7f0000000000000000000000000000000000000000000000000000 0000000000008260a8565b60005550565b60006020828403121560a157600080 fd5b5035919050565b6000821982111560c857634e487b7160e01b6000526011 60045260246000fd5b50019056fea26469706673582212200b220ab5e3b5c534 b47030c3b89735e224c0d52e5454511a0274bc5bbbfd2abd64736f6c63430008 0f0033

在memory\[0x00:0x0103\] = code\[0x7b:0x017e\];执行之后，我们可以看到deployed code已经被拷贝到memory中。在操作码中，对应CODECOPY。

![](https://storage.googleapis.com/papyrus_images/83e57b7a5cf471f34e3c1143420e926844417644c6e0566282654ce1a6034ec7.png)

第3个关键点是memory\[0x66:0x86\] = temp2;

这一句对应构造函数中的c = \_c;

因为c是immutable，所以c的值并不会占据storage空间，而是会被在字节码中写死。所以还需要在字节码中，把c的值赋上。

![](https://storage.googleapis.com/papyrus_images/aa707a175858fc0c9f2095a6084653ceb6c5591b92cd649d9a5aa0a7766ae3ae.png)

最后的形态如下：

memory中时最终的deployed code，而栈中的两个数字则指定了memory中deployed code的位置。

0x103 = 259，这就是deployed code的大小。

![](https://storage.googleapis.com/papyrus_images/b12a5eb37cae5f223fa586131b6831f6208107289e58e0b3d5683af84608c005.png)

deploy函数的返回值就是最终的deployed code，紧接着deployed code就会被写入区块链，从而完成合约部署过程。

---

*Originally published on [rbtree](https://paragraph.com/@rbtree/evm)*
