众所周知,许多区块链存在可扩展性和拥塞问题。这些问题具有广泛的影响,从交易时间变慢到交易费用增加和用户体验下降。
一种解决方案是使用L2 (第二层)链使 web3 成为多链。以太坊 L2,如Optimism 、 Arbitrum和Polygon ,建立在以太坊网络之上,但比以太坊更快、更便宜。
然而,作为权衡,它们通常不如以太坊安全。这就是为什么 L2 处理日常用户活动,同时仍然依赖以太坊 L1 作为安全和去中心化结算和数据可用性层的幕后基础。
这是一个很好的解决方案——但是,仅在以太坊上就有很多 L2;每一个都是独立的网络,具有自己的细微差别和经验。
构建和使用在这些网络和以太坊 L1 之间互操作和移动的 dapp 可能是乏味、困难的,并且对用户和开发人员来说体验不佳。
我们需要的是让 web3 成为一种多链体验,消费者不需要知道他们正在使用哪个链(坦率地说,不关心),开发人员可以依赖任何网络最好地支持他们的 dapps 的需求.
通过转移到这个多链区块链互联网,web3 成为每个相关人员的更好体验。
不幸的是,允许 dapps 在链之间移动是一项艰巨的技术挑战。在本文中,我们将研究一种解决方案——使用 Infura RPC 端点和 Truffle Boxes 构建并无缝桥接这些网络。
具体来说,我们将使用 Optimism Bridge Truffle Box在以太坊 Goerli 测试网上创建一个项目,并桥接到 Optimism Goerli。
作为我们示例解决方案的核心,我们将依赖Truffle Boxes——来自 ConsenSys 的“快捷”样板(例如合约、库、模块,甚至是功能齐全的 dapp),您可以使用它们来构建 dapp。
对于多链解决方案,它们构建在许多 L2 网络的Infura RPC 节点之上。
如上所述,我们将特别依赖Optimism Bridge Truffle Box 。这个盒子有从 L1 和 L2 与 Optimism 桥交互所需的所有合约,以及一组用于部署、调用函数和在层之间传递消息/值的迁移。
它甚至有一个帮助脚本,可以完成我们需要看到的一切。我们只需要将它拆箱即可获得我们需要的一切!据 Trufflesuite.com 称,该盒子包括:
注:桥接器是一种允许独立的区块链相互通信、发送代币、NFT 等的工具。
在开始之前,我们需要满足以下先决条件:
node -v && npm -v
满足先决条件后,请访问 Infura 网站以登录(或注册一个新帐户)。
成功注册后,页面重定向到 Infura 仪表板,我们可以在其中创建新的 API 密钥,如下所示。
单击“创建新密钥”按钮并填写所需信息。
创建 API 密钥后,您的项目 ID 将显示在仪表板上的 API 密钥部分下,如下所示。复制并保存在某个地方;您将在本教程的后面部分需要它。
接下来,我们将设置一个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>
该命令应下载并运行 npm install 作为拆箱过程的一部分。
现在,运行以下命令将目录更改为我们刚刚创建的新目录。
cd truffle-bridge-demo
注意:truffle-bridge-demo 是我们创建的目录的名称。
我们应该有类似于下面显示的内容。
这 。 dotenv
npm 包已安装,但我们需要在开箱后创建的 .env 文件中添加一些信息。
truffle truffle-config.ovm.js
文件期望 .env 文件中存在一个 GOERLI_MNEMONIC 值,用于在 Ethereum Goerli 和 Optimism Goerli 测试网上运行命令,以及一个 INFURA_KEY 以连接到网络。
GOERLI_MNEMONIC="<your-wallet-mnemonic>" INFURA_KEY="<your-infura-key>"
将 <your-infura-key> 替换为我们之前从 Infura 仪表板获得的信息。 (注意:切勿与任何人共享您的私钥(助记词),并妥善保管)。并将 <your-wallet-mnemonic> 替换为您的助记符,如下所示:
要从 Metamask 中检索助记符,请在您的 Metamask 上单击下面显示的图标。
接下来,点击导出私钥按钮复制助记词。
Git 忽略此项目中的 .env 文件以帮助保护您的私人数据。避免向 GitHub 泄露您的私钥是一种良好的安全做法。
当我们打开项目时,我们项目的所有必需合同和脚本都已为我们创建。在下一步中,让我们通过各个合同和迁移来了解网络之间如何进行桥接和交互。
合约contract/ethereum/GreeterL1.sol
向您展示了如何通过 Optimism 桥从 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
使用上述合约从以太坊向 Optimism 发送消息。
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 桥向另一个方向 (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
来自动化编译合约、运行迁移和发送消息的过程。
接下来,我们将实际部署我们的合约到 Goerli。 helper脚本方便了以太坊歌尔里和Optimism歌尔里之间消息的编译、迁移和桥接。
在这些网络上,我们需要测试网 ETH 才能使用它。要接收一些,请使用水龙头。我们还需要将 Optimism 附加组件添加到您的 Infura 帐户。
接下来,我们将运行以下命令来启动项目。
npm run deploy
下面是一个 URL,用于在完成迁移后确认(通过 Etherscan)桥接消息。
第 4 次迁移完成后,将提供用于通过 Etherscan 确认桥接消息的链接。
我们已经成功地设置、安装、构建、部署并完成了我们之前拆箱的项目。接下来,我们将在歌尔力以太坊测试网上验证该项目。
前往Goerli Etherscan区块浏览器并粘贴部署时在我们的 CLI 上显示的 txn 地址 0xbcc1746a9ebbfcfb71665225c1a353a8c8dc9a1aa528a3babcb5b046d615a353。
如果我们希望用户和开发人员的体验不断改善,多链 web3 世界至关重要。为了实现这一目标,我们需要让 dapp 能够在链之间快速无缝地进行通信的方法。
希望我们使用 Optimism Bridge Truffle Box 演示的示例向您展示了一种相对简单快捷的入门方法。要了解更多信息,请查看官方文档。
祝你有美好的一天!