Skip to content

RAAC US Prime Rate Oracle

Overview

The RAACPrimeRateOracle is a Chainlink Functions-based oracle that fetches the latest US Prime Rate from multiple off-chain data sources and updates the LendingPool. The protocol’s borrowing curve derives from the Prime Rate (baseline equals one half of the Prime Rate), so timely and controlled updates are critical.

  • Built on the same BaseChainlinkFunctionsOracle used by the Real Estate oracle
  • Off-chain job runs every 4 hours; only sends an on-chain request if the value changed
  • On fulfillment, calls lendingPool.setPrimeRate(newPrimeRate) and emits PrimeRateUpdated

Architecture

flowchart TD
    subgraph "Off-chain Infrastructure"
        S1[US Gov / Financial Data APIs]
        S2[External Sources 2+]
        J[Scheduler every 4h] --> A[AWS Job]
        A -->|fetch & compare| C{Prime rate changed?}
        C -->|No| Z[No on-chain action]
        C -->|Yes| R[sendRequest args]
    end

    R --> O[RAACPrimeRateOracle<br/>Chainlink Functions]
    O -->|DON executes JS| D[Chainlink DON]
    D -->|returns primeRate| O
    O -->|setPrimeRate primeRate| L[LendingPool]
    L -->|ReserveLibrary::setPrimeRate| RL[Reserve Library]

    style O fill:#e1f5fe
    style L fill:#f3e5f5
    style RL fill:#f3e5f5

Key points:

  • Scheduler runs every 4 hours. It queries 2+ independent sources and triggers on-chain only when a change is detected.
  • Chainlink Functions returns an encoded uint256 prime rate (RAY), forwarded to the LendingPool.
  • ReserveLibrary enforces max ±5% change per update and updates derived rates.

Purpose

  • Keep Prime Rate aligned with reputable external sources
  • Enforce guarded updates via library-level limits (±5%)
  • Deterministically adjust borrowing curve parameters derived from Prime Rate

Request Model & Cadence

  • Arguments: as required by the Chainlink Functions source (internal to the oracle)
  • Cadence: every 4 hours (off-chain). Only calls sendRequest when value changed
  • Fulfillment: single scalar uint256 prime rate (RAY), decoded on-chain

The JavaScript source used by the Chainlink Functions DON to fetch the US Prime Rate is published here: IPFS (Pinata gateway).


Fulfillment Flow

  1. Off-chain job (cron: 4h) aggregates sources and detects a change
  2. Job calls RAACPrimeRateOracle.sendRequest (owner-gated)
  3. Chainlink DON executes the source, returns uint256 primeRate
  4. Oracle decodes and calls lendingPool.setPrimeRate(primeRate)
  5. LendingPool delegates to ReserveLibrary.setPrimeRate which:
  6. Rejects non-positive values
  7. Caps change to ±5%
  8. Updates primeRate, baseRate = 25% of prime, optimalRate = 50% of prime
  9. Recomputes interest and liquidity

Key Functions

Function Description Access
sendRequest(args, bytesArgs) Triggers Chainlink Functions request onlyOwner
getPrimeRate() Returns last stored prime rate (RAY) view

Internals (Hooks)

Hook Purpose
_emitRequestEvents(requestId, args) Emits PrimeRateRequestSent
_beforeFulfill(args) Pre-fulfillment hook (no-op)
_processResponse(requestId, response) Decodes uint256 rate, writes to lendingPool, emits PrimeRateUpdated

Events

  • PrimeRateRequestSent(bytes32 requestId)
  • PrimeRateUpdated(uint256 primeRate)

Integration with Lending Pool

On fulfillment, the oracle calls lendingPool.setPrimeRate(lastPrimeRate). The LendingPool then applies ReserveLibrary.setPrimeRate with safety checks:

function setPrimeRate(
    ReserveData storage reserve,
    ReserveRateData storage rateData,
    uint256 newPrimeRate
) internal {
    if (newPrimeRate < 1) revert PrimeRateMustBePositive();
    if (newPrimeRate > WadRayMath.RAY) revert PrimeRateChangeExceedsLimit();

    uint256 oldPrimeRate = rateData.primeRate;

    if (oldPrimeRate > 0) {
        uint256 maxIncrease = oldPrimeRate + WadRayMath.RAY.percentMul(5_00);
        uint256 maxDecrease = oldPrimeRate < WadRayMath.RAY.percentMul(5_00)
            ? 0
            : oldPrimeRate - WadRayMath.RAY.percentMul(5_00);
        if (newPrimeRate > maxIncrease || newPrimeRate < maxDecrease) {
            revert PrimeRateChangeExceedsLimit();
        }
    }

    updateReserveInterests(reserve, rateData);

    rateData.primeRate = newPrimeRate;
    rateData.baseRate = rateData.primeRate.percentMul(25_00);
    rateData.optimalRate = rateData.primeRate.percentMul(50_00);
    // maxRate set separately by protocol

    updateInterestRatesAndLiquidity(reserve, rateData, 0, 0);

    emit PrimeRateUpdated(oldPrimeRate, newPrimeRate);
}

Borrow Rate Relationship

The protocol uses one-half of the US Prime Rate as the baseline for borrowing cost. Updating the Prime Rate directly adjusts the lending pool’s interest curve parameters.

Change Limits (±5%)

For security, any single update cannot change the Prime Rate by more than 5% (up or down). Larger moves must occur over multiple intervals.


Operational Notes

  • Scheduler: Off-chain job runs every 4 hours. It aggregates 2+ sources and only submits on-chain if the value changes
  • Owner-gated: sendRequest is restricted to the owner/operator account
  • Observability: Track PrimeRateRequestSent and PrimeRateUpdated for monitoring

  • BaseChainlinkFunctionsOracle – shared request/fulfillment framework
  • LendingPool – receives prime rate updates via setPrimeRate
  • ReserveLibrary – enforces update bounds and recalculates rates