Skip to content

Commit

Permalink
feat(ACI): Generate JS function proto for each of contract function (#…
Browse files Browse the repository at this point in the history
…439)

* feat(ACI): Generate JS function for contract base on ACI
  • Loading branch information
nduchak authored May 23, 2019
1 parent d0777f5 commit 2f47b4d
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 62 deletions.
74 changes: 54 additions & 20 deletions es/contract/aci.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@
* @export ContractACI
* @example import ContractACI from '@aeternity/aepp-sdk/es/contract/aci'
*/
import Joi from 'joi-browser'

import AsyncInit from '../utils/async-init'
import { decode } from '../tx/builder/helpers'
import { encodeBase58Check } from '../utils/crypto'
import { toBytes } from '../utils/bytes'
import Joi from 'joi-browser'
import * as R from 'ramda'

const SOPHIA_TYPES = [
'int',
Expand All @@ -45,6 +47,7 @@ function encodeAddress (address, prefix = 'ak') {
const encodedAddress = encodeBase58Check(addressBuffer)
return `${prefix}_${encodedAddress}`
}

/**
* Transform decoded data to JS type
* @param aci
Expand Down Expand Up @@ -279,6 +282,8 @@ function getFunctionACI (aci, name) {
* @param {String} source Contract source code
* @param {Object} [options] Options object
* @param {Object} [options.aci] Contract ACI
* @param {Object} [options.contractAddress] Contract address
* @param {Object} [options.opt] Contract options
* @return {ContractInstance} JS Contract API
* @example
* const contractIns = await client.getContractInstance(sourceCode)
Expand All @@ -287,14 +292,30 @@ function getFunctionACI (aci, name) {
* const callResult = await contractIns.call('setState', [123])
* const staticCallResult = await contractIns.call('setState', [123], { callStatic: true })
*/
async function getContractInstance (source, { aci, contractAddress } = {}) {
async function getContractInstance (source, { aci, contractAddress, opt } = {}) {
aci = aci || await this.contractGetACI(source)
const defaultOptions = {
skipArgsConvert: false,
skipTransformDecoded: false,
callStatic: false,
deposit: 0,
gasPrice: 1000000000, // min gasPrice 1e9
amount: 0,
gas: 1600000 - 21000,
top: null, // using for contract call static
waitMined: true,
verify: false
}
const instance = {
interface: aci.interface,
aci: aci.encoded_aci.contract,
source,
compiled: null,
deployInfo: { address: contractAddress }
deployInfo: { address: contractAddress },
options: R.merge(defaultOptions, opt),
setOptions (opt) {
this.options = R.merge(this.options, opt)
}
}
/**
* Compile contract
Expand Down Expand Up @@ -327,6 +348,21 @@ async function getContractInstance (source, { aci, contractAddress } = {}) {
*/
instance.call = call(this).bind(instance)

instance.methods = instance
.aci
.functions
.reduce(
(acc, { name }) => ({
...acc,
[name]: function () {
return name !== 'init'
? instance.call(name, Object.values(arguments))
: instance.deploy(Object.values(arguments))
}
}),
{}
)

return instance
}

Expand All @@ -351,42 +387,40 @@ function transformReturnType (returns) {
}

function call (self) {
return async function (fn, params = [], options = {
skipArgsConvert: false,
skipTransformDecoded: false,
callStatic: false
}) {
return async function (fn, params = [], options = {}) {
const opt = R.merge(this.options, options)
const fnACI = getFunctionACI(this.aci, fn)
if (!fn) throw new Error('Function name is required')
if (!this.deployInfo.address) throw new Error('You need to deploy contract before calling!')

params = !options.skipArgsConvert ? await prepareArgsForEncode(fnACI, params) : params
const result = options.callStatic
? await self.contractCallStatic(this.source, this.deployInfo.address, fn, params, {
top: options.top,
options
params = !opt.skipArgsConvert ? await prepareArgsForEncode(fnACI, params) : params
const result = opt.callStatic
? await self.contractCallStatic(opt.source || this.source, this.deployInfo.address, fn, params, {
top: opt.top,
opt
})
: await self.contractCall(this.source, this.deployInfo.address, fn, params, options)
: await self.contractCall(opt.source || this.source, this.deployInfo.address, fn, params, opt)
return {
...result,
decode: async (type, opt = {}) =>
decode: async (type, decodeOptions = {}) =>
transformDecodedData(
fnACI.returns,
await self.contractDecodeData(type || transformReturnType(fnACI.returns), result.result.returnValue),
{ ...options, ...opt }
{ ...opt, ...decodeOptions }
)
}
}
}

function deploy (self) {
return async function (init = [], options = { skipArgsConvert: false }) {
return async function (init = [], options = {}) {
const opt = R.merge(this.options, options)
const fnACI = getFunctionACI(this.aci, 'init')
if (!this.compiled) await this.compile()
init = !options.skipArgsConvert ? await prepareArgsForEncode(fnACI, init) : init
init = !opt.skipArgsConvert ? await prepareArgsForEncode(fnACI, init) : init

const { owner, transaction, address, createdAt, result } = await self.contractDeploy(this.compiled, this.source, init, options)
this.deployInfo = { owner, transaction, address, createdAt, result }
const { owner, transaction, address, createdAt, result, rawTx } = await self.contractDeploy(this.compiled, opt.source || this.source, init, opt)
this.deployInfo = { owner, transaction, address, createdAt, result, rawTx }
return this
}
}
Expand Down
Loading

0 comments on commit 2f47b4d

Please sign in to comment.