Skip to content

使用X-Tokens Pallet发送XC-20s

概览

为同质化资产转移构建XCM消息通道并非一件易事。因此,开发者可以通过利用wrapper函数/pallet在Polkadot/Kusama上使用XCM功能。

此类包装器的一个示例是X-Tokens Pallet,用于提供通过XCM转移同质化资产的不同方法。

本教程将向您展示如何利用X-Tokens Pallet在生态系统(中继链/平行链)中从基于Moonbeam的网络发送XC-20s至其他链。此外,您还将学习如何使用X-Tokens预编译通过以太坊API执行同样的操作。

开发者须知若发送不正确的XCM消息可能会导致资金丢失。因此,XCM功能需先在测试网上进行测试后才可移至生产环境。

XCM相关定义

本教程假设您已经拥有基础的XCM知识。如果没有,请阅读XCM概览页面。

对于本指南,您需要了解以下定义:

  • 主权账户 — 生态系统中每条链预设的账户,用于中继链和其他平行链。它的地址为特定单词和平行链ID连接的 blake2 哈希(中继链中的主权账户为blake2(para+ParachainID),其他中的主权账户为blake2(sibl+ParachainID)平行链),将哈希截断为正确的长度。该帐户归root所支配,只能通过SUDO(如果有)或民主(公投)使用。主权账户通常在其它链上签署 XCM 消息
  • Multilocation — 一种指定整个中继链或平行链生态系统中来源点(相对或绝对来源)的方式;例如,它可用于指定特定的平行链、资产、账户,甚至是一个Pallet。一般而言,multilocation定义包含为parentsinterior:

    • parents - 是指需要从来源点“跳跃”多少次可以进入parent区块链
    • interior,是指定义目标点需要多少个字段

    例如,要从另一个平行链中定位ID为1000的平行链,multilocation将是 { "parents": 1, "interior": { "X1": [{ "Parachain": 1000 }]}}

X-Tokens Pallet接口

Extrinsics

X-Tokens Pallet提供以下extrinsics(函数):

  • transfer(currencyId, amount, dest, destWeightLimit) — 转移一个币种,根据原生Token(自身储备)或是资产ID定义
  • transferMultiasset(asset, dest, destWeightLimit) — 转移一种同质化资产,根据其multilocation定义
  • transferMultiassetWithFee(asset, fee, dest, destWeightLimit) — 转移一种同质化资产,但其能够让资产的发送者使用另外一种资产支付转移费用。两者皆根据其multilocation定义
  • transferMultiassets(assets, feeItem, dest, destWeightLimit) — 转移多种同质化资产,并定义其中哪种资产将会被用于支付转移费用。所有资产皆由其multilocation定义
  • transferMulticurrencies(currencies, feeItem, dest, destWeightLimit) — 转移多个币种,并定义其中哪种币种将会被用于支付转移费用。所有币种都将通过原生Token(自身储备)或是资产ID定义
  • transferWithFee(currencyId, amount, fee, dest, destWeightLimit) — 转移一个币种,但允许资产发送者使用不同的资产支付转移费用。两者皆由其multilocation定义

其中需要提供消息输入的函数定义如下:

  • currencyId/currencies — 将通过XCM转移的币种ID。不同runtime有不同的方法定义ID。以基于Moonbeam的网络为例子,币种可以被定义为:

    • SelfReserve - 代表原生Token
    • ForeignAsset - 代表外部XC-20(请不要与XC-20地址混淆)的资产ID
    • LocalAssetReserve - 弃用 通过使用Erc20货币类型来使用 local XC-20s
    • Erc20 - 代表本地XC-20(ERC-20)的合约地址
  • amount — 将通过XCM转移的Token数量

  • dest — 一个multilocation,用于定义将通过XCM转移Token的目标地址。其支持不同地址格式,如20或32字节的地址(以太坊或是Substrate格式)
  • destWeightLimit — 一个枚举,表示您提供给目标链希望其执行XCM消息的最大执行时间。该枚举包含以下选项:

    • Unlimited - 允许使用全部gas来支付权重
    • Limited - 将gas的使用量限制为特定值

    如果您提供的权重不足,XCM将会执行失败,且资金将会被锁定在主权账户或是特定的pallet中。正确设置目标权重非常重要,这将避免XCM失败

  • asset/assets — 个用于定义通过XCM转移资产的multilocation。每条平行链将会有不同定义资产的方式。举例而言,基于Moonbeam的网络将会由其原生Token的Balances Pallet索引定义

  • fee — 一个用于定义支付XCM在目标链上执行的multilocation
  • feeItem — 一个用于定义发送多样资产位置的索引,将用于支付XCM在目标链上的执行。举例而言,如果仅发送一种资产,feeItem将会是0

