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,232 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ACCOUNT_SIZE,
|
|
3
|
+
AccountLayout,
|
|
4
|
+
MINT_SIZE,
|
|
5
|
+
MintLayout,
|
|
6
|
+
TOKEN_2022_PROGRAM_ID,
|
|
7
|
+
TOKEN_PROGRAM_ID,
|
|
8
|
+
} from "@solana/spl-token";
|
|
9
|
+
import { PublicKey, VersionedTransaction } from "@solana/web3.js";
|
|
10
|
+
import type { RpcMethodHandler } from "../../types";
|
|
11
|
+
|
|
12
|
+
export const sendTransaction: RpcMethodHandler = (id, params, context) => {
|
|
13
|
+
const [encodedTx] = params;
|
|
14
|
+
try {
|
|
15
|
+
const txData = Buffer.from(encodedTx, "base64");
|
|
16
|
+
const tx = VersionedTransaction.deserialize(txData);
|
|
17
|
+
|
|
18
|
+
// Snapshot pre balances
|
|
19
|
+
const msg: any = tx.message as any;
|
|
20
|
+
const rawKeys: any[] = Array.isArray(msg.staticAccountKeys)
|
|
21
|
+
? msg.staticAccountKeys
|
|
22
|
+
: Array.isArray(msg.accountKeys)
|
|
23
|
+
? msg.accountKeys
|
|
24
|
+
: [];
|
|
25
|
+
const staticKeys = rawKeys
|
|
26
|
+
.map((k: any) => {
|
|
27
|
+
try {
|
|
28
|
+
return typeof k === "string" ? new PublicKey(k) : (k as PublicKey);
|
|
29
|
+
} catch {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
.filter(Boolean) as PublicKey[];
|
|
34
|
+
const preBalances = staticKeys.map((pk) => {
|
|
35
|
+
try {
|
|
36
|
+
return Number(context.svm.getBalance(pk));
|
|
37
|
+
} catch {
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Collect SPL token accounts from instructions for pre/post token balance snapshots
|
|
43
|
+
const msgAny: any = msg;
|
|
44
|
+
const compiled = Array.isArray(msgAny.compiledInstructions)
|
|
45
|
+
? msgAny.compiledInstructions
|
|
46
|
+
: Array.isArray(msgAny.instructions)
|
|
47
|
+
? msgAny.instructions
|
|
48
|
+
: [];
|
|
49
|
+
const tokenProgramIds = new Set([
|
|
50
|
+
TOKEN_PROGRAM_ID.toBase58(),
|
|
51
|
+
TOKEN_2022_PROGRAM_ID.toBase58(),
|
|
52
|
+
]);
|
|
53
|
+
const tokenAccountSet = new Set<string>();
|
|
54
|
+
for (const ci of compiled) {
|
|
55
|
+
try {
|
|
56
|
+
const pid = staticKeys[ci.programIdIndex]?.toBase58();
|
|
57
|
+
if (!pid || !tokenProgramIds.has(pid)) continue;
|
|
58
|
+
const accIdxs: number[] = Array.isArray(ci.accountKeyIndexes)
|
|
59
|
+
? ci.accountKeyIndexes
|
|
60
|
+
: Array.isArray(ci.accounts)
|
|
61
|
+
? ci.accounts
|
|
62
|
+
: [];
|
|
63
|
+
for (const ix of accIdxs) {
|
|
64
|
+
const addr = staticKeys[ix]?.toBase58();
|
|
65
|
+
if (addr) tokenAccountSet.add(addr);
|
|
66
|
+
}
|
|
67
|
+
} catch {}
|
|
68
|
+
}
|
|
69
|
+
// Pre token balances
|
|
70
|
+
const preTokenBalances: any[] = [];
|
|
71
|
+
const ataToInfo = new Map<
|
|
72
|
+
string,
|
|
73
|
+
{
|
|
74
|
+
mint?: string;
|
|
75
|
+
owner?: string;
|
|
76
|
+
amount: bigint;
|
|
77
|
+
accountIndex: number;
|
|
78
|
+
decimals?: number;
|
|
79
|
+
}
|
|
80
|
+
>();
|
|
81
|
+
const missingPre = new Set<string>();
|
|
82
|
+
for (const addr of tokenAccountSet) {
|
|
83
|
+
try {
|
|
84
|
+
const pk = new PublicKey(addr);
|
|
85
|
+
const idx = staticKeys.findIndex((k) => k.equals(pk));
|
|
86
|
+
const acc = context.svm.getAccount(pk);
|
|
87
|
+
if (!acc || (acc.data?.length ?? 0) < ACCOUNT_SIZE) {
|
|
88
|
+
// Track placeholder; we'll fill mint/owner/decimals after send
|
|
89
|
+
ataToInfo.set(addr, { amount: 0n, accountIndex: idx >= 0 ? idx : 0 });
|
|
90
|
+
missingPre.add(addr);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const decAcc = AccountLayout.decode(Buffer.from(acc.data));
|
|
94
|
+
const mintPk = new PublicKey(decAcc.mint);
|
|
95
|
+
const mintAcc = context.svm.getAccount(mintPk);
|
|
96
|
+
let decimals = 0;
|
|
97
|
+
if (mintAcc && (mintAcc.data?.length ?? 0) >= MINT_SIZE) {
|
|
98
|
+
const m = MintLayout.decode(
|
|
99
|
+
Buffer.from(mintAcc.data).slice(0, MINT_SIZE),
|
|
100
|
+
);
|
|
101
|
+
decimals = Number(m.decimals ?? 0);
|
|
102
|
+
}
|
|
103
|
+
const ownerPk = new PublicKey(decAcc.owner);
|
|
104
|
+
const amt = BigInt(decAcc.amount.toString());
|
|
105
|
+
ataToInfo.set(addr, {
|
|
106
|
+
mint: mintPk.toBase58(),
|
|
107
|
+
owner: ownerPk.toBase58(),
|
|
108
|
+
amount: amt,
|
|
109
|
+
accountIndex: idx >= 0 ? idx : 0,
|
|
110
|
+
decimals,
|
|
111
|
+
});
|
|
112
|
+
const uiAmount = Number(amt) / 10 ** decimals;
|
|
113
|
+
preTokenBalances.push({
|
|
114
|
+
accountIndex: idx >= 0 ? idx : 0,
|
|
115
|
+
mint: mintPk.toBase58(),
|
|
116
|
+
owner: ownerPk.toBase58(),
|
|
117
|
+
uiTokenAmount: {
|
|
118
|
+
amount: amt.toString(),
|
|
119
|
+
decimals,
|
|
120
|
+
uiAmount,
|
|
121
|
+
uiAmountString: String(uiAmount),
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
} catch {}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const result = context.svm.sendTransaction(tx);
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
131
|
+
const maybeErr = (result as any).err?.();
|
|
132
|
+
if (maybeErr) {
|
|
133
|
+
return context.createErrorResponse(
|
|
134
|
+
id,
|
|
135
|
+
-32003,
|
|
136
|
+
"Transaction failed",
|
|
137
|
+
maybeErr,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
} catch {}
|
|
141
|
+
|
|
142
|
+
const signature = tx.signatures[0]
|
|
143
|
+
? context.encodeBase58(tx.signatures[0])
|
|
144
|
+
: context.encodeBase58(new Uint8Array(64).fill(0));
|
|
145
|
+
context.notifySignature(signature);
|
|
146
|
+
// Snapshot post balances and capture logs for rich view
|
|
147
|
+
const postBalances = staticKeys.map((pk) => {
|
|
148
|
+
try {
|
|
149
|
+
return Number(context.svm.getBalance(pk));
|
|
150
|
+
} catch {
|
|
151
|
+
return 0;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
// Post token balances
|
|
155
|
+
const postTokenBalances: any[] = [];
|
|
156
|
+
for (const addr of tokenAccountSet) {
|
|
157
|
+
try {
|
|
158
|
+
const pk = new PublicKey(addr);
|
|
159
|
+
const idx = staticKeys.findIndex((k) => k.equals(pk));
|
|
160
|
+
const acc = context.svm.getAccount(pk);
|
|
161
|
+
if (!acc || (acc.data?.length ?? 0) < ACCOUNT_SIZE) continue;
|
|
162
|
+
const decAcc = AccountLayout.decode(Buffer.from(acc.data));
|
|
163
|
+
const mintPk = new PublicKey(decAcc.mint);
|
|
164
|
+
const ownerPk = new PublicKey(decAcc.owner);
|
|
165
|
+
const mintAcc = context.svm.getAccount(mintPk);
|
|
166
|
+
let decimals = 0;
|
|
167
|
+
if (mintAcc && (mintAcc.data?.length ?? 0) >= MINT_SIZE) {
|
|
168
|
+
const m = MintLayout.decode(
|
|
169
|
+
Buffer.from(mintAcc.data).slice(0, MINT_SIZE),
|
|
170
|
+
);
|
|
171
|
+
decimals = Number(m.decimals ?? 0);
|
|
172
|
+
}
|
|
173
|
+
const amt = BigInt(decAcc.amount.toString());
|
|
174
|
+
const uiAmount = Number(amt) / 10 ** decimals;
|
|
175
|
+
postTokenBalances.push({
|
|
176
|
+
accountIndex:
|
|
177
|
+
idx >= 0 ? idx : (ataToInfo.get(addr)?.accountIndex ?? 0),
|
|
178
|
+
mint: mintPk.toBase58(),
|
|
179
|
+
owner: ownerPk.toBase58(),
|
|
180
|
+
uiTokenAmount: {
|
|
181
|
+
amount: amt.toString(),
|
|
182
|
+
decimals,
|
|
183
|
+
uiAmount,
|
|
184
|
+
uiAmountString: String(uiAmount),
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
// Add missing pre entry as zero if account was unfunded before
|
|
188
|
+
if (missingPre.has(addr)) {
|
|
189
|
+
const preUi = 0;
|
|
190
|
+
preTokenBalances.push({
|
|
191
|
+
accountIndex:
|
|
192
|
+
idx >= 0 ? idx : (ataToInfo.get(addr)?.accountIndex ?? 0),
|
|
193
|
+
mint: mintPk.toBase58(),
|
|
194
|
+
owner: ownerPk.toBase58(),
|
|
195
|
+
uiTokenAmount: {
|
|
196
|
+
amount: "0",
|
|
197
|
+
decimals,
|
|
198
|
+
uiAmount: preUi,
|
|
199
|
+
uiAmountString: String(preUi),
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
} catch {}
|
|
204
|
+
}
|
|
205
|
+
let logs: string[] = [];
|
|
206
|
+
try {
|
|
207
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
208
|
+
const anyRes: any = result;
|
|
209
|
+
if (typeof anyRes?.logs === "function") logs = anyRes.logs();
|
|
210
|
+
else if (typeof anyRes?.meta === "function")
|
|
211
|
+
logs = anyRes.meta()?.logs?.() ?? [];
|
|
212
|
+
} catch {}
|
|
213
|
+
context.recordTransaction(signature, tx, {
|
|
214
|
+
logs,
|
|
215
|
+
fee: 5000,
|
|
216
|
+
blockTime: Math.floor(Date.now() / 1000),
|
|
217
|
+
preBalances,
|
|
218
|
+
postBalances,
|
|
219
|
+
preTokenBalances,
|
|
220
|
+
postTokenBalances,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return context.createSuccessResponse(id, signature);
|
|
224
|
+
} catch (error: any) {
|
|
225
|
+
return context.createErrorResponse(
|
|
226
|
+
id,
|
|
227
|
+
-32003,
|
|
228
|
+
"Transaction failed",
|
|
229
|
+
error.message,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { VersionedTransaction } from "@solana/web3.js";
|
|
2
|
+
import type { RpcMethodHandler } from "../../types";
|
|
3
|
+
|
|
4
|
+
export const simulateTransaction: RpcMethodHandler = (id, params, context) => {
|
|
5
|
+
const [encodedTx] = params;
|
|
6
|
+
try {
|
|
7
|
+
const txData = Buffer.from(encodedTx, "base64");
|
|
8
|
+
const tx = VersionedTransaction.deserialize(txData);
|
|
9
|
+
const result = context.svm.simulateTransaction(tx);
|
|
10
|
+
|
|
11
|
+
if ("err" in result) {
|
|
12
|
+
const errorMeta = result.meta();
|
|
13
|
+
// To maximize client compatibility, report err as null and return logs
|
|
14
|
+
// (Some clients fail to deserialize unknown enum variants or numeric codes.)
|
|
15
|
+
return context.createSuccessResponse(id, {
|
|
16
|
+
context: { slot: Number(context.slot) },
|
|
17
|
+
value: {
|
|
18
|
+
err: null,
|
|
19
|
+
logs: errorMeta.logs(),
|
|
20
|
+
accounts: null,
|
|
21
|
+
unitsConsumed: Number(errorMeta.computeUnitsConsumed()),
|
|
22
|
+
returnData: null,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const meta = result.meta();
|
|
28
|
+
const returnData = meta.returnData();
|
|
29
|
+
|
|
30
|
+
return context.createSuccessResponse(id, {
|
|
31
|
+
context: { slot: Number(context.slot) },
|
|
32
|
+
value: {
|
|
33
|
+
err: null,
|
|
34
|
+
logs: meta.logs(),
|
|
35
|
+
accounts: null,
|
|
36
|
+
unitsConsumed: Number(meta.computeUnitsConsumed()),
|
|
37
|
+
returnData: returnData
|
|
38
|
+
? {
|
|
39
|
+
programId: context.encodeBase58(returnData.programId()),
|
|
40
|
+
data: [
|
|
41
|
+
Buffer.from(returnData.data()).toString("base64"),
|
|
42
|
+
"base64",
|
|
43
|
+
],
|
|
44
|
+
}
|
|
45
|
+
: null,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
} catch (error: any) {
|
|
49
|
+
return context.createErrorResponse(
|
|
50
|
+
id,
|
|
51
|
+
-32003,
|
|
52
|
+
"Simulation failed",
|
|
53
|
+
error.message,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
};
|