Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add deployer preset #467

Merged
merged 23 commits into from
Oct 21, 2022
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions src/openzeppelin/utils/presets/UniversalDeployer.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts for Cairo v0.4.0b (utils/presets/UniversalDeployer.cairo)
martriay marked this conversation as resolved.
Show resolved Hide resolved

%lang starknet
martriay marked this conversation as resolved.
Show resolved Hide resolved

from starkware.starknet.common.syscalls import get_caller_address, deploy
from starkware.cairo.common.cairo_builtins import HashBuiltin
from starkware.cairo.common.hash import hash2
from starkware.cairo.common.bool import FALSE, TRUE

@event
func ContractDeployed(
address: felt,
deployer: felt,
classHash: felt,
salt: felt
martriay marked this conversation as resolved.
Show resolved Hide resolved
) {
}

@external
func deployContract{
syscall_ptr: felt*,
pedersen_ptr: HashBuiltin*,
range_check_ptr
}(
classHash: felt,
salt: felt,
unique: felt,
calldata_len: felt,
calldata: felt*
) -> (address: felt) {
let (deployer) = get_caller_address();

tempvar prefix;
if (unique == TRUE) {
prefix = deployer;
} else {
prefix = 'UniversalDeployerContract';
martriay marked this conversation as resolved.
Show resolved Hide resolved
}

let (_salt) = hash2{hash_ptr=pedersen_ptr}(prefix, salt);

let (address) = deploy(
class_hash=classHash,
contract_address_salt=_salt,
constructor_calldata_size=calldata_len,
constructor_calldata=calldata,
deploy_from_zero=FALSE,
martriay marked this conversation as resolved.
Show resolved Hide resolved
);

ContractDeployed.emit(
address=address,
deployer=deployer,
classHash=classHash,
salt=_salt
);

return (address=address);
}
12 changes: 6 additions & 6 deletions tests/account/test_Account.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import pytest
from signers import MockSigner, get_raw_invoke
from utils import assert_revert, get_contract_class, cached_contract, TRUE, State, Account
from utils import assert_revert, get_contract_class, cached_contract, TRUE, IACCOUNT_ID, State, Account


signer = MockSigner(123456789987654321)
other = MockSigner(987654321123456789)

IACCOUNT_ID = 0xa66bd575


@pytest.fixture(scope='module')
def contract_classes():
Expand Down Expand Up @@ -112,7 +110,7 @@ async def test_return_value(account_factory):
read_info = await signer.send_transactions(account, [(initializable.contract_address, 'initialized', [])])
call_info = await initializable.initialized().call()
(call_result, ) = call_info.result
assert read_info.call_info.retdata[1] == call_result #1
assert read_info.call_info.retdata[1] == call_result # 1


@ pytest.mark.asyncio
Expand All @@ -138,7 +136,8 @@ async def test_nonce(account_factory):

# higher nonce
await assert_revert(
signer.send_transactions(account, [(initializable.contract_address, 'initialize', [])], nonce=current_nonce + 1),
signer.send_transactions(account, [(
initializable.contract_address, 'initialize', [])], nonce=current_nonce + 1),
reverted_with="Invalid transaction nonce. Expected: {}, got: {}.".format(
current_nonce, current_nonce + 1
)
Expand Down Expand Up @@ -184,7 +183,8 @@ async def test_account_takeover_with_reentrant_call(account_factory):
account, _, _, _, attacker = account_factory

await assert_revert(
signer.send_transaction(account, attacker.contract_address, 'account_takeover', []),
signer.send_transaction(
account, attacker.contract_address, 'account_takeover', []),
reverted_with="Account: no reentrant call"
)

Expand Down
4 changes: 1 addition & 3 deletions tests/account/test_EthAccount.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import pytest
from utils import assert_revert, get_contract_class, cached_contract, TRUE, FALSE, State
from utils import assert_revert, get_contract_class, cached_contract, TRUE, FALSE, IACCOUNT_ID, State
from signers import MockEthSigner, get_raw_invoke

private_key = b'\x01' * 32
signer = MockEthSigner(b'\x01' * 32)
other = MockEthSigner(b'\x02' * 32)

IACCOUNT_ID = 0xa66bd575


@pytest.fixture(scope='module')
def contract_defs():
Expand Down
4 changes: 1 addition & 3 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
ZERO_ADDRESS = 0
TRUE = 1
FALSE = 0

TRANSACTION_VERSION = 0

IACCOUNT_ID = 0xa66bd575
martriay marked this conversation as resolved.
Show resolved Hide resolved

_root = Path(__file__).parent.parent

Expand Down
99 changes: 99 additions & 0 deletions tests/utils/test_UniversalDeployer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import pytest
from starkware.starknet.core.os.contract_address.contract_address import calculate_contract_address_from_hash
from starkware.crypto.signature.fast_pedersen_hash import pedersen_hash
from starkware.starknet.core.os.class_hash import compute_class_hash

from signers import MockSigner
from utils import (
State,
Account,
get_contract_class,
assert_event_emitted,
cached_contract,
str_to_felt,
martriay marked this conversation as resolved.
Show resolved Hide resolved
IACCOUNT_ID,
FALSE,
TRUE,
)

signer = MockSigner(123456789987654321)


@pytest.fixture(scope='module')
def contract_classes():
account_cls = Account.get_class
deployer_cls = get_contract_class('UniversalDeployer')

return account_cls, deployer_cls


@pytest.fixture(scope='module')
async def deployer_init(contract_classes):
_, deployer_cls = contract_classes
starknet = await State.init()
account = await Account.deploy(signer.public_key)
deployer = await starknet.deploy(contract_class=deployer_cls)
return (
starknet.state,
account,
deployer
)


@pytest.fixture
def deployer_factory(contract_classes, deployer_init):
account_cls, deployer_cls = contract_classes
state, account, deployer = deployer_init
_state = state.copy()
_account = cached_contract(_state, account_cls, account)
deployer = cached_contract(_state, deployer_cls, deployer)

return _account, deployer


@pytest.mark.asyncio
@pytest.mark.parametrize('unique', [TRUE, FALSE])
async def test_deployment(deployer_factory, unique):
account, deployer = deployer_factory
salt = 1234567875432 # random value
calldata = [signer.public_key]
class_hash = compute_class_hash(
contract_class=Account.get_class, hash_func=pedersen_hash)

# deploy contract
params = [class_hash, salt, unique, len(calldata), *calldata]
deploy_exec_info = await signer.send_transaction(account, deployer.contract_address, 'deployContract', params)
deployed_address = deploy_exec_info.call_info.retdata[1]

# check address
if unique:
prefix = account.contract_address
else:
prefix = str_to_felt('UniversalDeployerContract')

actual_salt = pedersen_hash(prefix, salt)
expected_address = calculate_contract_address_from_hash(
salt=actual_salt,
class_hash=class_hash,
constructor_calldata=calldata,
deployer_address=deployer.contract_address
)

assert deployed_address == expected_address

# check deployment
tx_exec_info = await signer.send_transaction(account, deployed_address, 'supportsInterface', [IACCOUNT_ID])
is_account = tx_exec_info.call_info.retdata[1]
assert is_account == TRUE

assert_event_emitted(
deploy_exec_info,
from_address=deployer.contract_address,
name='ContractDeployed',
data=[
deployed_address, # contractAddress
account.contract_address, # deployer
class_hash, # classHash
actual_salt, # salt
]
)