将Moonbeam预编译智能合约功能与波卡指定技术交互,再结合Axelar通用消息传递(GMP),能够实现其他链无法完成的独特交互。阅读本文了解Connected Contracts互连合约如何通过只与单条链交互连接Axelar的EVM链发送Token至Centrifuge等DeFi平行链。

这篇文章相对更深奥,如果您尚未了解Axelar,请先阅读Axelar的概况文章和Moonbeam最近发布的关于Axelar SDK解释的文章。

Axelar合约概况

Axelar是一条连接不同区块链并传递安全跨链通信的区块链。每个Axelar网络的验证人在链上运行Axelar支持的轻节点。这个动态验证人通过监控每条链的Axelar网关合约来达成共识,以确认消息从一条链传送至另一条链。Axelar网关合约是我们在后续demo中交互的两个Axelar合约的其中一个。

图片来源于Axelar Network

另一个我们将使用的合约是Axelar Gas Receiver微服务。当您使用Axelar网关发送跨链交易时,IAxelarGasReceiver将允许您为目标链上的后续交易支付费用。虽然非必要,但这允许终端用户只发送一笔交易来自动更新目标链,并以他们所持有的源链Token来支付交易费用。

非波卡链与平行链间的转移

Axelar将帮助我们从非波卡链获取Token到Moonbeam。但是这些桥接的Token将如何从Moonbeam转移到其他平行链?

Moonbeam允许开发者创建称为可铸造的XC-20跨链(XCM)资产,可以通过波卡的XCM消息格式桥接至其他平行链。这些XCM资产如同Moonbeam网络上的普通ERC-20资产一样,可以通过智能合约进行铸造、销毁和转移。使用XTokens预编译的智能合约,开发者可以使用典型的Solidity智能合约将这些Token发送至其他链。

Axelar专为Moonbeam编写了一个智能合约,将其桥接的Token包装到可铸造的XC-20中,允许Axelar的Token通过Moonbeam能够触达波卡生态系统的其他部分。此合约尚未部署至主网,但可以作为一个示例,展示包装合约如何用于桥接资产。

如之前所提及的,Axelar是一个转接桥,允许用户在链之间传送Token。Axelar的通用消息传递允许开发者连接链之间的合约,即使不在同一条链上,用户也可以通过单个交易调用智能合约。最重要的是您可以在同时使用这两个功能。

在单笔交易中,您将从一条链发送Token至Moonbase Alpha,将其包装到一个可铸造XC-20,并最终使用XTokens预编译来进行跨链传送。在本示例中,您将要向Centrifuge处理的测试网传送资产。请注意,Centrifuge在本示例中仅用于示例。

互连合约Connected Contracts

互连合约需要两个角色:源链上的发送者和目标链上的接收者。很多dapp可能希望在链之间来回传送相同类型的消息,即无缝提供跨链可替代的流通性。这些dapp需要在相同的合约中有发送者和接收者组件。

在本示例中,只有一条链(Moonbase Alpha)拥有接收功能,因为其他Axelar连接的链都不是波卡平行链。因此,会有两个智能合约来分别处理发送和接收逻辑。

ReceiveCrossChainXToken

首先来看一下目标链(Moonbase)合约,因其规定希望接收的信息。

ReceiveCrossChainXToken继承自IAxelarExecutable,这允许其通过Axelar网关进行调用。该母合约有两个可覆盖的函数,_execute_executeWithToken,这将允许开发者在合约收到来自Axelar网关合约调用时更改逻辑。两个函数拥有相同的输入,但是_executeWithToken还将包含tokenSymbolamount来描述跨链传送的Token。智能合约希望可以接收Token,以便将其包装到XCM资产中,因此这将覆盖_executeWithToken函数。

此函数收到很多信息,但在本示例中,只需考虑Axelar提供的两个部分:负载和接收的Token数量。

Axelar发送给接收者智能合约的负载是来自发送者合约的基本信息,序列化为动态字节对象。智能合约将负载解码为两个结构和一个uint64,这将之后的XTokens预编译中使用。

剩下的逻辑在不同的函数wrapAndSendxUSDC中。将逻辑分离到另一个函数并不是必要的,但是这允许用户在源链上包装和发送可铸造的XC-20。

