Skip to content

Validating Permission-Flags in PermissionedV4Router and PermissionedPositionManager#523

Merged
SocksNFlops merged 6 commits into
permissioned-poolsfrom
socksnflops/eco-191-sc-h-01-user-permission-checks-can-be-bypassed-when
Apr 14, 2026
Merged

Validating Permission-Flags in PermissionedV4Router and PermissionedPositionManager#523
SocksNFlops merged 6 commits into
permissioned-poolsfrom
socksnflops/eco-191-sc-h-01-user-permission-checks-can-be-bypassed-when

Conversation

@SocksNFlops

Copy link
Copy Markdown
Collaborator

Related Issue

Description of changes

  • Adds an additional check to validate that SWAP_ALLOWED and LIQUIDITY_ALLOWED flags can't be used for the other's operations during user-funded settlement paths
  • Added unit tests to validate checks

… of PermissionedV4Router + PermissionedPositionManager
…of permission-flags in both payer modes of PermissionedV4Router + PermissionedPositionManager
@SocksNFlops SocksNFlops self-assigned this Apr 13, 2026
@linear

linear Bot commented Apr 13, 2026

Copy link
Copy Markdown

}

function deployInsecureHooks(address manager_) internal returns (address deployedHooksAddr) {
uint160 flags = (1 << 11) | (1 << 7);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flags should now include BEFORE_SWAP_RETURNS_DELTA_FLAG (1 << 3). Here and everywhere else we deploy the hooks

@SocksNFlops
SocksNFlops merged commit 147e768 into permissioned-pools Apr 14, 2026
5 checks passed
@SocksNFlops
SocksNFlops deleted the socksnflops/eco-191-sc-h-01-user-permission-checks-can-be-bypassed-when branch April 14, 2026 14:52
SocksNFlops added a commit that referenced this pull request May 27, 2026
* Initial commit: Permissioned Pools

* Hook implementation

* fix infinite loop

* Wrapped token tests (#477)

* Initial commit: wrapped token tests

* fix imports

* Permissioned router tests, permissioned posm tests (#479)

* initial commit

* touch ups, rm shadowed vars

* bring in wrapped change

* format

* nit change to pure

* external, pure nit

* reset getCode paths

* relative imports

* fix isolated mint_clear_revert test

* use relative paths for mock allowlist

* use BaseAllowList not BaseAllowlist

* change allowList to allowlist

* move to permissioned tests folder

* all tests working

* merging new changes from permissioned-pools

* touch ups, rm shadowed vars

* bring in wrapped change

* format

* nit change to pure

* external, pure nit

* reset getCode paths

* relative imports

* fix isolated mint_clear_revert test

* use relative paths for mock allowlist

* use BaseAllowList not BaseAllowlist

* change allowList to allowlist

* move to permissioned tests folder

* all tests working

* replace mockAllowlist with permissionedPoolsBase

* rm mock allowlist

* run linter

* rm unused

* change casing on const

* reorganize vars

* add most of the tests mentioned in comments

* all paths covered

* seperate hooks contract instead of delegate router

* minor cleanup

* use all combinations of permissioned and normal in liq tests

* spacing nit

* nit, edit note

* add clarifying comment to wrapped factory balance check

* remove circular dependency from permissioned pools implementation (#482)

* initial commit

* rm fallback from permissioned v4 router, leave receive

* fix github ci

* use currency instead of ierc20 in some tests

* add comment to hooks on why verifyAllowlist is sufficient

* inherit from BaseHook contract

* remove unused import, generate gas snapshot

---------

Co-authored-by: gretzke <daniel@gretzke.de>

* expect specific errors (#486)

* Permissioned Pools: Transfers by admins (#483)

* Initial commit: forced transfers by admins

* fix merge conflict

* remove event

* add granular allow list and multiple hooks and fix revert tests (#484)

* Initial commit: forced transfers by admins

* add granular allow list and multiple hooks and fix revert tests

* add coverage for receive

* permissioned v4 router test spacing nits

* change allowed hook check

* allowedHooks on pposm

* nit

* add inheritdoc to transferFrom

* fix merge conflict

* make setAllowedHook external

* fix test resulting for resolving conflict

* simplify checkAllowedHooks

* fix snapshot

---------

Co-authored-by: gretzke <daniel@gretzke.de>

* permissioned-pools: match universalrouter interface (#488)

* initial commit

* linted

* make dispatch internal

* changed revert for command not implemented

* add underscore to internal/prive functions on permissioned v4 router

* applied fmt to permissioned v4 router

* Permissioned Pools: Ability to pause swapping (#485)

* Add check whether swapping is enabled

* Gas snapshot

* Add natspec to event

* Rename to permissions adapter (#489)

* emit event when hooks are allowed/disallowed (#498)

* spelling fix

* Fix typo

* Validating Permission-Flags in PermissionedV4Router and PermissionedPositionManager (#523)

* fix(permissioned-hooks): enforce permission-flags in both payer modes of PermissionedV4Router + PermissionedPositionManager

* tests(permissioned-hooks): adding unit tests to validate enforcement of permission-flags in both payer modes of PermissionedV4Router + PermissionedPositionManager

* update gas snapshot for PermissionedV4Router

* fixing snapshots and unused variable nit

* fixing snapshots one more time

* nit: updating comments in permposm and permv4router

* Fix(PermissionedPositionManager): Adding Recipient checks to mint function (#524)

* fix(permissioned-hooks): enforce permission-flags in both payer modes of PermissionedV4Router + PermissionedPositionManager

* tests(permissioned-hooks): adding unit tests to validate enforcement of permission-flags in both payer modes of PermissionedV4Router + PermissionedPositionManager

* update gas snapshot for PermissionedV4Router

* fixing snapshots and unused variable nit

* fixing snapshots one more time

* fix: validate mint recipient has LIQUIDITY_ALLOWED in PermissionedPositionManager

* Fix(PermissionedPositionManager): Overriding _mapSettlementAmount to use underlying permissionedToken balances (#525)

* fix(permissioned-hooks): enforce permission-flags in both payer modes of PermissionedV4Router + PermissionedPositionManager

* tests(permissioned-hooks): adding unit tests to validate enforcement of permission-flags in both payer modes of PermissionedV4Router + PermissionedPositionManager

* update gas snapshot for PermissionedV4Router

* fixing snapshots and unused variable nit

* fixing snapshots one more time

* fix: validate mint recipient has LIQUIDITY_ALLOWED in PermissionedPositionManager

* fix(permissioned-position-manager): add _mapSettleAmount override for CONTRACT_BALANCE

* feat(permissioned-hooks): require verified adapter in beforeInitialize (#527)

* fix(permissioned-position-manager): updating the name and symbol of perm-posm to be different from regular posm (#529)

* fix(permissioned-position-manager): updating the name and symbol to be different from
   regular posm

* nit: fixing test name

* nit: adding dev-comment to specify eip712 domain separator

* fix(permissioned-pools): address audit findings N-01, N-02, N-06 (ECO-200, ECO-222, ECO-226)  (#530)

* fix(permissioned-position-manager): updating the name and symbol to be different from
   regular posm

* nit: fixing test name

* fix: address audit findings N-01, N-02, N-06

* lint: running linter and snapshots

* nit: fixing error notes

* fix(permissioned-position-manager): validate recipient in transferFrom (ECO-196) (#532)

Apply the same LIQUIDITY_ALLOWED check used in _mint to the `to` address
on admin-initiated transfers, so admins cannot move a permissioned
position to a recipient that is not allowlisted for each permissioned
currency in the pool.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(permissioned-pools): use solmate SafeTransferLib in PermissionsAdapter._unwrap (ECO-198) (#534)

* fix(permissioned-pools): use SafeERC20.safeTransfer in PermissionsAdapter._unwrap (ECO-198)

Replaces the bare PERMISSIONED_TOKEN.transfer call with SafeERC20.safeTransfer so that
tokens which return false on failure (or silently fail under pause/freeze logic) revert
the whole transaction instead of burning adapter supply without paying out the underlying.

Fixes ECO-198.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* upating snapshot

* refactor(permissioned-pools): use solmate SafeTransferLib in PermissionsAdapter._unwrap

Switches from OpenZeppelin's SafeERC20 to solmate's SafeTransferLib for consistency
with the rest of the repo (e.g. WstETHHook).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(permissioned-pools): alias OZ ERC20 so solmate ERC20 keeps the plain name

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(permissioned-pools): alias solmate ERC20 as SafeERC20 to avoid collision

Keeps OZ's ERC20 plain (it's the inheritance base) and aliases solmate's ERC20
as SafeERC20 for the SafeTransferLib using directive.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(permissioned-pools): update PermissionedV4Router snapshot after SafeTransferLib swap

solmate SafeTransferLib is slightly cheaper than OZ SafeERC20 on the unwrap path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: SocksNFlops <91764028+SocksNFlops@users.noreply.github.com>

* fix(permissioned-posm): enforce hook allowlist on liquidity increases (#537)

* fix(permissioned-posm): enforce hook allowlist on liquidity increases (ECO-218)

* chore(permissioned-posm): reformat with forge fmt 1.4.3 to satisfy CI

* nit(tests): fixing function name to be more clear

* refactor(permission-flags)[eco-225]: removing unused hasFlags function (#535)

* fix(permissioned-pools): move HookMinerCreate3 to test/shared (ECO-219) (#536)

* fix(permissions-adapter): tolerate tokens without standard ERC20 metadata (ECO-223) (#538)

* docs(permissioned-v4-router): adding natspec to execute() to clarify allow-revert behavior (#540)

* doc(permissioned-v4-router): adding natspec to execute() to clarify allow-revert behavior

* Update src/hooks/permissionedPools/PermissionedV4Router.sol

Co-authored-by: ens_408 <eric.sanchirico@uniswap.org>

---------

Co-authored-by: ens_408 <eric.sanchirico@uniswap.org>

* feat: emit Swap and ModifyLiquidity events from PermissionedHooks and PositionManager (#528)

* feat: emit Swap and ModifyLiquidity events from V4Router and PositionManager

Surface user-facing Swap and ModifyLiquidity events so indexers can attribute
actions to the actual end user (msg.sender) rather than the router/posm contract
address logged by PoolManager.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: formatting and update gas snapshots

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(PermissionsAdapter): pass tokenAddress to allowlist checker and emit verification deposit event

- Add `tokenAddress` parameter to `IAllowlistChecker.checkAllowlist` so a single allowlist checker can serve multiple assets without a round-trip back into the adapter to resolve which asset is being checked.
- Add `depositForVerification(amount)` and `VerificationDeposit` event to PermissionsAdapter, giving asset issuers a filterable on-chain signal for the initial verification deposit instead of a vanilla ERC20 transfer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(MockAllowlistChecker): name tokenAddress param instead of commenting it out

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(MockAllowlistChecker): derive token from tokenAddress param

The mock now matches the new interface's purpose — resolve per-token allowlist lookups from the tokenAddress Uniswap passes in, rather than a constructor-provided token. This also uncovered test_permission_flag_partial_permissions relying on the old mock's single-token quirk; the PermissionedHooks _verifyAllowlist actually requires both currencies to be allowed, so inverted the test (and renamed with _reverts suffix) to match the real behavior, consistent with test_permission_flag_partial_permissions_swap on the router side.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: update permissioned router gas snapshot

Reflects removal of the MockAllowlistChecker token SLOAD now that the mock reads tokenAddress directly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(V4Router): gate Swap event emission to permissioned-token pools

Plumb PermissionsAdapterFactory through the V4Router constructor and only
emit Swap events when at least one of the pool currencies is a verified
permissioned token. Pools composed of unpermissioned tokens no longer pay
the ~32k gas tax for event emission.

Moves the Swap event tests from V4RouterTest to PermissionedV4RouterTest,
where they exercise the only path that now emits, and updates gas snapshots.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(PermissionedHooks): emit Swap event from afterSwap hook

Moves the permissioned-pool Swap event emission out of V4Router and into
PermissionedHooks._afterSwap. V4Router no longer needs a reference to
PermissionsAdapterFactory, and unpermissioned pools pay zero incremental
gas. Emission lives where permissions are already enforced, and the hook
reads the end-user address via IMsgSender(sender).msgSender().

- Reverts V4Router constructor signature and removes Swap event wiring
- Adds AFTER_SWAP flag (bit 6) to PermissionedHooks
- Removes afterSwap from test_hooks revert expectations
- Swap event tests now assert emission from the hook address

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(IV4Router): drop unused Swap event declaration

No router in this repo emits Swap anymore — emission moved to
PermissionedHooks in the previous commit. The IV4Router declaration
was stale (NatSpec claimed router emission) and redundant with the
one on PermissionedHooks. Tests now reference PermissionedHooks.Swap
directly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* style(V4Router.t.sol): remove trailing blank line

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(PositionManager): assert ModifyLiquidity emits exactly once per action

Mint runs through the MINT_POSITION path and increase/decrease/burn run
through _modifyLiquidity. They're distinct paths today, but a future
refactor that unifies them could silently double-emit. Record logs over
each lifecycle action and assert exactly one event from address(lpm).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* style(test): drop unused amount1 decode in swap event helper

Use the blank positional slot in destructuring instead of decoding and
discarding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: update gas snapshots and apply forge fmt after merge

Regenerate snapshots from the test run and reformat long Swap event emit
and helper signatures after merging origin/permissioned-pools.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: regenerate gas snapshots with --isolate (CI profile)

Snapshots were previously written without --isolate, which the CI test
job runs with. Regenerated using forge 1.3.6 + FOUNDRY_PROFILE=ci to
match the CI snapshot check.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(permissioned-position-manager): Adds `unwindPosition` and `withdrawClaim` functionality (#542)

* fix(permissioned-position-manager): removing transferability of perm-posm positions

* feat(permissioned-position-manager): adding unwind-position + withdraw-claim functionality

* cleaning up implementation

* updating action names to be consistent

* fix(permissioned-position-adapter): removing default-recipient parameter in unwind-position fn

* fix(permissioned-position-manager): updating position-unwound event to emit once per leg to provide more info about actual recipient and asset distribution during the unwind

* nits: cleaning up nits

* nit: fixing natspec of withdrawClaim()

* fix(permissioned-position-manager): sweep underlying for adapter currencies (ECO-197) (#533)

* fix(permissioned-position-manager): sweep underlying for adapter currencies (ECO-197)

When TAKE(adapter, ADDRESS_THIS, ...) runs, PermissionsAdapter._update auto-unwraps
on transfer out of the PoolManager, so POSM ends up holding the underlying
permissioned token rather than the adapter token. The base _sweep only looks at
the adapter currency balance (0) and leaves the underlying stranded, where a
later caller could claim it.

Override _sweep in PermissionedPositionManager to resolve verified adapter
currencies to their underlying token before sweeping.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* nit: running

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: SocksNFlops <91764028+SocksNFlops@users.noreply.github.com>

* fix(Permissioned-position-manager): unsubscribing on unwind (#543)

* fix(permissioned-position-manager): unsubscribing in unwind-position

* nit: adding subscribe action placeholder

* Socksnflops/eco 194 followup block unverified adapter init (#544)

* fix(permissioned-hooks): unverified-adapter follow-up

* chore: trigger CI

* refactor(PositionManager): rename ModifyLiquidity event to ModifyPosition (#546)

PoolManager already declares an `event ModifyLiquidity(PoolId, address, int24, int24, int256, bytes32)` with the same parameter types as the one PR #528 added to PositionManager. Any downstream contract that inherits from both — e.g. universal-router's `ImportsForTypechain.sol` shim that pulls both into the hardhat build — fails to compile with `DeclarationError: Event with same name and parameter types defined twice`.

Rename the periphery event to `ModifyPosition` so downstream consumers don't have to work around the collision. Same parameters, same emit sites, same semantics (`sender` is the end user, not the position manager).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(permissioned-routing): strip PermissionedV4Router to abstract contract (#526)

* fix(permissioned-hooks): enforce permission-flags in both payer modes of PermissionedV4Router + PermissionedPositionManager

* tests(permissioned-hooks): adding unit tests to validate enforcement of permission-flags in both payer modes of PermissionedV4Router + PermissionedPositionManager

* fixing snapshots and unused variable nit

* refactor(permissioned-routing): strip PermissionedV4Router to abstract contract

* fix(ci): format MockPermissionedRouter

* fix(ci): format MockPermissionedRouter and fix snapshot

* lint: fixing lint issue in tests

* Add per hop slippage checks (#497)

* remove hooks and move to hook repo (#510)

* remove hooks and move to other repo

* remove hook script

* fix snapshots

* fix: resolve zizmor GitHub Actions security findings (#515)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Add warning comments to deltas functions (#517)

* feat: add per-hop slippage to single swaps and flip to output/input ratio (#516)

* fix: increase per-hop slippage precision from 1e18 to 1e36

The 1e18 precision caused the price calculation to truncate to 0 for
token pairs with extreme value ratios (e.g. WBTC/PEPE), silently
disabling the slippage check. 1e36 provides sufficient precision
while remaining overflow-safe (uint128_max * 1e36 < uint256_max).

Adds regression test with an extreme price pool verifying the check
still fires for high ratio pairs.

* feat: add per-hop slippage checks to single swap commands

Adds uint256 maxHopSlippage field to ExactInputSingleParams and
ExactOutputSingleParams. When non-zero, enforces a price-based
(amountIn * PRECISION / amountOut) slippage check. This prevents
sandwich attacks when single swaps follow V2/V3 hops with positive
slippage in the Universal Router.

Introduces V4TooLittleReceivedPerHopSingle and
V4TooMuchRequestedPerHopSingle errors for the single swap case.

* refactor: flip per-hop slippage to output/input ratio with min check

Changes price formula from amountIn/amountOut (revert if > max) to
amountOut/amountIn (revert if < min). This makes 0 a natural disabled
value for single swap maxHopSlippage since any real output/input ratio
is always > 0, removing the need for an explicit != 0 guard.

* chore: update gas snapshots

* lint

* OZ L04

* snapshots

* oz L05

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* oz N08

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* PR comments

* oz l07

* bytecode hash snapshot

---------

Co-authored-by: Alice Henshaw <henshawalice@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Alice <34962750+hensha256@users.noreply.github.com>

* bump version (#518)

* docs: update BaseHook in README (#519)

* fix: pin npm to specific version in deploy workflow (#520)

* fix: pin npm to specific version in deploy workflow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: pin npm install to git commit SHA instead of registry version

Use github:npm/cli#<sha> to bypass the npm registry entirely,
preventing supply chain attacks on the package manager itself.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(migration): migrating out PermissionedPoolHooks dependency

---------

Co-authored-by: Daniel Gretzke <daniel@gretzke.de>
Co-authored-by: diana <diana.kocsis@uniswap.org>
Co-authored-by: David Gilman <david.gilman@uniswap.org>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Alice Henshaw <henshawalice@gmail.com>
Co-authored-by: Alice <34962750+hensha256@users.noreply.github.com>
Co-authored-by: Alan Wu <60207036+alanhwu@users.noreply.github.com>

* fix(permissioned-pools)[eco-354]: dedupe duplicate statements in Perm… (#548)

* fix(permissioned-pools)[eco-354]: dedupe duplicate statements in PermissionedPositionManager._pay()

* chore: updating snapshots

* fix(permissioned-pools)[eco-352]: use OZ ERC165Checker for IAllowlistChecker validation (#549)

* fix(permissioned-position-manager): reject mints without verified-adapters (#550)

* fix(permissioned-position-manager): reject mints in ordinary pools (ECO-344)

* chore: running forge fmt

* docs(permissions-adapter)[eco-343]: document allowedWrappers trust requirement (#551)

* docs(permissioned-pools)[eco-343]: document allowedWrappers trust anchor on IPermissionsAdapter

* nit: adding back in example natspec comments

* fix(permissioned-pools): block permissioned _pay when swapping disabled (#552)

* fix(permissioned-position-manager): enforce owner LIQUIDITY_ALLOWED on increase (#554)

* fix(permissions-adapter): reject pool-manager self-transfer/unwrap (#553)

* fix(permissions-adapter): reject pool-manager self-transfer (ECO-340)

* chore: updating snpathos

* fix(permissioned-position-manager): require BURN_6909 owner matches action executor (#555)

* fix(permissioned-position-manager): require BURN_6909 owner matches action executor

* chore: fix snapshot and shorten test name for forge fmt

* fix(permissioned-position-manager)[eco-424]: authorize UNSUBSCRIBE and UNWIND_WITH_FALLBACK actions (#556)

* fix(permissioned-hooks): enforce permission-flags in both payer modes of PermissionedV4Router + PermissionedPositionManager

* tests(permissioned-hooks): adding unit tests to validate enforcement of permission-flags in both payer modes of PermissionedV4Router + PermissionedPositionManager

* fixing snapshots and unused variable nit

* refactor(permissioned-routing): strip PermissionedV4Router to abstract contract

* fix(ci): format MockPermissionedRouter

* fix(ci): format MockPermissionedRouter and fix snapshot

* lint: fixing lint issue in tests

* Add per hop slippage checks (#497)

* remove hooks and move to hook repo (#510)

* remove hooks and move to other repo

* remove hook script

* fix snapshots

* fix: resolve zizmor GitHub Actions security findings (#515)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Add warning comments to deltas functions (#517)

* feat: add per-hop slippage to single swaps and flip to output/input ratio (#516)

* fix: increase per-hop slippage precision from 1e18 to 1e36

The 1e18 precision caused the price calculation to truncate to 0 for
token pairs with extreme value ratios (e.g. WBTC/PEPE), silently
disabling the slippage check. 1e36 provides sufficient precision
while remaining overflow-safe (uint128_max * 1e36 < uint256_max).

Adds regression test with an extreme price pool verifying the check
still fires for high ratio pairs.

* feat: add per-hop slippage checks to single swap commands

Adds uint256 maxHopSlippage field to ExactInputSingleParams and
ExactOutputSingleParams. When non-zero, enforces a price-based
(amountIn * PRECISION / amountOut) slippage check. This prevents
sandwich attacks when single swaps follow V2/V3 hops with positive
slippage in the Universal Router.

Introduces V4TooLittleReceivedPerHopSingle and
V4TooMuchRequestedPerHopSingle errors for the single swap case.

* refactor: flip per-hop slippage to output/input ratio with min check

Changes price formula from amountIn/amountOut (revert if > max) to
amountOut/amountIn (revert if < min). This makes 0 a natural disabled
value for single swap maxHopSlippage since any real output/input ratio
is always > 0, removing the need for an explicit != 0 guard.

* chore: update gas snapshots

* lint

* OZ L04

* snapshots

* oz L05

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* oz N08

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* PR comments

* oz l07

* bytecode hash snapshot

---------

Co-authored-by: Alice Henshaw <henshawalice@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Alice <34962750+hensha256@users.noreply.github.com>

* bump version (#518)

* docs: update BaseHook in README (#519)

* fix: pin npm to specific version in deploy workflow (#520)

* fix: pin npm to specific version in deploy workflow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: pin npm install to git commit SHA instead of registry version

Use github:npm/cli#<sha> to bypass the npm registry entirely,
preventing supply chain attacks on the package manager itself.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(migration): migrating out PermissionedPoolHooks dependency

* fix(permissioned-position-manager)[eco-424]: authorize UNSUBSCRIBE and UNWIND_WITH_FALLBACK actions

* chore(permissioned-position-manager): tighten unwind params comment

* refactor(permissioned-position-manager): inline UNWIND_WITH_FALLBACK auth predicate

* chore: running forge fmt

* chore(permissioned-position-manager): remove issue ref from test comment

---------

Co-authored-by: Daniel Gretzke <daniel@gretzke.de>
Co-authored-by: diana <diana.kocsis@uniswap.org>
Co-authored-by: David Gilman <david.gilman@uniswap.org>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Alice Henshaw <henshawalice@gmail.com>
Co-authored-by: Alice <34962750+hensha256@users.noreply.github.com>
Co-authored-by: Alan Wu <60207036+alanhwu@users.noreply.github.com>

* fix(permissioned-position-manager): resolve recipient sentinel before emitting ClaimWithdrawn (#557)

* fix(permissioned-position-manager): resolve recipient sentinel before emitting ClaimWithdrawn

* chore: revert stray gas snapshot drift

* fix(permissions-adapter): use safeTransferFrom in depositForVerification (#558)

* fix(permissions-adapter): use safeTransferFrom in depositForVerification

* chore(snapshots): bump router gas snapshot for safeTransferFrom adapter bytecode shift

* build(foundry): drop posm optimizer_runs to 500 to fit PermissionedPositionManager size

* chore: updating gas snapshots

---------

Co-authored-by: ens_408 <eric.sanchirico@uniswap.org>
Co-authored-by: SocksNFlops <91764028+SocksNFlops@users.noreply.github.com>
Co-authored-by: Cody Born <cody.born@uniswap.org>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: diana <diana.kocsis@uniswap.org>
Co-authored-by: David Gilman <david.gilman@uniswap.org>
Co-authored-by: Alice Henshaw <henshawalice@gmail.com>
Co-authored-by: Alice <34962750+hensha256@users.noreply.github.com>
Co-authored-by: Alan Wu <60207036+alanhwu@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants