Skip to content

RFC: EIP-2612 permit signing helper #132

@pay-skill

Description

@pay-skill

Summary

EIP-2612 permit() is the most common signing operation for payment protocols using USDC and other ERC-20 tokens on EVM chains. OWS can sign permits today via signTypedData, but the caller must manually construct the full EIP-712 typed data JSON — including fetching the token's nonce, name, version, and building the correct type array.

A purpose-built permit helper would eliminate this boilerplate and reduce a common class of errors (wrong nonce, stale domain separator, missing EIP712Domain fields).

Current Flow (Manual)

// 1. Fetch token metadata (name, version — varies per token)
// 2. Fetch permit nonce from contract
// 3. Construct EIP-712 typed data JSON
const typedData = JSON.stringify({
  types: {
    EIP712Domain: [
      { name: "name", type: "string" },
      { name: "version", type: "string" },
      { name: "chainId", type: "uint256" },
      { name: "verifyingContract", type: "address" },
    ],
    Permit: [
      { name: "owner", type: "address" },
      { name: "spender", type: "address" },
      { name: "value", type: "uint256" },
      { name: "nonce", type: "uint256" },
      { name: "deadline", type: "uint256" },
    ],
  },
  primaryType: "Permit",
  domain: { name: "USD Coin", version: "2", chainId: 8453, verifyingContract: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" },
  message: { owner, spender, value: "1000000", nonce: currentNonce, deadline },
});

// 4. Sign via OWS
const result = signTypedData(walletId, "evm", typedData);

Steps 1-3 are error-prone and identical across every protocol that uses permits. The domain separator fields vary by token (USDC uses version "2", DAI uses version "1", some tokens omit version entirely).

Proposed Helper

// signPermit(walletId, chain, permitParams) → SignResult
interface PermitParams {
  token: string;           // ERC-20 contract address
  spender: string;         // address to approve
  value: string;           // amount in base units
  deadline: number;        // Unix timestamp
  // Optional overrides:
  nonce?: number;          // auto-fetched if omitted
  rpcUrl?: string;         // for nonce lookup
}

The helper would:

  1. Fetch the token's name(), version() (via eip712Domain() if available, fallback to common patterns), and nonces(owner) from the chain
  2. Construct the EIP-712 typed data JSON
  3. Call signTypedData internally
  4. Return the signature

Why This Belongs in OWS

  • Permits are the gasless UX primitive. Every payment protocol, DEX aggregator, and lending protocol uses them. They're as fundamental as transfer().
  • The domain separator is the hard part. Different tokens have different EIP-712 domain configurations. Getting this wrong produces valid-looking but rejected signatures. A helper that handles the common cases (USDC, USDT, DAI) and falls back to eip712Domain() for others would prevent hours of debugging.
  • The ows pay command already handles x402 payments. A permit helper is the natural complement — x402 uses facilitator-managed approvals, but direct protocol interactions need permits.

Scope

This could be:

  • A new CLI command: ows sign permit --token 0x... --spender 0x... --value 1000000 --deadline 1711387200
  • A new SDK function alongside signTypedData
  • A documented recipe/example (lightest touch)

The CLI command would be most useful for scripting and CI pipelines. The SDK function would help protocol integrators.

Happy to contribute a PR with the implementation — we've built this exact helper in our payment SDK and can upstream the logic.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions