Skip to content

Commit 6e27f77

Browse files
committed
feat: accept abi method reference as a parameter to methodSelector function
1 parent 1db13fb commit 6e27f77

File tree

6 files changed

+36
-11
lines changed

6 files changed

+36
-11
lines changed

examples/precompiled/contract.algo.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export class HelloFactory extends Contract {
99

1010
const helloApp = itxn
1111
.applicationCall({
12-
appArgs: [methodSelector('create(string)void'), encodeArc4('hello')],
12+
appArgs: [methodSelector(Hello.prototype.create), encodeArc4('hello')],
1313
approvalProgram: compiled.approvalProgram,
1414
clearStateProgram: compiled.clearStateProgram,
1515
globalNumBytes: 1,
@@ -18,7 +18,7 @@ export class HelloFactory extends Contract {
1818

1919
const txn = itxn
2020
.applicationCall({
21-
appArgs: [methodSelector('greet(string)string'), encodeArc4('world')],
21+
appArgs: [methodSelector(Hello.prototype.greet), encodeArc4('world')],
2222
appId: helloApp,
2323
})
2424
.submit()
@@ -29,7 +29,7 @@ export class HelloFactory extends Contract {
2929
itxn
3030
.applicationCall({
3131
appId: helloApp,
32-
appArgs: [methodSelector('delete()void')],
32+
appArgs: [methodSelector(Hello.prototype.delete)],
3333
onCompletion: OnCompleteAction.DeleteApplication,
3434
})
3535
.submit()

examples/precompiled/precompiled-apps.algo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ abstract class HelloBase extends Contract {
1515
}
1616

1717
export class Hello extends HelloBase {
18-
@abimethod({ onCreate: 'require' })
18+
@abimethod({ name: 'helloCreate', onCreate: 'require' })
1919
create(greeting: string) {
2020
this.greeting.value = greeting
2121
}

src/abi-metadata.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type { DeliberateAny } from './typescript-helpers'
77

88
export interface AbiMetadata {
99
methodName: string
10+
methodNameOverride?: string
1011
methodSignature: string | undefined
1112
argTypes: string[]
1213
returnType: string
@@ -42,6 +43,8 @@ export const captureMethodConfig = <T extends Contract>(
4243
config?: AbiMethodConfig<T> | BareMethodConfig,
4344
): void => {
4445
const metadata = getContractMethodAbiMetadata(contract, methodName)
46+
47+
metadata.methodNameOverride = config && 'name' in config ? config.name : undefined
4548
metadata.onCreate = config?.onCreate ?? 'disallow'
4649
metadata.allowActions = ([] as OnCompleteActionStr[]).concat(config?.allowActions ?? 'NoOp')
4750
}
@@ -53,7 +56,7 @@ export const hasAbiMetadata = <T extends Contract>(contract: T): boolean => {
5356
)
5457
}
5558
export const getContractAbiMetadata = <T extends BaseContract>(contract: T): Record<string, AbiMetadata> => {
56-
if ((contract as DeliberateAny)[isContractProxy]) {
59+
if ((contract as DeliberateAny)[AbiMetaSymbol]) {
5760
return (contract as DeliberateAny)[AbiMetaSymbol] as Record<string, AbiMetadata>
5861
}
5962
const contractClass = contract.constructor as { new (): T }
@@ -73,7 +76,7 @@ export const getArc4Signature = (metadata: AbiMetadata): string => {
7376
if (metadata.methodSignature === undefined) {
7477
const argTypes = metadata.argTypes.map((t) => JSON.parse(t) as TypeInfo).map(getArc4TypeName)
7578
const returnType = getArc4TypeName(JSON.parse(metadata.returnType) as TypeInfo)
76-
metadata.methodSignature = `${metadata.methodName}(${argTypes.join(',')})${returnType}`
79+
metadata.methodSignature = `${metadata.methodNameOverride ?? metadata.methodName}(${argTypes.join(',')})${returnType}`
7780
}
7881
return metadata.methodSignature
7982
}

src/impl/contract.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { arc4, bytes } from '@algorandfoundation/algorand-typescript'
22
import { encodingUtil } from '@algorandfoundation/puya-ts'
3-
import { captureMethodConfig } from '../abi-metadata'
3+
import { captureMethodConfig, getArc4Selector, getContractMethodAbiMetadata } from '../abi-metadata'
44
import type { DeliberateAny } from '../typescript-helpers'
55
import { BaseContract } from './base-contract'
66
import { sha512_256 } from './crypto'
@@ -38,6 +38,11 @@ export function baremethod<TContract extends Contract>(config?: arc4.BareMethodC
3838
}
3939
}
4040

41-
export const methodSelector: typeof arc4.methodSelector = (methodSignature: string): bytes => {
42-
return sha512_256(Bytes(encodingUtil.utf8ToUint8Array(methodSignature))).slice(0, 4)
41+
export const methodSelector = (methodSignature: Parameters<typeof arc4.methodSelector>[0], contract?: DeliberateAny): bytes => {
42+
if (typeof methodSignature === 'string') {
43+
return sha512_256(Bytes(encodingUtil.utf8ToUint8Array(methodSignature))).slice(0, 4)
44+
} else {
45+
const abiMetadata = getContractMethodAbiMetadata(contract, methodSignature.name)
46+
return Bytes(getArc4Selector(abiMetadata))
47+
}
4348
}

src/test-transformer/node-factory.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,16 @@ export const nodeFactory = {
107107
[typeInfoArg, ...(node.arguments ?? [])].filter((arg) => !!arg),
108108
)
109109
},
110+
111+
callMethodSelectorFunction(node: ts.CallExpression) {
112+
if (
113+
node.arguments.length === 1 &&
114+
ts.isPropertyAccessExpression(node.arguments[0]) &&
115+
ts.isPropertyAccessExpression(node.arguments[0].expression)
116+
) {
117+
const contractIdenifier = node.arguments[0].expression.expression
118+
return factory.updateCallExpression(node, node.expression, node.typeArguments, [...node.arguments, contractIdenifier])
119+
}
120+
return node
121+
},
110122
} satisfies Record<string, (...args: DeliberateAny[]) => ts.Node>

src/test-transformer/visitors.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,11 @@ class ExpressionVisitor {
168168
const targetTypeInfo = getGenericTypeInfo(targetType)
169169
infoArg = targetTypeInfo
170170
}
171-
updatedNode = stubbedFunctionName ? nodeFactory.callStubbedFunction(stubbedFunctionName, updatedNode, infoArg) : updatedNode
171+
updatedNode = stubbedFunctionName
172+
? isCallingMethodSelector(stubbedFunctionName)
173+
? nodeFactory.callMethodSelectorFunction(updatedNode)
174+
: nodeFactory.callStubbedFunction(stubbedFunctionName, updatedNode, infoArg)
175+
: updatedNode
172176
}
173177
return needsToCaptureTypeInfo
174178
? nodeFactory.captureGenericTypeInfo(ts.visitEachChild(updatedNode, this.visit, this.context), JSON.stringify(info))
@@ -417,9 +421,10 @@ const tryGetStubbedFunctionName = (node: ts.CallExpression, helper: VisitorHelpe
417421
if (sourceFileName && !algotsModulePaths.some((s) => sourceFileName.includes(s))) return undefined
418422
}
419423
const functionName = functionSymbol?.getName() ?? identityExpression.text
420-
const stubbedFunctionNames = ['interpretAsArc4', 'decodeArc4', 'encodeArc4', 'emit']
424+
const stubbedFunctionNames = ['interpretAsArc4', 'decodeArc4', 'encodeArc4', 'emit', 'methodSelector']
421425
return stubbedFunctionNames.includes(functionName) ? functionName : undefined
422426
}
423427

424428
const isCallingDecodeArc4 = (functionName: string | undefined): boolean => ['decodeArc4', 'encodeArc4'].includes(functionName ?? '')
425429
const isCallingEmit = (functionName: string | undefined): boolean => 'emit' === (functionName ?? '')
430+
const isCallingMethodSelector = (functionName: string | undefined): boolean => 'methodSelector' === (functionName ?? '')

0 commit comments

Comments
 (0)