您需要注意此合约仅需要aUSDC。更通用的合约可能需要使用Axelar提供给我们的额外参数。

现在您已经从跨链消息中整理出来初始信息,接下来让我们来看一下大部分逻辑。

首先,合约批准Axelar包装合约使用Axelar的aUSDC。然后,在记录一些事件的进程后,合约允许包装合约将aUSDC包装到xUSDC中。最后,在触发另一个事件并根据波卡的特殊性进行调整后,合约尝试使用xTokens预编译发送XCM消息,将xUSDC转移到另一条平行链。

波卡有一个系统要求像xUSDC这样的本地资产至少有一个“单位”的xUSDC来“保持账户活跃”。这就是为什么如果合约中xUSDC的数量等于或少于即将发送的数量,智能合约会保留1个“单位”的xUSDC。这个特殊性是因为波卡的安全功能,该功能源自于一个系统,确保用户不会意外丢失所有的资产,但是在本示例中是一个小阻碍。

这是接收端智能合约的完整分解。下一个合约会更直接明了。

SendCrossChainXToken

Moonbase合约要接收的内容已经确定,现在您需要编写合约来发送这部分数据。查看目标链合约。

在SendCrossChainXToken中只有一个相关函数,sendxUSDCToParachain,其将与AxelarGateway交互,从而发送带有Token的跨链消息。这也将使用IAxelarGasService合约,以便可以自动支付在目标链上的执行费用(gas)。

此函数采用与xTokens预编译相同的参数,因为调用者本质上是从不同的链上进行调用。

然后,合约将调用者的aUSDC从调用者转移至合约,以便Axelar的网关合约可以稍后从SendCrossChainXToken移除aUSDC。请注意,调用者必须在此工作之前批准合约,实际上可能只需要操作一次。

接下来,合约将负载编码到字节对象,以便后续在目标链中对其解码。编码与解码的顺序相同,请自行检查!

下面一个步骤,是在目标链上支付gas费用。我们计划是从目标链发送aUSDC和自定义数据(负载),因此合约必须支付同时发送Token的合约调用费用。在此次实施中,合约将用源链的原生Token为目标链的交易支付费用。

原生Token是用户用于支付gas费用的Token。举例来说,我用Fantom测试网作为源链,则原生Token为FTM。您可以根据目标链的原生Token和Moonbase Alpha的原生Token之间的转换率使用原生Token为目标链支付gas费用,转换率将基于其对应的主网(本示例中为Fantom转换到Moonbeam)。

综上所述,因为智能合约正在发送包含Token的智能调用并以DEV来支付目标链的gas费用,因此该合约将使用payNativeGasForContractCallWithToken函数。请注意合约使用的链名称为“Moonbeam”而非“Moonbase”,因为Axelar要求主网名称。

最后,合约允许网关合约从合约获取Token并发送跨链消息。

Deploying Axelar Connected Contracts

在设置代码库之前需要准备一些先决条件。您将需要访问一个拥有以下设置的solidity钱包:

  • 从Moonbase Alpha的faucet获取的DEV
  • 另一种Axelar支持的EVM链的原生Token(如下所示)
  • 从Axelar的Discord faucet获取的能够代替aUSDC的原生Token

以下表格列出了一些Axelar支持的EVM链,包含其测试网faucet的链接:

最后,您可能希望创建一个测试网Centrifuge钱包。因为您可以发送Token到任何预存在的钱包并监控由此产生的交易,建议您控制目标钱包以便后续您想要转回Token。Centrifuge提供了一个连接至Moonbase Alpha的测试网平行链,您可以在polkadot.js上与其交互。如果您还不知道如何在Centrifuge上创建钱包,您可以观看此教程视频学习如何使用polkadot.js扩展程序。

当您设置完账户后,您将需要获取其十六进制格式的地址。最简便的方式是复制缩短的地址(以kA开始),打开Shawn Tabrizi网站,并将缩短的地址复制到AccountId to Hex工具栏中。

如果您不想操作此过程,您可以直接使用以下账户:

“`

0x6a5a0c5de2400f2f0eccfaf65c765c40bd85af4e370e7da51f36942ed3546f24

“`

