TECHNICAL REFERENCE / PRE-LAUNCH

Protocol
documentation.

This is an implementation reference for the contracts and keeper in this repository—not a yield page. It distinguishes deployed behavior from pending launch work and names the operational trust assumptions.

Base mainnet · 8453Solidity · 0.8.26Addresses · pending deployment

Fees become stock balances, then direct payouts.

Basket has one intended ETH/BASKET Uniswap v4 market. Its hook collects a 3% native-ETH fee on both directions of trade and forwards it to DividendVault. The vault accounts for 10% of each allocation as protocol revenue and makes the remaining 90% available for B20 stock purchases.

ETH / BASKETUniswap v4 pool
3.00% hook feeNative ETH to vault
90 / 10 splitStocks / protocol revenue
B20 push payoutOn-chain holder registry
Fee accounting per ETH/BASKET trade volume
DestinationRateHow it is accounted for
LP fee1.00%Pool configuration; separate from the hook.
Hook fee3.00%Collected in native ETH by BasketFeeHook.
B20 purchase budget2.70%90% of the hook allocation, split by active basket weights.
Protocol revenue0.30%10% of the hook allocation, tracked as platformClaimable.

Important: the hook itself only collects the 3% fee. It does not initialize a pool, choose a price or provide liquidity.

The four implementation surfaces.

These are the source modules that define the protocol today. There are no production Basket contract addresses yet.

BasketToken

src/BasketToken.sol

Fixed-supply BASKET ERC-20. Maintains the enumerable holder registry used by the vault; it does not ask an indexer or explorer for recipients.

  • 1,000,000,000 BASKET fixed supply
  • 10,000–100,000 BASKET eligibility range
  • O(1) holder removal with swap-and-pop

BasketFeeHook

src/BasketFeeHook.sol

The Uniswap v4 hook attached to the intended ETH/BASKET pool. It takes 300 bps in native ETH and settles it directly to the vault.

  • 300 bps hook fee
  • ETH must be currency0
  • CREATE2 address mined for v4 flags

DividendVault

src/DividendVault.sol

Holds hook ETH and acquired B20 balances, tracks protocol revenue, snapshots eligible holders and pushes dividends in batches.

  • 1 hour minimum between cycle starts
  • 90% stock budget / 10% protocol revenue
  • No native-ETH emergency withdrawal

Keeper

keeper/src/keeper.ts

Off-chain executor for quotes, buy transactions and payout batches. It reads the recipient set from contracts; it never uploads a holder list.

  • Base chain ID 8453
  • 250 default snapshot batch
  • 25 default payout batch
How the hook determines when to collect ETH

The pool must place native ETH in currency0. The hook collects before the swap when ETH is specified, and after the swap when it is not. It uses Uniswap v4 return deltas to settle exactly 300 bps to the vault.

A keeper executes; contracts determine recipients.

The keeper discovers B20 swap routes off-chain, but it does not build a Merkle tree or submit an address list. Recipient enumeration, captured balances and payout accounting live in the token and vault contracts.

  1. 01
    buyStocks(minOuts, routerCalldatas)Keeper

    Uses available native ETH, retains 10% in platformClaimable and divides the other 90% over the current active basket by weight. Every configured stock must have a complete route or the supplied keeper skips the buy.

  2. 02
    snapshotHolders(count)Keeper

    Reads BasketToken.holderAt(i) in pages, excludes infrastructure and reward-excluded accounts, records balance plus address in one word, and accumulates eligibleSupply.

  3. 03
    startCycle()Keeper

    Requires a complete snapshot (or creates a one-transaction snapshot for a small registry), freezes each distributable B20 pot and sets nextDistribution to now + 1 hour.

  4. 04
    distributeBatch(count)Keeper

    Pushes every frozen B20 asset to the next page of recipients. A failed B20 receiver-policy check records an unpaid entitlement instead of reverting the whole batch.

  5. 05
    flushUnpaidDividend(holder, stock)Anyone

    Retries a recorded failed payment to its original holder. It cannot redirect that entitlement or redivide it across other accounts.

Payout formulastock pot × min(snapshot balance, live balance) ÷ eligibleSupply

Timing: nextDistribution is set when startCycle() begins, so a new cycle cannot start for one hour. Stock purchases are keeper-driven and are not independently rate-limited by this interval.

Holder discovery is on-chain.

