TECHNICAL REFERENCE / LIVE ON BASE

Protocol
documentation.

This is an implementation reference for the contracts and keepers behind Stockify—not a yield page. It describes what the deployed code does, and names the operational trust assumptions rather than leaving them implied.

Base mainnet · 8453Solidity · 0.8.26Unaudited

Fees become stock balances, then direct payouts.

Stockify has one ETH/STFY 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 deploys the remaining 90% into B20 stock purchases, which are then pushed to STFY holders.

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

Important: the hook only collects the 3% fee. It does not initialize a pool, choose a price or provide liquidity, and it does not verify which token sits opposite ETH — it collects on any v4 pool that names it with native ETH as currency0.

The implementation surfaces.

These are the components that define the protocol. Deployed addresses are in the last section.

StockifyToken

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

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

StockifyFeeHook

The Uniswap v4 hook on the ETH/STFY 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

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
  • Owner-curated venue allowlist
  • No native-ETH emergency withdrawal

StockifyRouter

Stateless buy/sell router for the ETH/STFY pool, because aggregators will not route a pool whose hook is not on their allowlist. It holds no funds and has no owner.

  • buy(minOut) payable · sell(amountIn, minOut)
  • Refunds input the pool would not take
  • Approvals are made to this contract, not a proxy

IndexFactory / IndexTreasury

A separate product: one EIP-1167 treasury per launch that points its creator fees here. See the Indices section below — its roles are not the vault's.

  • CREATE2 address known before deployment
  • Composition immutable after creation
  • Clones are never upgraded

Keepers

Two off-chain executors, one per product. Both discover routes and submit transactions; neither supplies a recipient 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.

How a purchase reaches a venue without the vault holding a router

The vault does not parse routes and does not store a router address. Each leg names a venue the owner has allowlisted with setSwapTarget; the vault sizes the input itself, writes that amount into the route's own calldata at the offset the keeper supplies, approves exactly that amount and revokes the approval in the same call. The result is judged purely by balance deltas, so a listed venue can make a leg fail but cannot take custody of more than one leg's input.

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(targets, routeCalldatas, amountInOffsets, minOuts)Keeper

    Sizes the spend from available native ETH, wraps the 90% stock budget into WETH and divides it over the active index by weight. Each leg names an allowlisted venue and the byte offset of the amount inside its own calldata, which the vault overwrites with the real spend before calling. Protocol revenue accrues on what was actually spent, not on what was offered.

  2. 02
    snapshotHolders(count)Keeper

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

  3. 03
    startCycle()Keeper

    Requires a complete snapshot (or captures a small registry in one transaction), 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.

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

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.

The active index is not a constant. The owner can replace the buy index and its weights between cycles, so what the vault holds is whatever stocksLength() and stockAt(i) report right now. Any asset ever admitted stays in the distribution set, so a rotation cannot strand stock already acquired.

A second product, with different roles.

A coin launched elsewhere can point its creator fee stream at a treasury minted by IndexFactory. From then on those fees either buy tokenized equity that is pushed to the coin's holders, or buy the coin back and burn it. One EIP-1167 clone per coin; what it buys is fixed at creation and clones are never upgraded.

Mode 0

Buy the basket, pay holders

Fees buy each basket name by weight and the balance is pushed pro-rata on coin balance. Wallets under 10,000 whole coins are skipped and their slice stays with the holders above the line.

Mode 1

Buy the coin back, burn it

No basket, no holder list, no rounds. burn() is permissionless because it has one destination and cannot change any holder's share relative to another's.

Cadence

15 minutes to 1 week

Chosen at creation. A round opens no sooner than the interval and only when there is something to pay; it can be continued across transactions inside a shorter batch window.

Split of every harvestplatform fee off the top → creatorShareBps of the remainder → the rest to holders

The platform fee is read from the factory rather than fixed in a clone, and is capped at 20% in the factory's own code. The creator's accrued share is fenced from the buy path in both directions: a purchase can never spend it, and a payout can never reach it.

