Skip to content

Commit

Permalink
feat: support fromBytes and fromLog methods for ARC4Encoded types
Browse files Browse the repository at this point in the history
commit 7492df1
Author: Bobby Lat <[email protected]>
Date:   Wed Nov 27 18:06:01 2024 +0800

    chore: update cross-spawn version and vulnerability number to match the report

commit 817d0ed
Author: Bobby Lat <[email protected]>
Date:   Wed Nov 27 17:42:48 2024 +0800

    chore: update approval output after upgrading puyapy to the latest in main branch

commit 6c31fad
Author: Bobby Lat <[email protected]>
Date:   Wed Nov 27 13:04:47 2024 +0800

    refactor: according to review feedback
    - un-export NativeForArc4Int and CompatForArc4Int types
    - add code invariant to make sure ptype is actually ARC4EncodedType
    - rename arc4Prefix to arc4ReturnValuePrefix for clarity
    - remove extra handling of bytes constant as reinterpretCast does it anyway
    - remove extra resolveToPType calls

commit 1caf84d
Author: Bobby Lat <[email protected]>
Date:   Wed Nov 27 10:21:00 2024 +0800

    chore: fix test broken by upgrading puya to version 4.0.0

commit 2f2273e
Author: Bobby Lat <[email protected]>
Date:   Wed Nov 27 09:35:43 2024 +0800

    chore: upgrade localnet images in ci pipline

commit 66167f4
Author: Bobby Lat <[email protected]>
Date:   Tue Nov 26 14:17:08 2024 +0800

    refactor: simplify type info passed to fromBytes and fromLog method

commit 72dc26a
Author: Bobby Lat <[email protected]>
Date:   Tue Nov 19 15:10:49 2024 +0800

    chore: upgrade algots package version

commit 0c66e7c
Author: Bobby Lat <[email protected]>
Date:   Tue Nov 19 15:04:21 2024 +0800

    chore: fix npm audit vulnerability

commit 334848f
Author: Bobby Lat <[email protected]>
Date:   Tue Nov 12 09:49:09 2024 +0800

    refactor: remove n and m parameters from UFixedNxM constructor

commit be7cc30
Author: Bobby Lat <[email protected]>
Date:   Mon Nov 18 16:36:20 2024 +0800

    feat: support fromBytes and fromLog methods for ARC4Encoded types

commit 269360a
Author: Bobby Lat <[email protected]>
Date:   Mon Nov 18 10:55:34 2024 +0800

    feat: support calling equals method of bytes type

commit c623cb3
Author: Bobby Lat <[email protected]>
Date:   Mon Nov 11 11:29:10 2024 +0800

    refactor: export more arc4 types for stub implementation

commit 848216b
Author: Bobby Lat <[email protected]>
Date:   Fri Nov 8 14:42:01 2024 +0800

    refactor: export generic PTypes for testing package to capture generic type info

commit 4fbcb01
Author: Bobby Lat <[email protected]>
Date:   Fri Nov 8 11:08:51 2024 +0800

    fix: LogicSig has been renamed to LogicSigClassModel

commit 21e747a
Merge: ccec890 9fcfe48
Author: boblat <[email protected]>
Date:   Tue Nov 12 10:51:05 2024 +0800

    Merge pull request #45 from algorandfoundation/refactor-export-types

    refactor: export more PTypes for the testing packageto capture generic type info

commit 9fcfe48
Author: Bobby Lat <[email protected]>
Date:   Tue Nov 12 10:46:17 2024 +0800

    refactor: export more PTypes for the testing packageto capture generict type info
  • Loading branch information
tristanmenzel committed Nov 27, 2024
1 parent 5451cd3 commit 08912a1
Show file tree
Hide file tree
Showing 21 changed files with 754 additions and 306 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/ci-all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ jobs:
diffTextLen=${#diffText}
[ $diffTextLen -eq 0 ] && echo 'No changes' || (echo 'ERROR: Code differs to published version. Please bump package version or revert changes'; echo $diffText; exit 1;)
ci-puya-ts:
name: 'Build @algorandfoundation/puya-ts'
uses: ./.github/workflows/node-ci.yml
Expand All @@ -48,5 +47,5 @@ jobs:
python-version: 3.12.6
pre-test-script: |
pipx install algokit --python 3.12.6
algokit localnet start
algokit localnet reset --update
pipx install git+https://github.com/algorandfoundation/puya --python 3.12.6
5 changes: 5 additions & 0 deletions .nsprc
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
{
"1100563": {
"active": true,
"notes": "Waiting for https://github.com/npm/cli/issues/7902 to be resolved",
"expiry": "2024-12-31"
}
}
3 changes: 1 addition & 2 deletions packages/algo-ts/.nsprc
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
{
}
{}
94 changes: 88 additions & 6 deletions packages/algo-ts/src/arc4/encoded-types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { avmError, avmInvariant } from '../impl/errors'
import { arrayUtil, BytesCls, getNumber, getUint8Array, isBytes, isUint64 } from '../impl/primitives'
import { biguint, BigUintCompat, Bytes, bytes, BytesBacked, StringCompat, Uint64, uint64, Uint64Compat } from '../primitives'
import { biguint, BigUintCompat, Bytes, bytes, BytesBacked, BytesCompat, StringCompat, Uint64, uint64, Uint64Compat } from '../primitives'
import { Account } from '../reference'
import { DeliberateAny } from '../typescript-helpers'
import { err } from '../util'

export type BitSize = 8 | 16 | 32 | 64 | 128 | 256 | 512
Expand All @@ -19,6 +20,14 @@ abstract class ARC4Encoded implements BytesBacked {
equals(other: this): boolean {
return this.bytes.equals(other.bytes)
}

static fromBytes<T extends ARC4Encoded>(this: { new (...args: DeliberateAny): T }, bytes: BytesCompat): T {
throw new Error('todo')
}

static fromLog<T extends ARC4Encoded>(this: { new (...args: DeliberateAny): T }, log: BytesCompat): T {
throw new Error('todo')
}
}

export class Str extends ARC4Encoded {
Expand All @@ -41,27 +50,54 @@ export class UintN<N extends BitSize> extends ARC4Encoded {
get native(): NativeForArc4Int<N> {
throw new Error('TODO')
}

static fromBytes<N extends BitSize, I = UintN<N>>(this: { new (...args: DeliberateAny): I }, bytes: BytesCompat): I {
throw new Error('todo')
}

static fromLog<N extends BitSize, I = UintN<N>>(this: { new (...args: DeliberateAny): I }, log: BytesCompat): I {
throw new Error('todo')
}
}
export class UFixedNxM<N extends BitSize, M extends number> extends ARC4Encoded {
[TypeProperty]?: `arc4.UFixedNxM<${N}x${M}>`
constructor(v: `${number}.${number}`, n?: N, m?: M) {
constructor(v: `${number}.${number}`) {
super()
}

get native(): NativeForArc4Int<N> {
throw new Error('TODO')
}
}

static fromBytes<N extends BitSize, M extends number, I = UFixedNxM<N, M>>(
this: { new (...args: DeliberateAny): I },
bytes: BytesCompat,
): I {
throw new Error('todo')
}

static fromLog<N extends BitSize, M extends number, I = UFixedNxM<N, M>>(this: { new (...args: DeliberateAny): I }, log: BytesCompat): I {
throw new Error('todo')
}
}
export class Byte extends UintN<8> {
constructor(v?: Uint64Compat) {
super(v)
}

static fromBytes<I = Byte>(this: { new (...args: DeliberateAny): I }, bytes: BytesCompat): I {
throw new Error('todo')
}

static fromLog<I = Byte>(this: { new (...args: DeliberateAny): I }, log: BytesCompat): I {
throw new Error('todo')
}
}
export class Bool {
export class Bool extends ARC4Encoded {
[TypeProperty]?: `arc4.Bool`
#v: boolean
constructor(v?: boolean) {
super()
this.#v = v ?? false
}

Expand Down Expand Up @@ -179,8 +215,21 @@ export class StaticArray<TItem extends ARC4Encoded, TLength extends number> exte
copy(): StaticArray<TItem, TLength> {
return new StaticArray<TItem, TLength>(...this.items)
}
}

static fromBytes<TItem extends ARC4Encoded, TLength extends number, I = StaticArray<TItem, TLength>>(
this: { new (...args: DeliberateAny): I },
bytes: BytesCompat,
): I {
throw new Error('todo')
}

static fromLog<TItem extends ARC4Encoded, TLength extends number, I = StaticArray<TItem, TLength>>(
this: { new (...args: DeliberateAny): I },
log: BytesCompat,
): I {
throw new Error('todo')
}
}
export class DynamicArray<TItem extends ARC4Encoded> extends Arc4ReadonlyArray<TItem> {
[TypeProperty]?: `arc4.DynamicArray<${TItem[typeof TypeProperty]}>`
constructor(...items: TItem[]) {
Expand All @@ -207,6 +256,14 @@ export class DynamicArray<TItem extends ARC4Encoded> extends Arc4ReadonlyArray<T
copy(): DynamicArray<TItem> {
return new DynamicArray<TItem>(...this.items)
}

static fromBytes<TItem extends ARC4Encoded, I = DynamicArray<TItem>>(this: { new (...args: DeliberateAny): I }, bytes: BytesCompat): I {
throw new Error('todo')
}

static fromLog<TItem extends ARC4Encoded, I = DynamicArray<TItem>>(this: { new (...args: DeliberateAny): I }, log: BytesCompat): I {
throw new Error('todo')
}
}
type ExpandTupleType<T extends ARC4Encoded[]> = T extends [infer T1 extends ARC4Encoded, ...infer TRest extends ARC4Encoded[]]
? TRest extends []
Expand All @@ -233,6 +290,20 @@ export class Tuple<TTuple extends [ARC4Encoded, ...ARC4Encoded[]]> extends ARC4E
get native(): TTuple {
return this.#items
}

static fromBytes<TTuple extends [ARC4Encoded, ...ARC4Encoded[]], I = Tuple<TTuple>>(
this: { new (...args: TTuple): I },
bytes: BytesCompat,
): I {
throw new Error('todo')
}

static fromLog<TTuple extends [ARC4Encoded, ...ARC4Encoded[]], I = Tuple<TTuple>>(
this: { new (...args: TTuple): I },
log: BytesCompat,
): I {
throw new Error('todo')
}
}

export class Address extends Arc4ReadonlyArray<Byte> {
Expand Down Expand Up @@ -274,9 +345,20 @@ class StructImpl<T extends StructConstraint> extends StructBase {
})
}
}
static fromBytes<T extends StructConstraint, I = StructBase & T>(this: { new (args: T): I }, bytes: BytesCompat): I {
throw new Error('todo')
}

