StockTokenLendingPool

0x377543d376eca00a4f958186b9eb4e798f5635d3

Verification
Verified
v0.8.28+commit.7893614a
Type
Contract
9,672 bytes
ABI entries
84
35 read · 16 write
License
none

Contract information

Address
0x377543d376eca00a4f958186b9eb4e798f5635d3
Chain
Robinhood Chain (4663)
Compiler
v0.8.28+commit.7893614a
Optimization
Enabled
Creation tx
0x81030bdaba…8a8218db7f

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (35)

BPS()uint256
MIN_HEALTH_FACTOR()uint256
SEQUENCER_GRACE_PERIOD()uint256
WAD()uint256
YEAR()uint256
baseBorrowRatePerSecond()uint256
borrowIndex()uint256
borrowLimit(address, address)uint256
closeFactorBps()uint16
collateralBalance(address, address)uint256
collateralValueUsd(address, address)uint256
currentBorrowAprBps()uint256
debtAsset()address
debtBalance(address)uint256
globalSupplyCap()uint256
healthFactor(address, address)uint256
lastAccrualTimestamp()uint256
liquidationLimit(address, address)uint256
liquidityAvailable()uint256
markets(address)bool, uint16, uint16, uint16, uint64, address, uint256, uint256, uint256
originationFeeBps()uint16
owner()address
paused()bool
protocolLiquidationFeeBps()uint16
protocolReserves()uint256
reserveFactorBps()uint16
sequencerUptimeFeed()address
slopeBorrowRatePerSecond()uint256
suppliedLiquidity(address)uint256
totalDebt()uint256
totalLiquidityShares()uint256
totalSuppliedLiquidity()uint256
treasury()address
userBorrowIndex(address)uint256
withdrawableLiquidity(address)uint256

Events (14)

BorrowedCollateralDepositedCollateralWithdrawnInterestAccruedLiquidatedLiquidityWithdrawnMarketListedOwnershipTransferredPausedRepaidRiskParamsUpdatedSuppliedTreasuryUpdatedUnpaused

ABI

