L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Productivité · SKILL.md

jb-revloans

jb-revloans

REVLoans contract mechanics for Juicebox V5 revnets. Use when: (1) implementing loan borrow/repay/refinance flows, (2) calculating loan fees and prepaid amounts, (3) understanding collateral burn/remint mechanics, (4) building loan-related UIs, (5) explaining loan solvency. Covers borrowFrom, repayLoan, reallocateCollateralFromLoan, and liquidation mechanic…

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

name: jb-revloans description: | REVLoans contract mechanics for Juicebox V5 revnets. Use when: (1) implementing loan borrow/repay/refinance flows, (2) calculating loan fees and prepaid amounts, (3) understanding collateral burn/remint mechanics, (4) building loan-related UIs, (5) explaining loan solvency. Covers borrowFrom, repayLoan, reallocateCollateralFromLoan, and liquidation mechanics.

REVLoans Contract Mechanics

Problem

Building loan functionality for revnets requires understanding the REVLoans contract's specific mechanics: how collateral works (burn vs lock), fee structures, and the functions for borrowing, repaying, and refinancing.

Context / Trigger Conditions

  • Implementing borrow flow in a revnet UI
  • Building repay or refinance functionality
  • Explaining how loan fees work to users
  • Calculating prepaid fee amounts
  • Understanding collateral mechanics (why tokens are burned, not locked)
  • Implementing liquidation detection

Solution

Contract Constants

// Liquidation timeline
uint256 constant LOAN_LIQUIDATION_DURATION = 3650 days  // 10 years

// Prepaid fee bounds (basis points)
uint256 constant MIN_PREPAID_FEE_PERCENT = 25   // 0.25% minimum (6 months)
uint256 constant MAX_PREPAID_FEE_PERCENT = 500  // 5% maximum (10 years)

// REV protocol fee on prepaid amount
uint256 constant REV_PREPAID_FEE_PERCENT = 10   // 0.1% to REV

Fee Structure

When a loan is taken:

  1. Prepaid Fee (2.5% - 50%): Paid upfront, covers interest for prepaid duration
  2. REV Fee (0.1%): Goes to REV protocol from prepaid amount
  3. Internal Fee (2.5%): Added back to treasury as revenue
Total Fee = prepaidFee + revFee
Net to Borrower = borrowAmount - prepaidFee - revFee
Treasury Impact = -borrowAmount + internalFee

Key Functions

borrowFrom - Take a Loan

function borrowFrom(
    uint256 projectId,
    address terminal,
    address token,
    uint256 amount,           // Amount to borrow (in base token)
    uint256 collateral,       // Tokens to lock as collateral
    address beneficiary,      // Who receives borrowed funds
    uint256 prepaidFeePercent // Fee percent (25-500 basis points)
) external returns (uint256 loanId)

Collateral Mechanics:

  • Collateral tokens are BURNED at origination, not locked
  • This is key: the loan is secured by the right to remint tokens on repayment
  • Burning increases floor price for all remaining holders

repayLoan - Repay and Unlock Collateral

function repayLoan(
    uint256 loanId,
    uint256 collateralToReturn, // Portion of collateral to unlock
    address beneficiary         // Who receives the unlocked tokens
) external payable

Repayment Mechanics:

  • Can repay partially (return some collateral)
  • Tokens are REMINTED to beneficiary pro rata
  • Repayment amount = proportional share of original borrow
repaymentRequired = (collateralToReturn / totalCollateral) * borrowAmount

reallocateCollateralFromLoan - Refinance

function reallocateCollateralFromLoan(
    uint256 loanId,
    uint256 collateralToReallocate, // Collateral to move
    uint256 minBorrowAmount,        // Min additional borrow (slippage)
    address beneficiary,            // Who receives new funds
    uint256 newPrepaidFeePercent    // New prepaid duration
) external returns (uint256 newLoanId)

Refinancing Mechanics:

  • Extracts value from appreciated collateral
  • Original loan collateral partially/fully moved to new loan
  • "Headroom" = borrowable amount - current debt
// Calculate headroom
const borrowableNow = revLoans.borrowableAmountFrom(projectId, collateral, ...)
const headroom = borrowableNow - originalBorrowAmount
// If headroom > 0, can refinance to extract the difference

liquidateExpiredLoansFrom - Liquidate Old Loans

function liquidateExpiredLoansFrom(
    uint256 projectId,
    uint256[] calldata loanIds,
    address payable beneficiary
) external

