原创 PlatON PlatON前天
本教程从alaya js sdk的安装开始,分别讲述了连接Alaya网络并执行基本查询、转账及签名操作及智能合约交互的相关内容。本教程力求翔实地展现了Alaya Js Sdk的使用方法,让开发者能够快速入门Alaya Js Sdk。
| 智能合约
在明白了如何进行签名之后,我们就可以再深一步地了解如何使用智能合约了。
注:这里并不讨论如何编写智能合约,只讨论如何使用js sdk进行智能合约的相关操作。本教程以solidity合约为例。
在使用alaya sdk与智能合约产生交互之前,我们先明确一点,那就是alaya sdk能做什么?
- alaya sdk可以编译智能合约吗?不能,现阶段,alaya的智能合约可以通过使用alaya-truffle,PlatONStudio来进行编译,具体请参照:EVM智能合约 · Alaya
- alaya sdk可以部署智能合约吗?可以!后面我们就细讲一下如何用alaya sdk部署智能合约。
- alaya sdk可以与智能合约交互吗?当然可以!这个是alaya sdk的主要功能之一。
智能合约部署
在智能合约部署之前,需要对智能合约进行编译,具体的编译方式可以参照:
https://alaya.network/alaya-devdocs/zh-CN/Solidity_Getting_started#编译helloworld合约
同时经过测试,使用下面的链接进行编译:
在alaya网络上也能正常运行。编译后会获得abi和bytecode,这些内容在后面的代码中会用到。由于本教程覆盖范围问题,所以不对编译过程作更详细的介绍,本教程中使用[EVM智能合约·Alaya](/alaya-devdocs/zh-CN/Solidity_Getting_started#创建helloworld合约)此创建的HelloWorld合约。
编译后的两个变量分别如下:
let bytecode = "0x608060405234801561001057600080fd5b5061036b806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806317d7de7c1461003b578063c47f0027146100b8575b600080fd5b61004361015e565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561007d578181015183820152602001610065565b50505050905090810190601f1680156100aa5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610043600480360360208110156100ce57600080fd5b8101906020810181356401000000008111156100e957600080fd5b8201836020820111156100fb57600080fd5b8035906020019184600183028401116401000000008311171561011d57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295506101f5945050505050565b60008054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156101ea5780601f106101bf576101008083540402835291602001916101ea565b820191906000526020600020905b8154815290600101906020018083116101cd57829003601f168201915b505050505090505b90565b805160609061020b90600090602085019061029e565b506000805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156102925780601f1061026757610100808354040283529160200191610292565b820191906000526020600020905b81548152906001019060200180831161027557829003601f168201915b50505050509050919050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106102df57805160ff191683800117855561030c565b8280016001018555821561030c579182015b8281111561030c5782518255916020019190600101906102f1565b5061031892915061031c565b5090565b6101f291905b80821115610318576000815560010161032256fea265627a7a72315820087868e5d428c0270af7145556516942e16a5db5ec467f7dcdca4cfa9ec099bc64736f6c63430005110032";
let abi = [
{
"constant": false,
"inputs": [
{
"internalType": "string",
"name": "_name",
"type": "string"
}
],
"name": "setName",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getName",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
];
为了缩减代码长度,后续代码中可能会对直接使用这两个变量而不再另外对其进行赋值。
智能合约部署操作其实非常简单,和之前的转账操作非常类似,也遵循生成交易->签名->发送交易的顺序。下面我们就直接看一下代码吧!
var Web3a = require("web3");
var AlayaAccounts = require("web3/packages/web3-eth-accounts");
async function main() {
let web3a = new Web3a('http://47.241.91.2:6789');
let bytecode = "";
// !!!!将主网的私钥写到代码上并分享给别人是非常危险的操作!!!!
// address for the privatekey below: atp1sznvsju6gjy3kmgnfgm526jf6e8x83twuctefh
var privateKey="0x5ae02064df442340f861136acbfc4bd62b3d48393903bd6aac77ce0e7aaa9e5e";
var chainId = 201030; //201018 for mainnet
var act = new AlayaAccounts(web3a.currentProvider, "atp");
act = act.privateKeyToAccount(privateKey);
let from = act.address;
let nonce = web3a.utils.numberToHex(await web3a.platon.getTransactionCount(from));
let gasPrice = await web3a.platon.getGasPrice();
let tx = {
from: from,
chainId: chainId,
gasPrice: gasPrice,
nonce: nonce,
data: bytecode
};
// 评估此交易所需要的交易费用
let gas = await web3a.platon.estimateGas(tx);
tx.gas = gas;
// 签名交易
let signTx = await act.signTransaction(tx);
// 发送交易
let receipt = await web3a.platon.sendSignedTransaction(signTx.rawTransaction);
console.log("The receipt of the transaction is: " + JSON.stringify(receipt));
}
main();
注意:上述代码不可直接运行,代码中使用了bytecode变量但未对其进行赋值,需要对bytecode变量进行赋值后使用。
是不是对这个交易非常熟悉,和之前转账交易最大两点不同是:
- 没有了to和value两个值
- 增加了data值,data中存储bytecode
与智能合约交互
与智能合约的交互是一种经常会遇到的操作,但实际上网络上能将其说明白的,特别是在使用公共节点时将其说明白的教程并不多。我们在看其他教程会产生一种疑问,那就是我不需要privateKey就可以与智能合约进行交互吗?
这个问题的答案其实是不确定的,由于我们与智能合约的交互通常有两种方式,一是call,二是send。
| call
有时,我们只需要从区块链上查询一些数据,并不会改变区块链的状态,这时我们就可以使用call的方式来与区块链交互。由于我们没有对区块链进行改变,所以不需要付gas,也不需要私钥签名,所以使用起来非常方便。
var Web3a = require("web3");
async function main() {
let web3a = new Web3a('http://47.241.91.2:6789');
// 我已经部署好的智能合约的地址
let contractAddress = "atp1pcvx85klajfw9mvy3tf07acmqxaek5nanuy5t6";
console.log("contract address: " + contractAddress);
let abi = [];
// 构建一个合约对象
let contract = new web3a.platon.Contract(abi, contractAddress);
let name = await contract.methods.getName().call();
console.log("name before change: " + name);
}
main();
注意:上述代码不可直接运行,代码中使用了abi变量但未对其进行赋值,需要对abi变量进行赋值后使用。
大家可以看到,call的形式非常简单,甚至都不需要私钥,就能直接从区块链上查询信息。如果你使用我的合约地址,你会从区块链上查询到name为rileyge,如果你使用自己新部署的合约,那么name为空。
| send
注意,send只是一种叫法,实际上后面你会看到,在下面的代码中我们并没有用到send函数。什么时候要采用send的方式与区块链进行交互呢?简单地说就是当操作需要改变区块链的状态时,就需要采用send的方式来与区块链进行交互。此时必须对交易进行签名并付gas。
如果你在其他地方看到类似的教程,你会发现他们使用的都是解锁的账号。根据我们前面所说,解锁账号一般只在私有节点或者有相关钱包时使用,而一般的nodejs程序是不会使用钱包的(当然也可以使用),那么如何使用没有解锁的账号与智能合约以send的方式交互呢?
还是之前的套路:生成交易->签名->发送交易。交易如何生成是此交易重点。
var Web3a = require("web3");
var AlayaAccounts = require("web3/packages/web3-eth-accounts");
async function main() {
let web3a = new Web3a('http://47.241.91.2:6789');
// !!!!将主网的私钥写到代码上并分享给别人是非常危险的操作!!!!
// address for the privatekey below: atp1sznvsju6gjy3kmgnfgm526jf6e8x83twuctefh
var privateKey="0x5ae02064df442340f861136acbfc4bd62b3d48393903bd6aac77ce0e7aaa9e5e";
var chainId = 201030; //201018 for mainnet
var act = new AlayaAccounts(web3a.currentProvider, "atp");
act = act.privateKeyToAccount(privateKey);
let from = act.address;
let gasPrice = await web3a.platon.getGasPrice();
// 之前已经部署好的智能合约
let contractAddress = "atp1pcvx85klajfw9mvy3tf07acmqxaek5nanuy5t6";
let abi = [];
let contract = new web3a.platon.Contract(abi, contractAddress);
let name = await contract.methods.getName().call();
console.log("name before change: " + name);
let trans = contract.methods.setName("rileyge");
let options ={
to : trans._parent._address, //也可以使用contractAddress
data : trans.encodeABI(), //abi的构造方法可以参照
gas : await trans.estimateGas({from: from}),
gasPrice: gasPrice,
chainId : chainId
}
signTx = await act.signTransaction(options);
// 发送交易
receipt = await web3a.platon.sendSignedTransaction(signTx.rawTransaction);
name = await contract.methods.getName().call();
console.log("name after change: " + name);
}
main();
注意:上述代码不可直接运行,代码中使用了abi变量但未对其进行赋值,需要对abi变量进行赋值后使用。
如果使用已经部署好的智能合约,需要将setName函数中的变量修改后再看效果。上面一段代码有好几处需要注意的点,都集中在options这个变量中。
- trans变量是我们通过Contract这个类构造的关于setName这个方法的交易。这个交易的具体用处下面几点有说明。
- to设置为智能合约的地址,这个十分好理解。
- data里面其实是将函数名及变量进行了abi编码,是个比较复杂的过程,具体编码方式函数ABI编码规则 (github.com)。不过好在alaya sdk已经帮我们处理好了这些,只要调用encodeABI()函数就行。
- 最后一个问题应该属于alaya sdk的一个bug,我已经在alaya sdk的chainId和前缀冲突 · Issue #61 · PlatONnetwork/client-sdk-js (github.com)中详细说明了问题,并提供了一种暂时的解决方案。
交易构建小结:
- 交易构建时常用的量有:from,to,value,gas,gasPrice,data,chainId等。其实在很多地方一些变量都是有默认值 的,如gas和gasPrice。所以如果你看到哪些交易构建时没有设置这个变量,也不要奇怪。
- 这些变量中chainId是一个比较有趣的变量。此变量一定要设置,而且千万不能设置错误。这个变量的出现其实是了避免在不同的链上进行重放攻击的。
- data变量的构建是构建交易的重点,一些复杂的交易都是将数据以某种编码形式放到了data中。
alaya sdk与智能合约的交互是不是也很简单?其他更多的内容就自己再去探索吧!
总结
本教程到此暂时就结束了,关于alaya js sdk还有在浏览器中使用、与内置合约交互两大块内容。但由于现在alaya js sdk可能会进行版本更新,更新后对上述两种操作的影响会比较大,所以暂时搁置上述两项内容教程的编写。待升级完成后再进行编写。
特别鸣谢本教程贡献者@RileyGe
本文转载自https://mp.weixin.qq.com/s/CXco58D_kl9z4Mntleq6RQ