It is well known that many blockchains suffer from scalability and congestion issues. These issues have a wide range of impacts, from slow transaction times to increased transaction fees to poor user experience.
One solution is that web3 is multi chain Use L2 (Layer 2) chains. Ethereum L2 such as Optimism, Arbitrum and Polygon are built on the Ethereum network but are faster and cheaper than Ethereum.
However, as a trade-off, it is often less secure than Ethereum. As such, L2 relies on his Ethereum L1 as the behind-the-scenes foundation for a secure, decentralized payment and data availability layer, while handling day-to-day user activity.
This is a great solution, but there are many L2s on Ethereum alone. Each is a standalone network with its own nuances and experiences.
Building and using dapps that interoperate and move between these networks and Ethereum L1 can be a tedious, difficult, and poor experience for users and developers.
What we need is that web3 is multi chain Consumers don’t need to know which chain they’re using (and frankly don’t care), and developers can rely on the network that best supports their dapps needs.
By moving to this multi-chain blockchain internetweb3 will be a better experience for everyone involved.
Unfortunately, allowing dapps to travel across chains is a technical challenge. In this post, we’ll look at one solution built using Infura RPC endpoints and Truffle Box to seamlessly bridge these networks.
Specifically, we use the Optimism Bridge Truffle Box to create a project on the Ethereum Goerli testnet and bridge it to Optimism Goerli.
Running Multi-Chain Dapps with Infura and Truffle Box
truffle box
At the core of our example solution, we rely on Truffle Boxes, a ConsenSys “shortcut” boilerplate that can be used to build dapps (contracts, libraries, modules, even fully functional dapps, etc.).
For multi-chain solutions, much of the L2 network is built on top of Infura RPC nodes.
As mentioned above, specifically Optimism Bridge Truffle BoxThis box contains all the contracts needed to interact with the Optimism bridge from both L1 and L2, along with a set of migrations for deploying, calling functions, and passing messages/values between layers. is included.
It also has a helper script that does everything you need to see everything working. Just unbox and get everything you need. According to Trufflesuite.com, the box includes:
- “L1 Contract Sending Messages Through the Optimism Bridge
- Migration to send messages from Ethereum to Optimism
- L2 contract to send messages through the Optimism bridge
- Migration to send messages from Optimism to Ethereum
- Scripts that automate compiling contracts, running migrations, and sending messages
- A script that automates the sending of ETH and DAO over the bridge”
note: bridge A tool that allows independent blockchains to communicate with each other and send tokens, NFTs, etc.
prerequisite
Before you begin, you need the following prerequisites:
- Node.js and its package manager NPM.
- Verify that Node.js is installed using the following terminal command:
node -v && npm -v
Step 1 — Create an Infura Account to Access the Network
After completing the prerequisites, visit the Infura website and log in (or sign up for a new account).
After a successful sign up, the page will redirect to the Infura dashboard where you can create a new API key as shown below.
Click the Create a New Key button and enter the required information.
Once you have created your API key, your project ID will appear under the API KEY section of your dashboard as shown below. Make a copy and keep it somewhere. You will need it later in this tutorial.
Step 2 — Setup and Installation
next, Truffle Optimism Bridge Box. You can run the unbox command in any directory of your choice with the following command:
npx truffle unbox optimism-bridge <DIRECTORY_NAME>
npm install -g truffle
truffle unbox optimism-bridge <DIRECTORY_NAME>
This command should download and run npm install as part of the unboxing process.
Then run the following command to change directory to the new directory you just created.
cd truffle-bridge-demo
Note: truffle-bridge-demo is the name of the created directory.
You should have something like the one shown below.
.dotenv
The npm package is installed, but we need to add some information to the .env file created after unpacking.
of truffle-config.ovm.js
The file expects the presence of the GOERLI_MNEMONIC value in the .env file to run commands on the Ethereum Goerli and Optimism Goerli testnets, and the presence of the INFURA_KEY to connect to the network.
GOERLI_MNEMONIC="<your-wallet-mnemonic>"
INFURA_KEY="<your-infura-key>"
To get the mnemonic from the Metamask, click the icon displayed below the Metamask.
next, Export private key Click the button to copy the mnemonic.
Git will ignore this project’s .env file to protect your personal data. It’s a good security practice not to disclose your private key to GitHub.
Step 3 — Bridging Using Truffle L2 Boxes
Unpacking the project created all the necessary contracts and scripts for the project. In this next step, let’s discuss individual contracts and transitions to understand how bridging and interactions occur between networks.
the contract contract/ethereum/GreeterL1.sol
shows how to send a message from L1 to L2 over the optimism bridge.
//SPDX-License-Identifier: Unlicense
// This contract runs on L1, and controls a Greeter on L2.
pragma solidity ^0.8.0;
import { ICrossDomainMessenger } from
"@eth-optimism/contracts/libraries/bridge/ICrossDomainMessenger.sol";
contract GreeterL1 {
address crossDomainMessengerAddr = 0x5086d1eEF304eb5284A0f6720f79403b4e9bE294;
address greeterL2Addr = 0xC0836cCc8FBa87637e782Dde6e6572aD624fb984;
function setGreeting(string calldata _greeting) public {
bytes memory message;
message = abi.encodeWithSignature("setGreeting(string)",
_greeting);
ICrossDomainMessenger(crossDomainMessengerAddr).sendMessage(
greeterL2Addr,
message,
1000000 // within the free gas limit amount
);
} // function setGreeting
} // contract GreeterL1
Migration migrations/3_set_L2_greeting.js
Send a message from Ethereum to Optimism using the above contract.
var Greeter = artifacts.require("GreeterL1");
/**
* Set L2 Greeting
* Run this migration on L1 to update the L1 greeting.
*/
module.exports = async function (deployer) {
console.log("Updating the L2 Greetings contract from L1! 👋👋");
const instance = await Greeter.deployed();
const tx = await instance.setGreeting("👋 Greetings from Truffle!");
console.log(`🙌 Greeter txn confirmed on L1! ${tx.receipt.transactionHash}`);
console.log(`🛣️ Bridging message to L2 Greeter contract...`);
console.log(
`🕐 In about 1 minute, check the Greeter contract "read" function: https://goerli-optimism.etherscan.io/address/0xC0836cCc8FBa87637e782Dde6e6572aD624fb984#readContract`
);
};
next, contracts/optimism/GreeterL2.sol
The contract sends messages in the opposite direction (L2->L1) through the Optimism bridge.
//SPDX-License-Identifier: Unlicense
// This contract runs on L2, and controls a Greeter on L1.
pragma solidity ^0.8.0;
import { ICrossDomainMessenger } from
"@eth-optimism/contracts/libraries/bridge/ICrossDomainMessenger.sol";
contract GreeterL2 {
address crossDomainMessengerAddr = 0x4200000000000000000000000000000000000007;
address greeterL1Addr = 0x7fA4D972bB15B71358da2D937E4A830A9084cf2e;
function setGreeting(string calldata _greeting) public {
bytes memory message;
message = abi.encodeWithSignature("setGreeting(string)",
_greeting);
ICrossDomainMessenger(crossDomainMessengerAddr).sendMessage(
greeterL1Addr,
message,
1000000 // irrelevant here
);
} // function setGreeting
} // contract GreeterL2
Migration migrations/4_set_L1_greeting.js
Send a message from Optimism to Ethereum using the above contract.
require("dotenv").config();
const sdk = require("@eth-optimism/sdk");
const ethers = require("ethers");
const Greeter = artifacts.require("GreeterL2");
const goerliMnemonic = process.env["GOERLI_MNEMONIC"];
const infuraKey = process.env["INFURA_KEY"];
const sleep = (milliseconds) => {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
};
/**
* Set L1 Greeting
* Run this migration on L1 to update the L1 greeting.
*/
module.exports = async function (deployer) {
const newGreeting = "👋 Greetings from Truffle!"; //<---- CHANGE THIS VALUE TO YOUR NAME!!!
const instance = await Greeter.deployed();
console.log("Updating the L1 Greetings contract from L2! 👋");
const tx = await instance.setGreeting(newGreeting);
const txHash = tx.receipt.transactionHash;
console.log(`🙌🙌 Greeter txn confirmed on L2! ${txHash}`);
console.log(
`🛣️ Bridging message to L1 Greeter contract.\n 🕐 This will take at least 1-5 min...`
);
// Set providers for Optimism sdk
const l1Provider = new ethers.providers.JsonRpcProvider(
"https://goerli.infura.io/v3/" + infuraKey
);
const l2Provider = new ethers.providers.JsonRpcProvider(
"https://optimism-goerli.infura.io/v3/" + infuraKey
);
// Connect an L1 signer
const wallet = ethers.Wallet.fromMnemonic(goerliMnemonic);
const l1Signer = wallet.connect(l1Provider);
// Initialize sdk messenger
const crossChainMessenger = new sdk.CrossChainMessenger({
l1ChainId: 5,
l2ChainId: 420,
l1SignerOrProvider: l1Signer,
l2SignerOrProvider: l2Provider,
});
let statusReady = false;
// Sleep for 1 min during L2 -> L1 bridging
await sleep(60000); // 60 seconds
// Poll the L1 msg status
while (!statusReady) {
let status = null;
status = await crossChainMessenger.getMessageStatus(txHash);
statusReady = status == sdk.MessageStatus.READY_FOR_RELAY;
if (!statusReady) {
console.log(
"Message not yet received on L1.\n 🕐 Retrying in 10 seconds..."
);
await sleep(10000); // 10 seconds
}
}
console.log("📬 Message received! Finalizing...");
// Open the message on L1
finalize = await crossChainMessenger.finalizeMessage(txHash);
console.log(
`🎉 Message finalized. Check the L1 Greeter contract "read" function: https://goerli.etherscan.io/address/0x7fA4D972bB15B71358da2D937E4A830A9084cf2e#readContract`
);
};
In the script directory goerli_bridge_message.mjs
When goerli_bridge_value.js
Automate the process of compiling contracts, running migrations, and sending messages.
Step 4 — Complete compilation, migration and bridging of contracts between Ethereum Goerli and Optimism Goerli
Now it’s time to actually deploy the contract to Goerli. Helper scripts facilitate compiling, migrating, and bridging messages between Ethereum Goerli and Optimism Goerli.
These networks should use the testnet ETH. Use the faucet to receive some. You will also need to add the Optimism add-on to your Infura account.
Then run the following command to start the project:
npm run deploy
Below is the URL to see the bridged messages (via Etherscan) after the migration is complete.
After completing the fourth migration, you will be provided with a link to see the messages bridged via Etherscan.
Step 5 — Use Block Explore to Verify Your Project Works on the Goerli Testnet
I’ve successfully setup, installed, built, deployed, and walked through a previously unboxed project. Next, validate your project on the Goerli Ethereum testnet.
Go to the Goerli Etherscan block explorer and paste the txn address 0xbcc1746a9ebbfcfb71665225c1a353a8c8dc9a1aa528a3babcb5b046d615a353 shown in the CLI during deployment.
https://goerli-optimism.etherscan.io/tx/0xbcc1746a9ebbfcfb71665225c1a353a8c8dc9a1aa528a3babcb5b046d615a353
Conclusion
A multi-chain web3 world is essential to continue improving the user and developer experience. And to achieve that, we need a way for dapps to communicate between chains quickly and seamlessly.
I hope the example I described using the Optimism Bridge Truffle Box shows you how to get started relatively easily and quickly. See the official documentation for more details.
Have a really great day!
Loading
. . . comment& more!