秘籍
原文:https://docs.elrond.com/sdk-and-tools/erdjs/erdjs-cookbook
本页将引导您完成使用erdj处理常见任务的过程。
重要
这本秘籍利用了erdjs 10
。为了从erdjs 9.x
迁移到erdjs 10
,请遵循迁移指南。
创建网络提供商
创建 API 提供程序:
import { ApiNetworkProvider } from "@elrondnetwork/erdjs-network-providers";
let networkProvider = new ApiNetworkProvider("https://devnet-api.elrond.com");
创建代理提供程序:
import { ProxyNetworkProvider } from "@elrondnetwork/erdjs-network-providers";
let networkProvider = new ProxyNetworkProvider("https://devnet-gateway.elrond.com");
重要
仅使用@elrondnetwork/erdjs-network-providers
中的类作为起点。随着你的 dApp 成熟,确保你切换到使用你自己的网络提供商,根据你的需求定制(无论是从默认的衍生还是从头编写一个新的),直接与Elrond API(或网关)交互。
关于这个话题,请看扩展 erdjs 。
抓取网络参数
let networkConfig = await networkProvider.getNetworkConfig();
console.log(networkConfig.MinGasPrice);
console.log(networkConfig.ChainID);
处理账目
同步账户对象
下面的代码片段(从网络)获取账户的随机数和余额,并更新账户的本地表示。
let addressOfAlice = new Address("erd1...");
let alice = new Account(addressOfAlice);
let aliceOnNetwork = await networkProvider.getAccount(addressOfAlice);
alice.update(aliceOnNetwork);
console.log(alice.nonce);
console.log(alice.balance);
本地管理发送方随机数
当发送一批交易时,您通常必须首先从网络获取账户随机数(见上文),然后在本地管理它(例如,在签署和广播交易时增加):
alice.incrementNonce();
或者,您也可以使用:
transaction.setNonce(alice.getNonceThenIncrement());
如需进一步参考,请参见随机数管理。
准备支付对象
重要
在 erdjs 9x 中,使用类Balance
和BalanceBuilder
准备支付。在 erdjs 10 中,我们使用TokenPayment
。
用于 EGLD 转账的TokenPayment
对象(价值变动):
let firstPayment = TokenPayment.egldFromAmount("1.5");
let secondPayment = TokenPayment.egldFromBigInteger("1500000000000000000");
console.log(firstPayment.valueOf(), secondPayment.valueOf());
console.log(firstPayment.toPrettyString(), secondPayment.toPrettyString());
用于转移可替换的代币的TokenPayment
对象:
let identifier = "FOO-123456";
let numDecimals = 2;
let firstPayment = TokenPayment.fungibleFromAmount(identifier, "1.5", numDecimals);
let secondPayment = TokenPayment.fungibleFromBigInteger(identifier, "4000", numDecimals);
console.log(firstPayment.toString()); // Will output: 150.
console.log(firstPayment.toPrettyString()); // Will output: 1.50 FOO-123456.
console.log(secondPayment.toString()); // Will output: 4000.
console.log(secondPayment.toPrettyString()); // Will output: 40.00 FOO-123456.
用于转移半可替代代币的TokenPayment
对象:
let nonce = 3;
let quantity = 50;
let payment = TokenPayment.semiFungible(identifier, nonce, quantity);
用于转移不可替代的代币的TokenPayment
对象(数量不需要为 NFTs 指定,因为代币只是同类代币中的一种):
let nonce = 7;
let payment = TokenPayment.nonFungible(identifier, nonce);
用于传送元 esdt 代币的TokenPayment
对象:
let payment = TokenPayment.metaEsdtFromAmount(identifier, nonce, "0.1", numDecimals);
广播交易
准备简单的交易
let tx = new Transaction({
data: new TransactionPayload("helloWorld"),
gasLimit: 70000,
receiver: new Address("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"),
value: TokenPayment.egldFromAmount(1),
chainID: "D"
});
使用网络提供商广播
let txHash = await networkProvider.sendTransaction(tx);
请注意,在广播交易之前,需要对其进行签名。签名可以使用签名提供程序来实现。
重要
注意,出于各种目的,我们建议使用 dapp-core 而不是自己集成签名供应商。
广播使用axios
let data = tx.toSendable();
let url = "https://devnet-api.elrond.com/transactions";
let response = await axios.post(url, data, {
headers: {
"Content-Type": "application/json",
},
});
let txHash = response.data.txHash;
等待交易完成
let watcher = new TransactionWatcher(networkProvider);
let transactionOnNetwork = await watcher.awaitCompleted(tx);
如果只有txHash
可用,则:
let transactionOnNetwork = await watcher.awaitCompleted({ getHash: () => txHash });
console.log(transactionOnNetwork);
为了等待多个交易:
await Promise.all([watcher.awaitCompleted(tx1), watcher.awaitCompleted(tx2), watcher.awaitCompleted(tx3)]);
对于不同的等待策略,也可参见扩展 erdjs 。
代币转账
单 ESDT 转移
let payment = TokenPayment.fungibleFromAmount("COUNTER-8b028f", "100.00", 0);
let data = new ESDTTransferPayloadBuilder()
.setPayment(payment)
.build();
transactions.push(new Transaction({
nonce: 7,
receiver: new Address("erd1..."),
data: data,
gasLimit: 50000 + 1500 * data.length() + 300000,
chainID: "D"
}));
单 NFT 转移
let payment = TokenPayment.nonFungible("ERDJS-38f249", 1);
let payload = new ESDTNFTTransferPayloadBuilder()
.setPayment(payment)
.setDestination(new Address("erd1..."))
.build();
transactions.push(new Transaction({
nonce: 7,
// Same as sender address!
receiver: new Address("erd1..."),
data: data,
gasLimit: 50000 + 1500 * data.length() + 1000000,
chainID: "D"
}));
单 SFT 转移
let payment = TokenPayment.semiFungible("SEMI-9efd0f", 1, 5);
let payload = new ESDTNFTTransferPayloadBuilder()
.setPayment(payment)
.setDestination(new Address("erd1..."))
.build();
transactions.push(new Transaction({
nonce: 7,
// Same as sender address!
receiver: new Address("erd1..."),
data: data,
gasLimit: 50000 + 1500 * data.length() + 1000000,
chainID: "D"
}));
多 ESDT / NFT 转移
let paymentOne = TokenPayment.nonFungible("ERDJS-38f249", 1);
let paymentTwo = TokenPayment.fungibleFromAmount("BAR-c80d29", "10.00", 18);
let payments = [paymentOne, paymentTwo];
let payload = new MultiESDTNFTTransferPayloadBuilder()
.setPayments(payments)
.setDestination(new Address("erd1..."))
.build();
transactions.push(new Transaction({
nonce: 7,
// Same as sender address!
receiver: new Address("erd1..."),
data: data,
gasLimit: 50000 + 1500 * data.length() + 1000000 * payments.length,
chainID: "D"
}));
合约部署
从文件中加载字节码
import { Code } from "@elrondnetwork/erdjs";
import { promises } from "fs";
let buffer: Buffer = await promises.readFile(file);
let code = Code.fromBuffer(buffer);
从 URL 加载字节码
import axios, { AxiosResponse } from "axios";
let response: AxiosResponse<ArrayBuffer> = await axios.get("https://.../myContract.wasm", {
responseType: "arraybuffer",
transformResponse: [],
headers: {
"Accept": "application/wasm"
}
});
let buffer = Buffer.from(response.data);
let code = Code.fromBuffer(buffer);
执行合约部署
创建一个SmartContract
对象:
let contract = new SmartContract();
准备部署交易:
let transaction = contract.deploy({
code: code,
codeMetadata: new CodeMetadata(/* set the parameters accordingly */),
initArguments: [/* set the initial arguments, if any */],
gasLimit: 20000000,
chainID: "D"
});
然后,设置交易随机数。
请注意,帐户随机数必须事先同步。此外,在本地增加部署者的 nonce(可选)。
transaction.setNonce(deployer.getNonceThenIncrement());
然后使用您选择的钱包/签名提供商对交易进行签名。签署后,您通常会计算合约地址(确定性可计算),如下所示:
let contractAddress = SmartContract.computeAddress(transaction.getSender(), transaction.getNonce());
为了广播交易并等待其完成,请使用网络提供商和交易监视器:
await networkProvider.sendTransaction(transaction);
let transactionOnNetwork = await new TransactionWatcher(networkProvider).awaitCompleted(transaction);
最后,解析结果:
let { returnCode } = new ResultsParser().parseUntypedOutcome(transactionOnNetwork);
ABI
从文件中加载 ABI
import { AbiRegistry } from "@elrondnetwork/erdjs";
import { promises } from "fs";
let jsonContent: string = await promises.readFile("myAbi.json", { encoding: "utf8" });
let json = JSON.parse(jsonContent);
let abiRegistry = AbiRegistry.create(json);
let abi = new SmartContractAbi(abiRegistry, ["MyContract"]); ... let contract = new SmartContract({ address: new Address("erd1..."), abi: abi });
从一个网址加载 ABI
import axios, { AxiosResponse } from "axios";
let response: AxiosResponse = await axios.get("https://.../myAbi.json");
let abiRegistry = AbiRegistry.create(response.data);
let abi = new SmartContractAbi(abiRegistry, ["MyContract"]); ... let contract = new SmartContract({ address: new Address("erd1..."), abi: abi });
合约查询
当 ABI 不在时
let contractAddress = new Address("erd1qqq...");
let contract = new SmartContract({ address: contractAddress });
let addressOfAlice = new Address("erd1...");
let query = contract.createQuery({
func: new ContractFunction("getClaimableRewards"),
args: [new AddressValue(addressOfAlice)],
caller: new Address("erd1...")
});
let queryResponse = await networkProvider.queryContract(query);
let bundle = resultsParser.parseUntypedQueryResponse(queryResponse);
console.log(bundle.returnCode);
console.log(bundle.returnMessage);
console.log(bundle.values);
使用Interaction
,当 ABI 不可用时
let func = new ContractFunction("getClaimableRewards");
let args = [new AddressValue(addressOfAlice)];
let query = new Interaction(contract, func, args)
.withQuerent(new Address("erd1..."))
.buildQuery();
let queryResponse = await networkProvider.queryContract(query);
然后,如上解析响应。
当 ABI 可用
let query = contract.createQuery({
func: new ContractFunction("getClaimableRewards"),
args: [new AddressValue(addressOfAlice)],
caller: new Address("erd1...")
});
let queryResponse = await networkProvider.queryContract(query);
let endpointDefinition = contract.getEndpoint("getClaimableRewards");
let { firstValue, secondValue, returnCode } = resultsParser.parseQueryResponse(queryResponse, endpointDefinition);
使用Interaction
,当 ABI 可用
准备交互,检查它,然后构建查询:
let interaction = <Interaction>contract.methods.getLotteryInfo(["myLottery]);
let query = interaction.check().buildQuery();
然后,运行查询并解析结果:
let queryResponse = await networkProvider.queryContract(query);
let endpointDefinition = interaction.getEndpoint();
let { firstValue, secondValue, returnCode } = resultsParser.parseQueryResponse(queryResponse, endpointDefinition);
根据上下文,重新解释(投射)结果:
let firstValueAsStruct = <Struct>firstValue;
return firstValueAsStruct;
合约交互
当 ABI 不在时
let contractAddress = new Address("erd1qqq...");
let contract = new SmartContract({ address: contractAddress });
let addressOfCarol = new Address("erd1...");
let tx = contract.call({
func: new ContractFunction("transferToken"),
gasLimit: 5000000,
args: [new AddressValue(addressOfCarol), new U64Value(1000)],
chainID: "D"
});
tx.setNonce(alice.nonce);
然后,签名,广播tx
并等待其完成。
使用Interaction
,当 ABI 不可用时
let contract = new SmartContract({ address: contractAddress });
let dummyFunction = new ContractFunction("dummy");
let args = [new U32Value(100)];
let interaction = new Interaction(contract, dummyFunction, args);
let tx = interaction
.withNonce(7)
.withValue(TokenPayment.egldFromAmount(1))
.withGasLimit(20000000)
.withChainID("D")
.buildTransaction();
然后,签名,广播tx
并等待其完成。
使用Interaction
,当 ABI 可用
let contract = new SmartContract({ address: contractAddress, abi: abi });
let tx = contract.methods.dummy([new U32Value(100)])
.withNonce(7)
.withValue(TokenPayment.egldFromAmount(1))
.withGasLimit(20000000)
.withChainID("D")
.buildTransaction();
传送&执行
给定一个交互:
let interaction = contract.methods.doStuff([]);
也可以将代币传输应用于智能合约调用。
对于一次性付款,请执行以下操作:
// Fungible token
interaction.withSingleESDTTransfer(TokenPayment.fungibleFromAmount("FOO-6ce17b", "1.5", 18));
// Non-fungible token
interaction.withSingleESDTNFTTransfer(TokenPayment.nonFungible("ERDJS-38f249", 1));
对于多次付款:
interaction.withMultiESDTNFTTransfer([
TokenPayment.fungibleFromAmount("FOO-6ce17b", "1.5", 18)
TokenPayment.nonFungible("ERDJS-38f249", 1)
]);
解析合约结果
重要
当默认ResultsParser
行为不当时,请在 GitHub 上打开一个问题,并且也提供尽可能多的关于不可解析结果的细节(例如,如果可能的话提供交易对象的转储——确保删除任何敏感信息)。
当 ABI 不在时
let resultsParser = new ResultsParser();
let transactionOnNetwork = await networkProvider.getTransaction(txHash);
let { returnCode, returnMessage, values } = resultsParser.parseUntypedOutcome(transactionOnNetwork, endpointDefinition);
当 ABI 可用
let resultsParser = new ResultsParser();
let transactionOnNetwork = await networkProvider.getTransaction(txHash);
let { returnCode } = resultsParser.parseOutcome(transactionOnNetwork, endpointDefinition);
endpointDefinition
可以从Interaction
对象中获得,如果在上下文中可用的话:
let endpointDefinition = interaction.getEndpoint();
或者,endpointDefinition
可以从SmartContract
对象中获得:
let endpointDefinition = smartContract.getEndpoint("myFunction");
要定制默认解析器,请参见扩展 erdjs 。
解码交易元数据
利用transaction-decoder
为了从交易有效负载中解码元数据(函数、参数、传输),请执行以下操作:
import { TransactionDecoder, TransactionMetadata } from "@elrondnetwork/transaction-decoder";
let transactionOnNetwork = await networkProvider.getTransaction(txHash);
let metadata = new TransactionDecoder().getTransactionMetadata({
sender: transactionOnNetwork.sender.bech32(),
receiver: transactionOnNetwork.receiver.bech32(),
data: transactionOnNetwork.data.toString("base64"),
value: transactionOnNetwork.value.toString(),
type: transactionOnNetwork.type
});
利用erdjs 9x
的esdtHelpers
和scArgumentsParser
在erdjs 10
中esdtHelpers
和scArgumentsParser
类已经被移除,取而代之的是@ elrond network/transaction-decoder(见上)。
但是,您仍然可以在以下位置找到以前的实现: