docs/superpowers/specs/2026-08-06-deposit-rail-gas-economics.md §4.
The problem
The keeper fronts native gas for every deposit-rail sweep. Two things make the shipped recovery model untenable. The existing channel is being deleted. Today the keeper recovers gas via thedistribute() distributor incentive — a bps fraction of gross, capped by the pool’s committed distributionIncentiveBps. The plan to zero that incentive and restore a true 1% flat removes the only mechanism by which deposit-rail gas is recovered at all. Two issues written independently, in direct conflict.
The bps model does not track the cost in either direction. Gas is a fixed cost per invoice; the incentive is a percentage of gross per distribution. At 50 bps on a 0.10 recovered:
Worse, the incentive is charged per distribution, not per invoice. A merchant batching 1,000 small invoices into one distribute pays 50 bps once while the platform paid 1,000 forwarder deploys. TRON needs a $368 order for 50 bps to cover a single invoice.
The mechanism
Take the reimbursement at distribute, not at sweep, and route it through the existing treasury transfer.Final split
MerchantPool.sol:203.
bps becomes a ceiling, not a formula
The cap must be min(bps, absolute)
A bps-only cap recreates the exact mismatch this design exists to fix. TRON’s 92 order but 500 bps on a 10,000 order authorises a $500 claim, which permissionless distribute() makes reachable by anyone.
Recipient is the committed treasury address, never msg.sender
Today’s incentive pays
msg.sender — correct for a permissionless incentive, wrong for a gas reimbursement. Once the merchant presses Claim, the merchant is msg.sender and would receive their own gas debt. The recipient must be the treasury address fixed in the init code. This is a semantic change from the deployed contract, not a parameter change.Why treasury rather than a separate keeper payee
Stablecoin sitting in the keeper address is useless — the keeper needs native gas. Whoever receives the reimbursement, treasury must still run sell-stablecoin, buy-native, fund-keeper. A fourth recipient buys nothing operationally while costing 15,000–30,000 gas per distribution, which the merchant pays as part of their Claim transaction. Accounting stays decomposable: treasury receivesgross × treasuryBps / 10_000 + reimbursement, and both components are recoverable off-chain because the bps value is committed and known.
The receipt ledger
The contract never knows about gas. All pricing lives off-chain.1
Send
Keeper sends the clone-and-sweep transaction.
2
Read the receipt
gasUsed × effectiveGasPrice on EVM, energy_usage_total × sun rate on TRON.3
Snapshot USD at spend time
Convert via Chainlink and store the snapshot. Do not recompute at distribute — a price move would re-price the debt.
4
Write the row
(invoice, chain, native_spent, usd_at_spend, token_units).5
Sum at distribute
Total the unreimbursed rows for that pool and token, pass as
reimbursement, mark them reimbursed.6
Carry forward the remainder
Anything the cap rejected stays open for the next distribution.
Carry-forward, never claim-gating
Deduction only works when there is something to deduct from. An underpaid, wrong-token, or never-settled invoice leaves gas spent against a pool with little in it. The answer is to carry the shortfall to the next distribution, still capped. A merchant who keeps trading always repays; one who stops leaves a small unrecovered balance.A carried debt must never block or delay
distribute() or a claim. Blocking a payout is the withholding power described in Distribution — it produces the identical merchant net while handing the platform a capability a regulator will treat as custody.