feat: DelegatedMaker — one-transaction, no-signature, no-escrow maker flow - #432
Merged
deacix merged 1 commit intoAug 5, 2026
Conversation
… flow Co-authored-by: Sergej Kunz <info@deacix.de>
deacix
marked this pull request as ready for review
August 5, 2026 07:46
deacix
merged commit Aug 5, 2026
df9c8e4
into
cursor/fusion-announcement-anchored-auction-9252
3 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #430 — the announcement-anchored auction and the maker-only, signature-free
OrderRegistratorthis contract builds on. The base branch iscursor/fusion-announcement-anchored-auction-9252; GitHub retargets this PR tomasterwhen #430 merges, and since the repo merges with merge commits the diff collapses to theDelegatedMakerchanges with no rebase.Split out of #430 deliberately: this is the one custody-adjacent surface of the feature set. A contract that concentrates standing allowances reviews differently from pure pricing logic, so the auction core and this helper each get a review pass matched to their risk. Nothing in #430 references this contract; it composes from the outside like any other maker.
What it is
DelegatedMakergives ERC-20 makers the flow the feature set was built toward — one transaction per order, no signature, no escrow. One shared contract is the maker for all of its users (CoW Protocol's presign plus its VaultRelayer just-in-time pull, as prior art):createOrderrecords the caller as the order's owner (the presign read byisValidSignature), validates that the order names this contract as maker, the remaining invalidator (the bit-invalidator nonce space would be shared across users) and a pre-interaction routed to this contract, then announces — anchoring the auction clock at the create block. Funds stay in the owner's wallet until the fill: the pre-interaction pulls exactly the filled amount through the owner's standing allowance immediately before the maker-asset transfer, topping up the protocol's own allowance for the maker asset whenever it runs short (this lazy top-up replaced a one-shotapproveRouterstep during #430's pruning rounds, fixing the latent depletion defect for tokens that decrement even infinite allowances), so custody time is zero and the contract holds no balances between transactions.createOrderWithPermitfolds the owner's EIP-2612 allowance into the create transaction itself — owner pinned to the caller, failures swallowed the way the protocol treats maker permits, so a front-run permit changes nothing and a wrong one leaves an order that a later approval revives.cancelOrderdeletes the presign and invalidates at the protocol as the maker — both load-bearing, since the core validates ERC-1271 only on the first fill (OrderMixin._fillContractOrder) — and the duplicate check reads the registrator's ever-registered state, so a cancelled order cannot be re-created into a presigned-but-protocol-dead shell. The contract concentrates standing allowances (the same trust shape as the router itself) and has no owner powers over user funds.Proceeds must route to the creator, one way or the other. The default rule is
receiver == creator. A non-creator receiver is accepted in exactly one shape — fee collection: the order's post-interaction must target the receiver itself (which isFeeTaker's own distribution condition,order.receiver.get() == address(this)), carry the custom-receiver flag, and name the creator in the custom-receiver slot, with the post-interaction maker trait set. That guarantees a conformingFeeTaker-layout settlement forwards the net to the wallet that funded the order; anything else — a zero receiver, missing hook trait, short fee bytes, a foreign target or a foreign custom receiver — revertsInvalidFeeReceiver, because each of those strands or diverts the taking asset (FeeTakerwould otherwise pay the maker's share toorder.maker, the shared contract, which deliberately cannot withdraw). The fee contract itself is the creator's choice and trust; naming a hostile one harms only the creator, whose funds alone back the order.How it fits among the flows
The Safe and classic-EOA lanes ship in #430; this PR adds the top lane and the just-in-time pull row.
Open product decision: the fee shape
The fee-collection validation is the most coupling-heavy part of this contract — it mirrors
FeeTaker's custom-receiver byte layout (_CUSTOM_RECEIVER_OFFSET = 61), the only place in the stack that depends on another contract's internals. It is kept because production Fusion orders route fees through the settlement-as-receiver, which is exactly this shape. If a feeless v1 for delegated orders is acceptable (the quoter zeroes integrator and protocol fees for them), the whole validation collapses to the one-linereceiver == creatorrule and the coupling disappears; re-adding it later is purely additive. Flagging the choice here rather than deciding it.Tests
18 unit tests against
LimitOrderProtocol, restored byte-identical from #430's pre-split state: the one-transaction lifecycle (create → anchored at the create block → filled with the empty signature, wallet untouched until the fill, the shared contract holding nothing before or after, and the protocol allowance self-provisioned on the first fill); exact just-in-time pulls across partial fills with proceeds landing on the owner directly; every creation rule rejected (wrong maker, zero receiver, bit-invalidator traits, missing pre-interaction hook, foreign pre-interaction target, duplicate hash, cancel-then-recreate); fee collection in the full production shape — fee taker as receiver paying the creator as custom receiver, anchored auction through the fee taker's getters, exclusivity through its post-interaction tail, the pull on the same order — plus five fee-shape reverts (missing flag, foreign custom receiver, foreign target, short bytes, missing hook trait) with a positive control; both permit paths — the allowance folded into one create transaction, and a front-run permit swallowed with the order still created and fillable; cancellation of a half-filled order killing the rest at the protocol (the first-fill-only ERC-1271 nuance); pull authorization (non-protocol callers and ownerless orders both refused); and two users on the shared maker isolated from each other's orders and cancellations.2 mainnet-fork integration tests (the fourth and fifth of the stack's five,
yarn test:forkwithMAINNET_RPC_URLset):DelegatedMakerflow end to end through the live Aggregation Router V6: allowance, onecreateOrdertransaction, and a fill that pulls just in time — nothing signed, nothing escrowed, the protocol allowance self-provisioned inside the fill;0x2Ad5004c60e16E54d5007C80CE329Adde5B51Ef5): the settlement is the order's receiver and pays the creator as its custom receiver, with a deliberate surplus fee set — the fill's curve premium lands above the scaled estimate and the settlement takes exactlysurplusFee%of it, pinning the surplus-on-premium interaction feat: announcement-anchored Fusion auction, volume-ladder fill pricing, and signature-free maker flows #430's composition notes reference.On this branch the full stack holds 100% coverage across statements, branches, functions and lines on all three contracts (
DelegatedMaker, plus #430'sFusionAnchoredAuctionandOrderRegistrator); 239 unit tests and all 5 fork tests pass.Review trail
The contract went through #430's seven Bugbot rounds while it lived there — including the round dedicated to the maker-only authorization and the round dedicated to the fee shape and permit paths — plus an eighth round on this split, focused on the restore commit and the stack's consistency (registrator two-argument
registerOrder, theannouncedAtduplicate check, the pull path, the fee offsets, permit owner-pinning, the dual-action cancel): no findings. The Slither run and the audit cross-checks recorded in #430's trail covered this contract as well.Follow-ups
FeeTaker's custom-receiver layout by design; if the layout ever moves, the offsets in_createOrdermove with it.