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.
- package/LICENSE +21 -0
- package/README.md +2777 -2
- package/bin/sememe.mjs +556 -0
- package/index.mjs +48 -0
- package/interface/ata-ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL.json +62 -0
- package/interface/native-11111111111111111111111111111111.json +226 -0
- package/interface/native-ComputeBudget111111111111111111111111111111.json +72 -0
- package/interface/raydium-675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8.json +352 -0
- package/interface/token-TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA.json +334 -0
- package/interface/token-TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb.json +535 -0
- package/interface/token-metadata.json +132 -0
- package/package.json +71 -4
- package/src/account-list.mjs +308 -0
- package/src/account-union.mjs +523 -0
- package/src/account.mjs +199 -0
- package/src/acquire.mjs +145 -0
- package/src/adopt.mjs +206 -0
- package/src/ata-interface.mjs +276 -0
- package/src/ata.mjs +223 -0
- package/src/base58.mjs +96 -0
- package/src/canonical.mjs +24 -0
- package/src/cli-surface.mjs +300 -0
- package/src/codec.mjs +1118 -0
- package/src/conformance.mjs +431 -0
- package/src/corpus/harvest-compute-budget-2026-10-31.json +19122 -0
- package/src/corpus/harvest-system-2026-10-31.json +26090 -0
- package/src/decode.mjs +286 -0
- package/src/dialect.mjs +180 -0
- package/src/discriminators.mjs +192 -0
- package/src/drift.mjs +172 -0
- package/src/gap.mjs +205 -0
- package/src/lighthouse.mjs +168 -0
- package/src/lock.mjs +440 -0
- package/src/native-audit.mjs +56 -0
- package/src/native-corpus.mjs +57 -0
- package/src/native-interface.mjs +252 -0
- package/src/native.mjs +324 -0
- package/src/peer.mjs +95 -0
- package/src/program-id.mjs +74 -0
- package/src/pull.mjs +117 -0
- package/src/rawle.mjs +454 -0
- package/src/raydium-interface.mjs +747 -0
- package/src/raydium.mjs +391 -0
- package/src/receipt-digest.mjs +84 -0
- package/src/receipt-name.mjs +128 -0
- package/src/receipt-order.mjs +56 -0
- package/src/shank.mjs +73 -0
- package/src/short-forms.mjs +209 -0
- package/src/store-read.mjs +253 -0
- package/src/store-write.mjs +46 -0
- package/src/token-interface.mjs +394 -0
- package/src/token-metadata-interface.mjs +371 -0
- package/src/token-metadata.mjs +317 -0
- package/src/token.mjs +443 -0
- package/src/union.mjs +428 -0
- package/vendor/anza-solana-sdk/8db406e2edfafa25939ec782b77b5b6f30de164d/LICENSE.txt +201 -0
- package/vendor/anza-solana-sdk/8db406e2edfafa25939ec782b77b5b6f30de164d/compute-budget-interface/src/lib.rs +93 -0
- package/vendor/anza-solana-sdk/8db406e2edfafa25939ec782b77b5b6f30de164d/system-interface/src/instruction.rs +1899 -0
- package/vendor/lighthouse/4c579479c98635e419b1b167f08be02a71604a71/LICENSE +21 -0
- package/vendor/lighthouse/4c579479c98635e419b1b167f08be02a71604a71/lighthouse.json +2769 -0
- package/vendor/raydium-amm/91739d8d9f5cf347e0ce14ebe4d2bb7069fb31b6/LICENSE +201 -0
- package/vendor/raydium-amm/91739d8d9f5cf347e0ce14ebe4d2bb7069fb31b6/program/src/instruction.rs +1189 -0
- package/vendor/raydium-amm/91739d8d9f5cf347e0ce14ebe4d2bb7069fb31b6/program/src/state.rs +1348 -0
- package/vendor/spl-associated-token-account/fbf5379e7398ac8b231c73ee8a9ff677b43dd0ec/LICENSE +201 -0
- package/vendor/spl-associated-token-account/fbf5379e7398ac8b231c73ee8a9ff677b43dd0ec/interface/src/instruction.rs +169 -0
- package/vendor/spl-associated-token-account/fbf5379e7398ac8b231c73ee8a9ff677b43dd0ec/interface/src/lib.rs +12 -0
- package/vendor/spl-associated-token-account/fbf5379e7398ac8b231c73ee8a9ff677b43dd0ec/program/src/processor.rs +308 -0
- package/vendor/spl-token/737788ff5bcc3f1f44cfb222f25483187650be76/LICENSE.txt +176 -0
- package/vendor/spl-token/737788ff5bcc3f1f44cfb222f25483187650be76/interface/src/instruction.rs +1963 -0
- package/vendor/spl-token-2022/5251dcc07ecbcb721d6c4f0231090defadf00311/LICENSE.txt +176 -0
- package/vendor/spl-token-2022/5251dcc07ecbcb721d6c4f0231090defadf00311/interface/src/instruction.rs +2868 -0
- package/vendor/spl-token-metadata-interface/7038969ec74a89f1afdb9bc97c112d4d26fead9a/LICENSE +201 -0
- package/vendor/spl-token-metadata-interface/7038969ec74a89f1afdb9bc97c112d4d26fead9a/interface/src/error.rs +58 -0
- package/vendor/spl-token-metadata-interface/7038969ec74a89f1afdb9bc97c112d4d26fead9a/interface/src/instruction.rs +596 -0
- package/vendor/spl-token-metadata-interface/7038969ec74a89f1afdb9bc97c112d4d26fead9a/interface/src/lib.rs +21 -0
- package/vendor/spl-token-metadata-interface/7038969ec74a89f1afdb9bc97c112d4d26fead9a/interface/src/state.rs +229 -0
package/bin/sememe.mjs
ADDED
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
// `pull` is imported LAZILY (inside the branch): it is the only path that
|
|
6
|
+
// needs @solana/web3.js. A consumer who installed the package for DECODING
|
|
7
|
+
// only — the common case — must be able to run `sememe decode` without any
|
|
8
|
+
// package installed at all, so the acquisition dependency is never loaded
|
|
9
|
+
// unless a pull is actually requested.
|
|
10
|
+
import { decodePayload, decodeStoredAccount, decodeUnionPayload, decodeUnionStoredAccount, adopt, parsePayload } from '../src/decode.mjs';
|
|
11
|
+
import {
|
|
12
|
+
decodeNativeInstruction, nativeProvenance, nativePrograms, isNativeProgram,
|
|
13
|
+
} from '../src/native.mjs';
|
|
14
|
+
import {
|
|
15
|
+
decodeTokenInstruction, tokenProvenance, tokenPrograms, isTokenProgram,
|
|
16
|
+
} from '../src/token.mjs';
|
|
17
|
+
import {
|
|
18
|
+
decodeRaydiumInstruction, raydiumProvenance, raydiumPrograms, isRaydiumProgram,
|
|
19
|
+
} from '../src/raydium.mjs';
|
|
20
|
+
import {
|
|
21
|
+
decodeAtaInstruction, ataProvenance, ataPrograms, isAtaProgram,
|
|
22
|
+
} from '../src/ata.mjs';
|
|
23
|
+
import { toJsonSafe } from '../src/codec.mjs';
|
|
24
|
+
import { VERIFIED_SHORT_FORMS, SHORT_FORM_SOURCE } from '../src/short-forms.mjs';
|
|
25
|
+
// The DECLARED surface of every command (which flags it takes, how many
|
|
26
|
+
// positionals it consumes). Imported statically: it has no imports of its
|
|
27
|
+
// own, so it stays inside the decode core's zero-dependency guarantee.
|
|
28
|
+
import { parseCommandArgs, UsageError } from '../src/cli-surface.mjs';
|
|
29
|
+
|
|
30
|
+
const usage = `usage:
|
|
31
|
+
sememe absent-help
|
|
32
|
+
print the chain-verified short wire forms that --absent verified licenses.
|
|
33
|
+
sememe pull <program-id> [--store <directory>] [--rpc <https-url>]
|
|
34
|
+
sememe gap <program-id> [--rpc <https-url>]
|
|
35
|
+
answer the question "why is there no interface here?" BEFORE you pull.
|
|
36
|
+
Probes both on-chain IDL addresses and returns one of:
|
|
37
|
+
pullable an interface exists on chain that this program's
|
|
38
|
+
store simply does not have yet -> run sememe pull
|
|
39
|
+
no-interface-published neither address answers -> pulling it can never
|
|
40
|
+
help: adopt a published copy or derive upstream
|
|
41
|
+
present-undecodable an account exists but its schema is unusable
|
|
42
|
+
vendored answered by sememe's pinned wire table, no IDL
|
|
43
|
+
unknown the RPC failed — deliberately NOT an absence
|
|
44
|
+
Never reports an RPC failure as an absence, and never tells you to pull
|
|
45
|
+
a program that publishes nothing (the fix would never arrive).
|
|
46
|
+
sememe adopt <program-id> <file> [--store <directory>] [--source <label>]
|
|
47
|
+
store a first-party PUBLISHED interface copy next to the on-chain pull, so
|
|
48
|
+
union decode has a second verified source. The receipt says "published":
|
|
49
|
+
it is provenance of bytes, not proof the bytes describe the deployed program.
|
|
50
|
+
sememe adopt-lighthouse [--store <directory>]
|
|
51
|
+
the measured #1-remaining-gap case, wired end to end: adopt Lighthouse
|
|
52
|
+
(L2TExMFK…, 1,214 payloads in the committed 6-block window) from the
|
|
53
|
+
vendored first-party copy (Jac0xb/lighthouse, MIT), hash-checked against
|
|
54
|
+
its pin BEFORE any write; the receipt's origin stays declared.
|
|
55
|
+
sememe lock [--store <directory>] [--out <file>]
|
|
56
|
+
snapshot the WHOLE store into a sememe.lock document: for every program,
|
|
57
|
+
the exact fingerprint its newest verified receipt answered with, plus
|
|
58
|
+
source/slot/receipt provenance. Every receipt is re-checked through the
|
|
59
|
+
store reader first — a store the reader refuses produces NO lock, never
|
|
60
|
+
a lock over damage. Commit the file; CI then runs "sememe verify".
|
|
61
|
+
sememe verify [--store <directory>] [--lock <file>] [--mode error|warn]
|
|
62
|
+
re-check the store against the lock and refuse drift BY NAME:
|
|
63
|
+
drifted the store answers a pinned program with a DIFFERENT
|
|
64
|
+
fingerprint (a newer pull replaced it) — reported with
|
|
65
|
+
both fingerprints
|
|
66
|
+
missing the store has no receipt for a pinned program
|
|
67
|
+
unverified a pinned object fails the store reader's re-hash check
|
|
68
|
+
Extra store programs are reported, never a violation: the lock pins
|
|
69
|
+
minimums, not store equality. --mode error (default) exits 1 when any
|
|
70
|
+
violation exists (the CI gate); --mode warn reports and exits 0.
|
|
71
|
+
sememe decode <program-id> <payload> [--store <directory>] [--trailing report|error] [--absent error|report|verified]
|
|
72
|
+
payload: base58 or hex instruction data (discriminator + args)
|
|
73
|
+
--trailing report (default): decode the IDL-described prefix and report
|
|
74
|
+
the bytes the IDL does not explain under "trailing"; --trailing error
|
|
75
|
+
refuses the payload instead.
|
|
76
|
+
--absent error (default): a payload that ENDS where a declared field
|
|
77
|
+
would start is refused as truncated. --absent report: decode it and
|
|
78
|
+
name the absent fields. --absent verified: decode it only when a
|
|
79
|
+
chain witness covers that exact wire form (see --absent-help).
|
|
80
|
+
sememe decode --union <program-id> <payload> [--store <directory>]
|
|
81
|
+
decode against BOTH stored sources (on-chain + adopted). The result names
|
|
82
|
+
the source that answered, the other source's verdict, and any layout
|
|
83
|
+
drift between them. Byte evidence arbitrates; a name conflict is refused.
|
|
84
|
+
sememe account <program-id> <account-data> [--store <directory>] [--name <account-type>]
|
|
85
|
+
account-data: base64 account bytes (as getMultipleAccounts returns them)
|
|
86
|
+
or hex. Without --name the account's 8-byte discriminator picks the
|
|
87
|
+
account type from the stored IDL.
|
|
88
|
+
sememe account --union <program-id> <account-data> [--store <directory>]
|
|
89
|
+
decode against BOTH stored sources (on-chain + adopted), like
|
|
90
|
+
"sememe decode --union". The result names the source that answered, the
|
|
91
|
+
other source's verdict, and any layout drift between them. Byte evidence
|
|
92
|
+
arbitrates; a name conflict is refused.
|
|
93
|
+
sememe conform <transaction.json> [--store <directory>] [--only <program-id>]
|
|
94
|
+
transaction.json: an RPC getTransaction/getBlock response entry
|
|
95
|
+
(encoding "json") with its meta — as saved by any RPC client.
|
|
96
|
+
Checks EVERY instruction of the transaction against the interface the
|
|
97
|
+
verified store declares for its program: account count, signer flags,
|
|
98
|
+
pinned addresses, and extra accounts. A disagreement is named; an
|
|
99
|
+
unreadable transaction shape is REFUSED, never reported as conforming.
|
|
100
|
+
sememe decode-native <program-id> <payload> [--trailing report|error]
|
|
101
|
+
payload: base58 or hex instruction data. For the NATIVE programs (System,
|
|
102
|
+
ComputeBudget) there is no IDL to pull: their interface is pinned from
|
|
103
|
+
the upstream runtime, and this decodes against that pin. The result names
|
|
104
|
+
the instruction, its arguments, and whether the bytes re-encode exactly.
|
|
105
|
+
sememe native-provenance [program-id]
|
|
106
|
+
print the pinned source, encoding rule and measured traffic for a native
|
|
107
|
+
program (or for all of them).
|
|
108
|
+
sememe decode-token <program-id> <payload> [--trailing report|error]
|
|
109
|
+
payload: base58 or hex instruction data. For SPL TOKEN and TOKEN-2022
|
|
110
|
+
there is no IDL account to pull at either address: their interface is
|
|
111
|
+
pinned from the upstream Rust crates (pack/unpack wire rule), and this
|
|
112
|
+
decodes against that pin. Extension sub-instructions report as trailing.
|
|
113
|
+
Token-2022 payloads whose first byte is no token tag fall through to the
|
|
114
|
+
token-METADATA table (spl-token-metadata-interface, 8-byte
|
|
115
|
+
discriminators) — the same fallback Token-2022's own processor takes.
|
|
116
|
+
sememe token-provenance [program-id]
|
|
117
|
+
print the pinned crate, encoding rule and variant count for a token
|
|
118
|
+
program (or for both).
|
|
119
|
+
sememe decode-raydium <program-id> <payload> [--trailing report|error]
|
|
120
|
+
payload: base58 or hex instruction data. For RAYDIUM AMM V4 there is no
|
|
121
|
+
IDL account to pull at either address: its interface is pinned from the
|
|
122
|
+
upstream program source (sparse u8 tag, pack/unpack wire rule), and this
|
|
123
|
+
decodes against that pin. Variants upstream itself refuses stay refused.
|
|
124
|
+
sememe raydium-provenance [program-id]
|
|
125
|
+
print the pinned upstream commit, encoding rule and variant counts for a
|
|
126
|
+
Raydium program (or for all of them).
|
|
127
|
+
sememe decode-ata <program-id> [<payload>] [--empty] [--trailing report|error]
|
|
128
|
+
payload: base58 or hex instruction data, or --empty for the processor's
|
|
129
|
+
own empty-input Create fallback. For the SPL ASSOCIATED TOKEN ACCOUNT
|
|
130
|
+
program there is no IDL account to pull at either address: its interface
|
|
131
|
+
is pinned from the upstream program source (borsh unit enum, empty-input
|
|
132
|
+
processor fallback), and this decodes against that pin.
|
|
133
|
+
sememe ata-provenance [program-id]
|
|
134
|
+
print the pinned upstream commit, encoding rule and variant counts for
|
|
135
|
+
the SPL Associated Token Account program.
|
|
136
|
+
|
|
137
|
+
Every command declares the flags it accepts and how many positional arguments
|
|
138
|
+
it consumes. A flag the command does not know, a flag given twice, a flag
|
|
139
|
+
without a value, or a surplus positional argument is REFUSED BY NAME before
|
|
140
|
+
any work happens -- no store is read, no file is written, no RPC is
|
|
141
|
+
contacted. The refusal names the closest supported flag:
|
|
142
|
+
|
|
143
|
+
sememe lock --store ./store --o out.lock
|
|
144
|
+
sememe lock: unknown flag "--o" — did you mean "--out"?
|
|
145
|
+
supported flags: --store, --out`;
|
|
146
|
+
|
|
147
|
+
const [command, ...tokens] = process.argv.slice(2);
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
// Parse the whole command line AGAINST THE SPEC, up front, before any
|
|
151
|
+
// branch runs: an unknown flag, a repeated flag, a missing flag value or a
|
|
152
|
+
// surplus positional argument is refused here, before a store is read, a
|
|
153
|
+
// file is written or an RPC is contacted. Previously the flags were
|
|
154
|
+
// searched for by name, so a typo was silently dropped and the command ran
|
|
155
|
+
// against whatever it would have done without that flag — a lock written to
|
|
156
|
+
// a different path than the one the caller named, a pull that touched the
|
|
157
|
+
// default store after a --stor typo.
|
|
158
|
+
const { flags, positionals } = parseCommandArgs(command, tokens);
|
|
159
|
+
const program = positionals[0];
|
|
160
|
+
// The positionals the branches consume with args.shift(): everything after
|
|
161
|
+
// the first one. Flags are already separated, so no order matters.
|
|
162
|
+
const args = positionals.slice(1);
|
|
163
|
+
const unionRequested = flags['--union'] === true;
|
|
164
|
+
const option = (name, fallback) => (Object.prototype.hasOwnProperty.call(flags, name) ? flags[name] : fallback);
|
|
165
|
+
|
|
166
|
+
if (command === 'absent-help') {
|
|
167
|
+
// The evidence behind --absent verified, printed so a user can see WHICH
|
|
168
|
+
// wire forms are licensed before relying on the flag.
|
|
169
|
+
console.log(JSON.stringify({
|
|
170
|
+
source: SHORT_FORM_SOURCE,
|
|
171
|
+
forms: VERIFIED_SHORT_FORMS,
|
|
172
|
+
}, null, 2));
|
|
173
|
+
} else if (command === 'pull') {
|
|
174
|
+
if (!program) throw Error('pull needs a program id');
|
|
175
|
+
const store = option('--store', path.join(os.homedir(), '.sememe'));
|
|
176
|
+
const rpc = option('--rpc', 'https://api.mainnet-beta.solana.com');
|
|
177
|
+
let pull;
|
|
178
|
+
try {
|
|
179
|
+
({ pull } = await import('../src/pull.mjs'));
|
|
180
|
+
} catch (error) {
|
|
181
|
+
// The library already refuses with a complete sentence naming the fix
|
|
182
|
+
// (src/peer.mjs); wrapping it again produced a doubled message that
|
|
183
|
+
// said "install it next to sememe" twice. Anything else still gets the
|
|
184
|
+
// contextual wrap with the cause appended.
|
|
185
|
+
if (error?.code === 'peer_dependency_missing') throw error;
|
|
186
|
+
throw Error(`pull needs @solana/web3.js: install it next to sememe (npm i @solana/web3.js@1.98.4) — ${String(error?.message ?? error).slice(0, 120)}`);
|
|
187
|
+
}
|
|
188
|
+
console.log(JSON.stringify(await pull(program, store, rpc), null, 2));
|
|
189
|
+
} else if (command === 'gap') {
|
|
190
|
+
if (!program) throw Error('gap needs a program id');
|
|
191
|
+
const rpc = option('--rpc', 'https://api.mainnet-beta.solana.com');
|
|
192
|
+
let probe, where, nextStep;
|
|
193
|
+
try {
|
|
194
|
+
// Lazily imported like `pull`: the only path that needs web3.js.
|
|
195
|
+
({ probeGap: probe, nextStep } = await import('../src/gap.mjs'));
|
|
196
|
+
({ addresses: where } = await import('../src/acquire.mjs'));
|
|
197
|
+
} catch (error) {
|
|
198
|
+
if (error?.code === 'peer_dependency_missing') throw error;
|
|
199
|
+
throw Error(`gap needs @solana/web3.js: install it next to sememe (npm i @solana/web3.js@1.98.4) — ${String(error?.message ?? error).slice(0, 120)}`);
|
|
200
|
+
}
|
|
201
|
+
// The same classifier the qualification report uses, so what a user is
|
|
202
|
+
// told and what the research artifact records are one code path.
|
|
203
|
+
const { rows } = await probe([{ program, payloads: 0 }], { url: rpc });
|
|
204
|
+
const row = rows[0];
|
|
205
|
+
console.log(JSON.stringify({ ...row, addresses: await where(program), next: nextStep(row, program) }, null, 2));
|
|
206
|
+
if (row.verdict === 'unknown') process.exitCode = 3;
|
|
207
|
+
} else if (command === 'lock') {
|
|
208
|
+
if (!program) {
|
|
209
|
+
// No program argument: lock the WHOLE store (the common case). A program
|
|
210
|
+
// id here would otherwise be swallowed silently, so an explicit store
|
|
211
|
+
// wide lock must be chosen by omitting the argument entirely.
|
|
212
|
+
const store = option('--store', path.join(os.homedir(), '.sememe'));
|
|
213
|
+
const out = option('--out', 'sememe.lock');
|
|
214
|
+
const { writeLock } = await import('../src/lock.mjs');
|
|
215
|
+
const doc = await writeLock(store, out);
|
|
216
|
+
console.log(JSON.stringify(doc, null, 2));
|
|
217
|
+
} else {
|
|
218
|
+
throw Error(`lock takes no program id — it snapshots the whole store; got "${program}"`);
|
|
219
|
+
}
|
|
220
|
+
} else if (command === 'verify') {
|
|
221
|
+
const store = option('--store', path.join(os.homedir(), '.sememe'));
|
|
222
|
+
const lockFile = option('--lock', 'sememe.lock');
|
|
223
|
+
const mode = option('--mode', 'error');
|
|
224
|
+
if (mode !== 'error' && mode !== 'warn') throw Error('--mode must be "error" or "warn"');
|
|
225
|
+
if (program) throw Error(`verify takes no program id — it checks the whole lock; got "${program}"`);
|
|
226
|
+
const { checkLock } = await import('../src/lock.mjs');
|
|
227
|
+
const report = await checkLock(store, lockFile, { mode });
|
|
228
|
+
console.log(JSON.stringify(report, null, 2));
|
|
229
|
+
if (report.violations > 0) {
|
|
230
|
+
console.error(JSON.stringify({
|
|
231
|
+
note: `store does not match ${lockFile}: ${report.drifted.length} drifted, ${report.missing.length} missing, ${report.unverified.length} unverified, ${report.relabelled.length} relabelled, ${report.unbound.length} unbound`,
|
|
232
|
+
drifted: report.drifted.map((d) => d.program),
|
|
233
|
+
missing: report.missing,
|
|
234
|
+
unverified: report.unverified.map((u) => (u.scope === 'extra'
|
|
235
|
+
? `${u.program} (extra: the lock does not pin it — re-pull that program's receipt)`
|
|
236
|
+
: u.program)),
|
|
237
|
+
relabelled: report.relabelled.map((r) => r.program),
|
|
238
|
+
unbound: report.unbound.map((r) => r.program),
|
|
239
|
+
}));
|
|
240
|
+
// The note goes to stderr as a WARNING. The exit stays 0: in error mode
|
|
241
|
+
// checkLock already threw above (that is the gate), so reaching here
|
|
242
|
+
// with violations can only be warn mode — a warning that fails the
|
|
243
|
+
// build is not a warning.
|
|
244
|
+
}
|
|
245
|
+
} else if (command === 'adopt') {
|
|
246
|
+
if (!program) throw Error('adopt needs a program id');
|
|
247
|
+
const file = args.shift();
|
|
248
|
+
if (!file) throw Error('adopt needs the interface file to store');
|
|
249
|
+
const store = option('--store', path.join(os.homedir(), '.sememe'));
|
|
250
|
+
const source = option('--source', 'published');
|
|
251
|
+
const origin = option('--origin', undefined);
|
|
252
|
+
const adopted = await adopt(program, file, store, {
|
|
253
|
+
source,
|
|
254
|
+
...(origin === undefined ? {} : { origin }),
|
|
255
|
+
});
|
|
256
|
+
// The origin's STATUS travels with the result: a declared label is a
|
|
257
|
+
// claim the caller typed, not a provenance sememe checked. The CLI says
|
|
258
|
+
// so out loud, so an adopter cannot mistake a stored string for a
|
|
259
|
+
// verified one.
|
|
260
|
+
console.log(JSON.stringify({
|
|
261
|
+
...adopted,
|
|
262
|
+
originStatus: adopted.originStatus ?? 'declared',
|
|
263
|
+
originNote: adopted.originNote
|
|
264
|
+
?? 'declared origin, not verified: sememe did not check that this repository, commit or file exists — provenance of bytes, not proof they describe the deployed program',
|
|
265
|
+
}, null, 2));
|
|
266
|
+
} else if (command === 'adopt-lighthouse') {
|
|
267
|
+
const store = option('--store', path.join(os.homedir(), '.sememe'));
|
|
268
|
+
const { adoptLighthouse } = await import('../src/lighthouse.mjs');
|
|
269
|
+
const { adopt: adoptFn } = await import('../src/adopt.mjs');
|
|
270
|
+
const result = await adoptLighthouse(store, { adopt: adoptFn });
|
|
271
|
+
console.log(JSON.stringify({
|
|
272
|
+
program: result.receipt.program,
|
|
273
|
+
source: result.receipt.source,
|
|
274
|
+
origin: result.receipt.origin,
|
|
275
|
+
originStatus: result.receipt.originStatus,
|
|
276
|
+
originNote: result.receipt.originNote,
|
|
277
|
+
fingerprint: result.receipt.fingerprint,
|
|
278
|
+
instructionCount: result.receipt.instructionCount,
|
|
279
|
+
path: result.receipt.path,
|
|
280
|
+
receiptPath: result.receipt.receiptPath,
|
|
281
|
+
}, null, 2));
|
|
282
|
+
} else if (command === 'decode') {
|
|
283
|
+
if (!program) throw Error('decode needs a program id');
|
|
284
|
+
const payload = args.shift();
|
|
285
|
+
if (!payload) throw Error('decode needs an instruction payload');
|
|
286
|
+
const store = option('--store', path.join(os.homedir(), '.sememe'));
|
|
287
|
+
const trailing = option('--trailing', undefined);
|
|
288
|
+
if (trailing !== undefined && trailing !== 'report' && trailing !== 'error') {
|
|
289
|
+
throw Error('--trailing must be "report" or "error"');
|
|
290
|
+
}
|
|
291
|
+
const absent = option('--absent', undefined);
|
|
292
|
+
if (absent !== undefined && !['error', 'report', 'verified'].includes(absent)) {
|
|
293
|
+
throw Error('--absent must be "error", "report" or "verified"');
|
|
294
|
+
}
|
|
295
|
+
const union = unionRequested;
|
|
296
|
+
const extra = { ...(trailing === undefined ? {} : { trailing }), ...(absent === undefined ? {} : { absent }) };
|
|
297
|
+
// A program that publishes NO interface — the two token programs, the two
|
|
298
|
+
// native programs, Raydium AMM v4 — has no stored receipt, so the store
|
|
299
|
+
// path cannot answer for it. That is not a missing-pull problem and the
|
|
300
|
+
// error must not say it is: name the vendored path that CAN answer, or a
|
|
301
|
+
// first-time user reads "run sememe pull" as the fix and pulls forever.
|
|
302
|
+
const vendored = (programId) => (isTokenProgram(programId) ? 'decode-token'
|
|
303
|
+
: isNativeProgram(programId) ? 'decode-native'
|
|
304
|
+
: isRaydiumProgram(programId) ? 'decode-raydium'
|
|
305
|
+
: isAtaProgram(programId) ? 'decode-ata' : null);
|
|
306
|
+
let decoded;
|
|
307
|
+
try {
|
|
308
|
+
decoded = union
|
|
309
|
+
? await decodeUnionPayload(store, program, payload, extra)
|
|
310
|
+
: await decodePayload(store, program, payload, extra);
|
|
311
|
+
} catch (error) {
|
|
312
|
+
const message = String(error?.message ?? error);
|
|
313
|
+
const command = vendored(program);
|
|
314
|
+
if (command && (message.startsWith('no stored IDL for program') || message.startsWith('no sememe store at'))) {
|
|
315
|
+
// The vendored answer is the whole answer: no pull will ever create a
|
|
316
|
+
// receipt for this program, so the "run sememe pull" part of the
|
|
317
|
+
// store's message is dropped rather than left to contradict the hint.
|
|
318
|
+
throw Error(`${program} publishes no interface to pull, so there is nothing to store — ` +
|
|
319
|
+
`its wire table is pinned from upstream instead.\n` +
|
|
320
|
+
` use: sememe ${command} ${program} <payload>`);
|
|
321
|
+
}
|
|
322
|
+
throw error;
|
|
323
|
+
}
|
|
324
|
+
console.log(JSON.stringify(decoded, null, 2));
|
|
325
|
+
// A short-form decode is a measurement-backed claim, so the evidence is
|
|
326
|
+
// surfaced on stderr as well: a scripted caller should not have to know
|
|
327
|
+
// to look inside the JSON to learn the field was absent and why.
|
|
328
|
+
if (decoded.shortForm) {
|
|
329
|
+
console.error(JSON.stringify({
|
|
330
|
+
note: `${decoded.name} decoded as a chain-verified short form: the wire carries no `
|
|
331
|
+
+ `${JSON.stringify(decoded.shortForm.absent)}`,
|
|
332
|
+
verified: decoded.shortForm.verified,
|
|
333
|
+
absent: decoded.shortForm.absent,
|
|
334
|
+
witnesses: decoded.shortForm.witnesses,
|
|
335
|
+
source: decoded.shortForm.source,
|
|
336
|
+
}));
|
|
337
|
+
}
|
|
338
|
+
// Unexplained bytes are a completeness finding, not a crash: report them
|
|
339
|
+
// on stderr so scripted callers notice without losing the decode result.
|
|
340
|
+
if (decoded.trailing.count > 0 && trailing !== 'error') {
|
|
341
|
+
console.error(JSON.stringify({
|
|
342
|
+
note: `${decoded.trailing.count} byte(s) after ${decoded.name} are not described by the stored IDL`,
|
|
343
|
+
consumed: decoded.consumed,
|
|
344
|
+
bytes: decoded.bytes,
|
|
345
|
+
trailing: decoded.trailing,
|
|
346
|
+
}));
|
|
347
|
+
}
|
|
348
|
+
} else if (command === 'conform') {
|
|
349
|
+
// The first positional after `conform` is the file (it lands in `program`
|
|
350
|
+
// like every other command's first argument).
|
|
351
|
+
const file = program ?? args.shift();
|
|
352
|
+
if (!file) throw Error('conform needs a transaction JSON file');
|
|
353
|
+
const store = option('--store', path.join(os.homedir(), '.sememe'));
|
|
354
|
+
const only = option('--only', undefined);
|
|
355
|
+
const envelope = JSON.parse(readFileSync(file, 'utf8'));
|
|
356
|
+
// Accept either a bare transaction entry or the full RPC envelope.
|
|
357
|
+
const transaction = envelope?.result ?? envelope;
|
|
358
|
+
const { checkTransaction } = await import('../src/conformance.mjs');
|
|
359
|
+
const report = await checkTransaction(transaction, store, only ? { only: [only] } : {});
|
|
360
|
+
console.log(JSON.stringify(report, null, 2));
|
|
361
|
+
const disagreement = report.summary.byVerdict;
|
|
362
|
+
const violations = Object.entries(disagreement)
|
|
363
|
+
.filter(([kind]) => ['missing-declared-accounts', 'declared-signer-not-signing', 'fixed-address-mismatch', 'undeclared-signing-account'].includes(kind));
|
|
364
|
+
if (violations.length) {
|
|
365
|
+
console.error(JSON.stringify({ note: 'account-list disagreement(s) found', kinds: violations.map(([kind, count]) => `${kind}:${count}`) }));
|
|
366
|
+
process.exitCode = 1;
|
|
367
|
+
}
|
|
368
|
+
} else if (command === 'decode-native') {
|
|
369
|
+
if (!program) throw Error('decode-native needs a program id');
|
|
370
|
+
const payload = args.shift();
|
|
371
|
+
if (!payload) throw Error('decode-native needs an instruction payload');
|
|
372
|
+
const trailing = option('--trailing', 'report');
|
|
373
|
+
if (trailing !== 'report' && trailing !== 'error') {
|
|
374
|
+
throw Error('--trailing must be "report" or "error"');
|
|
375
|
+
}
|
|
376
|
+
const decoded = decodeNativeInstruction(program, parsePayload(payload), { trailing });
|
|
377
|
+
console.log(JSON.stringify({
|
|
378
|
+
program,
|
|
379
|
+
kind: 'native-instruction',
|
|
380
|
+
name: decoded.name,
|
|
381
|
+
selector: decoded.selector,
|
|
382
|
+
selectorWidth: decoded.selectorWidth,
|
|
383
|
+
args: toJsonSafe(decoded.args),
|
|
384
|
+
bytes: decoded.bytes,
|
|
385
|
+
consumed: decoded.consumed,
|
|
386
|
+
trailing: decoded.trailing,
|
|
387
|
+
roundTripByteExact: decoded.roundTripByteExact,
|
|
388
|
+
interface: nativeProvenance(program),
|
|
389
|
+
}, null, 2));
|
|
390
|
+
} else if (command === 'native-provenance') {
|
|
391
|
+
const wanted = program;
|
|
392
|
+
if (wanted) {
|
|
393
|
+
const provenance = nativeProvenance(wanted);
|
|
394
|
+
if (!provenance) throw Error(`${wanted} is not a native program sememe pins`);
|
|
395
|
+
console.log(JSON.stringify(provenance, null, 2));
|
|
396
|
+
} else {
|
|
397
|
+
console.log(JSON.stringify(nativePrograms().map((id) => nativeProvenance(id)), null, 2));
|
|
398
|
+
}
|
|
399
|
+
} else if (command === 'decode-token') {
|
|
400
|
+
if (!program) throw Error('decode-token needs a program id');
|
|
401
|
+
const payload = args.shift();
|
|
402
|
+
if (!payload) throw Error('decode-token needs an instruction payload');
|
|
403
|
+
const trailing = option('--trailing', 'report');
|
|
404
|
+
if (trailing !== 'report' && trailing !== 'error') {
|
|
405
|
+
throw Error('--trailing must be "report" or "error"');
|
|
406
|
+
}
|
|
407
|
+
const decoded = decodeTokenInstruction(program, parsePayload(payload), { trailing });
|
|
408
|
+
if (decoded.refused === 'not-token') {
|
|
409
|
+
throw Error(`${program} is not a token program sememe pins — try decode-native or decode`);
|
|
410
|
+
}
|
|
411
|
+
console.log(JSON.stringify({
|
|
412
|
+
program,
|
|
413
|
+
kind: 'token-instruction',
|
|
414
|
+
name: decoded.name,
|
|
415
|
+
tag: decoded.tag,
|
|
416
|
+
selectorWidth: decoded.selectorWidth,
|
|
417
|
+
args: toJsonSafe(decoded.args),
|
|
418
|
+
authorityType: decoded.authorityType,
|
|
419
|
+
bytes: decoded.bytes,
|
|
420
|
+
consumed: decoded.consumed,
|
|
421
|
+
trailing: decoded.trailing,
|
|
422
|
+
roundTripByteExact: decoded.roundTripByteExact,
|
|
423
|
+
interface: tokenProvenance(program),
|
|
424
|
+
}, null, 2));
|
|
425
|
+
} else if (command === 'token-provenance') {
|
|
426
|
+
const wanted = program;
|
|
427
|
+
if (wanted) {
|
|
428
|
+
const provenance = tokenProvenance(wanted);
|
|
429
|
+
if (!provenance) throw Error(`${wanted} is not a token program sememe pins`);
|
|
430
|
+
console.log(JSON.stringify(provenance, null, 2));
|
|
431
|
+
} else {
|
|
432
|
+
console.log(JSON.stringify(tokenPrograms().map((id) => tokenProvenance(id)), null, 2));
|
|
433
|
+
}
|
|
434
|
+
} else if (command === 'raydium-provenance') {
|
|
435
|
+
const wanted = program;
|
|
436
|
+
if (wanted) {
|
|
437
|
+
const provenance = raydiumProvenance(wanted);
|
|
438
|
+
if (!provenance) throw Error(`${wanted} is not a Raydium program sememe pins`);
|
|
439
|
+
console.log(JSON.stringify(provenance, null, 2));
|
|
440
|
+
} else {
|
|
441
|
+
console.log(JSON.stringify(raydiumPrograms().map((id) => raydiumProvenance(id)), null, 2));
|
|
442
|
+
}
|
|
443
|
+
} else if (command === 'decode-raydium') {
|
|
444
|
+
if (!program) throw Error('decode-raydium needs a program id');
|
|
445
|
+
const payload = args.shift();
|
|
446
|
+
if (!payload) throw Error('decode-raydium needs an instruction payload');
|
|
447
|
+
const trailing = option('--trailing', 'report');
|
|
448
|
+
if (trailing !== 'report' && trailing !== 'error') {
|
|
449
|
+
throw Error('--trailing must be "report" or "error"');
|
|
450
|
+
}
|
|
451
|
+
const decoded = decodeRaydiumInstruction(program, parsePayload(payload), { trailing });
|
|
452
|
+
if (decoded.refused === 'not-raydium') {
|
|
453
|
+
throw Error(`${program} is not a Raydium program sememe pins — try decode-native, decode-token or decode`);
|
|
454
|
+
}
|
|
455
|
+
console.log(JSON.stringify({
|
|
456
|
+
program,
|
|
457
|
+
kind: 'raydium-instruction',
|
|
458
|
+
name: decoded.name,
|
|
459
|
+
tag: decoded.tag,
|
|
460
|
+
selectorWidth: decoded.selectorWidth,
|
|
461
|
+
args: toJsonSafe(decoded.args),
|
|
462
|
+
absent: decoded.absent,
|
|
463
|
+
bytes: decoded.bytes,
|
|
464
|
+
consumed: decoded.consumed,
|
|
465
|
+
trailing: decoded.trailing,
|
|
466
|
+
roundTripByteExact: decoded.roundTripByteExact,
|
|
467
|
+
interface: raydiumProvenance(program),
|
|
468
|
+
}, null, 2));
|
|
469
|
+
} else if (command === 'decode-ata') {
|
|
470
|
+
if (!program) throw Error('decode-ata needs a program id');
|
|
471
|
+
const trailing = option('--trailing', 'report');
|
|
472
|
+
if (trailing !== 'report' && trailing !== 'error') {
|
|
473
|
+
throw Error('--trailing must be "report" or "error"');
|
|
474
|
+
}
|
|
475
|
+
// The empty payload is a REAL instruction for this program: the pin's
|
|
476
|
+
// processor executes empty input as Create. It therefore needs a way to
|
|
477
|
+
// be said out loud, and `--empty` is that way — the base58/hex parsers
|
|
478
|
+
// have no empty string, by design.
|
|
479
|
+
const empty = flags['--empty'] === true;
|
|
480
|
+
const payloadArg = args.shift();
|
|
481
|
+
if (!payloadArg && !empty) {
|
|
482
|
+
throw Error('decode-ata needs an instruction payload (use --empty for the empty-input Create fallback)');
|
|
483
|
+
}
|
|
484
|
+
if (payloadArg && empty) {
|
|
485
|
+
throw Error('decode-ata: give a payload or --empty, not both');
|
|
486
|
+
}
|
|
487
|
+
const decoded = decodeAtaInstruction(program, empty ? Buffer.alloc(0) : parsePayload(payloadArg), { trailing });
|
|
488
|
+
if (decoded.refused === 'not-ata') {
|
|
489
|
+
throw Error(`${program} is not a program sememe pins with the ATA table — try decode-native, decode-token, decode-raydium or decode`);
|
|
490
|
+
}
|
|
491
|
+
console.log(JSON.stringify({
|
|
492
|
+
program,
|
|
493
|
+
kind: 'ata-instruction',
|
|
494
|
+
name: decoded.name,
|
|
495
|
+
tag: decoded.tag,
|
|
496
|
+
selectorWidth: decoded.selectorWidth,
|
|
497
|
+
args: {},
|
|
498
|
+
bytes: decoded.bytes,
|
|
499
|
+
consumed: decoded.consumed,
|
|
500
|
+
trailing: decoded.trailing,
|
|
501
|
+
roundTripByteExact: decoded.roundTripByteExact,
|
|
502
|
+
note: decoded.note,
|
|
503
|
+
interface: ataProvenance(program),
|
|
504
|
+
}, null, 2));
|
|
505
|
+
} else if (command === 'ata-provenance') {
|
|
506
|
+
const provenance = program ? ataProvenance(program) : null;
|
|
507
|
+
if (program && !provenance) {
|
|
508
|
+
throw Error(`${program} is not a program sememe pins with the ATA table — try decode-native, decode-token, decode-raydium or decode`);
|
|
509
|
+
}
|
|
510
|
+
console.log(JSON.stringify(provenance ?? ataPrograms().map((id) => ataProvenance(id)), null, 2));
|
|
511
|
+
} else if (command === 'account') {
|
|
512
|
+
if (!program) throw Error('account needs a program id');
|
|
513
|
+
const data = args.shift();
|
|
514
|
+
if (!data) throw Error('account needs the account data (base64 or hex)');
|
|
515
|
+
const store = option('--store', path.join(os.homedir(), '.sememe'));
|
|
516
|
+
const name = option('--name', undefined);
|
|
517
|
+
const trailing = option('--trailing', undefined);
|
|
518
|
+
if (trailing !== undefined && trailing !== 'report' && trailing !== 'error') {
|
|
519
|
+
throw Error('--trailing must be "report" or "error"');
|
|
520
|
+
}
|
|
521
|
+
// The union path reads BOTH verified store sources for the program (the
|
|
522
|
+
// on-chain pull and the adopted published copy) and arbitrates by byte
|
|
523
|
+
// evidence, exactly like `sememe decode --union`. Without it the account
|
|
524
|
+
// path read ONE receipt, so a store holding two copies of a program had
|
|
525
|
+
// two opinions about an account's layout and only one was consulted.
|
|
526
|
+
const decoded = unionRequested
|
|
527
|
+
? await decodeUnionStoredAccount(store, program, data,
|
|
528
|
+
name === undefined ? { trailing } : { name, trailing })
|
|
529
|
+
: await decodeStoredAccount(store, program, data,
|
|
530
|
+
name === undefined ? { trailing } : { name, trailing });
|
|
531
|
+
console.log(JSON.stringify(decoded, null, 2));
|
|
532
|
+
if (decoded.trailing.count > 0 && trailing !== 'error') {
|
|
533
|
+
console.error(JSON.stringify({
|
|
534
|
+
note: `${decoded.trailing.count} byte(s) after the described struct are not described by the stored IDL`,
|
|
535
|
+
consumed: decoded.consumed,
|
|
536
|
+
bytes: decoded.bytes,
|
|
537
|
+
trailing: decoded.trailing,
|
|
538
|
+
}));
|
|
539
|
+
}
|
|
540
|
+
} else {
|
|
541
|
+
console.error(usage);
|
|
542
|
+
process.exitCode = 2;
|
|
543
|
+
}
|
|
544
|
+
} catch (error) {
|
|
545
|
+
if (error instanceof UsageError) {
|
|
546
|
+
// A malformed command line is refused BEFORE any work, and it is a usage
|
|
547
|
+
// problem, not a failure of the work: plain text on stderr — the same
|
|
548
|
+
// channel the unknown-command usage dump uses — and exit 2. The machine
|
|
549
|
+
// readable JSON error channel stays for errors the work itself produced.
|
|
550
|
+
console.error(error.message);
|
|
551
|
+
process.exitCode = 2;
|
|
552
|
+
} else {
|
|
553
|
+
console.error(JSON.stringify({ error: String(error?.message ?? error) }));
|
|
554
|
+
process.exitCode = 1;
|
|
555
|
+
}
|
|
556
|
+
}
|
package/index.mjs
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// sememe's package entry: the offline decode surface, and nothing else.
|
|
2
|
+
//
|
|
3
|
+
// The split is deliberate and enforced (test/package.test.mjs,
|
|
4
|
+
// test/deps.test.mjs):
|
|
5
|
+
//
|
|
6
|
+
// the entry ("sememe")
|
|
7
|
+
// - decodes instructions, accounts and event self-CPIs from a local,
|
|
8
|
+
// fingerprint-verified store; re-checks the receipt on every read;
|
|
9
|
+
// - imports only node: builtins and files inside this package, so a
|
|
10
|
+
// consumer installs ZERO runtime dependencies;
|
|
11
|
+
// - never touches the network, the CLI, or the qualification harness.
|
|
12
|
+
//
|
|
13
|
+
// the acquisition subpath ("sememe/pull")
|
|
14
|
+
// - acquisition: finds a program's IDL account on chain and writes the
|
|
15
|
+
// store. Needs @solana/web3.js (declared as an optional peer) and a
|
|
16
|
+
// network. A decoder-only consumer never loads it.
|
|
17
|
+
//
|
|
18
|
+
// A package that exports its harness is a harness. This file exists so the
|
|
19
|
+
// consumer's surface is a list you can read in one screen.
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
decodePayload,
|
|
23
|
+
decodeUnionPayload,
|
|
24
|
+
decodeStoredAccount,
|
|
25
|
+
decodeUnionStoredAccount,
|
|
26
|
+
findStoredIdl,
|
|
27
|
+
findStoredIdls,
|
|
28
|
+
adopt,
|
|
29
|
+
parsePayload,
|
|
30
|
+
decodeHex,
|
|
31
|
+
decodeBase58,
|
|
32
|
+
unionDecode,
|
|
33
|
+
unionAccountDecode,
|
|
34
|
+
UnionRefusal,
|
|
35
|
+
verifiedShortForm,
|
|
36
|
+
VERIFIED_SHORT_FORMS,
|
|
37
|
+
} from './src/decode.mjs';
|
|
38
|
+
|
|
39
|
+
export { encodeBase58 } from './src/base58.mjs';
|
|
40
|
+
export { canonicalJson } from './src/canonical.mjs';
|
|
41
|
+
// The native programs (System, ComputeBudget) are NOT re-exported here on
|
|
42
|
+
// purpose: src/native.mjs derives its wire table from the pinned upstream
|
|
43
|
+
// sources AT IMPORT TIME and refuses to load when the committed artifact
|
|
44
|
+
// disagrees. That loud failure is right for the native decoder, but it must
|
|
45
|
+
// not take the whole entry down for a consumer who only decodes an Anchor
|
|
46
|
+
// instruction. Use `sememe/native` for those two programs. The same applies
|
|
47
|
+
// to the two token programs (SPL Token, Token-2022) — `sememe/token` derives
|
|
48
|
+
// their wire tables from the pinned upstream Rust crates the same way.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"program": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL",
|
|
3
|
+
"label": "SPL Associated Token Account",
|
|
4
|
+
"scheme": "native-ata",
|
|
5
|
+
"source": {
|
|
6
|
+
"repo": "solana-program/associated-token-account",
|
|
7
|
+
"pin": "fbf5379e7398ac8b231c73ee8a9ff677b43dd0ec",
|
|
8
|
+
"license": "Apache-2.0 (LICENSE vendored beside the sources)",
|
|
9
|
+
"enum": "AssociatedTokenAccountInstruction (borsh unit enum: the variant index is the one-byte payload)",
|
|
10
|
+
"programSource": "vendored interface/src/lib.rs declare_id",
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"file": "interface/src/instruction.rs",
|
|
14
|
+
"role": "AssociatedTokenAccountInstruction: borsh unit enum, serialise arms carry the wire byte",
|
|
15
|
+
"sha256": "sha256:033191cb1316fe2370fb9f694b0a0f9f140f647ca44661f07cb661ed4aa8c56d"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"file": "program/src/processor.rs",
|
|
19
|
+
"role": "runtime verdict: empty input falls back to Create, borsh try_from_slice judges the rest, InvalidInstructionData refuses",
|
|
20
|
+
"sha256": "sha256:e8bf0dd2d9ac87ebc488faf73dee2530df6d6dc83bc19907def973c5209953ca"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"file": "interface/src/lib.rs",
|
|
24
|
+
"role": "the program id (declare_id), read from upstream, not re-typed",
|
|
25
|
+
"sha256": "sha256:a1bf5de71e01902c9a5b42604ba4eb696c7ef4c100bc6c72a0b59a926f4efad0"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"vendoredAt": "vendor/spl-associated-token-account/fbf5379e7398ac8b231c73ee8a9ff677b43dd0ec",
|
|
29
|
+
"derivation": "src/ata-interface.mjs: tag = the declaration index upstream's borsh serialise arm pushes; args = none (unit enum); runtime verdicts read from the processor (empty input falls back to Create, borsh try_from_slice judges, InvalidInstructionData refuses)"
|
|
30
|
+
},
|
|
31
|
+
"runtime": {
|
|
32
|
+
"judge": "borsh AssociatedTokenAccountInstruction::try_from_slice(input)",
|
|
33
|
+
"emptyInputFallback": "Create",
|
|
34
|
+
"unknownInputRefusal": "ProgramError::InvalidInstructionData",
|
|
35
|
+
"source": "program/src/processor.rs at the pin"
|
|
36
|
+
},
|
|
37
|
+
"selector": {
|
|
38
|
+
"width": 1,
|
|
39
|
+
"encoding": "borsh enum index, u8; empty input is the processor's own Create fallback, not an unknown selector"
|
|
40
|
+
},
|
|
41
|
+
"argEncoding": "unit enum: payload is exactly the one index byte; no argument bytes",
|
|
42
|
+
"instructions": [
|
|
43
|
+
{
|
|
44
|
+
"tag": 0,
|
|
45
|
+
"name": "Create",
|
|
46
|
+
"wire": "borsh-unit-enum",
|
|
47
|
+
"args": []
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"tag": 1,
|
|
51
|
+
"name": "CreateIdempotent",
|
|
52
|
+
"wire": "borsh-unit-enum",
|
|
53
|
+
"args": []
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"tag": 2,
|
|
57
|
+
"name": "RecoverNested",
|
|
58
|
+
"wire": "borsh-unit-enum",
|
|
59
|
+
"args": []
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|