-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
277 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
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 faimiliar with the `devnet postman docs <https://0xspaceshard.github.io/starknet-devnet-rs/docs/postman>`_ and messaging mechanism: | ||
|
||
- `Writing messaging contracts <https://book.cairo-lang.org/ch16-04-L1-L2-messaging.html>`_ | ||
- `Mechanism overview <https://docs.starknet.io/architecture-and-concepts/network-architecture/messaging-mechanism/>`_ | ||
- `StarkGate example <https://docs.starknet.io/architecture-and-concepts/network-architecture/messaging-mechanism/>`_ | ||
|
||
L1 network setup | ||
---------------- | ||
|
||
First of all you should deploy `messaging contract <https://github.com/0xSpaceShard/starknet-devnet-rs/blob/138120b355c44ae60269167b326d1a267f7af0a8/contracts/l1-l2-messaging/solidity/src/MockStarknetMessaging.sol>`_ on ethereum network. | ||
|
||
.. codesnippet:: ../../starknet_py/tests/e2e/docs/guide/test_postman_load.py | ||
:language: python | ||
:dedent: 4 | ||
|
||
|
||
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 this functionality, you need to deploy a contract that has this functionality. | ||
Example contract: `l1_l2.cairo <https://github.com/0xSpaceShard/starknet-devnet-js/blob/5069ec3397f31a408d3df2734ae40d93b42a0f7f/test/data/l1_l2.cairo>`_ | ||
|
||
.. codesnippet:: ../../starknet_py/tests/e2e/docs/guide/test_postman_load.py | ||
:language: python | ||
:dedent: 4 | ||
:start-after: docs: messaging-contract-start | ||
:end-before: docs: messaging-contract-end | ||
|
||
|
||
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. | ||
|
||
.. codesnippet:: ../../starknet_py/tests/e2e/docs/guide/test_postman_load.py | ||
:language: python | ||
:dedent: 4 | ||
:start-after: docs: flush-1-start | ||
:end-before: docs: flush-1-end | ||
|
||
L1 -> L2 | ||
-------- | ||
|
||
Sending mock transactions from L1 to L2 does not require L1 node to be running. | ||
|
||
.. codesnippet:: ../../starknet_py/tests/e2e/docs/guide/test_postman_load.py | ||
:language: python | ||
:dedent: 4 | ||
:start-after: docs: send-l2-start | ||
:end-before: docs: send-l2-end | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import pytest | ||
|
||
from starknet_py.hash.selector import get_selector_from_name | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_postman_load(devnet_client, l1_l2_contract, account): | ||
# pylint: disable=import-outside-toplevel | ||
|
||
eth_account_address = 1390849295786071768276380950238675083608645509734 | ||
|
||
# docs: start | ||
from starknet_py.devnet_utils.devnet_client import DevnetClient | ||
|
||
client = DevnetClient(node_url="http://127.0.0.1:5050") | ||
|
||
# docs: end | ||
client: DevnetClient = devnet_client | ||
# docs: start | ||
# Load 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") | ||
# docs: end | ||
|
||
# docs: messaging-contract-start | ||
from starknet_py.contract import Contract | ||
|
||
# Address of your contract that is emitting messages | ||
contract_address = "0x12345" | ||
|
||
# docs: messaging-contract-end | ||
contract_address = l1_l2_contract.address | ||
|
||
# docs: messaging-contract-start | ||
contract = await Contract.from_address(address=contract_address, provider=account) | ||
|
||
await contract.functions["increase_balance"].invoke_v1( | ||
user=account.address, amount=100, max_fee=int(1e16) | ||
) | ||
|
||
# docs: messaging-contract-end | ||
assert await contract.functions["get_balance"].call(user=account.address) == (100,) | ||
|
||
# docs: messaging-contract-start | ||
# Invoking function that is emitting message | ||
await contract.functions["withdraw"].invoke_v1( | ||
user=account.address, | ||
amount=100, | ||
l1_address=eth_account_address, | ||
max_fee=int(1e16), | ||
) | ||
# docs: messaging-contract-end | ||
assert await contract.functions["get_balance"].call(user=account.address) == (0,) | ||
|
||
# docs: flush-1-start | ||
# 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, | ||
) | ||
# docs: flush-1-end | ||
|
||
# docs: send-l2-start | ||
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, 420], | ||
nonce="0x0", | ||
paid_fee_on_l1="0xfffffffffff", | ||
) | ||
|
||
# Sending messages from L1 to L2. | ||
flush_response = await client.postman_flush() | ||
# docs: send-l2-end | ||
|
||
assert await contract.functions["get_balance"].call(user=account.address) == (420,) |
110 changes: 110 additions & 0 deletions
110
starknet_py/tests/e2e/mock/contracts_v2/src/l1_l2.cairo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
//! L1 L2 messaging demo contract. | ||
//! Rewrite in Cairo 1 of the contract from previous Devnet version: | ||
//! https://github.com/0xSpaceShard/starknet-devnet/blob/e477aa1bbe2348ba92af2a69c32d2eef2579d863/test/contracts/cairo/l1l2.cairo | ||
//! | ||
//! This contract does not use interface to keep the code as simple as possible. | ||
//! | ||
|
||
#[starknet::contract] | ||
mod l1_l2 { | ||
const MESSAGE_WITHDRAW: felt252 = 0; | ||
|
||
#[storage] | ||
struct Storage { | ||
// Balances (users) -> (amount). | ||
balances: LegacyMap<felt252, felt252>, | ||
} | ||
|
||
#[event] | ||
#[derive(Drop, starknet::Event)] | ||
enum Event { | ||
DepositFromL1: DepositFromL1, | ||
} | ||
|
||
#[derive(Drop, starknet::Event)] | ||
struct DepositFromL1 { | ||
#[key] | ||
user: felt252, | ||
#[key] | ||
amount: felt252, | ||
} | ||
|
||
/// Gets the balance of the `user`. | ||
#[external(v0)] | ||
fn get_balance(self: @ContractState, user: felt252) -> felt252 { | ||
self.balances.read(user) | ||
} | ||
|
||
/// Increases the balance of the `user` for the `amount`. | ||
#[external(v0)] | ||
fn increase_balance(ref self: ContractState, user: felt252, amount: felt252) { | ||
let balance = self.balances.read(user); | ||
self.balances.write(user, balance + amount); | ||
} | ||
|
||
/// Withdraws the `amount` for the `user` and sends a message to `l1_address` to | ||
/// send the funds. | ||
#[external(v0)] | ||
fn withdraw(ref self: ContractState, user: felt252, amount: felt252, l1_address: felt252) { | ||
assert(amount.is_non_zero(), 'Amount must be positive'); | ||
|
||
let balance = self.balances.read(user); | ||
assert(balance.is_non_zero(), 'Balance is already 0'); | ||
|
||
// We need u256 to make comparisons. | ||
let balance_u: u256 = balance.into(); | ||
let amount_u: u256 = amount.into(); | ||
assert(balance_u >= amount_u, 'Balance will be negative'); | ||
|
||
let new_balance = balance - amount; | ||
|
||
self.balances.write(user, new_balance); | ||
|
||
let payload = array![MESSAGE_WITHDRAW, user, amount,]; | ||
|
||
starknet::send_message_to_l1_syscall(l1_address, payload.span()).unwrap(); | ||
} | ||
|
||
/// Withdraws the `amount` for the `user` and sends a message to `l1_address` to | ||
/// send the funds. | ||
#[external(v0)] | ||
fn withdraw_from_lib( | ||
ref self: ContractState, user: felt252, amount: felt252, l1_address: felt252, message_sender_class_hash: starknet::ClassHash, | ||
) { | ||
assert(amount.is_non_zero(), 'Amount must be positive'); | ||
|
||
let balance = self.balances.read(user); | ||
assert(balance.is_non_zero(), 'Balance is already 0'); | ||
|
||
// We need u256 to make comparisons. | ||
let balance_u: u256 = balance.into(); | ||
let amount_u: u256 = amount.into(); | ||
assert(balance_u >= amount_u, 'Balance will be negative'); | ||
|
||
let new_balance = balance - amount; | ||
|
||
self.balances.write(user, new_balance); | ||
|
||
let calldata = array![user, amount, l1_address]; | ||
|
||
starknet::SyscallResultTrait::unwrap_syscall( | ||
starknet::library_call_syscall( | ||
message_sender_class_hash, | ||
selector!("send_withdraw_message"), | ||
calldata.span(), | ||
) | ||
); | ||
} | ||
|
||
/// Deposits the `amount` for the `user`. Can only be called by the sequencer itself, | ||
/// after having fetched some messages from the L1. | ||
#[l1_handler] | ||
fn deposit(ref self: ContractState, from_address: felt252, user: felt252, amount: felt252) { | ||
// In a real case scenario, here we would assert from_address value | ||
|
||
let balance = self.balances.read(user); | ||
self.balances.write(user, balance + amount); | ||
|
||
self.emit(DepositFromL1 { user, amount }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,3 +9,4 @@ mod test_contract; | |
mod test_enum; | ||
mod test_option; | ||
mod token_bridge; | ||
mod l1_l2; |