存储函数

X-Tokens Pallet包含以下只读存储函数:

  • palletVersion() - 提供正在使用的X-Tokens Pallet的版本

Pallet常量

X-Tokens Pallet包含以下只读函数以获取pallet常量:

  • baseXcmWeight() - 返回执行所需的基础XCM权重
  • selfLocation() - 返回链的multilocation

用于X-Tokens转账的XCM指令

X-Tokens Pallet使用的XCM指令定义于X-Tokens Open Runtime Module Library

无论用的是哪一个传输方法,将原生资产送回其原始链(例如,xcDOT从Moonbeam返回Polkadot)和将原生资产从原始链发送到目标链(例如,DOT从Polkadot发送到Moonbeam)的操作指示都是相同的。

举例来说,如果想将DOT转移至Moonbeam,可以使用以下XCM指令:

  1. TransferReserveAsset - 在Polkadot上执行,将资产从初始账户转移并存入目标账户。在这个例子中,目标账户是Moonbeam在Polkadot上的Sovereign账户。然后它将发送一条XCM信息至目标链,也就是Moonbeam。消息中包含了将要执行的的XCM指令
  2. ReserveAssetDeposited - 在Moonbeam上执行,将Sovereign账户收到资产的本地表示存储在临时保管寄存中,这是一个在Cross-Consensus Virtual Machine (XCVM) 中的临时地址。
  3. ClearOrigin - 在Moonbeam上执行。用于确保之后的XCM指令不使用XCM发送者的权限
  4. BuyExecution - 在Moonbeam上执行。使用保管的资产支付执行费用。具体费用根据目标链定义,在这个例子中目标为Moonbeam
  5. DepositAsset - 在Moonbeam上执行。将资产从保管中移除并且发送至Moonbeam上的目标账户

学习更多关于如何使用搭建XCM指令来传输本地资产至目标链,比如将Dot发送至Moonbeam,您可以参考X-Tokens Open Runtime Module Library作为例子。您可能需要transfer_self_reserve_asset这个函数。在这个函数中,您会发现它调用了TransferReserveAsset函数并且使用了assets, dest, 与 xcm三个参数。其中xcm参数包括了BuyExecutionDepositAsset指令。您可以访问Polkadot的Github库,在那您可以找到TransferReserveAsset这个指令。这条XCM消息结合了ReserveAssetDeposited指令,ClearOrigin指令与xcm参数,xcm参数包括BuyExecutionDepositAsset指令。

使用以下指令将xcDOT从Moonbeam发送回Polkadot:

  1. WithdrawAsset - 在Moonbeam上执行。移除资产并将其存入保管寄存中
  2. InitiateReserveWithdraw - 在Moonbeam上执行。将资产从保管寄存中移除(本质上它会被burn掉)并发送一条XCM消息至目标链。XCM消息以WithdrawAsset指令开始
  3. WithdrawAsset - 在Polkadot上执行。移除资产并将其存入保管寄存
  4. ClearOrigin - 在Polkadot上执行。用于确保之后的XCM指令不使用XCM发送者的权限
  5. BuyExecution - 在Polkadot上执行。使用保管的资产支付执行费用。具体费用根据目标链定义,在这个例子中目标为Polkadot
  6. DepositAsset - 在Polkadot上执行。将资产从保管中移除并且发送至Polkadot上的目标账户

