Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ARC4 tuple, bool, string, ufixednxm #41

Merged
merged 8 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,4 @@ export default [
'no-unused-private-class-members': 'off',
},
},
{
files: ['**/*.algo.ts'],
rules: {
'@typescript-eslint/explicit-member-accessibility': [
'error',
{
accessibility: 'off',

overrides: {
methods: 'explicit',
},
},
],
},
},
]
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"dev:examples": "tsx src/cli.ts build examples --output-awst --output-awst-json",
"dev:approvals": "rimraf tests/approvals/out && tsx src/cli.ts build tests/approvals --output-awst --output-awst-json --no-output-teal --output-ssa-ir --out-dir out/[name]",
"dev:expected-output": "tsx src/cli.ts build tests/expected-output --dry-run",
"dev:testing": "tsx src/cli.ts build tests/approvals/arc4-types.algo.ts --output-awst --output-awst-json --output-ssa-ir --log-level=info --log-level debug --out-dir out/[name]",
"dev:testing": "tsx src/cli.ts build tests/approvals/pre-approved-sale.algo.ts --output-awst --output-awst-json --output-ssa-ir --log-level=info --log-level info --out-dir out/[name]",
"audit": "better-npm-audit audit",
"format": "prettier --write .",
"lint": "eslint \"src/**/*.ts\"",
Expand Down
4 changes: 2 additions & 2 deletions packages/algo-ts/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 59 additions & 15 deletions packages/algo-ts/src/arc4/encoded-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export type BitSize = 8 | 16 | 32 | 64 | 128 | 256 | 512
type NativeForArc4Int<N extends BitSize> = N extends 8 | 16 | 32 | 64 ? uint64 : biguint
type CompatForArc4Int<N extends BitSize> = N extends 8 | 16 | 32 | 64 ? Uint64Compat : BigUintCompat

