SwapChan
Never holds your keys · your wallet signs
blocktools CLI

EVM: what a Permit2 signing request commits to, verified offline

Task: a dApp (here Uniswap) asks your wallet to sign Permit2 typed data. See exactly what the signature authorises and check who signed it, with no…

⚠️ DANGEROUS: use at your own risk. Code that handles keys and signs transactions should always be read and audited by you before you trust it with real funds. Used incorrectly, it can destroy your money.

Task: a dApp (here Uniswap) asks your wallet to sign Permit2 typed data. See exactly what the signature authorises and check who signed it, with no network. Part of the EVM contract recipes.

fixtures/evm/permit2-single-02a.json is a real request: the PermitSingle and signature carried inside the mainnet Universal Router tx 02a (decoded in evm-decode-multicall.md); provenance in fixtures/evm/PROVENANCE.md.

What it commits to

  • domain: Permit2 on chain 1, contract 0x000000000022D473030F116dDEE9F6B43aC78BA3. A different chain id or contract is a different digest.

  • details.token, details.amount: which token and how much the spender may pull (uint160; the largest uint160 means unlimited).

  • details.expiration: when that allowance ends (unix seconds); details.nonce: replay protection.

  • spender: who may pull (here the Universal Router); sigDeadline: until when this signature can be used.

    jq -c '.typed_data | {chain: .domain.chainId, contract: .domain.verifyingContract, token: .message.details.token, amount: .message.details.amount, expiration: .message.details.expiration, spender: .message.spender, sigDeadline: .message.sigDeadline}' recipes/fixtures/evm/permit2-single-02a.json

Who signed it

typed-data-verify recomputes the domain separator, message hash and digest, recovers the signer and compares it with signer (the tx sender):

typed-data-verify recipes/fixtures/evm/permit2-single-02a.json | jq -c '{primary_type, chain_id, digest, recovered, status}'

Change one field (say the spender) and the same signature recovers someone else:

jq '.typed_data.message.spender = "0x000000000000000000000000000000000000dead"' recipes/fixtures/evm/permit2-single-02a.json | typed-data-verify

Before signing, the wallet shows you the request, not a signature: run typed-data-verify request.json on the eth_signTypedData_v4 params (without --sig) and compare digest with what the wallet displays (UNTESTED here: no wallet in CI). Permit2 PermitBatch and PermitTransferFrom have other types; they are not shown here.