Skip to main content
Sigvex

Permit Vulnerabilities Remediation

How to use ERC-2612 permit safely across front-running, nonce handling, and deadline enforcement.

Permit Vulnerabilities Remediation

Overview

Related Detector: Permit Vulnerabilities

ERC-2612 permit enables gasless approvals from an off-chain signature, but a naive integration can be griefed by a front-run permit, desynchronized by out-of-order nonces, or replayed if the deadline is loose. This guide covers the integration as a whole; see also Permit Frontrunning and Missing Deadline for the individual issues.

Before (Vulnerable)

function depositWithPermit(uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external {
    token.permit(msg.sender, address(this), amount, deadline, v, r, s);  // reverts if front-run
    token.transferFrom(msg.sender, address(this), amount);
}

After (Fixed — tolerate a consumed permit, enforce a real deadline)

function depositWithPermit(uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external {
    require(deadline >= block.timestamp, "Permit expired");   // enforce, don't accept max
    // If the permit was already applied (front-run/replayed), proceed on the allowance.
    if (token.allowance(msg.sender, address(this)) < amount) {
        try token.permit(msg.sender, address(this), amount, deadline, v, r, s) {} catch {}
    }
    require(token.allowance(msg.sender, address(this)) >= amount, "Permit failed");
    token.transferFrom(msg.sender, address(this), amount);
}

Use short deadlines, rely on the token’s own nonce for replay protection, and never treat a reverting permit as a hard failure when the allowance is already sufficient.

Common Mistakes

  • Letting a front-runner’s permit submission revert and fail the whole operation (see permit-frontrunning).
  • Passing type(uint256).max as the deadline, removing the replay window bound.
  • Assuming every token implements ERC-2612; some use Permit2 or no permit at all — branch accordingly.

References