feat(fees): mirror the swap-fee split and cover it end-to-end - #54
Open
heifner wants to merge 2 commits into
Open
feat(fees): mirror the swap-fee split and cover it end-to-end#54heifner wants to merge 2 commits into
heifner wants to merge 2 commits into
Conversation
Follows the depot change that pays the winning underwriter and each participating reserve owner out of the WIRE-leg fee. Harness: - WireReserveTool.splitWireFee mirrors sysio::opp::amm::split_wire_fee exactly — five shares (underwriter / rewards / emissions / src reserve / dst reserve), every rate off the same gross leg, exact conservation. - SwapScenarioContext.reserveOwnerFee reads a reserve's rate and earned amounts through the typed table accessor. - Steps.contracts.sysio.reserv gains planSetrsvfee / planClaimrsvfee (owner-signed; the owner is not in the action data, so it rides the step input) and planSetconfig. - The bootstrap seeds sysio.reserv::setconfig with a zero emissions share, so no swap fee leaves custody at settlement by default. Flows: - flow-swap-private-reserves gains a ReserveOwnerFees phase (the owner prices both private reserves at 1% and 2%) and a ClaimReserveOwnerFees phase (owner claims each accrual). PhaseA/PhaseB now assert each reserve accrued its OWN rate off the same leg. One account owns both reserves, so a single swap pays it twice — which is the per-reserve rule under test rather than merely asserted. Two different rates are used so a swapped or double-counted side cannot hide behind equal numbers. - The swap-from-wire / swap-to-wire / with-underwriting custody assertions now expect only the DRAINED rewards half to leave custody; the underwriter half stays as a uwfees accrual until claimed. Verified: flow-swap-private-reserves SUCCEEDED against a live cluster. Against a 99,009,900 gross leg the on-chain split was exact — source reserve 990,099 (1%), destination 1,980,198 (2%), underwriter 148,514, rewards 148,515, emissions 0 — with the destination gaining precisely the 95,742,574 remainder. Change-Id: I4f2fe583973bf002e9e773f1cca16f8c48d5476c
This was referenced Aug 4, 2026
Open
… placeholder `FromWireRevertFeeBps` mirrored the contract's `DEFAULT_FROMWIRE_REVERT_FEE_BPS` — its JSDoc says so — but stayed at 10 when that default moved to 500 for the launch value. Every cluster therefore priced from-WIRE revert churn at 0.1% while the network will charge 5%. Inert today: the fee is charged only on the two caller-fault `drainfwq` branches (unpriceable target reserve, variance exceeded at drain), and no flow drives either, so no flow's economics change. It mattered anyway — the harness is the reference cluster, and a future flow touching that path would have silently inherited the stale rate. Nothing pinned the value. The contract's own `drainfwq_charges_revert_fee_on_ caller_fault` sets its rate via `setconfig` rather than reading the default, so the drift was invisible to it too. Adds the missing bootstrap-payload assertion, which walks the composed tree to the `UnderwriterConfig` phase's `configure-uwrit` step — the only seam, since the payload is built inline in `ClusterBuildDefaults.create`. Change-Id: I111303c4389a3413c17412f85f26c6fec5626a30
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.
Harness + flow side of the swap-fee change that pays the winning underwriter and each participating reserve owner out of the WIRE leg.
Harness
WireReserveTool.splitWireFeemirrorssysio::opp::amm::split_wire_feeexactly — five shares (underwriter / rewards / emissions / src reserve / dst reserve), every rate off the same gross leg, exact conservation.SwapScenarioContext.reserveOwnerFeereads a reserve's rate and earned amounts through the typed table accessor.Steps.contracts.sysio.reservgainsplanSetrsvfee/planClaimrsvfee(owner-signed — the owner isn't in the action data, so it rides the step input) andplanSetconfig.sysio.reserv::setconfigwith a zero emissions share, so no swap fee leaves custody at settlement by default.Flow coverage
flow-swap-private-reserveswas the right home: one account owns both private reserves, so a single swap charges it twice — the per-reserve rule gets exercised, not just asserted.ReserveOwnerFeesphase — the owner prices the ETH reserve at 1% and the SOL reserve at 2%. Deliberately different rates, so a swapped or double-counted side can't hide behind equal numbers.ClaimReserveOwnerFeesphase — owner claims each accrual; both sweep to zero, lifetime totals survive, and the owner's WIRE rises by exactly what the pair earned.uwfeesaccrual until claimed.Verification
[flow-swap-private-reserves] SUCCEEDEDagainst a live cluster — exit 0, every Report stepok, including all six new fee steps.Against a 99,009,900 gross WIRE leg the on-chain split was exact:
Destination gained precisely the 95,742,574 remainder. The one-subunit asymmetry between rewards and the underwriter is the remainder-taking design working — nothing leaks.
Also:
pnpm build+pnpm lintclean; 125 tests pass across the touched suites.Known gap
SwapScenarioContext.test.tscannot execute on Node 22 — it imports the flow barrel →FlowCLI→yargs@18(ESM-only), and jest'srequire(ESM)needs Node ≥24.9. Its newreserveOwnerFeecases type-check but I have not seen them go green locally; they'll run in CI. This is the same pre-existing cause as the other env-blocked suites on this host, not something this PR introduces.Note on the rebase
Branched off fresh
origin/master, which already carried the SEC-129 uwreq lifecycle fix (PR #38). I had made the same fix independently while chasing a compile error from the regenerated types — that duplicate was dropped in favour of upstream's, and the tree was rebuilt, re-linted, and re-tested against the current base.Related PRs — one change across four repos
Land in this order; the first three are coupled:
SysioContractTypesfrom those ABIswire-libraries-ts#60 must merge before wire-tools-ts#54 (the harness builds against the committed
SysioContractTypes), and both need wire-sysio#546's ABIs first. The manifest PR is doc-only and independent.Running the e2e gate on this set needs all three code branches passed explicitly:
BRANCH_WIRE_SYSIO=feat/swap-fee-distribution,BRANCH_WIRE_LIBRARIES_TS=feat/swap-fee-distribution,BRANCH_WIRE_TOOLS_TS=feat/swap-fee-distribution.The tools-ts override is not optional — without it the gate discovers and runs master's flows, so none of the new fee coverage executes. wire-ethereum / wire-solana stay on their manifest defaults (
next); neither repo is touched by this change.Gate run: all 13 flows passed (2026-08-05) — resolved
wire-sysio@f982725a,wire-libraries-ts@ab92a844,wire-tools-ts@74b2f092; wire-ethereum / wire-solana on manifest defaultnext.