构建L1和L2之间互操作的dApp可能很困难。人们需要了解如何使用Optimism Bridge的Truffle Box作为连接以太坊和Optimism的简单方法。
译者 | 李睿
审校 | 孙淑娟
众所周知,许多区块链都存在可扩展性和拥塞问题。这些问题有广泛的影响,从交易时间变慢,到交易费用增加,以及用户体验下降。
一种解决方案是使用L2(第二层)区块链使Web3成为多链。以太坊L2(例如Optimism、Arbitrum和Polygon)构建在以太坊网络之上,但比以太坊更快,更便宜。然而,作为一种权衡,它们通常不如以太坊安全。这就是L2处理日常用户活动,同时仍然依赖以太坊L1作为安全、去中心化结算和数据可用性层的幕后基础的原因。
这是一个很好的解决方案,仅以太坊上就有许多L2,每个L2都是一个独立的网络,有自己的细微差别和体验。构建和使用在这些网络和以太坊L1之间互操作和移动的dApp可能是乏味和困难的,而且对用户和开发人员来说也是糟糕的体验。
现在需要的是Web3成为一种多链体验,消费者不需要知道他们使用的是哪条区块链(坦率地说不在乎),开发者可以依赖任何最支持他们dApp需求的网络。通过转移到区块链的多链互联网,Web3为每个参与者提供了更好的体验。
不幸的是,允许dApp在区块链之间移动是一项艰巨的技术挑战。本文将研究一种解决方案——使用Infura RPC端点和Truffle Box进行构建,并无缝地桥接这些网络。具体来说, 将使用Optimism Bridge的Truffle Box在以太坊Goerli测试网上创建一个项目,并连接到Optimism Goerli。
使用Infura和Truffle Boxes运行多链dApp
Truffle Boxes
作为示例解决方案的核心,将依赖于来自ConsenSys的“快捷”样板文件(例如合约、库、模块,甚至是功能齐全的dApp),企业可以使用它们来构建自己的dApp。对于多链解决方案,它们构建在许多L2网络的Infura RPC节点之上。
如上所述,特别依赖Optimism Bridge的Truffle Box。Truffle Box包含L1和L2与Optimism Bridge交互所需的所有合约,以及一组用于在层之间部署、调用函数和传递消息/值的迁移。它甚至有一个辅助脚本,可以完成需要的所有操作。只需要打开Truffle Box就能得到需要的一切! Truffle Box包括:
- 通过Optimism Bridge发送消息的L1合约。
- 从以太坊向OptimismBridge发送消息的迁移。
- 通过Optimism Bridge发送消息的L2合约。
- 从OptimismBridge向以太坊发送消息的迁移。
- 自动编译契约、运行迁移和发送消息的脚本。
- 通过Optimism Bridge自动发送ETH和DAO的脚本。
注:桥接(Bridge)是一种工具,允许独立的区块链彼此通信,并发送令牌、NFT等。
先决条件
在开始之前,需要具备以下先决条件:
使用以下终端命令验证已经安装了Node.js:
- Infura帐户
- MetaMask账户
- 基本了解JavaScript和Solidity
步骤1:创建用于访问网络的Infura帐户
在完成先决条件后,登录Infura网站(或注册新帐户)。
在成功注册之后,页面重定向到Infura仪表板,在那里可以创建一个新的API密钥,如下图所示:
点击“Create a New Key”按钮,填写所需信息。
在创建API密钥后,项目ID将显示在仪表板的API key部分下面,如下面所示。复制并保存在某个地方,将在本教程的后面用到它。
步骤2:设置和安装
接下来,将设置Truffle Optimism Bridge Box。可以使用以下命令在选择的任何目录中运行unbox命令。
npx truffle unbox optimism-bridge <DIRECTORY_NAME>
用选择的目录名替换<DIRECTORY_NAME>。或者,可以全局安装Truffle并运行unbox命令。
npm install -g truffle
truffle unbox optimism-bridge <DIRECTORY_NAME>
该命令应该下载并运行npminstall作为unbox过程的一部分。
现在,运行以下命令将目录更改为刚刚创建的新目录。
注:truffle-bridge-demo是创建的目录的名称。
应该得到类似于下面所示的结果。
.dotenv npm包已经安装,但是需要在开箱后创建的.env文件中添加一些信息。truulu -config.ovm.js文件期望在.env文件中存在一个GOERLI_MNEMONIC值,用于在以太坊Goerli和Optimism Goerli测试网上运行命令,并期望INFURA_KEY连接到网络。
GOERLI_MNEMONIC="<your-wallet-mnemonic>"
INFURA_KEY="<your-infura-key>"
将<your-infura-key>替换为之前从Infura仪表板中获得的信息。【注意:永远不要与任何人分享私钥(助记符),并妥善保管】。将<your-wallet-mnemonic>替换为助记符,如下所示:
要从Metamask中检索助记符,单击Metamask上所示的图标。
接下来,单击Export Private Key按钮复制助记符。
Git忽略了这个项目中的.env文件,以帮助保护私有数据。避免将私钥泄露给GitHub是一个很好的安全实践。
步骤3:使用Truffle L2 Boxes桥接
当打开项目的盒子时,创建了项目的所有必要合同和脚本。在下一步中,我们将详细介绍各个合约和迁移,以理解桥接和交互是如何在网络之间发生的。
合约contract/ethereum/GreeterL1.sol展示了如何通过Optimism bridge将消息从L1发送到L2。
//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
迁移migrations/3_set_L2_greeting.js使用上面的合约从以太坊发送消息给optimistic。
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`
);
};
接下来,contracts/optimism/GreeterL2.sol合约通过Optimism bridge向另一个方向(L2->L1)发送消息。
/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
迁移migrations/4_set_L1_greeting.js使用上述合约将消息从Optimism发送到以太坊。
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`
);
};
在脚本目录中,还有goerli_bridge_message.mjs和goerli_bridge_value.js来自动化编译合约、运行迁移和发送消息的过程。
步骤4:完成Ethereum Goerli和Optimism Goerli之间的合约编译、迁移和桥接
接下来,将把合约部署到Goerli。助手脚本促进了Ethereum Goerli和Optimism Goerli之间的编译、迁移和桥接消息。在这些网络上,需要测试网ETH来使用它。还需要将Optimism插件添加到Infura帐户。
接下来,将运行以下命令来启动项目。
下面是一个URL,用于在完成迁移后确认(通过Etherscan)桥接消息。
在第四次迁移完成后,将提供一个通过Etherscan确认桥接消息的链接。
步骤5:用块资源管理器在Goerli测试网上验证项目是否成功
已经成功地设置、安装、构建、部署了前面打开的项目。接下来,将在Goerli Ethereum测试网上验证该项目。
转到Goerli Etherscan块资源管理器,并粘贴txn地址
0xbcc1746a9ebbfcfb71665225c1a353a8c8dc9a1aa528a3babcb5b046d615a353,该地址在部署时在CLI上显示。
https://goerli-optimism.etherscan.io/tx/0xbcc1746a9ebbfcfb71665225c1a353a8c8dc9a1aa528a3babcb5b046d615a353
结论
如果希望用户和开发人员的体验持续改善,那么多链Web3世界是至关重要的。为了实现这一点,需要让dApp在区块链之间快速无缝地通信。希望使用Optimism Bridge Truffle Box的例子能给一个相对简单快速的入门方法。要了解更多信息,可以查看官方文档。
原文标题:Using Truffle L2 Boxes to Bridge Blockchain Networks,作者:John Vester