solforge 0.2.8 → 0.2.10

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solforge",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "private": false,
@@ -3,4 +3,8 @@ import { decodeBase58 } from "../server/lib/base58";
3
3
  const s = process.argv[2] || "84eT";
4
4
  const bytes = decodeBase58(s);
5
5
  console.log(bytes);
6
- console.log(Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join(" "));
6
+ console.log(
7
+ Array.from(bytes)
8
+ .map((b) => b.toString(16).padStart(2, "0"))
9
+ .join(" "),
10
+ );
@@ -1,264 +1,328 @@
1
1
  import {
2
- AddressLookupTableInstruction,
3
- ComputeBudgetInstruction,
4
- PublicKey,
5
- StakeInstruction,
6
- SystemInstruction,
7
- SystemProgram,
8
- TransactionInstruction,
9
- VoteInstruction,
2
+ AddressLookupTableInstruction,
3
+ ComputeBudgetInstruction,
4
+ PublicKey,
5
+ StakeInstruction,
6
+ SystemInstruction,
7
+ SystemProgram,
8
+ TransactionInstruction,
9
+ VoteInstruction,
10
10
  } from "@solana/web3.js";
11
- import { TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID } from "@solana/spl-token";
11
+ import {
12
+ TOKEN_PROGRAM_ID,
13
+ ASSOCIATED_TOKEN_PROGRAM_ID,
14
+ TOKEN_2022_PROGRAM_ID,
15
+ } from "@solana/spl-token";
12
16
  import { tryParseSplToken } from "./parsers/spl-token";
13
17
  import { tryParseAta } from "./parsers/spl-associated-token-account";
14
18
  import { decodeBase58 as _decodeBase58 } from "./base58";
15
19
 
16
20
  export type ParsedInstruction =
17
- | { program: string; programId: string; parsed: { type: string; info: any } }
18
- | { programId: string; accounts: string[]; data: string };
21
+ | {
22
+ program: string;
23
+ programId: string;
24
+ parsed: { type: string; info: unknown };
25
+ }
26
+ | { programId: string; accounts: string[]; data: string };
19
27
 
20
28
  function makeIx(
21
- programId: string,
22
- accountKeys: string[],
23
- accounts: number[],
24
- dataBase58: string,
29
+ programId: string,
30
+ accountKeys: string[],
31
+ accounts: number[],
32
+ dataBase58: string,
25
33
  ): TransactionInstruction {
26
- const keys = accounts.map((i) => ({
27
- pubkey: new PublicKey(accountKeys[i] || SystemProgram.programId),
28
- isSigner: false,
29
- isWritable: false,
30
- }));
31
- const data = Buffer.from(_decodeBase58(dataBase58));
32
- return new TransactionInstruction({ programId: new PublicKey(programId), keys, data });
34
+ const keys = accounts.map((i) => ({
35
+ pubkey: new PublicKey(accountKeys[i] || SystemProgram.programId),
36
+ isSigner: false,
37
+ isWritable: false,
38
+ }));
39
+ const data = Buffer.from(_decodeBase58(dataBase58));
40
+ return new TransactionInstruction({
41
+ programId: new PublicKey(programId),
42
+ keys,
43
+ data,
44
+ });
33
45
  }
34
46
 
