The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Priors listing page.
Reputation for AI agents that is earned, public and recomputable: every point is real money held at risk for real time, and anyone can rebuild the number from the events.
Reviews are cheap to fake. Priors keeps a public record built from on-chain repayments, and Score v2 counts only debt someone else put at risk. It is not unfakeable: the score's own page lists what each attack costs and what still gets through.
claude mcp add --transport http priors https://mcp.priors.trade/mcp, or add https://mcp.priors.trade/mcp to any
MCP client (other clients).priors.trade · a prior is what you believe about an agent before you deal with it. This one is backed by money.
A small on-chain credit pool lends unsecured USDG ($5 to $500) to agents registered on ERC-8004. Every line is backed, dollar for dollar, by someone who put money behind that agent. Every repayment and every default is a public event. From those events you get a credit-backed trust score anyone can query before trusting an agent, and a public record of which agents pay their debts back.
Reviews are cheap to fake. Repaid debt isn't.
Status: Priors v2 is live on Robinhood Chain
Pool v2 (
CreditPoolV2) and treasury v4 are live on Robinhood Chain mainnet (chain 4663) since block 71,702,460, and the seat vault (SeatVaultV3) since 2026-09-25. Addresses come fromdeployments/4663.v2.json, sonpx priors-v2 statusand the SDK resolve them with no configuration. The v1 pool is paused; every v1 repayment record was imported into v2, so agents kept their history. A second seat vault for bigger lines, the growth seat vault (SeatVaultV4, $50 lines), is live since 2026-09-29 and takes no new seats since 2026-10-09.SeatVaultV5is live since 2026-10-09 with owner-only backing (redeployed on 2026-10-11 with the fixes of GHSA-g626 and GHSA-8pxr): an agent's owner stakes its own $PRIORS to open the agent's line, which V5's root backs 100% in USDG. It has had internal reviews, no external audit.What v2 changes. v1 lent earned credit that nobody backed, and every hard v1 finding lived there. v2 drops it: every line is 100% backed by a backer's locked pool shares, a default burns that backer's shares worth the principal and the fee, and lenders never take a loan loss. Lines need the agent owner's signed consent, agents can change sponsor between loans, and the pool's owner is the PoolSteward, which only the timelock can drive.
The timelock's delay goes from 48 hours to none. The owner decided it on 2026-10-09: the Safe scheduled
updateDelay(0), executable from 2026-10-11 19:49 UTC. Until then every timelock operation waits 48 hours, and the ones already scheduled keep their ready times. From then the Safe (2-of-3) can schedule and execute in the same block: the pool's owner calls through the steward (whose policy bounds still apply), the steward's handback, a stock vault upgrade, and the engines' and SeatVaultV5's timelocked settings. Each one is still public on chain, but there is no time to react before it runs.Four ways onto the ledger. A treasury invite (a $5 line from treasury v4, which is funded by $PRIORS creator fees), a seat (someone puts about five lines' worth of $PRIORS behind your agent and the growth seat vault backs a $50 line, after 10 repaid loans), a backer who stakes USDG and vouches for you directly, or, since 2026-09-28, a stock line (you deposit Robinhood stock tokens and the stock vault backs a line against them). Lenders deposit USDG and earn 50% of every fee.
What was found before launch, and fixed. A self-backing loop that took lender yield (fixed with a fee lock), a utilization-cap freeze (cap set to 100%), a farmable keeper bounty (set to 0), and three seat-vault issues (offers bound to the owner, idle seats expire, a history gate plus a seat priced far above the line it opens).
docs/SECURITY-v2.mdlists every finding with its test and what remains.What that does not buy: v2 has had several internal adversarial reviews and a second-model review of the fixes, but no third-party audit. No path to lender principal was found, and that is still not a proof. Launch sizes stay small for exactly that reason. Read it, run it, integrate against it; size your exposure like someone who knows an audit hasn't happened.
Take part: priors.trade has a Participate page (deposits, seats, roots), /agent and /invite.
docs/PARTICIPATE.mdis the same for code.
Run the npx commands inside the clone: there they run this repository's own CLI. Outside it, npx priors is an
unrelated npm package and priors-v2 on npm is only a placeholder that prints these instructions, so never run
them with PRIORS_KEY set anywhere else.
The wallet needs a little native gas, enough USDG to pay the fee (about $0.012 on $5 for 7 days) and, for a
self-service invite, the 5 USDG bond (step 2 below). A brand-new agent cannot take a seat:
npx priors-v2 join --seat <staker> takes a staker's seat offer only once the agent has repaid 10 loans (new seats go
to the growth seat vault; SeatVaultV3's 10 seats were all taken as of 2026-09-29). Without an invite, a first line comes
from a backer or a stock line.
That starts a local chain, deploys the v1 contracts with mocks, and walks one fresh ERC-8004 identity through the entire record — register, take the treasury's $5 line, borrow, hold, repay, score. The local devnet has not moved to v2 yet; the flow and the score have the same shape, the backing rules differ (v1's "earned capacity" below does not exist on v2):
Requires Foundry and Node 20+.
The v1 walkthrough was run for real on Robinhood Chain with real USDG, start to finish, through the CLI in this repo, and its records have now moved twice.
Agent #437 is the interesting one, because its record survived the migration to the fixed
contract. It repaid twice on the original pool; those two loans were carried across by
importRecords, and it has since borrowed and repaid again on the new one:
| step | transaction |
|---|---|
| borrow $5 for 8 days, on the new pool | 0xff8c3e1f… |
| repay principal + fee | 0x0cfc083c… |
npx priors report 437 read the result back off the v1 pool: 3 loans repaid, $15 of volume,
$0.036665 of fees paid, no defaults. At the v2 cutover every v1 record was imported again
(importFromV1), and on v2 agent #437 still shows its 3 repaid loans — the point of carrying
history rather than restarting it. Agent #443 came through the same way.
A record and a credit line are separate things, and a migration carries only the record: capacity
is not imported, so after a cutover an agent shows its full history with a $0 line and no
sponsor until it asks the treasury for one (firstLine, which only the identity's own controller
can call). That is why a freshly migrated agent reads as sponsor #0 for a while. On v2 it asks treasury v4 (an
invite), a staker (a seat) or a backer for a new line.
There is no demo mode in any of that — same contracts, same ERC-8004 registry, same USDG.
This repo ships the procedure as an agent skill, so you don't have to read any of it:
Then say: "give my agent a credit history on Priors". The skill knows the steps, which chain and pool it is
on, what to do when the treasury's epoch cap is spent, and that it must not claim an agent is registered when it
isn't. Codex and other harnesses read AGENTS.md at the repo root, which carries the same
instructions.
The skill and the hosted MCP server in one install. In Claude Code:
The skill runs as /priors:priors, and the read-only server connects as plugin:priors:priors-read, with no key. To
pay, borrow and repay with the agent's own wallet, add the local server too: skills/README.md
has the command, which keeps the key in your environment.
Read-only, hosted, no key: add https://mcp.priors.trade/mcp (Streamable HTTP) to any MCP client.
In Claude (web, desktop, phone), this link
opens the Add custom connector form with Priors filled in. VS Code installs it from
vscode:mcp/install?{"name":"priors","type":"http","url":"https://mcp.priors.trade/mcp"} (URL-encoded) or
code --add-mcp '{"name":"priors","type":"http","url":"https://mcp.priors.trade/mcp"}', and Cursor from
cursor://anysphere.cursor-deeplink/mcp/install?name=priors&config= plus the base64 of {"url":"https://mcp.priors.trade/mcp"}.
ChatGPT takes it in developer mode (Settings, Security and login), on its plugins page, with no authentication.
It answers an agent's record and score, the pool's figures, recent loans and the facilitator's services, and it
cannot sign or send anything. It is also an MCP App: in hosts that render MCP Apps (Claude, ChatGPT, VS Code, Cursor,
Goose) its answers come with a Priors view, and request_borrow / request_repay prepare a borrow or a repayment as
two links where the agent's owner confirms it: Go mode (go.priors.trade), with their own account, or the agent console
on priors.trade, with the wallet that owns the agent. To pay x402 URLs, borrow and repay with the agent's own wallet, run the local server in
packages/mcp (the key stays in your environment). Both packages are on npm:
npx -y @priors/mcp runs the local server, and npm i @priors/x402 installs the payer. From a clone,
npm install at the root links them instead. The local server is also on the official MCP Registry as
io.github.priors-agents/priors, and the hosted one as io.github.priors-agents/priors-read. Since 0.5.0 the wallet
can also keep idle USDG in savings (a USDG vault on Morpho: the agent's own money, with the vault's risks; see
packages/x402), and a borrow on a SeatVaultV5 line refreshes it first. Autopay (repaying each loan on
time from the agent's wallet) is in the packages and refuses to run until AutoRepay v2 is deployed. 0.2.11 carries
0.5.0's security fixes for those staying on the 0.2 line.
The same record is a paid x402 v2 endpoint: https://api.priors.trade/v1/report/{id} and /v1/score/{id}, 0.03 USDG
each on Robinhood Chain, settled through https://facilitator.priors.trade. An unpaid call answers 402 with the
requirements in its PAYMENT-REQUIRED header; packages/x402 pays it (createPayer({ signer }).pay(url)).
Merchants can sign up for the facilitator themselves at https://x402.priors.trade/merchants. A merchant can also
check who is paying before serving them. recordGate in @priors/x402 reads the payer's Priors record before the
facilitator sees the payment, and can refuse defaulters, ask for repaid loans or price by record. A refused payment
moves nothing (packages/x402).
| 1 | identity | register(uri) on the ERC-8004 registry, once. Your NFT is your identity. Already have an id? Skip this (set PRIORS_AGENT_ID if it predates v2 or was transferred to you). The pool never sees your keys, only the id. |
| 2 | a line | Treasury invite: at priors.trade/invite you post a 5 USDG bond in InviteBond from the wallet that owns the agent, prove you own it to the Telegram bot, and the bot signs treasury v4's invite for your id on the spot; you redeem it with your pool consent → $5 (at most $25 of new treasury lines a week). The bond comes back once the agent has repaid 3 qualified loans with none open, or after 4 days if it never gets a line; a default sends it to the Safe, so taking a first line and walking away nets nothing. Seat: a staker offers a seat of $PRIORS on your id and you accept it → $50 on the growth seat vault, loans of at most 7 days (your agent needs 10 repaid loans first). Backer: a root vouches any size with your signed consent. Stock line: you deposit accepted Robinhood stock tokens and the stock vault backs a line against them (see "Stock lines" below). |
| 3 | borrow, hold, repay | quoteFee → about $0.011666 for $5 over 7 days. borrow → USDG in your wallet. Do work. repay → principal + fee. Under 7 days repays fine but does not count: dollar-days are the score. |
| 4 | grow | Treasury v4 raise(agentId) → $25, after 3 qualified loans, 14 days, score ≥ 100 and a clean record: on hold since 2026-10-12 (the Safe set minQualified to 1,000,000). Beyond that, lines grow by finding a bigger backer: v2 has no unbacked "earned" credit. |
Or from JavaScript, on ethers v6, inside the clone (the priors/… imports resolve to this repository there; it is not
published on npm under that name):
docs/PARTICIPATE.md covers every role (lender, staker, backer, agent);
docs/FLOAT.md covers x402 payments on credit and the public facilitator at
https://facilitator.priors.trade; docs/AGENTS.md is the guide for agent builders.
A whole agent instead: agent001 holds its own wallet, joins Priors,
borrows and repays before the due date by itself, sells a service over x402, and talks from a terminal or Telegram. Its
money moves go through @priors/mcp. agent001 sandbox runs the whole loop on a local fork with play money. It also
carries a Priors skill for OpenClaw and Hermes Agent (skills/priors;
in Hermes, hermes skills install priors-agents/agent001/skills/priors): check an agent's record before paying it, and
pay, borrow and repay with the MCP tools.
Since 2026-09-28 the stock vault (StockVault, root #6424) backs a line with the agent's own Robinhood stock tokens.
The agent's owner deposits one of the 35 accepted tokens (listed with their Chainlink feeds in
deployments/stock-assets.4663.json) and the vault vouches a line of that
token's loan-to-value: 25-50% of the deposit's value, plus up to 15 points for a record of repaying treasury credit,
at most $250. Loans on it are ordinary pool loans, with the same fee, terms and default rules. Every new loan is
checked against the collateral's value now, so a falling price shrinks what can be drawn, and new credit waits during
a lending hold (a sharp price move, a multiplier change such as a split, a paused or blocked token). Closing gives
every token back, at once with no loan open, otherwise with the last repayment; a line unused for 30 days (7 if
never drawn) can be closed by anyone. A default seizes the whole deposit.
status(agentId) shows the tokens behind a stock line. @priors/mcp (stock_assets, stock_position) and
@priors/x402's credit helpers read the vault too, since 0.2.0; 0.2.1 carries the
security fixes of the private reports. A stock line counts as the borrower's own money in Priors
Score v2 (weights 2.0.1). The tokens are Robinhood's, under its own terms (including where they may be held), and
their issuer can pause, block, burn or upgrade them. The vault is the one upgradeable Priors contract: what its owner,
the Safe, can and cannot do is in docs/SECURITY-v2.md.
Miss a due date by more than three days and anyone can mark the loan defaulted. The record is marked defaulted forever, the identity can never borrow or back anyone again, and the owner's address is marked too. The sponsor pays: a backer's (or the treasury's) locked shares are burnt for the principal and the fee; on a seat, half the staker's $PRIORS is burnt. Your branch burns in the grove on priors.trade, in public, and stays burnt.
There is no appeal. That is why the score means something.
docs/PRIORS-GROWTH-SEATS.md).score(agentId) and creditReport(agentId) on
CreditLensV2, the same formula as v1, and every input is an event you can recompute yourself. Score v1 is
dollar-days: principal × how long you actually held it (capped at the contracted term), summed over repaid loans
— up to 400 points. Plus qualified loans (term ≥ 7 days, 20 points each, up to 200), backing (up to 150), age (up
to 150). Your own default is 0, always. The cheapest path to a high score is the honest one: borrow a meaningful
amount, hold it for weeks, repay.https://priors.trade/api/score-v2, in the paid
API and in both MCP servers: it counts only risk someone else took, lateness, x402 income from payers that aren't
the agent's own, and trust rungs that need time. Engine, weights and tests are in sdk/ and scripts/; how it
works and what stops gaming it: docs/SCORE-v2.md. The on-chain score above is unchanged.https://priors.trade/api/check?agent=<id> or ?address=<0x…>: free,
no key, CORS open. It returns the agent's repayment record and score, and a README badge at
https://priors.trade/api/badge/<id>.svg. See docs/CHECK-API.md.node scripts/verify-attestation.mjs <agentId>
checks one from the chain alone: docs/ATTESTATION-v2.md.docs/DESIGN.md has the v1 math and attacks; docs/SECURITY-v2.md the
v2 findings.
| Loan size | $5 – $500 |
| Term | 1 – 30 days |
| Fee | 1% per 30 days, pro rata, plus the sponsor's premium (0 on treasury and seat lines) |
| Fee split | 50% lenders · 25% sponsor · 25% reserve |
| Lender hold period | 7 days. Withdrawing sooner leaves 0.5% behind for the lenders who stayed |
| Grace before anyone can default you | 3 days |
| Counts as a qualified loan at | 7 days |
maxUtilizationBps · keeperBounty | 10000 · 0 |
| Treasury v4 | first line $5 · raise to $25 · $100 of new lines per 7-day epoch · idle after 30 days |
Seats (SeatVaultV3, root #6234) | seat ≈ $25 of $PRIORS (12,000 on 2026-09-25, resized with the price by the SeatSizer) · line $5 · 50% burnt on default · agent needs 3 repaid loans · seat expires after 30 idle days · $50 of new lines per 7-day epoch |
Growth seats (SeatVaultV4, root #6466; live since 2026-09-29) | seat ≈ 5 lines of $PRIORS (120,000 at deployment, resized by its own SeatSizer within [110,000, 20,000,000]) · line $50 · 50% burnt on default · agent needs 10 repaid loans · loans of at most 7 days · 20 open seats · seat expires after 30 idle days · $1,000 of new lines per 7-day epoch · protocol seats closed |
Stock lines (StockVault, root #6424) | 35 stock tokens · line 25-50% of the deposit's value by token, plus up to 15 points for a record of repaid treasury credit, never above 70% · at most $250 a line · $1,000 of new lines per 7-day epoch · $1,000 of open lines per token ($500 on CRCL, CRWV, NBIS, RGTI, SNDK, USAR) · whole deposit seized on default · idle after 30 days (7 if never drawn) |
| Minimum root stake | $10 |
| Pool owner | PoolSteward since 2026-10-06 (no owner, not upgradeable), which only the TimelockController can call (the Safe proposes and executes, no admin; a 48 h delay, none from 2026-10-11 19:49 UTC), within a fixed policy: sponsor ≤ 25%, reserve ≤ 40%, lenders ≥ 35%, fee ≤ 2% per 30 days; the Safe is guardian (pause ≤ 14 days, exits never pause) |
The target chain is Robinhood Chain mainnet (id 4663).
| Address | |
|---|---|
| CreditPoolV2 (live) | 0x281210097f0de7A8FB6F87310AF0f089c9C8DE21 |
| CreditLensV2 (score and report views) | 0x9d7035722bd42C551f82FEB9FDDd17453AEF3D9B |
TreasurySponsorV4 (live, root #6228, invite-gated) | 0x0c5091235A25bBFD3F5a009cBe04120D0CBAD573 |
SeatVaultV3 (live since 2026-09-25, root #6234) | 0x59D155C42A9263fA7596867b992bB3e84dF680a9 |
SeatVaultV2 (retired and paused, root #6229; replaced by V3 for audit V-2) | 0x6D934C07a33E7285cE691A9B258cdB53F18e6B5F |
| PoolSteward (owns the pool since 2026-10-06; only the timelock can call it) | 0x6D9D4135417E0AB2aafc69Fc842525Af279a5Da8 |
| TimelockController (drives the PoolSteward; 48 h delay, none from 2026-10-11 19:49 UTC) | 0x5d984C274035F81BB327d532897a902C5125F87c |
| SeatSizer (owns the seat vault since 2026-09-26; resizes the seat from the $PRIORS price, within bounds) | 0xd24B6484f4E68d72Fd2d3AF7bD036560B2ed5E61 |
SeatVaultV4, the growth seat vault (live since 2026-09-29, root #6466) | 0xb1c3a04496238D62E3c93118C297163855e22192 |
| SeatSizer of the growth seat vault (owns it; the Safe owns the sizer) | 0x97C4e594D458f8BBE961d5384Bbd8a9Cc2D18777 |
SeatVaultV5 (live since 2026-10-11, root #8762; owner-only backing; it replaced the deployment of 2026-10-09, 0xdE29D3A627d77CFA13c5331C1B7B181a4C5B752E, root #8641, which never had a book and whose root's USDG moved to it) | 0xC999014C6218EC41f0aE412d49AE8f2aA4c539fD |
SeatVaultV5's linked libraries (src/v5/; V5 calls them by DELEGATECALL) | V5Pos 0xd2d393Daf7bABE6ac090a97765407256673Bf9Bc, V5Steps 0x3a4B951E0eae25DD327224395ecE0C6fD6345f94, V5Book 0x90992387BbfBcF6aB179B33535d283FEE2d832F4, V5Records 0x23C3660Cd36401F6adE16f51f81F629a6aE6d826, V5Exit 0x0BB0CF27E9a0f5DB38a3F58fc31b1f90D784CbC7, V5Auto 0x22914DF387771bCB167Bfd1777678887D1AB1375, V5Sync 0x2FE6cC16DA24694D48BbDD8C83d4732ca23276F4, V5View 0xE5175D7C9C30E210197D23A45A3e0bbe66cF1846, V5Lens 0x294a483e54aBE64d2037df370974304C0F3B09F7 |
StockVault (live since 2026-09-28, root #6424; lines against stock tokens; a Transparent proxy, StockVaultProxy) | 0xbEcd07EC689988e16b870C121756C4c2C8cb02B6 |
| StockVault implementation (behind the proxy; since 2026-10-11) | 0x796FCcBdab6d473Ab22728D246AcAea0905b8E0B |
| StockVault ProxyAdmin (the only way to upgrade the vault; owned by the timelock since 2026-10-07, by the Safe before; with no delay from 2026-10-11 19:49 UTC) | 0x5174A18550a295cd25aF59416a56B7e4c38C8Afc |
| Safe (2-of-3; proposes to the timelock, owns treasury v4 and both SeatSizers, so every other seat vault power, and the stock vault's settings) | 0x20c6816B2419616238772591965E6E9AbE493fD5 |
| RevenueRouter (the steward's sweep and the vaults' fees go here) | 0xb2E217C5841a968C48Eee41DCbF7D2a03cCA4F43 |
| BuyAndBack (engine 1) | 0xEe40675aBEC90E52211526845433C10c80fD569E |
| PriorsLiquidity (engine 2) | 0xbEC159832D05749557F3f972c4823979C8dC8451 |
| SwapLimiter (the engines' depth guard) | 0x4535b1d92ebBe8176463dE4b58Ca9B66A231eb0E |
| KeeperHelper (SeatSizerV4's keeper since 2026-10-06; the keeper key's one entry point) | 0x3Ba233d2ac1C6233C5fA954C2DaaD83fcCFE5372 |
| InviteBond (the bond an automatic invite needs; no admin) | 0x8BE478c754D9124D11e78dB20F5bf4dA45403275 |
| ERC-8004 Identity Registry | 0x8004A169FB4a3325136EB29fA0ceB6D2e539a432 |
| USDG (Robinhood's 6-decimal dollar, the pool asset) | 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 |
| $PRIORS token (Pons V2) | 0xeDBf91223639800BCd5756815CAf908Df3b890bE |
| x402 facilitator | https://facilitator.priors.trade (merchant sign-up: https://x402.priors.trade/merchants) |
| Hosted MCP (read-only) | https://mcp.priors.trade/mcp |
| Paid API (x402 v2, 0.03 USDG) | https://api.priors.trade |
RPC (official; the only one that serves eth_getLogs) | https://rpc.mainnet.chain.robinhood.com |
| RPC backups, for plain calls | https://robinhood-rpc.publicnode.com, https://rpc.ordofi.network |
| Explorer | https://robinhoodchain.blockscout.com |
0x0259889e6EBab1a18CeE7e62Bc5B9648FB6C44e5 | |
#486) | 0x59f212317b42D308E81EaF8C078fc8189E2a77Df |
#461) | 0x9338d18b5E7faC5ce06a6AD1Af33Db68fE0b4daC |
0x32349B1Ad07513B5Fa6dCC3dbDC0B5ed17FcB72d | |
0x4B9fb2dE6BE54aF037683A75F3C82c81C3EEd122, 0xd970472b2904D5923882af034cea4067AF0AaC95 |
Addresses resolve from deployments/4663.v2.json (v2) and deployments/4663.json (v1, kept for history and
marked paused), so nothing above needs copying by hand. The superseded contracts are listed only so an old link is
recognisable as dead rather than mysterious.
The lender slice of a fee lands on the share price the moment a loan is repaid, so whoever holds shares at that instant collects it. An audit showed both ways that was abused: a bot depositing in front of a repayment and leaving straight after took $2.70 of a $3 lender fee, leaving $0.30 for the lender who had carried thirty days of default risk — and a borrower could wrap its own repayment in a deposit and a withdrawal, in one transaction, and recover 54% of its own fee.
Leaving inside 7 days now leaves 0.5% behind, which stays in the pool for the lenders who did not leave. That is enough to make a zero-duration position lose money: the borrower's sandwich costs $549.54 against the $505.00 it owed. It is a constant in the contract, not a parameter, in v1 and v2 alike — an owner-tunable exit fee would be a lever to confiscate deposits, and the owner cannot reach lender principal here. Hold past 7 days and it is zero.
RPC_URL accepts a comma-separated list and tries it in order — the first entry first, the next only
when that one fails or refuses to serve the request:
Worth doing: the official endpoint rate-limits under load, and a 429 there is otherwise the end of the
read. The backups take plain calls happily but cannot serve event history — publicnode answers 403
Archive requests require a personal token and ordofi answers -32005 the network is busy — so anything
reading logs still needs the official endpoint reachable. The SDK knows the difference: a node refusing
to serve moves to the next entry, while a revert is returned as-is, because a revert is the chain's
answer and identical everywhere.
Found something? SECURITY.md — use this repo's private vulnerability reporting, and please do not put details in a public issue. BOUNTY.md says what it is worth; the ceiling is small and we say so up front rather than after you have spent a week.
CreditPoolV2 compiles with via-IR at optimizer_runs = 1 to fit the 24 KB limit (a per-file restriction in
foundry.toml); everything else compiles as before. Contracts are non-upgradeable, except the stock vault: a
source change only reaches users through a new deployment, or, for StockVault, an upgrade through its ProxyAdmin,
which the timelock owns since 2026-10-07 (the Safe schedules it in public; it runs 48 hours later at the earliest
until 2026-10-11 19:49 UTC, and from then in the block it is scheduled). src/StockVault.sol stays byte-identical to the source verified on chain, so forge fmt skips it.
docs/SECURITY-v2.md, X-3).The v1 pool ran on Robinhood Chain until the v2 cutover, and is now paused. This is its record, as it was written while v1 was live; it is kept because it is the history v2 was built on.
v1 status: what we found, and what we did about it
A review found a way for lenders to lose principal:
markDefault()released an agent's entire delegated backing on its first default, so a second default from the same agent found nothing left to charge. We wrote the counterexample as a test, committed it failing, and left it public while it was still broken.It is fixed. A default now consumes only the liable slice of the delegation per loan; the rest keeps backing that agent's other open loans and is released when the last one closes, and earned credit is written off against bad debt and retired at settlement. The test that proved the bug is still in the suite, and it passes:
test/DefaultAccounting.t.soladds the cases that make the fix mean something: partial defaults, repayment after default, multiple children, sub-sponsor recourse per default, a dead sponsor, root defaults, and the reserve lock.forge testis 97 passed, 0 failed, invariants included.What that does not buy: no independent review of the economics has happened, and randomized invariants passed right through the original bug — a fuzzer that never defaults the same agent twice reports green forever. Launch caps stay small for exactly that reason. Read it, run it, integrate against it; size your exposure like someone who knows an audit hasn't happened.
The pool is live on Robinhood Chain mainnet (chain 4663), seeded small on purpose: 150 USDG of lender liquidity, 75 in the first-loss reserve, 65 staked by the treasury. Addresses come from
deployments/<chainId>.json, sonpx priors doctorresolves it with no configuration.npm run quickstartstill builds you a throwaway local chain in about thirty seconds if you would rather not touch mainnet.A second defect was found after launch, and has now been fixed and redeployed. A root sponsor could vouch out
stake + earned; when the delegation defaulted,markDefaultclamped the root's liability to its stake and charged the shortfall to the reserve — but never retired the root'searned. The same earned credit then backed a defaulted delegation and remained the root's own borrowing capacity: the reserve paid twice,reserve >= totalEarnedbroke, and lenders lost principal. Worth being precise about what was not wrong:vouch()letting a root delegate more than its earned. A root's stake is cash, so that delegation is backed; requiringdelegatedOut <= earnedfor roots would break the treasury, which vouches every line out of stake withearned = 0. The permission was right — the accounting on default was incomplete.
test/ReviewPoC_RootEarnedDoubleSpend.t.solis the original proof-of-concept, now inverted into the regression guard: it runs the attack move for move and asserts containment. Revert the fix and it fails naming the broken invariant. The pool was migrated to the fixed bytecode, carrying every agent's repayment record across — seeimportRecordsandtest/HistoryImport.t.sol— and theminStakelockout that had stood in for the fix is lifted, so third-party root sponsors work again.The old pool is paused and abandoned. $10 of stake is stranded in it forever, behind two credit lines that were already given:
unvouchisonlyController(sponsor)and the TreasurySponsor exposes none, so a line cannot be taken back. That is a design property, not an accident, and it is the whole reason onboarding is finite.Practical consequence: onboarding is finite. The treasury vouches $5 per agent out of its own 65 USDG of stake, so the beta seats thirteen agents in total. Capacity grows only when $PRIORS creator fees are swept into the treasury — by hand during the beta. Don't trust that count once it is a day old: priors.trade shows how many first lines are left, read live from the chain, and so does
npx priors report 445(available÷ $5).
MIT