Skip to content

Commit

Permalink
Use transparent proxy for accounts (kkrt-labs#1285)
Browse files Browse the repository at this point in the history
## Pull request type

Please check the type of change your PR introduces:

- [ ] Bugfix
- [x] Feature
- [ ] Code style update (formatting, renaming)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] Documentation content changes
- [ ] Other (please describe):

## What is the current behavior?

Accounts have their own implementation and needs to be upgraded when
Kakarot is.

Resolves kkrt-labs#1280
Resolves kkrt-labs#1275

## What is the new behavior?

Accounts class is never updated and is a transparent proxy. Each call to
the account starts by fetching
the current implementation in Kakarot, then make a `library_call`.

<!-- Reviewable:start -->
- - -
This change is [<img src="https://reviewable.io/review_button.svg"
height="34" align="absmiddle"
alt="Reviewable"/>](https://reviewable.io/reviews/kkrt-labs/kakarot/1285)
<!-- Reviewable:end -->

---------

Co-authored-by: enitrat <[email protected]>
  • Loading branch information
ClementWalter and enitrat authored Jul 23, 2024
1 parent 4e7dfe0 commit c366211
Show file tree
Hide file tree
Showing 65 changed files with 960 additions and 1,443 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,5 @@ PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python
# To override the default web3 provider uri http://localhost:8545, putting as default
# the default Kakarot RPC URL of kakarot-rpc repo
WEB3_HTTP_PROVIDER_URI="http://0.0.0.0:3030"

HYPOTHESIS_PROFILE=dev
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ jobs:
with:
version: nightly
- name: Run tests
env:
HYPOTHESIS_PROFILE: ci
run: make test-unit
- name: Upload coverage report to codecov
uses: codecov/codecov-action@v3
Expand Down
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ setup: fetch-ssj-artifacts
poetry install

test: build-sol build-cairo1 deploy
poetry run pytest tests/src -m "not NoCI" --log-cli-level=INFO -n logical
poetry run pytest tests/end_to_end
poetry run pytest tests/src -m "not NoCI" --log-cli-level=INFO -n logical --seed 42
poetry run pytest tests/end_to_end --seed 42

test-unit: build-sol
poetry run pytest tests/src -m "not NoCI" -n logical
poetry run pytest tests/src -m "not NoCI" -n logical --seed 42

# run make run-nodes in other terminal
test-end-to-end: build-sol build-cairo1 deploy
poetry run pytest tests/end_to_end
poetry run pytest tests/end_to_end --seed 42

deploy: build build-sol
poetry run python ./kakarot_scripts/deploy_kakarot.py
Expand Down
7 changes: 5 additions & 2 deletions kakarot_scripts/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ class ArtifactType(Enum):
COMPILED_CONTRACTS = [
{"contract_name": "kakarot", "is_account_contract": False},
{"contract_name": "account_contract", "is_account_contract": True},
{"contract_name": "account_contract_fixture", "is_account_contract": True},
{"contract_name": "uninitialized_account_fixture", "is_account_contract": False},
{"contract_name": "uninitialized_account", "is_account_contract": False},
{"contract_name": "EVM", "is_account_contract": False},
{"contract_name": "OpenzeppelinAccount", "is_account_contract": True},
Expand All @@ -217,7 +217,10 @@ class ArtifactType(Enum):
DECLARED_CONTRACTS = [
{"contract_name": "kakarot", "cairo_version": ArtifactType.cairo0},
{"contract_name": "account_contract", "cairo_version": ArtifactType.cairo0},
{"contract_name": "account_contract_fixture", "cairo_version": ArtifactType.cairo0},
{
"contract_name": "uninitialized_account_fixture",
"cairo_version": ArtifactType.cairo0,
},
{"contract_name": "uninitialized_account", "cairo_version": ArtifactType.cairo0},
{"contract_name": "EVM", "cairo_version": ArtifactType.cairo0},
{"contract_name": "OpenzeppelinAccount", "cairo_version": ArtifactType.cairo0},
Expand Down
2 changes: 1 addition & 1 deletion kakarot_scripts/deploy_kakarot.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
async def main():
# %% Declarations
account = await get_starknet_account()
logger.info(f"ℹ️ Using account {hex(account.address)} as deployer")
logger.info(f"ℹ️ Using account 0x{account.address:064x} as deployer")

class_hash = {
contract["contract_name"]: await declare(contract)
Expand Down
73 changes: 49 additions & 24 deletions kakarot_scripts/utils/kakarot.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class EvmTransactionError(Exception):
def get_solidity_artifacts(
contract_app: str,
contract_name: str,
) -> Web3Contract:
):
import toml

try:
Expand Down Expand Up @@ -162,13 +162,13 @@ async def deploy(
if WEB3.is_connected():
evm_address = int(receipt.contractAddress or receipt.to, 16)
starknet_address = (
await _call_starknet("kakarot", "compute_starknet_address", evm_address)
await _call_starknet("kakarot", "get_starknet_address", evm_address)
).contract_address
else:
starknet_address, evm_address = response
contract.address = Web3.to_checksum_address(f"0x{evm_address:040x}")
contract.starknet_address = starknet_address
logger.info(f"✅ {contract_name} deployed at address {contract.address}")
logger.info(f"✅ {contract_name} deployed at: {contract.address}")

return contract

Expand Down Expand Up @@ -395,6 +395,29 @@ async def send_pre_eip155_transaction(
)


async def eth_get_code(address: Union[int, str]):
starknet_address = await get_starknet_address(address)
return bytes(
(
await _call_starknet(
"account_contract", "bytecode", address=starknet_address
)
).bytecode
)


async def eth_get_transaction_count(address: Union[int, str]):
starknet_address = await get_starknet_address(address)
return (
await _call_starknet("account_contract", "get_nonce", address=starknet_address)
).nonce


async def eth_balance_of(address: Union[int, str]):
starknet_address = await get_starknet_address(address)
return await get_balance(starknet_address)


async def eth_send_transaction(
to: Union[int, str],
data: Union[str, bytes],
Expand Down Expand Up @@ -522,25 +545,38 @@ async def send_starknet_transaction(


async def compute_starknet_address(address: Union[str, int]):
"""
Compute the Starknet address of an EVM address.
Warning: use get_starknet_address for getting the actual address of an account.
"""
evm_address = int(address, 16) if isinstance(address, str) else address
kakarot_contract = _get_starknet_contract("kakarot")
return (
await kakarot_contract.functions["compute_starknet_address"].call(evm_address)
).contract_address


async def get_starknet_address(address: Union[str, int]):
"""
Get the registered Starknet address of an EVM address, or the one it would get
if it was deployed right now with Kakarot.
Warning: this may not be the same as compute_starknet_address if kakarot base uninitialized class hash has changed.
"""
evm_address = int(address, 16) if isinstance(address, str) else address
kakarot_contract = _get_starknet_contract("kakarot")
return (
await kakarot_contract.functions["get_starknet_address"].call(evm_address)
).starknet_address


async def deploy_and_fund_evm_address(evm_address: str, amount: float):
"""
Deploy an EOA linked to the given EVM address and fund it with amount ETH.
"""
starknet_address = (
await _call_starknet(
"kakarot", "compute_starknet_address", int(evm_address, 16)
)
).contract_address

starknet_address = await get_starknet_address(int(evm_address, 16))
account_balance = await get_balance(evm_address)
await fund_address(evm_address, amount - account_balance)
if account_balance < amount:
await fund_address(evm_address, amount - account_balance)
if not await _contract_exists(starknet_address):
await _invoke_starknet(
"kakarot", "deploy_externally_owned_account", int(evm_address, 16)
Expand All @@ -549,7 +585,7 @@ async def deploy_and_fund_evm_address(evm_address: str, amount: float):


async def fund_address(address: Union[str, int], amount: float):
starknet_address = await compute_starknet_address(address)
starknet_address = await get_starknet_address(address)
logger.info(
f"ℹ️ Funding EVM address {address} at Starknet address {hex(starknet_address)}"
)
Expand Down Expand Up @@ -605,17 +641,6 @@ async def store_bytecode(bytecode: Union[str, bytes], **kwargs):
return evm_address


async def eth_get_code(address: Union[int, str]):
starknet_address = await compute_starknet_address(address)
return bytes(
(
await _call_starknet(
"account_contract", "bytecode", address=starknet_address
)
).bytecode
)


async def deploy_with_presigned_tx(
deployer_evm_address: str, signed_tx: bytes, amount=0.1, name=""
):
Expand All @@ -626,6 +651,6 @@ async def deploy_with_presigned_tx(
deployer_evm_address, deployer_starknet_address, signed_tx
)
deployed_address = response[1]
logger.info(f"✅ {name} Deployed at 0x{deployed_address:040x}")
deployed_starknet_address = await compute_starknet_address(deployed_address)
logger.info(f"✅ {name} Deployed at: 0x{deployed_address:040x}")
deployed_starknet_address = await get_starknet_address(deployed_address)
return {"address": deployed_address, "starknet_address": deployed_starknet_address}
2 changes: 1 addition & 1 deletion kakarot_scripts/utils/l1.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def deploy_on_l1(

evm_address = int(receipt.contractAddress or receipt.to, 16)
contract.address = Web3.to_checksum_address(f"0x{evm_address:040x}")
logger.info(f"✅ {contract_name} deployed at address {contract.address}")
logger.info(f"✅ {contract_name} deployed at: {contract.address}")

return contract

Expand Down
9 changes: 4 additions & 5 deletions kakarot_scripts/utils/starknet.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ async def deploy_starknet_account(class_hash=None, private_key=None, amount=1):
max_fee=_max_fee,
)
status = await wait_for_transaction(res.hash)
logger.info(f"{status} Account deployed at address {hex(res.account.address)}")
logger.info(f"{status} Account deployed at: 0x{res.account.address:064x}")

return {
"address": res.account.address,
Expand Down Expand Up @@ -483,7 +483,7 @@ async def deploy(contract_name, *args):
)
status = await wait_for_transaction(deploy_result.hash)
logger.info(
f"{status} {contract_name} deployed at: {hex(deploy_result.deployed_contract.address)}"
f"{status} {contract_name} deployed at: 0x{deploy_result.deployed_contract.address:064x}"
)
return {
"address": deploy_result.deployed_contract.address,
Expand Down Expand Up @@ -553,8 +553,7 @@ async def invoke(contract: Union[str, int], *args, **kwargs):
)
status = await wait_for_transaction(response.transaction_hash)
logger.info(
f"{status} {contract}.{args[0]} invoked at tx: %s",
hex(response.transaction_hash),
f"{status} {contract}.{args[0]} invoked at tx: 0x{response.transaction_hash:064x}"
)
return response.transaction_hash

Expand Down Expand Up @@ -600,5 +599,5 @@ async def wait_for_transaction(tx_hash):
)
return "✅"
except Exception as e:
logger.error(f"Error while waiting for transaction {tx_hash}: {e}")
logger.error(f"Error while waiting for transaction 0x{tx_hash:064x}: {e}")
return "❌"
14 changes: 7 additions & 7 deletions solidity_contracts/src/CairoPrecompiles/DualVmToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ contract DualVmToken {
uint256[] memory kakarotCallData = new uint256[](1);
kakarotCallData[0] = uint256(uint160(account));
uint256 accountStarknetAddress =
abi.decode(kakarot.staticcallCairo("compute_starknet_address", kakarotCallData), (uint256));
abi.decode(kakarot.staticcallCairo("get_starknet_address", kakarotCallData), (uint256));
uint256[] memory balanceOfCallData = new uint256[](1);
balanceOfCallData[0] = accountStarknetAddress;
bytes memory returnData = starknetToken.staticcallCairo("balance_of", balanceOfCallData);
Expand All @@ -73,12 +73,12 @@ contract DualVmToken {
uint256[] memory ownerAddressCalldata = new uint256[](1);
ownerAddressCalldata[0] = uint256(uint160(owner));
uint256 ownerStarknetAddress =
abi.decode(kakarot.staticcallCairo("compute_starknet_address", ownerAddressCalldata), (uint256));
abi.decode(kakarot.staticcallCairo("get_starknet_address", ownerAddressCalldata), (uint256));

uint256[] memory spenderAddressCalldata = new uint256[](1);
spenderAddressCalldata[0] = uint256(uint160(spender));
uint256 spenderStarknetAddress =
abi.decode(kakarot.staticcallCairo("compute_starknet_address", spenderAddressCalldata), (uint256));
abi.decode(kakarot.staticcallCairo("get_starknet_address", spenderAddressCalldata), (uint256));

uint256[] memory allowanceCallData = new uint256[](2);
allowanceCallData[0] = ownerStarknetAddress;
Expand Down Expand Up @@ -107,7 +107,7 @@ contract DualVmToken {
uint256[] memory spenderAddressCalldata = new uint256[](1);
spenderAddressCalldata[0] = uint256(uint160(spender));
uint256 spenderStarknetAddress =
abi.decode(kakarot.staticcallCairo("compute_starknet_address", spenderAddressCalldata), (uint256));
abi.decode(kakarot.staticcallCairo("get_starknet_address", spenderAddressCalldata), (uint256));

// Split amount in [low, high]
uint128 amountLow = uint128(amount);
Expand All @@ -127,7 +127,7 @@ contract DualVmToken {
uint256[] memory toAddressCalldata = new uint256[](1);
toAddressCalldata[0] = uint256(uint160(to));
uint256 toStarknetAddress =
abi.decode(kakarot.staticcallCairo("compute_starknet_address", toAddressCalldata), (uint256));
abi.decode(kakarot.staticcallCairo("get_starknet_address", toAddressCalldata), (uint256));

// Split amount in [low, high]
uint128 amountLow = uint128(amount);
Expand All @@ -146,12 +146,12 @@ contract DualVmToken {
uint256[] memory fromAddressCalldata = new uint256[](1);
fromAddressCalldata[0] = uint256(uint160(from));
uint256 fromStarknetAddress =
abi.decode(kakarot.staticcallCairo("compute_starknet_address", fromAddressCalldata), (uint256));
abi.decode(kakarot.staticcallCairo("get_starknet_address", fromAddressCalldata), (uint256));

uint256[] memory toAddressCalldata = new uint256[](1);
toAddressCalldata[0] = uint256(uint160(to));
uint256 toStarknetAddress =
abi.decode(kakarot.staticcallCairo("compute_starknet_address", toAddressCalldata), (uint256));
abi.decode(kakarot.staticcallCairo("get_starknet_address", toAddressCalldata), (uint256));

uint128 amountLow = uint128(amount);
uint128 amountHigh = uint128(amount >> 128);
Expand Down
10 changes: 4 additions & 6 deletions src/backend/starknet.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,16 @@ namespace Starknet {
) -> (account_address: felt) {
alloc_locals;

// Deploy generic uninitialized account used to get a deterministic starknet address
let (kakarot_address: felt) = get_contract_address();
let (
uninitialized_account_class_hash: felt
) = Kakarot_uninitialized_account_class_hash.read();
let (constructor_calldata: felt*) = alloc();
assert constructor_calldata[0] = kakarot_address;
assert constructor_calldata[1] = evm_address;
let (constructor_calldata_len, constructor_calldata) = Account.get_constructor_calldata(
evm_address
);
let (starknet_address) = deploy_syscall(
uninitialized_account_class_hash,
contract_address_salt=evm_address,
constructor_calldata_size=2,
constructor_calldata_size=constructor_calldata_len,
constructor_calldata=constructor_calldata,
deploy_from_zero=TRUE,
);
Expand Down
Loading

0 comments on commit c366211

Please sign in to comment.