Off-Chain Signatures
Off-chain signatures are the pattern of authorizing on-chain actions by signing a message off-chain and letting a smart contract verify the signature later. The signer pays no gas to produce the signature; whoever submits it to the chain pays gas for the verification and the authorized action. EIP-712 is the standard that makes this pattern safe at scale.
Why sign off-chain
Every on-chain transaction costs gas and occupies block space. Many authorizations do not need to hit the chain immediately — or at all:
- Deferred execution: An exchange order that sits in a book until matched.
- Batched execution: Many approvals collected off-chain and submitted together.
- Conditional execution: An action that should only fire if some other condition is met.
- Gasless UX: A relayer pays gas on the user’s behalf after verifying the user’s signature.
- Off-chain consensus: A multisig whose threshold is reached before any transaction hits the chain.
In all of these, the signature itself is the authorization. The user signs once; the signature can be used later by anyone who holds it, subject to the application’s rules.
The shape of an off-chain signature flow
- Sign: The user signs a structured message with their wallet via
eth_signTypedData. The wallet displays the message contents (field names and values) from the EIP-712 type definition and asks for consent. - Transport: The signature and the original message travel off-chain — through an API, a message relay, a peer-to-peer network, or even a QR code.
- Submit: Some party (the user, a relayer, a counterparty, an aggregator) submits the signature and message to a contract in a transaction.
- Verify: The contract reconstructs the EIP-712 digest from the message, calls
ecrecover(or EIP-1271 for smart accounts), and checks the recovered address matches the expected signer. - Execute: If verification succeeds, the contract performs the authorized action.
Steps 1 and 4 depend entirely on EIP-712 doing its job: the same canonical digest must be produced at the wallet and at the contract from the same message.
What EIP-712 provides
EIP-712 is what makes off-chain signatures safe:
- Human-readable signing: The wallet shows
{from: 0x..., to: 0x..., amount: 100}rather than a 32-byte hex blob. Users can give informed consent. - Type separation:
typeHashensures structurally identical messages from different types do not collide. - Domain separation:
domainSeparatorbinds signatures to a specific chain, contract, protocol, and version. See EIP-712 Domain Separator. - Deterministic encoding: The wallet and the contract compute the same 32-byte digest from the same message.
- Injective encoding: The
\x19\x01prefix ensures the digest cannot collide with a valid transaction encoding or an\x19Ethereum Signed Messagestring.
See Ethereum Signed Message Encoding for the full disambiguation scheme.
What EIP-712 does NOT provide
Two critical security properties are explicitly out of scope for the hashing standard:
Replay protection within a domain
An attacker who obtains a valid signature can submit it repeatedly unless the application prevents it. The domain separator prevents cross-domain replay, but within a single contract, the same signature can be submitted twice.
Standard mitigations:
- Nonces: Each signature includes a monotonically increasing nonce unique to the signer. The contract tracks
usedNonces[signer]and rejects reuse. ERC-2612permituses this pattern. - Idempotency: Design the action so that submitting it twice produces the same final state (e.g., “set balance to X” instead of “add X to balance”).
- Single-use tokens: Generate a random nonce per signature and mark it as spent on first use.
- Deadlines: Include a timestamp; the contract rejects signatures past the deadline. Bounds the replay window but does not eliminate it.
Frontrunning
An attacker who sees a user’s signature in the mempool (or in any other broadcast channel) can submit it first with a higher gas price. For applications where the submitter matters, this can steal the intended benefit.
Standard mitigations:
- Submitter binding: Include the submitter address in the signed message; the contract verifies
msg.sender == signedSubmitter. - Private mempools: Flashbots and similar services bypass the public mempool.
- Commit-reveal: Two-phase authorization where the commit is harmless and the reveal is short enough to race.
- Order-agnostic outcomes: Design actions so the order of submission does not change the result.
Applied examples
- ERC-2612
permit: Gasless ERC-20 approvals. User signs aPermitstruct; anyone submits it to grant allowance. Preventsapprove-then-transferFromtwo-transaction flows. - OpenSea / Seaport orders: User signs an order describing a listing; buyers submit matching orders on-chain to settle.
- Farcaster
SignedKeyRequest: User’s custody address signs authorization for a new ed25519 signing key. See Creating a Farcaster Account by Hand for a walkthrough that does the EIP-712 computation by hand withcast. - Safe transactions: Owners sign
SafeTxstructs off-chain; when the threshold of signatures is reached, anyone submits them inexecTransaction. - Sign-In with Ethereum (EIP-4361): User signs a structured login message; the web app verifies the signature to establish a session.
Smart account signatures: EIP-1271
EOAs sign with ecrecover. Smart contract accounts cannot — they have no private key. ERC-1271 bridges the gap: a contract implements isValidSignature(bytes32 hash, bytes signature) returns (bytes4 magicValue). Verifiers call this function instead of ecrecover when the signer is a contract. See Smart Account Signatures for the full branching pattern verifiers use.
Safe’s implementation wraps the hash in a SafeMessage(bytes message) struct under a Safe-specific EIP-712 domain, then either checks a stored signature (via signMessage) or validates a threshold of owner signatures at call time. The Farcaster walkthrough above uses the pre-approved variant: the Safe runs one on-chain transaction to record “yes, I signed that digest” before the verifier’s callback arrives.
Connections
- EIP-712: Typed structured data hashing and signing: the standard that makes off-chain signing safe
- EIP-712 Domain Separator: the mechanism preventing cross-domain replay
- EIP-712 hashStruct: how the signed digest is computed
- Ethereum Signed Message Encoding: the leading-byte disambiguation that prevents collisions
- ERC-1271: Standard Signature Validation Method for Contracts: the standard extending off-chain signing to smart accounts
- Smart Account Signatures: the EOA vs contract signer verification pattern
- Creating a Farcaster Account by Hand: walkthrough of an off-chain signature flow with Safe + EIP-1271