11import { BigUintCompat , Bytes , bytes , internal , StringCompat , Uint64Compat } from '@algorandfoundation/algorand-typescript'
2- import { BitSize , Bool , Byte , Str , UFixedNxM , UintN } from '@algorandfoundation/algorand-typescript/arc4'
2+ import { ARC4Encoded , BitSize , Bool , Byte , Str , UFixedNxM , UintN } from '@algorandfoundation/algorand-typescript/arc4'
33import { encodingUtil } from '@algorandfoundation/puya-ts'
44import assert from 'assert'
55import { ABI_RETURN_VALUE_LOG_PREFIX , BITS_IN_BYTE , UINT64_SIZE } from '../constants'
6- import { TypeInfo } from '../encoders'
6+ import { fromBytes , TypeInfo } from '../encoders'
77import { DeliberateAny } from '../typescript-helpers'
88import { asBigUint , asBigUintCls , asBytesCls , asUint64 , asUint8Array } from '../util'
99
@@ -15,10 +15,10 @@ type CompatForArc4Int<N extends BitSize> = N extends 8 | 16 | 32 | 64 ? Uint64Co
1515export class UintNImpl < N extends BitSize > extends UintN < N > {
1616 private value : Uint8Array
1717 private bitSize : N
18- private typeInfo : TypeInfo
18+ typeInfo : TypeInfo
1919
2020 constructor ( typeInfoString : string , v ?: CompatForArc4Int < N > ) {
21- super ( v )
21+ super ( )
2222 this . typeInfo = JSON . parse ( typeInfoString )
2323 this . bitSize = parseInt ( ( this . typeInfo . genericArgs as TypeInfo [ ] ) ! [ 0 ] . name , 10 ) as N
2424
@@ -40,32 +40,45 @@ export class UintNImpl<N extends BitSize> extends UintN<N> {
4040 return Bytes ( this . value )
4141 }
4242
43- static fromBytesImpl ( typeInfo : string , value : internal . primitives . StubBytesCompat ) : UintNImpl < BitSize > {
44- const result = new UintNImpl < BitSize > ( typeInfo )
45- result . value = asUint8Array ( value )
46- return result
43+ equals ( other : this) : boolean {
44+ if ( ! ( other instanceof UintNImpl ) || JSON . stringify ( this . typeInfo ) !== JSON . stringify ( other . typeInfo ) ) {
45+ throw new internal . errors . CodeError ( `Expected expression of type ${ this . typeInfo . name } , got ${ other . typeInfo . name } ` )
46+ }
47+ return this . bytes . equals ( other . bytes )
4748 }
4849
49- static fromLogImpl ( typeInfo : string , value : internal . primitives . StubBytesCompat ) : UintNImpl < BitSize > {
50- const bytesValue = asBytesCls ( value )
51- assert ( bytesValue . slice ( 0 , 4 ) . equals ( ABI_RETURN_VALUE_LOG_PREFIX ) , 'ABI return prefix not found' )
52- return UintNImpl . fromBytesImpl ( typeInfo , bytesValue . slice ( 4 ) )
50+ static fromBytesImpl (
51+ value : internal . primitives . StubBytesCompat | Uint8Array ,
52+ typeInfo : string | TypeInfo ,
53+ prefix : 'none' | 'log' = 'none' ,
54+ ) : UintNImpl < BitSize > {
55+ let bytesValue = asBytesCls ( value )
56+ if ( prefix === 'log' ) {
57+ assert ( bytesValue . slice ( 0 , 4 ) . equals ( ABI_RETURN_VALUE_LOG_PREFIX ) , 'ABI return prefix not found' )
58+ bytesValue = bytesValue . slice ( 4 )
59+ }
60+ const typeInfoString = typeof typeInfo === 'string' ? typeInfo : JSON . stringify ( typeInfo )
61+ const result = new UintNImpl < BitSize > ( typeInfoString )
62+ result . value = asUint8Array ( bytesValue )
63+ return result
5364 }
5465}
5566
5667const regExpNxM = ( maxPrecision : number ) => new RegExp ( `^\\d*\\.?\\d{0,${ maxPrecision } }$` )
5768const trimTrailingDecimalZeros = ( v : string ) => v . replace ( / ( \d + \. \d * ?) 0 + $ / , '$1' ) . replace ( / \. $ / , '' )
69+ type uFixedNxMGenericArgs = { n : TypeInfo ; m : TypeInfo }
5870export class UFixedNxMImpl < N extends BitSize , M extends number > extends UFixedNxM < N , M > {
5971 private value : Uint8Array
60- private typeInfo : TypeInfo
6172 private bitSize : N
6273 private precision : M
74+ private typeInfo : TypeInfo
6375
6476 constructor ( typeInfoString : string , v : `${number } .${number } `) {
6577 super ( v )
6678 this . typeInfo = JSON . parse ( typeInfoString )
67- this . bitSize = parseInt ( ( this . typeInfo . genericArgs as TypeInfo [ ] ) ! [ 0 ] . name , 10 ) as N
68- this . precision = parseInt ( ( this . typeInfo . genericArgs as TypeInfo [ ] ) ! [ 1 ] . name , 10 ) as M
79+ const genericArgs = this . typeInfo . genericArgs as uFixedNxMGenericArgs
80+ this . bitSize = parseInt ( genericArgs . n . name , 10 ) as N
81+ this . precision = parseInt ( genericArgs . m . name , 10 ) as M
6982
7083 const trimmedValue = trimTrailingDecimalZeros ( v )
7184 assert ( regExpNxM ( this . precision ) . test ( trimmedValue ) , `expected positive decimal literal with max of ${ this . precision } decimal places` )
@@ -86,28 +99,34 @@ export class UFixedNxMImpl<N extends BitSize, M extends number> extends UFixedNx
8699 return Bytes ( this . value )
87100 }
88101
89- equals ( other : UFixedNxM < DeliberateAny , DeliberateAny > ) : boolean {
90- const otherImpl = other as UFixedNxMImpl < DeliberateAny , DeliberateAny >
91- return this . bitSize === otherImpl . bitSize && this . precision === otherImpl . precision && this . value === otherImpl . value
102+ equals ( other : this) : boolean {
103+ if ( ! ( other instanceof UFixedNxMImpl ) || JSON . stringify ( this . typeInfo ) !== JSON . stringify ( other . typeInfo ) ) {
104+ throw new internal . errors . CodeError ( `Expected expression of type ${ this . typeInfo . name } , got ${ other . typeInfo . name } ` )
105+ }
106+ return this . bytes . equals ( other . bytes )
92107 }
93108
94- static fromBytesImpl ( typeInfo : string , value : internal . primitives . StubBytesCompat ) : UFixedNxM < BitSize , number > {
95- const result = new UFixedNxMImpl < BitSize , number > ( typeInfo , '0.0' )
96- result . value = asUint8Array ( value )
109+ static fromBytesImpl (
110+ value : internal . primitives . StubBytesCompat | Uint8Array ,
111+ typeInfo : string | TypeInfo ,
112+ prefix : 'none' | 'log' = 'none' ,
113+ ) : UFixedNxM < BitSize , number > {
114+ let bytesValue = asBytesCls ( value )
115+ if ( prefix === 'log' ) {
116+ assert ( bytesValue . slice ( 0 , 4 ) . equals ( ABI_RETURN_VALUE_LOG_PREFIX ) , 'ABI return prefix not found' )
117+ bytesValue = bytesValue . slice ( 4 )
118+ }
119+ const typeInfoString = typeof typeInfo === 'string' ? typeInfo : JSON . stringify ( typeInfo )
120+ const result = new UFixedNxMImpl < BitSize , number > ( typeInfoString , '0.0' )
121+ result . value = asUint8Array ( bytesValue )
97122 return result
98123 }
99-
100- static fromLogImpl ( typeInfo : string , value : internal . primitives . StubBytesCompat ) : UFixedNxM < BitSize , number > {
101- const bytesValue = asBytesCls ( value )
102- assert ( bytesValue . slice ( 0 , 4 ) . equals ( ABI_RETURN_VALUE_LOG_PREFIX ) , 'ABI return prefix not found' )
103- return UFixedNxMImpl . fromBytesImpl ( typeInfo , bytesValue . slice ( 4 ) )
104- }
105124}
106125
107126export class ByteImpl extends Byte {
108127 private value : UintNImpl < 8 >
109128
110- constructor ( typeInfoString : string , v : CompatForArc4Int < 8 > ) {
129+ constructor ( typeInfoString : string , v ? : CompatForArc4Int < 8 > ) {
111130 super ( v )
112131 this . value = new UintNImpl < 8 > ( typeInfoString , v )
113132 }
@@ -120,19 +139,30 @@ export class ByteImpl extends Byte {
120139 return this . value . bytes
121140 }
122141
123- static fromBytesImpl ( typeInfo : string , value : internal . primitives . StubBytesCompat ) : Byte {
124- return UintNImpl . fromBytesImpl ( typeInfo , value ) as Byte
142+ equals ( other : this) : boolean {
143+ if ( ! ( other instanceof ByteImpl ) || JSON . stringify ( this . value . typeInfo ) !== JSON . stringify ( other . value . typeInfo ) ) {
144+ throw new internal . errors . CodeError ( `Expected expression of type ${ this . value . typeInfo . name } , got ${ other . value . typeInfo . name } ` )
145+ }
146+ return this . bytes . equals ( other . bytes )
125147 }
126148
127- static fromLogImpl ( typeInfo : string , value : internal . primitives . StubBytesCompat ) : Byte {
128- return UintNImpl . fromLogImpl ( typeInfo , value ) as Byte
149+ static fromBytesImpl (
150+ value : internal . primitives . StubBytesCompat | Uint8Array ,
151+ typeInfo : string | TypeInfo ,
152+ prefix : 'none' | 'log' = 'none' ,
153+ ) : ByteImpl {
154+ const uintNValue = UintNImpl . fromBytesImpl ( value , typeInfo , prefix ) as UintNImpl < 8 >
155+ const typeInfoString = typeof typeInfo === 'string' ? typeInfo : JSON . stringify ( typeInfo )
156+ const result = new ByteImpl ( typeInfoString )
157+ result . value = uintNValue
158+ return result
129159 }
130160}
131161
132162export class StrImpl extends Str {
133163 private value : Uint8Array
134164
135- constructor ( s ?: StringCompat ) {
165+ constructor ( _typeInfoString : string , s ?: StringCompat ) {
136166 super ( )
137167 const bytesValue = asBytesCls ( s ?? '' )
138168 const bytesLength = encodeLength ( bytesValue . length . asNumber ( ) )
@@ -146,16 +176,27 @@ export class StrImpl extends Str {
146176 return Bytes ( this . value )
147177 }
148178
149- static fromBytesImpl ( bytes : internal . primitives . StubBytesCompat ) : StrImpl {
150- const strValue = new StrImpl ( )
151- strValue . value = asUint8Array ( bytes )
152- return strValue
179+ equals ( other : this) : boolean {
180+ if ( ! ( other instanceof StrImpl ) ) {
181+ throw new internal . errors . CodeError ( `Expected expression of type ${ Str } , got ${ ( other as object ) . constructor . name } ` )
182+ }
183+ return this . bytes . equals ( other . bytes )
153184 }
154185
155- static fromLogImpl ( value : internal . primitives . StubBytesCompat ) : StrImpl {
156- const bytesValue = asBytesCls ( value )
157- assert ( bytesValue . slice ( 0 , 4 ) . equals ( ABI_RETURN_VALUE_LOG_PREFIX ) , 'ABI return prefix not found' )
158- return StrImpl . fromBytesImpl ( bytesValue . slice ( 4 ) )
186+ static fromBytesImpl (
187+ value : internal . primitives . StubBytesCompat | Uint8Array ,
188+ typeInfo : string | TypeInfo ,
189+ prefix : 'none' | 'log' = 'none' ,
190+ ) : StrImpl {
191+ let bytesValue = asBytesCls ( value )
192+ if ( prefix === 'log' ) {
193+ assert ( bytesValue . slice ( 0 , 4 ) . equals ( ABI_RETURN_VALUE_LOG_PREFIX ) , 'ABI return prefix not found' )
194+ bytesValue = bytesValue . slice ( 4 )
195+ }
196+ const typeInfoString = typeof typeInfo === 'string' ? typeInfo : JSON . stringify ( typeInfo )
197+ const result = new StrImpl ( typeInfoString )
198+ result . value = asUint8Array ( bytesValue )
199+ return result
159200 }
160201}
161202const TRUE_BIGINT_VALUE = 128n
@@ -164,7 +205,7 @@ const FALSE_BIGINT_VALUE = 0n
164205export class BoolImpl extends Bool {
165206 private value : Uint8Array
166207
167- constructor ( v ?: boolean ) {
208+ constructor ( _typeInfoString : string , v ?: boolean ) {
168209 super ( v )
169210 this . value = encodingUtil . bigIntToUint8Array ( v ? TRUE_BIGINT_VALUE : FALSE_BIGINT_VALUE , 1 )
170211 }
@@ -177,15 +218,43 @@ export class BoolImpl extends Bool {
177218 return Bytes ( this . value )
178219 }
179220
180- static fromBytesImpl ( value : internal . primitives . StubBytesCompat ) : BoolImpl {
181- const result = new BoolImpl ( )
182- result . value = asUint8Array ( value )
221+ static fromBytesImpl (
222+ value : internal . primitives . StubBytesCompat | Uint8Array ,
223+ typeInfo : string | TypeInfo ,
224+ prefix : 'none' | 'log' = 'none' ,
225+ ) : BoolImpl {
226+ let bytesValue = asBytesCls ( value )
227+ if ( prefix === 'log' ) {
228+ assert ( bytesValue . slice ( 0 , 4 ) . equals ( ABI_RETURN_VALUE_LOG_PREFIX ) , 'ABI return prefix not found' )
229+ bytesValue = bytesValue . slice ( 4 )
230+ }
231+ const typeInfoString = typeof typeInfo === 'string' ? typeInfo : JSON . stringify ( typeInfo )
232+ const result = new BoolImpl ( typeInfoString )
233+ result . value = asUint8Array ( bytesValue )
183234 return result
184235 }
236+ }
237+
238+ export function interpretAsArc4Impl < T extends ARC4Encoded > (
239+ typeInfoString : string ,
240+ bytes : internal . primitives . StubBytesCompat ,
241+ prefix : 'none' | 'log' = 'none' ,
242+ ) : T {
243+ const typeInfo = JSON . parse ( typeInfoString )
244+ return getArc4Encoder < T > ( typeInfo ) ( bytes , typeInfo , prefix )
245+ }
185246
186- static fromLogImpl ( value : internal . primitives . StubBytesCompat ) : BoolImpl {
187- const bytesValue = asBytesCls ( value )
188- assert ( bytesValue . slice ( 0 , 4 ) . equals ( ABI_RETURN_VALUE_LOG_PREFIX ) , 'ABI return prefix not found' )
189- return BoolImpl . fromBytesImpl ( bytesValue . slice ( 4 ) )
247+ export const arc4Encoders : Record < string , fromBytes < DeliberateAny > > = {
248+ Bool : BoolImpl . fromBytesImpl ,
249+ Byte : ByteImpl . fromBytesImpl ,
250+ Str : StrImpl . fromBytesImpl ,
251+ 'UintN<.*>' : UintNImpl . fromBytesImpl ,
252+ 'UFixedNxM<.*>' : UFixedNxMImpl . fromBytesImpl ,
253+ }
254+ export const getArc4Encoder = < T > ( typeInfo : TypeInfo , encoders ?: Record < string , fromBytes < DeliberateAny > > ) : fromBytes < T > => {
255+ const encoder = Object . entries ( encoders ?? arc4Encoders ) . find ( ( [ k , _ ] ) => new RegExp ( `^${ k } $` , 'i' ) . test ( typeInfo . name ) ) ?. [ 1 ]
256+ if ( ! encoder ) {
257+ throw new Error ( `No encoder found for type ${ typeInfo . name } ` )
190258 }
259+ return encoder as fromBytes < T >
191260}
0 commit comments