Documentation

How B20Fold works

Enough to understand what you are depositing into, what earns, who can do what, and what can go wrong. Primary sources are linked inline.

Overview

B20Fold runs Uniswap v4 DualPool pools on Base (chain 8453). Every pool quotes one asset against USDC: tokenized stocks issued as Coinbase B20 xxxc tokens, WETH, and the protocol token FOLD. The USDC side of each pool is folded into a Morpho USDC vault so it keeps earning lending yield while it also stands ready to provide swap liquidity. One LP share is a pro-rata slice of both legs.

B20Fold is a Base clone of Twofold (twofold.fi, Robinhood Chain). The hook is the unmodified, factory-deployed Uniswap DualPoolHook; B20Fold adds a policy layer (OperatorController), a canonical pool Registry, an immutable vault allowlist and a FOLD staking vault that receives the USDC half of protocol harvests.

What a DualPool is

DualPool is described by Uniswap as a "market-making protocol built as a Uniswap v4 hook" (Uniswap docs: DualPool overview). In a vanilla pool, liquidity sits in the pool at all times earning only swap fees. In a DualPool, capital rests in ERC-4626 vaults between swaps and is deployed as concentrated liquidity just-in-time, per swap, so LPs earn swap fees and vault yield on the same token (Uniswap docs).

Externally a DualPool is an ordinary v4 pool: swappers and routers execute standard v4 swaps at the pool's static fee. Between swaps the pool holds approximately zero resident v4 liquidity, which is why the site reads reserves and quotes from the hook's views (getReserves, previewDeposit, getDistribution) rather than from pool state (Uniswap docs). Uniswap states the DualPool contracts were audited by OpenZeppelin in May–June 2026 with no critical or high-severity findings (Uniswap docs). That audit covers the hook, not B20Fold's controller, registry or staking contracts.

Liquidity distributions

The operator sets a list of buckets per pool — tick ranges with weights in basis points. On each swap the hook pulls inventory from the vault and places it across those buckets around the current price, then folds it back afterwards. The pool page renders the buckets as bars; the highlighted bucket contains the current tick. The operator can only move buckets within maxRangeTicks of the current tick.

Folding USDC into Morpho

The quote leg of every pool is bound to an ERC-4626 vault from the allowlist — Morpho USDC vaults on Base. Morpho describes itself as decentralized lending infrastructure; vaults are curated lending products that allocate deposits across Morpho markets (Morpho docs). Vault APY shown on this site is the Morpho API's netApy for the vault and is independent of the pool's swap-fee income (Morpho docs).

Only the USDC leg is folded. The asset leg (a stock token, WETH or FOLD) stays in the hook. If a vault misbehaves, the operator can revoke the hook's approval to it (emergencyRevokeVault); changing the allowlist itself is a 48-hour timelocked owner action.

B20 tokens

B20 is Base's native token standard and a superset of ERC-20: transfer, transferFrom, approve, balanceOf and allowance behave exactly as in ERC-20, while B20 adds memos, policy gating, granular pausing and ERC-2612 permit (Base docs: B20). B20 tokens are implemented as Rust precompiles deployed through the singleton B20Factory precompile rather than as EVM contracts, and the Asset variant supports 6–18 decimals (Base docs).

The tokenized stocks in B20Fold pools are Coinbase-issued B20 xxxc assets with 8 decimals. Because they are B20 Assets, issuers may enforce transfer policies (blocklist or allowlist) on holders — an allowlist-gated stock token can make a pool untradeable for non-allowlisted addresses (Base docs). FOLD itself is created as an adminless B20 asset when the factory precompile is available; otherwise a plain fixed-supply ERC-20 with no owner, mint or pause is used. The Contracts page shows which variant a deployment uses.

Providing liquidity

  1. Open a pool and enter a USDC amount or a share count. Shares are minted at the current reserve ratio: shares = totalShares × usdcIn / usdcReserve; previewDeposit returns the exact amounts of both legs.
  2. Approve both tokens to the hook, then call addLiquidity(key, shares, max0, max1, deadline). The site caps each leg at preview × 1.005 and sets a 20-minute deadline.
  3. Withdraw with removeLiquidity(key, shares, min0, min1, deadline); minimums are preview × 0.995. If minDepositBlocks is set, fresh deposits must age that many blocks first.

