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