The model, what a default does, the invariants and every parameter of AgentCreditPool — read live from the contract on Robinhood Chain where a value is deploy-time, documented from its own source where it isn't. Full source: contracts/src/AgentCreditPool.sol.
One pool of USDG. An agent's line can be backed by up to 20 distinct sponsors at once, each at its own premium, plus any number of seats (see § 03) on top. If a loan defaults, the loss is burned only from the pool-shares of the sponsors who actually backed that loan — never a lender's, and never a sponsor who backed a different agent or joined this one after the default.
backing(r) = sharesValue(r) // r's own pool shares, in USDG
free(r) = backing(r) - delegatedOut(r) - feeLocked(r)
available(a) = delegatedIn(a) - principalOut(a) // what's left on agent a's line
vouch(r -> a, amount): require amount <= free(r) // + the owner's EIP-712 consent, see below
delegatedOut(r) += amount ; delegatedIn(a) += amount
borrow(a, amount, term): require minLoan <= amount <= available(a)
principalOut(a) += amount
// amount splits pro-rata across a's CURRENT sponsors, by stake.amount;
// each sponsor's own fee share is frozen into loan.shares[] right here
feeLocked(sponsor) += its share of the fee
repay(a, principal+fee): fee -> lenders 60% + each sponsor 25% (its own cut) + reserve 15%Deposits USDG, holds pool shares, earns 60% of every fee. Every line is backed by a sponsor's own locked shares, and a default burns those — not a lender's principal, by construction.
Vouches USDG capacity for one agent, from its own deposited pool shares, with the agent owner's signed consent. Earns 25% of that agent's fees; risks exactly what it vouched, only on a loan it actually backed.
An identity (ERC-8004-shaped). Borrows $5–$500 for 1–30 days at 1%/30d within the line its sponsors vouched. Its own owner or configured operatorWallet can borrow; anyone can repay on its behalf.
Takes 15% of every base fee, paid out immediately on repay — never accrues in the contract itself. No owner, no admin function anywhere in this contract to redirect it.
Four transitions. The last one is a default, where the backing sponsors pay.
repay → agent's record grows (loansRepaid, volumeRepaid); the line stays as-is.
default → agent is permanently defaulted, can never borrow again; the owner's default count records it.
require now > loan.defaultableAt // dueAt + GRACE_PERIOD(3d), fixed at borrow
for each SponsorShare in loan.shares:
burn ceil[(principal+fee) * totalShares / totalAssets] of that sponsor's own shares
// if its live stake somehow falls short (should be unreachable — vouch()/
// borrow() only ever check freeCapacity at that moment), the shortfall
// becomes explicit totalBadDebt instead of diluting anyone else
for every sponsor of this agent (used by this loan or not):
release its full remaining committed capacity — the agent can never
borrow again, so nothing should stay locked against it
agent.defaulted = true ; agent.delegatedIn = 0
share price: never falls for an uninvolved lender or sponsorA seat backs an agent with 10 or more repaid loans using a fixed-ratio lock of a separate token (intended to be $AUEVO), on top of the same real-USDG capacity an ordinary vouch requires — never instead of it. The reasoning for that ordering (why burning an unrelated token alone can never protect a lender) is laid out in full on the Seats page. Short version: 50% of the seat's token lock burns on default of the loan it backed; the rest returns, same as an ordinary sponsor's pool-share burn running alongside it.
Reviewed by hand and exercised by 118 integration-test assertions against a local chain (contracts/test/run-credit.mjs) — not an automated invariant fuzzer, and not a paid, independent, professional audit. Treat these as intended properties the tests check, not a guarantee.
totalBadDebt stays 0 and share price never falls, except through the explicit, visible backstop path — no loan loss reaches an uninvolved lender.
For every agent, principalOut ≤ delegatedIn. For every sponsor, delegatedOut ≤ its own pool-share value at the moment of any vouch or borrow — checked on-chain, not assumed.
A default burns only the shares (and, for a seat, the token lock) of sponsors who backed that specific loan — never a different agent's sponsor, never a sponsor who joined after that loan was drawn.
The first table is read live from the deployed pool — a real decision made at deploy time, not a code detail. The second is fixed in the contract source itself and identical for any deployment of this version.
| minLoan / maxLoan | 5000000 – 500000000 (raw units) | smallest / largest loan |
| feeBps | 1% per 30 days | base fee, pro-rata by term |
| minRootStake | 10000000 (raw units) | to enroll as a root (sponsor or lender) |
| asset | 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 | the stablecoin agents borrow and repay in |
| pool | 0xc7a04d94361de7a30d099057c6746217b6aa0d2e | this contract's own address |
| LENDER_FEE_BPS / SPONSOR_FEE_BPS | 60% / 25% | of each fee; the remainder (15%) goes to the reserve |
| MAX_PREMIUM_BPS | 2% per 30 days | ceiling a sponsor's own premium can ever reach |
| GRACE_PERIOD | 3 days | after dueAt before markDefault() is allowed |
| MIN_TERM_DAYS / MAX_TERM_DAYS | 1 / 30 | loan duration bounds |
| MAX_SPONSORS_PER_AGENT | 20 | caps the per-agent loop in borrow()/repay()/markDefault() — bounds gas, not a design preference |
| SEAT_MIN_REPAID_LOANS | 10 | repaid loans an agent needs before a seat can back it |
| SEAT_BURN_BPS | 50% | of a seat's token lock burned on default of the loan it backed |
Every write a lender, sponsor, seat-holder or agent can call, plus the views this app's own pages read from.
| deposit(uint256 amount) | lender |
| withdraw(uint256 amount) | lender |
| enrollRoot() | sponsor/seat-holder |
| vouch(agentId, amount, premiumBps, maxPremiumBps, nonce, deadline, sig) | sponsor |
| vouchSeat(agentId, amount, premiumBps, maxPremiumBps, nonce, deadline, sig) | seat-holder |
| borrow(agentId, amount, termDays, to) | agent (owner/operatorWallet) |
| repay(loanId) | anyone |
| markDefault(loanId) | anyone (permissionless, matches Priors) |
| sharesValue(address) / freeCapacity(address) | view |
| agentInfo(agentId) / available(agentId) | view |
| sponsorsOf(agentId) / sponsorStakeOf(agentId, sponsor) | view |
| loanInfo(loanId) / loanSharesOf(loanId) | view |
| seatEligible(agentId) / seatTokenRequiredFor(amount) | view |
| isRoot(address) / nextLoanId() / totalBadDebt() | view |