The fee engine
ETH denomination, collection, batching, slippage, thresholds and failure modes.
On this page
The fee engine is the part of the protocol most likely to be copied and most likely to be copied badly. This page states the reasoning behind each choice so the trade-offs are visible.
Why ETH denomination
A tax token that collects in its own supply has a structural problem: to turn the tax into anything useful it must sell the tax. That selling happens into its own pool, so the mechanism that is supposed to support the token also applies pressure to it, and the pressure is largest exactly when volume is highest.
Denominating in ETH removes the loop. Four consequences follow:
- Purchasing power does not collapse in a drawdown. A fee of 3% of an ETH-denominated notional is worth the same in dollars whether BANK is up fourfold or down ninety per cent.
- No reflexive sell pressure. The router never holds or sells BANK. Funding a purchase is not a market event in the token.
- The broker only accepts dollars. ETH has the deepest and most reliable path to USD, so the conversion is short and quotable in advance.
- Accounting stays legible. Every batch reduces to one sentence: x ETH became y shares at an average of z dollars. That is the only claim the protocol ever has to defend.
Collection
The token’s transfer hook fires only when one side of a transfer is a registered pool. On a buy, the fee is taken from the ETH the trader supplies before the token is delivered. On a sell, it is taken from the ETH proceeds before they reach the seller. In both cases the trader sees a slightly worse effective price and never sees a separate charge.
| Action | Rate | Taken from |
|---|---|---|
| Buy | 3.00% | Charged on the ETH leg of the swap, before the token is delivered. |
| Sell | 3.00% | Charged on the ETH proceeds of the swap, before they reach the seller. |
| Transfer | 0.00% | Wallet-to-wallet transfers are untaxed. Only market trades pay. |
Wallet-to-wallet transfers are exempt. This is not generosity: taxing transfers punishes custody hygiene, breaks integrations that route through intermediate addresses, and produces accrual that has nothing to do with trading activity.
The split
Collected ETH is divided once, at the router, by a single parameter:
| Destination | Share | Purpose |
|---|---|---|
| HOOD purchases | 70% | Converted to USD and spent on Robinhood common stock through the brokerage adapter. |
| Liquidity | 15% | Paired and locked into the primary BANK pool to keep the market tradable at size. |
| Operations & brokerage | 10% | Commissions, custody, attestation costs, indexer and relayer gas for distributions. |
| Treasury buffer | 5% | Held in ETH to smooth distributions across weekends and Nasdaq holidays. |
Batching
Fees arrive continuously in fragments. Buying equity with each fragment would be absurd: commissions, conversion spread and gas would consume more than the fragment is worth. So the vault accumulates and executes in batches.
Batching also produces a cleaner audit trail. One batch equals one order equals one average fill price, which is far easier to reconcile against a brokerage statement than hundreds of odd-lot fills.
function _maybeExecute() internal {
uint256 pending = address(this).balance;
if (pending < minPurchaseWei) return; // wait for the next fee
if (!marketOpen()) return; // wait for the Nasdaq open
emit PurchaseRequested(pending, block.timestamp);
}Conversion and slippage
The adapter quotes the ETH-to-USD conversion before it commits. If realised slippage against the quote exceeds the configured tolerance the conversion aborts and the ETH stays in the vault untouched, to be retried on the next cycle.
A second collar applies to the equity leg. The adapter refuses to submit if the quoted price sits outside a band around the prevailing mark, which prevents the reserve from being filled into a distressed print during a volatility halt or an auction imbalance.
Thresholds
A batch must clear 0.25 ETH before an order is placed. Below that, the economics do not work:
- Fixed brokerage costs do not scale down with order size.
- Conversion spread is worse on small notional.
- Reporting a fill on-chain costs the same gas regardless of size.
The pending balance is public, so the distance to the next batch is always observable. Nothing is lost while waiting — the ETH is in the vault, not in anyone’s pocket.
Failure modes
| Failure | Behaviour | Recovery |
|---|---|---|
| Market closed | No order submitted; ETH pools in the vault. | Executes on the next open. Holder accrual is unaffected. |
| Stock halted | Adapter refuses to submit while the halt is active. | Retries after the halt clears, subject to the price collar. |
| Conversion slippage exceeded | Conversion aborts before any ETH is spent. | Retried on the next cycle with a fresh quote. |
| Batch under threshold | No action. Balance rolls into the next batch. | Automatic once volume accumulates. |
| Fill report reverts | Shares are held but not yet credited on-chain. | Report is idempotent and replayable; the fill is not lost. |
| Adapter offline | Vault accumulates indefinitely; nothing is spent. | Multisig can rotate the adapter after the timelock. |
Parameters
| Parameter | Value | Changeable |
|---|---|---|
| tradeFeeBps | 300 | Timelock, capped |
| transferFeeBps | 0 | Immutable at zero |
| hoodAllocationBps | 7000 | Timelock, floored |
| minPurchaseWei | 0.25 ETH | Timelock |
| conversionSlippageBps | 50 | Timelock |
| priceCollarBps | 300 | Timelock |
Caps and floors are enforced in the contract, not by policy: the trade fee cannot be raised above its deployment cap and the HOOD allocation cannot be lowered below its floor, whatever the multisig proposes. Details in governance.