Skip to content
bankhood.

The fee engine

ETH denomination, collection, batching, slippage, thresholds and failure modes.

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:

  1. 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.
  2. No reflexive sell pressure. The router never holds or sells BANK. Funding a purchase is not a market event in the token.
  3. The broker only accepts dollars. ETH has the deepest and most reliable path to USD, so the conversion is short and quotable in advance.
  4. 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.

ActionRateTaken from
Buy3.00%Charged on the ETH leg of the swap, before the token is delivered.
Sell3.00%Charged on the ETH proceeds of the swap, before they reach the seller.
Transfer0.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:

DestinationSharePurpose
HOOD purchases70%Converted to USD and spent on Robinhood common stock through the brokerage adapter.
Liquidity15%Paired and locked into the primary BANK pool to keep the market tradable at size.
Operations & brokerage10%Commissions, custody, attestation costs, indexer and relayer gas for distributions.
Treasury buffer5%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.

batch trigger
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

FailureBehaviourRecovery
Market closedNo order submitted; ETH pools in the vault.Executes on the next open. Holder accrual is unaffected.
Stock haltedAdapter refuses to submit while the halt is active.Retries after the halt clears, subject to the price collar.
Conversion slippage exceededConversion aborts before any ETH is spent.Retried on the next cycle with a fresh quote.
Batch under thresholdNo action. Balance rolls into the next batch.Automatic once volume accumulates.
Fill report revertsShares are held but not yet credited on-chain.Report is idempotent and replayable; the fill is not lost.
Adapter offlineVault accumulates indefinitely; nothing is spent.Multisig can rotate the adapter after the timelock.

Parameters

ParameterValueChangeable
tradeFeeBps300Timelock, capped
transferFeeBps0Immutable at zero
hoodAllocationBps7000Timelock, floored
minPurchaseWei0.25 ETHTimelock
conversionSlippageBps50Timelock
priceCollarBps300Timelock

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.