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 +1 -1
- package/scripts/decode-b58.ts +5 -1
- package/server/lib/instruction-parser.ts +302 -238
- package/server/lib/parsers/spl-associated-token-account.ts +36 -30
- package/server/lib/parsers/spl-token.ts +310 -142
- package/server/methods/account/request-airdrop.ts +121 -105
- package/server/methods/admin/mint-to.ts +29 -14
- package/server/methods/transaction/get-transaction.ts +390 -326
- package/server/methods/transaction/inner-instructions.test.ts +91 -50
- package/server/methods/transaction/send-transaction.ts +269 -236
- package/server/rpc-server.ts +101 -104
- package/server/types.ts +56 -56
- package/src/db/schema/transactions.ts +29 -29
- package/src/db/schema/tx-account-states.ts +16 -14
- package/src/db/tx-store.ts +97 -99
- package/src/migrations-bundled.ts +4 -4
|
@@ -3,42 +3,48 @@ import { ASSOCIATED_TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
|
|
3
3
|
|
|
4
4
|
// Keep shape compatible with instruction-parser
|
|
5
5
|
export type ParsedInstruction =
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
| {
|
|
7
|
+
program: string;
|
|
8
|
+
programId: string;
|
|
9
|
+
parsed: { type: string; info: unknown };
|
|
10
|
+
}
|
|
11
|
+
| { programId: string; accounts: string[]; data: string };
|
|
8
12
|
|
|
9
|
-
function ok(programId: string, type: string, info:
|
|
10
|
-
|
|
13
|
+
function ok(programId: string, type: string, info: unknown): ParsedInstruction {
|
|
14
|
+
return {
|
|
15
|
+
program: "spl-associated-token-account",
|
|
16
|
+
programId,
|
|
17
|
+
parsed: { type, info },
|
|
18
|
+
};
|
|
11
19
|
}
|
|
12
20
|
|
|
13
21
|
function asBase58(ix: TransactionInstruction, idx: number): string | undefined {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
try {
|
|
23
|
+
return ix.keys[idx]?.pubkey?.toBase58();
|
|
24
|
+
} catch {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
export function tryParseAta(
|
|
22
|
-
|
|
23
|
-
|
|
30
|
+
ix: TransactionInstruction,
|
|
31
|
+
programIdStr: string,
|
|
24
32
|
): ParsedInstruction | null {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
33
|
+
try {
|
|
34
|
+
if (!ix.programId.equals(ASSOCIATED_TOKEN_PROGRAM_ID)) return null;
|
|
35
|
+
// Both create (empty) and createIdempotent ([1]) map to type "create" in explorers
|
|
36
|
+
const type = "create";
|
|
37
|
+
// Expected keys: [payer, associatedToken, owner, mint, systemProgram, tokenProgram]
|
|
38
|
+
const info = {
|
|
39
|
+
source: asBase58(ix, 0),
|
|
40
|
+
account: asBase58(ix, 1),
|
|
41
|
+
wallet: asBase58(ix, 2),
|
|
42
|
+
mint: asBase58(ix, 3),
|
|
43
|
+
systemProgram: asBase58(ix, 4),
|
|
44
|
+
tokenProgram: asBase58(ix, 5),
|
|
45
|
+
};
|
|
46
|
+
return ok(programIdStr, type, info);
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
43
50
|
}
|
|
44
|
-
|
|
@@ -1,172 +1,340 @@
|
|
|
1
1
|
import type { PublicKey, TransactionInstruction } from "@solana/web3.js";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
decodeMintToCheckedInstruction,
|
|
4
|
+
decodeTransferInstruction,
|
|
5
|
+
decodeTransferCheckedInstruction,
|
|
6
|
+
decodeInitializeAccount3Instruction,
|
|
7
|
+
decodeInitializeImmutableOwnerInstruction,
|
|
8
|
+
decodeTransferCheckedInstructionUnchecked,
|
|
9
|
+
decodeTransferInstructionUnchecked,
|
|
10
|
+
decodeInitializeAccount3InstructionUnchecked,
|
|
11
|
+
decodeInitializeImmutableOwnerInstructionUnchecked,
|
|
10
12
|
} from "@solana/spl-token";
|
|
11
13
|
import { u8 } from "@solana/buffer-layout";
|
|
14
|
+
import { PublicKey as PK } from "@solana/web3.js";
|
|
15
|
+
import {
|
|
16
|
+
TOKEN_PROGRAM_ID as TOKEN_PROGRAM_V1,
|
|
17
|
+
TOKEN_2022_PROGRAM_ID as TOKEN_PROGRAM_2022,
|
|
18
|
+
getAssociatedTokenAddressSync,
|
|
19
|
+
} from "@solana/spl-token";
|
|
12
20
|
|
|
13
21
|
// Keep shape compatible with instruction-parser
|
|
14
22
|
export type ParsedInstruction =
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
| {
|
|
24
|
+
program: string;
|
|
25
|
+
programId: string;
|
|
26
|
+
parsed: { type: string; info: unknown };
|
|
27
|
+
}
|
|
28
|
+
| { programId: string; accounts: string[]; data: string };
|
|
17
29
|
|
|
18
|
-
function ok(programId: string, type: string, info:
|
|
19
|
-
|
|
30
|
+
function ok(programId: string, type: string, info: unknown): ParsedInstruction {
|
|
31
|
+
// Use a single label for both SPL v1 and Token-2022 for compatibility with UIs
|
|
32
|
+
return { program: "spl-token", programId, parsed: { type, info } };
|
|
20
33
|
}
|
|
21
34
|
|
|
22
35
|
function asBase58(pk: PublicKey | undefined): string | undefined {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
try {
|
|
37
|
+
return pk ? pk.toBase58() : undefined;
|
|
38
|
+
} catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
28
41
|
}
|
|
29
42
|
|
|
30
43
|
export function tryParseSplToken(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
ix: TransactionInstruction,
|
|
45
|
+
programIdStr: string,
|
|
46
|
+
_accountKeys: string[],
|
|
47
|
+
dataBase58: string,
|
|
48
|
+
tokenBalanceHints?: Array<{ mint: string; decimals: number }>,
|
|
35
49
|
): ParsedInstruction | null {
|
|
36
|
-
|
|
37
|
-
|
|
50
|
+
try {
|
|
51
|
+
// Accept both SPL Token and Token-2022 program ids
|
|
52
|
+
// We pass the actual program id to decoders for strict match
|
|
53
|
+
const programPk = new PK(programIdStr);
|
|
54
|
+
// Don't early-return on program id; allow both programs
|
|
55
|
+
|
|
56
|
+
// MintToChecked
|
|
57
|
+
try {
|
|
58
|
+
const m = decodeMintToCheckedInstruction(ix, programPk);
|
|
59
|
+
const amount = m.data.amount;
|
|
60
|
+
const decimals = m.data.decimals;
|
|
61
|
+
const amtStr =
|
|
62
|
+
typeof amount === "bigint" ? amount.toString() : String(amount);
|
|
63
|
+
const base = BigInt(10) ** BigInt(decimals);
|
|
64
|
+
const whole = BigInt(amtStr) / base;
|
|
65
|
+
const frac = BigInt(amtStr) % base;
|
|
66
|
+
const fracStr = frac
|
|
67
|
+
.toString()
|
|
68
|
+
.padStart(decimals, "0")
|
|
69
|
+
.replace(/0+$/, "");
|
|
70
|
+
const uiStr = fracStr.length ? `${whole}.${fracStr}` : `${whole}`;
|
|
71
|
+
return ok(programIdStr, "mintToChecked", {
|
|
72
|
+
account: asBase58(m.keys.destination.pubkey),
|
|
73
|
+
mint: asBase58(m.keys.mint.pubkey),
|
|
74
|
+
mintAuthority: asBase58(m.keys.authority.pubkey),
|
|
75
|
+
tokenAmount: {
|
|
76
|
+
amount: amtStr,
|
|
77
|
+
decimals,
|
|
78
|
+
uiAmount: Number(uiStr),
|
|
79
|
+
uiAmountString: uiStr,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
} catch {}
|
|
38
83
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
84
|
+
// Transfer / TransferChecked (strict)
|
|
85
|
+
try {
|
|
86
|
+
const t = decodeTransferInstruction(ix, programPk);
|
|
87
|
+
const amt = t.data.amount;
|
|
88
|
+
return ok(programIdStr, "transfer", {
|
|
89
|
+
amount: typeof amt === "bigint" ? amt.toString() : String(amt),
|
|
90
|
+
source: asBase58(t.keys.source.pubkey),
|
|
91
|
+
destination: asBase58(t.keys.destination.pubkey),
|
|
92
|
+
authority: asBase58(t.keys.owner.pubkey),
|
|
93
|
+
});
|
|
94
|
+
} catch {}
|
|
43
95
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
tokenAmount: {
|
|
60
|
-
amount: amtStr,
|
|
61
|
-
decimals,
|
|
62
|
-
uiAmount: Number(uiStr),
|
|
63
|
-
uiAmountString: uiStr,
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
} catch {}
|
|
96
|
+
try {
|
|
97
|
+
const t = decodeTransferCheckedInstruction(ix, programPk);
|
|
98
|
+
const amt = t.data.amount;
|
|
99
|
+
const decimals = t.data.decimals;
|
|
100
|
+
return ok(programIdStr, "transferChecked", {
|
|
101
|
+
tokenAmount: {
|
|
102
|
+
amount: typeof amt === "bigint" ? amt.toString() : String(amt),
|
|
103
|
+
decimals,
|
|
104
|
+
},
|
|
105
|
+
source: asBase58(t.keys.source.pubkey),
|
|
106
|
+
destination: asBase58(t.keys.destination.pubkey),
|
|
107
|
+
authority: asBase58(t.keys.owner.pubkey),
|
|
108
|
+
mint: asBase58(t.keys.mint.pubkey),
|
|
109
|
+
});
|
|
110
|
+
} catch {}
|
|
67
111
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
});
|
|
78
|
-
} catch {}
|
|
112
|
+
// InitializeAccount3 (strict)
|
|
113
|
+
try {
|
|
114
|
+
const a = decodeInitializeAccount3Instruction(ix, programPk);
|
|
115
|
+
return ok(programIdStr, "initializeAccount3", {
|
|
116
|
+
account: asBase58(a.keys.account.pubkey),
|
|
117
|
+
mint: asBase58(a.keys.mint.pubkey),
|
|
118
|
+
owner: asBase58(a.data.owner),
|
|
119
|
+
});
|
|
120
|
+
} catch {}
|
|
79
121
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
decimals,
|
|
88
|
-
},
|
|
89
|
-
source: asBase58(t.keys.source.pubkey),
|
|
90
|
-
destination: asBase58(t.keys.destination.pubkey),
|
|
91
|
-
authority: asBase58(t.keys.owner.pubkey),
|
|
92
|
-
mint: asBase58(t.keys.mint.pubkey),
|
|
93
|
-
});
|
|
94
|
-
} catch {}
|
|
122
|
+
// InitializeImmutableOwner
|
|
123
|
+
try {
|
|
124
|
+
const im = decodeInitializeImmutableOwnerInstruction(ix, programPk);
|
|
125
|
+
return ok(programIdStr, "initializeImmutableOwner", {
|
|
126
|
+
account: asBase58(im.keys.account.pubkey),
|
|
127
|
+
});
|
|
128
|
+
} catch {}
|
|
95
129
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
130
|
+
// GetAccountDataSize: decode extension types (u16 little-endian sequence)
|
|
131
|
+
try {
|
|
132
|
+
const bytes = Buffer.from(bs58decode(dataBase58));
|
|
133
|
+
const t = u8().decode(bytes);
|
|
134
|
+
// 21 corresponds to TokenInstruction.GetAccountDataSize
|
|
135
|
+
if (t === 21) {
|
|
136
|
+
const mint = asBase58(ix.keys[0]?.pubkey);
|
|
137
|
+
const extCodes: number[] = [];
|
|
138
|
+
for (let i = 1; i + 1 < bytes.length; i += 2) {
|
|
139
|
+
const code = bytes[i] | (bytes[i + 1] << 8);
|
|
140
|
+
extCodes.push(code);
|
|
141
|
+
}
|
|
142
|
+
const extMap: Record<number, string> = {
|
|
143
|
+
7: "immutableOwner",
|
|
144
|
+
8: "memoTransfer",
|
|
145
|
+
9: "nonTransferable",
|
|
146
|
+
12: "permanentDelegate",
|
|
147
|
+
14: "transferHook",
|
|
148
|
+
15: "transferHookAccount",
|
|
149
|
+
18: "metadataPointer",
|
|
150
|
+
19: "tokenMetadata",
|
|
151
|
+
20: "groupPointer",
|
|
152
|
+
21: "tokenGroup",
|
|
153
|
+
22: "groupMemberPointer",
|
|
154
|
+
23: "tokenGroupMember",
|
|
155
|
+
25: "scaledUiAmountConfig",
|
|
156
|
+
26: "pausableConfig",
|
|
157
|
+
27: "pausableAccount",
|
|
158
|
+
};
|
|
159
|
+
const extensionTypes = extCodes.map((c) => extMap[c] || String(c));
|
|
160
|
+
return ok(programIdStr, "getAccountDataSize", { mint, extensionTypes });
|
|
161
|
+
}
|
|
162
|
+
} catch {}
|
|
105
163
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
164
|
+
// Unchecked fallbacks: decode data fields even if keys/validation missing
|
|
165
|
+
try {
|
|
166
|
+
const raw = bs58decode(dataBase58);
|
|
167
|
+
const op = raw[0];
|
|
168
|
+
// Transfer
|
|
169
|
+
if (op === 3) {
|
|
170
|
+
const t = decodeTransferInstructionUnchecked(ix);
|
|
171
|
+
const amt = t.data.amount;
|
|
172
|
+
return ok(programIdStr, "transfer", {
|
|
173
|
+
amount: typeof amt === "bigint" ? amt.toString() : String(amt),
|
|
174
|
+
source: asBase58(t.keys.source?.pubkey),
|
|
175
|
+
destination: asBase58(t.keys.destination?.pubkey),
|
|
176
|
+
authority: asBase58(t.keys.owner?.pubkey),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
// TransferChecked
|
|
180
|
+
if (op === 12) {
|
|
181
|
+
const t = decodeTransferCheckedInstructionUnchecked(ix);
|
|
182
|
+
const amt = t.data.amount;
|
|
183
|
+
const decimals = t.data.decimals;
|
|
184
|
+
const hintMint = (() => {
|
|
185
|
+
try {
|
|
186
|
+
const dec = Number(decimals);
|
|
187
|
+
const candidates = (tokenBalanceHints || []).filter(
|
|
188
|
+
(h) => Number(h.decimals) === dec,
|
|
189
|
+
);
|
|
190
|
+
if (candidates.length === 1) return candidates[0].mint;
|
|
191
|
+
// Prefer non-zero decimals over 0 (filters out native 4uQe mint in many cases)
|
|
192
|
+
const nonZero = candidates.filter((c) => c.decimals > 0);
|
|
193
|
+
if (nonZero.length === 1) return nonZero[0].mint;
|
|
194
|
+
// Fall back to first candidate if multiple
|
|
195
|
+
return candidates[0]?.mint;
|
|
196
|
+
} catch {
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
})();
|
|
200
|
+
return ok(programIdStr, "transferChecked", {
|
|
201
|
+
tokenAmount: {
|
|
202
|
+
amount: typeof amt === "bigint" ? amt.toString() : String(amt),
|
|
203
|
+
decimals,
|
|
204
|
+
},
|
|
205
|
+
source: asBase58(t.keys.source?.pubkey),
|
|
206
|
+
destination: asBase58(t.keys.destination?.pubkey),
|
|
207
|
+
authority: asBase58(t.keys.owner?.pubkey),
|
|
208
|
+
mint: asBase58(t.keys.mint?.pubkey) || hintMint,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
// InitializeAccount3
|
|
212
|
+
if (op === 18) {
|
|
213
|
+
const a = decodeInitializeAccount3InstructionUnchecked(ix);
|
|
214
|
+
const hintMint = (() => {
|
|
215
|
+
try {
|
|
216
|
+
// Prefer single non-zero-decimals mint in this tx
|
|
217
|
+
const nonZero = (tokenBalanceHints || []).filter(
|
|
218
|
+
(h) => h.decimals > 0,
|
|
219
|
+
);
|
|
220
|
+
if (nonZero.length === 1) return nonZero[0].mint;
|
|
221
|
+
// Fall back to first available mint
|
|
222
|
+
return (tokenBalanceHints || [])[0]?.mint;
|
|
223
|
+
} catch {
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
})();
|
|
227
|
+
const ownerStr = asBase58(a.data.owner);
|
|
228
|
+
const mintStr = asBase58(a.keys.mint?.pubkey) || hintMint;
|
|
229
|
+
let accountStr = asBase58(a.keys.account?.pubkey);
|
|
230
|
+
try {
|
|
231
|
+
if (!accountStr && ownerStr && mintStr) {
|
|
232
|
+
const ownerPk = new PK(ownerStr);
|
|
233
|
+
const mintPk = new PK(mintStr);
|
|
234
|
+
const programId =
|
|
235
|
+
programIdStr === TOKEN_PROGRAM_2022.toBase58()
|
|
236
|
+
? TOKEN_PROGRAM_2022
|
|
237
|
+
: TOKEN_PROGRAM_V1;
|
|
238
|
+
const ata = getAssociatedTokenAddressSync(
|
|
239
|
+
mintPk,
|
|
240
|
+
ownerPk,
|
|
241
|
+
true,
|
|
242
|
+
programId,
|
|
243
|
+
);
|
|
244
|
+
accountStr = ata.toBase58();
|
|
245
|
+
}
|
|
246
|
+
} catch {}
|
|
247
|
+
return ok(programIdStr, "initializeAccount3", {
|
|
248
|
+
account: accountStr,
|
|
249
|
+
mint: mintStr,
|
|
250
|
+
owner: ownerStr,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
// InitializeImmutableOwner
|
|
254
|
+
if (op === 22) {
|
|
255
|
+
const im = decodeInitializeImmutableOwnerInstructionUnchecked(ix);
|
|
256
|
+
return ok(programIdStr, "initializeImmutableOwner", {
|
|
257
|
+
account: asBase58(im.keys.account?.pubkey),
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
} catch {}
|
|
113
261
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
262
|
+
// Fallback: classify by TokenInstruction opcode (first byte) when nothing else matched
|
|
263
|
+
try {
|
|
264
|
+
const raw = bs58decode(dataBase58);
|
|
265
|
+
if (raw.length > 0) {
|
|
266
|
+
const op = raw[0];
|
|
267
|
+
const map: Record<number, string> = {
|
|
268
|
+
0: "initializeMint",
|
|
269
|
+
1: "initializeAccount",
|
|
270
|
+
2: "initializeMultisig",
|
|
271
|
+
3: "transfer",
|
|
272
|
+
4: "approve",
|
|
273
|
+
5: "revoke",
|
|
274
|
+
6: "setAuthority",
|
|
275
|
+
7: "mintTo",
|
|
276
|
+
8: "burn",
|
|
277
|
+
9: "closeAccount",
|
|
278
|
+
10: "freezeAccount",
|
|
279
|
+
11: "thawAccount",
|
|
280
|
+
12: "transferChecked",
|
|
281
|
+
13: "approveChecked",
|
|
282
|
+
14: "mintToChecked",
|
|
283
|
+
15: "burnChecked",
|
|
284
|
+
16: "initializeAccount2",
|
|
285
|
+
17: "syncNative",
|
|
286
|
+
18: "initializeAccount3",
|
|
287
|
+
19: "initializeMultisig2",
|
|
288
|
+
20: "initializeMint2",
|
|
289
|
+
21: "getAccountDataSize",
|
|
290
|
+
22: "initializeImmutableOwner",
|
|
291
|
+
23: "amountToUiAmount",
|
|
292
|
+
24: "uiAmountToAmount",
|
|
293
|
+
25: "initializeMintCloseAuthority",
|
|
294
|
+
26: "transferFeeExtension",
|
|
295
|
+
27: "confidentialTransferExtension",
|
|
296
|
+
28: "defaultAccountStateExtension",
|
|
297
|
+
29: "reallocate",
|
|
298
|
+
30: "memoTransferExtension",
|
|
299
|
+
31: "createNativeMint",
|
|
300
|
+
32: "initializeNonTransferableMint",
|
|
301
|
+
33: "interestBearingMintExtension",
|
|
302
|
+
34: "cpiGuardExtension",
|
|
303
|
+
35: "initializePermanentDelegate",
|
|
304
|
+
36: "transferHookExtension",
|
|
305
|
+
39: "metadataPointerExtension",
|
|
306
|
+
40: "groupPointerExtension",
|
|
307
|
+
41: "groupMemberPointerExtension",
|
|
308
|
+
43: "scaledUiAmountExtension",
|
|
309
|
+
44: "pausableExtension",
|
|
310
|
+
};
|
|
311
|
+
const type = map[op];
|
|
312
|
+
if (type) return ok(programIdStr, type, {});
|
|
313
|
+
}
|
|
314
|
+
} catch {}
|
|
147
315
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
316
|
+
// Unknown SPL token instruction (unrecognized opcode)
|
|
317
|
+
return null;
|
|
318
|
+
} catch {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
153
321
|
}
|
|
154
322
|
|
|
155
323
|
// Local base58 decode to avoid importing from sibling file
|
|
156
324
|
const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
157
325
|
const BASE = BigInt(ALPHABET.length);
|
|
158
326
|
function bs58decode(str: string): Uint8Array {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
327
|
+
let num = 0n;
|
|
328
|
+
for (const char of str) {
|
|
329
|
+
const index = ALPHABET.indexOf(char);
|
|
330
|
+
if (index === -1) throw new Error("Invalid base58 character");
|
|
331
|
+
num = num * BASE + BigInt(index);
|
|
332
|
+
}
|
|
333
|
+
const bytes: number[] = [];
|
|
334
|
+
while (num > 0n) {
|
|
335
|
+
bytes.unshift(Number(num % 256n));
|
|
336
|
+
num = num / 256n;
|
|
337
|
+
}
|
|
338
|
+
for (let i = 0; i < str.length && str[i] === "1"; i++) bytes.unshift(0);
|
|
339
|
+
return new Uint8Array(bytes.length > 0 ? bytes : [0]);
|
|
172
340
|
}
|