Blend developer docs
Read baskets, quote trades, submit transactions, and index Blend on Robinhood Chain.
Are you an LLM or using an LLM? Use these two for context: llms.txt and llms-full.txt.
Protocol architecture
Blend baskets are ERC-20 tokens backed by fixed quantities of constituent assets.
One share maps to a fixed amount of every active asset in its vault. Prices can change the basket's value and percentage weights, but they do not rebalance the underlying tokens.
A user who already holds every constituent can mint through the vault. A user who starts with USDG or ETH can use the router, which trades every constituent and then mints or redeems the share. All constituent swaps and share issuance or redemption execute atomically in one transaction.
Every active asset must cover its units across all outstanding basket shares.
| Contract | Responsibilities | Key restriction |
|---|---|---|
| Factory | Deploys baskets and lists canonical vaults | Asset custody is delegated to each vault |
| Vault | Holds assets, issues shares, and processes redemption | Holder backing is unavailable for administrative withdrawal |
| Hook | Stores routes and settles multi-asset trades | Registration is restricted to approved factories |
| Router | Converts between a quote asset and complete basket shares | The caller supplies slippage limits and deadlines |
| Lens | Returns previews, checks, and quotes through eth_call | Read-only; no asset transfers |
Basket accounting
Each basket specifies a quantity of every constituent token per share.
Units and backing
units(token) tells you how much of a constituent belongs to one full basket share. backing(token) is the amount reserved for every holder. Owner revenue, protocol revenue, and recovery value live in separate buckets.
Market price changes do not modify these accounting values. Onchain maintenance methods can modify them: accrete adds surplus to holder units, resyncmatches the books to the real balance, and recovery calls write value down or restore it.
Basket lifecycle
- DeployChoose 1–16 assets, their units per share, fee settings, rate limits, and the basket owner.
- RegisterAdd one route per asset for USDG or native ETH. Routes are not required for direct minting or redemption.
- OperateUsers mint, redeem, buy, and sell. The vault checks backing, limits, deadlines, and hourly capacity.
- MaintainAnyone can reconcile balances, accrete surplus, sweep protocol fees, observe dead routes, and restore recovered assets.
Transaction methods
Vault methods accept constituent assets directly. Router methods accept or return a single quote asset.
| Operation | Method | Input | Validation parameters |
|---|---|---|---|
| Direct mint | BasketVault.mint | Every active constituent | maxIn[] and deadline |
| Direct redeem | BasketVault.redeem | Basket shares | minOut[] and deadline |
| Exact-input buy | BlendRouter.buy | USDG or native ETH | minShares and deadline |
| Exact-output buy | BlendRouter.buyExact | USDG or native ETH | maxIn and deadline |
| Sell | BlendRouter.sell | Basket shares | minOut, deadline, optional skip list |
| Compose atomically | flashMint / flashRedeem | Settlement in a callback | Full repayment in the same transaction |
msg.value and needs no token approval.Fees and revenue
Fees are calculated independently for each active constituent using the current vault configuration.
Every mint and redemption charges each active constituent separately. The vault adds a basket-owner fee and a separately calculated fixed Blend protocol fee. Routed trades also pay each constituent pool's fee and price impact. The Blend hook pool itself uses fee 0 and therefore adds no separate basket-level trading fee.
Fee calculation
| Fee component | Formula for one constituent | Accounting bucket |
|---|---|---|
| Mint backing | ceil(shares * units / 1e18) | Holder backing |
| Mint owner fee | ceil(base * mintFeeBps / 10,000) | treasury[token] |
| Mint protocol fee | ceil(base * 35 / 10,000) | protocolFees[token] |
| Redeem gross | floor(shares * units / 1e18) | Removed from holder backing |
| Redeem owner fee | floor(gross * redeemFeeBps / 10,000) | treasury[token] |
| Redeem protocol fee | floor(gross * 20 / 10,000) | protocolFees[token] |
| Redeem payout | gross - owner fee - protocol fee | Sent to the recipient |
Each basket sets its mint and redeem owner fees separately, up to 100 bps (1%). Read mintFeeBps() and redeemFeeBps() instead of assuming the maximum. Mint calculations round up; redemption calculations round down. Use previewMint and BlendLens.previewRedeem for the exact token amounts.
Onchain fee buckets
treasury[token]- The basket owner can withdraw this bucket. Before withdrawal, anyone may call
accreteto move it into holder backing. protocolFees[token]- Anyone may sweep the complete bucket to
protocolFeeRecipient(). Accretion never spends this bucket. If the vault excludes the constituent before a sweep, the unswept balance moves toforfeitedwith the leg's backing and owner treasury. forfeited[token]- Recovery value reserved for holders: skipped legs, excluded legs (including any unswept fee buckets on them), and the recovery share new mints pay in. Accretion consumes it before owner treasury.
- Route execution
- Routed trades pay each constituent pool's fee and price impact. Those costs follow pool accounting and are not split by the vault.
100 + 0.10 + 0.35 = 100.45 units. Redeeming a gross 100 units pays 100 - 0.10 - 0.20 = 99.70 units. Integer rounding, recovery contributions, and routed pool costs are omitted here.A mint may also contribute to existing forfeited + writtenDown recovery value. This prevents new entrants from claiming value reserved for earlier holders; it is not a fee. The keeper attempts accretion every ten minutes. The vault limits growth to 0.50% per fully refilled 9.125-day step and 20% per year, with at least one share of supply. A leg skipped by redeemExcluding charges no fee and moves its full gross amount from backing to forfeited value.
Execution limits
| Limit | Definition | Integration impact |
|---|---|---|
| Route cost | Pool fee and price impact on each constituent leg | Only applies to router trades |
| Minimum issue | 0.001 shares | Bounds rounding error on small mints |
| Throttles | Issue and redeem capacity refill over one hour | Can limit flow even when route liquidity is available |
| Redemption floor | At least 10% of supply per hour | Prevents configuration from disabling exits |
| Route capacity | Quoted for the selected slippage budget | The shallowest weighted leg sets the limit |
BlendLens.previewBuy returns cost per share and conservative route capacity. Current throttle headroom is excluded and must be read or indexed separately.
Failure recovery
Recovery methods preserve access to transferable constituents when a token or route fails.
| Condition | Protocol behavior | Integration requirement |
|---|---|---|
| Issuance is paused | Stops mint, buy, and flash entry paths. Redemption stays open. | Disable entry actions, but keep exits visible. |
| A token will not transfer | redeemExcluding returns the working assets. | Show exactly which leg is skipped and forfeited. |
| A token is excluded | The operator can act now, or anyone can prove failure and repeat after one day. | Index ConstituentExcluded and remove that leg from new issuance. |
| Vault accounting does not match its balance | resync reconciles the books to the token balance. | Check isReconciled before presenting current state. |
| A route has no liquidity | Anyone can report it; the owner can replace it after one day. | Keep direct mint and redemption available. |
Network setup
The following values are used for wallet configuration, contract reads, and event backfills.
- Network
- Robinhood Chain mainnet
- Chain ID
4663- Native asset
- ETH
- Public RPC
https://rpc.mainnet.chain.robinhood.com- Explorer
- robinhoodchain.blockscout.com ↗
- Factory start block
55,989,258- Basket share decimals
18- Canonical quotes
- USDG (6 decimals) and native ETH (18 decimals)
Contract reference
Fetch an ABI directly from this site, or use the manifest to resolve every contract.
GET /abis
GET /abis/basket-factory.json
GET /abis/basket-vault.json
GET /abis/blend-hook.json
GET /abis/blend-router.json
GET /abis/blend-lens.json| Contract | Address or discovery | ABI | Primary methods |
|---|---|---|---|
| BasketFactoryDeploys vaults and records every canonical basket. | 0x40bd43B7ff1D673e03B129dCE371761a6E81E305 | JSON ↗7 functions · 1 events | deploy, predict, deployed, deployedCount, isBasket |
| BasketVaultERC-20 basket share, custody, fees, throttles, and recovery accounting. | Read BasketDeployed.vault | JSON ↗80 functions · 22 events | previewMint, mint, redeem, treasury, protocolFees, accrete, sweepProtocolFees |
| BlendHookRegisters basket pools and constituent routes; prices and settles routed trades. | 0x219B93D7c067f3cCc9E25aecDbecf1279D1Fc888 | JSON ↗33 functions · 10 events | register, poolKeyFor, routesFor, capacity, quoteLegs, replaceRoute |
| BlendRouterUser-facing exact-input, exact-output, and sell transactions. | 0x70DDEa1f1C509cE6554de3E12370Bf8A29bA4604 | JSON ↗6 functions · 2 events | buy, buyExact, sell |
| BlendLensRead and quote surface that requires no funded or approved caller. | 0xbB95752f5F1464e2Bb4f17652daF68c7f75f6c05 | JSON ↗12 functions · 0 events | previewBuy, previewRedeem, quoteBuy, quoteBuyExactOut, quoteSell |
Shared infrastructure
- Uniswap v4 PoolManager
0x8366a39CC670B4001A1121B8F6A443A643e40951- Native ETH currency
0x0000...0000
Integration workflow
Start at the factory, read each vault, then use the hook and lens for route state and quotes.
Loading ABIs
const manifest = await fetch("/abis").then((r) => r.json());
const vaultAbi = await fetch("/abis/basket-vault.json").then((r) => r.json());| Operation | Contract call |
|---|---|
| Discover baskets | Index BasketFactory.BasketDeployed, or read deployedCount() and deployed(i). |
| Validate an address | BasketFactory.isBasket(vault) |
| Read composition | Call constituents(), then multicall units(token), backing(token), and excluded(token). |
| Read vault fees | Call mintFeeBps(), redeemFeeBps(), the protocol fee constants, and protocolFeeRecipient(). |
| Read fee balances | For each token, read treasury(token), protocolFees(token), and forfeited(token). |
| Preview direct flows | Call previewMint(shares) or BlendLens.previewRedeem(vault, shares). |
| Resolve a basket pool | Call BlendHook.poolKeyFor(vault, quote). |
| Read constituent routes | BlendHook.routesFor(key) |
| Quote a trade | Simulate quoteBuy, quoteBuyExactOut, or quoteSell on the lens. |
| Submit a trade | Send the same pool key and basket address to BlendRouter, with a limit and deadline. |
0, tick spacing 60, and the BlendHook address. One wrong field derives a different pool identifier. Integrations should read poolKeyForinstead of rebuilding it locally.Indexed API
These endpoints are backed by the indexer and are useful for product surfaces. Responses include x-indexed-block. Use contract reads and logs when you need the exact chain head or a trust-minimized source.
GET /api/blend/baskets
GET /api/blend/basket/:address
GET /api/blend/assets
GET /api/blend/holdings/:address
GET /api/blend/activity/:address
GET /api/blend/units
GET /api/blend/leaderboard
GET /api/blend/ethEvery basket row (in baskets, basket and holdings) carries deployedAt, the unix time of its BasketDeployed event, and deployedBlock, both as decimal strings. Each of its legs carries the vault's buckets for that token as raw decimal strings: treasury, protocolFees, forfeited and writtenDown.
activity/:address is the basket's event history, newest first: its latest 150 events of every kind except Accreted, merged with its latest 50 Accreted events. units is the units-per-share history of every vault whose units have changed, in 4-hour UTC buckets: { block, bucketSeconds, vaults }, where each vault has exact and, per token, a start level and steps, pairs of bucket end and units after it, oldest first. exact is false when the replayed history does not end on the units the vault holds now.
Indexing
Start at factory block 55,989,258 and backfill in small ranges. Wide log requests can time out on the public RPC.
| Contract | Events | Indexed state |
|---|---|---|
| BasketFactory | BasketDeployed | Canonical vault, creator, symbol, and deployment order |
| BasketVault | Minted, Redeemed, Transfer | Share activity and holder balances |
| BasketVault | FeesSet, ThrottlesSet, PausedSet, OperatorLockSet | Current configuration and availability |
| BasketVault | ConstituentAdded, ConstituentExcluded, UnitsWrittenDown, Accreted | Composition and unit changes |
| BasketVault | TreasuryWithdrawn, ProtocolFeesSwept | Owner withdrawals and protocol-fee settlement by constituent |
| BlendHook | Registered, RoutesExtended, RouteDeadObserved, RouteReplaced | Basket pool and route topology |
| BlendHook | Bought, Sold | Routed volume in shares and quote units |
Events provide historical state transitions. Live prices, pool depth, reconciliation status, and refilled throttle headroom require current contract reads.
Permissions
Administrative roles can change how a basket operates. They cannot withdraw holder backing or disable ordinary redemption.
| Actor | Authorized operations | Restrictions |
|---|---|---|
| Basket owner | While unlocked: set fees and throttles, pause issuance, add constituents. At any time, locked or not: register the basket, append routes for added constituents, replace a route a day after it is observed empty, unpause issuance, withdraw owner treasury | Exclude assets, bypass fee caps, withdraw backing, or block redemption |
| Operator | Lock configuration, pause issuance, exclude a failed asset, reduce owner fees to zero | Raise fees, move backing, block redemption, or change the fixed operator |
| Hook governance | Approve factories and transfer hook governance | Deregister baskets, alter a basket's routes, or move vault funds |
| Anyone | Accrete, resync, sweep protocol fees, prove asset failure, unexclude recovered assets, observe dead routes | Redirect proceeds or override contract checks |
New baskets start operator-locked. A creator may also choose immutable parameters at deployment, permanently disabling later parameter changes.
Risks
Blend limits some failure modes, but market, token, infrastructure, and governance risk remain.
- Asset exposure. Tokenized stocks provide price exposure, not direct stock ownership, voting rights, or guaranteed dividends. Access may be restricted by location.
- Issuer control. Asset issuers may upgrade contracts, pause transfers, freeze accounts, rebase balances, or stop supporting an asset.
- Liquidity. A single shallow or empty constituent route can stop the routed basket trade even while direct redemption remains available.
- Token behavior. Fee-on-transfer tokens are rejected on mint; unusual transfer behavior can still close issuance or require exclusion.
- Execution. Quotes are estimates. Always set deadlines and minimum-output or maximum-input bounds.
- Operational dependencies. Interfaces, indexers, RPCs, wallets, and automation can be delayed or unavailable. Verify critical state onchain.
SB Security reviewed the protocol in August 2026. Twenty-one of twenty-five findings were fixed; four were accepted with documented constraints. A review reduces risk but does not eliminate it.
For holders
What holders get from the way the vault works, and where each rule lives in the contract.
Reinvested dividends
Robinhood stock tokens implement ERC-8056. Robinhood reinvests dividends by raising a token's uiMultiplier(), the number of underlying shares one token represents, and leaves raw balances unchanged. BasketVault accounts in raw token units, so a basket keeps holding the same number of tokens while each token represents more shares. Route pools price the token, not the share, so the live NAV reflects reinvested dividends once pool prices adjust to the new multiplier. The app's NAV chart multiplies each per-share stock close by the token's multiplier in force at that close, read from its UIMultiplierUpdated events, so each dividend counts from its effectiveAt. Amounts and timing are set by the issuer and aren't guaranteed; see Risks. Read current values with each token's uiMultiplier() or BlendLens.uiMultipliers(vault).
Fee accretion
Every mint and redemption charges the basket owner's fee, booked to treasury[token], and Blend's protocol fee, booked to protocolFees[token]. Anyone may call accrete(token, unitsDelta) to move value into holder backing, raising units[token] for every share. It spends forfeited first, then treasury, and never protocolFees. Growth is capped at 0.50% of the leg's anchor units (or its current units, if lower) per 9.125-day refill, at most 20% of the anchor a year without compounding, and needs at least one whole share outstanding. The owner can withdraw treasury before it is accreted. forfeited holds value left by redemptions that skipped a leg, legs written off by exclusion, new minters' buy-ins, and recovered write-downs. The app's NAV chart replays each leg's units from the indexer's Accreted and UnitsWrittenDown history, so fee growth counts from the first close after it; when that history can't be read, the note under the chart says fee growth is missing.
NAV chart
Each point is a market close (a 4-hour bar on the 7D range), except that the latest can be a bar still trading. When a range starts before the basket was deployed, the line has one more point: the basket's value at the moment of deployment, each constituent at the close of its last full hourly bar before then (a stock outside trading hours at its last extended-hours price), with its units and multiplier at that moment. The headline change counts from that point, the same on every range, and the line before it is drawn dashed as a backtest: of the starting holdings, or of today's holdings when the units history can't be read. If a constituent's price at deployment can't be read yet, no change is shown until it can. A portfolio's chart counts from the latest launch among the baskets held. Neither returns nor future dividends are guaranteed.
Redemption
redeem and redeemExcluding have no pause check. A pause stops minting and flashRedeem, never ordinary redemption. The redemption throttle cannot be set below 10% of supply per hour (MIN_REDEMPTION_PCT_RATE), and its absolute rate must cover at least a tenth of supply (MIN_REDEMPTION_AMT_DIVISOR). Both are checked whenever throttles are set, and the absolute rate again on every mint, so the whole supply can redeem within ten hourly periods.
Frozen constituents
If a constituent reverts on transfer, a plain redeem reverts with it. Holders can call redeemExcluding(shares, to, skip), or BlendRouter.sell with a skip list, to take every other leg and forfeit the skipped leg's gross amount to forfeited. Only accrete (or unexclude, once the leg is excluded) pays that out to holders, and only while shares are outstanding; if every share is redeemed, resync or unexclude moves what is left to Blend's protocol-fee bucket. While the token stays frozen, that value cannot leave the vault. The operator can exclude a failed constituent at once with setExcluded, or anyone can with forceExclude after two failed transfer probes at least a day apart. unexclude restores it once it transfers again.