@cityofzion/bs-neo3 0.7.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/.rush/temp/operation/build/all.log +1 -0
  2. package/.rush/temp/operation/build/state.json +1 -1
  3. package/.rush/temp/package-deps_build.json +7 -5
  4. package/.rush/temp/shrinkwrap-deps.json +354 -130
  5. package/CHANGELOG.json +22 -0
  6. package/CHANGELOG.md +11 -0
  7. package/bs-neo3.build.log +1 -2
  8. package/dist/BSNeo3.d.ts +28 -28
  9. package/dist/BSNeo3.js +184 -184
  10. package/dist/DoraBDSNeo3.d.ts +12 -12
  11. package/dist/DoraBDSNeo3.js +169 -169
  12. package/dist/DoraESNeo3.d.ts +7 -7
  13. package/dist/DoraESNeo3.js +19 -19
  14. package/dist/FlamingoEDSNeo3.d.ts +8 -8
  15. package/dist/FlamingoEDSNeo3.js +45 -44
  16. package/dist/GhostMarketNDSNeo3.d.ts +10 -10
  17. package/dist/GhostMarketNDSNeo3.js +75 -75
  18. package/dist/RpcBDSNeo3.d.ts +16 -16
  19. package/dist/RpcBDSNeo3.js +153 -153
  20. package/dist/assets/tokens/common.json +14 -14
  21. package/dist/assets/tokens/mainnet.json +116 -116
  22. package/dist/constants.d.ts +7 -7
  23. package/dist/constants.js +28 -28
  24. package/dist/index.d.ts +6 -6
  25. package/dist/index.js +22 -22
  26. package/jest.config.ts +13 -13
  27. package/jest.setup.ts +1 -1
  28. package/package.json +33 -32
  29. package/src/BSNeo3.ts +228 -228
  30. package/src/DoraBDSNeo3.ts +180 -180
  31. package/src/DoraESNeo3.ts +19 -19
  32. package/src/FlamingoEDSNeo3.ts +47 -45
  33. package/src/GhostMarketNDSNeo3.ts +117 -117
  34. package/src/RpcBDSNeo3.ts +157 -157
  35. package/src/__tests__/BDSNeo3.spec.ts +126 -126
  36. package/src/__tests__/BSNeo3.spec.ts +142 -142
  37. package/src/__tests__/DoraESNeo3.spec.ts +23 -23
  38. package/src/__tests__/FlamingoEDSNeo3.spec.ts +48 -45
  39. package/src/__tests__/GhostMarketNDSNeo3.spec.ts +43 -43
  40. package/src/__tests__/utils/sleep.ts +1 -1
  41. package/src/assets/tokens/common.json +13 -13
  42. package/src/assets/tokens/mainnet.json +115 -115
  43. package/src/constants.ts +29 -29
  44. package/src/index.ts +6 -6
  45. package/tsconfig.build.json +4 -4
  46. package/tsconfig.json +14 -14
  47. package/dist/BDSNeo3.d.ts +0 -15
  48. package/dist/BDSNeo3.js +0 -211
  49. package/dist/assets/tokens.json +0 -128
  50. package/dist/exceptions.d.ts +0 -3
  51. package/dist/exceptions.js +0 -8
  52. package/dist/explorer/dora/DoraNeo3Responses.d.ts +0 -174
  53. package/dist/explorer/dora/DoraNeo3Responses.js +0 -3
  54. package/dist/explorer/dora/DoraNeo3Routes.d.ts +0 -6
  55. package/dist/explorer/dora/DoraNeo3Routes.js +0 -9
  56. package/dist/explorer/index.d.ts +0 -6
  57. package/dist/explorer/index.js +0 -23
  58. package/docs/.nojekyll +0 -1
  59. package/docs/assets/highlight.css +0 -22
  60. package/docs/assets/main.js +0 -58
  61. package/docs/assets/search.js +0 -1
  62. package/docs/assets/style.css +0 -1280
  63. package/docs/classes/BDSNeo3.html +0 -253
  64. package/docs/classes/BSNeo3.html +0 -442
  65. package/docs/index.html +0 -82
  66. package/docs/interfaces/DoraNeo3Abi.html +0 -78
  67. package/docs/interfaces/DoraNeo3Asset.html +0 -117
  68. package/docs/interfaces/DoraNeo3AssetState.html +0 -96
  69. package/docs/interfaces/DoraNeo3Balance.html +0 -89
  70. package/docs/interfaces/DoraNeo3ConsensusNode.html +0 -75
  71. package/docs/interfaces/DoraNeo3Contract.html +0 -110
  72. package/docs/interfaces/DoraNeo3Event.html +0 -75
  73. package/docs/interfaces/DoraNeo3Features.html +0 -84
  74. package/docs/interfaces/DoraNeo3HistoryState.html +0 -75
  75. package/docs/interfaces/DoraNeo3Invocation.html +0 -75
  76. package/docs/interfaces/DoraNeo3Item.html +0 -131
  77. package/docs/interfaces/DoraNeo3Manifest.html +0 -117
  78. package/docs/interfaces/DoraNeo3Metadata.html +0 -138
  79. package/docs/interfaces/DoraNeo3Method.html +0 -102
  80. package/docs/interfaces/DoraNeo3Nef.html +0 -103
  81. package/docs/interfaces/DoraNeo3Notification.html +0 -82
  82. package/docs/interfaces/DoraNeo3Parameter.html +0 -75
  83. package/docs/interfaces/DoraNeo3Permission.html +0 -75
  84. package/docs/interfaces/DoraNeo3Signer.html +0 -75
  85. package/docs/interfaces/DoraNeo3Token.html +0 -96
  86. package/docs/interfaces/DoraNeo3Transaction.html +0 -166
  87. package/docs/interfaces/DoraNeo3TransactionHistory.html +0 -75
  88. package/docs/interfaces/DoraNeo3Transfer.html +0 -117
  89. package/docs/interfaces/DoraNeo3Witness.html +0 -75
  90. package/docs/modules.html +0 -122
  91. package/docs/variables/DORA_ASSET.html +0 -81
  92. package/docs/variables/DORA_BALANCE.html +0 -81
  93. package/docs/variables/DORA_CONTRACT.html +0 -81
  94. package/docs/variables/DORA_NODES.html +0 -81
  95. package/docs/variables/DORA_TRANSACTION.html +0 -81
  96. package/docs/variables/DORA_TRANSACTIONS.html +0 -81
  97. package/docs/variables/explorerOptions.html +0 -86
  98. package/docs/variables/gasInfoNeo3.html +0 -90
  99. package/docs/variables/neoInfoNeo3.html +0 -90