Pools with externalDepositsEnabled = false accept liquidity only from the OperatorController; the deposit panel is disabled for them. Paused pools (livePools = false) revert on swaps and deposits until the operator resumes them.

FOLD staking

The protocol's own LP position (bootstrap plus top-ups) is tracked as basis per pool. The operator may harvest at most maxHarvestBps of it per call. The USDC side of a harvest is sent to the StakingVault through notifyReward and streamed to stakers over the rewards period; the other leg goes to treasury. Staking is instant; unstaking is a two-step request → cooldown → withdraw flow. Requesting again adds to the pending amount and restarts the timer for the whole pending balance, cooling stake earns nothing, and cancelUnstake puts it back to work immediately. exit requests unstake of the full balance and claims rewards in one call. The cooldown is capped at 30 days and the rewards period at 90 days.

Admin keys & timelock

KeyCanCannot
Owner (Ownable2Step)Open pools, set parameters, toggle external deposits, queue timelocked actionsExecute ownership transfer, allowlist swap or protocol-principal withdrawal before the 48h timelock elapses
Operator (hot key)Re-range buckets within maxRangeTicks, harvest ≤ maxHarvestBps, pause / unpause, refresh vault approvalsTouch external LP shares, change vaults, move principal
WatchdogPause poolsUnpause, move funds, change any parameter
AnyoneAdd / remove their own liquidity, stake, swap through standard v4 routersRegister pools (Registry is controller-only)

Timelocked actions are queued on-chain by id and are visible through timelockEta(id); the queue can be cancelled but not shortened. Live parameter values are on the Contracts page.

Risks

  • Vault risk. Folded USDC is exposed to the Morpho vault's curator decisions, its underlying markets, and bad debt or liquidity shortfalls in those markets (Morpho docs). If the vault cannot honour withdrawals, the hook cannot pay out the USDC leg.
  • Impermanent loss. An LP share holds both legs. When the asset price moves, the pool rebalances against you like any concentrated-liquidity position; vault yield and fees may not cover the difference.
  • Oracle deviation. Pool price can drift from the Chainlink reference feed. The pools page shows the deviation in basis points; a large deviation means the pool is mispriced relative to the wider market and arbitrage will move value out of LP shares.
  • Issuer and policy risk. B20 stock tokens can carry transfer policies, pausing and freeze-and-reissue controls enforced by the issuer (Base docs). Holders and pools inherit those constraints.
  • Admin keys. Owner, operator and watchdog keys exist. A compromised operator key can pause pools and move buckets within the tick limit; a compromised owner can queue — but not immediately execute — ownership transfer or principal withdrawal. Watch timelockEta.
  • Smart-contract risk. The DualPool hook has a public audit (Uniswap docs); B20Fold's own contracts are unaudited. Only deposit what you can afford to lose.

FAQ

Do I need FOLD to provide liquidity?

No. Any pool accepts its two legs (asset + USDC). FOLD is only needed to stake for harvest rewards.

Where does my yield come from?

Two places: swap fees at the pool's static fee tier (0.05% stocks, 0.01% WETH, 1% FOLD) and the Morpho vault APY on the USDC leg. Both accrue into the value of your shares — there is no separate claim step for LPs.

Why does the pool show almost no v4 liquidity on explorers?

Because inventory rests in the vault and the hook between swaps and is deployed just-in-time. Use the hook's quote views or this site's pool page for real depth (Uniswap docs).

How do I know a pool is genuine?

Only the OperatorController can register pools. Call Registry.isGenuine(poolId) or check that the pool appears on the Pools page, which reads the Registry directly.

Can the team withdraw my liquidity?

No. The controller only manages the protocol's own position (basis). External LP shares are held by the hook on your behalf and are never touched by the controller.

What wallets are supported?

Any injected EIP-1193 wallet (Rabby, MetaMask, Coinbase Wallet browser extension). The site will prompt to switch or add Base (chain 8453).

How is APR on the stake page calculated?

rewardRatePerSecond × 365 days ÷ (totalStaked × FOLD price), with the FOLD price read from the FOLD/USDC pool. If no FOLD pool is registered yet, the page shows USDC per FOLD per year instead.