diff --git a/package-lock.json b/package-lock.json index 693225e0..1896e9c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13152,7 +13152,7 @@ }, "packages/algo-ts/dist": { "name": "@algorandfoundation/algorand-typescript", - "version": "0.0.1-alpha.15", + "version": "0.0.1-alpha.16", "dev": true, "peerDependencies": { "tslib": "^2.6.2" diff --git a/packages/algo-ts/package.json b/packages/algo-ts/package.json index adc134c3..e6b099b8 100644 --- a/packages/algo-ts/package.json +++ b/packages/algo-ts/package.json @@ -1,6 +1,6 @@ { "name": "@algorandfoundation/algorand-typescript", - "version": "0.0.1-alpha.15", + "version": "0.0.1-alpha.16", "description": "This package contains definitions for the types which comprise Algorand TypeScript which can be compiled to run on the Algorand Virtual Machine using the Puya compiler.", "private": false, "main": "index.js", diff --git a/packages/algo-ts/src/index.ts b/packages/algo-ts/src/index.ts index d3b84a20..9aa28fda 100644 --- a/packages/algo-ts/src/index.ts +++ b/packages/algo-ts/src/index.ts @@ -12,3 +12,4 @@ export * from './state' export * as itxn from './itxn' export * as gtxn from './gtxn' export { TransactionType } from './transactions' +export { Base64, Ec, Ecdsa, VrfVerify } from './op-types' diff --git a/packages/algo-ts/src/op.ts b/packages/algo-ts/src/op.ts index 4a5ba18d..ca00fecb 100644 --- a/packages/algo-ts/src/op.ts +++ b/packages/algo-ts/src/op.ts @@ -139,5 +139,3 @@ export const AssetHolding = createObjectProxy('AssetHolding') export const AssetParams = createObjectProxy('AssetParams') export const Block = createObjectProxy('Block') export const Box = createObjectProxy('Box') - -export { VrfVerify } from './op-types' diff --git a/packages/algo-ts/src/util.ts b/packages/algo-ts/src/util.ts index 8c450ac6..7c652bfa 100644 --- a/packages/algo-ts/src/util.ts +++ b/packages/algo-ts/src/util.ts @@ -1,7 +1,7 @@ -import { biguint, BigUintCompat, BytesCompat, StringCompat, uint64, Uint64Compat } from './primitives' 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' export function log(...args: Array): void { ctxMgr.instance.log(args.map(toBytes).reduce((left, right) => left.concat(right))) @@ -14,7 +14,7 @@ export function assert(condition: unknown, message?: string): asserts condition } export function err(message?: string): never { - throw new AvmError(message ?? 'Err') + throw new AvmError(message ?? 'err opcode executed') } type NumericComparison = T | { lessThan: T } | { greaterThan: T } | { greaterThanEq: T } | { lessThanEq: T } | { between: [T, T] } diff --git a/scripts/build-op-module.ts b/scripts/build-op-module.ts index 0122f5aa..27d6565d 100644 --- a/scripts/build-op-module.ts +++ b/scripts/build-op-module.ts @@ -162,6 +162,23 @@ const TYPE_MAP: Record = { any: AlgoTsType.Uint64 | AlgoTsType.Bytes, } +const INPUT_TYPE_MAP: Record = { + application: AlgoTsType.Application | AlgoTsType.Uint64, + asset: AlgoTsType.Asset | AlgoTsType.Uint64, +} + +const INPUT_ALGOTS_TYPE_MAP: Record = { + [AlgoTsType.Asset]: AlgoTsType.Asset | AlgoTsType.Uint64, + [AlgoTsType.Application]: AlgoTsType.Application | AlgoTsType.Uint64, + [AlgoTsType.Bytes]: AlgoTsType.Bytes, + [AlgoTsType.Uint64]: AlgoTsType.Uint64, + [AlgoTsType.Boolean]: AlgoTsType.Boolean, + [AlgoTsType.Account]: AlgoTsType.Account, + [AlgoTsType.Void]: AlgoTsType.Void, + [AlgoTsType.BigUint]: AlgoTsType.BigUint, + [AlgoTsType.Enum]: AlgoTsType.Enum +} + const ARG_ENUMS = Object.entries(langSpec.arg_enums).map(([name, values], index): EnumDef => { const enumValues = values.map( (v): EnumValue => ({ @@ -398,13 +415,13 @@ export function buildOpModule() { }, docs: member.doc, name: getEnumOpName(member.name, opNameConfig), - immediateArgs: def.immediate_args.map((i) => ({ name: camelCase(i.name), type: getMappedType(i.immediate_type, i.arg_enum) })), + immediateArgs: def.immediate_args.map((i) => ({ name: camelCase(i.name), type: getMappedType(i.immediate_type, i.arg_enum, true) })), stackArgs: def.stack_inputs.map((sa, i) => { if (i === enumArg.modifies_stack_input) { invariant(member.stackType, 'Member must have stack type') - return { name: camelCase(sa.name), type: member.stackType } + return { name: camelCase(sa.name), type: INPUT_ALGOTS_TYPE_MAP[member.stackType] ?? member.stackType } } - return { name: camelCase(sa.name), type: getMappedType(sa.stack_type, null) } + return { name: camelCase(sa.name), type: getMappedType(sa.stack_type, null, true) } }), returnTypes: def.stack_outputs.map((o, i) => { if (i === enumArg.modifies_stack_output) { @@ -421,8 +438,8 @@ export function buildOpModule() { type: 'op-function', opCode: opCode, name: getOpName(def.name, opNameConfig), - immediateArgs: def.immediate_args.map((i) => ({ name: camelCase(i.name), type: getMappedType(i.immediate_type, i.arg_enum) })), - stackArgs: def.stack_inputs.map((i) => ({ name: camelCase(i.name), type: getMappedType(i.stack_type, null) })), + immediateArgs: def.immediate_args.map((i) => ({ name: camelCase(i.name), type: getMappedType(i.immediate_type, i.arg_enum, true) })), + stackArgs: def.stack_inputs.map((i) => ({ name: camelCase(i.name), type: getMappedType(i.stack_type, null, true) })), returnTypes: def.stack_outputs.map((o) => getMappedType(o.stack_type, null)), docs: getOpDocs(def), }), @@ -542,7 +559,7 @@ function getEnumOpName(enumMember: string, config: OpNameConfig): string { return camelCase([config.prefix, enumMember].filter(Boolean).join('_')) } -function getMappedType(t: string | null, enumName: string | null): AlgoTsType { +function getMappedType(t: string | null, enumName: string | null, isInput: boolean = false): AlgoTsType { invariant(t !== 'arg_enum' || enumName !== undefined, 'Must provide enumName for arg_enum types') if (t === null || t === undefined) { throw new Error('Missing type') @@ -552,7 +569,7 @@ function getMappedType(t: string | null, enumName: string | null): AlgoTsType { invariant(enumDef, `Definition must exist for ${enumName}`) return enumDef.typeFlag } - const mappedType = TYPE_MAP[t ?? ''] + const mappedType = isInput ? INPUT_TYPE_MAP[t ?? ''] ?? TYPE_MAP[t ?? ''] : TYPE_MAP[t ?? ''] invariant(mappedType, `Mapped type must exist for ${t}`) return mappedType } diff --git a/scripts/generate-op-ptypes.ts b/scripts/generate-op-ptypes.ts index 1462a84f..449c9b30 100644 --- a/scripts/generate-op-ptypes.ts +++ b/scripts/generate-op-ptypes.ts @@ -1,5 +1,5 @@ +import { camelCase, pascalCase } from 'change-case' import * as fs from 'fs' -import { camelCase } from 'change-case' import type { EnumDef, OpModule } from './build-op-module' import { buildOpModule, ENUMS_TO_EXPOSE } from './build-op-module' @@ -16,7 +16,7 @@ function* emitHeader() { function* emitTypes(module: OpModule) { function* emitEnumPType(enumDef: EnumDef) { yield `export const ${camelCase(enumDef.tsName)}PType = new IntrinsicEnumType({ - name: '${enumDef.name}', + name: '${pascalCase(enumDef.name)}', module: \`\${Constants.algoTsPackage}/op-types.d.ts\`, members: [` for (const member of enumDef.members) { diff --git a/scripts/generate-op-types.ts b/scripts/generate-op-types.ts index fd964972..38ad64bb 100644 --- a/scripts/generate-op-types.ts +++ b/scripts/generate-op-types.ts @@ -76,9 +76,9 @@ import { Account, Application, Asset } from './reference' } } function* emitArgType(argType: AlgoTsType) { - if (hasFlags(argType, AlgoTsType.Application)) yield 'Application | uint64' + if (hasFlags(argType, AlgoTsType.Application)) yield 'Application' if (hasFlags(argType, AlgoTsType.Account)) yield 'Account' - if (hasFlags(argType, AlgoTsType.Asset)) yield 'Asset | uint64' + if (hasFlags(argType, AlgoTsType.Asset)) yield 'Asset' if (hasFlags(argType, AlgoTsType.Uint64)) yield 'uint64' if (hasFlags(argType, AlgoTsType.Bytes)) yield 'bytes' if (hasFlags(argType, AlgoTsType.Boolean)) yield 'boolean' diff --git a/src/awst_build/eb/bytes-expression-builder.ts b/src/awst_build/eb/bytes-expression-builder.ts index b3001b6f..5fd4d885 100644 --- a/src/awst_build/eb/bytes-expression-builder.ts +++ b/src/awst_build/eb/bytes-expression-builder.ts @@ -9,7 +9,7 @@ import { wtypes } from '../../awst/wtypes' import { CodeError, wrapInCodeError } from '../../errors' import { logger } from '../../logger' -import { base32ToUint8Array, base64ToUint8Array, hexToUint8Array, uint8ArrayToUtf8, utf8ToUint8Array } from '../../util' +import { base32ToUint8Array, base64ToUint8Array, enumKeyFromValue, hexToUint8Array, uint8ArrayToUtf8, utf8ToUint8Array } from '../../util' import type { InstanceType, PType } from '../ptypes' import { ArrayPType, @@ -193,6 +193,12 @@ export class BytesExpressionBuilder extends InstanceExpressionBuilder, typeArgs: ReadonlyArray, sourceLocation: SourceLocation): NodeBuilder { + const { + args: [other], + } = parseFunctionArgs({ + args, + typeArgs, + genericTypeArgs: 0, + callLocation: sourceLocation, + funcName: enumKeyFromValue(this.op, BytesBinaryOperator), + argSpec: (a) => [a.required(bytesPType)], + }) + return new BytesExpressionBuilder( + nodeFactory.bytesBinaryOperation({ + wtype: wtypes.bytesWType, + left: this.expr, + right: other.resolve(), + op: this.op, + sourceLocation, + }), + ) + } +} + export class ToStringBuilder extends ParameterlessFunctionBuilder { constructor(private expr: awst.Expression) { super( diff --git a/src/awst_build/eb/op-module-builder.ts b/src/awst_build/eb/op-module-builder.ts index 13617dcc..f48495ba 100644 --- a/src/awst_build/eb/op-module-builder.ts +++ b/src/awst_build/eb/op-module-builder.ts @@ -6,7 +6,7 @@ import { enumerate, invariant } from '../../util' import type { IntrinsicOpGrouping, IntrinsicOpMapping } from '../op-metadata' import { OP_METADATA } from '../op-metadata' import type { PType } from '../ptypes' -import { IntrinsicEnumType, IntrinsicFunctionGroupType, IntrinsicFunctionType, stringPType } from '../ptypes' +import { IntrinsicEnumType, IntrinsicFunctionGroupType, IntrinsicFunctionType, stringPType, voidPType } from '../ptypes' import { typeRegistry } from '../type-registry' import { FunctionBuilder, InstanceExpressionBuilder, NodeBuilder } from './index' import { requestConstantOfType, requestExpressionOfType } from './util' @@ -33,7 +33,7 @@ export class IntrinsicOpGroupBuilder extends NodeBuilder { } const metaData = this.opGrouping.ops[name] - if (metaData.signatures.some((s) => s.argNames.length)) { + if (metaData.signatures.some((s) => s.argNames.length || s.returnType.equals(voidPType))) { return new GroupedIntrinsicOpBuilder(sourceLocation, metaData) } diff --git a/src/awst_build/op-metadata.ts b/src/awst_build/op-metadata.ts index 7c583fd4..3d76c558 100644 --- a/src/awst_build/op-metadata.ts +++ b/src/awst_build/op-metadata.ts @@ -228,7 +228,7 @@ export const OP_METADATA: Record( throw new Error(`${message} ${value}`) } +export const enumKeyFromValue = ( + value: TValue, + enumType: Record, + message: string = 'Invalid enum value: ', +) => { + const key = Object.entries(enumType).find(([_, v]) => v === value)?.[0] + if (key) { + return key + } + throw new Error(`${message} ${value}`) +} + export const convertEnum = ( value: TEnumIn, fromEnum: Record,