Skip to content

Execution

Permit2 vs ERC-2612: Map Both Permission Layers

Trace two spender grants through a shared Permit2 allowance, revocation and restoration. Then check why a one-time transfer leaves no reusable remainder.

Lime ribbon passes through two ivory ceramic aperture plates beside a torn paper ticket on a dark surface.
Illustration for execution; examples in this article are educational.

ERC-2612 lets a token accept a signed instruction that changes its own spending allowance. Permit2 adds a separate permission system: the token must authorize Permit2, which then checks either a reusable spender allowance or a one-time transfer signature. Compare the actual module, both permission layers, the nonce scope and the time limits. A short-lived signature does not automatically remove the token's underlying approval to Permit2.

Start with the contract that holds each permission

With a native ERC-2612 permit, the token verifies a signature and sets its ordinary allowance for the named spender. Signing alone changes nothing; a successful on-chain call activates the permission and advances the owner's nonce on that token. The deadline limits when that call can execute. The resulting allowance has no expiry supplied by ERC-2612.

Permit2 is an intermediary. The owner first authorizes the Permit2 contract at the token. Permit2 then applies its own rules before calling the token's transfer mechanism. This prerequisite remains true for both of its modules, including SignatureTransfer. Uniswap's Permit2 overview documents the initial approval separately from downstream permissions.

The distinction matters when a wallet displays only a friendly application name. Inspect both spender addresses: Permit2 at the token layer, then the integrating router or other contract at the Permit2 layer.

Permission modelWhere authority is recordedWhat remains after activation or use?
Native ERC-2612Allowance in the tokenAn ordinary token allowance
Permit2 AllowanceTransferToken approval plus a Permit2 grantA reusable grant until exhausted, expired or revoked
Permit2 SignatureTransferToken approval plus an unused signed transferNo reusable downstream grant after successful use

A token need not implement ERC-2612 for an integration to use Permit2. That does not make every token, wallet or router compatible with every flow. Verify the actual contracts and supported signature mechanism. The broader permit-versus-approval guide covers token-native activation; this comparison follows the additional Permit2 state.

A reusable grant has two clocks and its own nonce

Permit2's AllowanceTransfer module stores an amount, expiration and nonce for each owner, token and spender combination. A signed grant also has a separate sigDeadline.

The signature deadline answers whether an unused grant may still be installed. The allowance expiration answers whether the installed grant may still be spent. Passing the first deadline does not remove a grant already installed with a later expiration.

Nonce scope also changes. Two AllowanceTransfer permits for the same owner and token can both use nonce 0 when they name different spenders with current nonce 0. Each advances its own record. Two native ERC-2612 permits for that owner on the same token instead compete for the token's single current owner nonce, even when their spenders differ.

Here, detailed Permit2 behavior refers to Uniswap's upstream implementation at commit cc56ad0. Its allowance storage library advances the nonce when installing a signed allowance. Spending against an existing grant does not require another permit signature. Inspect the deployed implementation before applying these details to a particular chain.

Two spenders share one upstream allowance

Consider this hypothetical educational example. Owner O holds 1,000 Q, a token with six decimals and conventional finite allowance accounting as in OpenZeppelin Contracts v5.4.0 ERC20. There are no transfer fees, rebases, other transfers or reorganizations. Each action after the starting row is a separate transaction; the rejected attempt reverts completely.

At 12:00 UTC, O has approved Permit2 for 400 Q at the token. Two signed AllowanceTransfer grants have also activated: router R receives 300 Q of authority and router S receives 200 Q. Both signatures had an execution deadline of 12:00:30; both installed grants expire at 13:00. Their independent permit nonces have each advanced from 0 to 1.

R and S receive permission limits without reserved token balances. They share O's balance and the token's allowance to Permit2.

Time and completed actionO balance, QToken → Permit2, QR grant, QS grant, Q
12:00: grants active1,000400300200
12:01: R transfers 100900300200200
12:02: S transfers 16074014020040
12:03: R requests 150; rejected74014020040
12:04: O revokes token approval740020040
12:05: O approves Permit2 for 12074012020040
12:06: R transfers 606806014040

At 12:03, R's 200 Q grant is sufficient for its 150 Q request, but the shared upstream allowance is only 140 Q. The token transfer fails. The pinned AllowanceTransfer implementation checks its internal grant and then calls the token. The downstream limit is only one condition for success.

Although that implementation reduces a finite internal grant before calling the token, the failed transfer does not leave R with a 50 Q grant. EVM revert semantics roll back state changes in the reverted execution. R remains at 200 Q. Earlier successful transactions remain effective.

The arithmetic reconciles: the first two transfers total 260 Q, leaving a 740 Q balance and 140 Q upstream allowance. The later 60 Q transfer leaves a 680 Q balance, a 60 Q upstream allowance and a 140 Q R grant. The original 400 Q approval was 400,000,000 base units.

At 12:05, restoring the upstream approval allows R to use its existing unexpired grant without a fresh signature. The earlier revocation did not delete either grant inside Permit2. Both grants' nonces remain 1 throughout the spending and token-approval changes.

At 13:00:01, the expired grants cannot authorize another transfer, even though their stored amounts may still be positive. The pinned implementation rejects spending when the block timestamp is greater than the expiration; equality passes that particular time check. Sufficient balances and permissions still matter.

A one-time transfer has no reusable remainder

SignatureTransfer uses a different model. In a separate hypothetical example, O again has 1,000 Q and a finite 400 Q token allowance to Permit2. O signs a single transfer permission with a 100 Q ceiling and nonce 517. The authorized spender successfully requests only 65 Q before the deadline.

The resulting balance is 935 Q and the upstream allowance is 335 Q. The unused 35 Q of the signed ceiling is not a remaining AllowanceTransfer grant. The successful call consumes the one-time nonce, so that signature cannot later authorize the other 35 Q. This follows from the pinned SignatureTransfer implementation, which checks the requested amount against the ceiling and spends the nonce for the call.

Its nonce is a bitmap location, not the next sequential number required for every transfer. For nonce 517, the word index is 2 and bit index is 5: 517 = 2 × 256 + 5. Starting from an empty word, successful use sets mask 32, because 2⁵ = 32. Other unused bits remain independently available. Allocating the same owner nonce to two signatures creates a conflict even if their tokens or spenders differ.

Do not mark a nonce consumed merely because a transaction was submitted. If the entire execution reverts, the nonce update reverts too. Resolve an uncertain outcome from the receipt and current bitmap before signing a replacement.

Spending permission does not establish acceptable swap terms

For SignatureTransfer, the signed hash binds the spender to the contract calling Permit2. The ordinary signed payload includes the token, maximum amount, nonce and deadline. The recipient and requested transfer amount are supplied separately by that spender. A valid base permit therefore does not, by itself, establish the intended recipient, minimum output or exact trading instruction.

An integrating router must enforce caller and execution context. Uniswap's SignatureTransfer security guidance describes the failure case where a router accepts someone else's valid signature without checking the intended caller. Its witness variant can bind extra signed trade context; the integration must also enforce the meaning of those fields.

Review the swap's minimum output and price limit alongside the spending permission. They constrain different parts of execution. Likewise, Uniswap v4 hook permissions concern pool callbacks; a hook permission is not evidence that a wallet authorized token spending.

Close access at the layer you intend to retire

Permit2 has separate tools for stored grants and unused signatures. Its AllowanceTransfer interface exposes lockdown to clear selected grant amounts and invalidateNonces to advance a selected owner-token-spender nonce. Clearing an amount does not invalidate a still-usable signed permit; advancing the nonce does not clear an already active amount. SignatureTransfer instead offers bitmap invalidation for selected unordered nonces. Check the relevant state after the operation executes.

Use this worksheet when reviewing a flow or investigating a failed transfer:

RecordEvidence to capture
IdentityChain, owner, token, Permit2 deployment and downstream spender
Upstream permissionToken allowance to Permit2 and read block
Reusable grantAmount, expiration and owner-token-spender nonce
One-time permissionCeiling, deadline, nonce word and bit status
Execution intentRecipient, requested amount and enforced trade constraints
RetirementSuccessful receipt, resulting permission state and unused signatures addressed

The token-allowance guide explains the base permission inventory. Add the Permit2 records to it instead of replacing it with a signature history. Keep usable signature bytes out of routine logs and support messages.

Before restoring an upstream token approval, inspect every existing Permit2 grant that the change could make usable again. For an ordinary finite AllowanceTransfer path, compare the requested amount with the owner's balance, the token's allowance to Permit2 and the spender's unexpired internal grant. Record all three at the same block before planning another transfer.

Keep the context with the example.

Each worked example is illustrative, with its sources and assumptions in the article. Read our editorial standards for authorship, verification and corrections.