35
- function ok(program: string, programId: string, type: string, info: any): ParsedInstruction {
36
- return { program, programId, parsed: { type, info } };
47
+ function ok(
48
+ program: string,
49
+ programId: string,
50
+ type: string,
51
+ info: unknown,
52
+ ): ParsedInstruction {
53
+ return { program, programId, parsed: { type, info } };
37
54
  }
38
55
 
39
56
  export function parseInstruction(
40
- programId: string,
41
- accounts: number[],
42
- dataBase58: string,
43
- accountKeys: string[],
57
+ programId: string,
58
+ accounts: number[],
59
+ dataBase58: string,
60
+ accountKeys: string[],
61
+ tokenBalanceHints?: Array<{ mint: string; decimals: number }>,
44
62
  ): ParsedInstruction {
45
- try {
46
- const pid = new PublicKey(programId);
47
- const ix = makeIx(programId, accountKeys, accounts, dataBase58);
63
+ try {
64
+ const pid = new PublicKey(programId);
65
+ const ix = makeIx(programId, accountKeys, accounts, dataBase58);
48
66
 
49
- // SPL Token
50
- if (pid.equals(TOKEN_PROGRAM_ID)) {
51
- const parsed = tryParseSplToken(ix, programId, accountKeys, dataBase58);
52
- if (parsed) return parsed;
53
- }
67
+ // SPL Token (legacy) and Token-2022
68
+ if (pid.equals(TOKEN_PROGRAM_ID) || pid.equals(TOKEN_2022_PROGRAM_ID)) {
69
+ const parsed = tryParseSplToken(
70
+ ix,
71
+ programId,
72
+ accountKeys,
73
+ dataBase58,
74
+ tokenBalanceHints,
75
+ );
76
+ if (parsed) return parsed;
77
+ }
54
78
 
55
- // Associated Token Account
56
- if (pid.equals(ASSOCIATED_TOKEN_PROGRAM_ID)) {
57
- const parsed = tryParseAta(ix, programId);
58
- if (parsed) return parsed;
59
- }
79
+ // Associated Token Account
80
+ if (pid.equals(ASSOCIATED_TOKEN_PROGRAM_ID)) {
81
+ const parsed = tryParseAta(ix, programId);
82
+ if (parsed) return parsed;
83
+ }
60
84
 
61
- // System Program
62
- if (pid.equals(SystemProgram.programId)) {
63
- const t = SystemInstruction.decodeInstructionType(ix);
64
- switch (t) {
65
- case "Create": {
66
- const p = SystemInstruction.decodeCreateAccount(ix);
67
- const from = p.fromPubkey.toBase58();
68
- const newAcc = p.newAccountPubkey.toBase58();
69
- const owner = p.programId.toBase58();
70
- return ok("system", programId, "createAccount", {
71
- // Explorer-compatible field names
72
- source: from,
73
- newAccount: newAcc,
74
- owner,
75
- lamports: Number(p.lamports),
76
- space: Number(p.space),
77
- // Keep legacy aliases too
78
- fromPubkey: from,
79
- newAccountPubkey: newAcc,
80
- programId: owner,
81
- });
82
- }
83
- case "Transfer": {
84
- const p = SystemInstruction.decodeTransfer(ix);
85
- return ok("system", programId, "transfer", {
86
- source: p.fromPubkey.toBase58(),
87
- destination: p.toPubkey.toBase58(),
88
- lamports: Number(p.lamports),
89
- });
90
- }
91
- case "TransferWithSeed": {
92
- const p = SystemInstruction.decodeTransferWithSeed(ix);
93
- return ok("system", programId, "transferWithSeed", {
94
- fromPubkey: p.fromPubkey.toBase58(),
95
- basePubkey: p.basePubkey.toBase58(),
96
- toPubkey: p.toPubkey.toBase58(),
97
- lamports: Number(p.lamports),
98
- seed: p.seed,
99
- programId: p.programId.toBase58(),
100
- });
101
- }
102
- case "Allocate": {
103
- const p = SystemInstruction.decodeAllocate(ix);
104
- return ok("system", programId, "allocate", {
105
- accountPubkey: p.accountPubkey.toBase58(),
106
- space: Number(p.space),
107
- });
108
- }
109
- case "AllocateWithSeed": {
110
- const p = SystemInstruction.decodeAllocateWithSeed(ix);
111
- return ok("system", programId, "allocateWithSeed", {
112
- accountPubkey: p.accountPubkey.toBase58(),
113
- basePubkey: p.basePubkey.toBase58(),
114
- seed: p.seed,
115
- space: Number(p.space),
116
- programId: p.programId.toBase58(),
117
- });
118
- }
119
- case "Assign": {
120
- const p = SystemInstruction.decodeAssign(ix);
121
- return ok("system", programId, "assign", {
122
- accountPubkey: p.accountPubkey.toBase58(),
123
- programId: p.programId.toBase58(),
124
- });
125
- }
126
- case "AssignWithSeed": {
127
- const p = SystemInstruction.decodeAssignWithSeed(ix);
128
- return ok("system", programId, "assignWithSeed", {
129
- accountPubkey: p.accountPubkey.toBase58(),
130
- basePubkey: p.basePubkey.toBase58(),
131
- seed: p.seed,
132
- programId: p.programId.toBase58(),
133
- });
134
- }
135
- case "InitializeNonceAccount":
136
- case "AdvanceNonceAccount":
137
- case "WithdrawNonceAccount":
138
- case "AuthorizeNonceAccount":
139
- case "CreateWithSeed":
140
- case "UpgradeNonceAccount": {
141
- // For brevity: rely on type only; details can be added if needed
142
- return ok("system", programId, t, {});
143
- }
144
- default:
145
- break;
146
- }
147
- }
85
+ // System Program
86
+ if (pid.equals(SystemProgram.programId)) {
87
+ try {
88
+ const t = SystemInstruction.decodeInstructionType(ix);
89
+ switch (t) {
90
+ case "Create": {
91
+ try {
92
+ const p = SystemInstruction.decodeCreateAccount(ix);
93
+ const from = p.fromPubkey.toBase58();
94
+ const newAcc = p.newAccountPubkey.toBase58();
95
+ const owner = p.programId.toBase58();
96
+ return ok("system", programId, "createAccount", {
97
+ // Explorer-compatible field names
98
+ source: from,
99
+ newAccount: newAcc,
100
+ owner,
101
+ lamports: Number(p.lamports),
102
+ space: Number(p.space),
103
+ // Keep legacy aliases too
104
+ fromPubkey: from,
105
+ newAccountPubkey: newAcc,
106
+ programId: owner,
107
+ });
108
+ } catch {
109
+ return ok("system", programId, "createAccount", {});
110
+ }
111
+ }
112
+ case "Transfer": {
113
+ try {
114
+ const p = SystemInstruction.decodeTransfer(ix);
115
+ return ok("system", programId, "transfer", {
116
+ source: p.fromPubkey.toBase58(),
117
+ destination: p.toPubkey.toBase58(),
118
+ lamports: Number(p.lamports),
119
+ });
120
+ } catch {
121
+ return ok("system", programId, "transfer", {});
122
+ }
123
+ }
124
+ case "TransferWithSeed": {
125
+ try {
126
+ const p = SystemInstruction.decodeTransferWithSeed(ix);
127
+ return ok("system", programId, "transferWithSeed", {
128
+ fromPubkey: p.fromPubkey.toBase58(),
129
+ basePubkey: p.basePubkey.toBase58(),
130
+ toPubkey: p.toPubkey.toBase58(),
131
+ lamports: Number(p.lamports),
132
+ seed: p.seed,
133
+ programId: p.programId.toBase58(),
134
+ });
135
+ } catch {
136
+ return ok("system", programId, "transferWithSeed", {});
137
+ }
138
+ }
139
+ case "Allocate": {
140
+ try {
141
+ const p = SystemInstruction.decodeAllocate(ix);
142
+ return ok("system", programId, "allocate", {
143
+ accountPubkey: p.accountPubkey.toBase58(),
144
+ space: Number(p.space),
145
+ });
146
+ } catch {
147
+ return ok("system", programId, "allocate", {});
148
+ }
149
+ }
150
+ case "AllocateWithSeed": {
151
+ try {
152
+ const p = SystemInstruction.decodeAllocateWithSeed(ix);
153
+ return ok("system", programId, "allocateWithSeed", {
154
+ accountPubkey: p.accountPubkey.toBase58(),
155
+ basePubkey: p.basePubkey.toBase58(),
156
+ seed: p.seed,
157
+ space: Number(p.space),
158
+ programId: p.programId.toBase58(),
159
+ });
160
+ } catch {
161
+ return ok("system", programId, "allocateWithSeed", {});
162
+ }
163
+ }
164
+ case "Assign": {
165
+ try {
166
+ const p = SystemInstruction.decodeAssign(ix);
167
+ return ok("system", programId, "assign", {
168
+ accountPubkey: p.accountPubkey.toBase58(),
169
+ programId: p.programId.toBase58(),
170
+ });
171
+ } catch {
172
+ return ok("system", programId, "assign", {});
173
+ }
174
+ }
175
+ case "AssignWithSeed": {
176
+ try {
177
+ const p = SystemInstruction.decodeAssignWithSeed(ix);
178
+ return ok("system", programId, "assignWithSeed", {
179
+ accountPubkey: p.accountPubkey.toBase58(),
180
+ basePubkey: p.basePubkey.toBase58(),
181
+ seed: p.seed,
182
+ programId: p.programId.toBase58(),
183
+ });
184
+ } catch {
185
+ return ok("system", programId, "assignWithSeed", {});
186
+ }
187
+ }
188
+ case "InitializeNonceAccount":
189
+ case "AdvanceNonceAccount":
190
+ case "WithdrawNonceAccount":
191
+ case "AuthorizeNonceAccount":
192
+ case "CreateWithSeed":
193
+ case "UpgradeNonceAccount": {
194
+ return ok("system", programId, t, {});
195
+ }
196
+ default:
197
+ return ok("system", programId, t, {});
198
+ }
199
+ } catch {
200
+ // If we cannot even decode the instruction type, fallthrough to raw
201
+ }
202
+ }
148
203
 
149
- // Compute Budget
150
- try {
151
- const t = ComputeBudgetInstruction.decodeInstructionType(ix);
152
- switch (t) {
153
- case "SetComputeUnitLimit": {
154
- const p = ComputeBudgetInstruction.decodeSetComputeUnitLimit(ix);
155
- return ok("computeBudget", programId, "setComputeUnitLimit", { units: Number(p.units) });
156
- }
157
- case "SetComputeUnitPrice": {
158
- const p = ComputeBudgetInstruction.decodeSetComputeUnitPrice(ix);
159
- return ok("computeBudget", programId, "setComputeUnitPrice", { microLamports: Number(p.microLamports) });
160
- }
161
- case "RequestHeapFrame": {
162
- const p = ComputeBudgetInstruction.decodeRequestHeapFrame(ix);
163
- return ok("computeBudget", programId, "requestHeapFrame", { bytes: Number(p.bytes) });
164
- }
165
- case "RequestUnits": {
166
- const p = ComputeBudgetInstruction.decodeRequestUnits(ix);
167
- return ok("computeBudget", programId, "requestUnits", { units: Number(p.units), additionalFee: Number(p.additionalFee) });
168
- }
169
- }
170
- } catch {}
204
+ // Compute Budget
205
+ try {
206
+ const t = ComputeBudgetInstruction.decodeInstructionType(ix);
207
+ switch (t) {
208
+ case "SetComputeUnitLimit": {
209
+ const p = ComputeBudgetInstruction.decodeSetComputeUnitLimit(ix);
210
+ return ok("computeBudget", programId, "setComputeUnitLimit", {
211
+ units: Number(p.units),
212
+ });
213
+ }
214
+ case "SetComputeUnitPrice": {
215
+ const p = ComputeBudgetInstruction.decodeSetComputeUnitPrice(ix);
216
+ return ok("computeBudget", programId, "setComputeUnitPrice", {
217
+ microLamports: Number(p.microLamports),
218
+ });
219
+ }
220
+ case "RequestHeapFrame": {
221
+ const p = ComputeBudgetInstruction.decodeRequestHeapFrame(ix);
222
+ return ok("computeBudget", programId, "requestHeapFrame", {
223
+ bytes: Number(p.bytes),
224
+ });
225
+ }
226
+ case "RequestUnits": {
227
+ const p = ComputeBudgetInstruction.decodeRequestUnits(ix);
228
+ return ok("computeBudget", programId, "requestUnits", {
229
+ units: Number(p.units),
230
+ additionalFee: Number(p.additionalFee),
231
+ });
232
+ }
233
+ }
234
+ } catch {}
171
235
 
172
- // Stake
173
- try {
174
- const t = StakeInstruction.decodeInstructionType(ix);
175
- switch (t) {
176
- case "Initialize":
177
- return ok("stake", programId, "initialize", {});
178
- case "Delegate": {
179
- const p = StakeInstruction.decodeDelegate(ix);
180
- return ok("stake", programId, "delegate", {
181
- stakePubkey: p.stakePubkey.toBase58(),
182
- votePubkey: p.votePubkey.toBase58(),
183
- authorizedPubkey: p.authorizedPubkey.toBase58(),
184
- });
185
- }
186
- case "Authorize": {
187
- const p = StakeInstruction.decodeAuthorize(ix);
188
- return ok("stake", programId, "authorize", {
189
- stakePubkey: p.stakePubkey.toBase58(),
190
- authorizedPubkey: p.authorizedPubkey.toBase58(),
191
- newAuthorizedPubkey: p.newAuthorizedPubkey.toBase58(),
192
- stakeAuthorizationType: p.stakeAuthorizationType.index,
193
- });
194
- }
195
- case "AuthorizeWithSeed":
196
- return ok("stake", programId, "authorizeWithSeed", {});
197
- case "Split":
198
- return ok("stake", programId, "split", {});
199
- case "Withdraw":
200
- return ok("stake", programId, "withdraw", {});
201
- case "Deactivate":
202
- return ok("stake", programId, "deactivate", {});
203
- case "Merge":
204
- return ok("stake", programId, "merge", {});
205
- }
206
- } catch {}
236
+ // Stake
237
+ try {
238
+ const t = StakeInstruction.decodeInstructionType(ix);
239
+ switch (t) {
240
+ case "Initialize":
241
+ return ok("stake", programId, "initialize", {});
242
+ case "Delegate": {
243
+ const p = StakeInstruction.decodeDelegate(ix);
244
+ return ok("stake", programId, "delegate", {
245
+ stakePubkey: p.stakePubkey.toBase58(),
246
+ votePubkey: p.votePubkey.toBase58(),
247
+ authorizedPubkey: p.authorizedPubkey.toBase58(),
248
+ });
249
+ }
250
+ case "Authorize": {
251
+ const p = StakeInstruction.decodeAuthorize(ix);
252
+ return ok("stake", programId, "authorize", {
253
+ stakePubkey: p.stakePubkey.toBase58(),
254
+ authorizedPubkey: p.authorizedPubkey.toBase58(),
255
+ newAuthorizedPubkey: p.newAuthorizedPubkey.toBase58(),
256
+ stakeAuthorizationType: p.stakeAuthorizationType.index,
257
+ });
258
+ }
259
+ case "AuthorizeWithSeed":
260
+ return ok("stake", programId, "authorizeWithSeed", {});
261
+ case "Split":
262
+ return ok("stake", programId, "split", {});
263
+ case "Withdraw":
264
+ return ok("stake", programId, "withdraw", {});
265
+ case "Deactivate":
266
+ return ok("stake", programId, "deactivate", {});
267
+ case "Merge":
268
+ return ok("stake", programId, "merge", {});
269
+ }
270
+ } catch {}
207
271
 
208
- // Vote
209
- try {
210
- const t = VoteInstruction.decodeInstructionType(ix);
211
- switch (t) {
212
- case "InitializeAccount":
213
- return ok("vote", programId, "initialize", {});
214
- case "Authorize":
215
- return ok("vote", programId, "authorize", {});
216
- case "AuthorizeWithSeed":
217
- return ok("vote", programId, "authorizeWithSeed", {});
218
- case "Withdraw":
219
- return ok("vote", programId, "withdraw", {});
220
- default:
221
- return ok("vote", programId, t, {});
222
- }
223
- } catch {}
272
+ // Vote
273
+ try {
274
+ const t = VoteInstruction.decodeInstructionType(ix);
275
+ switch (t) {
276
+ case "InitializeAccount":
277
+ return ok("vote", programId, "initialize", {});
278
+ case "Authorize":
279
+ return ok("vote", programId, "authorize", {});
280
+ case "AuthorizeWithSeed":
281
+ return ok("vote", programId, "authorizeWithSeed", {});
282
+ case "Withdraw":
283
+ return ok("vote", programId, "withdraw", {});
284
+ default:
285
+ return ok("vote", programId, t, {});
286
+ }
287
+ } catch {}
224
288
 
225
- // Address Lookup Table
226
- try {
227
- const t = AddressLookupTableInstruction.decodeInstructionType(ix);
228
- switch (t) {
229
- case "CreateLookupTable":
230
- case "ExtendLookupTable":
231
- case "CloseLookupTable":
232
- case "FreezeLookupTable":
233
- case "DeactivateLookupTable":
234
- return ok("address-lookup-table", programId, t, {});
235
- }
236
- } catch {}
289
+ // Address Lookup Table
290
+ try {
291
+ const t = AddressLookupTableInstruction.decodeInstructionType(ix);
292
+ switch (t) {
293
+ case "CreateLookupTable":
294
+ case "ExtendLookupTable":
295
+ case "CloseLookupTable":
296
+ case "FreezeLookupTable":
297
+ case "DeactivateLookupTable":
298
+ return ok("address-lookup-table", programId, t, {});
299
+ }
300
+ } catch {}
237
301
 
238
- // Memo program: parse utf8 memo if possible
239
- if (
240
- programId === "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" ||
241
- programId === "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo"
242
- ) {
243
- try {
244
- const bytes = _decodeBase58(dataBase58);
245
- const memo = new TextDecoder().decode(bytes);
246
- return ok("spl-memo", programId, "memo", { memo });
247
- } catch {}
248
- return ok("spl-memo", programId, "memo", {});
249
- }
302
+ // Memo program: parse utf8 memo if possible
303
+ if (
304
+ programId === "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" ||
305
+ programId === "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo"
306
+ ) {
307
+ try {
308
+ const bytes = _decodeBase58(dataBase58);
309
+ const memo = new TextDecoder().decode(bytes);
310
+ return ok("spl-memo", programId, "memo", { memo });
311
+ } catch {}
312
+ return ok("spl-memo", programId, "memo", {});
313
+ }
250
314
 
251
- // Fallback: unknown program, return raw
252
- return {
253
- programId,
254
- accounts: accounts.map((i) => accountKeys[i] || ""),
255
- data: dataBase58,
256
- };
257
- } catch {
258
- return {
259
- programId,
260
- accounts: accounts.map((i) => accountKeys[i] || ""),
261
- data: dataBase58,
262
- };
263
- }
315
+ // Fallback: unknown program, return raw
316
+ return {
317
+ programId,
318
+ accounts: accounts.map((i) => accountKeys[i] || ""),
319
+ data: dataBase58,
320
+ };
321
+ } catch {
322
+ return {
323
+ programId,
324
+ accounts: accounts.map((i) => accountKeys[i] || ""),
325
+ data: dataBase58,
326
+ };
327
+ }
264
328
  }