Mocking interaction with L1#

Abstract#

In order to test interaction with L1 contracts, devnet client provides a way to mock the L1 interaction. Before taking a look at the examples, please get familiar with the devnet postman docs and messaging mechanism:

L1 network setup#

First of all you should deploy messaging contract on ethereum network or load the existing one.

from starknet_py.devnet_utils.devnet_client import DevnetClient

client = DevnetClient(node_url="http://127.0.0.1:5050")

# Deploying the messaging contract on ETH network
# e.g. anvil eth devnet https://github.com/foundry-rs/foundry/tree/master/crates/anvil
await client.postman_load(network_url="http://127.0.0.1:8545")

L2 -> L1#

Deploying L2 interaction contract#

Interaction with L1 is done by sending a message using send_message_to_l1_syscall function. So in order to test it, you need to deploy a contract that has this functionality. Example contract: l1_l2.cairo

from starknet_py.contract import Contract

# Address of your contract that is emitting messages
contract_address = "0x12345"

contract = await Contract.from_address(address=contract_address, provider=account)

await contract.functions["increase_balance"].invoke_v3(
    user=account.address,
    amount=100,
    l1_resource_bounds=ResourceBounds(
        max_amount=50000, max_price_per_unit=int(1e12)
    ),
)

# Invoking function that is emitting message
await contract.functions["withdraw"].invoke_v3(
    user=account.address,
    amount=100,
    l1_address=eth_account_address,
    l1_resource_bounds=ResourceBounds(
        max_amount=50000, max_price_per_unit=int(1e12)
    ),
)

Consuming message#

After deploying the contract, you need to flush the messages to the L1 network. And then you can consume the message on the L1 network.

# Sending messages from L2 to L1.
flush_response = await client.postman_flush()

message = flush_response.messages_to_l1[0]

message_hash = await client.consume_message_from_l2(
    from_address=message.from_address,
    to_address=message.to_address,
    payload=message.payload,
)

L1 -> L2#

Sending mock transactions from L1 to L2 does not require L1 node to be running.

await client.send_message_to_l2(
    l2_contract_address=contract_address,
    entry_point_selector=get_selector_from_name("deposit"),
    l1_contract_address="0xa000000000000000000000000000000000000001",
    payload=[account.address, 100],
    nonce="0x0",
    paid_fee_on_l1="0xfffffffffff",
)

# Sending messages from L1 to L2.
flush_response = await client.postman_flush()