This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
- Always read files before editing them. Never attempt to edit a file you haven't read in the current session.
- Before making changes, present a brief plan. Do not copy files unnecessarily or make excessive multi-file changes without confirmation.
# Build
forge build
# Run all tests
forge test -vvv
# Run tests for a specific contract
forge test --mc <ContractName> -vv
# Run a single test
forge test --mc <ContractName> --mt <testFunctionName> -vvvv
# Lint check / fix
npm run check # prettier --check
npm run fix # prettier --write
# Deploy a script
forge script <script_path> --rpc-url <rpc_url> --private-key <pk> --broadcast --verify -vvv --via-irRequired env vars for fork tests:
BSC_RPC— BSC mainnet RPC (used by most fork tests)BSC_TESTNET_RPC_URL,SEPOLIA_RPC_URL,ETHEREUM_RPC_URL— secondary forks
This is a BSC lending protocol. The main contract is Moolah (src/moolah/Moolah.sol) — a Morpho Blue–style isolated lending market. Markets are defined by MarketParams (loanToken, collateralToken, oracle, irm, lltv) and identified by a bytes32 Id derived from MarketParamsLib.id().
Key modules:
-
src/moolah/— Core protocol.Moolah.solmanages isolated lending markets. Access control usesMANAGER,OPERATOR, andPAUSERroles.createMarketrequiresOPERATORrole (or no OPERATOR set).borrowrequires the borrower to callsetAuthorization(spender, true)first. -
src/moolah-vault/—MoolahVault.sol, a curated multi-market vault that allocates deposited liquidity across Moolah markets. -
src/broker/—LendingBroker.solwraps Moolah for structured borrow positions (single collateral/loan pair per broker).CreditBroker.solhandles credit-based borrowing with interest relaying. -
src/provider/— Providers sit between users and Moolah markets, abstracting collateral management:BNBProvider.sol/ETHProvider.sol— wrap native BNB/ETH into WBNB/WETH on supply and unwrap on withdraw.SlisBNBProvider.sol— accepts slisBNB as collateral, tracks per-user deposits across multiple markets (userMarketDeposit,userTotalDeposit), and mints/burnsclisXXXLP tokens proportional to the user's BNB-denominated value (viaStakeManager.convertSnBnbToBnb). A portion of LP is minted to MPC wallets as protocol reserve (userLpRate,mpcWallets). Supports a pluggableslisBNBxMinter: when set, the legacy LP logic is phased out and all slisBNBx minting is delegated toSlisBNBxMinterviaISlisBNBxMinter.rebalance(account). Delegation of LP tokens to another address is supported viadelegateAllTo.SmartProvider.sol— accepts aStableSwapLPCollateraltoken as Moolah collateral. Users can supply raw token pairs to the stable swap pool (receiving LP) or supply existing LP directly; withdraw variants include proportional, imbalanced, and single-coin exits. Also supports the pluggableslisBNBxMinter: when set,_syncPositioncallsISlisBNBxMinter.rebalance(account)after every position change so users earn slisBNBx on top of swap fees. ImplementsIOracle— prices the LP token asmin(price0, price1) × virtual_price.V3Provider.sol— manages a single Uniswap/PancakeSwap V3 concentrated liquidity position; issues ERC20 shares as Moolah collateral. Seedocs/V3Provider.mdfor full details.
-
src/utils/PositionMigrator.sol— Migrates CDP positions (fromlista-dao-contractsInteraction contract) into Moolah markets via flash loans. -
src/utils/SlisBNBxMinter.sol— Central hub for minting slisBNBx (Binance Launchpool participation token) to users who deposit collateral through registered provider modules. Each module (SlisBNBProvider,SmartProvider, etc.) callsISlisBNBxMinter.rebalance(account)after every position change; the minter pulls the user's BNB-denominated balance viaISlisBNBxModule.getUserBalanceInBnb, applies a per-modulediscountandfeeRate, then mints/burns slisBNBx to the user's delegatee and to MPC fee wallets. Key design points:- Pluggable modules: any provider can register as a module via
addModule(address, ModuleConfig). Disabling setsdiscount = 100%. - Delegation: users can redirect their slisBNBx to another address via
delegateAllTo; modules sync delegatee changes viasyncDelegatee. - MPC fee wallets: protocol fee portion of slisBNBx is distributed across capped MPC wallets; minting fills from first wallet, burning drains from last.
- Transition from legacy LP:
SlisBNBProviderburns all legacyclisXXXLP before handing off to the minter on the first sync afterslisBNBxMinteris set.
- Pluggable modules: any provider can register as a module via
-
src/oracle/—OracleAdaptor.solwraps Chainlink/custom feeds into Moolah'speek(token)interface. -
src/dex/— Stable swap pools used for solvBTC/BTCB and similar pairs; LP tokens can be used as collateral viaStableSwapLPCollateral.sol. -
src/interest-rate-model/—InterestRateModel.sol(standard) and fixed-rate IRM. The alphaIrm address0x5F9f9173B405C6CEAfa7f98d09e4B8447e9797E6is the production IRM for most lisUSD markets.
External dependencies (via lib/):
lista-dao-contracts.git— CDP Interaction contract, SlisBNBProvider CDP, HelioProviderV2. These are upgraded in fork tests before migration tests run.openzeppelin-contracts-upgradeable— All upgradeable contracts use UUPS pattern.solady— Used forSafeTransferLib.
Key production addresses (BSC mainnet):
- Moolah:
0x8F73b65B4caAf64FBA2aF91cC5D4a2A1318E5D8C - multiOracle:
0xf3afD82A4071f272F403dC176916141f44E6c750 - Timelock (admin):
0x07D274a68393E8b8a2CCf19A2ce4Ba3518735253 - Moolah market operator:
0xd7e38800201D6a42C408Bf79d8723740C4E7f631
After any Solidity code change, run forge build before proceeding. If compilation fails, fix it immediately before making further changes. Set --force false (or omit --force) to use the incremental build cache — only use forge build --force when debugging cache-related issues.
When writing or fixing Foundry tests, pay close attention to: tick spacing alignment, vm.prank consumption order, oracle mock setup, and minimum liquidity requirements. Run forge test --match-test <testName> after each test change.
Fork tests call vm.createSelectFork(vm.envString("BSC_RPC"), <block>) in setUp. When testing PositionMigrator, three contracts must be upgraded in setUp before the migration runs (via IProxyAdmin.upgrade / IUUPSUpgradeable.upgradeTo): Interaction, HelioProviderV2, and SlisBNBProvider. The Interaction.migrator() return value is vm.mockCall-ed to return the migrator address.
To create a fresh Moolah market in a test, the test contract needs OPERATOR role — grant it via IAccessControl(address(MOOLAH)).grantRole(keccak256("OPERATOR"), address(this)) while pranked as the timelock admin (who holds DEFAULT_ADMIN_ROLE).