Repository navigation
Conversation
The executor runs a hop through an inner executor the router approved, then asks the subsidy payer to pay a signed subsidy in the hop's output token to where the Dispatcher measures the hop's output, so the Dispatcher counts it. It points the router's callback slot at the inner executor, so callbacks reach it directly. A payer revert emits SubsidyPaymentFailed and the swap goes on. The Dispatcher's callback slot constant becomes internal, so a test can compare it with the executor's copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The caller picks the inner executor, so the model adds a TychoSubsidizing variant whose transfer data, swap and funds-expected address forward to any other modeled executor. Its swap writes the inner executor into the callback slot, so callbacks go to it. The subsidy payer is not modeled. The model reports no suspicious outcomes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each test runs a swap through the wrapper and compares it with the same swap without it, against a mock payer that pays and records its arguments. They cover callback and transfer inner executors, splits, sequential routes, wrap and unwrap ends, fees and slippage capture, vault credit, minAmountOut, a reverting payer, rejected inner executors, and a guard that the wrapper's callback slot matches the Dispatcher's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pper The data offsets are used only as calldata slice bounds, and the payment-failed event cannot be reentered because every router entry point is nonReentrant. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cover the swap data layout, the callback-slot write, paying into the Dispatcher's measureAt, and why the payer holds the funds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed subsidies through the router: tampered inner data, an expired deadline and the unsigned placeholder skip with the payer's reason; a contract caller pays whether bound to the contract or the sending EOA; a relayed call and a front-run copy skip without spending the nonce, and the user's own transaction still pays; a replay, a paused payer and an empty payer fill without the subsidy; a receiver that rejects ETH fails the same way as the swap without the wrapper. Shared helpers move into TychoSubsidizingTestBase. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Troshchk
force-pushed
the
kt/ENG-6414-subsidizing-executor
branch
from
October 19, 2026 14:17
a796200 to
15b213f
Compare
This branch has not been deployed
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.
Adds
TychoSubsidizingExecutor, a registered executor that wraps one hop. It runs the hop through an approved inner executor, then callsSubsidyPayer.payto pay a signed subsidy in the hop's output token. ENG-6414.Based on the
SubsidyPayerPR (lp/subsidy-payer).What it does
[boundTo: 20][amount: 16][minAmountIn: 16][nonce: 32][deadline: 6][signature: 65][innerExecutor: 20][innerData].innerSwapHashiskeccak256(innerExecutor ‖ innerData).getTransferDataandfundsExpectedAddressforward to it and map the wrapper's own address back to the router._CURRENTLY_SWAPPING_EXECUTOR_SLOT, so pool callbacks reach the inner executor.measureAt(outputToRouter ? router : receiver) insideswap(), so the Dispatcher counts the subsidy as hop output.payruns intry/catch. A payer revert emitsSubsidyPaymentFailed(nonce)from the router and the swap fills without the subsidy.Other changes:
Dispatcher._CURRENTLY_SWAPPING_EXECUTOR_SLOTis nowinternal, sotestCallbackSlotMatchesDispatchercan compare it with the wrapper's copy. The router's deployed bytecode is unchanged.TychoSubsidizingvariant that forwards to any modelled executor. It reports 0 suspicious outcomes.crates/tycho-execution/CLAUDE.mdgets a "Subsidies" section.Tests
forge test --match-contract TychoSubsidizing: 35 pass. Slither reports no findings.TychoSubsidizingExecutorTest(24) uses a mock payer that pays and records its arguments, and compares each wrapped swap with the same swap without the wrapper:outputToRouterinner executor, V3 and V4 callbacks, native ETH paid to the router and to the receiverminAmountOut, a reverting payer, rejected inner executors, the single-hop cycle, short data, the slot guardTychoSubsidizingWithPayerTest(11) uses the realSubsidyPayerwith a test signer:innerData, an expired deadline and the unsigned placeholder fill without the subsidyboundToset to the contract or to the sending EOApayis reached.Gas: the wrapper adds about 11,900 gas to a UniV2 or UniV3 single swap, measured with a payer that returns
falseat once. The payer's own cost is not included.Not included
No runtime bytecode fixture. Protocol testing looks fixtures up by protocol system, and the wrapper is not a protocol. Its fixture is built from an
executor_deployments.jsonentry, which needs the payer's address, so it comes with the deployment.Replaces the sketch in #1563.
🤖 Generated with Claude Code