The Scroll Messenger
The Scroll Messenger contracts allow for sending arbitrary messages from L1 to L2 or vice versa. This enables executing functions on another chain in a secure and permissionless way. To send a message from L1 to L2, use the messenger smart contract deployed on L1, L1ScrollMessenger
. To send a message from L2 to L1, use the contract deployed on L2, L2ScrollMessenger
. You can find the smart contract addresses in the Scroll Contracts page.
Sending a Message
Sending a cross-chain message through the ScrollMessenger
is performed by calling the sendMessage
function. In this function you set the target
address that will receive the message in the other chain, a value
parameter for ETH transfers and message
byte array to be sent as CALLDATA
for smart contract execution. Notice the contract also expects a gasLimit
parameter that you can safely set to 0
and a refundAddress
which you can set as the sender EOA. These last two parameters are legacy since L1 to L2 transactions don’t need to pay gas and L2 to L1 gas costs are handled differently, as we’ll cover in the next section.
Relaying Transactions on L1
Unlike L1 to L2 transactions, L2 to L1 transactions need to be finalized using the relayMessageWithProof
function on the Scroll Messenger contract. We call this process “submitting an Execute Withdrawal transaction”, and it is required for both sending arbitrary messages and transferring assets through a gateway or the router. When you use relayMessageWithProof
, you’ll have to provide a Merkle inclusion proof showing your transaction is included in the trie of “withdrawal” messages, along with other parameters. Producing this proof and these values can be done locally and permissionlessly, but at the moment, the easiest way to retrieve these parameters is through our backend APIs:
- Scroll Sepolia API: https://sepolia-api-bridge-v2.scroll.io/api/
- Scroll API: https://mainnet-api-bridge-v2.scroll.io/api/
Supply the address of the EOA or contract responsible for initiating the original transaction on L2 to the /unclaimed
endpoint. The API backend will provide you with all the necessary information to successfully conclude the transaction on L1. Take a look at the following example:
https://sepolia-api-bridge-v2.scroll.io/api/l2/unclaimed/withdrawals?address=0x031407eaaffFB4f1EC2c999bE4477E52C81de3c5&page=1&page_size=10
The API should return your transaction data in the following format:
{ "errcode": 0, "errmsg": "", "data": { "results": [ { "hash": "0xd56f873c953e798521020d8b6885e7509069fcf0e12bdb1227b840c2d29c6645", "replay_tx_hash": "", "refund_tx_hash": "", "message_hash": "0x65f7d787ff0a3fe7f42b603650afc9c0906f44cd913d52a9e37d1defbe526b6e", "token_type": 1, "token_ids": [], "token_amounts": [ "0" ], "message_type": 2, "l1_token_address": "", "l2_token_address": "", "block_number": 11517097, "tx_status": 0, "counterpart_chain_tx": { "hash": "", "block_number": 0 }, "claim_info": { "from": "0x707e55a12557E89915D121932F83dEeEf09E5d70", "to": "0xC96dde523FB7aB544DCe99F78C10272502452Ae7", "value": "0", "nonce": "388896", "message": "0x32e43a11", "proof": { "batch_index": "113627", "merkle_proof": "0x595e51b1e616792354ed5337d73ab55e8cb62fec3d5b9254e0c5549a6359e07cad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d3021ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85e58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344572901eb2c02c80041fe5b5524ab76baafbca7c7bcad9fcbd8a8f492f14ccfeb887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968ffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f83936fdf2b4de30e14ed4e80b8ad6789e4da1c12dc0da39b4a0d87faf93f62b6843fb096223ed1df0ad0a5e4ad87b3bbeca3ec02bbd30b173a9e8bb06f6cdbdb0f5b57c8b6a31abc39d2ac02c6c49d65e06bd47bdc87a4c26f9e37d04aaa134438cb3e31fffb058537dac499fbd1fad3548af7ccc35028cebfb875e1541f37d5a53490c6ceeb450aecdc82e28293031d10c7d73bf85e57bf041a97360aa2c5d99c4c1bd797081f10d69a340d28e20e54bae159d5eddb976c97b3193a8bd5a38e8afc715bd0872994d0083b471117d24f5be50dd072551f0d41ffe8137346039d94d98dc6e569c87f338c68fa23139bafe238eb1308610d4a9b148fc1955128d4ce7874b09783cef2e7750f7ea24f6090c9ce47f33cf25ca5e16a1207b4a50fda2be1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a1ef973d30ca636d922d10ae577c73bc4fe92699225f30c0c2e9d6727bceb256d" }, "claimable": true }, "block_timestamp": 1755015985, "batch_deposit_fee": "" } ], "total": 1 }}
When the transaction finalizes on L1, claim_info
object under the result
json returned will have all the information needed to execute your transaction on L1. The
parameters needed by the relayMessageWithProof
are: from
, to
, value
, nonce
, message
and proof
. Supply these to the relayMessageWithProof
function on L1 to execute and finalize your transaction on L1.
Messenger API
sendMessage
See implementation at L2ScrollMessenger on Github.
function sendMessage( address target, uint256 value, bytes calldata message, uint256 gasLimit, address refundAddress) external payable;
Sends arbitrary data from one chain to another. It allows us to execute functions cross-chain.
Parameter | Description |
---|---|
target | The address of the account that receives the message. The receiver can be either a smart contract or an EOA wallet. |
value | The amount of ether passed when calling the target contract. |
message | The content of the message. This is the arbitrary calldata to be executed. |
gasLimit | Gas limit required to complete the message relay on the corresponding chain. |
refundAddress | The address of the account that will receive the refunded fee. |
relayMessageWithProof
See implementation at L1ScrollMessenger on Github.
function relayMessageWithProof( address from, address to, uint256 value, uint256 nonce, bytes memory message, L2MessageProof memory proof) external;
Execute a L2 to L1 message with message proof.
Parameter | Description |
---|---|
from | The address of the sender of the message. |
to | The address of the recipient of the message. |
value | The msg.value passed to the message call. |
nonce | The nonce of the message to avoid replay attack. |
message | The content of the message. |
proof | The proof used to verify the correctness of the transaction. |