Integrate
Borro for desks and bots.
Every market is on Morpho and every position is in your own name. Read the markets as JSON, send transactions straight to the contracts, and run your strategy from a wallet, a Safe or your own executor contract.
Quickstart
Three reads and you are trading. Chain id 4663, your own RPC endpoint, your own keys.
Read the registry
/contracts.json has every address, deploy block, code hash, ABI, owner and timelock, and every market's Morpho ids, oracles and LLTVs. Check each code hash against the chain.
Read the markets
/markets.json has prices, rates, what is free to borrow and the market clock; /prices.json has route quotes against the feed for arbitrage.
Call the contracts
Approve USDG, then BorroShort.open. Watch /health/<wallet>.json or Morpho directly, and close with BorroShort.close.
const reg = await fetch("https://borro.markets/contracts.json").then((r) => r.json());
const nvda = reg.markets.find((m) => m.symbol === "NVDA");
const id = nvda.short.market_id;
// quoteSell always reverts with QuoteResult(amount): that revert is the answer.
const gross = await quote(id, "sell", borrowAmount);
const net = gross - (gross * openFeeBps) / 10000n;
const minProceeds = (net * 9950n) / 10000n; // 0.5% slippage
const deposit = (notionalUsdg * 10000n) / 4000n - minProceeds; // open at 40% LTV
await wallet.writeContract({ address: USDG, abi: erc20, functionName: "approve", args: [BORRO_SHORT, deposit] });
const gas = await pub.estimateContractGas(openCall);
await wallet.writeContract({ ...openCall, gas: (gas * 13n) / 10n }); // add 30%: see Gascd examples/typescript && npm install
BORRO_RPC_URL=<your fork> BORRO_API=https://borro.markets fork/e2e.sh
BORRO_RPC_URL=<your RPC> PRIVATE_KEY=0x... node quickstart.tsfrom borro import Borro
b = Borro(rpc_url=os.environ["BORRO_RPC_URL"], private_key=os.environ["PRIVATE_KEY"])
nvda = b.market("NVDA")
b.open_short(nvda, notional_usd=100, target_ltv=0.4)
print(b.short_position(nvda)) # owed, held, health, liquidation price
b.close_short(nvda)cd examples/python && python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
BORRO_RPC_URL=<your RPC> PRIVATE_KEY=0x... .venv/bin/python quickstart.pyThe quickstarts cover every flow a desk needs: read the markets, open a short, watch its health, add to its deposit and close it; lend a stock and USDG and withdraw them; borrow USDG against a stock and repay; read the fees paid and Locker Rebates. They take the RPC from BORRO_RPC_URL and have no default. To come: the public link to the examples.
Contracts
The ones a desk calls. The full registry, with ABIs at /abi/<Name>.json, is /contracts.json.
| Contract | Address | What it does | Owner | Source |
|---|---|---|---|---|
| BorroShort | 0xd2D686cEdf225c5C9815187A08812783904E62Eb | Opens and closes shorts in one transaction; positions are keyed to the caller. | Hoard Safe | Verified |
| BorroRouter | 0x85ccddcb0588A434BD57F86A59381E40154650Ca | Lend a stock and post it as collateral for a USDG loan in one call. | Hoard Safe | Verified |
| Morpho | 0x9D53d5E3bd5E8d4Cbfa6DB1ca238AEA02E651010 | Every market, position, health check and liquidation. | Morpho | Verified |
| bUSDG | 0xF8e0ab443098c0738489B5BAFFbd32A1341F2289 | Borro's USDG vault: lend USDG. | Hoard Safe | Verified |
| Steakhouse USDG | 0xBeEff033F34C046626B8D0A041844C5d1A5409dd | Holds the money behind every short. Curated by Steakhouse Financial, not Borro. | Steakhouse | Verified |
| USDG | 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 | Global Dollar, 6 decimals. | Its issuer | Verified |
| BorroLiquidator | 0x0320edb6543Ba5482dd2e3C626934F0eDD501A4f | Liquidates for anyone with no capital of their own. | None | Verified |
| BorroLock | 0x2560E389aFEF15D80eFd33e6befaAF017A9eE37f | $BORRO locks: the Drip boost and Locker Rebates. | None | Verified |
| Market | Short market id | Short LLTV | USDG loan market id | Loan LLTV | Feed |
|---|---|---|---|---|---|
| Every market's ids, oracles, caps and routes are in /contracts.json. | |||||
Every contract here is source-verified on Blockscout, and every one of Borro's own on Sourcify too. Borro's are partial matches: the deployed bytecode is identical to the published source, and only the metadata hash differs.
From the registry.
How a short works on chain
open(id, deposit, borrowAmount, minProceeds, deadline) takes your USDG deposit, holds it as Steakhouse USDG shares, borrows the stock on Morpho for your BorroAccount, sells it through the stock's route, takes the open fee, and holds the sale money too.
- Morpho checks the borrow against the deposit plus
minProceeds, so size the deposit as notional over your target LTV, lessminProceeds. close(id, shares, heldOut, topUp, maxCost, deadline)buys the stock back from the money held, repays, and pays the rest out in USDG. Pass the maximum number for all of it.addDeposit,takeDepositandborrow(the stock to your wallet, unsold) work on the same position.- A basket is one
multicallof opens: all legs or none.
Where your position lives. Morpho.position(marketId, BorroShort.accountOf(wallet)). The account's address is known before it exists. A USDG loan is your wallet's own Morpho position.
Health. Morpho's own test: collateral times oracle.price() over 1036, times the LLTV, against what is owed. A short is liquidated when the stock rises to its liquidation price; a USDG loan when the stock falls to it.
Lending. Each Borro vault is a Morpho Vault V2: deposit(assets, onBehalf) and redeem(shares, receiver, onBehalf). Stock on loan to shorters cannot be withdrawn until it is returned.
Borrowing USDG. BorroRouter.lendAndPost, then borrow, repay and withdrawCollateral on Morpho directly.
Events to subscribe to
Every event of these contracts, with its fields, is in /contracts.json under events.
| Contract | Event | topic0 | Indexed |
|---|---|---|---|
| BorroShort | Opened(shorter, id, deposit, borrowed, proceeds, fee) | 0x7ab3a51df0a150b760784d7ac9684631c3e5b3749cb9256747cbc49d1de6f204 | shorter, id |
| BorroShort | Closed(shorter, id, returned, cost, paidOut) | 0x4bcd5ff109723fc19ac22b570e437c0d1ea2d595d26f610b2589bee1f41fe076 | shorter, id |
| BorroShort | AccountOpened(shorter, account) | 0x4c85972a811b8f152b75ea02817b25ede88583f498d8da0de57b41bccda176de | shorter |
| BorroShort | Paused(id, paused) | 0xa499c64cb41b6d862c41c8a979bb269a3ec682e7f77aae46a081ecae2984f1e7 | id |
| BorroShort | RouteQueued(id, v3Pool, readyAt) | 0x4d0a388a42aea3132c1b0c833aa00ad152edf4fa284543856809e3b2520072b7 | id |
| Morpho | Borrow(id, caller, onBehalf, receiver, assets, shares) | 0x570954540bed6b1304a87dfe815a5eda4a648f7097a16240dcd85c9b5fd42a43 | id, onBehalf, receiver |
| Morpho | Repay(id, caller, onBehalf, assets, shares) | 0x52acb05cebbd3cd39715469f22afbf5a17496295ef3bc9bb5944056c63ccaa09 | id, caller, onBehalf |
| Morpho | SupplyCollateral(id, caller, onBehalf, assets) | 0xa3b9472a1399e17e123f3c2e6586c23e504184d504de59cdaa2b375e880c6184 | id, caller, onBehalf |
| Morpho | WithdrawCollateral(id, caller, onBehalf, receiver, assets) | 0xe80ebd7cc9223d7382aab2e0d1d6155c65651f83d53c8b9b06901d167e321142 | id, onBehalf, receiver |
| Morpho | Liquidate(id, caller, borrower, repaidAssets, repaidShares, seizedAssets, badDebtAssets, badDebtShares) | 0xa4946ede45d0c6f06a0f5ce92c9ad3b4751452d2fe0e25010783bcab57a67e41 | id, caller, borrower |
| BorroRouter | LentAndPosted(who, id, stock, assets, shares) | 0xe205b9b15a06a521cc9b734bce0a570204d4090521073faabaf9962f09220753 | who, id |
| BorroLock | LockChanged(account, amount, unlockAt) | 0xac9f677e99a4df77ed2008bfe08de2e751aab75dce03486489e20585d79e91ce | account |
| Vault V2 | Deposit(sender, onBehalf, assets, shares) | 0xdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7 | sender, onBehalf |
| Vault V2 | Withdraw(sender, receiver, onBehalf, assets, shares) | 0xfbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db | sender, receiver, onBehalf |
A short's Morpho events name its BorroAccount, not the wallet. To watch a wallet's short liquidations, filter Liquidate by market id and borrower = accountOf(wallet). A USDG loan's events name the wallet itself.
Errors and what they mean
Simulate before you send: a revert comes back by name. Morpho's own reverts are plain strings.
| Error | Selector | Meaning | What to do |
|---|---|---|---|
| TooLittle(got, wanted) | 0x618bc7c9 | The sale brought less than minProceeds after the fee. | Quote again; widen slippage or cut size. |
| TooMuch(cost, limit) | 0x6073b21f | Buying back costs more than maxCost, or more than the money held plus topUp. | Quote again; raise maxCost or add topUp. |
| Expired() | 0x203d82d8 | The deadline passed before it landed. | Resend with a fresh deadline. |
| IsPaused() | 0x1309a563 | New shorts are paused on this stock. | Closing still works. |
| NotListed() | 0x665c1c57 | The market id is not listed. | Use the ids in the registry. |
| NothingOwed() | 0x797dba54 | This caller has no position there. | Close from the address that opened it. |
| PoolRanDry() | 0x76a15698 | The route could not fill the size. | Cut size; check depth in /prices.json. |
| QuoteResult(amount) | 0x17080d7e | The answer of quoteSell or quoteBuy, not a failure. | Decode the amount. |
| TooFewShares(got, wanted) | 0xe2ce129e | The vault gave fewer shares than minShares. | Lower minShares. |
| AbsoluteCapExceeded() | 0x4616e4af | The vault's cap on its market is full. | Deposit less. |
| "insufficient collateral" | Error(string) | Morpho: the borrow or withdrawal would pass the LLTV. | Add collateral or borrow less. |
| "insufficient liquidity" | Error(string) | Morpho: not that much is free to borrow or withdraw. | Size to what is free. |
| "zero assets" | Error(string) | Morpho: an amount of zero, such as an open with no deposit and no minProceeds. | Pass a deposit or minProceeds. |
Oracle and weekend rules
Prices are Chainlink's Robinhood feeds: 8 decimals, 24 hours on weekdays, a 0.5% deviation and a 24-hour heartbeat. Each market's Morpho oracle has no owner and cannot be changed.
While the stock market is shut, from Friday 8pm to Sunday 8pm New York time and on market holidays, the feeds pause at their last print and every contract keeps using it: opens, borrows, health checks and liquidations. The first print after a weekend lands at midnight UTC on Monday and can move past a liquidation price in one step.
The site's rule is not on chain. Borro's site sizes a new short at the top of Borro's weekend band and values posted stock at its bottom while the market is shut. The contracts do not enforce that, so a bot that opens on a weekend carries the Monday gap itself.
The clock. /markets.json market_clock gives open, the next close and the reopen. /weekend.json gives the same, and each stock's weekend price and 95% band while the market is shut.
Fees, and Locker Rebates for market makers
- Open fee: 0.10% of the sale money today, read live from
openFeeBps(). The contract caps it at 0.30%. - Borrow fee: shorters and USDG borrowers pay the market's Morpho rate to its lenders. Borro's vaults keep 10% of what their lenders earn.
Locker Rebates. Lock $BORRO with at least 30 days left and Borro gives back its own fees on your shorts and loans, weekly in USDG once a dollar or more is owed. Your share is the lock's value over 10% of what you borrow, up to all of it: a $10,000 position needs $1,000 of $BORRO locked for the full rebate.
Round trips get their opening fee back, times your share, however soon they close. A rebate is never more than the fee paid, and round trips are left off the leaderboards. To come: the date rebates start paying.
Limits, caps and liquidations
Liquidation is Morpho's own: anyone can liquidate once what a position owes passes its collateral times the LLTV.
| Stocks | Short LLTV | USDG loan LLTV | Liquidation bonus, short | Liquidation bonus, loan |
|---|---|---|---|---|
| SPY, QQQ | 77% | 62.5% | 7.4% | 12.7% |
| NVDA, AAPL, GOOGL, MSFT | 62.5% | 62.5% | 12.7% | 12.7% |
| TSLA, MSTR, COIN, PLTR, SPCX, GME | 62.5% | 38.5% | 12.7% | 15% |
- Caps: each stock vault's cap on what it lends, and Borro's USDG vault's cap on each loan market, are in the registry and live in
/markets.json. What you can borrow now is the market's free liquidity. - Changes: raising a cap waits 3 days in public; lowering one is immediate.
- Liquidators: BorroLiquidator does it with no capital of your own, or call Morpho's
liquidate. A shortfall a liquidation cannot cover falls on that market's lenders. - Watch health:
/health/<wallet>.jsonor the MCPhealthtool lists every position worst first, with the limit used and the price that liquidates it.
Smart-contract wallets
Every call works from a contract. Positions belong to whoever calls, so a position an executor opens is the executor's.
| Caller | On chain | $BORRO Drip | Locker Rebates |
|---|---|---|---|
| Regular wallet | Works | Yes | Yes, with its own lock |
| EIP-7702 wallet | Works | Yes | Yes, with its own lock |
| Safe | Works | Yes | Yes, with its own lock |
| Executor contract | Works | No | Yes, with its own lock. Coming: linked to a main wallet's lock. |
| ERC-4337 account | Works | No, unless it is a Safe | Yes, with its own lock. Coming: linked. |
Tested on a fork. An executor contract opened and closed a short in one transaction; a Safe batched approve and open; an ERC-4337 smart account opened and closed through user operations; an EIP-7702 wallet batched approve and open; one call opened a two-stock basket.
The Drip is for wallets only: regular wallets, Safes and EIP-7702 wallets. Executor contracts earn no Drip.
Linked executors are coming. Your main wallet, or a Safe through EIP-1271, holds the lock and signs a list of its executor contracts. The lock then covers the wallet and every executor together, their borrowing is added up, and rebates are paid to the main wallet. To come: the address to send the signed list to, and the date it goes live.
Bookrunner borrow bindings need a signature from a wallet's own key today, so Safes and other contracts cannot bind yet.
The JSON API
Public, read only, open to any origin. Described as OpenAPI 3.1 at /openapi.json.
| Route | What it is | New copy | Timestamp |
|---|---|---|---|
| /contracts.json | The contract registry | Per release | generated_at |
| /markets.json | Every market, live | 60 seconds | read_at |
| /prices.json | Route quotes against the feed | 30 seconds | at |
| /health/<wallet>.json | A wallet's health and liquidation prices | 10 seconds | read_at |
| /weekend.json | The market clock and weekend prices | 5 minutes | at |
| /stats.json | Totals and per-stock numbers | 5 minutes | updatedAt |
| /rebates.json | Locker Rebates statement | Daily | updatedAt |
- Every answer carries an
ETag: send it back asIf-None-Matchfor a 304. X-Borro-Updated-AtandX-Borro-Data-Agesay when the data was made and how old it is./v1/before any path answers the same; version 1 only ever gains fields.
- Live answers and the MCP server share one allowance per address with
/rpc: 250 units each 10 seconds. Past it, the last good copy or a 429 withRetry-After. - For exact integers, read the chain: the JSON gives decimals in whole units.
MCP for agents
An MCP server at https://borro.markets/mcp: tools that read Borro, and tools that prepare unsigned transactions, approvals first, each simulated from the wallet. It never holds or asks for keys.
claude mcp add --transport http borro https://borro.markets/mcpEvery tool is listed at /tools.json; more on the Agents page.
Gas
Measured on a fork of Robinhood Chain. Add 30% to every estimate.
| Action | Gas |
|---|---|
| Approve USDG or a stock | 58,000 to 64,000 |
| Open a short | 1,183,000 to 1,197,000 |
| Add to a short's deposit | 432,000 to 461,000 |
| Close a short | 545,000 to 556,000 |
| Open and close in one executor transaction | 1,359,000 |
| Open a two-stock basket | 1,674,000 |
| Lend a stock / withdraw it | 262,000 to 290,000 / 221,000 to 263,000 |
| Lend USDG / withdraw it | 368,000 to 376,000 / 317,000 to 337,000 |
| Lend and post a stock | 383,000 |
| Borrow USDG / repay / take collateral back | 197,000 / 79,000 / 71,000 |
Why the 30%. The vault behind every short accrues interest lazily, and the first transaction in a new second pays for it. On the fork, transactions sent with the plain estimate ran out of gas.
Security, plainly
- Owner: the Hoard Safe, 0x8A22d6e75e9427b9D57e42a7984200029e7E173C, needs 2 of its 3 signers. It owns BorroShort, BorroRouter and every Borro vault.
- What it can do on BorroShort: list markets, change a stock's route only after a 2-day public wait, pause new opens (never closes), and set the open fee up to 0.30%. It cannot reach a position, and the code cannot be upgraded.
- Vaults: raising caps, changing adapters, fees or gates waits 3 days in public. Every timelock is in the registry.
- No owner: BorroLock and BorroLiquidator.
- Source: public at github.com/Borro-Markets/contracts. Borro's own contracts are verified on Sourcify and Blockscout, so you can read the exact source of what is deployed; the Morpho-made ones are verified too. Borro's are partial matches: the bytecode is identical to the published source and only the metadata hash differs. Each contract's status is in the registry.
- Audit: Borro's own contracts have not been audited by an outside firm. They have had internal adversarial reviews, and caps are kept small. An application to the Arbitrum Security Program is in.
- Morpho: Morpho Blue and Morpho Vault V2 are Morpho's own contracts, audited by outside firms.
- To come: a security contact for reports.
Status and contact
The chain is the source of truth: every number on this page can be read from it directly. No uptime or response time is promised for the JSON routes or the MCP server.
To come: a status page for pauses, keeper outages and route changes, and a contact for desks. Until then, write to team@borro.markets.

