Skip to content

feat: add MetaSwap batch calldata enforcer - #201

Open
hanzel98 wants to merge 1 commit into
mainfrom
feat/metaswap-batch-calldata-enforcer
Open

feat: add MetaSwap batch calldata enforcer#201
hanzel98 wants to merge 1 commit into
mainfrom
feat/metaswap-batch-calldata-enforcer

Conversation

@hanzel98

@hanzel98 hanzel98 commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add MetaSwapBatchCalldataEnforcer for canonical DelegationManager batch redemptions.
  • Let one delegation authorize an ERC-20 approval plus MetaSwap call, an approval reset plus approval and swap, or one native-token swap.
  • Integrate deployment into the shared caveat-enforcer deployment script and document the security assumptions in the existing enforcer guide.

Behavior

Signed terms bind the MetaSwap target, input token, input amount, and approval shape. The enforcer requires BATCH_DEFAULT_MODE and accepts only:

Native:       [MetaSwap.swap{ value: amount }(...)]
ERC-20:       [token.approve(metaSwap, amount), MetaSwap.swap(...)]
ERC-20 reset: [token.approve(metaSwap, 0), token.approve(metaSwap, amount), MetaSwap.swap(...)]

The swap target, value, selector, tokenFrom, and amount must match the terms. aggregatorId and route data remain flexible.

Usage

bytes memory terms = abi.encodePacked(
    metaSwap,
    tokenIn,
    tokenInAmount,
    bytes1(resetApproval ? 1 : 0)
);

Caveat memory caveat = Caveat({
    enforcer: address(metaSwapBatchCalldataEnforcer),
    terms: terms,
    args: hex""
});

// Redeem ExecutionLib.encodeBatch(executions) with ModeLib.encodeSimpleBatch().

Add LimitedCallsEnforcer to make the delegation one-shot. Add ERC20BalanceChangeEnforcer or NativeBalanceChangeEnforcer to enforce tokenOutMin.

Security

The delegate is trusted to choose safe swap data: aggregatorId and route data are supplied by the redeemer and are not restricted by this enforcer. The enforcer binds the direct input approval and MetaSwap input fields, but it does not validate route side effects, adapter behavior, output amount, or a mutable MetaSwap implementation. A residual allowance may remain if MetaSwap spends less than the approved amount.

Deployment and verification

The enforcer is deployed through the existing shared script:

forge script script/DeployCaveatEnforcers.s.sol \
  --rpc-url <rpc_url> --private-key $PRIVATE_KEY --broadcast

The existing script/verification/verify-enforcer-contracts.sh remains the canonical verification flow after deployed addresses are recorded. No enforcer-specific script or environment variable is introduced.

Test plan

  • ERC-20 approve(amount) + swap
  • ERC-20 approve(0) + approve(amount) + swap
  • Native-token swap{ value: amount }
  • Canonical DelegationManager redemption
  • Flexible aggregatorId and route data
  • Tampered target, value, selector, token, amount, approval, mode, terms, and batch shape
  • Composition with LimitedCallsEnforcer and ERC20BalanceChangeEnforcer
  • 100% enforcer line, statement, branch, and function coverage
  • 1,058 non-fork regression tests passing

Note

Medium Risk
New delegation path can approve up to a signed ERC-20 amount and call a configured MetaSwap contract while route data remains delegate-controlled; output and adapter behavior are not enforced by this caveat alone.

Overview
Adds MetaSwapBatchCalldataEnforcer, a new caveat enforcer that lets a delegator sign off on a single BATCH_DEFAULT_MODE redemption with a fixed MetaSwap input story while the redeemer still picks routing.

Signed terms pack metaSwap, tokenIn (address(0) = native), tokenInAmount, and an optional reset-approval flag. beforeHook accepts only one of three batch shapes: native swap with matching msg.value, ERC-20 approve + swap, or approve(0) + approve(amount) + swap. It pins targets, values, approve spender/amounts, and swap tokenFrom/amount; aggregatorId and route data stay unconstrained.

The shared DeployCaveatEnforcers script deploys the new singleton, CaveatEnforcers.md documents behavior and trust assumptions (route/output not validated; residual allowance possible; compose with LimitedCallsEnforcer and balance-change enforcers). Tests cover validation edge cases, end-to-end DelegationManager redemption, and composition with ERC20BalanceChangeEnforcer / LimitedCallsEnforcer.

Reviewed by Cursor Bugbot for commit 579dd26. Bugbot is set up for automated code reviews on this repo. Configure here.

@hanzel98
hanzel98 requested a review from a team as a code owner August 29, 2026 23:53
@hanzel98
hanzel98 force-pushed the feat/metaswap-batch-calldata-enforcer branch from b9e7058 to 6086455 Compare August 30, 2026 00:13
Bind swap inputs and approval shape while preserving redeemer-selected route data for limit orders.
@hanzel98
hanzel98 force-pushed the feat/metaswap-batch-calldata-enforcer branch from 6086455 to 579dd26 Compare August 30, 2026 18:33
@hanzel98 hanzel98 self-assigned this Aug 30, 2026
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