package/src/BSNeo3.ts CHANGED
@@ -1,228 +1,228 @@
1
- import {
2
- BlockchainDataService,
3
- BlockchainService,
4
- BSClaimable,
5
- Account,
6
- ExchangeDataService,
7
- BDSClaimable,
8
- Token,
9
- BSWithNameService,
10
- Network,
11
- PartialBy,
12
- TransferParam,
13
- BSCalculableFee,
14
- NftDataService,
15
- BSWithNft,
16
- AccountWithDerivationPath,
17
- BSWithExplorerService,
18
- ExplorerService,
19
- } from '@cityofzion/blockchain-service'
20
- import { api, u, wallet } from '@cityofzion/neon-js'
21
- import Neon from '@cityofzion/neon-core'
22
- import { NeonInvoker } from '@cityofzion/neon-invoker'
23
- import { NeonParser } from '@cityofzion/neon-parser'
24
- import { ABI_TYPES } from '@cityofzion/neo3-parser'
25
- import { ContractInvocation } from '@cityofzion/neo3-invoker'
26
-
27
- import { RPCBDSNeo3 } from './RpcBDSNeo3'
28
- import { DoraBDSNeo3 } from './DoraBDSNeo3'
29
- import { DEFAULT_URL_BY_NETWORK_TYPE, DERIVATION_PATH, NEO_NS_HASH, TOKENS } from './constants'
30
- import { FlamingoEDSNeo3 } from './FlamingoEDSNeo3'
31
- import { GhostMarketNDSNeo3 } from './GhostMarketNDSNeo3'
32
- import { keychain } from '@cityofzion/bs-asteroid-sdk'
33
- import { DoraESNeo3 } from './DoraESNeo3'
34
-
35
- export class BSNeo3<BSCustomName extends string = string>
36
- implements BlockchainService, BSClaimable, BSWithNameService, BSCalculableFee, BSWithNft, BSWithExplorerService
37
- {
38
- readonly blockchainName: BSCustomName
39
- readonly feeToken: Token
40
- readonly claimToken: Token
41
- readonly burnToken: Token
42
- readonly derivationPath: string
43
-
44
- blockchainDataService!: BlockchainDataService & BDSClaimable
45
- nftDataService!: NftDataService
46
- exchangeDataService!: ExchangeDataService
47
- explorerService!: ExplorerService
48
- tokens: Token[]
49
- network!: Network
50
-
51
- constructor(blockchainName: BSCustomName, network: PartialBy<Network, 'url'>) {
52
- this.blockchainName = blockchainName
53
- this.tokens = TOKENS[network.type]
54
-
55
- this.derivationPath = DERIVATION_PATH
56
- this.feeToken = this.tokens.find(token => token.symbol === 'GAS')!
57
- this.burnToken = this.tokens.find(token => token.symbol === 'NEO')!
58
- this.claimToken = this.tokens.find(token => token.symbol === 'GAS')!
59
- this.setNetwork(network)
60
- }
61
-
62
- setNetwork(param: PartialBy<Network, 'url'>) {
63
- const network = {
64
- type: param.type,
65
- url: param.url ?? DEFAULT_URL_BY_NETWORK_TYPE[param.type],
66
- }
67
- this.network = network
68
-
69
- if (network.type === 'custom') {
70
- this.blockchainDataService = new RPCBDSNeo3(network, this.feeToken, this.claimToken)
71
- } else {
72
- this.blockchainDataService = new DoraBDSNeo3(network, this.feeToken, this.claimToken)
73
- }
74
-
75
- this.exchangeDataService = new FlamingoEDSNeo3(network.type)
76
- this.nftDataService = new GhostMarketNDSNeo3(network.type)
77
- this.explorerService = new DoraESNeo3(network.type)
78
- }
79
-
80
- validateAddress(address: string): boolean {
81
- return wallet.isAddress(address, 53)
82
- }
83
-
84
- validateEncrypted(encryptedKey: string): boolean {
85
- return wallet.isNEP2(encryptedKey)
86
- }
87
-
88
- validateKey(key: string): boolean {
89
- return wallet.isWIF(key) || wallet.isPrivateKey(key)
90
- }
91
-
92
- validateNameServiceDomainFormat(domainName: string): boolean {
93
- if (!domainName.endsWith('.neo')) return false
94
- return true
95
- }
96
-
97
- generateAccountFromMnemonic(mnemonic: string[] | string, index: number): AccountWithDerivationPath {
98
- keychain.importMnemonic(Array.isArray(mnemonic) ? mnemonic.join(' ') : mnemonic)
99
- const path = this.derivationPath.replace('?', index.toString())
100
- const childKey = keychain.generateChildKey('neo', path)
101
- const key = childKey.getWIF()
102
- const { address } = new wallet.Account(key)
103
- return { address, key, type: 'wif', derivationPath: path }
104
- }
105
-
106
- generateAccountFromKey(key: string): Account {
107
- const type = wallet.isWIF(key) ? 'wif' : wallet.isPrivateKey(key) ? 'privateKey' : undefined
108
- if (!type) throw new Error('Invalid key')
109
-
110
- const { address } = new wallet.Account(key)
111
- return { address, key, type }
112
- }
113
-
114
- async decrypt(encryptedKey: string, password: string): Promise<Account> {
115
- let BsReactNativeDecrypt: any
116
-
117
- try {
118
- const { NativeModules } = require('react-native')
119
- BsReactNativeDecrypt = NativeModules.BsReactNativeDecrypt
120
- } catch {
121
- const key = await wallet.decrypt(encryptedKey, password)
122
- return this.generateAccountFromKey(key)
123
- }
124
-
125
- if (!BsReactNativeDecrypt) {
126
- throw new Error('@CityOfZion/bs-react-native-decrypt is not installed')
127
- }
128
-
129
- const privateKey = await BsReactNativeDecrypt.decryptNeo3(encryptedKey, password)
130
- return this.generateAccountFromKey(privateKey)
131
- }
132
-
133
- async calculateTransferFee(param: TransferParam): Promise<string> {
134
- const account = new wallet.Account(param.senderAccount.key)
135
- const invoker = await NeonInvoker.init({
136
- rpcAddress: this.network.url,
137
- account,
138
- })
139
-
140
- const invocations = this.buildTransferInvocation(param, account)
141
-
142
- const { networkFee, systemFee } = await invoker.calculateFee({
143
- invocations,
144
- signers: [],
145
- })
146
-
147
- return networkFee.add(systemFee).toDecimal(this.feeToken.decimals)
148
- }
149
-
150
- async transfer(param: TransferParam): Promise<string> {
151
- const account = new wallet.Account(param.senderAccount.key)
152
- const invoker = await NeonInvoker.init({
153
- rpcAddress: this.network.url,
154
- account,
155
- })
156
-
157
- const invocations = this.buildTransferInvocation(param, account)
158
-
159
- const transactionHash = await invoker.invokeFunction({
160
- invocations,
161
- signers: [],
162
- })
163
-
164
- return transactionHash
165
- }
166
-
167
- async claim(account: Account): Promise<string> {
168
- const neoAccount = new wallet.Account(account.key)
169
- const facade = await api.NetworkFacade.fromConfig({ node: this.network.url })
170
-
171
- const transactionHash = await facade.claimGas(neoAccount, {
172
- signingCallback: api.signWithAccount(neoAccount),
173
- })
174
-
175
- return transactionHash
176
- }
177
-
178
- async resolveNameServiceDomain(domainName: string): Promise<any> {
179
- const parser = NeonParser
180
- const invoker = await NeonInvoker.init({ rpcAddress: this.network.url })
181
- const response = await invoker.testInvoke({
182
- invocations: [
183
- {
184
- scriptHash: NEO_NS_HASH,
185
- operation: 'ownerOf',
186
- args: [{ type: 'String', value: domainName }],
187
- },
188
- ],
189
- })
190
-
191
- if (response.stack.length === 0) {
192
- throw new Error(response.exception ?? 'unrecognized response')
193
- }
194
-
195
- const parsed = parser.parseRpcResponse(response.stack[0] as any, {
196
- type: ABI_TYPES.HASH160.name,
197
- })
198
- const address = parser.accountInputToAddress(parsed.replace('0x', ''))
199
- return address
200
- }
201
-
202
- private buildTransferInvocation(
203
- { intent, tipIntent }: TransferParam,
204
- account: Neon.wallet.Account
205
- ): ContractInvocation[] {
206
- const intents = [intent, ...(tipIntent ? [tipIntent] : [])]
207
-
208
- const invocations: ContractInvocation[] = intents.map(intent => {
209
- return {
210
- operation: 'transfer',
211
- scriptHash: intent.tokenHash,
212
- args: [
213
- { type: 'Hash160', value: account.address },
214
- { type: 'Hash160', value: intent.receiverAddress },
215
- {
216
- type: 'Integer',
217
- value: intent.tokenDecimals
218
- ? u.BigInteger.fromDecimal(intent.amount, intent.tokenDecimals).toString()
219
- : intent.amount,
220
- },
221
- { type: 'Any', value: '' },
222
- ],
223
- }
224
- })
225
-
226
- return invocations
227
- }
228
- }
1
+ import {
2
+ BlockchainDataService,
3
+ BlockchainService,
4
+ BSClaimable,
5
+ Account,
6
+ ExchangeDataService,
7
+ BDSClaimable,
8
+ Token,
9
+ BSWithNameService,
10
+ Network,
11
+ PartialBy,
12
+ TransferParam,
13
+ BSCalculableFee,
14
+ NftDataService,
15
+ BSWithNft,
16
+ AccountWithDerivationPath,
17
+ BSWithExplorerService,
18
+ ExplorerService,
19
+ } from '@cityofzion/blockchain-service'
20
+ import { api, u, wallet } from '@cityofzion/neon-js'
21
+ import Neon from '@cityofzion/neon-core'
22
+ import { NeonInvoker } from '@cityofzion/neon-invoker'
23
+ import { NeonParser } from '@cityofzion/neon-parser'
24
+ import { ABI_TYPES } from '@cityofzion/neo3-parser'
25
+ import { ContractInvocation } from '@cityofzion/neo3-invoker'
26
+
27
+ import { RPCBDSNeo3 } from './RpcBDSNeo3'
28
+ import { DoraBDSNeo3 } from './DoraBDSNeo3'
29
+ import { DEFAULT_URL_BY_NETWORK_TYPE, DERIVATION_PATH, NEO_NS_HASH, TOKENS } from './constants'
30
+ import { FlamingoEDSNeo3 } from './FlamingoEDSNeo3'
31
+ import { GhostMarketNDSNeo3 } from './GhostMarketNDSNeo3'
32
+ import { keychain } from '@cityofzion/bs-asteroid-sdk'
33
+ import { DoraESNeo3 } from './DoraESNeo3'
34
+
35
+ export class BSNeo3<BSCustomName extends string = string>
36
+ implements BlockchainService, BSClaimable, BSWithNameService, BSCalculableFee, BSWithNft, BSWithExplorerService
37
+ {
38
+ readonly blockchainName: BSCustomName
39
+ readonly feeToken: Token
40
+ readonly claimToken: Token
41
+ readonly burnToken: Token
42
+ readonly derivationPath: string
43
+
44
+ blockchainDataService!: BlockchainDataService & BDSClaimable
45
+ nftDataService!: NftDataService
46
+ exchangeDataService!: ExchangeDataService
47
+ explorerService!: ExplorerService
48
+ tokens: Token[]
49
+ network!: Network
50
+
51
+ constructor(blockchainName: BSCustomName, network: PartialBy<Network, 'url'>) {
52
+ this.blockchainName = blockchainName
53
+ this.tokens = TOKENS[network.type]
54
+
55
+ this.derivationPath = DERIVATION_PATH
56
+ this.feeToken = this.tokens.find(token => token.symbol === 'GAS')!
57
+ this.burnToken = this.tokens.find(token => token.symbol === 'NEO')!
58
+ this.claimToken = this.tokens.find(token => token.symbol === 'GAS')!
59
+ this.setNetwork(network)
60
+ }
61
+
62
+ setNetwork(param: PartialBy<Network, 'url'>) {
63
+ const network = {
64
+ type: param.type,
65
+ url: param.url ?? DEFAULT_URL_BY_NETWORK_TYPE[param.type],
66
+ }
67
+ this.network = network
68
+
69
+ if (network.type === 'custom') {
70
+ this.blockchainDataService = new RPCBDSNeo3(network, this.feeToken, this.claimToken)
71
+ } else {
72
+ this.blockchainDataService = new DoraBDSNeo3(network, this.feeToken, this.claimToken)
73
+ }
74
+
75
+ this.exchangeDataService = new FlamingoEDSNeo3(network.type)
76
+ this.nftDataService = new GhostMarketNDSNeo3(network.type)
77
+ this.explorerService = new DoraESNeo3(network.type)
78
+ }
79
+
80
+ validateAddress(address: string): boolean {
81
+ return wallet.isAddress(address, 53)
82
+ }
83
+
84
+ validateEncrypted(encryptedKey: string): boolean {
85
+ return wallet.isNEP2(encryptedKey)
86
+ }
87
+
88
+ validateKey(key: string): boolean {
89
+ return wallet.isWIF(key) || wallet.isPrivateKey(key)
90
+ }
91
+
92
+ validateNameServiceDomainFormat(domainName: string): boolean {
93
+ if (!domainName.endsWith('.neo')) return false
94
+ return true
95
+ }
96
+
97
+ generateAccountFromMnemonic(mnemonic: string[] | string, index: number): AccountWithDerivationPath {
98
+ keychain.importMnemonic(Array.isArray(mnemonic) ? mnemonic.join(' ') : mnemonic)
99
+ const path = this.derivationPath.replace('?', index.toString())
100
+ const childKey = keychain.generateChildKey('neo', path)
101
+ const key = childKey.getWIF()
102
+ const { address } = new wallet.Account(key)
103
+ return { address, key, type: 'wif', derivationPath: path }
104
+ }
105
+
106
+ generateAccountFromKey(key: string): Account {
107
+ const type = wallet.isWIF(key) ? 'wif' : wallet.isPrivateKey(key) ? 'privateKey' : undefined
108
+ if (!type) throw new Error('Invalid key')
109
+
110
+ const { address } = new wallet.Account(key)
111
+ return { address, key, type }
112
+ }
113
+
114
+ async decrypt(encryptedKey: string, password: string): Promise<Account> {
115
+ let BsReactNativeDecrypt: any
116
+
117
+ try {
118
+ const { NativeModules } = require('react-native')
119
+ BsReactNativeDecrypt = NativeModules.BsReactNativeDecrypt
120
+ } catch {
121
+ const key = await wallet.decrypt(encryptedKey, password)
122
+ return this.generateAccountFromKey(key)
123
+ }
124
+
125
+ if (!BsReactNativeDecrypt) {
126
+ throw new Error('@CityOfZion/bs-react-native-decrypt is not installed')
127
+ }
128
+
129
+ const privateKey = await BsReactNativeDecrypt.decryptNeo3(encryptedKey, password)
130
+ return this.generateAccountFromKey(privateKey)
131
+ }
132
+
133
+ async calculateTransferFee(param: TransferParam): Promise<string> {
134
+ const account = new wallet.Account(param.senderAccount.key)
135
+ const invoker = await NeonInvoker.init({
136
+ rpcAddress: this.network.url,
137
+ account,
138
+ })
139
+
140
+ const invocations = this.buildTransferInvocation(param, account)
141
+
142
+ const { networkFee, systemFee } = await invoker.calculateFee({
143
+ invocations,
144
+ signers: [],
145
+ })
146
+
147
+ return networkFee.add(systemFee).toDecimal(this.feeToken.decimals)
148
+ }
149
+
150
+ async transfer(param: TransferParam): Promise<string> {
151
+ const account = new wallet.Account(param.senderAccount.key)
152
+ const invoker = await NeonInvoker.init({
153
+ rpcAddress: this.network.url,
154
+ account,
155
+ })
156
+
157
+ const invocations = this.buildTransferInvocation(param, account)
158
+
159
+ const transactionHash = await invoker.invokeFunction({
160
+ invocations,
161
+ signers: [],
162
+ })
163
+
164
+ return transactionHash
165
+ }
166
+
167
+ async claim(account: Account): Promise<string> {
168
+ const neoAccount = new wallet.Account(account.key)
169
+ const facade = await api.NetworkFacade.fromConfig({ node: this.network.url })
170
+
171
+ const transactionHash = await facade.claimGas(neoAccount, {
172
+ signingCallback: api.signWithAccount(neoAccount),
173
+ })
174
+
175
+ return transactionHash
176
+ }
177
+
178
+ async resolveNameServiceDomain(domainName: string): Promise<any> {
179
+ const parser = NeonParser
180
+ const invoker = await NeonInvoker.init({ rpcAddress: this.network.url })
181
+ const response = await invoker.testInvoke({
182
+ invocations: [
183
+ {
184
+ scriptHash: NEO_NS_HASH,
185
+ operation: 'ownerOf',
186
+ args: [{ type: 'String', value: domainName }],
187
+ },
188
+ ],
189
+ })
190
+
191
+ if (response.stack.length === 0) {
192
+ throw new Error(response.exception ?? 'unrecognized response')
193
+ }
194
+
195
+ const parsed = parser.parseRpcResponse(response.stack[0] as any, {
196
+ type: ABI_TYPES.HASH160.name,
197
+ })
198
+ const address = parser.accountInputToAddress(parsed.replace('0x', ''))
199
+ return address
200
+ }
201
+
202
+ private buildTransferInvocation(
203
+ { intent, tipIntent }: TransferParam,
204
+ account: Neon.wallet.Account
205
+ ): ContractInvocation[] {
206
+ const intents = [intent, ...(tipIntent ? [tipIntent] : [])]
207
+
208
+ const invocations: ContractInvocation[] = intents.map(intent => {
209
+ return {
210
+ operation: 'transfer',
211
+ scriptHash: intent.tokenHash,
212
+ args: [
213
+ { type: 'Hash160', value: account.address },
214
+ { type: 'Hash160', value: intent.receiverAddress },
215
+ {
216
+ type: 'Integer',
217
+ value: intent.tokenDecimals
218
+ ? u.BigInteger.fromDecimal(intent.amount, intent.tokenDecimals).toString()
219
+ : intent.amount,
220
+ },
221
+ { type: 'Any', value: '' },
222
+ ],
223
+ }
224
+ })
225
+
226
+ return invocations
227
+ }
228
+ }