A failed transaction gave me a hex string starting with 0x38ed1739 and nothing else. Here's how I actually read it.
I was staring at a reverted transaction on Etherscan, and the Input Data field was just raw calldata — no obvious way to tell what it was actually trying to do. Etherscan decodes it automatically only if the contract is verified and it recognizes the function; for an unverified contract, or when I only have a guess at the signature, I need a tool that decodes against a signature I type myself, not one it has to already know about. That's the whole reason the ABI Decoder/Encoder exists: paste calldata, type the signature you think matches, see if you're right.
Type a signature like transfer(address,uint256) and the tool computes the real 4-byte function selector — the first 4 bytes of the keccak256 hash of that signature, using ethers.js's own id() function, not a guess. It checks that against the first 4 bytes of whatever calldata you pasted. Get the signature wrong — wrong parameter order, wrong type, a typo — and you get a mismatch badge instead of a confident-looking but garbage decode. I once had the parameter order backwards on a signature typed from memory; without the selector check I'd have decoded 3 parameters that looked plausible and moved on, wrong.
Arrays and tuples, not just the easy types
A lot of quick ABI tools handle a 256-bit uint256 and a 20-byte address and give up past that. This one decodes address[] arrays, nested tuples written as (type1,type2), strings, and bytes — anything Solidity's ABI encoding actually supports, each value still padded to a 32-byte word underneath. Each decoded parameter shows up as 4 things in its own row: an index, its type, the value, a copy button. The built-in swap example — real calldata starting 0x38ed1739 — uses a genuine Uniswap Router swapExactTokensForTokens call on purpose, because it has an address[] array buried in the middle of its 5 parameters, exactly the case where a simpler decoder gives up.

Encoding is the trip back
Switch to Encode mode and the signature you typed turns into a form — one input per parameter, type-aware. Arrays take comma-separated values. Tuples take a JSON array. Fill it in and it builds real calldata, selector included, ready to paste into a script or a raw transaction. I use this after decoding something I want to replay with one value changed: decode the original, tweak a parameter, re-encode, done. No second tool, no throwaway ethers.js script of my own.

Nothing you paste leaves your browser
Both directions run entirely client-side through ethers.js. No server round-trip. No logging of which contracts or addresses you've been inspecting. That matters more than it sounds like it should — decoding calldata from a contract you're auditing, or a transaction you're debugging before it's public, isn't something you want sitting in a stranger's server logs. 3 examples are built in to try immediately: a plain transfer (0xa9059cbb), an approve (0x095ea7b3), and that Uniswap swap (0x38ed1739) with the array parameter. Decode any of them first, see both the simple and dynamic-type paths work, then trust it with your own calldata.
