@5ive-tech/sdk 1.1.13 → 1.1.14
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 +16 -0
- package/dist/FiveSDK.d.ts +43 -1
- package/dist/FiveSDK.js +6 -0
- package/dist/accounts/index.d.ts +10 -28
- package/dist/accounts/index.js +33 -61
- package/dist/assets/vm/five_vm_wasm.d.ts +8 -0
- package/dist/assets/vm/five_vm_wasm.js +25 -0
- package/dist/assets/vm/five_vm_wasm_bg.wasm +0 -0
- package/dist/assets/vm/five_vm_wasm_bg.wasm.d.ts +3 -0
- package/dist/bin/gen-types.js +0 -0
- package/dist/compiler/BytecodeCompiler.js +10 -6
- package/dist/compiler/source-normalization.d.ts +1 -0
- package/dist/compiler/source-normalization.js +67 -0
- package/dist/constants/headers.d.ts +2 -0
- package/dist/constants/headers.js +2 -0
- package/dist/crypto/index.d.ts +8 -1
- package/dist/crypto/index.js +27 -14
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/modules/accounts.js +1 -1
- package/dist/modules/deploy.js +165 -97
- package/dist/modules/execute.d.ts +5 -0
- package/dist/modules/execute.js +107 -49
- package/dist/modules/fees.js +2 -2
- package/dist/modules/namespaces.d.ts +11 -0
- package/dist/modules/namespaces.js +64 -0
- package/dist/program/FiveProgram.js +4 -3
- package/dist/program/TypeGenerator.js +8 -1
- package/dist/project/config.js +113 -1
- package/dist/project/workspace.d.ts +5 -0
- package/dist/testing/TestDiscovery.d.ts +1 -0
- package/dist/testing/TestDiscovery.js +18 -2
- package/dist/testing/TestRunner.js +4 -1
- package/dist/types.d.ts +16 -5
- package/dist/types.js +1 -0
- package/dist/utils/abi.js +33 -10
- package/dist/utils/transaction.d.ts +16 -0
- package/dist/utils/transaction.js +81 -5
- package/dist/wasm/compiler/CompilationLogic.js +3 -3
- package/dist/wasm/vm.d.ts +2 -2
- package/dist/wasm/vm.js +10 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,22 @@ Client-agnostic TypeScript SDK for interacting with 5ive DSL programs on Solana.
|
|
|
8
8
|
npm install @5ive-tech/sdk @solana/web3.js
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## Localnet Validator Integration Test
|
|
12
|
+
|
|
13
|
+
The localnet integration suite is intentionally environment-gated. Before running:
|
|
14
|
+
|
|
15
|
+
1. Rebuild the localnet SBF artifact:
|
|
16
|
+
- `./scripts/build-five-solana-cluster.sh --cluster localnet`
|
|
17
|
+
2. Start a local validator and deploy the rebuilt localnet-compatible program.
|
|
18
|
+
3. Export the deployed VM program id:
|
|
19
|
+
- `export FIVE_VM_PROGRAM_ID=<local-program-id>`
|
|
20
|
+
|
|
21
|
+
Then run:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm run test:localnet
|
|
25
|
+
```
|
|
26
|
+
|
|
11
27
|
## Quick Start
|
|
12
28
|
|
|
13
29
|
### 1) Compile to `.five`
|
package/dist/FiveSDK.d.ts
CHANGED
|
@@ -5,6 +5,25 @@ import { FiveSDKConfig, FiveScriptSource, FiveBytecode, CompilationOptions, Comp
|
|
|
5
5
|
import { ScriptMetadata } from "./metadata/index.js";
|
|
6
6
|
import * as Namespaces from "./modules/namespaces.js";
|
|
7
7
|
import * as Admin from "./modules/admin.js";
|
|
8
|
+
export type ExecuteAccountMetadata = Map<string, {
|
|
9
|
+
isSigner: boolean;
|
|
10
|
+
isWritable: boolean;
|
|
11
|
+
isSystemAccount?: boolean;
|
|
12
|
+
}>;
|
|
13
|
+
export interface ExecuteOnSolanaOptions {
|
|
14
|
+
debug?: boolean;
|
|
15
|
+
network?: string;
|
|
16
|
+
computeUnitLimit?: number;
|
|
17
|
+
computeUnitPrice?: number;
|
|
18
|
+
maxRetries?: number;
|
|
19
|
+
skipPreflight?: boolean;
|
|
20
|
+
vmStateAccount?: string;
|
|
21
|
+
fiveVMProgramId?: string;
|
|
22
|
+
abi?: any;
|
|
23
|
+
accountMetadata?: ExecuteAccountMetadata;
|
|
24
|
+
feeShardIndex?: number;
|
|
25
|
+
payerAccount?: string;
|
|
26
|
+
}
|
|
8
27
|
/**
|
|
9
28
|
* Main Five SDK class - entry point for all Five VM interactions
|
|
10
29
|
*/
|
|
@@ -95,6 +114,29 @@ export declare class FiveSDK {
|
|
|
95
114
|
resolvedScript?: string;
|
|
96
115
|
bindingAddress: string;
|
|
97
116
|
}>;
|
|
117
|
+
static setNamespaceSymbolPriceOnChain(symbol: string, priceLamports: number, options: {
|
|
118
|
+
managerScriptAccount: string;
|
|
119
|
+
connection: any;
|
|
120
|
+
signerKeypair: any;
|
|
121
|
+
fiveVMProgramId?: string;
|
|
122
|
+
debug?: boolean;
|
|
123
|
+
}): Promise<{
|
|
124
|
+
transactionId?: string;
|
|
125
|
+
symbol: Namespaces.ScopedNamespace["symbol"];
|
|
126
|
+
priceLamports: number;
|
|
127
|
+
}>;
|
|
128
|
+
static getNamespaceSymbolPriceOnChain(symbol: string, options: {
|
|
129
|
+
managerScriptAccount: string;
|
|
130
|
+
connection: any;
|
|
131
|
+
signerKeypair: any;
|
|
132
|
+
fiveVMProgramId?: string;
|
|
133
|
+
debug?: boolean;
|
|
134
|
+
}): Promise<{
|
|
135
|
+
transactionId?: string;
|
|
136
|
+
symbol: Namespaces.ScopedNamespace["symbol"];
|
|
137
|
+
priceLamports: number;
|
|
138
|
+
priceSol: number;
|
|
139
|
+
}>;
|
|
98
140
|
static compile(source: FiveScriptSource | string, options?: CompilationOptions & {
|
|
99
141
|
debug?: boolean;
|
|
100
142
|
}): Promise<CompilationResult>;
|
|
@@ -209,7 +251,7 @@ export declare class FiveSDK {
|
|
|
209
251
|
static getCachedScriptMetadata(scriptAccount: string, connection: any, cacheTTL?: number): Promise<ScriptMetadata>;
|
|
210
252
|
static invalidateMetadataCache(scriptAccount: string): void;
|
|
211
253
|
static getMetadataCacheStats(): any;
|
|
212
|
-
static executeOnSolana(scriptAccount: string, connection: any, signerKeypair: any, functionName: string | number, parameters?: any[], accounts?: string[], options?:
|
|
254
|
+
static executeOnSolana(scriptAccount: string, connection: any, signerKeypair: any, functionName: string | number, parameters?: any[], accounts?: string[], options?: ExecuteOnSolanaOptions): Promise<{
|
|
213
255
|
success: boolean;
|
|
214
256
|
result?: any;
|
|
215
257
|
transactionId?: string;
|
package/dist/FiveSDK.js
CHANGED
|
@@ -105,6 +105,12 @@ export class FiveSDK {
|
|
|
105
105
|
static async resolveNamespaceOnChain(namespaceValue, options) {
|
|
106
106
|
return Namespaces.resolveNamespaceOnChain(namespaceValue, options);
|
|
107
107
|
}
|
|
108
|
+
static async setNamespaceSymbolPriceOnChain(symbol, priceLamports, options) {
|
|
109
|
+
return Namespaces.setNamespaceSymbolPriceOnChain(symbol, priceLamports, options);
|
|
110
|
+
}
|
|
111
|
+
static async getNamespaceSymbolPriceOnChain(symbol, options) {
|
|
112
|
+
return Namespaces.getNamespaceSymbolPriceOnChain(symbol, options);
|
|
113
|
+
}
|
|
108
114
|
// ==================== Script Compilation ====================
|
|
109
115
|
static async compile(source, options = {}) {
|
|
110
116
|
const sourceContent = typeof source === 'string' ? source : source.content;
|
package/dist/accounts/index.d.ts
CHANGED
|
@@ -105,19 +105,14 @@ export interface AccountValidationResult {
|
|
|
105
105
|
/**
|
|
106
106
|
* Account creation parameters
|
|
107
107
|
*/
|
|
108
|
-
export interface
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
/** Additional lamports beyond rent exemption */
|
|
116
|
-
additionalLamports?: number;
|
|
108
|
+
export interface ProvisioningPlan {
|
|
109
|
+
address: string;
|
|
110
|
+
bump: number;
|
|
111
|
+
rentLamports: number;
|
|
112
|
+
creationMode: 'program_init_required';
|
|
113
|
+
ownerProgramId: string;
|
|
114
|
+
createInstruction: null;
|
|
117
115
|
}
|
|
118
|
-
/**
|
|
119
|
-
* Solana transaction instruction interface
|
|
120
|
-
*/
|
|
121
116
|
export interface TransactionInstruction {
|
|
122
117
|
/** Program ID to invoke */
|
|
123
118
|
programId: string;
|
|
@@ -136,10 +131,7 @@ export interface TransactionInstruction {
|
|
|
136
131
|
export declare class FiveAccountManager {
|
|
137
132
|
private programId;
|
|
138
133
|
constructor(programId?: string);
|
|
139
|
-
|
|
140
|
-
* Encode System Program CreateAccount instruction
|
|
141
|
-
*/
|
|
142
|
-
private encodeCreateAccountInstruction;
|
|
134
|
+
private serializeInstruction;
|
|
143
135
|
/**
|
|
144
136
|
* Create script account PDA and return serialized instruction
|
|
145
137
|
*/
|
|
@@ -152,21 +144,11 @@ export declare class FiveAccountManager {
|
|
|
152
144
|
/**
|
|
153
145
|
* Create metadata account for script
|
|
154
146
|
*/
|
|
155
|
-
createMetadataAccount(scriptAccount: string,
|
|
156
|
-
address: string;
|
|
157
|
-
bump: number;
|
|
158
|
-
createInstruction: TransactionInstruction;
|
|
159
|
-
rentLamports: number;
|
|
160
|
-
}>;
|
|
147
|
+
createMetadataAccount(scriptAccount: string, _payerAddress: string): Promise<ProvisioningPlan>;
|
|
161
148
|
/**
|
|
162
149
|
* Create user state account for script interaction
|
|
163
150
|
*/
|
|
164
|
-
createUserStateAccount(userPublicKey: string, scriptAccount: string): Promise<
|
|
165
|
-
address: string;
|
|
166
|
-
bump: number;
|
|
167
|
-
createInstruction: any;
|
|
168
|
-
rentLamports: number;
|
|
169
|
-
}>;
|
|
151
|
+
createUserStateAccount(userPublicKey: string, scriptAccount: string): Promise<ProvisioningPlan>;
|
|
170
152
|
/**
|
|
171
153
|
* Validate account constraints for script execution
|
|
172
154
|
*/
|
package/dist/accounts/index.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { PDAUtils, SolanaPublicKeyUtils, RentCalculator, AccountValidator } from '../crypto/index.js';
|
|
8
8
|
import { ProgramIdResolver } from '../config/ProgramIdResolver.js';
|
|
9
|
+
import { PublicKey, SystemProgram } from '@solana/web3.js';
|
|
9
10
|
/**
|
|
10
11
|
* AccountType enum for test compatibility
|
|
11
12
|
*/
|
|
@@ -26,44 +27,35 @@ export class FiveAccountManager {
|
|
|
26
27
|
constructor(programId) {
|
|
27
28
|
this.programId = ProgramIdResolver.resolve(programId);
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
// Account size
|
|
40
|
-
view.setUint32(4, params.size, true);
|
|
41
|
-
// Rent lamports (calculated)
|
|
42
|
-
const rentLamports = params.rentExempt ? RentCalculator.calculateRentExemption(params.size) : 0;
|
|
43
|
-
view.setBigUint64(8, BigInt(rentLamports), true);
|
|
44
|
-
// Owner program ID would be encoded here in real implementation
|
|
45
|
-
// Return the basic instruction data
|
|
46
|
-
return new Uint8Array(buffer);
|
|
30
|
+
serializeInstruction(instruction) {
|
|
31
|
+
return {
|
|
32
|
+
programId: instruction.programId.toBase58(),
|
|
33
|
+
accounts: instruction.keys.map((key) => ({
|
|
34
|
+
pubkey: key.pubkey.toBase58(),
|
|
35
|
+
isSigner: key.isSigner,
|
|
36
|
+
isWritable: key.isWritable,
|
|
37
|
+
})),
|
|
38
|
+
data: new Uint8Array(instruction.data),
|
|
39
|
+
};
|
|
47
40
|
}
|
|
48
41
|
/**
|
|
49
42
|
* Create script account PDA and return serialized instruction
|
|
50
43
|
*/
|
|
51
44
|
async createScriptAccount(bytecode, payerAddress) {
|
|
52
|
-
const pda = await PDAUtils.deriveScriptAccount(bytecode, this.programId);
|
|
45
|
+
const pda = await PDAUtils.deriveScriptAccount(bytecode, payerAddress, this.programId);
|
|
53
46
|
const rentLamports = RentCalculator.getScriptAccountRent(bytecode.length);
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
};
|
|
47
|
+
const payerPubkey = new PublicKey(payerAddress);
|
|
48
|
+
const scriptPubkey = new PublicKey(pda.address);
|
|
49
|
+
const programPubkey = new PublicKey(this.programId);
|
|
50
|
+
const createInstruction = this.serializeInstruction(SystemProgram.createAccountWithSeed({
|
|
51
|
+
fromPubkey: payerPubkey,
|
|
52
|
+
newAccountPubkey: scriptPubkey,
|
|
53
|
+
basePubkey: payerPubkey,
|
|
54
|
+
seed: pda.seed,
|
|
55
|
+
lamports: rentLamports,
|
|
56
|
+
space: bytecode.length + 256,
|
|
57
|
+
programId: programPubkey,
|
|
58
|
+
}));
|
|
67
59
|
return {
|
|
68
60
|
address: pda.address,
|
|
69
61
|
bump: pda.bump,
|
|
@@ -74,26 +66,16 @@ export class FiveAccountManager {
|
|
|
74
66
|
/**
|
|
75
67
|
* Create metadata account for script
|
|
76
68
|
*/
|
|
77
|
-
async createMetadataAccount(scriptAccount,
|
|
69
|
+
async createMetadataAccount(scriptAccount, _payerAddress) {
|
|
78
70
|
const pda = await PDAUtils.deriveMetadataAccount(scriptAccount, this.programId);
|
|
79
71
|
const rentLamports = RentCalculator.getMetadataAccountRent();
|
|
80
|
-
const createInstruction = {
|
|
81
|
-
programId: '11111111111111111111111111111112', // System Program
|
|
82
|
-
accounts: [
|
|
83
|
-
{ pubkey: payerAddress, isSigner: true, isWritable: true },
|
|
84
|
-
{ pubkey: pda.address, isSigner: false, isWritable: true }
|
|
85
|
-
],
|
|
86
|
-
data: this.encodeCreateAccountInstruction({
|
|
87
|
-
size: 1024, // 1KB for metadata
|
|
88
|
-
owner: this.programId,
|
|
89
|
-
rentExempt: true
|
|
90
|
-
})
|
|
91
|
-
};
|
|
92
72
|
return {
|
|
93
73
|
address: pda.address,
|
|
94
74
|
bump: pda.bump,
|
|
95
|
-
|
|
96
|
-
|
|
75
|
+
rentLamports,
|
|
76
|
+
creationMode: 'program_init_required',
|
|
77
|
+
ownerProgramId: this.programId,
|
|
78
|
+
createInstruction: null,
|
|
97
79
|
};
|
|
98
80
|
}
|
|
99
81
|
/**
|
|
@@ -105,20 +87,10 @@ export class FiveAccountManager {
|
|
|
105
87
|
return {
|
|
106
88
|
address: pda.address,
|
|
107
89
|
bump: pda.bump,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
{ pubkey: userPublicKey, isSigner: true, isWritable: true },
|
|
113
|
-
{ pubkey: this.programId, isSigner: false, isWritable: false }
|
|
114
|
-
],
|
|
115
|
-
data: this.encodeCreateAccountInstruction({
|
|
116
|
-
size: 512, // 512 bytes for user state
|
|
117
|
-
owner: this.programId,
|
|
118
|
-
rentExempt: true
|
|
119
|
-
})
|
|
120
|
-
},
|
|
121
|
-
rentLamports
|
|
90
|
+
rentLamports,
|
|
91
|
+
creationMode: 'program_init_required',
|
|
92
|
+
ownerProgramId: this.programId,
|
|
93
|
+
createInstruction: null,
|
|
122
94
|
};
|
|
123
95
|
}
|
|
124
96
|
/**
|
|
@@ -280,6 +280,10 @@ export class WasmCompilationOptions {
|
|
|
280
280
|
* Enable or disable debug information
|
|
281
281
|
*/
|
|
282
282
|
with_debug_info(enabled: boolean): WasmCompilationOptions;
|
|
283
|
+
/**
|
|
284
|
+
* Enable or disable REQUIRE_BATCH lowering.
|
|
285
|
+
*/
|
|
286
|
+
with_disable_require_batch(enabled: boolean): WasmCompilationOptions;
|
|
283
287
|
/**
|
|
284
288
|
* Enable or disable enhanced error reporting
|
|
285
289
|
*/
|
|
@@ -348,6 +352,10 @@ export class WasmCompilationOptions {
|
|
|
348
352
|
* Enable bytecode compression
|
|
349
353
|
*/
|
|
350
354
|
compress_output: boolean;
|
|
355
|
+
/**
|
|
356
|
+
* Disable REQUIRE_BATCH lowering in compiler pipeline.
|
|
357
|
+
*/
|
|
358
|
+
disable_require_batch: boolean;
|
|
351
359
|
/**
|
|
352
360
|
* Enable constraint caching optimization
|
|
353
361
|
*/
|
|
@@ -924,6 +924,14 @@ class WasmCompilationOptions {
|
|
|
924
924
|
const ret = wasm.__wbg_get_wasmcompilationoptions_compress_output(this.__wbg_ptr);
|
|
925
925
|
return ret !== 0;
|
|
926
926
|
}
|
|
927
|
+
/**
|
|
928
|
+
* Disable REQUIRE_BATCH lowering in compiler pipeline.
|
|
929
|
+
* @returns {boolean}
|
|
930
|
+
*/
|
|
931
|
+
get disable_require_batch() {
|
|
932
|
+
const ret = wasm.__wbg_get_wasmcompilationoptions_disable_require_batch(this.__wbg_ptr);
|
|
933
|
+
return ret !== 0;
|
|
934
|
+
}
|
|
927
935
|
/**
|
|
928
936
|
* Enable constraint caching optimization
|
|
929
937
|
* @returns {boolean}
|
|
@@ -1025,6 +1033,13 @@ class WasmCompilationOptions {
|
|
|
1025
1033
|
set compress_output(arg0) {
|
|
1026
1034
|
wasm.__wbg_set_wasmcompilationoptions_compress_output(this.__wbg_ptr, arg0);
|
|
1027
1035
|
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Disable REQUIRE_BATCH lowering in compiler pipeline.
|
|
1038
|
+
* @param {boolean} arg0
|
|
1039
|
+
*/
|
|
1040
|
+
set disable_require_batch(arg0) {
|
|
1041
|
+
wasm.__wbg_set_wasmcompilationoptions_disable_require_batch(this.__wbg_ptr, arg0);
|
|
1042
|
+
}
|
|
1028
1043
|
/**
|
|
1029
1044
|
* Enable constraint caching optimization
|
|
1030
1045
|
* @param {boolean} arg0
|
|
@@ -1323,6 +1338,16 @@ class WasmCompilationOptions {
|
|
|
1323
1338
|
const ret = wasm.wasmcompilationoptions_with_debug_info(ptr, enabled);
|
|
1324
1339
|
return WasmCompilationOptions.__wrap(ret);
|
|
1325
1340
|
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Enable or disable REQUIRE_BATCH lowering.
|
|
1343
|
+
* @param {boolean} enabled
|
|
1344
|
+
* @returns {WasmCompilationOptions}
|
|
1345
|
+
*/
|
|
1346
|
+
with_disable_require_batch(enabled) {
|
|
1347
|
+
const ptr = this.__destroy_into_raw();
|
|
1348
|
+
const ret = wasm.wasmcompilationoptions_with_disable_require_batch(ptr, enabled);
|
|
1349
|
+
return WasmCompilationOptions.__wrap(ret);
|
|
1350
|
+
}
|
|
1326
1351
|
/**
|
|
1327
1352
|
* Enable or disable enhanced error reporting
|
|
1328
1353
|
* @param {boolean} enabled
|
|
Binary file
|
|
@@ -116,6 +116,8 @@ export const __wbg_get_wasmcompilationoptions_compress_output: (a: number) => nu
|
|
|
116
116
|
export const __wbg_set_wasmcompilationoptions_compress_output: (a: number, b: number) => void;
|
|
117
117
|
export const __wbg_get_wasmcompilationoptions_enable_module_namespaces: (a: number) => number;
|
|
118
118
|
export const __wbg_set_wasmcompilationoptions_enable_module_namespaces: (a: number, b: number) => void;
|
|
119
|
+
export const __wbg_get_wasmcompilationoptions_disable_require_batch: (a: number) => number;
|
|
120
|
+
export const __wbg_set_wasmcompilationoptions_disable_require_batch: (a: number, b: number) => void;
|
|
119
121
|
export const wasmcompilationoptions_new: () => number;
|
|
120
122
|
export const wasmcompilationoptions_with_mode: (a: number, b: number, c: number) => number;
|
|
121
123
|
export const wasmcompilationoptions_with_optimization_level: (a: number, b: number, c: number) => number;
|
|
@@ -137,6 +139,7 @@ export const wasmcompilationoptions_with_export_format: (a: number, b: number, c
|
|
|
137
139
|
export const wasmcompilationoptions_with_debug_info: (a: number, b: number) => number;
|
|
138
140
|
export const wasmcompilationoptions_with_compression: (a: number, b: number) => number;
|
|
139
141
|
export const wasmcompilationoptions_with_module_namespaces: (a: number, b: number) => number;
|
|
142
|
+
export const wasmcompilationoptions_with_disable_require_batch: (a: number, b: number) => number;
|
|
140
143
|
export const wasmcompilationoptions_production_optimized: () => number;
|
|
141
144
|
export const wasmcompilationoptions_development_debug: () => number;
|
|
142
145
|
export const wasmcompilationoptions_fast_iteration: () => number;
|
package/dist/bin/gen-types.js
CHANGED
|
File without changes
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { readFile } from "fs/promises";
|
|
10
10
|
import { CompilationSDKError, } from "../types.js";
|
|
11
11
|
import { normalizeAbiFunctions } from "../utils/abi.js";
|
|
12
|
+
import { normalizeWasmCompilerSource } from "./source-normalization.js";
|
|
12
13
|
/**
|
|
13
14
|
* Bytecode compiler for Five scripts
|
|
14
15
|
*/
|
|
@@ -25,6 +26,7 @@ export class BytecodeCompiler {
|
|
|
25
26
|
async compile(source, options = {}) {
|
|
26
27
|
const startTime = Date.now();
|
|
27
28
|
const sourceContent = typeof source === 'string' ? source : source.content;
|
|
29
|
+
const normalizedSourceContent = normalizeWasmCompilerSource(sourceContent);
|
|
28
30
|
const sourceFilename = typeof source === 'string' ? 'unknown.v' : source.filename || 'unknown.v';
|
|
29
31
|
// Compile source (debug info available in this.debug mode)
|
|
30
32
|
try {
|
|
@@ -38,7 +40,7 @@ export class BytecodeCompiler {
|
|
|
38
40
|
target: options.target || "vm",
|
|
39
41
|
debug: options.debug || false,
|
|
40
42
|
maxSize: options.maxSize || 1048576, // 1MB default
|
|
41
|
-
optimizationLevel:
|
|
43
|
+
optimizationLevel: "production", // Public SDK surface is locked to production
|
|
42
44
|
sourceFile: sourceFilename,
|
|
43
45
|
// Pass through metrics options
|
|
44
46
|
metricsFormat: options.metricsFormat,
|
|
@@ -48,7 +50,7 @@ export class BytecodeCompiler {
|
|
|
48
50
|
comprehensiveMetrics: options.comprehensiveMetrics,
|
|
49
51
|
};
|
|
50
52
|
// Perform compilation
|
|
51
|
-
const result = await this.wasmCompiler.compile(
|
|
53
|
+
const result = await this.wasmCompiler.compile(normalizedSourceContent, compilerOptions);
|
|
52
54
|
const compilationTime = Date.now() - startTime;
|
|
53
55
|
if (result.success && result.bytecode) {
|
|
54
56
|
let abiData = result.abi;
|
|
@@ -114,7 +116,7 @@ export class BytecodeCompiler {
|
|
|
114
116
|
: undefined;
|
|
115
117
|
throw new CompilationSDKError(`Compilation error: ${error instanceof Error ? error.message : "Unknown error"}`, {
|
|
116
118
|
...(inheritedDetails || {}),
|
|
117
|
-
source:
|
|
119
|
+
source: normalizedSourceContent.substring(0, 200),
|
|
118
120
|
options,
|
|
119
121
|
});
|
|
120
122
|
}
|
|
@@ -216,6 +218,7 @@ export class BytecodeCompiler {
|
|
|
216
218
|
*/
|
|
217
219
|
async validateSource(source) {
|
|
218
220
|
const code = typeof source === 'string' ? source : source.content;
|
|
221
|
+
const normalizedCode = normalizeWasmCompilerSource(code);
|
|
219
222
|
if (this.debug) {
|
|
220
223
|
console.log(`[BytecodeCompiler] Validating source (${code.length} chars)...`);
|
|
221
224
|
}
|
|
@@ -223,7 +226,7 @@ export class BytecodeCompiler {
|
|
|
223
226
|
if (!this.wasmCompiler) {
|
|
224
227
|
await this.loadWasmCompiler();
|
|
225
228
|
}
|
|
226
|
-
const result = await this.wasmCompiler.validateSource(
|
|
229
|
+
const result = await this.wasmCompiler.validateSource(normalizedCode);
|
|
227
230
|
return {
|
|
228
231
|
valid: result.valid,
|
|
229
232
|
errors: result.errors ? this.transformErrors(result.errors) : undefined,
|
|
@@ -372,6 +375,7 @@ export class BytecodeCompiler {
|
|
|
372
375
|
*/
|
|
373
376
|
async generateABI(source) {
|
|
374
377
|
const code = typeof source === 'string' ? source : source.content;
|
|
378
|
+
const normalizedCode = normalizeWasmCompilerSource(code);
|
|
375
379
|
if (this.debug) {
|
|
376
380
|
console.log(`[BytecodeCompiler] Generating ABI for source (${code.length} chars)...`);
|
|
377
381
|
}
|
|
@@ -379,7 +383,7 @@ export class BytecodeCompiler {
|
|
|
379
383
|
if (!this.wasmCompiler) {
|
|
380
384
|
await this.loadWasmCompiler();
|
|
381
385
|
}
|
|
382
|
-
const abi = await this.wasmCompiler.generateABI(
|
|
386
|
+
const abi = await this.wasmCompiler.generateABI(normalizedCode);
|
|
383
387
|
const normalizedFunctions = normalizeAbiFunctions(abi?.functions ?? abi);
|
|
384
388
|
return { ...abi, functions: normalizedFunctions };
|
|
385
389
|
}
|
|
@@ -387,7 +391,7 @@ export class BytecodeCompiler {
|
|
|
387
391
|
if (this.debug) {
|
|
388
392
|
console.log(`[BytecodeCompiler] ABI generation error: ${error}`);
|
|
389
393
|
}
|
|
390
|
-
throw new CompilationSDKError(`ABI generation failed: ${error instanceof Error ? error.message : "Unknown error"}`, { source:
|
|
394
|
+
throw new CompilationSDKError(`ABI generation failed: ${error instanceof Error ? error.message : "Unknown error"}`, { source: normalizedCode.substring(0, 100) });
|
|
391
395
|
}
|
|
392
396
|
}
|
|
393
397
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function normalizeWasmCompilerSource(source: string): string;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const SPL_TOKEN_PROGRAM_ID = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
|
|
2
|
+
function normalizeAccountContextAccess(source) {
|
|
3
|
+
const placeholders = new Map();
|
|
4
|
+
let nextId = 0;
|
|
5
|
+
const protect = (match) => {
|
|
6
|
+
const token = `__FIVE_CTX_${nextId++}__`;
|
|
7
|
+
placeholders.set(token, match);
|
|
8
|
+
return token;
|
|
9
|
+
};
|
|
10
|
+
let normalized = source.replace(/(\b[A-Za-z_][A-Za-z0-9_]*)\.ctx\.(key|lamports|owner|data|bump)\b/g, protect);
|
|
11
|
+
normalized = normalized.replace(/(\b[A-Za-z_][A-Za-z0-9_]*)\.(key|lamports|owner|data|bump)\b/g, '$1.ctx.$2');
|
|
12
|
+
for (const [token, original] of placeholders.entries()) {
|
|
13
|
+
normalized = normalized.split(token).join(original);
|
|
14
|
+
}
|
|
15
|
+
return normalized;
|
|
16
|
+
}
|
|
17
|
+
function normalizeSplTokenModule(source) {
|
|
18
|
+
const importPattern = /^\s*use\s+std::interfaces::spl_token;\s*$/m;
|
|
19
|
+
if (!importPattern.test(source) && !/\bspl_token::[A-Za-z_][A-Za-z0-9_]*\s*\(/.test(source)) {
|
|
20
|
+
return source;
|
|
21
|
+
}
|
|
22
|
+
let normalized = source.replace(importPattern, '');
|
|
23
|
+
const methods = new Set();
|
|
24
|
+
for (const match of normalized.matchAll(/\bspl_token::([A-Za-z_][A-Za-z0-9_]*)\s*\(/g)) {
|
|
25
|
+
methods.add(match[1]);
|
|
26
|
+
}
|
|
27
|
+
if (methods.size === 0) {
|
|
28
|
+
return normalized;
|
|
29
|
+
}
|
|
30
|
+
const signatures = Array.from(methods)
|
|
31
|
+
.sort()
|
|
32
|
+
.map((method) => {
|
|
33
|
+
switch (method) {
|
|
34
|
+
case 'transfer':
|
|
35
|
+
return ' transfer(source: account @mut, destination: account @mut, authority: account @signer, amount: u64);';
|
|
36
|
+
case 'mint_to':
|
|
37
|
+
return ' mint_to(mint: account @mut, destination: account @mut, authority: account @signer, amount: u64);';
|
|
38
|
+
case 'burn':
|
|
39
|
+
return ' burn(source: account @mut, mint: account @mut, authority: account @signer, amount: u64);';
|
|
40
|
+
case 'approve':
|
|
41
|
+
return ' approve(source: account @mut, delegate: account, authority: account @signer, amount: u64);';
|
|
42
|
+
case 'revoke':
|
|
43
|
+
return ' revoke(source: account @mut, authority: account @signer);';
|
|
44
|
+
case 'freeze_account':
|
|
45
|
+
return ' freeze_account(account_to_freeze: account @mut, mint: account @mut, freeze_authority: account @signer);';
|
|
46
|
+
case 'thaw_account':
|
|
47
|
+
return ' thaw_account(account_to_thaw: account @mut, mint: account @mut, freeze_authority: account @signer);';
|
|
48
|
+
case 'transfer_checked':
|
|
49
|
+
return ' transfer_checked(source: account @mut, mint: account @mut, destination: account @mut, authority: account @signer, amount: u64, decimals: u8);';
|
|
50
|
+
default:
|
|
51
|
+
return ` ${method}(source: account @mut, destination: account @mut, authority: account @signer, amount: u64);`;
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
.join('\n');
|
|
55
|
+
normalized = normalized.replace(/\bspl_token::([A-Za-z_][A-Za-z0-9_]*)\s*\(/g, 'SPLToken.$1(');
|
|
56
|
+
if (/^\s*interface\s+SPLToken\b/m.test(normalized)) {
|
|
57
|
+
return normalized;
|
|
58
|
+
}
|
|
59
|
+
const interfaceDecl = `interface SPLToken @program("${SPL_TOKEN_PROGRAM_ID}") {\n${signatures}\n}\n\n`;
|
|
60
|
+
return `${interfaceDecl}${normalized.trimStart()}`;
|
|
61
|
+
}
|
|
62
|
+
export function normalizeWasmCompilerSource(source) {
|
|
63
|
+
let normalized = source;
|
|
64
|
+
normalized = normalizeAccountContextAccess(normalized);
|
|
65
|
+
normalized = normalizeSplTokenModule(normalized);
|
|
66
|
+
return normalized;
|
|
67
|
+
}
|
package/dist/crypto/index.d.ts
CHANGED
|
@@ -9,9 +9,10 @@ export declare class PDAUtils {
|
|
|
9
9
|
/**
|
|
10
10
|
* Derive script account using seed-based derivation compatible with SystemProgram.createAccountWithSeed
|
|
11
11
|
* @param bytecode - The bytecode to derive address for
|
|
12
|
+
* @param basePublicKey - The deployer/base public key used for createWithSeed
|
|
12
13
|
* @param programId - The Five VM program ID (required - no default to enforce explicit configuration)
|
|
13
14
|
*/
|
|
14
|
-
static deriveScriptAccount(bytecode: Uint8Array, programId: string): Promise<{
|
|
15
|
+
static deriveScriptAccount(bytecode: Uint8Array, basePublicKey: string, programId: string): Promise<{
|
|
15
16
|
address: string;
|
|
16
17
|
bump: number;
|
|
17
18
|
seed: string;
|
|
@@ -127,6 +128,12 @@ export declare class RentCalculator {
|
|
|
127
128
|
* Calculate minimum rent-exempt balance for account size (legacy method)
|
|
128
129
|
*/
|
|
129
130
|
static calculateRentExemption(accountSize: number): number;
|
|
131
|
+
/**
|
|
132
|
+
* Query rent from RPC when possible and fall back to local estimation otherwise.
|
|
133
|
+
*/
|
|
134
|
+
static calculateRentExemptionWithConnection(accountSize: number, connection?: {
|
|
135
|
+
getMinimumBalanceForRentExemption?: (size: number) => Promise<number>;
|
|
136
|
+
}): Promise<number>;
|
|
130
137
|
/**
|
|
131
138
|
* Get estimated rent for script account based on bytecode size
|
|
132
139
|
*/
|
package/dist/crypto/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Five SDK crypto utilities.
|
|
3
3
|
*/
|
|
4
4
|
import bs58 from 'bs58';
|
|
5
|
-
import { randomBytes } from 'crypto';
|
|
5
|
+
import { createHash, randomBytes } from 'crypto';
|
|
6
6
|
import { PublicKey } from '@solana/web3.js';
|
|
7
7
|
/**
|
|
8
8
|
* Program Derived Address (PDA) utilities - pure implementation
|
|
@@ -11,21 +11,20 @@ export class PDAUtils {
|
|
|
11
11
|
/**
|
|
12
12
|
* Derive script account using seed-based derivation compatible with SystemProgram.createAccountWithSeed
|
|
13
13
|
* @param bytecode - The bytecode to derive address for
|
|
14
|
+
* @param basePublicKey - The deployer/base public key used for createWithSeed
|
|
14
15
|
* @param programId - The Five VM program ID (required - no default to enforce explicit configuration)
|
|
15
16
|
*/
|
|
16
|
-
static async deriveScriptAccount(bytecode, programId) {
|
|
17
|
+
static async deriveScriptAccount(bytecode, basePublicKey, programId) {
|
|
17
18
|
try {
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// Use simplified approach; requires deployer's pubkey
|
|
26
|
-
// Return seed-based result that's compatible with System Program
|
|
19
|
+
const deployerKey = new PublicKey(basePublicKey);
|
|
20
|
+
const programKey = new PublicKey(programId);
|
|
21
|
+
const seed = createHash('sha256')
|
|
22
|
+
.update(Buffer.from(bytecode))
|
|
23
|
+
.digest('hex')
|
|
24
|
+
.slice(0, 32);
|
|
25
|
+
const address = await PublicKey.createWithSeed(deployerKey, seed, programKey);
|
|
27
26
|
return {
|
|
28
|
-
address:
|
|
27
|
+
address: address.toBase58(),
|
|
29
28
|
bump: 0, // Seed-based accounts don't use bumps
|
|
30
29
|
seed: seed
|
|
31
30
|
};
|
|
@@ -40,8 +39,8 @@ export class PDAUtils {
|
|
|
40
39
|
static async findProgramAddress(seeds, programId) {
|
|
41
40
|
const crypto = await import('crypto');
|
|
42
41
|
const programIdBytes = Base58Utils.decode(programId);
|
|
43
|
-
// Try bump
|
|
44
|
-
for (let bump = 255; bump >=
|
|
42
|
+
// Try the full Solana bump range from 255 down to 0.
|
|
43
|
+
for (let bump = 255; bump >= 0; bump--) {
|
|
45
44
|
const seedsWithBump = [...seeds, Buffer.from([bump])];
|
|
46
45
|
// Create the hash input
|
|
47
46
|
let hashInput = Buffer.alloc(0);
|
|
@@ -270,6 +269,20 @@ export class RentCalculator {
|
|
|
270
269
|
const rentExemption = Math.ceil((rentPerYear * this.RENT_EXEMPTION_THRESHOLD) / (365 * 24 * 60 * 60));
|
|
271
270
|
return rentExemption;
|
|
272
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Query rent from RPC when possible and fall back to local estimation otherwise.
|
|
274
|
+
*/
|
|
275
|
+
static async calculateRentExemptionWithConnection(accountSize, connection) {
|
|
276
|
+
if (connection?.getMinimumBalanceForRentExemption) {
|
|
277
|
+
try {
|
|
278
|
+
return await connection.getMinimumBalanceForRentExemption(accountSize);
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
// Fall back to local estimation below.
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return this.calculateRentExemption(accountSize);
|
|
285
|
+
}
|
|
273
286
|
/**
|
|
274
287
|
* Get estimated rent for script account based on bytecode size
|
|
275
288
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export * from './wasm/vm.js';
|
|
|
15
15
|
export * from './wasm/compiler/index.js';
|
|
16
16
|
export * from './wasm/loader.js';
|
|
17
17
|
export * from './testing/index.js';
|
|
18
|
+
export * from './utils/abi.js';
|
|
18
19
|
export * from './program/index.js';
|
|
19
20
|
export * from './modules/namespaces.js';
|
|
20
21
|
export { ProgramIdResolver } from './config/ProgramIdResolver.js';
|