Cross-Chain Bridges 101 — How do tokens flow between blockchains?

This article belongs to a series analyzing monitoring and security analysis of cross-chain protocols.

Access the paper below👇👇👇

XChainWatcher: Monitoring and Identifying Attacks in Cross-Chain Bridges

Photo by Francisco De Legarreta C. on Unsplash

TL;DR

Cross-chain bridges enable blockchains to communicate and transfer assets across different networks, addressing the fragmentation in DeFi and unlocking new use cases. This post breaks down how these bridges work, using the Ronin Bridge as an example. It covers the deposit and withdrawal processes, key components, and the role of Validators in relaying transactions. Understanding the mechanics behind cross-chain bridges is crucial for enhancing blockchain interoperability and building more connected decentralized applications (dApps).

Why cross-chain bridges?

Cross-chain bridges unlock the full potential of blockchains by enabling communication between different networks, addressing liquidity fragmentation in DeFi protocols, and supporting new use cases where multiple blockchains’ features are crucial for a single dApp.

Users may choose to transfer their tokens to other blockchains for various reasons:

taking advantage of unique properties such as lower transaction fees or faster processing speeds.seek access to other protocols available on alternative blockchainsensure consistency across the same protocol deployed in multiple blockchains, making them consistent governance-wise (i.e., decisions enforced in one blockchain are also propagated to the other)

Unlike most DeFi applications that operate on a single chain, these dApps facilitate interoperability across two or more blockchains. Check the article below, where I explore the key reasons why cross-chain protocols are vital for the future of blockchain technology.

The Importance of Cross-Chain Protocols in Web3 [2024]

How do they work?

The figure below illustrates the components of a cross-chain bridge, showing a source chain (S in blue) and a target chain (T in green) with a one-way flow for token deposits. A deposit represents the transfer of tokens from a source blockchain (S) to a target blockchain (T). On the contrary, transferring tokens from the target blockchain (T) back to the source blockchain (S) is called a Withdrawal. In this figure, we abstract the architecture of the “Validators” — an off-chain component that relays data between networks (check our previous article discussing different architectures and their security guarantees).

The flow of data through the components of a cross-chain bridge for the deposit of tokens

The cross-chain process begins with the sender issuing a Deposit transaction on S (1), where user funds are escrowed. The escrow is enforced by transferring tokens to a bridge contract (2). This triggers the emission of events by the respective token contract (3) and the bridge contract (4). Then, the Validators issue the corresponding Deposit transaction on T (5). Depending on the inner mechanics of the bridge, the contract validates a set of signatures by validators (e.g., a multi-signature), or a cryptographic proof (e.g., a Merkle or Zero-Knowledge proof) in (6). If valid, this transaction triggers the unlocking or minting of tokens in T (7), generating events in each contract (8) and (9). The beneficiary can then use these funds in the destination blockchain.

GIF — A token bridge employing a lock-mint paradigm (the original tokens are kept locked in the source chain and the equivalent amount is minted in the destination). Note that the number of entities was simplified for demonstration purposes.

The process for withdrawing tokens is similar, though with some additional caveats. Usually, validators fetch the events from the target blockchain and generate signatures, but the users are the ones who issue the transaction to S to withdraw the tokens as a way to avoid increasing the operational cost of the protocol. This process is usually managed using the UI of each bridge. Below, there is the flow for the withdrawal of tokens.

The flow of data through the components of a cross-chain bridge for the withdrawal of tokens

[Note that there may be slight changes in the processes involving the validators (step 5 in both images) depending on the validator architecture and proving systems. Also, the emission of events in a Token Contract may not happen when dealing with native tokens (e.g., ETH in Ethereum)].

Let’s look at a real example (Ronin Bridge)

To analyze transactions of cross-chain bridges, we need to analyze transactions on multiple blockchains. Let me give you an example based on older data from the Ronin Bridge. The Ronin Bridge connects Ethereum (S) and the Ronin blockchain (T). To analyze an example of a cross-chain transaction, we will leverage the block explorers of both blockchains to decode the information.

Deposit of Tokens

The sender issues a first deposit transaction to the bridge contract in Ethereum. Let’s take a look at the decoded input data for that transaction:

Function: depositERC20For(address _user, address _token, uint256 _amount)

