The protocol

How the pool works, in full.

The model, what a default does, the score, the invariants and every parameter of pool v2, read live from the contracts on Robinhood Chain. Nothing on this page is a promise the code doesn't keep.

§ 01

Model

One pool of USDG. Every agent's line is vouched by one backer, all of it out of that backer's own stake. What a backer vouches, it can lose. That is the entire trust mechanism.

accountingCreditPoolV2.sol
backing(r)    = value of r's locked pool shares     // a root's stake, earning lender yield too
free(r)       = backing(r) − delegatedOut(r) − feeLocked(r)
available(a)  = delegatedIn(a) − principalOut(a)     // from its one sponsor, 0 without one

vouch(r → a, x):  require x ≤ free(r) ;  a new sponsor needs the owner's signed consent
                   delegatedOut(r) += x ;  delegatedIn(a) += x
                   // a line is only ever a backer's stake: repaying grows the record, not the line

borrow(a, P):     require minLoan ≤ P ≤ available(a)
                   principalOut(a) += P ;  fee = P · feeBps · term / 30d
                   feeLocked(sponsor) += fee             // fixed at borrow, held in the backer's stake

repay(a, P+fee): fee → lenders 60% · sponsor(a) 25% · reserve 15%
                   // plus any premium the owner consented to (at most 2% per 30 d), all to the sponsor
lenderDeposits USDG, holds shares, earns 60% of every base fee. Every line is fully backed by a backer's locked shares and a default burns those shares, so no path has been found for a loan loss to reach lender principal. Leaving takes liquidity; within 7 days of a deposit a 0.5% fee stays with the lenders who stay. Experimental contracts; capital is at risk.
root sponsorLocks USDG as pool shares and vouches lines out of them, each line starting only with the agent owner's signed consent. The stake earns lender yield, and it pays when a line it vouched defaults. Anyone can become one: the minimum stake is set in the parameters below, and stake that backs nothing can be unlocked.
agentAn ERC-8004 identity. Borrows $5 to $500 for 1 to 30 days at 1% per 30 days, within the line its one sponsor vouched. Repaying builds its record, not its line: a line grows only when its backer vouches more of its stake. With no loan open it can leave, or move to another sponsor it signs for.
consentAn EIP-712 signature from the agent's NFT owner, naming one sponsor and the most premium it accepts. Only that sponsor's owner can use it, and it stops working if the NFT is sold. Nobody gets a line without having signed for it.
reserveTakes 15% of every base fee. Backers pay for defaults, so it covers no loss beyond rounding dust on a slash; it also pays the keeper bounty in the parameters below (0 today). Only the owner, a 48-hour timelock, can move it.
§ 02

Loan lifecycle and what a default does

Four transitions. The last one is the one that gives the score its meaning.

state machineloan.status
defaultmarkDefault()
require now > defaultableAt                     // dueAt + grace, fixed at borrow

burn ⌈(P + fee) · totalShares / totalAssets⌉ of sponsor's locked shares
                                                // the backer pays the principal and the unpaid fee
delegatedOut(sponsor) −= P ;  delegatedIn(a) −= P
childrenDefaulted(sponsor) += 1                 // −75 on the backer's score
defaulted(a) = true                             // for good: score 0, no new line
ownerDefaults(owner at borrow) += 1             // custodians aside, that wallet's agents can't borrow again
hook(sponsor).onDefault(…)                      // a $PRIORS seat settles and burns in the same transaction

share price: never falls                        // lenders are untouched
totalBadDebt = 0                                // live on robinhood chain, checked by the invariant suite before every site deploy
§ 03

Score

Six terms over the on-chain record, 0 to 1000. A pure function of creditReport(agent). No oracle, no committee, no review. The two big terms are dollar-days and week-long loans, so the cheapest way to a high score is to hold real money for real time and give it back.

ScoreLib.solv1
score(r) = defaulted ? 0 :
    min(400, dollarDaysRepaid / $10)     // Σ principal × actual holding time, capped at term
  + min(200, 20 · qualifiedRepaid)      // loans held for ≥ 7 d
  + min(150, delegatedIn / $5)          // someone's money at risk for you
  + min(150, 2 · daysEnrolled)
  + min(100, 50 · recourseHonored)      // carried over from v1 only: pool v2 has no recourse
  − 75 · childrenDefaulted              // per defaulted loan it backed
// one-day loans churned for cents move nothing; capital held for weeks does
recorded score inputs—
dollar-days repaid —
qualified loans —
backing at risk —
time enrolled —
recourse honored —
vouched defaulters —
score(—) · recorded contract score—
§ 04

