为互连合约设计多链感知dApp

Moonbeam小月光
Moonbeam 中文

--

大多数去中心化应用只部署在单条链上,且只熟悉所部署的生态系统。一些最大的dApp在不同链上部署相同的合约,但它们的智能合约没有链之间的互操作性:每一个智能合约仍局限于它们所处的网络。

智能合约互操作性的未来正通过Connected Contracts互连合约在Moonbeam的生态系统上扩张,这使得数据和流动性可以在网络间无缝跨越。来看看一个多链感知dApp的例子,它将useDAppNext.js这样的传统Web3前端工具与Axelar的通用消息传递(一个跨链消息传递)协议相结合。类似于这样的dApp已在Moonbeam部署,且未来还会有更多。

一个新的趋势使得这些多链感知智能合约可以运用来自波卡、以太坊、Avalanche、Cosmos等等的逻辑和功能。就连dApp巨头SushiSwap也通过创建其自己的跨链AMM,SushiXSwap,来开始运用这个科技。在今后,互连合约会使更多、更大规模、不受单链限制的交换、市场和借贷协议成为可能。

当在智能合约改变时,前端也随之变化。

Axelar合约介绍

在介绍前端的设计之前,您至少应该对互连合约运作方式有基础了解。Moonbeam为互连合约提供如LayerZero等多种解决方案,且更多即将上线。在本文的示例dApp中,Axelar是我们的互连合约解决方案,但即便您不打算使用Axelar,该文中的许多设计理念也值得思考。

Axelar是一个连接不同区块链的区块网络,其提供安全的跨链沟通。Axelar网络中的每个验证人都在Axelar支持的链上运行轻节点。这种动态验证人组通过监控每条链的Axelar网关合约,也就是该示例合约所交互的两个Axelar合约之一,以达成共识来确认消息从一条链传递到另一条链上。

图片来源于Axelar Network

另一个合约是Axelar Gas Receiver微服务。每当一个dApp使用Axelar网关来发送一个跨链交易时,IAxelarGasReceiver会让用户为目标链上后续的交易付费。它允许终端用户只需发送一个交易便可以自动更新目标链,并可以用他们已有的源链Token来支付所有的交易费。

多链设计策略

该计划是设计一个能够从一个EVM链的互连合约(如Moonbeam)向另一个EVM链的互连合约(如以太坊)发送数据的前端。您可能已经很熟悉大部分dApp是如何运作的:

  1. 用户决定将dApp连接到他们的钱包。
  2. 前端呈现一个用户可能想确认的交易。
  3. 用户使用MetaMask或其他钱包来签署并发送交易。
  4. 交易被发送至一个节点,然后前端会监控它是否成功。
  5. 在交易成功确认后前端进行更新。

示例前端与此无太大差别,只不过因为这是多链情景,每一步都会有更多要管理的选项!用户可以连接至多条链,所以可以使用多个合约进行交易。因为有多条链和多个合约,您需要展示每一条链和每一个合约的数据。然而最复杂的是,取决于使用的是哪一个互连合约解决方案,您需要监控至多三个不同的网络以确认交易成功!

对于大多数这些问题,许多Ethers.js对象的阵列都可以解决。只要一个dApp能够从一条链上的一个合约中读取数据,它就也能从另一条链上的合约读取数据。相比之下,更困难的问题是监控跨链交易。

首先,用户需要签署并向源链上的跨链互连智能合约发送一个交易。因为本示例使用的跨链中介是Axelar,该跨链互连智能合约会提醒源链上Axelar网关合约有一个新的待处理跨链消息。其次,Axelar网络会接收到该消息并以去中心化的方式来批准它。第三,当跨链消息被批准后,Axelar网路会尝试使用跨链消息中的数据来更新目标链。

所以有三个网络:源链、Axelar网络、和目标链。那么您该如何追踪这么多东西?

如果您是个有经验的dApp开发者,您或许不用学习任何新东西就能够熟练运用某些工具。像Ethers.js这样能够与以太坊JSON RPC交互的库已经可以监控支持EVM的链上的交易,这样的链包括源链和目标链。

