Skip to content
Security
Skill

/permit2-patterns

Detect Permit2 / EIP-2612 lifecycle bugs — signature lifecycle, allowance transfer vs signature transfer confusion, nonce reuse, deadline manipulation, witness-data misuse. Activate on `permit`, `permitTransferFrom`, `IPermit2`, `SignatureTransfer`, `AllowanceTransfer`,

From plugin
rugproof
1152 skills23 agents45 commands4 hooks

Claude Code

$ npx -y skills add omermaksutii/RugProof --skill permit2-patterns --agent claude-code

SKILL.md

permit2-patterns.SKILL.md
name: permit2-patterns
description: Detect Permit2 / EIP-2612 lifecycle bugs — signature lifecycle, allowance transfer vs signature transfer confusion, nonce reuse, deadline manipulation, witness-data misuse. Activate on `permit`, `permitTransferFrom`, `IPermit2`, `SignatureTransfer`, `AllowanceTransfer`, `PermitWitnessTransferFrom`, `PermitBatchTransferFrom`.

Permit2 / EIP-2612 detection

Background

EIP-2612 (`permit`) — token-native gasless approval, per-token nonce. Permit2 (Uniswap) — canonical contract bridging legacy ERC20s into the permit world; two APIs:

  • **SignatureTransfer** — one-shot, sig consumed atomically.
  • **AllowanceTransfer** — persistent allowance with nonce + expiration.

When this applies

  • Routers, swap aggregators, vaults using Permit2 / EIP-2612 for gasless approve flows
  • Custom permit implementations
  • Apps calling `IPermit2.permitTransferFrom`, `permitWitnessTransferFrom`, `permit`

Detection patterns

Permit2 `permitTransferFrom` without amount check (CRITICAL)

permit2.permitTransferFrom(permit, transferDetails, owner, signature);
// uses `transferDetails.requestedAmount` — caller-controlled

Caller can request `permit.permitted.amount` (the max), regardless of intent. App must enforce the actual transfer amount.

Witness data ignored (HIGH)

`permitWitnessTransferFrom` includes a user-signed `witness` blob. App must validate it matches the intended action; otherwise sig can be reused with different context.

SignatureTransfer vs AllowanceTransfer confusion (HIGH)

SignatureTransfer is one-shot. AllowanceTransfer is persistent with nonce + expiration. Mixing → over-approval or replay.

Allowance not invalidated after one-shot use (HIGH)

Some implementations using AllowanceTransfer don't increment nonce after consumption.

Missing deadline (HIGH)

Permit sigs without deadline are bearer instruments forever.

EIP-2612 permit failure swallowed (MEDIUM)

try IERC20Permit(token).permit(...) {} catch {}
token.transferFrom(...);   // ← if permit fails, falls back to stale allowance

Acceptable IFF the app then validates allowance is sufficient.

Permit + transferFrom unbundled (MEDIUM — MEV)

Submitting `permit` in one tx then `transferFrom` in another lets a searcher front-run the permit and grief.

Hardcoded chainId / DOMAIN_SEPARATOR cached (HIGH)

Some EIP-2612 impls cache DOMAIN_SEPARATOR in constructor. Post-chain-fork, this is wrong. Re-derive based on `block.chainid`. See [[signature-replay]].

DAI-style permit (HIGH)

DAI uses a non-standard permit signature (allowed-bool, no amount). Apps that hit DAI with standard EIP-2612 ABI will revert.

USDC EIP-3009 (different from EIP-2612) (MEDIUM)

USDC uses `transferWithAuthorization` / `receiveWithAuthorization` instead of permit on most chains. Apps assuming permit-on-USDC break.

Nonce reuse across signers (HIGH)

Custom permit impls that use a global nonce instead of per-signer nonce → cross-signer collision.

Bitmap nonce model assumed but not used (HIGH)

Permit2 uses a bitmap for nonces (word index + bit). Custom forks sometimes simplify this and break.

Severity rubric

| Pattern | Severity | |---|---| | `permitTransferFrom` with caller-controlled requestedAmount | **Critical** | | AllowanceTransfer not invalidating nonce on one-shot use | **High** | | Sig without deadline | **High** | | Cached DOMAIN_SEPARATOR not fork-aware | **High** | | Witness ignored in permitWitnessTransferFrom | **High** | | Failed permit swallowed, allowance not re-checked | **Medium** | | Permit + tx unbundled (MEV grief) | **Medium** | | DAI/USDC-specific sig variant mishandled | **High** | | Global nonce instead of per-signer | **High** |

Remediation patterns

  • App-side: validate `transferDetails.requestedAmount <= permit.permitted.amount` AND matches intent.
  • Bundle `permit` + consuming op in one tx.
  • Use Permit2's canonical contracts via OZ / Uniswap reference impls.
  • For DAI / USDC: dispatch on token type, use appropriate sig variant.
  • For chain forks: re-derive `DOMAIN_SEPARATOR` when chainId changes.

False-positive notes

  • Apps that just call `IERC20Permit.permit(...)` directly and don't trust the result are typically fine.
  • Uniswap V4 / Universal Router use Permit2 canonically — audit how they pass requestedAmount.

Related

  • [[signature-replay]]
  • [[approval-issues]]
  • [[mev-frontrunning]]
Read more
@omermaksutii
Ships withrugproof

Rugproof your code before someone else does. 🌐 Live site: omermaksutii.github.io/RugProof 📦 Latest: v1.0.0 — 45 commands · 23 agents · 45 skills · 13 MCP servers · tested, offline-first, with rule packs, a benchmark, non-EVM coverage, and post-deploy

1152 skills23 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
11
Star history of omermaksutii/RugProof on GitHub, a chart by star-history.com

Other skills on rugproof.

More skills from other plugins.

api-security

api-security

Use for authorized security assessment of REST, GraphQL, WebSocket, or SOAP APIs, including discovery, authentication,…

@zhaoxuya520@zhaoxuya520Install Skill
nemoclaw-user-guide

nemoclaw-user-guide

Find official NemoClaw documentation for installation, configuration, operation, or troubleshooting. Also handles requested docs…

@nvidia@nvidiaInstall Skill

Questions about permit2-patterns.

Which coding agents can use permit2-patterns?
permit2-patterns installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.