ETH Price: $3,273.04 (+0.89%)

Contract

0x47a58Bd6D2567ABb3b7d25637a9BE82a1674b37A
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw Hourly ...174237502023-06-06 20:24:11584 days ago1686083051IN
0x47a58Bd6...a1674b37A
0 ETH0.00546934
Withdraw Hourly ...174237442023-06-06 20:22:59584 days ago1686082979IN
0x47a58Bd6...a1674b37A
0 ETH0.0046416531.56339788
Withdraw Hourly ...148923582022-06-02 17:58:48953 days ago1654192728IN
0x47a58Bd6...a1674b37A
0 ETH0.0105601571.91899972
Buy Hourly Bond ...148898072022-06-02 7:57:51954 days ago1654156671IN
0x47a58Bd6...a1674b37A
0 ETH0.0122653269.94129859
Withdraw Hourly ...146205022022-04-20 7:14:07997 days ago1650438847IN
0x47a58Bd6...a1674b37A
0 ETH0.0051581539.75946095
Buy Hourly Bond ...146168852022-04-19 17:20:24997 days ago1650388824IN
0x47a58Bd6...a1674b37A
0 ETH0.0089989946.57938597
Buy Hourly Bond ...144867842022-03-30 9:23:491018 days ago1648632229IN
0x47a58Bd6...a1674b37A
0 ETH0.0064599334.80175767
Buy Hourly Bond ...143541972022-03-09 18:28:311038 days ago1646850511IN
0x47a58Bd6...a1674b37A
0 ETH0.0084443146.86678357
Withdraw Hourly ...143497192022-03-09 1:42:501039 days ago1646790170IN
0x47a58Bd6...a1674b37A
0 ETH0.0042588632.80873787
Withdraw Hourly ...141314812022-02-03 6:45:001073 days ago1643870700IN
0x47a58Bd6...a1674b37A
0 ETH0.0113773377.48430491
Buy Hourly Bond ...141120002022-01-31 6:25:361076 days ago1643610336IN
0x47a58Bd6...a1674b37A
0 ETH0.0153816787.7118625
Withdraw Hourly ...137061962021-11-29 3:06:481139 days ago1638155208IN
0x47a58Bd6...a1674b37A
0 ETH0.0107741176.1755269
Buy Hourly Bond ...134972362021-10-27 4:15:171172 days ago1635308117IN
0x47a58Bd6...a1674b37A
0 ETH0.02523382135.92514741
Buy Hourly Bond ...134922782021-10-26 9:51:321173 days ago1635241892IN
0x47a58Bd6...a1674b37A
0 ETH0.0089936469.86873075
Withdraw Hourly ...134920782021-10-26 9:05:011173 days ago1635239101IN
0x47a58Bd6...a1674b37A
0 ETH0.0141771100.32699167
Buy Hourly Bond ...134615092021-10-21 14:18:171177 days ago1634825897IN
0x47a58Bd6...a1674b37A
0 ETH0.0145408382.9170598
Withdraw Hourly ...134547622021-10-20 13:03:521179 days ago1634735032IN
0x47a58Bd6...a1674b37A
0 ETH0.0064244455
Withdraw Hourly ...134546282021-10-20 12:33:191179 days ago1634733199IN
0x47a58Bd6...a1674b37A
0 ETH0.0070142848.53943667
Withdraw Hourly ...134543652021-10-20 11:32:571179 days ago1634729577IN
0x47a58Bd6...a1674b37A
0 ETH0.0081392569.98442711
Buy Hourly Bond ...133518872021-10-04 9:50:521195 days ago1633341052IN
0x47a58Bd6...a1674b37A
0 ETH0.0083773447.87518707
Withdraw Hourly ...132616652021-09-20 8:57:211209 days ago1632128241IN
0x47a58Bd6...a1674b37A
0 ETH0.0056672444.40335679
Withdraw Hourly ...132298072021-09-15 10:37:221214 days ago1631702242IN
0x47a58Bd6...a1674b37A
0 ETH0.00506635
Withdraw Hourly ...132297522021-09-15 10:23:431214 days ago1631701423IN
0x47a58Bd6...a1674b37A
0 ETH0.0039472830
Withdraw Hourly ...132221962021-09-14 6:28:151215 days ago1631600895IN
0x47a58Bd6...a1674b37A
0 ETH0.0048590138.0788381
Buy Hourly Bond ...131065092021-08-27 8:46:431233 days ago1630054003IN
0x47a58Bd6...a1674b37A
0 ETH0.0107207161.26717341
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Lending

Compiler Version
v0.8.3+commit.8d00100c

Optimization Enabled:
Yes with 5000 runs

Other Settings:
default evmVersion, None license
File 1 of 14 : Lending.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "./Fund.sol";
import "./HourlyBondSubscriptionLending.sol";
import "../libraries/IncentiveReporter.sol";

// TODO activate bonds for lending

/// @title Manage lending for a variety of bond issuers
contract Lending is RoleAware, HourlyBondSubscriptionLending {
    /// mapping issuers to tokens
    /// (in crossmargin, the issuers are tokens  themselves)
    mapping(address => address) public issuerTokens;

    /// In case of shortfall, adjust debt
    mapping(address => uint256) public haircuts;

    /// map of available issuers
    mapping(address => bool) public activeIssuers;

    uint256 constant BORROW_RATE_UPDATE_WINDOW = 60 minutes;

    address public immutable MFI;

    constructor(address _MFI, address _roles) RoleAware(_roles) {
        MFI = _MFI;
    }

    /// Make a issuer available for protocol
    function activateIssuer(address issuer) external {
        activateIssuer(issuer, issuer);
    }

    /// Make issuer != token available for protocol (isol. margin)
    function activateIssuer(address issuer, address token)
        public
        onlyOwnerExecActivator
    {
        activeIssuers[issuer] = true;
        issuerTokens[issuer] = token;
    }

    /// Remove a issuer from trading availability
    function deactivateIssuer(address issuer) external onlyOwnerExecActivator {
        activeIssuers[issuer] = false;
    }

    /// Set lending cap
    function setLendingCap(address issuer, uint256 cap)
        external
        onlyOwnerExecActivator
    {
        lendingMeta[issuer].lendingCap = cap;
    }

    /// Set withdrawal window
    function setWithdrawalWindow(uint256 window) external onlyOwnerExec {
        withdrawalWindow = window;
    }

    function setNormalRatePerPercent(uint256 rate) external onlyOwnerExec {
        normalRatePerPercent = rate;
    }

    function setHighRatePerPercent(uint256 rate) external onlyOwnerExec {
        highRatePerPercent = rate;
    }

    /// Set hourly yield APR for issuer
    function setHourlyYieldAPR(address issuer, uint256 aprPercent)
        external
        onlyOwnerExecActivator
    {
        YieldAccumulator storage yieldAccumulator =
            hourlyBondYieldAccumulators[issuer];

        if (yieldAccumulator.accumulatorFP == 0) {
            uint256 yieldFP = FP48 + (FP48 * aprPercent) / 100 / (24 * 365);
            hourlyBondYieldAccumulators[issuer] = YieldAccumulator({
                accumulatorFP: FP48,
                lastUpdated: block.timestamp,
                hourlyYieldFP: yieldFP
            });
        } else {
            YieldAccumulator storage yA =
                getUpdatedHourlyYield(
                    issuer,
                    yieldAccumulator,
                    RATE_UPDATE_WINDOW
                );
            yA.hourlyYieldFP = (FP48 * (100 + aprPercent)) / 100 / (24 * 365);
        }
    }

    /// @dev how much interest has accrued to a borrowed balance over time
    function applyBorrowInterest(
        uint256 balance,
        address issuer,
        uint256 yieldQuotientFP
    ) external returns (uint256 balanceWithInterest, uint256 accumulatorFP) {
        require(isBorrower(msg.sender), "Not approved call");

        YieldAccumulator storage yA = borrowYieldAccumulators[issuer];
        updateBorrowYieldAccu(yA);
        accumulatorFP = yA.accumulatorFP;

        balanceWithInterest = applyInterest(
            balance,
            accumulatorFP,
            yieldQuotientFP
        );

        uint256 deltaAmount = balanceWithInterest - balance;
        LendingMetadata storage meta = lendingMeta[issuer];
        meta.totalBorrowed += deltaAmount;
    }

    /// @dev view function to get balance with borrowing interest applied
    function viewWithBorrowInterest(
        uint256 balance,
        address issuer,
        uint256 yieldQuotientFP
    ) external view returns (uint256) {
        uint256 accumulatorFP =
            viewCumulativeYieldFP(
                borrowYieldAccumulators[issuer],
                block.timestamp
            );
        return applyInterest(balance, accumulatorFP, yieldQuotientFP);
    }

    /// @dev gets called by router to register if a trader borrows issuers
    function registerBorrow(address issuer, uint256 amount) external {
        require(isBorrower(msg.sender), "Not approved borrower");
        require(activeIssuers[issuer], "Not approved issuer");

        LendingMetadata storage meta = lendingMeta[issuer];
        meta.totalBorrowed += amount;

        getUpdatedHourlyYield(
            issuer,
            hourlyBondYieldAccumulators[issuer],
            BORROW_RATE_UPDATE_WINDOW
        );

        require(
            meta.totalLending >= meta.totalBorrowed,
            "Insufficient lending"
        );
    }

    /// @dev gets called when external sources provide lending
    function registerLend(address issuer, uint256 amount) external {
        require(isLender(msg.sender), "Not an approved lender");
        require(activeIssuers[issuer], "Not approved issuer");
        LendingMetadata storage meta = lendingMeta[issuer];
        addToTotalLending(meta, amount);

        getUpdatedHourlyYield(
            issuer,
            hourlyBondYieldAccumulators[issuer],
            RATE_UPDATE_WINDOW
        );
    }

    /// @dev gets called when external sources pay withdraw their bobnd
    function registerWithdrawal(address issuer, uint256 amount) external {
        require(isLender(msg.sender), "Not an approved lender");
        require(activeIssuers[issuer], "Not approved issuer");
        LendingMetadata storage meta = lendingMeta[issuer];
        subtractFromTotalLending(meta, amount);

        getUpdatedHourlyYield(
            issuer,
            hourlyBondYieldAccumulators[issuer],
            RATE_UPDATE_WINDOW
        );
    }

    /// @dev gets called by router if loan is extinguished
    function payOff(address issuer, uint256 amount) external {
        require(isBorrower(msg.sender), "Not approved borrower");
        lendingMeta[issuer].totalBorrowed -= amount;
    }

    /// @dev get the borrow yield for a specific issuer/token
    function viewAccumulatedBorrowingYieldFP(address issuer)
        external
        view
        returns (uint256)
    {
        YieldAccumulator storage yA = borrowYieldAccumulators[issuer];
        return viewCumulativeYieldFP(yA, block.timestamp);
    }

    function viewAPRPer10k(YieldAccumulator storage yA)
        internal
        view
        returns (uint256)
    {
        uint256 hourlyYieldFP = yA.hourlyYieldFP;

        uint256 aprFP =
            ((hourlyYieldFP * 10_000 - FP48 * 10_000) * 365 days) / (1 hours);

        return aprFP / FP48;
    }

    /// @dev get current borrowing interest per 10k for a token / issuer
    function viewBorrowAPRPer10k(address issuer)
        external
        view
        returns (uint256)
    {
        return viewAPRPer10k(borrowYieldAccumulators[issuer]);
    }

    /// @dev get current lending APR per 10k for a token / issuer
    function viewHourlyBondAPRPer10k(address issuer)
        external
        view
        returns (uint256)
    {
        return viewAPRPer10k(hourlyBondYieldAccumulators[issuer]);
    }

    /// @dev In a liquidity crunch make a fallback bond until liquidity is good again
    function makeFallbackBond(
        address issuer,
        address holder,
        uint256 amount
    ) external {
        require(isLender(msg.sender), "Not an approved lender");
        _makeHourlyBond(issuer, holder, amount);
    }

    /// @dev withdraw an hour bond
    function withdrawHourlyBond(address issuer, uint256 amount) external {
        HourlyBond storage bond = hourlyBondAccounts[issuer][msg.sender];
        super._withdrawHourlyBond(issuer, bond, amount, msg.sender);

        if (bond.amount == 0) {
            delete hourlyBondAccounts[issuer][msg.sender];
        }

        disburse(issuer, msg.sender, amount);

        IncentiveReporter.subtractFromClaimAmount(issuer, msg.sender, amount);
    }

    /// Shut down hourly bond account for `issuer`
    function closeHourlyBondAccount(address issuer) external {
        HourlyBond storage bond = hourlyBondAccounts[issuer][msg.sender];

        uint256 amount = bond.amount;
        super._withdrawHourlyBond(issuer, bond, amount, msg.sender);

        disburse(issuer, msg.sender, amount);

        delete hourlyBondAccounts[issuer][msg.sender];

        IncentiveReporter.subtractFromClaimAmount(issuer, msg.sender, amount);
    }

    /// @dev buy hourly bond subscription
    function buyHourlyBondSubscription(address issuer, uint256 amount)
        external
    {
        require(activeIssuers[issuer], "Not approved issuer");

        collectToken(issuer, msg.sender, amount);

        super._makeHourlyBond(issuer, msg.sender, amount);

        IncentiveReporter.addToClaimAmount(issuer, msg.sender, amount);
    }

    function initBorrowYieldAccumulator(address issuer)
        external
        onlyOwnerExecActivator
    {
        YieldAccumulator storage yA = borrowYieldAccumulators[issuer];
        require(yA.accumulatorFP == 0, "don't re-initialize");

        yA.accumulatorFP = FP48;
        yA.lastUpdated = block.timestamp;
        yA.hourlyYieldFP = FP48 + (FP48 * borrowMinAPR) / 1000 / (365 * 24);
    }

    function setBorrowingFactorPercent(uint256 borrowingFactor)
        external
        onlyOwnerExec
    {
        borrowingFactorPercent = borrowingFactor;
    }

    function issuanceBalance(address issuer)
        internal
        view
        override
        returns (uint256)
    {
        address token = issuerTokens[issuer];
        if (token == issuer) {
            // cross margin
            return IERC20(token).balanceOf(fund());
        } else {
            return lendingMeta[issuer].totalLending - haircuts[issuer];
        }
    }

    function disburse(
        address issuer,
        address recipient,
        uint256 amount
    ) internal {
        uint256 haircutAmount = haircuts[issuer];
        if (haircutAmount > 0 && amount > 0) {
            uint256 totalLending = lendingMeta[issuer].totalLending;
            uint256 adjustment =
                (amount * min(totalLending, haircutAmount)) / totalLending;
            amount = amount - adjustment;
            haircuts[issuer] -= adjustment;
        }

        address token = issuerTokens[issuer];
        Fund(fund()).withdraw(token, recipient, amount);
    }

    function collectToken(
        address issuer,
        address source,
        uint256 amount
    ) internal {
        Fund(fund()).depositFor(source, issuerTokens[issuer], amount);
    }

    function haircut(uint256 amount) external {
        haircuts[msg.sender] += amount;
    }

    function addIncentive(
        address token,
        uint256 amount,
        uint256 endTimestamp
    ) external onlyOwnerExecActivator {
        LendingMetadata storage meta = lendingMeta[token];
        meta.incentiveEnd = endTimestamp;
        meta.incentiveTarget = amount;
        meta.incentiveLastUpdated = block.timestamp;
    }

    function disburseIncentive(
        HourlyBond storage bond,
        LendingMetadata storage meta,
        address holder
    ) internal override {
        uint256 allocationDelta =
            meta.cumulIncentiveAllocationFP - bond.incentiveAllocationStart;
        if (allocationDelta > 0) {
            uint256 disburseAmount = (allocationDelta * bond.amount) / FP48;
            Fund(fund()).withdraw(MFI, holder, disburseAmount);
            bond.incentiveAllocationStart += allocationDelta;
        }
    }

    function withdrawIncentive(address token) external {
        LendingMetadata storage meta = lendingMeta[token];
        updateIncentiveAllocation(meta);
        disburseIncentive(
            hourlyBondAccounts[token][msg.sender],
            meta,
            msg.sender
        );
    }
}

