askill
jb-revloans

jb-revloansSafety 90Repository

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.

2 stars
1.2k downloads
Updated 2/7/2026

Package Files

Loading files...
SKILL.md

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

Install

Download ZIP
Requires askill CLI v1.0+

AI Quality Score

95/100Analyzed 2/9/2026

An exceptionally well-documented skill covering the REVLoans contract mechanics for Juicebox V5. It provides deep technical insights, Solidity function signatures, fee calculations, and a practical React UI implementation pattern.

90
100
85
95
98

Metadata

Licenseunknown
Version-
Updated2/7/2026
PublisherDemerzels-lab

Tags

githubtesting