Invariants

Checked after every call by a stateful fuzzer driving deposits, withdrawals, stakes, consented vouches, handoffs, freezes, borrows, repayments, defaults, failing hooks, parameter changes and time. These are intended properties, not a guarantee of safety. Additional default scenarios are under review.

I1 · solvency
totalBadDebt = 0
share price never falls
⇒ no loan loss reaches lenders
I2 · cash
balance(USDG) ≥
  poolLiquidity + reserve
  + unclaimedSponsorFees
backer stake is pool shares, not a balance of its own
I3 · exposure
∀a: principalOut(a) ≤ delegatedIn(a)
∀r: delegatedOut(r) ≤ backing(r) + dust
no one lends what nobody backs
Live
on Robinhood Chain
7
invariants
256 × 400
fuzz runs × depth
—
lender loss to date
§ 05

Parameters

Read from getParams() on the deployed pool. Owner-adjustable within bounds, and the owner is a 48-hour timelock; every change emits ParamsUpdated.

paramvaluemeaning
§ 06

$PRIORS and the treasury

Launched on Pons, Robinhood Chain's launchpad, paired with USDG. The token backs agents through seats: a staker puts a seat of $PRIORS behind the $5 line of an agent with 3 or more repaid loans, earns the sponsor share of its fees, and half the seat burns if the agent defaults. The creator fee is not routed to the treasury: the treasury is topped up by hand, by sending it USDG and calling sweep(), which anyone can do. A sweep sends half to the pool's reserve and stakes half as pool shares under the treasury's own ERC-8004 identity, a root sponsor. From that stake it gives each invited agent its first $5 line and raises clean records (3 qualified loans, 14 days, a score of 100 or more) to $25, at most $25 of new lines a week. Its agents' sponsor fees go to its fee wallet. A default burns its shares. Its tree is in the grove, next to everyone else's.

treasuryTreasurySponsorV4.sol
you ────────── send USDG ───▶ TreasurySponsorV4.sweep()  // by hand, anyone
                              ├── 50% ─▶ pool.fundReserve()  // reserve
                              └── 50% ─▶ pool.addStake(id)   // stake, as pool shares

firstLine(agent)  invite + owner's consent ─▶ vouch $5          // once per invite
raise(agent)      3 qualified · 14 d · score ≥ 100 · clean ─▶ line $25   // anyone
                   Σ new lines ≤ $25 per 7 d epoch                  // sybil cap
reclaim(agent)    idle 30 d, no loan open ─▶ line back            // anyone

collect()         its agents' sponsor fees ─▶ fee wallet          // anyone
default            its shares burn · branch burns · in public
treasuryfunded by hand: anyone can send it USDG and call sweep()
reserve— in the pool's reserve today
seats$PRIORS behind an agent's $5 line, earning the sponsor share of its fees; half the seat burns on a default. Back an agent
treasury feesits sponsor share of what its agents pay goes to its fee wallet through collect()
contractnot launched yet
launchPons, USDG pair. The creator fee accrues to the creator's Pons escrow, not to the treasury. ponsfamily.com/launchpad
§ 07

Interface

Everything an agent, a sponsor or a lender can do on pool v2. The score is one view call on the lens; the API in the repo serves the same number over x402, and sdk/priors-v2.mjs wraps the lot, simulating every write before it is sent.

deposit(uint256 assets, address receiver, uint256 minShares) → shareslender
withdraw(uint256 shares, address receiver) → assetslender
enrollRoot(uint256 rootId, uint256 assets)sponsor
vouchWithConsent(uint256 sponsorId, uint256 agentId, uint256 amount, uint256 premiumBps, Consent c, bytes sig)sponsor
borrow(uint256 agentId, uint256 amount, uint64 term, address to, uint256 maxFee) → loanIdagent
repay(uint256 loanId, uint256 expectedAgentId, uint256 maxDue)anyone
leave(uint256 agentId)agent
markDefault(uint256 loanId)anyone
claimSponsorFees(uint256 sponsorId, address to)sponsor
lens.score(uint256 agentId) → uint256view
lens.creditReport(uint256 agentId) → Reportview
treasury.firstLine(uint256 agentId, uint64 expiry, bytes invite, Consent c, bytes consentSig)anyone
treasury.raise(uint256 agentId) · reclaim(uint256 agentId)anyone
treasury.sweep() · collect()anyone
GET /v1/score/:agentIdoptional x402
verifyany RPC
§ 08

Addresses

These are the contracts this page reads, on Robinhood Chain (chain id 4663). The light in the top bar shows the block it last read.