Skip to content

Commit 2fae1ea

Browse files
committed
refactor: avoid using wtype property in constructing generic type info for increased fidelity
- also: - refactor: use regular expression to match encoders to the type name
1 parent 865e049 commit 2fae1ea

File tree

5 files changed

+36
-34
lines changed

5 files changed

+36
-34
lines changed

src/encoders.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ import { AssetCls } from './impl/asset'
66

77
export interface GenericTypeInfo {
88
name: string
9-
wtypeName?: string
10-
genericArgs?: GenericTypeInfo[]
9+
genericArgs?: GenericTypeInfo[] | Record<string, GenericTypeInfo>
1110
}
1211

1312
type fromBytes<T> = (val: Uint8Array, typeInfo: GenericTypeInfo) => T
@@ -42,23 +41,20 @@ const transactionTypeFromBytes: fromBytes<TransactionType> = (val) => {
4241

4342
export const encoders = {
4443
account: AccountCls.fromBytes,
45-
Account: AccountCls.fromBytes,
4644
application: ApplicationCls.fromBytes,
47-
Application: ApplicationCls.fromBytes,
4845
asset: AssetCls.fromBytes,
49-
Asset: AssetCls.fromBytes,
50-
bool: booleanFromBytes,
51-
boolean: booleanFromBytes,
46+
'bool(ean)?': booleanFromBytes,
5247
biguint: bigUintFromBytes,
5348
bytes: bytesFromBytes,
5449
string: stringFromBytes,
5550
uint64: uint64FromBytes,
5651
OnCompleteAction: onCompletionFromBytes,
5752
TransactionType: transactionTypeFromBytes,
53+
// 'Tuple<*>': tupleFromBytes,
5854
}
5955

6056
export const getEncoder = <T>(typeInfo: GenericTypeInfo): fromBytes<T> => {
61-
const encoder = encoders[typeInfo.name as keyof typeof encoders]
57+
const encoder = Object.entries(encoders).find(([k, _]) => new RegExp(`^${k}$`, 'i').test(typeInfo.name))?.[1]
6258
if (!encoder) {
6359
throw new Error(`No encoder found for type ${typeInfo.name}`)
6460
}

src/impl/state.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
import { AccountMap } from '../collections/custom-key-map'
1818
import { MAX_BOX_SIZE } from '../constants'
1919
import { lazyContext } from '../context-helpers/internal-context'
20-
import { getEncoder } from '../encoders'
20+
import { GenericTypeInfo, getEncoder } from '../encoders'
2121
import { getGenericTypeInfo } from '../runtime-helpers'
2222
import { asBytes, asBytesCls, asNumber, asUint8Array, conactUint8Arrays, toBytes } from '../util'
2323

@@ -131,7 +131,7 @@ export class BoxCls<TValue> {
131131

132132
private get fromBytes() {
133133
const typeInfo = getGenericTypeInfo(this)
134-
const valueType = typeInfo!.genericArgs![0]
134+
const valueType = (typeInfo!.genericArgs! as GenericTypeInfo[])[0]
135135
return (val: Uint8Array) => getEncoder<TValue>(valueType)(val, valueType)
136136
}
137137

@@ -199,7 +199,7 @@ export class BoxMapCls<TKey, TValue> {
199199

200200
private get fromBytes() {
201201
const typeInfo = getGenericTypeInfo(this)
202-
const valueType = typeInfo!.genericArgs![1]
202+
const valueType = (typeInfo!.genericArgs! as GenericTypeInfo[])[1]
203203
return (val: Uint8Array) => getEncoder<TValue>(valueType)(val, valueType)
204204
}
205205

src/subcontexts/contract-context.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ interface States {
3333
}
3434

3535
const isUint64GenericType = (typeInfo: GenericTypeInfo | undefined) => {
36-
if (!typeInfo?.genericArgs?.length) return false
36+
if (!Array.isArray(typeInfo?.genericArgs) || !typeInfo?.genericArgs?.length) return false
3737
return typeInfo.genericArgs.some((t) => t.name.toLocaleLowerCase() === 'uint64')
3838
}
3939

src/test-transformer/visitors.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -187,22 +187,25 @@ class ClassVisitor {
187187
}
188188
}
189189

190-
const getGenericTypeInfo = (type: PType | PType['wtype']): GenericTypeInfo => {
191-
let genericArgs = undefined
192-
let wtypeName = undefined
193-
if (type instanceof PType) {
194-
genericArgs = typeRegistry.isGeneric(type)
195-
? type.getGenericArgs().map(getGenericTypeInfo)
196-
: type.wtype && Object.hasOwn(type.wtype, 'types')
197-
? (type.wtype as DeliberateAny).types.map(getGenericTypeInfo)
198-
: undefined
199-
wtypeName = type.wtype?.name
200-
} else if (type) {
201-
genericArgs = Object.hasOwn(type, 'types') ? (type as DeliberateAny).types.map(getGenericTypeInfo) : undefined
202-
wtypeName = type.name
190+
const getGenericTypeInfo = (type: PType): GenericTypeInfo => {
191+
let genericArgs: GenericTypeInfo[] | Record<string, GenericTypeInfo> | undefined = typeRegistry.isGeneric(type)
192+
? type.getGenericArgs().map(getGenericTypeInfo)
193+
: undefined
194+
195+
if (!genericArgs || !genericArgs.length) {
196+
if (Object.hasOwn(type, 'items')) {
197+
genericArgs = (type as DeliberateAny).items.map(getGenericTypeInfo)
198+
} else if (Object.hasOwn(type, 'itemType')) {
199+
genericArgs = [getGenericTypeInfo((type as DeliberateAny).itemType)]
200+
} else if (Object.hasOwn(type, 'properties')) {
201+
genericArgs = Object.fromEntries(
202+
Object.entries((type as DeliberateAny).properties).map(([key, value]) => [key, getGenericTypeInfo(value as PType)]),
203+
)
204+
}
203205
}
204-
const result: GenericTypeInfo = { name: type?.name ?? 'unknown', wtypeName: wtypeName }
205-
if (genericArgs && genericArgs.length) {
206+
207+
const result: GenericTypeInfo = { name: type?.name ?? 'unknown' }
208+
if (genericArgs && (genericArgs.length || Object.keys(genericArgs).length)) {
206209
result.genericArgs = genericArgs
207210
}
208211
return result

tests/artifacts/box-contract/contract.algo.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
import { arc4, assert, Box, bytes, Bytes, op, TransactionType, uint64, Uint64 } from '@algorandfoundation/algorand-typescript'
1+
import { arc4, assert, Box, Bytes, op, TransactionType, uint64, Uint64 } from '@algorandfoundation/algorand-typescript'
2+
3+
// type X = [a: uint64, b: string, c: bytes]
4+
// type Y = { a: uint64; b: string; c: bytes }
5+
// type Z = { x: X; y: Y }
26

3-
type X = [a: uint64, b: string, c: bytes]
4-
type Y = { a: uint64; b: string; c: bytes }
5-
type Z = { x: X; y: Y }
67
export class BoxContract extends arc4.Contract {
78
oca = Box<arc4.OnCompleteAction>({ key: Bytes('oca') })
89
txn = Box<TransactionType>({ key: Bytes('txn') })
9-
a = Box<X>({ key: Bytes('a') })
10-
b = Box<Y>({ key: Bytes('b') })
11-
c = Box<Z>({ key: Bytes('c') })
10+
11+
// a = Box<X>({ key: Bytes('a') })
12+
// b = Box<Y>({ key: Bytes('b') })
13+
// c = Box<Z>({ key: Bytes('c') })
14+
// d = Box<Z[]>({ key: Bytes('d') })
1215

1316
@arc4.abimethod()
1417
public storeEnums(): void {

0 commit comments

Comments
 (0)