对于合约平台,合约操作相关接口使用最为广泛,因此 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