Contract Overview
Balance:
0 DEV
My Name Tag:
Not Available
[ Download CSV Export ]
Latest 25 internal transaction
[ Download CSV Export ]
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
MasterStateDelegator
Compiler Version
v0.8.13+commit.abaa5c0e
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
//SPDX-License-Identifier: UNLICENSE pragma solidity ^0.8.4; import "../../master/MasterState.sol"; import "../../master/MasterAdmin.sol"; import "../../master/interfaces/IMasterState.sol"; import "../../master/MasterStorage.sol"; import "../interfaces/IDelegator.sol"; import "./events/MasterStateDelegatorEvents.sol"; contract MasterStateDelegator is MasterStorage, IMasterState, MasterStateDelegatorEvents, IDelegator { constructor( address _delegateeAddress, address middleLayerAddress, address eccAddress, address crmAddress, address irmAddress, address primeOracleAddress ) { admin = delegatorAdmin = payable(msg.sender); setDelegateeAddress(_delegateeAddress); _delegatecall(abi.encodeWithSelector( MasterState.initialize.selector, middleLayerAddress, eccAddress, crmAddress, irmAddress, primeOracleAddress )); } function borrowBalanceStored( address account, uint256 chainId, address loanMarketAsset ) external view override returns (uint256 totalBorrowBalance, uint256 principal) { bytes memory data = _staticcall(abi.encodeWithSelector( MasterState.borrowBalanceStored.selector, account, chainId, loanMarketAsset )); (totalBorrowBalance, principal) = abi.decode(data, (uint256, uint256)); } function accrueInterestOnSingleLoanMarket( uint256 chainId, address loanMarketAsset ) external override { _delegatecall(abi.encodeWithSelector( MasterState.accrueInterestOnSingleLoanMarket.selector, chainId, loanMarketAsset )); } function accrueInterestOnAllLoanMarkets( address user ) external override { _delegatecall(abi.encodeWithSelector( MasterState.accrueInterestOnAllLoanMarkets.selector, user )); } function enterMarkets( address[] calldata tokens, uint256[] calldata chainIds ) external override returns (bool[] memory successes) { bytes memory data = _delegatecall(abi.encodeWithSelector( MasterState.enterMarkets.selector, tokens, chainIds )); (successes) = abi.decode(data, (bool[])); emit EnterMarkets(successes); } function exitMarket( uint256 chainId, address token ) external override returns (bool success) { bytes memory data = _delegatecall(abi.encodeWithSelector( MasterState.exitMarket.selector, chainId, token )); (success) = abi.decode(data, (bool)); } function getAccountLiquidity( address account, address pTokenModify, uint256 redeemTokens, uint256 borrowAmount ) external view override returns (uint256 liquidity, uint256 shortfall) { bytes memory data = _staticcall(abi.encodeWithSelector( MasterState.getAccountLiquidity.selector, account, pTokenModify, redeemTokens, borrowAmount )); (liquidity, shortfall) = abi.decode(data, (uint256, uint256)); } function liquidateCalculateSeizeTokens( address pTokenCollateral, uint256 chainId, uint256 actualRepayAmount, address loanAssetMarket ) external override returns (uint256 amount) { bytes memory data = _delegatecall(abi.encodeWithSelector( MasterState.liquidateCalculateSeizeTokens.selector, pTokenCollateral, chainId, actualRepayAmount, loanAssetMarket )); (amount) = abi.decode(data, (uint256)); } function liquidateBorrowAllowed( address pTokenCollateral, address borrower, uint256 chainId, uint256 repayAmount, address loanMarketAsset ) external view override returns (bool success) { bytes memory data = _staticcall(abi.encodeWithSelector( MasterState.liquidateBorrowAllowed.selector, pTokenCollateral, borrower, chainId, repayAmount, loanMarketAsset )); (success) = abi.decode(data, (bool)); } function liquidateBorrow( address pTokenCollateral, address borrower, uint256 chainId, uint256 repayAmount, address route, address loanMarketAsset, address masterLoanMarketAsset ) external override payable returns (bool success) { bytes memory data = _delegatecall(abi.encodeWithSelector( MasterState.liquidateBorrow.selector, pTokenCollateral, borrower, chainId, repayAmount, route, loanMarketAsset, masterLoanMarketAsset )); (success) = abi.decode(data, (bool)); emit LiquidateBorrow(success); } function changeOwner(address newOwner) external override { _delegatecall(abi.encodeWithSelector( MasterAdmin.changeOwner.selector, newOwner )); } function changeMiddleLayer( IMiddleLayer newMid ) external override { _delegatecall(abi.encodeWithSelector( MasterAdmin.changeMiddleLayer.selector, newMid )); } /** * @notice Add the market to the markets mapping and set it as listed * @dev Admin function to set isListed and add support for the market * @param token The address of the market (token) to list * @param chainId corresponding chain of the market */ function supportMarket( address token, uint256 chainId, uint256 initialExchangeRate_, uint8 decimals_, address underlying_ ) external override { _delegatecall(abi.encodeWithSelector( MasterAdmin.supportMarket.selector, token, chainId, initialExchangeRate_, decimals_, underlying_ )); } function listCollateralMarket(uint256 chainId, address token, bool shouldList) external override { _delegatecall(abi.encodeWithSelector( MasterAdmin.listCollateralMarket.selector, chainId, token, shouldList )); } function pauseMarket( uint256 chainId, address token, bool pause ) external override { _delegatecall(abi.encodeWithSelector( MasterAdmin.pauseMarket.selector, chainId, token, pause )); } function supportLoanMarket( uint256 chainId, address loanMarketAsset, uint256 borrowIndex, address underlying, uint8 decimals ) external override { _delegatecall(abi.encodeWithSelector( MasterAdmin.supportLoanMarket.selector, chainId, loanMarketAsset, borrowIndex, underlying, decimals )); } function pauseLoanMarket( uint256 chainId, address loanMarketAsset, bool shouldPause ) external override { _delegatecall(abi.encodeWithSelector( MasterAdmin.pauseLoanMarket.selector, chainId, loanMarketAsset, shouldPause )); } function listLoanMarket(uint256 chainId, address loanMarketAsset, bool shouldList) external override { _delegatecall(abi.encodeWithSelector( MasterAdmin.listLoanMarket.selector, chainId, loanMarketAsset, shouldList )); } function changeLiqIncentive(uint256 newLiqIncentive) external override { _delegatecall(abi.encodeWithSelector( MasterAdmin.changeLiqIncentive.selector, newLiqIncentive )); } function changeCloseFactor(uint256 newCloseFactor) external override { _delegatecall(abi.encodeWithSelector( MasterAdmin.changeCloseFactor.selector, newCloseFactor )); } function changeFactorDecimals(uint8 newFactorDecimals) external override { _delegatecall(abi.encodeWithSelector( MasterAdmin.changeFactorDecimals.selector, newFactorDecimals )); } function changeProtocolSeizeShare(uint256 newProtocolSeizeShare) external override { _delegatecall(abi.encodeWithSelector( MasterAdmin.changeProtocolSeizeShare.selector, newProtocolSeizeShare )); } // ? This is safe so long as the implmentation contract does not have any obvious // ? vulns around calling functions with raised permissions ie admin function callable by anyone // controlled-delegatecall,low-level-call // slither-disable-next-line all fallback() external payable { /* If a function is not defined above, we can still call it using msg.data. */ (bool success,) = delegateeAddress.delegatecall(msg.data); assembly { let free_mem_ptr := mload(0x40) returndatacopy(free_mem_ptr, 0, returndatasize()) switch success case 0 { revert(free_mem_ptr, returndatasize()) } default { return(free_mem_ptr, returndatasize()) } } } }
//SPDX-License-Identifier: UNLICENSE pragma solidity ^0.8.4; import "./interfaces/IMaster.sol"; import "./interfaces/IMasterState.sol"; import "./MasterEvents.sol"; import "./MasterAdmin.sol"; import "./MasterMessageHandler.sol"; import "./MasterInternals.sol"; contract MasterState is IMaster, IMasterState, MasterEvents, MasterAdmin, MasterMessageHandler, MasterInternals { function initialize( address middleLayerAddress, address eccAddress, address crmAddress, address irmAddress, address primeOracle ) external onlyOwner() { require( address(middleLayerAddress) != address(0) || address(eccAddress) != address(0) || address(crmAddress) != address(0) || address(irmAddress) != address(0) || address(primeOracle) != address(0), "NON_ZEROADDRESS" ); middleLayer = IMiddleLayer(middleLayerAddress); ecc = IECC(eccAddress); accrualBlockNumber = block.number; collateralRatioModel = ICRM(crmAddress); interestRateModel = IIRM(irmAddress); oracle = IPrimeOracle(primeOracle); } function borrowBalanceStored( address account, uint256 chainId, address loanMarketAsset ) external override view returns (uint256, uint256) { return _borrowBalanceStored( account, chainId, loanMarketAsset ); } function accrueInterestOnSingleLoanMarket( uint256 chainId, address loanMarketAsset ) external override { _accrueInterestOnSingleLoanMarket(chainId, loanMarketAsset); } function accrueInterestOnAllLoanMarkets( address user ) external override { _accrueInterestOnAllLoanMarkets(user); } function enterMarkets(address[] calldata tokens, uint256[] calldata chainIds) public override returns (bool[] memory r) { uint256 tokensLen = tokens.length; uint256 chainIdLen = chainIds.length; require(tokensLen == chainIdLen, "ARRAY_LENGTH"); r = new bool[](tokensLen); for (uint256 i; i < tokensLen; i++) { address token = tokens[i]; uint256 chainId = chainIds[i]; r[i] = _addToMarket(token, chainId, msg.sender); } } function exitMarket( uint256 chainId, address token ) external override returns (bool) { return _exitMarket(chainId, token, msg.sender); } function getAccountLiquidity( address account, address pTokenModify, uint256 redeemTokens, uint256 borrowAmount ) external view override returns (uint256, uint256) { return _getHypotheticalAccountLiquidity( account, pTokenModify, redeemTokens, borrowAmount ); } function liquidateCalculateSeizeTokens( address pTokenCollateral, uint256 chainId, uint256 actualRepayAmount, address loanMarketAsset ) external override returns (uint256) { return _liquidateCalculateSeizeTokens( pTokenCollateral, chainId, actualRepayAmount, loanMarketAsset ); } function liquidateBorrowAllowed( address pTokenCollateral, address borrower, uint256 chainId, uint256 repayAmount, address loanMarketAsset ) external view override returns (bool) { return _liquidateBorrowAllowed( pTokenCollateral, borrower, chainId, repayAmount, loanMarketAsset ); } function liquidateBorrow( address pTokenCollateral, address borrower, uint256 chainId, uint256 repayAmount, address route, address loanMarketAsset, address masterLoanMarketAsset ) external override payable returns (bool) { // TODO: Add optionality for user to choose accruing interest on all or one. _accrueInterestOnAllLoanMarkets(borrower); return _liquidateBorrow( pTokenCollateral, borrower, chainId, repayAmount, route, loanMarketAsset, masterLoanMarketAsset ); } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "./MasterModifiers.sol"; import "./MasterEvents.sol"; import "./interfaces/IMaster.sol"; import "./interfaces/IMasterState.sol"; abstract contract MasterAdmin is IMaster, IMasterState, MasterModifiers, MasterEvents { function changeOwner(address newOwner) external override onlyOwner() { if(newOwner == address(0)) revert AddressExpected(); admin = newOwner; emit ChangeOwner(newOwner); } function changeMiddleLayer( IMiddleLayer newMid ) external override onlyOwner() { emit ChangeMiddleLayer(address(middleLayer), address(newMid)); middleLayer = newMid; } /** * @notice Add the market to the markets mapping and set it as listed * @dev Admin function to set isListed and add support for the market * @param token The address of the market (token) to list * @param chainId corresponding chain of the market */ function supportMarket( address token, uint256 chainId, uint256 initialExchangeRate, uint8 decimals, address underlying ) external override onlyOwner() { if (markets[chainId][token].isListed) revert MarketExists(); markets[chainId][token].isListed = true; markets[chainId][token].initialExchangeRate = initialExchangeRate; markets[chainId][token].decimals = decimals; markets[chainId][token].underlying = underlying; emit CollateralMarketListed(chainId, token, true); } function listCollateralMarket( uint256 chainId, address token, bool shouldList ) external override onlyOwner() { // require(loanMarkets[chainId][loanMarketAsset].isListed != shouldList, "COLLATERAL_MARKET_LISTING_NO_OP"); markets[chainId][token].isListed = shouldList; emit CollateralMarketListed(chainId, token, shouldList); } function pauseMarket(uint256 chainId, address token, bool pause) external override onlyOwner() { markets[chainId][token].isPaused = pause; emit MarketPaused(chainId, token, pause); } function supportLoanMarket( uint256 chainId, address loanMarketAsset, uint256 borrowIndex, address underlying, uint8 decimals ) external override onlyOwner() { require(!loanMarkets[chainId][loanMarketAsset].isListed, "LOAN_MARKET_IS_LISTED"); loanMarkets[chainId][loanMarketAsset].isListed = true; loanMarkets[chainId][loanMarketAsset].borrowIndex = borrowIndex; loanMarkets[chainId][loanMarketAsset].decimals = decimals; loanMarkets[chainId][loanMarketAsset].underlying = underlying; emit LoanMarketListed(chainId, loanMarketAsset, true); } function listLoanMarket( uint256 chainId, address loanMarketAsset, bool shouldList ) external override onlyOwner() { // require(loanMarkets[chainId][loanMarketAsset].isListed != shouldList, "LOAN_MARKET_LISTING_NO_OP"); loanMarkets[chainId][loanMarketAsset].isListed = shouldList; emit LoanMarketListed(chainId, loanMarketAsset, shouldList); } function pauseLoanMarket( uint256 chainId, address loanMarketAsset, bool shouldPause ) external override onlyOwner() { loanMarkets[chainId][loanMarketAsset].isPaused = shouldPause; emit LoanMarketPaused(chainId, loanMarketAsset, shouldPause); } function changeLiqIncentive(uint256 newLiqIncentive) external override onlyOwner() { liquidityIncentive = newLiqIncentive; emit ChangeLiqIncentive(newLiqIncentive); } function changeCloseFactor(uint256 newCloseFactor) external override onlyOwner() { closeFactor = newCloseFactor; emit ChangeCloseFactor(newCloseFactor); } function changeFactorDecimals(uint8 newFactorDecimals) external override onlyOwner() { factorDecimals = newFactorDecimals; emit ChangeFactorDecimals(newFactorDecimals); } function changeProtocolSeizeShare(uint256 newProtocolSeizeShare) external override onlyOwner() { protocolSeizeShare = newProtocolSeizeShare; emit ChangeProtocolSeizeShare(newProtocolSeizeShare); } function setReserveFactor(uint256 newFactor) external onlyOwner() { reserveFactor = newFactor; } }
//SPDX-License-Identifier: UNLICENSE pragma solidity ^0.8.4; import "../MasterStorage.sol"; abstract contract IMasterState is MasterStorage { function borrowBalanceStored( address account, uint256 chainId, address loanMarketAsset ) external view virtual returns (uint256, uint256); function accrueInterestOnSingleLoanMarket( uint256 chainId, address loanMarketAsset ) external virtual; function accrueInterestOnAllLoanMarkets( address user ) external virtual; function enterMarkets( address[] calldata tokens, uint256[] calldata chainIds ) external virtual returns (bool[] memory r); function exitMarket( uint256 chainId, address token ) external virtual returns (bool success); function getAccountLiquidity( address account, address pTokenModify, uint256 redeemTokens, uint256 borrowAmount ) external view virtual returns (uint256, uint256); function liquidateCalculateSeizeTokens( address pTokenCollateral, uint256 chainId, uint256 actualRepayAmount, address loanMarketAsset ) external virtual returns (uint256); function liquidateBorrowAllowed( address pTokenCollateral, address borrower, uint256 chainId, uint256 repayAmount, address loanMarketAsset ) external view virtual returns (bool); function liquidateBorrow( address pTokenCollateral, address borrower, uint256 chainId, uint256 repayAmount, address route, address loanMarketAsset, address masterLoanMarketAsset ) external virtual payable returns (bool); function changeOwner(address newOwner) external virtual; function changeMiddleLayer( IMiddleLayer newMid ) external virtual; /** * @notice Add the market to the markets mapping and set it as listed * @dev Admin function to set isListed and add support for the market * @param token The address of the market (token) to list * @param chainId corresponding chain of the market */ function supportMarket( address token, uint256 chainId, uint256 initialExchangeRate_, uint8 decimals_, address underlying_ ) external virtual; function listCollateralMarket( uint256 chainId, address token, bool shouldList ) external virtual; function pauseMarket( uint256 chainId, address token, bool pause ) external virtual; function supportLoanMarket( uint256 chainId, address loanMarketAsset, uint256 borrowIndex, address underlying, uint8 decimals ) external virtual; function listLoanMarket( uint256 chainId, address loanMarketAsset, bool shouldList ) external virtual; function pauseLoanMarket( uint256 chainId, address loanMarketAsset, bool shouldPause ) external virtual; function changeLiqIncentive(uint256 newLiqIncentive) external virtual; function changeCloseFactor(uint256 newCloseFactor) external virtual; function changeFactorDecimals(uint8 newFactorDecimals) external virtual; function changeProtocolSeizeShare(uint256 newProtocolSeizeShare) external virtual; }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "../master/oracle/interfaces/IPrimeOracle.sol"; import "../middleLayer/interfaces/IMiddleLayer.sol"; import "../ecc/interfaces/IECC.sol"; import "../master/irm/interfaces/IIRM.sol"; import "../master/crm/interfaces/ICRM.sol"; abstract contract MasterStorage { /// @notice Administrator for this contract address public admin; // slither-disable-next-line unused-state IMiddleLayer internal middleLayer; // slither-disable-next-line unused-state IECC internal ecc; IIRM public interestRateModel; ICRM public collateralRatioModel; IPrimeOracle public oracle; /// @notice Block number that interest was last accrued at uint256 public accrualBlockNumber; uint256 public liquidityIncentive = 5e6; // 5% uint256 public closeFactor = 50e6; // 50% uint256 public protocolSeizeShare = 5e6; // 5% uint256 public reserveFactor = 80e6; // 80% uint8 public factorDecimals = 8; mapping(uint256 /* chainId */ => mapping(address /* user */ => mapping(address /* token */ => uint256 /* tokenBalance */))) public collateralBalances; struct Market { uint256 initialExchangeRate; uint256 totalSupply; address underlying; uint8 decimals; bool isListed; bool isPaused; } struct CollateralMarket { uint256 chainId; address token; } mapping(address /* user */ => mapping(uint256 /* chainId */ => mapping(address /* token */ => bool /* isMember */))) public accountMembership; /// @notice Official mapping of tokens -> Market metadata. mapping(uint256 /* chainId */ => mapping(address /* token */ => Market)) public markets; /// @notice All collateral markets in use by a particular user. mapping(address /* user */ => CollateralMarket[]) public accountCollateralMarkets; /// @notice Container for borrow balance information struct BorrowSnapshot { uint256 principal; /// @notice Total balance (with accrued interest), after applying the most recent balance-changing action uint256 interestIndex; /// @notice Global borrowIndex as of the most recent balance-changing action } /// @notice Represents one of the loan markets available by all satellite loan agents. Key: <chainId, loanMarketAsset> struct LoanMarket { uint256 totalReserves; /// @notice Total amount of reserves of the underlying held in this market. uint256 totalBorrows; /// @notice Total amount of outstanding borrows of the underlying in this market. uint256 borrowIndex; /// @notice Accumulator of the total earned interest rate since the opening of the market. address underlying; /// @notice The underlying asset for which this loan market exists, e.g. USP, BTC, ETH. uint8 decimals; /// @notice The decimals of the underlying asset, e.g. 18. bool isListed; /// @notice Whether or not this market is listed. bool isPaused; /// @notice User can no longer borrow on this market, but interest will still accrue. } struct LoanMarketIndex { uint256 chainId; address loanMarketAsset; /// @notice The asset for which this market exists, e.g. e.g. USP, pBTC, pETH. } /// @notice Mapping of account addresses to outstanding borrow balance. mapping(address /* borrower */ => mapping(uint256 /* chainId */ => mapping(address /* loanMarketAsset */ => BorrowSnapshot))) public accountLoanMarketBorrows; /// @notice Mapping of all borrowers currently using this loan market. mapping(address /* borrower */ => mapping(uint256 /* chainId */ => mapping(address /* loanMarketAsset */ => bool /* isMember */))) public isLoanMarketMember; /// @notice All currently supported loan market assets, e.g. USP, pBTC, pETH. mapping(uint256 /* chainId */ => mapping(address /* loanMarketAsset */ => LoanMarket)) public loanMarkets; /// @notice All loan markets in use by a particular borrower. mapping(address /* borrower */ => LoanMarketIndex[] /* loanMarketIndices */) public accountLoanMarketIndices; }
//SPDX-License-Identifier: UNLICENSE pragma solidity ^0.8.4; import "../common/DelegatorModifiers.sol"; import "../common/DelegatorErrors.sol"; import "../common/DelegatorEvents.sol"; import "../common/DelegatorStorage.sol"; abstract contract IDelegator is DelegatorEvents, DelegatorStorage, DelegatorUtil, DelegatorModifiers, DelegatorErrors { function setDelegateeAddress( address newDelegateeAddress ) public onlyAdmin() { if(newDelegateeAddress == address(0)) revert AddressExpected(); (delegateeAddress, newDelegateeAddress) = (newDelegateeAddress, delegateeAddress); emit DelegateeAddressUpdate(newDelegateeAddress, delegateeAddress); } }
//SPDX-License-Identifier: Unlicense pragma solidity ^0.8.4; import "../../../master/MasterStorage.sol"; abstract contract MasterStateDelegatorEvents is MasterStorage { event BorrowBalanceStored(uint256 totalBorrowBalance, uint256 principal); event EnterMarkets(bool[] successes); event ExchangeRateStored(uint256 rate); event GetAccountLiquidity(uint256 liquidity, uint256 shortfall); event LiquidateCalculateSeizeTokens(uint256 amount); event LiquidateBorrowAllowed(bool success); event LiquidateBorrow(bool success); }
//SPDX-License-Identifier: Unlicense pragma solidity ^0.8.4; import "../MasterStorage.sol"; import "../../util/CommonErrors.sol"; abstract contract IMaster is MasterStorage, CommonErrors { /** * @notice Return the borrow balance of account based on stored data * @param account The address whose balance should be calculated * @return (error code, the calculated balance or 0 if error code is non-zero) */ function _borrowBalanceStored( address account, uint256 chainId, address loanMarketAsset ) internal view virtual returns (uint256, uint256); function _accrueInterestOnSingleLoanMarket( uint256 chainId, address loanMarketAsset ) internal virtual; function _accrueInterestOnAllLoanMarkets( address account ) internal virtual; /** * @notice Add the market to the borrower's "assets in" for liquidity calculations * @param token The market to enter * @param chainId The chainId * @param borrower The address of the account to modify */ function _addToMarket( address token, uint256 chainId, address borrower ) internal virtual returns (bool); function _exitMarket( uint256 chainId, address token, address user ) internal virtual returns (bool); /** * @notice Get a snapshot of the account's balance, and the cached exchange rate * @dev This is used by risk engine to more efficiently perform liquidity checks. * @param user Address of the account to snapshot * @param chainId metadata of the ptoken * @param token metadata of the ptoken * @return (possible error, token balance, exchange rate) */ function _getAccountSnapshot( address user, uint256 chainId, address token ) internal view virtual returns (uint256, uint256); function _getHypotheticalAccountLiquidity( address account, address pTokenModify, uint256 redeemTokens, uint256 borrowAmount ) internal view virtual returns (uint256, uint256); /** * @notice The liquidator liquidates the borrowers collateral. * The collateral seized is transferred to the liquidator. * @param borrower The borrower of this pToken to be liquidated * @param pTokenCollateral The market in which to seize collateral from the borrower * @param repayAmount The amount of the underlying borrowed asset to repay * @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount. */ function _liquidateBorrow( address pTokenCollateral, address borrower, uint256 chainId, uint256 repayAmount, address route, address loanMarketAsset, address masterLoanMarketAsset ) internal virtual returns (bool); function _liquidateCalculateSeizeTokens( address pTokenCollateral, uint256 chainId, uint256 actualRepayAmount, address loanMarketAsset ) internal view virtual returns (uint256); function _liquidateBorrowAllowed( address pTokenCollateral, address borrower, uint256 chainId, uint256 repayAmount, address loanMarketAsset ) internal virtual returns (bool); function _satelliteLiquidateBorrow( uint256 chainId, address borrower, address liquidator, uint256 seizeTokens, address pTokenCollateral, address route ) internal virtual; function _enterLoanMarket( uint256 chainId, address loanMarketAsset, address borrower ) internal virtual returns (bool); function _exitLoanMarket( uint256 chainId, address loanMarketAsset, address borrower ) internal virtual returns (bool); }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.4; abstract contract MasterEvents { event NewCollateralBalance( address indexed user, uint256 indexed chainId, address indexed collateral ); event CollateralDeposited( address indexed user, uint256 indexed chainId, address indexed collateral, uint256 balance, uint256 amountDeposited, uint256 totalSupply ); event CollateralWithdrawn( address indexed user, uint256 indexed chainId, address indexed collateral, uint256 balance, uint256 amountWithdrawn, uint256 totalSupply ); event CollateralWithdrawalRejection( address indexed user, uint256 indexed chainId, address indexed collateral, uint256 balance, uint256 amount, uint256 shortfall ); event LoanApproved( address indexed user, uint256 balance, uint256 amount, uint256 totalBorrows, address loanMarketAsset ); event LoanRejected( address indexed user, uint256 balance, uint256 amount, uint256 shortfall, address loanMarketAsset ); event LoanRepaid( address indexed user, uint256 balance, uint256 amountRepaid, uint256 totalBorrows, address loanMarketAsset ); /// @notice Emitted when an account enters a deposit market event CollateralMarketEntered(uint256 chainId, address token, address borrower); event CollateralMarketExited(uint256 chainId, address token, address borrower); event LoanMarketEntered(uint256 chainId, address loanMarketAsset, address borrower); event LoanMarketExited(uint256 chainId, address loanMarketAsset, address borrower); event ReceiveFromChain(uint256 _srcChainId, address _fromAddress); /// @notice Event emitted when a borrow is liquidated event LiquidateBorrow( address liquidator, address borrower, uint256 repayAmount, address cTokenCollateral, uint256 seizeTokens ); // Master Admin Events event MarketPaused(uint256 chainId, address token, bool isPaused); event LoanMarketPaused(uint256 chainId, address loanMarketAsset, bool isPaused); event AddChain(uint256 chainId); event ChangeOwner(address newOwner); event ChangeMiddleLayer(address oldMid, address newMid); event CollateralMarketListed( uint256 indexed chainId, address indexed token, bool listed ); event LoanMarketListed( uint256 indexed chainId, address indexed loanMarketAsset, bool listed ); event ChangeLiqIncentive(uint256 newLiqIncentive); event ChangeCloseFactor(uint256 newCloseFactor); event ChangeFactorDecimals(uint8 newFactorDecimals); event ChangeCollateralFactor(uint256 newCollateralFactor); event ChangeProtocolSeizeShare(uint256 newProtocolSeizeShare); event AccountLiquidity(uint256 collateral, uint256 borrowPlusEffects); event MessageFailed(bytes data); }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "../interfaces/IHelper.sol"; import "./interfaces/IMaster.sol"; import "./MasterModifiers.sol"; import "./MasterEvents.sol"; abstract contract MasterMessageHandler is IMaster, MasterModifiers, MasterEvents { // slither-disable-next-line assembly function _satelliteLiquidateBorrow( uint256 chainId, address borrower, address liquidator, uint256 seizeTokens, address pTokenCollateral, address route ) internal virtual override { bytes memory payload = abi.encode( uint256(0), IHelper.SLiquidateBorrow( IHelper.Selector.SATELLITE_LIQUIDATE_BORROW, borrower, liquidator, seizeTokens, pTokenCollateral ) ); bytes32 metadata = ecc.preRegMsg(payload, msg.sender); assembly { mstore(add(payload, 0x20), metadata) } middleLayer.msend{value: msg.value}( chainId, payload, // bytes payload payable(msg.sender), // refund address route ); } // pass in the erc20 prevBalance, newBalance /// @dev Update the collateral balance for the given arguments /// @notice This will come from the satellite chain- the approve models function masterDeposit( IHelper.MDeposit memory params, bytes32 metadata, uint256 chainId ) external payable onlyMid() { if (!ecc.preProcessingValidation(abi.encode(params), metadata)) revert EccMessageAlreadyProcessed(); if (!ecc.flagMsgValidated(abi.encode(params), metadata)) revert EccFailedToValidate(); if (!_addToMarket(params.pToken, chainId, params.user)) { // error } // do not accept new deposits on a paused market if (markets[chainId][params.pToken].isPaused) revert MarketIsPaused(); if (collateralBalances[chainId][params.user][params.pToken] == 0) { _addToMarket(params.pToken, chainId, params.user); emit NewCollateralBalance(params.user, chainId, params.pToken); } collateralBalances[chainId][params.user][params.pToken] += params.amountIncreased; markets[chainId][params.pToken].totalSupply += params.amountIncreased; emit CollateralDeposited( params.user, chainId, params.pToken, collateralBalances[chainId][params.user][params.pToken], params.amountIncreased, markets[chainId][params.pToken].totalSupply ); // fallback to satellite to report receipt } // slither-disable-next-line assembly function borrowAllowed( IHelper.MBorrowAllowed memory params, bytes32 metadata, uint256 chainId, address fallbackAddress ) external payable onlyMid() { if (!ecc.preProcessingValidation(abi.encode(params), metadata)) revert EccMessageAlreadyProcessed(); if (!ecc.flagMsgValidated(abi.encode(params), metadata)) revert EccFailedToValidate(); _accrueInterestOnSingleLoanMarket(chainId, params.loanMarketAsset); (, uint256 shortfall) = _getHypotheticalAccountLiquidity( params.user, address(0), 0, params.borrowAmount ); //if approved, update the balance and fire off a return message // slither-disable-next-line incorrect-equality if (shortfall == 0) { (uint256 _accountLoanMarketBorrows, ) = _borrowBalanceStored(params.user, chainId, params.loanMarketAsset); if (_accountLoanMarketBorrows == 0) { require( _enterLoanMarket( chainId, params.loanMarketAsset, params.user ), "ENTER_LOAN_MARKET_FAILED" ); } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) accountLoanMarketBorrows[params.user][chainId][params.loanMarketAsset].principal = _accountLoanMarketBorrows + params.borrowAmount; accountLoanMarketBorrows[params.user][chainId][params.loanMarketAsset].interestIndex = loanMarkets[chainId][params.loanMarketAsset].borrowIndex; uint256 totalBorrows = loanMarkets[chainId][params.loanMarketAsset].totalBorrows; loanMarkets[chainId][params.loanMarketAsset].totalBorrows = totalBorrows + params.borrowAmount; bytes memory payload = abi.encode( uint256(0), IHelper.FBBorrow( IHelper.Selector.FB_BORROW, params.user, params.borrowAmount, params.loanMarketAsset ) ); bytes32 _metadata = ecc.preRegMsg(payload, params.user); assembly { mstore(add(payload, 0x20), _metadata) } middleLayer.msend{ value: msg.value }( chainId, payload, // bytes payload payable(params.user), // refund address fallbackAddress ); emit LoanApproved( params.user, accountLoanMarketBorrows[params.user][chainId][params.loanMarketAsset].principal, params.borrowAmount, totalBorrows, params.loanMarketAsset ); } else { emit LoanRejected( params.user, accountLoanMarketBorrows[params.user][chainId][params.loanMarketAsset].principal, params.borrowAmount, shortfall, params.loanMarketAsset ); } } function masterRepay( IHelper.MRepay memory params, bytes32 metadata, uint256 chainId ) external payable onlyMid() { if (!ecc.preProcessingValidation(abi.encode(params), metadata)) revert EccMessageAlreadyProcessed(); if (!ecc.flagMsgValidated(abi.encode(params), metadata)) revert EccFailedToValidate(); _accrueInterestOnSingleLoanMarket(chainId, params.loanMarketAsset); (uint256 _accountLoanMarketBorrows,) = _borrowBalanceStored(params.borrower, chainId, params.loanMarketAsset); if (_accountLoanMarketBorrows < params.amountRepaid) revert RepayTooMuch(params.amountRepaid, _accountLoanMarketBorrows); loanMarkets[chainId][params.loanMarketAsset].totalBorrows -= params.amountRepaid; accountLoanMarketBorrows[params.borrower][chainId][params.loanMarketAsset].principal = _accountLoanMarketBorrows - params.amountRepaid; if (accountLoanMarketBorrows[params.borrower][chainId][params.loanMarketAsset].principal == 0) { bool exitedLoanMarket = _exitLoanMarket(chainId, params.loanMarketAsset, params.borrower); require(exitedLoanMarket, "EXIT_LOAN_MARKET_FAILED"); } emit LoanRepaid( params.borrower, accountLoanMarketBorrows[params.borrower][chainId][params.loanMarketAsset].principal, params.amountRepaid, loanMarkets[chainId][params.loanMarketAsset].totalBorrows, params.loanMarketAsset ); // TODO: fallback to satellite to report receipt } // slither-disable-next-line assembly function redeemAllowed( IHelper.MRedeemAllowed memory params, bytes32 metadata, uint256 chainId, address fallbackAddress ) external payable onlyMid() { if (!ecc.preProcessingValidation(abi.encode(params), metadata)) revert EccMessageAlreadyProcessed(); if (!ecc.flagMsgValidated(abi.encode(params), metadata)) revert EccFailedToValidate(); _accrueInterestOnAllLoanMarkets(params.user); //calculate hypothetical liquidity for the user //make sure we also check that the redeem isn't more than what's deposited // bool approved = true; (, uint256 shortfall) = _getHypotheticalAccountLiquidity( params.user, params.pToken, params.amount, 0 ); //if approved, update the balance and fire off a return message // slither-disable-next-line incorrect-equality if (shortfall == 0) { if (collateralBalances[chainId][params.user][params.pToken] == params.amount) { bool success = _exitMarket(chainId, params.pToken, params.user); require(success, "EXIT_COLLATERAL_MARKET_FAILED"); } collateralBalances[chainId][params.user][params.pToken] -= params.amount; markets[chainId][params.pToken].totalSupply -= params.amount; bytes memory payload = abi.encode( uint256(0), IHelper.FBRedeem( IHelper.Selector.FB_REDEEM, params.pToken, params.user, params.amount ) ); bytes32 _metadata = ecc.preRegMsg(payload, params.user); assembly { mstore(add(payload, 0x20), _metadata) } middleLayer.msend{value: msg.value}( chainId, payload, // bytes payload payable(params.user), // refund address fallbackAddress ); emit CollateralWithdrawn( params.user, chainId, params.pToken, collateralBalances[chainId][params.user][params.pToken], params.amount, markets[chainId][params.pToken].totalSupply ); } else { emit CollateralWithdrawalRejection( params.user, chainId, params.pToken, collateralBalances[chainId][params.user][params.pToken], params.amount, shortfall ); } } }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "./interfaces/IMaster.sol"; import "./MasterEvents.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; import "./irm/IRMStorage.sol"; import "../util/CommonErrors.sol"; abstract contract MasterInternals is IMaster, MasterEvents { /** * @notice Return the borrow balance of account based on stored data * @param account The address whose balance should be calculated * @return (error code, the calculated balance or 0 if error code is non-zero) */ function _borrowBalanceStored( address account, uint256 chainId, address loanMarketAsset ) internal view virtual override returns (uint256, uint256) { /* Note: we do not assert that the market is up to date */ BorrowSnapshot storage borrowSnapshot = accountLoanMarketBorrows[account][chainId][loanMarketAsset]; /* If borrowBalance = 0 then borrowIndex is likely also 0. * Rather than failing the calculation with a division by 0, we immediately return 0 in this case. */ if (borrowSnapshot.principal == 0) { return (0, 0); } /* Calculate new borrow balance using the interest index: * recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex */ uint256 borrowIndex = loanMarkets[chainId][loanMarketAsset].borrowIndex; uint256 principalTimesIndex = borrowSnapshot.principal * borrowIndex; uint256 result = principalTimesIndex / borrowSnapshot.interestIndex; return (result, borrowSnapshot.principal); } function _accrueInterestOnSingleLoanMarket( uint256 chainId, address loanMarketAsset ) internal virtual override { if (accrualBlockNumber != block.number) { LoanMarket memory loanMarket = loanMarkets[chainId][loanMarketAsset]; uint256 totalBorrowsNew; uint256 borrowIndexNew; uint256 totalReservesNew; { uint256 accrualBlockNumberPrior = accrualBlockNumber; uint256 borrowIndexPrior = loanMarket.borrowIndex; uint256 reservesPrior = loanMarket.totalReserves; uint256 borrowsPrior = loanMarket.totalBorrows; uint256 borrowRatePrior = interestRateModel.setBorrowRate(); uint256 blockDelta = block.number - accrualBlockNumberPrior; uint256 simpleInterestFactor = borrowRatePrior * blockDelta; uint8 normalizeFactor = IRMStorage(address(interestRateModel)).borrowInterestRateDecimals(); uint256 interestAccumulated = (simpleInterestFactor * borrowsPrior) / 10**normalizeFactor; totalBorrowsNew = interestAccumulated + borrowsPrior; borrowIndexNew = (simpleInterestFactor * borrowIndexPrior) / 10**normalizeFactor + borrowIndexPrior; totalReservesNew = (reserveFactor * interestAccumulated) / 10**normalizeFactor + reservesPrior; } ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) accrualBlockNumber = block.number; loanMarket.borrowIndex = borrowIndexNew; loanMarket.totalBorrows = totalBorrowsNew; loanMarket.totalReserves = totalReservesNew; loanMarkets[chainId][loanMarketAsset] = loanMarket; // emit AccrueInterest(interestAccumulated, borrowIndexNew, totalBorrowsNew); } } function _accrueInterestOnAllLoanMarkets( address account ) internal virtual override { LoanMarketIndex[] memory _accountLoanMarketIndicesForBorrower = accountLoanMarketIndices[account]; uint256 numAccountLoanMarketsIndicesForBorrower = _accountLoanMarketIndicesForBorrower.length; for (uint256 i; i < numAccountLoanMarketsIndicesForBorrower; i++) { if (accrualBlockNumber != block.number) { _accrueInterestOnSingleLoanMarket( _accountLoanMarketIndicesForBorrower[i].chainId, _accountLoanMarketIndicesForBorrower[i].loanMarketAsset ); // emit AccrueInterest(interestAccumulated, borrowIndexNew, totalBorrowsNew); } } } /** * @notice Add the market to the borrower's "assets in" for liquidity calculations * @param token The market to enter * @param chainId The chainId * @param borrower The address of the account to modify */ // slither-disable-next-line low-level-calls function _addToMarket( address token, uint256 chainId, address borrower ) internal virtual override returns (bool) { if (accountMembership[borrower][chainId][token]) { return true; } if (!markets[chainId][token].isListed) { return false; } accountMembership[borrower][chainId][token] = true; CollateralMarket memory market = CollateralMarket({ token: token, chainId: chainId }); accountCollateralMarkets[borrower].push(market); emit CollateralMarketEntered(market.chainId, market.token, borrower); return true; } /* * @notice Removes asset from sender's account liquidity calculation * @dev Sender must not be providing necessary collateral for an outstanding borrow. * @param pTokenAddress The address of the asset to be removed */ function _exitMarket(uint256 chainId, address token, address user) internal override returns (bool) { Market storage marketToExit = markets[chainId][token]; if (!marketToExit.isListed) { return false; } if (!accountMembership[user][chainId][token]) { return true; } accountMembership[user][chainId][token] = false; CollateralMarket[] memory _accountCollateralMarkets = accountCollateralMarkets[user]; uint256 numAccountCollateralMarkets = _accountCollateralMarkets.length; uint256 accountCollateralMarketIndex = numAccountCollateralMarkets; for (uint256 i = 0; i < numAccountCollateralMarkets; i++) { if (_accountCollateralMarkets[i].token == token && _accountCollateralMarkets[i].chainId == chainId) { accountCollateralMarketIndex = i; break; } } // We *must* have found the asset in the list or our redundant data structure is broken - TODO: can this never be reached? if (accountCollateralMarketIndex == numAccountCollateralMarkets) revert NotInMarket(chainId, token); accountCollateralMarkets[user][accountCollateralMarketIndex] = accountCollateralMarkets[user][numAccountCollateralMarkets - 1]; accountCollateralMarkets[user].pop(); emit CollateralMarketExited(chainId, token, user); return true; } /** * @notice Get a snapshot of the account's balance, and the cached exchange rate * @dev This is used by risk engine to more efficiently perform liquidity checks. * @param user Address of the account to snapshot * @param chainId metadata of the ptoken * @param token metadata of the ptoken * @return (possible error, token balance, exchange rate) */ function _getAccountSnapshot( address user, uint256 chainId, address token ) internal view virtual override returns (uint256, uint256) { uint256 pTokenBalance = collateralBalances[chainId][user][token]; uint256 exchangeRate = markets[chainId][token].initialExchangeRate; return (pTokenBalance, exchangeRate); } /** * @dev Local vars for avoiding stack-depth limits in calculating account liquidity. * Note that `pTokenBalance` is the number of pTokens the account owns in the market, * whereas `borrowBalance` is the amount of underlying that the account has borrowed. */ struct AccountLiquidityLocalVars { uint256 sumCollateral; uint256 sumBorrowPlusEffects; uint256 pTokenBalance; uint256 borrowBalance; uint256 collateralFactor; uint256 exchangeRate; uint256 oraclePrice; uint256 oracleDecimals; uint256 tokensToDenom; } function _getHypotheticalAccountLiquidity( address account, address pTokenModify, uint256 redeemTokens, uint256 borrowAmount ) internal view virtual override returns (uint256, uint256) { AccountLiquidityLocalVars memory vars; // Holds all our calculation results // FIXME: Break out LoanMarket and CollateralMarket calculations into separate functions. /* LOAN MARKET CALCULATION */ LoanMarketIndex[] memory _accountLoanMarketIndicesForBorrower = accountLoanMarketIndices[account]; uint256 numAccountLoanMarketsIndicesForBorrower = _accountLoanMarketIndicesForBorrower.length; for (uint256 i; i < numAccountLoanMarketsIndicesForBorrower; i++) { (uint256 borrowBalanceStored,) = _borrowBalanceStored( account, _accountLoanMarketIndicesForBorrower[i].chainId, _accountLoanMarketIndicesForBorrower[i].loanMarketAsset ); vars.sumBorrowPlusEffects += borrowBalanceStored; } /* END LOAN MARKET CALCULATION */ /* COLLATERAL MARKET CALCULATION */ // For each asset the account is in CollateralMarket[] memory assets = accountCollateralMarkets[account]; if (assets.length == 0) revert AccountNoAssets(account); // slither-disable-next-line uninitialized-local for (uint256 i; i < assets.length; i++) { CollateralMarket memory asset = assets[i]; if (!markets[asset.chainId][asset.token].isListed) { continue; } (vars.pTokenBalance, vars.exchangeRate) = _getAccountSnapshot( account, asset.chainId, asset.token ); // Unlike prime protocol, getUnderlyingPrice is relatively expensive because we use ChainLink as our primary price feed. // If user has no supply / borrow balance on this asset, and user is not redeeming / borrowing this asset, skip it. // slither-disable-next-line incorrect-equality if (vars.pTokenBalance == 0) { continue; } uint256 precision = markets[asset.chainId][asset.token].decimals; uint256 multiplier = 10**precision; // TODO: Logic check this - want borrow/redeem operations to get LTV ratio based on PUSD, otherwise, want liquidations to not be affected // Liquidate: getAbsMaxLtvRatio - don't want to liquidate based on PUSD price if (borrowAmount == 0 && redeemTokens == 0) { vars.collateralFactor = collateralRatioModel.getAbsMaxLtvRatio(asset.chainId, asset.token); // liquidate } else if (markets[asset.chainId][asset.token].isPaused && borrowAmount > 0) { // if borrow and market paused, do not count towards acc liquidity continue; } else { // borrow/redeem vars.collateralFactor = collateralRatioModel.getCurrentMaxLtvRatio(asset.chainId, asset.token); } // FIXME: using hard coded price of 1 (vars.oraclePrice, vars.oracleDecimals) = oracle.getUnderlyingPrice(asset.chainId, asset.token); if (vars.oraclePrice == 0) revert InvalidPrice(); // Pre-compute a conversion factor from tokens -> usp (should be 10e18) vars.tokensToDenom = ((vars.collateralFactor * vars.exchangeRate) / 10**factorDecimals); vars.tokensToDenom = (vars.tokensToDenom * vars.oraclePrice) / 10**vars.oracleDecimals; // sumCollateral += tokensToDenom * pTokenBalance vars.sumCollateral = (vars.tokensToDenom * vars.pTokenBalance) / multiplier + vars.sumCollateral; if (asset.token == pTokenModify) { // redeem effect // sumBorrowPlusEffects += tokensToDenom * redeemTokens vars.sumBorrowPlusEffects += (vars.tokensToDenom * redeemTokens) / multiplier; /* normalize */ } } /* COLLATERAL MARKET CALCULATION */ // //get the multiplier and the oracle price from the loanAgent // // Read the balances and exchange rate from the pToken // (vars.pTokenBalance, vars.exchangeRate) = asset.getAccountSnapshot( // account // ); // // sumBorrowPlusEffects += oraclePrice * borrowBalance // borrow effect // sumBorrowPlusEffects += oraclePrice * borrowAmount vars.sumBorrowPlusEffects += borrowAmount; // These are safe, as the underflow condition is checked first if (vars.sumCollateral > vars.sumBorrowPlusEffects) { return (vars.sumCollateral - vars.sumBorrowPlusEffects, 0); } else { return (0, vars.sumBorrowPlusEffects - vars.sumCollateral); } } function _liquidateCalculateSeizeTokens( address pTokenCollateral, uint256 chainId, uint256 actualRepayAmount, address loanMarketAsset ) internal view virtual override returns (uint256) { /* TODO: Read oracle prices for borrowed and collateral markets */ // PUSD Price uint8 pusdDecimals = ERC20(loanMarketAsset).decimals(); uint256 priceBorrowed = 10**pusdDecimals; (uint256 priceCollateral, ) = oracle.getUnderlyingPrice(chainId, pTokenCollateral); if (priceCollateral == 0 || priceBorrowed == 0) revert InvalidPrice(); uint8 pTokenDecimals = markets[chainId][pTokenCollateral].decimals; uint256 pusdMultiplier = 10**pusdDecimals; uint256 pTokenMultiplier = 10**pTokenDecimals; uint256 exchangeRate = markets[chainId][pTokenCollateral].initialExchangeRate; uint256 incentive; // liquidityIncentive, and CollateralFactor should be customized for each market if(pusdDecimals >= factorDecimals){ incentive = liquidityIncentive*10**(pusdDecimals - factorDecimals); } else{ incentive = liquidityIncentive/10**(factorDecimals - pusdDecimals); } /* * Get the exchange rate and calculate the number of collateral tokens to seize: * seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral * seizeTokens = seizeAmount / exchangeRate * = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate) */ uint256 seizeTokensInPusd = (actualRepayAmount * (pusdMultiplier + incentive) * priceBorrowed) / (pusdMultiplier**2); uint256 pTokenExchangeRate = priceCollateral * pusdMultiplier / priceBorrowed; uint256 seizeTokens = seizeTokensInPusd * pTokenExchangeRate / pusdMultiplier; return seizeTokens; } function _liquidateBorrowAllowed( address pTokenCollateral, address borrower, uint256 chainId, uint256 repayAmount, address loanMarketAsset ) internal view virtual override returns (bool) { if (!markets[chainId][pTokenCollateral].isListed) { return false; } /* The borrower must have shortfall in order to be liquidatable */ (, uint256 shortfall) = _getHypotheticalAccountLiquidity( borrower, address(0), 0, 0 ); // slither-disable-next-line incorrect-equality if (shortfall == 0) { return false; } /* The liquidator may not repay more than what is allowed by the closeFactor */ (uint256 borrowBalance, ) = _borrowBalanceStored( borrower, chainId, loanMarketAsset ); uint256 maxClose = (closeFactor * borrowBalance) / 10**factorDecimals; if (repayAmount > maxClose) { return false; } return true; } struct RepayBorrowLocalVars { uint256 repayAmount; uint256 borrowerIndex; uint256 accountBorrows; uint256 accountBorrowsNew; uint256 totalBorrowsNew; uint256 actualRepayAmount; } function _repayBorrowFresh( address borrower, uint256 repayAmount /*override*/, uint256 chainId, address loanMarketAsset ) internal virtual returns (uint256) { // slither-disable-next-line uninitialized-local RepayBorrowLocalVars memory vars; /* We remember the original borrowerIndex for verification purposes */ vars.borrowerIndex = accountLoanMarketBorrows[borrower][chainId][loanMarketAsset].interestIndex; /* We fetch the amount the borrower owes, with accumulated interest */ (vars.accountBorrows, ) = _borrowBalanceStored( borrower, chainId, loanMarketAsset ); /* If repayAmount == -1, repayAmount = accountBorrows */ // As of Solidity v0.8 Explicit conversions between literals and an integer type T are only allowed if the literal lies between type(T).min and type(T).max. In particular, replace usages of uint(-1) with type(uint).max. // type(uint).max vars.actualRepayAmount = repayAmount == type(uint256).max ? vars.accountBorrows : repayAmount; ///////////////////////// // EFFECTS & INTERACTIONS // (No safe failures beyond this point) /* * We call doTransferIn for the payer and the repayAmount * Note: The pToken must handle variations between ERC-20 and ETH underlying. * On success, the pToken holds an additional repayAmount of cash. * doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred. * it returns the amount actually transferred, in case of a fee. */ // vars.actualRepayAmount = doTransferIn(payer, vars.repayAmount); // TODO: Handle this in lz call // PUSDAddress.burnFrom(/*msg.sender*/ -> payer, vars.repayAmount); // burn the pusd // vars.actualRepayAmount = vars.repayAmount; /* * We calculate the new borrower and total borrow balances, failing on underflow: * accountBorrowsNew = accountBorrows - actualRepayAmount * totalBorrowsNew = totalBorrows - actualRepayAmount */ if ( vars.accountBorrows < vars.actualRepayAmount ) revert RepayTooMuch(vars.actualRepayAmount, vars.accountBorrows); LoanMarket memory loanMarket = loanMarkets[chainId][loanMarketAsset]; vars.accountBorrowsNew = vars.accountBorrows - vars.actualRepayAmount; vars.totalBorrowsNew = loanMarket.totalBorrows - vars.actualRepayAmount; accountLoanMarketBorrows[borrower][chainId][loanMarketAsset].principal = vars.accountBorrowsNew; accountLoanMarketBorrows[borrower][chainId][loanMarketAsset].interestIndex = loanMarket.borrowIndex; loanMarkets[chainId][loanMarketAsset].totalBorrows = vars.totalBorrowsNew; /* We emit a RepayBorrow event */ // emit RepayBorrow( // payer, // borrower, // vars.actualRepayAmount, // vars.accountBorrowsNew, // vars.totalBorrowsNew // ); return vars.actualRepayAmount; } function _seizeAllowed() internal virtual returns (bool) { // return seizeGuardianPaused; } function _liquidateBorrow( address pTokenCollateral, address borrower, uint256 chainId, uint256 repayAmount, address route, address loanMarketAsset, address masterLoanMarketAsset ) internal virtual override returns (bool) { /* Fail if liquidate not allowed */ if (!_liquidateBorrowAllowed( pTokenCollateral, borrower, chainId, repayAmount, loanMarketAsset ) ) revert LiquidateDisallowed(); /* Fail if repayAmount = 0 */ if (repayAmount == 0) revert ExpectedRepayAmount(); /* Fail if repayAmount = -1 */ // NOTE: What case is this check covering? // require(repayAmount != type(uint128).max, "INVALID_CLOSE_AMOUNT_REQUESTED | LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX"); // Fail if repayBorrow fails uint256 actualRepayAmount = _repayBorrowFresh( // msg.sender, // ! payer value unused in function call borrower, repayAmount, chainId, loanMarketAsset ); uint256 protocolSeizeShareAmount = (actualRepayAmount * protocolSeizeShare)/(10**factorDecimals); // We calculate the number of collateral tokens that will be seized uint256 seizeTokens = _liquidateCalculateSeizeTokens( pTokenCollateral, chainId, actualRepayAmount - protocolSeizeShareAmount, loanMarketAsset ); // uint256 collateralBalance = collateralBalances[chainId][borrower][pTokenCollateral]; // Revert if borrower collateral token balance < seizeTokens if (collateralBalances[chainId][borrower][pTokenCollateral] < seizeTokens) revert SeizeTooMuch(); accountLoanMarketBorrows[borrower][chainId][loanMarketAsset].principal += protocolSeizeShareAmount; collateralBalances[chainId][borrower][pTokenCollateral] = collateralBalances[chainId][borrower][pTokenCollateral] - seizeTokens; collateralBalances[chainId][msg.sender][pTokenCollateral] += seizeTokens; markets[chainId][pTokenCollateral].totalSupply -= seizeTokens; loanMarkets[chainId][loanMarketAsset].totalReserves += protocolSeizeShareAmount; ERC20Burnable(masterLoanMarketAsset).burnFrom(msg.sender, actualRepayAmount); // ! If this call fails on satellite we accept a fallback call // ! to revert above state changes _satelliteLiquidateBorrow( chainId, borrower, msg.sender, seizeTokens, pTokenCollateral, route ); /* We emit a LiquidateBorrow event */ // emit LiquidateBorrow( // msg.sender, // borrower, // actualRepayAmount, // address(pTokenCollateral), // seizeTokens // ); return true; } function _enterLoanMarket( uint256 chainId, address loanMarketAsset, address borrower ) internal override returns (bool) { if (isLoanMarketMember[borrower][chainId][loanMarketAsset]) { return true; } if (!loanMarkets[chainId][loanMarketAsset].isListed) { return false; } isLoanMarketMember[borrower][chainId][loanMarketAsset] = true; accountLoanMarketIndices[borrower].push(LoanMarketIndex({ chainId: chainId, loanMarketAsset: loanMarketAsset })); emit LoanMarketEntered(chainId, loanMarketAsset, borrower); return true; } function _exitLoanMarket( uint256 chainId, address loanMarketAsset, address borrower ) internal override returns (bool) { LoanMarket memory loanMarket = loanMarkets[chainId][loanMarketAsset]; if (!loanMarket.isListed) { return false; } if (!isLoanMarketMember[borrower][chainId][loanMarketAsset]) { return true; } isLoanMarketMember[borrower][chainId][loanMarketAsset] = false; LoanMarketIndex[] memory _accountLoanMarketIndicesForBorrower = accountLoanMarketIndices[borrower]; uint256 numAccountLoanMarketIndices = _accountLoanMarketIndicesForBorrower.length; uint256 accountCollateralMarketIndex = numAccountLoanMarketIndices; for (uint256 i = 0; i < numAccountLoanMarketIndices; i++) { if ( _accountLoanMarketIndicesForBorrower[i].loanMarketAsset == loanMarketAsset && _accountLoanMarketIndicesForBorrower[i].chainId == chainId ) { accountCollateralMarketIndex = i; break; } } require(accountCollateralMarketIndex != numAccountLoanMarketIndices, "ASSET_NOT_FOUND"); accountCollateralMarkets[borrower][accountCollateralMarketIndex] = accountCollateralMarkets[borrower][numAccountLoanMarketIndices - 1]; accountCollateralMarkets[borrower].pop(); emit LoanMarketExited(chainId, loanMarketAsset, borrower); return true; } }
//SPDX-License-Identifier: Unlicense pragma solidity ^0.8.4; abstract contract CommonErrors { error AccountNoAssets(address account); error AddressExpected(); error EccMessageAlreadyProcessed(); error EccFailedToValidate(); error ExpectedRedeemAmount(); error ExpectedRepayAmount(); error InsufficientReserves(); error InvalidPayload(); error InvalidPrice(); error MarketExists(); error MarketIsPaused(); error NotInMarket(uint256 chainId, address token); error OnlyAuth(); error OnlyGateway(); error OnlyMiddleLayer(); error OnlyOwner(); error OnlyRoute(); error Reentrancy(); error RepayTooMuch(uint256 repayAmount, uint256 maxAmount); error RedeemTooMuch(); error NotEnoughBalance(address token, address who); error LiquidateDisallowed(); error SeizeTooMuch(); error RouteNotSupported(address route); error TransferFailed(address from, address dest); error TransferPaused(); error UnknownRevert(); }
//SPDX-License-Identifier: Unlicense pragma solidity ^0.8.4; import "./IPrimeOracleGetter.sol"; /** * @title IPrimeOracle * @author Prime * @notice The core interface for the Prime Oracle */ interface IPrimeOracle { /** * @dev Emitted after the price data feed of an asset is updated * @param asset The address of the asset * @param chainId The chainId of the asset * @param feed The price feed of the asset */ event PrimaryFeedUpdated(uint256 chainId, address indexed asset, address indexed feed); /** * @dev Emitted after the price data feed of an asset is updated * @param asset The address of the asset * @param feed The price feed of the asset */ event SecondaryFeedUpdated(uint256 chainId, address indexed asset, address indexed feed); /** * @notice Sets or replaces price feeds of assets * @param asset The addresses of the assets * @param feed The addresses of the price feeds */ function setPrimaryFeed(uint256 chainId, address asset, IPrimeOracleGetter feed) external; /** * @notice Sets or replaces price feeds of assets * @param assets The addresses of the assets * @param feeds The addresses of the price feeds */ function setSecondaryFeeds(uint256[] calldata chainIds, address[] calldata assets, IPrimeOracleGetter[] calldata feeds) external; /** * @return Returns the price of PUSD **/ function getPusdPrice() external view returns (uint256, uint256); /** * @notice Get the underlying price of a cToken asset * @param asset The PToken collateral to get the sasset price of * @param chainId the chainId to get an asset price for * @return The underlying asset price. * Zero means the price is unavailable. */ function getUnderlyingPrice(uint256 chainId, address asset) external view returns (uint256, uint256); /** * @notice Get the underlying borrow price of PUSD * @return The underlying borrow price * Zero means the price is unavailable. */ function getUnderlyingPriceBorrow() external view returns (uint256); }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.4; abstract contract IMiddleLayer { /** * @notice routes and encodes messages for you * @param params - abi.encode() of the struct related to the selector, used to generate _payload * all params starting with '_' are directly sent to the lz 'send()' function */ function msend( uint256 _dstChainId, bytes memory params, address payable _refundAddress, address fallbackAddress ) external payable virtual; function mreceive( uint256 _srcChainId, bytes memory payload ) external virtual; }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.4; interface IECC { struct Metadata { bytes5 soph; // start of payload hash uint40 creation; uint16 nonce; // in case the same exact message is sent multiple times the same block, we increase the nonce in metadata address sender; } function preRegMsg( bytes memory payload, address instigator ) external returns (bytes32 metadata); function preProcessingValidation( bytes memory payload, bytes32 metadata ) external view returns (bool allowed); function flagMsgValidated( bytes memory payload, bytes32 metadata ) external returns (bool); // function rsm(uint256 messagePtr) external returns (bool); }
//SPDX-License-Identifier: Unlicense pragma solidity ^0.8.4; interface IIRM { function setBasisPointsTickSize(uint256 price) external returns (uint256 tickSize); function setBasisPointsUpperTick(uint256 upperTick) external returns (uint256 tick); function setBasisPointsLowerTick(uint256 lowerTick) external returns (uint256 tick); function setPusdLowerTargetPrice(uint256 lowerPrice) external returns (uint256 price); function setPusdUpperTargetPrice(uint256 upperPrice) external returns (uint256 price); function setBorrowRate() external returns (uint256 rate); function setObservationPeriod(uint256 obsPeriod) external returns (uint256); }
//SPDX-License-Identifier: Unlicense pragma solidity ^0.8.4; interface ICRM { function setPusdPriceCeiling(uint256 price) external returns (uint256 ceiling); function setPusdPriceFloor(uint256 price) external returns (uint256 floor); function setAbsMaxLtvRatio(uint256 chainId, address market, uint256 _maxLtvRatio) external; function setCollateralRatioModel(uint256 chainId, address markets, ICRM collateralRatioModel) external; //TODO: put in separate interface function getCurrentMaxLtvRatio(uint256 chainId, address asset) external view returns (uint256 ratio); function getAbsMaxLtvRatio(uint256 chainId, address asset) external view returns (uint256); }
//SPDX-License-Identifier: Unlicense pragma solidity ^0.8.4; /** * @title IPrimeOracleGetter * @author Prime * @notice Interface for the Prime price oracle. **/ interface IPrimeOracleGetter { /** * @dev Emitted after the price data feed of an asset is updated * @param asset The address of the asset * @param feed The price feed of the asset */ event AssetFeedUpdated(uint256 chainId, address indexed asset, address indexed feed); /** * @notice Gets the price feed of an asset * @param asset The addresses of the asset * @return address of asset feed */ function getAssetFeed(uint256 chainId, address asset) external view returns (address); /** * @notice Sets or replaces price feeds of assets * @param asset The addresses of the assets * @param feed The addresses of the price feeds */ function setAssetFeed(uint256 chainId, address asset, address feed) external; /** * @notice Returns the price data in the denom currency * @param quoteToken A token to return price data for * @param denomToken A token to price quoteToken against * @return return price of the asset from the oracle **/ function getAssetPrice(uint256 chainId, address quoteToken, address denomToken) external view returns (uint256); /** * @notice Returns the price data in the denom currency * @param quoteToken A token to return price data for * @param denomToken A token to price quoteToken against * @return return price of the asset from the oracle **/ function getPriceDecimals(uint256 chainId, address quoteToken, address denomToken) external view returns (uint256); }
//SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "./MasterStorage.sol"; import "./interfaces/IMaster.sol"; abstract contract MasterModifiers is MasterStorage, IMaster { modifier onlyOwner() { if(msg.sender != admin) revert OnlyOwner(); _; } modifier onlyMid() { if (IMiddleLayer(msg.sender) != middleLayer) revert OnlyMiddleLayer(); _; } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.4; interface IHelper { enum Selector { MASTER_DEPOSIT, MASTER_REDEEM_ALLOWED, FB_REDEEM, MASTER_REPAY, MASTER_BORROW_ALLOWED, FB_BORROW, SATELLITE_LIQUIDATE_BORROW, PUSD_BRIDGE } // !!!! // @dev // an artificial uint256 param for metadata should be added // after packing the payload // metadata can be generated via call to ecc.preRegMsg() struct MDeposit { Selector selector; // = Selector.MASTER_DEPOSIT address user; address pToken; uint256 previousAmount; uint256 amountIncreased; } struct MRedeemAllowed { Selector selector; // = Selector.MASTER_REDEEM_ALLOWED address pToken; address user; uint256 amount; } struct FBRedeem { Selector selector; // = Selector.FB_REDEEM address pToken; address user; uint256 redeemAmount; } struct MRepay { Selector selector; // = Selector.MASTER_REPAY address borrower; uint256 amountRepaid; address loanMarketAsset; } struct MBorrowAllowed { Selector selector; // = Selector.MASTER_BORROW_ALLOWED address user; uint256 borrowAmount; address loanMarketAsset; } struct FBBorrow { Selector selector; // = Selector.FB_BORROW address user; uint256 borrowAmount; address loanMarketAsset; } struct SLiquidateBorrow { Selector selector; // = Selector.SATELLITE_LIQUIDATE_BORROW address borrower; address liquidator; uint256 seizeTokens; address pTokenCollateral; } struct PUSDBridge { uint8 selector; // = Selector.PUSD_BRIDGE address minter; uint256 amount; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol) pragma solidity ^0.8.0; import "../ERC20.sol"; import "../../../utils/Context.sol"; /** * @dev Extension of {ERC20} that allows token holders to destroy both their own * tokens and those that they have an allowance for, in a way that can be * recognized off-chain (via event analysis). */ abstract contract ERC20Burnable is Context, ERC20 { /** * @dev Destroys `amount` tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 amount) public virtual { _burn(_msgSender(), amount); } /** * @dev Destroys `amount` tokens from `account`, deducting from the caller's * allowance. * * See {ERC20-_burn} and {ERC20-allowance}. * * Requirements: * * - the caller must have allowance for ``accounts``'s tokens of at least * `amount`. */ function burnFrom(address account, uint256 amount) public virtual { _spendAllowance(account, _msgSender(), amount); _burn(account, amount); } }
//SPDX-License-Identifier: Unlicense pragma solidity ^0.8.4; import "../oracle/interfaces/IPrimeOracle.sol"; abstract contract IRMStorage { address public admin; IPrimeOracle public primeOracle; uint256 public borrowInterestRatePerBlock; uint8 public borrowInterestRateDecimals; uint256 public basisPointsTickSize; uint256 public basisPointsUpperTick; uint256 public basisPointsLowerTick; uint256 public pusdLowerTargetPrice; uint256 public pusdUpperTargetPrice; uint256 public lastObservationTimestamp; uint256 public observationPeriod; uint256 public blocksPerYear; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20.sol"; import "./extensions/IERC20Metadata.sol"; import "../../utils/Context.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20, IERC20Metadata { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * The default value of {decimals} is 18. To select a different value for * {decimals} you should overload it. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless this function is * overridden; * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address to, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _transfer(owner, to, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _approve(owner, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom( address from, address to, uint256 amount ) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, _allowances[owner][spender] + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { address owner = _msgSender(); uint256 currentAllowance = _allowances[owner][spender]; require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(owner, spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `sender` to `recipient`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer( address from, address to, uint256 amount ) internal virtual { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(from, to, amount); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; } _balances[to] += amount; emit Transfer(from, to, amount); _afterTokenTransfer(from, to, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; _balances[account] += amount; emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; } _totalSupply -= amount; emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Spend `amount` form the allowance of `owner` toward `spender`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance( address owner, address spender, uint256 amount ) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); unchecked { _approve(owner, spender, currentAllowance - amount); } } } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address from, address to, uint256 amount ) internal virtual {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
//SPDX-License-Identifier: UNLICENSE pragma solidity ^0.8.4; import "./DelegatorErrors.sol"; import "./DelegatorStorage.sol"; contract DelegatorModifiers is DelegatorStorage { // slither-disable-next-line unused-return modifier onlyAdmin() { if (msg.sender != delegatorAdmin) revert DelegatorErrors.AdminOnly(msg.sender); _; } }
//SPDX-License-Identifier: UNLICENSE pragma solidity ^0.8.4; import "./DelegatorUtil.sol"; import "./DelegatorStorage.sol"; contract DelegatorErrors { error DelegatecallFailed(address delegateeAddress, bytes selectorAndParams); error NoValueToFallback(address msgSender, uint256 msgValue); error AdminOnly(address msgSender); }
//SPDX-License-Identifier: UNLICENSE pragma solidity ^0.8.4; import "./DelegatorUtil.sol"; import "./DelegatorStorage.sol"; contract DelegatorEvents { event DelegateeAddressUpdate(address oldDelegateeAddress, address newDelegateeAddress); }
//SPDX-License-Identifier: UNLICENSE pragma solidity ^0.8.4; contract DelegatorStorage { address payable public delegatorAdmin; address public delegateeAddress; }
//SPDX-License-Identifier: UNLICENSE pragma solidity ^0.8.4; import "./DelegatorErrors.sol"; import "./DelegatorStorage.sol"; import "../../util/CommonErrors.sol"; contract DelegatorUtil is DelegatorStorage, CommonErrors { // slither-disable-next-line assembly function _safeRevert(bool success, bytes memory _returnData) internal pure { if (success) return; // If the _res length is less than 68, then the transaction failed silently (without a revert message) if (_returnData.length < 68) revert UnknownRevert(); assembly { // Slice the sighash. _returnData := add(_returnData, 0x04) } revert(abi.decode(_returnData, (string))); // All that remains is the revert string } // ? This is safe so long as the implmentation contract does not have any obvious // ? vulns around calling functions with raised permissions ie admin function callable by anyone // controlled-delegatecall,low-level-calls // slither-disable-next-line all function _delegatecall( bytes memory selector ) internal returns (bytes memory) { (bool success, bytes memory data) = delegateeAddress.delegatecall(selector); assembly { if eq(success, 0) { revert(add(data, 0x20), returndatasize()) } } return data; } function delegateToImplementation(bytes memory selector) public returns (bytes memory) { return _delegatecall(selector); } function _staticcall( bytes memory selector ) public view returns (bytes memory) { (bool success, bytes memory data) = address(this).staticcall(abi.encodeWithSignature("delegateToImplementation(bytes)", selector)); assembly { if eq(success, 0) { revert(add(data, 0x20), returndatasize()) } } return abi.decode(data, (bytes)); } }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
[{"inputs":[{"internalType":"address","name":"_delegateeAddress","type":"address"},{"internalType":"address","name":"middleLayerAddress","type":"address"},{"internalType":"address","name":"eccAddress","type":"address"},{"internalType":"address","name":"crmAddress","type":"address"},{"internalType":"address","name":"irmAddress","type":"address"},{"internalType":"address","name":"primeOracleAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AccountNoAssets","type":"error"},{"inputs":[],"name":"AddressExpected","type":"error"},{"inputs":[{"internalType":"address","name":"msgSender","type":"address"}],"name":"AdminOnly","type":"error"},{"inputs":[{"internalType":"address","name":"delegateeAddress","type":"address"},{"internalType":"bytes","name":"selectorAndParams","type":"bytes"}],"name":"DelegatecallFailed","type":"error"},{"inputs":[],"name":"EccFailedToValidate","type":"error"},{"inputs":[],"name":"EccMessageAlreadyProcessed","type":"error"},{"inputs":[],"name":"ExpectedRedeemAmount","type":"error"},{"inputs":[],"name":"ExpectedRepayAmount","type":"error"},{"inputs":[],"name":"InsufficientReserves","type":"error"},{"inputs":[],"name":"InvalidPayload","type":"error"},{"inputs":[],"name":"InvalidPrice","type":"error"},{"inputs":[],"name":"LiquidateDisallowed","type":"error"},{"inputs":[],"name":"MarketExists","type":"error"},{"inputs":[],"name":"MarketIsPaused","type":"error"},{"inputs":[{"internalType":"address","name":"msgSender","type":"address"},{"internalType":"uint256","name":"msgValue","type":"uint256"}],"name":"NoValueToFallback","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"who","type":"address"}],"name":"NotEnoughBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"token","type":"address"}],"name":"NotInMarket","type":"error"},{"inputs":[],"name":"OnlyAuth","type":"error"},{"inputs":[],"name":"OnlyGateway","type":"error"},{"inputs":[],"name":"OnlyMiddleLayer","type":"error"},{"inputs":[],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"OnlyRoute","type":"error"},{"inputs":[],"name":"RedeemTooMuch","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"uint256","name":"maxAmount","type":"uint256"}],"name":"RepayTooMuch","type":"error"},{"inputs":[{"internalType":"address","name":"route","type":"address"}],"name":"RouteNotSupported","type":"error"},{"inputs":[],"name":"SeizeTooMuch","type":"error"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"dest","type":"address"}],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"TransferPaused","type":"error"},{"inputs":[],"name":"UnknownRevert","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"totalBorrowBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"principal","type":"uint256"}],"name":"BorrowBalanceStored","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldDelegateeAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newDelegateeAddress","type":"address"}],"name":"DelegateeAddressUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool[]","name":"successes","type":"bool[]"}],"name":"EnterMarkets","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rate","type":"uint256"}],"name":"ExchangeRateStored","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"liquidity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shortfall","type":"uint256"}],"name":"GetAccountLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"success","type":"bool"}],"name":"LiquidateBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"success","type":"bool"}],"name":"LiquidateBorrowAllowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LiquidateCalculateSeizeTokens","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"bytes","name":"selector","type":"bytes"}],"name":"_staticcall","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"accountCollateralMarkets","outputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"token","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"accountLoanMarketBorrows","outputs":[{"internalType":"uint256","name":"principal","type":"uint256"},{"internalType":"uint256","name":"interestIndex","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"accountLoanMarketIndices","outputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"loanMarketAsset","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"accountMembership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accrualBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"accrueInterestOnAllLoanMarkets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"loanMarketAsset","type":"address"}],"name":"accrueInterestOnSingleLoanMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"loanMarketAsset","type":"address"}],"name":"borrowBalanceStored","outputs":[{"internalType":"uint256","name":"totalBorrowBalance","type":"uint256"},{"internalType":"uint256","name":"principal","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newCloseFactor","type":"uint256"}],"name":"changeCloseFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"newFactorDecimals","type":"uint8"}],"name":"changeFactorDecimals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newLiqIncentive","type":"uint256"}],"name":"changeLiqIncentive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IMiddleLayer","name":"newMid","type":"address"}],"name":"changeMiddleLayer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"changeOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newProtocolSeizeShare","type":"uint256"}],"name":"changeProtocolSeizeShare","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"closeFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"collateralBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralRatioModel","outputs":[{"internalType":"contract ICRM","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"selector","type":"bytes"}],"name":"delegateToImplementation","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"delegateeAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"delegatorAdmin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"chainIds","type":"uint256[]"}],"name":"enterMarkets","outputs":[{"internalType":"bool[]","name":"successes","type":"bool[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"token","type":"address"}],"name":"exitMarket","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factorDecimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"pTokenModify","type":"address"},{"internalType":"uint256","name":"redeemTokens","type":"uint256"},{"internalType":"uint256","name":"borrowAmount","type":"uint256"}],"name":"getAccountLiquidity","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"shortfall","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interestRateModel","outputs":[{"internalType":"contract IIRM","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"isLoanMarketMember","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pTokenCollateral","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"address","name":"route","type":"address"},{"internalType":"address","name":"loanMarketAsset","type":"address"},{"internalType":"address","name":"masterLoanMarketAsset","type":"address"}],"name":"liquidateBorrow","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pTokenCollateral","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"address","name":"loanMarketAsset","type":"address"}],"name":"liquidateBorrowAllowed","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pTokenCollateral","type":"address"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"uint256","name":"actualRepayAmount","type":"uint256"},{"internalType":"address","name":"loanAssetMarket","type":"address"}],"name":"liquidateCalculateSeizeTokens","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidityIncentive","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"shouldList","type":"bool"}],"name":"listCollateralMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"loanMarketAsset","type":"address"},{"internalType":"bool","name":"shouldList","type":"bool"}],"name":"listLoanMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"loanMarkets","outputs":[{"internalType":"uint256","name":"totalReserves","type":"uint256"},{"internalType":"uint256","name":"totalBorrows","type":"uint256"},{"internalType":"uint256","name":"borrowIndex","type":"uint256"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"bool","name":"isListed","type":"bool"},{"internalType":"bool","name":"isPaused","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"markets","outputs":[{"internalType":"uint256","name":"initialExchangeRate","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"bool","name":"isListed","type":"bool"},{"internalType":"bool","name":"isPaused","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"contract IPrimeOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"loanMarketAsset","type":"address"},{"internalType":"bool","name":"shouldPause","type":"bool"}],"name":"pauseLoanMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"pause","type":"bool"}],"name":"pauseMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"protocolSeizeShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reserveFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newDelegateeAddress","type":"address"}],"name":"setDelegateeAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"loanMarketAsset","type":"address"},{"internalType":"uint256","name":"borrowIndex","type":"uint256"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"supportLoanMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"uint256","name":"initialExchangeRate_","type":"uint256"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"address","name":"underlying_","type":"address"}],"name":"supportMarket","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6080604052624c4b4060078190556302faf08060089081556009919091556304c4b400600a55600b805460ff191690911790553480156200003f57600080fd5b5060405162001e6138038062001e61833981016040819052620000629162000272565b60148054336001600160a01b031991821681179092556000805490911690911790556200008f8662000118565b6040516001600160a01b038087166024830152808616604483015280851660648301528084166084830152821660a48201526200010b90630a2ca2bd60e11b9060c40160408051808303601f190181529190526020810180516001600160e01b0319939093166001600160e01b0393841617905290620001d416565b5050505050505062000331565b6014546001600160a01b031633146200014a576040516304083a8360e21b815233600482015260240160405180910390fd5b6001600160a01b0381166200017257604051635f3ee81760e01b815260040160405180910390fd5b601580546001600160a01b039283166001600160a01b0319821681179092556040805193909116808452602084019290925290917f0ae1e30120787fcf9f31444475f1820ec64c87ab1bbeb8b4faf230eafe35b7d9910160405180910390a150565b60155460405160609160009182916001600160a01b031690620001f9908690620002f3565b600060405180830381855af49150503d806000811462000236576040519150601f19603f3d011682016040523d82523d6000602084013e6200023b565b606091505b509092509050816200024e573d60208201fd5b9392505050565b80516001600160a01b03811681146200026d57600080fd5b919050565b60008060008060008060c087890312156200028c57600080fd5b620002978762000255565b9550620002a76020880162000255565b9450620002b76040880162000255565b9350620002c76060880162000255565b9250620002d76080880162000255565b9150620002e760a0880162000255565b90509295509295509295565b6000825160005b81811015620003165760208186018101518583015201620002fa565b8181111562000326576000828501525b509190910192915050565b611b2080620003416000396000f3fe6080604052600436106102675760003560e01c806389125b7511610144578063d09b4992116100b6578063ee5f9a461161007a578063ee5f9a4614610984578063f3fdb15a146109a4578063f45f7a2d146109c4578063f851a44014610a09578063f90401c214610a29578063f96912dc14610a4957610267565b8063d09b4992146108d8578063d35fb353146108f8578063d566e2ff14610924578063d7e402db14610944578063ed1391331461096457610267565b8063aca0a95211610108578063aca0a9521461077d578063af9395c81461079d578063b4e4d9d2146107bd578063b6aba2b314610878578063b965971514610898578063c8a5fba3146108b857610267565b806389125b75146106b15780638d777c74146106e6578063a1919f93146106fc578063a6f9dae11461073d578063a7a79fc71461075d57610267565b80633f7ecff5116101dd57806360277f90116101a157806360277f90146105525780636c540baf1461058a578063767818b5146105a05780637dc0d1d0146106515780638228d20914610671578063889ba9e11461069157610267565b80633f7ecff5146104ac5780634322b714146104e9578063469a76ab146104ff5780634aa919421461051f578063569aaf421461053257610267565b806319f8d3091161022f57806319f8d309146103e95780631ec02f3a1461040957806330143faa14610436578063328d8ca11461045657806332cf078b1461047657806335f632661461048c57610267565b80630287809d146102e45780630464fdf71461033557806305308b9f146103555780630933c1ed1461036b57806317f8f77914610398575b6015546040516000916001600160a01b0316906102879083903690611294565b600060405180830381855af49150503d80600081146102c2576040519150601f19603f3d011682016040523d82523d6000602084013e6102c7565b606091505b505090506040513d6000823e8180156102de573d82f35b3d82fd5b005b3480156102f057600080fd5b506103226102ff3660046112bc565b600c60209081526000938452604080852082529284528284209052825290205481565b6040519081526020015b60405180910390f35b34801561034157600080fd5b506102e2610350366004611314565b610a69565b34801561036157600080fd5b5061032260085481565b34801561037757600080fd5b5061038b6103863660046113de565b610aea565b60405161032c919061148a565b3480156103a457600080fd5b506103d96103b33660046114bd565b600d60209081526000938452604080852082529284528284209052825290205460ff1681565b604051901515815260200161032c565b3480156103f557600080fd5b506102e2610404366004611502565b610afb565b34801561041557600080fd5b50610429610424366004611585565b610b21565b60405161032c91906115f1565b34801561044257600080fd5b506102e2610451366004611637565b610b9f565b34801561046257600080fd5b506102e2610471366004611502565b610bca565b34801561048257600080fd5b5061032260095481565b34801561049857600080fd5b506102e26104a7366004611654565b610bea565b3480156104b857600080fd5b506104cc6104c736600461166d565b610c09565b604080519283526001600160a01b0390911660208301520161032c565b3480156104f557600080fd5b50610322600a5481565b34801561050b57600080fd5b506102e261051a366004611699565b610c4e565b6103d961052d3660046116f6565b610c94565b34801561053e57600080fd5b506102e261054d36600461177a565b610d4e565b34801561055e57600080fd5b50601554610572906001600160a01b031681565b6040516001600160a01b03909116815260200161032c565b34801561059657600080fd5b5061032260065481565b3480156105ac57600080fd5b506106116105bb366004611795565b600e6020908152600092835260408084209091529082529020805460018201546002909201549091906001600160a01b0381169060ff600160a01b8204811691600160a81b8104821691600160b01b9091041686565b6040805196875260208701959095526001600160a01b039093169385019390935260ff1660608401529015156080830152151560a082015260c00161032c565b34801561065d57600080fd5b50600554610572906001600160a01b031681565b34801561067d57600080fd5b5061038b61068c3660046113de565b610d6f565b34801561069d57600080fd5b50601454610572906001600160a01b031681565b3480156106bd57600080fd5b506106d16106cc3660046117c5565b610e32565b6040805192835260208301919091520161032c565b3480156106f257600080fd5b5061032260075481565b34801561070857600080fd5b506103d96107173660046114bd565b601160209081526000938452604080852082529284528284209052825290205460ff1681565b34801561074957600080fd5b506102e2610758366004611637565b610ecd565b34801561076957600080fd5b506103d961077836600461180b565b610ef4565b34801561078957600080fd5b506102e2610798366004611654565b610f5f565b3480156107a957600080fd5b506102e26107b8366004611502565b610f7e565b3480156107c957600080fd5b506108346107d8366004611795565b60126020908152600092835260408084209091529082529020805460018201546002830154600390930154919290916001600160a01b0381169060ff600160a01b8204811691600160a81b8104821691600160b01b9091041687565b604080519788526020880196909652948601939093526001600160a01b03909116606085015260ff166080840152151560a0830152151560c082015260e00161032c565b34801561088457600080fd5b506102e2610893366004611637565b610f9e565b3480156108a457600080fd5b506106d16108b33660046114bd565b611058565b3480156108c457600080fd5b506103d96108d3366004611795565b6110b8565b3480156108e457600080fd5b506102e26108f3366004611502565b611101565b34801561090457600080fd5b50600b546109129060ff1681565b60405160ff909116815260200161032c565b34801561093057600080fd5b506102e261093f366004611654565b611121565b34801561095057600080fd5b506102e261095f366004611795565b611140565b34801561097057600080fd5b506102e261097f366004611637565b611173565b34801561099057600080fd5b50600454610572906001600160a01b031681565b3480156109b057600080fd5b50600354610572906001600160a01b031681565b3480156109d057600080fd5b506106d16109df3660046114bd565b60106020908152600093845260408085208252928452828420905282529020805460019091015482565b348015610a1557600080fd5b50600054610572906001600160a01b031681565b348015610a3557600080fd5b506104cc610a4436600461166d565b61119a565b348015610a5557600080fd5b50610322610a6436600461185c565b6111b6565b604051602481018690526001600160a01b038086166044830152606482018590528316608482015260ff821660a4820152610ae290630464fdf760e01b9060c4015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611218565b505050505050565b6060610af582611218565b92915050565b610b1b6319f8d30960e01b848484604051602401610aab939291906118a6565b50505050565b60606000610b47631ec02f3a60e01b87878787604051602401610aab94939291906118c7565b905080806020019051810190610b5d919061194b565b91507f85674fb153a3f819a32ccd00a784a8981db5b83b9f3e30bcad1cc15aec17b99282604051610b8e91906115f1565b60405180910390a150949350505050565b6040516001600160a01b0382166024820152610bc69063180a1fd560e11b90604401610aab565b5050565b610b1b63328d8ca160e01b848484604051602401610aab939291906118a6565b60405160248101829052610bc690631afb193360e11b90604401610aab565b60136020528160005260406000208181548110610c2557600080fd5b6000918252602090912060029091020180546001909101549092506001600160a01b0316905082565b6040516001600160a01b038087166024830152604482018690526064820185905260ff84166084830152821660a4820152610ae29063469a76ab60e01b9060c401610aab565b6040516001600160a01b0380891660248301528088166044830152606482018790526084820186905280851660a483015280841660c4830152821660e48201526000908190610cef906325548ca160e11b9061010401610aab565b905080806020019051810190610d0591906119fd565b91507f56eb3f79a1e39a37a57f1516657004298569fe2091f8d86ea994cb2a835d052582604051610d3a911515815260200190565b60405180910390a150979650505050505050565b60405160ff82166024820152610bc690632b4d57a160e11b90604401610aab565b6060600080306001600160a01b031684604051602401610d8f919061148a565b60408051601f198184030181529181526020820180516001600160e01b0316630933c1ed60e01b17905251610dc49190611a1a565b600060405180830381855afa9150503d8060008114610dff576040519150601f19603f3d011682016040523d82523d6000602084013e610e04565b606091505b50909250905081610e16573d60208201fd5b80806020019051810190610e2a9190611a36565b949350505050565b6040516001600160a01b03808616602483015284166044820152606481018390526084810182905260009081908190610ea9906389125b7560e01b9060a4015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152610d6f565b905080806020019051810190610ebf9190611aad565b909890975095505050505050565b6040516001600160a01b0382166024820152610bc69063a6f9dae160e01b90604401610aab565b6040516001600160a01b03808716602483015280861660448301526064820185905260848201849052821660a48201526000908190610f3e9063a7a79fc760e01b9060c401610e72565b905080806020019051810190610f5491906119fd565b979650505050505050565b60405160248101829052610bc69063565054a960e11b90604401610aab565b610b1b63af9395c860e01b848484604051602401610aab939291906118a6565b6014546001600160a01b03163314610fcf576040516304083a8360e21b815233600482015260240160405180910390fd5b6001600160a01b038116610ff657604051635f3ee81760e01b815260040160405180910390fd5b601580546001600160a01b039283166001600160a01b0319821681179092556040805193909116808452602084019290925290917f0ae1e30120787fcf9f31444475f1820ec64c87ab1bbeb8b4faf230eafe35b7d9910160405180910390a150565b6040516001600160a01b0380851660248301526044820184905282166064820152600090819081906110959063b965971560e01b90608401610e72565b9050808060200190518101906110ab9190611aad565b9097909650945050505050565b604051602481018390526001600160a01b038216604482015260009081906110eb9063c8a5fba360e01b90606401610aab565b905080806020019051810190610e2a91906119fd565b610b1b63d09b499260e01b848484604051602401610aab939291906118a6565b60405160248101829052610bc69063d566e2ff60e01b90604401610aab565b604051602481018390526001600160a01b038216604482015261116e9063d7e402db60e01b90606401610aab565b505050565b6040516001600160a01b0382166024820152610bc69063ed13913360e01b90604401610aab565b600f6020528160005260406000208181548110610c2557600080fd5b6040516001600160a01b03808616602483015260448201859052606482018490528216608482015260009081906111f890633e5a44b760e21b9060a401610aab565b90508080602001905181019061120e9190611ad1565b9695505050505050565b60155460405160609160009182916001600160a01b03169061123b908690611a1a565b600060405180830381855af49150503d8060008114611276576040519150601f19603f3d011682016040523d82523d6000602084013e61127b565b606091505b5090925090508161128d573d60208201fd5b9392505050565b8183823760009101908152919050565b6001600160a01b03811681146112b957600080fd5b50565b6000806000606084860312156112d157600080fd5b8335925060208401356112e3816112a4565b915060408401356112f3816112a4565b809150509250925092565b803560ff8116811461130f57600080fd5b919050565b600080600080600060a0868803121561132c57600080fd5b85359450602086013561133e816112a4565b9350604086013592506060860135611355816112a4565b9150611363608087016112fe565b90509295509295909350565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff811182821017156113ae576113ae61136f565b604052919050565b600067ffffffffffffffff8211156113d0576113d061136f565b50601f01601f191660200190565b6000602082840312156113f057600080fd5b813567ffffffffffffffff81111561140757600080fd5b8201601f8101841361141857600080fd5b803561142b611426826113b6565b611385565b81815285602083850101111561144057600080fd5b81602084016020830137600091810160200191909152949350505050565b60005b83811015611479578181015183820152602001611461565b83811115610b1b5750506000910152565b60208152600082518060208401526114a981604085016020870161145e565b601f01601f19169190910160400192915050565b6000806000606084860312156114d257600080fd5b83356114dd816112a4565b92506020840135915060408401356112f3816112a4565b80151581146112b957600080fd5b60008060006060848603121561151757600080fd5b833592506020840135611529816112a4565b915060408401356112f3816114f4565b60008083601f84011261154b57600080fd5b50813567ffffffffffffffff81111561156357600080fd5b6020830191508360208260051b850101111561157e57600080fd5b9250929050565b6000806000806040858703121561159b57600080fd5b843567ffffffffffffffff808211156115b357600080fd5b6115bf88838901611539565b909650945060208701359150808211156115d857600080fd5b506115e587828801611539565b95989497509550505050565b6020808252825182820181905260009190848201906040850190845b8181101561162b57835115158352928401929184019160010161160d565b50909695505050505050565b60006020828403121561164957600080fd5b813561128d816112a4565b60006020828403121561166657600080fd5b5035919050565b6000806040838503121561168057600080fd5b823561168b816112a4565b946020939093013593505050565b600080600080600060a086880312156116b157600080fd5b85356116bc816112a4565b945060208601359350604086013592506116d8606087016112fe565b915060808601356116e8816112a4565b809150509295509295909350565b600080600080600080600060e0888a03121561171157600080fd5b873561171c816112a4565b9650602088013561172c816112a4565b95506040880135945060608801359350608088013561174a816112a4565b925060a088013561175a816112a4565b915060c088013561176a816112a4565b8091505092959891949750929550565b60006020828403121561178c57600080fd5b61128d826112fe565b600080604083850312156117a857600080fd5b8235915060208301356117ba816112a4565b809150509250929050565b600080600080608085870312156117db57600080fd5b84356117e6816112a4565b935060208501356117f6816112a4565b93969395505050506040820135916060013590565b600080600080600060a0868803121561182357600080fd5b853561182e816112a4565b9450602086013561183e816112a4565b9350604086013592506060860135915060808601356116e8816112a4565b6000806000806080858703121561187257600080fd5b843561187d816112a4565b93506020850135925060408501359150606085013561189b816112a4565b939692955090935050565b9283526001600160a01b039190911660208301521515604082015260600190565b6040808252810184905260008560608301825b8781101561190a5782356118ed816112a4565b6001600160a01b03168252602092830192909101906001016118da565b5083810360208501528481526001600160fb1b0385111561192a57600080fd5b8460051b915081866020830137600091016020019081529695505050505050565b6000602080838503121561195e57600080fd5b825167ffffffffffffffff8082111561197657600080fd5b818501915085601f83011261198a57600080fd5b81518181111561199c5761199c61136f565b8060051b91506119ad848301611385565b81815291830184019184810190888411156119c757600080fd5b938501935b838510156119f157845192506119e1836114f4565b82825293850193908501906119cc565b98975050505050505050565b600060208284031215611a0f57600080fd5b815161128d816114f4565b60008251611a2c81846020870161145e565b9190910192915050565b600060208284031215611a4857600080fd5b815167ffffffffffffffff811115611a5f57600080fd5b8201601f81018413611a7057600080fd5b8051611a7e611426826113b6565b818152856020838501011115611a9357600080fd5b611aa482602083016020860161145e565b95945050505050565b60008060408385031215611ac057600080fd5b505080516020909101519092909150565b600060208284031215611ae357600080fd5b505191905056fea264697066735822122070ab916365ffeeb050fc6163f79162e6ad9c43fa0bc0a79549507eae0855d16964736f6c634300080d00330000000000000000000000002c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff000000000000000000000000d9216fcb8ca2ae7d545754ff675fcb479c33dc35000000000000000000000000a1b9f2938ee8a19d71da4228d278da013d5eabdc0000000000000000000000007cf533d95e8578020aa4fa56b680339d0993e7010000000000000000000000008dd8ef9b3eed255734b5dbddfa2b3bf07f28756100000000000000000000000095e14e63a47a9da8c705d38c82e066d7581db51a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000002c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff000000000000000000000000d9216fcb8ca2ae7d545754ff675fcb479c33dc35000000000000000000000000a1b9f2938ee8a19d71da4228d278da013d5eabdc0000000000000000000000007cf533d95e8578020aa4fa56b680339d0993e7010000000000000000000000008dd8ef9b3eed255734b5dbddfa2b3bf07f28756100000000000000000000000095e14e63a47a9da8c705d38c82e066d7581db51a
-----Decoded View---------------
Arg [0] : _delegateeAddress (address): 0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff
Arg [1] : middleLayerAddress (address): 0xd9216fcb8ca2ae7d545754ff675fcb479c33dc35
Arg [2] : eccAddress (address): 0xa1b9f2938ee8a19d71da4228d278da013d5eabdc
Arg [3] : crmAddress (address): 0x7cf533d95e8578020aa4fa56b680339d0993e701
Arg [4] : irmAddress (address): 0x8dd8ef9b3eed255734b5dbddfa2b3bf07f287561
Arg [5] : primeOracleAddress (address): 0x95e14e63a47a9da8c705d38c82e066d7581db51a
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 0000000000000000000000002c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff
Arg [1] : 000000000000000000000000d9216fcb8ca2ae7d545754ff675fcb479c33dc35
Arg [2] : 000000000000000000000000a1b9f2938ee8a19d71da4228d278da013d5eabdc
Arg [3] : 0000000000000000000000007cf533d95e8578020aa4fa56b680339d0993e701
Arg [4] : 0000000000000000000000008dd8ef9b3eed255734b5dbddfa2b3bf07f287561
Arg [5] : 00000000000000000000000095e14e63a47a9da8c705d38c82e066d7581db51a
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|