Polkadot.js API库¶
概览¶
Polkadot.js是一组工具,可让您与Polkadot及其平行链(例如Moonbeam)进行交互。Polkadot.js API是Polkadot.js集合的一个组件,它也是一个的=Javascript库。它能帮助开发者访问Moonbeam节点,与Substrate界面交互,让开发者能读取和写入数据到网络。
您可以使用Polkadot.js API查询链上数据并从Moonbeam的Substrate端发送extrinsic。您可以查询Moonbeam的runtime(运行时)常量、链状态、事件、交易(extrinsic)数据等。
在这篇文章中,你能找到Polkadot.js API库中可用功能的概述,以及一些使用Polkadot.js API库与Moonbeam网络交互的常用代码示例。
查看先决条件¶
在开始安装和使用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库。您可以通过运行以下命令将其安装至您的项目目录:
npm i @polkadot/api
yarn add @polkadot/api
该库还包括其他核心组件,如用于账户管理的Keyring,或一些别的在本指南中会使用到的其它组件。
创建API Provider实例¶
与以太坊API库相似,您必须先实例化一个Polkadot.js API的API实例。使用您想要交互的Moonbeam网络的websocket端点创建WsProvider
。
要在Moonbeam或Moonriver网络上测试本指南中的示例,您可以从受支持的网络端点提供商之一获取您自己的端点和API密钥。
// Import
import { ApiPromise, WsProvider } from '@polkadot/api';
const main = async () => {
// Construct API provider
const wsProvider = new WsProvider('INSERT_WSS_API_ENDPOINT');
const api = await ApiPromise.create({ provider: wsProvider });
// Code goes here
await api.disconnect();
}
main();
// Import
import { ApiPromise, WsProvider } from '@polkadot/api';
const main = async () => {
// Construct API provider
const wsProvider = new WsProvider('INSERT_WSS_API_ENDPOINT');
const api = await ApiPromise.create({ provider: wsProvider });
// Code goes here
await api.disconnect();
}
main();
// Import
import { ApiPromise, WsProvider } from '@polkadot/api';
const main = async () => {
// Construct API provider
const wsProvider = new WsProvider('wss://wss.api.moonbase.moonbeam.network');
const api = await ApiPromise.create({ provider: wsProvider });
// Code goes here
await api.disconnect();
}
main();
// Import
import { ApiPromise, WsProvider } from '@polkadot/api';
const main = async () => {
// Construct API provider
const wsProvider = new WsProvider('ws://127.0.0.1:9944');
const api = await ApiPromise.create({ provider: wsProvider });
// Code goes here
await api.disconnect();
}
main();
元数据和动态API修饰¶
在深入了解通过Polkadot.js API库执行不同任务的细节之前,您需要先了库的一些基本运作原理。
当Polkadot.js API连接至节点时,首要做的一件事是检索元数据并根据元数据信息修饰API。元数据有效地以
api.<type>.<module>.<section>
形式提供数据。
这些数据适合以下<type>
类别之一
query
- 获取state信息的端口tx
- 或许交易的端口rpc
- 用来发送RPC请求的端口consts
- 用来获取runtime常数的端口
因此,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)
来查看所有可用端点的列表:
console.log(api.query);
假设您已成功初始化API, 这里是一个获取给定地址的基本账户信息的代码示例::
// Define wallet address
const addr = 'INSERT_ADDRESS';
// 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}`
);
// 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.query
、api.tx
和api.derive
只是包装RPC调用,以节点预期的编码格式提供信息。您可以通过检查api.rpc
对象来查看所有可用端点的列表:
console.log(api.rpc);
api.rpc
接口遵循api.query
类似的格式,例如:
// 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
API也提供了订阅(subscription)端口. RPC支持使用订阅函数。您可以通过修改先前的示例,使其开始使用订阅函数来监听新的区块。请注意在复用示例代码时,您需要删除API disconnect部分, 否则WSS连接将会关闭.
// 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}`
);
});
// Remove await api.disconnect()!
api.rpc.subscribe*
函数的基本模式是将回调传递给订阅函数,这将在每个新条目被导入时触发。
其他在api.query.*
下的调用可以通过类似的方式修改以使用订阅函数,包括具有参数的调用。以下是一个如何订阅账户余额变化的示例:
// 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}`
);
});
// Remove await 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';
// 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 alice = keyringECDSA.addFromUri(`${mnemonic}/${ethDerPath}`);
console.log(`Ethereum Derivation Path: ${ethDerPath}`);
console.log(`Derived Ethereum Address from Mnemonic: ${alice.address}`);
// Extract private key from mnemonic
const privateKey = u8aToHex(
hdEthereum(mnemonicToLegacySeed(mnemonic, '', false, 64), ethDerPath)
.secretKey
);
console.log(`Derived Private Key from Mnemonic: ${privateKey}`);
// Import the required packages
import Keyring from '@polkadot/keyring';
// Import Ethereum account from mnemonic
const keyringECDSA = new Keyring({ type: 'ethereum' });
const privateKeyInput = 'INSERT_PK';
// Extract address from private key
const alice = keyringECDSA.addFromUri(privateKeyInput);
console.log(`Derived Address from provided Private Key: ${alice.address}`);
在Moonbeam上发送交易¶
交易端口通常为 api.tx.<module>.<method>
形式, 其中模块和方法包装都是通过元数据生成的。这允许您提交事务使其包含在区块中,如传送、部署合约、与Pallet交互或者Moonbeam支持的其他内容等。您可以通过访问api.tx
对象来查看所有可用端点的列表,例如:
console.log(api.tx);
发送交易¶
Polkadot.js API可用于向网络发送交易。假设您已初始化了 API: 和一个 keyring instance,您可以使用以下代码段发送一个基本交易(此代码示例还将返回交易calldata与成功提交后的交易哈希):
// 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, 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}`);
查看完整脚本
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)中的事务。
交易费信息¶
transaction端点还提供了一个根据给定 api.tx.<module>.<method>
获取权重的方法. 您需要在使用特定module
和method
构建完整个交易之后使用paymentInfo
函数。.
paymetnInfo
函数以refTime
and proofSize
的形式返回权重信息, 并以此来计算交易费用. 这在通过 XCM 进行远程执行调用时非常有用.
假设您已成功初始化API,以下代码片段展示了如何获取一个简单转账交易的weight信息:
// Transaction to get weight information
const tx = api.tx.balances.transfer('INSERT_BOBS_ADDRESS', BigInt(12345));
// Get weight info
const { partialFee, weight } = await tx.paymentInfo('INSERT_SENDERS_ADDRESS');
console.log(`Transaction weight: ${weight}`);
console.log(`Transaction fee: ${partialFee.toHuman()}`);
查看完整文件
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 });
// Transaction to get weight information
const tx = api.tx.balances.transfer('INSERT_BOBS_ADDRESS', BigInt(12345));
// Get weight info
const { partialFee, weight } = await tx.paymentInfo('INSERT_SENDERS_ADDRESS');
console.log(`Transaction weight: ${weight}`);
console.log(`Transaction fee: ${partialFee.toHuman()}`);
// Disconnect the API
await api.disconnect();
};
main();
交易事件¶
任何事务将会发出事件,无论如何这将始终为特定事务发送system.ExtrinsicSuccess
或system.ExtrinsicFailed
事件。这些为事务提供整体执行结果,即执行成功或失败。
根据发送的事务,可能还会发出一些其他事件,例如余额转账事件,这其中可能包括一个或多个balance.Transfer
事件。
转账API页面包含一个示例代码片段,用于订阅新的最终区块头并检索所有balance.Transfer
事件。
批处理事务¶
Polkadot.js API允许通过api.tx.utility.batch
函数批处理事务。这些批处理事务从同一个发送者按顺序依次处理。事务处理费可以使用paymentInfo
函数来计算。假设您已成功初始化API,取得了keyring 和 账户,以下示例进行了多笔转账,同时也使用api.tx.parachainStaking
模块来发起请求以减少特定候选收集人的绑定量:
// Construct a list of transactions to batch
const collator = 'INSERT_COLLATORS_ADDRESS';
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 API here!
}
});
查看完整脚本
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端点列表。
-
- 接口 -
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": [] }'
- 接口 -
-
- 接口 -
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"] }'
- 接口 -
-
- 接口
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存储库页面以获取其中的可用方法列表库及其相关文档。
| Created: May 10, 2022