You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: gate nested V4_SWAP behind an explicit opt-in entrypoint
#491 ran V4 actions inside a foreign PoolManager unlock whenever
poolManager.isUnlocked() was true, inferring consent from chain state. All V4
deltas accrue under the router's address for the lifetime of that unlock while
the router's own locker clears between external calls, so one call could leave
unpaid debt that a later call's SETTLE_ALL or OPEN_DELTA settlement paid from a
different caller's funds (audit finding M-01).
Consent now lives on the entrypoint. executeNested sets a transient flag for the
duration of the call and the nested branch requires it, so only a caller that
opened the surrounding unlock can reach the path. It cannot live in the payload:
EXECUTE_SIGNED_TYPEHASH commits to keccak256(commands), and a command byte would
let the route author, rather than the composer whose capital is at risk, make the
decision. Plain execute now reverts NestedExecutionNotPermitted instead.
Signed routes cannot nest in this version. executeSignedNested would restore
that, but costs 252 bytes against 473 remaining, and the RESOLVE proposal needs
the same headroom. Documented on executeNested as a deliberate omission.
Also carries the calldata re-encode (BP-1/2/3): the v4 parameter decoders follow
struct offsets without bounding them against the enclosing input, so forwarding
a slice of transaction calldata let them read bytes the signature never covered.
executeV4SwapWithinUnlock re-enters through the ABI encoder so calldata ends
where the input ends. This stays load-bearing under the opt-in, since an attacker
can opt themselves in.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0 commit comments