File 2 of 14 : Ownable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../utils/Context.sol";
/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

File 3 of 14 : IERC20.sol
// SPDX-License-Identifier: MIT

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 `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, 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 4 of 14 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 5 of 14 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain`call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
      return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 6 of 14 : Context.sol
// SPDX-License-Identifier: MIT

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) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

File 7 of 14 : BaseLending.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import "./RoleAware.sol";

/// @title Base lending behavior
abstract contract BaseLending {
    uint256 constant FP48 = 2**48;
    uint256 constant ACCUMULATOR_INIT = 10**18;

    uint256 constant hoursPerYear = 365 days / (1 hours);
    uint256 constant CHANGE_POINT = 82;
    uint256 public normalRatePerPercent =
        (FP48 * 12) / hoursPerYear / CHANGE_POINT / 100;
    uint256 public highRatePerPercent =
        (FP48 * (135 - 12)) / hoursPerYear / (100 - CHANGE_POINT) / 100;

    struct YieldAccumulator {
        uint256 accumulatorFP;
        uint256 lastUpdated;
        uint256 hourlyYieldFP;
    }

    struct LendingMetadata {
        uint256 totalLending;
        uint256 totalBorrowed;
        uint256 lendingCap;
        uint256 cumulIncentiveAllocationFP;
        uint256 incentiveLastUpdated;
        uint256 incentiveEnd;
        uint256 incentiveTarget;
    }
    mapping(address => LendingMetadata) public lendingMeta;

    /// @dev accumulate interest per issuer (like compound indices)
    mapping(address => YieldAccumulator) public borrowYieldAccumulators;

    /// @dev simple formula for calculating interest relative to accumulator
    function applyInterest(
        uint256 balance,
        uint256 accumulatorFP,
        uint256 yieldQuotientFP
    ) internal pure returns (uint256) {
        // 1 * FP / FP = 1
        return (balance * accumulatorFP) / yieldQuotientFP;
    }

    function currentLendingRateFP(uint256 totalLending, uint256 totalBorrowing)
        internal
        view
        returns (uint256 rate)
    {
        rate = FP48;
        uint256 utilizationPercent =
            totalLending > 0 ? (100 * totalBorrowing) / totalLending : 0;
        if (utilizationPercent < CHANGE_POINT) {
            rate += utilizationPercent * normalRatePerPercent;
        } else {
            rate +=
                CHANGE_POINT *
                normalRatePerPercent +
                (utilizationPercent - CHANGE_POINT) *
                highRatePerPercent;
        }
    }

    /// @dev minimum
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a > b) {
            return b;
        } else {
            return a;
        }
    }

    /// @dev maximum
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a > b) {
            return a;
        } else {
            return b;
        }
    }

    /// Available tokens to this issuance
    function issuanceBalance(address issuance)
        internal
        view
        virtual
        returns (uint256);
}

File 8 of 14 : Fund.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "../interfaces/IWETH.sol";
import "./RoleAware.sol";

/// @title Manage funding
contract Fund is RoleAware {
    using SafeERC20 for IERC20;
    /// wrapped ether
    address public immutable WETH;

    constructor(address _WETH, address _roles) RoleAware(_roles) {
        WETH = _WETH;
    }

    /// Deposit an active token
    function deposit(address depositToken, uint256 depositAmount) external {
        IERC20(depositToken).safeTransferFrom(
            msg.sender,
            address(this),
            depositAmount
        );
    }

    /// Deposit token on behalf of `sender`
    function depositFor(
        address sender,
        address depositToken,
        uint256 depositAmount
    ) external {
        require(isFundTransferer(msg.sender), "Unauthorized deposit");
        IERC20(depositToken).safeTransferFrom(
            sender,
            address(this),
            depositAmount
        );
    }

    /// Deposit to wrapped ether
    function depositToWETH() external payable {
        IWETH(WETH).deposit{value: msg.value}();
    }

    // withdrawers role
    function withdraw(
        address withdrawalToken,
        address recipient,
        uint256 withdrawalAmount
    ) external {
        require(isFundTransferer(msg.sender), "Unauthorized withdraw");
        IERC20(withdrawalToken).safeTransfer(recipient, withdrawalAmount);
    }

    // withdrawers role
    function withdrawETH(address recipient, uint256 withdrawalAmount) external {
        require(isFundTransferer(msg.sender), "Unauthorized withdraw");
        IWETH(WETH).withdraw(withdrawalAmount);
        Address.sendValue(payable(recipient), withdrawalAmount);
    }
}

File 9 of 14 : HourlyBondSubscriptionLending.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "./BaseLending.sol";

struct HourlyBond {
    uint256 amount;
    uint256 yieldQuotientFP;
    uint256 moduloHour;
    uint256 incentiveAllocationStart;
}

