Features
June 2, 2026 · View on GitHub
A catalogue of what Web3 Decoder does, with pointers to where each capability is implemented. For how they fit together see ARCHITECTURE.md.
Feature map
mindmap
root((Web3 Decoder))
Burp integration
Web3 Request tab
Web3 Response tab
Web3 suite tab
Proxy history annotations
Decoded editor
Structured tree (default)
JSON view
Summary chips
Copy + explorer links
Decoding
eth_call calldata
JSON-RPC results
Single + batch
Multicall recursion
ABI resolution
Local cache
Bundled ABIs
Etherscan v2 + legacy
Passive detection
4byte fallback
Proxy awareness
ERC1967
ZeppelinOS legacy
Editing
Re-encode args
Standalone codec
Management UI
Chains + API keys
Cached ABIs
Detected ABIs
ABI editor
1. Burp integration
Three surfaces are added to Burp:
- Web3 Request editor tab — decoded view of JSON-RPC
eth_callcalldata, editable. - Web3 Response editor tab — decoded view of the matching JSON-RPC
result. - Web3 suite tab — a dashboard for chains, API keys, cached ABIs, and a standalone calldata codec.
Implemented in: Web3DecoderExtension, Web3EditorTabProvider, Web3RequestEditor,
Web3ResponseEditor, ui/MainChainsPanel.
The editor tabs only enable themselves for relevant traffic: the request tab requires
an eth_call or eth_sendRawTransaction entry (single or anywhere in a batch), and
the response tab requires a decodable eth_call request and a result in the
response (isEnabledFor in each editor) — irrelevant JSON-RPC traffic doesn't get a
disabled stub tab.
Proxy history annotations
A Burp HttpHandler (Web3HistoryAnnotator) tags every Proxy history row whose request
carries an eth_call / eth_sendRawTransaction with the decoded function signature, so
the row note reads e.g. transfer(address,uint256) → 0xa0b…6c2 without opening the
editor. The handler returns immediately; the decode (which may hit the network for chain
id, ABI, 4byte, or proxy slots) runs on a background executor and mutates the live
history row's Annotations once ready — the same path Burp uses for hand-edited notes,
so the annotation persists. Results are LRU-cached so identical requests don't re-decode.
Implemented in: annotation/Web3HistoryAnnotator, annotation/AnnotationNoteBuilder.
2. JSON-RPC request decoding
- Decodes
eth_callparams[0].datainto afunctionsignature plus typedargs, resolving the target fromparams[0].to. - Handles single JSON-RPC objects and batch arrays in one pass.
- Each batch entry reports a
status—decoded,skipped, orerror— with areasonwhen it isn't decoded (e.g. "Method is not eth_call."). - Output includes the resolved
decodeSource(cache/builtin/etherscan/detected/4byte) and proxy metadata when applicable.
Implemented in: Web3RequestContextDecoder.decodeAndStore, Web3DataDecoder.decodeInput,
ABIInputDecoder.decodeInput.
Supported Solidity types
ABIInputDecoder maps the full common type surface to web3j types:
address,bool,stringuint8…uint256,int8…int256(8/16/32/64/128/256), with bareuint/int→ 256bytes, and fixedbytes1…bytes32- dynamic (
T[]) and fixed (T[n]) arrays, including arrays of tuples - nested structs/tuples (static and dynamic), rendered as ordered, named maps when component names are present (positional lists otherwise)
3. JSON-RPC response decoding
- Decodes the
resultfor a previously decodedeth_call, using the function/ABI recovered from the request viaRequestContextStore. - Supports single and batch responses, matching entries to their request by JSON-RPC
idfirst, then by positional index, then a legacy single-entry fallback. - Self-sufficient after one send: if the Web3 Request tab was never opened, the response editor decodes the request on demand (off the EDT) to rebuild the context.
Implemented in: Web3ResponseEditor.decodeResponseAsync / decodeBatchResponse /
resolveContextWithKey, ABIInputDecoder.decodeOutput.
4. Re-encoding & edit workflow
- Edit the decoded
argsin the Web3 Request tab and, on send, the extension re-encodes them into valid calldata and writes it back toparams[0].data. - Works for single requests and per-entry across batches (only
decodedentries are re-encoded). - The standalone Calldata Codec (Web3 suite tab) decodes and re-encodes calldata outside of request history, given a chain id + contract address.
Implemented in: Web3RequestEditor.getRequest → applySingleEdit / applyBatchEdits,
ABIInputDecoder.encodeInput, ui/CalldataCodecPanel.
5. Decoded editor experience
Both editor tabs render through a shared DecodedEditorPanel that wraps the JSON editor
with a top control bar and a collapsible structured tree:
- Structured view (default) — read-only JSON→tree projection. The function
signature is the bold disclosure header;
decodeSourceandstatusappear as tinted chip badges; addresses get a copy popup and an↗link to the chain's block explorer; Multicallcalls/nestedCallsrender as nested call nodes that carry their own function + source + status chips. Long hex (callData,rawCallData,result,returnData) renders as a soft-wrapping row with a byte-count suffix and is preview-by-default (truncated with a click-to-expand toggle); largeargstrees auto-collapse so the surface stays scannable. - JSON view — the existing editable Web3 JSON editor (syntax + semantic highlighting, line numbers, search bar, bracket matcher, live validity, soft-wrap for long hex). Toggling between cards re-parses from the JSON text, so edits made on the JSON side always show up structured; the JSON view is the single source of truth for re-encoding.
- Top control bar — left-side summary chips (function signature + abbreviated
toaddress), proxy chip when applicable, Structured/JSON segmented toggle, Expand all / Collapse all (Structured only), a copy button, and a block-explorer link for the target address. Theme-aware (FlatLaf light/dark) and width-aware (the controls cluster wraps above the summary when the editor is too narrow).
Fields that are noise once the structure is rendered (depth, allowFailure) are
hidden from the body — they're already implicit in the tree position or the call-node
chip.
Implemented in: ui/editor/DecodedEditorPanel, ui/editor/StructuredDecodedView,
ui/editor/Web3JsonEditor, ui/editor/EditorTheme, ui/editor/BlockExplorer.
6. ABI resolution & caching
Resolution priority (see the flowchart in ARCHITECTURE.md):
- Local cache (
CachedAbiProvider→CachedAbis). - Bundled ABIs (
BuiltinAbiProvider) — e.g. Multicall3 at its canonical cross-chain address. - Block explorer (
EtherscanAbiProvider) — Etherscan v2 multichain endpoint (api.etherscan.io/v2/api?chainid=…), falling back to the chain's legacy/apihost when v2 doesn't support the chain. Explorer hits are cached automatically. - Detected ABI pool (
DetectedAbiStoreviaWeb3DataDecoder) — see §7. - 4byte fallback — see §8.
Steps 1–3 are chain-scoped; steps 4–5 are chain-agnostic. When eth_chainId can't be
determined, Web3RequestContextDecoder builds a decoder with no AbiProvider and goes
straight to steps 4–5 rather than refusing to decode.
The source that produced an ABI is tracked and surfaced as decodeSource.
Implemented in: abi/AbiProvider and its IAbiProvider implementations.
7. Passive ABI detection
A Burp PassiveScanCheck (AbiDetectionScanCheck, registered as
ScanCheckType.PER_REQUEST) inspects every audited HTTP response body and extracts
embedded Solidity ABIs. The pipeline has four stages, each unit-tested in isolation:
- Token scan — fixed-needle search for
stateMutability:"/internalType:"(plus the strict-JSON"stateMutability":"/"internalType":"shapes) returns candidate offsets. No regex, O(N) per body. - Boundary extraction — string-aware, depth-tracking walk left from each hit to the
outermost enclosing
[{...}]array. Walks past inner sub-arrays (inputs:[{...}],components:[{...}]) using bracket-depth so we don't lock onto them by accident. - Normalization — Jackson lenient parse (
ALLOW_UNQUOTED_FIELD_NAMES,ALLOW_SINGLE_QUOTES,ALLOW_TRAILING_COMMA) with a preprocess pass that translates minified!0/!1to JSONtrue/false. Re-serialises with sorted keys so two logically-equal ABIs produce byte-identical canonical strings. - Validation — array of objects, every entry has a
typefrom the closed vocabulary (function|constructor|event|fallback|receive|error), and forfunctionentries the 4-byte selector is computed viaAbiSelectorBuilder. The canonical JSON's SHA-256 becomes the fingerprint used for dedup.
Validated ABIs are persisted in DetectedAbiStore (under extensionData(), so the
pool is project-scoped like CachedAbis). The store keeps an in-memory
selector → fingerprint index for O(1) decode-time lookups, enforces a 1024-entry
capacity cap with FIFO eviction, and exposes snapshot() / remove(fingerprint) for the
UI to read and edit.
When a response introduces at least one previously-unknown ABI, an informational
audit issue is raised — "Solidity contract ABI detected" — whose detail lists every
function with its canonical signature and 4-byte selector, plus the events and errors:
ABI 1 — 24 functions, 7 events, 3 errors
Fingerprint: a1b2c3d4e5f6…
Functions
balanceOf(address) → 0x70a08231
transfer(address,uint256) → 0xa9059cbb
...
The decoder consults the pool after the chain-scoped providers (cache → builtin →
etherscan) and before the 4byte fallback. Hits are tagged decodeSource: "detected".
Implemented in: detection/AbiDetectionScanCheck, detection/AbiDetectionPipeline,
detection/AbiTokenScanner, detection/AbiBoundaryExtractor, detection/AbiNormalizer,
detection/AbiValidator, detection/DetectedAbiStore, detection/AbiSelectorBuilder.
8. 4byte fallback decoding
When no ABI can be found, the selector is looked up against
api.4byte.sourcify.dev. Candidate signatures are ordered with
verified-contract matches first, a synthetic ABI is generated from each candidate
(including nested tuple parsing), and decode/re-encode proceed against it.
Implemented in: abi/FourByteSignatureProvider, Web3DataDecoder.decodeWithFourByte.
9. Proxy-aware decoding
For delegating proxies, the extension reads the implementation address from a known
storage slot via eth_getStorageAt and decodes against the implementation's ABI
(falling back to an ABI cached under the proxy address if the implementation's is
unavailable). Results are cached per rpcUrl|address.
Supported detectors:
- ERC-1967 implementation slot (
0x360894…bbc). - Legacy ZeppelinOS / OpenZeppelin
UpgradeabilityProxyslot (0x7050c9…8c3).
Decoded output is annotated with isProxy, proxyTarget, implementationAddress, and
decodedUsing.
Implemented in: proxies/ProxyDetector, ERC1967ProxyDetector,
ZeppelinOSProxyDetector; consumed by Web3DataDecoder.
10. Multicall recursive decoding
Recognizes the six common Multicall3 aggregate variants and recursively decodes the inner calls they bundle:
aggregate, tryAggregate, aggregate3, aggregate3Value, blockAndAggregate,
tryBlockAndAggregate.
- Each nested call is decoded to its own function/args, with per-node
status, adepthfield, andallowFailure/valuewhere the variant carries them. - Recursion is depth-limited (max depth 3) to bound runaway nesting.
- On the response side, each inner
returnDatais decoded against the per-call context stored during request decoding — including nested multicalls — so aggregated results are fully expanded.
Implemented in: decoder/MulticallRecursiveDecoder,
Web3ResponseEditor.enrichMulticallReturnData.
11. Chain & explorer management UI
- Add / edit / remove chain definitions (chain id, name, explorer host).
- Per-chain explorer API key, plus a shared Etherscan-family key used when a chain has no explicit key (color-coded status: Set / Overridden / Not Set / N/A).
- Default chains load from
chains.jsonand customizations persist in Burp Preferences. - Deprecated chain migration: persisted legacy ids are remapped to current ones
(e.g. Ropsten/Rinkeby/Goerli/Kovan
3/4/5/42 → 11155111Sepolia;17000 → 560048).
Implemented in: ui/ChainsListPanel, persistence/ExtensionConfig,
persistence/ChainInfo.
12. Cached & Detected ABI management UI
The bottom-left slot of the Web3 suite tab is split into two side-by-side lists.
Cached ABIs (left):
- Add a cached ABI manually (paste JSON, validated) or fetch it from the configured explorer (async, off-EDT).
- Remove cached entries.
- Open a cached ABI in the editor, validate it, and save updates back to the cache.
Detected ABIs (right):
- Every entry the passive scanner has accumulated, with a short fingerprint, function count, and the URL the ABI was first seen at.
- Click a row to view the ABI JSON read-only in the shared ABI editor. The editor header
reads
Detected ABI: <short-fingerprint> (from <first-seen-url>)and the Save button stays disabled — detected ABIs aren't tied to a chain+address pair. - Remove unwanted entries from the pool (e.g. obvious false positives).
Implemented in: ui/CachedAbisListPanel, ui/DetectedAbisListPanel,
ui/AbiEditorPanel (setAbi / setDetectedAbi), persistence/CachedAbis,
detection/DetectedAbiStore.
13. Standalone Calldata Codec panel
- Decode calldata given chain id + contract address context.
- Optional RPC URL enables proxy-aware decoding in the standalone tool.
- Renders nested multicall decode details, and re-encodes edited JSON back to calldata.
Implemented in: ui/CalldataCodecPanel.
14. Raw transaction decoding (eth_sendRawTransaction)
Decodes the single signed-transaction hex carried in eth_sendRawTransaction
params[0]. The Web3 Request tab shows a transaction envelope — txType
(legacy / eip1559 / eip2930 / eip4844 / eip7702), embedded chainId, the
EC-recovered from (best-effort), to, nonce, value, and the type-appropriate
gas fields — alongside the decoded inner function and args, which run through the
same ABI pipeline as eth_call (proxy detection, 4byte fallback, multicall recursion).
- The chain id embedded in the signed transaction is authoritative and is used to
pick the ABI source, so decoding works in Repeater without an
eth_chainIdprobe. Only pre-EIP-155 legacy transactions (which carry no chain id) fall back to the probe. - Contract-creation (
toabsent) and plain value transfers (no calldata) render the envelope only. - Read-only: a signed transaction is not re-encoded on send (re-signing is future work).
- Single and batch requests are supported, mixed freely with
eth_callentries.
Implemented in: decoder/RawTransactionDecoder,
Web3RequestContextDecoder.decodeRawTransactionInto, Web3RequestEditor.