设置代码库

现在,您已经拥有了一个资金充足的钱包并选择了Axelar支持的EVM链来发送跨链消息。接下来您可以开始研究代码了!

复制以下代码库:

“`

git clonehttps://github.com/jboetticher/axelar-parachain-hop.git

“`

在您刚刚复制的代码库的文件夹中,您将根据以下格式使用您的钱包私钥创建一个secrets.json文件:

“`

{

“privateKey”: “YOUR_PRIVATE_KEY”

}

“`

现在,您已经完成了所有的设置,可以开始部署了!

使用脚本部署

要帮助合约部署,脚本文件夹中有两个脚本,destDeploy.jsoriginDeploy.js。这两个脚本与ethers.js部署非常相似,您如果您熟悉使用hardhat,您可能已经完成了部署。本文将不会讨论代码。

首先,通过在项目主目录中运行destDeploy.js脚本在Moonbase Alpha上部署目标链合约:

“`

npx hardhat run scripts/destDeploy.js –network moonbase

“`

您将看到如下方所示的内容。确保复制您要部署至的地址并将其粘贴至安全的地方,以便后续使用。

现在,您可以在您选择的Axelar连接的EVM上部署源链合约。代码库的hardhat.config.js文件有很多可用网络,各种faucet已在上述列出。以下命令将在测试网上部署并批准部署的100个测试网aUSDC合约。

“`

npx hardhat run scripts/originDeploy.js –network moonbase

“`

您应该在输出处看到如下所示内容。再次提醒,请确保复制您部署的地址。

平行链之间如何转移非波卡token

现在看看平行链之间的非波卡token转移方案。或者更确切地说是启动它的脚本。

相关脚本位于scripts文件夹中,名为axelarSend.js

第一步,配置。您将需要改变更改顶部的脚本以放入您的目标链地址(Moonbase Alpha的ReceiveCrossChainXToken)和您的源链地址(SendCrossChainXToken)。这就是为何在上一部分保存两个地址的原因!

您还需要以十六进制格式输入您的Centrifuge地址,这是您在部署时查找先决条件时获得的。

该脚本使用Axelar SDK来预计目标链gas fee。作为开发者,需预计在目标链上的支出的gas金额,因为很难预计只能由特定合约调用的函数。在本例中,gas高估在200,000。在真实生产环境中,您可能想对支出的gas金额进行基准测试。然而,如果最终您确实高估了很多,您将得到Axelar gas服务的退款。

由Axelar SDK提供的estimateGasFee函数将找到源链的本地token和目标链的本地token之间的转换,以找到发送到目标链的正确数量。

如果您查看以下案例截图,疑问为何使用辅助函数testnetToMainnetChainName,因为相比测试网名称(如Moonbase Alpha),Axelar更想要主网名称(Moonbeam)。

现在,查看发送到源链的交易:

粗略观察,这些输入看起来很奇怪,但理解这些输入,则需要对Substrate(工具链波卡平行链建立在其上)和 xTokens在Moonbase Alpha上如何预编译的工作原理进行简要解释。

Substrate允许开发者构建模块化的区块链,这就是波卡如何获得平行链的方式。这些在不同区块链上提供不同功能的模块中的每一个都称为pallet。在构建平行链时,每个pallet都有一个 ID:pallet ID。

波卡允许每一个平行链通过XCM技术互相之间交流。每个平行链都需要一个身份:平行链ID。

xTokens预编译,即ReceiveCrossChainXToken合约与之交互的预编译合约,不由任何solidity代码组成。它实际上被运用到平行链中,并直接与xTokens pallet交互。需要Substrate理解的数据才可以操作(如果你是Solidity开发者,这与你习惯的或许不同)。xTokens预编译管理的每个资产都有自己的资产ID。

总结来说,发送方合约使用Axelar的GMP将xTokens预编译所需的数据从EVM链发送到Moonbase Alpha。下一步将分析所发送的xTokens预编译的数据。

ReceiveCrossChainXToken中使用的xTokens预编译函数是transferMultiasset。它采用一个Multilocation结构来表示资产,一个uint256来表示您希望发送的金额,另一个Multilocation结构来表示我们将资金发送到哪里,一个uint64来表示权重。

