EVM patterns
Permit2 Revocation: Allowances, Pending Signatures and Reapproval
Removing the token's approval blocks Permit2 token movement but leaves internal permission records in place. Permit2 revocation must match the route being removed: a standing allowance, a pending signed allowance update or a SignatureTransfer message.

Key facts
- Original experiment
- All 16 subsets of 4 defined revocation actions, with each route evaluated independently
- Immediate blocking
- 9 of 16 combinations block every modeled spending route
- After token reapproval
- 2 of 16 combinations still block every original modeled route
- Measurement boundary
- Constructed authorization states, not live-wallet or EVM execution tests
Separate the permission stores
A token approval and a Permit2 allowance are different records.
The token's approval lets Permit2 move that token on the owner's behalf. Permit2 then applies its own authorization rules to a spender. A pending signed permit introduces another question: whether a message that has not yet been submitted can recreate permission later. Reading only the current allowance leaves that question unanswered.
Permit2 revocation means changing the specific state that authorizes an unwanted spending route. Depending on the route, that can require clearing a stored allowance, advancing an ordered nonce or invalidating a bit in an unordered nonce bitmap. Removing the underlying token approval blocks token movement through Permit2, but does not erase all those internal records.
The Uniswap Permit2 overview separates the system into AllowanceTransfer and SignatureTransfer. The former stores a standing allowance with an amount and expiration. The latter uses a nonce-scoped signature for a transfer without creating the same standing allowance. Their revocation mechanisms should not be treated as interchangeable.
| Store | Key | What it controls |
|---|---|---|
| ERC-20 allowance | Owner and Permit2 address | Token movement by Permit2 |
| AllowanceTransfer amount and expiration | Owner, token and spender | Repeated spending through the stored allowance |
| AllowanceTransfer nonce | Owner, token and spender | Acceptance of signed allowance updates |
| SignatureTransfer bitmap | Owner, word position and bit | Acceptance of the corresponding one-use signature |
The distinction explains an apparently contradictory interface result. A token explorer can show a large approval to Permit2 while an application-specific Permit2 allowance has expired. Conversely, a cleared application allowance can coexist with an unsubmitted signed update whose nonce still matches. The observations concern different records, so neither should overwrite the other in a security report.
For a treasury, the unit of review is a permission route with a named owner and chain. Include the token and spender wherever the relevant store requires them. A label such as "Uniswap permission removed" is too broad unless it states which contract record changed. The signature replay analysis provides the wider context for nonce and domain separation.
Original experiment across every revocation combination
We modeled every subset of a defined set of revocation actions, then checked the same permissions after the token approval was restored. The comparison separates immediate blocking from persistent cleanup of the preexisting Permit2 records. It is a state model based on the opened source, not an experiment against deployed contracts.
Method
- Reference date
- ; fetched Uniswap source bytes are frozen by hash.
- Population
- All 16 subsets of 4 actions: revoke token approval, lock down the stored pair, invalidate the pending ordered nonce and invalidate the pending signature bit.
- Starting permissions
- One live stored allowance, one valid pending AllowanceTransfer permit and one valid pending SignatureTransfer message.
- Controlled assumptions
- One owner, token and spender; enough token balance; unexpired signatures; an ordinary token honoring its allowance.
- Comparison
- Each spending route is evaluated independently from the same starting state, first immediately after cleanup and then after restoring the underlying token approval.
- Exclusions
- No cases removed. No new signatures or application permissions are issued after cleanup.
The retained script is seo/research/authorization-boundaries-2026-10-03/measurements.py. The full rows are in seo/research/authorization-boundaries-2026-10-03/permit2-revocation/measurement.json. These repository artifacts are not public download links. The script's --recheck option reproduces the complete result.
Results
| Outcome | Combinations | Interpretation |
|---|---|---|
| All modeled routes blocked immediately | 9 of 16 | Includes combinations relying on zero token approval |
| All modeled routes still blocked after token reapproval | 2 of 16 | All relevant internal permissions were also invalidated |
| Immediate protection lost after token reapproval | 7 of 16 | At least one original internal spending route remained usable |
The two persistent combinations both clear the stored pair and invalidate the relevant nonce in each family. They differ only in whether token approval was also revoked. That difference no longer affects the second observation because the experiment explicitly restores the token approval before evaluating it.
Lockdown alone leaves the pending allowance permit's nonce intact. Ordered nonce invalidation alone leaves the standing amount intact. Either action also leaves the separate SignatureTransfer bitmap untouched. The experiment makes these distinctions visible without assuming that a single button performs a complete cleanup.
Restoring token approval is a deliberate counterfactual in this model. We did not restore an approval in anyone's wallet. The result asks a useful maintenance question: if a future application flow asks the owner to approve Permit2 again, which old permissions could become effective under the model's assumptions?
Limits
- The 16 combinations are a finite constructed population, not a survey of users or a probability estimate.
- The script does not execute Solidity, validate signatures or model transaction ordering.
- Each route is checked independently, so a successful hypothetical spend does not consume state before another route is evaluated.
- Only the named pending messages are invalidated. Unknown signatures with other valid nonces require separate analysis.
A zero token approval can be an immediate block without being a durable cleanup of every internal permission. That conclusion is narrower than saying all revocations need all actions. The required actions depend on which records and signed messages actually exist.
Invalidate the correct nonce family
AllowanceTransfer uses an ordered nonce for an owner-token-spender combination. The opened AllowanceTransfer implementation compares a signed nonce with the stored value before applying a permit. Its invalidateNonces function moves that stored value forward. This addresses signed updates that would otherwise match the old value.
The same implementation's lockdown function sets the selected pair's amount to zero. It does not advance the pair's nonce. Its direct approve path updates amount and expiration without using the signed-permit update path. These are useful operations with different postconditions, and a review should preserve those differences.
| Operation | Immediate state change | Separate concern |
|---|---|---|
| Token approval set to zero | Token blocks spending through Permit2 | Internal Permit2 records remain |
lockdown | Selected standing amounts become zero | Signed allowance updates may retain a valid nonce |
invalidateNonces | Ordered nonce advances for the selected pair | Existing standing amount is not cleared |
invalidateUnorderedNonces | Selected bitmap bits become used | Other words and unset bits remain separate |
SignatureTransfer uses a different namespace. Its nonce selects a bitmap word and a bit within that word. The source derives the word position by shifting the nonce right by 8 bits and uses the low bits for the bit position. invalidateUnorderedNonces marks the selected mask bits in the owner's word. It does not modify AllowanceTransfer's ordered nonce.
A revocation tool therefore needs to know which signature family it is handling. Supplying a number called "nonce" is not enough. Preserve the typed message schema, owner and verifying contract along with the chain. For the bitmap flow, show the word and mask that the proposed transaction will invalidate. A mismatched mask can leave the intended message usable while invalidating a different one.
The ordered function also imposes constraints on the new value. The opened source requires it to exceed the old nonce and limits the increment to the maximum uint16 value. Do not invent a universal "set nonce to maximum" cleanup instruction. Read the current state and the actual contract's constraints before constructing an invalidation transaction.
The SignatureTransfer documentation describes its unordered nonce scheme and caller-context requirements. Those details belong in the evidence record because a current allowance view cannot reveal the entire set of signed messages someone may hold off-chain.
Check exact scope and expiration boundaries
An ordered nonce belongs to a particular token-spender pair for an owner. Advancing it for one pair does not invalidate a message for another pair. A cleanup script must preserve those keys all the way from discovery through calldata construction to post-transaction verification. A deduplication step that groups only by token can accidentally collapse distinct spender permissions.
The signature bitmap has a different grouping rule. Two nonces can select the same word while using different bits, or select different words entirely. Invalidating a mask in one word does not describe the state of another word. The verification view should show the requested bits as set and retain the word position used for the read.
Expiration boundaries need literal tests against the implementation. The opened source rejects a signature when the current timestamp is greater than its deadline. It similarly checks a stored allowance's expiration using a greater-than comparison. Equality is therefore a distinct boundary to test; a user-interface countdown rounded to minutes is not a substitute for the contract's timestamp rule.
Separate elapsed time from explicit invalidation in the record. A message rejected because its deadline has passed does not demonstrate that a nonce invalidation worked. A message rejected because the token approval is zero does not demonstrate that its internal permission is gone. Construct a control case that removes the unrelated blocking condition when testing each intended postcondition.
The complete-combination experiment uses this separation deliberately: its second observation restores the token approval so that retained internal routes become visible. In an actual test environment, also preserve the intended token balance and unexpired deadline while checking the nonce change. Otherwise an apparently successful negative test can conceal a wrong word, mask or pair.
Bind signatures to the intended action
A signature can be cryptographically valid and still be used through an incorrectly constrained integration. Uniswap's SignatureTransfer guidance warns integrators to ensure that signatures are usable only in the intended caller context. A router that accepts any valid message without checking that context can expose a route the signer did not intend.
- Identify the signed message family and its verifying contract on the selected chain.
- Trace how the integration derives the owner and validates the caller allowed to use the message.
- Trace the destination and requested transfer amount through the integration's execution parameters.
- Check the appropriate nonce store at execution and verify that replay attempts fail.
- Verify any additional witness data against the application's actual policy.
The opened Permit2 hashing source binds the spender through its message construction. That does not eliminate the integrating contract's responsibility for its own entrypoints. If a contract is the permissioned spender, its callable methods determine who can cause it to use that permission and for what purpose. Review those methods with the same care as the permit itself.
Witness-based transfers let an integration bind additional data into a signature. The existence of a witness parameter is not proof that the relevant business constraint was included or enforced. Establish what the witness represents, how its type is formed and which code validates the relationship between that data and the transfer being made.
Deadline and allowance expiration also answer different questions. A signed allowance update has a deadline for accepting the signature. The resulting stored allowance has its own expiration for spending. The implementation compares the relevant timestamp at each operation. An interface that shows only one date can cause a reviewer to miss the other boundary.
These checks belong beside the token integration review, because token behavior remains a dependency. The model assumes a token that honors its allowance normally. Unusual token behavior, transfer restrictions or changed contract code require additional testing before a permission matrix can predict execution.
Verify the remaining authority
Verification should start with a statement of what the cleanup is meant to remove. If the goal is to stop a known spender immediately, the required postcondition differs from invalidating every known outstanding message for future reuse. Write that goal before selecting operations, then inspect the records that can establish it.
- Record the account, chain, token and relevant spender addresses. Verify the Permit2 deployment being used instead of trusting a copied address label.
- Read the underlying token allowance and the selected AllowanceTransfer tuple at a recorded block.
- Classify known signed messages by family and capture their nonce and deadline without exposing private keys.
- Select the state changes needed for the stated cleanup goal. Explain which other permissions each operation leaves untouched.
- After inclusion, read the affected records again at the resulting block and preserve the transaction identifiers.
- Review whether later token reapproval would make any retained permission effective under its own remaining conditions.
Unknown off-chain signatures remain an evidence limit.
A blockchain query can show the current nonce and bitmap state, but cannot prove that nobody holds an unsubmitted message. Record the known applications and signing history used in the investigation. An empty list in a scanner is only as complete as that scanner's discovery method.
Transaction ordering can change a cleanup's result. A pending permit might be used before an invalidation is included. The model deliberately excludes this race, so the article does not claim that preparing the correct transaction guarantees prevention. Verify the included state and subsequent activity rather than assuming the planned sequence became the chain's sequence.
During incident response, preserve evidence before overwriting local records that help identify the messages involved. The incident response guide explains how to keep the investigation and containment decisions connected. Deleting an application's cached approval does not change an on-chain nonce or an already distributed signature.
For a contract integration review, the Pharos Production member profile provides provider context, while the security tools hub helps distinguish inspection tools from broader assessments. A report should name the router and permission flows actually examined. A Permit2 reference audit alone does not review a new router's caller checks.
Design a revocation interface that tells the truth
An interface should show the state transition it is proposing. "Clear this spender's stored allowance" is different from "invalidate these signed permits" and from "remove the token's approval to Permit2." Use labels that match the contract operation. After confirmation, report the observed postcondition rather than a blanket claim that all permissions are gone.
A useful confirmation view identifies the chain and the affected token-spender pair. For a bitmap invalidation it also explains which message nonces the mask covers. Where discovery is incomplete, display that limit next to the result. The absence of a discovered message should not be presented as proof that no message exists.
- Before signing
- Explain the exact record being changed and the permission it will remove.
- After inclusion
- Show the verified state at the included block and any failed verification reads.
- Before reapproval
- Review retained internal permissions that could become effective again.
- When evidence is incomplete
- Name the unknown permission class and the source of the uncertainty.
The original experiment's useful distinction is persistence. Several combinations stop every modeled route immediately, but fewer remain effective when the underlying token approval returns. That result does not prescribe one universal cleanup transaction. It gives developers and operators a concrete question to answer: which old authorizations would the next approval reactivate?
Frequently asked questions
Does a gasless signing flow mean invalidation is also off-chain?
No. A signature can be created off-chain, while changing a contract nonce or bitmap requires an included on-chain state change. The interface should distinguish rejecting a new signing request from invalidating a message already signed.
Does ERC-2612 use the same nonce records as Permit2?
No. ERC-2612 is implemented by the token and has its own permit nonce rules. Permit2 is a separate contract system. Identify the verifying contract before selecting a revocation method.
Can a batch cleanup be accepted when only one transaction appears in the explorer?
Transaction count does not establish coverage. Decode the batch and verify the postcondition for every intended pair or bitmap word. A single transaction can contain several actions, but its receipt alone does not describe their full effect.