BasketToken maintains an array of eligible accounts every time a balance changes. Accounts qualify at the current threshold, initially 100,000 BASKET; governance can only set it between 10,000 and 100,000 BASKET.

Registry

No explorer dependency

The vault calls holderCount() and holderAt(i). Its keeper can therefore distribute without Blockscout, Etherscan or an off-chain holder database.

Balance clamp

Sells reduce a captured weight

At payment, the snapshot amount is capped to the holder's current balance. A balance returned after capture cannot receive the full historic weight.

Rejected B20 transfer

Entitlement stays attached

If a B20 receiver policy rejects a transfer, the amount is recorded as unpaid for that holder and can be retried with flushUnpaidDividend.

Snapshot semantics. Paginated snapshots are not a single-block atomic snapshot: transfers can mutate the swap-and-pop registry between keeper calls. The vault de-duplicates seen addresses and applies the live-balance clamp, but operators should treat multi-transaction capture as an operationally sensitive period.

Explicit permissions, not implied automation.

Owner multisig

Configuration and emergency custody

Can change policy and use the ERC-20 emergency path. This is a material trust role and should be a multisig at deployment.

Keeper

Routes, buys and batches

Can buy stocks and advance snapshots and payouts, but cannot change thresholds, basket weights, recipients or ownership.

Platform recipient

Claims only accrued revenue

Can claim platformClaimable. The owner can rotate this recipient; it has no direct access to the stock budget.

Anyone

Retries an unpaid dividend

Can call flushUnpaidDividend, but tokens can only be sent to the recorded rightful holder.

Owner-controlled operations
Token eligibilitysetMinShareBalance(10k–100k) and setRewardsExcluded.
Basket policysetBasket(stocks, weights) between cycles only; weights must total 10,000 bps.
OperatorssetKeeper, setPlatformRecipient and setMaxGrossSpendPerCycle.
InfrastructuresetExcluded accepts contract addresses only, protecting ordinary wallet holders from this vault-level control.
Emergency pathemergencyWithdrawERC20 can recover every ERC-20 in custody, including B20 stocks; it intentionally has no matching native-ETH path.
Execution trust and recovery risk. The router address is immutable, but buyStocks forwards keeper-supplied router calldata and keeper-supplied minimum outputs. The configured cap applies per buyStocks call, not to a calendar-hour total. Separately, abortCycle() clears a partial cycle without recording which holders were already paid; it should not be used after any payout batch. These are current implementation constraints, not guarantees removed by the UI.

How BASKET and the dividend assets trade.

Basket is designed around one ETH/BASKET Uniswap v4 pool. Liquidity providers set the initial price and add liquidity in a separate pool-initialization transaction; the hook does neither. Until that happens, BASKET has no live market or price.

BASKET market

ETH / BASKET on Uniswap v4

The intended pool uses a 1% LP fee plus Basket's 3% native-ETH hook fee on buys and sells. The hook fee is forwarded to the dividend vault.

Stock assets

Base B20 tokenized stocks

Dividends are paid in the B20 assets acquired by the vault. B20 assets can apply their own sender and receiver transfer policies.

Route availability

No route, no stock purchase

The keeper buys the active basket only when every configured stock has a complete route. If one is unavailable, hook ETH remains in the vault for a later attempt.

Pre-launch: the deployment script deliberately leaves the pool uninitialized. Pool price, liquidity and the first public BASKET route are launch decisions, not protocol constants.

Implementation present; launch addresses pending.

Network configuration
Base chain8453Target network
v4 PoolManager0x498581fF718922c3f8e6A244956aF099B2652b2bUniswap deployment reference ↗
Universal Router0x6fF5693b99212Da76ad316178A184AB56D299b43Immutable vault dependency
BASKET / vault / hookNot deployedPublish verified addresses here after deployment
01

Deploy and verify

Deploy the token, vault and CREATE2-mined hook; set the multisig owner, platform recipient, keeper and a reviewed spend cap.

02

Set market parameters

Decide the initial ETH/BASKET price and liquidity separately. The deployment script deliberately does not initialize a v4 pool.

03

Validate routes and policies

Confirm each active B20 route and receiver policy. The supplied keeper skips a full buy when any active stock has no complete route.

04

Publish operations

Publish verified addresses, multisig and keeper policy, monitoring, and the first distribution transaction data before calling the market live.

RELATED

See the empty distribution ledger.

Until the protocol is live, the dashboard intentionally shows no estimated APY or simulated payout history.

Distribution desk