Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions script/broker/deploy_broker.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ contract DeployLendingBroker is DeployBase {
console.log("Deployer: ", deployer);
vm.startBroadcast(deployerPrivateKey);

// Deploy LendingBroker implementation
LendingBroker impl = new LendingBroker(moolah, interestRelayer, oracle, wbnb);
// Deploy LendingBroker implementation (single impl shared across all proxies)
LendingBroker impl = new LendingBroker(moolah, wbnb);
console.log("LendingBroker implementation: ", address(impl));

// Deploy LendingBroker proxy
Expand All @@ -51,7 +51,9 @@ contract DeployLendingBroker is DeployBase {
bot,
pauser,
rateCalculator,
maxFixedLoanPositions
maxFixedLoanPositions,
interestRelayer,
oracle
)
);
console.log("LendingBroker proxy: ", address(proxy));
Expand Down
18 changes: 5 additions & 13 deletions script/broker/deploy_brokerImpl.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ contract DeployLendingBrokerImpl is DeployBase {
0xc26CaAcb00854c5460030B0aFde60C37D9d39C79,
0x3ade951523e81dD45e5787bb0b95Ce7341Db1287
];
address moolah;
address wbnb;

function setUp() public {
moolah = vm.envAddress("MOOLAH");
wbnb = vm.envOr("WBNB", address(0));
}

