Skip to content

Polkadot.js API库

概览

Polkadot.js API库允许应用程序开发者查询Moonbeam节点并使用JavaScript与节点的Polkadot或Substrate接口交互。在本教程中您将找到可用功能的概述和一些常用的代码示例,助您快速使用Polkadot.js API库与Moonbeam网络交互。

什么是Polkadot.js?

Polkadot.js是一组工具,可让您与Polkadot及其平行链(例如Moonbeam)进行交互。Polkadot.js API是Polkadot.js集合的一个组件,是一种JavaScript API,允许您与Moonbeam节点交互以读取和写入数据到网络。

您可以使用Polkadot.js API查询链上数据并从Moonbeam的Substrate端发送extrinsic。您可以查询Moonbeam的runtime(运行时)常量、链状态、事件、交易(extrinsic)数据等。

查看先决条件

在开始安装和使用Polkadot.js API库之前,您需要先安装Node.js。

本教程操作需安装Node.js(这个示例将使用v16.x)和「npm package manager」。您可通过Node.js下载或自行运行以下代码完成安装

curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash -

sudo apt install -y nodejs
# You can use homebrew (https://docs.brew.sh/Installation)
brew install node

# Or you can use nvm (https://github.com/nvm-sh/nvm)
nvm install node

您可以通过请求每个安装包的版本来验证是否安装正确:

node -v
npm -v

要在Moonbeam或Moonriver网络上测试本指南中的示例,您可以从受支持的网络端点提供商之一获取您自己的端点和API密钥。

安装Polkadot.js API库

首先,您需要通过应用程序包管理器(如yarn)为您的项目安装Polkadot.js API库。您可以通过运行以下命令将其安装至您的项目目录:

yarn add @polkadot/api

创建API Provider实例

以太坊API库相似,您必须先实例化一个Polkadot.js API的API实例。使用您想要交互的Moonbeam网络的websocket端点创建WsProvider

要在Moonbeam或Moonriver网络上测试本指南中的示例,您可以从受支持的网络端点提供商之一获取您自己的端点和API密钥。

// Import
import { ApiPromise, WsProvider } from '@polkadot/api';

// Construct API provider
const wsProvider = new WsProvider('INSERT_WSS_API_ENDPOINT');
const api = await ApiPromise.create({ provider: wsProvider });
// Import
import { ApiPromise, WsProvider } from '@polkadot/api';

// Construct API provider
const wsProvider = new WsProvider('INSERT_WSS_API_ENDPOINT');
const api = await ApiPromise.create({ provider: wsProvider });
// Import
import { ApiPromise, WsProvider } from '@polkadot/api';

// Construct API provider
const wsProvider = new WsProvider('wss://wss.api.moonbase.moonbeam.network');
const api = await ApiPromise.create({ provider: wsProvider });
// Import
import { ApiPromise, WsProvider } from '@polkadot/api';

// Construct API provider
const wsProvider = new WsProvider('ws://127.0.0.1:9944');
const api = await ApiPromise.create({ provider: wsProvider });

元数据和动态API修饰

在深入了解通过Polkadot.js API库执行不同任务的细节之前,您需要先了库的一些基本运作原理。

当Polkadot.js API连接至节点时,首要做的一件事是检索元数据并根据元数据信息修饰API。元数据有效地以api.<type>.<module>.<section>形式提供数据,这些数据适合以下<type>类别之一:constsquerytx

因此,api.{consts, query, tx}.<module>.<method>端点中包含的所有信息都不是硬编码在API中的。这将允许如Moonbeam这样的平行链通过其Pallet自定义端点,这些端点可以通过Polkadot.js API库直接访问。

查询Moonbeam的链上数据

在这一部分,您将学习如何使用Polkadot.js API库查询链上信息。

Moonbeam链状态查询

这个类别的查询将检索与链当前状态的相关信息。这些端点通常采用api.query.<module>.<method>形式,其中模块和函数修饰是通过元数据生成。您可以通过检查api.query对象、通过console.log(api.query)或其他方式查看所有可用端点的列表。

以下是一个代码示例,用于检索给定地址的基本账户信息:

// Initialize the API provider as in the previous section
...

// Define wallet address
const addr = 'MOONBEAM-WALLET-ADDRESS-HERE';

// Retrieve the last timestamp
const now = await api.query.timestamp.now();

