Skip to content

Commit

Permalink
Add get_create2_address()
Browse files Browse the repository at this point in the history
  • Loading branch information
fjarri committed Nov 10, 2024
1 parent 6675506 commit 43f4dcc
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 0 deletions.
6 changes: 6 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,12 @@ Utility classes
:members:


Utility methods
---------------

.. autofunction:: pons.get_create2_address


Compiled and deployed contracts
-------------------------------

Expand Down
2 changes: 2 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Added
- ``ClientSession.eth_get_logs()`` and ``eth_get_filter_logs()``. (PR_68_)
- Support for a custom block number in gas estimation methods. (PR_70_)
- ``LocalProvider`` accepts an ``evm_version`` parameter. (PR_78_)
- ``get_create2_address()``. (PR_80_)


Fixed
Expand Down Expand Up @@ -73,6 +74,7 @@ Fixed
.. _PR_76: https://github.com/fjarri-eth/pons/pull/76
.. _PR_77: https://github.com/fjarri-eth/pons/pull/77
.. _PR_78: https://github.com/fjarri-eth/pons/pull/78
.. _PR_80: https://github.com/fjarri-eth/pons/pull/80


0.7.0 (09-07-2023)
Expand Down
2 changes: 2 additions & 0 deletions pons/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
from ._local_provider import LocalProvider, SnapshotID
from ._provider import HTTPProvider, ProtocolError, Provider, Unreachable
from ._signer import AccountSigner, Signer
from ._utils import get_create2_address

__all__ = [
"ABIDecodingError",
Expand Down Expand Up @@ -98,4 +99,5 @@
"Unreachable",
"abi",
"compile_contract_file",
"get_create2_address",
]
14 changes: 14 additions & 0 deletions pons/_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from ethereum_rpc import Address, keccak


def get_create2_address(deployer: Address, init_code: bytes, salt: int) -> Address:
"""
Returns the deterministic deployed contract address as produced by ``CREATE2`` opcode.
Here `deployer` is the contract address invoking ``CREATE2``
(**not** the transaction initiator),
``init_code`` is the deployment code (see :py:attr:`~pons.BoundConstructorCall.data_bytes`),
and ``salt`` is an integer up to 256 bits in length.
"""
salt_bytes = salt.to_bytes(32, byteorder="big")
contract_address = keccak(b"\xff" + bytes(deployer) + salt_bytes + keccak(init_code))[-20:]
return Address(contract_address)
45 changes: 45 additions & 0 deletions tests/TestUtils.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
pragma solidity >=0.8.0 <0.9.0;


contract ToDeploy {
uint256 public state;

constructor(uint256 _state) {
state = _state;
}

function getState() public view returns (uint256) {
return state;
}
}


contract Create2Deployer {
event Deployed(
address deployedAddress
);

function deploy(bytes memory bytecode, uint256 _salt) public payable {
address addr;
bool success = true;

assembly {
addr := create2(
callvalue(),
add(bytecode, 0x20), // Skip the first 32 bytes, which is the size of `bytecode`
mload(bytecode), // Load the size of code contained in the first 32 bytes
_salt
)

if iszero(extcodesize(addr)) {
success := false
}
}

if (!success) {
revert("Failed to deploy the contract");
}

emit Deployed(addr);
}
}
31 changes: 31 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import secrets
from pathlib import Path

import pytest

from pons import EVMVersion, compile_contract_file, get_create2_address


@pytest.fixture
def compiled_contracts():
path = Path(__file__).resolve().parent / "TestUtils.sol"
return compile_contract_file(path, evm_version=EVMVersion.CANCUN)


async def test_create2(session, root_signer, compiled_contracts):
compiled_deployer = compiled_contracts["Create2Deployer"]
compiled_to_deploy = compiled_contracts["ToDeploy"]

deployer = await session.deploy(root_signer, compiled_deployer.constructor())

salt = secrets.randbelow(1 << 256)
to_deploy = compiled_to_deploy.constructor(123)
events = await session.transact(
root_signer,
deployer.method.deploy(to_deploy.data_bytes, salt),
return_events=[deployer.event.Deployed],
)
assert len(events[deployer.event.Deployed]) == 1
assert events[deployer.event.Deployed][0] == dict(
deployedAddress=get_create2_address(deployer.address, to_deploy.data_bytes, salt)
)

0 comments on commit 43f4dcc

Please sign in to comment.