EIP-712 Inspector: what am I signing?
A typed-data signature can hand over your tokens or NFTs without a transaction — that is how most wallet-draining phishing works. Paste the eth_signTypedData_v4 request before you sign: the inspector recognises the format, says what you would be authorising, flags what is dangerous and shows every EIP-712 hash, computed locally.
The typed data stays in this browser. Only when you press “Check on chain” are the spender and operator addresses sent to a public RPC node of the domain’s chain (listed next to the button), to ask whether they are contracts.
A signature can be as good as a transaction
EIP-712 (the specification) lets a site ask a wallet to sign structured data instead of an opaque hash: a primary type such as Permit, the types it is built from, a domain that binds the signature to one application, chain and contract, and the message itself. The wallet shows the fields, the user signs, and no transaction is sent — which is why typed-data signatures feel harmless.
They are not. A contract that knows the format can accept the signature later, from anyone, and act on it. An ERC-2612 permit sets an allowance on a token; a Uniswap Permit2 signature does the same for every token approved to Permit2; a Seaport order lists your NFTs for sale at the price you signed. Phishing sites rely on exactly that: a "login" or "claim" that is really an unlimited allowance to the attacker, or a listing of your NFTs for nothing. Once signed, the attacker submits it whenever they like and pays the gas themselves.
What the inspector recognises
It matches the exact type strings of the contracts that consume them, not just their names:
- token permits: ERC-2612 (USDC, most modern tokens), the DAI-style permit that approves everything or nothing, and NFT permits (ERC-4494, such as Uniswap V3 positions);
- Uniswap Permit2: single and batch allowances, one-time signature transfers, and transfers bound to a witness order (UniswapX and similar protocols);
- marketplace orders: Seaport (OpenSea) single and bulk orders, Blur orders and bulk roots, LooksRare maker orders;
- CoW Protocol swap orders;
- gasless operator approvals, which let an operator move every item of a collection.
For each it writes one sentence: who could spend what, until when. Anything it does not recognise is shown field by field, with a scan for maximal values, time fields and spender-like addresses.
What the warnings mean
- Unlimited or effectively unlimited allowance — the spender can take the whole balance, including tokens received later, until you revoke it.
- The signer receives nothing, or dust — a Seaport, Blur or LooksRare order that gives your items away. Real listings pay you.
- Not the genuine contract — the domain names a Permit2, Seaport or CoW address that is not the deployed one. The list of genuine addresses, with their EIP-712 domains, was read from the chains and is part of the page.
- Look-alike token — the domain calls itself "USD Coin" but the contract is not USDC on that chain.
- Chain mismatch — the domain's
chainIdis not the chain your wallet is on. A legitimate site asks you to sign for the chain you use; signing for another one is a classic trick. A domain withoutchainIdis valid everywhere (EIP-155 is the same idea for transactions). - Never expires, or years away — a deadline that keeps the signature usable long after you forgot it.
- Unknown spender — an address that is not a router, marketplace or protocol contract we know. The on-chain check goes one step further: it asks the chain whether each spender has contract code. An approval to a plain account is how drainers collect funds.
The page also lists members that are present in the message but not in its type: wallets may display them, but they are not signed.
Every hash, as your wallet computes it
The digest a wallet signs is keccak256(0x19 0x01 ‖ domainSeparator ‖ hashStruct(message)). The inspector shows each step: the encodeType string of every struct, its type hash, the 32-byte words of encodeData, the domain separator and the message's struct hash. Where the specification is silent — a missing struct member, a short bytesN value — it follows MetaMask's signTypedData_v4, because that is the digest people actually sign, and it says so in the notes. A hardware wallet that shows "domain hash" and "message hash" can be compared with these values; the Ethereum signature verifier recovers who signed a given digest.
What runs in your browser
Parsing, hashing, recognition and every check run in this page. Pasting a request never sends it anywhere. Wallet secrets are refused before anything else: a seed phrase, an extended private key or a private key labelled as one stops the inspector with an explanation, and nothing is processed.
The only request the page can make is the optional on-chain check. When you press "Check on chain", the spender and operator addresses — and nothing else — are sent as eth_getCode calls to a public RPC node of the chain named in the domain; the button shows which node, and another public node of the same chain is tried if the first does not answer.