// Retrieve the account balance & current nonce via the system module
const { nonce, data: balance } = await api.query.system.account(addr);

// Retrieve the given account's next index/nonce, taking txs in the pool into account
const nextNonce = await api.rpc.system.accountNextIndex(addr);

console.log(`${now}: balance of ${balance.free} and a current nonce of ${nonce} and next nonce of ${nextNonce}`);

// Disconnect the API
api.disconnect();
查看完整脚本
import { ApiPromise, WsProvider } from '@polkadot/api';

const main = async () => {
  // Construct API provider
  const wsProvider = new WsProvider('INSERT_WSS_ENDPOINT');
  const api = await ApiPromise.create({ provider: wsProvider });

  // Define wallet address
  const addr = 'INSERT_ADDRESS';

  // Retrieve the last timestamp via the timestamp module
  const now = await api.query.timestamp.now();

  // Retrieve the account balance & current nonce via the system module
  const { nonce, data: balance } = await api.query.system.account(addr);

  console.log(
    `${now}: balance of ${balance.free} and a current nonce of ${nonce}`
  );

  // Disconnect the API
  await api.disconnect();
};

main();

Moonbeam RPC查询

RPC调用为与节点之间的数据传输提供了骨干网。这意味着所有API端点,如api.queryapi.txapi.derive只是包装RPC调用,以节点预期的编码格式提供信息。

api.rpc接口遵循api.query类似的格式,例如:

// Initialize the API provider as in the previous section
...

// Retrieve the chain name
const chain = await api.rpc.system.chain();

// Retrieve the latest header
const lastHeader = await api.rpc.chain.getHeader();

// Log the information
console.log(`${chain}: last block #${lastHeader.number} has hash ${lastHeader.hash}`);

// Disconnect the API
api.disconnect();
查看完整脚本
import { ApiPromise, WsProvider } from '@polkadot/api';

const main = async () => {
  // Construct API provider
  const wsProvider = new WsProvider('INSERT_WSS_ENDPOINT');
  const api = await ApiPromise.create({ provider: wsProvider });

  // Retrieve the chain name
  const chain = await api.rpc.system.chain();

  // Retrieve the latest header
  const lastHeader = await api.rpc.chain.getHeader();

  // Log the information
  console.log(
    `${chain}: last block #${lastHeader.number} has hash ${lastHeader.hash}`
  );

  // Disconnect the API
  await api.disconnect();
};

main();

查询订阅

RPC支持使用订阅函数。您可以修改先前的示例,使其开始使用订阅函数来监听新的区块。

// Initialize the API provider as in the previous section
...

// Retrieve the chain name
const chain = await api.rpc.system.chain();

// Subscribe to the new headers
await api.rpc.chain.subscribeNewHeads((lastHeader) => {
  console.log(`${chain}: last block #${lastHeader.number} has hash ${lastHeader.hash}`);
});

// Disconnect the API
api.disconnect();

api.rpc.subscribe*函数的基本模式是将回调传递给订阅函数,这将在每个新条目被导入时触发。

其他在api.query.*下的调用可以通过类似的方式修改以使用订阅函数,包括具有参数的调用。以下是一个如何订阅账户余额变化的示例:

// Initialize the API provider as in the previous section
...

// Define wallet address
const addr = 'MOONBEAM-WALLET-ADDRESS-HERE';

// Subscribe to balance changes for a specified account
await api.query.system.account(addr, ({ nonce, data: balance }) => {
  console.log(`free balance is ${balance.free} with ${balance.reserved} reserved and a nonce of ${nonce}`);
});

// Disconnect the API
api.disconnect();
查看完整脚本
import { ApiPromise, WsProvider } from '@polkadot/api';

const main = async () => {
  // Construct API provider
  const wsProvider = new WsProvider('INSERT_WSS_ENDPOINT');
  const api = await ApiPromise.create({ provider: wsProvider });

  // Retrieve the chain name
  const chain = await api.rpc.system.chain();

  // Subscribe to the new headers
  await api.rpc.chain.subscribeNewHeads((lastHeader) => {
    console.log(
      `${chain}: last block #${lastHeader.number} has hash ${lastHeader.hash}`
    );
  });

  // Define wallet address
  const addr = 'INSERT_ADDRESS';

  // Subscribe to balance changes for a specified account
  await api.query.system.account(addr, ({ nonce, data: balance }) => {
    console.log(
      `free balance is ${balance.free} with ${balance.reserved} reserved and a nonce of ${nonce}`
    );

    // Handle API disconnect here if needed
  });
};

