skills$openclaw/jb-revloans
mejango9.6k

by mejango

jb-revloans – OpenClaw Skill

jb-revloans is an OpenClaw Skills integration for coding workflows. |

9.6k stars3.3k forksSecurity L1
Updated Feb 7, 2026Created Feb 7, 2026coding

Skill Snapshot

namejb-revloans
description| OpenClaw Skills integration.
ownermejango
repositorymejango/juicypath: jb-revloans
languageMarkdown
licenseMIT
topics
securityL1
installopenclaw add @mejango/juicy:jb-revloans
last updatedFeb 7, 2026

Maintainer

mejango

mejango

Maintains jb-revloans in the OpenClaw Skills directory.

View GitHub profile
File Explorer
1 files
jb-revloans
SKILL.md
8.7 KB
SKILL.md

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

README.md

No README available.

Permissions & Security

Security level L1: Low-risk skills with minimal permissions. Review inputs and outputs before running in production.

Requirements

  • OpenClaw CLI installed and configured.
  • Language: Markdown
  • License: MIT
  • Topics:

FAQ

How do I install jb-revloans?

Run openclaw add @mejango/juicy:jb-revloans in your terminal. This installs jb-revloans into your OpenClaw Skills catalog.

Does this skill run locally or in the cloud?

OpenClaw Skills execute locally by default. Review the SKILL.md and permissions before running any skill.

Where can I verify the source code?

The source repository is available at https://github.com/openclaw/skills/tree/main/skills/mejango/juicy. Review commits and README documentation before installing.