# Borro: the desk, the resident box, the agent's option

**Status (2026-10-25):** release 2.3.0 (a minor: nothing widens what a wallet can sign). Built on
`claude/affectionate-pasteur-ffjuks`; the desk's move from the bank's hall to the exchange floor came over from
`claude/borro-film` (the desk and its cast only; the film's own files stay on that branch).
Read only: the city holds no key, builds no transaction, relays nothing and signs for no one. No city funds, no
subsidy, and no team-agent position. Borro's short product (BorroShort, pairs, baskets, bear coins) is left out on
purpose: nothing here reads, shows or points to it.

Borro (https://borro.markets) is a stock-lending desk on Robinhood Chain (chain 4663) built on Morpho. For each of
twelve tokenized stocks there is a **lending vault** (a Morpho Vault V2: bNVDA, bTSLA…) that lenders deposit the stock
into. There is one **USDG vault** (bUSDG) that lends USDG. For each stock there is a **USDG loan market** on Morpho
where a wallet posts its stock-vault shares and borrows USDG against them. Every position is the wallet's own.

## 1. What was found, and where

| what | where |
|---|---|
| the site | https://borro.markets (terms: /terms; risks: /#risks; integrators: /developers) |
| **the MCP server** | `https://borro.markets/mcp` — streamable HTTP, stateless, no login. `GET` answers a plain-text description; `POST` JSON-RPC 2.0. `initialize` answers `serverInfo { name: "borro", version: "1.0.0" }`, protocol 2025-06-18. 45 tools. It "never holds or asks for keys: every transaction is signed and sent by the wallet's owner" |
| the registry | `https://borro.markets/contracts.json` (schema 1, generated at block 94,432,018): every address, deploy block, code hash, ABI link, owner, timelock, and each market's Morpho ids, oracles and LLTVs. The same list is the MCP `rules` tool |
| the ABIs | `https://borro.markets/abi/<Name>.json` (VaultV2, Morpho, AdaptiveCurveIrm, ChainlinkFeed, MorphoChainlinkOracleV2, BorroRouter…). The functions the read calls are copied into `js/econ/borro.abi.json` |
| the source | https://github.com/Borro-Markets/contracts (commit `129f1e8`, 2026-10-17). `src/BorroRouter.sol:66` is `lendAndPost` (lend a stock into its vault and post the shares on Morpho in one call) |
| verification | Borro's own contracts: Sourcify `match` (partial: creation and runtime bytecode identical, only the metadata hash differs) and Blockscout partial. Morpho, the IRM and Steakhouse USDG: Sourcify match. USDG: exact match. The registry's `audit` field: **"No outside audit yet. Internal adversarial reviews only. Caps are kept small."** |
| the owner | the "Hoard Safe" `0x8A22d6e75e9427b9D57e42a7984200029e7E173C`, a 2-of-3 Safe 1.4.1: owner and curator of every Borro vault, owner of BorroRouter. Vault V2 timelocks: 3 days for risky curator changes (adapters, caps up, fees, gates) |

**Checked on chain, not assumed** (2026-10-25, block 97,239,867, the public RPC):

- `keccak256(eth_getCode)` equals the registry's `codehash` for all 17 contracts listed and all 12 stock vaults.
- `Morpho.idToMarketParams(id)` equals the registry's params for all 24 market ids (12 lending, 12 USDG loan).
- Every stock vault's `asset()` is that stock's token; bUSDG's is USDG.
- The read itself re-runs the params and asset checks once per instance (`api/_borro.js fixed()`) and **refuses to
  answer** if the chain disagrees. The market ids are not written anywhere in the client tree: the read computes each
  one as `keccak256(abi.encode(params))` (Morpho's `MarketParamsLib.id`), and `test/borrotest.mjs` holds all 24 to the
  registry's.
- Borro's MCP `markets` answer and the city's own read agree (NVDA's USDG market: 6,804.97 supplied, 804.29 borrowed,
  borrow rate 0.25% a year).

## 2. The contracts

| | address | what |
|---|---|---|
| Morpho Blue | `0x9D53d5E3bd5E8d4Cbfa6DB1ca238AEA02E651010` | every market, position, health check and liquidation |
| AdaptiveCurveIrm | `0x2BD3d5965B26B51814AC95127B2b80dD6CcC0fa1` | the rate model of every Borro market (`borrowRateView`) |
| bUSDG (Borro USDG vault) | `0xF8e0ab443098c0738489B5BAFFbd32A1341F2289` | lend USDG; its one adapter `0x45233242b7aFA26Bd36Cd7E7C9Db23bD8668cbEc` lends into the twelve loan markets under caps |
| BorroRouter | `0x85ccddcb0588A434BD57F86A59381E40154650Ca` | `lendAndPost`: lend a stock and post the vault shares in one call |
| BorroLiquidator | `0x0320edb6543Ba5482dd2e3C626934F0eDD501A4f` | liquidates for anyone, paying with what it seizes; no owner |
| Steakhouse USDG | `0xBeEff033F34C046626B8D0A041844C5d1A5409dd` | Steakhouse's vault (not Borro's): the collateral of the stock lending markets |
| USDG | `0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168` | Global Dollar (Paxos), 6 decimals, upgradeable by its issuer |

The twelve stocks, each with its vault (18 decimals), its Chainlink feed (8 decimals, 24/5) and its USDG loan limit
(the Morpho LLTV) are in `js/econ/borro.js`:

| stock | vault | USDG loan limit | liquidation bonus |
|---|---|---|---|
| SPY, QQQ, NVDA, AAPL, GOOGL, MSFT | bSPY `0xC3ED…60c1`, bQQQ `0xbAD8…cc17`, bNVDA `0x508E…7CB9`, bAAPL `0x24Fe…c64a`, bGOOGL `0xf073…0E4f`, bMSFT `0x8032…4e71` | 62.5% | 12.68% |
| TSLA, MSTR, COIN, PLTR, SPCX, GME | bTSLA `0x60b3…c988`, bMSTR `0x2B74…37e9`, bCOIN `0x7BA7…1257`, bPLTR `0xFE94…7B27`, bSPCX `0x1261…9791`, bGME `0x3284…4C3d` | 38.5% | 15% (Morpho's cap) |

The bonus is Morpho's: `min(1.15, 1 / (1 − 0.3 × (1 − LLTV)))`.

## 3. The flows (what an agent does from its own wallet)

1. **Lend a stock.** `approve(vault or router, exact amount)` on the stock, then `vault.deposit(assets, onBehalf)`.
   The shares stay in the wallet. The vault lends the stock on that stock's Morpho lending market. Only what is not
   borrowed out can be withdrawn at once (`redeem` / `withdraw`); the rest comes back as it is repaid.
2. **Lend USDG.** The same with bUSDG. Its lenders are paid the interest the USDG borrowers pay, less the vault's
   10% performance fee. A withdrawal may wait while the money is lent.
3. **Borrow USDG against a stock.** `BorroRouter.lendAndPost` (lend the stock and post the vault shares as Morpho
   collateral in one call), then `Morpho.borrow` up to the stock's limit, `Morpho.repay`, and
   `Morpho.withdrawCollateral` to take the shares back.

Borro's MCP builds each of these: `supply { wallet, asset, amount, post? }`, `withdraw`, `borrow`, `repay`, and the reads
`markets`, `market`, `prices`, `rules`, `position`, `health`. Each builder answers
`transactions[{ step, kind, to, data, value, chainId }]` with a `simulation` from the wallet (`eth_simulateV1`). Tried
read-only on 2026-10-25 against #100108's wallet: `supply NVDA 0.01 post:true` gave an exact `approve` to the router
and `lendAndPost`, both simulating ok; `borrow NVDA 0.5` simulated "insufficient collateral" (nothing posted), as it
should; `withdraw USDG 1` gave `bUSDG.withdraw`, simulating ok. Borro appends an 8-byte ASCII tag ("BORROMCP") after
the ABI data of every call. Nothing was signed or sent.

## 4. The risks

- **Liquidation.** A USDG loan is liquidatable once `debt > collateral × oracle price / 1e36 × LLTV`. Anyone may then
  repay it and take the posted shares at the bonus above. The read shows `health = limit / debt` (1 or less is
  liquidatable), Borro's words (`ok`, `warn` from 85% of the limit, `danger` from 95%, `liquidatable`), and the stock
  price at which the loan becomes liquidatable.
- **The weekend gap.** The Chainlink feeds stop while the exchange is shut (Friday 20:00 to Sunday 20:00 New York, and
  NYSE holidays). Morpho keeps using the last price. The first print after the reopen can jump past a loan's
  liquidation price in one step. Borro's site sizes positions against a weekend band; the contracts do not.
- **Lent stock is lent out.** It cannot be withdrawn until it is returned, and a stock's lenders share the loss if one
  of its borrowers is not closed out before the price moves past what backs it (Borro's own `what_can_go_wrong`).
- **USDG lenders may wait** while borrowers use the money. bUSDG lends only into Borro's twelve markets.
- **The rest:** no outside audit yet; USDG and the stock tokens are issued by others (Paxos; Robinhood Assets (Jersey)
  Limited) and can be frozen or lose their peg; Steakhouse USDG is curated by Steakhouse, not Borro; the Hoard Safe can
  change caps and allocations at once and the rest after a 3-day timelock.

## 5. The read: `api/_borro.js`

`GET /api/agents?op=borro`, public and unsigned, rate-limited per IP like every other read. **Everything comes from
the chain** (Morpho, the vaults, the IRM, the Chainlink feeds and Morpho's oracles), through Multicall3. Nothing is
read from Borro's API or MCP.

| query | answers |
|---|---|
| (none) | per stock: price and its age; the vault's `totalAssets`; the lending market's supplied, borrowed, free and utilisation; the lenders' rate now; the USDG loan market's supplied, borrowed, free, utilisation, borrow rate now, LLTV and bonus; and bUSDG's size and its lenders' rate now |
| `&wallet=0x…` | per stock: stock lent (vault shares in the wallet, and `convertToAssets`), stock posted (Morpho collateral), USDG owed (borrow shares to assets, with interest to now), health, status, liquidation price; and USDG lent in bUSDG |
| `&view=desk` | the markets, plus the city's wallet agents who lend or borrow on Borro, **by name, never by wallet**, and how many wallets were read |
| `&view=resident&i=N&from=production` | on a preview mirroring production only: one of production's residents from its public civic read |

**The rates** are Morpho's variable rates now, a year, simple: `borrowRateView × 31,536,000`; supply = borrow ×
utilisation × (1 − Morpho's fee, 0 on all Borro markets). A vault's lenders' rate is the market's supply rate on the
part the vault has lent, over all it holds, less its 10% fee. Borro's own $BORRO rewards are not in them. Market totals
are brought to now with Morpho's own interest formula (`wTaylorCompounded`) before anything is divided.

**The switches.** `BORRO_ENABLED`: unset, off on production and on in previews (as Hedgehood's). `BORRO_RPC_URL`:
default the chain's own RPC. `BORRO_MIRROR_FROM=production`: on a preview only, the desk's roster and the mirrored
resident pages come from production's public civic read.

**Cost.** The markets: three multicalls cold (the address check once an instance, the state, the rates), two after.
A wallet: one, plus one for `convertToAssets` on what it holds. The desk: all 76 of production's wallet agents in seven
chunks of 300 calls (2.1 s cold, measured). Each answer is held in the instance (markets and wallets 60 s, the desk
120 s, the roster 10 min) and at the CDN (`s-maxage=60`). **No store command of its own:** Borro's positions are
contract state, not an event log, so there is nothing to index or keep. The desk's roster reads the city document the
route already serves, at most once in ten minutes an instance (and on a mirroring preview, not at all).

## 6. The surfaces

- **`/borro`** (`borro.html`, `js/borropage.js`): the desk page, in the olive finance kit of `/longbow` and
  `/hedgehood` (`css/finance-pages.css`). Borro's name in its lockup (plain text; the marks follow in a patch), the
  city's wallet agents on Borro, four tiles, the book of twelve stocks, how it works, and the risk in plain words. Linked
  from `/bank`, `/longbow` and `/hedgehood`.
- **The desk on the exchange floor** (`js/borro/borrodesk.js`, `js/borro/borrorules.js`; run by `js/exchange.js`): one
  counter on the trading floor of the San Verano Stock Exchange, before the board wall's big board and east of the pit,
  where the floor's traffic walks up the aisle toward the board and the board's own light falls. In the room's own idiom
  (a dark slab on steel frames, a brass nosing, Borro's amber under the lip), the name BORRO cut into its front and lit
  from inside the cut, a lightbox over it on two brass posts with the name and *LEND STOCK · LEND USDG · BORROW USDG*
  in plain text (text signage only: no mark but the name), a desk screen with **the Borro markets** redrawn from
  `?op=borro&view=desk` once a minute (each stock's price, stock lent, USDG borrowed, borrow rate now and limit), and a
  floor safe behind the counter against the board wall, its door on a hinge. No light of its own (the floor keeps its
  nine). Its boxes go on the city's colliders and its cells on the floor's walk grid. The floor's cast gives it two places
  (`js/exchangecast.js`): a clerk behind the counter — one of the roster's own residents, by identity, six in a hundred
  want the desk first — and, through the afternoon, a visitor before it: a resident whose own wallet lends or borrows on
  Borro, its standing on its plate ("lends 0.481 NVDA, 0.054 TSLA, 40 USDG on Borro"); nobody stands there otherwise.
  Both walk to it round the pit's rail. (It stood in the bank's hall until 2026-10-25; the hall is as it was.)
- **The resident box** (`resident.html`, **BORRO · LENDING AND USDG LOANS**): for a resident whose own key is an EVM
  wallet. Per stock: lent, posted, USDG borrowed, health, liquidation price, limit; USDG lent; the block read. **Hidden**
  for a native, a token-keyed resident, a retired key and a deployed agent (its wallet is its owner's). A failed read
  says "unavailable, not empty".

**Counted, not named.** A resident that opted out of credit (`creditOptOut`: its credit record's `oo`, the day it did;
`cr.oo` on production's public civic read, which a mirroring preview reads) is counted in the desk's totals
(`counts.onBorro`, `lending`, `borrowing`, `unnamed`) and is never named: not on `/borro`, not on the desk screen's head
line beyond the count, and never at the counter as a visitor. `/borro` says how many such residents there are, never who.
Everyone else is named by default, as at the Longbow and Hedgehood desks. Its own resident page still shows its own box.
On 2026-10-25 no resident of production had opted out. `test/borrotest.mjs` covers both paths and the mirror.

**Borro's marks** are not in this release: they wait for Borro's approval and follow in a patch. Until then `/borro`
shows the name in plain text in its lockup, and the desk's lightbox and counter are text only, as they always were.

## 7. The agent's option

`skill.md` §7 and `GET /api/agents?op=spec` → `borro` (also `GET /api/agents/onchain` → `borro`) point a wallet agent
to Borro's MCP for lending and USDG loans from its own wallet, list the ten tools it should use, the two flows, the
transaction shape and the liquidation risk. It is offered as an option, never an instruction. They say nothing about
Borro's short product. The city's own MCP and its signing allowlist (`toSign`) are unchanged: Borro's transactions
never pass through the city, and the city's `simulate` tool does not run them (Borro's own builders simulate each
step).

## 8. Not done, by decision

- **No team-agent position.** No resident of the team's lends or borrows on Borro, and nothing here seeds one.
- **No subsidy and no city funds.** The city puts no money into Borro and backs no position.
- **No shorts.** BorroShort, pairs, baskets, bear coins, options, gap cover, farms, leverage and credit lines are not
  read, shown or pointed to.

## 9. Questions for Borro's team

1. Is there a partner or attribution code we should pass, or is the "BORROMCP" calldata tag your only marker for MCP
   traffic? (We pass none: the city builds no transaction.)
2. Will `contracts.json` keep its schema (market ids, params, code hashes)? Our read checks params and code against the
   chain, and a new market needs a commit on our side before it shows.
3. When AAPL, GOOGL and MSFT go past their 0.001-share seed deposits, will their caps change? (Read live; noted because
   the board shows them nearly empty today.)
4. Is there a usage policy for agents calling the MCP at volume? We send none ourselves; residents' agents might.
5. An outside audit: is one planned, and when?

## 10. Verification (the frames)

Taken on 2026-10-25 against the branch preview at `84c856c`
(`grand-theft-claude-4nhv139a2-degens-projects-c603fbda.vercel.app`, build `gtc-19a184f1`), with `?from=production`
(production's wallet agents, the chain read live), block 97,258,394. In `docs/borro/` (the frames that showed Borro's
marks — `/borro` and the desk in the bank's hall — are out of the tree until Borro approves the marks):

- `fable-harlow-100108-2026-10-25.jpg`: `/resident?i=100108&from=production` on the preview. The Borro box: 0.480802
  NVDA, 0.053987 TSLA and 0.653034 SPCX lent, nothing posted, no loan, 40 USDG in bUSDG.
- `native-0-2026-10-25.jpg`: `/resident?i=0&from=production` on the preview (Anouk Stavrakakis, a native): no wallet,
  no Borro box.

On production's 76 wallet agents, one is on Borro today: #100108 Fable Harlow ("Fable and Codex"), lending 0.4808 NVDA,
0.054 TSLA, 0.653 SPCX and 40 USDG. Nobody in the city borrows USDG on Borro.

**The release frames (2.3.0, on `main` at a64301c).** Taken against the branch preview at `753352a`
(`grand-theft-claude-ehhx2zg7u-degens-projects-c603fbda.vercel.app`), headless (SwiftShader), the real client walked
into the exchange (`window.__debug`: the door, the clock pinned, a still from a set camera, `test/yardbridge.mjs`):

- `desk-day-exchange-2026-10-25.jpg`, `desk-day-wide-exchange-2026-10-25.jpg` (12:30) and
  `desk-night-exchange-2026-10-25.jpg`, `desk-night-wide-exchange-2026-10-25.jpg` (22:30): the counter before the big board,
  east of the pit, the lightbox, the markets screen. Nobody at it: the preview's own city has no residents on the floor
  (the production mirror stayed on the film branch), so neither the clerk nor a visitor is cast.

**Tests.** `test/borrotest.mjs`: 57 checks, all passing (one holds the lightbox's name clear of its column). `npm run
check` is clean. The rest of `npm test` passes except suites that fail the same way on `main` at a64301c: `filmcrafttest`
(it reads `js/hedgehoodfilm.js`, which is not in the repo), `clinictest` (the trailer's twelfth cut) and `raetotest` (the
deploy desk's model write). `erc8004test` failed on `main` too (it still expected nothing minted) and is fixed here: it
holds the record to the seven minted ids. `test/mcptest.mjs` (outside `npm test`): the same two checks fail as on `main`.
