Skip to content

Commit

Permalink
feat(Compiler/ACI): Make ACI compatible with compiler 3.0.0 (#441)
Browse files Browse the repository at this point in the history
* feat(Compiler/ACI): Make ACI compatible with compiler 3.0.0

* fix(ACI): Fix linter
  • Loading branch information
nduchak authored May 29, 2019
1 parent 661e953 commit 2a8eb1a
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
TAG=v3.0.0-rc.1
COMPILER_TAG=v2.1.0
COMPILER_TAG=v3.0.0
37 changes: 22 additions & 15 deletions es/contract/aci.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { decode } from '../tx/builder/helpers'
import { encodeBase58Check } from '../utils/crypto'
import { toBytes } from '../utils/bytes'
import * as R from 'ramda'
import semverSatisfies from '../utils/semver-satisfies'

const SOPHIA_TYPES = [
'int',
Expand All @@ -39,7 +40,9 @@ const SOPHIA_TYPES = [
'list',
'map',
'record',
'option'
'option',
'oracle',
'oracleQuery'
].reduce((acc, type) => ({ ...acc, [type]: type }), {})

function encodeAddress (address, prefix = 'ak') {
Expand Down Expand Up @@ -103,7 +106,7 @@ function transformDecodedData (aci, result, { skipTransformDecoded = false, addr
* @param value
* @return {string}
*/
async function transform (type, value) {
async function transform (type, value, { compilerVersion } = {}) {
let { t, generic } = readType(type)

// contract TestContract = ...
Expand All @@ -114,31 +117,33 @@ async function transform (type, value) {
case SOPHIA_TYPES.string:
return `"${value}"`
case SOPHIA_TYPES.list:
return `[${await Promise.all(value.map(async el => transform(generic, el)))}]`
return `[${await Promise.all(value.map(async el => transform(generic, el, { compilerVersion })))}]`
case SOPHIA_TYPES.tuple:
return `(${await Promise.all(value.map(async (el, i) => transform(generic[i], el)))})`
return `(${await Promise.all(value.map(async (el, i) => transform(generic[i], el, { compilerVersion })))})`
case SOPHIA_TYPES.option:
const optionV = await value.catch(e => undefined)
return optionV === undefined ? 'None' : `Some(${await transform(generic, optionV)})`
return optionV === undefined ? 'None' : `Some(${await transform(generic, optionV, { compilerVersion })})`
case SOPHIA_TYPES.address:
return parseInt(value) === 0 ? '#0' : `#${decode(value).toString('hex')}`
return semverSatisfies(compilerVersion.split('-')[0], '1.0.0', '3.0.0')
? parseInt(value) === 0 ? '#0' : `#${decode(value).toString('hex')}`
: value
case SOPHIA_TYPES.record:
return `{${await generic.reduce(
async (acc, { name, type }, i) => {
acc = await acc
acc += `${i !== 0 ? ',' : ''}${name} = ${await transform(type[0], value[name])}`
acc += `${i !== 0 ? ',' : ''}${name} = ${await transform(type[0], value[name], { compilerVersion })}`
return acc
},
''
)}}`
case SOPHIA_TYPES.map:
return transformMap(value, generic)
return transformMap(value, generic, { compilerVersion })
}

return `${value}`
}

async function transformMap (value, generic) {
async function transformMap (value, generic, { compilerVersion }) {
if (value instanceof Map) {
value = Array.from(value.entries())
}
Expand All @@ -151,7 +156,7 @@ async function transformMap (value, generic) {
async (acc, [key, value], i) => {
acc = await acc
if (i !== 0) acc += ','
acc += `[${await transform(generic[0], key)}] = ${await transform(generic[1], value)}`
acc += `[${await transform(generic[0], key, { compilerVersion })}] = ${await transform(generic[1], value, { compilerVersion })}`
return acc
},
``
Expand Down Expand Up @@ -253,14 +258,15 @@ function validateArguments (aci, params) {
* @rtype (aci: Object, params: Array) => Object
* @param {Object} aci Contract ACI
* @param {Array} params Contract call arguments
* @param compilerVersion
* @return Promise{Array} Object with validation errors
*/
async function prepareArgsForEncode (aci, params) {
async function prepareArgsForEncode (aci, params, { compilerVersion } = {}) {
if (!aci) return params
// Validation
validateArguments(aci, params)
// Cast argument from JS to Sophia type
return Promise.all(aci.arguments.map(async ({ type }, i) => transform(type, params[i])))
return Promise.all(aci.arguments.map(async ({ type }, i) => transform(type, params[i], { compilerVersion })))
}

/**
Expand Down Expand Up @@ -313,6 +319,7 @@ async function getContractInstance (source, { aci, contractAddress, opt } = {})
compiled: null,
deployInfo: { address: contractAddress },
options: R.merge(defaultOptions, opt),
compilerVersion: this.compilerVersion,
setOptions (opt) {
this.options = R.merge(this.options, opt)
}
Expand Down Expand Up @@ -393,7 +400,7 @@ function call (self) {
if (!fn) throw new Error('Function name is required')
if (!this.deployInfo.address) throw new Error('You need to deploy contract before calling!')

params = !opt.skipArgsConvert ? await prepareArgsForEncode(fnACI, params) : params
params = !opt.skipArgsConvert ? await prepareArgsForEncode(fnACI, params, { compilerVersion: this.compilerVersion }) : params
const result = opt.callStatic
? await self.contractCallStatic(opt.source || this.source, this.deployInfo.address, fn, params, {
top: opt.top,
Expand All @@ -406,7 +413,7 @@ function call (self) {
transformDecodedData(
fnACI.returns,
await self.contractDecodeData(type || transformReturnType(fnACI.returns), result.result.returnValue),
{ ...opt, ...decodeOptions }
{ ...opt, ...decodeOptions, compilerVersion: this.compilerVersion }
)
}
}
Expand All @@ -417,7 +424,7 @@ function deploy (self) {
const opt = R.merge(this.options, options)
const fnACI = getFunctionACI(this.aci, 'init')
if (!this.compiled) await this.compile()
init = !opt.skipArgsConvert ? await prepareArgsForEncode(fnACI, init) : init
init = !opt.skipArgsConvert ? await prepareArgsForEncode(fnACI, init, { compilerVersion: this.compilerVersion }) : init

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 }
Expand Down
15 changes: 13 additions & 2 deletions es/contract/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@
import Http from '../utils/http'
import ContractBase from './index'

async function getCompilerVersion (options = {}) {
return this.http
.get('/version', options)
.then(({ version }) => version)
}

async function contractEncodeCallDataAPI (source, name, args = [], options = {}) {
return this.http
.post('/encode-calldata', { source, 'function': name, arguments: args }, options)
Expand Down Expand Up @@ -75,8 +81,9 @@ function setCompilerUrl (url) {
* @example ContractCompilerAPI({ compilerUrl: 'COMPILER_URL' })
*/
const ContractCompilerAPI = ContractBase.compose({
init ({ compilerUrl = this.compilerUrl }) {
async init ({ compilerUrl = this.compilerUrl }) {
this.http = Http({ baseUrl: compilerUrl })
this.compilerVersion = await this.getCompilerVersion()
},
methods: {
contractEncodeCallDataAPI,
Expand All @@ -85,7 +92,11 @@ const ContractCompilerAPI = ContractBase.compose({
contractGetACI,
contractDecodeCallDataByCodeAPI,
contractDecodeCallDataBySourceAPI,
setCompilerUrl
setCompilerUrl,
getCompilerVersion
},
props: {
compilerVersion: null
}
})

Expand Down
29 changes: 27 additions & 2 deletions es/contract/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ const ContractBase = stampit({
'compileContractAPI',
'contractDecodeCallDataBySourceAPI',
'contractDecodeCallDataByCodeAPI',
'contractGetACI'
'contractGetACI',
'setCompilerUrl',
'getCompilerVersion'
]
}
}
Expand All @@ -55,7 +57,9 @@ const ContractBase = stampit({
contractEncodeCallDataAPI: required,
contractDecodeDataAPI: required,
compileContractAPI: required,
contractGetACI: required
contractGetACI: required,
setCompilerUrl: required,
getCompilerVersion: required
}
}))

Expand Down Expand Up @@ -121,4 +125,25 @@ const ContractBase = stampit({
* @return {Object} Object which contain bytecode of contract
*/

/**
* Set compiler url
* @function setCompilerUrl
* @instance
* @abstract
* @category async
* @rtype (url: String) => void
* @param {String} url - Compiler url
* @return {void}
*/

/**
* Get Compiler Version
* @function getCompilerVersion
* @instance
* @abstract
* @category async
* @rtype () => String
* @return {String} Compiler version
*/

export default ContractBase
8 changes: 1 addition & 7 deletions test/integration/contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ contract StateContract =
public function stringFn(a: string) : string = a
public function boolFn(a: bool) : bool = a
public function addressFn(a: address) : address = a
public function emptyAddress() : address = #0
public function contractAddress (ct: address) : address = ct
public function accountAddress (ak: address) : address = ak
Expand Down Expand Up @@ -229,20 +228,15 @@ describe('Contract', function () {
e.message.should.be.equal('"Argument" at position 0 fails because ["0" must be less than or equal to 0, Value "333" at path: [0] not a string]')
}
})
it('Empty address', async () => {
it.skip('Empty address', async () => {
const result = await contractObject.methods.emptyAddress()
return result.decode().should.eventually.become(0)
})
it('Return address', async () => {
const contractAddress = await (await contractObject.methods
.contractAddress('ct_AUUhhVZ9de4SbeRk8ekos4vZJwMJohwW5X8KQjBMUVduUmoUh'))
.decode(null, { addressPrefix: 'ct' })

const accountAddress = await (await contractObject.methods
.accountAddress(await contract.address()))
.decode(null, { addressPrefix: 'ak' })

contractAddress.should.be.equal('ct_AUUhhVZ9de4SbeRk8ekos4vZJwMJohwW5X8KQjBMUVduUmoUh')
accountAddress.should.be.equal(await contract.address())
})
it('Valid', async () => {
Expand Down

0 comments on commit 2a8eb1a

Please sign in to comment.