spec v1 · immutable
DOC :: REPO-PRICING-V1 · METHODOLOGY · REGIMESHIFT CLEARINGHOUSE

Repo pricing — from Agent-SOFR to signed loan quote

How an Agent-SOFR rate becomes a specific, EIP-712-signed, on-chain-settleable loan quote. Covers per-LTV variance premium, regime LTV caps, the matcher's quote-construction algorithm, and the Aave-style default split.
Versionv1 (2026-05-22)
StatusProduction
Settlement contractInterAgentRepoV4 · Base mainnet
Audit status3 rounds · 0 findings outstanding
Builds onagent-sofr-v1
Sourcematcher/quote_engine.py
1) The quote-rate formula

A loan quote rate combines four components. The first three are loan-agnostic (snapshot of the Agent-SOFR oracle state); the fourth is the matcher's flat fee.

quote_rate_bps = base_anchor_bps // from agent-sofr-v1, section 2 + variance_premium_bps(LTV, σ_T, T) // per-loan, see section 2 below + regime_premium_bps(regime) // from agent-sofr-v1, section 4 + ORCHESTRATOR_TAKE_BPS // flat 5 bps to matcher

Two of these terms (base anchor, regime premium) are identical to what the Agent-SOFR oracle publishes — they're not loan-specific. The variance premium is loan-specific because it depends on the loan's LTV and duration.

2) Per-loan variance premium

Same Black-Cox first-passage formula as agent-sofr-v1 §3, but with the loan's actual LTV and duration_sec plugged in (instead of the generic 80% / 1h used for the headline rate).

σ_T = √( (cv + λ·j²) · T ) // T in 5-min bars z = -ln(1/LTV) / σ_T P_default = Φ(z) // standard normal CDF variance_premium_bps = LTV · P_default · LGD · (BARS_PER_YEAR / T) · 10000 with LGD = 0.05

The matcher's compute_collateral mode does the inverse: given a target rate, find the LTV that produces it. Bisection on LTV against the equation above, clamped at the regime's max LTV cap.

3) Regime-dependent LTV caps

Even when the math allows higher LTV, we cap by regime. Caps are tightened from initial v1 values after audit round 1 — a 3% absolute buffer below the contract's 95% liquidation threshold gives the matching engine wiggle room without ever generating quotes the contract would reject at origination.

RegimeMax LTV (cap)Aave staticCapital efficiency Δ
RESTING 92% 80% +12%
LOW 90% 80% +10%
NORMAL 85% 80% +5%
ELEVATED 80% 80% 0% (parity)
HIGH 70% 80% −10% (safer for lenders)
EXTREME 55% 80% −25% (matching paused)

Weighted-average gain (using time-share weights from agent-sofr-v1): ~8% capital efficiency improvement in calm markets, with materially better lender protection in shocks. Aave can't adapt because LTV requires a multi-week governance vote.

4) Loan-quote EIP-712 schema

The matcher signs every quote with an EIP-712 typed-data signature. The contract's originate() verifies the signature with ECDSA.recover against the configured oracleSigner address. A quote is non-replayable across contract versions because the EIP-712 domain includes the version string.

EIP712Domain:
  name:              "InterAgentRepo"
  version:           "4"                                  // V4 is the active deployment
  chainId:           8453                                  // Base mainnet
  verifyingContract: "0x9d3b61d13a839968ffad94a0eedf73153c2fb31c"

Quote:
  borrower:          address
  lender:            address
  principalToken:    address                                // e.g. USDC
  principalAmount:   uint256                                // in native ERC-20 units
  collateralToken:   address                                // e.g. WETH
  collateralAmount:  uint256
  expiryTimestamp:   uint256                                // loan must be repaid before this
  rateBps:           uint256                                // the rate computed above
  nonce:             bytes32                                // = loanId on chain

The nonce doubles as the loanId in the contract's loans mapping — the matcher generates it from a hash of (borrower, lender, expiryTimestamp, principalAmount) so it's deterministic but unique per (counterparty, terms) tuple.

5) Settlement lifecycle

Once both counterparties have approved their respective ERC-20s (USDC for lender, WETH for borrower), either party can submit the signed quote to originate(). The contract:

  1. Verifies EIP-712 signature against oracleSigner
  2. Verifies expiryTimestamp > block.timestamp
  3. Verifies initial LTV ≤ 93% (R1-#1 post-audit on-chain check)
  4. Verifies loan duration ≥ 120s (R1-#2 post-audit)
  5. Verifies rateBps ≤ ceiling (R1-#3 post-audit)
  6. Pulls collateral from borrower, principal from lender to borrower
  7. Records loan, emits LoanOriginated event

From that point, three terminal states are possible:

Terminal stateTriggerOutcome
Repaid happy path Borrower calls repay(loanId) before expiry Principal + interest → lender. Full collateral → borrower.
Liquidated Anyone calls liquidate(loanId) when on-chain LTV ≥ 95% (post-grace-period of 60s) Aave-style split (see §6). Liquidator gets 3% bounty.
Defaulted Anyone calls defaultLoan(loanId) after expiryTimestamp without repayment Aave-style split (see §6). Caller gets 3% bounty.
6) Default split — Aave-style fair distribution

When a loan terminates via liquidation or expiry-default, the collateral is split four ways (introduced in audit R1-#4):

RecipientSharePurpose
Liquidator / triggerer 3% of collateral Bounty incentivising decentralised liquidator competition
Insurance pool 1% of collateral Cross-loan loss buffer (separate contract address, rotatable)
Lender debt-equivalent principalAmount + accrued interest, denominated in collateral at Chainlink price
Borrower remaining excess If collateral > debt + fees, excess refunded to borrower

Why this matters: in earlier versions (V1/V2), the entire collateral went to the lender on default. That was an over-pay to lenders that disincentivised borrowing. The Aave-style split (R1-#4 fix in V3+) makes the contract fair: lenders are made whole, the system gets a small reserve buffer, and the borrower gets back whatever's left after paying their debt.

7) On-chain invariants enforced post-audit

The contract enforces these properties at the bytecode level. They're not just convention — every originate() reverts unless all hold:

InvariantEnforced whereAudit finding addressed
Initial LTV ≤ 93% originate() R1-#1
Loan duration ≥ 120s originate() R1-#2
rateBps ≤ sanity ceiling originate() R1-#3
Aave-style default split (not lender-takes-all) defaultLoan() / liquidate() R1-#4
repay() is NOT pausable (owner can't grief borrower into default) Pausable mixin scope R2-#2
Chainlink price feed staleness ≤ 1h liquidate() R1-#5 (heartbeat check)
Grace period 60s after origination before liquidate allowed liquidate() (anti-flash-loan)
EIP-712 domain version = "4" originate() sig verifyNon-replayable across V1/V2/V3/V4

Full audit reports (round 1, 2, 3): github.com/regimeshift-xyz/regimeshift-clearinghouse/tree/main/audit. Outstanding findings: 0. Retired contracts (V1/V2/V3): oracleSigner rotated to 0x...dEaD on-chain.

Methodology version repo-pricing-v1
Last revised 2026-05-22
Source repo regimeshift-xyz/regimeshift-clearinghouse
Active contract InterAgentRepoV4 on Base
Content hash see /methodology/HASHES.txt
Verify integrity curl https://regimeshift.xyz/methodology/repo-pricing-v1.html | shasum -a 256