diff --git a/src/awst_build/contract-visitor.ts b/src/awst_build/contract-visitor.ts index 8bada4df..2626bea4 100644 --- a/src/awst_build/contract-visitor.ts +++ b/src/awst_build/contract-visitor.ts @@ -1,5 +1,5 @@ import ts from 'typescript' -import { ContractReference } from '../awst/models' +import { ARC4BareMethodConfig, ARC4CreateOption, ContractReference, OnCompletionAction } from '../awst/models' import { nodeFactory } from '../awst/node-factory' import type * as awst from '../awst/nodes' import type { ContractMethod } from '../awst/nodes' @@ -9,7 +9,7 @@ import { voidWType } from '../awst/wtypes' import { Constants } from '../constants' import { AwstBuildFailureError, NotSupported, TodoError } from '../errors' import { logger } from '../logger' -import { codeInvariant, invariant } from '../util' +import { codeInvariant, invariant, isIn } from '../util' import type { ClassElements } from '../visitor/syntax-names' import type { Visitor } from '../visitor/visitor' import { accept } from '../visitor/visitor' @@ -58,6 +58,25 @@ export class ContractVisitor extends BaseVisitor implements Visitor + isIn(s.arc4MethodConfig?.create, [ARC4CreateOption.Require, ARC4CreateOption.Allow]), + ) + const hasBareNoopMethod = this._subroutines.some( + (s) => s.arc4MethodConfig?.isBare && isIn(OnCompletionAction.NoOp, s.arc4MethodConfig.allowedCompletionTypes), + ) + if (!isAbstract && !hasCreateMethod) { + if (hasBareNoopMethod) { + logger.error( + sourceLocation, + `Non-abstract ARC4 contract has no methods which can be called to create the contract. ` + + `An implicit one could not be inserted as there is already a bare method handling the NoOp on completion action. ` + + `In order to allow creating the contract specify { onCreate: 'allow' } or { onCreate: 'require' } in an @abimethod or @baremethod decorator above the chosen method.`, + ) + } else { + // TODO: This should check base classes + this._subroutines.push(this.makeDefaultCreate(sourceLocation)) + } + } const cref = ContractReference.fromPType(this._contractPType) this.result = new ContractFragment({ name: this._className, @@ -118,6 +137,27 @@ export class ContractVisitor extends BaseVisitor implements Visitor { return [ diff --git a/tests/expected-output/cant-create.algo.ts b/tests/expected-output/cant-create.algo.ts new file mode 100644 index 00000000..91efbae9 --- /dev/null +++ b/tests/expected-output/cant-create.algo.ts @@ -0,0 +1,22 @@ +import { abimethod, Contract } from '@algorandfoundation/algorand-typescript' +import { baremethod } from '@algorandfoundation/algorand-typescript/arc4' + +// @expect-error Non-abstract ARC4 contract has no methods which can be called to create the contract... +export class CantCreate extends Contract { + @baremethod({ allowActions: 'NoOp' }) + public handleBare() {} +} + +export abstract class CantCreateAbstract extends Contract { + @baremethod({ allowActions: 'NoOp' }) + public handleBare() {} +} + +export abstract class NoBare extends Contract { + @abimethod({ allowActions: 'NoOp' }) + public handleNoop() {} +} +export abstract class NoNoOp extends Contract { + @baremethod({ allowActions: 'UpdateApplication' }) + public handleUpdate() {} +}