Skip to content

Commit dfcc813

Browse files
committed
MarketFactory support fixed term
1 parent c912d74 commit dfcc813

4 files changed

Lines changed: 192 additions & 2 deletions

File tree

src/broker/interfaces/IBroker.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,13 @@ interface IBroker is IBrokerBase {
167167
/// @dev get the total debt of a user including principal and interest
168168
/// @param user The address of the user
169169
function getUserTotalDebt(address user) external view returns (uint256 totalDebt);
170+
171+
/// @dev set the market id for the broker, callable by owner only
172+
/// @param marketId The market id to set
173+
function setMarketId(Id marketId) external;
174+
175+
/// @dev toggle the liquidation whitelist status of an account
176+
/// @param account The address of the account
177+
/// @param isAddition Whether to add or remove the account from the whitelist
178+
function toggleLiquidationWhitelist(address account, bool isAddition) external;
170179
}

src/broker/interfaces/IRateCalculator.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ interface IRateCalculator {
2525
*/
2626
function getRate(address broker) external view returns (uint256);
2727

28+
function registerBroker(address _broker, uint256 _ratePerSecond, uint256 _maxRatePerSecond) external;
29+
2830
/// ------------------------------
2931
/// Events
3032
/// ------------------------------

src/moolah/MarketFactory.sol

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,23 @@ import { IBuyBack } from "moolah/interfaces/IBuyBack.sol";
1010
import { IListaAutoBuyBack } from "moolah/interfaces/IListaAutoBuyBack.sol";
1111
import { IPublicLiquidator } from "liquidator/IPublicLiquidator.sol";
1212
import { ISmartProvider } from "../provider/interfaces/IProvider.sol";
13+
import { IBroker } from "../broker/interfaces/IBroker.sol";
14+
import { IBrokerLiquidator } from "../liquidator/IBrokerLiquidator.sol";
15+
import { IRateCalculator } from "../broker/interfaces/IRateCalculator.sol";
1316

1417
contract MarketFactory is UUPSUpgradeable, AccessControlEnumerableUpgradeable {
1518
using MarketParamsLib for MarketParams;
1619

20+
struct FixedTermMarketParams {
21+
address broker;
22+
address loanToken;
23+
address collateralToken;
24+
address irm;
25+
uint256 lltv;
26+
uint256 ratePerSecond;
27+
uint256 maxRatePerSecond;
28+
}
29+
1730
IMoolah public immutable moolah;
1831
ILiquidator public immutable liquidator;
1932
IListaRevenueDistributor public immutable revenueDistributor;
@@ -24,9 +37,15 @@ contract MarketFactory is UUPSUpgradeable, AccessControlEnumerableUpgradeable {
2437
address public immutable sliBNB;
2538
address public immutable BNBProvider;
2639
address public immutable slisBNBProvider;
40+
IRateCalculator public immutable rateCalculator;
41+
IBrokerLiquidator public immutable brokerLiquidator;
2742

2843
bytes32 public constant OPERATOR = keccak256("OPERATOR");
44+
bytes32 public constant PAUSER = keccak256("PAUSER");
45+
bytes32 public constant BOT = keccak256("BOT");
2946

47+
event BrokerMarketDeployed(FixedTermMarketParams fixedTermMarketParams, Id marketId, address broker);
48+
event CommonMarketDeployed(MarketParams marketParams, Id marketId);
3049
/**
3150
* @dev constructor to set immutable variables
3251
* @param _moolah The address of the Moolah contract
@@ -39,6 +58,8 @@ contract MarketFactory is UUPSUpgradeable, AccessControlEnumerableUpgradeable {
3958
* @param _sliBNB The address of the sliBNB token
4059
* @param _BNBProvider The address of the BNB provider
4160
* @param _slisBNBProvider The address of the slisBNB provider
61+
* @param _rateCalculator The address of the rate calculator contract
62+
* @param _brokerLiquidator The address of the broker liquidator contract
4263
*/
4364
constructor(
4465
address _moolah,
@@ -50,7 +71,9 @@ contract MarketFactory is UUPSUpgradeable, AccessControlEnumerableUpgradeable {
5071
address _WBNB,
5172
address _sliBNB,
5273
address _BNBProvider,
53-
address _slisBNBProvider
74+
address _slisBNBProvider,
75+
address _rateCalculator,
76+
address _brokerLiquidator
5477
) {
5578
// sanity check for constructor arguments
5679
require(_moolah != address(0), "ZeroAddress");
@@ -63,6 +86,7 @@ contract MarketFactory is UUPSUpgradeable, AccessControlEnumerableUpgradeable {
6386
require(_sliBNB != address(0), "ZeroAddress");
6487
require(_BNBProvider != address(0), "ZeroAddress");
6588
require(_slisBNBProvider != address(0), "ZeroAddress");
89+
require(_rateCalculator != address(0), "ZeroAddress");
6690
// set immutable variables
6791
moolah = IMoolah(_moolah);
6892
liquidator = ILiquidator(_liquidator);
@@ -74,6 +98,8 @@ contract MarketFactory is UUPSUpgradeable, AccessControlEnumerableUpgradeable {
7498
sliBNB = _sliBNB;
7599
BNBProvider = _BNBProvider;
76100
slisBNBProvider = _slisBNBProvider;
101+
rateCalculator = IRateCalculator(_rateCalculator);
102+
brokerLiquidator = IBrokerLiquidator(_brokerLiquidator);
77103

78104
_disableInitializers();
79105
}
@@ -85,6 +111,7 @@ contract MarketFactory is UUPSUpgradeable, AccessControlEnumerableUpgradeable {
85111
*/
86112
function initialize(address admin, address operator) public initializer {
87113
require(admin != address(0) && operator != address(0), "ZeroAddress");
114+
require(operator != address(0), "ZeroAddress");
88115
__AccessControl_init();
89116

90117
_grantRole(DEFAULT_ADMIN_ROLE, admin);
@@ -143,6 +170,30 @@ contract MarketFactory is UUPSUpgradeable, AccessControlEnumerableUpgradeable {
143170
_createMarket(param, liquidatorWhitelist, supplyWhitelist, liquidatorMarketWhitelist, liquidatorSmartProvider);
144171
}
145172

173+
/**
174+
* @dev Creates new fixed term markets with the given parameters and configures the related contracts
175+
* @param params An array of FixedTermMarketParams for the markets to be created
176+
*/
177+
function batchCreateFixedTermMarkets(
178+
FixedTermMarketParams[] calldata params
179+
) external onlyRole(OPERATOR) returns (Id[] memory) {
180+
require(params.length > 0, "empty market params");
181+
182+
Id[] memory ids = new Id[](params.length);
183+
for (uint256 i = 0; i < params.length; i++) {
184+
ids[i] = _createFixedTermMarket(params[i]);
185+
}
186+
return ids;
187+
}
188+
189+
/**
190+
* @dev Creates a new fixed term market with the given parameters and configures the related contracts
191+
* @param param The FixedTermMarketParams for the market to be created
192+
*/
193+
function createFixedTermMarket(FixedTermMarketParams calldata param) external onlyRole(OPERATOR) returns (Id) {
194+
return _createFixedTermMarket(param);
195+
}
196+
146197
function _createMarket(
147198
MarketParams memory param,
148199
address[] memory liquidatorWhitelist,
@@ -209,6 +260,50 @@ contract MarketFactory is UUPSUpgradeable, AccessControlEnumerableUpgradeable {
209260
if (liquidatorSmartProvider) {
210261
_configSmartProvider(id, param.oracle, param.collateralToken);
211262
}
263+
264+
emit CommonMarketDeployed(param, id);
265+
}
266+
267+
function _createFixedTermMarket(FixedTermMarketParams memory param) private returns (Id id) {
268+
IBroker broker = IBroker(param.broker);
269+
require(param.broker != address(0), "Zero broker address");
270+
271+
// moolah create market
272+
MarketParams memory marketParam = MarketParams({
273+
loanToken: param.loanToken,
274+
collateralToken: param.collateralToken,
275+
oracle: param.broker,
276+
irm: param.irm,
277+
lltv: param.lltv
278+
});
279+
moolah.createMarket(marketParam);
280+
Id id = marketParam.id();
281+
282+
// moolah set liquidation whitelist
283+
Id[] memory ids = new Id[](1);
284+
ids[0] = id;
285+
address[][] memory whitelist = new address[][](1);
286+
whitelist[0] = new address[](1);
287+
whitelist[0][0] = param.broker;
288+
moolah.batchToggleLiquidationWhitelist(ids, whitelist, true);
289+
290+
// broker set market id
291+
broker.setMarketId(id);
292+
293+
// broker set liquidator whitelist
294+
broker.toggleLiquidationWhitelist(address(brokerLiquidator), true);
295+
296+
// moolah set broker
297+
moolah.setMarketBroker(id, param.broker, true);
298+
299+
// rate calculator register broker
300+
rateCalculator.registerBroker(param.broker, param.ratePerSecond, param.maxRatePerSecond);
301+
302+
// broker liquidator set market whitelist
303+
brokerLiquidator.setMarketToBroker(Id.unwrap(id), param.broker, true);
304+
305+
emit BrokerMarketDeployed(param, id, param.broker);
306+
return id;
212307
}
213308

214309
function _configSmartProvider(Id id, address provider, address collateral) private {

test/moolah/MarketFactoryTest.sol

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ import { ERC20Mock } from "moolah/mocks/ERC20Mock.sol";
1818
import { MockProvider } from "./mocks/MockProvider.sol";
1919
import { OracleMock } from "moolah/mocks/OracleMock.sol";
2020
import { MockSmartProvider } from "./mocks/MockSmartProvider.sol";
21+
import { RateCalculator, RateConfig } from "../../src/broker/RateCalculator.sol";
22+
import { BrokerLiquidator } from "../../src/liquidator/BrokerLiquidator.sol";
23+
import { LendingBroker } from "../../src/broker/LendingBroker.sol";
2124

2225
contract MarketFactoryTest is Test {
2326
using MarketParamsLib for MarketParams;
@@ -35,6 +38,8 @@ contract MarketFactoryTest is Test {
3538
ERC20Mock WBNB;
3639
ERC20Mock slisBNB;
3740
OracleMock oracle;
41+
RateCalculator rateCalculator;
42+
BrokerLiquidator brokerLiquidator;
3843

3944
address admin;
4045
address manager;
@@ -67,6 +72,20 @@ contract MarketFactoryTest is Test {
6772
);
6873
irm = InterestRateModel(address(irmProxy));
6974

75+
RateCalculator rateCalculatorImpl = new RateCalculator();
76+
ERC1967Proxy rateCalculatorProxy = new ERC1967Proxy(
77+
address(rateCalculatorImpl),
78+
abi.encodeWithSelector(rateCalculatorImpl.initialize.selector, admin, manager, bot)
79+
);
80+
rateCalculator = RateCalculator(address(rateCalculatorProxy));
81+
82+
BrokerLiquidator brokerLiquidatorImpl = new BrokerLiquidator(address(moolah));
83+
ERC1967Proxy brokerLiquidatorProxy = new ERC1967Proxy(
84+
address(brokerLiquidatorImpl),
85+
abi.encodeWithSelector(brokerLiquidatorImpl.initialize.selector, admin, manager, bot)
86+
);
87+
brokerLiquidator = BrokerLiquidator(address(brokerLiquidatorProxy));
88+
7089
liquidator = new MockLiquidator();
7190
publicLiquidator = new MockLiquidator();
7291
listaRevenueDistributor = new MockListaRevenueDistributor();
@@ -90,7 +109,9 @@ contract MarketFactoryTest is Test {
90109
address(WBNB),
91110
address(slisBNB),
92111
address(bnbProvider),
93-
address(slisBNBProvider)
112+
address(slisBNBProvider),
113+
address(rateCalculator),
114+
address(brokerLiquidator)
94115
);
95116
ERC1967Proxy marketFactoryProxy = new ERC1967Proxy(
96117
address(marketFactoryImpl),
@@ -106,6 +127,8 @@ contract MarketFactoryTest is Test {
106127
vm.startPrank(admin);
107128
moolah.grantRole(moolah.OPERATOR(), address(marketFactory));
108129
moolah.grantRole(moolah.MANAGER(), address(marketFactory));
130+
rateCalculator.grantRole(rateCalculator.MANAGER(), address(marketFactory));
131+
brokerLiquidator.grantRole(brokerLiquidator.MANAGER(), address(marketFactory));
109132
vm.stopPrank();
110133
}
111134

@@ -427,4 +450,65 @@ contract MarketFactoryTest is Test {
427450
"Provider mismatch for BNB market"
428451
);
429452
}
453+
454+
function testCreateFixedTermMarket() public {
455+
address relayer = makeAddr("relayer");
456+
uint256 ratePerSecond = 1000000000195993755570992534;
457+
uint256 maxRatePerSecond = 1000000008319516284844716199;
458+
ERC20Mock loanToken = new ERC20Mock();
459+
ERC20Mock collateralToken = new ERC20Mock();
460+
LendingBroker broker = newLendingBroker(relayer);
461+
462+
MarketFactory.FixedTermMarketParams memory params = MarketFactory.FixedTermMarketParams({
463+
broker: address(broker),
464+
loanToken: address(loanToken),
465+
collateralToken: address(collateralToken),
466+
irm: address(irm),
467+
lltv: lltv80,
468+
ratePerSecond: ratePerSecond,
469+
maxRatePerSecond: maxRatePerSecond
470+
});
471+
oracle.setPrice(address(loanToken), 1e8);
472+
oracle.setPrice(address(collateralToken), 1e8);
473+
474+
vm.startPrank(admin);
475+
broker.grantRole(broker.MANAGER(), address(marketFactory));
476+
vm.stopPrank();
477+
478+
vm.startPrank(operator);
479+
Id id = marketFactory.createFixedTermMarket(params);
480+
vm.stopPrank();
481+
482+
assertEq(Id.unwrap(id), Id.unwrap(broker.MARKET_ID()), "Market ID mismatch between broker and market factory");
483+
assertEq(moolah.brokers(id), address(broker), "Broker not set for market");
484+
assertTrue(moolah.isLiquidationWhitelist(id, address(broker)), "Broker should be in liquidation whitelist");
485+
assertEq(
486+
broker.getLiquidationWhitelist()[0],
487+
address(brokerLiquidator),
488+
"Liquidation whitelist mismatch for broker"
489+
);
490+
assertEq(
491+
brokerLiquidator.brokerToMarketId(address(broker)),
492+
Id.unwrap(id),
493+
"Market ID mismatch in broker liquidator"
494+
);
495+
}
496+
497+
function newLendingBroker(address replayer) private returns (LendingBroker) {
498+
LendingBroker lendingBrokerImpl = new LendingBroker(address(moolah), replayer, address(oracle));
499+
ERC1967Proxy lendingBrokerProxy = new ERC1967Proxy(
500+
address(lendingBrokerImpl),
501+
abi.encodeWithSelector(
502+
lendingBrokerImpl.initialize.selector,
503+
admin,
504+
manager,
505+
bot,
506+
pauser,
507+
address(rateCalculator),
508+
100
509+
)
510+
);
511+
512+
return LendingBroker(address(lendingBrokerProxy));
513+
}
430514
}

0 commit comments

Comments
 (0)