Every wallet approval screen asks you to trust a hex blob. Block explorers decode single calls but fall apart on Safe multisig batches — the exact transactions where the stakes are highest and a hidden inner call does the most damage.
Callsign is built for the person about to sign. Paste the calldata or the transaction hash; it tells you the function, every argument with its type, and — when the payload is a `multiSend` — every sub-transaction inside it, each decoded in turn. It runs entirely in the browser. Nothing is uploaded, no wallet is connected, and the only state kept is your last twelve lookups in localStorage.


ENGINEERING CHALLENGES
A hand-written Safe multiSend byte-walker
Safe packs its batch transactions in a non-ABI encoding: one byte of operation, 20 bytes of target, 32 bytes of value, 32 bytes of data length, then the payload — repeated, with no framing. parseMultiSendTxs walks the buffer by hand and refuses to slice past its end (`if (dataLength * 2 > clean.length - offset) break`) rather than throw on a truncated paste. Each sub-transaction is then decoded on its own, recursing one level deep behind a depth guard so a batch that contains a batch cannot spin forever.
Selector disambiguation without guessing
A 4-byte selector is not unique — 4byte.directory returns many signatures for a popular one. Their row IDs are monotonic, so the earliest submission is almost always the canonical signature and the spam collides in later. Callsign sorts candidates ascending by ID, flags the top pick LIKELY, and when it is genuinely ambiguous it shows a picker instead of silently choosing.
Promise-memoised resolution
The selector cache is a Map<string, Promise<…>> — it stores the in-flight promise, not just the resolved value. A multiSend batch with twenty identical inner selectors therefore issues one network request, not twenty. The entry is deleted on rejection so a transient failure does not poison the cache.
Classifying the input by arithmetic
Valid ABI calldata is a 4-byte selector plus some number of 32-byte words, so its hex length is always ≡ 8 (mod 64). A 64-character hex string can never satisfy that, so it is unambiguously a transaction hash — no heuristic needed. Explorer URLs are parsed for both the hash and, by reverse-mapping the host, the chain. Odd-length, non-hex, and too-short inputs each get their own specific error.
AUTO chain detection that tells you why it failed
Given a bare hash, detectTx fans out eth_getTransactionByHash across all six chains in parallel and takes the first hit. It distinguishes "this hash exists on no chain" from "no RPC endpoint was reachable" by tracking whether any probe actually completed — so a network problem never masquerades as a not-found.
STACK
OUTCOME
- ▸Ships as a static client app on Vercel — the only server code is three caching proxy routes.
- ▸Share links carry the decode: `?data=` for calldata, `?tx=&chain=` for a lookup, replayed from storage without re-hitting an RPC.
- ▸The through-line: it always shows how it read your input and flags ambiguity rather than pretending to be certain.
Open to audits, contract work, and security writing.
Competitive & private audits · Secure Web3 contract work · Security writing. If you have a protocol going to production, or one that already is — let's talk.