学习更多关于如何使用搭建XCM指令来传输本地资产至目标链,比如将xcDOT发送至Polkadot,您可以参考X-Tokens Open Runtime Module Library作为例子。您可能需要transfer_to_reserve这个函数。在这个函数中,您会发现它调用了WithdrawAsset函数,然后调用InitiateReserveWithdraw并且使用了assets, dest, 与 xcm三个参数。其中xcm参数包括了BuyExecutionDepositAsset指令。您可以访问Polkadot的Github库,在那您可以找到InitiateReserveWithdraw instruction这个指令。这条XCM消息结合了WithdrawAsset指令,ClearOrigin指令与xcm参数,xcm参数包括BuyExecutionDepositAsset指令。

使用X-Tokens Pallet构建XCM消息

此教程将会包含使用X-Tokens Pallet构建XCM消息的过程,更详细来说为使用transfertransferMultiasset函数。然而,这两种情况仍然可以外推至其他函数,特别是当您熟悉了multilocation的使用之后。

注意事项

每条平行链皆能够通过pallet允许/禁止特定函数。因此,开发者需要确认使用的函数是被平行链允许的。相反来说,如果使用了被禁止的函数,交易将会如同system.CallFiltered显示一般失败。

本教程将以转移xcUNIT Token为例。xcUNIT是Alphanet中继链Token的XC-20形式。本教程也同样适用于其他XC-20 Token。

查看先决条件

要跟随本教程操作,您需要准备以下内容:

  • 一个拥有资金的账户。 您可以每24小时一次从Moonbase Alpha水龙头上获取DEV代币以在Moonbase Alpha上进行测试
  • 一些xcUNIT Token。您可以在Moonbeam-Swap上将DEV Token(Moonbase Alpha的原生Token)兑换成xcUNIT。Moonbeam-Swap是Moonbase Alpha上的Uniswap-V2版本的示范协议。

    Moonbeam Swap xcUNIT

要查看您的xcUNIT余额,您可以使用以下地址将XC-20添加至MetaMask:

0xFfFFfFff1FcaCBd218EDc0EbA20Fc2308C778080

注意事项

想要了解如何计算预编译地址,您可以查看计算外部XC-20预编译地址教程。

本教程也同样适用于其他的外部XC-20或本地XC-20。如果您要针对另一个外部XC-20调整本教程,您需要拥有要转移资产的资产ID以及该资产的小数位数,您可以根据外部XC-20s的检索列表获取这些信息。如果您要针对本地XC-20调整本教程,则需要拥有XC-20的合约地址。

如果您要转移本地XC-20,请注意每个网络的转移仅限于以下gas单位:

200,000
200,000
200,000

X-Tokens转移函数

在本示例中,您将会构建一个XCM消息,通过X-Tokens Pallet的transfer函数将xcUNIT从Moonbase Alpha转移回其中继链上。为此,您需要使用Polkadot.js API

由于您将使用X-Tokens Pallet的transfer函数进行交互,您可以执行以下步骤来获取currencyIdamountdestdestWeightLimit的参数:

  1. 定义currencyId。对于外部XC-20,您将使用ForeignAsset货币类型和资产ID,在本示例中为42259045809535163221576417993425387648。对于本地XC-20,您将需要Token地址。在JavaScript中,将翻译为:

    const currencyId = { 
      ForeignAsset: { 
        ForeignAsset: 42259045809535163221576417993425387648n 
      } 
    };
    
    const currencyId = { Erc20: { contractAddress: 'INSERT_ERC_20_ADDRESS' } };
    
  2. 指定要转移的amount。在本示例中,您将发送1个xcUNIT,即有12位小数位:

    const amount = 1000000000000n;
    
  3. 定义目的地的multilocation,这将定位来自Moonbase Alpha的中继链上的账户。请注意,中继链可以接收的唯一资产是它自己的:

    const dest = { 
      V3: { 
        parents: 1, 
        interior: { X1: { AccountId32: { id: relayAccount } } } 
      } 
    };
    

    注意事项

    对于AccountId32AccountIndex64AccountKey20,您可以选择指定network参数。如果不指定,则默认为None

  4. destWeightLimit设置为Unlimited。在JavaScript中,您需要将Unlimited设置为null(如XcmV3WeightLimit的TypeScript接口中所述):

    const destWeightLimit = { Unlimited: null };
    

    注意事项

    如果您想限制目标权重,可以使用Limited来实现,这需要您输入refTimeproofSize的值。其中refTime是可用于执行的计算时间量,proofSize是可使用的存储量(以字节为单位)。

    在JavaScript中,这会转换为:

    { Limited: { refTime: 'INSERT_ALLOWED_AMOUNT', proofSize: 'INSERT_ALLOWED_AMOUNT' } };
    

