Pré-computar endereço do Contrato com Create2

porMatheusem06/06/2022

Nesse artigo iremos aprender a como pré-computar um endereço de contrato antes de ser implantado na blockchain.

Pré-computar um endereço

O endereço do contrato pode ser pré-computado, antes que o contrato seja implantado, usando create2

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Factory {
// Retorna o endereço do contrato recém-implantado
function deploy(
address _owner,
uint _foo,
bytes32 _salt
) public payable returns (address) {
// Esta sintaxe é uma nova maneira de invocar create2 sem assembly,
// você só precisa passar salt como parâmetro
// https://docs.soliditylang.org/en/latest/control-structures.html#salted-contract-creations-create2
return address(new TestContract{salt: _salt}(_owner, _foo));
}
}
// Esta é a maneira mais antiga de fazer isso usando assembly
contract FactoryAssembly {
event Deployed(address addr, uint salt);
// 1. Obtenha o bytecode do contrato a ser implantado
// NOTA: _owner e _foo são argumentos do construtor do TestContract
function getBytecode(address _owner, uint _foo) public pure returns (bytes memory) {
bytes memory bytecode = type(TestContract).creationCode;
return abi.encodePacked(bytecode, abi.encode(_owner, _foo));
}
// 2. Calcule o endereço do contrato a ser implantado
// NOTA: _salt é um número aleatório usado para criar um endereço
function getAddress(bytes memory bytecode, uint _salt)
public
view
returns (address)
{
bytes32 hash = keccak256(
abi.encodePacked(bytes1(0xff), address(this), _salt, keccak256(bytecode))
);
// NOTA: converter os últimos 20 bytes de hash para endereçar
return address(uint160(uint(hash)));
}
// 3. Implantar o contrato
// NOTA:
// Verifique o log de eventos Deployed que contém o endereço do TestContract implantado.
// O endereço no log deve ser igual ao endereço computado acima.
function deploy(bytes memory bytecode, uint _salt) public payable {
address addr;
/*
NOTA: Como chamar create2
create2(v, p, n, s)
criar novo contrato com código na memória p para p + n
e envia v wei
e retorna o novo endereço
where new address = first 20 bytes of keccak256(0xff + address(this) + s + keccak256(mem[p…(p+n)))
s = big-endian 256-bit value
*/
assembly {
addr := create2(
callvalue(), // wei enviado com a chamada atual
// O código real começa após pular os primeiros 32 bytes
add(bytecode, 0x20),
mload(bytecode), // Carregar o tamanho do código contido nos primeiros 32 bytes
_salt // Salt dos argumentos da função
)
if iszero(extcodesize(addr)) {
revert(0, 0)
}
}
emit Deployed(addr, _salt);
}
}
contract TestContract {
address public owner;
uint public foo;
constructor(address _owner, uint _foo) payable {
owner = _owner;
foo = _foo;
}
function getBalance() public view returns (uint) {
return address(this).balance;
}
}

Testar no Remix