[
  {
    "inputs": [
      {
        "internalType": "contract IERC20",
        "name": "_debtAsset",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "initialOwner",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_treasury",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_sequencerUptimeFeed",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "AmountZero",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "BorrowCapExceeded",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "CloseFactorExceeded",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "EnforcedPause",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ExpectedPause",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "HealthyPosition",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InsufficientLiquidity",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidOracle",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "MarketNotListed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OraclePaused",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OracleStale",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "OwnableInvalidOwner",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "OwnableUnauthorizedAccount",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ReentrancyGuardReentrantCall",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "RepayTooLarge",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "SafeERC20FailedOperation",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SupplyCapExceeded",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "UnsafePosition",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "fee",
        "type": "uint256"
      }
    ],
    "name": "Borrowed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "CollateralDeposited",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "CollateralWithdrawn",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "interest",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "supplierInterest",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "reserves",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newBorrowIndex",
        "type": "uint256"
      }
    ],
    "name": "InterestAccrued",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "liquidator",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "collateralToken",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "repaid",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "seized",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "protocolFee",
        "type": "uint256"
      }
    ],
    "name": "Liquidated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "lender",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "shares",
        "type": "uint256"
      }
    ],
    "name": "LiquidityWithdrawn",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "priceFeed",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "collateralFactorBps",
        "type": "uint16"
      },
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "liquidationThresholdBps",
        "type": "uint16"
      },
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "liquidationBonusBps",
        "type": "uint16"
      },
      {
        "indexed": false,
        "internalType": "uint64",
        "name": "maxStaleness",
        "type": "uint64"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "borrowCap",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "supplyCap",
        "type": "uint256"
      }
    ],
    "name": "MarketListed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousOwner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "OwnershipTransferred",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "Paused",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "Repaid",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "reserveFactorBps",
        "type": "uint16"
      },
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "originationFeeBps",
        "type": "uint16"
      },
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "closeFactorBps",
        "type": "uint16"
      },
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "protocolLiquidationFeeBps",
        "type": "uint16"
      }
    ],
    "name": "RiskParamsUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "lender",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "shares",
        "type": "uint256"
      }
    ],
    "name": "Supplied",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "treasury",
        "type": "address"
      }
    ],
    "name": "TreasuryUpdated",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "Unpaused",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "BPS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MIN_HEALTH_FACTOR",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "SEQUENCER_GRACE_PERIOD",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "WAD",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "YEAR",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "accrueInterest",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "baseBorrowRatePerSecond",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "stockToken",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "borrow",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "borrowIndex",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "stockToken",
        "type": "address"
      }
    ],
    "name": "borrowLimit",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "closeFactorBps",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "collateralBalance",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "stockToken",
        "type": "address"
      }
    ],
    "name": "collateralValueUsd",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "currentBorrowAprBps",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "debtAsset",
    "outputs": [
      {
        "internalType": "contract IERC20",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      }
    ],
    "name": "debtBalance",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "stockToken",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "depositCollateral",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "globalSupplyCap",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "stockToken",
        "type": "address"
      }
    ],
    "name": "healthFactor",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "lastAccrualTimestamp",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "stockToken",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "repayAmount",
        "type": "uint256"
      }
    ],
    "name": "liquidate",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "stockToken",
        "type": "address"
      }
    ],
    "name": "liquidationLimit",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "liquidityAvailable",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "stockToken",
        "type": "address"
      },
      {
        "internalType": "contract AggregatorV3Interface",
        "name": "priceFeed",
        "type": "address"
      },
      {
        "internalType": "uint16",
        "name": "collateralFactorBps",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "liquidationThresholdBps",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "liquidationBonusBps",
        "type": "uint16"
      },
      {
        "internalType": "uint64",
        "name": "maxStaleness",
        "type": "uint64"
      },
      {
        "internalType": "uint256",
        "name": "borrowCap",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "supplyCap",
        "type": "uint256"
      }
    ],
    "name": "listMarket",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "markets",
    "outputs": [
      {
        "internalType": "bool",
        "name": "listed",
        "type": "bool"
      },
      {
        "internalType": "uint16",
        "name": "collateralFactorBps",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "liquidationThresholdBps",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "liquidationBonusBps",
        "type": "uint16"
      },
      {
        "internalType": "uint64",
        "name": "maxStaleness",
        "type": "uint64"
      },
      {
        "internalType": "contract AggregatorV3Interface",
        "name": "priceFeed",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "borrowCap",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "supplyCap",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "totalBorrowed",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "originationFeeBps",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "owner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pause",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "paused",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "protocolLiquidationFeeBps",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "protocolReserves",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "renounceOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "repay",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "reserveFactorBps",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "sequencerUptimeFeed",
    "outputs": [
      {
        "internalType": "contract AggregatorV3Interface",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint16",
        "name": "_reserveFactorBps",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "_originationFeeBps",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "_closeFactorBps",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "_protocolLiquidationFeeBps",
        "type": "uint16"
      }
    ],
    "name": "setRiskParams",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_treasury",
        "type": "address"
      }
    ],
    "name": "setTreasury",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "slopeBorrowRatePerSecond",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "suppliedLiquidity",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "supplyLiquidity",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalDebt",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalLiquidityShares",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalSuppliedLiquidity",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "transferOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "treasury",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "unpause",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "userBorrowIndex",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "stockToken",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "withdrawCollateral",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "withdrawLiquidity",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "withdrawProtocolReserves",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "lender",
        "type": "address"
      }
    ],
    "name": "withdrawableLiquidity",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

interface AggregatorV3Interface {
    function decimals() external view returns (uint8);
    function latestRoundData() external view returns (uint80, int256, uint256, uint256, uint80);
}

interface IOraclePauseFlag {
    function oraclePaused() external view returns (bool);
}