/// @title Here we offer subscriptions to auto-renewing hourly bonds
/// Funds are locked in for an 50 minutes per hour, while interest rates float
abstract contract HourlyBondSubscriptionLending is BaseLending {
    mapping(address => YieldAccumulator) hourlyBondYieldAccumulators;

    uint256 constant RATE_UPDATE_WINDOW = 10 minutes;
    uint256 public withdrawalWindow = 20 minutes;
    uint256 constant MAX_HOUR_UPDATE = 4;
    // issuer => holder => bond record
    mapping(address => mapping(address => HourlyBond))
        public hourlyBondAccounts;

    uint256 public borrowingFactorPercent = 200;

    uint256 constant borrowMinAPR = 25;
    uint256 constant borrowMinHourlyYield =
        FP48 + (borrowMinAPR * FP48) / 1000 / hoursPerYear;

    function _makeHourlyBond(
        address issuer,
        address holder,
        uint256 amount
    ) internal {
        HourlyBond storage bond = hourlyBondAccounts[issuer][holder];
        LendingMetadata storage meta = lendingMeta[issuer];
        addToTotalLending(meta, amount);
        updateHourlyBondAmount(issuer, bond, holder);

        if (bond.amount == 0) {
            bond.moduloHour = block.timestamp % (1 hours);
        }
        bond.amount += amount;
    }

    function updateHourlyBondAmount(
        address issuer,
        HourlyBond storage bond,
        address holder
    ) internal {
        uint256 yieldQuotientFP = bond.yieldQuotientFP;

        YieldAccumulator storage yA =
            getUpdatedHourlyYield(
                issuer,
                hourlyBondYieldAccumulators[issuer],
                RATE_UPDATE_WINDOW
            );

        LendingMetadata storage meta = lendingMeta[issuer];

        if (yieldQuotientFP > 0) {
            disburseIncentive(bond, meta, holder);
            uint256 oldAmount = bond.amount;

            bond.amount = applyInterest(
                bond.amount,
                yA.accumulatorFP,
                yieldQuotientFP
            );

            uint256 deltaAmount = bond.amount - oldAmount;
            addToTotalLending(meta, deltaAmount);
        } else {
            bond.incentiveAllocationStart = meta.cumulIncentiveAllocationFP;
        }
        bond.yieldQuotientFP = yA.accumulatorFP;
    }

    // Retrieves bond balance for issuer and holder
    function viewHourlyBondAmount(address issuer, address holder)
        public
        view
        returns (uint256)
    {
        HourlyBond storage bond = hourlyBondAccounts[issuer][holder];
        uint256 yieldQuotientFP = bond.yieldQuotientFP;

        uint256 cumulativeYield =
            viewCumulativeYieldFP(
                hourlyBondYieldAccumulators[issuer],
                block.timestamp
            );

        if (yieldQuotientFP > 0) {
            return applyInterest(bond.amount, cumulativeYield, yieldQuotientFP);
        } else {
            return bond.amount;
        }
    }

    function _withdrawHourlyBond(
        address issuer,
        HourlyBond storage bond,
        uint256 amount,
        address holder
    ) internal {
        subtractFromTotalLending(lendingMeta[issuer], amount);
        updateHourlyBondAmount(issuer, bond, holder);

        // how far the current hour has advanced (relative to acccount hourly clock)
        uint256 currentOffset = (block.timestamp - bond.moduloHour) % (1 hours);

        require(
            withdrawalWindow >= currentOffset,
            "Tried withdrawing outside subscription cancellation time window"
        );

        bond.amount -= amount;
    }

    function calcCumulativeYieldFP(
        YieldAccumulator storage yieldAccumulator,
        uint256 timeDelta
    ) internal view returns (uint256 accumulatorFP) {
        uint256 secondsDelta = timeDelta % (1 hours);
        // linearly interpolate interest for seconds
        // FP * FP * 1 / (FP * 1) = FP
        accumulatorFP =
            yieldAccumulator.accumulatorFP +
            (yieldAccumulator.accumulatorFP *
                (yieldAccumulator.hourlyYieldFP - FP48) *
                secondsDelta) /
            (FP48 * 1 hours);

        uint256 hoursDelta = timeDelta / (1 hours);
        if (hoursDelta > 0) {
            uint256 accumulatorBeforeFP = accumulatorFP;
            for (uint256 i = 0; hoursDelta > i && MAX_HOUR_UPDATE > i; i++) {
                // FP48 * FP48 / FP48 = FP48
                accumulatorFP =
                    (accumulatorFP * yieldAccumulator.hourlyYieldFP) /
                    FP48;
            }

            // a lot of time has passed
            if (hoursDelta > MAX_HOUR_UPDATE) {
                // apply interest in non-compounding way
                accumulatorFP +=
                    ((accumulatorFP - accumulatorBeforeFP) *
                        (hoursDelta - MAX_HOUR_UPDATE)) /
                    MAX_HOUR_UPDATE;
            }
        }
    }

    /// @dev updates yield accumulators for both borrowing and lending
    /// issuer address represents a token
    function updateHourlyYield(address issuer)
        public
        returns (uint256 hourlyYield)
    {
        return
            getUpdatedHourlyYield(
                issuer,
                hourlyBondYieldAccumulators[issuer],
                RATE_UPDATE_WINDOW
            )
                .hourlyYieldFP;
    }

    /// @dev updates yield accumulators for both borrowing and lending
    function getUpdatedHourlyYield(
        address issuer,
        YieldAccumulator storage accumulator,
        uint256 window
    ) internal returns (YieldAccumulator storage) {
        uint256 lastUpdated = accumulator.lastUpdated;
        uint256 timeDelta = (block.timestamp - lastUpdated);

        if (timeDelta > window) {
            YieldAccumulator storage borrowAccumulator =
                borrowYieldAccumulators[issuer];

            accumulator.accumulatorFP = calcCumulativeYieldFP(
                accumulator,
                timeDelta
            );

            LendingMetadata storage meta = lendingMeta[issuer];

            accumulator.hourlyYieldFP = currentLendingRateFP(
                meta.totalLending,
                meta.totalBorrowed
            );
            accumulator.lastUpdated = block.timestamp;

            updateBorrowYieldAccu(borrowAccumulator);

            borrowAccumulator.hourlyYieldFP = max(
                borrowMinHourlyYield,
                FP48 +
                    (borrowingFactorPercent *
                        (accumulator.hourlyYieldFP - FP48)) /
                    100
            );
        }

        return accumulator;
    }

    function updateBorrowYieldAccu(YieldAccumulator storage borrowAccumulator)
        internal
    {
        uint256 timeDelta = block.timestamp - borrowAccumulator.lastUpdated;

        if (timeDelta > RATE_UPDATE_WINDOW) {
            borrowAccumulator.accumulatorFP = calcCumulativeYieldFP(
                borrowAccumulator,
                timeDelta
            );

            borrowAccumulator.lastUpdated = block.timestamp;
        }
    }

    function getUpdatedBorrowYieldAccuFP(address issuer)
        external
        returns (uint256)
    {
        YieldAccumulator storage yA = borrowYieldAccumulators[issuer];
        updateBorrowYieldAccu(yA);
        return yA.accumulatorFP;
    }

    function viewCumulativeYieldFP(
        YieldAccumulator storage yA,
        uint256 timestamp
    ) internal view returns (uint256) {
        uint256 timeDelta = (timestamp - yA.lastUpdated);
        if (timeDelta > RATE_UPDATE_WINDOW) {
            return calcCumulativeYieldFP(yA, timeDelta);
        } else {
            return yA.accumulatorFP;
        }
    }

    function viewYearlyIncentivePer10k(address token)
        external
        view
        returns (uint256)
    {
        LendingMetadata storage meta = lendingMeta[token];
        if (
            meta.incentiveEnd < block.timestamp ||
            meta.incentiveLastUpdated > meta.incentiveEnd
        ) {
            return 0;
        } else {
            uint256 timeDelta = meta.incentiveEnd - meta.incentiveLastUpdated;

            // scale to 1 year
            return
                (10_000 * (365 days) * meta.incentiveTarget) /
                (1 + meta.totalLending * timeDelta);
        }
    }

    function updateIncentiveAllocation(LendingMetadata storage meta) internal {
        uint256 endTime = min(meta.incentiveEnd, block.timestamp);
        if (meta.incentiveTarget > 0 && endTime > meta.incentiveLastUpdated) {
            uint256 timeDelta = endTime - meta.incentiveLastUpdated;
            uint256 targetDelta =
                min(
                    meta.incentiveTarget,
                    (timeDelta * meta.incentiveTarget) /
                        (meta.incentiveEnd - meta.incentiveLastUpdated)
                );
            meta.incentiveTarget -= targetDelta;
            meta.cumulIncentiveAllocationFP +=
                (targetDelta * FP48) /
                (1 + meta.totalLending);
            meta.incentiveLastUpdated = block.timestamp;
        }
    }

    function addToTotalLending(LendingMetadata storage meta, uint256 amount)
        internal
    {
        updateIncentiveAllocation(meta);
        meta.totalLending += amount;
    }

    function subtractFromTotalLending(
        LendingMetadata storage meta,
        uint256 amount
    ) internal {
        updateIncentiveAllocation(meta);
        meta.totalLending -= amount;
    }

    function disburseIncentive(
        HourlyBond storage bond,
        LendingMetadata storage meta,
        address holder
    ) internal virtual;
}

File 10 of 14 : RoleAware.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "./Roles.sol";

/// @title Role management behavior
/// Main characters are for service discovery
/// Whereas roles are for access control
contract RoleAware {
    Roles public immutable roles;
    mapping(uint256 => address) public mainCharacterCache;
    mapping(address => mapping(uint256 => bool)) public roleCache;

    constructor(address _roles) {
        require(_roles != address(0), "Please provide valid roles address");
        roles = Roles(_roles);
    }

    modifier noIntermediary() {
        require(
            msg.sender == tx.origin,
            "Currently no intermediaries allowed for this function call"
        );
        _;
    }

    // @dev Throws if called by any account other than the owner or executor
    modifier onlyOwnerExec() {
        require(
            owner() == msg.sender || executor() == msg.sender,
            "Roles: caller is not the owner"
        );
        _;
    }

    modifier onlyOwnerExecDisabler() {
        require(
            owner() == msg.sender ||
                executor() == msg.sender ||
                disabler() == msg.sender,
            "Caller is not the owner, executor or authorized disabler"
        );
        _;
    }

    modifier onlyOwnerExecActivator() {
        require(
            owner() == msg.sender ||
                executor() == msg.sender ||
                isTokenActivator(msg.sender),
            "Caller is not the owner, executor or authorized activator"
        );
        _;
    }

    function updateRoleCache(uint256 role, address contr) public virtual {
        roleCache[contr][role] = roles.getRole(role, contr);
    }

    function updateMainCharacterCache(uint256 role) public virtual {
        mainCharacterCache[role] = roles.mainCharacters(role);
    }

    function owner() internal view returns (address) {
        return roles.owner();
    }

    function executor() internal returns (address) {
        return roles.executor();
    }

    function disabler() internal view returns (address) {
        return mainCharacterCache[DISABLER];
    }

    function fund() internal view returns (address) {
        return mainCharacterCache[FUND];
    }

    function lending() internal view returns (address) {
        return mainCharacterCache[LENDING];
    }

    function marginRouter() internal view returns (address) {
        return mainCharacterCache[MARGIN_ROUTER];
    }

    function crossMarginTrading() internal view returns (address) {
        return mainCharacterCache[CROSS_MARGIN_TRADING];
    }

    function feeController() internal view returns (address) {
        return mainCharacterCache[FEE_CONTROLLER];
    }

    function price() internal view returns (address) {
        return mainCharacterCache[PRICE_CONTROLLER];
    }

    function admin() internal view returns (address) {
        return mainCharacterCache[ADMIN];
    }

    function incentiveDistributor() internal view returns (address) {
        return mainCharacterCache[INCENTIVE_DISTRIBUTION];
    }

    function tokenAdmin() internal view returns (address) {
        return mainCharacterCache[TOKEN_ADMIN];
    }

    function isBorrower(address contr) internal view returns (bool) {
        return roleCache[contr][BORROWER];
    }

    function isFundTransferer(address contr) internal view returns (bool) {
        return roleCache[contr][FUND_TRANSFERER];
    }

    function isMarginTrader(address contr) internal view returns (bool) {
        return roleCache[contr][MARGIN_TRADER];
    }

    function isFeeSource(address contr) internal view returns (bool) {
        return roleCache[contr][FEE_SOURCE];
    }

    function isMarginCaller(address contr) internal view returns (bool) {
        return roleCache[contr][MARGIN_CALLER];
    }

    function isLiquidator(address contr) internal view returns (bool) {
        return roleCache[contr][LIQUIDATOR];
    }

    function isAuthorizedFundTrader(address contr)
        internal
        view
        returns (bool)
    {
        return roleCache[contr][AUTHORIZED_FUND_TRADER];
    }

    function isIncentiveReporter(address contr) internal view returns (bool) {
        return roleCache[contr][INCENTIVE_REPORTER];
    }

    function isTokenActivator(address contr) internal view returns (bool) {
        return roleCache[contr][TOKEN_ACTIVATOR];
    }

    function isStakePenalizer(address contr) internal view returns (bool) {
        return roleCache[contr][STAKE_PENALIZER];
    }

    function isLender(address contr) internal view returns (bool) {
        return roleCache[contr][LENDER];
    }
}

