Skip to content
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
4 changes: 2 additions & 2 deletions examples/calculator/contract.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { internal, op, Uint64 } from '@algorandfoundation/algorand-typescript'
import { internal, Uint64 } from '@algorandfoundation/algorand-typescript'
import { TestExecutionContext } from '@algorandfoundation/algorand-typescript-testing'
import { afterEach, describe, expect, it } from 'vitest'
import MyContract from './contract.algo'
Expand Down Expand Up @@ -31,7 +31,7 @@ describe('Calculator', () => {
.createScope([
ctx.any.txn.applicationCall({
appId: application,
appArgs: [op.itob(Uint64(1)), op.itob(Uint64(2)), op.itob(Uint64(3))],
appArgs: [Uint64(1), Uint64(2), Uint64(3)],
}),
])
.execute(contract.approvalProgram)
Expand Down
29 changes: 14 additions & 15 deletions package-lock.json

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

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,16 @@
"rollup": "^4.24.0",
"semantic-release": "^24.1.2",
"tsx": "4.19.1",
"typescript": "5.6.2",
"typescript": "^5.7.2",
"vitest": "2.1.2"
},
"peerDependencies": {
"tslib": "^2.6.2"
},
"dependencies": {
"@algorandfoundation/algokit-utils": "^6.2.1",
"@algorandfoundation/algorand-typescript": "^0.0.1-alpha.20",
"@algorandfoundation/puya-ts": "^1.0.0-alpha.32",
"@algorandfoundation/algorand-typescript": "^0.0.1-alpha.22",
"@algorandfoundation/puya-ts": "^1.0.0-alpha.34",
"algosdk": "^2.9.0",
"elliptic": "^6.5.7",
"js-sha256": "^0.11.0",
Expand Down
22 changes: 11 additions & 11 deletions patches/typescript+5.6.2.patch → patches/typescript+5.7.2.patch
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
diff --git a/node_modules/typescript/lib/typescript.d.ts b/node_modules/typescript/lib/typescript.d.ts
index 963c573..299a8a4 100644
index 6780dd1..8700e72 100644
--- a/node_modules/typescript/lib/typescript.d.ts
+++ b/node_modules/typescript/lib/typescript.d.ts
@@ -6116,6 +6116,7 @@ declare namespace ts {
@@ -6159,6 +6159,7 @@ declare namespace ts {
getIndexInfoOfType(type: Type, kind: IndexKind): IndexInfo | undefined;
getIndexInfosOfType(type: Type): readonly IndexInfo[];
getIndexInfosOfIndexSymbol: (indexSymbol: Symbol) => IndexInfo[];
getIndexInfosOfIndexSymbol: (indexSymbol: Symbol, siblingSymbols?: Symbol[] | undefined) => IndexInfo[];
+ getTypeArgumentsForResolvedSignature(signature: Signature): readonly Type[] | undefined;
getSignaturesOfType(type: Type, kind: SignatureKind): readonly Signature[];
getIndexTypeOfType(type: Type, kind: IndexKind): Type | undefined;
getBaseTypes(type: InterfaceType): BaseType[];
diff --git a/node_modules/typescript/lib/typescript.js b/node_modules/typescript/lib/typescript.js
index 90f3266..9daa319 100644
index 33387ea..a1f35b3 100644
--- a/node_modules/typescript/lib/typescript.js
+++ b/node_modules/typescript/lib/typescript.js
@@ -50171,6 +50171,7 @@ function createTypeChecker(host) {
@@ -50655,6 +50655,7 @@ function createTypeChecker(host) {
getGlobalDiagnostics,
getRecursionIdentity,
getUnmatchedProperties,
+ getTypeArgumentsForResolvedSignature,
getTypeOfSymbolAtLocation: (symbol, locationIn) => {
const location = getParseTreeNode(locationIn);
return location ? getTypeOfSymbolAtLocation(symbol, location) : errorType;
@@ -92895,6 +92896,9 @@ function createTypeChecker(host) {
Debug.assert(specifier && nodeIsSynthesized(specifier) && specifier.text === "tslib", `Expected sourceFile.imports[0] to be the synthesized tslib import`);
return specifier;
@@ -71776,6 +71777,9 @@ function createTypeChecker(host) {
}
}
}
+ function getTypeArgumentsForResolvedSignature(signature) {
+ return signature.mapper && instantiateTypes((signature.target ?? signature).typeParameters ?? [], signature.mapper);
+ }
}
function isNotAccessor(declaration) {
return !isAccessor(declaration);
function getUnmatchedProperty(source, target, requireOptionalProperties, matchDiscriminantProperties) {
return firstOrUndefinedIterator(getUnmatchedProperties(source, target, requireOptionalProperties, matchDiscriminantProperties));
}
83 changes: 69 additions & 14 deletions src/abi-metadata.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { BaseContract, Contract } from '@algorandfoundation/algorand-typescript'
import { AbiMethodConfig, BareMethodConfig, CreateOptions, OnCompleteActionStr } from '@algorandfoundation/algorand-typescript/arc4'
import { ABIMethod } from 'algosdk'
import { TypeInfo } from './encoders'
import { getArc4TypeName as getArc4TypeNameForARC4Encoded } from './impl/encoded-types'
import { DeliberateAny } from './typescript-helpers'

export interface AbiMetadata {
methodName: string
methodSelector: string
methodSignature: string | undefined
argTypes: string[]
returnType: string
onCreate?: CreateOptions
allowActions?: OnCompleteActionStr[]
}
const AbiMetaSymbol = Symbol('AbiMetadata')
export const isContractProxy = Symbol('isContractProxy')
export const attachAbiMetadata = (contract: { new (): Contract }, methodName: string, metadata: AbiMetadata): void => {
const metadatas: Record<string, AbiMetadata> = (AbiMetaSymbol in contract ? contract[AbiMetaSymbol] : {}) as Record<string, AbiMetadata>
metadatas[methodName] = metadata
Expand All @@ -23,38 +27,89 @@ export const attachAbiMetadata = (contract: { new (): Contract }, methodName: st
}
}

export const copyAbiMetadatas = <T extends BaseContract>(sourceContract: T, targetContract: T): void => {
const metadatas = getContractAbiMetadata(sourceContract)
Object.defineProperty(targetContract, AbiMetaSymbol, {
value: metadatas,
writable: true,
enumerable: false,
})
}

export const captureMethodConfig = <T extends Contract>(
contract: T,
methodName: string,
config?: AbiMethodConfig<T> | BareMethodConfig,
): void => {
const metadata = ensureMetadata(contract, methodName)
const metadata = getContractMethodAbiMetadata(contract, methodName)
metadata.onCreate = config?.onCreate ?? 'disallow'
metadata.allowActions = ([] as OnCompleteActionStr[]).concat(config?.allowActions ?? 'NoOp')
}

const ensureMetadata = <T extends Contract>(contract: T, methodName: string): AbiMetadata => {
if (!hasAbiMetadata(contract)) {
const contractClass = contract.constructor as { new (): T }
Object.getOwnPropertyNames(Object.getPrototypeOf(contract)).forEach((name) => {
attachAbiMetadata(contractClass, name, { methodName: name, methodSelector: name, argTypes: [], returnType: '' })
})
}
return getAbiMetadata(contract, methodName)
}

export const hasAbiMetadata = <T extends Contract>(contract: T): boolean => {
const contractClass = contract.constructor as { new (): T }
return (
Object.getOwnPropertySymbols(contractClass).some((s) => s.toString() === AbiMetaSymbol.toString()) || AbiMetaSymbol in contractClass
)
}

export const getAbiMetadata = <T extends BaseContract>(contract: T, methodName: string): AbiMetadata => {
export const getContractAbiMetadata = <T extends BaseContract>(contract: T): Record<string, AbiMetadata> => {
if ((contract as DeliberateAny)[isContractProxy]) {
return (contract as DeliberateAny)[AbiMetaSymbol] as Record<string, AbiMetadata>
}
const contractClass = contract.constructor as { new (): T }
const s = Object.getOwnPropertySymbols(contractClass).find((s) => s.toString() === AbiMetaSymbol.toString())
const metadatas: Record<string, AbiMetadata> = (
s ? (contractClass as DeliberateAny)[s] : AbiMetaSymbol in contractClass ? contractClass[AbiMetaSymbol] : {}
) as Record<string, AbiMetadata>
return metadatas
}

export const getContractMethodAbiMetadata = <T extends BaseContract>(contract: T, methodName: string): AbiMetadata => {
const metadatas = getContractAbiMetadata(contract)
return metadatas[methodName]
}

export const getArc4Signature = (metadata: AbiMetadata): string => {
if (metadata.methodSignature === undefined) {
const argTypes = metadata.argTypes.map((t) => JSON.parse(t) as TypeInfo).map(getArc4TypeName)
const returnType = getArc4TypeName(JSON.parse(metadata.returnType) as TypeInfo)
const method = new ABIMethod({ name: metadata.methodName, args: argTypes.map((t) => ({ type: t })), returns: { type: returnType } })
metadata.methodSignature = method.getSignature()
}
return metadata.methodSignature
}

const getArc4TypeName = (t: TypeInfo): string => {
const map: Record<string, string | ((t: TypeInfo) => string)> = {
void: 'void',
account: 'account',
application: 'application',
asset: 'asset',
boolean: 'bool',
biguint: 'uint512',
bytes: 'byte[]',
string: 'string',
uint64: 'uint64',
OnCompleteAction: 'uint64',
TransactionType: 'uint64',
Transaction: 'txn',
PaymentTxn: 'pay',
KeyRegistrationTxn: 'keyreg',
AssetConfigTxn: 'acfg',
AssetTransferTxn: 'axfer',
AssetFreezeTxn: 'afrz',
ApplicationTxn: 'appl',
'Tuple<.*>': (t) =>
`(${Object.values(t.genericArgs as Record<string, TypeInfo>)
.map(getArc4TypeName)
.join(',')})`,
}
const entry = Object.entries(map).find(([k, _]) => new RegExp(`^${k}$`, 'i').test(t.name))?.[1]
if (entry === undefined) {
return getArc4TypeNameForARC4Encoded(t) ?? t.name
}
if (entry instanceof Function) {
return entry(t)
}
return entry
}
21 changes: 21 additions & 0 deletions src/context-helpers/context-util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { internal, uint64 } from '@algorandfoundation/algorand-typescript'
import { AbiMetadata } from '../abi-metadata'
import { ApplicationTransaction } from '../impl/transactions'
import { lazyContext } from './internal-context'

export const checkRoutingConditions = (appId: uint64, metadata: AbiMetadata) => {
const appData = lazyContext.getApplicationData(appId)
const isCreating = appData.isCreating
if (isCreating && metadata.onCreate === 'disallow') {
throw new internal.errors.CodeError('method can not be called while creating')
}
if (!isCreating && metadata.onCreate === 'require') {
throw new internal.errors.CodeError('method can only be called while creating')
}
const txn = lazyContext.activeGroup.activeTransaction
if (txn instanceof ApplicationTransaction && metadata.allowActions && !metadata.allowActions.includes(txn.onCompletion)) {
throw new internal.errors.CodeError(
`method can only be called with one of the following on_completion values: ${metadata.allowActions.join(', ')}`,
)
}
}
8 changes: 5 additions & 3 deletions src/context-helpers/internal-context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Account, internal } from '@algorandfoundation/algorand-typescript'
import { Account, BaseContract, internal } from '@algorandfoundation/algorand-typescript'
import { AccountData } from '../impl/account'
import { ApplicationData } from '../impl/application'
import { AssetData } from '../impl/asset'
Expand Down Expand Up @@ -60,8 +60,10 @@ class InternalContext {
return data
}

getApplicationData(id: internal.primitives.StubUint64Compat): ApplicationData {
const data = this.ledger.applicationDataMap.get(id)
getApplicationData(id: internal.primitives.StubUint64Compat | BaseContract): ApplicationData {
const uint64Id =
id instanceof BaseContract ? this.ledger.getApplicationForContract(id).id : internal.primitives.Uint64Cls.fromCompat(id)
const data = this.ledger.applicationDataMap.get(uint64Id)
if (!data) {
throw internal.errors.internalError('Unknown application, check correct testing context is active')
}
Expand Down
4 changes: 2 additions & 2 deletions src/impl/app-global.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Application, Bytes, bytes, internal, Uint64, uint64 } from '@algorandfoundation/algorand-typescript'
import { lazyContext } from '../context-helpers/internal-context'
import { asBytes } from '../util'
import { asBytes, toBytes } from '../util'
import { getApp } from './app-params'

export const AppGlobal: internal.opTypes.AppGlobalType = {
Expand All @@ -22,7 +22,7 @@ export const AppGlobal: internal.opTypes.AppGlobalType = {
if (!exists) {
return [Bytes(), false]
}
return [state!.value as bytes, exists]
return [toBytes(state!.value), exists]
},
getExUint64(a: Application | internal.primitives.StubUint64Compat, b: internal.primitives.StubBytesCompat): readonly [uint64, boolean] {
const app = getApp(a)
Expand Down
4 changes: 2 additions & 2 deletions src/impl/app-local.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Account, Application, Bytes, bytes, internal, Uint64, uint64 } from '@algorandfoundation/algorand-typescript'
import { lazyContext } from '../context-helpers/internal-context'
import { asBytes } from '../util'
import { asBytes, toBytes } from '../util'
import { getAccount } from './acct-params'
import { getApp } from './app-params'

Expand Down Expand Up @@ -32,7 +32,7 @@ export const AppLocal: internal.opTypes.AppLocalType = {
if (!exists) {
return [Bytes(), false]
}
return [state!.value as bytes, exists]
return [toBytes(state!.value), exists]
},
getExUint64: function (
a: Account | internal.primitives.StubUint64Compat,
Expand Down
Loading
Loading