diff --git a/src/abi-metadata.ts b/src/abi-metadata.ts index 52826f8..1b16d49 100644 --- a/src/abi-metadata.ts +++ b/src/abi-metadata.ts @@ -22,8 +22,14 @@ const metadataStore: WeakMap<{ new (): Contract }, Record> const contractSymbolMap: Map = new Map() const contractMap: WeakMap = new WeakMap() /** @internal */ -export const attachAbiMetadata = (contract: { new (): Contract }, methodName: string, metadata: AbiMetadata, fileName: string): void => { - const contractFullName = `${fileName}::${contract.prototype.constructor.name}` +export const attachAbiMetadata = ( + contract: { new (): Contract }, + methodName: string, + metadata: AbiMetadata, + fileName: string, + contractName: string, +): void => { + const contractFullName = `${fileName}::${contractName}` if (!contractSymbolMap.has(contractFullName)) { contractSymbolMap.set(contractFullName, Symbol(contractFullName)) } diff --git a/src/test-transformer/node-factory.ts b/src/test-transformer/node-factory.ts index 484de3f..cf99b7f 100644 --- a/src/test-transformer/node-factory.ts +++ b/src/test-transformer/node-factory.ts @@ -81,7 +81,13 @@ export const nodeFactory = { factory.createCallExpression( factory.createPropertyAccessExpression(factory.createIdentifier('runtimeHelpers'), factory.createIdentifier('attachAbiMetadata')), undefined, - [classIdentifier, methodName, metadata, factory.createStringLiteral(sourceFileName)], + [ + classIdentifier, + methodName, + metadata, + factory.createStringLiteral(sourceFileName), + factory.createStringLiteral(classIdentifier.text), + ], ), ) }, diff --git a/src/test-transformer/visitors.ts b/src/test-transformer/visitors.ts index da41ea9..a2793e7 100644 --- a/src/test-transformer/visitors.ts +++ b/src/test-transformer/visitors.ts @@ -355,6 +355,7 @@ class MethodDecVisitor extends FunctionOrMethodVisitor { class ClassVisitor { private isArc4: boolean + private _sourceFileName: string | undefined constructor( private context: Context, private helper: VisitorHelper, @@ -368,6 +369,13 @@ class ClassVisitor { return this.visit(this.classDec) as ts.ClassDeclaration } + private get sourceFileName(): string { + if (!this._sourceFileName) { + this._sourceFileName = normalisePath(this.classDec.parent.getSourceFile().fileName, this.context.currentDirectory) + } + return this._sourceFileName + } + private visit = (node: ts.Node): ts.Node => { if (ts.isMethodDeclaration(node)) { if (this.classDec.name && this.isArc4) { @@ -375,8 +383,9 @@ class ClassVisitor { if (methodType instanceof ptypes.FunctionPType) { const argTypes = methodType.parameters.map((p) => JSON.stringify(getGenericTypeInfo(p[1]))) const returnType = JSON.stringify(getGenericTypeInfo(methodType.returnType)) - const sourceFileName = normalisePath(this.classDec.parent.getSourceFile().fileName, this.context.currentDirectory) - this.helper.additionalStatements.push(nodeFactory.attachMetaData(sourceFileName, this.classDec.name, node, argTypes, returnType)) + this.helper.additionalStatements.push( + nodeFactory.attachMetaData(this.sourceFileName, this.classDec.name, node, argTypes, returnType), + ) } } diff --git a/tests/arc4/abicall-decorated.algo.spec.ts b/tests/arc4/abicall-decorated.algo.spec.ts new file mode 100644 index 0000000..9c8b0d2 --- /dev/null +++ b/tests/arc4/abicall-decorated.algo.spec.ts @@ -0,0 +1,32 @@ +import { ApplicationSpy } from '@algorandfoundation/algorand-typescript-testing' +import { methodSelector } from '@algorandfoundation/algorand-typescript/arc4' +import { afterEach, describe, expect, test } from 'vitest' +import { TestExecutionContext } from '../../src/test-execution-context' +import { Hello } from '../artifacts/abicall-decorated/contract.algo' +import { DecoratedGreeter } from '../artifacts/abicall-decorated/decorated-greeter.algo' + +describe('abicalll polytype ', () => { + const ctx = new TestExecutionContext() + + afterEach(() => { + ctx.reset() + }) + + test('test call contract one', async () => { + const greeter = ctx.contract.create(DecoratedGreeter) + const hello = ctx.contract.create(Hello) + + const greeterApp = ctx.ledger.getApplicationForContract(greeter) + + hello.createApplication(greeterApp) + + const spy = new ApplicationSpy() + spy.onAbiCall(methodSelector(DecoratedGreeter.prototype.greet), (itxnContext) => { + itxnContext.setReturnValue('Hello, World, from Algorand') + }) + ctx.addApplicationSpy(spy) + + const result = hello.greet() + expect(result).toEqual('Hello, World, from Algorand') + }) +}) diff --git a/tests/artifacts/abicall-decorated/contract.algo.ts b/tests/artifacts/abicall-decorated/contract.algo.ts new file mode 100644 index 0000000..413bb25 --- /dev/null +++ b/tests/artifacts/abicall-decorated/contract.algo.ts @@ -0,0 +1,22 @@ +import type { Application } from '@algorandfoundation/algorand-typescript' +import { Contract, GlobalState, abimethod } from '@algorandfoundation/algorand-typescript' +import { abiCall } from '@algorandfoundation/algorand-typescript/arc4' +import type { DecoratedGreeter } from './decorated-greeter.algo' + +export class Hello extends Contract { + greeterApp = GlobalState({ key: 'greeterApp' }) + + @abimethod({ onCreate: 'require' }) + createApplication(greeterApp: Application): void { + this.greeterApp.value = greeterApp + } + + greet(): string { + abiCall({ appId: this.greeterApp.value, args: ['Hello'] }) + abiCall({ appId: this.greeterApp.value, args: ['World'] }) + + const { returnValue } = abiCall({ appId: this.greeterApp.value, args: ['from Algorand'] }) + + return returnValue + } +} diff --git a/tests/artifacts/abicall-decorated/decorated-greeter.algo.ts b/tests/artifacts/abicall-decorated/decorated-greeter.algo.ts new file mode 100644 index 0000000..f97a3b0 --- /dev/null +++ b/tests/artifacts/abicall-decorated/decorated-greeter.algo.ts @@ -0,0 +1,22 @@ +import { abimethod, contract, Contract, GlobalState } from '@algorandfoundation/algorand-typescript' + +@contract({ name: 'Greeter', avmVersion: 11 }) +export class DecoratedGreeter extends Contract { + greeting = GlobalState({ initialValue: '' }) + name = GlobalState({ initialValue: '' }) + + @abimethod() + setGreeting(greeting: string) { + this.greeting.value = greeting + } + + @abimethod() + setName(name: string) { + this.name.value = name + } + + @abimethod() + greet(from: string): string { + return `${this.greeting.value}, ${this.name.value}, ${from}` + } +} diff --git a/tests/artifacts/circurlar-reference/circular-reference-2.algo.ts b/tests/artifacts/circurlar-reference/circular-reference-2.algo.ts index cebb403..6ccc480 100644 --- a/tests/artifacts/circurlar-reference/circular-reference-2.algo.ts +++ b/tests/artifacts/circurlar-reference/circular-reference-2.algo.ts @@ -1,8 +1,9 @@ import type { Application } from '@algorandfoundation/algorand-typescript' -import { Contract, log } from '@algorandfoundation/algorand-typescript' +import { contract, Contract, log } from '@algorandfoundation/algorand-typescript' import { abiCall } from '@algorandfoundation/algorand-typescript/arc4' import type { ContractOne } from './circular-reference.algo' +@contract({ name: 'ContractTwo' }) export class ContractTwo extends Contract { test(appId: Application) { const result = abiCall({ appId, args: [appId] }) diff --git a/tests/artifacts/circurlar-reference/circular-reference.algo.ts b/tests/artifacts/circurlar-reference/circular-reference.algo.ts index e6b111f..2e12610 100644 --- a/tests/artifacts/circurlar-reference/circular-reference.algo.ts +++ b/tests/artifacts/circurlar-reference/circular-reference.algo.ts @@ -1,8 +1,9 @@ import type { Application } from '@algorandfoundation/algorand-typescript' -import { Contract, log } from '@algorandfoundation/algorand-typescript' +import { contract, Contract, log } from '@algorandfoundation/algorand-typescript' import { abiCall } from '@algorandfoundation/algorand-typescript/arc4' import type { ContractTwo } from './circular-reference-2.algo' +@contract({ name: 'ContractOne' }) export class ContractOne extends Contract { test(appId: Application) { const result = abiCall({ appId, args: [appId] })