参数编码与返回值
参数编码与解码
介绍Vision Network中调用智能合约时传递参数的编码及解码。
在通过HTTP接口triggersmartcontract调用智能合约时,需要传递编码后的parameter,在此我们通过VRC20-VST合约为例,提供javascript代码向开发者展示参数如何进行编码。solidity中对参数编解码详细说明请参考solidity文档。
参数编码
以VRC20-VST合约中的转账函数为例:
function transfer(address to, uint256 value) public returns (bool);
假设给地址46b03b026cdeb86dc1c50d13f55a8ff5650eb252ad 转账 50000 VDT,调用的triggersmartcontract接口如下:
curl --request POST \
--url https://vtest.infragrid.v.network/wallet/triggersmartcontract \
--header 'Content-Type: application/json' \
--data '{"contract_address":"4697bcc10fc1013976431eaef5ae1d2ca2f556b7d8",
"owner_address":"46b03b026cdeb86dc1c50d13f55a8ff5650eb252ad",
"function_selector":"transfer(address,uint256)",
"parameter":"0000000000000000000000002ed5dd8a98aea00ae32517742ea5289761b2710e0000000000000000000000000000000000000000000000000000000ba43b7400",
"call_value":0,
"fee_limit":1000000000,
"call_token_value":0,
"token_id":0}'
上面脚本中,parameter的编码需要根据ABI规则进行,规则比较复杂,我们可以可以利用ethers库的进行编码,示例代码如下:
// 参数转码 代码
// 建议使用ethers4.0.47版本
var ethers = require('ethers')
const AbiCoder = ethers.utils.AbiCoder;
const ADDRESS_PREFIX_REGEX = /^(46)/;
const ADDRESS_PREFIX = "46";
async function encodeParams(inputs){
let typesValues = inputs
let parameters = ''
if (typesValues.length == 0)
return parameters
const abiCoder = new AbiCoder();
let types = [];
const values = [];
for (let i = 0; i < typesValues.length; i++) {
let {type, value} = typesValues[i];
if (type == 'address')
value = value.replace(ADDRESS_PREFIX_REGEX, '0x');
else if (type == 'address[]')
value = value.map(v => toHex(v).replace(ADDRESS_PREFIX_REGEX, '0x'));
types.push(type);
values.push(value);
}
// 额外输出参与加密的参数
console.log(types, values)
try {
parameters = abiCoder.encode(types, values).replace(/^(0x)/, '');
} catch (ex) {
console.log(ex);
}
return parameters
}
async function main() {
let inputs = [
{type: 'address', value: "46b03b026cdeb86dc1c50d13f55a8ff5650eb252ad"},
{type: 'uint256', value: 100000000}
]
let parameters = await encodeParams(inputs)
console.log(parameters)
}
main();
示例代码输出参与加密的参数 以及 加密结果,方便核对
[ 'address', 'uint256' ] [ '0xb03b026cdeb86dc1c50d13f55a8ff5650eb252ad', 100000000 ]
000000000000000000000000b03b026cdeb86dc1c50d13f55a8ff5650eb252ad0000000000000000000000000000000000000000000000000000000005f5e100
参数解码
调用的triggersmartcontract生成交易对象,然后签名广播,交易成功上链后,可以通过gettransactionbyid获取链上的交易信息:
curl --request POST \
--url https://vtest.infragrid.v.network/wallet/gettransactionbyid \
--header 'Content-Type: application/json' \
--data '{"value":"29a72205a1dcba5142b72ad2daf9b7deb63be8a42da09fa6863d05df4932176c"}'
得到的结果
{
"ret": [
{
"contractRet": "SUCCESS"
}
],
..........
"raw_data": {
"contract": [
{
"parameter": {
"value": {
"data": "a9059cbb0000000000000000000000002ed5dd8a98aea00ae32517742ea5289761b2710e0000000000000000000000000000000000000000000000000000000ba43b7400",
"owner_address": "46b03b026cdeb86dc1c50d13f55a8ff5650eb252ad",
"contract_address": "4697bcc10fc1013976431eaef5ae1d2ca2f556b7d8"
},
"type_url": "type.googleapis.com/protocol.TriggerSmartContract"
},
..........
}
上面返回值中的的raw_data.contract[0].parameter.value.data字段就是调用transfer(address to, uint256 value) 函数的参数,但是data字段和参数的编码小节描述的triggersmartcontract的parameter字段并不一样,前面还多了a9059cbb这4个字节,这4个字节是方法ID,这源自ASCII格式的 transfer(address,uint256) 签名的 Keccak 哈希的前 4 字节,用于虚拟机对函数的寻址。
下面代码是对data字段进行解码,获取出transfer函数传递的参数:
var ethers = require('ethers')
const AbiCoder = ethers.utils.AbiCoder;
const ADDRESS_PREFIX_REGEX = /^(46)/;
const ADDRESS_PREFIX = "46";
//types:参数类型列表,如果函数有多个返回值,列表中类型的顺序应该符合定义的顺序
//output: 解码前的数据
//ignoreMethodHash:对函数返回值解码,ignoreMethodHash填写false,如果是对gettransactionbyid结果中的data字段解码时,ignoreMethodHash填写true
async function decodeParams(types, output, ignoreMethodHash) {
if (!output || typeof output === 'boolean') {
ignoreMethodHash = output;
output = types;
}
if (ignoreMethodHash && output.replace(/^0x/, '').length % 64 === 8)
output = '0x' + output.replace(/^0x/, '').substring(8);
const abiCoder = new AbiCoder();
if (output.replace(/^0x/, '').length % 64)
throw new Error('The encoded string is not valid. Its length must be a multiple of 64.');
return abiCoder.decode(types, output).reduce((obj, arg, index) => {
if (types[index] == 'address')
arg = ADDRESS_PREFIX + arg.substr(2).toLowerCase();
obj.push(arg);
return obj;
}, []);
}
async function main() {
let data = 'a9059cbb0000000000000000000000002ed5dd8a98aea00ae32517742ea5289761b2710e0000000000000000000000000000000000000000000000000000000ba43b7400'
result = await decodeParams(['address', 'uint256'], data, true)
console.log(result)
}
main();
得到结果
[ '462ed5dd8a98aea00ae32517742ea5289761b2710e',
BigNumber { _hex: '0x0ba43b7400', _isBigNumber: true } ]
Updated about 3 years ago