Skip to content

fix(universal-router-sdk): mask ALLOW_REVERT_FLAG bit in commandParser - #700

Open
gomesalexandre wants to merge 2 commits into
Uniswap:mainfrom
gomesalexandre:fix_urs_allowrevertflag_mask
Open

gomesalexandre wants to merge 2 commits into
Uniswap:mainfrom
gomesalexandre:fix_urs_allowrevertflag_mask

Conversation

@gomesalexandre

Copy link
Copy Markdown

Found by inspection, no filed issue.

GenericCommandParser.getCommands parsed each command byte straight into a
CommandType enum value without stripping the ALLOW_REVERT_FLAG bit (0x80) that
routerCommands.ts ORs onto revertible commands like EXECUTE_SUB_PLAN. Real
calldata containing such a command (e.g. a sub-plan encoded as 0x21 | 0x80 = 0xa1) has no matching entry in commandDefinition, so parse() throws
TypeError: Cannot read properties of undefined (reading 'parser').

This mirrors on-chain dispatch semantics: Commands.sol defines
FLAG_ALLOW_REVERT = 0x80 and COMMAND_TYPE_MASK = 0x7f, and Dispatcher.sol:57
dispatches via uint8(commandType & Commands.COMMAND_TYPE_MASK). The parser should
mask the same way the contract does before treating the byte as a command type -
this PR makes it do that.

Fix

  • Export ALLOW_REVERT_FLAG from routerCommands.ts (was previously unexported;
    no other package in the monorepo depends on it, and it's not re-exported from
    src/index.ts, so nothing new enters the public API surface).
  • In commandParser.ts, mask it off before the enum cast: parseInt(byte, 16) & ~ALLOW_REVERT_FLAG.
  • Sibling bug, same root cause: test/utils/uniswapData.ts's parseCommands used a
    stale & 0x3f mask, which already silently zeroed out ACROSS_V4_DEPOSIT_V3 = 0x40 on its own terms (independent of this fix - 0x40 & 0x3f === 0). Fixed to
    use the same correct mask.

EXECUTE_SUB_PLAN is the only command type the SDK's own encoder can ever set this
flag on (REVERTIBLE_COMMANDS gates it, addCommand throws for allowRevert on
anything else), so the new test covers the one case the SDK itself can produce -
masking every byte unconditionally is still correct and contract-faithful for
arbitrary/foreign calldata, matching Dispatcher.sol's own unconditional mask.

Parse output (UniversalRouterCommand) intentionally doesn't surface whether the
flag was set on a given command - that field never existed in the type, and adding
it would be a separate API addition, not part of this fix.

Testing

New regression test in commandParser.test.ts, using the real
RoutePlanner().addSubPlan() codepath (not a hand-crafted byte) - builds a
WRAP_ETH sub-plan, asserts the encoded command byte is literally 0xa1, then
asserts CommandParser.parseCalldata resolves it back to
CommandType.EXECUTE_SUB_PLAN without throwing.

Guard-validated: reverted just the getCommands masking, confirmed the new test
fails with the exact TypeError described above, restored, confirmed 22/22 pass in
that file. Full hardhat test suite: 477 passing, 1 failing - the failure is
uniswapTrades.test.ts's mainnet-fork before all hook hitting a 403 from a public
RPC (no FORK_URL configured in this environment), isolated as pre-existing and
unrelated via git stash comparison (identical failure with zero diff applied).
tsc --noEmit and lint both clean. Changeset added (patch,
@uniswap/universal-router-sdk).

gomesalexandre and others added 2 commits August 18, 2026 19:51
GenericCommandParser.getCommands() parsed each command byte straight
into a CommandType enum value without stripping the ALLOW_REVERT_FLAG
bit (0x80) that routerCommands.ts ORs onto revertible commands like
EXECUTE_SUB_PLAN. Real calldata containing such a command (e.g. a
sub-plan encoded as 0x21 | 0x80 = 0xa1) has no matching entry in
commandDefinition, so parse() throws TypeError reading 'parser' off
undefined.

Mask the flag off before the enum cast, using the newly-exported
ALLOW_REVERT_FLAG constant instead of a magic number. Also fixes the
same stale-mask bug in test/utils/uniswapData.ts's parseCommands
helper, which used `& 0x3f` - a mask that already silently zeroed out
ACROSS_V4_DEPOSIT_V3 (0x40) on its own terms.

Co-Authored-By: Claude <noreply@anthropic.com>
@gomesalexandre
gomesalexandre requested a review from a team as a code owner August 18, 2026 17:55
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.

1 participant