Contract 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f

Contract Overview

Balance:
0 DEV
Txn Hash Method
Block
From
To
Value [Txn Fee]
0x81854f7a1f9428e349de99a41b98926344a1e1ba00999b8676489c0fa66d1907Support Loan Mar...25726282022-07-29 22:30:06187 days 48 mins ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.005228479
0xd6e4bde3ea95b41069059c5d84acdd3e4e373c9c45102dc184191495e988941fSupport Market25726162022-07-29 22:27:36187 days 51 mins ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.005229484
0x83c29707cdfb7a579ca17f891ba2c92b3938301fd2e594538dda6674151f8981Support Market25726062022-07-29 22:24:54187 days 54 mins ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.005229484
0x1543f244adab6c59961a421e0c5c8c3d3cfb1c9631b1380f62ec06dc3ac45cfcSupport Market25725972022-07-29 22:22:30187 days 56 mins ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.005229484
0x12255ff79fdda94fc90c9b77d9cd4caa0a6b3644ae68b2965dfe1ac214ca7799Support Loan Mar...25725902022-07-29 22:20:54187 days 58 mins ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.005228479
0xd7916e6a7e24cc2f5200dd600f485616225b19b642c49be5291b02fb16583545Support Market25725792022-07-29 22:18:06187 days 1 hr ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.00522868
0x4dc279ea2c9ec73a22abdb0a50783b59b4215a36d9c5f6856371b26ca4033619Support Market25725692022-07-29 22:15:48187 days 1 hr ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.00522868
0x4866dd464b33fa627a9fb31ba5b834050a294115b708476734f7184731e3cbcdSupport Market25725602022-07-29 22:13:00187 days 1 hr ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.005229484
0x90cc996239ebfaaaa944d7832993ee2604684448e7ef0178252b1823e2ce7d7dSupport Market25725522022-07-29 22:10:48187 days 1 hr ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.005229484
0x90c6f4de2ce30784fcac36e0b341ffbc49e1212c6783e1194d5f90ef0ea8148cSupport Loan Mar...25725462022-07-29 22:09:00187 days 1 hr ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.005228479
0x1b2fb78f4d5aadfb0777da914e0cddceca809ec8fa9d94b2b95f0d60c2b3b762Support Market25725382022-07-29 22:06:42187 days 1 hr ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.005229484
0xa75ebf74a58d98d41dd2b598d68e3a35f9555c1dc069d696ead0241c3e8245e7Support Market25725262022-07-29 22:04:12187 days 1 hr ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.00522868
0x6b3d57ad328534ccfa281623bc9adae702fb9940c05a44b3455eff42f50ff741Support Market25725172022-07-29 22:01:54187 days 1 hr ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.005229484
0xb7e15b19e5a33d99cb8656d40c9b06f54fad91f52452b23c4f9e7dc7b49bc747Support Market25725072022-07-29 21:59:24187 days 1 hr ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.005229484
0x6e93e23aa9f3ac59d2a312c220cf13ac02faa2eb2df969aefde6ef20b7a291a7Support Loan Mar...25725002022-07-29 21:57:54187 days 1 hr ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.005229283
0x4c2c1a7dd9cbe39a4cd9a6465be70c339ef81348e7a2fd82c9c9e69e4ffd9cf6Support Market25724902022-07-29 21:55:12187 days 1 hr ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.005230288
0x42388da9bf85edc1584324dba12bdc625e3658d6a1ac2b1567cdcaa220cad70bSupport Market25724792022-07-29 21:52:18187 days 1 hr ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.005230288
0x646b79d4b0fd55055f8817bb327fd8a112f491daf93e60093c7e2af7a2b7d410Support Market25724672022-07-29 21:49:36187 days 1 hr ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.005229484
0xa41505bc5a95cf2de9c27dbfd5789488e266c9f91dc2d6c943e63ad44f4b201aSupport Market25724592022-07-29 21:47:18187 days 1 hr ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV0.005230288
0x160b02b74f7bbeff2817788a57d8afb57b65efa6aecd52e10269fef03e2430050x6080604025724192022-07-29 21:35:24187 days 1 hr ago0x16f4898f47c085c41d7cc6b1dc72b91ea617dcbb IN  Create: MasterStateDelegator0 DEV0.126231216
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xea69e4f572572e89507aa7d433bca096ece32ba2cfee727036db965a817b01d125728502022-07-29 23:27:18186 days 23 hrs ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0xea69e4f572572e89507aa7d433bca096ece32ba2cfee727036db965a817b01d125728502022-07-29 23:27:18186 days 23 hrs ago 0xd9216fcb8ca2ae7d545754ff675fcb479c33dc350x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV
0x29d1f0a9479c028a4e599f5a210a18580af923535421053fec260b8a4bc3b39125728332022-07-29 23:23:36186 days 23 hrs ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0x29d1f0a9479c028a4e599f5a210a18580af923535421053fec260b8a4bc3b39125728332022-07-29 23:23:36186 days 23 hrs ago 0xd9216fcb8ca2ae7d545754ff675fcb479c33dc350x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV
0xc55b5f904557e910593a1b7180cb5d5c2eebff8404ded333ac66f0a8fbdf7a8325728332022-07-29 23:23:36186 days 23 hrs ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0xc55b5f904557e910593a1b7180cb5d5c2eebff8404ded333ac66f0a8fbdf7a8325728332022-07-29 23:23:36186 days 23 hrs ago 0xd9216fcb8ca2ae7d545754ff675fcb479c33dc350x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV
0xfbde4384eda898161466536f7d77a653c0830040cebfd6a383cb261e58fd82bd25727062022-07-29 22:50:30187 days 28 mins ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0xfbde4384eda898161466536f7d77a653c0830040cebfd6a383cb261e58fd82bd25727062022-07-29 22:50:30187 days 28 mins ago 0xd9216fcb8ca2ae7d545754ff675fcb479c33dc350x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0 DEV
0x81854f7a1f9428e349de99a41b98926344a1e1ba00999b8676489c0fa66d190725726282022-07-29 22:30:06187 days 48 mins ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0xd6e4bde3ea95b41069059c5d84acdd3e4e373c9c45102dc184191495e988941f25726162022-07-29 22:27:36187 days 51 mins ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0x83c29707cdfb7a579ca17f891ba2c92b3938301fd2e594538dda6674151f898125726062022-07-29 22:24:54187 days 54 mins ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0x1543f244adab6c59961a421e0c5c8c3d3cfb1c9631b1380f62ec06dc3ac45cfc25725972022-07-29 22:22:30187 days 56 mins ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0x12255ff79fdda94fc90c9b77d9cd4caa0a6b3644ae68b2965dfe1ac214ca779925725902022-07-29 22:20:54187 days 58 mins ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0xd7916e6a7e24cc2f5200dd600f485616225b19b642c49be5291b02fb1658354525725792022-07-29 22:18:06187 days 1 hr ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0x4dc279ea2c9ec73a22abdb0a50783b59b4215a36d9c5f6856371b26ca403361925725692022-07-29 22:15:48187 days 1 hr ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0x4866dd464b33fa627a9fb31ba5b834050a294115b708476734f7184731e3cbcd25725602022-07-29 22:13:00187 days 1 hr ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0x90cc996239ebfaaaa944d7832993ee2604684448e7ef0178252b1823e2ce7d7d25725522022-07-29 22:10:48187 days 1 hr ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0x90c6f4de2ce30784fcac36e0b341ffbc49e1212c6783e1194d5f90ef0ea8148c25725462022-07-29 22:09:00187 days 1 hr ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0x1b2fb78f4d5aadfb0777da914e0cddceca809ec8fa9d94b2b95f0d60c2b3b76225725382022-07-29 22:06:42187 days 1 hr ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0xa75ebf74a58d98d41dd2b598d68e3a35f9555c1dc069d696ead0241c3e8245e725725262022-07-29 22:04:12187 days 1 hr ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0x6b3d57ad328534ccfa281623bc9adae702fb9940c05a44b3455eff42f50ff74125725172022-07-29 22:01:54187 days 1 hr ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0xb7e15b19e5a33d99cb8656d40c9b06f54fad91f52452b23c4f9e7dc7b49bc74725725072022-07-29 21:59:24187 days 1 hr ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0x6e93e23aa9f3ac59d2a312c220cf13ac02faa2eb2df969aefde6ef20b7a291a725725002022-07-29 21:57:54187 days 1 hr ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0x4c2c1a7dd9cbe39a4cd9a6465be70c339ef81348e7a2fd82c9c9e69e4ffd9cf625724902022-07-29 21:55:12187 days 1 hr ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
0x42388da9bf85edc1584324dba12bdc625e3658d6a1ac2b1567cdcaa220cad70b25724792022-07-29 21:52:18187 days 1 hr ago 0x4a661ada7db66885c4f6d712a9b43f77cb2ce24f0x2c343d0f6eeff4e87e3444cd24cfc1eeaf86a5ff0 DEV
[ Download CSV Export 
Loading
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 Source Code Verified (Exact Match)

Contract Name:
MasterStateDelegator

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 31 : MasterStateDelegator.sol
//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()) }
        }
    }
}

