@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,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple logging that can be silenced in production
|
|
3
|
+
*/
|
|
4
|
+
const isProduction = (typeof process !== 'undefined' && process?.env?.NODE_ENV === 'production');
|
|
5
|
+
export const log = {
|
|
6
|
+
debug: (message, ...args) => !isProduction && console.debug(`[FiveSDK] ${message}`, ...args),
|
|
7
|
+
info: (message, ...args) => !isProduction && console.info(`[FiveSDK] ${message}`, ...args),
|
|
8
|
+
warn: (message, ...args) => console.warn(`[FiveSDK] ${message}`, ...args),
|
|
9
|
+
error: (message, ...args) => console.error(`[FiveSDK] ${message}`, ...args)
|
|
10
|
+
};
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Five SDK Script Metadata System
|
|
3
|
+
*
|
|
4
|
+
* Real implementation for parsing script account data and extracting ABI information
|
|
5
|
+
* from deployed Five scripts. This replaces mock implementations with production-ready
|
|
6
|
+
* Solana account data parsing.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Account data interface for client-agnostic blockchain interactions
|
|
10
|
+
*/
|
|
11
|
+
export interface AccountData {
|
|
12
|
+
/** Account address as base58 string */
|
|
13
|
+
address: string;
|
|
14
|
+
/** Account data as byte array */
|
|
15
|
+
data: Uint8Array;
|
|
16
|
+
/** Account owner program ID */
|
|
17
|
+
owner: string;
|
|
18
|
+
/** Account balance in lamports */
|
|
19
|
+
lamports: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Account fetcher interface for retrieving account data
|
|
23
|
+
*/
|
|
24
|
+
export interface AccountFetcher {
|
|
25
|
+
/** Get single account data */
|
|
26
|
+
getAccountData(address: string): Promise<AccountData | null>;
|
|
27
|
+
/** Get multiple account data in batch */
|
|
28
|
+
getMultipleAccountsData(addresses: string[]): Promise<Map<string, AccountData | null>>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Script metadata extracted from deployed script accounts
|
|
32
|
+
*/
|
|
33
|
+
export interface ScriptMetadata {
|
|
34
|
+
/** Script account address */
|
|
35
|
+
address: string;
|
|
36
|
+
/** Script bytecode */
|
|
37
|
+
bytecode: Uint8Array;
|
|
38
|
+
/** Script ABI with function definitions */
|
|
39
|
+
abi: ScriptABI;
|
|
40
|
+
/** Deploy timestamp */
|
|
41
|
+
deployedAt: number;
|
|
42
|
+
/** Script version */
|
|
43
|
+
version: string;
|
|
44
|
+
/** Authority that deployed the script */
|
|
45
|
+
authority: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Script ABI definition
|
|
49
|
+
*/
|
|
50
|
+
export interface ScriptABI {
|
|
51
|
+
/** Script name */
|
|
52
|
+
name: string;
|
|
53
|
+
/** Function definitions */
|
|
54
|
+
functions: FunctionDefinition[];
|
|
55
|
+
/** Type definitions */
|
|
56
|
+
types?: TypeDefinition[];
|
|
57
|
+
/** Account constraints */
|
|
58
|
+
accounts?: AccountConstraint[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Function definition in script ABI
|
|
62
|
+
*/
|
|
63
|
+
export interface FunctionDefinition {
|
|
64
|
+
/** Function name */
|
|
65
|
+
name: string;
|
|
66
|
+
/** Function index in bytecode */
|
|
67
|
+
index: number;
|
|
68
|
+
/** Function parameters */
|
|
69
|
+
parameters: ParameterDefinition[];
|
|
70
|
+
/** Return type */
|
|
71
|
+
returnType?: string;
|
|
72
|
+
/** Return type (snake_case from compiled ABI) */
|
|
73
|
+
return_type?: string | null;
|
|
74
|
+
/** Function visibility */
|
|
75
|
+
visibility: 'public' | 'private';
|
|
76
|
+
/** Function visibility (snake_case from compiled ABI) */
|
|
77
|
+
is_public?: boolean;
|
|
78
|
+
/** Documentation */
|
|
79
|
+
docs?: string;
|
|
80
|
+
/** Bytecode offset (from compiled ABI) */
|
|
81
|
+
bytecode_offset?: number;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Parameter definition
|
|
85
|
+
*/
|
|
86
|
+
export interface ParameterDefinition {
|
|
87
|
+
/** Parameter name */
|
|
88
|
+
name: string;
|
|
89
|
+
/** Parameter type */
|
|
90
|
+
type: string;
|
|
91
|
+
/** Parameter type (snake_case from compiled ABI) */
|
|
92
|
+
param_type?: string;
|
|
93
|
+
/** Whether this is an account parameter */
|
|
94
|
+
is_account?: boolean;
|
|
95
|
+
/** Account attributes (e.g., "mut", "signer", "init") */
|
|
96
|
+
attributes?: string[];
|
|
97
|
+
/** Whether parameter is optional */
|
|
98
|
+
optional?: boolean;
|
|
99
|
+
/** Parameter documentation */
|
|
100
|
+
docs?: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Type definition
|
|
104
|
+
*/
|
|
105
|
+
export interface TypeDefinition {
|
|
106
|
+
/** Type name */
|
|
107
|
+
name: string;
|
|
108
|
+
/** Type structure */
|
|
109
|
+
structure: 'struct' | 'enum' | 'alias';
|
|
110
|
+
/** Type fields (for structs) */
|
|
111
|
+
fields?: Array<{
|
|
112
|
+
name: string;
|
|
113
|
+
type: string;
|
|
114
|
+
}>;
|
|
115
|
+
/** Type variants (for enums) */
|
|
116
|
+
variants?: Array<{
|
|
117
|
+
name: string;
|
|
118
|
+
value?: number;
|
|
119
|
+
}>;
|
|
120
|
+
/** Alias target (for type aliases) */
|
|
121
|
+
target?: string;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Account constraint definition
|
|
125
|
+
*/
|
|
126
|
+
export interface AccountConstraint {
|
|
127
|
+
/** Account name */
|
|
128
|
+
name: string;
|
|
129
|
+
/** Account type */
|
|
130
|
+
type: 'signer' | 'writable' | 'readonly' | 'pda';
|
|
131
|
+
/** Seeds for PDA derivation */
|
|
132
|
+
seeds?: string[];
|
|
133
|
+
/** Required account properties */
|
|
134
|
+
properties?: Record<string, any>;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Script metadata parser and manager
|
|
138
|
+
*/
|
|
139
|
+
export declare class ScriptMetadataParser {
|
|
140
|
+
private static readonly SCRIPT_MAGIC;
|
|
141
|
+
private static readonly CURRENT_VERSION;
|
|
142
|
+
private static readonly HEADER_SIZE;
|
|
143
|
+
/**
|
|
144
|
+
* Parse script metadata from account data
|
|
145
|
+
*/
|
|
146
|
+
static parseMetadata(accountData: Uint8Array, address: string): ScriptMetadata;
|
|
147
|
+
/**
|
|
148
|
+
* Get script metadata from blockchain using account fetcher
|
|
149
|
+
*/
|
|
150
|
+
static getScriptMetadata(accountFetcher: AccountFetcher, scriptAddress: string): Promise<ScriptMetadata>;
|
|
151
|
+
/**
|
|
152
|
+
* Get multiple script metadata entries using account fetcher
|
|
153
|
+
*/
|
|
154
|
+
static getMultipleScriptMetadata(accountFetcher: AccountFetcher, scriptAddresses: string[]): Promise<Map<string, ScriptMetadata | null>>;
|
|
155
|
+
/**
|
|
156
|
+
* Extract function signatures from ABI
|
|
157
|
+
*/
|
|
158
|
+
static extractFunctionSignatures(abi: ScriptABI): Array<{
|
|
159
|
+
name: string;
|
|
160
|
+
index: number;
|
|
161
|
+
parameters: ParameterDefinition[];
|
|
162
|
+
signature: string;
|
|
163
|
+
}>;
|
|
164
|
+
/**
|
|
165
|
+
* Generate function signature string
|
|
166
|
+
*/
|
|
167
|
+
static generateFunctionSignature(func: FunctionDefinition): string;
|
|
168
|
+
/**
|
|
169
|
+
* Validate script ABI structure
|
|
170
|
+
*/
|
|
171
|
+
static validateABI(abi: any): {
|
|
172
|
+
valid: boolean;
|
|
173
|
+
errors: string[];
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Validate function definition
|
|
177
|
+
*/
|
|
178
|
+
private static validateFunction;
|
|
179
|
+
private static readU32;
|
|
180
|
+
private static readU64;
|
|
181
|
+
private static arraysEqual;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Client-agnostic metadata cache
|
|
185
|
+
*/
|
|
186
|
+
export declare class MetadataCache {
|
|
187
|
+
private cache;
|
|
188
|
+
private defaultTTL;
|
|
189
|
+
/**
|
|
190
|
+
* Get metadata from cache or fetch
|
|
191
|
+
*/
|
|
192
|
+
getMetadata(scriptAddress: string, fetcher: (address: string) => Promise<ScriptMetadata>, ttl?: number): Promise<ScriptMetadata>;
|
|
193
|
+
/**
|
|
194
|
+
* Invalidate cache entry
|
|
195
|
+
*/
|
|
196
|
+
invalidate(scriptAddress: string): void;
|
|
197
|
+
/**
|
|
198
|
+
* Clear expired entries
|
|
199
|
+
*/
|
|
200
|
+
cleanup(): void;
|
|
201
|
+
/**
|
|
202
|
+
* Get cache statistics
|
|
203
|
+
*/
|
|
204
|
+
getStats(): {
|
|
205
|
+
size: number;
|
|
206
|
+
hitRate: number;
|
|
207
|
+
entries: Array<{
|
|
208
|
+
address: string;
|
|
209
|
+
age: number;
|
|
210
|
+
ttl: number;
|
|
211
|
+
}>;
|
|
212
|
+
};
|
|
213
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Five SDK Script Metadata System
|
|
3
|
+
*
|
|
4
|
+
* Real implementation for parsing script account data and extracting ABI information
|
|
5
|
+
* from deployed Five scripts. This replaces mock implementations with production-ready
|
|
6
|
+
* Solana account data parsing.
|
|
7
|
+
*/
|
|
8
|
+
import { Base58Utils } from '../crypto/index.js';
|
|
9
|
+
import { normalizeAbiFunctions } from '../utils/abi.js';
|
|
10
|
+
/**
|
|
11
|
+
* Script metadata parser and manager
|
|
12
|
+
*/
|
|
13
|
+
export class ScriptMetadataParser {
|
|
14
|
+
/**
|
|
15
|
+
* Parse script metadata from account data
|
|
16
|
+
*/
|
|
17
|
+
static parseMetadata(accountData, address) {
|
|
18
|
+
if (accountData.length < this.HEADER_SIZE) {
|
|
19
|
+
throw new Error(`Invalid script account: data too small (${accountData.length} bytes, minimum ${this.HEADER_SIZE})`);
|
|
20
|
+
}
|
|
21
|
+
let offset = 0;
|
|
22
|
+
// Parse header
|
|
23
|
+
const magic = accountData.slice(offset, offset + 8);
|
|
24
|
+
offset += 8;
|
|
25
|
+
if (!this.arraysEqual(magic, this.SCRIPT_MAGIC)) {
|
|
26
|
+
throw new Error('Invalid script account: magic bytes mismatch');
|
|
27
|
+
}
|
|
28
|
+
const version = this.readU32(accountData, offset);
|
|
29
|
+
offset += 4;
|
|
30
|
+
if (version > this.CURRENT_VERSION) {
|
|
31
|
+
throw new Error(`Unsupported script version: ${version} (max supported: ${this.CURRENT_VERSION})`);
|
|
32
|
+
}
|
|
33
|
+
const timestamp = this.readU64(accountData, offset);
|
|
34
|
+
offset += 8;
|
|
35
|
+
const authority = accountData.slice(offset, offset + 32);
|
|
36
|
+
offset += 32;
|
|
37
|
+
const bytecodeLength = this.readU32(accountData, offset);
|
|
38
|
+
offset += 4;
|
|
39
|
+
const abiLength = this.readU32(accountData, offset);
|
|
40
|
+
offset += 4;
|
|
41
|
+
// Skip reserved space
|
|
42
|
+
offset += 8;
|
|
43
|
+
// Validate data lengths
|
|
44
|
+
const expectedSize = this.HEADER_SIZE + bytecodeLength + abiLength;
|
|
45
|
+
if (accountData.length < expectedSize) {
|
|
46
|
+
throw new Error(`Invalid script account: expected ${expectedSize} bytes, got ${accountData.length}`);
|
|
47
|
+
}
|
|
48
|
+
// Extract bytecode
|
|
49
|
+
const bytecode = accountData.slice(offset, offset + bytecodeLength);
|
|
50
|
+
offset += bytecodeLength;
|
|
51
|
+
// Extract and parse ABI
|
|
52
|
+
const abiData = accountData.slice(offset, offset + abiLength);
|
|
53
|
+
const abiJson = new TextDecoder().decode(abiData);
|
|
54
|
+
let abi;
|
|
55
|
+
try {
|
|
56
|
+
abi = JSON.parse(abiJson);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
throw new Error(`Invalid ABI JSON: ${error instanceof Error ? error.message : 'Parse error'}`);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
address,
|
|
63
|
+
bytecode,
|
|
64
|
+
abi,
|
|
65
|
+
deployedAt: timestamp,
|
|
66
|
+
version: version.toString(),
|
|
67
|
+
authority: Base58Utils.encode(authority)
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get script metadata from blockchain using account fetcher
|
|
72
|
+
*/
|
|
73
|
+
static async getScriptMetadata(accountFetcher, scriptAddress) {
|
|
74
|
+
try {
|
|
75
|
+
// Validate address format (basic base58 check)
|
|
76
|
+
if (!scriptAddress || scriptAddress.length < 32 || scriptAddress.length > 44) {
|
|
77
|
+
throw new Error(`Invalid script address format: ${scriptAddress}`);
|
|
78
|
+
}
|
|
79
|
+
// Fetch account data
|
|
80
|
+
const accountData = await accountFetcher.getAccountData(scriptAddress);
|
|
81
|
+
if (!accountData) {
|
|
82
|
+
throw new Error(`Script account not found: ${scriptAddress}`);
|
|
83
|
+
}
|
|
84
|
+
if (!accountData.data || accountData.data.length === 0) {
|
|
85
|
+
throw new Error(`Script account has no data: ${scriptAddress}`);
|
|
86
|
+
}
|
|
87
|
+
// Parse metadata
|
|
88
|
+
return this.parseMetadata(accountData.data, scriptAddress);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
throw new Error(`Failed to get script metadata for ${scriptAddress}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get multiple script metadata entries using account fetcher
|
|
96
|
+
*/
|
|
97
|
+
static async getMultipleScriptMetadata(accountFetcher, scriptAddresses) {
|
|
98
|
+
const results = new Map();
|
|
99
|
+
// Validate addresses (basic format check)
|
|
100
|
+
const validAddresses = [];
|
|
101
|
+
for (const address of scriptAddresses) {
|
|
102
|
+
if (address && address.length >= 32 && address.length <= 44) {
|
|
103
|
+
validAddresses.push(address);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
results.set(address, null);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (validAddresses.length === 0) {
|
|
110
|
+
return results;
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
// Batch fetch account data
|
|
114
|
+
const accountDataMap = await accountFetcher.getMultipleAccountsData(validAddresses);
|
|
115
|
+
// Parse metadata for each account
|
|
116
|
+
for (const address of validAddresses) {
|
|
117
|
+
const accountData = accountDataMap.get(address);
|
|
118
|
+
if (!accountData || !accountData.data || accountData.data.length === 0) {
|
|
119
|
+
results.set(address, null);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
const metadata = this.parseMetadata(accountData.data, address);
|
|
124
|
+
results.set(address, metadata);
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
console.warn(`Failed to parse metadata for ${address}:`, error);
|
|
128
|
+
results.set(address, null);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
throw new Error(`Batch metadata fetch failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
134
|
+
}
|
|
135
|
+
return results;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Extract function signatures from ABI
|
|
139
|
+
*/
|
|
140
|
+
static extractFunctionSignatures(abi) {
|
|
141
|
+
const functions = normalizeAbiFunctions(abi.functions ?? abi).map((func) => ({
|
|
142
|
+
name: func.name,
|
|
143
|
+
index: func.index,
|
|
144
|
+
parameters: func.parameters,
|
|
145
|
+
returnType: func.returnType,
|
|
146
|
+
visibility: func.visibility ?? 'public',
|
|
147
|
+
}));
|
|
148
|
+
return functions.map(func => ({
|
|
149
|
+
name: func.name,
|
|
150
|
+
index: func.index,
|
|
151
|
+
parameters: func.parameters,
|
|
152
|
+
signature: this.generateFunctionSignature(func)
|
|
153
|
+
}));
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Generate function signature string
|
|
157
|
+
*/
|
|
158
|
+
static generateFunctionSignature(func) {
|
|
159
|
+
const paramStrings = func.parameters.map(param => `${param.name}: ${param.type}${param.optional ? '?' : ''}`);
|
|
160
|
+
const returnType = func.returnType ? ` -> ${func.returnType}` : '';
|
|
161
|
+
return `${func.name}(${paramStrings.join(', ')})${returnType}`;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Validate script ABI structure
|
|
165
|
+
*/
|
|
166
|
+
static validateABI(abi) {
|
|
167
|
+
const errors = [];
|
|
168
|
+
if (!abi || typeof abi !== 'object') {
|
|
169
|
+
errors.push('ABI must be an object');
|
|
170
|
+
return { valid: false, errors };
|
|
171
|
+
}
|
|
172
|
+
if (typeof abi.name !== 'string' || abi.name.length === 0) {
|
|
173
|
+
errors.push('ABI must have a non-empty name');
|
|
174
|
+
}
|
|
175
|
+
const functions = normalizeAbiFunctions(abi.functions ?? abi);
|
|
176
|
+
if (functions.length === 0) {
|
|
177
|
+
errors.push('ABI must have at least one function');
|
|
178
|
+
}
|
|
179
|
+
for (let i = 0; i < functions.length; i++) {
|
|
180
|
+
const func = functions[i];
|
|
181
|
+
const funcErrors = this.validateFunction(func, i);
|
|
182
|
+
errors.push(...funcErrors);
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
valid: errors.length === 0,
|
|
186
|
+
errors
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Validate function definition
|
|
191
|
+
*/
|
|
192
|
+
static validateFunction(func, index) {
|
|
193
|
+
const errors = [];
|
|
194
|
+
const prefix = `Function ${index}`;
|
|
195
|
+
if (typeof func.name !== 'string' || func.name.length === 0) {
|
|
196
|
+
errors.push(`${prefix}: must have a non-empty name`);
|
|
197
|
+
}
|
|
198
|
+
if (typeof func.index !== 'number' || func.index < 0) {
|
|
199
|
+
errors.push(`${prefix}: must have a non-negative index`);
|
|
200
|
+
}
|
|
201
|
+
if (!Array.isArray(func.parameters)) {
|
|
202
|
+
errors.push(`${prefix}: must have a parameters array`);
|
|
203
|
+
}
|
|
204
|
+
if (func.visibility && !['public', 'private'].includes(func.visibility)) {
|
|
205
|
+
errors.push(`${prefix}: visibility must be 'public' or 'private'`);
|
|
206
|
+
}
|
|
207
|
+
return errors;
|
|
208
|
+
}
|
|
209
|
+
// Utility methods for binary data parsing
|
|
210
|
+
static readU32(data, offset) {
|
|
211
|
+
return (data[offset] |
|
|
212
|
+
(data[offset + 1] << 8) |
|
|
213
|
+
(data[offset + 2] << 16) |
|
|
214
|
+
(data[offset + 3] << 24)) >>> 0; // Convert to unsigned
|
|
215
|
+
}
|
|
216
|
+
static readU64(data, offset) {
|
|
217
|
+
// Read as two 32-bit values and combine (JavaScript limitation for large numbers)
|
|
218
|
+
const low = this.readU32(data, offset);
|
|
219
|
+
const high = this.readU32(data, offset + 4);
|
|
220
|
+
return low + (high * 0x100000000);
|
|
221
|
+
}
|
|
222
|
+
static arraysEqual(a, b) {
|
|
223
|
+
if (a.length !== b.length)
|
|
224
|
+
return false;
|
|
225
|
+
for (let i = 0; i < a.length; i++) {
|
|
226
|
+
if (a[i] !== b[i])
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
ScriptMetadataParser.SCRIPT_MAGIC = new Uint8Array([
|
|
233
|
+
0x46, 0x49, 0x56, 0x45, 0x5F, 0x53, 0x43, 0x52 // "FIVE_SCR"
|
|
234
|
+
]);
|
|
235
|
+
ScriptMetadataParser.CURRENT_VERSION = 1;
|
|
236
|
+
ScriptMetadataParser.HEADER_SIZE = 64; // Fixed header size
|
|
237
|
+
/**
|
|
238
|
+
* Client-agnostic metadata cache
|
|
239
|
+
*/
|
|
240
|
+
export class MetadataCache {
|
|
241
|
+
constructor() {
|
|
242
|
+
this.cache = new Map();
|
|
243
|
+
this.defaultTTL = 5 * 60 * 1000; // 5 minutes
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Get metadata from cache or fetch
|
|
247
|
+
*/
|
|
248
|
+
async getMetadata(scriptAddress, fetcher, ttl = this.defaultTTL) {
|
|
249
|
+
const now = Date.now();
|
|
250
|
+
const cached = this.cache.get(scriptAddress);
|
|
251
|
+
if (cached && (now - cached.timestamp) < cached.ttl) {
|
|
252
|
+
return cached.metadata;
|
|
253
|
+
}
|
|
254
|
+
// Fetch fresh metadata
|
|
255
|
+
const metadata = await fetcher(scriptAddress);
|
|
256
|
+
// Cache the result
|
|
257
|
+
this.cache.set(scriptAddress, {
|
|
258
|
+
metadata,
|
|
259
|
+
timestamp: now,
|
|
260
|
+
ttl
|
|
261
|
+
});
|
|
262
|
+
return metadata;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Invalidate cache entry
|
|
266
|
+
*/
|
|
267
|
+
invalidate(scriptAddress) {
|
|
268
|
+
this.cache.delete(scriptAddress);
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Clear expired entries
|
|
272
|
+
*/
|
|
273
|
+
cleanup() {
|
|
274
|
+
const now = Date.now();
|
|
275
|
+
for (const [address, entry] of this.cache.entries()) {
|
|
276
|
+
if ((now - entry.timestamp) >= entry.ttl) {
|
|
277
|
+
this.cache.delete(address);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Get cache statistics
|
|
283
|
+
*/
|
|
284
|
+
getStats() {
|
|
285
|
+
const now = Date.now();
|
|
286
|
+
return {
|
|
287
|
+
size: this.cache.size,
|
|
288
|
+
hitRate: 0, // Would need to track hits/misses
|
|
289
|
+
entries: Array.from(this.cache.entries()).map(([address, entry]) => ({
|
|
290
|
+
address,
|
|
291
|
+
age: now - entry.timestamp,
|
|
292
|
+
ttl: entry.ttl
|
|
293
|
+
}))
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ScriptMetadata } from "../metadata/index.js";
|
|
2
|
+
export declare function fetchAccountAndDeserialize(accountAddress: string, connection: any, // Solana Connection object
|
|
3
|
+
options?: {
|
|
4
|
+
debug?: boolean;
|
|
5
|
+
parseMetadata?: boolean;
|
|
6
|
+
validateEncoding?: boolean;
|
|
7
|
+
}): Promise<{
|
|
8
|
+
success: boolean;
|
|
9
|
+
accountInfo?: {
|
|
10
|
+
address: string;
|
|
11
|
+
owner: string;
|
|
12
|
+
lamports: number;
|
|
13
|
+
dataLength: number;
|
|
14
|
+
};
|
|
15
|
+
scriptMetadata?: ScriptMetadata;
|
|
16
|
+
rawBytecode?: Uint8Array;
|
|
17
|
+
decodedData?: {
|
|
18
|
+
header: any;
|
|
19
|
+
bytecode: Uint8Array;
|
|
20
|
+
abi?: any;
|
|
21
|
+
functions?: Array<{
|
|
22
|
+
name: string;
|
|
23
|
+
index: number;
|
|
24
|
+
parameters: any[];
|
|
25
|
+
}>;
|
|
26
|
+
};
|
|
27
|
+
error?: string;
|
|
28
|
+
logs?: string[];
|
|
29
|
+
}>;
|
|
30
|
+
export declare function fetchMultipleAccountsAndDeserialize(accountAddresses: string[], connection: any, options?: {
|
|
31
|
+
debug?: boolean;
|
|
32
|
+
parseMetadata?: boolean;
|
|
33
|
+
validateEncoding?: boolean;
|
|
34
|
+
batchSize?: number;
|
|
35
|
+
}): Promise<Map<string, {
|
|
36
|
+
success: boolean;
|
|
37
|
+
accountInfo?: any;
|
|
38
|
+
scriptMetadata?: ScriptMetadata;
|
|
39
|
+
rawBytecode?: Uint8Array;
|
|
40
|
+
decodedData?: any;
|
|
41
|
+
error?: string;
|
|
42
|
+
logs?: string[];
|
|
43
|
+
}>>;
|
|
44
|
+
export declare function deserializeParameters(instructionData: Uint8Array, expectedTypes?: string[], options?: {
|
|
45
|
+
debug?: boolean;
|
|
46
|
+
}): Promise<{
|
|
47
|
+
success: boolean;
|
|
48
|
+
parameters?: Array<{
|
|
49
|
+
type: string;
|
|
50
|
+
value: any;
|
|
51
|
+
}>;
|
|
52
|
+
functionIndex?: number;
|
|
53
|
+
discriminator?: number;
|
|
54
|
+
error?: string;
|
|
55
|
+
}>;
|
|
56
|
+
export declare function validateBytecodeEncoding(bytecode: Uint8Array, debug?: boolean): Promise<{
|
|
57
|
+
valid: boolean;
|
|
58
|
+
error?: string;
|
|
59
|
+
info?: string;
|
|
60
|
+
}>;
|