@5ive-tech/sdk 1.1.2

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 (119) hide show
  1. package/README.md +279 -0
  2. package/dist/FiveSDK.d.ts +336 -0
  3. package/dist/FiveSDK.js +395 -0
  4. package/dist/accounts/index.d.ts +254 -0
  5. package/dist/accounts/index.js +543 -0
  6. package/dist/assets/vm/dummy.file +0 -0
  7. package/dist/assets/vm/five_vm_wasm.d.ts +762 -0
  8. package/dist/assets/vm/five_vm_wasm.js +3754 -0
  9. package/dist/assets/vm/five_vm_wasm_bg.js +3307 -0
  10. package/dist/assets/vm/five_vm_wasm_bg.wasm +0 -0
  11. package/dist/assets/vm/five_vm_wasm_bg.wasm.d.ts +247 -0
  12. package/dist/assets/vm/package.json +11 -0
  13. package/dist/bin/gen-types.d.ts +2 -0
  14. package/dist/bin/gen-types.js +35 -0
  15. package/dist/compiler/BytecodeCompiler.d.ts +83 -0
  16. package/dist/compiler/BytecodeCompiler.js +379 -0
  17. package/dist/config/ConfigManager.d.ts +13 -0
  18. package/dist/config/ConfigManager.js +27 -0
  19. package/dist/config/ProgramIdResolver.d.ts +62 -0
  20. package/dist/config/ProgramIdResolver.js +104 -0
  21. package/dist/crypto/index.d.ts +211 -0
  22. package/dist/crypto/index.js +451 -0
  23. package/dist/encoding/ParameterEncoder.d.ts +31 -0
  24. package/dist/encoding/ParameterEncoder.js +278 -0
  25. package/dist/index.d.ts +21 -0
  26. package/dist/index.js +28 -0
  27. package/dist/lib/bytecode-encoder.d.ts +62 -0
  28. package/dist/lib/bytecode-encoder.js +281 -0
  29. package/dist/logging/index.d.ts +9 -0
  30. package/dist/logging/index.js +10 -0
  31. package/dist/metadata/index.d.ts +213 -0
  32. package/dist/metadata/index.js +296 -0
  33. package/dist/modules/accounts.d.ts +60 -0
  34. package/dist/modules/accounts.js +275 -0
  35. package/dist/modules/deploy.d.ts +90 -0
  36. package/dist/modules/deploy.js +1118 -0
  37. package/dist/modules/execute.d.ts +90 -0
  38. package/dist/modules/execute.js +649 -0
  39. package/dist/modules/fees.d.ts +14 -0
  40. package/dist/modules/fees.js +112 -0
  41. package/dist/modules/namespaces.d.ts +39 -0
  42. package/dist/modules/namespaces.js +190 -0
  43. package/dist/modules/state-diff.d.ts +35 -0
  44. package/dist/modules/state-diff.js +342 -0
  45. package/dist/modules/vm-state.d.ts +7 -0
  46. package/dist/modules/vm-state.js +44 -0
  47. package/dist/program/AccountResolver.d.ts +67 -0
  48. package/dist/program/AccountResolver.js +134 -0
  49. package/dist/program/BorshSchemaGenerator.d.ts +8 -0
  50. package/dist/program/BorshSchemaGenerator.js +57 -0
  51. package/dist/program/FiveProgram.d.ts +144 -0
  52. package/dist/program/FiveProgram.js +282 -0
  53. package/dist/program/FunctionBuilder.d.ts +114 -0
  54. package/dist/program/FunctionBuilder.js +347 -0
  55. package/dist/program/ProgramAccount.d.ts +38 -0
  56. package/dist/program/ProgramAccount.js +170 -0
  57. package/dist/program/TypeGenerator.d.ts +90 -0
  58. package/dist/program/TypeGenerator.js +195 -0
  59. package/dist/program/index.d.ts +24 -0
  60. package/dist/program/index.js +21 -0
  61. package/dist/project/config.d.ts +5 -0
  62. package/dist/project/config.js +33 -0
  63. package/dist/project/toml.d.ts +6 -0
  64. package/dist/project/toml.js +43 -0
  65. package/dist/project/workspace.d.ts +160 -0
  66. package/dist/project/workspace.js +73 -0
  67. package/dist/testing/AccountMetaGenerator.d.ts +121 -0
  68. package/dist/testing/AccountMetaGenerator.js +261 -0
  69. package/dist/testing/AccountTestFixture.d.ts +211 -0
  70. package/dist/testing/AccountTestFixture.js +530 -0
  71. package/dist/testing/OnChainAccountManager.d.ts +81 -0
  72. package/dist/testing/OnChainAccountManager.js +260 -0
  73. package/dist/testing/StateSerializer.d.ts +65 -0
  74. package/dist/testing/StateSerializer.js +330 -0
  75. package/dist/testing/TestDiscovery.d.ts +79 -0
  76. package/dist/testing/TestDiscovery.js +274 -0
  77. package/dist/testing/TestRunner.d.ts +117 -0
  78. package/dist/testing/TestRunner.js +346 -0
  79. package/dist/testing/index.d.ts +14 -0
  80. package/dist/testing/index.js +13 -0
  81. package/dist/types.d.ts +356 -0
  82. package/dist/types.js +32 -0
  83. package/dist/utils/abi.d.ts +31 -0
  84. package/dist/utils/abi.js +92 -0
  85. package/dist/utils/transaction.d.ts +5 -0
  86. package/dist/utils/transaction.js +48 -0
  87. package/dist/validation/InputValidator.d.ts +142 -0
  88. package/dist/validation/InputValidator.js +332 -0
  89. package/dist/validation/index.d.ts +4 -0
  90. package/dist/validation/index.js +4 -0
  91. package/dist/wasm/compiler/AbiLogic.d.ts +4 -0
  92. package/dist/wasm/compiler/AbiLogic.js +37 -0
  93. package/dist/wasm/compiler/AnalysisLogic.d.ts +6 -0
  94. package/dist/wasm/compiler/AnalysisLogic.js +61 -0
  95. package/dist/wasm/compiler/CompilationLogic.d.ts +10 -0
  96. package/dist/wasm/compiler/CompilationLogic.js +431 -0
  97. package/dist/wasm/compiler/FiveCompiler.d.ts +48 -0
  98. package/dist/wasm/compiler/FiveCompiler.js +183 -0
  99. package/dist/wasm/compiler/InfoLogic.d.ts +6 -0
  100. package/dist/wasm/compiler/InfoLogic.js +24 -0
  101. package/dist/wasm/compiler/OptimizationLogic.d.ts +2 -0
  102. package/dist/wasm/compiler/OptimizationLogic.js +13 -0
  103. package/dist/wasm/compiler/ValidationLogic.d.ts +7 -0
  104. package/dist/wasm/compiler/ValidationLogic.js +26 -0
  105. package/dist/wasm/compiler/index.d.ts +2 -0
  106. package/dist/wasm/compiler/index.js +2 -0
  107. package/dist/wasm/compiler/types.d.ts +8 -0
  108. package/dist/wasm/compiler/types.js +1 -0
  109. package/dist/wasm/compiler/utils.d.ts +8 -0
  110. package/dist/wasm/compiler/utils.js +75 -0
  111. package/dist/wasm/index.d.ts +9 -0
  112. package/dist/wasm/index.js +12 -0
  113. package/dist/wasm/instance.d.ts +1 -0
  114. package/dist/wasm/instance.js +26 -0
  115. package/dist/wasm/loader.d.ts +7 -0
  116. package/dist/wasm/loader.js +112 -0
  117. package/dist/wasm/vm.d.ts +33 -0
  118. package/dist/wasm/vm.js +250 -0
  119. package/package.json +59 -0
@@ -0,0 +1,347 @@
1
+ /**
2
+ * FunctionBuilder - Fluent API for building Five VM function calls
3
+ *
4
+ * Provides chainable methods for specifying function parameters:
5
+ * - .accounts() - Map account names to their addresses
6
+ * - .args() - Map data parameters to their values
7
+ * - .instruction() - Generate serialized instruction data
8
+ *
9
+ * Usage:
10
+ * ```typescript
11
+ * const ix = await program
12
+ * .function('add_amount')
13
+ * .accounts({ counter: counter1, owner: user1.publicKey })
14
+ * .args({ amount: 10 })
15
+ * .instruction();
16
+ * ```
17
+ */
18
+ import { AccountResolver } from './AccountResolver.js';
19
+ /**
20
+ * FunctionBuilder implements fluent API for function calls
21
+ */
22
+ export class FunctionBuilder {
23
+ constructor(functionDef, scriptAccount, abi, options) {
24
+ this.accountsMap = new Map();
25
+ this.argsMap = new Map();
26
+ this.resolvedAccounts = new Set();
27
+ this.functionDef = functionDef;
28
+ this.scriptAccount = scriptAccount;
29
+ this.abi = abi;
30
+ this.options = options;
31
+ }
32
+ /**
33
+ * Specify accounts for this function call
34
+ * Accepts either base58 strings or PublicKey objects
35
+ *
36
+ * @param accounts - Map of account names to addresses
37
+ * @returns this for method chaining
38
+ */
39
+ accounts(accounts) {
40
+ for (const [name, address] of Object.entries(accounts)) {
41
+ const addressStr = typeof address === 'string' ? address : address.toBase58();
42
+ this.accountsMap.set(name, addressStr);
43
+ this.resolvedAccounts.add(addressStr);
44
+ }
45
+ if (this.options.debug) {
46
+ console.log(`[FunctionBuilder] Set accounts: ${Array.from(this.accountsMap.keys()).join(', ')}`);
47
+ }
48
+ return this;
49
+ }
50
+ /**
51
+ * Specify data parameters for this function call
52
+ *
53
+ * @param args - Map of parameter names to values
54
+ * @returns this for method chaining
55
+ */
56
+ args(args) {
57
+ for (const [name, value] of Object.entries(args)) {
58
+ this.argsMap.set(name, value);
59
+ }
60
+ if (this.options.debug) {
61
+ console.log(`[FunctionBuilder] Set args: ${Array.from(this.argsMap.keys()).join(', ')}`);
62
+ }
63
+ return this;
64
+ }
65
+ /**
66
+ * Build and return serialized instruction data
67
+ * This is the main method that orchestrates parameter resolution and instruction generation
68
+ *
69
+ * @returns SerializedInstruction ready for transaction building
70
+ */
71
+ async instruction() {
72
+ // Validate parameters later, after auto-injection
73
+ // Resolve system accounts (auto-inject when needed)
74
+ const resolver = new AccountResolver(this.options);
75
+ const resolvedSystemAccounts = resolver.resolveSystemAccounts(this.functionDef, this.accountsMap);
76
+ // Merge system accounts into our map
77
+ const systemAccountsList = [];
78
+ for (const [name, address] of Object.entries(resolvedSystemAccounts)) {
79
+ this.accountsMap.set(name, address);
80
+ this.resolvedAccounts.add(address);
81
+ systemAccountsList.push(address); // Track for inclusion in accounts list
82
+ }
83
+ // Resolve PDA accounts (auto-derive based on seeds) using a temporary FiveProgram for findAddress.
84
+ const { FiveProgram } = await import('./FiveProgram.js');
85
+ const programForUtil = new FiveProgram(this.scriptAccount, this.abi, this.options);
86
+ const resolvedPdaAccounts = await resolver.resolvePdaAccounts(this.abi, this.accountsMap, programForUtil);
87
+ // Merge PDA accounts
88
+ for (const [name, address] of Object.entries(resolvedPdaAccounts)) {
89
+ this.accountsMap.set(name, address);
90
+ this.resolvedAccounts.add(address);
91
+ systemAccountsList.push(address);
92
+ }
93
+ // Now validate that all required parameters are provided (including auto-injected ones)
94
+ this.validateParameters();
95
+ // Merge parameters in ABI order (accounts first, then data)
96
+ const { mergedParams, accountPubkeys } = this.mergeParameters();
97
+ // Append system accounts to the account list (they go at the end)
98
+ const allAccountPubkeys = [...accountPubkeys, ...systemAccountsList];
99
+ // Build account metadata from ABI attributes
100
+ const accountMetadata = this.buildAccountMetadata(allAccountPubkeys, resolvedSystemAccounts);
101
+ if (this.options.debug) {
102
+ console.log(`[FunctionBuilder] Building instruction for function '${this.functionDef.name}'`);
103
+ console.log(`[FunctionBuilder] Merged params:`, mergedParams);
104
+ console.log(`[FunctionBuilder] Account metadata:`, accountMetadata);
105
+ }
106
+ // Call existing SDK method to generate instruction
107
+ // This reuses the proven parameter encoding logic
108
+ const instruction = await this.generateInstructionData(mergedParams, allAccountPubkeys, accountMetadata);
109
+ if (this.options.debug) {
110
+ console.log(`[FunctionBuilder] Generated instruction:`, instruction);
111
+ }
112
+ return instruction;
113
+ }
114
+ /**
115
+ * Build a Solana Transaction containing this instruction
116
+ */
117
+ async transaction(options = {}) {
118
+ const ix = await this.instruction();
119
+ // Import Solana web3 dynamically
120
+ const { Transaction, TransactionInstruction, ComputeBudgetProgram, PublicKey } = await import('@solana/web3.js');
121
+ const tx = new Transaction();
122
+ // add compute budget if requested
123
+ if (options.computeUnits) {
124
+ tx.add(ComputeBudgetProgram.setComputeUnitLimit({ units: options.computeUnits }));
125
+ }
126
+ // construct instruction
127
+ const solanaIx = new TransactionInstruction({
128
+ programId: new PublicKey(ix.programId),
129
+ keys: ix.keys.map(key => ({
130
+ pubkey: new PublicKey(key.pubkey),
131
+ isSigner: key.isSigner,
132
+ isWritable: key.isWritable
133
+ })),
134
+ data: Buffer.from(ix.data, 'base64')
135
+ });
136
+ tx.add(solanaIx);
137
+ return tx;
138
+ }
139
+ /**
140
+ * Send transaction with this instruction (RPC)
141
+ * requires a provider to be set in options
142
+ */
143
+ async rpc(options = {}) {
144
+ const provider = this.options.provider;
145
+ if (!provider || !provider.sendAndConfirm) {
146
+ throw new Error("RPC method requires a Provider with sendAndConfirm support");
147
+ }
148
+ const tx = await this.transaction({ computeUnits: options.computeUnits });
149
+ // Send
150
+ const signers = options.signers || [];
151
+ return await provider.sendAndConfirm(tx, signers, {
152
+ skipPreflight: options.skipPreflight
153
+ });
154
+ }
155
+ /**
156
+ * Validate that all required parameters are provided
157
+ * @throws Error if any required parameter is missing
158
+ */
159
+ validateParameters() {
160
+ for (const param of this.functionDef.parameters) {
161
+ if (param.is_account) {
162
+ if (!this.accountsMap.has(param.name)) {
163
+ throw new Error(`Missing required account '${param.name}' for function '${this.functionDef.name}'`);
164
+ }
165
+ }
166
+ else {
167
+ if (this.argsMap.get(param.name) === undefined) {
168
+ throw new Error(`Missing required argument '${param.name}' for function '${this.functionDef.name}'`);
169
+ }
170
+ }
171
+ }
172
+ }
173
+ /**
174
+ * Merge account and data parameters in ABI order
175
+ * Returns both merged array and list of account pubkeys for instruction building
176
+ *
177
+ * @returns Object with mergedParams array and accountPubkeys list
178
+ */
179
+ mergeParameters() {
180
+ const mergedParams = [];
181
+ const accountPubkeys = [];
182
+ for (const param of this.functionDef.parameters) {
183
+ if (param.is_account) {
184
+ // Account parameter - must be in accountsMap
185
+ const pubkey = this.accountsMap.get(param.name);
186
+ if (!pubkey) {
187
+ throw new Error(`Missing account '${param.name}'`);
188
+ }
189
+ mergedParams.push(pubkey);
190
+ accountPubkeys.push(pubkey);
191
+ }
192
+ else {
193
+ // Data parameter - must be in argsMap
194
+ const value = this.argsMap.get(param.name);
195
+ if (value === undefined) {
196
+ throw new Error(`Missing argument '${param.name}'`);
197
+ }
198
+ mergedParams.push(value);
199
+ }
200
+ }
201
+ return { mergedParams, accountPubkeys };
202
+ }
203
+ /**
204
+ * Build account metadata (isSigner, isWritable) from ABI attributes
205
+ *
206
+ * @param accountPubkeys - List of account pubkeys in order
207
+ * @param systemAccounts - System accounts that were auto-injected
208
+ * @returns Map of pubkey to metadata
209
+ */
210
+ buildAccountMetadata(accountPubkeys, systemAccounts) {
211
+ const metadata = new Map();
212
+ // First pass: identify if there's an @init constraint and find the payer
213
+ let hasInit = false;
214
+ let payerPubkey;
215
+ for (const param of this.functionDef.parameters) {
216
+ if (param.is_account) {
217
+ const attributes = param.attributes || [];
218
+ if (attributes.includes('init')) {
219
+ hasInit = true;
220
+ if (this.options.debug) {
221
+ console.log(`[FunctionBuilder] Detected @init constraint on parameter: ${param.name}`);
222
+ }
223
+ // Find the payer - typically the @signer in an @init context
224
+ // Check if there's another account marked @signer for the payer
225
+ for (const payerParam of this.functionDef.parameters) {
226
+ if (payerParam.is_account &&
227
+ payerParam !== param &&
228
+ (payerParam.attributes || []).includes('signer')) {
229
+ payerPubkey = this.accountsMap.get(payerParam.name);
230
+ if (this.options.debug) {
231
+ console.log(`[FunctionBuilder] Found payer for @init: ${payerParam.name} = ${payerPubkey}`);
232
+ }
233
+ break;
234
+ }
235
+ }
236
+ break;
237
+ }
238
+ }
239
+ }
240
+ // Build metadata for function parameters
241
+ for (const param of this.functionDef.parameters) {
242
+ if (param.is_account) {
243
+ const pubkey = this.accountsMap.get(param.name);
244
+ if (!pubkey)
245
+ continue;
246
+ const attributes = param.attributes || [];
247
+ const isWritable = attributes.includes('mut') ||
248
+ attributes.includes('init') ||
249
+ // If this is the payer for @init, it must be writable
250
+ (hasInit && pubkey === payerPubkey);
251
+ const entry = {
252
+ isSigner: attributes.includes('signer'),
253
+ isWritable,
254
+ };
255
+ if (this.options.debug) {
256
+ console.log(`[FunctionBuilder] Account metadata for ${param.name}: pubkey=${pubkey}, isSigner=${entry.isSigner}, isWritable=${entry.isWritable}, isPayer=${hasInit && pubkey === payerPubkey}`);
257
+ }
258
+ metadata.set(pubkey, entry);
259
+ }
260
+ }
261
+ // Mark system accounts
262
+ for (const [name, address] of Object.entries(systemAccounts)) {
263
+ if (!metadata.has(address)) {
264
+ metadata.set(address, {
265
+ isSigner: false,
266
+ isWritable: false,
267
+ isSystemAccount: true,
268
+ });
269
+ }
270
+ }
271
+ return metadata;
272
+ }
273
+ /**
274
+ * Generate instruction data using serialization
275
+ * Integrates with FiveSDK.generateExecuteInstruction() for parameter encoding
276
+ *
277
+ * @param mergedParams - Parameters in ABI order
278
+ * @param accountList - List of all account pubkeys (function params + system accounts)
279
+ * @param accountMetadata - Account metadata (isSigner, isWritable)
280
+ * @returns SerializedInstruction
281
+ */
282
+ async generateInstructionData(mergedParams, accountList, accountMetadata) {
283
+ // Account list is already passed in
284
+ // Dynamically import FiveSDK to avoid circular dependencies
285
+ const { FiveSDK } = await import('../FiveSDK.js');
286
+ // Call the SDK's generateExecuteInstruction method.
287
+ // This handles fixed-size typed execute encoding and parameter validation.
288
+ const executionResult = await FiveSDK.generateExecuteInstruction(this.scriptAccount, this.functionDef.index, // Use function index directly
289
+ mergedParams, // All parameters in merged order
290
+ accountList, // Account pubkey list
291
+ undefined, // No connection needed - we have ABI
292
+ {
293
+ debug: this.options.debug,
294
+ abi: this.abi, // Pass ABI for parameter encoding
295
+ fiveVMProgramId: this.options.fiveVMProgramId,
296
+ vmStateAccount: this.options.vmStateAccount,
297
+ adminAccount: this.options.feeReceiverAccount,
298
+ accountMetadata: accountMetadata, // Pass account metadata for correct isWritable flags
299
+ });
300
+ // Map SDK's instruction format (with 'accounts') to SerializedInstruction format (with 'keys')
301
+ const sdkInstruction = executionResult.instruction;
302
+ // Convert accounts array to keys array with proper naming
303
+ const keys = (sdkInstruction.accounts || []).map((acc) => {
304
+ // Handle both string and PublicKey-like objects
305
+ let pubkeyStr;
306
+ if (typeof acc.pubkey === 'string') {
307
+ pubkeyStr = acc.pubkey;
308
+ }
309
+ else if (acc.pubkey && typeof acc.pubkey.toBase58 === 'function') {
310
+ pubkeyStr = acc.pubkey.toBase58();
311
+ }
312
+ else {
313
+ pubkeyStr = String(acc.pubkey);
314
+ }
315
+ return {
316
+ pubkey: pubkeyStr,
317
+ isSigner: acc.isSigner,
318
+ isWritable: acc.isWritable,
319
+ };
320
+ });
321
+ // Return the properly formatted serialized instruction
322
+ const instruction = {
323
+ programId: sdkInstruction.programId,
324
+ keys,
325
+ data: sdkInstruction.data,
326
+ };
327
+ return instruction;
328
+ }
329
+ /**
330
+ * Get the function definition
331
+ */
332
+ getFunctionDef() {
333
+ return this.functionDef;
334
+ }
335
+ /**
336
+ * Get accounts that have been set
337
+ */
338
+ getAccounts() {
339
+ return Object.fromEntries(this.accountsMap);
340
+ }
341
+ /**
342
+ * Get arguments that have been set
343
+ */
344
+ getArgs() {
345
+ return Object.fromEntries(this.argsMap);
346
+ }
347
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * ProgramAccount - Typed state fetching for Five VM
3
+ *
4
+ * Provides typed access to account state based on ABI definitions.
5
+ *
6
+ * Usage:
7
+ * ```typescript
8
+ * const myAccount = program.account('MyStruct');
9
+ * const state = await myAccount.fetch(publicKey);
10
+ * console.log(state.someField);
11
+ * ```
12
+ */
13
+ import { AccountFetcher } from '../metadata/index.js';
14
+ import type { ScriptABI } from '../metadata/index.js';
15
+ export declare class ProgramAccount {
16
+ private structName;
17
+ private abi;
18
+ private fetcher?;
19
+ constructor(structName: string, abi: ScriptABI, fetcher?: AccountFetcher);
20
+ /**
21
+ * Fetch and decode account state
22
+ *
23
+ * @param address - Account address to fetch
24
+ * @returns Decoded account state object
25
+ */
26
+ fetch(address: string): Promise<any>;
27
+ /**
28
+ * Decode raw account data based on ABI
29
+ *
30
+ * @param data - Raw byte array
31
+ * @returns Decoded JavaScript object
32
+ */
33
+ decode(data: Uint8Array): any;
34
+ private readField;
35
+ private findStructDefinition;
36
+ private simpleDecode;
37
+ private readU32;
38
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * ProgramAccount - Typed state fetching for Five VM
3
+ *
4
+ * Provides typed access to account state based on ABI definitions.
5
+ *
6
+ * Usage:
7
+ * ```typescript
8
+ * const myAccount = program.account('MyStruct');
9
+ * const state = await myAccount.fetch(publicKey);
10
+ * console.log(state.someField);
11
+ * ```
12
+ */
13
+ import { BorshSchemaGenerator } from './BorshSchemaGenerator.js';
14
+ import * as borsh from 'borsh';
15
+ export class ProgramAccount {
16
+ constructor(structName, abi, fetcher) {
17
+ this.structName = structName;
18
+ this.abi = abi;
19
+ this.fetcher = fetcher;
20
+ }
21
+ /**
22
+ * Fetch and decode account state
23
+ *
24
+ * @param address - Account address to fetch
25
+ * @returns Decoded account state object
26
+ */
27
+ async fetch(address) {
28
+ if (!this.fetcher) {
29
+ throw new Error('Account fetcher not provided. Cannot fetch account data.');
30
+ }
31
+ const accountData = await this.fetcher.getAccountData(address);
32
+ if (!accountData) {
33
+ return null;
34
+ }
35
+ return this.decode(accountData.data);
36
+ }
37
+ /**
38
+ * Decode raw account data based on ABI
39
+ *
40
+ * @param data - Raw byte array
41
+ * @returns Decoded JavaScript object
42
+ */
43
+ decode(data) {
44
+ // 1. Find struct definition in ABI
45
+ const structDef = this.findStructDefinition(this.structName);
46
+ if (!structDef) {
47
+ // Fallback: if no types defined, return raw data
48
+ return { data };
49
+ }
50
+ // 2. Decode based on fields
51
+ // NOTE: This uses the robust BorshSchemaGenerator to map ABI types to runtime schema
52
+ try {
53
+ const generator = new BorshSchemaGenerator(this.abi);
54
+ // We need to construct a class that matches the schema for borsh to deserialize into
55
+ // The generator effectively returns a map where keys are these classes
56
+ const schema = generator.generate(structDef);
57
+ // Find the class constructor from the map keys
58
+ const SchemaClass = Array.from(schema.keys())[0];
59
+ if (!SchemaClass) {
60
+ throw new Error(`Failed to generate schema for ${this.structName}`);
61
+ }
62
+ // Fallback: Manual deserialization using BinaryReader
63
+ // This bypasses potential issues with borsh.deserialize returning empty objects for dynamic classes
64
+ try {
65
+ const reader = new borsh.BinaryReader(Buffer.from(data));
66
+ const result = new SchemaClass();
67
+ const structSchema = schema.get(SchemaClass);
68
+ if (structSchema && structSchema.kind === 'struct') {
69
+ for (const [fieldName, fieldType] of structSchema.fields) {
70
+ result[fieldName] = this.readField(reader, fieldType);
71
+ }
72
+ return result;
73
+ }
74
+ }
75
+ catch (manualErr) {
76
+ }
77
+ // Fallback to library if manual failed (though manual is preferred now)
78
+ return borsh.deserialize(schema, SchemaClass, Buffer.from(data));
79
+ }
80
+ catch (e) {
81
+ console.error(`Borsh decoding failed for ${this.structName}:`, e);
82
+ // Fallback to simple decode if borsh fails (e.g. mismatch)
83
+ return this.simpleDecode(data, structDef);
84
+ }
85
+ }
86
+ readField(reader, fieldType) {
87
+ if (typeof fieldType === 'string') {
88
+ switch (fieldType) {
89
+ case 'u8': return reader.readU8();
90
+ case 'u16': return reader.readU16();
91
+ case 'u32': return reader.readU32();
92
+ case 'u64': return reader.readU64();
93
+ case 'u128': return reader.readU128();
94
+ case 'i8': return reader.readU8();
95
+ case 'i16': return reader.readU16();
96
+ case 'i32': return reader.readU32();
97
+ case 'i64': return reader.readU64();
98
+ case 'i128': return reader.readU128();
99
+ case 'bool': return reader.readU8() !== 0;
100
+ case 'string': return reader.readString();
101
+ }
102
+ }
103
+ if (Array.isArray(fieldType)) {
104
+ // Fixed array [length] or [Type] (Vec)
105
+ if (typeof fieldType[0] === 'number') {
106
+ return reader.readFixedArray(fieldType[0]);
107
+ }
108
+ }
109
+ return null;
110
+ }
111
+ findStructDefinition(name) {
112
+ if (!this.abi.types)
113
+ return undefined;
114
+ return this.abi.types.find(t => t.name === name && t.structure === 'struct');
115
+ }
116
+ simpleDecode(data, structDef) {
117
+ const result = {};
118
+ let offset = 0;
119
+ // Basic discriminator check (8 bytes for Five accounts) could go here
120
+ // offset += 8;
121
+ if (!structDef.fields)
122
+ return result;
123
+ for (const field of structDef.fields) {
124
+ // Very basic decoding logic for primitive types
125
+ // A robust implementation would use a proper deserialization library
126
+ if (offset >= data.length)
127
+ break;
128
+ switch (field.type) {
129
+ case 'u8':
130
+ case 'bool':
131
+ result[field.name] = data[offset];
132
+ offset += 1;
133
+ break;
134
+ case 'u32':
135
+ case 'i32':
136
+ result[field.name] = this.readU32(data, offset);
137
+ offset += 4;
138
+ break;
139
+ case 'u64':
140
+ case 'i64':
141
+ // Read as Number for simplicity, BigInt for precision
142
+ const low = this.readU32(data, offset);
143
+ const high = this.readU32(data, offset + 4);
144
+ result[field.name] = low + (high * 0x100000000); // Danger: precision loss > 2^53
145
+ offset += 8;
146
+ break;
147
+ case 'Pubkey':
148
+ case 'address':
149
+ if (offset + 32 <= data.length) {
150
+ // We return base58 string in a real implementation
151
+ // Here we just slice because we don't have bs58 dep in this file
152
+ result[field.name] = data.slice(offset, offset + 32);
153
+ offset += 32;
154
+ }
155
+ break;
156
+ default:
157
+ // Skip unknown variable-length types to avoid reading garbage
158
+ // console.warn(`Skipping decoding of complex type ${field.type}`);
159
+ break;
160
+ }
161
+ }
162
+ return result;
163
+ }
164
+ readU32(data, offset) {
165
+ return (data[offset] |
166
+ (data[offset + 1] << 8) |
167
+ (data[offset + 2] << 16) |
168
+ (data[offset + 3] << 24)) >>> 0;
169
+ }
170
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * TypeGenerator - Generate TypeScript types from Five VM script ABI
3
+ *
4
+ * Creates type-safe interfaces for:
5
+ * - Function parameters (accounts and data)
6
+ * - Return types
7
+ * - Type-safe builder methods
8
+ *
9
+ * Example generated interface:
10
+ * ```typescript
11
+ * export interface CounterProgram {
12
+ * initialize(params: {
13
+ * accounts: { counter: PublicKey | string; owner: PublicKey | string };
14
+ * }): FunctionBuilder;
15
+ *
16
+ * add_amount(params: {
17
+ * accounts: { counter: PublicKey | string; owner: PublicKey | string };
18
+ * args: { amount: number };
19
+ * }): FunctionBuilder;
20
+ * }
21
+ * ```
22
+ */
23
+ import type { ScriptABI } from '../metadata/index.js';
24
+ export interface TypeGeneratorOptions {
25
+ /** Name of the generated program interface */
26
+ scriptName?: string;
27
+ /** Enable debug logging */
28
+ debug?: boolean;
29
+ /** Custom namespace for generated types */
30
+ namespace?: string;
31
+ /** Include JSDoc comments */
32
+ includeJSDoc?: boolean;
33
+ }
34
+ /**
35
+ * TypeGenerator creates TypeScript interfaces from ABI
36
+ */
37
+ export declare class TypeGenerator {
38
+ private abi;
39
+ private options;
40
+ constructor(abi: ScriptABI, options?: TypeGeneratorOptions);
41
+ /**
42
+ * Generate TypeScript interface definitions from ABI
43
+ *
44
+ * @returns TypeScript code as string
45
+ */
46
+ generate(): string;
47
+ /**
48
+ * Generate TypeScript function signature
49
+ *
50
+ * @param func - Function definition
51
+ * @param indent - Indentation level (spaces)
52
+ * @returns Lines of TypeScript code
53
+ */
54
+ private generateFunctionSignature;
55
+ /**
56
+ * Generate parameter type definitions for a function
57
+ *
58
+ * @param func - Function definition
59
+ * @returns Lines of TypeScript code
60
+ */
61
+ private generateFunctionParameterType;
62
+ /**
63
+ * Convert Five VM type to TypeScript type
64
+ *
65
+ * @param fiveType - Five VM type string (e.g., "u64", "Account")
66
+ * @returns TypeScript type string
67
+ */
68
+ private typeToTypeScript;
69
+ /**
70
+ * Generate interface name from script name
71
+ * E.g., "counter" → "CounterProgram"
72
+ *
73
+ * @param scriptName - Name of the script
74
+ * @returns Interface name
75
+ */
76
+ private generateInterfaceName;
77
+ /**
78
+ * Capitalize first letter
79
+ *
80
+ * @param str - String to capitalize
81
+ * @returns Capitalized string
82
+ */
83
+ private capitalize;
84
+ /**
85
+ * Get the generated ABI as TypeScript AST (for advanced use)
86
+ *
87
+ * @returns Object representation of types
88
+ */
89
+ getABIStructure(): Record<string, any>;
90
+ }