2424 */
2525import AsyncInit from '../utils/async-init'
2626import { decode } from '../tx/builder/helpers'
27- import { aeEncodeKey } from '../utils/crypto'
27+ import { encodeBase58Check } from '../utils/crypto'
2828import { toBytes } from '../utils/bytes'
2929
3030const SOPHIA_TYPES = [
@@ -34,7 +34,8 @@ const SOPHIA_TYPES = [
3434 'address' ,
3535 'bool' ,
3636 'list' ,
37- 'map'
37+ 'map' ,
38+ 'record'
3839] . reduce ( ( acc , type , i ) => {
3940 acc [ type ] = type
4041 return acc
@@ -47,7 +48,12 @@ const SOPHIA_TYPES = [
4748 * @return {string }
4849 */
4950function transform ( type , value ) {
50- const { t, generic } = readType ( type )
51+ let { t, generic } = readType ( type )
52+
53+ // contract TestContract = ...
54+ // fn(ct: TestContract)
55+ if ( typeof value === 'string' && value . slice ( 0 , 2 ) === 'ct' ) t = SOPHIA_TYPES . address // Handle Contract address transformation
56+
5157 switch ( t ) {
5258 case SOPHIA_TYPES . string :
5359 return `"${ value } "`
@@ -56,8 +62,18 @@ function transform (type, value) {
5662 case SOPHIA_TYPES . tuple :
5763 return `(${ value . map ( ( el , i ) => transform ( generic [ i ] , el ) ) } )`
5864 case SOPHIA_TYPES . address :
59- return `#${ decode ( value , 'ak' ) . toString ( 'hex' ) } `
65+ return `#${ decode ( value ) . toString ( 'hex' ) } `
66+ case SOPHIA_TYPES . record :
67+ return `{${ generic . reduce (
68+ ( acc , { name, type } , i ) => {
69+ if ( i !== 0 ) acc += ','
70+ acc += `${ name } = ${ transform ( type [ 0 ] , value [ name ] ) } `
71+ return acc
72+ } ,
73+ ''
74+ ) } }`
6075 }
76+
6177 return `${ value } `
6278}
6379
@@ -101,21 +117,29 @@ function validate (type, value) {
101117 }
102118}
103119
120+ function encodeAddress ( address , prefix = 'ak' ) {
121+ const addressBuffer = Buffer . from ( address , 'hex' )
122+ const encodedAddress = encodeBase58Check ( addressBuffer )
123+ return `${ prefix } _${ encodedAddress } `
124+ }
104125/**
105126 * Transform decoded data to JS type
106127 * @param aci
107128 * @param result
108129 * @param transformDecodedData
109130 * @return {* }
110131 */
111- function transformDecodedData ( aci , result , { skipTransformDecoded = false } = { } ) {
132+ function transformDecodedData ( aci , result , { skipTransformDecoded = false , addressPrefix = 'ak' } = { } ) {
112133 if ( skipTransformDecoded ) return result
113134 const { t, generic } = readType ( aci , true )
135+
114136 switch ( t ) {
115137 case SOPHIA_TYPES . bool :
116138 return ! ! result . value
117139 case SOPHIA_TYPES . address :
118- return aeEncodeKey ( toBytes ( result . value , true ) )
140+ return result . value === 0
141+ ? 0
142+ : encodeAddress ( toBytes ( result . value , true ) , addressPrefix )
119143 case SOPHIA_TYPES . map :
120144 const [ keyT , valueT ] = generic
121145 return result . value
@@ -132,6 +156,15 @@ function transformDecodedData (aci, result, { skipTransformDecoded = false } = {
132156 return result . value . map ( ( { value } ) => transformDecodedData ( generic , { value } ) )
133157 case SOPHIA_TYPES . tuple :
134158 return result . value . map ( ( { value } , i ) => { return transformDecodedData ( generic [ i ] , { value } ) } )
159+ case SOPHIA_TYPES . record :
160+ return result . value . reduce (
161+ ( acc , { name, value } , i ) =>
162+ ( {
163+ ...acc ,
164+ [ generic [ i ] . name ] : transformDecodedData ( generic [ i ] . type , { value } )
165+ } ) ,
166+ { }
167+ )
135168 }
136169 return result . value
137170}
@@ -229,24 +262,32 @@ async function getContractInstance (source, { aci, contractAddress } = {}) {
229262 return instance
230263}
231264
265+ // @TODO Remove after compiler can decode using type from ACI
232266function transformReturnType ( returns ) {
233- if ( typeof returns === 'string' ) return returns
234- if ( typeof returns === 'object' ) {
235- const [ [ key , value ] ] = Object . entries ( returns )
236- return `${ key !== 'tuple' ? key : '' } (${ value
237- . reduce (
238- ( acc , el , i ) => {
239- if ( i !== 0 ) acc += ','
240- acc += transformReturnType ( el )
241- return acc
242- } ,
243- '' )
244- } )`
267+ try {
268+ if ( typeof returns === 'string' ) return returns
269+ if ( typeof returns === 'object' ) {
270+ const [ [ key , value ] ] = Object . entries ( returns )
271+ return `${ key !== 'tuple' && key !== 'record' ? key : '' } (${ value
272+ . reduce (
273+ ( acc , el , i ) => {
274+ if ( i !== 0 ) acc += ','
275+ acc += transformReturnType ( key !== 'record' ? el : el . type [ 0 ] )
276+ return acc
277+ } ,
278+ '' ) } )`
279+ }
280+ } catch ( e ) {
281+ return null
245282 }
246283}
247284
248285function call ( self ) {
249- return async function ( fn , params = [ ] , options = { skipArgsConvert : false , skipTransformDecoded : false , callStatic : false } ) {
286+ return async function ( fn , params = [ ] , options = {
287+ skipArgsConvert : false ,
288+ skipTransformDecoded : false ,
289+ callStatic : false
290+ } ) {
250291 const fnACI = getFunctionACI ( this . aci , fn )
251292 if ( ! fn ) throw new Error ( 'Function name is required' )
252293 if ( ! this . deployInfo . address ) throw new Error ( 'You need to deploy contract before calling!' )
@@ -258,10 +299,14 @@ function call (self) {
258299 options
259300 } )
260301 : await self . contractCall ( this . source , this . deployInfo . address , fn , params , options )
261- const returnType = await transformReturnType ( fnACI . returns )
262302 return {
263303 ...result ,
264- decode : async ( ) => transformDecodedData ( fnACI . returns , await self . contractDecodeData ( returnType , result . result . returnValue ) , options )
304+ decode : async ( type , opt = { } ) =>
305+ transformDecodedData (
306+ fnACI . returns ,
307+ await self . contractDecodeData ( type || transformReturnType ( fnACI . returns ) , result . result . returnValue ) ,
308+ { ...options , ...opt }
309+ )
265310 }
266311 }
267312}
0 commit comments