static fromLog<T extends StructConstraint, I = StructBase & T>(this: { new (args: T): I }, log: BytesCompat): I {
throw new Error('todo')
}
}

type StructConstructor = new <T extends StructConstraint>(initial: T) => StructBase & T
type StructConstructor = {
new <T extends StructConstraint>(initial: T): StructBase & T
fromBytes: <T extends StructConstraint>(bytes: BytesCompat) => StructBase & T
fromLog: <T extends StructConstraint>(log: BytesCompat) => StructBase & T
}

export const Struct = StructImpl as StructConstructor

Expand Down
2 changes: 1 addition & 1 deletion packages/algo-ts/src/impl/primitives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ export class BytesCls extends AlgoTsPrimitiveCls {
return new BytesCls(arrayUtil.arrayAt(this.#v, i))
}

slice(start: undefined | StubUint64Compat, end: undefined | StubUint64Compat): BytesCls {
slice(start?: StubUint64Compat, end?: StubUint64Compat): BytesCls {
const sliced = arrayUtil.arraySlice(this.#v, start, end)
return new BytesCls(sliced)
}
Expand Down
2 changes: 1 addition & 1 deletion src/awst_build/context/awst-build-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import type { NodeBuilder } from '../eb'
import type { AppStorageDeclaration } from '../models/app-storage-declaration'
import type { ContractClassModel } from '../models/contract-class-model'
import { CompilationSet } from '../models/contract-class-model'
import type { LogicSigClassModel } from '../models/logic-sig-class-model'
import type { ContractClassPType, PType } from '../ptypes'
import { typeRegistry } from '../type-registry'
import { TypeResolver } from '../type-resolver'
import { EvaluationContext } from './evaluation-context'
import { SwitchLoopContext } from './switch-loop-context'
import { UniqueNameResolver } from './unique-name-resolver'
import type { LogicSigClassModel } from '../models/logic-sig-class-model'

export interface AwstBuildContext {
/**
Expand Down
8 changes: 4 additions & 4 deletions src/awst_build/eb/arc4/arrays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ import { SliceFunctionBuilder } from '../shared/slice-function-builder'
import { UInt64ExpressionBuilder } from '../uint64-expression-builder'
import { requireExpressionOfType } from '../util'
import { parseFunctionArgs } from '../util/arg-parsing'
import { Arc4EncodedBaseExpressionBuilder } from './base'
import { Arc4EncodedBaseClassBuilder, Arc4EncodedBaseExpressionBuilder } from './base'

export class DynamicArrayClassBuilder extends ClassBuilder {
export class DynamicArrayClassBuilder extends Arc4EncodedBaseClassBuilder {
readonly ptype = DynamicArrayConstructor

newCall(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): InstanceBuilder {
Expand Down Expand Up @@ -63,7 +63,7 @@ export class DynamicArrayClassBuilder extends ClassBuilder {
)
}
}
export class StaticArrayClassBuilder extends ClassBuilder {
export class StaticArrayClassBuilder extends Arc4EncodedBaseClassBuilder {
readonly ptype = StaticArrayConstructor

newCall(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): InstanceBuilder {
Expand Down Expand Up @@ -105,7 +105,7 @@ export class StaticArrayClassBuilder extends ClassBuilder {
)
}
}
export class AddressClassBuilder extends ClassBuilder {
export class AddressClassBuilder extends Arc4EncodedBaseClassBuilder {
readonly ptype = AddressClass

newCall(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): InstanceBuilder {
Expand Down
119 changes: 115 additions & 4 deletions src/awst_build/eb/arc4/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,31 @@ import { EqualityComparison } from '../../../awst/nodes'
import type { SourceLocation } from '../../../awst/source-location'
import { wtypes } from '../../../awst/wtypes'
import { CodeError } from '../../../errors'
import type { PType } from '../../ptypes'
import type { ARC4EncodedType } from '../../ptypes/arc4-types'
import { instanceEb } from '../../type-registry'
import { codeInvariant, hexToUint8Array } from '../../../util'
import { bytesPType, type PType } from '../../ptypes'
import { ARC4EncodedType } from '../../ptypes/arc4-types'
import { instanceEb, typeRegistry } from '../../type-registry'
import { BooleanExpressionBuilder } from '../boolean-expression-builder'
import { BytesExpressionBuilder } from '../bytes-expression-builder'
import type { InstanceBuilder, NodeBuilder } from '../index'
import { BuilderComparisonOp, FunctionBuilder, InstanceExpressionBuilder } from '../index'
import { BuilderComparisonOp, ClassBuilder, FunctionBuilder, InstanceExpressionBuilder } from '../index'
import { parseFunctionArgs } from '../util/arg-parsing'

export abstract class Arc4EncodedBaseClassBuilder extends ClassBuilder {
constructor(sourceLocation: SourceLocation) {
super(sourceLocation)
}
memberAccess(name: string, sourceLocation: SourceLocation): NodeBuilder {
switch (name) {
case 'fromBytes':
return new Arc4EncodedFromBytesFunctionBuilder(sourceLocation)
case 'fromLog':
return new Arc4EncodedFromLogFunctionBuilder(sourceLocation)
}
return super.memberAccess(name, sourceLocation)
}
}

export class Arc4EncodedBaseExpressionBuilder<T extends ARC4EncodedType> extends InstanceExpressionBuilder<T> {
constructor(expr: Expression, ptype: T) {
super(expr, ptype)
Expand Down Expand Up @@ -85,3 +101,98 @@ class Arc4EqualsFunctionBuilder extends FunctionBuilder {
)
}
}

export class Arc4EncodedFromBytesFunctionBuilder extends FunctionBuilder {
constructor(
sourceLocation: SourceLocation,
private ptypeFactory?: (args: PType[]) => ARC4EncodedType,
private genericArgsCount?: number,
) {
super(sourceLocation)
}
call(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): NodeBuilder {
const funcName = 'fromBytes'
const {
ptypes,
args: [initialValueBuilder],
} = parseFunctionArgs({
args,
typeArgs,
genericTypeArgs: this.genericArgsCount ?? 1,
funcName,
argSpec: (a) => [a.required(bytesPType)],
callLocation: sourceLocation,
})

const ptype = this.ptypeFactory ? this.ptypeFactory(ptypes) : ptypes[0]
codeInvariant(ptype instanceof ARC4EncodedType, 'Expected ARC4EncodedType')
const initialValue = initialValueBuilder.resolve()
const initialValueExpr = nodeFactory.reinterpretCast({
wtype: ptype.wtype,
sourceLocation,
expr: initialValue,
})

return typeRegistry.getInstanceEb(initialValueExpr, ptype)
}
}

export class Arc4EncodedFromLogFunctionBuilder extends FunctionBuilder {
constructor(
sourceLocation: SourceLocation,
private ptypeFactory?: (args: PType[]) => ARC4EncodedType,
private genericArgsCount?: number,
) {
super(sourceLocation)
}

call(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): NodeBuilder {
const funcName = 'fromLog'
const {
ptypes,
args: [initialValueBuilder],
} = parseFunctionArgs({
args,
typeArgs,
genericTypeArgs: this.genericArgsCount ?? 1,
funcName,
argSpec: (a) => [a.required(bytesPType)],
callLocation: sourceLocation,
})

const ptype = this.ptypeFactory ? this.ptypeFactory(ptypes) : ptypes[0]
codeInvariant(ptype instanceof ARC4EncodedType, 'Expected ARC4EncodedType')
const initialValue = initialValueBuilder.resolve()
const arc4Value = nodeFactory.intrinsicCall({
opCode: 'extract',
immediates: [4n, 0n],
wtype: bytesPType.wtype,
stackArgs: [initialValue],
sourceLocation,
})
const arc4ValueExpr = nodeFactory.reinterpretCast({
wtype: ptype.wtype,
sourceLocation,
expr: arc4Value,
})
const arc4ReturnValuePrefix = nodeFactory.intrinsicCall({
opCode: 'extract',
immediates: [0n, 4n],
wtype: bytesPType.wtype,
stackArgs: [initialValue],
sourceLocation,
})
const arc4PrefixIsValid = nodeFactory.bytesComparisonExpression({
operator: EqualityComparison.eq,
lhs: arc4ReturnValuePrefix,
rhs: nodeFactory.bytesConstant({ value: hexToUint8Array('151F7C75'), sourceLocation }),
sourceLocation,
})

const fromLogExpr = nodeFactory.checkedMaybe({
expr: nodeFactory.tupleExpression({ items: [arc4ValueExpr, arc4PrefixIsValid], sourceLocation }),
comment: 'ARC4 prefix is valid',
})
return typeRegistry.getInstanceEb(fromLogExpr, ptype)
}
}
5 changes: 2 additions & 3 deletions src/awst_build/eb/arc4/bool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import type { SourceLocation } from '../../../awst/source-location'
import type { PType } from '../../ptypes'
import { ARC4BoolClass, ARC4BooleanType, type ARC4EncodedType } from '../../ptypes/arc4-types'
import type { InstanceBuilder, NodeBuilder } from '../index'
import { ClassBuilder } from '../index'
import { Arc4EncodedBaseExpressionBuilder } from './base'
import { Arc4EncodedBaseClassBuilder, Arc4EncodedBaseExpressionBuilder } from './base'

export class BoolClassBuilder extends ClassBuilder {
export class BoolClassBuilder extends Arc4EncodedBaseClassBuilder {
readonly ptype = ARC4BoolClass

newCall(args: ReadonlyArray<NodeBuilder>, typeArgs: ReadonlyArray<PType>, sourceLocation: SourceLocation): InstanceBuilder {
Expand Down
Loading

0 comments on commit 08912a1

Please sign in to comment.