solforge 0.1.7 → 0.2.1
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 +367 -393
- package/docs/API.md +379 -0
- package/docs/CONFIGURATION.md +407 -0
- package/docs/bun-single-file-executable.md +585 -0
- package/docs/cli-plan.md +154 -0
- package/docs/data-indexing-plan.md +214 -0
- package/docs/gui-roadmap.md +202 -0
- package/package.json +38 -51
- package/server/index.ts +5 -0
- package/server/lib/base58.ts +33 -0
- package/server/lib/faucet.ts +110 -0
- package/server/lib/spl-token.ts +57 -0
- package/server/methods/TEMPLATE.md +117 -0
- package/server/methods/account/get-account-info.ts +90 -0
- package/server/methods/account/get-balance.ts +27 -0
- package/server/methods/account/get-multiple-accounts.ts +83 -0
- package/server/methods/account/get-parsed-account-info.ts +21 -0
- package/server/methods/account/index.ts +12 -0
- package/server/methods/account/parsers/index.ts +52 -0
- package/server/methods/account/parsers/loader-upgradeable.ts +66 -0
- package/server/methods/account/parsers/spl-token.ts +237 -0
- package/server/methods/account/parsers/system.ts +4 -0
- package/server/methods/account/request-airdrop.ts +219 -0
- package/server/methods/admin/adopt-mint-authority.ts +94 -0
- package/server/methods/admin/clone-program-accounts.ts +55 -0
- package/server/methods/admin/clone-program.ts +152 -0
- package/server/methods/admin/clone-token-accounts.ts +117 -0
- package/server/methods/admin/clone-token-mint.ts +82 -0
- package/server/methods/admin/create-mint.ts +114 -0
- package/server/methods/admin/create-token-account.ts +137 -0
- package/server/methods/admin/helpers.ts +70 -0
- package/server/methods/admin/index.ts +10 -0
- package/server/methods/admin/list-mints.ts +21 -0
- package/server/methods/admin/load-program.ts +52 -0
- package/server/methods/admin/mint-to.ts +278 -0
- package/server/methods/block/get-block-height.ts +5 -0
- package/server/methods/block/get-block.ts +35 -0
- package/server/methods/block/get-blocks-with-limit.ts +23 -0
- package/server/methods/block/get-latest-blockhash.ts +12 -0
- package/server/methods/block/get-slot.ts +5 -0
- package/server/methods/block/index.ts +6 -0
- package/server/methods/block/is-blockhash-valid.ts +23 -0
- package/server/methods/epoch/get-cluster-nodes.ts +17 -0
- package/server/methods/epoch/get-epoch-info.ts +16 -0
- package/server/methods/epoch/get-epoch-schedule.ts +15 -0
- package/server/methods/epoch/get-highest-snapshot-slot.ts +9 -0
- package/server/methods/epoch/get-leader-schedule.ts +8 -0
- package/server/methods/epoch/get-max-retransmit-slot.ts +9 -0
- package/server/methods/epoch/get-max-shred-insert-slot.ts +9 -0
- package/server/methods/epoch/get-slot-leader.ts +6 -0
- package/server/methods/epoch/get-slot-leaders.ts +9 -0
- package/server/methods/epoch/get-stake-activation.ts +9 -0
- package/server/methods/epoch/get-stake-minimum-delegation.ts +9 -0
- package/server/methods/epoch/get-vote-accounts.ts +19 -0
- package/server/methods/epoch/index.ts +13 -0
- package/server/methods/epoch/minimum-ledger-slot.ts +5 -0
- package/server/methods/fee/get-fee-calculator-for-blockhash.ts +12 -0
- package/server/methods/fee/get-fee-for-message.ts +8 -0
- package/server/methods/fee/get-fee-rate-governor.ts +16 -0
- package/server/methods/fee/get-fees.ts +14 -0
- package/server/methods/fee/get-recent-prioritization-fees.ts +22 -0
- package/server/methods/fee/index.ts +5 -0
- package/server/methods/get-address-lookup-table.ts +31 -0
- package/server/methods/index.ts +265 -0
- package/server/methods/performance/get-recent-performance-samples.ts +25 -0
- package/server/methods/performance/get-transaction-count.ts +5 -0
- package/server/methods/performance/index.ts +2 -0
- package/server/methods/program/get-block-commitment.ts +9 -0
- package/server/methods/program/get-block-production.ts +14 -0
- package/server/methods/program/get-block-time.ts +21 -0
- package/server/methods/program/get-blocks.ts +11 -0
- package/server/methods/program/get-first-available-block.ts +9 -0
- package/server/methods/program/get-genesis-hash.ts +6 -0
- package/server/methods/program/get-identity.ts +6 -0
- package/server/methods/program/get-inflation-governor.ts +15 -0
- package/server/methods/program/get-inflation-rate.ts +10 -0
- package/server/methods/program/get-inflation-reward.ts +12 -0
- package/server/methods/program/get-largest-accounts.ts +8 -0
- package/server/methods/program/get-parsed-program-accounts.ts +12 -0
- package/server/methods/program/get-parsed-token-accounts-by-delegate.ts +12 -0
- package/server/methods/program/get-parsed-token-accounts-by-owner.ts +12 -0
- package/server/methods/program/get-program-accounts.ts +221 -0
- package/server/methods/program/get-supply.ts +13 -0
- package/server/methods/program/get-token-account-balance.ts +64 -0
- package/server/methods/program/get-token-accounts-by-delegate.ts +81 -0
- package/server/methods/program/get-token-accounts-by-owner.ts +390 -0
- package/server/methods/program/get-token-largest-accounts.ts +80 -0
- package/server/methods/program/get-token-supply.ts +38 -0
- package/server/methods/program/index.ts +21 -0
- package/server/methods/solforge/index.ts +155 -0
- package/server/methods/system/get-health.ts +5 -0
- package/server/methods/system/get-minimum-balance-for-rent-exemption.ts +13 -0
- package/server/methods/system/get-version.ts +9 -0
- package/server/methods/system/index.ts +3 -0
- package/server/methods/transaction/get-confirmed-transaction.ts +11 -0
- package/server/methods/transaction/get-parsed-transaction.ts +21 -0
- package/server/methods/transaction/get-signature-statuses.ts +72 -0
- package/server/methods/transaction/get-signatures-for-address.ts +45 -0
- package/server/methods/transaction/get-transaction.ts +428 -0
- package/server/methods/transaction/index.ts +7 -0
- package/server/methods/transaction/send-transaction.ts +232 -0
- package/server/methods/transaction/simulate-transaction.ts +56 -0
- package/server/rpc-server.ts +474 -0
- package/server/types.ts +74 -0
- package/server/ws-server.ts +171 -0
- package/src/cli/bootstrap.ts +67 -0
- package/src/cli/commands/airdrop.ts +37 -0
- package/src/cli/commands/config.ts +39 -0
- package/src/cli/commands/mint.ts +187 -0
- package/src/cli/commands/program-clone.ts +124 -0
- package/src/cli/commands/program-load.ts +64 -0
- package/src/cli/commands/rpc-start.ts +46 -0
- package/src/cli/commands/token-adopt-authority.ts +37 -0
- package/src/cli/commands/token-clone.ts +113 -0
- package/src/cli/commands/token-create.ts +81 -0
- package/src/cli/main.ts +130 -0
- package/src/cli/run-solforge.ts +98 -0
- package/src/cli/setup-utils.ts +54 -0
- package/src/cli/setup-wizard.ts +256 -0
- package/src/cli/utils/args.ts +15 -0
- package/src/config/index.ts +130 -0
- package/src/db/index.ts +83 -0
- package/src/db/schema/accounts.ts +23 -0
- package/src/db/schema/address-signatures.ts +31 -0
- package/src/db/schema/index.ts +5 -0
- package/src/db/schema/meta-kv.ts +9 -0
- package/src/db/schema/transactions.ts +29 -0
- package/src/db/schema/tx-accounts.ts +33 -0
- package/src/db/tx-store.ts +229 -0
- package/src/gui/public/app.css +1 -0
- package/src/gui/public/build/main.css +1 -0
- package/src/gui/public/build/main.js +303 -0
- package/src/gui/public/build/main.js.txt +231 -0
- package/src/gui/public/index.html +19 -0
- package/src/gui/server.ts +297 -0
- package/src/gui/src/api.ts +127 -0
- package/src/gui/src/app.tsx +390 -0
- package/src/gui/src/components/airdrop-mint-form.tsx +216 -0
- package/src/gui/src/components/clone-program-modal.tsx +183 -0
- package/src/gui/src/components/clone-token-modal.tsx +211 -0
- package/src/gui/src/components/modal.tsx +127 -0
- package/src/gui/src/components/programs-panel.tsx +112 -0
- package/src/gui/src/components/status-panel.tsx +122 -0
- package/src/gui/src/components/tokens-panel.tsx +116 -0
- package/src/gui/src/hooks/use-interval.ts +17 -0
- package/src/gui/src/index.css +529 -0
- package/src/gui/src/main.tsx +17 -0
- package/src/migrations-bundled.ts +17 -0
- package/src/rpc/start.ts +44 -0
- package/scripts/postinstall.cjs +0 -103
- package/tsconfig.json +0 -28
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RpcMethodHandler } from "../../types";
|
|
2
|
+
import { getTransaction } from "./get-transaction";
|
|
3
|
+
|
|
4
|
+
export const getConfirmedTransaction: RpcMethodHandler = async (
|
|
5
|
+
id,
|
|
6
|
+
params,
|
|
7
|
+
context,
|
|
8
|
+
) => {
|
|
9
|
+
// Alias to getTransaction for older clients
|
|
10
|
+
return getTransaction(id, params, context);
|
|
11
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { RpcMethodHandler } from "../../types";
|
|
2
|
+
import { getTransaction } from "./get-transaction";
|
|
3
|
+
|
|
4
|
+
export const getParsedTransaction: RpcMethodHandler = async (
|
|
5
|
+
id,
|
|
6
|
+
params,
|
|
7
|
+
context,
|
|
8
|
+
) => {
|
|
9
|
+
const [signature, config] = params || [];
|
|
10
|
+
const cfg = { ...(config || {}), encoding: "jsonParsed" };
|
|
11
|
+
try {
|
|
12
|
+
return await getTransaction(id, [signature, cfg], context);
|
|
13
|
+
} catch (error: any) {
|
|
14
|
+
return context.createErrorResponse(
|
|
15
|
+
id,
|
|
16
|
+
-32603,
|
|
17
|
+
"Internal error",
|
|
18
|
+
error.message,
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { RpcMethodHandler } from "../../types";
|
|
2
|
+
|
|
3
|
+
export const getSignatureStatuses: RpcMethodHandler = async (
|
|
4
|
+
id,
|
|
5
|
+
params,
|
|
6
|
+
context,
|
|
7
|
+
) => {
|
|
8
|
+
const [signatures] = params;
|
|
9
|
+
|
|
10
|
+
let persisted: Map<string, { slot: number; err: any | null }> = new Map();
|
|
11
|
+
try {
|
|
12
|
+
persisted = (await context.store?.getStatuses(signatures)) || new Map();
|
|
13
|
+
} catch {}
|
|
14
|
+
|
|
15
|
+
const statuses = signatures.map((sig: string) => {
|
|
16
|
+
try {
|
|
17
|
+
// Prefer locally recorded transactions for reliability with CLI tooling
|
|
18
|
+
const rec = context.getRecordedTransaction(sig);
|
|
19
|
+
if (rec) {
|
|
20
|
+
const errVal: any = rec.err ?? null;
|
|
21
|
+
const status = errVal ? { Err: errVal } : { Ok: null };
|
|
22
|
+
return {
|
|
23
|
+
slot: rec.slot,
|
|
24
|
+
confirmations: errVal ? 0 : null,
|
|
25
|
+
err: errVal,
|
|
26
|
+
confirmationStatus: errVal ? "processed" : "finalized",
|
|
27
|
+
status,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const db = persisted.get(sig);
|
|
31
|
+
if (db) {
|
|
32
|
+
const errVal: any = db.err ?? null;
|
|
33
|
+
const status = errVal ? { Err: errVal } : { Ok: null };
|
|
34
|
+
return {
|
|
35
|
+
slot: db.slot,
|
|
36
|
+
confirmations: errVal ? 0 : null,
|
|
37
|
+
err: errVal,
|
|
38
|
+
confirmationStatus: errVal ? "processed" : "finalized",
|
|
39
|
+
status,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const sigBytes = context.decodeBase58(sig);
|
|
44
|
+
const tx = (context.svm as any).getTransaction(sigBytes);
|
|
45
|
+
if (!tx) return null;
|
|
46
|
+
|
|
47
|
+
let errVal: any = null;
|
|
48
|
+
try {
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
|
+
errVal = "err" in tx ? (tx as any).err() : null;
|
|
51
|
+
} catch {
|
|
52
|
+
errVal = null;
|
|
53
|
+
}
|
|
54
|
+
const status = errVal ? { Err: errVal } : { Ok: null };
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
slot: Number(context.slot),
|
|
58
|
+
confirmations: errVal ? 0 : null,
|
|
59
|
+
err: errVal,
|
|
60
|
+
confirmationStatus: errVal ? "processed" : "finalized",
|
|
61
|
+
status,
|
|
62
|
+
};
|
|
63
|
+
} catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return context.createSuccessResponse(id, {
|
|
69
|
+
context: { slot: Number(context.slot) },
|
|
70
|
+
value: statuses,
|
|
71
|
+
});
|
|
72
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import type { RpcMethodHandler } from "../../types";
|
|
3
|
+
|
|
4
|
+
export const getSignaturesForAddress: RpcMethodHandler = async (
|
|
5
|
+
id,
|
|
6
|
+
params,
|
|
7
|
+
context,
|
|
8
|
+
) => {
|
|
9
|
+
try {
|
|
10
|
+
const [address, config] = params || [];
|
|
11
|
+
if (typeof address !== "string") throw new Error("Invalid address");
|
|
12
|
+
// Validate pubkey
|
|
13
|
+
new PublicKey(address);
|
|
14
|
+
const limit = Math.max(1, Math.min(Number(config?.limit ?? 1000), 1000));
|
|
15
|
+
const before =
|
|
16
|
+
typeof config?.before === "string" ? config.before : undefined;
|
|
17
|
+
const until = typeof config?.until === "string" ? config.until : undefined;
|
|
18
|
+
|
|
19
|
+
if (!context.store) return context.createSuccessResponse(id, []);
|
|
20
|
+
try {
|
|
21
|
+
const entries = await context.store.getSignaturesForAddress(address, {
|
|
22
|
+
before,
|
|
23
|
+
until,
|
|
24
|
+
limit,
|
|
25
|
+
});
|
|
26
|
+
return context.createSuccessResponse(id, entries);
|
|
27
|
+
} catch (e) {
|
|
28
|
+
try {
|
|
29
|
+
console.warn("[rpc] getSignaturesForAddress: db read failed", e);
|
|
30
|
+
} catch {}
|
|
31
|
+
// Graceful fallback: return empty list instead of error
|
|
32
|
+
return context.createSuccessResponse(id, []);
|
|
33
|
+
}
|
|
34
|
+
} catch (error: any) {
|
|
35
|
+
try {
|
|
36
|
+
console.error("[rpc] getSignaturesForAddress error", error);
|
|
37
|
+
} catch {}
|
|
38
|
+
return context.createErrorResponse(
|
|
39
|
+
id,
|
|
40
|
+
-32603,
|
|
41
|
+
"Internal error",
|
|
42
|
+
error.message,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { VersionedTransaction } from "@solana/web3.js";
|
|
2
|
+
import type { RpcMethodHandler } from "../../types";
|
|
3
|
+
|
|
4
|
+
export const getTransaction: RpcMethodHandler = async (id, params, context) => {
|
|
5
|
+
const [signature, config] = params || [];
|
|
6
|
+
const encoding = config?.encoding ?? "json";
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
const rec = context.getRecordedTransaction(signature);
|
|
10
|
+
if (rec) {
|
|
11
|
+
const tx = rec.tx as any;
|
|
12
|
+
if (encoding === "base64") {
|
|
13
|
+
const raw = Buffer.from(tx.serialize()).toString("base64");
|
|
14
|
+
// Top-level version is required by some clients
|
|
15
|
+
const isV0 =
|
|
16
|
+
typeof (tx.message as any)?.version === "number"
|
|
17
|
+
? (tx.message as any).version === 0
|
|
18
|
+
: true;
|
|
19
|
+
return context.createSuccessResponse(id, {
|
|
20
|
+
slot: rec.slot,
|
|
21
|
+
transaction: [raw, "base64"],
|
|
22
|
+
version: isV0 ? 0 : "legacy",
|
|
23
|
+
meta: {
|
|
24
|
+
status: rec.err ? { Err: rec.err } : { Ok: null },
|
|
25
|
+
err: rec.err ?? null,
|
|
26
|
+
fee: rec.fee,
|
|
27
|
+
loadedAddresses: { writable: [], readonly: [] },
|
|
28
|
+
preBalances: Array.isArray(rec.preBalances) ? rec.preBalances : [],
|
|
29
|
+
postBalances: Array.isArray(rec.postBalances)
|
|
30
|
+
? rec.postBalances
|
|
31
|
+
: [],
|
|
32
|
+
innerInstructions: [],
|
|
33
|
+
logMessages: rec.logs || [],
|
|
34
|
+
preTokenBalances: Array.isArray((rec as any).preTokenBalances)
|
|
35
|
+
? (rec as any).preTokenBalances
|
|
36
|
+
: [],
|
|
37
|
+
postTokenBalances: Array.isArray((rec as any).postTokenBalances)
|
|
38
|
+
? (rec as any).postTokenBalances
|
|
39
|
+
: [],
|
|
40
|
+
rewards: [],
|
|
41
|
+
},
|
|
42
|
+
blockTime: rec.blockTime,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const msg: any = tx.message as any;
|
|
47
|
+
const rawKeys1: any[] = Array.isArray(msg.staticAccountKeys)
|
|
48
|
+
? msg.staticAccountKeys
|
|
49
|
+
: Array.isArray(msg.accountKeys)
|
|
50
|
+
? msg.accountKeys
|
|
51
|
+
: [];
|
|
52
|
+
const accountKeys = rawKeys1.map((k: any) => {
|
|
53
|
+
try {
|
|
54
|
+
return typeof k === "string" ? k : k.toBase58();
|
|
55
|
+
} catch {
|
|
56
|
+
return String(k);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
const compiled = Array.isArray(msg.compiledInstructions)
|
|
60
|
+
? msg.compiledInstructions
|
|
61
|
+
: Array.isArray(msg.instructions)
|
|
62
|
+
? msg.instructions
|
|
63
|
+
: [];
|
|
64
|
+
const instructions = compiled.map((ci: any) => {
|
|
65
|
+
const dataBytes: Uint8Array =
|
|
66
|
+
ci.data instanceof Uint8Array ? ci.data : Buffer.from(ci.data);
|
|
67
|
+
return {
|
|
68
|
+
programIdIndex: ci.programIdIndex,
|
|
69
|
+
accounts: Array.from(ci.accountKeyIndexes || ci.accounts || []),
|
|
70
|
+
data: context.encodeBase58(dataBytes),
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
const addressTableLookups = (msg.addressTableLookups || []).map(
|
|
74
|
+
(l: any) => ({
|
|
75
|
+
accountKey:
|
|
76
|
+
typeof l.accountKey?.toBase58 === "function"
|
|
77
|
+
? l.accountKey.toBase58()
|
|
78
|
+
: String(l.accountKey),
|
|
79
|
+
writableIndexes: Array.from(l.writableIndexes || []),
|
|
80
|
+
readonlyIndexes: Array.from(l.readonlyIndexes || []),
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
83
|
+
const header = msg.header || {
|
|
84
|
+
numRequiredSignatures: tx.signatures.length,
|
|
85
|
+
numReadonlySignedAccounts: 0,
|
|
86
|
+
numReadonlyUnsignedAccounts: 0,
|
|
87
|
+
};
|
|
88
|
+
const recentBlockhash = msg.recentBlockhash || "";
|
|
89
|
+
|
|
90
|
+
const isV0 = typeof msg.version === "number" ? msg.version === 0 : true;
|
|
91
|
+
const result: any = {
|
|
92
|
+
slot: rec.slot,
|
|
93
|
+
transaction: {
|
|
94
|
+
signatures: [signature],
|
|
95
|
+
message: {
|
|
96
|
+
accountKeys,
|
|
97
|
+
header,
|
|
98
|
+
recentBlockhash,
|
|
99
|
+
instructions,
|
|
100
|
+
addressTableLookups,
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
version: isV0 ? 0 : "legacy",
|
|
104
|
+
meta: {
|
|
105
|
+
status: rec.err ? { Err: rec.err } : { Ok: null },
|
|
106
|
+
err: rec.err ?? null,
|
|
107
|
+
fee: rec.fee,
|
|
108
|
+
loadedAddresses: { writable: [], readonly: [] },
|
|
109
|
+
preBalances: Array.isArray(rec.preBalances) ? rec.preBalances : [],
|
|
110
|
+
postBalances: Array.isArray(rec.postBalances) ? rec.postBalances : [],
|
|
111
|
+
innerInstructions: [],
|
|
112
|
+
logMessages: rec.logs || [],
|
|
113
|
+
preTokenBalances: Array.isArray((rec as any).preTokenBalances)
|
|
114
|
+
? (rec as any).preTokenBalances
|
|
115
|
+
: [],
|
|
116
|
+
postTokenBalances: Array.isArray((rec as any).postTokenBalances)
|
|
117
|
+
? (rec as any).postTokenBalances
|
|
118
|
+
: [],
|
|
119
|
+
rewards: [],
|
|
120
|
+
},
|
|
121
|
+
blockTime: rec.blockTime,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
if (encoding === "jsonParsed") {
|
|
125
|
+
const SYSTEM_PROGRAM_ID = "11111111111111111111111111111111";
|
|
126
|
+
const accountKeysParsed = accountKeys.map((pk: string, i: number) => ({
|
|
127
|
+
pubkey: pk,
|
|
128
|
+
signer:
|
|
129
|
+
typeof msg.isAccountSigner === "function"
|
|
130
|
+
? !!msg.isAccountSigner(i)
|
|
131
|
+
: i < (header?.numRequiredSignatures ?? 0),
|
|
132
|
+
writable:
|
|
133
|
+
typeof msg.isAccountWritable === "function"
|
|
134
|
+
? !!msg.isAccountWritable(i)
|
|
135
|
+
: i < (header?.numRequiredSignatures ?? 0),
|
|
136
|
+
}));
|
|
137
|
+
const parsedInstructions = compiled.map((ci: any) => {
|
|
138
|
+
const programId = accountKeys[ci.programIdIndex];
|
|
139
|
+
let parsed: any;
|
|
140
|
+
try {
|
|
141
|
+
const data: Uint8Array =
|
|
142
|
+
ci.data instanceof Uint8Array ? ci.data : Buffer.from(ci.data);
|
|
143
|
+
if (programId === SYSTEM_PROGRAM_ID && data.length >= 12) {
|
|
144
|
+
const dv = new DataView(
|
|
145
|
+
data.buffer,
|
|
146
|
+
data.byteOffset,
|
|
147
|
+
data.byteLength,
|
|
148
|
+
);
|
|
149
|
+
const discriminator = dv.getUint32(0, true);
|
|
150
|
+
if (
|
|
151
|
+
discriminator === 2 &&
|
|
152
|
+
(ci.accountKeyIndexes?.length ?? 0) >= 2
|
|
153
|
+
) {
|
|
154
|
+
const lamports = Number(dv.getBigUint64(4, true));
|
|
155
|
+
const source = accountKeys[ci.accountKeyIndexes[0]];
|
|
156
|
+
const destination = accountKeys[ci.accountKeyIndexes[1]];
|
|
157
|
+
parsed = {
|
|
158
|
+
type: "transfer",
|
|
159
|
+
info: { source, destination, lamports },
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} catch {}
|
|
164
|
+
if (parsed) return { program: "system", programId, parsed };
|
|
165
|
+
return {
|
|
166
|
+
programId,
|
|
167
|
+
accounts: (ci.accountKeyIndexes || []).map(
|
|
168
|
+
(ix: number) => accountKeys[ix],
|
|
169
|
+
),
|
|
170
|
+
data: context.encodeBase58(
|
|
171
|
+
ci.data instanceof Uint8Array ? ci.data : Buffer.from(ci.data),
|
|
172
|
+
),
|
|
173
|
+
};
|
|
174
|
+
});
|
|
175
|
+
result.transaction.message.accountKeys = accountKeysParsed;
|
|
176
|
+
result.transaction.message.instructions = parsedInstructions;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return context.createSuccessResponse(id, result);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Fallback: persistent store
|
|
183
|
+
try {
|
|
184
|
+
const row = await context.store?.getTransaction(signature);
|
|
185
|
+
if (row) {
|
|
186
|
+
const errVal = row.errJson ? JSON.parse(row.errJson) : null;
|
|
187
|
+
const preBalances = JSON.parse(row.preBalancesJson || "[]");
|
|
188
|
+
const postBalances = JSON.parse(row.postBalancesJson || "[]");
|
|
189
|
+
const logs = JSON.parse(row.logsJson || "[]");
|
|
190
|
+
const versionVal =
|
|
191
|
+
row.version === "0" || row.version === 0 ? 0 : row.version;
|
|
192
|
+
if (encoding === "base64") {
|
|
193
|
+
return context.createSuccessResponse(id, {
|
|
194
|
+
slot: Number(row.slot),
|
|
195
|
+
transaction: [row.rawBase64, "base64"],
|
|
196
|
+
version: versionVal,
|
|
197
|
+
meta: {
|
|
198
|
+
status: errVal ? { Err: errVal } : { Ok: null },
|
|
199
|
+
err: errVal,
|
|
200
|
+
fee: Number(row.fee),
|
|
201
|
+
loadedAddresses: { writable: [], readonly: [] },
|
|
202
|
+
preBalances,
|
|
203
|
+
postBalances,
|
|
204
|
+
innerInstructions: [],
|
|
205
|
+
logMessages: logs,
|
|
206
|
+
preTokenBalances: JSON.parse(row.preTokenBalancesJson || "[]"),
|
|
207
|
+
postTokenBalances: JSON.parse(row.postTokenBalancesJson || "[]"),
|
|
208
|
+
rewards: [],
|
|
209
|
+
},
|
|
210
|
+
blockTime: row.blockTime ? Number(row.blockTime) : null,
|
|
211
|
+
});
|
|
212
|
+
} else if (encoding === "jsonParsed") {
|
|
213
|
+
// Build jsonParsed similar to in-memory path
|
|
214
|
+
const raw = Buffer.from(row.rawBase64, "base64");
|
|
215
|
+
const tx = VersionedTransaction.deserialize(raw);
|
|
216
|
+
const msg: any = tx.message as any;
|
|
217
|
+
const rawKeys2: any[] = Array.isArray(msg.staticAccountKeys)
|
|
218
|
+
? msg.staticAccountKeys
|
|
219
|
+
: Array.isArray(msg.accountKeys)
|
|
220
|
+
? msg.accountKeys
|
|
221
|
+
: [];
|
|
222
|
+
const accountKeys = rawKeys2.map((k: any) => {
|
|
223
|
+
try {
|
|
224
|
+
return typeof k === "string" ? k : k.toBase58();
|
|
225
|
+
} catch {
|
|
226
|
+
return String(k);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
const header = msg.header || {
|
|
230
|
+
numRequiredSignatures: tx.signatures.length,
|
|
231
|
+
numReadonlySignedAccounts: 0,
|
|
232
|
+
numReadonlyUnsignedAccounts: 0,
|
|
233
|
+
};
|
|
234
|
+
const compiled = Array.isArray(msg.compiledInstructions)
|
|
235
|
+
? msg.compiledInstructions
|
|
236
|
+
: Array.isArray(msg.instructions)
|
|
237
|
+
? msg.instructions
|
|
238
|
+
: [];
|
|
239
|
+
const parsedInstructions = compiled.map((ci: any) => {
|
|
240
|
+
const programId = accountKeys[ci.programIdIndex];
|
|
241
|
+
let parsed: any;
|
|
242
|
+
try {
|
|
243
|
+
const data: Uint8Array =
|
|
244
|
+
ci.data instanceof Uint8Array ? ci.data : Buffer.from(ci.data);
|
|
245
|
+
// Minimal system transfer parser
|
|
246
|
+
if (
|
|
247
|
+
programId === "11111111111111111111111111111111" &&
|
|
248
|
+
data.length >= 12
|
|
249
|
+
) {
|
|
250
|
+
const dv = new DataView(
|
|
251
|
+
data.buffer,
|
|
252
|
+
data.byteOffset,
|
|
253
|
+
data.byteLength,
|
|
254
|
+
);
|
|
255
|
+
const discriminator = dv.getUint32(0, true);
|
|
256
|
+
if (
|
|
257
|
+
discriminator === 2 &&
|
|
258
|
+
(ci.accountKeyIndexes?.length ?? 0) >= 2
|
|
259
|
+
) {
|
|
260
|
+
const lamports = Number(dv.getBigUint64(4, true));
|
|
261
|
+
const source = accountKeys[ci.accountKeyIndexes[0]];
|
|
262
|
+
const destination = accountKeys[ci.accountKeyIndexes[1]];
|
|
263
|
+
parsed = {
|
|
264
|
+
type: "transfer",
|
|
265
|
+
info: { source, destination, lamports },
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
} catch {}
|
|
270
|
+
if (parsed) return { program: "system", programId, parsed };
|
|
271
|
+
return {
|
|
272
|
+
programId,
|
|
273
|
+
accounts: (ci.accountKeyIndexes || []).map(
|
|
274
|
+
(ix: number) => accountKeys[ix],
|
|
275
|
+
),
|
|
276
|
+
data: context.encodeBase58(
|
|
277
|
+
ci.data instanceof Uint8Array ? ci.data : Buffer.from(ci.data),
|
|
278
|
+
),
|
|
279
|
+
};
|
|
280
|
+
});
|
|
281
|
+
const accountKeysParsed = accountKeys.map(
|
|
282
|
+
(pk: string, i: number) => ({
|
|
283
|
+
pubkey: pk,
|
|
284
|
+
signer:
|
|
285
|
+
typeof msg.isAccountSigner === "function"
|
|
286
|
+
? !!msg.isAccountSigner(i)
|
|
287
|
+
: i < (header?.numRequiredSignatures ?? 0),
|
|
288
|
+
writable:
|
|
289
|
+
typeof msg.isAccountWritable === "function"
|
|
290
|
+
? !!msg.isAccountWritable(i)
|
|
291
|
+
: i < (header?.numRequiredSignatures ?? 0),
|
|
292
|
+
}),
|
|
293
|
+
);
|
|
294
|
+
const result: any = {
|
|
295
|
+
slot: Number(row.slot),
|
|
296
|
+
transaction: {
|
|
297
|
+
signatures: [signature],
|
|
298
|
+
message: {
|
|
299
|
+
accountKeys: accountKeysParsed,
|
|
300
|
+
header,
|
|
301
|
+
recentBlockhash: msg.recentBlockhash || "",
|
|
302
|
+
instructions: parsedInstructions,
|
|
303
|
+
addressTableLookups: msg.addressTableLookups || [],
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
version: row.version === "0" || row.version === 0 ? 0 : row.version,
|
|
307
|
+
meta: {
|
|
308
|
+
status: errVal ? { Err: errVal } : { Ok: null },
|
|
309
|
+
err: errVal,
|
|
310
|
+
fee: Number(row.fee),
|
|
311
|
+
loadedAddresses: { writable: [], readonly: [] },
|
|
312
|
+
preBalances,
|
|
313
|
+
postBalances,
|
|
314
|
+
innerInstructions: [],
|
|
315
|
+
logMessages: logs,
|
|
316
|
+
preTokenBalances: JSON.parse(row.preTokenBalancesJson || "[]"),
|
|
317
|
+
postTokenBalances: JSON.parse(row.postTokenBalancesJson || "[]"),
|
|
318
|
+
rewards: [],
|
|
319
|
+
},
|
|
320
|
+
blockTime: row.blockTime ? Number(row.blockTime) : null,
|
|
321
|
+
};
|
|
322
|
+
return context.createSuccessResponse(id, result);
|
|
323
|
+
} else {
|
|
324
|
+
const raw = Buffer.from(row.rawBase64, "base64");
|
|
325
|
+
const tx = VersionedTransaction.deserialize(raw);
|
|
326
|
+
const msg: any = tx.message as any;
|
|
327
|
+
const rawKeys3: any[] = Array.isArray(msg.staticAccountKeys)
|
|
328
|
+
? msg.staticAccountKeys
|
|
329
|
+
: Array.isArray(msg.accountKeys)
|
|
330
|
+
? msg.accountKeys
|
|
331
|
+
: [];
|
|
332
|
+
const accountKeys = rawKeys3.map((k: any) => {
|
|
333
|
+
try {
|
|
334
|
+
return typeof k === "string" ? k : k.toBase58();
|
|
335
|
+
} catch {
|
|
336
|
+
return String(k);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
const header = msg.header || {
|
|
340
|
+
numRequiredSignatures: tx.signatures.length,
|
|
341
|
+
numReadonlySignedAccounts: 0,
|
|
342
|
+
numReadonlyUnsignedAccounts: 0,
|
|
343
|
+
};
|
|
344
|
+
const compiled = Array.isArray(msg.compiledInstructions)
|
|
345
|
+
? msg.compiledInstructions
|
|
346
|
+
: Array.isArray(msg.instructions)
|
|
347
|
+
? msg.instructions
|
|
348
|
+
: [];
|
|
349
|
+
const instructions = compiled.map((ci: any) => ({
|
|
350
|
+
programIdIndex: ci.programIdIndex,
|
|
351
|
+
accounts: Array.from(ci.accountKeyIndexes || ci.accounts || []),
|
|
352
|
+
data: context.encodeBase58(
|
|
353
|
+
ci.data instanceof Uint8Array ? ci.data : Buffer.from(ci.data),
|
|
354
|
+
),
|
|
355
|
+
}));
|
|
356
|
+
const result: any = {
|
|
357
|
+
slot: Number(row.slot),
|
|
358
|
+
transaction: {
|
|
359
|
+
signatures: [signature],
|
|
360
|
+
message: {
|
|
361
|
+
accountKeys,
|
|
362
|
+
header,
|
|
363
|
+
recentBlockhash: msg.recentBlockhash || "",
|
|
364
|
+
instructions,
|
|
365
|
+
addressTableLookups: msg.addressTableLookups || [],
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
version: versionVal,
|
|
369
|
+
meta: {
|
|
370
|
+
status: errVal ? { Err: errVal } : { Ok: null },
|
|
371
|
+
err: errVal,
|
|
372
|
+
fee: Number(row.fee),
|
|
373
|
+
loadedAddresses: { writable: [], readonly: [] },
|
|
374
|
+
preBalances,
|
|
375
|
+
postBalances,
|
|
376
|
+
innerInstructions: [],
|
|
377
|
+
logMessages: logs,
|
|
378
|
+
preTokenBalances: JSON.parse(row.preTokenBalancesJson || "[]"),
|
|
379
|
+
postTokenBalances: JSON.parse(row.postTokenBalancesJson || "[]"),
|
|
380
|
+
rewards: [],
|
|
381
|
+
},
|
|
382
|
+
blockTime: row.blockTime ? Number(row.blockTime) : null,
|
|
383
|
+
};
|
|
384
|
+
return context.createSuccessResponse(id, result);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
} catch {}
|
|
388
|
+
|
|
389
|
+
// Fallback to LiteSVM history when no local record exists
|
|
390
|
+
const sigBytes = context.decodeBase58(signature);
|
|
391
|
+
const txh = (context.svm as any).getTransaction(sigBytes);
|
|
392
|
+
if (!txh) return context.createSuccessResponse(id, null);
|
|
393
|
+
|
|
394
|
+
const isError = "err" in txh;
|
|
395
|
+
const logs = isError ? txh.meta().logs() : txh.logs();
|
|
396
|
+
const errVal = isError ? txh.err() : null;
|
|
397
|
+
const status = isError ? { Err: errVal } : { Ok: null };
|
|
398
|
+
const isV0 = true;
|
|
399
|
+
return context.createSuccessResponse(id, {
|
|
400
|
+
slot: Number(context.slot),
|
|
401
|
+
transaction: {
|
|
402
|
+
signatures: [signature],
|
|
403
|
+
},
|
|
404
|
+
version: isV0 ? 0 : "legacy",
|
|
405
|
+
meta: {
|
|
406
|
+
status,
|
|
407
|
+
err: errVal,
|
|
408
|
+
fee: 5000,
|
|
409
|
+
loadedAddresses: { writable: [], readonly: [] },
|
|
410
|
+
preBalances: [],
|
|
411
|
+
postBalances: [],
|
|
412
|
+
innerInstructions: [],
|
|
413
|
+
logMessages: logs,
|
|
414
|
+
preTokenBalances: [],
|
|
415
|
+
postTokenBalances: [],
|
|
416
|
+
rewards: [],
|
|
417
|
+
},
|
|
418
|
+
blockTime: Math.floor(Date.now() / 1000),
|
|
419
|
+
});
|
|
420
|
+
} catch (error: any) {
|
|
421
|
+
return context.createErrorResponse(
|
|
422
|
+
id,
|
|
423
|
+
-32602,
|
|
424
|
+
"Invalid params",
|
|
425
|
+
error.message,
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { getConfirmedTransaction } from "./get-confirmed-transaction";
|
|
2
|
+
export { getParsedTransaction } from "./get-parsed-transaction";
|
|
3
|
+
export { getSignatureStatuses } from "./get-signature-statuses";
|
|
4
|
+
export { getSignaturesForAddress } from "./get-signatures-for-address";
|
|
5
|
+
export { getTransaction } from "./get-transaction";
|
|
6
|
+
export { sendTransaction } from "./send-transaction";
|
|
7
|
+
export { simulateTransaction } from "./simulate-transaction";
|