abstract class AbiEncoded implements BytesBacked {
abstract class ARC4Encoded implements BytesBacked {
abstract __type?: string
get bytes(): bytes {
throw new Error('todo')
}
Expand All @@ -18,24 +19,32 @@ abstract class AbiEncoded implements BytesBacked {
}
}

export class Str extends AbiEncoded {
constructor(s: StringCompat) {
export class Str extends ARC4Encoded {
__type?: 'arc4.Str'
#value: string
constructor(s?: StringCompat) {
super()
this.#value = s ?? ''
}
get native(): string {
throw new Error('TODO')
return this.#value
}
}
export class UintN<N extends BitSize> extends AbiEncoded {
export class UintN<N extends BitSize> extends ARC4Encoded {
__type?: `arc4.UintN<${N}>`

constructor(v?: CompatForArc4Int<N>) {
super()
}
get native(): NativeForArc4Int<N> {
throw new Error('TODO')
}
}
export class UFixedNxM<N extends BitSize, M extends number> {
constructor(v: `${number}:${number}`, n?: N, m?: M) {}
export class UFixedNxM<N extends BitSize, M extends number> extends ARC4Encoded {
__type?: `arc4.UFixedNxM<${N}x${M}>`
constructor(v: `${number}.${number}`, n?: N, m?: M) {
super()
}

get native(): NativeForArc4Int<N> {
throw new Error('TODO')
Expand All @@ -48,6 +57,7 @@ export class Byte extends UintN<8> {
}
}
export class Bool {
__type?: `arc4.Bool`
#v: boolean
constructor(v?: boolean) {
this.#v = v ?? false
Expand All @@ -58,7 +68,7 @@ export class Bool {
}
}

abstract class Arc4ReadonlyArray<TItem extends AbiEncoded> extends AbiEncoded {
abstract class Arc4ReadonlyArray<TItem extends ARC4Encoded> extends ARC4Encoded {
protected items: TItem[]
protected constructor(items: TItem[]) {
super()
Expand Down Expand Up @@ -155,7 +165,8 @@ abstract class Arc4ReadonlyArray<TItem extends AbiEncoded> extends AbiEncoded {
[index: uint64]: TItem
}

export class StaticArray<TItem extends AbiEncoded, TLength extends number> extends Arc4ReadonlyArray<TItem> {
export class StaticArray<TItem extends ARC4Encoded, TLength extends number> extends Arc4ReadonlyArray<TItem> {
__type?: `arc4.StaticArray<${TItem['__type']}, ${TLength}>`
constructor()
constructor(...items: TItem[] & { length: TLength })
constructor(...items: TItem[])
Expand All @@ -168,7 +179,8 @@ export class StaticArray<TItem extends AbiEncoded, TLength extends number> exten
}
}

export class DynamicArray<TItem extends AbiEncoded> extends Arc4ReadonlyArray<TItem> {
export class DynamicArray<TItem extends ARC4Encoded> extends Arc4ReadonlyArray<TItem> {
__type?: `arc4.DynamicArray<${TItem['__type']}>`
constructor(...items: TItem[]) {
super(items)
}
Expand All @@ -194,17 +206,26 @@ export class DynamicArray<TItem extends AbiEncoded> extends Arc4ReadonlyArray<TI
return new DynamicArray<TItem>(...this.items)
}
}
type ExpandTupleType<T extends ARC4Encoded[]> = T extends [infer T1 extends ARC4Encoded, ...infer TRest extends ARC4Encoded[]]
? TRest extends []
? `${T1['__type']}`
: `${T1['__type']},${ExpandTupleType<TRest>}`
: ''

type ItemAt<TTuple extends unknown[], TIndex extends number> = undefined extends TTuple[TIndex] ? never : TTuple[TIndex]

export class Tuple<TTuple extends unknown[]> {
export class Tuple<TTuple extends [ARC4Encoded, ...ARC4Encoded[]]> extends ARC4Encoded {
__type?: `arc4.Tuple<${ExpandTupleType<TTuple>}>`
#items: TTuple
constructor(...items: TTuple) {
super()
this.#items = items
}

at<TIndex extends number>(index: TIndex): ItemAt<TTuple, TIndex> {
return (this.#items[index] ?? err('Index out of bounds')) as ItemAt<TTuple, TIndex>
at<TIndex extends keyof TTuple>(index: TIndex): TTuple[TIndex] {
return this.#items[index] ?? err('Index out of bounds')
}

get length(): TTuple['length'] & uint64 {
return this.#items.length
}

get native(): TTuple {
Expand All @@ -213,6 +234,7 @@ export class Tuple<TTuple extends unknown[]> {
}

export class Address extends Arc4ReadonlyArray<Byte> {
__type?: `arc4.Address`
constructor(value?: Account | string | bytes) {
let byteValues: Uint8Array
if (value === undefined) {
Expand All @@ -233,3 +255,25 @@ export class Address extends Arc4ReadonlyArray<Byte> {
return Account(Bytes(this.items.map((i) => i.native)))
}
}

type StructConstraint = Record<string, ARC4Encoded>

class StructBase extends ARC4Encoded {
__type = 'arc4.Struct'
}
class StructImpl<T extends StructConstraint> extends StructBase {
constructor(initial: T) {
super()
for (const [prop, val] of Object.entries(initial)) {
Object.defineProperty(this, prop, {
value: val,
writable: true,
enumerable: true,
})
}
}
}

type StructConstructor = new <T extends StructConstraint>(initial: T) => StructBase & T

export const Struct = StructImpl as StructConstructor
2 changes: 2 additions & 0 deletions packages/algo-ts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ export * from './state'
export * as itxn from './itxn'
export * as gtxn from './gtxn'
export { TransactionType } from './transactions'
export { LogicSig } from './logic-sig'
export { TemplateVar } from './template-var'
export { Base64, Ec, Ecdsa, VrfVerify } from './op-types'
5 changes: 5 additions & 0 deletions packages/algo-ts/src/logic-sig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { uint64 } from './primitives'

export abstract class LogicSig {
abstract program(): boolean | uint64
}
3 changes: 3 additions & 0 deletions packages/algo-ts/src/template-var.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function TemplateVar<T>(variableName: string, prefix = 'TMPL_'): T {
throw new Error('TODO')
}
4 changes: 2 additions & 2 deletions packages/algo-ts/src/util.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ctxMgr } from './execution-context'
import { AssertError, AvmError } from './impl/errors'
import { toBytes } from './impl/primitives'
import { biguint, BigUintCompat, BytesCompat, StringCompat, uint64, Uint64Compat } from './primitives'
import { biguint, BigUintCompat, BytesBacked, BytesCompat, StringCompat, uint64, Uint64Compat } from './primitives'

export function log(...args: Array<Uint64Compat | BytesCompat | BigUintCompat | StringCompat>): void {
export function log(...args: Array<Uint64Compat | BytesCompat | BigUintCompat | StringCompat | BytesBacked>): void {
ctxMgr.instance.log(args.map(toBytes).reduce((left, right) => left.concat(right)))
}

Expand Down
9 changes: 8 additions & 1 deletion src/awst/models.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ContractClassPType } from '../awst_build/ptypes'
import type { ContractClassPType, LogicSigPType } from '../awst_build/ptypes'
import type { Props } from '../typescript-helpers'
import type { SourceLocation } from './source-location'

Expand Down Expand Up @@ -123,6 +123,13 @@ export class LogicSigReference extends ModelBase {
toString(): string {
return this.id
}

static fromPType(logicSigPType: LogicSigPType): LogicSigReference {
return new LogicSigReference({
name: logicSigPType.name,
moduleName: logicSigPType.module,
})
}
}

export enum TransactionKind {
Expand Down
10 changes: 5 additions & 5 deletions src/awst/node-factory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CodeError } from '../errors'
import type { DeliberateAny, Props } from '../typescript-helpers'
import { codeInvariant, invariant } from '../util'
import { codeInvariant, instanceOfAny, invariant } from '../util'
import type { Expression, Statement } from './nodes'
import {
AssignmentExpression,
Expand Down Expand Up @@ -54,10 +54,10 @@ const explicitNodeFactory = {
...props,
})
},
stringConstant(props: { value: string; sourceLocation: SourceLocation }): StringConstant {
stringConstant(props: { value: string; sourceLocation: SourceLocation; wtype?: wtypes.WType }): StringConstant {
return new StringConstant({
...props,
wtype: wtypes.stringWType,
wtype: props.wtype ?? wtypes.stringWType,
})
},
uInt64Constant({
Expand Down Expand Up @@ -239,8 +239,8 @@ const explicitNodeFactory = {
},
tupleItemExpression(props: Omit<Props<TupleItemExpression>, 'wtype'>) {
invariant(
props.base.wtype instanceof wtypes.WTuple && props.base.wtype.types.length > Number(props.index),
'expr.base must be WTuple with length greater than index',
instanceOfAny(props.base.wtype, wtypes.WTuple, wtypes.ARC4Tuple) && props.base.wtype.types.length > Number(props.index),
'expr.base must be tuple type with length greater than index',
)
return new TupleItemExpression({
...props,
Expand Down
6 changes: 5 additions & 1 deletion src/awst/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export class DecimalConstant extends Expression {
this.value = props.value
}
declare wtype: wtypes.ARC4UFixedNxM
value: number
value: string
accept<T>(visitor: ExpressionVisitor<T>): T {
return visitor.visitDecimalConstant(this)
}
Expand Down Expand Up @@ -1190,11 +1190,13 @@ export class LogicSignature extends RootNode {
this.shortName = props.shortName
this.program = props.program
this.docstring = props.docstring
this.avmVersion = props.avmVersion
}
id: LogicSigReference
shortName: string
program: Subroutine
docstring: string | null
avmVersion: bigint | null
accept<T>(visitor: RootNodeVisitor<T>): T {
return visitor.visitLogicSignature(this)
}
Expand Down Expand Up @@ -1263,6 +1265,7 @@ export class Contract extends RootNode {
this.appState = props.appState
this.stateTotals = props.stateTotals
this.reservedScratchSpace = props.reservedScratchSpace
this.avmVersion = props.avmVersion
}
id: ContractReference
name: string
Expand All @@ -1274,6 +1277,7 @@ export class Contract extends RootNode {
appState: Array<AppStorageDefinition>
stateTotals: StateTotals | null
reservedScratchSpace: Set<bigint>
avmVersion: bigint | null
accept<T>(visitor: RootNodeVisitor<T>): T {
return visitor.visitContract(this)
}
Expand Down
13 changes: 8 additions & 5 deletions src/awst/to-code-visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export class ToCodeVisitor
return `${expression.value}`
}
visitDecimalConstant(expression: nodes.DecimalConstant): string {
throw new TodoError('Method not implemented.', { sourceLocation: expression.sourceLocation })
return `${expression.value}m`
}
visitBoolConstant(expression: nodes.BoolConstant): string {
return expression.value ? 'True' : 'False'
Expand All @@ -92,7 +92,7 @@ export class ToCodeVisitor
return `"${expression.value}"`
}
visitTemplateVar(expression: nodes.TemplateVar): string {
throw new TodoError('Method not implemented.', { sourceLocation: expression.sourceLocation })
return `TemplateVar[${expression.wtype}](${expression.name})`
}
visitMethodConstant(expression: nodes.MethodConstant): string {
return `Method("${expression.value}")`
Expand All @@ -116,7 +116,7 @@ export class ToCodeVisitor
return `${expression.base.accept(this)}.push(...${expression.other.accept(this)}`
}
visitARC4Decode(expression: nodes.ARC4Decode): string {
return `ARC4_DECODE(${expression.value})`
return `ARC4_DECODE(${expression.value.accept(this)})`
}
visitIntrinsicCall(expression: nodes.IntrinsicCall): string {
const immediates = expression.immediates.length ? `<${expression.immediates.map((i) => i).join(', ')}>` : ''
Expand Down Expand Up @@ -257,7 +257,10 @@ export class ToCodeVisitor
return `STATE_EXISTS(${expression.field.accept(this)})`
}
visitNewStruct(expression: nodes.NewStruct): string {
throw new TodoError('Method not implemented.', { sourceLocation: expression.sourceLocation })
const props = Array.from(expression.values)
.map(([k, v]) => `${k}=${v.accept(this)}`)
.join(', ')
return `new ${expression.wtype.name}(${props})`
}
visitExpressionStatement(statement: nodes.ExpressionStatement): string[] {
return [statement.expr.accept(this)]
Expand Down Expand Up @@ -321,7 +324,7 @@ export class ToCodeVisitor
return [`${prefix}${statement.memberName}(): ${statement.returnType}`, '{', ...indent(statement.body.accept(this)), '}', '']
}
visitLogicSignature(moduleStatement: nodes.LogicSignature): string[] {
throw new TodoError('Method not implemented.', { sourceLocation: moduleStatement.sourceLocation })
return ['', `logicsig ${moduleStatement.id} {`, ...indent(moduleStatement.program.body.accept(this)), '}']
}

private currentContract: ContractReference[] = []
Expand Down
Loading