1- import type { Contract } from '@algorandfoundation/algorand-typescript'
21import type { AbiMethodConfig , BareMethodConfig , CreateOptions , OnCompleteActionStr } from '@algorandfoundation/algorand-typescript/arc4'
32import js_sha512 from 'js-sha512'
43import type { TypeInfo } from './encoders'
4+ import { Contract } from './impl/contract'
55import { getArc4TypeName as getArc4TypeNameForARC4Encoded } from './impl/encoded-types'
6- import type { DeliberateAny } from './typescript-helpers'
76
87export interface AbiMetadata {
98 methodName : string
@@ -14,40 +13,15 @@ export interface AbiMetadata {
1413 onCreate ?: CreateOptions
1514 allowActions ?: OnCompleteActionStr [ ]
1615}
17- const AbiMetaSymbol = Symbol ( 'AbiMetadata' )
18- const overwrittenMetadata : Array < readonly [ Contract | { new ( ) : Contract } , Record < string , AbiMetadata > ] > = [ ]
16+ export const AbiMetaSymbol = Symbol ( 'AbiMetadata' )
1917export const isContractProxy = Symbol ( 'isContractProxy' )
18+ const metadataStore : Map < { new ( ) : Contract } , Record < string , AbiMetadata > > = new Map ( )
2019export const attachAbiMetadata = ( contract : { new ( ) : Contract } , methodName : string , metadata : AbiMetadata ) : void => {
21- const metadatas : Record < string , AbiMetadata > = ( AbiMetaSymbol in contract ? contract [ AbiMetaSymbol ] : { } ) as Record < string , AbiMetadata >
22-
23- // classes inherited from the same parent shares the same metadata object stored in `AbiMetaSymbol`
24- // to prevent one subclass from overwriting the metadata of another subclass, we store the overwritten metadata in a separate array
25- if ( metadatas [ methodName ] ) {
26- let contractMetadatas = overwrittenMetadata . find ( ( [ c ] ) => c === contract )
27- if ( ! contractMetadatas ) {
28- contractMetadatas = [ contract , { } ]
29- overwrittenMetadata . push ( contractMetadatas )
30- }
31- contractMetadatas [ 1 ] [ methodName ] = metadata
32- } else {
33- metadatas [ methodName ] = metadata
34- }
35- if ( ! ( AbiMetaSymbol in contract ) ) {
36- Object . defineProperty ( contract , AbiMetaSymbol , {
37- value : metadatas ,
38- writable : true ,
39- enumerable : true ,
40- } )
20+ if ( ! metadataStore . has ( contract ) ) {
21+ metadataStore . set ( contract , { } )
4122 }
42- }
43-
44- export const copyAbiMetadatas = < T extends Contract > ( sourceContract : T , targetContract : T ) : void => {
45- const metadatas = getContractAbiMetadata ( sourceContract )
46- Object . defineProperty ( targetContract , AbiMetaSymbol , {
47- value : metadatas ,
48- writable : true ,
49- enumerable : false ,
50- } )
23+ const metadatas : Record < string , AbiMetadata > = metadataStore . get ( contract ) as Record < string , AbiMetadata >
24+ metadatas [ methodName ] = metadata
5125}
5226
5327export const captureMethodConfig = < T extends Contract > (
@@ -61,23 +35,33 @@ export const captureMethodConfig = <T extends Contract>(
6135 metadata . allowActions = ( [ ] as OnCompleteActionStr [ ] ) . concat ( config ?. allowActions ?? 'NoOp' )
6236}
6337
64- export const hasAbiMetadata = < T extends Contract > ( contract : T ) : boolean => {
65- const contractClass = contract . constructor as { new ( ) : T }
66- return (
67- Object . getOwnPropertySymbols ( contractClass ) . some ( ( s ) => s . toString ( ) === AbiMetaSymbol . toString ( ) ) || AbiMetaSymbol in contractClass
68- )
69- }
70- export const getContractAbiMetadata = < T extends Contract > ( contract : T ) : Record < string , AbiMetadata > => {
71- const overwrittenMetadataEntry = overwrittenMetadata . find ( ( [ c ] ) => c === contract )
72- if ( ( contract as DeliberateAny ) [ AbiMetaSymbol ] ) {
73- return { ...( ( contract as DeliberateAny ) [ AbiMetaSymbol ] as Record < string , AbiMetadata > ) , ...overwrittenMetadataEntry ?. [ 1 ] }
38+ export const getContractAbiMetadata = < T extends Contract > ( contract : T | { new ( ) : T } ) : Record < string , AbiMetadata > => {
39+ // Initialize result object to store merged metadata
40+ const result : Record < string , AbiMetadata > = { }
41+
42+ // Get the contract's class
43+ let currentClass = contract instanceof Contract ? ( contract . constructor as { new ( ) : T } ) : contract
44+
45+ // Walk up the prototype chain
46+ while ( currentClass && currentClass . prototype ) {
47+ // Find metadata for current class
48+ const currentMetadata = metadataStore . get ( currentClass )
49+
50+ if ( currentMetadata ) {
51+ // Merge metadata with existing result (don't override existing entries)
52+ const classMetadata = currentMetadata
53+ for ( const [ methodName , metadata ] of Object . entries ( classMetadata ) ) {
54+ if ( ! ( methodName in result ) ) {
55+ result [ methodName ] = metadata
56+ }
57+ }
58+ }
59+
60+ // Move up the prototype chain
61+ currentClass = Object . getPrototypeOf ( currentClass )
7462 }
75- const contractClass = contract . constructor as { new ( ) : T }
76- const s = Object . getOwnPropertySymbols ( contractClass ) . find ( ( s ) => s . toString ( ) === AbiMetaSymbol . toString ( ) )
77- const metadatas : Record < string , AbiMetadata > = (
78- s ? ( contractClass as DeliberateAny ) [ s ] : AbiMetaSymbol in contractClass ? contractClass [ AbiMetaSymbol ] : { }
79- ) as Record < string , AbiMetadata >
80- return { ...metadatas , ...overwrittenMetadataEntry ?. [ 1 ] }
63+
64+ return result
8165}
8266
8367export const getContractMethodAbiMetadata = < T extends Contract > ( contract : T , methodName : string ) : AbiMetadata => {
0 commit comments