Liquidation Mechanics:

  • Only callable after LOAN_LIQUIDATION_DURATION (10 years)
  • Collateral is permanently burned (already was, just can't be reclaimed)
  • Original borrowed funds were already withdrawn

Calculating Borrowable Amount

The amount borrowable for a given collateral depends on the current cash-out value:

// On-chain: REVLoans.borrowableAmountFrom()
function borrowableAmountFrom(
    uint256 projectId,
    uint256 collateral,      // Token amount as collateral
    uint256 decimals,        // Token decimals (18 for ETH)
    uint256 currency         // 1 = ETH, 2 = USDC
) external view returns (uint256)

// The borrowable amount ≈ cash-out value of collateral
// Uses bonding curve formula internally

Loan State Tracking

Each loan is an ERC-721 NFT with this state:

struct REVLoan {
    uint256 projectId;
    uint256 collateral;       // Remaining collateral
    uint256 borrowAmount;     // Original borrow amount
    uint256 prepaidDuration;  // Seconds of prepaid time remaining
    uint256 prepaidFeePercent;
    uint256 createdAt;        // Timestamp
    address terminal;
    address token;
    string tokenUri;
}

Solvency Guarantee

From the academic whitepaper:

"The revnet remains solvent for any sequence of loans, regardless of their number, sizes, or whether they default."

This is because:

  1. Collateral is burned at origination (reduces supply)
  2. Borrow amount ≤ cash-out value of burned collateral
  3. Treasury backing never drops below zero

UI Implementation Pattern

// BorrowDialog flow
function BorrowDialog({ projectId, userBalance }) {
  const [collateral, setCollateral] = useState(0n)

  // Get borrowable amount for selected collateral
  const { data: borrowable } = useReadContract({
    address: REVLOANS_ADDRESS,
    abi: revLoansAbi,
    functionName: 'borrowableAmountFrom',
    args: [projectId, collateral, 18, 1],
  })

  // Calculate fees
  const prepaidFeePercent = 250 // 2.5% = 6 months
  const prepaidFee = (borrowable * BigInt(prepaidFeePercent)) / 10000n
  const revFee = (prepaidFee * 10n) / 10000n // 0.1%
  const netReceived = borrowable - prepaidFee - revFee

  // Execute borrow
  const { write: borrow } = useContractWrite({
    address: REVLOANS_ADDRESS,
    abi: revLoansAbi,
    functionName: 'borrowFrom',
    args: [projectId, terminal, token, borrowable, collateral, beneficiary, prepaidFeePercent],
  })

  return (
    <Dialog>
      <CollateralInput value={collateral} max={userBalance} onChange={setCollateral} />
      <BorrowableDisplay amount={borrowable} />
      <FeeBreakdown prepaid={prepaidFee} rev={revFee} net={netReceived} />
      <Button onClick={() => borrow()}>Borrow</Button>
    </Dialog>
  )
}

Verification

  1. Check loanOf(loanId) returns correct state after borrow
  2. Verify borrowableAmountFrom matches expected cash-out value
  3. Test partial repayment correctly returns proportional collateral
  4. Confirm refinance extracts correct headroom amount

Example

Complete borrow transaction:

import { revLoansAbi } from '@/abi/revLoans'

async function borrowFromRevnet(
  wallet: WalletClient,
  params: {
    projectId: bigint
    terminal: Address
    token: Address
    collateral: bigint
    prepaidMonths: number // 6-120 months
  }
) {
  // Calculate prepaid fee percent (25 BPS = 6 months, 500 BPS = 10 years)
  const prepaidFeePercent = Math.min(
    500,
    Math.max(25, Math.floor(params.prepaidMonths / 6 * 25))
  )

  // Get borrowable amount
  const borrowable = await revLoans.read.borrowableAmountFrom([
    params.projectId,
    params.collateral,
    18n,
    1n
  ])

  // Execute borrow
  const txHash = await wallet.writeContract({
    address: REVLOANS_ADDRESS,
    abi: revLoansAbi,
    functionName: 'borrowFrom',
    args: [
      params.projectId,
      params.terminal,
      params.token,
      borrowable,
      params.collateral,
      wallet.account.address, // beneficiary
      BigInt(prepaidFeePercent)
    ]
  })

  return txHash
}

Notes

  • Loans are non-custodial: no one can take your collateral except you (until liquidation)
  • Collateral burn is what makes loans "self-liquidating" - they secure themselves
  • The 10-year liquidation duration is extremely long - most DeFi loans liquidate quickly
  • Prepaid fee scales linearly: 2.5% for 6 months, 5% for 10 years
  • Interest after prepaid period: 5% annual, added to repayment amount
  • Multi-chain: loans exist on specific chains, can't be moved cross-chain

References

Skills enregistrées