现在您已经有了每个参数的值,您可以编写传送脚本了。 为此,您需要执行以下步骤:

  1. 提供调用的输入数据,这包含:
    • 用于创建提供商的Moonbase Alpha端点URL
    • transfer函数的每个参数值
  2. 创建Keyring实例,这将用于传送交易
  3. 创建Polkadot.js API提供商
  4. 使用currencyIdamountdestdestWeightLimit构建xTokens.transfer extrinsic
  5. 使用signAndSend extrinsic(函数)和第二步创建的Keyring实例发送交易

请记住

本教程仅用于演示目的,请勿将您的私钥存储在JavaScript文件中。

import { ApiPromise, WsProvider, Keyring } from '@polkadot/api'; // Version 9.13.6

// 1. Provide input data
const providerWsURL = 'wss://wss.api.moonbase.moonbeam.network';
const privateKey = 'INSERT_PRIVATE_KEY';
const relayAccount =
  '0xc4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a063'; // Alice's relay account address
const currencyId = { 
  ForeignAsset: { 
    ForeignAsset: 42259045809535163221576417993425387648n 
  }
};
const amount = 1000000000000n;
const dest = {
  V3: {
    parents: 1,
    interior: { X1: { AccountId32: { id: relayAccount } } },
  },
};
const destWeightLimit = { Unlimited: null };

// 2. Create Keyring instance
const keyring = new Keyring({ type: 'ethereum' });
const alice = keyring.addFromUri(privateKey);

const sendXc20 = async () => {
  // 3. Create Substrate API provider
  const substrateProvider = new WsProvider(providerWsURL);
  const api = await ApiPromise.create({ provider: substrateProvider });

  // 4. Craft the extrinsic
  const tx = api.tx.xTokens.transfer(currencyId, amount, dest, destWeightLimit);

  // 5. Send the transaction
  const txHash = await tx.signAndSend(alice);
  console.log(`Submitted with hash ${txHash}`);

  api.disconnect();
};

sendXc20();

注意事项

您可以使用以下编码的调用数据在Polkadot.js Apps上查看上述脚本的示例,该脚本将1个xcUNIT发送到中继链上Alice的账户:0x1e00018080778c30c20fa2ebc0ed18d2cbca1f0010a5d4e8000000000000000000 00000301010100c4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a06300

交易被处理后,中继链上的目标账户应该收到转账金额减去在目标链上执行XCM时扣除的一小笔费用。

X-Tokens转移MultiAsset(多种资产)函数

在本示例中,您将会构建一个XCM消息,通过X-Tokens Pallet的transferMultiasset函数将xcUNIT从Moonbase Alpha转移回其中继链上。

