合约接口

对于合约平台,合约操作相关接口使用最为广泛,因此 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 语言,默认配置为 chain.EVM

示例 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 来完成合约升级,步骤如下:

  1. solc-js 编译合约 A;

  2. 部署合约 A;

  3. 调用合约 A;

  4. 改合约代码得到合约 A’;

  5. solc-js 编译合约 A’;

  6. 本地部署合约 A’ 获取 runtime 字节码;

  7. 使用 A’ runtime字节码升级合约 A;

  8. 调用合约 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