File 11 of 14 : Roles.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";
import "../interfaces/IDependencyController.sol";

// we chose not to go with an enum
// to make this list easy to extend
uint256 constant FUND_TRANSFERER = 1;
uint256 constant MARGIN_CALLER = 2;
uint256 constant BORROWER = 3;
uint256 constant MARGIN_TRADER = 4;
uint256 constant FEE_SOURCE = 5;
uint256 constant LIQUIDATOR = 6;
uint256 constant AUTHORIZED_FUND_TRADER = 7;
uint256 constant INCENTIVE_REPORTER = 8;
uint256 constant TOKEN_ACTIVATOR = 9;
uint256 constant STAKE_PENALIZER = 10;
uint256 constant LENDER = 11;

uint256 constant FUND = 101;
uint256 constant LENDING = 102;
uint256 constant MARGIN_ROUTER = 103;
uint256 constant CROSS_MARGIN_TRADING = 104;
uint256 constant FEE_CONTROLLER = 105;
uint256 constant PRICE_CONTROLLER = 106;
uint256 constant ADMIN = 107;
uint256 constant INCENTIVE_DISTRIBUTION = 108;
uint256 constant TOKEN_ADMIN = 109;

uint256 constant DISABLER = 1001;
uint256 constant DEPENDENCY_CONTROLLER = 1002;

/// @title Manage permissions of contracts and ownership of everything
/// owned by a multisig wallet (0xEED9D1c6B4cdEcB3af070D85bfd394E7aF179CBd) during
/// beta and will then be transfered to governance
/// https://github.com/marginswap/governance
contract Roles is Ownable {
    mapping(address => mapping(uint256 => bool)) public roles;
    mapping(uint256 => address) public mainCharacters;

    constructor() Ownable() {
        // token activation from the get-go
        roles[msg.sender][TOKEN_ACTIVATOR] = true;
    }

    /// @dev Throws if called by any account other than the owner.
    modifier onlyOwnerExecDepController() {
        require(
            owner() == msg.sender ||
                executor() == msg.sender ||
                mainCharacters[DEPENDENCY_CONTROLLER] == msg.sender,
            "Roles: caller is not the owner"
        );
        _;
    }

    function giveRole(uint256 role, address actor)
        external
        onlyOwnerExecDepController
    {
        roles[actor][role] = true;
    }

    function removeRole(uint256 role, address actor)
        external
        onlyOwnerExecDepController
    {
        roles[actor][role] = false;
    }

    function setMainCharacter(uint256 role, address actor)
        external
        onlyOwnerExecDepController
    {
        mainCharacters[role] = actor;
    }

    function getRole(uint256 role, address contr) external view returns (bool) {
        return roles[contr][role];
    }

    /// @dev current executor
    function executor() public returns (address exec) {
        address depController = mainCharacters[DEPENDENCY_CONTROLLER];
        if (depController != address(0)) {
            exec = IDependencyController(depController).currentExecutor();
        }
    }
}

File 12 of 14 : IDependencyController.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

interface IDependencyController {
    function currentExecutor() external returns (address);
}

File 13 of 14 : IWETH.sol
pragma solidity >=0.5.0;

interface IWETH {
    function deposit() external payable;

    function transfer(address to, uint256 value) external returns (bool);

    function withdraw(uint256) external;
}

File 14 of 14 : IncentiveReporter.sol
library IncentiveReporter {
    event AddToClaim(address topic, address indexed claimant, uint256 amount);
    event SubtractFromClaim(
        address topic,
        address indexed claimant,
        uint256 amount
    );

    /// Start / increase amount of claim
    function addToClaimAmount(
        address topic,
        address recipient,
        uint256 claimAmount
    ) internal {
        emit AddToClaim(topic, recipient, claimAmount);
    }

    /// Decrease amount of claim
    function subtractFromClaimAmount(
        address topic,
        address recipient,
        uint256 subtractAmount
    ) internal {
        emit SubtractFromClaim(topic, recipient, subtractAmount);
    }
}

Settings
{
  "evmVersion": "istanbul",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 5000
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_MFI","type":"address"},{"internalType":"address","name":"_roles","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"MFI","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"}],"name":"activateIssuer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"activateIssuer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"activeIssuers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"endTimestamp","type":"uint256"}],"name":"addIncentive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"address","name":"issuer","type":"address"},{"internalType":"uint256","name":"yieldQuotientFP","type":"uint256"}],"name":"applyBorrowInterest","outputs":[{"internalType":"uint256","name":"balanceWithInterest","type":"uint256"},{"internalType":"uint256","name":"accumulatorFP","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"borrowYieldAccumulators","outputs":[{"internalType":"uint256","name":"accumulatorFP","type":"uint256"},{"internalType":"uint256","name":"lastUpdated","type":"uint256"},{"internalType":"uint256","name":"hourlyYieldFP","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowingFactorPercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"buyHourlyBondSubscription","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"}],"name":"closeHourlyBondAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"}],"name":"deactivateIssuer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"}],"name":"getUpdatedBorrowYieldAccuFP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"haircut","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"haircuts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"highRatePerPercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"hourlyBondAccounts","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"yieldQuotientFP","type":"uint256"},{"internalType":"uint256","name":"moduloHour","type":"uint256"},{"internalType":"uint256","name":"incentiveAllocationStart","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"}],"name":"initBorrowYieldAccumulator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"issuerTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lendingMeta","outputs":[{"internalType":"uint256","name":"totalLending","type":"uint256"},{"internalType":"uint256","name":"totalBorrowed","type":"uint256"},{"internalType":"uint256","name":"lendingCap","type":"uint256"},{"internalType":"uint256","name":"cumulIncentiveAllocationFP","type":"uint256"},{"internalType":"uint256","name":"incentiveLastUpdated","type":"uint256"},{"internalType":"uint256","name":"incentiveEnd","type":"uint256"},{"internalType":"uint256","name":"incentiveTarget","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"mainCharacterCache","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"},{"internalType":"address","name":"holder","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"makeFallbackBond","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"normalRatePerPercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"payOff","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"registerBorrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"registerLend","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"registerWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"roleCache","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"roles","outputs":[{"internalType":"contract Roles","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"borrowingFactor","type":"uint256"}],"name":"setBorrowingFactorPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"rate","type":"uint256"}],"name":"setHighRatePerPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"},{"internalType":"uint256","name":"aprPercent","type":"uint256"}],"name":"setHourlyYieldAPR","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"},{"internalType":"uint256","name":"cap","type":"uint256"}],"name":"setLendingCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"rate","type":"uint256"}],"name":"setNormalRatePerPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"window","type":"uint256"}],"name":"setWithdrawalWindow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"}],"name":"updateHourlyYield","outputs":[{"internalType":"uint256","name":"hourlyYield","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"role","type":"uint256"}],"name":"updateMainCharacterCache","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"role","type":"uint256"},{"internalType":"address","name":"contr","type":"address"}],"name":"updateRoleCache","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"}],"name":"viewAccumulatedBorrowingYieldFP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"}],"name":"viewBorrowAPRPer10k","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"}],"name":"viewHourlyBondAPRPer10k","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"},{"internalType":"address","name":"holder","type":"address"}],"name":"viewHourlyBondAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"address","name":"issuer","type":"address"},{"internalType":"uint256","name":"yieldQuotientFP","type":"uint256"}],"name":"viewWithBorrowInterest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"viewYearlyIncentivePer10k","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"issuer","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawHourlyBond","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"withdrawIncentive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawalWindow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