Expand All @@ -48,19 +50,9 @@ contract DeployLendingBrokerImpl is DeployBase {

vm.startBroadcast(deployerPrivateKey);

for (uint256 i = 0; i < brokers.length; i++) {
address payable proxy = payable(brokers[i]);

// Read constructor params from the existing proxy contract
address _moolah = address(LendingBroker(proxy).MOOLAH());
address _relayer = LendingBroker(proxy).RELAYER();
address _oracle = address(LendingBroker(proxy).ORACLE());

// Deploy LendingBroker implementation
LendingBroker impl = new LendingBroker(_moolah, _relayer, _oracle, wbnb);
console.log("Broker proxy:", proxy);
console.log(" New impl: ", address(impl));
}
// Deploy LendingBroker implementation
LendingBroker impl = new LendingBroker(moolah, wbnb);
console.log("LendingBroker implementation: ", address(impl));

vm.stopBroadcast();
}
Expand Down
4 changes: 2 additions & 2 deletions script/broker/deploy_brokerLiquidator.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ contract DeployBrokerLiquidator is DeployBase {
// grant roles to manager and admin
bytes32 MANAGER = keccak256("MANAGER");
bytes32 DEFAULT_ADMIN_ROLE = 0x0000000000000000000000000000000000000000000000000000000000000000;
BrokerLiquidator(address(proxy)).grantRole(MANAGER, manager);
BrokerLiquidator(address(proxy)).grantRole(DEFAULT_ADMIN_ROLE, timelock);
BrokerLiquidator(payable(address(proxy))).grantRole(MANAGER, manager);
BrokerLiquidator(payable(address(proxy))).grantRole(DEFAULT_ADMIN_ROLE, timelock);

vm.stopBroadcast();
}
Expand Down
2 changes: 1 addition & 1 deletion script/broker/deploy_broker_20260408.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ contract DeployXautBrokers is DeployBase {

function _deployBroker(string memory label, address relayer, address oracle, address deployer) internal {
// Deploy implementation
LendingBroker impl = new LendingBroker(MOOLAH, relayer, oracle, address(0));
LendingBroker impl = new LendingBroker(MOOLAH, address(0));
console.log(string.concat("LendingBroker(", label, ") impl: "), address(impl));

// Deploy proxy
Expand Down
59 changes: 59 additions & 0 deletions script/utils/deploy_marketFactory_testnet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.34;

import "forge-std/Script.sol";
import { DeployBase } from "../DeployBase.sol";

import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

import { MarketFactory } from "../../src/moolah/MarketFactory.sol";

contract MarketFactoryDeploy is DeployBase {
address moolah = 0x4c26397D4ef9EEae55735a1631e69Da965eBC41A;
address liquidator = 0x8096Bbe78eB83B83dD286c6062a1eFbE85305c97;
address publicLiquidator = 0x456500a836DD73A5aF6fD85632E4805a8dAb9a97;
address listaRevenueDistributor = 0xe36857af784fB2B8cFA22481b51Fa0c99D13fF20;
address buyback = 0x371b76E7C797AF9336443F6588B510c9d177315e;
address autoBuyback = 0xa4cb526E4D1CaF21f1DFA824f9B4728b217D1eBd;
address WBNB = 0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd;
address slisBNB = 0xCc752dC4ae72386986d011c2B485be0DAd98C744;
address BNBProvider = 0x297152bCC1dd5bC0Df527CB16E7Ff7348d7b1d72;
address slisBNBProvider = 0x0612c940460D68C16aA213315E32Fba579beD6A6;
address rateCalculator = 0x638B87aBD83C54CBaABBDfF096f94F795fe9e83c;
address brokerLiquidator = 0xeAe8EaB31E7299Cc4c7C6F08f3C1AA8eF08dC175;

function run() public {
uint256 deployerPrivateKey = _deployerKey();
address deployer = vm.addr(deployerPrivateKey);
address operator = deployer;
address pauser = deployer;
console.log("Deployer: ", deployer);
vm.startBroadcast(deployerPrivateKey);

// Deploy implementation
MarketFactory impl = new MarketFactory(
moolah,
liquidator,
publicLiquidator,
listaRevenueDistributor,
buyback,
autoBuyback,
WBNB,
slisBNB,
BNBProvider,
slisBNBProvider,
rateCalculator,
brokerLiquidator
);
console.log("Implementation: ", address(impl));

// Deploy proxy
ERC1967Proxy proxy = new ERC1967Proxy(
address(impl),
abi.encodeWithSelector(impl.initialize.selector, deployer, operator, pauser)
);
console.log("Loop WBNB Vault BNBProvider proxy: ", address(proxy));

vm.stopBroadcast();
}
}
66 changes: 46 additions & 20 deletions src/broker/LendingBroker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ contract LendingBroker is

// ------- Immutables -------
IMoolah public immutable MOOLAH;
address public immutable RELAYER;
IOracle public immutable ORACLE;
/// @dev Wrapped native token (e.g. WBNB). address(0) if native borrow/repay is not supported.
address public immutable WBNB;
uint256 public constant MAX_FIXED_TERM_APR = 13e26; // 1.3 * RATE_SCALE = 30% MAX APR
Expand Down Expand Up @@ -118,6 +116,10 @@ contract LendingBroker is
/// @dev liquidation whitelist
EnumerableSet.AddressSet private liquidationWhitelist;

// --- V2 storage (appended to preserve layout) ---
address public RELAYER;
IOracle public ORACLE;

// ------- Modifiers -------
modifier onlyMoolah() {
if (msg.sender != address(MOOLAH)) revert NotMoolah();
Expand All @@ -137,19 +139,12 @@ contract LendingBroker is
/**
* @dev Constructor for the LendingBroker contract
* @param moolah The address of the Moolah contract
* @param relayer The address of the BrokerInterestRelayer contract
* @param oracle The address of the oracle
* @param wbnb The address of the wrapped native token (e.g. WBNB). Pass address(0) to disable native support.
*/
constructor(address moolah, address relayer, address oracle, address wbnb) {
// zero address assert
if (moolah == address(0) || relayer == address(0) || oracle == address(0)) revert ZeroAddressProvided();
// set addresses
constructor(address moolah, address wbnb) {
if (moolah == address(0)) revert ZeroAddressProvided();
MOOLAH = IMoolah(moolah);
RELAYER = relayer;
ORACLE = IOracle(oracle);
WBNB = wbnb;

_disableInitializers();
}

Expand All @@ -161,22 +156,28 @@ contract LendingBroker is
* @param _pauser The address of the pauser
* @param _rateCalculator The address of the rate calculator
* @param _maxFixedLoanPositions The maximum number of fixed loan positions a user can have
* @param _relayer The address of the BrokerInterestRelayer contract
* @param _oracle The address of the oracle
*/
function initialize(
address _admin,
address _manager,
address _bot,
address _pauser,
address _rateCalculator,
uint256 _maxFixedLoanPositions
uint256 _maxFixedLoanPositions,
address _relayer,
address _oracle
) public initializer {
if (
_admin == address(0) ||
_manager == address(0) ||
_bot == address(0) ||
_pauser == address(0) ||
_rateCalculator == address(0) ||
_maxFixedLoanPositions == 0
_maxFixedLoanPositions == 0 ||
_relayer == address(0) ||
_oracle == address(0)
) revert ZeroAddressProvided();

__AccessControlEnumerable_init();
Expand All @@ -190,6 +191,8 @@ contract LendingBroker is
// init state variables
rateCalculator = _rateCalculator;
maxFixedLoanPositions = _maxFixedLoanPositions;
RELAYER = _relayer;
ORACLE = IOracle(_oracle);
}

///////////////////////////////////////
Expand Down Expand Up @@ -436,15 +439,16 @@ contract LendingBroker is
address user = msg.sender;
DynamicLoanPosition storage position = dynamicLoanPositions[user];
if (fixedLoanPositions[user].length >= maxFixedLoanPositions) revert ExceedMaxFixedPositions();
// cap amount by principal
amount = UtilsLib.min(amount, position.principal);
// accrue current rate so normalized debt reflects the latest interest
uint256 rate = IRateCalculator(rateCalculator).accrueRate(address(this));
uint256 actualDebt = BrokerMath.denormalizeBorrowAmount(position.normalizedDebt, rate);
uint256 totalInterest = actualDebt.zeroFloorSub(position.principal);

// force user to repay interest portion when converting to fixed
uint256 interestToRepay = BrokerMath.mulDivCeiling(amount, totalInterest, position.principal);
// prioritize clearing interest first, then principal
uint256 interestToRepay = UtilsLib.min(amount, totalInterest);
uint256 principalToMove = UtilsLib.min(amount - interestToRepay, position.principal);
amount = interestToRepay + principalToMove;

if (interestToRepay > 0) {
// borrow from Moolah to increase user's actual debt at moolah
_borrowFromMoolah(user, interestToRepay);
Expand All @@ -453,9 +457,9 @@ contract LendingBroker is
}

position.normalizedDebt = position.normalizedDebt.zeroFloorSub(
BrokerMath.normalizeBorrowAmount(amount + interestToRepay, rate, false)
BrokerMath.normalizeBorrowAmount(amount, rate, false)
);
position.principal -= amount;
position.principal -= principalToMove;

if (position.principal == 0) {
delete dynamicLoanPositions[user];
Expand All @@ -470,7 +474,7 @@ contract LendingBroker is
fixedLoanPositions[user].push(
FixedLoanPosition({
posId: fixedPosUuid,
principal: amount + interestToRepay,
principal: amount,
apr: term.apr,
start: start,
end: end,
Expand Down Expand Up @@ -1118,6 +1122,28 @@ contract LendingBroker is
emit BorrowPaused(paused);
}

/**
* @dev Set the relayer address (one-time migration from immutable to storage)
* @param _relayer The address of the BrokerInterestRelayer contract
*/
function setRelayer(address _relayer) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(_relayer != address(0), "broker/zero-address-provided");
require(RELAYER == address(0), "broker/already-set");
RELAYER = _relayer;
emit RelayerSet(_relayer);
}

/**
* @dev Set the oracle address (one-time migration from immutable to storage)
* @param _oracle The address of the oracle
*/
function setOracle(address _oracle) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(_oracle != address(0), "broker/zero-address-provided");
require(address(ORACLE) == address(0), "broker/already-set");
ORACLE = IOracle(_oracle);
emit OracleSet(_oracle);
}

/**
* @dev pause contract
*/
Expand Down
6 changes: 6 additions & 0 deletions src/broker/interfaces/IBroker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity 0.8.34;

import { Id, MarketParams, IMoolah } from "moolah/interfaces/IMoolah.sol";
import { IOracle } from "moolah/interfaces/IOracle.sol";

struct FixedTermAndRate {
uint256 termId;
Expand Down Expand Up @@ -106,6 +107,8 @@ interface IBroker is IBrokerBase {
event Liquidated(address indexed user, uint256 principalCleared, uint256 interestCleared);
event MarketIdSet(Id marketId);
event BorrowPaused(bool paused);
event RelayerSet(address indexed relayer);
event OracleSet(address indexed oracle);
event AddedLiquidationWhitelist(address indexed account);
event RemovedLiquidationWhitelist(address indexed account);
event EmergencyWithdrawn(address indexed sender, address indexed token, uint256 amount);
Expand Down Expand Up @@ -184,4 +187,7 @@ interface IBroker is IBrokerBase {
/// @param account The address of the account
/// @param isAddition Whether to add or remove the account from the whitelist
function toggleLiquidationWhitelist(address account, bool isAddition) external;

/// @dev the oracle used by the broker for price feeds
function ORACLE() external view returns (IOracle);
}
Loading
Loading