对于合约平台,合约操作相关接口使用最为广泛,因此 JS SDK 进行了很多优化,让合约操作变得更简单、高效。在使用合约相关接口之前,您首先需要了解什么是字节码和合约接口(ABI),相关内容参见 Cloud IDE 合约开发环境说明文档。
在以下接口介绍中,使用的合约样例程序 CreditManager
来自 Solidity 合约开发说明文档。JS SDK 可以与 Solidity 编译合约工具 solc-js
结合使用。solc-js 相关内容,参见 Solidity 合约编译工具文档。
构造合约实例
contract
用来构造一个合约实例,后续可部署、调用该合约。
请求参数
参数 | 必选 | 类型 | 说明 |
name | true | string | 目标合约的名称,此名称计算哈希后即是此合约的 identity。 |
abi | true | string | ABI 定义的 JSON 格式字符串。 |
vmType | false | string | 合约平台支持 Solidity 语言,默认配置为 |
示例 1
java script
const solc = require('@alipay/solc')
const contract = fs.readFileSync('./CreditManager.sol', {encoding: 'ascii'})
// 第二个参数设定为"1",会开启编译优化 optimiser
const output = solc.compile(contract, 1)
const abi = JSON.parse(output.contracts[':CreditManager'].interface)
const bytecode = output.contracts[':CreditManager'].bytecode
// 带上时间戳,防止合约名计算哈希后已存在
const contractName = 'contract'+Date.now()
// 初始化一个合约实例
let myContract = chain.ctr.contract(contractName, abi)
部署合约
new
用于部署一个合约。
请求参数
参数 | 必选 | 类型 | 说明 |
bytecode | true | string | 目标合约的字节码,为 16 进制表示,无需“0x”作为前缀。 |
data | true | object | 包含‘from’,‘parameters’等配置。 |
data 字段内容
字段 | 必选 | 类型 | 说明 |
from | true | string | 需要配置的当前账户名 |
parameters | false | Array | 如果合约包含初始化函数,并且此函数需要参数列表,可以通过 parameters 传递。 |
示例
java script
const solc = require('@alipay/solc')
const contract = fs.readFileSync('./CreditManager.sol', {encoding: 'ascii'})
// 第二个参数设定为"1",会开启编译优化 optimiser
const output = solc.compile(contract, 1)
const abi = JSON.parse(output.contracts[':CreditManager'].interface)
const bytecode = output.contracts[':CreditManager'].bytecode
// 带上时间戳,防止合约名计算哈希后已存在
const contractName = 'contract'+Date.now()
// 初始化一个合约实例
let myContract = chain.ctr.contract(contractName, abi)
// 部署合约,可传递初始化函数需要的参数
myContract.new(bytecode, {
from: 'Tester001',
// parameters: [param1, param2]
}, (err, contract, data) => {
console.log(data)
})
调用合约
直接使用目标合约方法名,完成合约方法调用。
请求参数
参数 | 必选 | 类型 | 说明 |
parameters | false | 不确定 | 参考具体的合约方法参数列表,参数个数不确定。 |
data | true | object | 包含‘from’配置 |
data 字段内容
字段 | 必选 | 类型 | 说明 |
from | true | string | 需要配置的当前账户名。 |
示例 1
在部署合约的回调函数中,调用合约方法:
java script
myContract.new(bytecode, {
from: 'Tester001',
// parameters: [param1, param2]
}, (err, contract, data) => {
console.log(data)
// 调用合约方法‘Issue’,Issue 方法需要两个参数:第一个参数是一个账户 identity,第二个参数是一个数值
myContract.Issue('0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5', 100 , { from: 'Tester001' }, (err, output, data) => {
console.log('output is:', output)
})
})
示例 2
在部署合约之后,调用合约方法。此方式连续写多个合约调用,会并发调用,如果有调用依赖,可使用回调。
java script
myContract.new(bytecode, {
from: 'Tester001',
// parameters: [param1, param2]
}, (err, contract, data) => {
console.log(data)
})
// 调用合约方法‘Issue’
myContract.Issue('0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5', 100 , { from: 'Tester001' }, (err, output, data) => {
console.log('output is:', output)
})
升级合约
update
对一个链上已存在合约进行升级。谨慎使用合约升级功能,合约升级需要兼容新旧合约的数据存储,正常情况下,不建议使用。
请求参数
参数 | 必选 | 类型 | 说明 |
bytecode | true | string | 目标合约的字节码,为 16 进制表示,无需“0x”作为前缀。 |
data | true | object | 空的 object |
示例 1
在部署合约的回调函数中,调用合约方法。
java script
myContract.update(bytecode, {}
, (err, contract, data) => {
console.log('contract update result:', data)
})
升级合约时,传入的bytecode
字节码是runtime
字节码,使用二进制 solc 编译工具的--bin-runtime
参数可以直接得到,是正常--bin
参数编译的字节码的子集。runtime
字节码通过本地执行合约部署操作也可获取到。
示例 2
通过本地合约部署获取 runtime
字节码用于升级合约:(可直接使用 solc-js)solc-js 工具在 JS 代码中默认使用 --bin
参数编译合约得到字节码,此字节码不能直接用于合约升级,但是通过一次“本地合约部署”之后即可得到 runtime
字节码用于合约升级使用。
仅适用于Solidity-0.4.24版本,0.6.4及以上版本请使用二进制solc编译工具获取runtime
字节码。
使用 solc-js 来完成合约升级,步骤如下:
solc-js 编译合约 A;
部署合约 A;
调用合约 A;
改合约代码得到合约 A’;
solc-js 编译合约 A’;
本地部署合约 A’ 获取
runtime
字节码;使用 A’
runtime
字节码升级合约 A;调用合约 A 验证升级情况。
const solc = require('@alipay/solc')
const contract = fs.readFileSync('./CreditManager.sol', {encoding: 'ascii'})
// 第二个参数设定为 "1",会开启编译优化 optimiser
const output = solc.compile(contract, 1)
const abi = JSON.parse(output.contracts[':CreditManager'].interface)
const bytecode = output.contracts[':CreditManager'].bytecode
console.log('bytecode:', bytecode)
// 带上时间戳,防止合约名计算哈希后已存在
let contractName = 'contract'+Date.now()
let myContract = chain.ctr.contract(contractName, abi)
// 使用 solc-js 编译结果字节码升级合约说明
myContract.new(bytecode, {
from: 'Tester001',
}, (err, contract, data) => {
console.log('contract new:', data)
myContract.Issue('0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5', 100 , { from: 'Tester001' }, (err, output, data) => {
console.log('output1 is:', output)
//改动合约 CreditManager.sol 计算逻辑(注意不可改动合约存储定义),然后得到新的 CreditManagerPro.sol 合约
//改动内容为:Issue方法内`credit[account] += value;`改为`credit[account] += value * 2;`
const contract_pro = fs.readFileSync('./CreditManagerPro.sol', {encoding: 'ascii'})
// 第二个参数设定为"1",会开启编译优化 optimiser
const output_pro = solc.compile(contract_pro, 1)
const abi_pro = JSON.parse(output_pro.contracts[':CreditManager'].interface)
const bytecode_pro = output_pro.contracts[':CreditManager'].bytecode
myContract = chain.ctr.contract(contractName, abi_pro)
myContract.new(bytecode_pro, {
from: 'Tester001',
local: true, //本地执行合约部署,目的为了模拟合约部署获取 runtime 字节码
block_number: data.block_number - 5 //使用之前几个区块,防止合约 ID 冲突
}, (err, contract, data) => {
console.log('local contract new result:', data)
// 使用本地执行合约部署得到的 runtime 字节码升级合约
myContract.update(data.receipt.output.replace('0x' + chain.EVM, ''), {
}, (err, contract, data) => {
console.log('contract update result:', data)
// 调用升级后的合约,发放积分
myContract.Issue('0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5', 100 , { from: 'Tester001' }, (err, output, data) => {
console.log('output2 is:', output.toString())
// 查看目标账户一共被发放的积分结果,若升级合约成功,则结果为:300
myContract.Query('0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5', { from: 'Tester001' }, (err, output, data) => {
console.log('output3 is:', output.toString())
})
})
})
})
})
})
示例执行结果:
...
contract update result: { msg_type: 63,
sequence: 7,
return_code: 0,
group_id: '0x0000000000000000000000000000000000000000',
receipt:
{ result: 0,
gas_used: 28610,
log_entry: [ [Object] ],
output: '' },
block_number: 86114,
transaction_index: 0,
api: 'QueryTransactionReceipt',
txhash:
'0x40a72b99ec64fca0e1cc5025920de086a2523b4761ea5020c0f43a5240dc754a' }
output2 is: true
output3 is: 300