@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.
- package/README.md +279 -0
- package/dist/FiveSDK.d.ts +336 -0
- package/dist/FiveSDK.js +395 -0
- package/dist/accounts/index.d.ts +254 -0
- package/dist/accounts/index.js +543 -0
- package/dist/assets/vm/dummy.file +0 -0
- package/dist/assets/vm/five_vm_wasm.d.ts +762 -0
- package/dist/assets/vm/five_vm_wasm.js +3754 -0
- package/dist/assets/vm/five_vm_wasm_bg.js +3307 -0
- package/dist/assets/vm/five_vm_wasm_bg.wasm +0 -0
- package/dist/assets/vm/five_vm_wasm_bg.wasm.d.ts +247 -0
- package/dist/assets/vm/package.json +11 -0
- package/dist/bin/gen-types.d.ts +2 -0
- package/dist/bin/gen-types.js +35 -0
- package/dist/compiler/BytecodeCompiler.d.ts +83 -0
- package/dist/compiler/BytecodeCompiler.js +379 -0
- package/dist/config/ConfigManager.d.ts +13 -0
- package/dist/config/ConfigManager.js +27 -0
- package/dist/config/ProgramIdResolver.d.ts +62 -0
- package/dist/config/ProgramIdResolver.js +104 -0
- package/dist/crypto/index.d.ts +211 -0
- package/dist/crypto/index.js +451 -0
- package/dist/encoding/ParameterEncoder.d.ts +31 -0
- package/dist/encoding/ParameterEncoder.js +278 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +28 -0
- package/dist/lib/bytecode-encoder.d.ts +62 -0
- package/dist/lib/bytecode-encoder.js +281 -0
- package/dist/logging/index.d.ts +9 -0
- package/dist/logging/index.js +10 -0
- package/dist/metadata/index.d.ts +213 -0
- package/dist/metadata/index.js +296 -0
- package/dist/modules/accounts.d.ts +60 -0
- package/dist/modules/accounts.js +275 -0
- package/dist/modules/deploy.d.ts +90 -0
- package/dist/modules/deploy.js +1118 -0
- package/dist/modules/execute.d.ts +90 -0
- package/dist/modules/execute.js +649 -0
- package/dist/modules/fees.d.ts +14 -0
- package/dist/modules/fees.js +112 -0
- package/dist/modules/namespaces.d.ts +39 -0
- package/dist/modules/namespaces.js +190 -0
- package/dist/modules/state-diff.d.ts +35 -0
- package/dist/modules/state-diff.js +342 -0
- package/dist/modules/vm-state.d.ts +7 -0
- package/dist/modules/vm-state.js +44 -0
- package/dist/program/AccountResolver.d.ts +67 -0
- package/dist/program/AccountResolver.js +134 -0
- package/dist/program/BorshSchemaGenerator.d.ts +8 -0
- package/dist/program/BorshSchemaGenerator.js +57 -0
- package/dist/program/FiveProgram.d.ts +144 -0
- package/dist/program/FiveProgram.js +282 -0
- package/dist/program/FunctionBuilder.d.ts +114 -0
- package/dist/program/FunctionBuilder.js +347 -0
- package/dist/program/ProgramAccount.d.ts +38 -0
- package/dist/program/ProgramAccount.js +170 -0
- package/dist/program/TypeGenerator.d.ts +90 -0
- package/dist/program/TypeGenerator.js +195 -0
- package/dist/program/index.d.ts +24 -0
- package/dist/program/index.js +21 -0
- package/dist/project/config.d.ts +5 -0
- package/dist/project/config.js +33 -0
- package/dist/project/toml.d.ts +6 -0
- package/dist/project/toml.js +43 -0
- package/dist/project/workspace.d.ts +160 -0
- package/dist/project/workspace.js +73 -0
- package/dist/testing/AccountMetaGenerator.d.ts +121 -0
- package/dist/testing/AccountMetaGenerator.js +261 -0
- package/dist/testing/AccountTestFixture.d.ts +211 -0
- package/dist/testing/AccountTestFixture.js +530 -0
- package/dist/testing/OnChainAccountManager.d.ts +81 -0
- package/dist/testing/OnChainAccountManager.js +260 -0
- package/dist/testing/StateSerializer.d.ts +65 -0
- package/dist/testing/StateSerializer.js +330 -0
- package/dist/testing/TestDiscovery.d.ts +79 -0
- package/dist/testing/TestDiscovery.js +274 -0
- package/dist/testing/TestRunner.d.ts +117 -0
- package/dist/testing/TestRunner.js +346 -0
- package/dist/testing/index.d.ts +14 -0
- package/dist/testing/index.js +13 -0
- package/dist/types.d.ts +356 -0
- package/dist/types.js +32 -0
- package/dist/utils/abi.d.ts +31 -0
- package/dist/utils/abi.js +92 -0
- package/dist/utils/transaction.d.ts +5 -0
- package/dist/utils/transaction.js +48 -0
- package/dist/validation/InputValidator.d.ts +142 -0
- package/dist/validation/InputValidator.js +332 -0
- package/dist/validation/index.d.ts +4 -0
- package/dist/validation/index.js +4 -0
- package/dist/wasm/compiler/AbiLogic.d.ts +4 -0
- package/dist/wasm/compiler/AbiLogic.js +37 -0
- package/dist/wasm/compiler/AnalysisLogic.d.ts +6 -0
- package/dist/wasm/compiler/AnalysisLogic.js +61 -0
- package/dist/wasm/compiler/CompilationLogic.d.ts +10 -0
- package/dist/wasm/compiler/CompilationLogic.js +431 -0
- package/dist/wasm/compiler/FiveCompiler.d.ts +48 -0
- package/dist/wasm/compiler/FiveCompiler.js +183 -0
- package/dist/wasm/compiler/InfoLogic.d.ts +6 -0
- package/dist/wasm/compiler/InfoLogic.js +24 -0
- package/dist/wasm/compiler/OptimizationLogic.d.ts +2 -0
- package/dist/wasm/compiler/OptimizationLogic.js +13 -0
- package/dist/wasm/compiler/ValidationLogic.d.ts +7 -0
- package/dist/wasm/compiler/ValidationLogic.js +26 -0
- package/dist/wasm/compiler/index.d.ts +2 -0
- package/dist/wasm/compiler/index.js +2 -0
- package/dist/wasm/compiler/types.d.ts +8 -0
- package/dist/wasm/compiler/types.js +1 -0
- package/dist/wasm/compiler/utils.d.ts +8 -0
- package/dist/wasm/compiler/utils.js +75 -0
- package/dist/wasm/index.d.ts +9 -0
- package/dist/wasm/index.js +12 -0
- package/dist/wasm/instance.d.ts +1 -0
- package/dist/wasm/instance.js +26 -0
- package/dist/wasm/loader.d.ts +7 -0
- package/dist/wasm/loader.js +112 -0
- package/dist/wasm/vm.d.ts +33 -0
- package/dist/wasm/vm.js +250 -0
- package/package.json +59 -0
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account Test Fixture Framework for Five VM
|
|
3
|
+
*
|
|
4
|
+
* Provides a reusable, composable pattern for testing Five account-system scripts.
|
|
5
|
+
* Builders use this to:
|
|
6
|
+
* 1. Define test accounts with specific constraints
|
|
7
|
+
* 2. Initialize account state before test execution
|
|
8
|
+
* 3. Validate constraints locally before on-chain testing
|
|
9
|
+
* 4. Reuse common account setups across multiple tests
|
|
10
|
+
*
|
|
11
|
+
* Example:
|
|
12
|
+
* ```
|
|
13
|
+
* const fixture = new AccountTestFixture()
|
|
14
|
+
* .addSignerAccount('payer')
|
|
15
|
+
* .addStateAccount('counter', { value: 0 })
|
|
16
|
+
* .addMutableAccount('target')
|
|
17
|
+
* .build();
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
import { AccountMetaGenerator } from './AccountMetaGenerator.js';
|
|
21
|
+
import { PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js';
|
|
22
|
+
import { OnChainAccountManager } from './OnChainAccountManager.js';
|
|
23
|
+
import { StateSerializer } from './StateSerializer.js';
|
|
24
|
+
/**
|
|
25
|
+
* Account Test Fixture Builder
|
|
26
|
+
* Fluent API for constructing test account setups
|
|
27
|
+
*/
|
|
28
|
+
export class AccountTestFixture {
|
|
29
|
+
constructor() {
|
|
30
|
+
this.specs = [];
|
|
31
|
+
this.stateTemplates = new Map();
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Add a signer account (@signer constraint)
|
|
35
|
+
* Used for transaction signers, authority checks
|
|
36
|
+
*/
|
|
37
|
+
addSignerAccount(name, options = {}) {
|
|
38
|
+
this.specs.push({
|
|
39
|
+
name,
|
|
40
|
+
type: 'signer',
|
|
41
|
+
description: options.description || `Signer account: ${name}`
|
|
42
|
+
});
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Add a mutable account (@mut constraint)
|
|
47
|
+
* Can be modified by the script
|
|
48
|
+
*/
|
|
49
|
+
addMutableAccount(name, state, options = {}) {
|
|
50
|
+
this.specs.push({
|
|
51
|
+
name,
|
|
52
|
+
type: 'mutable',
|
|
53
|
+
state,
|
|
54
|
+
description: options.description || `Mutable account: ${name}`
|
|
55
|
+
});
|
|
56
|
+
if (state) {
|
|
57
|
+
this.stateTemplates.set(name, state);
|
|
58
|
+
}
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Add a read-only account
|
|
63
|
+
* Cannot be modified by the script
|
|
64
|
+
*/
|
|
65
|
+
addReadOnlyAccount(name, state, options = {}) {
|
|
66
|
+
this.specs.push({
|
|
67
|
+
name,
|
|
68
|
+
type: 'readonly',
|
|
69
|
+
state,
|
|
70
|
+
description: options.description || `Read-only account: ${name}`
|
|
71
|
+
});
|
|
72
|
+
if (state) {
|
|
73
|
+
this.stateTemplates.set(name, state);
|
|
74
|
+
}
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Add a state account (@mut state: StateType)
|
|
79
|
+
* Typically for program state storage
|
|
80
|
+
*/
|
|
81
|
+
addStateAccount(name, state = {}, options = {}) {
|
|
82
|
+
this.specs.push({
|
|
83
|
+
name,
|
|
84
|
+
type: 'state',
|
|
85
|
+
state,
|
|
86
|
+
description: options.description || `State account: ${name}`
|
|
87
|
+
});
|
|
88
|
+
this.stateTemplates.set(name, state);
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Add an initialization account (@init constraint)
|
|
93
|
+
* For account creation patterns
|
|
94
|
+
*/
|
|
95
|
+
addInitAccount(name, options = {}) {
|
|
96
|
+
this.specs.push({
|
|
97
|
+
name,
|
|
98
|
+
type: 'init',
|
|
99
|
+
description: options.description || `Init account: ${name}`
|
|
100
|
+
});
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Add multiple accounts in one call
|
|
105
|
+
* Useful for standard patterns
|
|
106
|
+
*/
|
|
107
|
+
addPattern(pattern) {
|
|
108
|
+
switch (pattern) {
|
|
109
|
+
case 'authorization':
|
|
110
|
+
this.addSignerAccount('authority');
|
|
111
|
+
this.addStateAccount('state', { admin: '', authorized_users: 0 });
|
|
112
|
+
break;
|
|
113
|
+
case 'state-mutation':
|
|
114
|
+
this.addStateAccount('state', { count: 0, modification_count: 0 });
|
|
115
|
+
break;
|
|
116
|
+
case 'batch-operation':
|
|
117
|
+
this.addSignerAccount('authority');
|
|
118
|
+
this.addMutableAccount('account1');
|
|
119
|
+
this.addMutableAccount('account2');
|
|
120
|
+
this.addStateAccount('state', { operation_count: 0 });
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Compile fixture into accounts ready for execution
|
|
127
|
+
* Supports both local (synthetic) and on-chain (real) account modes
|
|
128
|
+
*/
|
|
129
|
+
async build(options = {}) {
|
|
130
|
+
const mode = options.mode || 'local';
|
|
131
|
+
if (mode === 'local') {
|
|
132
|
+
return this.buildLocal(options);
|
|
133
|
+
}
|
|
134
|
+
else if (mode === 'onchain') {
|
|
135
|
+
if (!options.connection || !options.payer) {
|
|
136
|
+
throw new Error('connection and payer required for onchain mode');
|
|
137
|
+
}
|
|
138
|
+
return this.buildOnChain(options);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
throw new Error(`Unknown mode: ${mode}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Build fixture with local synthetic accounts (existing behavior)
|
|
146
|
+
*/
|
|
147
|
+
async buildLocal(options = {}) {
|
|
148
|
+
const accounts = [];
|
|
149
|
+
const accountsByName = new Map();
|
|
150
|
+
const signerCount = this.specs.filter(s => s.type === 'signer').length;
|
|
151
|
+
const mutableCount = this.specs.filter(s => s.type === 'mutable' || s.type === 'state').length;
|
|
152
|
+
const readonlyCount = this.specs.filter(s => s.type === 'readonly').length;
|
|
153
|
+
const stateCount = this.specs.filter(s => s.type === 'state').length;
|
|
154
|
+
if (options.debug) {
|
|
155
|
+
console.log(`[AccountTestFixture] Building local fixture with ${this.specs.length} accounts:`);
|
|
156
|
+
}
|
|
157
|
+
for (const spec of this.specs) {
|
|
158
|
+
const account = await this.createAccount(spec, options);
|
|
159
|
+
accounts.push(account);
|
|
160
|
+
accountsByName.set(spec.name, account);
|
|
161
|
+
if (options.debug) {
|
|
162
|
+
console.log(` ${spec.name} (${spec.type}): ${account.pubkey.substring(0, 8)}...`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
accounts,
|
|
167
|
+
accountsByName,
|
|
168
|
+
stateData: this.stateTemplates,
|
|
169
|
+
specs: this.specs,
|
|
170
|
+
metadata: {
|
|
171
|
+
signerCount,
|
|
172
|
+
mutableCount,
|
|
173
|
+
readonlyCount,
|
|
174
|
+
stateCount
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Build fixture with real on-chain accounts
|
|
180
|
+
*/
|
|
181
|
+
async buildOnChain(options) {
|
|
182
|
+
const manager = new OnChainAccountManager(options.connection, options.payer, {
|
|
183
|
+
debug: options.debug,
|
|
184
|
+
cleanup: options.cleanup
|
|
185
|
+
});
|
|
186
|
+
const accounts = [];
|
|
187
|
+
const accountsByName = new Map();
|
|
188
|
+
const createdAccounts = [];
|
|
189
|
+
const signerCount = this.specs.filter(s => s.type === 'signer').length;
|
|
190
|
+
const mutableCount = this.specs.filter(s => s.type === 'mutable' || s.type === 'state').length;
|
|
191
|
+
const readonlyCount = this.specs.filter(s => s.type === 'readonly').length;
|
|
192
|
+
const stateCount = this.specs.filter(s => s.type === 'state').length;
|
|
193
|
+
if (options.debug) {
|
|
194
|
+
console.log(`[AccountTestFixture] Building on-chain fixture with ${this.specs.length} accounts`);
|
|
195
|
+
}
|
|
196
|
+
// Create each account on-chain
|
|
197
|
+
for (const spec of this.specs) {
|
|
198
|
+
let publicKey;
|
|
199
|
+
let keypair;
|
|
200
|
+
try {
|
|
201
|
+
if (spec.type === 'signer') {
|
|
202
|
+
// Create signer account with keypair
|
|
203
|
+
const result = await manager.createSignerAccount(LAMPORTS_PER_SOL);
|
|
204
|
+
publicKey = result.publicKey;
|
|
205
|
+
keypair = result.keypair;
|
|
206
|
+
if (options.debug) {
|
|
207
|
+
console.log(` ${spec.name} (signer): ${publicKey.toString()}`);
|
|
208
|
+
}
|
|
209
|
+
accounts.push({
|
|
210
|
+
pubkey: publicKey.toString(),
|
|
211
|
+
isSigner: true,
|
|
212
|
+
isWritable: true,
|
|
213
|
+
keypair: {
|
|
214
|
+
publicKey: publicKey.toString(),
|
|
215
|
+
secretKey: keypair.secretKey
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
else if (spec.type === 'state' || spec.type === 'mutable') {
|
|
220
|
+
// Create state/mutable account with initial data
|
|
221
|
+
const space = 1024; // Default space
|
|
222
|
+
const owner = options.fiveVMProgramId || new PublicKey('9MHGM73eszNUtmJS6ypDCESguxWhCBnkUPpTMyLGqURH');
|
|
223
|
+
// Serialize state data if provided
|
|
224
|
+
let initialData;
|
|
225
|
+
if (spec.state && spec.type === 'state') {
|
|
226
|
+
const stateDefinition = {
|
|
227
|
+
name: spec.name,
|
|
228
|
+
fields: Object.keys(spec.state).map(name => ({ name, type: 'u64' })) // Simple default types
|
|
229
|
+
};
|
|
230
|
+
initialData = StateSerializer.serialize(stateDefinition, spec.state, { debug: options.debug });
|
|
231
|
+
}
|
|
232
|
+
publicKey = await manager.createStateAccount(space, owner, initialData);
|
|
233
|
+
if (options.debug) {
|
|
234
|
+
console.log(` ${spec.name} (${spec.type}): ${publicKey.toString()}`);
|
|
235
|
+
}
|
|
236
|
+
accounts.push({
|
|
237
|
+
pubkey: publicKey.toString(),
|
|
238
|
+
isSigner: false,
|
|
239
|
+
isWritable: true
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
else if (spec.type === 'init') {
|
|
243
|
+
// Create init account (will be initialized by script)
|
|
244
|
+
const space = 1024;
|
|
245
|
+
const owner = options.fiveVMProgramId || new PublicKey('9MHGM73eszNUtmJS6ypDCESguxWhCBnkUPpTMyLGqURH');
|
|
246
|
+
publicKey = await manager.createAccount(space, owner);
|
|
247
|
+
if (options.debug) {
|
|
248
|
+
console.log(` ${spec.name} (init): ${publicKey.toString()}`);
|
|
249
|
+
}
|
|
250
|
+
accounts.push({
|
|
251
|
+
pubkey: publicKey.toString(),
|
|
252
|
+
isSigner: false,
|
|
253
|
+
isWritable: true
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
// Create readonly account
|
|
258
|
+
const space = 0;
|
|
259
|
+
const owner = options.fiveVMProgramId || new PublicKey('9MHGM73eszNUtmJS6ypDCESguxWhCBnkUPpTMyLGqURH');
|
|
260
|
+
publicKey = await manager.createAccount(space, owner);
|
|
261
|
+
if (options.debug) {
|
|
262
|
+
console.log(` ${spec.name} (readonly): ${publicKey.toString()}`);
|
|
263
|
+
}
|
|
264
|
+
accounts.push({
|
|
265
|
+
pubkey: publicKey.toString(),
|
|
266
|
+
isSigner: false,
|
|
267
|
+
isWritable: false
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
accountsByName.set(spec.name, accounts[accounts.length - 1]);
|
|
271
|
+
createdAccounts.push(publicKey);
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
275
|
+
throw new Error(`Failed to create account '${spec.name}': ${errorMessage}`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// Create cleanup function if enabled
|
|
279
|
+
const cleanup = options.cleanup
|
|
280
|
+
? async () => manager.cleanup()
|
|
281
|
+
: undefined;
|
|
282
|
+
return {
|
|
283
|
+
accounts,
|
|
284
|
+
accountsByName,
|
|
285
|
+
stateData: this.stateTemplates,
|
|
286
|
+
specs: this.specs,
|
|
287
|
+
metadata: {
|
|
288
|
+
signerCount,
|
|
289
|
+
mutableCount,
|
|
290
|
+
readonlyCount,
|
|
291
|
+
stateCount
|
|
292
|
+
},
|
|
293
|
+
cleanup
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Create individual account based on spec
|
|
298
|
+
*/
|
|
299
|
+
async createAccount(spec, options) {
|
|
300
|
+
// Determine writable and signer flags based on type
|
|
301
|
+
const isWritable = spec.type === 'mutable' || spec.type === 'state' || spec.type === 'init';
|
|
302
|
+
const isSigner = spec.type === 'signer';
|
|
303
|
+
// Use AccountMetaGenerator for consistent account creation
|
|
304
|
+
const constraints = {
|
|
305
|
+
name: spec.name,
|
|
306
|
+
writable: isWritable,
|
|
307
|
+
signer: isSigner
|
|
308
|
+
};
|
|
309
|
+
if (isSigner) {
|
|
310
|
+
return await AccountMetaGenerator['generateSignerAccount'](constraints, { debug: options.debug });
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
return await AccountMetaGenerator['generateRegularAccount'](constraints, { debug: options.debug });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Validate fixture against ABI constraints
|
|
318
|
+
*/
|
|
319
|
+
validateAgainstABI(abiFunction) {
|
|
320
|
+
const errors = [];
|
|
321
|
+
const warnings = [];
|
|
322
|
+
const requiredAccounts = abiFunction.accounts || [];
|
|
323
|
+
if (this.specs.length !== requiredAccounts.length) {
|
|
324
|
+
errors.push(`Account count mismatch: fixture has ${this.specs.length} accounts, ` +
|
|
325
|
+
`ABI requires ${requiredAccounts.length}`);
|
|
326
|
+
}
|
|
327
|
+
for (let i = 0; i < Math.min(this.specs.length, requiredAccounts.length); i++) {
|
|
328
|
+
const spec = this.specs[i];
|
|
329
|
+
const abiAccount = requiredAccounts[i];
|
|
330
|
+
// Check signer constraint
|
|
331
|
+
if (spec.type === 'signer' && !abiAccount.signer) {
|
|
332
|
+
warnings.push(`Account ${i} (${spec.name}): marked as signer but ABI doesn't require it`);
|
|
333
|
+
}
|
|
334
|
+
if (spec.type !== 'signer' && abiAccount.signer) {
|
|
335
|
+
errors.push(`Account ${i} (${spec.name}): ABI requires signer, but fixture provides ${spec.type}`);
|
|
336
|
+
}
|
|
337
|
+
// Check mutability constraint
|
|
338
|
+
const isWritable = spec.type === 'mutable' || spec.type === 'state' || spec.type === 'init';
|
|
339
|
+
if (isWritable && !abiAccount.writable) {
|
|
340
|
+
warnings.push(`Account ${i} (${spec.name}): marked as writable but ABI doesn't require it`);
|
|
341
|
+
}
|
|
342
|
+
if (!isWritable && abiAccount.writable) {
|
|
343
|
+
errors.push(`Account ${i} (${spec.name}): ABI requires writable, but fixture provides ${spec.type}`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
valid: errors.length === 0,
|
|
348
|
+
errors,
|
|
349
|
+
warnings
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Get summary of fixture specs for debugging
|
|
354
|
+
*/
|
|
355
|
+
getSummary() {
|
|
356
|
+
const lines = [
|
|
357
|
+
`Account Test Fixture (${this.specs.length} accounts)`,
|
|
358
|
+
'─'.repeat(40)
|
|
359
|
+
];
|
|
360
|
+
for (const spec of this.specs) {
|
|
361
|
+
const stateStr = spec.state ? ` with state: ${JSON.stringify(spec.state)}` : '';
|
|
362
|
+
lines.push(` • ${spec.name} (${spec.type})${stateStr}`);
|
|
363
|
+
}
|
|
364
|
+
lines.push('─'.repeat(40));
|
|
365
|
+
lines.push(`Signers: ${this.specs.filter(s => s.type === 'signer').length}, ` +
|
|
366
|
+
`Writable: ${this.specs.filter(s => s.type !== 'readonly' && s.type !== 'signer').length}`);
|
|
367
|
+
return lines.join('\n');
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Predefined fixture templates for common patterns
|
|
372
|
+
* Builders can extend these for their specific needs
|
|
373
|
+
*/
|
|
374
|
+
export class FixtureTemplates {
|
|
375
|
+
/**
|
|
376
|
+
* Simple state mutation pattern
|
|
377
|
+
* For scripts that increment counters, track modifications, etc.
|
|
378
|
+
*/
|
|
379
|
+
static stateCounter() {
|
|
380
|
+
return new AccountTestFixture()
|
|
381
|
+
.addStateAccount('state', {
|
|
382
|
+
count: 0,
|
|
383
|
+
modification_count: 0
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Authorization pattern
|
|
388
|
+
* For scripts that check permissions via @signer
|
|
389
|
+
*/
|
|
390
|
+
static authorization() {
|
|
391
|
+
return new AccountTestFixture()
|
|
392
|
+
.addSignerAccount('authority', {
|
|
393
|
+
description: 'Signer that has authorization'
|
|
394
|
+
})
|
|
395
|
+
.addStateAccount('state', {
|
|
396
|
+
admin: '11111111111111111111111111111111', // Placeholder public key
|
|
397
|
+
authorized_users: 0
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Account creation pattern
|
|
402
|
+
* For scripts using @init constraint
|
|
403
|
+
*/
|
|
404
|
+
static accountCreation() {
|
|
405
|
+
return new AccountTestFixture()
|
|
406
|
+
.addSignerAccount('payer')
|
|
407
|
+
.addInitAccount('new_account')
|
|
408
|
+
.addStateAccount('state', {
|
|
409
|
+
total_created: 0,
|
|
410
|
+
last_created: '11111111111111111111111111111111'
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Multi-account transaction pattern
|
|
415
|
+
* For scripts that operate on multiple mutable accounts
|
|
416
|
+
*/
|
|
417
|
+
static batchOperation() {
|
|
418
|
+
return new AccountTestFixture()
|
|
419
|
+
.addSignerAccount('authority')
|
|
420
|
+
.addMutableAccount('account1')
|
|
421
|
+
.addMutableAccount('account2')
|
|
422
|
+
.addStateAccount('state', {
|
|
423
|
+
operation_count: 0,
|
|
424
|
+
last_operator: '11111111111111111111111111111111'
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Complex authorization pattern
|
|
429
|
+
* For multi-signature or advanced permission schemes
|
|
430
|
+
*/
|
|
431
|
+
static multiSigPattern() {
|
|
432
|
+
return new AccountTestFixture()
|
|
433
|
+
.addSignerAccount('primary')
|
|
434
|
+
.addSignerAccount('secondary')
|
|
435
|
+
.addStateAccount('state', {
|
|
436
|
+
owner: '11111111111111111111111111111111',
|
|
437
|
+
authorized_signers: 2,
|
|
438
|
+
transaction_count: 0
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* PDA pattern
|
|
443
|
+
* For scripts that use Program Derived Addresses
|
|
444
|
+
*/
|
|
445
|
+
static pdaPattern() {
|
|
446
|
+
return new AccountTestFixture()
|
|
447
|
+
.addSignerAccount('payer')
|
|
448
|
+
.addInitAccount('pda_vault')
|
|
449
|
+
.addStateAccount('state', {
|
|
450
|
+
vault_bump: 255,
|
|
451
|
+
token_bump: 255
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Test execution builder
|
|
457
|
+
* Combines fixture with test parameters for execution
|
|
458
|
+
*/
|
|
459
|
+
export class AccountTestExecutor {
|
|
460
|
+
/**
|
|
461
|
+
* Bind fixture accounts to a test execution
|
|
462
|
+
*/
|
|
463
|
+
static bindFixture(fixture, functionName, parameters = []) {
|
|
464
|
+
const keypairs = new Map();
|
|
465
|
+
// Collect keypairs from signer accounts for transaction signing
|
|
466
|
+
for (const [name, account] of fixture.accountsByName) {
|
|
467
|
+
if (account.keypair) {
|
|
468
|
+
keypairs.set(name, account.keypair.secretKey);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return {
|
|
472
|
+
fixture,
|
|
473
|
+
functionName,
|
|
474
|
+
parameters,
|
|
475
|
+
accountAddresses: fixture.accounts.map(a => a.pubkey),
|
|
476
|
+
keypairs
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Validate execution context before running
|
|
481
|
+
*/
|
|
482
|
+
static validateContext(context) {
|
|
483
|
+
const errors = [];
|
|
484
|
+
const warnings = [];
|
|
485
|
+
// Verify all accounts are properly initialized
|
|
486
|
+
if (context.fixture.accounts.length === 0) {
|
|
487
|
+
errors.push('No accounts in fixture');
|
|
488
|
+
}
|
|
489
|
+
// Verify signer accounts have keypairs
|
|
490
|
+
for (const account of context.fixture.accounts) {
|
|
491
|
+
const spec = context.fixture.specs.find(s => s.name ===
|
|
492
|
+
Array.from(context.fixture.accountsByName.entries())
|
|
493
|
+
.find(([_, acc]) => acc === account)?.[0]);
|
|
494
|
+
if (spec?.type === 'signer' && !account.keypair) {
|
|
495
|
+
errors.push(`Signer account '${spec.name}' missing keypair`);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
// Verify account addresses are valid
|
|
499
|
+
if (context.accountAddresses.length !== context.fixture.accounts.length) {
|
|
500
|
+
errors.push('Account address count mismatch');
|
|
501
|
+
}
|
|
502
|
+
return {
|
|
503
|
+
valid: errors.length === 0,
|
|
504
|
+
errors,
|
|
505
|
+
warnings
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Get human-readable execution summary
|
|
510
|
+
*/
|
|
511
|
+
static getSummary(context) {
|
|
512
|
+
const lines = [
|
|
513
|
+
`Test Execution Context`,
|
|
514
|
+
'─'.repeat(50),
|
|
515
|
+
`Function: ${context.functionName}`,
|
|
516
|
+
`Parameters: ${JSON.stringify(context.parameters)}`,
|
|
517
|
+
`Accounts: ${context.fixture.accounts.length}`,
|
|
518
|
+
'─'.repeat(50)
|
|
519
|
+
];
|
|
520
|
+
for (let i = 0; i < context.fixture.accounts.length; i++) {
|
|
521
|
+
const account = context.fixture.accounts[i];
|
|
522
|
+
const spec = context.fixture.specs[i];
|
|
523
|
+
const signer = account.isSigner ? ' [SIGNER]' : '';
|
|
524
|
+
const writable = account.isWritable ? ' [WRITABLE]' : ' [READONLY]';
|
|
525
|
+
lines.push(` ${i}: ${spec.name}${signer}${writable}`);
|
|
526
|
+
lines.push(` Address: ${account.pubkey}`);
|
|
527
|
+
}
|
|
528
|
+
return lines.join('\n');
|
|
529
|
+
}
|
|
530
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* On-Chain Account Manager for Five VM Account-System Testing
|
|
3
|
+
*
|
|
4
|
+
* Creates and manages real Solana accounts on-chain for comprehensive account-system testing.
|
|
5
|
+
* Handles account creation, funding, initialization, and cleanup.
|
|
6
|
+
*/
|
|
7
|
+
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
|
8
|
+
export interface OnChainAccountManagerOptions {
|
|
9
|
+
debug?: boolean;
|
|
10
|
+
cleanup?: boolean;
|
|
11
|
+
maxRetries?: number;
|
|
12
|
+
retryDelay?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Manages creation and lifecycle of real Solana accounts for testing
|
|
16
|
+
*/
|
|
17
|
+
export declare class OnChainAccountManager {
|
|
18
|
+
private connection;
|
|
19
|
+
private payer;
|
|
20
|
+
private options;
|
|
21
|
+
private createdAccounts;
|
|
22
|
+
private signers;
|
|
23
|
+
constructor(connection: Connection, payer: Keypair, options?: OnChainAccountManagerOptions);
|
|
24
|
+
/**
|
|
25
|
+
* Create a signer account with generated keypair
|
|
26
|
+
* Transfers SOL from payer to fund the account
|
|
27
|
+
*/
|
|
28
|
+
createSignerAccount(lamports?: number): Promise<{
|
|
29
|
+
publicKey: PublicKey;
|
|
30
|
+
keypair: Keypair;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Create a regular account with specified space and owner
|
|
34
|
+
* Uses SystemProgram.createAccount for account creation
|
|
35
|
+
*/
|
|
36
|
+
createAccount(space: number, owner: PublicKey, lamports?: number): Promise<PublicKey>;
|
|
37
|
+
/**
|
|
38
|
+
* Create and initialize a state account with initial data
|
|
39
|
+
*/
|
|
40
|
+
createStateAccount(space: number, owner: PublicKey, initialData?: Uint8Array, lamports?: number): Promise<PublicKey>;
|
|
41
|
+
/**
|
|
42
|
+
* Create a PDA account at a specific seed path
|
|
43
|
+
*/
|
|
44
|
+
createPDAAccount(seeds: Buffer[], programId: PublicKey, space: number, owner?: PublicKey, lamports?: number): Promise<{
|
|
45
|
+
publicKey: PublicKey;
|
|
46
|
+
bump: number;
|
|
47
|
+
}>;
|
|
48
|
+
/**
|
|
49
|
+
* Write data to an account (requires account to be writable)
|
|
50
|
+
*/
|
|
51
|
+
writeAccountData(accountAddress: PublicKey, data: Uint8Array): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Check if an account exists
|
|
54
|
+
*/
|
|
55
|
+
checkAccountExists(publicKey: PublicKey): Promise<boolean>;
|
|
56
|
+
/**
|
|
57
|
+
* Ensure payer has sufficient balance
|
|
58
|
+
*/
|
|
59
|
+
ensureSufficientBalance(required: number): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Create account with retry logic
|
|
62
|
+
*/
|
|
63
|
+
createAccountWithRetry(createFn: () => Promise<PublicKey>, options?: {
|
|
64
|
+
maxRetries?: number;
|
|
65
|
+
retryDelay?: number;
|
|
66
|
+
}): Promise<PublicKey>;
|
|
67
|
+
/**
|
|
68
|
+
* Get a signer keypair if it was created by this manager
|
|
69
|
+
*/
|
|
70
|
+
getSignerKeypair(publicKey: PublicKey | string): Keypair | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* Get all created accounts
|
|
73
|
+
*/
|
|
74
|
+
getCreatedAccounts(): PublicKey[];
|
|
75
|
+
/**
|
|
76
|
+
* Cleanup: Close all created accounts and transfer remaining SOL back to payer
|
|
77
|
+
* This helps with test isolation and prevents account accumulation on testnet
|
|
78
|
+
*/
|
|
79
|
+
cleanup(): Promise<void>;
|
|
80
|
+
}
|
|
81
|
+
export default OnChainAccountManager;
|