/// @notice Retail-facing collateralized lending pool for Robinhood Chain stock tokens.
/// @dev Production-oriented accounting: utilization interest, LP receipt shares, reserves, caps, close factor, and L2 sequencer checks.
contract StockTokenLendingPool is Ownable, Pausable, ReentrancyGuard {
    using SafeERC20 for IERC20;

    uint256 public constant BPS = 10_000;
    uint256 public constant WAD = 1e18;
    uint256 public constant YEAR = 365 days;
    uint256 public constant MIN_HEALTH_FACTOR = 1e18;
    uint256 public constant SEQUENCER_GRACE_PERIOD = 1 hours;

    IERC20 public immutable debtAsset; // USDG on Robinhood Chain in production.
    AggregatorV3Interface public sequencerUptimeFeed;
    address public treasury;

    uint16 public reserveFactorBps = 2_000; // 20% of interest to protocol reserves.
    uint16 public originationFeeBps = 25; // 0.25% one-time borrow fee.
    uint16 public closeFactorBps = 5_000; // max 50% debt repay in one liquidation.
    uint16 public protocolLiquidationFeeBps = 200; // 2% of repaid value paid to treasury.
    uint256 public baseBorrowRatePerSecond = uint256(2e16) / YEAR; // 2% APR.
    uint256 public slopeBorrowRatePerSecond = uint256(30e16) / YEAR; // +30% APR at 100% utilization.

    uint256 public borrowIndex = WAD;
    uint256 public lastAccrualTimestamp;
    uint256 public totalDebt;
    uint256 public totalSuppliedLiquidity;
    uint256 public protocolReserves;
    uint256 public totalLiquidityShares;
    uint256 public globalSupplyCap;

    struct Market {
        bool listed;
        uint16 collateralFactorBps;
        uint16 liquidationThresholdBps;
        uint16 liquidationBonusBps;
        uint64 maxStaleness;
        AggregatorV3Interface priceFeed;
        uint256 borrowCap;
        uint256 supplyCap;
        uint256 totalBorrowed;
    }

    mapping(address => Market) public markets;
    mapping(address => mapping(address => uint256)) public collateralBalance; // user => stock token => raw token amount
    mapping(address => uint256) private debtPrincipal; // principal scaled at userBorrowIndex
    mapping(address => uint256) public userBorrowIndex;
    mapping(address => uint256) public suppliedLiquidity; // LP receipt shares; 1:1 before yield accrues.

    event MarketListed(address indexed token, address indexed priceFeed, uint16 collateralFactorBps, uint16 liquidationThresholdBps, uint16 liquidationBonusBps, uint64 maxStaleness, uint256 borrowCap, uint256 supplyCap);
    event Supplied(address indexed lender, uint256 amount, uint256 shares);
    event LiquidityWithdrawn(address indexed lender, uint256 amount, uint256 shares);
    event CollateralDeposited(address indexed user, address indexed token, uint256 amount);
    event CollateralWithdrawn(address indexed user, address indexed token, uint256 amount);
    event Borrowed(address indexed user, uint256 amount, uint256 fee);
    event Repaid(address indexed user, uint256 amount);
    event Liquidated(address indexed user, address indexed liquidator, address indexed collateralToken, uint256 repaid, uint256 seized, uint256 protocolFee);
    event InterestAccrued(uint256 interest, uint256 supplierInterest, uint256 reserves, uint256 newBorrowIndex);
    event RiskParamsUpdated(uint16 reserveFactorBps, uint16 originationFeeBps, uint16 closeFactorBps, uint16 protocolLiquidationFeeBps);
    event TreasuryUpdated(address indexed treasury);

    error AmountZero();
    error MarketNotListed();
    error UnsafePosition();
    error InsufficientLiquidity();
    error InvalidOracle();
    error OracleStale();
    error OraclePaused();
    error HealthyPosition();
    error RepayTooLarge();
    error BorrowCapExceeded();
    error SupplyCapExceeded();
    error CloseFactorExceeded();

    constructor(IERC20 _debtAsset, address initialOwner, address _treasury, address _sequencerUptimeFeed) Ownable(initialOwner) {
        require(address(_debtAsset) != address(0) && initialOwner != address(0) && _treasury != address(0), "zero address");
        debtAsset = _debtAsset;
        treasury = _treasury;
        sequencerUptimeFeed = AggregatorV3Interface(_sequencerUptimeFeed);
        lastAccrualTimestamp = block.timestamp;
    }

    function listMarket(
        address stockToken,
        AggregatorV3Interface priceFeed,
        uint16 collateralFactorBps,
        uint16 liquidationThresholdBps,
        uint16 liquidationBonusBps,
        uint64 maxStaleness,
        uint256 borrowCap,
        uint256 supplyCap
    ) external onlyOwner {
        require(stockToken != address(0) && address(priceFeed) != address(0), "zero address");
        require(collateralFactorBps < liquidationThresholdBps && liquidationThresholdBps <= BPS, "bad risk params");
        require(liquidationBonusBps <= 2_000, "bonus too high");
        require(maxStaleness > 0, "staleness required");
        markets[stockToken] = Market(true, collateralFactorBps, liquidationThresholdBps, liquidationBonusBps, maxStaleness, priceFeed, borrowCap, supplyCap, markets[stockToken].totalBorrowed);
        if (supplyCap > 0 && (globalSupplyCap == 0 || supplyCap < globalSupplyCap)) globalSupplyCap = supplyCap;
        emit MarketListed(stockToken, address(priceFeed), collateralFactorBps, liquidationThresholdBps, liquidationBonusBps, maxStaleness, borrowCap, supplyCap);
    }

    function setRiskParams(uint16 _reserveFactorBps, uint16 _originationFeeBps, uint16 _closeFactorBps, uint16 _protocolLiquidationFeeBps) external onlyOwner {
        require(_reserveFactorBps <= 5_000 && _originationFeeBps <= 1_000 && _closeFactorBps > 0 && _closeFactorBps <= BPS && _protocolLiquidationFeeBps <= 2_000, "bad risk params");
        reserveFactorBps = _reserveFactorBps;
        originationFeeBps = _originationFeeBps;
        closeFactorBps = _closeFactorBps;
        protocolLiquidationFeeBps = _protocolLiquidationFeeBps;
        emit RiskParamsUpdated(_reserveFactorBps, _originationFeeBps, _closeFactorBps, _protocolLiquidationFeeBps);
    }

    function setTreasury(address _treasury) external onlyOwner {
        require(_treasury != address(0), "zero address");
        treasury = _treasury;
        emit TreasuryUpdated(_treasury);
    }

    function pause() external onlyOwner { _pause(); }
    function unpause() external onlyOwner { _unpause(); }

    function accrueInterest() public {
        uint256 dt = block.timestamp - lastAccrualTimestamp;
        if (dt == 0) return;
        lastAccrualTimestamp = block.timestamp;
        if (totalDebt == 0) return;

        uint256 cash = _cashPriorReserves();
        uint256 util = totalDebt * WAD / (cash + totalDebt);
        uint256 rate = baseBorrowRatePerSecond + (slopeBorrowRatePerSecond * util / WAD);
        uint256 interest = totalDebt * rate * dt / WAD;
        if (interest == 0) return;

        uint256 reserves = interest * reserveFactorBps / BPS;
        uint256 supplierInterest = interest - reserves;
        totalDebt += interest;
        totalSuppliedLiquidity += supplierInterest;
        protocolReserves += reserves;
        borrowIndex += borrowIndex * interest / (totalDebt - interest);
        emit InterestAccrued(interest, supplierInterest, reserves, borrowIndex);
    }

    function supplyLiquidity(uint256 amount) external nonReentrant whenNotPaused {
        if (amount == 0) revert AmountZero();
        accrueInterest();
        if (globalSupplyCap > 0 && totalSuppliedLiquidity + amount > globalSupplyCap) revert SupplyCapExceeded();
        uint256 shares = totalLiquidityShares == 0 || totalSuppliedLiquidity == 0 ? amount : amount * totalLiquidityShares / totalSuppliedLiquidity;
        if (shares == 0) revert AmountZero();
        suppliedLiquidity[msg.sender] += shares;
        totalLiquidityShares += shares;
        totalSuppliedLiquidity += amount;
        debtAsset.safeTransferFrom(msg.sender, address(this), amount);
        emit Supplied(msg.sender, amount, shares);
    }

    function withdrawableLiquidity(address lender) public view returns (uint256) {
        if (totalLiquidityShares == 0) return 0;
        return suppliedLiquidity[lender] * totalSuppliedLiquidity / totalLiquidityShares;
    }

    function withdrawLiquidity(uint256 amount) external nonReentrant {
        if (amount == 0) revert AmountZero();
        accrueInterest();
        uint256 owed = withdrawableLiquidity(msg.sender);
        if (amount > owed) revert InsufficientLiquidity();
        if (liquidityAvailable() < amount) revert InsufficientLiquidity();
        uint256 shares = amount * totalLiquidityShares / totalSuppliedLiquidity;
        if (shares == 0) shares = 1;
        suppliedLiquidity[msg.sender] -= shares;
        totalLiquidityShares -= shares;
        totalSuppliedLiquidity -= amount;
        debtAsset.safeTransfer(msg.sender, amount);
        emit LiquidityWithdrawn(msg.sender, amount, shares);
    }

    function withdrawProtocolReserves(uint256 amount) external onlyOwner {
        if (amount == 0) revert AmountZero();
        protocolReserves -= amount;
        if (debtAsset.balanceOf(address(this)) < amount) revert InsufficientLiquidity();
        debtAsset.safeTransfer(treasury, amount);
    }

    function depositCollateral(address stockToken, uint256 amount) external nonReentrant whenNotPaused {
        if (amount == 0) revert AmountZero();
        if (!markets[stockToken].listed) revert MarketNotListed();
        _priceUsdWad(stockToken, markets[stockToken]);
        collateralBalance[msg.sender][stockToken] += amount;
        IERC20(stockToken).safeTransferFrom(msg.sender, address(this), amount);
        emit CollateralDeposited(msg.sender, stockToken, amount);
    }

    function withdrawCollateral(address stockToken, uint256 amount) external nonReentrant {
        if (amount == 0) revert AmountZero();
        accrueInterest();
        if (!markets[stockToken].listed) revert MarketNotListed();
        collateralBalance[msg.sender][stockToken] -= amount;
        if (!_isHealthy(msg.sender, stockToken)) revert UnsafePosition();
        IERC20(stockToken).safeTransfer(msg.sender, amount);
        emit CollateralWithdrawn(msg.sender, stockToken, amount);
    }

    function borrow(address stockToken, uint256 amount) external nonReentrant whenNotPaused {
        if (amount == 0) revert AmountZero();
        accrueInterest();
        Market storage market = markets[stockToken];
        if (!market.listed) revert MarketNotListed();
        if (market.borrowCap > 0 && market.totalBorrowed + amount > market.borrowCap) revert BorrowCapExceeded();
        uint256 fee = amount * originationFeeBps / BPS;
        if (liquidityAvailable() < amount) revert InsufficientLiquidity();
        _syncBorrower(msg.sender);
        debtPrincipal[msg.sender] += amount;
        totalDebt += amount;
        market.totalBorrowed += amount;
        if (!_isHealthy(msg.sender, stockToken)) revert UnsafePosition();
        if (fee > 0) debtAsset.safeTransfer(treasury, fee);
        debtAsset.safeTransfer(msg.sender, amount - fee);
        emit Borrowed(msg.sender, amount, fee);
    }

    function repay(uint256 amount) external nonReentrant whenNotPaused {
        if (amount == 0) revert AmountZero();
        accrueInterest();
        _syncBorrower(msg.sender);
        uint256 debt = debtPrincipal[msg.sender];
        if (amount > debt) amount = debt;
        debtPrincipal[msg.sender] = debt - amount;
        totalDebt -= amount;
        debtAsset.safeTransferFrom(msg.sender, address(this), amount);
        emit Repaid(msg.sender, amount);
    }

    function liquidate(address user, address stockToken, uint256 repayAmount) external nonReentrant whenNotPaused {
        if (repayAmount == 0) revert AmountZero();
        accrueInterest();
        _syncBorrower(user);
        Market storage market = markets[stockToken];
        if (!market.listed) revert MarketNotListed();
        if (healthFactor(user, stockToken) >= MIN_HEALTH_FACTOR) revert HealthyPosition();
        uint256 userDebt = debtPrincipal[user];
        uint256 maxRepay = userDebt * closeFactorBps / BPS;
        if (repayAmount > maxRepay) revert CloseFactorExceeded();
        if (repayAmount > userDebt) repayAmount = userDebt;

        uint256 price = _priceUsdWad(stockToken, market);
        uint256 liquidatorSeizeValue = repayAmount * (BPS + market.liquidationBonusBps) / BPS;
        uint256 protocolFee = repayAmount * protocolLiquidationFeeBps / BPS;
        uint256 seizeAmount = (liquidatorSeizeValue + protocolFee) * WAD / price;
        uint256 available = collateralBalance[user][stockToken];
        if (seizeAmount > available) seizeAmount = available;
        uint256 protocolSeize = protocolFee * WAD / price;
        if (protocolSeize > seizeAmount) protocolSeize = 0;
        uint256 liquidatorSeize = seizeAmount - protocolSeize;

        debtPrincipal[user] = userDebt - repayAmount;
        totalDebt -= repayAmount;
        market.totalBorrowed = market.totalBorrowed > repayAmount ? market.totalBorrowed - repayAmount : 0;
        collateralBalance[user][stockToken] = available - seizeAmount;
        debtAsset.safeTransferFrom(msg.sender, address(this), repayAmount);
        if (protocolFee > 0) debtAsset.safeTransferFrom(msg.sender, treasury, protocolFee);
        IERC20(stockToken).safeTransfer(msg.sender, liquidatorSeize);
        if (protocolSeize > 0) IERC20(stockToken).safeTransfer(treasury, protocolSeize);
        emit Liquidated(user, msg.sender, stockToken, repayAmount, seizeAmount, protocolFee);
    }

    function debtBalance(address user) public view returns (uint256) {
        uint256 index = userBorrowIndex[user];
        if (index == 0) return 0;
        return debtPrincipal[user] * borrowIndex / index;
    }

    function collateralValueUsd(address user, address stockToken) public view returns (uint256) {
        Market memory market = markets[stockToken];
        if (!market.listed) revert MarketNotListed();
        return collateralBalance[user][stockToken] * _priceUsdWad(stockToken, market) / WAD;
    }

    function borrowLimit(address user, address stockToken) public view returns (uint256) {
        Market memory market = markets[stockToken];
        if (!market.listed) revert MarketNotListed();
        return collateralValueUsd(user, stockToken) * market.collateralFactorBps / BPS;
    }

    function liquidationLimit(address user, address stockToken) public view returns (uint256) {
        Market memory market = markets[stockToken];
        if (!market.listed) revert MarketNotListed();
        return collateralValueUsd(user, stockToken) * market.liquidationThresholdBps / BPS;
    }

    function healthFactor(address user, address stockToken) public view returns (uint256) {
        uint256 debt = debtBalance(user);
        if (debt == 0) return type(uint256).max;
        return liquidationLimit(user, stockToken) * WAD / debt;
    }

    function liquidityAvailable() public view returns (uint256) {
        uint256 bal = debtAsset.balanceOf(address(this));
        return bal > protocolReserves ? bal - protocolReserves : 0;
    }

    function currentBorrowAprBps() external view returns (uint256) {
        uint256 cash = _cashPriorReserves();
        if (totalDebt == 0) return baseBorrowRatePerSecond * YEAR * BPS / WAD;
        uint256 util = totalDebt * WAD / (cash + totalDebt);
        return (baseBorrowRatePerSecond + (slopeBorrowRatePerSecond * util / WAD)) * YEAR * BPS / WAD;
    }

    function _syncBorrower(address user) internal {
        uint256 index = userBorrowIndex[user];
        if (index == 0) {
            userBorrowIndex[user] = borrowIndex;
            return;
        }
        debtPrincipal[user] = debtPrincipal[user] * borrowIndex / index;
        userBorrowIndex[user] = borrowIndex;
    }

    function _isHealthy(address user, address stockToken) internal view returns (bool) {
        return debtBalance(user) <= borrowLimit(user, stockToken);
    }

    function _priceUsdWad(address stockToken, Market memory market) internal view returns (uint256) {
        _checkSequencer();
        try IOraclePauseFlag(stockToken).oraclePaused() returns (bool paused) {
            if (paused) revert OraclePaused();
        } catch {}
        (uint80 roundId, int256 answer,, uint256 updatedAt, uint80 answeredInRound) = market.priceFeed.latestRoundData();
        if (answer <= 0 || updatedAt == 0 || answeredInRound < roundId) revert InvalidOracle();
        if (block.timestamp - updatedAt > market.maxStaleness) revert OracleStale();
        uint8 feedDecimals = market.priceFeed.decimals();
        if (feedDecimals > 18) {
            return uint256(answer) / (10 ** (feedDecimals - 18));
        }
        return uint256(answer) * (10 ** (18 - feedDecimals));
    }

    function _checkSequencer() internal view {
        if (address(sequencerUptimeFeed) == address(0)) return;
        (, int256 sequencerStatus, uint256 startedAt,,) = sequencerUptimeFeed.latestRoundData();
        require(sequencerStatus == 0, "Sequencer down"); // 0 = up
        require(block.timestamp - startedAt > SEQUENCER_GRACE_PERIOD, "Grace period not over");
    }

    function _cashPriorReserves() internal view returns (uint256) {
        uint256 bal = debtAsset.balanceOf(address(this));
        return bal > protocolReserves ? bal - protocolReserves : 0;
    }

}
Chain explorer1982msChain node84ms