构建Multilocation有点难以理解,所以Moonbeam的文档中提供了专门介绍。下面查看资产参数的第一个Multilocation结构:

“`

“`

0值表示该位置相对于当前的平行链。这是正确的,因为xUSDC是最初在Moonbase Alpha上注册的token,而不是其他平行链。

对于字节数组,每个条目的前4个字节(十六进制字符)表示如何解读其余部分的消息。

因此,第一个条目的0x04表示该条目是pallet ID。0x24等于36,因此该条目表示资产与Moonbase Alpha的第36个pallet相关,即xTokens pallet。

第二个条目的0x05表示通用索引,与xTokens pallet结合有意义。剩下的条目0xFD9D0BF45A2947A519A741C4B9E99EB6表示xUSDC的资产ID,所以是xUSDC的通用索引。

另一个参数相对简单。 30000 xUSDC等于30分。但您需要仔细查看目标链信息:

“`

“`

对比资产参数,数值1表示该位置不是相同的平行链。这是正确的,因为如果您只在本地发送xUSDC,这会破坏这个项目的目的。

字节数组的第一个条目的0x00表示条目是平行链ID。0x000007EF等于2031,即Centrifuge在Moonbase Alpha的测试中继链上的平行链ID。

第二个条目0x01表示32字节地址,就是Centrifuge使用的地址,因此CENTRIFUGE_ACCOUNT变量直接附加在后面。另外的0x00满足格式的要求。

权重参数与EVM链上gas相似,但有些细微差别,在此不做讨论。如您对此感兴趣,可以至波卡文档查阅。在这里使用1000000000即可。

后两个参数与xTokens预编译无关。您发送ReceiveCrossChainXToken的Moonbase Alpha地址,以便合约知道将消息定向到何处。最后,您通过交易发送的值将用于通过Axelar的gas接收器支付源链上的gas。

发送及监测交易

现在,您已经理解交易并已经正确配置脚本,请往下执行发送交易。

您可以使用两个浏览器监测交易。一个是Axelar测试网浏览器,该浏览器将提供您在Moonbase Alpha和源链上的相关活动的深入数据。另一个是polkadot.js,该浏览器将允许您查询在Centrifuge链状态的变化。

以下是交易的格式,NAME_OF_NETWORK是您选择的网络(在hardhat.config.js文件的网络配置中查看要使用的测试网网络的可能名称):

“`

npx hardhat run scripts/axelarSend.js –network NAME_OF_NETWORK

“`

发送交易之后,您应该能在控制台看到如下内容:

请复制交易哈希以监测您的交易情况。打开Axelar测试网浏览器,在右上角搜索栏粘贴交易哈希,即可检测交易状态。如果所有步骤顺利,交易最终完成。

打开polkadot.js上的Centrifuge测试网,连接至Moonbase Alpha。如果在Moonbase Alpha上执行消息之前执行此操作,您可能会捕获一个xcmpQueueevent。

请确保所有操作正确的情况下检查,或者如果您没有看到xcmpQueue event,您可以检查chain state或者确保您已经收到token。

在Developer标签下找到Chain state。您可以使用ormlTokens pallet查询您的token余额,现在,xUSDC作为一个ID 2的ForeignAsset。请注意,该结果金额可能与您原始发送的金额(300000)并不匹配,因为还牵涉到平行链转移中的费用。

假设以上步骤皆顺利的情况下,您已经完成平行链间的转移!此模板对其他使用Axelar资产的平行链也可重复,并且为生态间的资产无缝转移打开了大门。

如您需要任何帮助,请在Mooonbeam Discord与团队进行联系。

深入了解跨链互连合约

虽然Centrifuge不会在他们的主网上使用XC-20包装的aUSDC,但随着波卡生态系统中对新资产的需求增加,这种技术可能会被更多地使用。

如果读者对自己写互连合约和Axelar的GMP感兴趣的话,可以积极尝试更多用例。

您可以在Axelar网站上了解更多信息。您也可以阅读了解互连合约是如何将Moonbeam定位为区块链互操作性的领导者。