11import { BaseContract , Contract } from '@algorandfoundation/algorand-typescript'
22import { AbiMethodConfig , BareMethodConfig , CreateOptions , OnCompleteActionStr } from '@algorandfoundation/algorand-typescript/arc4'
3+ import { ABIMethod } from 'algosdk'
4+ import { TypeInfo } from './encoders'
5+ import { getArc4TypeName as getArc4TypeNameForARC4Encoded } from './impl/encoded-types'
36import { DeliberateAny } from './typescript-helpers'
47
58export interface AbiMetadata {
69 methodName : string
7- methodSelector : string
10+ methodSignature : string | undefined
811 argTypes : string [ ]
912 returnType : string
1013 onCreate ?: CreateOptions
1114 allowActions ?: OnCompleteActionStr [ ]
1215}
1316const AbiMetaSymbol = Symbol ( 'AbiMetadata' )
17+ export const isContractProxy = Symbol ( 'isContractProxy' )
1418export const attachAbiMetadata = ( contract : { new ( ) : Contract } , methodName : string , metadata : AbiMetadata ) : void => {
1519 const metadatas : Record < string , AbiMetadata > = ( AbiMetaSymbol in contract ? contract [ AbiMetaSymbol ] : { } ) as Record < string , AbiMetadata >
1620 metadatas [ methodName ] = metadata
@@ -23,38 +27,89 @@ export const attachAbiMetadata = (contract: { new (): Contract }, methodName: st
2327 }
2428}
2529
30+ export const copyAbiMetadatas = < T extends BaseContract > ( sourceContract : T , targetContract : T ) : void => {
31+ const metadatas = getContractAbiMetadata ( sourceContract )
32+ Object . defineProperty ( targetContract , AbiMetaSymbol , {
33+ value : metadatas ,
34+ writable : true ,
35+ enumerable : false ,
36+ } )
37+ }
38+
2639export const captureMethodConfig = < T extends Contract > (
2740 contract : T ,
2841 methodName : string ,
2942 config ?: AbiMethodConfig < T > | BareMethodConfig ,
3043) : void => {
31- const metadata = ensureMetadata ( contract , methodName )
44+ const metadata = getContractMethodAbiMetadata ( contract , methodName )
3245 metadata . onCreate = config ?. onCreate ?? 'disallow'
3346 metadata . allowActions = ( [ ] as OnCompleteActionStr [ ] ) . concat ( config ?. allowActions ?? 'NoOp' )
3447}
3548
36- const ensureMetadata = < T extends Contract > ( contract : T , methodName : string ) : AbiMetadata => {
37- if ( ! hasAbiMetadata ( contract ) ) {
38- const contractClass = contract . constructor as { new ( ) : T }
39- Object . getOwnPropertyNames ( Object . getPrototypeOf ( contract ) ) . forEach ( ( name ) => {
40- attachAbiMetadata ( contractClass , name , { methodName : name , methodSelector : name , argTypes : [ ] , returnType : '' } )
41- } )
42- }
43- return getAbiMetadata ( contract , methodName )
44- }
45-
4649export const hasAbiMetadata = < T extends Contract > ( contract : T ) : boolean => {
4750 const contractClass = contract . constructor as { new ( ) : T }
4851 return (
4952 Object . getOwnPropertySymbols ( contractClass ) . some ( ( s ) => s . toString ( ) === AbiMetaSymbol . toString ( ) ) || AbiMetaSymbol in contractClass
5053 )
5154}
52-
53- export const getAbiMetadata = < T extends BaseContract > ( contract : T , methodName : string ) : AbiMetadata => {
55+ export const getContractAbiMetadata = < T extends BaseContract > ( contract : T ) : Record < string , AbiMetadata > => {
56+ if ( ( contract as DeliberateAny ) [ isContractProxy ] ) {
57+ return ( contract as DeliberateAny ) [ AbiMetaSymbol ] as Record < string , AbiMetadata >
58+ }
5459 const contractClass = contract . constructor as { new ( ) : T }
5560 const s = Object . getOwnPropertySymbols ( contractClass ) . find ( ( s ) => s . toString ( ) === AbiMetaSymbol . toString ( ) )
5661 const metadatas : Record < string , AbiMetadata > = (
5762 s ? ( contractClass as DeliberateAny ) [ s ] : AbiMetaSymbol in contractClass ? contractClass [ AbiMetaSymbol ] : { }
5863 ) as Record < string , AbiMetadata >
64+ return metadatas
65+ }
66+
67+ export const getContractMethodAbiMetadata = < T extends BaseContract > ( contract : T , methodName : string ) : AbiMetadata => {
68+ const metadatas = getContractAbiMetadata ( contract )
5969 return metadatas [ methodName ]
6070}
71+
72+ export const getArc4Signature = ( metadata : AbiMetadata ) : string => {
73+ if ( metadata . methodSignature === undefined ) {
74+ const argTypes = metadata . argTypes . map ( ( t ) => JSON . parse ( t ) as TypeInfo ) . map ( getArc4TypeName )
75+ const returnType = getArc4TypeName ( JSON . parse ( metadata . returnType ) as TypeInfo )
76+ const method = new ABIMethod ( { name : metadata . methodName , args : argTypes . map ( ( t ) => ( { type : t } ) ) , returns : { type : returnType } } )
77+ metadata . methodSignature = method . getSignature ( )
78+ }
79+ return metadata . methodSignature
80+ }
81+
82+ const getArc4TypeName = ( t : TypeInfo ) : string => {
83+ const map : Record < string , string | ( ( t : TypeInfo ) => string ) > = {
84+ void : 'void' ,
85+ account : 'account' ,
86+ application : 'application' ,
87+ asset : 'asset' ,
88+ boolean : 'bool' ,
89+ biguint : 'uint512' ,
90+ bytes : 'byte[]' ,
91+ string : 'string' ,
92+ uint64 : 'uint64' ,
93+ OnCompleteAction : 'uint64' ,
94+ TransactionType : 'uint64' ,
95+ Transaction : 'txn' ,
96+ PaymentTxn : 'pay' ,
97+ KeyRegistrationTxn : 'keyreg' ,
98+ AssetConfigTxn : 'acfg' ,
99+ AssetTransferTxn : 'axfer' ,
100+ AssetFreezeTxn : 'afrz' ,
101+ ApplicationTxn : 'appl' ,
102+ 'Tuple<.*>' : ( t ) =>
103+ `(${ Object . values ( t . genericArgs as Record < string , TypeInfo > )
104+ . map ( getArc4TypeName )
105+ . join ( ',' ) } )`,
106+ }
107+ const entry = Object . entries ( map ) . find ( ( [ k , _ ] ) => new RegExp ( `^${ k } $` , 'i' ) . test ( t . name ) ) ?. [ 1 ]
108+ if ( entry === undefined ) {
109+ return getArc4TypeNameForARC4Encoded ( t ) ?? t . name
110+ }
111+ if ( entry instanceof Function ) {
112+ return entry ( t )
113+ }
114+ return entry
115+ }
0 commit comments