以太坊合约地址计算

1. 引言

以太坊创建合约的方式有2种:

  • 1)由EOA账号直接创建合约
  • 2)由其它智能合约创建智能合约
    • 2.1)通过CREATE(0xf0) opcode
    • 2.2)通过CREATE2(0xf5) opcode
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.7;
    
    contract OpCreates {
        function opCreate(bytes memory bytecode, uint length) public returns(address) {
            address addr;
            assembly {
                addr := create(0, 0xa0, length)
                sstore(0x0, addr)
            }
            return addr;
        }
    
        function opCreate2(bytes memory bytecode, uint length) public returns(address) {
            address addr;
            assembly {
                addr := create2(0, 0xa0, length, 0x2)
                sstore(0x0, addr)
            }
            return addr;
        }
    
        function sendValue() public payable {
            uint bal;
            assembly{
                bal := add(bal,callvalue())
                sstore(0x1, bal)
            }
        }
    
        function opCreateValue(bytes memory bytecode, uint length) public payable returns(address) {
            address addr;
            assembly {
                addr := create(500, 0xa0, length)
                sstore(0x0, addr)
            }
            return addr;
        }
    
        function opCreate2Value(bytes memory bytecode, uint length) public payable returns(address) {
            address addr;
            assembly {
                addr := create2(300, 0xa0, length, 0x55555)
                sstore(0x0, addr)
            }
            return addr;
        }
    }
    
    

2. 由EOA账号直接创建的合约——合约地址计算

由EOA账号直接创建的合约——合约地址计算规则,根据pyethereum有:

try:
    from Crypto.Hash import keccak
    sha3_256 = lambda x: keccak.new(digest_bits=256, data=x).digest()
except:
    import sha3 as _sha3
    sha3_256 = lambda x: _sha3.sha3_256(x).digest()

def mk_contract_address(sender, nonce):
    return sha3(rlp.encode([normalize_address(sender), nonce]))[12:]

即采用sender-and-nonce-hash方式来计算合约地址。

以太坊合约的地址是根据creator地址(sender) 和 该creator已发送的交易数(nonce)经RLP编码再采用Keccak-256哈希运算 确定性计算而来的:【下述示例应进一步拓展为支持nonce最大值 2 64 2^{64} 264

// Solidity 0.8及以上
function addressFrom(address _origin, uint _nonce) public pure returns (address) {
    bytes memory data;
    if (_nonce == 0x00)          data = abi.encodePacked(byte(0xd6), byte(0x94), _origin, byte(0x80));
    else if (_nonce <= 0x7f)     data = abi.encodePacked(byte(0xd6), byte(0x94), _origin, byte(_nonce));
    else if (_nonce <= 0xff)     data = abi.encodePacked(byte(0xd7), byte(0x94), _origin, byte(0x81), uint8(_nonce));
    else if (_nonce <= 0xffff)   data = abi.encodePacked(byte(0xd8), byte(0x94), _origin, byte(0x82), uint16(_nonce));
    else if (_nonce <= 0xffffff) data = abi.encodePacked(byte(0xd9), byte(0x94), _origin, byte(0x83), uint24(_nonce));
    else                         data = abi.encodePacked(byte(0xda), byte(0x94), _origin, byte(0x84), uint32(_nonce));
    return address(keccak256(data));
}
// Solidity 0.6
function addressFrom(address _origin, uint _nonce) public pure returns (address) {
    bytes memory data;
    if (_nonce == 0x00)          data = abi.encodePacked(byte(0xd6), byte(0x94), _origin, byte(0x80));
    else if (_nonce <= 0x7f)     data = abi.encodePacked(byte(0xd6), byte(0x94), _origin, uint8(_nonce));
    else if (_nonce <= 0xff)     data = abi.encodePacked(byte(0xd7), byte(0x94), _origin, byte(0x81), uint8(_nonce));
    else if (_nonce <= 0xffff)   data = abi.encodePacked(byte(0xd8), byte(0x94), _origin, byte(0x82), uint16(_nonce));
    else if (_nonce <= 0xffffff) data = abi.encodePacked(byte(0xd9), byte(0x94), _origin, byte(0x83), uint24(_nonce));
    else                         data = abi.encodePacked(byte(0xda), byte(0x94), _origin, byte(0x84), uint32(_nonce));
    return address(uint256(keccak256(data)));
}

可借助汇编代码进一步优化以节约gas费:

// Solidity 0.8及以上
function addressFrom(address _origin, uint _nonce) external pure returns (address _address) {
    bytes memory data;
    if(_nonce == 0x00)          data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), _origin, bytes1(0x80));
    else if(_nonce <= 0x7f)     data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), _origin, uint8(_nonce));
    else if(_nonce <= 0xff)     data = abi.encodePacked(bytes1(0xd7), bytes1(0x94), _origin, bytes1(0x81), uint8(_nonce));
    else if(_nonce <= 0xffff)   data = abi.encodePacked(bytes1(0xd8), bytes1(0x94), _origin, bytes1(0x82), uint16(_nonce));
    else if(_nonce <= 0xffffff) data = abi.encodePacked(bytes1(0xd9), bytes1(0x94), _origin, bytes1(0x83), uint24(_nonce));
    else                        data = abi.encodePacked(bytes1(0xda), bytes1(0x94), _origin, bytes1(0x84), uint32(_nonce));
    bytes32 hash = keccak256(data);
    assembly {
        mstore(0, hash)
        _address := mload(0)
    }
}
	// Solidity 0.6
    function addressFrom(address _origin, uint _nonce) public pure returns (address _address) {
        bytes memory data;
        if(_nonce == 0x00)          data = abi.encodePacked(byte(0xd6), byte(0x94), _origin, byte(0x80));
        else if(_nonce <= 0x7f)     data = abi.encodePacked(byte(0xd6), byte(0x94), _origin, uint8(_nonce));
        else if(_nonce <= 0xff)     data = abi.encodePacked(byte(0xd7), byte(0x94), _origin, byte(0x81), uint8(_nonce));
        else if(_nonce <= 0xffff)   data = abi.encodePacked(byte(0xd8), byte(0x94), _origin, byte(0x82), uint16(_nonce));
        else if(_nonce <= 0xffffff) data = abi.encodePacked(byte(0xd9), byte(0x94), _origin, byte(0x83), uint24(_nonce));
        else                        data = abi.encodePacked(byte(0xda), byte(0x94), _origin, byte(0x84), uint32(_nonce));
        bytes32 hash = keccak256(data);
        assembly {
            mstore(0, hash)
            _address := mload(0)
        }
    }

3. 由其它智能合约创建合约——合约地址计算

EIP-1014:Skinny CREATE2中,额外引入了新的opcode CREATE2(0xf5)来创建智能合约。CREATE2(0xf5)CREATE(0xf0)的工作模式相同,只是CREATE(0xf0)计算合约地址采用sender-and-nonce-hash方式,而CREATE2(0xf5)采用合约地址计算规则为:

keccak256( 0xff ++ senderAddress ++ salt ++ keccak256(init_code))[12:]

参考资料

[1] How to calculate an Ethereum Contract’s address during its creation using the Solidity language?
[2] How is the address of an Ethereum contract computed?

猜你喜欢

转载自blog.csdn.net/mutourend/article/details/128550718