F-2026-0006·permit-frontrunning

ERC-2612 permit frontrunning allows griefing DoS on all permit-dependent Bridge operations

Fixedbridgecross-chainkey-registrygithub.com/pdxwebdev/yadakeyeventwallet
TL;DR

Bridge calls IERC20Permit.permit() without a try/catch wrapper, so an attacker who frontruns the permit signature from the mempool consumes the nonce and forces every permit-dependent Bridge operation to revert.

Severity
MEDIUM
Impact
MEDIUM
Likelihood
MEDIUM
Method
MManual review
CAT.
Complexity
LOW
Exploitability
MEDIUM
02Section · Description

Description

The _executePermits function calls IERC20Permit2.permit() directly without a try/catch wrapper:

solidity
IERC20Permit2(permit.token).permit(
ectx.user,
address(this),
permit.amount,
permit.deadline,
permit.v,
permit.r,
permit.s
);

ERC-2612 permit signatures are visible in the mempool before inclusion. An attacker can extract the permit parameters from a pending transaction's calldata and frontrun by calling permit() directly on the ERC-20 token contract. The attacker's transaction succeeds (setting the allowance and consuming the nonce), so when the victim's Bridge transaction is included, the permit() call reverts because the signature/nonce is already used , causing the entire operation to fail.

This affects three critical Bridge functions that rely on _executePermits:

  • registerKeyPairWithTransfer, key pair registration with token transfers
  • transferBalanceToLatestKey, balance migration to latest rotated key
  • upgradeWithKeyRotation, contract upgrade with key rotation

The attacker cannot steal funds or profit from this attack, but can repeatedly grief any user attempting these operations, effectively blocking key registration, balance transfers, and upgrades that involve ERC-20 permits.

03Section · Impact

Impact

An attacker can prevent any permit-dependent Bridge operation from completing by frontrunning the permit signature. Users are unable to register key pairs with token transfers, migrate balances, or perform upgrades with key rotation until they work around the griefing by using a standard approve transaction instead, which requires an additional on-chain transaction and awareness of the issue.

04Section · Recommendation

Recommendation

Wrap the permit() call in a try/catch block. If the permit fails, check whether the allowance is already sufficient (which it will be if the attacker already submitted the permit). This is the standard mitigation used across the industry:

solidity
try IERC20Permit2(permit.token).permit(
ectx.user,
address(this),
permit.amount,
permit.deadline,
permit.v,
permit.r,
permit.s
) {} catch {
if (
IERC20(permit.token).allowance(
ectx.user,
address(this)
) < permit.amount
) {
revert InsufficientAllowance();
}
}
05Section · Resolution

Resolution

YadaCoin, Confirmed.

Zealynx, Fixed.

Status
Fixed
F-2026-0006

oog
zealynx

Smart Contract Security Digest

Monthly exploit breakdowns, audit checklists, and DeFi security research — straight to your inbox

© 2026 Zealynx