Two tiers of promise, and the default is the weaker one. A launchpad pays whoever a coin's creator split names, and falls back to the creator role only when no split is set. A launch that names a treasury as its fee recipient produces the first: the launching wallet keeps the role and can point the split back at itself at any time, with no delay and no signature from us. Only the role is beyond its reach, and a treasury can hold it only through the launchpad owner's timelocked transfer. Each index page states which one is in force and re-derives it live, so a stream that has been pointed away is reported as such rather than assumed.
The roles here are not the vault's — do not carry that model over. On an index treasury the KEEPER is the administrator: exclusions, the dust floor, pausing, stray-token rescue and ownership are all keeper calls. The OWNER is a label recording who the treasury was created for and carries no power at all — a creator points their launch's fees at a treasury and their involvement ends there, which is what stops a creator from freezing their own holders' payouts or shaping a distribution. What the keeper cannot do is take custody: it may only buy basket names, only sell the quote asset, never sell equity back, and never set payout weights, which are read from coin balances over a strictly ascending list so no address can appear twice.

Explicit permissions, not implied automation.

This section is about the STFY vault. The index treasuries assign these roles differently — see above.

Owner

Configuration and emergency custody

Can change policy, curate swap venues and use the ERC-20 emergency path. This is a material trust role and should be held by a multisig.

Keeper

Routes, buys and batches

Can buy stocks and advance snapshots and payouts, but cannot change thresholds, index weights, venues, 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
Venue allowlistsetSwapTarget(target, allowed) decides where a purchase may route. This is the control that bounds keeper execution — see the trust note below.
Token eligibilitysetMinShareBalance(10k–100k) and setRewardsExcluded.
Index policysetIndex(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. buyStocks forwards keeper-supplied route calldata and keeper-supplied minimum outputs. The vault measures the spend and the fill rather than trusting them, and a venue receives only one leg's approval, revoked in the same call — so the keeper cannot move funds to itself. What it can do is accept a poor fill at a listed venue, which makes the owner's allowlist, not the keeper, the real bound on execution. Separately: maxGrossSpendPerCycle applies per buyStocks call and not to a calendar-hour total, and abortCycle() clears a partial cycle without recording which holders were already paid, so it must not be used after any payout batch. A guardian contract exists that would refuse exactly that call; it is deliberately not installed. These are current implementation constraints.

How STFY and the dividend assets trade.

STFY trades in one ETH/STFY Uniswap v4 pool. Aggregators will not route a pool whose hook is not on their allowlist, so this site buys and sells through StockifyRouter, which calls the pool manager directly. Everything else — the tokenized equities — is routed through an aggregator.

STFY market

ETH / STFY on Uniswap v4

A 1% LP fee plus Stockify'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, checked before a trade is offered.

Route availability

No route, no stock purchase

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

Slippage: Base has no public mempool, so the minimum-output floors here are a sanity check against a mispriced fill rather than sandwich protection. The 3% hook fee is charged in ETH on the way through, which is why the panel's presets start well above an ordinary AMM default.

Everything below is on Base mainnet.

Stockify contracts
ContractSourceAddress
STFY tokenStockifyToken0xc6405d7a226e1c18e559be2f335f74c01ad07bf5
Dividend vaultDividendVault0x4Ee35c658b8032a7577096B60bd51Ae9909E4f98
Fee hookStockifyFeeHook0x47Ec48C74f3069e9Ae69406197821996d80200cC
Pool routerStockifyRouter0x72557a7A026b733E94d605Fcfd96e6e162274b0A
Index factoryIndexFactory0x78b50dFFE7250638D6F2A24f56B0849CefA69498
Network dependencies
Base chain8453Target network
v4 PoolManager0x498581fF718922c3f8e6A244956aF099B2652b2bUniswap deployment reference ↗
Swap venuesOwner allowlistCurated with setSwapTarget; there is no pinned router. Read the current state from the vault.
Not audited. This code has unit tests and has been deployed, but it has not undergone an independent security audit or a legal and compliance review. B20 transfer policy and jurisdictional eligibility are the holder's to establish.

RELATED

See what has actually been paid.

The distribution desk shows every settled cycle, decoded from the vault's own events. There is no estimated APY and no simulated history.

Distribution desk