Arguments:
_user 0xd1a6f4c55C50d8d775D1007A1EcD2C263d7570A31
_token 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB482 (USDC)
_amount 5000000000 (5000 USDC because the USDC token uses six decimal places)

So, the above transaction was issued by the user (sender) to the Ronin Bridge Contract, calling the depositERC20For function. The data used in the function call instructs the bridge to transfer 5000 USDC to the Ronin blockchain. We can see that the transaction was successful. Let’s also look at the execution logs. We see that the first event was emitted by the token contract (0xa0b8699… — USDC), transferring the 5000 tokens to the bridge contract (0x1a2a1c…). Finally, there is a TokenDeposited event emitted by the bridge contract with the following data:

_depositId: 1985750
_owner: 0xd1a6f4c55C50d8d775D1007A1EcD2C263d7570A3 (beneficiary)
_tokenAddress: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 (USDC)
_sidechainAddress: 0x0B7007c13325C48911F73A2daD5FA5dCBf808aDc (USDC in the Ronin blockchain)
_standard: 20 (ERC20)
_tokenNumber: 5000000000 (5000)

From this moment onwards, the tokens have been transferred to the bridge contract on the source chain, and the off-chain entities are responsible for relaying information to the target chain. Around 3 minutes later, there is a transaction issued in the Ronin blockchain by a Validator depositing tokens to the beneficiary address with the following input data:

Function: batchDepositERCTokenFor(uint256[],address[],address[],uint32[],uint256[])

Arguments:
_depositIds: 1985750
_owners: 0xd1a6f4c55C50d8d775D1007A1EcD2C263d7570A3 (beneficiary)
_tokens: 0x0B7007c13325C48911F73A2daD5FA5dCBf808aDc (USDC)
_standards: 20 (ERC20)
_tokenNumbers: 5000000000 (5000)

We can then again look at the execution logs and check the events emitted by each contract. Again, we have an event emitted by the USDC token contract transferring tokens from the bridge contract (0xe35d62) to the beneficiary, followed by an event emitted by the bridge contract. You may also check that the deposit IDs of the transactions on both chains match.

Withdrawal of Tokens

The withdrawal of tokens follows roughly the same scheme but in the opposite direction. Consider a transaction in the Ronin blockchain with the following input data:

Function: withdrawERC20For(address,address,uint256)

Arguments:
_owner: 0x1391C4C4e845d0E5aE0f80d7072389B4D1d8B064 (beneficiary)
_token: 0x97a9107C1793BC407d6F527b77e7fff4D812bece (AXS in Ronin)
_amount: 113138226530558651754 (113.13 AXS — the AXS token uses sixteen decimal places)

This transaction, issued by the sender, instructs the bridge to withdraw 113 AXS to the beneficiary in Ethereum. The off-chain Validators listen to the events emitted by the bridge contracts and generate digital signatures. A couple of minutes later, the user issues a transaction on Ethereum to complete the withdrawal process. Based on the same logic for deposits, check the execution logs and match the information on both chains. Note that the input data below for this transaction contains the signatures collected from Validators according to the security mechanism used by the bridge. The set of signatures is verified within the bridge smart contract.

Function: withdrawERC20For(uint256 _withdrawalId, address _user, address _token, uint256 _amount, bytes _signatures)

_withdrawalId 1056606
_user 0x1391C4C4e845d0E5aE0f80d7072389B4D1d8B064 (beneficiary)
_token 0xBB0E17EF65F82Ab018d8EDd776e8DD940327B28b (AXS in Ethereum)
_amount 113138226530558651754
_signatures 0x01c775e08ec07cdfe7151724ae9894080b369276……

What’s Next?

Now that you’ve grasped the foundational concepts of cross-chain bridges and how transactions are mapped across different blockchains, there’s much more to explore. In the upcoming post, we’ll dive deeper into the performance of cross-chain bridges and security vulnerabilities that have affected them in the past. Stay tuned for some fascinating insights!

A special thanks to Rafael Belchior for reviewing and making suggestions that improved this post.

Additional relevant resources:

What have we learned after 3.2 billion USD was stolen from cross-chain bridges?Exploring Open Challenges and Future Research Avenues in Cross-Chain [2024]https://medium.com/media/458abc358a707ec2e292e941416a5029/href

Cross-Chain Bridges 101 — How do tokens flow between blockchains? was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.

By

Leave a Reply

Your email address will not be published. Required fields are marked *