main();

为Moonbeam账户创建Keyring

Keyring用于维持密钥对以及任何数据的签署,无论是传送、消息或者合约交互。

创建Keyring实例

您可以通过创建Keyring级别的实例并指定使用的默认钱包地址类型来创建一个实例。对于Moonbeam网络,其默认钱包类型为ethereum

// Import the keyring as required
import { Keyring } from '@polkadot/api';

// Create a keyring instance
const keyring = new Keyring({ type: 'ethereum' });

添加账户到Keyring

将账户添加至keyring实例有多种方式,包括通过助记词和短格式密钥。以下范例代码将为您提供一些示例:

// Import the required packages
import Keyring from '@polkadot/keyring';
import { u8aToHex } from '@polkadot/util';
import { mnemonicToLegacySeed, hdEthereum } from '@polkadot/util-crypto';

// Import Ethereum account from mnemonic
const keyringECDSA = new Keyring({ type: 'ethereum' });
const mnemonic = 'INSERT_MNEMONIC';
const privateKeyInput = 'INSERT_PK';

// Define index of the derivation path and the derivation path
const index = 0;
const ethDerPath = "m/44'/60'/0'/0/" + index;
console.log(`Mnemonic: ${mnemonic}`);
console.log(`--------------------------\n`);

// Extract Ethereum address from mnemonic
const newPairEth = keyringECDSA.addFromUri(`${mnemonic}/${ethDerPath}`);
console.log(`Ethereum Derivation Path: ${ethDerPath}`);
console.log(`Derived Ethereum Address from Mnemonic: ${newPairEth.address}`);

// Extract private key from mnemonic
const privateKey = u8aToHex(
  hdEthereum(mnemonicToLegacySeed(mnemonic, '', false, 64), ethDerPath)
    .secretKey
);
console.log(`Derived Private Key from Mnemonic: ${privateKey}`);
console.log(`--------------------------\n`);

// Extract address from private key
const otherPair = keyringECDSA.addFromUri(privateKeyInput);
console.log(`Derived Address from provided Private Key: ${otherPair.address}`);

通过Polkadot.js API在Moonbeam上发送交易

由元数据确定的事务端点在api.tx端点上公开显示。这允许您提交事务使其包含在区块中,如传送、部署合约、与Pallet交互或者Moonbeam支持的其他内容等。

发送交易

以下是发送基本交易的示例。此代码示例还将检索交易的编码调用数据,以及交易哈希。

// Initialize the API provider as in the previous section
...

// Initialize the keyring instance as in the previous section
...

// Initialize wallet key pairs
const alice = keyring.addFromUri('ALICE-ACCOUNT-PRIVATE-KEY');
const bob = 'BOB-ACCOUNT-PUBLIC-KEY';

// Form the transaction
const tx = await api.tx.balances
  .transfer(bob, 12345n)

// Retrieve the encoded calldata of the transaction
const encodedCalldata = tx.method.toHex()
console.log(encodedCallData)

// Sign and send the transaction
const txHash = await tx
    .signAndSend(alice);

// Show the transaction hash
console.log(`Submitted with hash ${txHash}`);

// Disconnect the API
api.disconnect();
查看完整脚本
import { ApiPromise, WsProvider } from '@polkadot/api';
import Keyring from '@polkadot/keyring';

const main = async () => {
  // Construct API provider
  const wsProvider = new WsProvider('INSERT_WSS_ENDPOINT');
  const api = await ApiPromise.create({ provider: wsProvider });

  // Create a keyring instance (ECDSA)
  const keyring = new Keyring({ type: 'ethereum' });

  // Initialize wallet key pairs
  const alice = keyring.addFromUri('INSERT_ALICES_PRIVATE_KEY');
  const bob = 'INSERT_BOBS_ADDRESS';

  // Form the transaction
  const tx = await api.tx.balances.transfer(bob, BigInt(12345));

  // Retrieve the encoded calldata of the transaction
  const encodedCalldata = tx.method.toHex();
  console.log(`Encoded calldata: ${encodedCalldata}`);

  // Sign and send the transaction
  const txHash = await tx.signAndSend(alice);

  // Show the transaction hash
  console.log(`Submitted with hash ${txHash}`);

  // Disconnect the API
  await api.disconnect();
};