由于您将使用X-Tokens Pallet的transferMultiasset函数进行交互,您可以执行以下步骤来获取assetdestdestWeightLimit的参数:

  1. 定义asset的XCM资产multilocation,这将以Moonbase Alpha为起点的中继链中的UNIT Token为目标。每个链对自己的资产都有不同的看法。因此,您必须为每个目的地设置不同的资产multilocation

    // Multilocation for UNIT in the relay chain
    const asset = {
      V3: {
        id: {
          Concrete: {
            parents: 1,
            interior: null,
          },
        },
        fun: {
          Fungible: { Fungible: 1000000000000n }, // 1 token
        },
      },
    };
    
    // Multilocation for a local XC-20 on Moonbeam
    const asset = {
      V3: {
        id: {
          Concrete: {
            parents: 0,
            interior: {
              X2: [{ PalletInstance: 48 }, { AccountKey20: { key: 'INSERT_ERC_20_ADDRESS' } }],
            },
          },
        },
        fun: {
          Fungible: { Fungible: 1000000000000000000n }, // 1 token
        },
      },
    };
    
  2. 定义dest的XCM目的地multilocation,这将以Moonbase Alpha的中继链中的一个账户为起点:

    const dest = {
      V3: {
        parents: 1,
        interior: { X1: { AccountId32: { id: relayAccount } } },
      },
    };
    

    注意事项

    对于AccountId32AccountIndex64AccountKey20,您可以选择指定network参数。如果不指定,则默认为None

  3. 将目的地权重限制设置为Unlimited。在JavaScript中,您需要将Unlimited设置为null(如XcmV3WeightLimit的TypeScript接口中所述:

    const destWeightLimit = { Unlimited: null };
    

    注意事项

    如果您想限制目的地权重,可以使用Limited来实现,这需要您输入refTimeproofSize的值。其中refTime是可用于执行的计算时间量,proofSize是可使用的存储量(以字节为单位)。

    在JavaScript中,这会转换为:

    { Limited: { refTime: 'INSERT_ALLOWED_AMOUNT', proofSize: 'INSERT_ALLOWED_AMOUNT' } };
    

现在您已经有了每个参数的值,您可以编写转移脚本了。为此,您将执行以下步骤:

  1. 提供调用的输入数据,这包含:
    • 用于创建提供商的Moonbase Alpha端点URL
    • transferMultiasset函数的每个参数值
  2. 创建Keyring实例,这将用于传送交易
  3. 创建Polkadot.js API提供商
  4. 使用assetdestdestWeightLimit创建xTokens.transferMultiasset extrinsic
  5. 使用signAndSend extrinsic和第二步创建的Keyring实例发送交易

请记住

本教程仅用于演示目的,请勿将您的私钥存储在JavaScript文件中。

import { ApiPromise, WsProvider, Keyring } from '@polkadot/api'; // Version 9.13.6

// 1. Provide input data
const providerWsURL = 'wss://wss.api.moonbase.moonbeam.network';
const privateKey = 'INSERT_PRIVATE_KEY';
const relayAccount =
  '0xc4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a063'; // Alice's relay account address
const asset = {
  V3: {
    id: {
      Concrete: {
        parents: 1,
        interior: null,
      },
    },
    fun: {
      Fungible: { Fungible: 1000000000000n },
    },
  },
};
const dest = {
  V3: {
    parents: 1,
    interior: { X1: { AccountId32: { id: relayAccount } } },
  },
};
const destWeightLimit = { Unlimited: null };

// 2. Create Keyring instance
const keyring = new Keyring({ type: 'ethereum' });
const alice = keyring.addFromUri(privateKey);

const sendXc20 = async () => {
  // 3. Create Substrate API provider
  const substrateProvider = new WsProvider(providerWsURL);
  const api = await ApiPromise.create({ provider: substrateProvider });

  // 4. Craft the extrinsic
  const tx = api.tx.xTokens.transferMultiasset(asset, dest, destWeightLimit);

  // 5. Send the transaction
  const txHash = await tx.signAndSend(alice);
  console.log(`Submitted with hash ${txHash}`);

  api.disconnect();
};

sendXc20();

注意事项

您可以使用以下编码的调用数据在Polkadot.js Apps上查看上述脚本的示例,该脚本将1个xcUNIT发送到中继链上Alice的账户:0x1e010300010000070010a5d4e80301010100c4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a06300

交易被处理后,中继链上的目标账户应该收到转账金额减去在目标链上执行XCM时扣除的一小笔费用。

XC-Tokens预编译

X-Tokens预编译合约将会允许开发者通过基于Moonbeam网络的以太坊API访问XCM Token转移功能。如同其他预编译合约,X-Tokens预编译位于以下地址:

0x0000000000000000000000000000000000000804
0x0000000000000000000000000000000000000804
0x0000000000000000000000000000000000000804

注意事项

在Moonbeam使用预编译合约时,可能会出现一些意想不到的后果。 请参阅安全注意事项 页面了解更多信息。

X-Tokens Solidity接口

Xtokens.sol是一个开发者能够使用以太坊API与X-Tokens Pallet交互的接口。

此接口包含以下函数:

  • transfer(address currencyAddress, uint256 amount, Multilocation memory destination, uint64 weight) — 用于表示上述示例中提及的transfer函数。然而,在使用币种ID之外,您需要为currencyAddress提供资产地址:

    destination multilocation将会以一种特殊形式构建(我们将在下一部分提及)

  • transferMultiasset(Multilocation memory asset, uint256 amount, Multilocation memory destination, uint64 weight) — 用于表示上述示例中提及的transferMultiasset函数。两种multilocation将会以一种特殊形式构建(我们将在下一部分提及)

构建预编译Multilocation

在X-Tokens预编译接口中,Multilocation结构根据下列函数定义:

struct Multilocation {
    uint8 parents;
    bytes[] interior;
}

请注意每个multilocation皆有parents元素,请使用uint8和一组字节定义。Parents代表有多少“hops”在通过中继链中您需要执行的传递向上方向。作为uint8,您将会看到以下数值:

Origin Destination Parents Value
Parachain A Parachain A 0
Parachain A Relay Chain 1
Parachain A Parachain B 1

字节阵列(bytes[])定义了内部参数以及其在multilocation的内容。阵列的大小则根据以下定义interior数值:

Array Size Interior Value
[] 0 Here
[XYZ] 1 X1
[XYZ, ABC] 2 X2
[XYZ, ... N] N XN

注意事项

内部值Here通常用于中继链(作为目标链或以中继链资产为目标)。

假设所有字节阵列包含数据。所有元素的首个字节(2个十六进制数值)与XN部分的选择器相关,举例来说:

Byte Value Selector Data Type
0x00 Parachain bytes4
0x01 AccountId32 bytes32
0x02 AccountIndex64 u64
0x03 AccountKey20 bytes20
0x04 PalletInstance byte
0x05 GeneralIndex u128
0x06 GeneralKey bytes[]

接着,根据选择器及其数据类型,以下字节对应于提供的实际数据。请注意在Polkadot.js Apps示例中出现的AccountId32AccountIndex64AccountKey20network将会在最后添加。如下所示:

Selector Data Value Represents
Parachain "0x00+000007E7" Parachain ID 2023
AccountId32 "0x01+AccountId32+00" AccountId32, Network(Option) Null
AccountId32 "0x01+AccountId32+02" AccountId32, Network Polkadot
AccountKey20 "0x03+AccountKey20+00" AccountKey20, Network(Option) Null
PalletInstance "0x04+03" Pallet Instance 3

注意事项

interior数据通常需要使用引号包含。如果您未遵循此规则,您将会获得invalid tuple value错误。

以下代码片段介绍了Multilocation结构的一些示例,因为它们需要输入到X-Tokens预编译函数中:

// Multilocation targeting the relay chain or its asset from a parachain
{
    1, // parents = 1
    [] // interior = here
}

// Multilocation targeting Moonbase Alpha DEV token from another parachain
{
    1, // parents = 1
    // Size of array is 2, meaning is an X2 interior
    [
        '0x00000003E8', // Selector Parachain, ID = 1000 (Moonbase Alpha)
        '0x0403' // Pallet Instance = 3
    ]
}

// Multilocation targeting Alice's account on the relay chain from Moonbase Alpha
{
    1, // parents = 1
    // Size of array is 1, meaning is an X1 interior
    [
        '0x01c4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a06300' 
        // AccountKey32 Selector + Address in hex + Network(Option) Null
    ]
}

使用库与X-Tokens交互

使用库与以太坊API交互时,Multilocation结构可以像任何其他结构一样进行格式化。以下代码片段包括上述提及的X-Tokens转移函数X-Tokens多资产转移函数和Multilocation结构示例。您可以在Github上找到X-Tokens ABI

import ABI from './xtokensABI.js'; // Import the X-Tokens ABI
import { ethers } from 'ethers'; // Import Ethers library

const privateKey = 'INSERT_PRIVATE_KEY';

// Create Ethers provider and signer
const provider = new ethers.providers.JsonRpcProvider(
  'https://rpc.api.moonbase.moonbeam.network'
);
const signer = new ethers.Wallet(privateKey, provider);

// Create X-Tokens contract instance
const xTokens = new ethers.Contract(
  '0x0000000000000000000000000000000000000804',
  ABI,
  signer
);

// Arguments for the transfer function
const currencyAddress = '0xFfFFfFff1FcaCBd218EDc0EbA20Fc2308C778080'; // xcUNIT address
const amount = 1000000000000;
const destination = [
  // Target the relay chain from Moonbase Alpha
  1,
  // Target Alice's 32-byte relay chain account
  ['0x01c4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a06300'],
];
const weight = 304217000;

// Sends 1 xcUNIT to the relay chain using the transfer function
async function transferToAlice() {
  // Creates, signs, and sends the transfer transaction
  const transaction = await xTokens.transfer(
    currencyAddress,
    amount,
    destination,
    weight
  );

  // Waits for the transaction to be included in a block
  await transaction.wait();
  console.log(transaction);
}

transferToAlice();
import ABI from './xtokensABI.js'; // Import the X-Tokens ABI
import Web3 from 'web3'; // Import Web3 library

const privateKey = 'INSERT_PRIVATE_KEY';

// Create Web3 provider
const web3 = new Web3('https://rpc.api.moonbase.moonbeam.network'); // Change to network of choice

// Create contract instance
const xTokens = new web3.eth.Contract(
  ABI,
  '0x0000000000000000000000000000000000000804',
  { from: web3.eth.accounts.privateKeyToAccount(privateKey).address } // 'from' is necessary for gas estimation
);

// Arguments for the transfer function
const currencyAddress = '0xFfFFfFff1FcaCBd218EDc0EbA20Fc2308C778080'; // xcUNIT address
const amount = 1000000000000;
const destination = [
  // Target the relay chain from Moonbase Alpha
  1,
  // Target Alice's 32-byte relay chain account
  ['0x01c4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a06300'],
];
const weight = 304217000;

// Sends 1 xcUNIT to the relay chain using the transfer function
async function transferToAlice() {
  // Create transaction
  const transferTx = xTokens.methods.transfer(
    currencyAddress,
    amount,
    destination,
    weight
  );

  // Sign transaction
  const signedTx = await web3.eth.accounts.signTransaction(
    {
      to: '0x0000000000000000000000000000000000000804',
      data: transferTx.encodeABI(),
      gas: await transferTx.estimateGas(),
    },
    privateKey
  );

  // Send signed transaction
  const sendTx = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);
  console.log(sendTx);
}

transferToAlice();
from web3 import Web3

abi = "INSERT_XTOKENS_ABI"  # Paste or import the x-tokens ABI
private_key = "INSERT_PRIVATE_KEY"  # This is for demo purposes, never store your private key in plain text
address = "INSERT_ADDRESS"  # The wallet address that corresponds to your private key

# Create Web3 provider
web3 = Web3(Web3.HTTPProvider("https://rpc.api.moonbase.moonbeam.network"))

# Create contract instance
x_tokens = web3.eth.contract(
    address="0x0000000000000000000000000000000000000804", abi=abi
)

# Arguments for the transfer function
currencyAddress = "0xFfFFfFff1FcaCBd218EDc0EbA20Fc2308C778080" # xcUNIT address
amount = 1000000000000
destination = [
    # Target the relay chain from Moonbase Alpha
    1,
    # Target Alice's 32-byte relay chain account
    ["0x01c4db7bcb733e117c0b34ac96354b10d47e84a006b9e7e66a229d174e8ff2a06300"],
]
weight = 304217000

# Sends 1 xcUNIT to the relay chain using the transfer function
def transfer_to_alice():
    # Create transaction
    transferTx = x_tokens.functions.transfer(
        currencyAddress, amount, destination, weight
    ).build_transaction(
        {
            "from": address,
            "nonce": web3.eth.get_transaction_count(address),
        }
    )

    # Sign transaction
    signedTx = web3.eth.account.sign_transaction(transferTx, private_key)

    # Send tx and wait for receipt
    hash = web3.eth.send_raw_transaction(signedTx.rawTransaction)
    receipt = web3.eth.wait_for_transaction_receipt(hash)
    print(f"Tx successful with hash: { receipt.transactionHash.hex() }")


transfer_to_alice()

注意事项

要在Moonbeam或Moonriver上测试上述示例,您可以将RPC URL替换为您自己的端点和API密钥,您可以从支持的端点提供商中获取该密钥。

Last update: January 25, 2024
| Created: April 29, 2022