File 2 of 31 : MasterState.sol
//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
        );
    }
}

File 3 of 31 : MasterAdmin.sol
//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;
    }
}

File 4 of 31 : IMasterState.sol
//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;
}

File 5 of 31 : MasterStorage.sol
//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;
}

File 6 of 31 : IDelegator.sol
//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);
    }
}

File 7 of 31 : MasterStateDelegatorEvents.sol
//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);
}

File 8 of 31 : IMaster.sol
//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);
}

File 9 of 31 : MasterEvents.sol
//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);
}

File 10 of 31 : MasterMessageHandler.sol
//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
            );

        }
    }
}

File 11 of 31 : MasterInternals.sol
//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;
    }
}

File 12 of 31 : CommonErrors.sol
//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();
}

File 13 of 31 : IPrimeOracle.sol
//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);
}

File 14 of 31 : IMiddleLayer.sol
//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;
}

File 15 of 31 : IECC.sol
//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);
}

File 16 of 31 : IIRM.sol
//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);
}

File 17 of 31 : ICRM.sol
//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);
}

File 18 of 31 : IPrimeOracleGetter.sol
//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);

}

File 19 of 31 : MasterModifiers.sol
//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();
        _;
    }
}

File 20 of 31 : IHelper.sol
// 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;
    }
}

File 21 of 31 : ERC20Burnable.sol
// 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);
    }
}

File 22 of 31 : IRMStorage.sol
//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;
}

File 23 of 31 : ERC20.sol
// 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 {}
}

File 24 of 31 : Context.sol
// 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;
    }
}

File 25 of 31 : IERC20.sol
// 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);
}

File 26 of 31 : IERC20Metadata.sol
// 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);
}

File 27 of 31 : DelegatorModifiers.sol
//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);
        _;
    }
}

File 28 of 31 : DelegatorErrors.sol
//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);
}

File 29 of 31 : DelegatorEvents.sol
//SPDX-License-Identifier: UNLICENSE
pragma solidity ^0.8.4;

import "./DelegatorUtil.sol";
import "./DelegatorStorage.sol";

contract DelegatorEvents {

    event DelegateeAddressUpdate(address oldDelegateeAddress, address newDelegateeAddress);
}

File 30 of 31 : DelegatorStorage.sol
//SPDX-License-Identifier: UNLICENSE
pragma solidity ^0.8.4;

contract DelegatorStorage {

    address payable public delegatorAdmin;

    address public delegateeAddress;
}

File 31 of 31 : DelegatorUtil.sol
//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));
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract ABI

[{"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"}]

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


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading