@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,543 @@
1
+ /**
2
+ * Five SDK Account System
3
+ *
4
+ * Client-agnostic account management system with validation, PDA derivation,
5
+ * and account size calculations. Uses serialization instead of direct blockchain calls.
6
+ */
7
+ import { PDAUtils, SolanaPublicKeyUtils, RentCalculator, AccountValidator } from '../crypto/index.js';
8
+ /**
9
+ * AccountType enum for test compatibility
10
+ */
11
+ export const AccountType = {
12
+ SCRIPT: 'script',
13
+ METADATA: 'metadata',
14
+ USER_STATE: 'user_state',
15
+ SYSTEM: 'system',
16
+ RENT_SYSVAR: 'rent_sysvar',
17
+ CLOCK_SYSVAR: 'clock_sysvar',
18
+ SPL_TOKEN: 'spl_token',
19
+ CUSTOM: 'custom'
20
+ };
21
+ /**
22
+ * Account manager for Five VM scripts (serialization-based)
23
+ */
24
+ export class FiveAccountManager {
25
+ constructor(programId = 'FiveProgramID11111111111111111111111111111') {
26
+ this.programId = programId;
27
+ }
28
+ /**
29
+ * Encode System Program CreateAccount instruction
30
+ */
31
+ encodeCreateAccountInstruction(params) {
32
+ // Encoding for CreateAccount instruction
33
+ // In a real implementation, this would use proper Solana instruction encoding
34
+ const buffer = new ArrayBuffer(32);
35
+ const view = new DataView(buffer);
36
+ // Instruction discriminator for CreateAccount (0)
37
+ view.setUint32(0, 0, true);
38
+ // Account size
39
+ view.setUint32(4, params.size, true);
40
+ // Rent lamports (calculated)
41
+ const rentLamports = params.rentExempt ? RentCalculator.calculateRentExemption(params.size) : 0;
42
+ view.setBigUint64(8, BigInt(rentLamports), true);
43
+ // Owner program ID would be encoded here in real implementation
44
+ // Return the basic instruction data
45
+ return new Uint8Array(buffer);
46
+ }
47
+ /**
48
+ * Create script account PDA and return serialized instruction
49
+ */
50
+ async createScriptAccount(bytecode, payerAddress) {
51
+ const pda = await PDAUtils.deriveScriptAccount(bytecode, this.programId);
52
+ const rentLamports = RentCalculator.getScriptAccountRent(bytecode.length);
53
+ // Create serialized instruction for System Program CreateAccount
54
+ const createInstruction = {
55
+ programId: '11111111111111111111111111111112', // System Program
56
+ accounts: [
57
+ { pubkey: payerAddress, isSigner: true, isWritable: true },
58
+ { pubkey: pda.address, isSigner: false, isWritable: true }
59
+ ],
60
+ data: this.encodeCreateAccountInstruction({
61
+ size: bytecode.length + 256, // Bytecode + metadata
62
+ owner: this.programId,
63
+ rentExempt: true
64
+ })
65
+ };
66
+ return {
67
+ address: pda.address,
68
+ bump: pda.bump,
69
+ createInstruction,
70
+ rentLamports
71
+ };
72
+ }
73
+ /**
74
+ * Create metadata account for script
75
+ */
76
+ async createMetadataAccount(scriptAccount, payerAddress) {
77
+ const pda = await PDAUtils.deriveMetadataAccount(scriptAccount, this.programId);
78
+ const rentLamports = RentCalculator.getMetadataAccountRent();
79
+ const createInstruction = {
80
+ programId: '11111111111111111111111111111112', // System Program
81
+ accounts: [
82
+ { pubkey: payerAddress, isSigner: true, isWritable: true },
83
+ { pubkey: pda.address, isSigner: false, isWritable: true }
84
+ ],
85
+ data: this.encodeCreateAccountInstruction({
86
+ size: 1024, // 1KB for metadata
87
+ owner: this.programId,
88
+ rentExempt: true
89
+ })
90
+ };
91
+ return {
92
+ address: pda.address,
93
+ bump: pda.bump,
94
+ createInstruction,
95
+ rentLamports
96
+ };
97
+ }
98
+ /**
99
+ * Create user state account for script interaction
100
+ */
101
+ async createUserStateAccount(userPublicKey, scriptAccount) {
102
+ const pda = await PDAUtils.deriveUserStateAccount(userPublicKey, scriptAccount, this.programId);
103
+ const rentLamports = RentCalculator.getUserStateAccountRent();
104
+ return {
105
+ address: pda.address,
106
+ bump: pda.bump,
107
+ createInstruction: {
108
+ programId: '11111111111111111111111111111112', // System Program
109
+ accounts: [
110
+ { pubkey: pda.address, isSigner: false, isWritable: true },
111
+ { pubkey: userPublicKey, isSigner: true, isWritable: true },
112
+ { pubkey: this.programId, isSigner: false, isWritable: false }
113
+ ],
114
+ data: this.encodeCreateAccountInstruction({
115
+ size: 512, // 512 bytes for user state
116
+ owner: this.programId,
117
+ rentExempt: true
118
+ })
119
+ },
120
+ rentLamports
121
+ };
122
+ }
123
+ /**
124
+ * Validate account constraints for script execution
125
+ */
126
+ async validateAccountConstraints(accounts, constraints) {
127
+ const errors = [];
128
+ const warnings = [];
129
+ let totalRentCost = 0;
130
+ // Validate signers (if specified)
131
+ if (constraints.signers) {
132
+ const providedSigners = accounts.filter(acc => acc.isSigner).map(acc => acc.address);
133
+ for (const requiredSigner of constraints.signers) {
134
+ if (!providedSigners.includes(requiredSigner)) {
135
+ errors.push(`Missing required signer: ${requiredSigner}`);
136
+ }
137
+ }
138
+ }
139
+ // Validate writable accounts (if specified)
140
+ if (constraints.writableAccounts) {
141
+ const providedWritable = accounts.filter(acc => acc.isWritable).map(acc => acc.address);
142
+ for (const requiredWritable of constraints.writableAccounts) {
143
+ if (!providedWritable.includes(requiredWritable)) {
144
+ errors.push(`Missing required writable account: ${requiredWritable}`);
145
+ }
146
+ }
147
+ }
148
+ // Validate readonly accounts (if specified)
149
+ if (constraints.readonlyAccounts) {
150
+ const providedReadonly = accounts.filter(acc => !acc.isWritable).map(acc => acc.address);
151
+ for (const requiredReadonly of constraints.readonlyAccounts) {
152
+ if (!providedReadonly.includes(requiredReadonly)) {
153
+ errors.push(`Missing required readonly account: ${requiredReadonly}`);
154
+ }
155
+ }
156
+ }
157
+ // Validate account types (if specified)
158
+ if (constraints.typeConstraints) {
159
+ for (const [address, expectedType] of constraints.typeConstraints) {
160
+ const account = accounts.find(acc => acc.address === address);
161
+ if (!account) {
162
+ errors.push(`Missing account for type constraint: ${address}`);
163
+ continue;
164
+ }
165
+ if (account.type !== expectedType) {
166
+ errors.push(`Account ${address} has type ${account.type}, expected ${expectedType}`);
167
+ }
168
+ }
169
+ }
170
+ // Validate rent requirements (if specified)
171
+ if (constraints.rentRequirements) {
172
+ for (const [address, requiredRent] of constraints.rentRequirements) {
173
+ const account = accounts.find(acc => acc.address === address);
174
+ if (!account) {
175
+ continue; // Already handled above
176
+ }
177
+ if (account.lamports !== undefined && account.lamports < requiredRent) {
178
+ errors.push(`Account ${address} has ${account.lamports} lamports, needs ${requiredRent} for rent exemption`);
179
+ }
180
+ totalRentCost += requiredRent;
181
+ }
182
+ }
183
+ // Validate maximum accounts constraint
184
+ if (constraints.maxAccounts !== undefined && accounts.length > constraints.maxAccounts) {
185
+ errors.push(`Too many accounts: ${accounts.length}, maximum allowed: ${constraints.maxAccounts}`);
186
+ }
187
+ // Validate maximum total size constraint
188
+ if (constraints.maxTotalSize !== undefined) {
189
+ const totalSize = accounts.reduce((sum, acc) => sum + (acc.size || 0), 0);
190
+ if (totalSize > constraints.maxTotalSize) {
191
+ errors.push(`Total account size ${totalSize} exceeds maximum: ${constraints.maxTotalSize}`);
192
+ }
193
+ }
194
+ // Validate required types constraint
195
+ if (constraints.requiredTypes) {
196
+ const providedTypes = new Set(accounts.map(acc => acc.type));
197
+ for (const requiredType of constraints.requiredTypes) {
198
+ if (!providedTypes.has(requiredType)) {
199
+ errors.push(`Missing required account type: ${requiredType}`);
200
+ }
201
+ }
202
+ }
203
+ // Calculate costs for all accounts
204
+ const accountSizes = accounts.map(account => ({
205
+ type: account.type,
206
+ size: account.size || 0
207
+ }));
208
+ const costs = await this.calculateAccountCreationCosts(accountSizes);
209
+ totalRentCost = costs.rentExemption;
210
+ // Validate maximum rent cost constraint
211
+ if (constraints.maxRentCost !== undefined && totalRentCost > constraints.maxRentCost) {
212
+ errors.push(`Total rent cost ${totalRentCost} exceeds maximum: ${constraints.maxRentCost}`);
213
+ }
214
+ // Validate account addresses
215
+ for (const account of accounts) {
216
+ const addressValidation = AccountValidator.validateAddress(account.address);
217
+ if (!addressValidation.valid) {
218
+ errors.push(`Invalid account address ${account.address}: ${addressValidation.errors.join(', ')}`);
219
+ }
220
+ }
221
+ const valid = errors.length === 0;
222
+ const result = {
223
+ valid,
224
+ errors,
225
+ warnings
226
+ };
227
+ if (valid) {
228
+ result.costs = {
229
+ rentExemption: costs.rentExemption,
230
+ transactionFee: costs.transactionFees,
231
+ totalCost: costs.total
232
+ };
233
+ }
234
+ return result;
235
+ }
236
+ /**
237
+ * Get account info using client-agnostic account fetcher interface
238
+ */
239
+ async getAccountInfo(address, accountFetcher) {
240
+ if (!accountFetcher) {
241
+ throw new Error('Account fetcher required for blockchain operations. Use client-agnostic account fetcher interface.');
242
+ }
243
+ try {
244
+ const accountData = await accountFetcher.getAccountData(address);
245
+ if (!accountData) {
246
+ return null;
247
+ }
248
+ return {
249
+ address,
250
+ type: this.determineAccountTypeFromData(accountData, address),
251
+ isSigner: false, // Cannot determine from account info alone
252
+ isWritable: false, // Cannot determine from account info alone
253
+ owner: accountData.owner,
254
+ size: accountData.data.length,
255
+ lamports: accountData.lamports,
256
+ data: accountData.data
257
+ };
258
+ }
259
+ catch (error) {
260
+ console.warn(`Failed to get account info for ${address}:`, error);
261
+ return null;
262
+ }
263
+ }
264
+ /**
265
+ * Get multiple account infos in batch using client-agnostic interface
266
+ */
267
+ async getMultipleAccountInfos(addresses, accountFetcher) {
268
+ if (!accountFetcher) {
269
+ throw new Error('Account fetcher required for blockchain operations. Use client-agnostic account fetcher interface.');
270
+ }
271
+ const results = new Map();
272
+ // Validate addresses first
273
+ const validAddresses = [];
274
+ for (const address of addresses) {
275
+ if (SolanaPublicKeyUtils.isValid(address)) {
276
+ validAddresses.push(address);
277
+ }
278
+ else {
279
+ // Invalid address - set to null
280
+ results.set(address, null);
281
+ }
282
+ }
283
+ if (validAddresses.length === 0) {
284
+ return results;
285
+ }
286
+ try {
287
+ const accountsData = await accountFetcher.getMultipleAccountsData(validAddresses);
288
+ for (const address of validAddresses) {
289
+ const accountData = accountsData.get(address);
290
+ if (!accountData) {
291
+ results.set(address, null);
292
+ continue;
293
+ }
294
+ results.set(address, {
295
+ address,
296
+ type: this.determineAccountTypeFromData(accountData, address),
297
+ isSigner: false,
298
+ isWritable: false,
299
+ owner: accountData.owner,
300
+ size: accountData.data.length,
301
+ lamports: accountData.lamports,
302
+ data: accountData.data
303
+ });
304
+ }
305
+ }
306
+ catch (error) {
307
+ // Fallback to individual requests
308
+ for (const address of addresses) {
309
+ const accountInfo = await this.getAccountInfo(address, accountFetcher);
310
+ results.set(address, accountInfo);
311
+ }
312
+ }
313
+ return results;
314
+ }
315
+ /**
316
+ * Check if accounts exist and are properly initialized
317
+ */
318
+ async validateAccountsExist(addresses) {
319
+ const existing = [];
320
+ const missing = [];
321
+ const invalid = [];
322
+ for (const address of addresses) {
323
+ if (!SolanaPublicKeyUtils.isValid(address)) {
324
+ invalid.push(address);
325
+ continue;
326
+ }
327
+ const accountInfo = await this.getAccountInfo(address);
328
+ if (accountInfo) {
329
+ existing.push(address);
330
+ }
331
+ else {
332
+ missing.push(address);
333
+ }
334
+ }
335
+ return { existing, missing, invalid };
336
+ }
337
+ /**
338
+ * Calculate total costs for account creation
339
+ */
340
+ async calculateAccountCreationCosts(accounts) {
341
+ let totalRent = 0;
342
+ const breakdown = [];
343
+ for (const account of accounts) {
344
+ const rent = RentCalculator.calculateRentExemption(account.size);
345
+ totalRent += rent;
346
+ breakdown.push({
347
+ type: account.type,
348
+ size: account.size,
349
+ rent
350
+ });
351
+ }
352
+ const transactionFees = 5000 * accounts.length; // Base fee per account creation
353
+ const total = totalRent + transactionFees;
354
+ return {
355
+ rentExemption: totalRent,
356
+ transactionFees,
357
+ total,
358
+ breakdown
359
+ };
360
+ }
361
+ /**
362
+ * Build standard account list for script execution
363
+ */
364
+ buildExecutionAccounts(scriptAccount, userAccount, additionalAccounts = []) {
365
+ const accounts = [
366
+ {
367
+ address: scriptAccount,
368
+ type: 'script',
369
+ isSigner: false,
370
+ isWritable: false
371
+ },
372
+ {
373
+ address: userAccount,
374
+ type: 'custom',
375
+ isSigner: true,
376
+ isWritable: true
377
+ },
378
+ {
379
+ address: this.programId,
380
+ type: 'custom',
381
+ isSigner: false,
382
+ isWritable: false
383
+ },
384
+ {
385
+ address: '11111111111111111111111111111112', // System Program
386
+ type: 'system',
387
+ isSigner: false,
388
+ isWritable: false
389
+ }
390
+ ];
391
+ // Add additional accounts
392
+ for (const account of additionalAccounts) {
393
+ accounts.push({
394
+ address: account.address,
395
+ type: 'custom',
396
+ isSigner: account.isSigner,
397
+ isWritable: account.isWritable
398
+ });
399
+ }
400
+ return accounts;
401
+ }
402
+ // Private helper methods
403
+ determineAccountTypeFromData(accountData, address) {
404
+ const owner = accountData.owner;
405
+ // System accounts
406
+ if (owner === '11111111111111111111111111111112') {
407
+ return 'system';
408
+ }
409
+ // Five VM accounts
410
+ if (owner === this.programId) {
411
+ if (accountData.data.length > 1000) {
412
+ return 'script'; // Likely contains bytecode
413
+ }
414
+ else {
415
+ return 'metadata'; // Likely metadata or state
416
+ }
417
+ }
418
+ // SPL Token accounts
419
+ if (owner === 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA') {
420
+ return 'spl_token';
421
+ }
422
+ // Sysvar accounts
423
+ if (address === 'SysvarRent111111111111111111111111111111111') {
424
+ return 'rent_sysvar';
425
+ }
426
+ if (address === 'SysvarC1ock11111111111111111111111111111111') {
427
+ return 'clock_sysvar';
428
+ }
429
+ return 'custom';
430
+ }
431
+ }
432
+ /**
433
+ * Account utilities for client-agnostic operations
434
+ */
435
+ export class AccountUtils {
436
+ /**
437
+ * Build serializable account list (client-agnostic)
438
+ */
439
+ static buildSerializableAccounts(accounts) {
440
+ return accounts.map(account => ({
441
+ pubkey: account.address,
442
+ isSigner: account.isSigner,
443
+ isWritable: account.isWritable
444
+ }));
445
+ }
446
+ /**
447
+ * Deduplicate account list while preserving most permissive permissions
448
+ */
449
+ static deduplicateAccounts(accounts) {
450
+ const accountMap = new Map();
451
+ for (const account of accounts) {
452
+ const existing = accountMap.get(account.address);
453
+ if (!existing) {
454
+ accountMap.set(account.address, { ...account });
455
+ }
456
+ else {
457
+ // Keep most permissive permissions
458
+ existing.isSigner = existing.isSigner || account.isSigner;
459
+ existing.isWritable = existing.isWritable || account.isWritable;
460
+ }
461
+ }
462
+ return Array.from(accountMap.values());
463
+ }
464
+ /**
465
+ * Sort accounts by standard Solana conventions
466
+ */
467
+ static sortAccounts(accounts) {
468
+ return accounts.sort((a, b) => {
469
+ // Signers first
470
+ if (a.isSigner !== b.isSigner) {
471
+ return b.isSigner ? 1 : -1;
472
+ }
473
+ // Writable accounts next
474
+ if (a.isWritable !== b.isWritable) {
475
+ return b.isWritable ? 1 : -1;
476
+ }
477
+ // Alphabetical by address
478
+ return a.address.localeCompare(b.address);
479
+ });
480
+ }
481
+ /**
482
+ * Validate account list structure and compute statistics
483
+ */
484
+ static validateAccountList(accounts) {
485
+ const errors = [];
486
+ const requiredAccounts = [];
487
+ const optionalAccounts = [];
488
+ let totalSize = 0;
489
+ if (!Array.isArray(accounts)) {
490
+ errors.push('Accounts must be an array');
491
+ return {
492
+ valid: false,
493
+ errors,
494
+ totalSize: 0,
495
+ requiredAccounts: [],
496
+ optionalAccounts: []
497
+ };
498
+ }
499
+ for (let i = 0; i < accounts.length; i++) {
500
+ const account = accounts[i];
501
+ const prefix = `Account ${i}`;
502
+ // Validate address
503
+ if (!account.address || !SolanaPublicKeyUtils.isValid(account.address)) {
504
+ errors.push(`${prefix}: Invalid address`);
505
+ }
506
+ // Validate size
507
+ if (account.size !== undefined) {
508
+ if (account.size < 0) {
509
+ errors.push(`${prefix}: size must be positive`);
510
+ }
511
+ else {
512
+ totalSize += account.size;
513
+ }
514
+ }
515
+ // Categorize by required/optional
516
+ if (account.required) {
517
+ requiredAccounts.push(account);
518
+ }
519
+ else {
520
+ optionalAccounts.push(account);
521
+ }
522
+ }
523
+ return {
524
+ valid: errors.length === 0,
525
+ errors,
526
+ totalSize,
527
+ requiredAccounts,
528
+ optionalAccounts
529
+ };
530
+ }
531
+ /**
532
+ * Filter accounts by type
533
+ */
534
+ static filterAccountsByType(accounts, type) {
535
+ return accounts.filter(account => account.type === type);
536
+ }
537
+ /**
538
+ * Calculate total size of accounts
539
+ */
540
+ static calculateTotalSize(accounts) {
541
+ return accounts.reduce((total, account) => total + (account.size || 0), 0);
542
+ }
543
+ }
File without changes