Skip to content

feat: add MetaSwap delegation manager for exact batches and limit orders - #207

Open
hanzel98 wants to merge 3 commits into
mainfrom
feat/metaswap-order-delegation-manager
Open

hanzel98 wants to merge 3 commits into
mainfrom
feat/metaswap-order-delegation-manager

Conversation

@hanzel98

@hanzel98 hanzel98 commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Adds MetaSwapOrderDelegationManager for two flows: exact batches and MetaSwap limit orders.
  • An exact batch signs the batch hash, so the submitter cannot change a target, value, or calldata byte. A limit order signs the settlement bounds, and the redeemer supplies the route.
  • Redemption costs less because these flows no longer go through the generic delegation loop and external caveat hooks. The checks those flows need are enforced by the manager.
Flow User signs Submitter may change
Exact batch The batch hash Nothing
Limit order Tokens, amounts, approval shape, recipient, redeemers, optional timestamp and id The route and aggregator id

A limit order still requires a listed redeemer, checks the approval and swap shape, and requires the signed minimum output. disabledDelegations remains the one-shot for both flows. A failed fill rolls back.

Gas

Estimated transaction gas: 21,000 + calldata + execution. Each row is the same swap. Measured cold (one approach per test).

Exact batches

Today the signed call is a self-call into the 7702 account, and that call runs the batch. The order manager signs the batch hash and runs the batch directly.

Swap Delegation Manager self-call Order manager Saved
Native ETH in, token out 207,406 119,846 87,560 (42.2%)
Token in, native ETH out 244,371 150,307 94,064 (38.5%)
Token in, token out 267,138 172,834 94,304 (35.3%)
Token in, token out, with reset 280,704 179,889 100,815 (35.9%)

Limit orders

Today this is Calldata-pinned dual-root on DelegationManager: an approval root, then a swap root, redeemed in one call. The flexible settlement enforcer collapses that into one delegation. This manager does too, and it is cheaper than both.

The dual-root baseline uses IdEnforcer for the one-shot. The approval root uses id 2 and the swap root uses id 1, because both are redeemed in the same call. The flexible settlement enforcer requires a non-zero id. Swap tokenFrom and amount are pinned with AllowedCalldataEnforcer (same fields this manager validates).

Swap Calldata-pinned dual-root DelegationManager + flexible settlement enforcer Order manager
Native ETH in, token out 379,436 180,314 154,901
Token in, native ETH out 504,458 230,748 185,252
Token in, token out 510,530 235,363 209,757
Token in, token out, with reset 535,951 244,069 218,424

Saved against Calldata-pinned dual-root:

Swap Enforcer Order manager
Native ETH in, token out 199,122 (52.5%) 224,535 (59.2%)
Token in, native ETH out 273,710 (54.3%) 319,206 (63.3%)
Token in, token out 275,167 (53.9%) 300,773 (58.9%)
Token in, token out, with reset 291,882 (54.5%) 317,527 (59.2%)

The order manager is also cheaper than the enforcer on every shape:

Swap Saved Percent
Native ETH in, token out 25,413 14.1%
Token in, native ETH out 45,496 19.7%
Token in, token out 25,606 10.9%
Token in, token out, with reset 25,645 10.5%

Scope

  • One root delegation, one caveat, batch mode
  • An exact batch has no on-chain expiry. A limit order can set a timestamp window
  • Every id, including 0, burns the id bitmap for the delegator and blocks sibling orders that share it. The delegation hash is still one-shot as well. Cancelling does not burn the id
  • No delegation chains, try mode, pause, or enableDelegation
  • An EIP-7702 account that uses this manager redeems through it instead of DelegationManager

Test plan

  • MetaSwapOrderDelegationManagerTest passes, including both flows and the redemption guards
  • Dry-run script/DeployMetaSwapOrderDelegationManager.s.sol
  • Confirm the verify script reads META_SWAP_ORDER_DELEGATION_MANAGER_ADDRESS

@hanzel98
hanzel98 requested a review from a team as a code owner September 11, 2026 01:50

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8e74755. Configure here.

@hanzel98
hanzel98 force-pushed the feat/metaswap-order-delegation-manager branch from fa2e317 to 68a79cb Compare October 1, 2026 20:21
@hanzel98 hanzel98 changed the title feat: experimental MetaSwap order delegation manager feat: MetaSwap Order Delegation Manager Oct 1, 2026
One manager for gasless swaps and limit orders, with tests, deployment, and verification.
@hanzel98
hanzel98 force-pushed the feat/metaswap-order-delegation-manager branch from 68a79cb to 28c3d2e Compare October 1, 2026 20:25
@hanzel98 hanzel98 changed the title feat: MetaSwap Order Delegation Manager feat: add MetaSwap delegation manager for gasless swaps and limit orders Oct 1, 2026
The hash commits the whole batch, including each target and value, so ExactCalldata described only part of it.
@hanzel98 hanzel98 changed the title feat: add MetaSwap delegation manager for gasless swaps and limit orders feat: add MetaSwap delegation manager for exact batches and limit orders Oct 6, 2026
@hanzel98 hanzel98 self-assigned this Oct 13, 2026
Remove the id==0 bitmap skip so every FlexibleSettlement id burns the
bitmap and excludes siblings. Keep flexible gas rows on a nonzero id so
measurements stay comparable to the comparison harness.
@hanzel98

Copy link
Copy Markdown
Contributor Author

Calldata-pinned dual-root — how the delegations are built

Two root delegations in one redeemDelegations call (native input: swap root only).

Approval root (ERC-20 in only; single approve, or batch [approve(0), approve(amount)] for reset):

Enforcer Purpose
ExactExecution / ExactExecutionBatch Pins the exact approval execution(s) (target, value, calldata)
IdEnforcer One-shot for this approval root (distinct id from the swap)
RedeemerEnforcer Only allowlisted redeemers may redeem

Swap root (always single MetaSwap.swap):

Enforcer Purpose
AllowedTargetsEnforcer Target must be MetaSwap
AllowedMethodsEnforcer Selector must be swap
ValueLteEnforcer (ERC-20 in) / NativeTokenTransferAmountEnforcer (native in) Cap msg.value (0 for ERC-20 in; order size for native in)
AllowedCalldataEnforcer Pins tokenFrom + amount at callData[36:100] (one enforcer, 64 bytes). Leaves aggregatorId / route data free for the redeemer
ERC20BalanceChangeEnforcer or NativeBalanceChangeEnforcer Min token-out (balance increase ≥ tokenOutMin)
TimestampEnforcer Order expiry window
IdEnforcer One-shot / mutual exclusion for the order id
RedeemerEnforcer Same redeemer allowlist as approval

Same signed settlement bounds the flexible enforcer and this manager enforce in one place; dual-root still pays two signatures/chains and external caveat hooks.

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