solana-ts 0.1.0
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 +139 -0
- package/dist/engine.d.ts +62 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +302 -0
- package/dist/engine.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/persistence.d.ts +21 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +141 -0
- package/dist/persistence.js.map +1 -0
- package/dist/rpc/dispatcher.d.ts +47 -0
- package/dist/rpc/dispatcher.d.ts.map +1 -0
- package/dist/rpc/dispatcher.js +63 -0
- package/dist/rpc/dispatcher.js.map +1 -0
- package/dist/rpc/methods.d.ts +4 -0
- package/dist/rpc/methods.d.ts.map +1 -0
- package/dist/rpc/methods.js +263 -0
- package/dist/rpc/methods.js.map +1 -0
- package/dist/rpc/subscriptions.d.ts +14 -0
- package/dist/rpc/subscriptions.d.ts.map +1 -0
- package/dist/rpc/subscriptions.js +111 -0
- package/dist/rpc/subscriptions.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +104 -0
- package/dist/server.js.map +1 -0
- package/dist/test.d.ts +2 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +518 -0
- package/dist/test.js.map +1 -0
- package/dist/types.d.ts +40 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +29 -0
- package/dist/types.js.map +1 -0
- package/package.json +31 -0
- package/src/engine.ts +401 -0
- package/src/index.ts +72 -0
- package/src/persistence.ts +189 -0
- package/src/rpc/dispatcher.ts +94 -0
- package/src/rpc/methods.ts +312 -0
- package/src/rpc/subscriptions.ts +158 -0
- package/src/server.ts +127 -0
- package/src/test.ts +674 -0
- package/src/types.ts +68 -0
- package/tsconfig.json +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# solana-ts
|
|
2
|
+
|
|
3
|
+
A zero-idle-CPU local Solana RPC simulator powered by [LiteSVM](https://litesvm.com). Drop-in replacement for `solana-test-validator` that starts instantly, uses no CPU when idle, and persists state to a SQLite file.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
`solana-test-validator` ships a full validator binary — PoH, gossip, turbine, replay, leader schedule, voting — and burns CPU even when nothing is happening. This project strips all of that away and keeps only what matters for local app testing: an in-process SVM execution engine behind a Solana-compatible JSON-RPC server.
|
|
8
|
+
|
|
9
|
+
- Starts in under a second
|
|
10
|
+
- Zero CPU at idle (no slot ticking, no block production loop)
|
|
11
|
+
- Transactions execute synchronously and are immediately "confirmed"
|
|
12
|
+
- State persists to a single SQLite file across restarts
|
|
13
|
+
- Wire-compatible with `@solana/web3.js` v1 `Connection`
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm install
|
|
19
|
+
pnpm dev
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The server listens on `http://127.0.0.1:8899` (HTTP JSON-RPC) and `ws://127.0.0.1:8900` (WebSocket PubSub).
|
|
23
|
+
|
|
24
|
+
Connect from any Solana app:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { Connection } from "@solana/web3.js";
|
|
28
|
+
|
|
29
|
+
const connection = new Connection("http://localhost:8899", "confirmed");
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## CLI
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
solana-ts [options]
|
|
36
|
+
|
|
37
|
+
Options:
|
|
38
|
+
-H, --host <address> Bind address (default: "127.0.0.1")
|
|
39
|
+
-p, --port <number> HTTP/WS port (default: 8899)
|
|
40
|
+
-l, --ledger <path> SQLite database path (default: ~/.solana-ts/ledger.db)
|
|
41
|
+
--reset Clear ledger on start
|
|
42
|
+
--transaction-history <n> Max transactions to retain (default: 5000)
|
|
43
|
+
--log-level <level> error | warn | info | debug (default: "info")
|
|
44
|
+
-q, --quiet Suppress all output except errors
|
|
45
|
+
-V, --version Output version number
|
|
46
|
+
-h, --help Display help
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Examples:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Start with default settings
|
|
53
|
+
pnpm dev
|
|
54
|
+
|
|
55
|
+
# Bind to all interfaces on port 9000, fresh ledger each time
|
|
56
|
+
pnpm dev -- --host 0.0.0.0 --port 9000 --reset
|
|
57
|
+
|
|
58
|
+
# Use a specific ledger file
|
|
59
|
+
pnpm dev -- --ledger ./my-ledger.db
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Supported RPC methods
|
|
63
|
+
|
|
64
|
+
| Method | Notes |
|
|
65
|
+
|---|---|
|
|
66
|
+
| `getVersion` | |
|
|
67
|
+
| `getHealth` | |
|
|
68
|
+
| `getSlot` | |
|
|
69
|
+
| `getBlockHeight` | |
|
|
70
|
+
| `getLatestBlockhash` | |
|
|
71
|
+
| `isBlockhashValid` | |
|
|
72
|
+
| `getBalance` | |
|
|
73
|
+
| `getAccountInfo` | base64 encoding |
|
|
74
|
+
| `getMultipleAccounts` | base64 encoding |
|
|
75
|
+
| `getMinimumBalanceForRentExemption` | |
|
|
76
|
+
| `requestAirdrop` | |
|
|
77
|
+
| `sendTransaction` | base58 and base64 encoding |
|
|
78
|
+
| `simulateTransaction` | |
|
|
79
|
+
| `getSignatureStatuses` | |
|
|
80
|
+
| `getTransaction` | JSON-decoded format |
|
|
81
|
+
| `getEpochInfo` | |
|
|
82
|
+
| `getGenesisHash` | Static value |
|
|
83
|
+
| `getRecentBlockhash` | Legacy compat |
|
|
84
|
+
| `getFeeForMessage` | Returns 5000 |
|
|
85
|
+
| `getIdentity` | |
|
|
86
|
+
|
|
87
|
+
**WebSocket subscriptions:**
|
|
88
|
+
|
|
89
|
+
| Method | Notes |
|
|
90
|
+
|---|---|
|
|
91
|
+
| `signatureSubscribe` / `signatureUnsubscribe` | Required by `confirmTransaction` |
|
|
92
|
+
|
|
93
|
+
## Architecture
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
@solana/web3.js ─── HTTP JSON-RPC ──→ RPC Dispatcher ──→ Method Handlers
|
|
97
|
+
─── WebSocket ──────→ Subscription Mgr ─┘ │
|
|
98
|
+
SVM Engine
|
|
99
|
+
┌────┴────┐
|
|
100
|
+
LiteSVM SQLite
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Transactions execute synchronously inside LiteSVM. After each transaction, modified account state is persisted to SQLite. On restart, accounts and slot counter are restored from the database.
|
|
104
|
+
|
|
105
|
+
WebSocket runs on port+1 (e.g. 8900) to match the convention used by `@solana/web3.js`, which derives the WS URL as `rpc_port + 1`.
|
|
106
|
+
|
|
107
|
+
## Persistence
|
|
108
|
+
|
|
109
|
+
State is stored in a single SQLite file (default `~/.solana-ts/ledger.db`):
|
|
110
|
+
|
|
111
|
+
- **accounts** — address, lamports, data, owner, executable flag, rent epoch
|
|
112
|
+
- **transactions** — signature, slot, raw bytes, JSON metadata, block time
|
|
113
|
+
- **metadata** — key-value pairs (slot counter, etc.)
|
|
114
|
+
|
|
115
|
+
Use `--reset` to clear all state on startup.
|
|
116
|
+
|
|
117
|
+
## Tests
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
pnpm test
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Runs 36 tests covering basic RPC methods, airdrop/transfer flows, and persistence across server restarts (balance restore, slot resume, transaction history, `--reset` behavior, transacting on restored state).
|
|
124
|
+
|
|
125
|
+
## Project structure
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
src/
|
|
129
|
+
index.ts CLI entry point
|
|
130
|
+
server.ts HTTP + WebSocket server
|
|
131
|
+
engine.ts LiteSVM wrapper with persistence
|
|
132
|
+
persistence.ts SQLite storage layer
|
|
133
|
+
types.ts Shared types and logger
|
|
134
|
+
rpc/
|
|
135
|
+
dispatcher.ts JSON-RPC 2.0 routing
|
|
136
|
+
methods.ts RPC method handlers
|
|
137
|
+
subscriptions.ts WebSocket subscription manager
|
|
138
|
+
test.ts Integration tests
|
|
139
|
+
```
|
package/dist/engine.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type AccountInfo } from "@solana/web3.js";
|
|
2
|
+
import type { TransactionMeta, ServerConfig } from "./types.js";
|
|
3
|
+
type AccountInfoBytes = AccountInfo<Uint8Array>;
|
|
4
|
+
export interface SendTransactionResult {
|
|
5
|
+
signature: string;
|
|
6
|
+
slot: number;
|
|
7
|
+
meta: TransactionMeta;
|
|
8
|
+
}
|
|
9
|
+
export interface SimulateTransactionResult {
|
|
10
|
+
err: unknown | null;
|
|
11
|
+
logs: string[];
|
|
12
|
+
unitsConsumed: number;
|
|
13
|
+
}
|
|
14
|
+
export interface StoredTransaction {
|
|
15
|
+
signature: string;
|
|
16
|
+
slot: number;
|
|
17
|
+
blockTime: number;
|
|
18
|
+
meta: TransactionMeta;
|
|
19
|
+
data: Buffer;
|
|
20
|
+
}
|
|
21
|
+
type SignatureCallback = (err: unknown | null) => void;
|
|
22
|
+
export declare class SvmEngine {
|
|
23
|
+
private svm;
|
|
24
|
+
private persistence;
|
|
25
|
+
private slot;
|
|
26
|
+
private maxTxHistory;
|
|
27
|
+
private confirmedSignatures;
|
|
28
|
+
private signatureListeners;
|
|
29
|
+
private currentBlockhash;
|
|
30
|
+
private lastValidBlockHeight;
|
|
31
|
+
constructor(config: ServerConfig);
|
|
32
|
+
private restoreAccounts;
|
|
33
|
+
private refreshBlockhash;
|
|
34
|
+
private advanceSlot;
|
|
35
|
+
private persistAccounts;
|
|
36
|
+
private notifySignature;
|
|
37
|
+
getBalance(address: string): bigint | null;
|
|
38
|
+
getAccountInfo(address: string): AccountInfoBytes | null;
|
|
39
|
+
getLatestBlockhash(): {
|
|
40
|
+
blockhash: string;
|
|
41
|
+
lastValidBlockHeight: number;
|
|
42
|
+
};
|
|
43
|
+
getSlot(): number;
|
|
44
|
+
getBlockHeight(): number;
|
|
45
|
+
isBlockhashValid(blockhash: string): boolean;
|
|
46
|
+
getMinimumBalanceForRentExemption(dataLen: number): bigint;
|
|
47
|
+
requestAirdrop(address: string, lamports: bigint): SendTransactionResult;
|
|
48
|
+
sendTransaction(base64Tx: string): SendTransactionResult;
|
|
49
|
+
simulateTransaction(base64Tx: string): SimulateTransactionResult;
|
|
50
|
+
getSignatureStatuses(signatures: string[]): (null | {
|
|
51
|
+
slot: number;
|
|
52
|
+
confirmations: number | null;
|
|
53
|
+
err: unknown | null;
|
|
54
|
+
confirmationStatus: string;
|
|
55
|
+
})[];
|
|
56
|
+
getTransaction(signature: string): StoredTransaction | null;
|
|
57
|
+
onSignatureConfirmation(signature: string, callback: SignatureCallback): number;
|
|
58
|
+
removeSignatureListener(signature: string, index: number): void;
|
|
59
|
+
close(): void;
|
|
60
|
+
}
|
|
61
|
+
export {};
|
|
62
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAMA,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,KAAK,EAAc,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG5E,KAAK,gBAAgB,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;AAEhD,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,eAAe,CAAC;CACvB;AAED,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,KAAK,iBAAiB,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC;AAkCvD,qBAAa,SAAS;IACpB,OAAO,CAAC,GAAG,CAAU;IACrB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,mBAAmB,CAAqB;IAChD,OAAO,CAAC,kBAAkB,CAA0C;IACpE,OAAO,CAAC,gBAAgB,CAAc;IACtC,OAAO,CAAC,oBAAoB,CAAa;gBAE7B,MAAM,EAAE,YAAY;IAwBhC,OAAO,CAAC,eAAe;IAkBvB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,eAAe;IAsBvB,OAAO,CAAC,eAAe;IAWvB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAI1C,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAIxD,kBAAkB,IAAI;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,oBAAoB,EAAE,MAAM,CAAC;KAC9B;IAOD,OAAO,IAAI,MAAM;IAIjB,cAAc,IAAI,MAAM;IAIxB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAI5C,iCAAiC,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAI1D,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,qBAAqB;IAwCxE,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,qBAAqB;IA0DxD,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,yBAAyB;IAyBhE,oBAAoB,CAClB,UAAU,EAAE,MAAM,EAAE,GACnB,CAAC,IAAI,GAAG;QACT,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;QACpB,kBAAkB,EAAE,MAAM,CAAC;KAC5B,CAAC,EAAE;IA2BJ,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAa3D,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,GAAG,MAAM;IAa/E,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAU/D,KAAK,IAAI,IAAI;CAKd"}
|
package/dist/engine.js
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { LiteSVM, } from "litesvm";
|
|
2
|
+
import { PublicKey, VersionedTransaction, Transaction, } from "@solana/web3.js";
|
|
3
|
+
import bs58 from "bs58";
|
|
4
|
+
import { Persistence } from "./persistence.js";
|
|
5
|
+
import { log } from "./types.js";
|
|
6
|
+
function isFailedMeta(result) {
|
|
7
|
+
return typeof result.err === "function";
|
|
8
|
+
}
|
|
9
|
+
function parseTransactionBytes(bytes) {
|
|
10
|
+
const isVersioned = (bytes[0] & 0x80) !== 0;
|
|
11
|
+
if (isVersioned) {
|
|
12
|
+
const tx = VersionedTransaction.deserialize(bytes);
|
|
13
|
+
return {
|
|
14
|
+
tx,
|
|
15
|
+
accountKeys: [...tx.message.staticAccountKeys],
|
|
16
|
+
numSignatures: tx.signatures.length,
|
|
17
|
+
isVersioned: true,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const tx = Transaction.from(Buffer.from(bytes));
|
|
21
|
+
const msg = tx.compileMessage();
|
|
22
|
+
return {
|
|
23
|
+
tx,
|
|
24
|
+
accountKeys: [...msg.accountKeys],
|
|
25
|
+
numSignatures: tx.signatures.length,
|
|
26
|
+
isVersioned: false,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export class SvmEngine {
|
|
30
|
+
svm;
|
|
31
|
+
persistence;
|
|
32
|
+
slot;
|
|
33
|
+
maxTxHistory;
|
|
34
|
+
confirmedSignatures = new Set();
|
|
35
|
+
signatureListeners = new Map();
|
|
36
|
+
currentBlockhash = "";
|
|
37
|
+
lastValidBlockHeight = 0;
|
|
38
|
+
constructor(config) {
|
|
39
|
+
this.persistence = new Persistence(config.ledgerPath);
|
|
40
|
+
this.maxTxHistory = config.transactionHistory;
|
|
41
|
+
if (config.reset) {
|
|
42
|
+
this.persistence.clear();
|
|
43
|
+
log.info("Ledger cleared");
|
|
44
|
+
}
|
|
45
|
+
this.svm = new LiteSVM();
|
|
46
|
+
this.svm.withTransactionHistory(BigInt(config.transactionHistory));
|
|
47
|
+
const savedSlot = this.persistence.getMeta("slot");
|
|
48
|
+
this.slot = savedSlot ? parseInt(savedSlot, 10) : 1;
|
|
49
|
+
if (this.slot > 1) {
|
|
50
|
+
this.svm.warpToSlot(BigInt(this.slot));
|
|
51
|
+
}
|
|
52
|
+
this.restoreAccounts();
|
|
53
|
+
this.refreshBlockhash();
|
|
54
|
+
log.info(`SVM engine initialized at slot ${this.slot}`);
|
|
55
|
+
}
|
|
56
|
+
restoreAccounts() {
|
|
57
|
+
const accounts = this.persistence.getAllAccounts();
|
|
58
|
+
if (accounts.length === 0)
|
|
59
|
+
return;
|
|
60
|
+
for (const row of accounts) {
|
|
61
|
+
const pubkey = new PublicKey(row.address);
|
|
62
|
+
const info = {
|
|
63
|
+
data: row.data ? new Uint8Array(row.data) : new Uint8Array(0),
|
|
64
|
+
executable: row.executable === 1,
|
|
65
|
+
lamports: Number(row.lamports),
|
|
66
|
+
owner: new PublicKey(row.owner),
|
|
67
|
+
rentEpoch: row.rent_epoch,
|
|
68
|
+
};
|
|
69
|
+
this.svm.setAccount(pubkey, info);
|
|
70
|
+
}
|
|
71
|
+
log.info(`Restored ${accounts.length} accounts from ledger`);
|
|
72
|
+
}
|
|
73
|
+
refreshBlockhash() {
|
|
74
|
+
this.currentBlockhash = this.svm.latestBlockhash();
|
|
75
|
+
this.lastValidBlockHeight = this.slot + 300;
|
|
76
|
+
}
|
|
77
|
+
advanceSlot() {
|
|
78
|
+
this.slot++;
|
|
79
|
+
this.persistence.setMeta("slot", String(this.slot));
|
|
80
|
+
}
|
|
81
|
+
persistAccounts(keys) {
|
|
82
|
+
const rows = [];
|
|
83
|
+
for (const key of keys) {
|
|
84
|
+
const info = this.svm.getAccount(key);
|
|
85
|
+
if (info) {
|
|
86
|
+
rows.push({
|
|
87
|
+
address: key.toBase58(),
|
|
88
|
+
lamports: BigInt(info.lamports),
|
|
89
|
+
data: info.data.length > 0 ? Buffer.from(info.data) : null,
|
|
90
|
+
owner: info.owner.toBase58(),
|
|
91
|
+
executable: info.executable ? 1 : 0,
|
|
92
|
+
rent_epoch: info.rentEpoch ?? 0,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
this.persistence.deleteAccount(key.toBase58());
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (rows.length > 0) {
|
|
100
|
+
this.persistence.upsertAccounts(rows);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
notifySignature(signature, err) {
|
|
104
|
+
this.confirmedSignatures.add(signature);
|
|
105
|
+
const listeners = this.signatureListeners.get(signature);
|
|
106
|
+
if (listeners) {
|
|
107
|
+
for (const cb of listeners)
|
|
108
|
+
cb(err);
|
|
109
|
+
this.signatureListeners.delete(signature);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// --- Public API ---
|
|
113
|
+
getBalance(address) {
|
|
114
|
+
return this.svm.getBalance(new PublicKey(address));
|
|
115
|
+
}
|
|
116
|
+
getAccountInfo(address) {
|
|
117
|
+
return this.svm.getAccount(new PublicKey(address));
|
|
118
|
+
}
|
|
119
|
+
getLatestBlockhash() {
|
|
120
|
+
return {
|
|
121
|
+
blockhash: this.currentBlockhash,
|
|
122
|
+
lastValidBlockHeight: this.lastValidBlockHeight,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
getSlot() {
|
|
126
|
+
return this.slot;
|
|
127
|
+
}
|
|
128
|
+
getBlockHeight() {
|
|
129
|
+
return this.slot;
|
|
130
|
+
}
|
|
131
|
+
isBlockhashValid(blockhash) {
|
|
132
|
+
return blockhash === this.currentBlockhash;
|
|
133
|
+
}
|
|
134
|
+
getMinimumBalanceForRentExemption(dataLen) {
|
|
135
|
+
return this.svm.minimumBalanceForRentExemption(BigInt(dataLen));
|
|
136
|
+
}
|
|
137
|
+
requestAirdrop(address, lamports) {
|
|
138
|
+
const pubkey = new PublicKey(address);
|
|
139
|
+
const result = this.svm.airdrop(pubkey, lamports);
|
|
140
|
+
if (!result) {
|
|
141
|
+
throw new Error("Airdrop returned null");
|
|
142
|
+
}
|
|
143
|
+
const txErr = isFailedMeta(result) ? result.err().toString() : null;
|
|
144
|
+
const meta = isFailedMeta(result) ? result.meta() : result;
|
|
145
|
+
const sigBytes = meta.signature();
|
|
146
|
+
const signature = bs58.encode(sigBytes);
|
|
147
|
+
this.advanceSlot();
|
|
148
|
+
this.persistAccounts([pubkey]);
|
|
149
|
+
const txMeta = {
|
|
150
|
+
fee: 0,
|
|
151
|
+
err: txErr,
|
|
152
|
+
logs: meta.logs(),
|
|
153
|
+
computeUnitsConsumed: Number(meta.computeUnitsConsumed()),
|
|
154
|
+
preBalances: [],
|
|
155
|
+
postBalances: [],
|
|
156
|
+
};
|
|
157
|
+
this.persistence.upsertTransaction({
|
|
158
|
+
signature,
|
|
159
|
+
slot: this.slot,
|
|
160
|
+
data: Buffer.alloc(0),
|
|
161
|
+
meta: JSON.stringify(txMeta),
|
|
162
|
+
block_time: Math.floor(Date.now() / 1000),
|
|
163
|
+
});
|
|
164
|
+
this.notifySignature(signature, txErr);
|
|
165
|
+
this.refreshBlockhash();
|
|
166
|
+
log.info(`Airdrop ${lamports} lamports to ${address} -> ${signature}`);
|
|
167
|
+
return { signature, slot: this.slot, meta: txMeta };
|
|
168
|
+
}
|
|
169
|
+
sendTransaction(base64Tx) {
|
|
170
|
+
const bytes = Buffer.from(base64Tx, "base64");
|
|
171
|
+
const { tx, accountKeys, numSignatures } = parseTransactionBytes(bytes);
|
|
172
|
+
const preBalances = accountKeys.map((k) => {
|
|
173
|
+
const bal = this.svm.getBalance(k);
|
|
174
|
+
return bal !== null ? Number(bal) : 0;
|
|
175
|
+
});
|
|
176
|
+
const result = this.svm.sendTransaction(tx);
|
|
177
|
+
const failed = isFailedMeta(result);
|
|
178
|
+
const txErr = failed ? result.err().toString() : null;
|
|
179
|
+
const meta = failed ? result.meta() : result;
|
|
180
|
+
const sigBytes = meta.signature();
|
|
181
|
+
const signature = bs58.encode(sigBytes);
|
|
182
|
+
const postBalances = accountKeys.map((k) => {
|
|
183
|
+
const bal = this.svm.getBalance(k);
|
|
184
|
+
return bal !== null ? Number(bal) : 0;
|
|
185
|
+
});
|
|
186
|
+
const fee = failed
|
|
187
|
+
? 0
|
|
188
|
+
: numSignatures * 5000;
|
|
189
|
+
this.advanceSlot();
|
|
190
|
+
this.persistAccounts(accountKeys);
|
|
191
|
+
const txMeta = {
|
|
192
|
+
fee,
|
|
193
|
+
err: txErr,
|
|
194
|
+
logs: meta.logs(),
|
|
195
|
+
computeUnitsConsumed: Number(meta.computeUnitsConsumed()),
|
|
196
|
+
preBalances,
|
|
197
|
+
postBalances,
|
|
198
|
+
};
|
|
199
|
+
this.persistence.upsertTransaction({
|
|
200
|
+
signature,
|
|
201
|
+
slot: this.slot,
|
|
202
|
+
data: Buffer.from(bytes),
|
|
203
|
+
meta: JSON.stringify(txMeta),
|
|
204
|
+
block_time: Math.floor(Date.now() / 1000),
|
|
205
|
+
});
|
|
206
|
+
this.persistence.pruneTransactions(this.maxTxHistory);
|
|
207
|
+
this.notifySignature(signature, txErr);
|
|
208
|
+
this.refreshBlockhash();
|
|
209
|
+
if (failed) {
|
|
210
|
+
log.warn(`Transaction failed: ${signature} - ${txErr}`);
|
|
211
|
+
throw new Error(`Transaction simulation failed: ${txErr}`);
|
|
212
|
+
}
|
|
213
|
+
log.info(`Transaction confirmed: ${signature}`);
|
|
214
|
+
return { signature, slot: this.slot, meta: txMeta };
|
|
215
|
+
}
|
|
216
|
+
simulateTransaction(base64Tx) {
|
|
217
|
+
const bytes = Buffer.from(base64Tx, "base64");
|
|
218
|
+
const { tx } = parseTransactionBytes(bytes);
|
|
219
|
+
const result = this.svm.simulateTransaction(tx);
|
|
220
|
+
if ("err" in result && typeof result.err === "function") {
|
|
221
|
+
const failed = result;
|
|
222
|
+
const meta = failed.meta();
|
|
223
|
+
return {
|
|
224
|
+
err: failed.err().toString(),
|
|
225
|
+
logs: meta.logs(),
|
|
226
|
+
unitsConsumed: Number(meta.computeUnitsConsumed()),
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
const simInfo = result;
|
|
230
|
+
const meta = simInfo.meta();
|
|
231
|
+
return {
|
|
232
|
+
err: null,
|
|
233
|
+
logs: meta.logs(),
|
|
234
|
+
unitsConsumed: Number(meta.computeUnitsConsumed()),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
getSignatureStatuses(signatures) {
|
|
238
|
+
return signatures.map((sig) => {
|
|
239
|
+
if (this.confirmedSignatures.has(sig)) {
|
|
240
|
+
const stored = this.persistence.getTransaction(sig);
|
|
241
|
+
const meta = stored?.meta ? JSON.parse(stored.meta) : null;
|
|
242
|
+
return {
|
|
243
|
+
slot: stored?.slot ?? this.slot,
|
|
244
|
+
confirmations: null,
|
|
245
|
+
err: meta?.err ?? null,
|
|
246
|
+
confirmationStatus: "confirmed",
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
const stored = this.persistence.getTransaction(sig);
|
|
250
|
+
if (stored) {
|
|
251
|
+
this.confirmedSignatures.add(sig);
|
|
252
|
+
const meta = stored.meta ? JSON.parse(stored.meta) : null;
|
|
253
|
+
return {
|
|
254
|
+
slot: stored.slot,
|
|
255
|
+
confirmations: null,
|
|
256
|
+
err: meta?.err ?? null,
|
|
257
|
+
confirmationStatus: "confirmed",
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
getTransaction(signature) {
|
|
264
|
+
const stored = this.persistence.getTransaction(signature);
|
|
265
|
+
if (!stored)
|
|
266
|
+
return null;
|
|
267
|
+
return {
|
|
268
|
+
signature: stored.signature,
|
|
269
|
+
slot: stored.slot,
|
|
270
|
+
blockTime: stored.block_time,
|
|
271
|
+
meta: stored.meta ? JSON.parse(stored.meta) : null,
|
|
272
|
+
data: stored.data,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
onSignatureConfirmation(signature, callback) {
|
|
276
|
+
if (this.confirmedSignatures.has(signature)) {
|
|
277
|
+
const stored = this.persistence.getTransaction(signature);
|
|
278
|
+
const meta = stored?.meta ? JSON.parse(stored.meta) : null;
|
|
279
|
+
process.nextTick(() => callback(meta?.err ?? null));
|
|
280
|
+
return -1;
|
|
281
|
+
}
|
|
282
|
+
const listeners = this.signatureListeners.get(signature) ?? [];
|
|
283
|
+
listeners.push(callback);
|
|
284
|
+
this.signatureListeners.set(signature, listeners);
|
|
285
|
+
return listeners.length - 1;
|
|
286
|
+
}
|
|
287
|
+
removeSignatureListener(signature, index) {
|
|
288
|
+
const listeners = this.signatureListeners.get(signature);
|
|
289
|
+
if (listeners && index >= 0 && index < listeners.length) {
|
|
290
|
+
listeners.splice(index, 1);
|
|
291
|
+
if (listeners.length === 0) {
|
|
292
|
+
this.signatureListeners.delete(signature);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
close() {
|
|
297
|
+
this.persistence.setMeta("slot", String(this.slot));
|
|
298
|
+
this.persistence.close();
|
|
299
|
+
log.info("SVM engine shut down");
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import { createServer } from "./server.js";
|
|
7
|
+
import { setLogLevel } from "./types.js";
|
|
8
|
+
const DEFAULT_LEDGER_DIR = path.join(os.homedir(), ".solana-ts");
|
|
9
|
+
const DEFAULT_LEDGER_PATH = path.join(DEFAULT_LEDGER_DIR, "ledger.db");
|
|
10
|
+
const program = new Command();
|
|
11
|
+
program
|
|
12
|
+
.name("solana-ts")
|
|
13
|
+
.description("Zero-idle-CPU local Solana RPC simulator powered by LiteSVM")
|
|
14
|
+
.version("0.1.0")
|
|
15
|
+
.option("-H, --host <address>", "Bind address", "127.0.0.1")
|
|
16
|
+
.option("-p, --port <number>", "HTTP/WS port", "8899")
|
|
17
|
+
.option("-l, --ledger <path>", "SQLite database path", DEFAULT_LEDGER_PATH)
|
|
18
|
+
.option("--reset", "Clear ledger on start", false)
|
|
19
|
+
.option("--transaction-history <number>", "Max transactions to retain", "5000")
|
|
20
|
+
.option("--log-level <level>", "Log level: error | warn | info | debug", "info")
|
|
21
|
+
.option("-q, --quiet", "Suppress all output except errors", false)
|
|
22
|
+
.action(async (opts) => {
|
|
23
|
+
const logLevel = opts.quiet
|
|
24
|
+
? "error"
|
|
25
|
+
: opts.logLevel;
|
|
26
|
+
setLogLevel(logLevel);
|
|
27
|
+
const ledgerPath = path.resolve(opts.ledger);
|
|
28
|
+
const ledgerDir = path.dirname(ledgerPath);
|
|
29
|
+
if (!fs.existsSync(ledgerDir)) {
|
|
30
|
+
fs.mkdirSync(ledgerDir, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
const config = {
|
|
33
|
+
host: opts.host,
|
|
34
|
+
port: parseInt(opts.port, 10),
|
|
35
|
+
ledgerPath,
|
|
36
|
+
reset: opts.reset,
|
|
37
|
+
transactionHistory: parseInt(opts.transactionHistory, 10),
|
|
38
|
+
logLevel,
|
|
39
|
+
};
|
|
40
|
+
const server = createServer(config);
|
|
41
|
+
const shutdown = async () => {
|
|
42
|
+
console.log("\nShutting down...");
|
|
43
|
+
await server.stop();
|
|
44
|
+
process.exit(0);
|
|
45
|
+
};
|
|
46
|
+
process.on("SIGINT", shutdown);
|
|
47
|
+
process.on("SIGTERM", shutdown);
|
|
48
|
+
await server.start();
|
|
49
|
+
});
|
|
50
|
+
program.parse();
|
|
51
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGzC,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AACjE,MAAM,mBAAmB,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC;AAEvE,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CAAC,6DAA6D,CAAC;KAC1E,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,sBAAsB,EAAE,cAAc,EAAE,WAAW,CAAC;KAC3D,MAAM,CAAC,qBAAqB,EAAE,cAAc,EAAE,MAAM,CAAC;KACrD,MAAM,CACL,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,CACpB;KACA,MAAM,CAAC,SAAS,EAAE,uBAAuB,EAAE,KAAK,CAAC;KACjD,MAAM,CACL,gCAAgC,EAChC,4BAA4B,EAC5B,MAAM,CACP;KACA,MAAM,CACL,qBAAqB,EACrB,wCAAwC,EACxC,MAAM,CACP;KACA,MAAM,CAAC,aAAa,EAAE,mCAAmC,EAAE,KAAK,CAAC;KACjE,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK;QACzB,CAAC,CAAC,OAAO;QACT,CAAC,CAAE,IAAI,CAAC,QAAqC,CAAC;IAChD,WAAW,CAAC,QAAQ,CAAC,CAAC;IAEtB,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC3C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,MAAM,GAAiB;QAC3B,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QAC7B,UAAU;QACV,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,kBAAkB,EAAE,QAAQ,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,CAAC;QACzD,QAAQ;KACT,CAAC;IAEF,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAEpC,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAClC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAEhC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;AACvB,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AccountRow, TransactionRow } from "./types.js";
|
|
2
|
+
export declare class Persistence {
|
|
3
|
+
private db;
|
|
4
|
+
private stmts;
|
|
5
|
+
constructor(dbPath: string);
|
|
6
|
+
private migrate;
|
|
7
|
+
private prepareStatements;
|
|
8
|
+
upsertAccount(account: AccountRow): void;
|
|
9
|
+
upsertAccounts(accounts: AccountRow[]): void;
|
|
10
|
+
getAccount(address: string): AccountRow | undefined;
|
|
11
|
+
getAllAccounts(): AccountRow[];
|
|
12
|
+
deleteAccount(address: string): void;
|
|
13
|
+
upsertTransaction(tx: TransactionRow): void;
|
|
14
|
+
getTransaction(signature: string): TransactionRow | undefined;
|
|
15
|
+
pruneTransactions(maxCount: number): void;
|
|
16
|
+
setMeta(key: string, value: string): void;
|
|
17
|
+
getMeta(key: string): string | undefined;
|
|
18
|
+
clear(): void;
|
|
19
|
+
close(): void;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=persistence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../src/persistence.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE7D,qBAAa,WAAW;IACtB,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,KAAK,CAWX;gBAEU,MAAM,EAAE,MAAM;IAQ1B,OAAO,CAAC,OAAO;IA0Bf,OAAO,CAAC,iBAAiB;IAmDzB,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI;IAWxC,cAAc,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,IAAI;IAO5C,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAQnD,cAAc,IAAI,UAAU,EAAE;IAU9B,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAMpC,iBAAiB,CAAC,EAAE,EAAE,cAAc,GAAG,IAAI;IAU3C,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAI7D,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IASzC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAIzC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAOxC,KAAK,IAAI,IAAI;IAMb,KAAK,IAAI,IAAI;CAGd"}
|