此外,Axelar SDK允许开发者们追踪并恢复发送到Axelar链的交易。虽然在源链被包含在源链区块前查询SDK不会提供任何关于交易的信息,这么做能够告诉您关于Axelar和目标链交易的细节。

有几种方法能够让您结合这些工具来查询您需要的数据。该示例实施选择了:

  1. 使用Ethers.js来监控源链交易,直到其被发现。
  2. 使用Axelar SDK持续查询在Axelar网络和目标链上的消息状态。

前端

您可以尝试使用一个简单的前端来展示一个跨链dApp可行的运用(如下图所示)。与其交互的合约将从链到链发送一个短串字符,该合约与首篇Axelar博客文章中使用的智能合约很相似。举个例子,我把“fantom 2 moonbase < 3”这句短语从Moonbase Alpha(Moonbeam的测试网)发送到了Fantom的测试网。

前端是由TypeScript Next.js(React)作为框架构建的,因为React的受欢迎性及其广泛支持性。useDApp是与EVM进行交互的首选工具库,因为它是在备受欢迎的Ethers.js库基础上构建的、是专门为React前端构建的、且拥有多链支持。最后的一个原因是它采用了Semantic UI的组件和主题库以提供简便性。

React是一个十分受欢迎的框架,但其他还有很多框架,我们鼓励大家去探索。useDApp和Semantic UI也是一个道理:有很多能达成类似目的的其他方式,取决于大家的使用习惯和上手速度。本文将会提及实施要注意的一些重要概念,但主要还是展示上手实施的细节。

现在我们来看看示例前端。本文不会涵盖所有内容,我们假设您已经知道React以及一般网络开发的运作方式,但本文会针对一些习惯于使用某些库的开发者们可能不熟悉的部分来展开。我们建议您使用GitHub代码库来查看关于实施的更多细节。

在不同链间切换

能够在不同链间切换可以说是一个多链dApp中最重要的特性。大多允许您连接至钱包的前端库都或多或少会允许您切换链,但本文会向您展示如何在useDApp中使用该功能。

如果您从未使用过useDApp,那么您需要知道它需要带有配置的上下文提供者才能运作。该提供者位于_app.tsx文件中。该配置允许您轻易定义哪些网络是被接受的。在下面的示例中,我们配置了Moonbase Alpha以及其他一些测试网。

现在如果您的用户在错误的网络上,或是如果他们在您的dApp中表明他们想用useEthers hookswitchNetwork功能更改网络的话,您可以提示用户去更改他们的网络。值得注意的是,useEthers可以提供很多信息,包括当前连接的账户地址和用户所选择的网络。

所以在该dApp中,每当有人想要更改源链,前端就会自动提醒他们更改他们的钱包网络。这对允许用户经常在网络间转换的多链dApp十分方便。请注意这只对MetaMask有用。

无论您是否选择在您的个人项目中选择useDApp,您都应该追踪您的用户所连接的链ID并根据该数字来组织您的对象。

从多个合约中读取

使用useDApp从多个合约中读取也非常简单。前端将与其交互的合约HelloWorldMessage.sol位于GitHub代码库的以太坊文件中。所有的都是从映射(地址到字符串)中读取,在本合约中,每个用户都有其自己的消息。

项目如何从互连合约中读取数据:

  1. 表明一个状态变量,代表您想要读取合约所在的网络
  2. 从Ethers.js构建一个合约对象,该对象代表您想要读取互连合约的来源
  3. 使用自定义hook:useCall以获取数据对象

useCall由useDApp提供。这允许您会以比Ethers.js更简单的方式从合约读取数据。同时,这也会提供一个互连合约的合约对象,一个方式(lastMessage)和一个参数(用户地址)。

请注意useCall也有另一个可选的输入值,包括chainId元素。useDApp内置多链支持,允许开发者轻松决定他们想要读取的网络,这正是目前情况所需的。

