solforge 0.2.12 → 0.2.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/package.json +1 -5
- package/start.cjs +19 -23
- package/docs/API.md +0 -379
- package/docs/CONFIGURATION.md +0 -407
- package/docs/bun-single-file-executable.md +0 -585
- package/docs/cli-plan.md +0 -154
- package/docs/data-indexing-plan.md +0 -214
- package/docs/gui-roadmap.md +0 -202
- package/scripts/decode-b58.ts +0 -10
- package/scripts/install.sh +0 -112
- package/server/index.ts +0 -5
- package/server/lib/base58.ts +0 -33
- package/server/lib/faucet.ts +0 -110
- package/server/lib/instruction-parser.ts +0 -328
- package/server/lib/parsers/spl-associated-token-account.ts +0 -50
- package/server/lib/parsers/spl-token.ts +0 -340
- package/server/lib/spl-token.ts +0 -57
- package/server/methods/TEMPLATE.md +0 -117
- package/server/methods/account/get-account-info.ts +0 -86
- package/server/methods/account/get-balance.ts +0 -23
- package/server/methods/account/get-multiple-accounts.ts +0 -84
- package/server/methods/account/get-parsed-account-info.ts +0 -17
- package/server/methods/account/index.ts +0 -12
- package/server/methods/account/parsers/index.ts +0 -52
- package/server/methods/account/parsers/loader-upgradeable.ts +0 -79
- package/server/methods/account/parsers/spl-token.ts +0 -256
- package/server/methods/account/parsers/system.ts +0 -4
- package/server/methods/account/request-airdrop.ts +0 -271
- package/server/methods/admin/adopt-mint-authority.ts +0 -94
- package/server/methods/admin/clone-program-accounts.ts +0 -55
- package/server/methods/admin/clone-program.ts +0 -152
- package/server/methods/admin/clone-token-accounts.ts +0 -117
- package/server/methods/admin/clone-token-mint.ts +0 -82
- package/server/methods/admin/create-mint.ts +0 -114
- package/server/methods/admin/create-token-account.ts +0 -137
- package/server/methods/admin/helpers.ts +0 -70
- package/server/methods/admin/index.ts +0 -10
- package/server/methods/admin/list-mints.ts +0 -21
- package/server/methods/admin/load-program.ts +0 -52
- package/server/methods/admin/mint-to.ts +0 -266
- package/server/methods/block/get-block-height.ts +0 -5
- package/server/methods/block/get-block.ts +0 -31
- package/server/methods/block/get-blocks-with-limit.ts +0 -19
- package/server/methods/block/get-latest-blockhash.ts +0 -12
- package/server/methods/block/get-slot.ts +0 -5
- package/server/methods/block/index.ts +0 -6
- package/server/methods/block/is-blockhash-valid.ts +0 -19
- package/server/methods/epoch/get-cluster-nodes.ts +0 -17
- package/server/methods/epoch/get-epoch-info.ts +0 -16
- package/server/methods/epoch/get-epoch-schedule.ts +0 -15
- package/server/methods/epoch/get-highest-snapshot-slot.ts +0 -9
- package/server/methods/epoch/get-leader-schedule.ts +0 -8
- package/server/methods/epoch/get-max-retransmit-slot.ts +0 -9
- package/server/methods/epoch/get-max-shred-insert-slot.ts +0 -9
- package/server/methods/epoch/get-slot-leader.ts +0 -6
- package/server/methods/epoch/get-slot-leaders.ts +0 -9
- package/server/methods/epoch/get-stake-activation.ts +0 -9
- package/server/methods/epoch/get-stake-minimum-delegation.ts +0 -9
- package/server/methods/epoch/get-vote-accounts.ts +0 -19
- package/server/methods/epoch/index.ts +0 -13
- package/server/methods/epoch/minimum-ledger-slot.ts +0 -5
- package/server/methods/fee/get-fee-calculator-for-blockhash.ts +0 -12
- package/server/methods/fee/get-fee-for-message.ts +0 -8
- package/server/methods/fee/get-fee-rate-governor.ts +0 -16
- package/server/methods/fee/get-fees.ts +0 -14
- package/server/methods/fee/get-recent-prioritization-fees.ts +0 -22
- package/server/methods/fee/index.ts +0 -5
- package/server/methods/get-address-lookup-table.ts +0 -27
- package/server/methods/index.ts +0 -265
- package/server/methods/performance/get-recent-performance-samples.ts +0 -25
- package/server/methods/performance/get-transaction-count.ts +0 -5
- package/server/methods/performance/index.ts +0 -2
- package/server/methods/program/get-block-commitment.ts +0 -9
- package/server/methods/program/get-block-production.ts +0 -14
- package/server/methods/program/get-block-time.ts +0 -21
- package/server/methods/program/get-blocks.ts +0 -11
- package/server/methods/program/get-first-available-block.ts +0 -9
- package/server/methods/program/get-genesis-hash.ts +0 -6
- package/server/methods/program/get-identity.ts +0 -6
- package/server/methods/program/get-inflation-governor.ts +0 -15
- package/server/methods/program/get-inflation-rate.ts +0 -10
- package/server/methods/program/get-inflation-reward.ts +0 -12
- package/server/methods/program/get-largest-accounts.ts +0 -8
- package/server/methods/program/get-parsed-program-accounts.ts +0 -12
- package/server/methods/program/get-parsed-token-accounts-by-delegate.ts +0 -12
- package/server/methods/program/get-parsed-token-accounts-by-owner.ts +0 -12
- package/server/methods/program/get-program-accounts.ts +0 -221
- package/server/methods/program/get-supply.ts +0 -13
- package/server/methods/program/get-token-account-balance.ts +0 -60
- package/server/methods/program/get-token-accounts-by-delegate.ts +0 -82
- package/server/methods/program/get-token-accounts-by-owner.ts +0 -416
- package/server/methods/program/get-token-largest-accounts.ts +0 -81
- package/server/methods/program/get-token-supply.ts +0 -39
- package/server/methods/program/index.ts +0 -21
- package/server/methods/solforge/index.ts +0 -158
- package/server/methods/system/get-health.ts +0 -5
- package/server/methods/system/get-minimum-balance-for-rent-exemption.ts +0 -13
- package/server/methods/system/get-version.ts +0 -9
- package/server/methods/system/index.ts +0 -3
- package/server/methods/transaction/get-confirmed-transaction.ts +0 -11
- package/server/methods/transaction/get-parsed-transaction.ts +0 -17
- package/server/methods/transaction/get-signature-statuses.ts +0 -79
- package/server/methods/transaction/get-signatures-for-address.ts +0 -41
- package/server/methods/transaction/get-transaction.ts +0 -639
- package/server/methods/transaction/index.ts +0 -7
- package/server/methods/transaction/inner-instructions.test.ts +0 -104
- package/server/methods/transaction/send-transaction.ts +0 -469
- package/server/methods/transaction/simulate-transaction.ts +0 -57
- package/server/rpc-server.ts +0 -521
- package/server/types.ts +0 -109
- package/server/ws-server.ts +0 -178
- package/src/api-server-entry.ts +0 -109
- package/src/cli/bootstrap.ts +0 -67
- package/src/cli/commands/airdrop.ts +0 -37
- package/src/cli/commands/config.ts +0 -39
- package/src/cli/commands/mint.ts +0 -187
- package/src/cli/commands/program-clone.ts +0 -122
- package/src/cli/commands/program-load.ts +0 -64
- package/src/cli/commands/rpc-start.ts +0 -49
- package/src/cli/commands/token-adopt-authority.ts +0 -37
- package/src/cli/commands/token-clone.ts +0 -112
- package/src/cli/commands/token-create.ts +0 -81
- package/src/cli/main.ts +0 -158
- package/src/cli/run-solforge.ts +0 -112
- package/src/cli/setup-utils.ts +0 -54
- package/src/cli/setup-wizard.ts +0 -258
- package/src/cli/utils/args.ts +0 -15
- package/src/commands/add-program.ts +0 -333
- package/src/commands/init.ts +0 -122
- package/src/commands/list.ts +0 -136
- package/src/commands/mint.ts +0 -287
- package/src/commands/start.ts +0 -881
- package/src/commands/status.ts +0 -99
- package/src/commands/stop.ts +0 -405
- package/src/config/index.ts +0 -146
- package/src/config/manager.ts +0 -157
- package/src/db/index.ts +0 -83
- package/src/db/schema/accounts.ts +0 -23
- package/src/db/schema/address-signatures.ts +0 -31
- package/src/db/schema/index.ts +0 -6
- package/src/db/schema/meta-kv.ts +0 -9
- package/src/db/schema/transactions.ts +0 -36
- package/src/db/schema/tx-account-states.ts +0 -23
- package/src/db/schema/tx-accounts.ts +0 -33
- package/src/db/tx-store.ts +0 -264
- package/src/gui/public/app.css +0 -1556
- package/src/gui/public/build/main.css +0 -1569
- package/src/gui/public/build/main.js +0 -303
- package/src/gui/public/build/main.js.txt +0 -231
- package/src/gui/public/index.html +0 -19
- package/src/gui/server.ts +0 -296
- package/src/gui/src/api.ts +0 -127
- package/src/gui/src/app.tsx +0 -441
- package/src/gui/src/components/airdrop-mint-form.tsx +0 -246
- package/src/gui/src/components/clone-program-modal.tsx +0 -202
- package/src/gui/src/components/clone-token-modal.tsx +0 -230
- package/src/gui/src/components/modal.tsx +0 -134
- package/src/gui/src/components/programs-panel.tsx +0 -124
- package/src/gui/src/components/status-panel.tsx +0 -136
- package/src/gui/src/components/tokens-panel.tsx +0 -122
- package/src/gui/src/hooks/use-interval.ts +0 -17
- package/src/gui/src/index.css +0 -557
- package/src/gui/src/main.tsx +0 -17
- package/src/index.ts +0 -216
- package/src/migrations-bundled.ts +0 -23
- package/src/rpc/start.ts +0 -44
- package/src/services/api-server.ts +0 -504
- package/src/services/port-manager.ts +0 -174
- package/src/services/process-registry.ts +0 -153
- package/src/services/program-cloner.ts +0 -317
- package/src/services/token-cloner.ts +0 -811
- package/src/services/validator.ts +0 -293
- package/src/types/config.ts +0 -110
- package/src/utils/shell.ts +0 -110
- package/src/utils/token-loader.ts +0 -115
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ACCOUNT_SIZE,
|
|
3
|
-
AccountLayout,
|
|
4
|
-
getAssociatedTokenAddressSync,
|
|
5
|
-
TOKEN_PROGRAM_ID,
|
|
6
|
-
} from "@solana/spl-token";
|
|
7
|
-
import { PublicKey } from "@solana/web3.js";
|
|
8
|
-
import type { RpcMethodHandler } from "../../types";
|
|
9
|
-
|
|
10
|
-
// Create or overwrite a token account (ATA) with a specified amount
|
|
11
|
-
export const solforgeCreateTokenAccount: RpcMethodHandler = async (
|
|
12
|
-
id,
|
|
13
|
-
params,
|
|
14
|
-
context,
|
|
15
|
-
) => {
|
|
16
|
-
try {
|
|
17
|
-
const [mintStr, ownerStr, rawAmount, decimals] = params as [
|
|
18
|
-
string,
|
|
19
|
-
string | null | undefined,
|
|
20
|
-
number | string | bigint,
|
|
21
|
-
number?,
|
|
22
|
-
];
|
|
23
|
-
if (!mintStr || rawAmount == null)
|
|
24
|
-
return context.createErrorResponse(
|
|
25
|
-
id,
|
|
26
|
-
-32602,
|
|
27
|
-
"Invalid params: mint and amount required",
|
|
28
|
-
);
|
|
29
|
-
const mint = new PublicKey(mintStr);
|
|
30
|
-
const owner = ownerStr
|
|
31
|
-
? new PublicKey(ownerStr)
|
|
32
|
-
: context.getFaucet().publicKey;
|
|
33
|
-
// Amount is in base units (not UI). The layout stores a u64 bigint
|
|
34
|
-
const amount =
|
|
35
|
-
typeof rawAmount === "bigint" ? rawAmount : BigInt(rawAmount);
|
|
36
|
-
|
|
37
|
-
const buf = Buffer.alloc(ACCOUNT_SIZE);
|
|
38
|
-
type TokenAccountStruct = Parameters<typeof AccountLayout.encode>[0];
|
|
39
|
-
const tokenAccount = {
|
|
40
|
-
mint,
|
|
41
|
-
owner,
|
|
42
|
-
amount,
|
|
43
|
-
delegateOption: 0,
|
|
44
|
-
delegate: PublicKey.default,
|
|
45
|
-
delegatedAmount: 0n,
|
|
46
|
-
state: 1,
|
|
47
|
-
isNativeOption: 0,
|
|
48
|
-
isNative: 0n,
|
|
49
|
-
closeAuthorityOption: 0,
|
|
50
|
-
closeAuthority: PublicKey.default,
|
|
51
|
-
} satisfies Partial<TokenAccountStruct> as TokenAccountStruct;
|
|
52
|
-
AccountLayout.encode(tokenAccount, buf);
|
|
53
|
-
|
|
54
|
-
const rentLamports = Number(
|
|
55
|
-
context.svm.minimumBalanceForRentExemption(BigInt(ACCOUNT_SIZE)),
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
// Compute the canonical associated token account address for mint+owner
|
|
59
|
-
const address = getAssociatedTokenAddressSync(mint, owner, true);
|
|
60
|
-
|
|
61
|
-
context.svm.setAccount(address, {
|
|
62
|
-
lamports: rentLamports,
|
|
63
|
-
data: new Uint8Array(buf),
|
|
64
|
-
owner: TOKEN_PROGRAM_ID,
|
|
65
|
-
executable: false,
|
|
66
|
-
rentEpoch: 0,
|
|
67
|
-
});
|
|
68
|
-
try {
|
|
69
|
-
await context.store?.upsertAccounts([
|
|
70
|
-
{
|
|
71
|
-
address: address.toBase58(),
|
|
72
|
-
lamports: rentLamports,
|
|
73
|
-
ownerProgram: TOKEN_PROGRAM_ID.toBase58(),
|
|
74
|
-
executable: false,
|
|
75
|
-
rentEpoch: 0,
|
|
76
|
-
dataLen: ACCOUNT_SIZE,
|
|
77
|
-
dataBase64: undefined,
|
|
78
|
-
lastSlot: Number(context.slot),
|
|
79
|
-
},
|
|
80
|
-
]);
|
|
81
|
-
} catch {}
|
|
82
|
-
// Record a synthetic transaction so explorers can show activity
|
|
83
|
-
try {
|
|
84
|
-
const sig = `admin:mint:${address.toBase58()}:${Date.now()}`;
|
|
85
|
-
await context.store?.insertTransactionBundle({
|
|
86
|
-
signature: sig,
|
|
87
|
-
slot: Number(context.slot),
|
|
88
|
-
blockTime: Math.floor(Date.now() / 1000),
|
|
89
|
-
version: "legacy",
|
|
90
|
-
fee: 0,
|
|
91
|
-
err: null,
|
|
92
|
-
rawBase64: "",
|
|
93
|
-
preBalances: [],
|
|
94
|
-
postBalances: [],
|
|
95
|
-
logs: ["admin mint"],
|
|
96
|
-
accounts: [
|
|
97
|
-
{
|
|
98
|
-
address: address.toBase58(),
|
|
99
|
-
index: 0,
|
|
100
|
-
signer: false,
|
|
101
|
-
writable: true,
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
address: mint.toBase58(),
|
|
105
|
-
index: 1,
|
|
106
|
-
signer: false,
|
|
107
|
-
writable: false,
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
address: owner.toBase58(),
|
|
111
|
-
index: 2,
|
|
112
|
-
signer: false,
|
|
113
|
-
writable: false,
|
|
114
|
-
},
|
|
115
|
-
],
|
|
116
|
-
});
|
|
117
|
-
} catch {}
|
|
118
|
-
|
|
119
|
-
return context.createSuccessResponse(id, {
|
|
120
|
-
ok: true,
|
|
121
|
-
address: address.toBase58(),
|
|
122
|
-
mint: mintStr,
|
|
123
|
-
owner: owner.toBase58(),
|
|
124
|
-
amount: amount.toString(),
|
|
125
|
-
decimals: decimals ?? null,
|
|
126
|
-
});
|
|
127
|
-
} catch (e) {
|
|
128
|
-
return context.createErrorResponse(
|
|
129
|
-
id,
|
|
130
|
-
-32603,
|
|
131
|
-
"Create token account failed",
|
|
132
|
-
(e as Error)?.message || String(e),
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
export type { RpcMethodHandler } from "../../types";
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getMetadataPointerState,
|
|
3
|
-
MINT_SIZE,
|
|
4
|
-
TOKEN_2022_PROGRAM_ID,
|
|
5
|
-
TOKEN_PROGRAM_ID,
|
|
6
|
-
unpackMint,
|
|
7
|
-
} from "@solana/spl-token";
|
|
8
|
-
import type { AccountInfo, Connection, PublicKey } from "@solana/web3.js";
|
|
9
|
-
|
|
10
|
-
import type { RpcMethodContext } from "../../types";
|
|
11
|
-
|
|
12
|
-
export async function cloneMintExtensionAccounts(
|
|
13
|
-
conn: Connection,
|
|
14
|
-
context: RpcMethodContext,
|
|
15
|
-
mint: PublicKey,
|
|
16
|
-
info: AccountInfo<Buffer>,
|
|
17
|
-
): Promise<void> {
|
|
18
|
-
if (!info.data || info.data.length <= MINT_SIZE) return;
|
|
19
|
-
try {
|
|
20
|
-
const programId = info.owner.equals(TOKEN_2022_PROGRAM_ID)
|
|
21
|
-
? TOKEN_2022_PROGRAM_ID
|
|
22
|
-
: TOKEN_PROGRAM_ID;
|
|
23
|
-
const mintState = unpackMint(mint, info, programId);
|
|
24
|
-
if (!mintState.tlvData || mintState.tlvData.length === 0) return;
|
|
25
|
-
|
|
26
|
-
const metadataPtr = getMetadataPointerState(mintState);
|
|
27
|
-
if (metadataPtr?.metadataAddress) {
|
|
28
|
-
await cloneSingleAccount(
|
|
29
|
-
conn,
|
|
30
|
-
context,
|
|
31
|
-
metadataPtr.metadataAddress,
|
|
32
|
-
"metadata",
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
} catch (error) {
|
|
36
|
-
console.warn("[admin] mint extension clone skipped", {
|
|
37
|
-
mint: mint.toBase58(),
|
|
38
|
-
error: String(error),
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export async function cloneSingleAccount(
|
|
44
|
-
conn: Connection,
|
|
45
|
-
context: RpcMethodContext,
|
|
46
|
-
address: PublicKey,
|
|
47
|
-
label: string,
|
|
48
|
-
): Promise<void> {
|
|
49
|
-
try {
|
|
50
|
-
const info = await conn.getAccountInfo(address, "confirmed");
|
|
51
|
-
if (!info) {
|
|
52
|
-
console.warn(`[admin] ${label} account not found on endpoint`, {
|
|
53
|
-
address: address.toBase58(),
|
|
54
|
-
});
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
context.svm.setAccount(address, {
|
|
58
|
-
data: new Uint8Array(info.data as Buffer),
|
|
59
|
-
executable: info.executable,
|
|
60
|
-
lamports: Number(info.lamports),
|
|
61
|
-
owner: info.owner,
|
|
62
|
-
rentEpoch: 0,
|
|
63
|
-
});
|
|
64
|
-
} catch (error) {
|
|
65
|
-
console.warn(`[admin] clone ${label} account failed`, {
|
|
66
|
-
address: address.toBase58(),
|
|
67
|
-
error: String(error),
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export { solforgeAdoptMintAuthority } from "./adopt-mint-authority";
|
|
2
|
-
export { solforgeAdminCloneProgram } from "./clone-program";
|
|
3
|
-
export { solforgeAdminCloneProgramAccounts } from "./clone-program-accounts";
|
|
4
|
-
export { solforgeAdminCloneTokenAccounts } from "./clone-token-accounts";
|
|
5
|
-
export { solforgeAdminCloneTokenMint } from "./clone-token-mint";
|
|
6
|
-
export { solforgeCreateMint } from "./create-mint";
|
|
7
|
-
export { solforgeCreateTokenAccount } from "./create-token-account";
|
|
8
|
-
export { solforgeListMints } from "./list-mints";
|
|
9
|
-
export { solforgeLoadProgram } from "./load-program";
|
|
10
|
-
export { solforgeMintTo } from "./mint-to";
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { RpcMethodHandler } from "../../types";
|
|
2
|
-
|
|
3
|
-
export const solforgeListMints: RpcMethodHandler = async (
|
|
4
|
-
id,
|
|
5
|
-
_params,
|
|
6
|
-
context,
|
|
7
|
-
) => {
|
|
8
|
-
try {
|
|
9
|
-
const list = context.listMints ? context.listMints() : [];
|
|
10
|
-
return context.createSuccessResponse(id, list);
|
|
11
|
-
} catch (e) {
|
|
12
|
-
return context.createErrorResponse(
|
|
13
|
-
id,
|
|
14
|
-
-32603,
|
|
15
|
-
"List mints failed",
|
|
16
|
-
(e as Error)?.message || String(e),
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export type { RpcMethodHandler } from "../../types";
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { PublicKey } from "@solana/web3.js";
|
|
2
|
-
import type { RpcMethodHandler } from "../../types";
|
|
3
|
-
|
|
4
|
-
// Load a program ELF into LiteSVM for a given programId
|
|
5
|
-
export const solforgeLoadProgram: RpcMethodHandler = async (
|
|
6
|
-
id,
|
|
7
|
-
params,
|
|
8
|
-
context,
|
|
9
|
-
) => {
|
|
10
|
-
try {
|
|
11
|
-
const [programIdStr, elfBase64] = params as [string, string];
|
|
12
|
-
if (!programIdStr || !elfBase64)
|
|
13
|
-
return context.createErrorResponse(
|
|
14
|
-
id,
|
|
15
|
-
-32602,
|
|
16
|
-
"Invalid params: programId, base64 required",
|
|
17
|
-
);
|
|
18
|
-
const pid = new PublicKey(programIdStr);
|
|
19
|
-
const bytes = Uint8Array.from(Buffer.from(elfBase64, "base64"));
|
|
20
|
-
try {
|
|
21
|
-
context.svm.addProgram(pid, bytes);
|
|
22
|
-
} catch {}
|
|
23
|
-
// Mirror program account metadata as executable; owner = upgradeable loader for realism
|
|
24
|
-
const LOADER_UPGRADEABLE = new PublicKey(
|
|
25
|
-
"BPFLoaderUpgradeab1e11111111111111111111111",
|
|
26
|
-
);
|
|
27
|
-
context.svm.setAccount(pid, {
|
|
28
|
-
lamports: 1_000_000_000,
|
|
29
|
-
data: new Uint8Array(bytes.length), // minimal stub data for the program account itself
|
|
30
|
-
owner: LOADER_UPGRADEABLE,
|
|
31
|
-
executable: true,
|
|
32
|
-
rentEpoch: 0,
|
|
33
|
-
});
|
|
34
|
-
try {
|
|
35
|
-
context.registerProgram?.(pid);
|
|
36
|
-
} catch {}
|
|
37
|
-
return context.createSuccessResponse(id, {
|
|
38
|
-
ok: true,
|
|
39
|
-
programId: programIdStr,
|
|
40
|
-
size: bytes.length,
|
|
41
|
-
});
|
|
42
|
-
} catch (e) {
|
|
43
|
-
return context.createErrorResponse(
|
|
44
|
-
id,
|
|
45
|
-
-32603,
|
|
46
|
-
"Load program failed",
|
|
47
|
-
(e as Error)?.message || String(e),
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export type { RpcMethodHandler } from "../../types";
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ACCOUNT_SIZE,
|
|
3
|
-
AccountLayout,
|
|
4
|
-
createAssociatedTokenAccountInstruction,
|
|
5
|
-
createMintToCheckedInstruction,
|
|
6
|
-
getAssociatedTokenAddressSync,
|
|
7
|
-
MINT_SIZE,
|
|
8
|
-
MintLayout,
|
|
9
|
-
TOKEN_2022_PROGRAM_ID,
|
|
10
|
-
TOKEN_PROGRAM_ID,
|
|
11
|
-
} from "@solana/spl-token";
|
|
12
|
-
import type { TransactionInstruction } from "@solana/web3.js";
|
|
13
|
-
import {
|
|
14
|
-
PublicKey,
|
|
15
|
-
TransactionMessage,
|
|
16
|
-
VersionedTransaction,
|
|
17
|
-
} from "@solana/web3.js";
|
|
18
|
-
import type { RpcMethodHandler } from "../../types";
|
|
19
|
-
import { sendTransaction as sendTxRpc } from "../transaction/send-transaction";
|
|
20
|
-
|
|
21
|
-
// Mint via a real SPL Token transaction signed by faucet (must be mint authority)
|
|
22
|
-
export const solforgeMintTo: RpcMethodHandler = async (id, params, context) => {
|
|
23
|
-
try {
|
|
24
|
-
const [mintStr, ownerStr, rawAmount] = params as [
|
|
25
|
-
string,
|
|
26
|
-
string,
|
|
27
|
-
number | string | bigint,
|
|
28
|
-
];
|
|
29
|
-
if (!mintStr || !ownerStr || rawAmount == null)
|
|
30
|
-
return context.createErrorResponse(
|
|
31
|
-
id,
|
|
32
|
-
-32602,
|
|
33
|
-
"Invalid params: mint, owner, amount required",
|
|
34
|
-
);
|
|
35
|
-
const mint = new PublicKey(mintStr);
|
|
36
|
-
const owner = new PublicKey(ownerStr);
|
|
37
|
-
const faucet = context.getFaucet();
|
|
38
|
-
|
|
39
|
-
// Read mint to get decimals and authority
|
|
40
|
-
const mintAcc = context.svm.getAccount(mint);
|
|
41
|
-
if (!mintAcc)
|
|
42
|
-
return context.createErrorResponse(
|
|
43
|
-
id,
|
|
44
|
-
-32004,
|
|
45
|
-
"Mint not found in LiteSVM",
|
|
46
|
-
);
|
|
47
|
-
const mintInfo = MintLayout.decode(
|
|
48
|
-
Buffer.from(mintAcc.data).slice(0, MINT_SIZE),
|
|
49
|
-
);
|
|
50
|
-
const decimals = mintInfo.decimals;
|
|
51
|
-
const hasAuth = mintInfo.mintAuthorityOption === 1;
|
|
52
|
-
const authPk = hasAuth ? new PublicKey(mintInfo.mintAuthority) : null;
|
|
53
|
-
if (!hasAuth || !authPk || !authPk.equals(faucet.publicKey)) {
|
|
54
|
-
return context.createErrorResponse(
|
|
55
|
-
id,
|
|
56
|
-
-32000,
|
|
57
|
-
"Mint has no faucet authority; cannot mint real tokens",
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const ixs: TransactionInstruction[] = [];
|
|
62
|
-
// Detect which token program the mint belongs to (SPL v1 vs Token-2022)
|
|
63
|
-
const mintOwnerStr = (() => {
|
|
64
|
-
try {
|
|
65
|
-
return (mintAcc.owner as PublicKey).toBase58();
|
|
66
|
-
} catch {
|
|
67
|
-
return String(mintAcc.owner);
|
|
68
|
-
}
|
|
69
|
-
})();
|
|
70
|
-
const tokenProgramId =
|
|
71
|
-
mintOwnerStr === TOKEN_2022_PROGRAM_ID.toBase58()
|
|
72
|
-
? TOKEN_2022_PROGRAM_ID
|
|
73
|
-
: TOKEN_PROGRAM_ID;
|
|
74
|
-
|
|
75
|
-
// Derive ATA using the correct token program
|
|
76
|
-
const ata = getAssociatedTokenAddressSync(
|
|
77
|
-
mint,
|
|
78
|
-
owner,
|
|
79
|
-
true,
|
|
80
|
-
tokenProgramId,
|
|
81
|
-
);
|
|
82
|
-
const ataAcc = context.svm.getAccount(ata);
|
|
83
|
-
|
|
84
|
-
// Ensure ATA exists under the correct token program
|
|
85
|
-
if (!ataAcc || (ataAcc.data?.length ?? 0) < ACCOUNT_SIZE) {
|
|
86
|
-
ixs.push(
|
|
87
|
-
createAssociatedTokenAccountInstruction(
|
|
88
|
-
faucet.publicKey,
|
|
89
|
-
ata,
|
|
90
|
-
owner,
|
|
91
|
-
mint,
|
|
92
|
-
tokenProgramId,
|
|
93
|
-
),
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const amount =
|
|
98
|
-
typeof rawAmount === "bigint" ? rawAmount : BigInt(rawAmount);
|
|
99
|
-
ixs.push(
|
|
100
|
-
createMintToCheckedInstruction(
|
|
101
|
-
mint,
|
|
102
|
-
ata,
|
|
103
|
-
faucet.publicKey,
|
|
104
|
-
amount,
|
|
105
|
-
decimals,
|
|
106
|
-
[],
|
|
107
|
-
tokenProgramId,
|
|
108
|
-
),
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
// Build a VersionedTransaction (legacy message) to ensure consistent encoding/decoding downstream
|
|
112
|
-
let rb = context.svm.latestBlockhash();
|
|
113
|
-
if (!rb || rb.length === 0) {
|
|
114
|
-
const bh = new Uint8Array(32);
|
|
115
|
-
crypto.getRandomValues(bh);
|
|
116
|
-
rb = context.encodeBase58(bh);
|
|
117
|
-
}
|
|
118
|
-
const msg = new TransactionMessage({
|
|
119
|
-
payerKey: faucet.publicKey,
|
|
120
|
-
recentBlockhash: rb,
|
|
121
|
-
instructions: ixs,
|
|
122
|
-
});
|
|
123
|
-
const legacy = msg.compileToLegacyMessage();
|
|
124
|
-
const vtx = new VersionedTransaction(legacy);
|
|
125
|
-
vtx.sign([faucet]);
|
|
126
|
-
|
|
127
|
-
// Capture preBalances for primary accounts referenced and token pre amount
|
|
128
|
-
const trackedKeys = [faucet.publicKey, ata, mint, owner];
|
|
129
|
-
const _preBalances = trackedKeys.map((pk) => {
|
|
130
|
-
try {
|
|
131
|
-
return Number(context.svm.getBalance(pk) || 0n);
|
|
132
|
-
} catch {
|
|
133
|
-
return 0;
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
// Token mint decimals and pre amount
|
|
137
|
-
let decsForMint = 0;
|
|
138
|
-
let preTokenAmt: bigint = 0n;
|
|
139
|
-
try {
|
|
140
|
-
const mintAcc0 = context.svm.getAccount(mint);
|
|
141
|
-
const mintInfo0 = mintAcc0
|
|
142
|
-
? MintLayout.decode(Buffer.from(mintAcc0.data).slice(0, MINT_SIZE))
|
|
143
|
-
: undefined;
|
|
144
|
-
decsForMint = Number(mintInfo0?.decimals ?? decimals ?? 0);
|
|
145
|
-
const ataAcc0 = context.svm.getAccount(ata);
|
|
146
|
-
if (ataAcc0 && (ataAcc0.data?.length ?? 0) >= ACCOUNT_SIZE) {
|
|
147
|
-
const dec0 = AccountLayout.decode(Buffer.from(ataAcc0.data));
|
|
148
|
-
preTokenAmt = BigInt(dec0.amount.toString());
|
|
149
|
-
}
|
|
150
|
-
} catch {}
|
|
151
|
-
|
|
152
|
-
// Send via the standard RPC sendTransaction path to unify capture/parsing
|
|
153
|
-
const rawBase64ForRpc = Buffer.from(vtx.serialize()).toString("base64");
|
|
154
|
-
const rpcResp = await (sendTxRpc as RpcMethodHandler)(
|
|
155
|
-
id,
|
|
156
|
-
[rawBase64ForRpc],
|
|
157
|
-
context,
|
|
158
|
-
);
|
|
159
|
-
if (
|
|
160
|
-
rpcResp &&
|
|
161
|
-
typeof rpcResp === "object" &&
|
|
162
|
-
"error" in (rpcResp as Record<string, unknown>) &&
|
|
163
|
-
(rpcResp as Record<string, unknown>).error != null
|
|
164
|
-
) {
|
|
165
|
-
return rpcResp;
|
|
166
|
-
}
|
|
167
|
-
const signatureStr = (() => {
|
|
168
|
-
try {
|
|
169
|
-
const r = rpcResp as Record<string, unknown>;
|
|
170
|
-
const v = r?.result;
|
|
171
|
-
return v == null ? "" : String(v);
|
|
172
|
-
} catch {
|
|
173
|
-
return "";
|
|
174
|
-
}
|
|
175
|
-
})();
|
|
176
|
-
|
|
177
|
-
// Token balance deltas (pre/post) for ATA
|
|
178
|
-
type UiTokenAmount = {
|
|
179
|
-
amount: string;
|
|
180
|
-
decimals: number;
|
|
181
|
-
uiAmount: number;
|
|
182
|
-
uiAmountString: string;
|
|
183
|
-
};
|
|
184
|
-
type TokenBalance = {
|
|
185
|
-
accountIndex: number;
|
|
186
|
-
mint: string;
|
|
187
|
-
owner: string;
|
|
188
|
-
uiTokenAmount: UiTokenAmount;
|
|
189
|
-
};
|
|
190
|
-
const _preTokenBalances: TokenBalance[] = [];
|
|
191
|
-
let _postTokenBalances: TokenBalance[] = [];
|
|
192
|
-
try {
|
|
193
|
-
const decs = decsForMint;
|
|
194
|
-
const ui = (n: bigint) => ({
|
|
195
|
-
amount: n.toString(),
|
|
196
|
-
decimals: decs,
|
|
197
|
-
uiAmount: Number(n) / 10 ** decs,
|
|
198
|
-
uiAmountString: (Number(n) / 10 ** decs).toString(),
|
|
199
|
-
});
|
|
200
|
-
const preAmt = preTokenAmt;
|
|
201
|
-
const ataPostAcc = context.svm.getAccount(ata); // after send
|
|
202
|
-
const postAmt =
|
|
203
|
-
ataPostAcc && (ataPostAcc.data?.length ?? 0) >= ACCOUNT_SIZE
|
|
204
|
-
? BigInt(
|
|
205
|
-
AccountLayout.decode(
|
|
206
|
-
Buffer.from(ataPostAcc.data),
|
|
207
|
-
).amount.toString(),
|
|
208
|
-
)
|
|
209
|
-
: preAmt;
|
|
210
|
-
const msgAny = vtx.message as unknown as {
|
|
211
|
-
staticAccountKeys?: Array<string | PublicKey>;
|
|
212
|
-
accountKeys?: Array<string | PublicKey>;
|
|
213
|
-
};
|
|
214
|
-
const rawKeys: Array<string | PublicKey> = Array.isArray(
|
|
215
|
-
msgAny.staticAccountKeys,
|
|
216
|
-
)
|
|
217
|
-
? msgAny.staticAccountKeys
|
|
218
|
-
: Array.isArray(msgAny.accountKeys)
|
|
219
|
-
? msgAny.accountKeys
|
|
220
|
-
: [];
|
|
221
|
-
const keys = rawKeys.map((k: string | PublicKey) => {
|
|
222
|
-
try {
|
|
223
|
-
return typeof k === "string" ? k : new PublicKey(k).toBase58();
|
|
224
|
-
} catch {
|
|
225
|
-
return String(k);
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
const ataIndex = keys.indexOf(ata.toBase58());
|
|
229
|
-
preTokenBalances = [
|
|
230
|
-
{
|
|
231
|
-
accountIndex: ataIndex >= 0 ? ataIndex : 0,
|
|
232
|
-
mint: mint.toBase58(),
|
|
233
|
-
owner: owner.toBase58(),
|
|
234
|
-
uiTokenAmount: ui(preAmt),
|
|
235
|
-
},
|
|
236
|
-
];
|
|
237
|
-
_postTokenBalances = [
|
|
238
|
-
{
|
|
239
|
-
accountIndex: ataIndex >= 0 ? ataIndex : 0,
|
|
240
|
-
mint: mint.toBase58(),
|
|
241
|
-
owner: owner.toBase58(),
|
|
242
|
-
uiTokenAmount: ui(postAmt),
|
|
243
|
-
},
|
|
244
|
-
];
|
|
245
|
-
} catch {}
|
|
246
|
-
|
|
247
|
-
// send-transaction already records/announces signature and persists to DB
|
|
248
|
-
|
|
249
|
-
return context.createSuccessResponse(id, {
|
|
250
|
-
ok: true,
|
|
251
|
-
signature: signatureStr,
|
|
252
|
-
mint: mintStr,
|
|
253
|
-
owner: ownerStr,
|
|
254
|
-
amount: amount.toString(),
|
|
255
|
-
});
|
|
256
|
-
} catch (e) {
|
|
257
|
-
return context.createErrorResponse(
|
|
258
|
-
id,
|
|
259
|
-
-32603,
|
|
260
|
-
"MintTo failed",
|
|
261
|
-
(e as Error)?.message || String(e),
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
export type { RpcMethodHandler } from "../../types";
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { RpcMethodHandler } from "../../types";
|
|
2
|
-
|
|
3
|
-
export const getBlock: RpcMethodHandler = (id, params, context) => {
|
|
4
|
-
try {
|
|
5
|
-
const [slotRaw] = params || [];
|
|
6
|
-
const slot = Number(slotRaw);
|
|
7
|
-
if (!Number.isFinite(slot) || slot < 0) throw new Error("Invalid slot");
|
|
8
|
-
const parentSlot = Math.max(0, slot - 1);
|
|
9
|
-
const blockhash = context.svm.latestBlockhash();
|
|
10
|
-
const previousBlockhash = context.svm.latestBlockhash();
|
|
11
|
-
const SLOT_TIME_MS = 400;
|
|
12
|
-
const currentTime = Math.floor(Date.now() / 1000);
|
|
13
|
-
const slotDiff = Number(context.slot) - slot;
|
|
14
|
-
const blockTime =
|
|
15
|
-
slot > Number(context.slot)
|
|
16
|
-
? null
|
|
17
|
-
: currentTime -
|
|
18
|
-
Math.floor((Math.max(0, slotDiff) * SLOT_TIME_MS) / 1000);
|
|
19
|
-
return context.createSuccessResponse(id, {
|
|
20
|
-
blockhash,
|
|
21
|
-
previousBlockhash,
|
|
22
|
-
parentSlot,
|
|
23
|
-
transactions: [],
|
|
24
|
-
rewards: [],
|
|
25
|
-
blockTime,
|
|
26
|
-
});
|
|
27
|
-
} catch (error: unknown) {
|
|
28
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
29
|
-
return context.createErrorResponse(id, -32602, "Invalid params", message);
|
|
30
|
-
}
|
|
31
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { RpcMethodHandler } from "../../types";
|
|
2
|
-
|
|
3
|
-
export const getBlocksWithLimit: RpcMethodHandler = (id, params, context) => {
|
|
4
|
-
try {
|
|
5
|
-
const [startSlotRaw, limitRaw] = params || [];
|
|
6
|
-
const start = Number(startSlotRaw);
|
|
7
|
-
const limit = Number(limitRaw);
|
|
8
|
-
if (!Number.isFinite(start) || start < 0)
|
|
9
|
-
throw new Error("Invalid start slot");
|
|
10
|
-
if (!Number.isFinite(limit) || limit <= 0) throw new Error("Invalid limit");
|
|
11
|
-
const end = Math.min(Number(context.slot), start + limit - 1);
|
|
12
|
-
const blocks: number[] = [];
|
|
13
|
-
for (let s = start; s <= end; s++) blocks.push(s);
|
|
14
|
-
return context.createSuccessResponse(id, blocks);
|
|
15
|
-
} catch (error: unknown) {
|
|
16
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
17
|
-
return context.createErrorResponse(id, -32602, "Invalid params", message);
|
|
18
|
-
}
|
|
19
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { RpcMethodHandler } from "../../types";
|
|
2
|
-
|
|
3
|
-
export const getLatestBlockhash: RpcMethodHandler = (id, _params, context) => {
|
|
4
|
-
const blockhash = context.svm.latestBlockhash();
|
|
5
|
-
return context.createSuccessResponse(id, {
|
|
6
|
-
context: { slot: Number(context.slot) },
|
|
7
|
-
value: {
|
|
8
|
-
blockhash,
|
|
9
|
-
lastValidBlockHeight: Number(context.blockHeight + 150n),
|
|
10
|
-
},
|
|
11
|
-
});
|
|
12
|
-
};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export { getBlock } from "./get-block";
|
|
2
|
-
export { getBlockHeight } from "./get-block-height";
|
|
3
|
-
export { getBlocksWithLimit } from "./get-blocks-with-limit";
|
|
4
|
-
export { getLatestBlockhash } from "./get-latest-blockhash";
|
|
5
|
-
export { getSlot } from "./get-slot";
|
|
6
|
-
export { isBlockhashValid } from "./is-blockhash-valid";
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { RpcMethodHandler } from "../../types";
|
|
2
|
-
|
|
3
|
-
export const isBlockhashValid: RpcMethodHandler = (id, params, context) => {
|
|
4
|
-
try {
|
|
5
|
-
const [blockhash] = params || [];
|
|
6
|
-
const current = context.svm.latestBlockhash();
|
|
7
|
-
const isValid =
|
|
8
|
-
typeof blockhash === "string" &&
|
|
9
|
-
blockhash.length > 0 &&
|
|
10
|
-
blockhash === current;
|
|
11
|
-
return context.createSuccessResponse(id, {
|
|
12
|
-
context: { slot: Number(context.slot) },
|
|
13
|
-
value: isValid,
|
|
14
|
-
});
|
|
15
|
-
} catch (error: unknown) {
|
|
16
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
17
|
-
return context.createErrorResponse(id, -32602, "Invalid params", message);
|
|
18
|
-
}
|
|
19
|
-
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { RpcMethodHandler } from "../../types";
|
|
2
|
-
|
|
3
|
-
export const getClusterNodes: RpcMethodHandler = (id, _params, _context) => {
|
|
4
|
-
return {
|
|
5
|
-
jsonrpc: "2.0",
|
|
6
|
-
id,
|
|
7
|
-
result: [
|
|
8
|
-
{
|
|
9
|
-
pubkey: "11111111111111111111111111111111",
|
|
10
|
-
gossip: "127.0.0.1:8001",
|
|
11
|
-
tpu: "127.0.0.1:8003",
|
|
12
|
-
rpc: "127.0.0.1:8899",
|
|
13
|
-
version: "1.17.0",
|
|
14
|
-
},
|
|
15
|
-
],
|
|
16
|
-
};
|
|
17
|
-
};
|