1
1
import { BaseContract , Contract } from '@algorandfoundation/algorand-typescript'
2
2
import { 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'
3
6
import { DeliberateAny } from './typescript-helpers'
4
7
5
8
export interface AbiMetadata {
6
9
methodName : string
7
- methodSelector : string
10
+ methodSignature : string | undefined
8
11
argTypes : string [ ]
9
12
returnType : string
10
13
onCreate ?: CreateOptions
11
14
allowActions ?: OnCompleteActionStr [ ]
12
15
}
13
16
const AbiMetaSymbol = Symbol ( 'AbiMetadata' )
17
+ export const isContractProxy = Symbol ( 'isContractProxy' )
14
18
export const attachAbiMetadata = ( contract : { new ( ) : Contract } , methodName : string , metadata : AbiMetadata ) : void => {
15
19
const metadatas : Record < string , AbiMetadata > = ( AbiMetaSymbol in contract ? contract [ AbiMetaSymbol ] : { } ) as Record < string , AbiMetadata >
16
20
metadatas [ methodName ] = metadata
@@ -23,38 +27,89 @@ export const attachAbiMetadata = (contract: { new (): Contract }, methodName: st
23
27
}
24
28
}
25
29
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
+
26
39
export const captureMethodConfig = < T extends Contract > (
27
40
contract : T ,
28
41
methodName : string ,
29
42
config ?: AbiMethodConfig < T > | BareMethodConfig ,
30
43
) : void => {
31
- const metadata = ensureMetadata ( contract , methodName )
44
+ const metadata = getContractMethodAbiMetadata ( contract , methodName )
32
45
metadata . onCreate = config ?. onCreate ?? 'disallow'
33
46
metadata . allowActions = ( [ ] as OnCompleteActionStr [ ] ) . concat ( config ?. allowActions ?? 'NoOp' )
34
47
}
35
48
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
-
46
49
export const hasAbiMetadata = < T extends Contract > ( contract : T ) : boolean => {
47
50
const contractClass = contract . constructor as { new ( ) : T }
48
51
return (
49
52
Object . getOwnPropertySymbols ( contractClass ) . some ( ( s ) => s . toString ( ) === AbiMetaSymbol . toString ( ) ) || AbiMetaSymbol in contractClass
50
53
)
51
54
}
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
+ }
54
59
const contractClass = contract . constructor as { new ( ) : T }
55
60
const s = Object . getOwnPropertySymbols ( contractClass ) . find ( ( s ) => s . toString ( ) === AbiMetaSymbol . toString ( ) )
56
61
const metadatas : Record < string , AbiMetadata > = (
57
62
s ? ( contractClass as DeliberateAny ) [ s ] : AbiMetaSymbol in contractClass ? contractClass [ AbiMetaSymbol ] : { }
58
63
) 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 )
59
69
return metadatas [ methodName ]
60
70
}
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