main();

请注意signAndSend函数也可以接受如nonce等可选参数。例如,signAndSend(alice, { nonce: aliceNonce })。您可以使用状态查询的示例代码 来获取正确数据,包括内存池(mempool)中的事务。

事务事件

任何事务将会发出事件,无论如何这将始终为特定事务发送system.ExtrinsicSuccesssystem.ExtrinsicFailed事件。这些为事务提供整体执行结果,即执行成功或失败。

根据发送的事务,可能还会发出一些其他事件,例如余额转账事件,这其中可能包括一个或多个balance.Transfer事件。

转账API页面包含一个示例代码片段,用于订阅新的最终区块头并检索所有balance.Transfer事件。

批处理事务

Polkadot.js API允许通过api.tx.utility.batch函数批处理事务。这些批处理事务从同一个发送者按顺序依次处理。事务处理费可以使用paymentInfo函数来计算。以下示例进行了多笔转账,同时也使用api.tx.parachainStaking模块来发起请求以减少特定候选收集人的绑定量:

// Initialize the API provider as in the previous section
...

// Initialize the keyring instance as in the previous section
...

// Initialize wallet key pairs as in the previous section
...

// Construct a list of transactions to batch
const collator = 'COLLATOR-ACCOUNT-PUBLIC-KEY';
const txs = [
  api.tx.balances.transfer(bob, 12345n),
  api.tx.balances.transfer(charlie, 12345n),
  api.tx.parachainStaking.scheduleDelegatorBondLess(collator, 12345n)
];

// Estimate the fees as RuntimeDispatchInfo, using the signer (either
// address or locked/unlocked keypair) 
const info = await api.tx.utility
  .batch(txs)
  .paymentInfo(alice);

console.log(`estimated fees: ${info}`);

// Construct the batch and send the transactions
api.tx.utility
  .batch(txs)
  .signAndSend(alice, ({ status }) => {
    if (status.isInBlock) {
      console.log(`included in ${status.asInBlock}`);
    }
  });

// Disconnect the API
api.disconnect();
查看完整脚本
import { ApiPromise, WsProvider } from '@polkadot/api';
import Keyring from '@polkadot/keyring';

const main = async () => {
  // Construct API provider
  const wsProvider = new WsProvider('INSERT_WSS_ENDPOINT');
  const api = await ApiPromise.create({ provider: wsProvider });

  // Create a keyring instance (ECDSA)
  const keyring = new Keyring({ type: 'ethereum' });

  // Initialize wallet key pairs
  const alice = keyring.addFromUri('INSERT_ALICES_PRIVATE_KEY');

  // Construct a list of transactions to batch
  const collator = 'INSERT_COLLATORS_ADDRESS';
  const txs = [
    api.tx.balances.transfer('INSERT_BOBS_ADDRESS', BigInt(12345)),
    api.tx.balances.transfer('INSERT_CHARLEYS_ADDRESS', BigInt(12345)),
    api.tx.parachainStaking.scheduleDelegatorBondLess(collator, BigInt(12345)),
  ];

  // Estimate the fees as RuntimeDispatchInfo, using the signer (either
  // address or locked/unlocked keypair)
  const info = await api.tx.utility.batch(txs).paymentInfo(alice);

  console.log(`Estimated fees: ${info}`);

  // Construct the batch and send the transactions
  api.tx.utility.batch(txs).signAndSend(alice, async ({ status }) => {
    if (status.isInBlock) {
      console.log(`Included in ${status.asInBlock}`);

      // Disconnect the API
      await api.disconnect();
    }
  });
};

main();

注意事项

您可以通过添加console.log(api.tx.parachainStaking);到代码,查看parachainStaking模块的全部可用功能。

Substrate和自定义RPC请求

RPC作为函数在特定模块公开显示。这意味着一旦可使用后,您可以通过api.rpc.<module>.<method>(...params[])调用任意RPC。这也同样适用于以polkadotApi.rpc.eth.*形式使用Polkadot.js API访问以太坊RPC。

