sememe 0.0.0-stage → 0.4.22

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.
Files changed (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2777 -2
  3. package/bin/sememe.mjs +556 -0
  4. package/index.mjs +48 -0
  5. package/interface/ata-ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL.json +62 -0
  6. package/interface/native-11111111111111111111111111111111.json +226 -0
  7. package/interface/native-ComputeBudget111111111111111111111111111111.json +72 -0
  8. package/interface/raydium-675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8.json +352 -0
  9. package/interface/token-TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA.json +334 -0
  10. package/interface/token-TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb.json +535 -0
  11. package/interface/token-metadata.json +132 -0
  12. package/package.json +71 -4
  13. package/src/account-list.mjs +308 -0
  14. package/src/account-union.mjs +523 -0
  15. package/src/account.mjs +199 -0
  16. package/src/acquire.mjs +145 -0
  17. package/src/adopt.mjs +206 -0
  18. package/src/ata-interface.mjs +276 -0
  19. package/src/ata.mjs +223 -0
  20. package/src/base58.mjs +96 -0
  21. package/src/canonical.mjs +24 -0
  22. package/src/cli-surface.mjs +300 -0
  23. package/src/codec.mjs +1118 -0
  24. package/src/conformance.mjs +431 -0
  25. package/src/corpus/harvest-compute-budget-2026-10-31.json +19122 -0
  26. package/src/corpus/harvest-system-2026-10-31.json +26090 -0
  27. package/src/decode.mjs +286 -0
  28. package/src/dialect.mjs +180 -0
  29. package/src/discriminators.mjs +192 -0
  30. package/src/drift.mjs +172 -0
  31. package/src/gap.mjs +205 -0
  32. package/src/lighthouse.mjs +168 -0
  33. package/src/lock.mjs +440 -0
  34. package/src/native-audit.mjs +56 -0
  35. package/src/native-corpus.mjs +57 -0
  36. package/src/native-interface.mjs +252 -0
  37. package/src/native.mjs +324 -0
  38. package/src/peer.mjs +95 -0
  39. package/src/program-id.mjs +74 -0
  40. package/src/pull.mjs +117 -0
  41. package/src/rawle.mjs +454 -0
  42. package/src/raydium-interface.mjs +747 -0
  43. package/src/raydium.mjs +391 -0
  44. package/src/receipt-digest.mjs +84 -0
  45. package/src/receipt-name.mjs +128 -0
  46. package/src/receipt-order.mjs +56 -0
  47. package/src/shank.mjs +73 -0
  48. package/src/short-forms.mjs +209 -0
  49. package/src/store-read.mjs +253 -0
  50. package/src/store-write.mjs +46 -0
  51. package/src/token-interface.mjs +394 -0
  52. package/src/token-metadata-interface.mjs +371 -0
  53. package/src/token-metadata.mjs +317 -0
  54. package/src/token.mjs +443 -0
  55. package/src/union.mjs +428 -0
  56. package/vendor/anza-solana-sdk/8db406e2edfafa25939ec782b77b5b6f30de164d/LICENSE.txt +201 -0
  57. package/vendor/anza-solana-sdk/8db406e2edfafa25939ec782b77b5b6f30de164d/compute-budget-interface/src/lib.rs +93 -0
  58. package/vendor/anza-solana-sdk/8db406e2edfafa25939ec782b77b5b6f30de164d/system-interface/src/instruction.rs +1899 -0
  59. package/vendor/lighthouse/4c579479c98635e419b1b167f08be02a71604a71/LICENSE +21 -0
  60. package/vendor/lighthouse/4c579479c98635e419b1b167f08be02a71604a71/lighthouse.json +2769 -0
  61. package/vendor/raydium-amm/91739d8d9f5cf347e0ce14ebe4d2bb7069fb31b6/LICENSE +201 -0
  62. package/vendor/raydium-amm/91739d8d9f5cf347e0ce14ebe4d2bb7069fb31b6/program/src/instruction.rs +1189 -0
  63. package/vendor/raydium-amm/91739d8d9f5cf347e0ce14ebe4d2bb7069fb31b6/program/src/state.rs +1348 -0
  64. package/vendor/spl-associated-token-account/fbf5379e7398ac8b231c73ee8a9ff677b43dd0ec/LICENSE +201 -0
  65. package/vendor/spl-associated-token-account/fbf5379e7398ac8b231c73ee8a9ff677b43dd0ec/interface/src/instruction.rs +169 -0
  66. package/vendor/spl-associated-token-account/fbf5379e7398ac8b231c73ee8a9ff677b43dd0ec/interface/src/lib.rs +12 -0
  67. package/vendor/spl-associated-token-account/fbf5379e7398ac8b231c73ee8a9ff677b43dd0ec/program/src/processor.rs +308 -0
  68. package/vendor/spl-token/737788ff5bcc3f1f44cfb222f25483187650be76/LICENSE.txt +176 -0
  69. package/vendor/spl-token/737788ff5bcc3f1f44cfb222f25483187650be76/interface/src/instruction.rs +1963 -0
  70. package/vendor/spl-token-2022/5251dcc07ecbcb721d6c4f0231090defadf00311/LICENSE.txt +176 -0
  71. package/vendor/spl-token-2022/5251dcc07ecbcb721d6c4f0231090defadf00311/interface/src/instruction.rs +2868 -0
  72. package/vendor/spl-token-metadata-interface/7038969ec74a89f1afdb9bc97c112d4d26fead9a/LICENSE +201 -0
  73. package/vendor/spl-token-metadata-interface/7038969ec74a89f1afdb9bc97c112d4d26fead9a/interface/src/error.rs +58 -0
  74. package/vendor/spl-token-metadata-interface/7038969ec74a89f1afdb9bc97c112d4d26fead9a/interface/src/instruction.rs +596 -0
  75. package/vendor/spl-token-metadata-interface/7038969ec74a89f1afdb9bc97c112d4d26fead9a/interface/src/lib.rs +21 -0
  76. package/vendor/spl-token-metadata-interface/7038969ec74a89f1afdb9bc97c112d4d26fead9a/interface/src/state.rs +229 -0
package/src/token.mjs ADDED
@@ -0,0 +1,443 @@
1
+ // The SPL Token + Token-2022 decoder: the vendored-upstream path for the two
2
+ // programs that publish NO IDL account to pull.
3
+ //
4
+ // Why this file exists: measured 2026-11-02 on four contiguous finalized
5
+ // mainnet blocks, the two token programs carry 5,402 of 12,572 watched
6
+ // payloads (43%) and 99.7% of the "no interface" gap, and neither program
7
+ // answers at either pullable address. The upstream Rust interface crates ARE
8
+ // their interface, so the wire table is derived from the vendored crates by
9
+ // src/token-interface.mjs and committed as a reviewable artifact
10
+ // (interface/token-<program>.json).
11
+ //
12
+ // Ground truth chain, every row derived and then VERIFIED, not typed:
13
+ // 1. INTERFACE solana-program/token@737788ff interface/src/instruction.rs
14
+ // solana-program/token-2022@5251dcc0 interface/src/instruction.rs
15
+ // DERIVED by tools/token-interface.mjs into
16
+ // interface/token-<program>.json. This module reads the
17
+ // artifact at load and re-hashes the vendored Rust against the
18
+ // sha256 the artifact claims; a mismatch REFUSES the load
19
+ // instead of decoding from it — the same two-check contract
20
+ // as src/native.mjs (fresh derivation + artifact equality).
21
+ // 2. WIRE the pack()/unpack() pairs in those crates: tag = the
22
+ // integer upstream writes and matches, byte order = the pack
23
+ // arm's write sequence, COption = upstream's own
24
+ // pack_pubkey_option/pack_u64_option (1 tag byte + payload).
25
+ // 3. RUNTIME the same pin's processor dispatch: an unknown tag is
26
+ // InvalidInstruction, so an unknown selector here is a
27
+ // refusal, not a guess.
28
+ //
29
+ // What is deliberately NOT claimed: extension sub-instructions (Token-2022
30
+ // tags 26/27/28/30/33/34/36/37/39/40/41/42/43/44/46 dispatch into extension
31
+ // processors on input[1..]) are named at the top level only, and their
32
+ // sub-instruction bytes are reported as trailing. Token-2022's tag 255
33
+ // (Batch) unpacks upstream but the processor refuses it.
34
+ //
35
+ // The token-metadata FALLBACK (spl-token-metadata-interface, 8-byte
36
+ // discriminators) is now covered, because Token-2022's own processor falls
37
+ // through to it: `decode_instruction_type(input)` reads ONE byte, and when
38
+ // that byte is not a PodTokenInstruction variant the processor tries
39
+ // `TokenMetadataInstruction::unpack(input)` before giving up. A payload
40
+ // starting with 0xd2/0xdd/0xea/0xd7/0xfa is therefore an instruction this
41
+ // program EXECUTES, and refusing it as "unknown-selector" would have been a
42
+ // true statement about the token table and a false one about the program.
43
+ //
44
+ // The fallback is Token-2022 ONLY (legacy SPL Token's processor has no such
45
+ // branch) and it sits EXACTLY where upstream puts it: the 1-byte token
46
+ // selector is consulted FIRST, and only a payload whose first byte is not a
47
+ // token variant reaches the metadata table. Upstream does not fall through
48
+ // to metadata when a token variant is recognized but its body is bad, so
49
+ // neither does this module.
50
+ //
51
+ // Strictness mirrors src/native.mjs: an unknown selector, a truncated
52
+ // payload, an invalid option tag or an unknown authority byte is a refusal
53
+ // with a named reason — never a confident wrong label.
54
+
55
+ import { readFileSync } from 'node:fs';
56
+ import path from 'node:path';
57
+ import { fileURLToPath } from 'node:url';
58
+ import { decodeBase58, encodeBase58 } from './base58.mjs';
59
+ import {
60
+ deriveTokenInterface, TOKEN_SPECS, vendoredTokenHash,
61
+ SPL_TOKEN_PROGRAM, SPL_TOKEN_2022_PROGRAM,
62
+ } from './token-interface.mjs';
63
+ import {
64
+ decodeTokenMetadataInstruction, encodeTokenMetadataInstruction,
65
+ tokenMetadataProvenance, tokenMetadataNames, TokenMetadataCodecError,
66
+ TOKEN_METADATA_PROGRAM,
67
+ } from './token-metadata.mjs';
68
+
69
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
70
+ const ROOT = path.resolve(HERE, '..');
71
+
72
+ export { SPL_TOKEN_PROGRAM, SPL_TOKEN_2022_PROGRAM };
73
+
74
+ /**
75
+ * Load one token interface: DERIVE it from the vendored upstream Rust, then
76
+ * require the committed artifact to agree with the derivation byte for byte.
77
+ *
78
+ * Two checks, deliberately (the contract src/native.mjs already proved):
79
+ * - the derivation runs against the vendored bytes themselves, so a swapped
80
+ * or edited source file cannot silently change the table it describes;
81
+ * - the committed artifact must equal that derivation, so a tampered or
82
+ * stale artifact refuses the load instead of decoding from it.
83
+ */
84
+ function loadInterface(spec) {
85
+ const artifactPath = path.join(ROOT, 'interface', `token-${spec.program}.json`);
86
+ const derived = deriveTokenInterface(spec);
87
+ let current;
88
+ try {
89
+ current = readFileSync(artifactPath, 'utf8');
90
+ } catch (error) {
91
+ throw new Error(`token interface artifact for ${spec.label} is missing: ${error.message}`);
92
+ }
93
+ const expected = `${JSON.stringify(derived, null, 2)}\n`;
94
+ if (current !== expected) {
95
+ throw new Error(
96
+ `token interface for ${spec.label} refuses to load: interface/token-${spec.program}.json does not equal ` +
97
+ `a fresh derivation of ${derived.source.file} (${derived.source.fileSha256}, hashed now: ${vendoredTokenHash(spec)})`,
98
+ );
99
+ }
100
+ if (!derived.instructions.length || !derived.selector?.width) {
101
+ throw new Error(`token interface artifact for ${spec.label} is malformed`);
102
+ }
103
+ return derived;
104
+ }
105
+
106
+ const ARTIFACTS = new Map(TOKEN_SPECS.map((spec) => [spec.program, loadInterface(spec)]));
107
+ const AUTHORITY = new Map(TOKEN_SPECS.map((spec) => [
108
+ spec.program,
109
+ new Map(ARTIFACTS.get(spec.program).authorityType.map((entry) => [entry.byte, entry.name])),
110
+ ]));
111
+ const AUTHORITY_NAME = new Map(TOKEN_SPECS.map((spec) => [
112
+ spec.program,
113
+ new Map(ARTIFACTS.get(spec.program).authorityType.map((entry) => [entry.name, entry.byte])),
114
+ ]));
115
+
116
+ export class TokenCodecError extends Error {
117
+ constructor(reason, detail) {
118
+ super(detail ? `${reason}: ${detail}` : reason);
119
+ this.name = 'TokenCodecError';
120
+ this.reason = reason;
121
+ }
122
+ }
123
+
124
+ /** Whether `program` is one of the token programs this module owns. */
125
+ export function isTokenProgram(program) {
126
+ return ARTIFACTS.has(program);
127
+ }
128
+
129
+ /**
130
+ * Whether `program`'s processor falls through to the token-metadata table.
131
+ *
132
+ * Token-2022's processor does (`decode_instruction_type` fails ->
133
+ * `TokenMetadataInstruction::unpack`); legacy SPL Token has no such branch.
134
+ */
135
+ export function hasTokenMetadataFallback(program) {
136
+ return program === TOKEN_METADATA_PROGRAM;
137
+ }
138
+
139
+ /** Program ids this module can decode, for callers that enumerate. */
140
+ export function tokenPrograms() {
141
+ return [...ARTIFACTS.keys()];
142
+ }
143
+
144
+ /** Provenance for one token program, or null. */
145
+ export function tokenProvenance(program) {
146
+ const artifact = ARTIFACTS.get(program);
147
+ if (!artifact) return null;
148
+ return {
149
+ label: artifact.label,
150
+ interface: `${artifact.source.repo}@${artifact.source.pin.slice(0, 8)} ${artifact.source.file} TokenInstruction (explicit tags)`,
151
+ encoding: artifact.argEncoding,
152
+ runtime: artifact.runtime,
153
+ variants: artifact.instructions.length,
154
+ // Token-2022's processor falls through to the token-metadata crate; the
155
+ // fallback has its OWN vendored source, artifact and wire rules.
156
+ ...(hasTokenMetadataFallback(program) ? { fallback: tokenMetadataProvenance() } : {}),
157
+ };
158
+ }
159
+
160
+ function readLe(bytes, offset, size) {
161
+ let value = 0n;
162
+ for (let index = size - 1; index >= 0; index--) value = (value << 8n) | BigInt(bytes[offset + index]);
163
+ return value;
164
+ }
165
+
166
+ function u64ToNumber(value, what) {
167
+ if (value > BigInt(Number.MAX_SAFE_INTEGER)) throw Error(`${what} exceeds Number.MAX_SAFE_INTEGER`);
168
+ return Number(value);
169
+ }
170
+
171
+ /**
172
+ * Decode one arg. `trailing` types (utf8_rest, u16_vec, bytes) consume the
173
+ * REST of the payload and must be the last arg — which is exactly how
174
+ * upstream writes them, so the check below is upstream's own shape.
175
+ */
176
+ function decodeArg(type, bytes, offset, what) {
177
+ if (type === 'u8') {
178
+ if (offset + 1 > bytes.length) throw new TokenCodecError('truncated-u8', `${what}: truncated u8 at ${offset}`);
179
+ return { value: bytes[offset], size: 1 };
180
+ }
181
+ if (type === 'u16') {
182
+ if (offset + 2 > bytes.length) throw new TokenCodecError('truncated-u16', `${what}: truncated u16 at ${offset}`);
183
+ return { value: bytes[offset] | (bytes[offset + 1] << 8), size: 2 };
184
+ }
185
+ if (type === 'u64') {
186
+ if (offset + 8 > bytes.length) throw new TokenCodecError('truncated-u64', `${what}: truncated u64 at ${offset}`);
187
+ return { value: readLe(bytes, offset, 8), size: 8 };
188
+ }
189
+ if (type === 'pubkey') {
190
+ if (offset + 32 > bytes.length) throw new TokenCodecError('truncated-pubkey', `${what}: truncated pubkey at ${offset}`);
191
+ return { value: encodeBase58(bytes.subarray(offset, offset + 32)), size: 32 };
192
+ }
193
+ if (type === 'coption_pubkey' || type === 'coption_u64') {
194
+ const width = type === 'coption_pubkey' ? 32 : 8;
195
+ if (offset >= bytes.length) throw new TokenCodecError('truncated-coption-tag', `${what}: truncated option tag at ${offset}`);
196
+ const tag = bytes[offset];
197
+ if (tag === 0) return { value: null, size: 1 };
198
+ if (tag !== 1) throw new TokenCodecError('invalid-coption-tag', `${what}: option tag ${tag} at ${offset} (upstream writes 0 or 1)`);
199
+ if (offset + 1 + width > bytes.length) throw new TokenCodecError('truncated-coption-payload', `${what}: truncated option payload at ${offset}`);
200
+ if (type === 'coption_pubkey') return { value: encodeBase58(bytes.subarray(offset + 1, offset + 33)), size: 33 };
201
+ return { value: readLe(bytes, offset + 1, 8), size: 9 };
202
+ }
203
+ if (type === 'authority_type') {
204
+ if (offset >= bytes.length) throw new TokenCodecError('truncated-authority-type', `${what}: truncated authority byte at ${offset}`);
205
+ const byte = bytes[offset];
206
+ return { value: byte, size: 1 };
207
+ }
208
+ if (type === 'utf8_rest') {
209
+ const rest = bytes.subarray(offset);
210
+ const text = Buffer.from(rest).toString('utf8');
211
+ if (Buffer.from(text, 'utf8').compare(rest) !== 0) {
212
+ throw new TokenCodecError('invalid-utf8', `${what}: remaining ${rest.length} byte(s) are not valid utf8`);
213
+ }
214
+ return { value: text, size: rest.length };
215
+ }
216
+ if (type === 'u16_vec') {
217
+ const rest = bytes.length - offset;
218
+ if (rest % 2 !== 0) throw new TokenCodecError('truncated-u16', `${what}: ${rest} trailing byte(s), not a whole number of u16`);
219
+ const out = [];
220
+ for (let at = offset; at < bytes.length; at += 2) out.push(bytes[at] | (bytes[at + 1] << 8));
221
+ return { value: out, size: rest };
222
+ }
223
+ if (type === 'bytes') {
224
+ return { value: Buffer.from(bytes.subarray(offset)).toString('hex'), size: bytes.length - offset };
225
+ }
226
+ throw new TokenCodecError('unknown-arg-type', `unknown arg type ${type}`);
227
+ }
228
+
229
+ function encodeArg(type, value, what, program) {
230
+ if (type === 'u8') {
231
+ const n = typeof value === 'bigint' ? Number(value) : value;
232
+ if (!Number.isSafeInteger(n) || n < 0 || n > 0xff) throw Error(`${what}: not a u8`);
233
+ return Buffer.from([n]);
234
+ }
235
+ if (type === 'u16') {
236
+ const n = typeof value === 'bigint' ? Number(value) : value;
237
+ if (!Number.isSafeInteger(n) || n < 0 || n > 0xffff) throw Error(`${what}: not a u16`);
238
+ const out = Buffer.alloc(2);
239
+ out.writeUInt16LE(n);
240
+ return out;
241
+ }
242
+ if (type === 'u64') {
243
+ const v = typeof value === 'string' && /^-?\d+$/.test(value) ? BigInt(value)
244
+ : typeof value === 'number' ? BigInt(value)
245
+ : typeof value === 'bigint' ? value
246
+ : (() => { throw Error(`${what}: not a u64`); })();
247
+ if (v < 0n || v >= 1n << 64n) throw Error(`${what}: out of u64 range`);
248
+ const out = Buffer.alloc(8);
249
+ for (let index = 0; index < 8; index++) out[index] = Number((v >> BigInt(index * 8)) & 0xffn);
250
+ return out;
251
+ }
252
+ if (type === 'pubkey') {
253
+ const raw = Buffer.from(decodeBase58(String(value)));
254
+ if (raw.length !== 32) throw Error(`${what}: not a 32-byte pubkey`);
255
+ return raw;
256
+ }
257
+ if (type === 'coption_pubkey' || type === 'coption_u64') {
258
+ if (value === null || value === undefined) return Buffer.from([0]);
259
+ if (type === 'coption_pubkey') {
260
+ const raw = Buffer.from(decodeBase58(String(value)));
261
+ if (raw.length !== 32) throw Error(`${what}: not a 32-byte pubkey`);
262
+ return Buffer.concat([Buffer.from([1]), raw]);
263
+ }
264
+ const v = typeof value === 'string' && /^\d+$/.test(value) ? BigInt(value)
265
+ : typeof value === 'number' ? BigInt(value)
266
+ : typeof value === 'bigint' ? value
267
+ : (() => { throw Error(`${what}: not a u64`); })();
268
+ if (v < 0n || v >= 1n << 64n) throw Error(`${what}: out of u64 range`);
269
+ const out = Buffer.alloc(9);
270
+ out[0] = 1;
271
+ for (let index = 0; index < 8; index++) out[index + 1] = Number((v >> BigInt(index * 8)) & 0xffn);
272
+ return out;
273
+ }
274
+ if (type === 'authority_type') {
275
+ // The byte is the wire form; the NAME is a human label. Accept either,
276
+ // so a decoded instruction can be re-encoded without the caller having
277
+ // to look the byte up again.
278
+ let n;
279
+ if (typeof value === 'string') {
280
+ n = AUTHORITY_NAME.get(program)?.get(value);
281
+ if (n === undefined) throw Error(`${what}: unknown AuthorityType name ${value}`);
282
+ } else {
283
+ n = typeof value === 'bigint' ? Number(value) : value;
284
+ }
285
+ if (!Number.isSafeInteger(n) || n < 0 || n > 0xff) throw Error(`${what}: not an authority byte`);
286
+ return Buffer.from([n]);
287
+ }
288
+ if (type === 'utf8_rest') return Buffer.from(String(value), 'utf8');
289
+ if (type === 'u16_vec') {
290
+ const out = Buffer.alloc(value.length * 2);
291
+ value.forEach((entry, index) => out.writeUInt16LE(entry, index * 2));
292
+ return out;
293
+ }
294
+ if (type === 'bytes') return Buffer.from(String(value), 'hex');
295
+ throw Error(`unknown arg type ${type}`);
296
+ }
297
+
298
+ /**
299
+ * Decode one token instruction payload.
300
+ *
301
+ * Returns null with a NAMED REASON attached via `tokenRefusal` when the
302
+ * bytes cannot honestly be named — the tri-state matters: `program` is not a
303
+ * token program means the caller was never supposed to ask, `null` means the
304
+ * token program's own table refuses, a result means the bytes are named AND
305
+ * re-encodable.
306
+ *
307
+ * `trailing: 'report'` (default) returns the decoded instruction plus the
308
+ * byte count the table does not explain; 'error' refuses instead.
309
+ */
310
+ export function decodeTokenInstruction(program, bytes, { trailing = 'report' } = {}) {
311
+ const artifact = ARTIFACTS.get(program);
312
+ if (!artifact) return { refused: 'not-token' };
313
+ if (!(bytes instanceof Uint8Array) || bytes.length === 0) {
314
+ throw new TokenCodecError('empty-payload', `${program}: no instruction data`);
315
+ }
316
+ const tag = bytes[0];
317
+ const entry = artifact.instructions.find((ix) => ix.tag === tag);
318
+ if (!entry) {
319
+ // Upstream's own dispatch order: the ONE-BYTE token selector is tried
320
+ // first, and only when it does not match does the processor fall through
321
+ // to `TokenMetadataInstruction::unpack(input)`. A payload whose first
322
+ // byte is not a token variant is therefore either a metadata instruction
323
+ // (8-byte discriminator, Token-2022 only) or a refusal — and it is never
324
+ // a token instruction this table skipped.
325
+ if (hasTokenMetadataFallback(program)) {
326
+ try {
327
+ return decodeTokenMetadataInstruction(program, bytes);
328
+ } catch (error) {
329
+ // The fallback raises its own error type; the token module's
330
+ // contract is one error type with a named reason, so the reason is
331
+ // preserved and the type is normalized here.
332
+ if (error instanceof TokenMetadataCodecError) {
333
+ throw new TokenCodecError(error.reason, error.message);
334
+ }
335
+ throw error;
336
+ }
337
+ }
338
+ throw new TokenCodecError(
339
+ 'unknown-selector',
340
+ `${program}: tag ${tag} is not in the pinned ${artifact.label} interface`,
341
+ );
342
+ }
343
+ const args = {};
344
+ let offset = 1;
345
+ const order = entry.args.map((spec, index) => ({ spec, index }));
346
+ const restKinds = new Set(['utf8_rest', 'u16_vec', 'bytes']);
347
+ for (let index = 0; index < entry.args.length; index++) {
348
+ const spec = entry.args[index];
349
+ const decoded = decodeArg(spec.type, bytes, offset, entry.name);
350
+ args[spec.name] = decoded.value;
351
+ offset += decoded.size;
352
+ if (restKinds.has(spec.type) && index !== entry.args.length - 1) {
353
+ throw new TokenCodecError('rest-arg-not-last', `${program}: ${entry.name} rest arg ${spec.name} is not last — upstream order violated`);
354
+ }
355
+ }
356
+ void order;
357
+ const trailingBytes = bytes.length - offset;
358
+ if (trailingBytes > 0 && trailing === 'error') {
359
+ throw new TokenCodecError('trailing-bytes', `${program}: ${entry.name} explains ${offset} of ${bytes.length} byte(s)`);
360
+ }
361
+ const authorityByte = args.authority_type;
362
+ const authorityName = typeof authorityByte === 'number'
363
+ ? AUTHORITY.get(program).get(authorityByte)
364
+ : null;
365
+ if (typeof authorityByte === 'number' && authorityName === undefined) {
366
+ throw new TokenCodecError('unknown-authority-type', `${program}: authority byte ${authorityByte} is not a declared AuthorityType variant`);
367
+ }
368
+ return {
369
+ name: entry.name,
370
+ tag,
371
+ selectorWidth: 1,
372
+ args,
373
+ authorityType: authorityName,
374
+ consumed: offset,
375
+ bytes: bytes.length,
376
+ trailing: { count: trailingBytes },
377
+ roundTripByteExact: encodeTokenInstruction(program, entry.name, args).equals(Buffer.from(bytes)),
378
+ scheme: 'native-token',
379
+ source: 'pinned-vendored-upstream',
380
+ };
381
+ }
382
+
383
+ /** Why the last `decodeTokenInstruction` refused, or null if it did not. */
384
+ export function tokenRefusal(program, bytes) {
385
+ try {
386
+ const result = decodeTokenInstruction(program, bytes);
387
+ return result.refused ? result.refused : null;
388
+ } catch (error) {
389
+ return error instanceof TokenCodecError ? error.reason : `thrown: ${error.message}`;
390
+ }
391
+ }
392
+
393
+ /** Re-encode one token instruction from a name + args. Throws on mismatch. */
394
+ export function encodeTokenInstruction(program, name, values = {}) {
395
+ const artifact = ARTIFACTS.get(program);
396
+ if (!artifact) throw new TokenCodecError('not-token', program);
397
+ const entry = artifact.instructions.find((ix) => ix.name === name);
398
+ if (!entry) throw new TokenCodecError('unknown-variant', `${program}: ${name}`);
399
+ const chunks = [Buffer.from([entry.tag])];
400
+ for (const spec of entry.args) {
401
+ const value = values[spec.name];
402
+ if (value === undefined && spec.type !== 'coption_pubkey' && spec.type !== 'coption_u64') {
403
+ throw new TokenCodecError('missing-arg', `${name}.${spec.name}`);
404
+ }
405
+ chunks.push(encodeArg(spec.type, value, `${name}.${spec.name}`, program));
406
+ }
407
+ return Buffer.concat(chunks);
408
+ }
409
+
410
+ /** Names the table can claim for `program` (coverage callers). */
411
+ export function tokenNames(program) {
412
+ const artifact = ARTIFACTS.get(program);
413
+ if (!artifact) return [];
414
+ const base = artifact.instructions.map((entry) => entry.name);
415
+ // The processor's fallback branch is part of what the program accepts, so
416
+ // enumeration must include it — a name list that omits it would make a
417
+ // "known name" check refuse payloads the program executes.
418
+ return hasTokenMetadataFallback(program) ? [...base, ...tokenMetadataNames()] : base;
419
+ }
420
+
421
+ /** Whether `name` belongs to `program`'s token table (including the fallback). */
422
+ export function isTokenName(program, name) {
423
+ const artifact = ARTIFACTS.get(program);
424
+ if (!artifact) return false;
425
+ if (artifact.instructions.some((entry) => entry.name === name)) return true;
426
+ return hasTokenMetadataFallback(program) && tokenMetadataNames().includes(name);
427
+ }
428
+
429
+ /** AuthorityType byte -> name for `program` (exposed for callers/tests). */
430
+ export function authorityTypeName(program, byte) {
431
+ return AUTHORITY.get(program)?.get(byte) ?? null;
432
+ }
433
+
434
+ /** AuthorityType name -> byte for `program` (exposed for callers/tests). */
435
+ export function authorityTypeByte(program, name) {
436
+ return AUTHORITY_NAME.get(program)?.get(name) ?? null;
437
+ }
438
+
439
+ /** The AuthorityType pairs as the artifact records them. */
440
+ export function authorityTypes(program) {
441
+ const artifact = ARTIFACTS.get(program);
442
+ return artifact ? artifact.authorityType.map((entry) => ({ ...entry })) : [];
443
+ }