@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,342 @@
|
|
|
1
|
+
import { executeOnSolana } from "./execute.js";
|
|
2
|
+
import { fetchMultipleAccountsAndDeserialize } from "./accounts.js";
|
|
3
|
+
import { PDAUtils } from "../crypto/index.js";
|
|
4
|
+
import { ProgramIdResolver } from "../config/ProgramIdResolver.js";
|
|
5
|
+
export async function executeWithStateDiff(scriptAccount, connection, signerKeypair, functionName, parameters = [], options = {}) {
|
|
6
|
+
const logs = [];
|
|
7
|
+
try {
|
|
8
|
+
if (options.debug) {
|
|
9
|
+
console.log(`[FiveSDK] Starting execution with state diff tracking`);
|
|
10
|
+
console.log(` Script Account: ${scriptAccount}`);
|
|
11
|
+
console.log(` Function: ${functionName}`);
|
|
12
|
+
console.log(` Parameters: ${JSON.stringify(parameters)}`);
|
|
13
|
+
console.log(` Track Global Fields: ${options.trackGlobalFields}`);
|
|
14
|
+
}
|
|
15
|
+
const accountsToTrack = [scriptAccount];
|
|
16
|
+
if (options.includeVMState) {
|
|
17
|
+
const programId = ProgramIdResolver.resolve(options.fiveVMProgramId);
|
|
18
|
+
const vmStatePDAResult = await PDAUtils.deriveVMStatePDA(programId);
|
|
19
|
+
const vmStatePDA = vmStatePDAResult.address;
|
|
20
|
+
accountsToTrack.push(vmStatePDA);
|
|
21
|
+
if (options.debug) {
|
|
22
|
+
console.log(` Added VM State PDA to tracking: ${vmStatePDA}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (options.additionalAccounts) {
|
|
26
|
+
accountsToTrack.push(...options.additionalAccounts);
|
|
27
|
+
if (options.debug) {
|
|
28
|
+
console.log(` Added ${options.additionalAccounts.length} additional accounts to tracking`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
logs.push(`Tracking ${accountsToTrack.length} accounts for state changes`);
|
|
32
|
+
if (options.debug) {
|
|
33
|
+
console.log(`[FiveSDK] Fetching BEFORE state for ${accountsToTrack.length} accounts...`);
|
|
34
|
+
}
|
|
35
|
+
const beforeState = await fetchMultipleAccountsAndDeserialize(accountsToTrack, connection, {
|
|
36
|
+
debug: false,
|
|
37
|
+
parseMetadata: true,
|
|
38
|
+
validateEncoding: false,
|
|
39
|
+
});
|
|
40
|
+
let successfulBeforeFetches = 0;
|
|
41
|
+
for (const [address, result] of beforeState.entries()) {
|
|
42
|
+
if (result.success) {
|
|
43
|
+
successfulBeforeFetches++;
|
|
44
|
+
}
|
|
45
|
+
else if (options.debug) {
|
|
46
|
+
console.warn(`[FiveSDK] Warning: Failed to fetch BEFORE state for ${address}: ${result.error}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
logs.push(`BEFORE state: ${successfulBeforeFetches}/${accountsToTrack.length} accounts fetched`);
|
|
50
|
+
let beforeGlobalFields = {};
|
|
51
|
+
if (options.trackGlobalFields) {
|
|
52
|
+
const scriptBefore = beforeState.get(scriptAccount);
|
|
53
|
+
if (scriptBefore?.success && scriptBefore.scriptMetadata) {
|
|
54
|
+
beforeGlobalFields = extractGlobalFields(scriptBefore.scriptMetadata, "before");
|
|
55
|
+
if (options.debug) {
|
|
56
|
+
console.log(`[FiveSDK] Extracted ${Object.keys(beforeGlobalFields).length} global fields from BEFORE state`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (options.debug) {
|
|
61
|
+
console.log(`[FiveSDK] Executing script...`);
|
|
62
|
+
}
|
|
63
|
+
const executionResult = await executeOnSolana(scriptAccount, connection, signerKeypair, functionName, parameters, options.additionalAccounts || [], {
|
|
64
|
+
debug: options.debug,
|
|
65
|
+
network: options.network,
|
|
66
|
+
computeUnitLimit: options.computeUnitLimit,
|
|
67
|
+
});
|
|
68
|
+
if (!executionResult.success) {
|
|
69
|
+
logs.push(`Execution failed: ${executionResult.error}`);
|
|
70
|
+
return {
|
|
71
|
+
success: false,
|
|
72
|
+
error: `Script execution failed: ${executionResult.error}`,
|
|
73
|
+
logs,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
logs.push(`Execution successful: ${executionResult.transactionId}`);
|
|
77
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
78
|
+
if (options.debug) {
|
|
79
|
+
console.log(`[FiveSDK] Fetching AFTER state...`);
|
|
80
|
+
}
|
|
81
|
+
const afterState = await fetchMultipleAccountsAndDeserialize(accountsToTrack, connection, {
|
|
82
|
+
debug: false,
|
|
83
|
+
parseMetadata: true,
|
|
84
|
+
validateEncoding: false,
|
|
85
|
+
});
|
|
86
|
+
let successfulAfterFetches = 0;
|
|
87
|
+
for (const [address, result] of afterState.entries()) {
|
|
88
|
+
if (result.success) {
|
|
89
|
+
successfulAfterFetches++;
|
|
90
|
+
}
|
|
91
|
+
else if (options.debug) {
|
|
92
|
+
console.warn(`[FiveSDK] Warning: Failed to fetch AFTER state for ${address}: ${result.error}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
logs.push(`AFTER state: ${successfulAfterFetches}/${accountsToTrack.length} accounts fetched`);
|
|
96
|
+
let afterGlobalFields = {};
|
|
97
|
+
if (options.trackGlobalFields) {
|
|
98
|
+
const scriptAfter = afterState.get(scriptAccount);
|
|
99
|
+
if (scriptAfter?.success && scriptAfter.scriptMetadata) {
|
|
100
|
+
afterGlobalFields = extractGlobalFields(scriptAfter.scriptMetadata, "after");
|
|
101
|
+
if (options.debug) {
|
|
102
|
+
console.log(`[FiveSDK] Extracted ${Object.keys(afterGlobalFields).length} global fields from AFTER state`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (options.debug) {
|
|
107
|
+
console.log(`[FiveSDK] Computing state differences...`);
|
|
108
|
+
}
|
|
109
|
+
const changes = computeStateDifferences(beforeState, afterState, options.debug);
|
|
110
|
+
let globalFieldChanges = [];
|
|
111
|
+
if (options.trackGlobalFields) {
|
|
112
|
+
globalFieldChanges = computeGlobalFieldChanges(beforeGlobalFields, afterGlobalFields);
|
|
113
|
+
if (options.debug) {
|
|
114
|
+
console.log(`[FiveSDK] Found ${globalFieldChanges.length} global field changes`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
logs.push(`State analysis: ${changes.length} account changes, ${globalFieldChanges.length} global field changes`);
|
|
118
|
+
return {
|
|
119
|
+
success: true,
|
|
120
|
+
execution: {
|
|
121
|
+
transactionId: executionResult.transactionId,
|
|
122
|
+
result: executionResult.result,
|
|
123
|
+
computeUnitsUsed: executionResult.computeUnitsUsed,
|
|
124
|
+
logs: executionResult.logs,
|
|
125
|
+
},
|
|
126
|
+
stateDiff: {
|
|
127
|
+
beforeState,
|
|
128
|
+
afterState,
|
|
129
|
+
changes,
|
|
130
|
+
globalFieldChanges,
|
|
131
|
+
},
|
|
132
|
+
logs,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown state tracking error";
|
|
137
|
+
if (options.debug) {
|
|
138
|
+
console.error(`[FiveSDK] State diff execution failed: ${errorMessage}`);
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
success: false,
|
|
142
|
+
error: errorMessage,
|
|
143
|
+
logs,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function computeStateDifferences(beforeState, afterState, debug = false) {
|
|
148
|
+
const changes = [];
|
|
149
|
+
const allAccounts = new Set([...beforeState.keys(), ...afterState.keys()]);
|
|
150
|
+
for (const account of allAccounts) {
|
|
151
|
+
const before = beforeState.get(account);
|
|
152
|
+
const after = afterState.get(account);
|
|
153
|
+
if (debug) {
|
|
154
|
+
console.log(`[FiveSDK] Analyzing account ${account.substring(0, 8)}...`);
|
|
155
|
+
}
|
|
156
|
+
if (!before?.success && after?.success) {
|
|
157
|
+
changes.push({
|
|
158
|
+
account,
|
|
159
|
+
oldValue: null,
|
|
160
|
+
newValue: {
|
|
161
|
+
lamports: after.accountInfo?.lamports,
|
|
162
|
+
dataLength: after.accountInfo?.dataLength,
|
|
163
|
+
owner: after.accountInfo?.owner,
|
|
164
|
+
},
|
|
165
|
+
changeType: "created",
|
|
166
|
+
});
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (before?.success && !after?.success) {
|
|
170
|
+
changes.push({
|
|
171
|
+
account,
|
|
172
|
+
oldValue: {
|
|
173
|
+
lamports: before.accountInfo?.lamports,
|
|
174
|
+
dataLength: before.accountInfo?.dataLength,
|
|
175
|
+
owner: before.accountInfo?.owner,
|
|
176
|
+
},
|
|
177
|
+
newValue: null,
|
|
178
|
+
changeType: "deleted",
|
|
179
|
+
});
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (before?.success && after?.success) {
|
|
183
|
+
if (before.accountInfo?.lamports !== after.accountInfo?.lamports) {
|
|
184
|
+
changes.push({
|
|
185
|
+
account,
|
|
186
|
+
fieldName: "lamports",
|
|
187
|
+
oldValue: before.accountInfo?.lamports,
|
|
188
|
+
newValue: after.accountInfo?.lamports,
|
|
189
|
+
changeType: "modified",
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
if (before.accountInfo?.dataLength !== after.accountInfo?.dataLength) {
|
|
193
|
+
changes.push({
|
|
194
|
+
account,
|
|
195
|
+
fieldName: "dataLength",
|
|
196
|
+
oldValue: before.accountInfo?.dataLength,
|
|
197
|
+
newValue: after.accountInfo?.dataLength,
|
|
198
|
+
changeType: "modified",
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
if (before.rawBytecode && after.rawBytecode) {
|
|
202
|
+
if (!bytecodeEqual(before.rawBytecode, after.rawBytecode)) {
|
|
203
|
+
changes.push({
|
|
204
|
+
account,
|
|
205
|
+
fieldName: "bytecode",
|
|
206
|
+
oldValue: `${before.rawBytecode.length} bytes (hash: ${hashBytecode(before.rawBytecode)})`,
|
|
207
|
+
newValue: `${after.rawBytecode.length} bytes (hash: ${hashBytecode(after.rawBytecode)})`,
|
|
208
|
+
changeType: "modified",
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (before.scriptMetadata && after.scriptMetadata) {
|
|
213
|
+
compareScriptMetadata(before.scriptMetadata, after.scriptMetadata, account, changes);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (debug) {
|
|
218
|
+
console.log(`[FiveSDK] Found ${changes.length} total state changes`);
|
|
219
|
+
}
|
|
220
|
+
return changes;
|
|
221
|
+
}
|
|
222
|
+
function extractGlobalFields(scriptMetadata, phase) {
|
|
223
|
+
const globalFields = {};
|
|
224
|
+
try {
|
|
225
|
+
if (scriptMetadata.bytecode && scriptMetadata.bytecode.length > 6) {
|
|
226
|
+
const stateSection = extractStateSection(scriptMetadata.bytecode);
|
|
227
|
+
if (stateSection) {
|
|
228
|
+
Object.assign(globalFields, stateSection);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
console.warn(`[FiveSDK] Failed to extract global fields (${phase}):`, error);
|
|
234
|
+
}
|
|
235
|
+
return globalFields;
|
|
236
|
+
}
|
|
237
|
+
function computeGlobalFieldChanges(beforeFields, afterFields) {
|
|
238
|
+
const changes = [];
|
|
239
|
+
const allFields = new Set([
|
|
240
|
+
...Object.keys(beforeFields),
|
|
241
|
+
...Object.keys(afterFields),
|
|
242
|
+
]);
|
|
243
|
+
for (const fieldName of allFields) {
|
|
244
|
+
const oldValue = beforeFields[fieldName];
|
|
245
|
+
const newValue = afterFields[fieldName];
|
|
246
|
+
if (!deepEqual(oldValue, newValue)) {
|
|
247
|
+
changes.push({
|
|
248
|
+
fieldName,
|
|
249
|
+
oldValue,
|
|
250
|
+
newValue,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return changes;
|
|
255
|
+
}
|
|
256
|
+
function compareScriptMetadata(beforeMetadata, afterMetadata, account, changes) {
|
|
257
|
+
if (beforeMetadata.abi.functions.length !== afterMetadata.abi.functions.length) {
|
|
258
|
+
changes.push({
|
|
259
|
+
account,
|
|
260
|
+
fieldName: "function_count",
|
|
261
|
+
oldValue: beforeMetadata.abi.functions.length,
|
|
262
|
+
newValue: afterMetadata.abi.functions.length,
|
|
263
|
+
changeType: "modified",
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
if (beforeMetadata.abi.name !== afterMetadata.abi.name) {
|
|
267
|
+
changes.push({
|
|
268
|
+
account,
|
|
269
|
+
fieldName: "script_name",
|
|
270
|
+
oldValue: beforeMetadata.abi.name,
|
|
271
|
+
newValue: afterMetadata.abi.name,
|
|
272
|
+
changeType: "modified",
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
if (beforeMetadata.authority !== afterMetadata.authority) {
|
|
276
|
+
changes.push({
|
|
277
|
+
account,
|
|
278
|
+
fieldName: "authority",
|
|
279
|
+
oldValue: beforeMetadata.authority,
|
|
280
|
+
newValue: afterMetadata.authority,
|
|
281
|
+
changeType: "modified",
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
function extractStateSection(bytecode) {
|
|
286
|
+
try {
|
|
287
|
+
if (bytecode.length < 6)
|
|
288
|
+
return null;
|
|
289
|
+
const stateMarker = new Uint8Array([0xff, 0xfe]);
|
|
290
|
+
for (let i = 6; i < bytecode.length - 1; i++) {
|
|
291
|
+
if (bytecode[i] === stateMarker[0] &&
|
|
292
|
+
bytecode[i + 1] === stateMarker[1]) {
|
|
293
|
+
const stateData = {};
|
|
294
|
+
return stateData;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
console.warn("[FiveSDK] State section extraction failed:", error);
|
|
300
|
+
}
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
function bytecodeEqual(a, b) {
|
|
304
|
+
if (a.length !== b.length)
|
|
305
|
+
return false;
|
|
306
|
+
for (let i = 0; i < a.length; i++) {
|
|
307
|
+
if (a[i] !== b[i])
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
function hashBytecode(bytecode) {
|
|
313
|
+
let hash = 0;
|
|
314
|
+
for (let i = 0; i < bytecode.length; i++) {
|
|
315
|
+
hash = ((hash << 5) - hash + bytecode[i]) & 0xffffffff;
|
|
316
|
+
}
|
|
317
|
+
return hash.toString(16);
|
|
318
|
+
}
|
|
319
|
+
function deepEqual(a, b) {
|
|
320
|
+
if (a === b)
|
|
321
|
+
return true;
|
|
322
|
+
if (a == null || b == null)
|
|
323
|
+
return false;
|
|
324
|
+
if (typeof a !== typeof b)
|
|
325
|
+
return false;
|
|
326
|
+
if (typeof a === "object") {
|
|
327
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
328
|
+
return false;
|
|
329
|
+
const keysA = Object.keys(a);
|
|
330
|
+
const keysB = Object.keys(b);
|
|
331
|
+
if (keysA.length !== keysB.length)
|
|
332
|
+
return false;
|
|
333
|
+
for (const key of keysA) {
|
|
334
|
+
if (!keysB.includes(key))
|
|
335
|
+
return false;
|
|
336
|
+
if (!deepEqual(a[key], b[key]))
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { PDAUtils, Base58Utils } from "../crypto/index.js";
|
|
2
|
+
import { ProgramIdResolver } from "../config/ProgramIdResolver.js";
|
|
3
|
+
export async function getVMState(connection, fiveVMProgramId) {
|
|
4
|
+
const programId = ProgramIdResolver.resolve(fiveVMProgramId);
|
|
5
|
+
const vmStatePDA = await PDAUtils.deriveVMStatePDA(programId);
|
|
6
|
+
let accountData;
|
|
7
|
+
try {
|
|
8
|
+
if (typeof connection.getAccountInfo === 'function') {
|
|
9
|
+
let pubkey = vmStatePDA.address;
|
|
10
|
+
try {
|
|
11
|
+
const { PublicKey } = await import("@solana/web3.js");
|
|
12
|
+
pubkey = new PublicKey(vmStatePDA.address);
|
|
13
|
+
}
|
|
14
|
+
catch { }
|
|
15
|
+
const info = await connection.getAccountInfo(pubkey);
|
|
16
|
+
if (!info)
|
|
17
|
+
throw new Error("VM State account not found");
|
|
18
|
+
accountData = new Uint8Array(info.data);
|
|
19
|
+
}
|
|
20
|
+
else if (typeof connection.getAccountData === 'function') {
|
|
21
|
+
const info = await connection.getAccountData(vmStatePDA.address);
|
|
22
|
+
if (!info)
|
|
23
|
+
throw new Error("VM State account not found");
|
|
24
|
+
accountData = new Uint8Array(info.data);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
throw new Error("Invalid connection object: must support getAccountInfo or getAccountData");
|
|
28
|
+
}
|
|
29
|
+
if (accountData.length < 56)
|
|
30
|
+
throw new Error(`VM State account data too small: expected 56, got ${accountData.length}`);
|
|
31
|
+
const authority = Base58Utils.encode(accountData.slice(0, 32));
|
|
32
|
+
const view = new DataView(accountData.buffer, accountData.byteOffset, accountData.byteLength);
|
|
33
|
+
return {
|
|
34
|
+
authority,
|
|
35
|
+
scriptCount: Number(view.getBigUint64(32, true)),
|
|
36
|
+
deployFeeBps: view.getUint32(40, true),
|
|
37
|
+
executeFeeBps: view.getUint32(44, true),
|
|
38
|
+
isInitialized: accountData[48] === 1
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
throw new Error(`Failed to fetch VM state: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AccountResolver - Automatic system account injection
|
|
3
|
+
*
|
|
4
|
+
* Intelligently adds system accounts based on function constraints:
|
|
5
|
+
* - @init constraint → adds SystemProgram
|
|
6
|
+
* - Other constraints may add Rent, Clock, etc. (future)
|
|
7
|
+
*
|
|
8
|
+
* This reduces boilerplate by eliminating the need for developers to
|
|
9
|
+
* manually pass system accounts for every function call.
|
|
10
|
+
*/
|
|
11
|
+
import type { FunctionDefinition, ParameterDefinition, ScriptABI } from '../metadata/index.js';
|
|
12
|
+
import type { FiveProgramOptions } from './FiveProgram.js';
|
|
13
|
+
import type { FiveProgram } from './FiveProgram.js';
|
|
14
|
+
export interface ResolvedSystemAccounts {
|
|
15
|
+
[accountName: string]: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* AccountResolver handles automatic injection of system accounts
|
|
19
|
+
*/
|
|
20
|
+
export declare class AccountResolver {
|
|
21
|
+
private options;
|
|
22
|
+
constructor(options: FiveProgramOptions);
|
|
23
|
+
/**
|
|
24
|
+
* Resolve system accounts that should be auto-injected
|
|
25
|
+
* Detects @init constraints and adds SystemProgram if needed
|
|
26
|
+
*
|
|
27
|
+
* @param funcDef - Function definition from ABI
|
|
28
|
+
* @param providedAccounts - Accounts already provided by user
|
|
29
|
+
* @returns Map of system account names to their addresses
|
|
30
|
+
*/
|
|
31
|
+
resolveSystemAccounts(funcDef: FunctionDefinition, providedAccounts: Map<string, string>): ResolvedSystemAccounts;
|
|
32
|
+
/**
|
|
33
|
+
* Resolve PDA accounts based on ABI constraints
|
|
34
|
+
*
|
|
35
|
+
* @param abi - Script ABI containing account constraints
|
|
36
|
+
* @param providedAccounts - Currently known accounts (user provided + system)
|
|
37
|
+
* @param program - FiveProgram instance for derivation util
|
|
38
|
+
*/
|
|
39
|
+
resolvePdaAccounts(abi: ScriptABI, providedAccounts: Map<string, string>, program: FiveProgram): Promise<ResolvedSystemAccounts>;
|
|
40
|
+
/**
|
|
41
|
+
* Check if function has @init constraint on any parameter
|
|
42
|
+
* @init means account creation via CPI
|
|
43
|
+
*
|
|
44
|
+
* @param funcDef - Function definition
|
|
45
|
+
* @returns true if any parameter has @init attribute
|
|
46
|
+
*/
|
|
47
|
+
private hasInitConstraint;
|
|
48
|
+
/**
|
|
49
|
+
* Get account metadata from parameter attributes
|
|
50
|
+
* Maps Five DSL attributes to Solana account properties
|
|
51
|
+
*
|
|
52
|
+
* @param param - Parameter definition
|
|
53
|
+
* @returns Object with isSigner and isWritable flags
|
|
54
|
+
*/
|
|
55
|
+
getAccountMetadata(param: ParameterDefinition): {
|
|
56
|
+
isSigner: boolean;
|
|
57
|
+
isWritable: boolean;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Validate that all required accounts are provided after resolution
|
|
61
|
+
*
|
|
62
|
+
* @param funcDef - Function definition
|
|
63
|
+
* @param allAccounts - All accounts (user-provided + system-injected)
|
|
64
|
+
* @throws Error if required account is missing
|
|
65
|
+
*/
|
|
66
|
+
validateResolvedAccounts(funcDef: FunctionDefinition, allAccounts: Map<string, string>): void;
|
|
67
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AccountResolver - Automatic system account injection
|
|
3
|
+
*
|
|
4
|
+
* Intelligently adds system accounts based on function constraints:
|
|
5
|
+
* - @init constraint → adds SystemProgram
|
|
6
|
+
* - Other constraints may add Rent, Clock, etc. (future)
|
|
7
|
+
*
|
|
8
|
+
* This reduces boilerplate by eliminating the need for developers to
|
|
9
|
+
* manually pass system accounts for every function call.
|
|
10
|
+
*/
|
|
11
|
+
// Known system program IDs (Solana standard)
|
|
12
|
+
const SYSTEM_PROGRAM_ID = '11111111111111111111111111111111';
|
|
13
|
+
/**
|
|
14
|
+
* AccountResolver handles automatic injection of system accounts
|
|
15
|
+
*/
|
|
16
|
+
export class AccountResolver {
|
|
17
|
+
constructor(options) {
|
|
18
|
+
this.options = options;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Resolve system accounts that should be auto-injected
|
|
22
|
+
* Detects @init constraints and adds SystemProgram if needed
|
|
23
|
+
*
|
|
24
|
+
* @param funcDef - Function definition from ABI
|
|
25
|
+
* @param providedAccounts - Accounts already provided by user
|
|
26
|
+
* @returns Map of system account names to their addresses
|
|
27
|
+
*/
|
|
28
|
+
resolveSystemAccounts(funcDef, providedAccounts) {
|
|
29
|
+
const systemAccounts = {};
|
|
30
|
+
// Check if function has @init constraint
|
|
31
|
+
const hasInit = this.hasInitConstraint(funcDef);
|
|
32
|
+
if (hasInit && !providedAccounts.has('systemProgram')) {
|
|
33
|
+
systemAccounts['systemProgram'] = SYSTEM_PROGRAM_ID;
|
|
34
|
+
if (this.options.debug) {
|
|
35
|
+
console.log(`[AccountResolver] Auto-injecting SystemProgram for @init constraint`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return systemAccounts;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Resolve PDA accounts based on ABI constraints
|
|
42
|
+
*
|
|
43
|
+
* @param abi - Script ABI containing account constraints
|
|
44
|
+
* @param providedAccounts - Currently known accounts (user provided + system)
|
|
45
|
+
* @param program - FiveProgram instance for derivation util
|
|
46
|
+
*/
|
|
47
|
+
async resolvePdaAccounts(abi, providedAccounts, program) {
|
|
48
|
+
const pdaAccounts = {};
|
|
49
|
+
if (!abi.accounts)
|
|
50
|
+
return pdaAccounts;
|
|
51
|
+
// Filter for PDA constraints
|
|
52
|
+
const pdaConstraints = abi.accounts.filter(acc => acc.type === 'pda');
|
|
53
|
+
for (const constraint of pdaConstraints) {
|
|
54
|
+
if (providedAccounts.has(constraint.name)) {
|
|
55
|
+
continue; // User already provided it
|
|
56
|
+
}
|
|
57
|
+
// Resolve seeds
|
|
58
|
+
const seeds = [];
|
|
59
|
+
let canResolve = true;
|
|
60
|
+
for (const seed of (constraint.seeds || [])) {
|
|
61
|
+
if (seed.startsWith('"') && seed.endsWith('"')) {
|
|
62
|
+
// Static string seed: "my_seed"
|
|
63
|
+
seeds.push(seed.slice(1, -1));
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// Dynamic seed: account reference
|
|
67
|
+
const refAddr = providedAccounts.get(seed);
|
|
68
|
+
if (refAddr) {
|
|
69
|
+
seeds.push(refAddr);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// Dependency missing; single-pass resolution only.
|
|
73
|
+
canResolve = false;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (canResolve && seeds.length > 0) {
|
|
79
|
+
try {
|
|
80
|
+
const [pda] = await program.findAddress(seeds);
|
|
81
|
+
pdaAccounts[constraint.name] = pda;
|
|
82
|
+
if (this.options.debug) {
|
|
83
|
+
console.log(`[AccountResolver] Auto-derived PDA '${constraint.name}': ${pda}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
console.warn(`[AccountResolver] Failed to derive PDA '${constraint.name}':`, e);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return pdaAccounts;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Check if function has @init constraint on any parameter
|
|
95
|
+
* @init means account creation via CPI
|
|
96
|
+
*
|
|
97
|
+
* @param funcDef - Function definition
|
|
98
|
+
* @returns true if any parameter has @init attribute
|
|
99
|
+
*/
|
|
100
|
+
hasInitConstraint(funcDef) {
|
|
101
|
+
return funcDef.parameters.some((param) => {
|
|
102
|
+
const attributes = param.attributes || [];
|
|
103
|
+
return attributes.includes('init');
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get account metadata from parameter attributes
|
|
108
|
+
* Maps Five DSL attributes to Solana account properties
|
|
109
|
+
*
|
|
110
|
+
* @param param - Parameter definition
|
|
111
|
+
* @returns Object with isSigner and isWritable flags
|
|
112
|
+
*/
|
|
113
|
+
getAccountMetadata(param) {
|
|
114
|
+
const attributes = param.attributes || [];
|
|
115
|
+
return {
|
|
116
|
+
isSigner: attributes.includes('signer'),
|
|
117
|
+
isWritable: attributes.includes('mut') || attributes.includes('init'),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Validate that all required accounts are provided after resolution
|
|
122
|
+
*
|
|
123
|
+
* @param funcDef - Function definition
|
|
124
|
+
* @param allAccounts - All accounts (user-provided + system-injected)
|
|
125
|
+
* @throws Error if required account is missing
|
|
126
|
+
*/
|
|
127
|
+
validateResolvedAccounts(funcDef, allAccounts) {
|
|
128
|
+
for (const param of funcDef.parameters) {
|
|
129
|
+
if (param.is_account && !allAccounts.has(param.name)) {
|
|
130
|
+
throw new Error(`Required account '${param.name}' not provided and not auto-injected`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export class BorshSchemaGenerator {
|
|
2
|
+
constructor(abi) {
|
|
3
|
+
this.abi = abi;
|
|
4
|
+
}
|
|
5
|
+
generate(typeDef) {
|
|
6
|
+
const map = new Map();
|
|
7
|
+
this.processType(typeDef, map);
|
|
8
|
+
return map;
|
|
9
|
+
}
|
|
10
|
+
processType(typeDef, map) {
|
|
11
|
+
// Dynamic class creation for the schema
|
|
12
|
+
const StructClass = function () { };
|
|
13
|
+
Object.defineProperty(StructClass, 'name', { value: typeDef.name });
|
|
14
|
+
if (typeDef.structure === 'struct') {
|
|
15
|
+
const fields = typeDef.fields?.map(field => {
|
|
16
|
+
return [field.name, this.mapType(field.type)];
|
|
17
|
+
});
|
|
18
|
+
map.set(StructClass, { kind: 'struct', fields });
|
|
19
|
+
}
|
|
20
|
+
else if (typeDef.structure === 'enum') {
|
|
21
|
+
// Enum handling
|
|
22
|
+
const values = typeDef.variants?.map(v => {
|
|
23
|
+
// Safe cast or optional access as v matches structure
|
|
24
|
+
// Based on metadata definition, variants have { name, value? } but not explicit 'fields' in interface?
|
|
25
|
+
// We will map simple scalar enums
|
|
26
|
+
return [v.name, undefined];
|
|
27
|
+
});
|
|
28
|
+
map.set(StructClass, { kind: 'enum', values });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
mapType(type) {
|
|
32
|
+
switch (type) {
|
|
33
|
+
case 'u8': return 'u8';
|
|
34
|
+
case 'u16': return 'u16';
|
|
35
|
+
case 'u32': return 'u32';
|
|
36
|
+
case 'u64': return 'u64';
|
|
37
|
+
case 'u128': return 'u128';
|
|
38
|
+
case 'i8': return 'i8';
|
|
39
|
+
case 'i16': return 'i16';
|
|
40
|
+
case 'i32': return 'i32';
|
|
41
|
+
case 'i64': return 'i64';
|
|
42
|
+
case 'i128': return 'i128';
|
|
43
|
+
case 'bool': return 'u8'; // Borsh standard for bool
|
|
44
|
+
case 'string': return 'string';
|
|
45
|
+
case 'pubkey': return [32]; // Fixed array of 32 bytes
|
|
46
|
+
default:
|
|
47
|
+
// Handle vectors: Vec<T>
|
|
48
|
+
if (type.startsWith('Vec<')) {
|
|
49
|
+
const inner = type.slice(4, -1);
|
|
50
|
+
return [this.mapType(inner)];
|
|
51
|
+
}
|
|
52
|
+
// Handle arrays: [T; N]
|
|
53
|
+
// Handle option: Option<T>
|
|
54
|
+
return type; // Assume it's a known struct name
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|