一些通过Polkadot.js API可用的方法也可以通过Moonbeam节点上的JSON-RPC端点调用。本节将提供一些示例;您可以通过调用 api.rpc.rpc.methods() 或下面列出的 rpc_methods 端点来查看公开的RPC端点列表。

  • methods()

    • 接口 - api.rpc.rpc.methods
    • JSON-RPC - rpc_methods
    • 返回 - 节点公开的RPC方法列表
      curl --location --request POST 'https://rpc.api.moonbase.moonbeam.network' \
      --header 'Content-Type: application/json' \
      --data-raw '{
        "jsonrpc":"2.0",
        "id":1,
        "method":"rpc_methods",
        "params": []
      }'
    
  • getBlock(hash?: BlockHash)

    • 接口 - api.rpc.chain.getBlock
    • JSON-RPC - chain_getBlock
    • 返回 - 由区块哈希参数指定的区块header和body
      curl --location --request POST 'https://rpc.api.moonbase.moonbeam.network' \
      --header 'Content-Type: application/json' \
      --data-raw '{
        "jsonrpc":"2.0",
        "id":1,
        "method":"chain_getBlock",
        "params": ["0x870ad0935a27ed8684048860ffb341d469e091abc2518ea109b4d26b8c88dd96"]
      }'
    
  • getFinalizedHead()

    • 接口 api.rpc.chain.getFinalizedHead
    • JSON-RPC chain_getFinalizedHead
    • 返回 最新最终确定区块的区块哈希
      curl --location --request POST 'https://rpc.api.moonbase.moonbeam.network' \
      --header 'Content-Type: application/json' \
      --data-raw '{
        "jsonrpc":"2.0",
        "id":1,
        "method":"chain_getHeader",
        "params": []
      }'
    

共识和确定性页面提供了使用自定义RPC调用来检查交易确定性的示例。

Polkadot.js API实用工具方法

Polkadot.js API 还包括许多实用程序库,用于计算常用的加密原语和哈希函数。

以下示例通过首先计算其RLP(递归长度前缀)编码,然后使用keccak256对结果进行哈希来预先计算Legacy类型以太坊交易的交易哈希。

import { encode } from '@polkadot/util-rlp';
import { keccakAsHex } from '@polkadot/util-crypto';
import { numberToHex } from '@polkadot/util';

// Define the raw signed transaction
const txData = {
    nonce: numberToHex(1),
    gasPrice: numberToHex(21000000000),
    gasLimit: numberToHex(21000),
    to: '0xc390cC49a32736a58733Cf46bE42f734dD4f53cb',
    value: numberToHex(1000000000000000000),
    data: '',
    v: "0507",
    r: "0x5ab2f48bdc6752191440ce62088b9e42f20215ee4305403579aa2e1eba615ce8",
    s: "0x3b172e53874422756d48b449438407e5478c985680d4aaa39d762fe0d1a11683"
}

// Extract the values to an array
var txDataArray = Object.keys(txData)
    .map(function (key) {
        return txData[key];
    });

// Calculate the RLP encoded transaction
var encoded_tx = encode(txDataArray)

// Hash the encoded transaction using keccak256
console.log(keccakAsHex(encoded_tx))

您可以查看相应的NPM存储库页面以获取其中的可用方法列表库及其相关文档。

本网站的所有信息由第三方提供,仅供参考之用。Moonbeam文档网站(https://docs.moonbeam.network/)上列出和描述的任何项目与Moonbeam立场无关。Moonbeam Foundation不保证网站信息的准确性、完整性或真实性。如使用或依赖本网站信息,需自行承担相关风险,Moonbeam Foundation不承担任何责任和义务。这些材料的所有陈述和/或意见由提供方个人或实体负责,与Moonbeam Foundation立场无关,概不构成任何投资建议。对于任何特定事项或情况,应寻求专业权威人士的建议。此处的信息可能会包含或链接至第三方提供的信息与/或第三方服务(包括任何第三方网站等)。这类链接网站不受Moonbeam Foundation控制。Moonbeam Foundation对此类链接网站的内容(包括此类链接网站上包含的任何信息或资料)概不负责也不认可。这些链接内容仅为方便访客而提供,Moonbeam Foundation对因您使用此信息或任何第三方网站或服务提供的信息而产生的所有责任概不负责。
Last update: September 28, 2023
| Created: May 10, 2022