Edit me
English 中文

智能合约的部署和调用

Version 0.7.0

1. 部署合约

部署合约需要构建并发送相应的交易到链上执行。

构建合约需要提供合约内容的十六进制字符串,和一些配置参数。

合约内容一般.avm文件(对于NEO虚拟机)和.wasm文件(对于WASM虚拟机)提供。

配置参数如下:

code 合约内容,十六进制的字符串。

vmType 虚拟机类型。目前可选值有:

export enum VmType {
    NativeVM = 0xFF,
    NEOVM    = 0x80,
    WASMVM   = 0x90
}

name 合约的名称。普通字符串。可选值。

codeVersion 合约的版本。普通字符串。可选值。

author 合约作者。普通字符串。可选值。

email 合约作者的邮件地址。普通字符串。可选值。

desp 合约的描述。普通字符串。可选值。

needStorage 是否需要存储。布尔值。可选值。

import * as core from '../src/core'

//获取合约内容
var fs = require('fs')
var path = require('path')
let idContractAvm = fs.readFileSync(path.join(__dirname, '../src/smartcontract/data/IdContract.avm'))
var idContractAvmCode = ab2hexstring(idContractAvm)
var name = 'test',
	codeVersion = '1.0',
	author = 'alice',
	email = '',
	desp = '',
	needStorage = true;
//构建交易	
var tx = core.makeDeployCodeTransaction(idContractAvmCode, VmType.NEOVm, name, codeVersion, author, email, desp, needStorage)
//发送交易
var param = buildRestfulParam(tx)
var url = TEST_ONT_URL.sendRawTxByRestful
axios.post(url, param).then((res:any)=> {
	console.log('deploy res: '+ JSON.stringify(res.data))
	//6秒后查看部署结果。
    setTimeout(function () {
    	getContract(code, vmType)
    }, 6000)
    }).catch(err => {
   	   console.log('err: '+ err)
})

//从链上查询合约
const getContract = (avmCode, vmType=VmType.NEOVM) => {
    const codeHash = Address.fromVmCode(avmCode,vmType).serialize()
    let url = `${TEST_ONT_URL.REST_URL}/api/v1/contract/${codeHash}`
    console.log('url : '+ url)
    axios.get(url).then((res)=>{
        console.log(res.data)
    }).catch(err => {
        console.log(err)
    })
}

2. 调用合约

合约必须在成功部署后才能调用。 调用合约需要构建并发送相应的交易到链上执行。

2.1 通过abi文件构建交易

一般智能合约可以编译出相应的.avm文件和.abi文件(对于NEO虚拟机)。.abi文件是以JSON格式,描述智能合约的方法和参数。可以通过读取.abi文件方便的构建调用合约的交易。构建的交易可能还需要使用用户的私钥签名。

TS SDK 中与Abi相关的类有AbiInfo, AbiFunction, Parameter。

class AbiInfo {
    hash : string
    entrypoint : string
    functions : Array<AbiFunction>
}

hash 智能合约hash值。也称合约地址。用来区别不通合约的标志。

entrypoint 合约的入口函数名。

functions 合约提供的函数集合。

class AbiFunction {
    name : string
    returntype : string
    parameters : Array<Parameter>
}

name 函数名称。

returntype 函数返回值类型。

parameters 函数参数列表。

class Parameter {
    name : string
    type : ParameterType
    value : any
}

name 参数名称。

type 参数类型。

value 参数值。

通过读取合约的abi文件,构造相应的对象,能方便地提供构建交易时需要的函数名,参数,调用的合约哈希。

构造交易的函数所需参数如下:

function makeInvokeTransaction(funcName : string, parameters : Array<Parameter>, contractHash : string, vmType : VmType = VmType.NEOVM, fees : Array<Fee> = [])

funcName 调用的合约中函数名称。

parameters 函数的参数对象列表。

contractHash 合约hash。

vmType 虚拟机类型。

fees 发送交易的费用。

下面以ONT ID智能合约中注册ONT ID的方法为例说明这个过程。

//读取abi文件。这里文件导出的是JSON
import abiJson from '../smartcontract/data/idContract.abi'
//解析abi内容
const abiInfo = AbiInfo.parseJson(JSON.stringify(abiJson))

//获取AbiFunction
const abiFunction = abiInfo.getFunction('RegIdWithPublicKey')

const privateKey = '7c47df9664e7db85c1308c080f398400cb24283f5d922e76b478b5429e821b95'
const publicKey = '1202037fa2bbad721197da4f2882e4f8e4ef6a77dbc7cfbd72267cdd72dd60ac30b41e'
const ontid = '6469643a6f6e743a544d7876617353794747486e7574674d67657158443556713265706b6a476f737951'

//构造参数。注意这里的参数类型是ByteArray,即十六进制字符串。
let p1 = new Parameter(f.parameters[0].getName(), ParameterType.ByteArray, ontid)
let p2 = new Parameter(f.parameters[1].getName(), ParameterType.ByteArray, publicKey)

//设置参数
abiFunction.setParasValue(p1, p2)

//构造交易对象
let fees = []
let vmType = VmType.NEOVM
let tx = makeInvokeTransaction(abiFunction.name, abiFunction.parameters, abiInfo.hash, vmType, fees)

//签名交易。现在得到最终构造好的交易。
signTransaction(tx, privateKey)

2.2 构建基于WASM合约的交易

对于基于WASM虚拟机的智能合约,编译合约没有产生abi文件。但提供准确的函数名和参数对象也可以使用同样的方法构造交易。

const codeHash = '9007be541a1aef3d566aa219a74ef16e71644715'
const params = [
		new Parameter('p1', ParameterType.Int, 20), 
		new Parameter('p2', ParameterType.Int, 30)
	]
const funcName = 'add'
let tx = makeInvokeTransaction(funcName, params, codeHash, VmType.WASMVM)

2.3 发送交易

有多种方式发送交易到链上执行。

2.3.1 Restful

//使用封装的对象发送请求
let restClient = new RestClient()
//使用之前构造的交易对象,创建请求参数
restClient.sendRawTransaction(tx.serialize()).then(res => {
    console.log(res)
})

请求会返回该交易的hash。需要通过交易hash去查询合约的执行结果。比较简单的方法是,到本体的区块链浏览器上查询。

2.3.2 Rpc

let rpcClient = new RpcClient()
rpcClient.sendRawTransaction(tx.serialize()).then(res => {
    console.log(res)
})

Rpc请求与Restful类似。返回的结果也是交易hash。

2.3.3 Websocket

通过websocket发送请求,可以监听后台推送的消息。如果合约里写明了事件推送,合约方法调用后会有相应的推送消息。

//构造请求参数
let param = buildTxParam(tx)
let txSender = new TxSender(TEST_ONT_URL.SOCKET_URL)
//定义回调函数
//@param err 错误结果
//@param res 监听到的消息
//@param socket websocket对象
const callback = (err, res, socket) => {
    if(err) {
        console.log(err)
        socket.close()
        return;
    }
    if(res.Action === 'Notify') {
        console.log('Notify: '+ JSON.stringify(res))
        socket.close()
    }
}
txSender.sendTxWithSocket(param, callback)