Testing messages to L1
There exists a functionality allowing you to spy on messages sent to L1, similar to spying events.
Check the appendix for an exact API, structures and traits reference
Asserting messages to L1 is much simpler, since they are not wrapped with any structures in Cairo code (they are a plain felt252
array and an L1 address).
In snforge
they are expressed with a structure:
/// Raw message to L1 format (as seen via the RPC-API), can be used for asserting the sent messages.
struct MessageToL1 {
/// An ethereum address where the message is destined to go
to_address: starknet::EthAddress,
/// Actual payload which will be delivered to L1 contract
payload: Array<felt252>
}
Similarly, you can use snforge
library and call spy_messages_to_l1()
to initiate a spy:
use snforge_std::{spy_messages_to_l1};
#[test]
fn test_spying_l1_messages() {
let mut spy = spy_messages_to_l1();
// ...
}
With the spy ready to use, you can execute some code, and make the assertions:
- Either with the spy directly by using
assert_sent
/assert_not_sent
methods fromMessageToL1SpyAssertionsTrait
trait:
use starknet::EthAddress;
use snforge_std::{
declare, ContractClassTrait, DeclareResultTrait, spy_messages_to_l1,
MessageToL1SpyAssertionsTrait, MessageToL1,
};
use testing_messages_to_l1::{IMessageSenderDispatcher, IMessageSenderDispatcherTrait};
#[test]
fn test_spying_l1_messages() {
let mut spy = spy_messages_to_l1();
let contract = declare("MessageSender").unwrap().contract_class();
let (contract_address, _) = contract.deploy(@array![]).unwrap();
let dispatcher = IMessageSenderDispatcher { contract_address };
let receiver_address: felt252 = 0x2137;
dispatcher.greet_ethereum(receiver_address);
let expected_payload = array!['hello'];
let receiver_l1_address: EthAddress = receiver_address.try_into().unwrap();
spy
.assert_sent(
@array![
(
contract_address, // Message sender
MessageToL1 { // Message content (receiver and payload)
to_address: receiver_l1_address, payload: expected_payload
}
)
]
);
}
- Or use the messages' contents directly via
get_messages()
method of theMessageToL1SpyTrait
:
use starknet::EthAddress;
use snforge_std::{
declare, ContractClassTrait, DeclareResultTrait, spy_messages_to_l1, MessageToL1SpyTrait,
MessageToL1SpyAssertionsTrait, MessageToL1, MessageToL1FilterTrait,
};
use testing_messages_to_l1::{IMessageSenderDispatcher, IMessageSenderDispatcherTrait};
#[test]
fn test_spying_l1_messages_details() {
let mut spy = spy_messages_to_l1();
let contract = declare("MessageSender").unwrap().contract_class();
let (contract_address, _) = contract.deploy(@array![]).unwrap();
let dispatcher = IMessageSenderDispatcher { contract_address };
let receiver_address: felt252 = 0x2137;
let receiver_l1_address: EthAddress = receiver_address.try_into().unwrap();
dispatcher.greet_ethereum(receiver_address);
let messages = spy.get_messages();
// Use filtering optionally on MessagesToL1 instance
let messages_from_specific_address = messages.sent_by(contract_address);
let messages_to_specific_address = messages_from_specific_address.sent_to(receiver_l1_address);
// Get the messages from the MessagesToL1 structure
let (from, message) = messages_to_specific_address.messages.at(0);
// Assert the sender
assert!(*from == contract_address, "Sent from wrong address");
// Assert the MessageToL1 fields
assert!(*message.to_address == receiver_l1_address, "Wrong L1 address of the receiver");
assert!(message.payload.len() == 1, "There should be 3 items in the data");
assert!(*message.payload.at(0) == 'hello', "Expected \"hello\" in payload");
}