其实,使用 eosjs
来执行一个合约动作,目前好像有两种方式,第一种比较复杂,就是使用 eosjs.transact()
方法,而第二种则比较简单,首先使用 eosjs.contract()
方法获取合约,然后再调用该方法的返回值里的合约方法。
本章节,我们主要来讲讲 eosjs.contract()
方法及使用范例
准备
-
我们希望你是按照顺序一篇一偏读过来的,哈哈。不然你会发现我没有阐述合约信息.
-
假设你已经使用
npm i eosjs
安装了 eosjs -
假设存在一个文件
micromsg.js
文件,内容如下,接下来我们所有的 javascript 都在这个文件中
const Eos = require('eosjs'); const config = { account: { contract: "micromsg", // 合约账号 from: "xiaoming", // 发送者账号 to: "xiaohong" // 接收者账号 }, network: { protocol:'http', // 协议 http or https host: 'localhost', // 远程主机 port: '8080', // 远程端口号 keyProvider:['5J6Ly95q876iEwG1c4bBcWAWqHbibRmhDsxtSrnsSswpT7r7gAC'], // 私钥 chainId: 'cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f', // EOS 链 id timeout: 10000 // 10s 超时 } } var eos = Eos(config.network);
eosjs.contract()
方法
eosjs.contract()
方法用户获取一个合约,它有一个参数,也是唯一的一个参数,就是合约所在的账号名
但这个方法有点特殊,如果我们直接调用 console.log(eosjs.contract())
方法会抛出一个异常,而不是显示该方法的帮助信息
console.log(eos.contract());
运行结果如下
assert.js:86 throw new AssertionError(obj); ^ AssertionError [ERR_ASSERTION]: account string required at Object.abiAsync (/Users/yufei/Downloads/contract/micromsg/node_modules/eosjs/lib/abi-cache.js:32:12) at Object.genContractActions (/Users/yufei/Downloads/contract/micromsg/node_modules/eosjs/lib/write-api.js:352:28) at Object.merge.contract (/Users/yufei/Downloads/contract/micromsg/node_modules/eosjs/lib/write-api.js:112:14) at Object.<anonymous> (/Users/yufei/Downloads/contract/micromsg/micromsg.js:21:17) at Module._compile (internal/modules/cjs/loader.js:707:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:718:10) at Module.load (internal/modules/cjs/loader.js:605:32) at tryModuleLoad (internal/modules/cjs/loader.js:544:12) at Function.Module._load (internal/modules/cjs/loader.js:536:3) at Function.Module.runMain (internal/modules/cjs/loader.js:760:12)
但不管怎么样,从报错信息中可以看出,使用该方法,我们需要传递合约账号名
参数说明
参数 | 类型 | 默认值 | 说明 |
---|---|---|---|
account | string | 必填 | 合约所在的账号 |
返回值
如果合约存在,且配置正常,那么会返回当前合约的所有信息,同时返回当前合约可以调用的动作
结果很长,我只截取最重要的部分
{ transaction: [Function: _callee], read: [Function], send: [Function], fc: { abi: { version: 'eosio::abi/1.1', types: [], structs: [ { name: 'message', base: '', fields: [ { name: 'id', type: 'uint64' }, { name: 'from', type: 'name' }, { name: 'to', type: 'name' }, { name: 'msg', type: 'string' }, { name: 'tm', type: 'uint64' }, { name: 'readed', type: 'bool' } ] }, { name: 'read', base: '', fields: [ { name: 'to', type: 'name' }, { name: 'msg_id', type: 'uint64' } ] }, { name: 'send', base: '', fields: [ { name: 'from', type: 'name' }, { name: 'to', type: 'name' }, { name: 'msg', type: 'string' } ] } ], actions: [ { name: 'read', type: 'read', ricardian_contract: '' }, { name: 'send', type: 'send', ricardian_contract: '' } ], tables: [ { name: 'messages', index_type: 'i64', key_names: [], key_types: [], type: 'message' } ], ricardian_clauses: [], error_messages: [], abi_extensions: [], variants: [] }, schema: { message: { fields: { id: 'uint64', from: 'name', to: 'name', msg: 'string', tm: 'uint64', readed: 'bool' } }, read: { fields: { to: 'name', msg_id: 'uint64' }, action: { name: 'read', account: 'micromsg' } }, send: { fields: { from: 'name', to: 'name', msg: 'string' }, action: { name: 'send', account: 'micromsg' } } }, ....
触发异常
如果想要获取的合约不存在,则会抛出一个合约不存在的异常
eos.contract("noexist") .then(rs => console.log(rs)) .catch(e => console.log(e))
运行结果如下
{ Error: {"code":500,"message":"Internal Service Error","error":{"code":0,"name":"exception","what":"unspecified","details":[]}} at /Users/yufei/Downloads/contract/micromsg/node_modules/eosjs-api/lib/apigen.js:112:23 at process.internalTickCallback (internal/process/next_tick.js:77:7) status: 500, statusText: 'Internal Server Error' }
使用 eosjs.contract()
方法获取合约 abi 信息
如果合约存在,那么 eosjs.contract()
方法返回的结果中的 fc.abi
就是该合约的 abi 信息
eos.contract(config.account.contract) .then(rs => console.log(rs.fc.abi)) .catch(e => console.log(e))
运行结果如下
{ version: 'eosio::abi/1.1', types: [], structs: [ { name: 'message', base: '', fields: [ { name: 'id', type: 'uint64' }, { name: 'from', type: 'name' }, { name: 'to', type: 'name' }, { name: 'msg', type: 'string' }, { name: 'tm', type: 'uint64' }, { name: 'readed', type: 'bool' } ] }, { name: 'read', base: '', fields: [ { name: 'to', type: 'name' }, { name: 'msg_id', type: 'uint64' } ] }, { name: 'send', base: '', fields: [ { name: 'from', type: 'name' }, { name: 'to', type: 'name' }, { name: 'msg', type: 'string' } ] } ], actions: [ { name: 'read', type: 'read', ricardian_contract: '' }, { name: 'send', type: 'send', ricardian_contract: '' } ], tables: [ { name: 'messages', index_type: 'i64', key_names: [], key_types: [], type: 'message' } ], ricardian_clauses: [], error_messages: [], abi_extensions: [], variants: [] }
从返回的结果中可以看出,这和我们本地的 micromsg.abi
文件简直一模一样
cat micromsg.abi { "____comment": "This file was generated with eosio-abigen. DO NOT EDIT Sun Dec 2 08:15:50 2018", "version": "eosio::abi/1.1", "structs": [ { "name": "message", "base": "", "fields": [ { "name": "id", "type": "uint64" }, { "name": "from", "type": "name" }, { "name": "to", "type": "name" }, { "name": "msg", "type": "string" }, { "name": "tm", "type": "uint64" }, { "name": "readed", "type": "bool" } ] }, { "name": "read", "base": "", "fields": [ { "name": "to", "type": "name" }, { "name": "msg_id", "type": "uint64" } ] }, { "name": "send", "base": "", "fields": [ { "name": "from", "type": "name" }, { "name": "to", "type": "name" }, { "name": "msg", "type": "string" } ] } ], "types": [], "actions": [ { "name": "read", "type": "read", "ricardian_contract": "" }, { "name": "send", "type": "send", "ricardian_contract": "" } ], "tables": [ { "name": "messages", "type": "message", "index_type": "i64", "key_names": [], "key_types": [] } ], "ricardian_clauses": [], "variants": [], "abi_extensions": [] }
调用 micromsg
合约的 send()
方法
从前面的介绍中可以知道,合约的方法是直接在 eosjs.contract()
的返回结果中以键值的方式存在的。
{ transaction: [Function: _callee], read: [Function], send: [Function], ...
因此,一般的合约的动作调用格式如下
eos.contract(config.account.contract) .catch(e => console.log(e)) .then(contract =>{ console.log(contract.methodname(params)); })
其中, methodname
是合约的动作,params
是要传递给合约的动作的参数
跟 eosjs 其它方法一样,我们可以调用 send()
不传递任何参数来获取帮助信息,如下
eos.contract(config.account.contract) .catch(e => console.log(e)) .then(contract =>{ console.log(contract.send()); })
显示结果如下
CONTRACT micromsg ACTION send PARAMETERS { "from": "name", "to": "name", "msg": "string" } EXAMPLE micromsg.send({ "from": "", "to": "", "msg": "" })
另一方面,调用合约动作返回的同样是一个 Promise,因此,我们也需要使用 then()
和 catch()
来获取返回值和捕获异常
也就是说,从获取合约,到执行合约动作的代码一般如下
eos.contract(contractName) .catch(e => console.log(e)) .then(contract =>{ contract.methodname(params) .then( rs => { console.log(rs);}) .catch( e => console.log(e)) })
例如,我们要调用 micromsg
合约的 send()
动作,使用小明给小红发一条信息的代码如下
eos.contract(config.account.contract) .catch(e => console.log(e)) .then(contract =>{ contract.send({from:"xiaoming",to:"xiaohong",msg:"i miss you"}) .then( rs => { console.log(rs);}) .catch( e => console.log(e)) })
输出结果如下
{"code":500,"message":"Internal Service Error","error":{"code":3040003,"name":"tx_no_auths","what":"Transaction should have at least one required authority","details":[]}}
哎呀,报错了,错误提示我们没有添加授权
这是什么意思呢?其实很简单,错误信息和下面这条命令一样
cleos push action push micromsg send '["xiaoming","xiaohong","i miss you"]'
而正确的命令是
cleos push action push micromsg send '["xiaoming","xiaohong","i miss you"]' -p xiaoming
发现缺了什么没有,对,就是缺少 -p xiaoming
那么,在 eosjs
中要怎么传递这个参数呢?
哈哈,不用着急, 在 eosjs
中,几乎所有方法都存在一个隐形的参数 options
,这个参数是可选的,一般是方法的最后一个参数,用于指定一些配置项,
比如,我们可以使用下面的配置项来传递授权
{ authorization: ["xiaoming"] }
或者同时指定权限的方式
{ authorization: ["xiaoming@active"] }
当然了,对于我们的配置项,就是
{ authorization: [`${config.account.from}@active`] }
好了,我们可以该一下调用合约的方法,添加配置项
eos.contract(config.account.contract) .catch(e => console.log(e)) .then(contract =>{ contract.send({from:"xiaoming",to:"xiaohong",msg:"i miss you"},{authorization: [`${config.account.from}@active`]}) .then( rs => { console.log(rs);}) .catch( e => console.log(e)) })
运行结果如下
{ broadcast: true, transaction: { compression: 'none', transaction: { expiration: '2018-12-02T03:22:09', ref_block_num: 19324, ref_block_prefix: 2454067230, max_net_usage_words: 0, max_cpu_usage_ms: 0, delay_sec: 0, context_free_actions: [], actions: [ { account: 'micromsg', name: 'send', authorization: [ { actor: 'xiaoming', permission: 'active' } ], data: '0000006c3a498deb0000006cd2468deb0a69206d69737320796f75' } ], transaction_extensions: [] }, signatures: [ 'SIG_K1_K5AYPwMSPJXgVvNV39WzRQsmhFdTXfEevUH5TTQ8zVrh5XWEKPCbSdGNfmLeDAEHr4wJQuip2cyd75B4YV2uG1LEe2Gtk2' ] }, transaction_id: 'ca35e5151f87a150f8c74209e4717ce11330eff523a51ce216c4ce983c164392', processed: { id: 'ca35e5151f87a150f8c74209e4717ce11330eff523a51ce216c4ce983c164392', block_num: 84863, block_time: '2018-12-02T03:21:10.500', producer_block_id: null, receipt: { status: 'executed', cpu_usage_us: 542, net_usage_words: 15 }, elapsed: 542, net_usage: 120, scheduled: false, action_traces: [ { receipt: { receiver: 'micromsg', act_digest: 'b8f84135fd128efb56723def246fac9febbb0473425c487bd0a4926e37bd4d96', global_sequence: 84938, recv_sequence: 8, auth_sequence: [ [ 'xiaoming', 19 ] ], code_sequence: 1, abi_sequence: 1 }, act: { account: 'micromsg', name: 'send', authorization: [ { actor: 'xiaoming', permission: 'active' } ], data: { from: 'xiaoming', to: 'xiaohong', msg: 'i miss you' }, hex_data: '0000006c3a498deb0000006cd2468deb0a69206d69737320796f75' }, context_free: false, elapsed: 333, console: "#6 xiaoming send 'i miss you' to xiaohong at 1543720870", trx_id: 'ca35e5151f87a150f8c74209e4717ce11330eff523a51ce216c4ce983c164392', block_num: 84863, block_time: '2018-12-02T03:21:10.500', producer_block_id: null, account_ram_deltas: [ { account: 'micromsg', delta: 156 } ], except: null, inline_traces: [ { receipt: { receiver: 'xiaoming', act_digest: 'b8f84135fd128efb56723def246fac9febbb0473425c487bd0a4926e37bd4d96', global_sequence: 84939, recv_sequence: 7, auth_sequence: [ [ 'xiaoming', 20 ] ], code_sequence: 1, abi_sequence: 1 }, act: { account: 'micromsg', name: 'send', authorization: [ { actor: 'xiaoming', permission: 'active' } ], data: { from: 'xiaoming', to: 'xiaohong', msg: 'i miss you' }, hex_data: '0000006c3a498deb0000006cd2468deb0a69206d69737320796f75' }, context_free: false, elapsed: 7, console: '', trx_id: 'ca35e5151f87a150f8c74209e4717ce11330eff523a51ce216c4ce983c164392', block_num: 84863, block_time: '2018-12-02T03:21:10.500', producer_block_id: null, account_ram_deltas: [], except: null, inline_traces: [] }, { receipt: { receiver: 'xiaohong', act_digest: 'b8f84135fd128efb56723def246fac9febbb0473425c487bd0a4926e37bd4d96', global_sequence: 84940, recv_sequence: 7, auth_sequence: [ [ 'xiaoming', 21 ] ], code_sequence: 1, abi_sequence: 1 }, act: { account: 'micromsg', name: 'send', authorization: [ { actor: 'xiaoming', permission: 'active' } ], data: { from: 'xiaoming', to: 'xiaohong', msg: 'i miss you' }, hex_data: '0000006c3a498deb0000006cd2468deb0a69206d69737320796f75' }, context_free: false, elapsed: 3, console: '', trx_id: 'ca35e5151f87a150f8c74209e4717ce11330eff523a51ce216c4ce983c164392', block_num: 84863, block_time: '2018-12-02T03:21:10.500', producer_block_id: null, account_ram_deltas: [], except: null, inline_traces: [] } ] } ], except: null } }
哈哈,从执行结果中可以看出,我们的动作执行是提交成功的,也执行成功了,因为我们看到了输出
#6 xiaoming send 'i miss you' to xiaohong at 1543720870
也就是说,使用 eosjs 对任何合约方法的调用,除非是延时交易,不然会等到合约执行成功才返回。
合约动作调用失败
合约动作也有可能因为各种各样的原因调用失败从而引发异常,一般情况下,调用失败都是合约内使用 eosio_assert
抛出的异常
比如我们的 micromsg
合约,会检查 msg
参数的长度,如果为 0 则抛出异常
现在,我们来触发这个异常看看,我们把 msg
参数设置为空字符串
eos.contract(config.account.contract) .catch(e => console.log(e)) .then(contract =>{ contract.send({from:"xiaoming",to:"xiaohong",msg:""},{authorization: [`${config.account.from}@active`]}) .then( rs => { console.log(rs);}) .catch( e => console.log(e)) })
运行结果如下
{"code":500,"message":"Internal Service Error","error":{"code":3050003,"name":"eosio_assert_message_exception","what":"eosio_assert_message assertion failure","details":[]}}
这里没有显示具体
#1 • 4 年, 12 月 前 •
您当前教程的版本是多少呢,我安装您的文档实践,在get_code时出现“Returning WAST from get_code is no longer supported”,是因为节点版本太低还是?
#2 • 6 年 前 •
谢谢楼主,学到了,我想问问能做微信小程序吗?,eosjs和wxjs能一起吗?公司提的要求做微信小程序,没有头绪
#3 • 6 年 前 •
简单实用能看懂,赞一个,我刚把智能合约写完,只会cleos命令交互,学到了! 我在想这个能做微信小程序吗?,公司提的要求没有头绪
#4 • 6 年 前 •
简单实用能看懂,赞一个,我刚把智能合约写完,只会cleos命令交互,学到了! 我在想这个能做微信小程序吗?,公司提的要求没有头绪