60c0604052606460526122386200001f6601000000000000600c620001cd565b6200002b9190620001ac565b620000379190620001ac565b620000439190620001ac565b600255606462000055605282620001ef565b6122386200006c6601000000000000607b620001cd565b620000789190620001ac565b620000849190620001ac565b620000909190620001ac565b6003556104b060075560c8600955348015620000ab57600080fd5b5060405162002e9238038062002e92833981016040819052620000ce9162000175565b806001600160a01b038116620001355760405162461bcd60e51b815260206004820152602260248201527f506c656173652070726f766964652076616c696420726f6c6573206164647265604482015261737360f01b606482015260840160405180910390fd5b6001600160601b0319606091821b811660805292901b90911660a052506200021f565b80516001600160a01b03811681146200017057600080fd5b919050565b6000806040838503121562000188578182fd5b620001938362000158565b9150620001a36020840162000158565b90509250929050565b600082620001c857634e487b7160e01b81526012600452602481fd5b500490565b6000816000190483118215151615620001ea57620001ea62000209565b500290565b60008282101562000204576200020462000209565b500390565b634e487b7160e01b600052601160045260246000fd5b60805160601c60a05160601c612c246200026e6000396000818161079f01526126100152600081816103e50152818161084f01528181610bbe01528181612173015261220b0152612c246000f3fe608060405234801561001057600080fd5b506004361061030a5760003560e01c806391b46e051161019c578063db41039f116100ee578063ed29c12b11610097578063f848767311610071578063f8487673146107e7578063f884614b146107fa578063f90889c91461080d5761030a565b8063ed29c12b1461079a578063ee0862bf146107c1578063f51dfe7a146107d45761030a565b8063e69e2662116100c8578063e69e266214610755578063e9c3f77d1461075e578063ebe64bcc146107875761030a565b8063db41039f14610707578063dc683bcf1461071a578063e695fa681461072d5761030a565b8063cc4b26a311610150578063d19bd0a71161012a578063d19bd0a7146106d8578063d2b48934146106e1578063d4437dd8146106f45761030a565b8063cc4b26a314610668578063cd3b5dfb1461067b578063cfc14f53146106c55761030a565b8063ae4479dd11610181578063ae4479dd1461062f578063b42f60db14610642578063bfdeb719146106555761030a565b806391b46e051461059c578063ad037af6146105af5761030a565b806349d0e2ee1161026057806376668b67116102095780637a009135116101e35780637a009135146104fb5780637a1a04df1461050e57806383a042291461053c5761030a565b806376668b67146104ac57806376c308f2146104bf578063797384b2146104c85761030a565b80636d8d36ba1161023a5780636d8d36ba146104735780636e60e2491461048657806372d01257146104995761030a565b806349d0e2ee1461042d5780634b86daab1461044057806364f85ab1146104535761030a565b806328108026116102c2578063392f5f641161029c578063392f5f64146103e0578063447d52ba1461040757806346c87f801461041a5761030a565b806328108026146103795780632a6a897b146103ba57806336953912146103cd5761030a565b806314189db2116102f357806314189db21461033757806316de7a431461035d578063267031b8146103705761030a565b8063071060a61461030f5780630f7c43dc14610324575b600080fd5b61032261031d366004612a4c565b610820565b005b6103226103323660046129cd565b610917565b61034a610345366004612916565b6109a5565b6040519081526020015b60405180910390f35b61032261036b3660046129cd565b6109d0565b61034a60095481565b6103a2610387366004612916565b600a602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610354565b6103226103c83660046129cd565b610a62565b6103226103db366004612a4c565b610b01565b6103a27f000000000000000000000000000000000000000000000000000000000000000081565b610322610415366004612a64565b610b7f565b61034a610428366004612916565b610c70565b61032261043b366004612a4c565b610c98565b61032261044e366004612916565b610d16565b61034a610461366004612916565b600b6020526000908152604090205481565b6103226104813660046129cd565b610d23565b61034a610494366004612955565b610e44565b61034a6104a7366004612916565b610ebe565b61034a6104ba366004612a88565b610edf565b61034a60025481565b6104eb6104d6366004612916565b600c6020526000908152604090205460ff1681565b6040519015158152602001610354565b610322610509366004612916565b610f19565b6104eb61051c3660046129cd565b600160209081526000928352604080842090915290825290205460ff1681565b61057c61054a366004612955565b600860209081526000928352604080842090915290825290208054600182015460028301546003909301549192909184565b604080519485526020850193909352918301526060820152608001610354565b6103226105aa366004612a4c565b610fff565b6105fa6105bd366004612916565b6004602081905260009182526040909120805460018201546002830154600384015494840154600585015460069095015493959294919390919087565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e001610354565b61032261063d3660046129cd565b61107d565b6103226106503660046129f8565b611172565b61034a610663366004612916565b611269565b610322610676366004612916565b611301565b6106aa610689366004612916565b60056020526000908152604090208054600182015460029092015490919083565b60408051938452602084019290925290820152606001610354565b6103226106d3366004612916565b611480565b61034a60075481565b6103226106ef3660046129cd565b6114d0565b610322610702366004612a4c565b6115b4565b61032261071536600461298d565b6115db565b61034a610728366004612916565b611651565b61074061073b366004612a88565b611681565b60408051928352602083019190915201610354565b61034a60035481565b6103a261076c366004612a4c565b6000602081905290815260409020546001600160a01b031681565b610322610795366004612955565b61176f565b6103a27f000000000000000000000000000000000000000000000000000000000000000081565b6103226107cf3660046129cd565b611891565b61034a6107e2366004612916565b611a12565b6103226107f5366004612a4c565b611a33565b610322610808366004612916565b611ab1565b61032261081b3660046129cd565b611b3a565b6040517fb4ed0b6d000000000000000000000000000000000000000000000000000000008152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063b4ed0b6d9060240160206040518083038186803b15801561089957600080fd5b505afa1580156108ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d19190612939565b60009182526020829052604090912080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03909216919091179055565b6001600160a01b03821660009081526008602090815260408083203380855292529091209061094b90849083908590611cfe565b805461098a576001600160a01b038316600090815260086020908152604080832033845290915281208181556001810182905560028101829055600301555b610995833384611de4565b6109a0833384611f6d565b505050565b6001600160a01b03811660009081526005602052604081206109c78142611fb9565b9150505b919050565b6001600160a01b0382166000908152600c602052604090205460ff16610a3d5760405162461bcd60e51b815260206004820152601360248201527f4e6f7420617070726f766564206973737565720000000000000000000000000060448201526064015b60405180910390fd5b610a48823383611ffa565b610a538233836120b9565b610a5e82338361212c565b5050565b3360009081526001602090815260408083206003845290915290205460ff16610acd5760405162461bcd60e51b815260206004820152601560248201527f4e6f7420617070726f76656420626f72726f77657200000000000000000000006044820152606401610a34565b6001600160a01b03821660009081526004602052604081206001018054839290610af8908490612b17565b90915550505050565b33610b0a61216f565b6001600160a01b03161480610b2e575033610b23612207565b6001600160a01b0316145b610b7a5760405162461bcd60e51b815260206004820152601e60248201527f526f6c65733a2063616c6c6572206973206e6f7420746865206f776e657200006044820152606401610a34565b600755565b6040517f93552a3d000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b0382811660248301527f000000000000000000000000000000000000000000000000000000000000000016906393552a3d9060440160206040518083038186803b158015610c0057600080fd5b505afa158015610c14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c389190612a2c565b6001600160a01b0391909116600090815260016020908152604080832094835293905291909120805460ff1916911515919091179055565b6001600160a01b0381166000908152600560205260408120610c9181612278565b5492915050565b33610ca161216f565b6001600160a01b03161480610cc5575033610cba612207565b6001600160a01b0316145b610d115760405162461bcd60e51b815260206004820152601e60248201527f526f6c65733a2063616c6c6572206973206e6f7420746865206f776e657200006044820152606401610a34565b600355565b610d20818261176f565b50565b336000908152600160209081526040808320600b845290915290205460ff16610d8e5760405162461bcd60e51b815260206004820152601660248201527f4e6f7420616e20617070726f766564206c656e646572000000000000000000006044820152606401610a34565b6001600160a01b0382166000908152600c602052604090205460ff16610df65760405162461bcd60e51b815260206004820152601360248201527f4e6f7420617070726f76656420697373756572000000000000000000000000006044820152606401610a34565b6001600160a01b0382166000908152600460205260409020610e1881836122ac565b6001600160a01b0383166000908152600660205260409020610e3e9084906102586122c9565b50505050565b6001600160a01b038083166000818152600860209081526040808320948616835293815283822060018101549383526006909152928120909291908390610e8b9042611fb9565b90508115610eaa578254610ea09082846123e2565b9350505050610eb8565b5050549050610eb8565b5050505b92915050565b6001600160a01b0381166000908152600660205260408120610eb890612401565b6001600160a01b03821660009081526005602052604081208190610f039042611fb9565b9050610f108582856123e2565b95945050505050565b33610f2261216f565b6001600160a01b03161480610f46575033610f3b612207565b6001600160a01b0316145b80610f6c57503360009081526001602090815260408083206009845290915290205460ff165b610fde5760405162461bcd60e51b815260206004820152603960248201527f43616c6c6572206973206e6f7420746865206f776e65722c206578656375746f60448201527f72206f7220617574686f72697a656420616374697661746f72000000000000006064820152608401610a34565b6001600160a01b03166000908152600c60205260409020805460ff19169055565b3361100861216f565b6001600160a01b0316148061102c575033611021612207565b6001600160a01b0316145b6110785760405162461bcd60e51b815260206004820152601e60248201527f526f6c65733a2063616c6c6572206973206e6f7420746865206f776e657200006044820152606401610a34565b600955565b336000908152600160209081526040808320600b845290915290205460ff166110e85760405162461bcd60e51b815260206004820152601660248201527f4e6f7420616e20617070726f766564206c656e646572000000000000000000006044820152606401610a34565b6001600160a01b0382166000908152600c602052604090205460ff166111505760405162461bcd60e51b815260206004820152601360248201527f4e6f7420617070726f76656420697373756572000000000000000000000000006044820152606401610a34565b6001600160a01b0382166000908152600460205260409020610e188183612461565b3361117b61216f565b6001600160a01b0316148061119f575033611194612207565b6001600160a01b0316145b806111c557503360009081526001602090815260408083206009845290915290205460ff165b6112375760405162461bcd60e51b815260206004820152603960248201527f43616c6c6572206973206e6f7420746865206f776e65722c206578656375746f60448201527f72206f7220617574686f72697a656420616374697661746f72000000000000006064820152608401610a34565b6001600160a01b0390921660009081526004602081905260409091206005810193909355600683019190915542910155565b6001600160a01b0381166000908152600460205260408120600581015442118061129a575080600501548160040154115b156112a95760009150506109cb565b6000816004015482600501546112bf9190612b17565b82549091506112cf908290612ada565b6112da906001612aae565b60068301546112ee9064496cebb800612ada565b6112f89190612ac6565b925050506109cb565b3361130a61216f565b6001600160a01b0316148061132e575033611323612207565b6001600160a01b0316145b8061135457503360009081526001602090815260408083206009845290915290205460ff165b6113c65760405162461bcd60e51b815260206004820152603960248201527f43616c6c6572206973206e6f7420746865206f776e65722c206578656375746f60448201527f72206f7220617574686f72697a656420616374697661746f72000000000000006064820152608401610a34565b6001600160a01b038116600090815260056020526040902080541561142d5760405162461bcd60e51b815260206004820152601360248201527f646f6e27742072652d696e697469616c697a65000000000000000000000000006044820152606401610a34565b6601000000000000808255426001830155612238906103e89061145290601990612ada565b61145c9190612ac6565b6114669190612ac6565b611477906601000000000000612aae565b60029091015550565b6001600160a01b03811660009081526004602052604090206114a18161247e565b6001600160a01b0382166000908152600860209081526040808320338085529252909120610a5e918390612563565b336114d961216f565b6001600160a01b031614806114fd5750336114f2612207565b6001600160a01b0316145b8061152357503360009081526001602090815260408083206009845290915290205460ff165b6115955760405162461bcd60e51b815260206004820152603960248201527f43616c6c6572206973206e6f7420746865206f776e65722c206578656375746f60448201527f72206f7220617574686f72697a656420616374697661746f72000000000000006064820152608401610a34565b6001600160a01b03909116600090815260046020526040902060020155565b336000908152600b6020526040812080548392906115d3908490612aae565b909155505050565b336000908152600160209081526040808320600b845290915290205460ff166116465760405162461bcd60e51b815260206004820152601660248201527f4e6f7420616e20617070726f766564206c656e646572000000000000000000006044820152606401610a34565b6109a08383836120b9565b6001600160a01b03811660009081526006602052604081206116779083906102586122c9565b6002015492915050565b33600090815260016020908152604080832060038452909152812054819060ff166116ee5760405162461bcd60e51b815260206004820152601160248201527f4e6f7420617070726f7665642063616c6c0000000000000000000000000000006044820152606401610a34565b6001600160a01b038416600090815260056020526040902061170f81612278565b8054915061171e8683866123e2565b9250600061172c8785612b17565b6001600160a01b0387166000908152600460205260408120600181018054939450909284929061175d908490612aae565b92505081905550505050935093915050565b3361177861216f565b6001600160a01b0316148061179c575033611791612207565b6001600160a01b0316145b806117c257503360009081526001602090815260408083206009845290915290205460ff165b6118345760405162461bcd60e51b815260206004820152603960248201527f43616c6c6572206973206e6f7420746865206f776e65722c206578656375746f60448201527f72206f7220617574686f72697a656420616374697661746f72000000000000006064820152608401610a34565b6001600160a01b039182166000908152600c60209081526040808320805460ff19166001179055600a90915290208054919092167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116179055565b3360009081526001602090815260408083206003845290915290205460ff166118fc5760405162461bcd60e51b815260206004820152601560248201527f4e6f7420617070726f76656420626f72726f77657200000000000000000000006044820152606401610a34565b6001600160a01b0382166000908152600c602052604090205460ff166119645760405162461bcd60e51b815260206004820152601360248201527f4e6f7420617070726f76656420697373756572000000000000000000000000006044820152606401610a34565b6001600160a01b038216600090815260046020526040812060018101805491928492611991908490612aae565b90915550506001600160a01b03831660009081526006602052604090206119bc908490610e106122c9565b506001810154815410156109a05760405162461bcd60e51b815260206004820152601460248201527f496e73756666696369656e74206c656e64696e670000000000000000000000006044820152606401610a34565b6001600160a01b0381166000908152600560205260408120610eb890612401565b33611a3c61216f565b6001600160a01b03161480611a60575033611a55612207565b6001600160a01b0316145b611aac5760405162461bcd60e51b815260206004820152601e60248201527f526f6c65733a2063616c6c6572206973206e6f7420746865206f776e657200006044820152606401610a34565b600255565b6001600160a01b038116600090815260086020908152604080832033808552925290912080549091611ae890849084908490611cfe565b611af3833383611de4565b6001600160a01b038316600090815260086020908152604080832033808552925282208281556001810183905560028101839055600301919091556109a090849083611f6d565b33611b4361216f565b6001600160a01b03161480611b67575033611b5c612207565b6001600160a01b0316145b80611b8d57503360009081526001602090815260408083206009845290915290205460ff165b611bff5760405162461bcd60e51b815260206004820152603960248201527f43616c6c6572206973206e6f7420746865206f776e65722c206578656375746f60448201527f72206f7220617574686f72697a656420616374697661746f72000000000000006064820152608401610a34565b6001600160a01b03821660009081526006602052604090208054611cae5760006122386064611c35856601000000000000612ada565b611c3f9190612ac6565b611c499190612ac6565b611c5a906601000000000000612aae565b60408051606081018252660100000000000081524260208083019182528284019485526001600160a01b038916600090815260069091529290922090518155905160018201559051600290910155506109a0565b6000611cbd84836102586122c9565b90506122386064611cce8582612aae565b611cdf906601000000000000612ada565b611ce99190612ac6565b611cf39190612ac6565b600290910155505050565b6001600160a01b0384166000908152600460205260409020611d2090836122ac565b611d2b848483612699565b6000610e10846002015442611d409190612b17565b611d4a9190612b67565b9050806007541015611dc45760405162461bcd60e51b815260206004820152603f60248201527f5472696564207769746864726177696e67206f7574736964652073756273637260448201527f697074696f6e2063616e63656c6c6174696f6e2074696d652077696e646f77006064820152608401610a34565b82846000016000828254611dd89190612b17565b90915550505050505050565b6001600160a01b0383166000908152600b60205260409020548015801590611e0c5750600082115b15611e8b576001600160a01b0384166000908152600460205260408120549081611e36818561273f565b611e409086612ada565b611e4a9190612ac6565b9050611e568185612b17565b6001600160a01b0387166000908152600b6020526040812080549296508392909190611e83908490612b17565b909155505050505b6001600160a01b038085166000908152600a602052604090205416611ee1606560009081526020527f4a7203f705e51df4a56139a9b86b4a0970bed7ab7a3446eaffe2289989c1645c546001600160a01b031690565b6040517fd9caed120000000000000000000000000000000000000000000000000000000081526001600160a01b038381166004830152868116602483015260448201869052919091169063d9caed1290606401600060405180830381600087803b158015611f4e57600080fd5b505af1158015611f62573d6000803e3d6000fd5b505050505050505050565b604080516001600160a01b038581168252602082018490528416917f3397b60e3ca1e9c0c75a033d62ab8731e9b2937c0690ae5d7f1bd37886b19d7d91015b60405180910390a2505050565b600080836001015483611fcc9190612b17565b9050610258811115611fea57611fe28482612757565b915050610eb8565b50508154610eb8565b5092915050565b7f4a7203f705e51df4a56139a9b86b4a0970bed7ab7a3446eaffe2289989c1645c546001600160a01b038481166000908152600a60205260408082205481517fb3db428b00000000000000000000000000000000000000000000000000000000815287851660048201529084166024820152604481018690529051929093169263b3db428b926064808301939282900301818387803b15801561209c57600080fd5b505af11580156120b0573d6000803e3d6000fd5b50505050505050565b6001600160a01b038084166000818152600860209081526040808320948716835293815283822092825260049052919091206120f58184612461565b612100858386612699565b815461211857612112610e1042612b67565b60028301555b82826000016000828254611dd89190612aae565b604080516001600160a01b038581168252602082018490528416917f8ec70c9430377a4bba035133e27a5b0f3b9951bc080833f6832bcbbb70a96dba9101611fac565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156121ca57600080fd5b505afa1580156121de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122029190612939565b905090565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c34c08e56040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561226457600080fd5b505af11580156121de573d6000803e3d6000fd5b600081600101544261228a9190612b17565b9050610258811115610a5e576122a08282612757565b82554260018301555050565b6122b58261247e565b80826000016000828254610af89190612b17565b6001820154600090816122dc8242612b17565b9050838111156123d8576001600160a01b03861660009081526005602052604090206123088683612757565b86556001600160a01b038716600090815260046020526040902080546001820154612333919061286f565b600288015542600188015561234782612278565b6123cd6122386103e861236266010000000000006019612ada565b61236c9190612ac6565b6123769190612ac6565b612387906601000000000000612aae565b606466010000000000008a600201546123a09190612b17565b6009546123ad9190612ada565b6123b79190612ac6565b6123c8906601000000000000612aae565b6128fe565b826002018190555050505b5092949350505050565b6000816123ef8486612ada565b6123f99190612ac6565b949350505050565b600281015460009081610e106124206601000000000000612710612ada565b61242c84612710612ada565b6124369190612b17565b612444906301e13380612ada565b61244e9190612ac6565b90506123f9660100000000000082612ac6565b61246a8261247e565b80826000016000828254610af89190612aae565b600061248e82600501544261273f565b9050600082600601541180156124a75750816004015481115b15610a5e5760008260040154826124be9190612b17565b905060006124fc8460060154856004015486600501546124de9190612b17565b60068701546124ed9086612ada565b6124f79190612ac6565b61273f565b9050808460060160008282546125129190612b17565b90915550508354612524906001612aae565b612535660100000000000083612ada565b61253f9190612ac6565b8460030160008282546125529190612aae565b909155505042600485015550505050565b6000836003015483600301546125799190612b17565b90508015610e3e5783546000906601000000000000906125999084612ada565b6125a39190612ac6565b90506125e0606560009081526020527f4a7203f705e51df4a56139a9b86b4a0970bed7ab7a3446eaffe2289989c1645c546001600160a01b031690565b6040517fd9caed120000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152858116602483015260448201849052919091169063d9caed1290606401600060405180830381600087803b15801561266d57600080fd5b505af1158015612681573d6000803e3d6000fd5b5050505081856003016000828254611dd89190612aae565b60018201546001600160a01b03841660009081526006602052604081206126c49086906102586122c9565b6001600160a01b03861660009081526004602052604090209091508215612724576126f0858286612563565b84548254612700908290866123e2565b808755600090612711908390612b17565b905061271d8382612461565b505061272f565b600380820154908601555b5054600190930192909255505050565b600081831115612750575080610eb8565b5081610eb8565b600080612766610e1084612b67565b905061277b6601000000000000610e10612ada565b81660100000000000086600201546127939190612b17565b865461279f9190612ada565b6127a99190612ada565b6127b39190612ac6565b84546127bf9190612aae565b915060006127cf610e1085612ac6565b90508015612867578260005b80831180156127ea5750806004115b156128255766010000000000008760020154866128079190612ada565b6128119190612ac6565b94508061281d81612b2e565b9150506127db565b506004821115610eb457600461283b8184612b17565b6128458387612b17565b61284f9190612ada565b6128599190612ac6565b6128639085612aae565b9350505b505092915050565b660100000000000060008361288557600061289b565b83612891846064612ada565b61289b9190612ac6565b905060528110156128c4576002546128b39082612ada565b6128bd9083612aae565b9150611ff3565b6003546128d2605283612b17565b6128dc9190612ada565b6002546128ea906052612ada565b6128f49190612aae565b6123f99083612aae565b60008183111561290f575081610eb8565b5080610eb8565b600060208284031215612927578081fd5b813561293281612bd9565b9392505050565b60006020828403121561294a578081fd5b815161293281612bd9565b60008060408385031215612967578081fd5b823561297281612bd9565b9150602083013561298281612bd9565b809150509250929050565b6000806000606084860312156129a1578081fd5b83356129ac81612bd9565b925060208401356129bc81612bd9565b929592945050506040919091013590565b600080604083850312156129df578182fd5b82356129ea81612bd9565b946020939093013593505050565b600080600060608486031215612a0c578283fd5b8335612a1781612bd9565b95602085013595506040909401359392505050565b600060208284031215612a3d578081fd5b81518015158114612932578182fd5b600060208284031215612a5d578081fd5b5035919050565b60008060408385031215612a76578182fd5b82359150602083013561298281612bd9565b600080600060608486031215612a9c578283fd5b8335925060208401356129bc81612bd9565b60008219821115612ac157612ac1612b7b565b500190565b600082612ad557612ad5612baa565b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612b1257612b12612b7b565b500290565b600082821015612b2957612b29612b7b565b500390565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415612b6057612b60612b7b565b5060010190565b600082612b7657612b76612baa565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6001600160a01b0381168114610d2057600080fdfea26469706673582212208a451470ba8ee8ff877e735ffaac79f28bd8ba5c62f6730bd169f0963e1d946e64736f6c63430008030033000000000000000000000000aa4e3edb11afa93c41db59842b29de64b72e355b000000000000000000000000c6d13a49cdc5afc7798dd0eba7698db9bfcc1d8c

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061030a5760003560e01c806391b46e051161019c578063db41039f116100ee578063ed29c12b11610097578063f848767311610071578063f8487673146107e7578063f884614b146107fa578063f90889c91461080d5761030a565b8063ed29c12b1461079a578063ee0862bf146107c1578063f51dfe7a146107d45761030a565b8063e69e2662116100c8578063e69e266214610755578063e9c3f77d1461075e578063ebe64bcc146107875761030a565b8063db41039f14610707578063dc683bcf1461071a578063e695fa681461072d5761030a565b8063cc4b26a311610150578063d19bd0a71161012a578063d19bd0a7146106d8578063d2b48934146106e1578063d4437dd8146106f45761030a565b8063cc4b26a314610668578063cd3b5dfb1461067b578063cfc14f53146106c55761030a565b8063ae4479dd11610181578063ae4479dd1461062f578063b42f60db14610642578063bfdeb719146106555761030a565b806391b46e051461059c578063ad037af6146105af5761030a565b806349d0e2ee1161026057806376668b67116102095780637a009135116101e35780637a009135146104fb5780637a1a04df1461050e57806383a042291461053c5761030a565b806376668b67146104ac57806376c308f2146104bf578063797384b2146104c85761030a565b80636d8d36ba1161023a5780636d8d36ba146104735780636e60e2491461048657806372d01257146104995761030a565b806349d0e2ee1461042d5780634b86daab1461044057806364f85ab1146104535761030a565b806328108026116102c2578063392f5f641161029c578063392f5f64146103e0578063447d52ba1461040757806346c87f801461041a5761030a565b806328108026146103795780632a6a897b146103ba57806336953912146103cd5761030a565b806314189db2116102f357806314189db21461033757806316de7a431461035d578063267031b8146103705761030a565b8063071060a61461030f5780630f7c43dc14610324575b600080fd5b61032261031d366004612a4c565b610820565b005b6103226103323660046129cd565b610917565b61034a610345366004612916565b6109a5565b6040519081526020015b60405180910390f35b61032261036b3660046129cd565b6109d0565b61034a60095481565b6103a2610387366004612916565b600a602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610354565b6103226103c83660046129cd565b610a62565b6103226103db366004612a4c565b610b01565b6103a27f000000000000000000000000c6d13a49cdc5afc7798dd0eba7698db9bfcc1d8c81565b610322610415366004612a64565b610b7f565b61034a610428366004612916565b610c70565b61032261043b366004612a4c565b610c98565b61032261044e366004612916565b610d16565b61034a610461366004612916565b600b6020526000908152604090205481565b6103226104813660046129cd565b610d23565b61034a610494366004612955565b610e44565b61034a6104a7366004612916565b610ebe565b61034a6104ba366004612a88565b610edf565b61034a60025481565b6104eb6104d6366004612916565b600c6020526000908152604090205460ff1681565b6040519015158152602001610354565b610322610509366004612916565b610f19565b6104eb61051c3660046129cd565b600160209081526000928352604080842090915290825290205460ff1681565b61057c61054a366004612955565b600860209081526000928352604080842090915290825290208054600182015460028301546003909301549192909184565b604080519485526020850193909352918301526060820152608001610354565b6103226105aa366004612a4c565b610fff565b6105fa6105bd366004612916565b6004602081905260009182526040909120805460018201546002830154600384015494840154600585015460069095015493959294919390919087565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e001610354565b61032261063d3660046129cd565b61107d565b6103226106503660046129f8565b611172565b61034a610663366004612916565b611269565b610322610676366004612916565b611301565b6106aa610689366004612916565b60056020526000908152604090208054600182015460029092015490919083565b60408051938452602084019290925290820152606001610354565b6103226106d3366004612916565b611480565b61034a60075481565b6103226106ef3660046129cd565b6114d0565b610322610702366004612a4c565b6115b4565b61032261071536600461298d565b6115db565b61034a610728366004612916565b611651565b61074061073b366004612a88565b611681565b60408051928352602083019190915201610354565b61034a60035481565b6103a261076c366004612a4c565b6000602081905290815260409020546001600160a01b031681565b610322610795366004612955565b61176f565b6103a27f000000000000000000000000aa4e3edb11afa93c41db59842b29de64b72e355b81565b6103226107cf3660046129cd565b611891565b61034a6107e2366004612916565b611a12565b6103226107f5366004612a4c565b611a33565b610322610808366004612916565b611ab1565b61032261081b3660046129cd565b611b3a565b6040517fb4ed0b6d000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000c6d13a49cdc5afc7798dd0eba7698db9bfcc1d8c6001600160a01b03169063b4ed0b6d9060240160206040518083038186803b15801561089957600080fd5b505afa1580156108ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d19190612939565b60009182526020829052604090912080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03909216919091179055565b6001600160a01b03821660009081526008602090815260408083203380855292529091209061094b90849083908590611cfe565b805461098a576001600160a01b038316600090815260086020908152604080832033845290915281208181556001810182905560028101829055600301555b610995833384611de4565b6109a0833384611f6d565b505050565b6001600160a01b03811660009081526005602052604081206109c78142611fb9565b9150505b919050565b6001600160a01b0382166000908152600c602052604090205460ff16610a3d5760405162461bcd60e51b815260206004820152601360248201527f4e6f7420617070726f766564206973737565720000000000000000000000000060448201526064015b60405180910390fd5b610a48823383611ffa565b610a538233836120b9565b610a5e82338361212c565b5050565b3360009081526001602090815260408083206003845290915290205460ff16610acd5760405162461bcd60e51b815260206004820152601560248201527f4e6f7420617070726f76656420626f72726f77657200000000000000000000006044820152606401610a34565b6001600160a01b03821660009081526004602052604081206001018054839290610af8908490612b17565b90915550505050565b33610b0a61216f565b6001600160a01b03161480610b2e575033610b23612207565b6001600160a01b0316145b610b7a5760405162461bcd60e51b815260206004820152601e60248201527f526f6c65733a2063616c6c6572206973206e6f7420746865206f776e657200006044820152606401610a34565b600755565b6040517f93552a3d000000000000000000000000000000000000000000000000000000008152600481018390526001600160a01b0382811660248301527f000000000000000000000000c6d13a49cdc5afc7798dd0eba7698db9bfcc1d8c16906393552a3d9060440160206040518083038186803b158015610c0057600080fd5b505afa158015610c14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c389190612a2c565b6001600160a01b0391909116600090815260016020908152604080832094835293905291909120805460ff1916911515919091179055565b6001600160a01b0381166000908152600560205260408120610c9181612278565b5492915050565b33610ca161216f565b6001600160a01b03161480610cc5575033610cba612207565b6001600160a01b0316145b610d115760405162461bcd60e51b815260206004820152601e60248201527f526f6c65733a2063616c6c6572206973206e6f7420746865206f776e657200006044820152606401610a34565b600355565b610d20818261176f565b50565b336000908152600160209081526040808320600b845290915290205460ff16610d8e5760405162461bcd60e51b815260206004820152601660248201527f4e6f7420616e20617070726f766564206c656e646572000000000000000000006044820152606401610a34565b6001600160a01b0382166000908152600c602052604090205460ff16610df65760405162461bcd60e51b815260206004820152601360248201527f4e6f7420617070726f76656420697373756572000000000000000000000000006044820152606401610a34565b6001600160a01b0382166000908152600460205260409020610e1881836122ac565b6001600160a01b0383166000908152600660205260409020610e3e9084906102586122c9565b50505050565b6001600160a01b038083166000818152600860209081526040808320948616835293815283822060018101549383526006909152928120909291908390610e8b9042611fb9565b90508115610eaa578254610ea09082846123e2565b9350505050610eb8565b5050549050610eb8565b5050505b92915050565b6001600160a01b0381166000908152600660205260408120610eb890612401565b6001600160a01b03821660009081526005602052604081208190610f039042611fb9565b9050610f108582856123e2565b95945050505050565b33610f2261216f565b6001600160a01b03161480610f46575033610f3b612207565b6001600160a01b0316145b80610f6c57503360009081526001602090815260408083206009845290915290205460ff165b610fde5760405162461bcd60e51b815260206004820152603960248201527f43616c6c6572206973206e6f7420746865206f776e65722c206578656375746f60448201527f72206f7220617574686f72697a656420616374697661746f72000000000000006064820152608401610a34565b6001600160a01b03166000908152600c60205260409020805460ff19169055565b3361100861216f565b6001600160a01b0316148061102c575033611021612207565b6001600160a01b0316145b6110785760405162461bcd60e51b815260206004820152601e60248201527f526f6c65733a2063616c6c6572206973206e6f7420746865206f776e657200006044820152606401610a34565b600955565b336000908152600160209081526040808320600b845290915290205460ff166110e85760405162461bcd60e51b815260206004820152601660248201527f4e6f7420616e20617070726f766564206c656e646572000000000000000000006044820152606401610a34565b6001600160a01b0382166000908152600c602052604090205460ff166111505760405162461bcd60e51b815260206004820152601360248201527f4e6f7420617070726f76656420697373756572000000000000000000000000006044820152606401610a34565b6001600160a01b0382166000908152600460205260409020610e188183612461565b3361117b61216f565b6001600160a01b0316148061119f575033611194612207565b6001600160a01b0316145b806111c557503360009081526001602090815260408083206009845290915290205460ff165b6112375760405162461bcd60e51b815260206004820152603960248201527f43616c6c6572206973206e6f7420746865206f776e65722c206578656375746f60448201527f72206f7220617574686f72697a656420616374697661746f72000000000000006064820152608401610a34565b6001600160a01b0390921660009081526004602081905260409091206005810193909355600683019190915542910155565b6001600160a01b0381166000908152600460205260408120600581015442118061129a575080600501548160040154115b156112a95760009150506109cb565b6000816004015482600501546112bf9190612b17565b82549091506112cf908290612ada565b6112da906001612aae565b60068301546112ee9064496cebb800612ada565b6112f89190612ac6565b925050506109cb565b3361130a61216f565b6001600160a01b0316148061132e575033611323612207565b6001600160a01b0316145b8061135457503360009081526001602090815260408083206009845290915290205460ff165b6113c65760405162461bcd60e51b815260206004820152603960248201527f43616c6c6572206973206e6f7420746865206f776e65722c206578656375746f60448201527f72206f7220617574686f72697a656420616374697661746f72000000000000006064820152608401610a34565b6001600160a01b038116600090815260056020526040902080541561142d5760405162461bcd60e51b815260206004820152601360248201527f646f6e27742072652d696e697469616c697a65000000000000000000000000006044820152606401610a34565b6601000000000000808255426001830155612238906103e89061145290601990612ada565b61145c9190612ac6565b6114669190612ac6565b611477906601000000000000612aae565b60029091015550565b6001600160a01b03811660009081526004602052604090206114a18161247e565b6001600160a01b0382166000908152600860209081526040808320338085529252909120610a5e918390612563565b336114d961216f565b6001600160a01b031614806114fd5750336114f2612207565b6001600160a01b0316145b8061152357503360009081526001602090815260408083206009845290915290205460ff165b6115955760405162461bcd60e51b815260206004820152603960248201527f43616c6c6572206973206e6f7420746865206f776e65722c206578656375746f60448201527f72206f7220617574686f72697a656420616374697661746f72000000000000006064820152608401610a34565b6001600160a01b03909116600090815260046020526040902060020155565b336000908152600b6020526040812080548392906115d3908490612aae565b909155505050565b336000908152600160209081526040808320600b845290915290205460ff166116465760405162461bcd60e51b815260206004820152601660248201527f4e6f7420616e20617070726f766564206c656e646572000000000000000000006044820152606401610a34565b6109a08383836120b9565b6001600160a01b03811660009081526006602052604081206116779083906102586122c9565b6002015492915050565b33600090815260016020908152604080832060038452909152812054819060ff166116ee5760405162461bcd60e51b815260206004820152601160248201527f4e6f7420617070726f7665642063616c6c0000000000000000000000000000006044820152606401610a34565b6001600160a01b038416600090815260056020526040902061170f81612278565b8054915061171e8683866123e2565b9250600061172c8785612b17565b6001600160a01b0387166000908152600460205260408120600181018054939450909284929061175d908490612aae565b92505081905550505050935093915050565b3361177861216f565b6001600160a01b0316148061179c575033611791612207565b6001600160a01b0316145b806117c257503360009081526001602090815260408083206009845290915290205460ff165b6118345760405162461bcd60e51b815260206004820152603960248201527f43616c6c6572206973206e6f7420746865206f776e65722c206578656375746f60448201527f72206f7220617574686f72697a656420616374697661746f72000000000000006064820152608401610a34565b6001600160a01b039182166000908152600c60209081526040808320805460ff19166001179055600a90915290208054919092167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116179055565b3360009081526001602090815260408083206003845290915290205460ff166118fc5760405162461bcd60e51b815260206004820152601560248201527f4e6f7420617070726f76656420626f72726f77657200000000000000000000006044820152606401610a34565b6001600160a01b0382166000908152600c602052604090205460ff166119645760405162461bcd60e51b815260206004820152601360248201527f4e6f7420617070726f76656420697373756572000000000000000000000000006044820152606401610a34565b6001600160a01b038216600090815260046020526040812060018101805491928492611991908490612aae565b90915550506001600160a01b03831660009081526006602052604090206119bc908490610e106122c9565b506001810154815410156109a05760405162461bcd60e51b815260206004820152601460248201527f496e73756666696369656e74206c656e64696e670000000000000000000000006044820152606401610a34565b6001600160a01b0381166000908152600560205260408120610eb890612401565b33611a3c61216f565b6001600160a01b03161480611a60575033611a55612207565b6001600160a01b0316145b611aac5760405162461bcd60e51b815260206004820152601e60248201527f526f6c65733a2063616c6c6572206973206e6f7420746865206f776e657200006044820152606401610a34565b600255565b6001600160a01b038116600090815260086020908152604080832033808552925290912080549091611ae890849084908490611cfe565b611af3833383611de4565b6001600160a01b038316600090815260086020908152604080832033808552925282208281556001810183905560028101839055600301919091556109a090849083611f6d565b33611b4361216f565b6001600160a01b03161480611b67575033611b5c612207565b6001600160a01b0316145b80611b8d57503360009081526001602090815260408083206009845290915290205460ff165b611bff5760405162461bcd60e51b815260206004820152603960248201527f43616c6c6572206973206e6f7420746865206f776e65722c206578656375746f60448201527f72206f7220617574686f72697a656420616374697661746f72000000000000006064820152608401610a34565b6001600160a01b03821660009081526006602052604090208054611cae5760006122386064611c35856601000000000000612ada565b611c3f9190612ac6565b611c499190612ac6565b611c5a906601000000000000612aae565b60408051606081018252660100000000000081524260208083019182528284019485526001600160a01b038916600090815260069091529290922090518155905160018201559051600290910155506109a0565b6000611cbd84836102586122c9565b90506122386064611cce8582612aae565b611cdf906601000000000000612ada565b611ce99190612ac6565b611cf39190612ac6565b600290910155505050565b6001600160a01b0384166000908152600460205260409020611d2090836122ac565b611d2b848483612699565b6000610e10846002015442611d409190612b17565b611d4a9190612b67565b9050806007541015611dc45760405162461bcd60e51b815260206004820152603f60248201527f5472696564207769746864726177696e67206f7574736964652073756273637260448201527f697074696f6e2063616e63656c6c6174696f6e2074696d652077696e646f77006064820152608401610a34565b82846000016000828254611dd89190612b17565b90915550505050505050565b6001600160a01b0383166000908152600b60205260409020548015801590611e0c5750600082115b15611e8b576001600160a01b0384166000908152600460205260408120549081611e36818561273f565b611e409086612ada565b611e4a9190612ac6565b9050611e568185612b17565b6001600160a01b0387166000908152600b6020526040812080549296508392909190611e83908490612b17565b909155505050505b6001600160a01b038085166000908152600a602052604090205416611ee1606560009081526020527f4a7203f705e51df4a56139a9b86b4a0970bed7ab7a3446eaffe2289989c1645c546001600160a01b031690565b6040517fd9caed120000000000000000000000000000000000000000000000000000000081526001600160a01b038381166004830152868116602483015260448201869052919091169063d9caed1290606401600060405180830381600087803b158015611f4e57600080fd5b505af1158015611f62573d6000803e3d6000fd5b505050505050505050565b604080516001600160a01b038581168252602082018490528416917f3397b60e3ca1e9c0c75a033d62ab8731e9b2937c0690ae5d7f1bd37886b19d7d91015b60405180910390a2505050565b600080836001015483611fcc9190612b17565b9050610258811115611fea57611fe28482612757565b915050610eb8565b50508154610eb8565b5092915050565b7f4a7203f705e51df4a56139a9b86b4a0970bed7ab7a3446eaffe2289989c1645c546001600160a01b038481166000908152600a60205260408082205481517fb3db428b00000000000000000000000000000000000000000000000000000000815287851660048201529084166024820152604481018690529051929093169263b3db428b926064808301939282900301818387803b15801561209c57600080fd5b505af11580156120b0573d6000803e3d6000fd5b50505050505050565b6001600160a01b038084166000818152600860209081526040808320948716835293815283822092825260049052919091206120f58184612461565b612100858386612699565b815461211857612112610e1042612b67565b60028301555b82826000016000828254611dd89190612aae565b604080516001600160a01b038581168252602082018490528416917f8ec70c9430377a4bba035133e27a5b0f3b9951bc080833f6832bcbbb70a96dba9101611fac565b60007f000000000000000000000000c6d13a49cdc5afc7798dd0eba7698db9bfcc1d8c6001600160a01b0316638da5cb5b6040518163ffffffff1660e01b815260040160206040518083038186803b1580156121ca57600080fd5b505afa1580156121de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122029190612939565b905090565b60007f000000000000000000000000c6d13a49cdc5afc7798dd0eba7698db9bfcc1d8c6001600160a01b031663c34c08e56040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561226457600080fd5b505af11580156121de573d6000803e3d6000fd5b600081600101544261228a9190612b17565b9050610258811115610a5e576122a08282612757565b82554260018301555050565b6122b58261247e565b80826000016000828254610af89190612b17565b6001820154600090816122dc8242612b17565b9050838111156123d8576001600160a01b03861660009081526005602052604090206123088683612757565b86556001600160a01b038716600090815260046020526040902080546001820154612333919061286f565b600288015542600188015561234782612278565b6123cd6122386103e861236266010000000000006019612ada565b61236c9190612ac6565b6123769190612ac6565b612387906601000000000000612aae565b606466010000000000008a600201546123a09190612b17565b6009546123ad9190612ada565b6123b79190612ac6565b6123c8906601000000000000612aae565b6128fe565b826002018190555050505b5092949350505050565b6000816123ef8486612ada565b6123f99190612ac6565b949350505050565b600281015460009081610e106124206601000000000000612710612ada565b61242c84612710612ada565b6124369190612b17565b612444906301e13380612ada565b61244e9190612ac6565b90506123f9660100000000000082612ac6565b61246a8261247e565b80826000016000828254610af89190612aae565b600061248e82600501544261273f565b9050600082600601541180156124a75750816004015481115b15610a5e5760008260040154826124be9190612b17565b905060006124fc8460060154856004015486600501546124de9190612b17565b60068701546124ed9086612ada565b6124f79190612ac6565b61273f565b9050808460060160008282546125129190612b17565b90915550508354612524906001612aae565b612535660100000000000083612ada565b61253f9190612ac6565b8460030160008282546125529190612aae565b909155505042600485015550505050565b6000836003015483600301546125799190612b17565b90508015610e3e5783546000906601000000000000906125999084612ada565b6125a39190612ac6565b90506125e0606560009081526020527f4a7203f705e51df4a56139a9b86b4a0970bed7ab7a3446eaffe2289989c1645c546001600160a01b031690565b6040517fd9caed120000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000aa4e3edb11afa93c41db59842b29de64b72e355b81166004830152858116602483015260448201849052919091169063d9caed1290606401600060405180830381600087803b15801561266d57600080fd5b505af1158015612681573d6000803e3d6000fd5b5050505081856003016000828254611dd89190612aae565b60018201546001600160a01b03841660009081526006602052604081206126c49086906102586122c9565b6001600160a01b03861660009081526004602052604090209091508215612724576126f0858286612563565b84548254612700908290866123e2565b808755600090612711908390612b17565b905061271d8382612461565b505061272f565b600380820154908601555b5054600190930192909255505050565b600081831115612750575080610eb8565b5081610eb8565b600080612766610e1084612b67565b905061277b6601000000000000610e10612ada565b81660100000000000086600201546127939190612b17565b865461279f9190612ada565b6127a99190612ada565b6127b39190612ac6565b84546127bf9190612aae565b915060006127cf610e1085612ac6565b90508015612867578260005b80831180156127ea5750806004115b156128255766010000000000008760020154866128079190612ada565b6128119190612ac6565b94508061281d81612b2e565b9150506127db565b506004821115610eb457600461283b8184612b17565b6128458387612b17565b61284f9190612ada565b6128599190612ac6565b6128639085612aae565b9350505b505092915050565b660100000000000060008361288557600061289b565b83612891846064612ada565b61289b9190612ac6565b905060528110156128c4576002546128b39082612ada565b6128bd9083612aae565b9150611ff3565b6003546128d2605283612b17565b6128dc9190612ada565b6002546128ea906052612ada565b6128f49190612aae565b6123f99083612aae565b60008183111561290f575081610eb8565b5080610eb8565b600060208284031215612927578081fd5b813561293281612bd9565b9392505050565b60006020828403121561294a578081fd5b815161293281612bd9565b60008060408385031215612967578081fd5b823561297281612bd9565b9150602083013561298281612bd9565b809150509250929050565b6000806000606084860312156129a1578081fd5b83356129ac81612bd9565b925060208401356129bc81612bd9565b929592945050506040919091013590565b600080604083850312156129df578182fd5b82356129ea81612bd9565b946020939093013593505050565b600080600060608486031215612a0c578283fd5b8335612a1781612bd9565b95602085013595506040909401359392505050565b600060208284031215612a3d578081fd5b81518015158114612932578182fd5b600060208284031215612a5d578081fd5b5035919050565b60008060408385031215612a76578182fd5b82359150602083013561298281612bd9565b600080600060608486031215612a9c578283fd5b8335925060208401356129bc81612bd9565b60008219821115612ac157612ac1612b7b565b500190565b600082612ad557612ad5612baa565b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612b1257612b12612b7b565b500290565b600082821015612b2957612b29612b7b565b500390565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415612b6057612b60612b7b565b5060010190565b600082612b7657612b76612baa565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6001600160a01b0381168114610d2057600080fdfea26469706673582212208a451470ba8ee8ff877e735ffaac79f28bd8ba5c62f6730bd169f0963e1d946e64736f6c63430008030033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000aa4e3edb11afa93c41db59842b29de64b72e355b000000000000000000000000c6d13a49cdc5afc7798dd0eba7698db9bfcc1d8c

-----Decoded View---------------
Arg [0] : _MFI (address): 0xAa4e3edb11AFa93c41db59842b29de64b72E355B
Arg [1] : _roles (address): 0xC6D13A49cdC5Afc7798dd0eBA7698DB9BFCc1D8c

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000aa4e3edb11afa93c41db59842b29de64b72e355b
Arg [1] : 000000000000000000000000c6d13a49cdc5afc7798dd0eba7698db9bfcc1d8c


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.