Bridging
The Stackr SDK offers L1 to L2 bridging out of the box through the Bridge
Plugin.
import { Bridge } from "@stackr/sdk/plugins";
Bridge.init(rollup, {
handlers: {
// Define the bridging handler here.
}
})
How it works?
- You can set a
bridge
contract in your micro-rollup'sAppInbox
. - The
AppInbox
has acreateTicket
function which can only be invoked by the bridge. - When a ticket is created on the L1, the micro-rollup receives the event and processes the ticket.
Building your bridge
1. Write the Bridge contract
Developers can write their custom logic in the Bridge
contract and interact with the AppInbox
to create the ticket
that'll be procsessed on the MRU.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
interface ITicketFactory {
function createTicket(
bytes32 _identifier,
address _msgSender,
bytes memory _message
) external;
}
contract TokenBridge {
address appInbox;
constructor(address _appInbox) {
appInbox = _appInbox;
}
function bridgeETH(address _to) external payable {
require(_to != address(0), "bridgeTokens/zero-address");
require(msg.value > 0, "bridgeTokens/zero-amount");
bytes memory message = abi.encode(_to, msg.value);
bytes32 identifier = keccak256("BRIDGE_ETH");
ITicketFactory(appInbox).createTicket(
identifier,
msg.sender,
message
);
}
}
The bridgeETH
function —
- Encodes the data to be sent to the micro-rollup.
- Computes an identifier for the ticket. This is important as this identifier is used on the micro-rollup to match the ticket to a handler.
- Creates the ticket by calling the
AppInbox
with the ticketidentifier
,msg.sender
, and themessage
to be attached to the ticket.
2. Set the Bridge
on the AppInbox
.
Next up, you need to update the AppInbox
to know which Bridge
contract to accept ticket creation requests from.
Run the CLI command:
npx @stackr/cli add bridge
You'll be prompted with the bridge address:
? Bridge Contract Address
Enter your bridge contract address above and you're set! The CLI will update the Bridge
contract address inside the AppInbox
.
3. Write the handler to process tickets
You can define a handler to process each different type of ticket.
In our Bridge contract, we created a ticket with the identifier of BRIDGE_ETH
. So, we need to write a handler to process the tickets with that specific identifier.
The function defined as a handler has to return —
actionParams
- AnActionParams
object.
Bridge.init(rollup, {
handlers: {
'BRIDGE_ETH': async (args) => {
const [_to, _amount] = abiCoder.decode(['address', 'uint'], args.data);
const inputs = {
address: _to,
amount: Number(_amount)
}
const name = 'mintToken';
const domain = rollup.config.domain;
const types = rollup.getStfSchemaMap()[name];
const signature = await operator.signTypedData(domain, types, { name, inputs });
const actionParams = {
name,
inputs,
signature,
msgSender: operator.address
};
return {
actionParams
};
}
}
})
The function to handle BRIDGE_ETH
tickets —
- First, decodes the data sent with the ticket.
- Generates the operator's signature on the data.
- Constructs action parameters to be submitted to the rollup.
- Returns the
actionParams
object.
The micro-rollup then takes over and submits the returned action to the correct transition.
That's it! You've successfuly implemented bridging in your micro-rollup. You can extend this example to do arbitrary message passing. From arbitrary ERC-20 tokens to simply sending data to the rollup. For better understanding, we recommend going through the full codebase of the bridge example.