Bridge & Messaging

Bridging Basics

Glide is a "Layer 2" system and is fundamentally Equivalent Ethereum. However, Glide is also a distinct blockchain with its own blocks and transactions. App developers commonly need to move data and tokens between Glide and Ethereum (Example) . This process of moving data and tokens between the two networks is called "bridging".

Sending Data Between L1 and L2

Smart contracts on L1 (Offchain Glide) can interact with smart contracts on L2 (Glide) through a process called "bridging". This page explains how bridging works, how to use it, and what to watch out for.

This is a high-level overview of the bridging process. For a step-by-step tutorial on how to send data between L1 and L2, check out the Solidity tutorial.

Understanding Contract Calls

It can be easier to understand bridging if you first have a basic understanding of how contracts on EVM-based blockchains like Glide and Ethereum communicate within the same network. The interface for sending messages between Offchain L1 and Glide is designed to mimic the standard contract communication interface as much as possible.

Here's how a contract on Ethereum might trigger a function within another contract on Ethereum:

contract MyContract {
    function doTheThing(address myContractAddress, uint256 myFunctionParam) public {             
            MyOtherContract(myContractAddress).doSomething(myFunctionParam);
    }
}

Here, MyContract.doTheThing triggers a call to MyOtherContract.doSomething. Under the hood, Solidity is triggering the code for MyOtherContract by sending an ABI encoded(opens in a new tab) call for the doSomething function. A lot of this complexity is abstracted away to simplify the developer experience. Solidity also has manual encoding tools that allow us to demonstrate the same process in a more verbose way.

Here's how you might manually encode the same call:

contract MyContract {
    function doTheThing(address myContractAddress, uint256 myFunctionParam) public {                
        myContractAddress.call(
            abi.encodeCall(
                MyOtherContract.doSomething,
                (
                   myFunctionParam
                )
            )
        );
    }
}

Here you're using the low-level "call" function and one of the ABI encoding functions built into Solidity. Although these two code snippets look a bit different, they're doing the exact same thing. Because of limitations of Solidity, the Glide Stacks bridging interface is designed to look like the second code snippet.

Basics of Communication Between Layers

At a high level, the process for sending data between L1 and L2 is pretty similar to the process for sending data between two contracts on Ethereum (with a few caveats). Communication between L1 and L2 is made possible by a pair of special smart contracts called the "messenger" contracts. Each layer has its own messenger contract, which serves to abstract away some lower-level communication details, a lot like how HTTP libraries abstract away physical network connections.

We won't get into too much detail about these contracts here. The most important thing that you need to know is that each messenger contract has a sendMessage function that allows you to send a message to a contract on the other layer.

function sendMessage(
    address _target,
    bytes memory _message,
    uint32 _minGasLimit
) public;

The sendMessage function has three parameters:

  1. The address _target of the contract to call on the other layer.

  2. The bytes memory _message calldata to send to the contract on the other layer.

  3. The uint32 _minGasLimit minimum gas limit that can be used when executing the message on the other layer.

This is basically equivalent to:

address(_target).call{gas: _minGasLimit}(_message);

Except, of course, that you're calling a contract on a completely different network.

This is glossing over a lot of the technical details that make this whole thing work under the hood, but this should be enough to get you started. Want to call a contract on Glide from a contract on Offchain L1? It's dead simple:

// Pretend this is on L2
contract MyOptimisticContract {
    function doSomething(uint256 myFunctionParam) public {
        // ... some sort of code goes here
    }
}
 
// And pretend this is on L1
contract MyContract {
    function doTheThing(address myOptimisticContractAddress, uint256 myFunctionParam) public {
        messenger.sendMessage(
            myOptimisticContractAddress,
            abi.encodeCall(
                MyOptimisticContract.doSomething,
                (
                    myFunctionParam
                )
            ),
            1000000 // or use whatever gas limit you want
        )
    }
}

You can find the addresses of the L1CrossDomainMessenger and the L2CrossDomainMessenger contracts on Glide on the Contract Addresses page.

Communication Speed

Unlike calls between contracts on the same blockchain, calls between Ethereum and Glide are not instantaneous. The speed of a cross-chain transaction depends on the direction in which the transaction is sent.

For L1 to L2 Transactions

Transactions sent from L1 to L2 take approximately 1 minutes to get from Offchain Glide to Glide,,This is because the Sequencer waits for a certain number of L1 blocks to be created before including L1 to L2 transactions to avoid potentially annoying reorgs.

Understanding the Challenge Period

Optimistic Rollups are "optimistic" because they're based around the idea of publishing the result of a transaction to Offchain L1s without actually executing the transaction on L1s. In the "optimistic" case, this transaction result is correct and one can completely avoid the need to perform complicated (and expensive) logic on L1.

However, one still needs to find a way to prevent incorrect transaction results from being published in place of correct ones. Here's where the "fault proof" comes into play. Whenever a transaction result is published, it's considered "pending" for a period of time known as the challenge period. During this period of time, anyone may re-execute the transaction on Ethereum in an attempt to demonstrate that the published result was incorrect.

If someone can prove that a transaction result is faulty, then the result is scrubbed from existence and anyone can publish another result in its place (hopefully the correct one this time, financial punishments make faulty results very costly for their publishers). Once the window for a given transaction result has fully passed without a challenge, the result can be considered fully valid (or else someone would've challenged it).

Anyway, the point here is that you don't want to be making decisions about Layer 2 transaction results from inside a smart contract on Layer 1 until this challenge period has elapsed. Otherwise you might be making decisions based on an invalid transaction result. As a result, L2 ⇒ L1 messages sent using the standard messenger contracts cannot be relayed until they've waited out the full challenge period.

Last updated