@5ive-tech/sdk 1.1.16 → 1.1.18
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/LICENSE +21 -0
- package/dist/assets/vm/five_vm_wasm_bg.wasm +0 -0
- package/dist/config/VmClusterConfigResolver.d.ts +6 -0
- package/dist/config/VmClusterConfigResolver.js +16 -0
- package/dist/lib/bytecode-encoder.js +12 -2
- package/dist/metadata/index.d.ts +4 -0
- package/dist/modules/deploy.d.ts +2 -0
- package/dist/modules/deploy.js +106 -40
- package/dist/program/FiveProgram.d.ts +12 -0
- package/dist/program/FiveProgram.js +12 -0
- package/dist/program/FunctionBuilder.d.ts +2 -0
- package/dist/program/FunctionBuilder.js +51 -1
- package/dist/program/SessionManager.d.ts +61 -0
- package/dist/program/SessionManager.js +97 -0
- package/dist/program/index.d.ts +6 -0
- package/dist/program/index.js +6 -0
- package/dist/testing/TestDiscovery.js +6 -3
- package/dist/testing/TestRunner.js +31 -5
- package/dist/types.d.ts +1 -0
- package/dist/utils/abi.d.ts +2 -0
- package/dist/utils/abi.js +4 -0
- package/dist/wasm/loader.js +19 -4
- package/package.json +1 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Five VM Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
Binary file
|
|
@@ -3,6 +3,12 @@ export interface VmClusterProfile {
|
|
|
3
3
|
configPath: string;
|
|
4
4
|
programId: string;
|
|
5
5
|
feeVaultShardCount: number;
|
|
6
|
+
sessionService?: {
|
|
7
|
+
scriptAccount?: string;
|
|
8
|
+
codeHash?: string;
|
|
9
|
+
status?: 'active' | 'disabled';
|
|
10
|
+
version?: number;
|
|
11
|
+
};
|
|
6
12
|
}
|
|
7
13
|
export interface VmClusterAddresses {
|
|
8
14
|
cluster: VmClusterProfile['cluster'];
|
|
@@ -84,11 +84,27 @@ export class VmClusterConfigResolver {
|
|
|
84
84
|
throw new Error(`Invalid fee_vault_shard_count for cluster ${cluster}`);
|
|
85
85
|
}
|
|
86
86
|
const programId = new PublicKey(entry.program_id).toBase58();
|
|
87
|
+
const sessionScript = entry.session_v1_script_account
|
|
88
|
+
? new PublicKey(entry.session_v1_script_account).toBase58()
|
|
89
|
+
: undefined;
|
|
90
|
+
const sessionHash = entry.session_v1_code_hash
|
|
91
|
+
? new PublicKey(entry.session_v1_code_hash).toBase58()
|
|
92
|
+
: undefined;
|
|
93
|
+
const sessionStatus = entry.session_v1_status === 0 ? 'disabled' : 'active';
|
|
94
|
+
const sessionVersion = Number.isInteger(entry.session_v1_version)
|
|
95
|
+
? entry.session_v1_version
|
|
96
|
+
: 1;
|
|
87
97
|
return {
|
|
88
98
|
cluster: cluster,
|
|
89
99
|
configPath,
|
|
90
100
|
programId,
|
|
91
101
|
feeVaultShardCount: entry.fee_vault_shard_count,
|
|
102
|
+
sessionService: {
|
|
103
|
+
scriptAccount: sessionScript,
|
|
104
|
+
codeHash: sessionHash,
|
|
105
|
+
status: sessionStatus,
|
|
106
|
+
version: sessionVersion,
|
|
107
|
+
},
|
|
92
108
|
};
|
|
93
109
|
}
|
|
94
110
|
static deriveVmAddresses(profile) {
|
|
@@ -68,6 +68,7 @@ export class BytecodeEncoder {
|
|
|
68
68
|
maxLen: typeSpec.maxLen,
|
|
69
69
|
};
|
|
70
70
|
});
|
|
71
|
+
let wasmLoadError = null;
|
|
71
72
|
// Load WASM module using shared loader
|
|
72
73
|
if (!wasmModule) {
|
|
73
74
|
try {
|
|
@@ -88,11 +89,12 @@ export class BytecodeEncoder {
|
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
catch (e) {
|
|
91
|
-
// Silently ignore loader errors and try fallback
|
|
92
92
|
wasmModule = null;
|
|
93
|
+
wasmLoadError = e;
|
|
93
94
|
}
|
|
94
95
|
// Fallback: import the wasm-pack generated module for Node.js.
|
|
95
|
-
|
|
96
|
+
const hasWindow = typeof globalThis.window !== 'undefined';
|
|
97
|
+
if (!wasmModule && !hasWindow && typeof process !== 'undefined') {
|
|
96
98
|
console.log("[DEBUG] (SRC) Attempting wasm-pack module import...");
|
|
97
99
|
try {
|
|
98
100
|
const fs = await import('fs');
|
|
@@ -123,6 +125,14 @@ export class BytecodeEncoder {
|
|
|
123
125
|
// Don't throw - let it fall through to error handling below.
|
|
124
126
|
}
|
|
125
127
|
}
|
|
128
|
+
if (!wasmModule || !wasmModule.ParameterEncoder) {
|
|
129
|
+
const detail = wasmLoadError instanceof Error
|
|
130
|
+
? wasmLoadError.message
|
|
131
|
+
: wasmLoadError
|
|
132
|
+
? String(wasmLoadError)
|
|
133
|
+
: "WASM module loaded without ParameterEncoder";
|
|
134
|
+
throw new Error(`[BytecodeEncoder] WASM ParameterEncoder unavailable: ${detail}`);
|
|
135
|
+
}
|
|
126
136
|
}
|
|
127
137
|
const filteredParams = normalizedParameters;
|
|
128
138
|
const paramValues = filteredParams.map(param => {
|
package/dist/metadata/index.d.ts
CHANGED
|
@@ -92,6 +92,10 @@ export interface ParameterDefinition {
|
|
|
92
92
|
param_type?: string;
|
|
93
93
|
/** Whether this is an account parameter */
|
|
94
94
|
is_account?: boolean;
|
|
95
|
+
/** Compiler-injected parameter not authored in source */
|
|
96
|
+
implicit?: boolean;
|
|
97
|
+
/** Parameter origin */
|
|
98
|
+
source?: 'authored' | 'compiler';
|
|
95
99
|
/** Account attributes (e.g., "mut", "signer", "init") */
|
|
96
100
|
attributes?: string[];
|
|
97
101
|
/** Whether parameter is optional */
|
package/dist/modules/deploy.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ options?: {
|
|
|
16
16
|
fiveVMProgramId?: string;
|
|
17
17
|
computeBudget?: number;
|
|
18
18
|
exportMetadata?: ExportMetadataInput;
|
|
19
|
+
service?: "session_v1";
|
|
19
20
|
}): Promise<{
|
|
20
21
|
transaction: any;
|
|
21
22
|
scriptKeypair: any;
|
|
@@ -34,6 +35,7 @@ options?: {
|
|
|
34
35
|
vmStateAccount?: string;
|
|
35
36
|
adminAccount?: string;
|
|
36
37
|
exportMetadata?: ExportMetadataInput;
|
|
38
|
+
service?: "session_v1";
|
|
37
39
|
}): Promise<{
|
|
38
40
|
success: boolean;
|
|
39
41
|
programId?: string;
|
package/dist/modules/deploy.js
CHANGED
|
@@ -18,10 +18,20 @@ const FEE_VAULT_NAMESPACE_SEED = Buffer.from([
|
|
|
18
18
|
0xff, 0x66, 0x69, 0x76, 0x65, 0x5f, 0x76, 0x6d, 0x5f, 0x66, 0x65, 0x65,
|
|
19
19
|
0x5f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x31,
|
|
20
20
|
]);
|
|
21
|
+
const SESSION_V1_SERVICE_SEED = Buffer.from("session_v1", "utf-8");
|
|
22
|
+
const SERVICE_KIND_NONE = 0;
|
|
23
|
+
const SERVICE_KIND_SESSION_V1 = 1;
|
|
21
24
|
function clampShardCount(rawCount) {
|
|
22
25
|
const normalized = rawCount > 0 ? rawCount : DEFAULT_FEE_VAULT_SHARD_COUNT;
|
|
23
26
|
return Math.max(1, Math.min(MAX_FEE_VAULT_SHARD_COUNT, normalized));
|
|
24
27
|
}
|
|
28
|
+
function resolveServiceKind(service) {
|
|
29
|
+
if (!service)
|
|
30
|
+
return SERVICE_KIND_NONE;
|
|
31
|
+
if (service === "session_v1")
|
|
32
|
+
return SERVICE_KIND_SESSION_V1;
|
|
33
|
+
throw new Error(`Unsupported deploy service: ${service}`);
|
|
34
|
+
}
|
|
25
35
|
function normalizeRpcEndpoint(connection) {
|
|
26
36
|
return String(connection?.rpcEndpoint || connection?._rpcEndpoint || "").toLowerCase();
|
|
27
37
|
}
|
|
@@ -71,6 +81,14 @@ async function deriveProgramFeeVault(programId, shardIndex) {
|
|
|
71
81
|
}
|
|
72
82
|
async function resolveScriptAccountDerivation(bytecode, deployer, programId, options) {
|
|
73
83
|
const { PublicKey } = await import("@solana/web3.js");
|
|
84
|
+
if (options.service === "session_v1") {
|
|
85
|
+
const [servicePda, serviceBump] = PublicKey.findProgramAddressSync([SESSION_V1_SERVICE_SEED], new PublicKey(programId));
|
|
86
|
+
return {
|
|
87
|
+
address: servicePda.toBase58(),
|
|
88
|
+
bump: serviceBump,
|
|
89
|
+
seed: "session_v1",
|
|
90
|
+
};
|
|
91
|
+
}
|
|
74
92
|
if (options.scriptAccount) {
|
|
75
93
|
validator.validateBase58Address(options.scriptAccount, "options.scriptAccount");
|
|
76
94
|
if (!options.scriptSeed) {
|
|
@@ -181,7 +199,7 @@ export async function generateDeployInstruction(bytecode, deployer, options = {}
|
|
|
181
199
|
isSigner: false,
|
|
182
200
|
isWritable: false,
|
|
183
201
|
});
|
|
184
|
-
const instructionData = encodeDeployInstruction(bytecode, options.permissions || 0, exportMetadata, deployShardIndex, deployVault.bump);
|
|
202
|
+
const instructionData = encodeDeployInstruction(bytecode, options.permissions || 0, exportMetadata, deployShardIndex, deployVault.bump, resolveServiceKind(options.service));
|
|
185
203
|
const result = {
|
|
186
204
|
programId: programId,
|
|
187
205
|
instruction: {
|
|
@@ -193,15 +211,23 @@ export async function generateDeployInstruction(bytecode, deployer, options = {}
|
|
|
193
211
|
requiredSigners: [deployer],
|
|
194
212
|
estimatedCost: rentLamports + (options.extraLamports || 0),
|
|
195
213
|
bytecodeSize: bytecode.length,
|
|
196
|
-
setupInstructions:
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
214
|
+
setupInstructions: options.service === "session_v1"
|
|
215
|
+
? {
|
|
216
|
+
serviceDeployment: {
|
|
217
|
+
service: "session_v1",
|
|
218
|
+
pda: scriptAccount,
|
|
219
|
+
seed: scriptSeed,
|
|
220
|
+
},
|
|
221
|
+
}
|
|
222
|
+
: {
|
|
223
|
+
createScriptAccount: {
|
|
224
|
+
pda: scriptAccount,
|
|
225
|
+
seed: scriptSeed,
|
|
226
|
+
space: totalAccountSize,
|
|
227
|
+
rent: rentLamports,
|
|
228
|
+
owner: programId,
|
|
229
|
+
},
|
|
203
230
|
},
|
|
204
|
-
},
|
|
205
231
|
adminAccount: options.adminAccount,
|
|
206
232
|
};
|
|
207
233
|
if (options.debug) {
|
|
@@ -237,9 +263,18 @@ options = {}) {
|
|
|
237
263
|
const { Keypair, PublicKey, Transaction, TransactionInstruction, SystemProgram, ComputeBudgetProgram, } = await import("@solana/web3.js");
|
|
238
264
|
const programIdStr = ProgramIdResolver.resolve(options.fiveVMProgramId);
|
|
239
265
|
const programId = new PublicKey(programIdStr);
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
266
|
+
const serviceKind = resolveServiceKind(options.service);
|
|
267
|
+
let scriptKeypair = null;
|
|
268
|
+
let scriptPubkey;
|
|
269
|
+
if (serviceKind === SERVICE_KIND_SESSION_V1) {
|
|
270
|
+
const derived = PublicKey.findProgramAddressSync([SESSION_V1_SERVICE_SEED], new PublicKey(programIdStr));
|
|
271
|
+
scriptPubkey = derived[0];
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
scriptKeypair = Keypair.generate();
|
|
275
|
+
scriptPubkey = scriptKeypair.publicKey;
|
|
276
|
+
}
|
|
277
|
+
const scriptAccount = scriptPubkey.toString();
|
|
243
278
|
// Calculate account size and rent
|
|
244
279
|
const exportMetadata = encodeExportMetadata(options.exportMetadata);
|
|
245
280
|
const totalAccountSize = SCRIPT_ACCOUNT_HEADER_LEN + exportMetadata.length + bytecode.length;
|
|
@@ -275,18 +310,22 @@ options = {}) {
|
|
|
275
310
|
}));
|
|
276
311
|
}
|
|
277
312
|
// 2. Create Script Account
|
|
278
|
-
tx.add(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
313
|
+
tx.add(...(scriptKeypair
|
|
314
|
+
? [
|
|
315
|
+
SystemProgram.createAccount({
|
|
316
|
+
fromPubkey: deployerPublicKey,
|
|
317
|
+
newAccountPubkey: scriptKeypair.publicKey,
|
|
318
|
+
lamports: rentLamports,
|
|
319
|
+
space: totalAccountSize,
|
|
320
|
+
programId: programId,
|
|
321
|
+
}),
|
|
322
|
+
]
|
|
323
|
+
: []));
|
|
285
324
|
// 3. Deploy Instruction
|
|
286
|
-
const deployData = encodeDeployInstruction(bytecode, 0, exportMetadata);
|
|
325
|
+
const deployData = encodeDeployInstruction(bytecode, 0, exportMetadata, 0, undefined, serviceKind);
|
|
287
326
|
tx.add(new TransactionInstruction({
|
|
288
327
|
keys: [
|
|
289
|
-
{ pubkey:
|
|
328
|
+
{ pubkey: scriptPubkey, isSigner: false, isWritable: true },
|
|
290
329
|
{ pubkey: vmStatePubkey, isSigner: false, isWritable: true },
|
|
291
330
|
{ pubkey: deployerPublicKey, isSigner: true, isWritable: true },
|
|
292
331
|
],
|
|
@@ -297,7 +336,9 @@ options = {}) {
|
|
|
297
336
|
tx.recentBlockhash = blockhash;
|
|
298
337
|
tx.feePayer = deployerPublicKey;
|
|
299
338
|
// Partial sign with generated keys
|
|
300
|
-
|
|
339
|
+
if (scriptKeypair) {
|
|
340
|
+
tx.partialSign(scriptKeypair);
|
|
341
|
+
}
|
|
301
342
|
return {
|
|
302
343
|
transaction: tx,
|
|
303
344
|
scriptKeypair,
|
|
@@ -319,10 +360,25 @@ options = {}) {
|
|
|
319
360
|
}
|
|
320
361
|
// Generate script keypair like frontend-five
|
|
321
362
|
const { Keypair, PublicKey, Transaction, TransactionInstruction, SystemProgram, } = await import("@solana/web3.js");
|
|
322
|
-
const
|
|
323
|
-
|
|
363
|
+
const serviceKind = resolveServiceKind(options.service);
|
|
364
|
+
let scriptKeypair = null;
|
|
365
|
+
let scriptPubkey;
|
|
366
|
+
if (serviceKind === SERVICE_KIND_SESSION_V1) {
|
|
367
|
+
const derived = PublicKey.findProgramAddressSync([SESSION_V1_SERVICE_SEED], new PublicKey(programId));
|
|
368
|
+
scriptPubkey = derived[0];
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
scriptKeypair = Keypair.generate();
|
|
372
|
+
scriptPubkey = scriptKeypair.publicKey;
|
|
373
|
+
}
|
|
374
|
+
const scriptAccount = scriptPubkey.toString();
|
|
324
375
|
if (options.debug) {
|
|
325
|
-
|
|
376
|
+
if (scriptKeypair) {
|
|
377
|
+
console.log(`[FiveSDK] Generated script keypair: ${scriptAccount}`);
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
console.log(`[FiveSDK] Using canonical service PDA script account: ${scriptAccount}`);
|
|
381
|
+
}
|
|
326
382
|
}
|
|
327
383
|
// Calculate account size and rent
|
|
328
384
|
const exportMetadata = encodeExportMetadata(options.exportMetadata);
|
|
@@ -372,21 +428,23 @@ options = {}) {
|
|
|
372
428
|
}
|
|
373
429
|
catch { }
|
|
374
430
|
}
|
|
375
|
-
// 1) Create script account
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
431
|
+
// 1) Create script account for non-service deployments.
|
|
432
|
+
if (scriptKeypair) {
|
|
433
|
+
const createAccountIx = SystemProgram.createAccount({
|
|
434
|
+
fromPubkey: deployerKeypair.publicKey,
|
|
435
|
+
newAccountPubkey: scriptKeypair.publicKey,
|
|
436
|
+
lamports: rentLamports,
|
|
437
|
+
space: totalAccountSize,
|
|
438
|
+
programId: new PublicKey(programId),
|
|
439
|
+
});
|
|
440
|
+
tx.add(createAccountIx);
|
|
441
|
+
}
|
|
442
|
+
const deployData = encodeDeployInstruction(bytecode, 0, exportMetadata, deployShardIndex, deployVault.bump, serviceKind);
|
|
385
443
|
const instructionDataBuffer = Buffer.from(deployData);
|
|
386
444
|
const deployIx = new TransactionInstruction({
|
|
387
445
|
keys: [
|
|
388
446
|
{
|
|
389
|
-
pubkey:
|
|
447
|
+
pubkey: scriptPubkey,
|
|
390
448
|
isSigner: false,
|
|
391
449
|
isWritable: true,
|
|
392
450
|
},
|
|
@@ -419,7 +477,9 @@ options = {}) {
|
|
|
419
477
|
tx.recentBlockhash = blockhash;
|
|
420
478
|
tx.feePayer = deployerKeypair.publicKey;
|
|
421
479
|
tx.partialSign(deployerKeypair);
|
|
422
|
-
|
|
480
|
+
if (scriptKeypair) {
|
|
481
|
+
tx.partialSign(scriptKeypair);
|
|
482
|
+
}
|
|
423
483
|
const txSerialized = tx.serialize();
|
|
424
484
|
if (options.debug) {
|
|
425
485
|
console.log(`[FiveSDK] Transaction serialized: ${txSerialized.length} bytes`);
|
|
@@ -1085,13 +1145,14 @@ options = {}) {
|
|
|
1085
1145
|
};
|
|
1086
1146
|
}
|
|
1087
1147
|
}
|
|
1088
|
-
function encodeDeployInstruction(bytecode, permissions = 0, metadata = new Uint8Array(), feeShardIndex = 0, feeVaultBump) {
|
|
1148
|
+
function encodeDeployInstruction(bytecode, permissions = 0, metadata = new Uint8Array(), feeShardIndex = 0, feeVaultBump, serviceKind = SERVICE_KIND_NONE) {
|
|
1089
1149
|
const lengthBuffer = Buffer.allocUnsafe(4);
|
|
1090
1150
|
lengthBuffer.writeUInt32LE(bytecode.length, 0);
|
|
1091
1151
|
const metadataLenBuffer = Buffer.allocUnsafe(4);
|
|
1092
1152
|
metadataLenBuffer.writeUInt32LE(metadata.length, 0);
|
|
1093
1153
|
const hasFeeTrailer = typeof feeVaultBump === "number";
|
|
1094
|
-
const
|
|
1154
|
+
const hasServiceTrailer = serviceKind !== SERVICE_KIND_NONE;
|
|
1155
|
+
const result = new Uint8Array(1 + 4 + 1 + 4 + metadata.length + bytecode.length + (hasFeeTrailer ? 2 : 0) + (hasServiceTrailer ? 1 : 0));
|
|
1095
1156
|
result[0] = 8; // Deploy discriminator (matches on-chain FIVE program)
|
|
1096
1157
|
result.set(new Uint8Array(lengthBuffer), 1); // u32 LE length at bytes 1-4
|
|
1097
1158
|
result[5] = permissions; // permissions byte at byte 5
|
|
@@ -1103,6 +1164,10 @@ function encodeDeployInstruction(bytecode, permissions = 0, metadata = new Uint8
|
|
|
1103
1164
|
result[trailerOffset] = feeShardIndex & 0xff;
|
|
1104
1165
|
result[trailerOffset + 1] = feeVaultBump & 0xff;
|
|
1105
1166
|
}
|
|
1167
|
+
if (hasServiceTrailer) {
|
|
1168
|
+
const trailerOffset = 10 + metadata.length + bytecode.length + (hasFeeTrailer ? 2 : 0);
|
|
1169
|
+
result[trailerOffset] = serviceKind & 0xff;
|
|
1170
|
+
}
|
|
1106
1171
|
console.log(`[FiveSDK] Deploy instruction encoded:`, {
|
|
1107
1172
|
discriminator: result[0],
|
|
1108
1173
|
lengthBytes: Array.from(new Uint8Array(lengthBuffer)),
|
|
@@ -1110,7 +1175,8 @@ function encodeDeployInstruction(bytecode, permissions = 0, metadata = new Uint8
|
|
|
1110
1175
|
metadataLength: metadata.length,
|
|
1111
1176
|
bytecodeLength: bytecode.length,
|
|
1112
1177
|
totalInstructionLength: result.length,
|
|
1113
|
-
|
|
1178
|
+
serviceKind,
|
|
1179
|
+
expectedFormat: `[8, ${bytecode.length}_as_u32le, 0x${permissions.toString(16).padStart(2, '0')}, ${metadata.length}_as_u32le, metadata_bytes, bytecode_bytes, optional(shard,bump), optional(service_kind)]`,
|
|
1114
1180
|
instructionHex: Buffer.from(result).toString("hex").substring(0, 20) + "...",
|
|
1115
1181
|
});
|
|
1116
1182
|
return result;
|
|
@@ -21,6 +21,7 @@ import type { ScriptABI, FunctionDefinition, AccountFetcher } from '../metadata/
|
|
|
21
21
|
import type { Provider } from '../types.js';
|
|
22
22
|
import { FunctionBuilder } from './FunctionBuilder.js';
|
|
23
23
|
import { ProgramAccount } from './ProgramAccount.js';
|
|
24
|
+
import type { SessionManager } from './SessionManager.js';
|
|
24
25
|
export interface FiveProgramOptions {
|
|
25
26
|
/** Enable debug logging */
|
|
26
27
|
debug?: boolean;
|
|
@@ -34,6 +35,13 @@ export interface FiveProgramOptions {
|
|
|
34
35
|
feeReceiverAccount?: string;
|
|
35
36
|
/** Wallet/Network Provider for RPC calls */
|
|
36
37
|
provider?: Provider;
|
|
38
|
+
/** Optional session helper for delegated signer flows */
|
|
39
|
+
session?: {
|
|
40
|
+
manager: SessionManager;
|
|
41
|
+
mode?: 'auto' | 'force-direct' | 'force-session';
|
|
42
|
+
sessionAccountByFunction?: Record<string, string>;
|
|
43
|
+
delegateAccountByFunction?: Record<string, string>;
|
|
44
|
+
};
|
|
37
45
|
}
|
|
38
46
|
/**
|
|
39
47
|
* FiveProgram represents a deployed Five script with its ABI
|
|
@@ -109,6 +117,10 @@ export declare class FiveProgram {
|
|
|
109
117
|
* Get Five VM Program ID with consistent resolver precedence
|
|
110
118
|
*/
|
|
111
119
|
getFiveVMProgramId(): string;
|
|
120
|
+
/**
|
|
121
|
+
* Return a program instance that auto-applies session account/delegate mapping.
|
|
122
|
+
*/
|
|
123
|
+
withSession(config: NonNullable<FiveProgramOptions['session']>): FiveProgram;
|
|
112
124
|
/**
|
|
113
125
|
* Derive a Program Derived Address (PDA)
|
|
114
126
|
*
|
|
@@ -210,6 +210,18 @@ export class FiveProgram {
|
|
|
210
210
|
getFiveVMProgramId() {
|
|
211
211
|
return ProgramIdResolver.resolve(this.options.fiveVMProgramId);
|
|
212
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Return a program instance that auto-applies session account/delegate mapping.
|
|
215
|
+
*/
|
|
216
|
+
withSession(config) {
|
|
217
|
+
return new FiveProgram(this.scriptAccount, this.abi, {
|
|
218
|
+
...this.options,
|
|
219
|
+
session: {
|
|
220
|
+
mode: 'auto',
|
|
221
|
+
...config,
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
}
|
|
213
225
|
/**
|
|
214
226
|
* Derive a Program Derived Address (PDA)
|
|
215
227
|
*
|
|
@@ -77,6 +77,8 @@ export declare class FunctionBuilder {
|
|
|
77
77
|
skipPreflight?: boolean;
|
|
78
78
|
computeUnits?: number;
|
|
79
79
|
}): Promise<string>;
|
|
80
|
+
private applySessionDefaults;
|
|
81
|
+
private resolveSessionSigner;
|
|
80
82
|
/**
|
|
81
83
|
* Validate that all required parameters are provided
|
|
82
84
|
* @throws Error if any required parameter is missing
|
|
@@ -79,6 +79,8 @@ export class FunctionBuilder {
|
|
|
79
79
|
*/
|
|
80
80
|
async instruction() {
|
|
81
81
|
// Validate parameters later, after auto-injection
|
|
82
|
+
// Optional session auto-wiring.
|
|
83
|
+
this.applySessionDefaults();
|
|
82
84
|
// Resolve system accounts (auto-inject when needed)
|
|
83
85
|
const resolver = new AccountResolver(this.options);
|
|
84
86
|
const resolvedSystemAccounts = resolver.resolveSystemAccounts(this.functionDef, this.accountsMap);
|
|
@@ -156,11 +158,52 @@ export class FunctionBuilder {
|
|
|
156
158
|
}
|
|
157
159
|
const tx = await this.transaction({ computeUnits: options.computeUnits });
|
|
158
160
|
// Send
|
|
159
|
-
const signers = options.signers || [];
|
|
161
|
+
const signers = [...(options.signers || [])];
|
|
162
|
+
const sessionSigner = this.resolveSessionSigner();
|
|
163
|
+
if (sessionSigner) {
|
|
164
|
+
signers.push(sessionSigner);
|
|
165
|
+
}
|
|
160
166
|
return await provider.sendAndConfirm(tx, signers, {
|
|
161
167
|
skipPreflight: options.skipPreflight
|
|
162
168
|
});
|
|
163
169
|
}
|
|
170
|
+
applySessionDefaults() {
|
|
171
|
+
const session = this.options.session;
|
|
172
|
+
if (session?.mode === 'force-direct') {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const functionName = this.functionDef.name;
|
|
176
|
+
const sessionAddress = session?.sessionAccountByFunction?.[functionName];
|
|
177
|
+
const authorityAddress = this.accountsMap.get('owner') || this.accountsMap.get('authority');
|
|
178
|
+
for (const param of this.functionDef.parameters) {
|
|
179
|
+
if (!param.is_account)
|
|
180
|
+
continue;
|
|
181
|
+
const attrs = param.attributes || [];
|
|
182
|
+
const isImplicit = param.implicit === true || param.source === 'compiler';
|
|
183
|
+
const isLegacyInjectedSessionParam = param.name === '__session';
|
|
184
|
+
if ((attrs.includes('session') || isImplicit || param.name === '__session') &&
|
|
185
|
+
sessionAddress &&
|
|
186
|
+
!this.accountsMap.has(param.name)) {
|
|
187
|
+
this.accountsMap.set(param.name, sessionAddress);
|
|
188
|
+
}
|
|
189
|
+
// Direct-owner fallback for compiler-injected implicit session wiring:
|
|
190
|
+
// if no session manager config is present, alias hidden session/delegate
|
|
191
|
+
// accounts to owner/authority so @session can take the direct-owner path.
|
|
192
|
+
if ((isImplicit || isLegacyInjectedSessionParam) && !this.accountsMap.has(param.name) && authorityAddress) {
|
|
193
|
+
if (isLegacyInjectedSessionParam) {
|
|
194
|
+
this.accountsMap.set(param.name, authorityAddress);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
resolveSessionSigner() {
|
|
200
|
+
const session = this.options.session;
|
|
201
|
+
if (!session || session.mode === 'force-direct') {
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
const maybeSigner = session.manager?.delegateSigner;
|
|
205
|
+
return maybeSigner;
|
|
206
|
+
}
|
|
164
207
|
/**
|
|
165
208
|
* Validate that all required parameters are provided
|
|
166
209
|
* @throws Error if any required parameter is missing
|
|
@@ -169,6 +212,13 @@ export class FunctionBuilder {
|
|
|
169
212
|
for (const param of this.functionDef.parameters) {
|
|
170
213
|
if (param.is_account) {
|
|
171
214
|
if (!this.accountsMap.has(param.name)) {
|
|
215
|
+
const isImplicit = param.implicit === true ||
|
|
216
|
+
param.source === 'compiler' ||
|
|
217
|
+
param.name === '__session';
|
|
218
|
+
if (isImplicit) {
|
|
219
|
+
throw new Error(`Missing implicit account '${param.name}' for function '${this.functionDef.name}'. ` +
|
|
220
|
+
`Provide it via .accounts({ ${param.name}: ... }) or configure program.withSession(...) for auto-wiring.`);
|
|
221
|
+
}
|
|
172
222
|
throw new Error(`Missing required account '${param.name}' for function '${this.functionDef.name}'`);
|
|
173
223
|
}
|
|
174
224
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { FiveProgram } from './FiveProgram.js';
|
|
2
|
+
export interface SessionScope {
|
|
3
|
+
functions: string[];
|
|
4
|
+
ttlSlots?: number;
|
|
5
|
+
bindAccount?: string;
|
|
6
|
+
nonce?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface SessionCreateParams {
|
|
9
|
+
authority: string;
|
|
10
|
+
delegate: string;
|
|
11
|
+
targetProgram: string;
|
|
12
|
+
expiresAtSlot: number;
|
|
13
|
+
scopeHash: string;
|
|
14
|
+
bindAccount?: string;
|
|
15
|
+
nonce?: string;
|
|
16
|
+
payer?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface SessionRecord {
|
|
19
|
+
sessionAddress: string;
|
|
20
|
+
authority: string;
|
|
21
|
+
delegate: string;
|
|
22
|
+
targetProgram: string;
|
|
23
|
+
expiresAtSlot: number;
|
|
24
|
+
}
|
|
25
|
+
export interface CanonicalSessionService {
|
|
26
|
+
cluster: 'localnet' | 'devnet' | 'mainnet';
|
|
27
|
+
scriptAccount: string;
|
|
28
|
+
codeHash: string;
|
|
29
|
+
version: number;
|
|
30
|
+
status: 'active' | 'disabled';
|
|
31
|
+
}
|
|
32
|
+
export interface SessionManagerOptions {
|
|
33
|
+
identity?: CanonicalSessionService;
|
|
34
|
+
enforceCanonical?: boolean;
|
|
35
|
+
allowUnsafeOverride?: boolean;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Lightweight helper around a deployed session-manager script.
|
|
39
|
+
* Uses normal Five execute flow; no VM opcode/runtime changes required.
|
|
40
|
+
*/
|
|
41
|
+
export declare class SessionManager {
|
|
42
|
+
readonly managerProgram: FiveProgram;
|
|
43
|
+
readonly defaultTtlSlots: number;
|
|
44
|
+
readonly identity: CanonicalSessionService;
|
|
45
|
+
private readonly enforceCanonical;
|
|
46
|
+
private readonly allowUnsafeOverride;
|
|
47
|
+
constructor(managerProgram: FiveProgram, defaultTtlSlots?: number, // ~20m on Solana-like slot timings
|
|
48
|
+
options?: SessionManagerOptions);
|
|
49
|
+
static resolveCanonicalIdentity(input?: {
|
|
50
|
+
cluster?: 'localnet' | 'devnet' | 'mainnet';
|
|
51
|
+
vmProgramId?: string;
|
|
52
|
+
scriptAccount?: string;
|
|
53
|
+
codeHash?: string;
|
|
54
|
+
status?: 'active' | 'disabled';
|
|
55
|
+
version?: number;
|
|
56
|
+
}): CanonicalSessionService;
|
|
57
|
+
static scopeHashForFunctions(functions: string[]): string;
|
|
58
|
+
deriveSessionAddress(authority: string, delegate: string, targetProgram: string): Promise<string>;
|
|
59
|
+
buildCreateSessionInstruction(params: SessionCreateParams): Promise<import("../types.js").SerializedInstruction>;
|
|
60
|
+
buildRevokeSessionInstruction(authority: string, delegate: string, targetProgram: string, payer?: string): Promise<import("../types.js").SerializedInstruction>;
|
|
61
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { PublicKey } from '@solana/web3.js';
|
|
2
|
+
/**
|
|
3
|
+
* Lightweight helper around a deployed session-manager script.
|
|
4
|
+
* Uses normal Five execute flow; no VM opcode/runtime changes required.
|
|
5
|
+
*/
|
|
6
|
+
export class SessionManager {
|
|
7
|
+
constructor(managerProgram, defaultTtlSlots = 3000, // ~20m on Solana-like slot timings
|
|
8
|
+
options = {}) {
|
|
9
|
+
this.managerProgram = managerProgram;
|
|
10
|
+
this.defaultTtlSlots = defaultTtlSlots;
|
|
11
|
+
this.identity =
|
|
12
|
+
options.identity ||
|
|
13
|
+
SessionManager.resolveCanonicalIdentity({
|
|
14
|
+
vmProgramId: this.managerProgram.getFiveVMProgramId(),
|
|
15
|
+
});
|
|
16
|
+
this.allowUnsafeOverride = options.allowUnsafeOverride ?? false;
|
|
17
|
+
const strictByDefault = this.identity.cluster === 'mainnet';
|
|
18
|
+
this.enforceCanonical = options.enforceCanonical ?? strictByDefault;
|
|
19
|
+
if (this.identity.cluster === 'mainnet' &&
|
|
20
|
+
options.enforceCanonical === false &&
|
|
21
|
+
!this.allowUnsafeOverride) {
|
|
22
|
+
throw new Error('Disabling canonical session manager on mainnet requires allowUnsafeOverride');
|
|
23
|
+
}
|
|
24
|
+
if (this.enforceCanonical && this.identity.status !== 'active') {
|
|
25
|
+
throw new Error('Canonical session service is disabled for current cluster');
|
|
26
|
+
}
|
|
27
|
+
const hasPinnedScript = this.identity.scriptAccount !== '11111111111111111111111111111111';
|
|
28
|
+
if (this.enforceCanonical &&
|
|
29
|
+
hasPinnedScript &&
|
|
30
|
+
this.managerProgram.getScriptAccount() !== this.identity.scriptAccount) {
|
|
31
|
+
throw new Error('SessionManager program does not match canonical session_v1 service');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
static resolveCanonicalIdentity(input = {}) {
|
|
35
|
+
const vmProgramId = input.vmProgramId || process.env.FIVE_PROGRAM_ID;
|
|
36
|
+
if (!vmProgramId) {
|
|
37
|
+
throw new Error('SessionManager canonical identity requires vmProgramId or FIVE_PROGRAM_ID');
|
|
38
|
+
}
|
|
39
|
+
const vmProgram = new PublicKey(vmProgramId);
|
|
40
|
+
const [scriptPda] = PublicKey.findProgramAddressSync([Buffer.from('session_v1', 'utf-8')], vmProgram);
|
|
41
|
+
const cluster = input.cluster || (process.env.FIVE_VM_CLUSTER || 'localnet');
|
|
42
|
+
return {
|
|
43
|
+
cluster,
|
|
44
|
+
scriptAccount: input.scriptAccount || scriptPda.toBase58(),
|
|
45
|
+
codeHash: input.codeHash || '11111111111111111111111111111111',
|
|
46
|
+
version: input.version ?? 1,
|
|
47
|
+
status: input.status || 'active',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
static scopeHashForFunctions(functions) {
|
|
51
|
+
const sorted = [...functions].sort();
|
|
52
|
+
// Stable v1 hash seed; caller may replace with stronger domain-specific hashing if desired.
|
|
53
|
+
let acc = 0n;
|
|
54
|
+
for (const ch of sorted.join('|')) {
|
|
55
|
+
acc = (acc * 131n + BigInt(ch.charCodeAt(0))) & ((1n << 256n) - 1n);
|
|
56
|
+
}
|
|
57
|
+
return '0x' + acc.toString(16).padStart(64, '0');
|
|
58
|
+
}
|
|
59
|
+
async deriveSessionAddress(authority, delegate, targetProgram) {
|
|
60
|
+
const [pda] = await this.managerProgram.findAddress(['session', authority, delegate, targetProgram], this.managerProgram.getFiveVMProgramId());
|
|
61
|
+
return pda;
|
|
62
|
+
}
|
|
63
|
+
async buildCreateSessionInstruction(params) {
|
|
64
|
+
const sessionAddress = await this.deriveSessionAddress(params.authority, params.delegate, params.targetProgram);
|
|
65
|
+
const builder = this.managerProgram
|
|
66
|
+
.function('create_session')
|
|
67
|
+
.accounts({
|
|
68
|
+
session: sessionAddress,
|
|
69
|
+
authority: params.authority,
|
|
70
|
+
delegate: params.delegate,
|
|
71
|
+
})
|
|
72
|
+
.args({
|
|
73
|
+
target_program: params.targetProgram,
|
|
74
|
+
expires_at_slot: params.expiresAtSlot,
|
|
75
|
+
scope_hash: params.scopeHash,
|
|
76
|
+
bind_account: params.bindAccount || '11111111111111111111111111111111',
|
|
77
|
+
nonce: params.nonce || '0x00',
|
|
78
|
+
});
|
|
79
|
+
if (params.payer) {
|
|
80
|
+
builder.payer(params.payer);
|
|
81
|
+
}
|
|
82
|
+
return builder.instruction();
|
|
83
|
+
}
|
|
84
|
+
async buildRevokeSessionInstruction(authority, delegate, targetProgram, payer) {
|
|
85
|
+
const sessionAddress = await this.deriveSessionAddress(authority, delegate, targetProgram);
|
|
86
|
+
const builder = this.managerProgram
|
|
87
|
+
.function('revoke_session')
|
|
88
|
+
.accounts({
|
|
89
|
+
session: sessionAddress,
|
|
90
|
+
authority,
|
|
91
|
+
});
|
|
92
|
+
if (payer) {
|
|
93
|
+
builder.payer(payer);
|
|
94
|
+
}
|
|
95
|
+
return builder.instruction();
|
|
96
|
+
}
|
|
97
|
+
}
|
package/dist/program/index.d.ts
CHANGED
|
@@ -22,3 +22,9 @@ export { AccountResolver } from './AccountResolver.js';
|
|
|
22
22
|
export type { ResolvedSystemAccounts } from './AccountResolver.js';
|
|
23
23
|
export { TypeGenerator } from './TypeGenerator.js';
|
|
24
24
|
export type { TypeGeneratorOptions } from './TypeGenerator.js';
|
|
25
|
+
export * from './FiveProgram.js';
|
|
26
|
+
export * from './FunctionBuilder.js';
|
|
27
|
+
export * from './ProgramAccount.js';
|
|
28
|
+
export * from './TypeGenerator.js';
|
|
29
|
+
export * from './AccountResolver.js';
|
|
30
|
+
export * from './SessionManager.js';
|
package/dist/program/index.js
CHANGED
|
@@ -19,3 +19,9 @@ export { FiveProgram } from './FiveProgram.js';
|
|
|
19
19
|
export { FunctionBuilder } from './FunctionBuilder.js';
|
|
20
20
|
export { AccountResolver } from './AccountResolver.js';
|
|
21
21
|
export { TypeGenerator } from './TypeGenerator.js';
|
|
22
|
+
export * from './FiveProgram.js';
|
|
23
|
+
export * from './FunctionBuilder.js';
|
|
24
|
+
export * from './ProgramAccount.js';
|
|
25
|
+
export * from './TypeGenerator.js';
|
|
26
|
+
export * from './AccountResolver.js';
|
|
27
|
+
export * from './SessionManager.js';
|
|
@@ -18,7 +18,10 @@ export class TestDiscovery {
|
|
|
18
18
|
return testCases;
|
|
19
19
|
}
|
|
20
20
|
if (testCases && typeof testCases === 'object') {
|
|
21
|
-
return
|
|
21
|
+
return Object.entries(testCases).map(([name, value]) => ({
|
|
22
|
+
name,
|
|
23
|
+
...(typeof value === 'object' && value !== null ? value : {}),
|
|
24
|
+
}));
|
|
22
25
|
}
|
|
23
26
|
return [];
|
|
24
27
|
}
|
|
@@ -82,7 +85,7 @@ export class TestDiscovery {
|
|
|
82
85
|
}
|
|
83
86
|
for (const testCase of testCases) {
|
|
84
87
|
tests.push({
|
|
85
|
-
name: testCase.name,
|
|
88
|
+
name: testCase.name || testCase.function || testCase.id || 'unnamed_test',
|
|
86
89
|
path: file,
|
|
87
90
|
type: 'json-suite',
|
|
88
91
|
description: testCase.description,
|
|
@@ -116,7 +119,7 @@ export class TestDiscovery {
|
|
|
116
119
|
return [];
|
|
117
120
|
}
|
|
118
121
|
return testCases.map((testCase) => ({
|
|
119
|
-
name: testCase.name,
|
|
122
|
+
name: testCase.name || testCase.function || testCase.id || 'unnamed_test',
|
|
120
123
|
path: file,
|
|
121
124
|
type: 'json-suite',
|
|
122
125
|
description: testCase.description,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Five SDK usage. Provides comprehensive testing capabilities for Five VM scripts.
|
|
6
6
|
*/
|
|
7
7
|
import { readFile } from 'fs/promises';
|
|
8
|
-
import { basename } from 'path';
|
|
8
|
+
import { basename, dirname, join } from 'path';
|
|
9
9
|
import { FiveSDK } from '../FiveSDK.js';
|
|
10
10
|
import { TestDiscovery } from './TestDiscovery.js';
|
|
11
11
|
/**
|
|
@@ -203,6 +203,9 @@ export class FiveTestRunner {
|
|
|
203
203
|
const suites = [];
|
|
204
204
|
const byFile = new Map();
|
|
205
205
|
const loadedJsonSuites = new Set();
|
|
206
|
+
const jsonSuitePaths = new Set(discovered
|
|
207
|
+
.filter((test) => test.type === 'json-suite')
|
|
208
|
+
.map((test) => test.path));
|
|
206
209
|
for (const test of discovered) {
|
|
207
210
|
if (test.type === 'json-suite') {
|
|
208
211
|
if (loadedJsonSuites.has(test.path)) {
|
|
@@ -211,13 +214,30 @@ export class FiveTestRunner {
|
|
|
211
214
|
try {
|
|
212
215
|
const content = await readFile(test.path, 'utf8');
|
|
213
216
|
const data = JSON.parse(content);
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
+
const rawCases = data.tests || data.testCases || [];
|
|
218
|
+
const testCases = Array.isArray(rawCases)
|
|
219
|
+
? rawCases
|
|
220
|
+
: Object.entries(rawCases).map(([name, value]) => ({
|
|
221
|
+
name,
|
|
222
|
+
...(typeof value === 'object' && value !== null ? value : {}),
|
|
223
|
+
}));
|
|
224
|
+
const inferredSource = data.source
|
|
225
|
+
? join(dirname(test.path), data.source)
|
|
226
|
+
: test.path.replace(/\.test\.json$/i, '.test.v');
|
|
227
|
+
const defaultSource = inferredSource;
|
|
228
|
+
const normalizedCases = testCases.map((testCase, idx) => {
|
|
229
|
+
const name = testCase.name || testCase.function || testCase.id || `test_${idx}`;
|
|
230
|
+
return {
|
|
231
|
+
...testCase,
|
|
232
|
+
name,
|
|
233
|
+
source: testCase.source || defaultSource,
|
|
234
|
+
function: testCase.function || name,
|
|
235
|
+
};
|
|
236
|
+
});
|
|
217
237
|
suites.push({
|
|
218
238
|
name: data.name || basename(test.path, '.test.json'),
|
|
219
239
|
description: data.description,
|
|
220
|
-
testCases
|
|
240
|
+
testCases: normalizedCases
|
|
221
241
|
});
|
|
222
242
|
loadedJsonSuites.add(test.path);
|
|
223
243
|
}
|
|
@@ -227,6 +247,12 @@ export class FiveTestRunner {
|
|
|
227
247
|
continue;
|
|
228
248
|
}
|
|
229
249
|
if (test.type === 'v-source' && test.source) {
|
|
250
|
+
const jsonPath = test.path.endsWith('.test.v')
|
|
251
|
+
? test.path.replace(/\.test\.v$/i, '.test.json')
|
|
252
|
+
: test.path.replace(/\.v$/i, '.test.json');
|
|
253
|
+
if (jsonSuitePaths.has(jsonPath)) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
230
256
|
const cases = byFile.get(test.path) || [];
|
|
231
257
|
cases.push({
|
|
232
258
|
name: test.name,
|
package/dist/types.d.ts
CHANGED
package/dist/utils/abi.d.ts
CHANGED
package/dist/utils/abi.js
CHANGED
|
@@ -21,6 +21,8 @@ export function normalizeAbiFunctions(abiFunctions) {
|
|
|
21
21
|
optional: param.optional ?? false,
|
|
22
22
|
is_account: param.is_account ?? param.isAccount ?? false,
|
|
23
23
|
isAccount: param.isAccount ?? param.is_account ?? false,
|
|
24
|
+
implicit: param.implicit ?? false,
|
|
25
|
+
source: param.source ?? 'authored',
|
|
24
26
|
attributes: Array.isArray(param.attributes) ? [...param.attributes] : [],
|
|
25
27
|
}));
|
|
26
28
|
const existingParameterNames = new Set(normalizedParameters.map((param) => param.name));
|
|
@@ -39,6 +41,8 @@ export function normalizeAbiFunctions(abiFunctions) {
|
|
|
39
41
|
optional: false,
|
|
40
42
|
is_account: true,
|
|
41
43
|
isAccount: true,
|
|
44
|
+
implicit: false,
|
|
45
|
+
source: 'authored',
|
|
42
46
|
attributes,
|
|
43
47
|
};
|
|
44
48
|
})
|
package/dist/wasm/loader.js
CHANGED
|
@@ -89,17 +89,23 @@ export async function getWasmModule() {
|
|
|
89
89
|
// Universal initialization (Browser/Node) if default export is init function
|
|
90
90
|
if (mod && typeof mod.default === 'function') {
|
|
91
91
|
try {
|
|
92
|
-
await mod.default();
|
|
92
|
+
const initialized = await mod.default();
|
|
93
|
+
const normalizedInit = resolveEncoderModule(initialized);
|
|
94
|
+
if (normalizedInit) {
|
|
95
|
+
wasmModule = normalizedInit;
|
|
96
|
+
return wasmModule;
|
|
97
|
+
}
|
|
93
98
|
}
|
|
94
99
|
catch (initErr) {
|
|
95
100
|
tried.push({ path: candidate, error: initErr });
|
|
96
101
|
}
|
|
97
102
|
}
|
|
98
|
-
|
|
99
|
-
|
|
103
|
+
const normalized = resolveEncoderModule(mod);
|
|
104
|
+
if (normalized) {
|
|
105
|
+
wasmModule = normalized;
|
|
100
106
|
return wasmModule;
|
|
101
107
|
}
|
|
102
|
-
tried.push({ path: candidate, error: 'Module
|
|
108
|
+
tried.push({ path: candidate, error: 'Module missing ParameterEncoder export' });
|
|
103
109
|
}
|
|
104
110
|
catch (e) {
|
|
105
111
|
tried.push({ path: candidate, error: e });
|
|
@@ -110,3 +116,12 @@ export async function getWasmModule() {
|
|
|
110
116
|
.join('\n');
|
|
111
117
|
throw new Error(`Five VM WASM module not found or failed to load. Please ensure five-wasm is built.\nAttempts:\n${attempted}`);
|
|
112
118
|
}
|
|
119
|
+
const resolveEncoderModule = (mod) => {
|
|
120
|
+
if (!mod)
|
|
121
|
+
return null;
|
|
122
|
+
if (mod.ParameterEncoder)
|
|
123
|
+
return mod;
|
|
124
|
+
if (mod.default && mod.default.ParameterEncoder)
|
|
125
|
+
return mod.default;
|
|
126
|
+
return null;
|
|
127
|
+
};
|