Alaya JS SDK 快速入门教程(三):智能合约

原创 PlatON PlatON前天

Alaya JS SDK 快速入门教程(三):智能合约

本教程从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合约

同时经过测试,使用下面的链接进行编译:

https://remix.ethereum.org/

在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

(0)
PlatONWorld-M6的头像PlatONWorld-M6管理员
上一篇 28 10 月, 2021 08:47
下一篇 29 10 月, 2021 01:13

相关推荐

发表回复

登录后才能评论