如果您在没有使用useDApp的情况下,您可以使用useEffect hook来监测的networkToRead变量更改,然后使用您希望查询相关数据的库。

提交交易

在查看与Moonbase Alpha之间的信息之前,先查看合约以了解发送信息所使用的dApp。

sendMessage将会通过Axelar来跨链发送编码字符串和地址消息,并可选择在目标链上支付gas费用。请注意,为了支付此gas费用,您必须在交易中发送原生Token,以确保该函数可使用。

参数包含信息本身、目标链上Connected contract互连合约的地址,以及目标链的名称(无论是在主网还是测试网上,Axelar将显示主网名称,例如显示Moonbeam而非Moonbase)。

Axelar将在目标链合约上调用_execute函数以便接收消息。此合约将解码消息并将其存储在用于检索消息的映射中:

如果您想要学习如何部署此合约并使用您自己的合约,而不是GitHub代码库中提供的合约,请查看Axelar入门博客文章,其中介绍了如何在Moonbeam上使用Axelar。请确保使用此前端repo中的更新合约,而非之前博客中所提供的合约。

然而,发送交易的代码有些许复杂。目前可以先查看Axelar SDK部分的useAxelarFunction hook,并使用此函数发送交易。

useAxelarFunction hook是一个自定义hook,旨在帮助解决一条链上的交易涉及多个网络的事实。这将在下文展开描述,目前,假设从send函数导出的函数与useDApp useContractFunction hook所导出的send函数相同。

useContractFunction hook用于与改变状态的合约函数进行交互。对于dApp来说,这将监测交易状态并提供发送函数,以提示用户使用其钱包签署和发送交易。这也将为事件提供对象,但不会在场景中使用。

深入来了解一下如何在sendTransaction函数中使用发送函数,该函数将在UI中按键时触发。在以下代码片段的最下方,代码发送交易。这将传送sendMessage函数需要的所有参数,以及一个包含value元素的附加对象,来指定支付AxelarGasServices合约的原生Token数量。但是如何获取需要发送的原生Token数量呢?

如果您查看发送函数上方的代码,您将发现正在使用Axelar SDK。必须预估在目标链上需要花费的gas费用,因为很难预估,只能由特定合约调用的函数。在本示例中,预估需要花费不到200,000的gas费用。在真实生产环境中,您可能希望对您花费的gas数量进行基准测试。但是,如果您实际所花费费用远远超出了您的预估费用,您可以通过Axelar的gas服务获得退款

由Axelar SDK的AxelarQueryAPI提供的estimateGasFee函数将找到源链的原生Token和目标链的原生Token之间的转换,以找到正确的数量发送至目标链。此数值将最终包含在Axelar SDK函数中。

最后,如之前所述,此函数会在按下提交按钮时触发。

监控交易

现在来到最复杂的部分:监控多条链之间的交易。项目必须实施在设计策略中讨论的内容。

事实上,并非Moonbeam上的每个Connected contracts互连合约解决方案都有监控工具:对于部分解决方案来说,您只会在交易成功或者失败的时候知道交易的最终状态。如果您决定使用没有监控的互连合约解决方案,您可能需要在状态变更时,直接查询目标链的状态。另一方面,Axelar在其SDK中包含了许多状态和更强大的监控系统

在其他基于React的项目中可能会再调用到与Axelar连接的函数,这就是为什么此处存在自定义的React hook useAxelarFunction。它位于项目ethereum文件夹中的axelar文件夹中,文件名为useAxelarFunction.ts

hook旨在扩展useContractFunction hook。它将结合多个来源(所有三个链)的多个状态组合到一个状态枚举中,同时仍然可以根据需求提供所有查询的信息。

这篇博客删掉了很多冗长的状态更新代码,并专注于重要的部分,如下方的代码片段。以下代码段出现在脚本文件的最顶部。

可以看到,useContractFunction hook被使用,这代表它的功能正在扩展。同时,还有两个状态变量,一个通过枚举追踪所有三个交易的状态(被命名为AxelarTransactionState,不包含在SDK中),另一个保存SDK追踪提供的通用消息返回对象。 useContractFunction状态已重新命名为originState,因为新状态将追踪所有三个交易。

现在我们来快速查看如何使用useEffect hook。

在应用中,useEffect将在观察到的值变动时触发,在本例中为originState。虽然 useEffect返回函数中有额外的代码改变entierState枚举,但最重要的部分是当它遇到来自源链交易的成功返回时。它将适当地设置entireState枚举,然后调用 beginAxelarStatusCheck

在此复习监控交易的策略如下:

  1. 监控源链交易
  2. 通过Axelar SDK监控下个交易

useDApp将通过useContractFunction hook帮助监控源链交易。beginAxelarStatusCheck 将让我们监控下一个交易。

虽然在下面的代码片段中,删除了许多的状态解释逻辑,但主要逻辑仍然存在。首先,Axelar SDK中的AxelarGMPRecoveryAPI被初始化,因为它提供了查询交易状态的功能,直接让前端知道是否有任何交易失败。接着,该状态可被查询并呈现存储的状态。最后,除非事务已经完成,程序将在再次查询之前等待5秒。

虽然每5秒重新查询一次似乎不是最佳解决方案,但它是能通过Axelar执行此类操作的最简单方法。如果您选择使用Moonbeam的其他互连合约提供者,您可能会执行类似的事情或是找到可用的pub/sub服务。无论是哪种方式,只要向前端提供有关每个网络交易的数据并适当地处理它,就可以顺利运行。

useAxelarFunction挂钩提供的数据将会允许程序更新前端。举例来说,用户可以决定Axelar网络的交易是否处于待处理、成功、失败或是状态尚未决定:

接着数据将会向用户显示。

如果您有需要,您可以查看整个代码库了解这一部分是如何运作的,以及数据是如何处理并被制作成可阅读界面的详细步骤。

使用前端

您可以尝试使用前端来查看它是如何作用的。您可以在网站上访问它,也可以通过复制公共代码库并自行运行它。如果您选择复制代码库,则可以使用以下命令启动(在软件包安装完成后):

接着,点击右上角的按钮以连接到您的钱包。您可以通过输入消息、设置源链和目标链并按下提交按钮来发送交易。通过选择要从中读取的链,从底部的每个链的互连合约中读取。

当您从当前的开发背景过渡到可以在Moonbeam上使用互连合约的背景时,您会发现更多处理多链dApp的方法。无论您为自己的项目选择哪种技术堆栈,除了一般创建dApp的困难之外,您还必须找到一种方法来管理(来自多个链)多个交易、链和合约。

祝您操作顺利!

深入了解互连合约

最好的学习方式为进行实际操作,因此鼓励每位读者都尝试创建一个自己的Connected Contracts互连合约dApp! Moonbeam提供广泛的开发者支持,Axelar(以及其他的互连合约社区)很荣幸能够在您构建应用时全程提供帮助。如果您想了解如何在Moonbeam上进行开发的更多信息,Moonbuilder计划是一个能够深入了解如何构建属于自己的dApp的渠道。

您可以在Axelar的网站文档Discord频道了解更多信息。您可以在我们的互连合约概览页面了解Moonbeam是如何成为区块链可交互性的领导者。

如果您对Moonbeam感兴趣并想了解更多,请订阅我们的电子月报和关注我们的社交媒体账号。

关于Moonbeam

Moonbeam是一个智能合约平台,用于构建跨链互连应用程序,能够访问任何链上的用户、资产和服务。通过将来自以太坊、Cosmos、波卡等功能整合到一个平台中,Moonbeam解决了当今用户体验碎片化的问题,解锁了真正的互操作性,并为下一代应用程序奠定基础。Moonbeam平台使用集成的跨链信息传递,允许开发者创建访问多个远程区块链服务的智能合约。通过此方式结合Moonbeam的开发者友好型EVM平台、各类工具支持和Substrate架构,为构建互连应用程序提供理想化的开发环境。

--

--