Sepolia Testnet

Contract

0xd176bC4E1ab3546b8f446527c620164952385e41

Overview

ETH Balance

0 ETH

More Info

Multichain Info

N/A
Transaction Hash
Method
Block
From
To

There are no matching entries

2 Internal Transactions found.

Latest 2 internal transactions

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x60a0604048760822023-12-13 2:04:36585 days ago1702433076
0xd176bC4E...952385e41
 Contract Creation0 ETH
0x6101a06048760822023-12-13 2:04:36585 days ago1702433076  Contract Creation0 ETH

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xc3Ce4BFf...9ba910965
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
SecuredLine

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 200 runs

Other Settings:
london EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;
import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol";
import {LineLib} from "../../utils/LineLib.sol";
import {EscrowedLine} from "./EscrowedLine.sol";
import {SpigotedLine} from "./SpigotedLine.sol";
import {SpigotedLineLib} from "../../utils/SpigotedLineLib.sol";
import {LineOfCredit} from "./LineOfCredit.sol";
import {ILineOfCredit} from "../../interfaces/ILineOfCredit.sol";
import {ISecuredLine} from "../../interfaces/ISecuredLine.sol";

/**
 * @title  - Credit Cooperative Secured Line of Credit
 * @notice - The SecuredLine combines both collateral modules (SpigotedLine + EscrowedLine) with core lending functionality from LineOfCredit
 *         - to create a fully secured lending facility backed by revenue via Spigot or tokens via Escrow.
 * @dev    - modifies _liquidate(), _healthcheck(), _init(), and _declareInsolvent() functionality
 */
contract SecuredLine is SpigotedLine, EscrowedLine, ISecuredLine {
    constructor(
        address oracle_,
        address arbiter_,
        address borrower_,
        address payable swapTarget_,
        address spigot_,
        address escrow_,
        uint ttl_,
        uint8 defaultSplit_
    ) SpigotedLine(oracle_, arbiter_, borrower_, spigot_, swapTarget_, ttl_, defaultSplit_) EscrowedLine(escrow_) {}

    /**
     * @dev requires both Spigot and Escrow to pass _init to succeed
     */
    function _init() internal virtual override(SpigotedLine, EscrowedLine) {
        SpigotedLine._init();
        EscrowedLine._init();
    }

    /// see ISecuredLine.rollover
    function rollover(address newLine) external override onlyBorrower {
        // require all debt successfully paid already
        if (status != LineLib.STATUS.REPAID) {
            revert DebtOwed();
        }
        // require new line isn't activated yet
        if (ILineOfCredit(newLine).status() != LineLib.STATUS.UNINITIALIZED) {
            revert BadNewLine();
        }
        // we dont check borrower is same on both lines because borrower might want new address managing new line
        EscrowedLine._rollover(newLine);
        SpigotedLineLib.rollover(address(spigot), newLine);

        // ensure that line we are sending can accept them. There is no recovery option.
        try ILineOfCredit(newLine).init() {} catch {
            revert BadRollover();
        }
    }

    //  see IEscrowedLine.liquidate
    function liquidate(uint256 amount, address targetToken) external returns (uint256) {
        if (msg.sender != arbiter) {
            revert CallerAccessDenied();
        }
        if (_updateStatus(_healthcheck()) != LineLib.STATUS.LIQUIDATABLE) {
            revert NotLiquidatable();
        }

        // send tokens to arbiter for OTC sales
        return _liquidate(ids[0], amount, targetToken, msg.sender);
    }

    function _healthcheck() internal override(EscrowedLine, LineOfCredit) returns (LineLib.STATUS) {
        // check core (also cheap & internal) covenants before checking collateral conditions
        LineLib.STATUS s = LineOfCredit._healthcheck();
        if (s != LineLib.STATUS.ACTIVE) {
            // return early if non-default answer
            return s;
        }

        // check collateral ratio and return ACTIVE
        return EscrowedLine._healthcheck();
    }

    /**
     * @notice Wrapper for SpigotedLine and EscrowedLine internal functions
     * @dev - both underlying calls MUST return true for Line status to change to INSOLVENT
     * @return isInsolvent - if the entire Line including all collateral sources is fuly insolvent.
     */
    function _canDeclareInsolvent() internal virtual override(EscrowedLine, SpigotedLine) returns (bool) {
        return (EscrowedLine._canDeclareInsolvent() && SpigotedLine._canDeclareInsolvent());
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;
import {IInterestRateCredit} from "../interfaces/IInterestRateCredit.sol";
import {ILineOfCredit} from "../interfaces/ILineOfCredit.sol";
import {IOracle} from "../interfaces/IOracle.sol";
import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol";
import {SafeERC20} from "openzeppelin/token/ERC20/utils/SafeERC20.sol";
import {Denominations} from "chainlink/Denominations.sol";

/**
 * @title Credit Cooperative Line of Credit Library
 * @notice Core logic and variables to be reused across all Credit Cooperative Marketplace Line of Credit contracts
 */
library LineLib {
    using SafeERC20 for IERC20;

    address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; // mainnet WETH

    error EthSentWithERC20();
    error TransferFailed();
    error SendingEthFailed();
    error RefundEthFailed();

    error BadToken();

    event RefundIssued(address indexed recipient, uint256 value);

    enum STATUS {
        UNINITIALIZED,
        ACTIVE,
        LIQUIDATABLE,
        REPAID,
        INSOLVENT
    }

    /**
     * @notice - Send ETH or ERC20 token from this contract to an external contract
     * @param token - address of token to send out. Denominations.ETH for raw ETH
     * @param receiver - address to send tokens to
     * @param amount - amount of tokens to send
     */
    function sendOutTokenOrETH(address token, address receiver, uint256 amount) external returns (bool) {
        if (token == address(0)) {
            revert TransferFailed();
        }

        // both branches revert if call failed
        if (token != Denominations.ETH) {
            // ERC20
            IERC20(token).safeTransfer(receiver, amount);
        } else {
            // ETH
            bool success = _safeTransferFunds(receiver, amount);
            if (!success) {
                revert SendingEthFailed();
            }
        }
        return true;
    }

    /**
     * @notice - Receive ETH or ERC20 token at this contract from an external contract
     * @dev    - If the sender overpays, the difference will be refunded to the sender
     * @dev    - If the sender is unable to receive the refund, it will be diverted to the calling contract
     * @param token - address of token to receive. Denominations.ETH for raw ETH
     * @param sender - address that is sendingtokens/ETH
     * @param amount - amount of tokens to send
     */
    function receiveTokenOrETH(address token, address sender, uint256 amount) external returns (bool) {
        if (token == address(0)) {
            revert TransferFailed();
        }
        if (token != Denominations.ETH) {
            // ERC20
            if (msg.value != 0) {
                revert EthSentWithERC20();
            }
            IERC20(token).safeTransferFrom(sender, address(this), amount);
        } else {
            // ETH
            if (msg.value < amount) {
                revert TransferFailed();
            }

            if (msg.value > amount) {
                uint256 refund = msg.value - amount;

                if (_safeTransferFunds(msg.sender, refund)) {
                    emit RefundIssued(msg.sender, refund);
                }
            }
        }
        return true;
    }

    /**
     * @notice - Helper function to get current balance of this contract for ERC20 or ETH
     * @param token - address of token to check. Denominations.ETH for raw ETH
     */
    function getBalance(address token) external view returns (uint256) {
        if (token == address(0)) return 0;
        return token != Denominations.ETH ? IERC20(token).balanceOf(address(this)) : address(this).balance;
    }

    /**
     * @notice  - Helper function to safely transfer Eth using native call
     * @dev     - Errors should be handled in the calling function
     * @param recipient - address of the recipient
     * @param value - value to be sent (in wei)
     */
    function _safeTransferFunds(address recipient, uint256 value) internal returns (bool success) {
        (success, ) = payable(recipient).call{value: value}("");
    }
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

import {IEscrow} from "../../interfaces/IEscrow.sol";
import {LineLib} from "../../utils/LineLib.sol";
import {IEscrowedLine} from "../../interfaces/IEscrowedLine.sol";
import {ILineOfCredit} from "../../interfaces/ILineOfCredit.sol";

// used for importing NATSPEC docs, not used
import {LineOfCredit} from "./LineOfCredit.sol";

// import { SecuredLine } from "./SecuredLine.sol";

abstract contract EscrowedLine is IEscrowedLine, ILineOfCredit {
    // contract holding all collateral for borrower
    IEscrow public immutable escrow;

    constructor(address _escrow) {
        escrow = IEscrow(_escrow);
    }

    /**
     * see LineOfCredit._init and SecuredLine.init
     * @notice requires this Line is owner of the Escrowed collateral else Line will not init
     */
    function _init() internal virtual {
        if (escrow.line() != address(this)) revert BadModule(address(escrow));
    }

    /**
     * see LineOfCredit._healthcheck and SecuredLine._healthcheck
     * @notice returns LIQUIDATABLE if Escrow contract is undercollateralized, else returns ACTIVE
     */
    function _healthcheck() internal virtual returns (LineLib.STATUS) {
        if (escrow.isLiquidatable()) {
            return LineLib.STATUS.LIQUIDATABLE;
        }

        return LineLib.STATUS.ACTIVE;
    }

    /**
     * see SecuredlLine.liquidate
     * @notice sends escrowed tokens to liquidation.
     * @dev priviliegad function. Do checks before calling.
     *
     * @param id - The credit line being repaid via the liquidation
     * @param amount - amount of tokens to take from escrow and liquidate
     * @param targetToken - the token to take from escrow
     * @param to - the liquidator to send tokens to. could be OTC address or smart contract
     *
     * @return amount - the total amount of `targetToken` sold to repay credit
     */
    function _liquidate(
        bytes32 id,
        uint256 amount,
        address targetToken,
        address to
    ) internal virtual returns (uint256) {
        IEscrow escrow_ = escrow; // gas savings
        require(escrow_.liquidate(amount, targetToken, to));

        emit Liquidate(id, amount, targetToken, address(escrow_));

        return amount;
    }

    /**
     * see SecuredLine.declareInsolvent
     * @notice require all collateral sold off before declaring insolvent
     *(@dev priviliegad internal function.
     * @return isInsolvent - if Escrow contract is currently insolvent or not
     */
    function _canDeclareInsolvent() internal virtual returns (bool) {
        if (escrow.getCollateralValue() != 0) {
            revert NotInsolvent(address(escrow));
        }
        return true;
    }

    /**
     * see SecuredlLine.rollover
     * @notice helper function to allow borrower to easily swithc collateral to a new Line after repyment
     *(@dev priviliegad internal function.
     * @dev MUST only be callable if line is REPAID
     * @return - if function successfully executed
     */
    function _rollover(address newLine) internal virtual returns (bool) {
        require(escrow.updateLine(newLine));
        return true;
    }
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

import {Denominations} from "chainlink/Denominations.sol";
import {LineOfCredit} from "./LineOfCredit.sol";
import {LineLib} from "../../utils/LineLib.sol";
import {CreditLib} from "../../utils/CreditLib.sol";
import {SpigotedLineLib} from "../../utils/SpigotedLineLib.sol";
import {MutualConsent} from "../../utils/MutualConsent.sol";
import {ISpigot} from "../../interfaces/ISpigot.sol";
import {ISpigotedLine} from "../../interfaces/ISpigotedLine.sol";
import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol";
import {SafeERC20} from "openzeppelin/token/ERC20/utils/SafeERC20.sol";

/**
  * @title  - Credit Cooperative Spigoted Line of Credit
  * @notice - Line of Credit contract with additional functionality for integrating with a Spigot and revenue based collateral.
            - Allows Arbiter to repay debt using collateralized revenue streams onbehalf of Borrower and Lender(s)
  * @dev    - Inherits LineOfCredit functionality
 */
contract SpigotedLine is ISpigotedLine, LineOfCredit {
    using SafeERC20 for IERC20;

    /// @notice see Spigot
    ISpigot public immutable spigot;

    /// @notice - maximum revenue we want to be able to take from spigots if Line is in default
    uint8 constant MAX_SPLIT = 100;

    /// @notice % of revenue tokens to take from Spigot if the Line of Credit is healthy. 0 decimals
    uint8 public immutable defaultRevenueSplit;

    /// @notice exchange aggregator (mainly 0x router) to trade revenue tokens from a Spigot for credit tokens owed to lenders
    address payable public immutable swapTarget;

    /**
     * @notice - excess unsold revenue claimed from Spigot to be sold later or excess credit tokens bought from revenue but not yet used to repay debt
     *         - needed because the Line of Credit might have the same token being lent/borrower as being bought/sold so need to separate accounting.
     * @dev    - private variable so other Line modules do not interfer with Spigot functionality
     */
    mapping(address => uint256) private unusedTokens;

    /**
     * @notice - The SpigotedLine is a LineofCredit contract with additional functionality for integrating with a Spigot.
               - allows Borrower or Lender to repay debt using collateralized revenue streams
     * @param oracle_ - price oracle to use for getting all token values
     * @param arbiter_ - neutral party with some special priviliges on behalf of borrower and lender
     * @param borrower_ - the debitor for all credit positions in this contract
     * @param spigot_ - Spigot smart contract that is owned by this Line
     * @param swapTarget_ - 0x protocol exchange address to send calldata for trades to exchange revenue tokens for credit tokens
     * @param ttl_ - time to live for line of credit contract across all lenders set at deployment in order to set the term/expiry date
     * @param defaultRevenueSplit_ - The % of Revenue Tokens that the Spigot escrows for debt repayment if the Line is healthy.
     */
    constructor(
        address oracle_,
        address arbiter_,
        address borrower_,
        address spigot_,
        address payable swapTarget_,
        uint256 ttl_,
        uint8 defaultRevenueSplit_
    ) LineOfCredit(oracle_, arbiter_, borrower_, ttl_) {
        require(defaultRevenueSplit_ <= MAX_SPLIT);

        spigot = ISpigot(spigot_);
        defaultRevenueSplit = defaultRevenueSplit_;
        swapTarget = swapTarget_;
    }

    /**
     * see LineOfCredit._init and Securedline.init
     * @notice requires this Line is owner of the Escrowed collateral else Line will not init
     */
    function _init() internal virtual override(LineOfCredit) {
        if (spigot.owner() != address(this)) revert BadModule(address(spigot));
    }

    /**
     * see SecuredLine.declareInsolvent
     * @notice requires Spigot contract itselgf to be transfered to Arbiter and sold off to a 3rd party before declaring insolvent
     *(@dev priviliegad internal function.
     * @return isInsolvent - if Spigot contract is currently insolvent or not
     */
    function _canDeclareInsolvent() internal virtual override returns (bool) {
        return SpigotedLineLib.canDeclareInsolvent(address(spigot), arbiter);
    }

    /// see ISpigotedLine.claimAndRepay
    function claimAndRepay(
        address claimToken,
        bytes calldata zeroExTradeData
    ) external whileBorrowing nonReentrant returns (uint256) {
        bytes32 id = ids[0];
        Credit memory credit = _accrue(credits[id], id);

        if (msg.sender != arbiter) {
            revert CallerAccessDenied();
        }

        uint256 newTokens = _claimAndTrade(claimToken, credit.token, zeroExTradeData);

        uint256 repaid = newTokens + unusedTokens[credit.token];
        uint256 debt = credit.interestAccrued + credit.principal;

        // cap payment to debt value
        if (repaid > debt) repaid = debt;

        // update reserves based on usage
        if (repaid > newTokens) {
            // if using `unusedTokens` to repay line, reduce reserves
            uint256 diff = repaid - newTokens;
            emit ReservesChanged(credit.token, -int256(diff), 1);
            unusedTokens[credit.token] -= diff;
        } else {
            // else high revenue and bought more credit tokens than owed, fill reserves
            uint256 diff = newTokens - repaid;
            emit ReservesChanged(credit.token, int256(diff), 1);
            unusedTokens[credit.token] += diff;
        }

        credits[id] = _repay(credit, id, repaid, address(0)); // no payer, we already have funds

        emit RevenuePayment(claimToken, repaid);

        return newTokens;
    }

    /// see ISpigotedLine.useAndRepay
    function useAndRepay(uint256 amount) external whileBorrowing returns (bool) {
        bytes32 id = ids[0];
        Credit memory credit = credits[id];

        if (msg.sender != borrower && msg.sender != credit.lender) {
            revert CallerAccessDenied();
        }

        if (amount > credit.principal + credit.interestAccrued) {
            revert RepayAmountExceedsDebt(credit.principal + credit.interestAccrued);
        }

        if (amount > unusedTokens[credit.token]) {
            revert ReservesOverdrawn(credit.token, unusedTokens[credit.token]);
        }

        // reduce reserves before _repay calls token to prevent reentrancy
        unusedTokens[credit.token] -= amount;
        emit ReservesChanged(credit.token, -int256(amount), 0);

        credits[id] = _repay(_accrue(credit, id), id, amount, address(0)); // no payer, we already have funds

        emit RevenuePayment(credit.token, amount);

        return true;
    }

    /// see ISpigotedLine.claimAndTrade
    function claimAndTrade(
        address claimToken,
        bytes calldata zeroExTradeData
    ) external whileBorrowing nonReentrant returns (uint256) {
        if (msg.sender != arbiter) {
            revert CallerAccessDenied();
        }

        address targetToken = credits[ids[0]].token;
        uint256 newTokens = _claimAndTrade(claimToken, targetToken, zeroExTradeData);

        // add bought tokens to unused balance
        unusedTokens[targetToken] += newTokens;
        emit ReservesChanged(targetToken, int256(newTokens), 1);

        return newTokens;
    }

    /**
     * @notice  - Claims revenue tokens escrowed in Spigot and trades them for credit tokens.
     *          - MUST trade all available claim tokens to target credit token.
     *          - Excess credit tokens not used to repay dent are stored in `unused`
     * @dev     - priviliged internal function
     * @param claimToken - The revenue token escrowed in the Spigot to sell in trade
     * @param targetToken - The credit token that needs to be bought in order to pat down debt. Always `credits[ids[0]].token`
     * @param zeroExTradeData - 0x API data to use in trade to sell `claimToken` for target
     *
     * @return - amount of target tokens bought
     */
    function _claimAndTrade(
        address claimToken,
        address targetToken,
        bytes calldata zeroExTradeData
    ) internal returns (uint256) {
        if (claimToken == targetToken) {
            // same asset. dont trade
            return spigot.claimOwnerTokens(claimToken);
        } else {
            // trade revenue token for debt obligation
            (uint256 tokensBought, uint256 totalUnused) = SpigotedLineLib.claimAndTrade(
                claimToken,
                targetToken,
                swapTarget,
                address(spigot),
                unusedTokens[claimToken],
                zeroExTradeData
            );

            // we dont use revenue after this so can store now
            /// @dev ReservesChanged event for claim token is emitted in SpigotedLineLib.claimAndTrade
            unusedTokens[claimToken] = totalUnused;

            // the target tokens purchased
            return tokensBought;
        }
    }

    //  SPIGOT OWNER FUNCTIONS

    /// see ISpigotedLine.updateOwnerSplit
    function updateOwnerSplit(address revenueContract) external returns (bool) {
        return
            SpigotedLineLib.updateSplit(
                address(spigot),
                revenueContract,
                _updateStatus(_healthcheck()),
                defaultRevenueSplit
            );
    }

    /// see ISpigotedLine.addSpigot
    function addSpigot(address revenueContract, ISpigot.Setting calldata setting) external returns (bool) {
        if (msg.sender != arbiter) {
            revert CallerAccessDenied();
        }
        return spigot.addSpigot(revenueContract, setting);
    }

    /// see ISpigotedLine.updateWhitelist
    function updateWhitelist(bytes4 func, bool allowed) external returns (bool) {
        if (msg.sender != arbiter) {
            revert CallerAccessDenied();
        }
        return spigot.updateWhitelistedFunction(func, allowed);
    }

    /// see ISpigotedLine.releaseSpigot
    function releaseSpigot(address to) external returns (bool) {
        return SpigotedLineLib.releaseSpigot(address(spigot), _updateStatus(_healthcheck()), borrower, arbiter, to);
    }

    /// see ISpigotedLine.sweep
    function sweep(address to, address token, uint256 amount) external nonReentrant returns (uint256) {
        uint256 swept = SpigotedLineLib.sweep(
            to,
            token,
            amount,
            unusedTokens[token],
            _updateStatus(_healthcheck()),
            borrower,
            arbiter
        );

        if (swept != 0) {
            unusedTokens[token] -= swept;
            emit ReservesChanged(token, -int256(swept), 1);
        }

        return swept;
    }

    /// see ILineOfCredit.tradeable
    function tradeable(address token) external view returns (uint256) {
        return unusedTokens[token] + spigot.getOwnerTokens(token);
    }

    /// see ILineOfCredit.unused
    function unused(address token) external view returns (uint256) {
        return unusedTokens[token];
    }

    // allow claiming/trading in ETH
    receive() external payable {}
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

import {ISpigot} from "../interfaces/ISpigot.sol";
import {ISpigotedLine} from "../interfaces/ISpigotedLine.sol";
import {LineLib} from "../utils/LineLib.sol";
import {SpigotLib} from "../utils/SpigotLib.sol";
import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol";

import {Denominations} from "chainlink/Denominations.sol";

library SpigotedLineLib {
    /// @notice - maximum revenue we want to be able to take from spigots if Line is in default
    uint8 constant MAX_SPLIT = 100;

    error NoSpigot();

    error TradeFailed();

    error BadTradingPair();

    error CallerAccessDenied();

    error ReleaseSpigotFailed();

    error NotInsolvent(address module);

    error ReservesOverdrawn(address token, uint256 amountAvailable);

    event TradeSpigotRevenue(
        address indexed revenueToken,
        uint256 revenueTokenAmount,
        address indexed debtToken,
        uint256 indexed debtTokensBought
    );

    event ReservesChanged(
        address indexed token,
        int256 indexed diff,
        uint256 tokenType // 0 for revenue token, 1 for credit token
    );

    /**
     * @dev                 - priviliged internal function!
     * @notice              - Allows revenue tokens in 'escrowed' to be traded for credit tokens that aren't yet used to repay debt.
                            - The newly exchanged credit tokens are held in 'unusedTokens' ready for a Lender to withdraw using useAndRepay
                            - This feature allows a Borrower to take advantage of an increase in the value of the revenue token compared
                            - to the credit token and to in effect use less revenue tokens to be later used to repay the same amount of debt.
     * @dev                 - MUST trade all available claimTokens (unused + claimed) to targetTokens
     * @param claimToken    - The revenue token escrowed in the Spigot to sell in trade
     * @param targetToken   - The credit token that needs to be bought in order to pay down debt. Always `credits[ids[0]].token`
     * @param swapTarget    - The 0x exchange router address to call for trades
     * @param spigot        - The Spigot to claim from. Must be owned by adddress(this)
     * @param unused        - Current amount of unused claimTokens
     * @param zeroExTradeData - 0x API data to use in trade to sell `claimToken` for target
     * @return (uint, uint) - amount of targetTokens bought, total unused claimTokens after trade
     */
    function claimAndTrade(
        address claimToken,
        address targetToken,
        address payable swapTarget,
        address spigot,
        uint256 unused,
        bytes calldata zeroExTradeData
    ) external returns (uint256, uint256) {
        // can't trade into same token. causes double count for unused tokens
        if (claimToken == targetToken) {
            revert BadTradingPair();
        }

        // snapshot token balances now to diff after trade executes
        uint256 oldClaimTokens = LineLib.getBalance(claimToken);

        uint256 oldTargetTokens = LineLib.getBalance(targetToken);

        // @dev claim has to be called after we get balance
        // reverts if there are no tokens to claim
        uint256 claimed = ISpigot(spigot).claimOwnerTokens(claimToken);

        trade(claimed + unused, claimToken, swapTarget, zeroExTradeData);

        // underflow revert ensures we have more tokens than we started with
        uint256 tokensBought = LineLib.getBalance(targetToken) - oldTargetTokens;

        if (tokensBought == 0) {
            revert TradeFailed();
        } // ensure tokens bought

        uint256 newClaimTokens = LineLib.getBalance(claimToken);

        // ideally we could use oracle here to calculate # of tokens to receive
        // but sellToken might not have oracle. buyToken must have oracle

        emit TradeSpigotRevenue(claimToken, claimed, targetToken, tokensBought);

        // used reserve revenue to repay debt
        if (oldClaimTokens > newClaimTokens) {
            unchecked {
                // we check all values before math so can use unchecked
                uint256 diff = oldClaimTokens - newClaimTokens;

                emit ReservesChanged(claimToken, -int256(diff), 0);

                // used more tokens than we had in revenue reserves.
                // prevent borrower from pulling idle lender funds to repay other lenders
                if (diff > unused) revert ReservesOverdrawn(claimToken, unused);
                // reduce reserves by consumed amount
                else return (tokensBought, unused - diff);
            }
        } else {
            unchecked {
                // `unused` unlikely to overflow
                // excess revenue in trade. store in reserves
                uint256 diff = newClaimTokens - oldClaimTokens;

                emit ReservesChanged(claimToken, int256(diff), 0);

                return (tokensBought, unused + diff);
            }
        }
    }

    /**
     * @dev                     - priviliged internal function!
     * @notice                  - dumb func that executes arbitry code against a target contract
     * @param amount            - amount of revenue tokens to sell
     * @param sellToken         - revenue token being sold
     * @param swapTarget        - exchange aggregator to trade against
     * @param zeroExTradeData   - Trade data to execute against exchange for target token/amount
     * @return bool             - if trade was successful
     */
    function trade(
        uint256 amount,
        address sellToken,
        address payable swapTarget,
        bytes calldata zeroExTradeData
    ) public returns (bool) {
        if (sellToken == Denominations.ETH) {
            // if claiming/trading eth send as msg.value to dex
            (bool success, ) = swapTarget.call{value: amount}(zeroExTradeData); // TODO: test with 0x api data on mainnet fork
            if (!success) {
                revert TradeFailed();
            }
        } else {
            IERC20(sellToken).approve(swapTarget, amount);
            (bool success, ) = swapTarget.call(zeroExTradeData);
            if (!success) {
                revert TradeFailed();
            }
        }

        return true;
    }

    /**
     * @notice          - cleanup function when a Line of Credit facility has expired. Called in SecuredLine.rollover
     *                  - Reuse a Spigot across a single borrower's Line contracts
     * @param spigot    - The spigot address owned by this SpigotedLine
     * @param newLine   - The new line to transfer Spigot ownership to
     * @return bool     - if Spigot ownership was transferred or not
     */
    function rollover(address spigot, address newLine) external returns (bool) {
        require(ISpigot(spigot).updateOwner(newLine));
        return true;
    }

    function canDeclareInsolvent(address spigot, address arbiter) external view returns (bool) {
        // Must have called releaseSpigot() and sold off protocol / revenue streams already
        address owner_ = ISpigot(spigot).owner();
        if (address(this) == owner_ || arbiter == owner_) {
            revert NotInsolvent(spigot);
        }

        return true;
    }

    /**
     * @notice                  - Changes the revenue split between a Borrower's treasury and the LineOfCredit based on line health, runs with updateOwnerSplit()
     * @dev                     - callable by anyone
     * @param revenueContract   - spigoted contract to update
     * @return sucesss          - whether or not split was updated
     */
    function updateSplit(
        address spigot,
        address revenueContract,
        LineLib.STATUS status,
        uint8 defaultSplit
    ) external returns (bool) {
        (uint8 split, , bytes4 transferFunc) = ISpigot(spigot).getSetting(revenueContract);

        if (transferFunc == bytes4(0)) {
            revert NoSpigot();
        }

        if (status == LineLib.STATUS.ACTIVE && split != defaultSplit) {
            // if Line of Credit is healthy then set the split to the prior agreed default split of revenue tokens
            return ISpigot(spigot).updateOwnerSplit(revenueContract, defaultSplit);
        } else if (status == LineLib.STATUS.LIQUIDATABLE && split != MAX_SPLIT) {
            // if the Line of Credit is in distress then take all revenue to repay debt
            return ISpigot(spigot).updateOwnerSplit(revenueContract, MAX_SPLIT);
        }

        return false;
    }

    /**
   * @notice - Transfers ownership of the entire Spigot and its revenuw streams from its then Owner to either
             - the Borrower (if a Line of Credit has been been fully repaid) or
             - to the Arbiter (if the Line of Credit is liquidatable).
   * @dev    - callable by `borrower` or `arbiter`
   * @return - whether or not Spigot was released
  */
    function releaseSpigot(
        address spigot,
        LineLib.STATUS status,
        address borrower,
        address arbiter,
        address to
    ) external returns (bool) {
        if (status == LineLib.STATUS.REPAID && msg.sender == borrower) {
            if (!ISpigot(spigot).updateOwner(to)) {
                revert ReleaseSpigotFailed();
            }
            return true;
        }

        if (status == LineLib.STATUS.LIQUIDATABLE && msg.sender == arbiter) {
            if (!ISpigot(spigot).updateOwner(to)) {
                revert ReleaseSpigotFailed();
            }
            return true;
        }

        revert CallerAccessDenied();
    }

    /**
      * @notice -  Sends any remaining tokens (revenue or credit tokens) in the Spigot to the Borrower after the loan has been repaid.
                  -  In case of a Borrower default (loan status = liquidatable), this is a fallback mechanism to withdraw all the tokens and send them to the Arbiter
                  -  Does not transfer anything if line is healthy
      * @dev    - callable by `borrower` or `arbiter`
      * @return - whether or not spigot was released
    */
    function sweep(
        address to,
        address token,
        uint256 amount,
        uint256 available,
        LineLib.STATUS status,
        address borrower,
        address arbiter
    ) external returns (uint256) {
        if (available == 0) {
            return 0;
        }
        if (amount == 0) {
            // use all tokens if no amount specified specified
            amount = available;
        } else {
            if (amount > available) {
                revert ReservesOverdrawn(token, available);
            }
        }

        if (status == LineLib.STATUS.REPAID && msg.sender == borrower) {
            LineLib.sendOutTokenOrETH(token, to, amount);
            return amount;
        }

        if ((status == LineLib.STATUS.LIQUIDATABLE || status == LineLib.STATUS.INSOLVENT) && msg.sender == arbiter) {
            LineLib.sendOutTokenOrETH(token, to, amount);
            return amount;
        }

        revert CallerAccessDenied();
    }
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

import {Denominations} from "chainlink/Denominations.sol";
import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol";
import {SafeERC20} from "openzeppelin/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuard} from "openzeppelin/utils/ReentrancyGuard.sol";

import {LineLib} from "../../utils/LineLib.sol";
import {CreditLib} from "../../utils/CreditLib.sol";
import {CreditListLib} from "../../utils/CreditListLib.sol";
import {MutualConsent} from "../../utils/MutualConsent.sol";
import {InterestRateCredit} from "../interest-rate/InterestRateCredit.sol";

import {IOracle} from "../../interfaces/IOracle.sol";
import {ILineOfCredit} from "../../interfaces/ILineOfCredit.sol";

/**
 * @title  - Credit Cooperative Unsecured Line of Credit
 * @notice - Core credit facility logic for proposing, depositing, borrowing, and repaying debt.
 *         - Contains core financial covnenants around term length (`deadline`), collateral ratios, liquidations, etc.
 * @dev    - contains internal functions overwritten by SecuredLine, SpigotedLine, and EscrowedLine
 */
contract LineOfCredit is ILineOfCredit, MutualConsent, ReentrancyGuard {
    using SafeERC20 for IERC20;

    using CreditListLib for bytes32[];

    /// @notice - the timestamp that all creditors must be repaid by
    uint256 public immutable deadline;

    /// @notice - the account that can drawdown and manage debt positions
    address public immutable borrower;

    /// @notice - neutral 3rd party that mediates btw borrower and all lenders
    address public immutable arbiter;

    /// @notice - price feed to use for valuing credit tokens
    IOracle public immutable oracle;

    /// @notice - contract responsible for calculating interest owed on debt positions
    InterestRateCredit public immutable interestRate;

    /// @notice - current amount of active positions (aka non-null ids) in `ids` list
    uint256 private count;

    /// @notice - positions ids of all open credit lines.
    /// @dev    - may contain null elements
    bytes32[] public ids;

    /// @notice id -> position data
    mapping(bytes32 => Credit) public credits;

    /// @notice - current health status of line
    LineLib.STATUS public status;

    /**
     * @notice            - How to deploy a Line of Credit
     * @dev               - A Borrower and a first Lender agree on terms. Then the Borrower deploys the contract using the constructor below.
     *                      Later, both Lender and Borrower must call _mutualConsent() during addCredit() to actually enable funds to be deposited.
     * @param oracle_     - The price oracle to use for getting all token values.
     * @param arbiter_    - A neutral party with some special priviliges on behalf of Borrower and Lender.
     * @param borrower_   - The debitor for all credit lines in this contract.
     * @param ttl_        - The time to live for all credit lines for the Line of Credit facility (sets the maturity/term of the Line of Credit)
     */
    constructor(address oracle_, address arbiter_, address borrower_, uint256 ttl_) {
        oracle = IOracle(oracle_);
        arbiter = arbiter_;
        borrower = borrower_;
        deadline = block.timestamp + ttl_; //the deadline is the term/maturity/expiry date of the Line of Credit facility
        interestRate = new InterestRateCredit();

        emit DeployLine(oracle_, arbiter_, borrower_);
    }

    function init() external virtual {
        if (status != LineLib.STATUS.UNINITIALIZED) {
            revert AlreadyInitialized();
        }
        _init();
        _updateStatus(LineLib.STATUS.ACTIVE);
    }

    function _init() internal virtual {
        // If no collateral or Spigot then Line of Credit is immediately active
        return;
    }

    ///////////////
    // MODIFIERS //
    ///////////////

    modifier whileActive() {
        if (status != LineLib.STATUS.ACTIVE) {
            revert NotActive();
        }
        _;
    }

    modifier whileBorrowing() {
        if (count == 0 || credits[ids[0]].principal == 0) {
            revert NotBorrowing();
        }
        _;
    }

    modifier onlyBorrower() {
        if (msg.sender != borrower) {
            revert CallerAccessDenied();
        }
        _;
    }

    /**
     * @notice - mutualConsent() but hardcodes borrower address and uses the position id to
                 get Lender address instead of passing it in directly
     * @param id - position to pull lender address from for mutual consent agreement
    */
    modifier mutualConsentById(bytes32 id) {
        if (_mutualConsent(borrower, credits[id].lender)) {
            // Run whatever code is needed for the 2/2 consent
            _;
        }
    }

    /**
     * @notice - evaluates all covenants encoded in _healthcheck from different Line variants
     * @dev - updates `status` variable in storage if current status is diferent from existing status
     * @return - current health status of Line
     */
    function healthcheck() external returns (LineLib.STATUS) {
        // can only check if the line has been initialized
        require(uint256(status) >= uint256(LineLib.STATUS.ACTIVE));
        return _updateStatus(_healthcheck());
    }

    function _healthcheck() internal virtual returns (LineLib.STATUS) {
        // if line is in a final end state then do not run _healthcheck()
        LineLib.STATUS s = status;
        if (
            s == LineLib.STATUS.REPAID || // end state - good
            s == LineLib.STATUS.INSOLVENT // end state - bad
        ) {
            return s;
        }

        // Liquidate if all credit lines aren't closed by deadline
        if (block.timestamp >= deadline && count != 0) {
            emit Default(ids[0]); // can query all defaulted positions offchain once event picked up
            return LineLib.STATUS.LIQUIDATABLE;
        }

        // if nothing wrong, return to healthy ACTIVE state
        return LineLib.STATUS.ACTIVE;
    }

    /// see ILineOfCredit.declareInsolvent
    function declareInsolvent() external {
        if (arbiter != msg.sender) {
            revert CallerAccessDenied();
        }
        if (LineLib.STATUS.LIQUIDATABLE != _updateStatus(_healthcheck())) {
            revert NotLiquidatable();
        }

        if (_canDeclareInsolvent()) {
            _updateStatus(LineLib.STATUS.INSOLVENT);
        }
    }

    function _canDeclareInsolvent() internal virtual returns (bool) {
        // logic updated in Spigoted and Escrowed lines
        return true;
    }

    /// see ILineOfCredit.updateOutstandingDebt
    function updateOutstandingDebt() external override returns (uint256, uint256) {
        return _updateOutstandingDebt();
    }

    function _updateOutstandingDebt() internal returns (uint256 principal, uint256 interest) {
        // use full length not count because positions might not be packed in order
        uint256 len = ids.length;
        if (len == 0) return (0, 0);

        bytes32 id;
        for (uint256 i; i < len; ++i) {
            id = ids[i];

            // null element in array from closing a position. skip for gas savings
            if (id == bytes32(0)) {
                continue;
            }

            (Credit memory c, uint256 _p, uint256 _i) = CreditLib.getOutstandingDebt(
                credits[id],
                id,
                address(oracle),
                address(interestRate)
            );

            // update total outstanding debt
            principal += _p;
            interest += _i;
            // save changes to storage
            credits[id] = c;
        }
    }

    /// see ILineOfCredit.accrueInterest
    function accrueInterest() external override {
        uint256 len = ids.length;
        bytes32 id;
        for (uint256 i; i < len; ++i) {
            id = ids[i];
            Credit memory credit = credits[id];
            credits[id] = _accrue(credit, id);
        }
    }

    /// see ILineOfCredit.addCredit
    function addCredit(
        uint128 drate,
        uint128 frate,
        uint256 amount,
        address token,
        address lender
    ) external payable override nonReentrant whileActive mutualConsent(lender, borrower) returns (bytes32) {
        bytes32 id = _createCredit(lender, token, amount);

        _setRates(id, drate, frate);

        LineLib.receiveTokenOrETH(token, lender, amount);

        return id;
    }

    /// see ILineOfCredit.setRates
    function setRates(bytes32 id, uint128 drate, uint128 frate) external override mutualConsentById(id) {
        credits[id] = _accrue(credits[id], id);
        _setRates(id, drate, frate);
    }

    /// see ILineOfCredit.setRates
    function _setRates(bytes32 id, uint128 drate, uint128 frate) internal {
        interestRate.setRate(id, drate, frate);
        emit SetRates(id, drate, frate);
    }

    /// see ILineOfCredit.increaseCredit
    function increaseCredit(
        bytes32 id,
        uint256 amount
    ) external payable override nonReentrant whileActive mutualConsentById(id) {
        Credit memory credit = _accrue(credits[id], id);

        credit.deposit += amount;

        credits[id] = credit;

        LineLib.receiveTokenOrETH(credit.token, credit.lender, amount);

        emit IncreaseCredit(id, amount);
    }

    ///////////////
    // REPAYMENT //
    ///////////////

    /// see ILineOfCredit.depositAndClose
    function depositAndClose() external payable override nonReentrant whileBorrowing onlyBorrower {
        bytes32 id = ids[0];
        Credit memory credit = _accrue(credits[id], id);

        // Borrower deposits the outstanding balance not already repaid
        uint256 totalOwed = credit.principal + credit.interestAccrued;

        // Borrower clears the debt then closes the credit line
        credits[id] = _close(_repay(credit, id, totalOwed, borrower), id);
    }

    /// see ILineOfCredit.close
    function close(bytes32 id) external payable override nonReentrant onlyBorrower {
        Credit memory credit = _accrue(credits[id], id);

        uint256 facilityFee = credit.interestAccrued;

        // clear facility fees and close position
        credits[id] = _close(_repay(credit, id, facilityFee, borrower), id);
    }

    /// see ILineOfCredit.depositAndRepay
    function depositAndRepay(uint256 amount) external payable override nonReentrant whileBorrowing {
        bytes32 id = ids[0];
        Credit memory credit = _accrue(credits[id], id);

        if (amount > credit.principal + credit.interestAccrued) {
            revert RepayAmountExceedsDebt(credit.principal + credit.interestAccrued);
        }

        credits[id] = _repay(credit, id, amount, msg.sender);
    }

    ////////////////////
    // FUND TRANSFERS //
    ////////////////////

    /// see ILineOfCredit.borrow
    function borrow(bytes32 id, uint256 amount) external override nonReentrant whileActive onlyBorrower {
        Credit memory credit = _accrue(credits[id], id);

        if (!credit.isOpen) {
            revert PositionIsClosed();
        }

        if (amount > credit.deposit - credit.principal) {
            revert NoLiquidity();
        }

        credit.principal += amount;

        // save new debt before healthcheck and token transfer
        credits[id] = credit;

        // ensure that borrowing doesnt cause Line to be LIQUIDATABLE
        if (_updateStatus(_healthcheck()) != LineLib.STATUS.ACTIVE) {
            revert BorrowFailed();
        }

        LineLib.sendOutTokenOrETH(credit.token, borrower, amount);

        emit Borrow(id, amount);

        _sortIntoQ(id);
    }

    /// see ILineOfCredit.withdraw
    function withdraw(bytes32 id, uint256 amount) external override nonReentrant {
        // accrues interest and transfer funds to Lender addres
        credits[id] = CreditLib.withdraw(_accrue(credits[id], id), id, msg.sender, amount);
    }

    /**
     * @notice  - Steps the Queue be replacing the first element with the next valid credit line's ID
     * @dev     - Only works if the first element in the queue is null
     */
    function stepQ() external {
        ids.stepQ();
    }

    //////////////////////
    //  Internal  funcs //
    //////////////////////

    /**
     * @notice - updates `status` variable in storage if current status is diferent from existing status.
     * @dev - privileged internal function. MUST check params and logic flow before calling
     * @dev - does not save new status if it is the same as current status
     * @return status - the current status of the line after updating
     */
    function _updateStatus(LineLib.STATUS status_) internal returns (LineLib.STATUS) {
        if (status == status_) return status_;
        emit UpdateStatus(uint256(status_));
        return (status = status_);
    }

    /**
     * @notice - Generates position id and stores lender's position
     * @dev - positions have unique composite-index on [lineAddress, lenderAddress, tokenAddress]
     * @dev - privileged internal function. MUST check params and logic flow before calling
     * @param lender - address that will own and manage position
     * @param token - ERC20 token that is being lent and borrower
     * @param amount - amount of tokens lender will initially deposit
     */
    function _createCredit(address lender, address token, uint256 amount) internal returns (bytes32 id) {
        id = CreditLib.computeId(address(this), lender, token);

        // MUST not double add the credit line. once lender is set it cant be deleted even if position is closed.
        if (credits[id].lender != address(0)) {
            revert PositionExists();
        }

        credits[id] = CreditLib.create(id, amount, lender, token, address(oracle));

        ids.push(id); // add lender to end of repayment queue

        unchecked {
            ++count;
        }

        return id;
    }

    /**
    * @dev - Reduces `principal` and/or `interestAccrued` on a credit line.
                Expects checks for conditions of repaying and param sanitizing before calling
                e.g. early repayment of principal, tokens have actually been paid by borrower, etc.
    * @dev - privileged internal function. MUST check params and logic flow before calling
    * @dev syntatic sugar
    * @param id - position id with all data pertaining to line
    * @param amount - amount of Credit Token being repaid on credit line
    * @return credit - position struct in memory with updated values
    */
    function _repay(Credit memory credit, bytes32 id, uint256 amount, address payer) internal returns (Credit memory) {
        return CreditLib.repay(credit, id, amount, payer);
    }

    /**
     * @notice - accrues token demoninated interest on a lender's position.
     * @dev MUST call any time a position balance or interest rate changes
     * @dev syntatic sugar
     * @param credit - the lender position that is accruing interest
     * @param id - the position id for credit position
     */
    function _accrue(Credit memory credit, bytes32 id) internal returns (Credit memory) {
        return CreditLib.accrue(credit, id, address(interestRate));
    }

    /**
     * @notice - checks that a credit line is fully repaid and removes it
     * @dev deletes credit storage. Store any data u might need later in call before _close()
     * @dev - privileged internal function. MUST check params and logic flow before calling
     * @dev - when the line being closed is at the 0-index in the ids array, the null index is replaced using `.stepQ`
     * @return credit - position struct in memory with updated values
     */
    function _close(Credit memory credit, bytes32 id) internal virtual returns (Credit memory) {
        // update position data in state
        if (!credit.isOpen) {
            revert PositionIsClosed();
        }
        if (credit.principal != 0) {
            revert CloseFailedWithPrincipal();
        }

        credit.isOpen = false;

        // nullify the element for `id`
        ids.removePosition(id);

        // if positions was 1st in Q, cycle to next valid position
        if (ids[0] == bytes32(0)) ids.stepQ();

        unchecked {
            --count;
        }

        // If all credit lines are closed the the overall Line of Credit facility is declared 'repaid'.
        if (count == 0) {
            _updateStatus(LineLib.STATUS.REPAID);
        }

        emit CloseCreditPosition(id);

        return credit;
    }

    /**
     * @notice - Insert `p` into the next availble FIFO position in the repayment queue
               - once earliest slot is found, swap places with `p` and position in slot.
     * @dev - privileged internal function. MUST check params and logic flow before calling
     * @param p - position id that we are trying to find appropriate place for
     */
    function _sortIntoQ(bytes32 p) internal {
        uint256 lastSpot = ids.length - 1;
        uint256 nextQSpot = lastSpot;
        bytes32 id;
        for (uint256 i; i <= lastSpot; ++i) {
            id = ids[i];
            if (p != id) {
                if (
                    id == bytes32(0) || // deleted element. In the middle of the q because it was closed.
                    nextQSpot != lastSpot || // position already found. skip to find `p` asap
                    credits[id].principal != 0 //`id` should be placed before `p`
                ) continue;
                nextQSpot = i; // index of first undrawn line found
            } else {
                // nothing to update
                if (nextQSpot == lastSpot) return; // nothing to update
                // get id value being swapped with `p`
                bytes32 oldPositionId = ids[nextQSpot];
                // swap positions
                ids[i] = oldPositionId; // id put into old `p` position
                ids[nextQSpot] = p; // p put at target index

                emit SortedIntoQ(p, nextQSpot, i, oldPositionId);
            }
        }
    }

    /* GETTERS */

    /// see ILineOfCredit.interestAccrued
    function interestAccrued(bytes32 id) external view returns (uint256) {
        return CreditLib.interestAccrued(credits[id], id, address(interestRate));
    }

    /// see ILineOfCredit.counts
    function counts() external view returns (uint256, uint256) {
        return (count, ids.length);
    }

    /// see ILineOfCredit.available
    function available(bytes32 id) external view returns (uint256, uint256) {
        return (credits[id].deposit - credits[id].principal, credits[id].interestRepaid);
    }

    function nextInQ() external view returns (bytes32, address, address, uint256, uint256, uint256, uint128, uint128) {
        bytes32 next = ids[0];
        Credit memory credit = credits[next];
        // Add to docs that this view revertts if no queue
        (uint128 dRate, uint128 fRate) = CreditLib.getNextRateInQ(credit.principal, next, address(interestRate));
        return (
            next,
            credit.lender,
            credit.token,
            credit.principal,
            credit.deposit,
            CreditLib.interestAccrued(credit, next, address(interestRate)),
            dRate,
            fRate
        );
    }
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

import {LineLib} from "../utils/LineLib.sol";
import {IOracle} from "../interfaces/IOracle.sol";

interface ILineOfCredit {
    // Lender data
    struct Credit {
        //  all denominated in token, not USD
        uint256 deposit; // The total liquidity provided by a Lender in a given token on a Line of Credit
        uint256 principal; // The amount of a Lender's Deposit on a Line of Credit that has actually been drawn down by the Borrower (in Tokens)
        uint256 interestAccrued; // Interest due by a Borrower but not yet repaid to the Line of Credit contract
        uint256 interestRepaid; // Interest repaid by a Borrower to the Line of Credit contract but not yet withdrawn by a Lender
        uint8 decimals; // Decimals of Credit Token for calcs
        address token; // The token being lent out (Credit Token)
        address lender; // The person to repay
        bool isOpen; // Status of position
    }

    // General Events
    event UpdateStatus(uint256 indexed status); // store as normal uint so it can be indexed in subgraph

    event DeployLine(address indexed oracle, address indexed arbiter, address indexed borrower);

    event SortedIntoQ(bytes32 indexed id, uint256 indexed newIdx, uint256 indexed oldIdx, bytes32 oldId);

    // MutualConsent borrower/lender events

    event AddCredit(address indexed lender, address indexed token, uint256 indexed deposit, bytes32 id);
    // can only reference id once AddCredit is emitted because it will be indexed offchain

    event SetRates(bytes32 indexed id, uint128 indexed dRate, uint128 indexed fRate);

    event IncreaseCredit(bytes32 indexed id, uint256 indexed deposit);

    // Lender Events

    // Emits data re Lender removes funds (principal) - there is no corresponding function, just withdraw()
    event WithdrawDeposit(bytes32 indexed id, uint256 indexed amount);

    // Emits data re Lender withdraws interest - there is no corresponding function, just withdraw()
    event WithdrawProfit(bytes32 indexed id, uint256 indexed amount);

    // Emitted when any credit line is closed by the line's borrower or the position's lender
    event CloseCreditPosition(bytes32 indexed id);

    // After accrueInterest runs, emits the amount of interest added to a Borrower's outstanding balance of interest due
    // but not yet repaid to the Line of Credit contract
    event InterestAccrued(bytes32 indexed id, uint256 indexed amount);

    // Borrower Events

    // receive full line or drawdown on credit
    event Borrow(bytes32 indexed id, uint256 indexed amount);

    // Emits that a Borrower has repaid an amount of interest Results in an increase in interestRepaid, i.e. interest not yet withdrawn by a Lender). There is no corresponding function
    event RepayInterest(bytes32 indexed id, uint256 indexed amount);

    // Emits that a Borrower has repaid an amount of principal - there is no corresponding function
    event RepayPrincipal(bytes32 indexed id, uint256 indexed amount);

    event Default(bytes32 indexed id);

    // Access Errors
    error NotActive();
    error NotBorrowing();
    error CallerAccessDenied();

    // Tokens
    error TokenTransferFailed();
    error NoTokenPrice();

    // Line
    error BadModule(address module);
    error NoLiquidity();
    error PositionExists();
    error CloseFailedWithPrincipal();
    error NotInsolvent(address module);
    error NotLiquidatable();
    error AlreadyInitialized();
    error PositionIsClosed();
    error RepayAmountExceedsDebt(uint256 totalAvailable);
    error CantStepQ();
    error EthSupportDisabled();
    error BorrowFailed();

    // Fully public functions

    /**
     * @notice - Runs logic to ensure Line owns all modules are configured properly - collateral, interest rates, arbiter, etc.
     *          - Changes `status` from UNINITIALIZED to ACTIVE
     * @dev     - Reverts on failure to update status
     */
    function init() external;

    // MutualConsent functions

    /**
    * @notice        - On first call, creates proposed terms and emits MutualConsentRegistsered event. No position is created.
                      - On second call, creates position and stores in Line contract, sets interest rates, and starts accruing facility rate fees.
    * @dev           - Requires mutualConsent participants send EXACT same params when calling addCredit
    * @dev           - Fully executes function after a Borrower and a Lender have agreed terms, both Lender and borrower have agreed through mutualConsent
    * @dev           - callable by `lender` and `borrower`
    * @param drate   - The interest rate charged to a Borrower on borrowed / drawn down funds. In bps, 4 decimals.
    * @param frate   - The interest rate charged to a Borrower on the remaining funds available, but not yet drawn down
                        (rate charged on the available headroom). In bps, 4 decimals.
    * @param amount  - The amount of Credit Token to initially deposit by the Lender
    * @param token   - The Credit Token, i.e. the token to be lent out
    * @param lender  - The address that will manage credit line
    * @return id     - Lender's position id to look up in `credits`
  */
    function addCredit(
        uint128 drate,
        uint128 frate,
        uint256 amount,
        address token,
        address lender
    ) external payable returns (bytes32);

    /**
     * @notice           - lets Lender and Borrower update rates on the lender's position
     *                   - accrues interest before updating terms, per InterestRate docs
     *                   - can do so even when LIQUIDATABLE for the purpose of refinancing and/or renego
     * @dev              - callable by Borrower or Lender
     * @param id         - position id that we are updating
     * @param drate      - new drawn rate. In bps, 4 decimals
     * @param frate      - new facility rate. In bps, 4 decimals
     */
    function setRates(bytes32 id, uint128 drate, uint128 frate) external;

    /**
     * @notice           - Lets a Lender and a Borrower increase the credit limit on a position
     * @dev              - line status must be ACTIVE
     * @dev              - callable by borrower
     * @dev              - The function retains the `payable` designation, despite not accepting Eth via mutualConsent modifier, as a gas-optimization
     * @param id         - position id that we are updating
     * @param amount     - amount to deposit by the Lender
     */
    function increaseCredit(bytes32 id, uint256 amount) external payable;

    // Borrower functions

    /**
     * @notice       - Borrower chooses which lender position draw down on and transfers tokens from Line contract to Borrower
     * @dev          - callable by borrower
     * @param id     - the position to draw down on
     * @param amount - amount of tokens the borrower wants to withdraw
     */
    function borrow(bytes32 id, uint256 amount) external;

    /**
     * @notice       - Transfers token used in position id from msg.sender to Line contract.
     * @dev          - Available for anyone to deposit Credit Tokens to be available to be withdrawn by Lenders
     * @dev          - The function retains the `payable` designation, despite reverting with a non-zero msg.value, as a gas-optimization
     * @notice       - see LineOfCredit._repay() for more details
     * @param amount - amount of `token` in `id` to pay back
     */
    function depositAndRepay(uint256 amount) external payable;

    /**
     * @notice       - A Borrower deposits enough tokens to repay and close a credit line.
     * @dev          - callable by borrower
     * @dev          - The function retains the `payable` designation, despite reverting with a non-zero msg.value, as a gas-optimization
     */
    function depositAndClose() external payable;

    /**
     * @notice - Removes and deletes a position, preventing any more borrowing or interest.
     *         - Requires that the position principal has already been repais in full
     * @dev      - MUST repay accrued interest from facility fee during call
     * @dev - callable by `borrower` or Lender
     * @dev          - The function retains the `payable` designation, despite reverting with a non-zero msg.value, as a gas-optimization
     * @param id -the position id to be closed
     */
    function close(bytes32 id) external payable;

    // Lender functions

    /**
     * @notice - Withdraws liquidity from a Lender's position available to the Borrower.
     *         - Lender is only allowed to withdraw tokens not already lent out
     *         - Withdraws from repaid interest (profit) first and then deposit is reduced
     * @dev - can only withdraw tokens from their own position. If multiple lenders lend DAI, the lender1 can't withdraw using lender2's tokens
     * @dev - callable by Lender on `id`
     * @param id - the position id that Lender is withdrawing from
     * @param amount - amount of tokens the Lender would like to withdraw (withdrawn amount may be lower)
     */
    function withdraw(bytes32 id, uint256 amount) external;

    // Arbiter functions
    /**
     * @notice - Allow the Arbiter to signify that the Borrower is incapable of repaying debt permanently.
     *         - Recoverable funds for Lender after declaring insolvency = deposit + interestRepaid - principal
     * @dev    - Needed for onchain impairment accounting e.g. updating ERC4626 share price
     *         - MUST NOT have collateral left for call to succeed. Any collateral must already have been liquidated.
     * @dev    - Callable only by Arbiter.
     */
    function declareInsolvent() external;

    /**
     *
     * @notice - Updates accrued interest for the whole Line of Credit facility (i.e. for all credit lines)
     * @dev    - Loops over all position ids and calls related internal functions during which InterestRateCredit.sol
     *           is called with the id data and then 'interestAccrued' is updated.
     * @dev    - The related internal function _accrue() is called by other functions any time the balance on an individual
     *           credit line changes or if the interest rates of a credit line are changed by mutual consent
     *           between a Borrower and a Lender.
     */
    function accrueInterest() external;

    function healthcheck() external returns (LineLib.STATUS);

    /**
     * @notice - Cycles through position ids andselects first position with non-null principal to the zero index
     * @dev - Only works if the first element in the queue is null
     */
    function stepQ() external;

    /**
     * @notice - Returns the total debt of a Borrower across all positions for all Lenders.
     * @dev    - Denominated in USD, 8 decimals.
     * @dev    - callable by anyone
     * @return totalPrincipal - total amount of principal, in USD, owed across all positions
     * @return totalInterest - total amount of interest, in USD,  owed across all positions
     */
    function updateOutstandingDebt() external returns (uint256, uint256);

    // State getters

    function status() external returns (LineLib.STATUS);

    function borrower() external returns (address);

    function arbiter() external returns (address);

    function oracle() external returns (IOracle);

    /**
     * @notice - getter for amount of active ids + total ids in list
     * @return - (uint256, uint256) - active credit lines, total length
     */
    function counts() external view returns (uint256, uint256);

    /**
     * @notice - getter for amount of active ids + total ids in list
     * @return - (uint256, uint256) - active credit lines, total length
     */

    function interestAccrued(bytes32 id) external returns (uint256);

    /**
     * @notice - info on the next lender position that must be repaid
     * @return - (bytes32, address, address, uint, uint) - id, lender, token, principal, interestAccrued
     */
    function nextInQ() external view returns (bytes32, address, address, uint256, uint256, uint256, uint128, uint128);

    /**
     * @notice - how many tokens can be withdrawn from positions by borrower or lender
     * @return - (uint256, uint256) - remaining deposit, claimable interest
     */
    function available(bytes32 id) external returns (uint256, uint256);
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

import {IEscrowedLine} from "./IEscrowedLine.sol";
import {ISpigotedLine} from "./ISpigotedLine.sol";

interface ISecuredLine is IEscrowedLine, ISpigotedLine {
    // Rollover
    error DebtOwed();
    error BadNewLine();
    error BadRollover();

    // Borrower functions

    /**
     * @notice - helper function to allow Borrower to easily transfer settings and collateral from this line to a new line
     *         - usefull after ttl has expired and want to renew Line with minimal effort
     * @dev    - transfers Spigot and Escrow ownership to newLine. Arbiter functions on this Line will no longer work
     * @param newLine - the new, uninitialized Line deployed by borrower
     */
    function rollover(address newLine) external;
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

interface IInterestRateCredit {
    struct Rate {
        // The interest rate charged to a Borrower on borrowed / drawn down funds
        // in bps, 4 decimals
        uint128 dRate;
        // The interest rate charged to a Borrower on the remaining funds available, but not yet drawn down (rate charged on the available headroom)
        // in bps, 4 decimals
        uint128 fRate;
        // The time stamp at which accrued interest was last calculated on an ID and then added to the overall interestAccrued (interest due but not yet repaid)
        uint256 lastAccrued;
    }

    /**
     * @notice - allows `lineContract to calculate how much interest is owed since it was last calculated charged at time `lastAccrued`
     * @dev    - pure function that only calculates interest owed. Line is responsible for actually updating credit balances with returned value
     * @dev    - callable by `lineContract`
     * @param id - position id on Line to look up interest rates for
     * @param drawnBalance the balance of funds that a Borrower has drawn down on the credit line
     * @param facilityBalance the remaining balance of funds that a Borrower can still drawn down on a credit line (aka headroom)
     *
     * @return - the amount of interest to be repaid for this interest period
     */

    function accrueInterest(bytes32 id, uint256 drawnBalance, uint256 facilityBalance) external returns (uint256);

    /**
     * @notice - updates interest rates on a lender's position. Updates lastAccrued time to block.timestamp
     * @dev    - MUST call accrueInterest() on Line before changing rates. If not, lender will not accrue interest over previous interest period.
     * @dev    - callable by `line`
     * @return - if call was successful or not
     */
    function setRate(bytes32 id, uint128 dRate, uint128 fRate) external returns (bool);

    function getInterestAccrued(
        bytes32 id,
        uint256 drawnBalance,
        uint256 facilityBalance
    ) external view returns (uint256);

    function getRates(bytes32 id) external view returns (uint128, uint128);
}

pragma solidity ^0.8.16;

interface IOracle {
    /** current price for token asset. denominated in USD */
    function getLatestAnswer(address token) external returns (int256);

    /** Readonly function providing the current price for token asset. denominated in USD */
    function _getLatestAnswer(address token) external view returns (int256);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 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;

    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @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);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @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).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // 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 cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}

File 13 of 25 : Denominations.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

library Denominations {
  address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
  address public constant BTC = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB;

  // Fiat currencies follow https://en.wikipedia.org/wiki/ISO_4217
  address public constant USD = address(840);
  address public constant GBP = address(826);
  address public constant EUR = address(978);
  address public constant JPY = address(392);
  address public constant KRW = address(410);
  address public constant CNY = address(156);
  address public constant AUD = address(36);
  address public constant CAD = address(124);
  address public constant CHF = address(756);
  address public constant ARS = address(32);
  address public constant PHP = address(608);
  address public constant NZD = address(554);
  address public constant SGD = address(702);
  address public constant NGN = address(566);
  address public constant ZAR = address(710);
  address public constant RUB = address(643);
  address public constant INR = address(356);
  address public constant BRL = address(986);
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

interface IEscrow {
    struct Deposit {
        uint amount;
        bool isERC4626;
        address asset; // eip4626 asset else the erc20 token itself
        uint8 assetDecimals;
    }

    event AddCollateral(address indexed token, uint indexed amount);

    event RemoveCollateral(address indexed token, uint indexed amount);

    event EnableCollateral(address indexed token);

    error InvalidCollateral();

    error CallerAccessDenied();

    error UnderCollateralized();

    error NotLiquidatable();

    // State var getters.

    function line() external returns (address);

    function oracle() external returns (address);

    function borrower() external returns (address);

    function minimumCollateralRatio() external returns (uint32);

    // Functions

    function isLiquidatable() external returns (bool);

    function updateLine(address line_) external returns (bool);

    function getCollateralRatio() external returns (uint);

    function getCollateralValue() external returns (uint);

    function enableCollateral(address token) external returns (bool);

    function addCollateral(uint amount, address token) external payable returns (uint);

    function releaseCollateral(uint amount, address token, address to) external returns (uint);

    function liquidate(uint amount, address token, address to) external returns (bool);
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

import {IEscrow} from "./IEscrow.sol";

interface IEscrowedLine {
    event Liquidate(bytes32 indexed id, uint256 indexed amount, address indexed token, address escrow);

    /**
     * @notice - Forcefully take collateral from Escrow and repay debt for lender
     *          - current implementation just sends "liquidated" tokens to Arbiter to sell off how the deem fit and then manually repay with DepositAndRepay
     * @dev - only callable by Arbiter
     * @dev - Line status MUST be LIQUIDATABLE
     * @dev - callable by `arbiter`
     * @param amount - amount of `targetToken` expected to be sold off in  _liquidate
     * @param targetToken - token in escrow that will be sold of to repay position
     */
    function liquidate(uint256 amount, address targetToken) external returns (uint256);

    /// @notice the escrow contract backing this Line
    function escrow() external returns (IEscrow);
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;
import {Denominations} from "chainlink/Denominations.sol";
import {ILineOfCredit} from "../interfaces/ILineOfCredit.sol";
import {IOracle} from "../interfaces/IOracle.sol";
import {IInterestRateCredit} from "../interfaces/IInterestRateCredit.sol";
import {ILineOfCredit} from "../interfaces/ILineOfCredit.sol";
import {LineLib} from "./LineLib.sol";

/**
 * @title Credit Cooperative Line of Credit Library
 * @notice Core logic and variables to be reused across all Credit Cooperative Marketplace Line of Credit contracts
 */

library CreditLib {
    event AddCredit(address indexed lender, address indexed token, uint256 indexed deposit, bytes32 id);

    /// @notice Emitted when Lender withdraws from their initial deposit
    event WithdrawDeposit(bytes32 indexed id, uint256 indexed amount);

    /// @notice Emitted when Lender withdraws interest paid by borrower
    event WithdrawProfit(bytes32 indexed id, uint256 indexed amount);

    /// @notice Emits amount of interest (denominated in credit token) added to a Borrower's outstanding balance
    event InterestAccrued(bytes32 indexed id, uint256 indexed amount);

    // Borrower Events

    /// @notice Emits when Borrower has drawn down an amount (denominated in credit.token) on a credit line
    event Borrow(bytes32 indexed id, uint256 indexed amount);

    /// @notice Emits that a Borrower has repaid some amount of interest (denominated in credit.token)
    event RepayInterest(bytes32 indexed id, uint256 indexed amount);

    /// @notice Emits that a Borrower has repaid some amount of principal (denominated in credit.token)
    event RepayPrincipal(bytes32 indexed id, uint256 indexed amount);

    // Errors

    error NoTokenPrice();

    error PositionExists();

    error RepayAmountExceedsDebt(uint256 totalAvailable);

    error InvalidTokenDecimals();

    error NoQueue();

    error PositionIsClosed();

    error NoLiquidity();

    error CloseFailedWithPrincipal();

    error CallerAccessDenied();

    /**
     * @dev          - Creates a deterministic hash id for a credit line provided by a single Lender for a given token on a Line of Credit facility
     * @param line   - The Line of Credit facility concerned
     * @param lender - The address managing the credit line concerned
     * @param token  - The token being lent out on the credit line concerned
     * @return id
     */
    function computeId(address line, address lender, address token) external pure returns (bytes32) {
        return keccak256(abi.encode(line, lender, token));
    }

    // getOutstandingDebt() is called by updateOutstandingDebt()
    function getOutstandingDebt(
        ILineOfCredit.Credit memory credit,
        bytes32 id,
        address oracle,
        address interestRate
    ) external returns (ILineOfCredit.Credit memory c, uint256 principal, uint256 interest) {
        c = accrue(credit, id, interestRate);

        int256 price = IOracle(oracle).getLatestAnswer(c.token);

        principal = calculateValue(price, c.principal, c.decimals);
        interest = calculateValue(price, c.interestAccrued, c.decimals);

        return (c, principal, interest);
    }

    /**
     * @notice         - Calculates value of tokens.  Used for calculating the USD value of principal and of interest during getOutstandingDebt()
     * @dev            - Assumes Oracle returns answers in USD with 1e8 decimals
     *                 - If price < 0 then we treat it as 0.
     * @param price    - The Oracle price of the asset. 8 decimals
     * @param amount   - The amount of tokens being valued.
     * @param decimals - Token decimals to remove for USD price
     * @return         - The total USD value of the amount of tokens being valued in 8 decimals
     */
    function calculateValue(int price, uint256 amount, uint8 decimals) public pure returns (uint256) {
        return price <= 0 ? 0 : (amount * uint(price)) / (1 * 10 ** decimals);
    }

    /**
     * see ILineOfCredit._createCredit
     * @notice called by LineOfCredit._createCredit during every repayment function
     * @param oracle - interset rate contract used by line that will calculate interest owed
     */
    function create(
        bytes32 id,
        uint256 amount,
        address lender,
        address token,
        address oracle
    ) external returns (ILineOfCredit.Credit memory credit) {
        int price = IOracle(oracle).getLatestAnswer(token);
        if (price <= 0) {
            revert NoTokenPrice();
        }

        (bool passed, bytes memory result) = token.call(abi.encodeWithSignature("decimals()"));

        if (!passed || result.length == 0) {
            revert InvalidTokenDecimals();
        }

        uint8 decimals = abi.decode(result, (uint8));

        credit = ILineOfCredit.Credit({
            lender: lender,
            token: token,
            decimals: decimals,
            deposit: amount,
            principal: 0,
            interestAccrued: 0,
            interestRepaid: 0,
            isOpen: true
        });

        emit AddCredit(lender, token, amount, id);

        return credit;
    }

    /**
     * see ILineOfCredit._repay
     * @notice called by LineOfCredit._repay during every repayment function
     * @dev uses uncheckd math. assumes checks have been done in caller
     * @param credit - The lender position being repaid
     */
    function repay(
        ILineOfCredit.Credit memory credit,
        bytes32 id,
        uint256 amount,
        address payer
    ) external returns (ILineOfCredit.Credit memory) {
        if (!credit.isOpen) {
            revert PositionIsClosed();
        }

        unchecked {
            if (amount > credit.principal + credit.interestAccrued) {
                revert RepayAmountExceedsDebt(credit.principal + credit.interestAccrued);
            }

            if (amount <= credit.interestAccrued) {
                credit.interestAccrued -= amount;
                credit.interestRepaid += amount;
                emit RepayInterest(id, amount);
            } else {
                uint256 interest = credit.interestAccrued;
                uint256 principalPayment = amount - interest;

                // update individual credit line denominated in token
                credit.principal -= principalPayment;
                credit.interestRepaid += interest;
                credit.interestAccrued = 0;

                emit RepayInterest(id, interest);
                emit RepayPrincipal(id, principalPayment);
            }
        }

        // if we arent using funds from reserves to repay then pull tokens from target
        if(payer != address(0)) {
            LineLib.receiveTokenOrETH(credit.token, payer, amount);
        }

        return credit;
    }

    /**
     * see ILineOfCredit.withdraw
     * @notice called by LineOfCredit.withdraw during every repayment function
     * @dev uses uncheckd math. assumes checks have been done in caller
     * @param credit - The lender position that is being bwithdrawn from
     */
    function withdraw(
        ILineOfCredit.Credit memory credit,
        bytes32 id,
        address caller,
        uint256 amount
    ) external returns (ILineOfCredit.Credit memory) {
        if (caller != credit.lender) {
            revert CallerAccessDenied();
        }

        unchecked {
            if (amount > credit.deposit - credit.principal + credit.interestRepaid) {
                revert ILineOfCredit.NoLiquidity();
            }

            if (amount > credit.interestRepaid) {
                uint256 interest = credit.interestRepaid;

                credit.deposit -= amount - interest;
                credit.interestRepaid = 0;

                // emit events before setting to 0
                emit WithdrawDeposit(id, amount - interest);
                emit WithdrawProfit(id, interest);
            } else {
                credit.interestRepaid -= amount;
                emit WithdrawProfit(id, amount);
            }
        }

        LineLib.sendOutTokenOrETH(credit.token, credit.lender, amount);

        return credit;
    }

    /**
     * see ILineOfCredit._accrue
     * @notice called by LineOfCredit._accrue during every repayment function
     * @dev public to use in `getOutstandingDebt`
     * @param interest - interset rate contract used by line that will calculate interest owed
     */
    function accrue(
        ILineOfCredit.Credit memory credit,
        bytes32 id,
        address interest
    ) public returns (ILineOfCredit.Credit memory) {
        if (!credit.isOpen) {
            return credit;
        }
        unchecked {
            // interest will almost always be less than deposit
            // low risk of overflow unless extremely high interest rate

            // get token demoninated interest accrued
            uint256 accruedToken = IInterestRateCredit(interest).accrueInterest(id, credit.principal, credit.deposit);

            // update credit line balance
            credit.interestAccrued += accruedToken;

            emit InterestAccrued(id, accruedToken);
            return credit;
        }
    }

    function interestAccrued(
        ILineOfCredit.Credit memory credit,
        bytes32 id,
        address interest
    ) external view returns (uint256) {
        return
            credit.interestAccrued +
            IInterestRateCredit(interest).getInterestAccrued(id, credit.principal, credit.deposit);
    }

    function getNextRateInQ(uint256 principal, bytes32 id, address interest) external view returns (uint128, uint128) {
        if (principal == 0) {
            revert NoQueue();
        } else {
            return IInterestRateCredit(interest).getRates(id);
        }
    }
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

// forked from https://github.com/IndexCoop/index-coop-smart-contracts/blob/master/contracts/lib/MutualConsent.sol

 pragma solidity ^0.8.16;

/**
 * @title MutualConsent
 * @author Set Protocol
 *
 * The MutualConsent contract contains a modifier for handling mutual consents between two parties
 */
abstract contract MutualConsent {
    /* ============ State Variables ============ */

    // equivalent to longest msg.data bytes, ie addCredit
    uint256 constant MAX_DATA_LENGTH_BYTES = 164;

    // equivalent to any fn with no args, ie just a fn selector
    uint256 constant MIN_DATA_LENGTH_BYTES = 4;

    // Mapping of upgradable units and if consent has been initialized by other party
    mapping(bytes32 => address) public mutualConsentProposals;

    error Unauthorized();
    error InvalidConsent();
    error NotUserConsent();
    error NonZeroEthValue();

    // causes revert when the msg.data passed in has more data (ie arguments) than the largest known fn signature
    error UnsupportedMutualConsentFunction();

    /* ============ Events ============ */

    event MutualConsentRegistered(bytes32 proposalId, address taker);
    event MutualConsentRevoked(bytes32 proposalId);
    event MutualConsentAccepted(bytes32 proposalId);

    /* ============ Modifiers ============ */

    /**
     * @notice - allows a function to be called if only two specific stakeholders signoff on the tx data
     *         - signers can be anyone. only two signers per contract or dynamic signers per tx.
     */
    modifier mutualConsent(address _signerOne, address _signerTwo) {
        if (_mutualConsent(_signerOne, _signerTwo)) {
            // Run whatever code needed 2/2 consent
            _;
        }
    }

    /**
     *  @notice - allows a caller to revoke a previously created consent
     *  @dev    - MAX_DATA_LENGTH_BYTES is set at 164 bytes, which is the length of the msg.data
     *          - for the addCredit function. Anything over that is not valid and might be used in
     *          - an attempt to create a hash collision
     *  @param  _reconstrucedMsgData The reconstructed msg.data for the function call for which the
     *          original consent was created - comprised of the fn selector (bytes4) and abi.encoded
     *          function arguments.
     *
     */
    function revokeConsent(bytes calldata _reconstrucedMsgData) external {
        if (
            _reconstrucedMsgData.length > MAX_DATA_LENGTH_BYTES || _reconstrucedMsgData.length < MIN_DATA_LENGTH_BYTES
        ) {
            revert UnsupportedMutualConsentFunction();
        }

        bytes32 proposalIdToDelete = keccak256(abi.encodePacked(_reconstrucedMsgData, msg.sender));

        address consentor = mutualConsentProposals[proposalIdToDelete];

        if (consentor == address(0)) {
            revert InvalidConsent();
        }
        if (consentor != msg.sender) {
            revert NotUserConsent();
        } // note: cannot test, as no way to know what data (+msg.sender) would cause hash collision

        delete mutualConsentProposals[proposalIdToDelete];

        emit MutualConsentRevoked(proposalIdToDelete);
    }

    /* ============ Internal Functions ============ */

    function _mutualConsent(address _signerOne, address _signerTwo) internal returns (bool) {
        if (msg.sender != _signerOne && msg.sender != _signerTwo) {
            revert Unauthorized();
        }

        address nonCaller = _getNonCaller(_signerOne, _signerTwo);

        // The consent hash is defined by the hash of the transaction call data and sender of msg,
        // which uniquely identifies the function, arguments, and sender.
        bytes32 expectedProposalId = keccak256(abi.encodePacked(msg.data, nonCaller));

        if (mutualConsentProposals[expectedProposalId] == address(0)) {
            bytes32 newProposalId = keccak256(abi.encodePacked(msg.data, msg.sender));

            mutualConsentProposals[newProposalId] = msg.sender; // save caller's consent for nonCaller to accept

            emit MutualConsentRegistered(newProposalId, nonCaller);

            return false;
        }

        delete mutualConsentProposals[expectedProposalId];

        emit MutualConsentAccepted(expectedProposalId);

        return true;
    }

    function _getNonCaller(address _signerOne, address _signerTwo) internal view returns (address) {
        return msg.sender == _signerOne ? _signerTwo : _signerOne;
    }
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

interface ISpigot {
    struct Setting {
        uint8 ownerSplit; // x/100 % to Owner, rest to Operator
        bytes4 claimFunction; // function signature on contract to call and claim revenue
        bytes4 transferOwnerFunction; // function signature on contract to call and transfer ownership
    }

    // Spigot Events
    event AddSpigot(address indexed revenueContract, uint256 ownerSplit, bytes4 claimFnSig, bytes4 trsfrFnSig);

    event RemoveSpigot(address indexed revenueContract, address token);

    event UpdateWhitelistFunction(bytes4 indexed func, bool indexed allowed);

    event UpdateOwnerSplit(address indexed revenueContract, uint8 indexed split);

    event ClaimRevenue(address indexed token, uint256 indexed amount, uint256 escrowed, address revenueContract);

    event ClaimOwnerTokens(address indexed token, uint256 indexed amount, address owner);

    event ClaimOperatorTokens(address indexed token, uint256 indexed amount, address operator);

    // Stakeholder Events

    event UpdateOwner(address indexed newOwner);

    event UpdateOperator(address indexed newOperator);

    // Errors
    error BadFunction();

    error OperatorFnNotWhitelisted();

    error OperatorFnNotValid();

    error OperatorFnCallFailed();

    error ClaimFailed();

    error NoRevenue();

    error UnclaimedRevenue();

    error CallerAccessDenied();

    error BadSetting();

    error InvalidRevenueContract();

    // ops funcs

    function claimRevenue(
        address revenueContract,
        address token,
        bytes calldata data
    ) external returns (uint256 claimed);

    function operate(address revenueContract, bytes calldata data) external returns (bool);

    // owner funcs

    function claimOwnerTokens(address token) external returns (uint256 claimed);

    function claimOperatorTokens(address token) external returns (uint256 claimed);

    function addSpigot(address revenueContract, Setting memory setting) external returns (bool);

    function removeSpigot(address revenueContract) external returns (bool);

    // stakeholder funcs

    function updateOwnerSplit(address revenueContract, uint8 ownerSplit) external returns (bool);

    function updateOwner(address newOwner) external returns (bool);

    function updateOperator(address newOperator) external returns (bool);

    function updateWhitelistedFunction(bytes4 func, bool allowed) external returns (bool);

    // Getters
    function owner() external view returns (address);

    function operator() external view returns (address);

    function isWhitelisted(bytes4 func) external view returns (bool);

    function getOwnerTokens(address token) external view returns (uint256);

    function getOperatorTokens(address token) external view returns (uint256);

    function getSetting(
        address revenueContract
    ) external view returns (uint8 split, bytes4 claimFunc, bytes4 transferFunc);
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

import {ISpigot} from "./ISpigot.sol";

interface ISpigotedLine {
    /**
     * @notice - Log how many revenue tokens are used to repay debt after claimAndRepay
     *         - dont need to track value like other events because _repay already emits that
     *         - Mainly used to log debt that is paid via Spigot directly vs other sources. Without this event it's a lot harder to parse that offchain.
     */
    event RevenuePayment(address indexed token, uint256 indexed amount);

    error ReservesOverdrawn(address token, uint256 amountAvailable);

    /**
     * @notice - Log how many revenue tokens were traded for credit tokens.
     *         - Differs from RevenuePayment because we trade revenue at different times from repaying with revenue
     * @dev    - Can you use to figure out price of revenue tokens offchain since we only have an oracle for credit tokens
     * @dev    - Revenue tokens may be from reserves or from Spigot revenue.
     */
    event TradeSpigotRevenue(
        address indexed revenueToken,
        uint256 revenueTokenAmount,
        address indexed debtToken,
        uint256 indexed debtTokensBought
    );

    event ReservesChanged(
        address indexed token,
        int256 indexed diff,
        uint256 tokenType // 0 for revenue token, 1 for credit token
    );

    // Borrower functions

    /**
     * @notice - Directly repays a Lender using unused tokens already held by Line with no trading
     * @dev    - callable by `borrower` or first lender in repayment queue
     * @param amount       - amount of unused tokens to use to repay Lender
     * @return             - if function executed successfully
     */
    function useAndRepay(uint256 amount) external returns (bool);

    /**
     * @notice  - Claims revenue tokens from the Spigot, trades them for credit tokens via a Dex aggregator (Ox protocol) and uses the bought credit tokens to repay debt.
     *          - see SpigotedLine._claimAndTrade and SpigotedLineLib.claimAndTrade for more details on Spigot and trading logic
     *          - see LineOfCredit._repay() for more details on repayment logic
     * @dev     - does not trade asset if claimToken = credit.token
     * @dev     - callable by `arbiter`
     * @param claimToken       - The Revenue Token escrowed by Spigot to claim and use to repay debt
     * @param zeroExTradeData  - data generated by the 0x dex API to trade `claimToken` against their exchange contract
     * @return newTokens       - amount of credit tokens claimed or bought during call
     */
    function claimAndRepay(address claimToken, bytes calldata zeroExTradeData) external returns (uint256);

    /**
     *
     * @notice - allows borrower to trade revenue to credit tokens at a favorable price without repaying debt
     *         - sends all bought tokens to `unused` to be repaid later
     *         - see SpigotedLine._claimAndTrade and SpigotedLineLib.claimAndTrade for more details
     * @dev    - ensures first token in repayment queue is being bought
     * @dev    - callable by `arbiter`
     * @param claimToken      - The revenue token escrowed in the Spigot to sell in trade
     * @param zeroExTradeData - 0x API data to use in trade to sell `claimToken` for `credits[ids[0]]`
     * @return tokensBought   - amount of credit tokens bought
     */
    function claimAndTrade(address claimToken, bytes calldata zeroExTradeData) external returns (uint256 tokensBought);

    // Spigot management functions

    /**
     * @notice - allow Line (aka Owner on Spigot) to add new revenue streams to repay credit
     * @dev    - see Spigot.addSpigot()
     * @dev    - callable `arbiter` ONLY
     * @return            - if function call was successful
     */
    function addSpigot(address revenueContract, ISpigot.Setting calldata setting) external returns (bool);

    /**
     * @notice - Sets or resets the whitelisted functions that a Borrower [Operator] is allowed to perform on the revenue generating contracts
     * @dev    - see Spigot.updateWhitelistedFunction()
     * @dev    - callable `arbiter` ONLY
     * @return           - if function call was successful
     */
    function updateWhitelist(bytes4 func, bool allowed) external returns (bool);

    /**
     * @notice - Changes the revenue split between the Treasury and the Line (Owner) based upon the status of the Line of Credit
     * @dev    - callable by anyone
     * @param revenueContract   - spigot to update
     * @return didUpdate        - whether or not split was updated
     */
    function updateOwnerSplit(address revenueContract) external returns (bool);

    /**
     * @notice  - Transfers ownership of the entire Spigot from its then Owner to either the Borrower (if a Line of Credit has been been fully repaid)
     *          - or to the Arbiter (if the Line of Credit is liquidatable).
     * @dev     - callable by borrower + arbiter
     * @param to          - address that caller wants to transfer Spigot ownership to
     * @return bool       - whether or not a Spigot was released
     */
    function releaseSpigot(address to) external returns (bool);

    /**
     * @notice   - sends unused tokens to borrower if REPAID or arbiter if LIQUIDATABLE or INSOLVENT
     *           -  does not send tokens out if line is ACTIVE
     * @dev      - callable by `borrower` or `arbiter`
     * @param to           - address to send swept tokens to
     * @param token        - revenue or credit token to sweep
     * @param amount       - amount of reserve tokens to withdraw/liquidate
     */
    function sweep(address to, address token, uint256 amount) external returns (uint256);

    // getters

    /**
     * @notice - getter for `unusedTokens` mapping which is a private var
     * @param token      - address for an ERC20
     * @return amount    - amount of revenue tokens available to trade for credit tokens or credit tokens availble to repay debt with
     */
    function unused(address token) external returns (uint256);

    /**
     * @notice - Looksup `unusedTokens` + spigot.getOwnerTokens` for how many tokens arbiter must sell in claimAndTrade/Repay
     * @param token      - address for an ERC20 earned as revenue
     * @return amount    - amount of unused + claimable revenue tokens available to trade for credit tokens or credit tokens availble to repay debt with
     */
    function tradeable(address token) external returns (uint256);

    function spigot() external returns (ISpigot);
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

import {ReentrancyGuard} from "openzeppelin/utils/ReentrancyGuard.sol";
import {LineLib} from "../utils/LineLib.sol";
import {ISpigot} from "../interfaces/ISpigot.sol";

struct SpigotState {
    /// @notice Economic owner of Spigot revenue streams
    address owner;
    /// @notice account in charge of running onchain ops of spigoted contracts on behalf of owner
    address operator;
    /// @notice Total amount of revenue tokens help by the Spigot and available to be claimed by owner
    mapping(address => uint256) ownerTokens; // token -> claimable
    /// @notice Total amount of revenue tokens help by the Spigot and available to be claimed by operator
    mapping(address => uint256) operatorTokens; // token -> claimable
    /// @notice Functions that the operator is allowed to run on all revenue contracts controlled by the Spigot
    mapping(bytes4 => bool) whitelistedFunctions; // function -> allowed
    /// @notice Configurations for revenue contracts related to the split of revenue, access control to claiming revenue tokens and transfer of Spigot ownership
    mapping(address => ISpigot.Setting) settings; // revenue contract -> settings
}

/**
 * @notice - helper lib for Spigot
 * @dev see Spigot docs
 */
library SpigotLib {
    // Maximum numerator for Setting.ownerSplit param to ensure that the Owner can't claim more than 100% of revenue
    uint8 constant MAX_SPLIT = 100;
    // cap revenue per claim to avoid overflows on multiplication when calculating percentages
    uint256 constant MAX_REVENUE = type(uint256).max / MAX_SPLIT;

    function _claimRevenue(
        SpigotState storage self,
        address revenueContract,
        address token,
        bytes calldata data
    ) public returns (uint256 claimed) {
        if (self.settings[revenueContract].transferOwnerFunction == bytes4(0)) {
            revert InvalidRevenueContract();
        }

        uint256 existingBalance = LineLib.getBalance(token);

        if (self.settings[revenueContract].claimFunction == bytes4(0)) {
            // push payments

            // claimed = total balance - already accounted for balance
            claimed = existingBalance - self.ownerTokens[token] - self.operatorTokens[token];

            // underflow revert ensures we have more tokens than we started with and actually claimed revenue
        } else {
            // pull payments
            if (bytes4(data) != self.settings[revenueContract].claimFunction) {
                revert BadFunction();
            }
            (bool claimSuccess, ) = revenueContract.call(data);
            if (!claimSuccess) {
                revert ClaimFailed();
            }

            // claimed = total balance - existing balance
            claimed = LineLib.getBalance(token) - existingBalance;
            // underflow revert ensures we have more tokens than we started with and actually claimed revenue
        }

        if (claimed == 0) {
            revert NoRevenue();
        }

        // cap so uint doesnt overflow in split calculations.
        // can sweep by "attaching" a push payment spigot with same token
        if (claimed > MAX_REVENUE) claimed = MAX_REVENUE;

        return claimed;
    }

    /** see Spigot.claimRevenue */
    function claimRevenue(
        SpigotState storage self,
        address revenueContract,
        address token,
        bytes calldata data
    ) external returns (uint256 claimed) {
        claimed = _claimRevenue(self, revenueContract, token, data);

        // splits revenue stream according to Spigot settings
        uint256 ownerTokens = (claimed * self.settings[revenueContract].ownerSplit) / 100;
        // update escrowed balance
        self.ownerTokens[token] = self.ownerTokens[token] + ownerTokens;

        // update operator amount
        if (claimed > ownerTokens) {
            self.operatorTokens[token] = self.operatorTokens[token] + (claimed - ownerTokens);
        }

        emit ClaimRevenue(token, claimed, ownerTokens, revenueContract);

        return claimed;
    }

    /** see Spigot.operate */
    function operate(SpigotState storage self, address revenueContract, bytes calldata data) external returns (bool) {
        if (msg.sender != self.operator) {
            revert CallerAccessDenied();
        }

        // extract function signature from tx data and check whitelist
        bytes4 func = bytes4(data);

        if (!self.whitelistedFunctions[func]) {
            revert OperatorFnNotWhitelisted();
        }

        // cant claim revenue via operate() because that fucks up accounting logic. Owner shouldn't whitelist it anyway but just in case
        // also can't transfer ownership so Owner retains control of revenue contract
        if (
            func == self.settings[revenueContract].claimFunction ||
            func == self.settings[revenueContract].transferOwnerFunction
        ) {
            revert OperatorFnNotValid();
        }

        (bool success, ) = revenueContract.call(data);
        if (!success) {
            revert OperatorFnCallFailed();
        }

        return true;
    }

    /** see Spigot.claimOwnerTokens */
    function claimOwnerTokens(SpigotState storage self, address token) external returns (uint256 claimed) {
        if (msg.sender != self.owner) {
            revert CallerAccessDenied();
        }

        claimed = self.ownerTokens[token];

        if (claimed == 0) {
            revert ClaimFailed();
        }

        self.ownerTokens[token] = 0; // reset before send to prevent reentrancy

        LineLib.sendOutTokenOrETH(token, self.owner, claimed);

        emit ClaimOwnerTokens(token, claimed, self.owner);

        return claimed;
    }

    /** see Spigot.claimOperatorTokens */
    function claimOperatorTokens(SpigotState storage self, address token) external returns (uint256 claimed) {
        if (msg.sender != self.operator) {
            revert CallerAccessDenied();
        }

        claimed = self.operatorTokens[token];

        if (claimed == 0) {
            revert ClaimFailed();
        }

        self.operatorTokens[token] = 0; // reset before send to prevent reentrancy

        LineLib.sendOutTokenOrETH(token, self.operator, claimed);

        emit ClaimOperatorTokens(token, claimed, self.operator);

        return claimed;
    }

    /** see Spigot.addSpigot */
    function addSpigot(
        SpigotState storage self,
        address revenueContract,
        ISpigot.Setting memory setting
    ) external returns (bool) {
        if (msg.sender != self.owner) {
            revert CallerAccessDenied();
        }

        if (revenueContract == address(this)) {
            revert InvalidRevenueContract();
        }

        // spigot setting already exists
        if (self.settings[revenueContract].transferOwnerFunction != bytes4(0)) {
            revert SpigotSettingsExist();
        }

        // must set transfer func
        if (setting.transferOwnerFunction == bytes4(0)) {
            revert BadSetting();
        }
        if (setting.ownerSplit > MAX_SPLIT) {
            revert BadSetting();
        }

        self.settings[revenueContract] = setting;
        emit AddSpigot(revenueContract, setting.ownerSplit, setting.claimFunction, setting.transferOwnerFunction);

        return true;
    }

    /** see Spigot.removeSpigot */
    function removeSpigot(SpigotState storage self, address revenueContract) external returns (bool) {
        if (msg.sender != self.owner) {
            revert CallerAccessDenied();
        }

        (bool success, ) = revenueContract.call(
            abi.encodeWithSelector(
                self.settings[revenueContract].transferOwnerFunction,
                self.operator // assume function only takes one param that is new owner address
            )
        );
        require(success);

        delete self.settings[revenueContract];
        emit RemoveSpigot(revenueContract);

        return true;
    }

    /** see Spigot.updateOwnerSplit */
    function updateOwnerSplit(
        SpigotState storage self,
        address revenueContract,
        uint8 ownerSplit
    ) external returns (bool) {
        if (msg.sender != self.owner) {
            revert CallerAccessDenied();
        }
        if (ownerSplit > MAX_SPLIT) {
            revert BadSetting();
        }

        self.settings[revenueContract].ownerSplit = ownerSplit;
        emit UpdateOwnerSplit(revenueContract, ownerSplit);

        return true;
    }

    /** see Spigot.updateOwner */
    function updateOwner(SpigotState storage self, address newOwner) external returns (bool) {
        if (msg.sender != self.owner) {
            revert CallerAccessDenied();
        }
        require(newOwner != address(0));
        self.owner = newOwner;
        emit UpdateOwner(newOwner);
        return true;
    }

    /** see Spigot.updateOperator */
    function updateOperator(SpigotState storage self, address newOperator) external returns (bool) {
        if (msg.sender != self.operator && msg.sender != self.owner) {
            revert CallerAccessDenied();
        }
        require(newOperator != address(0));
        self.operator = newOperator;
        emit UpdateOperator(newOperator);
        return true;
    }

    /** see Spigot.updateWhitelistedFunction*/
    function updateWhitelistedFunction(SpigotState storage self, bytes4 func, bool allowed) external returns (bool) {
        if (msg.sender != self.owner) {
            revert CallerAccessDenied();
        }
        self.whitelistedFunctions[func] = allowed;
        emit UpdateWhitelistFunction(func, allowed);
        return true;
    }

    /** see Spigot.isWhitelisted*/
    function isWhitelisted(SpigotState storage self, bytes4 func) external view returns (bool) {
        return self.whitelistedFunctions[func];
    }

    /** see Spigot.getSetting*/
    function getSetting(
        SpigotState storage self,
        address revenueContract
    ) external view returns (uint8, bytes4, bytes4) {
        return (
            self.settings[revenueContract].ownerSplit,
            self.settings[revenueContract].claimFunction,
            self.settings[revenueContract].transferOwnerFunction
        );
    }

    // Spigot Events
    event AddSpigot(address indexed revenueContract, uint256 ownerSplit, bytes4 claimFnSig, bytes4 trsfrFnSig);

    event RemoveSpigot(address indexed revenueContract);

    event UpdateWhitelistFunction(bytes4 indexed func, bool indexed allowed);

    event UpdateOwnerSplit(address indexed revenueContract, uint8 indexed split);

    event ClaimRevenue(address indexed token, uint256 indexed amount, uint256 ownerTokens, address revenueContract);

    event ClaimOwnerTokens(address indexed token, uint256 indexed amount, address owner);

    event ClaimOperatorTokens(address indexed token, uint256 indexed amount, address ooperator);

    // Stakeholder Events

    event UpdateOwner(address indexed newOwner);

    event UpdateOperator(address indexed newOperator);

    event UpdateTreasury(address indexed newTreasury);

    // Errors

    error BadFunction();

    error OperatorFnNotWhitelisted();

    error OperatorFnNotValid();

    error OperatorFnCallFailed();

    error ClaimFailed();

    error NoRevenue();

    error UnclaimedRevenue();

    error CallerAccessDenied();

    error BadSetting();

    error InvalidRevenueContract();

    error SpigotSettingsExist();
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;
import {ILineOfCredit} from "../interfaces/ILineOfCredit.sol";
import {IOracle} from "../interfaces/IOracle.sol";
import {CreditLib} from "./CreditLib.sol";

/**
 * @title Credit Cooperative Line of Credit Library
 * @notice Core logic and variables to be reused across all Credit Cooperative Marketplace Line of Credit contracts
 */
library CreditListLib {
    event QueueCleared();
    event SortedIntoQ(bytes32 indexed id, uint256 indexed newIdx, uint256 indexed oldIdx, bytes32 oldId);
    error CantStepQ();

    /**
     * @notice  - Removes a position id from the active list of open positions.
     * @dev     - assumes `id` is stored only once in the `positions` array. if `id` occurs twice, debt would be double counted.
     * @param ids           - all current credit lines on the Line of Credit facility
     * @param id            - the hash id of the credit line to be removed from active ids after removePosition() has run
     * @return newPositions - all active credit lines on the Line of Credit facility after the `id` has been removed [Bob - consider renaming to newIds
     */
    function removePosition(bytes32[] storage ids, bytes32 id) external returns (bool) {
        uint256 len = ids.length;

        for (uint256 i; i < len; ++i) {
            if (ids[i] == id) {
                delete ids[i];
                return true;
            }
        }

        return true;
    }

    /**
     * @notice  - swap the first element in the queue, provided it is null, with the next available valid(non-null) id
     * @dev     - Must perform check for ids[0] being valid (non-zero) before calling
     * @param ids       - all current credit lines on the Line of Credit facility
     * @return swapped  - returns true if the swap has occurred
     */
    function stepQ(bytes32[] storage ids) external returns (bool) {
        if (ids[0] != bytes32(0)) {
            revert CantStepQ();
        }

        uint256 len = ids.length;
        if (len <= 1) return false;

        // skip the loop if we don't need
        if (len == 2 && ids[1] != bytes32(0)) {
            (ids[0], ids[1]) = (ids[1], ids[0]);
            emit SortedIntoQ(ids[0], 0, 1, ids[1]);
            return true;
        }

        // we never check the first id, because we already know it's null
        for (uint i = 1; i < len; ) {
            if (ids[i] != bytes32(0)) {
                (ids[0], ids[i]) = (ids[i], ids[0]); // swap the ids in storage
                emit SortedIntoQ(ids[0], 0, i, ids[i]);
                return true; // if we make the swap, return early
            }
            unchecked {
                ++i;
            }
        }
        emit QueueCleared();
        return false;
    }
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit/blob/master/COPYRIGHT.md

 pragma solidity ^0.8.16;

import {IInterestRateCredit} from "../../interfaces/IInterestRateCredit.sol";

contract InterestRateCredit is IInterestRateCredit {
    // 1 Julian astronomical year in seconds to use in calculations for rates = 31557600 seconds
    uint256 constant ONE_YEAR = 365.25 days;
    // Must divide by 100 too offset bps in numerator and divide by another 100 to offset % and get actual token amount
    uint256 constant BASE_DENOMINATOR = 10000;
    // = 31557600 * 10000 = 315576000000;
    uint256 constant INTEREST_DENOMINATOR = ONE_YEAR * BASE_DENOMINATOR;

    address immutable lineContract;

    mapping(bytes32 => Rate) public rates; // position id -> lending rates

    /**
     * @notice Interest rate / acrrued interest calculation contract for Line of Credit contracts
     */
    constructor() {
        lineContract = msg.sender;
    }

    ///////////  MODIFIERS  ///////////

    modifier onlyLineContract() {
        require(msg.sender == lineContract, "InterestRateCred: only line contract.");
        _;
    }

    /// see IInterestRateCredit.accrueInterest
    function accrueInterest(
        bytes32 id,
        uint256 drawnBalance,
        uint256 facilityBalance
    ) external override onlyLineContract returns (uint256 accrued) {
        accrued = _accrueInterest(id, drawnBalance, facilityBalance);
        // update last timestamp in storage
        rates[id].lastAccrued = block.timestamp;
    }

    function getInterestAccrued(
        bytes32 id,
        uint256 drawnBalance,
        uint256 facilityBalance
    ) external view returns (uint256) {
        return _accrueInterest(id, drawnBalance, facilityBalance);
    }

    function _accrueInterest(
        bytes32 id,
        uint256 drawnBalance,
        uint256 facilityBalance
    ) internal view returns (uint256) {
        Rate memory rate = rates[id];

        // get time since interest was last accrued iwth these balances
        uint256 timespan = block.timestamp - rate.lastAccrued;

        return (_calculateInterestOwed(rate.dRate, drawnBalance, timespan) +
            _calculateInterestOwed(rate.fRate, (facilityBalance - drawnBalance), timespan));
    }

    /**
     * @notice - total interest to accrue based on apr, balance, and length of time
     * @dev    - r = APR in bps, x = # tokens, t = time
     *         - interest = (r * x * t) / 1yr / 100
     * @param  bpsRate  - interest rate (APR) to charge against balance in bps (4 decimals)
     * @param  balance  - current balance for interest rate tier to charge interest against
     * @param  timespan - total amount of time that interest should be charged for
     *
     * @return interestOwed
     */
    function _calculateInterestOwed(
        uint256 bpsRate,
        uint256 balance,
        uint256 timespan
    ) internal pure returns (uint256) {
        return (bpsRate * balance * timespan) / INTEREST_DENOMINATOR;
    }

    /// see IInterestRateCredit.setRate
    function setRate(bytes32 id, uint128 dRate, uint128 fRate) external onlyLineContract returns (bool) {
        rates[id] = Rate({dRate: dRate, fRate: fRate, lastAccrued: block.timestamp});
        return true;
    }

    function getRates(bytes32 id) external view returns (uint128, uint128) {
        return (rates[id].dRate, rates[id].fRate);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[ERC-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC-20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @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://consensys.net/diligence/blog/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.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @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 or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * 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.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @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`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // 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
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}

Settings
{
  "remappings": [
    "chainlink/=lib/chainlink/contracts/src/v0.8/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "lib/forge-std:ds-test/=lib/forge-std/lib/ds-test/src/",
    "lib/openzeppelin-contracts:@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "lib/openzeppelin-contracts:ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
    "lib/openzeppelin-contracts:erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "lib/openzeppelin-contracts:forge-std/=lib/openzeppelin-contracts/lib/forge-std/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {
    "contracts/utils/CreditLib.sol": {
      "CreditLib": "0x18a744de156c9A1A6B6832fd4E47679146F087fA"
    },
    "contracts/utils/CreditListLib.sol": {
      "CreditListLib": "0xd3aC493cd5f02A5376849ccb494F233323AABE85"
    },
    "contracts/utils/EscrowLib.sol": {
      "EscrowLib": "0x2492eF0F83F7041867eC067582d5157290e09B06"
    },
    "contracts/utils/LineFactoryLib.sol": {
      "LineFactoryLib": "0x9dBeE9024C5DBd990783157c66ac65066cF277E4"
    },
    "contracts/utils/LineLib.sol": {
      "LineLib": "0xCE95035acd9B5aE1304F652412dB849F8629F377"
    },
    "contracts/utils/SpigotLib.sol": {
      "SpigotLib": "0x3182c0f682B84D0586F7d2C38fa24a7E720d2359"
    },
    "contracts/utils/SpigotedLineLib.sol": {
      "SpigotedLineLib": "0xa5De6E2A9503E227cA345f8aeb795A187a32F34e"
    }
  }
}

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"oracle_","type":"address"},{"internalType":"address","name":"arbiter_","type":"address"},{"internalType":"address","name":"borrower_","type":"address"},{"internalType":"address payable","name":"swapTarget_","type":"address"},{"internalType":"address","name":"spigot_","type":"address"},{"internalType":"address","name":"escrow_","type":"address"},{"internalType":"uint256","name":"ttl_","type":"uint256"},{"internalType":"uint8","name":"defaultSplit_","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[{"internalType":"address","name":"module","type":"address"}],"name":"BadModule","type":"error"},{"inputs":[],"name":"BadNewLine","type":"error"},{"inputs":[],"name":"BadRollover","type":"error"},{"inputs":[],"name":"BorrowFailed","type":"error"},{"inputs":[],"name":"CallerAccessDenied","type":"error"},{"inputs":[],"name":"CantStepQ","type":"error"},{"inputs":[],"name":"CloseFailedWithPrincipal","type":"error"},{"inputs":[],"name":"DebtOwed","type":"error"},{"inputs":[],"name":"EthSupportDisabled","type":"error"},{"inputs":[],"name":"InvalidConsent","type":"error"},{"inputs":[],"name":"NoLiquidity","type":"error"},{"inputs":[],"name":"NoTokenPrice","type":"error"},{"inputs":[],"name":"NonZeroEthValue","type":"error"},{"inputs":[],"name":"NotActive","type":"error"},{"inputs":[],"name":"NotBorrowing","type":"error"},{"inputs":[{"internalType":"address","name":"module","type":"address"}],"name":"NotInsolvent","type":"error"},{"inputs":[],"name":"NotLiquidatable","type":"error"},{"inputs":[],"name":"NotUserConsent","type":"error"},{"inputs":[],"name":"PositionExists","type":"error"},{"inputs":[],"name":"PositionIsClosed","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"totalAvailable","type":"uint256"}],"name":"RepayAmountExceedsDebt","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountAvailable","type":"uint256"}],"name":"ReservesOverdrawn","type":"error"},{"inputs":[],"name":"TokenTransferFailed","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnsupportedMutualConsentFunction","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lender","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"deposit","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"AddCredit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"CloseCreditPosition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"Default","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oracle","type":"address"},{"indexed":true,"internalType":"address","name":"arbiter","type":"address"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"}],"name":"DeployLine","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"deposit","type":"uint256"}],"name":"IncreaseCredit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InterestAccrued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"escrow","type":"address"}],"name":"Liquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"proposalId","type":"bytes32"}],"name":"MutualConsentAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"proposalId","type":"bytes32"},{"indexed":false,"internalType":"address","name":"taker","type":"address"}],"name":"MutualConsentRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"proposalId","type":"bytes32"}],"name":"MutualConsentRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RepayInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RepayPrincipal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"int256","name":"diff","type":"int256"},{"indexed":false,"internalType":"uint256","name":"tokenType","type":"uint256"}],"name":"ReservesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RevenuePayment","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":true,"internalType":"uint128","name":"dRate","type":"uint128"},{"indexed":true,"internalType":"uint128","name":"fRate","type":"uint128"}],"name":"SetRates","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"newIdx","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"oldIdx","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"oldId","type":"bytes32"}],"name":"SortedIntoQ","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"revenueToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"revenueTokenAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"debtToken","type":"address"},{"indexed":true,"internalType":"uint256","name":"debtTokensBought","type":"uint256"}],"name":"TradeSpigotRevenue","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"status","type":"uint256"}],"name":"UpdateStatus","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawProfit","type":"event"},{"inputs":[],"name":"accrueInterest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"drate","type":"uint128"},{"internalType":"uint128","name":"frate","type":"uint128"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"lender","type":"address"}],"name":"addCredit","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"revenueContract","type":"address"},{"components":[{"internalType":"uint8","name":"ownerSplit","type":"uint8"},{"internalType":"bytes4","name":"claimFunction","type":"bytes4"},{"internalType":"bytes4","name":"transferOwnerFunction","type":"bytes4"}],"internalType":"struct ISpigot.Setting","name":"setting","type":"tuple"}],"name":"addSpigot","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"arbiter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"available","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrower","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"claimToken","type":"address"},{"internalType":"bytes","name":"zeroExTradeData","type":"bytes"}],"name":"claimAndRepay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"claimToken","type":"address"},{"internalType":"bytes","name":"zeroExTradeData","type":"bytes"}],"name":"claimAndTrade","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"close","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"counts","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"credits","outputs":[{"internalType":"uint256","name":"deposit","type":"uint256"},{"internalType":"uint256","name":"principal","type":"uint256"},{"internalType":"uint256","name":"interestAccrued","type":"uint256"},{"internalType":"uint256","name":"interestRepaid","type":"uint256"},{"internalType":"uint8","name":"decimals","type":"uint8"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"bool","name":"isOpen","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deadline","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"declareInsolvent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultRevenueSplit","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"depositAndClose","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositAndRepay","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"escrow","outputs":[{"internalType":"contract IEscrow","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"healthcheck","outputs":[{"internalType":"enum LineLib.STATUS","name":"","type":"uint8"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ids","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"increaseCredit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"interestAccrued","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interestRate","outputs":[{"internalType":"contract InterestRateCredit","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"targetToken","type":"address"}],"name":"liquidate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"mutualConsentProposals","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextInQ","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint128","name":"","type":"uint128"},{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"contract IOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"releaseSpigot","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_reconstrucedMsgData","type":"bytes"}],"name":"revokeConsent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newLine","type":"address"}],"name":"rollover","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"uint128","name":"drate","type":"uint128"},{"internalType":"uint128","name":"frate","type":"uint128"}],"name":"setRates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"spigot","outputs":[{"internalType":"contract ISpigot","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"status","outputs":[{"internalType":"enum LineLib.STATUS","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stepQ","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapTarget","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"sweep","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"tradeable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"unused","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateOutstandingDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"revenueContract","type":"address"}],"name":"updateOwnerSplit","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"func","type":"bytes4"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"updateWhitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"useAndRepay","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

0x6101a06040523480156200001257600080fd5b506040516200590f3803806200590f833981016040819052620000359162000152565b600180556001600160a01b0380891660e05280881660c052861660a0528288888887898787868686846200006a81426200020c565b6080526040516200007b906200012b565b604051809103906000f08015801562000098573d6000803e3d6000fd5b506001600160a01b03908116610100526040518382169185811691908716907f823bb3f9854921652e62f2fcf067240bf72adb3bf3717b6e65c78f99df300e5890600090a4505050606460ff831611159050620000f457600080fd5b6001600160a01b039384166101205260ff166101405250811661016052939093166101805250620002349950505050505050505050565b61053680620053d983390190565b6001600160a01b03811681146200014f57600080fd5b50565b600080600080600080600080610100898b0312156200017057600080fd5b88516200017d8162000139565b60208a0151909850620001908162000139565b60408a0151909750620001a38162000139565b60608a0151909650620001b68162000139565b60808a0151909550620001c98162000139565b60a08a0151909450620001dc8162000139565b60c08a015160e08b0151919450925060ff81168114620001fb57600080fd5b809150509295985092959890939650565b808201808211156200022e57634e487b7160e01b600052601160045260246000fd5b92915050565b60805160a05160c05160e0516101005161012051610140516101605161018051614fcf6200040a6000396000818161083b01528181613287015281816139ef015281816141ce01528181614369015281816144100152818161444101526144df0152600081816106e401526134030152600081816108d701526127a701526000818161051f01528181610bad01528181610d6601528181612289015281816122e20152818161277a0152818161304c01528181613341015281816134250152818161428b01528181614330015261452301526000818161060801528181611f6501528181612ecb01528181612f90015281816131fb0152818161388a0152613db001526000818161065c015281816138680152613f6a01526000818161095d01528181610b5801528181610f8601528181611491015281816115a30152818161172e0152818161222401528181613099015281816130fc015261454b01526000818161069001528181610c3b01528181611222015281816112fd0152818161170d0152818161193701528181611c2d01528181611e53015281816120400152818161214f015281816125fd0152818161287f01528181612ae30152613078015260008181610355015261413f0152614fcf6000f3fe60806040526004361061026b5760003560e01c80637df1f1b911610144578063c3651574116100b6578063e701af991161007a578063e701af991461085d578063e96b496f146108c5578063ebb65a0f1461090b578063fac333ac1461092b578063fe25e00a1461094b578063feccf26f1461097f57600080fd5b8063c3651574146107db578063cb836209146107ee578063dfe9608314610801578063e1c7392a14610814578063e2fdcc171461082957600080fd5b8063921c5fcb11610108578063921c5fcb146107265780639637b5d314610746578063a4e7d38f14610766578063a6afed9514610786578063ac856fac1461079b578063b57cb9fc146107bb57600080fd5b80637df1f1b91461067e5780637df33c20146106b25780637e7f0cde146106d25780637eef544d14610706578063817cc1ea1461070e57600080fd5b8063501b56d8116101dd57806362c06767116101a157806362c067671461059657806367828fbd146105b65780636932854f146105d65780637c3a00fd146105f65780637c93ec301461062a5780637dc0d1d01461064a57600080fd5b8063501b56d8146104e35780635e235d541461050d5780635f618768146105415780635fae8b3d1461056157806362bfcc681461058157600080fd5b806329dcb0cf1161022f57806329dcb0cf1461034357806338deee8f1461037757806339c79e0c146103c557806342997913146103d8578063461035af146104985780634f42fc02146104ce57600080fd5b8063040cf0201461027757806318906bf7146102995780631a5bb098146102ce5780631acb7525146102ee578063200d2ed21461031c57600080fd5b3661027257005b600080fd5b34801561028357600080fd5b5061029761029236600461462f565b610994565b005b3480156102a557600080fd5b506102b96102b4366004614666565b610b4b565b60405190151581526020015b60405180910390f35b3480156102da57600080fd5b506102976102e93660046146a7565b610c30565b3480156102fa57600080fd5b5061030e61030936600461470d565b610e60565b6040519081526020016102c5565b34801561032857600080fd5b506005546103369060ff1681565b6040516102c5919061479a565b34801561034f57600080fd5b5061030e7f000000000000000000000000000000000000000000000000000000000000000081565b34801561038357600080fd5b506103ad6103923660046147a8565b6000602081905290815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016102c5565b6102976103d33660046147a8565b61120f565b3480156103e457600080fd5b5061044c6103f33660046147a8565b600460208190526000918252604090912080546001820154600283015460038401549484015460059094015492949193909260ff808316926001600160a01b0361010090910481169290811691600160a01b9091041688565b60408051988952602089019790975295870194909452606086019290925260ff1660808501526001600160a01b0390811660a08501521660c0830152151560e0820152610100016102c5565b3480156104a457600080fd5b5061030e6104b33660046146a7565b6001600160a01b031660009081526006602052604090205490565b3480156104da57600080fd5b506103366113cb565b3480156104ef57600080fd5b506104f8611406565b604080519283526020830191909152016102c5565b34801561051957600080fd5b506103ad7f000000000000000000000000000000000000000000000000000000000000000081565b34801561054d57600080fd5b5061030e61055c36600461470d565b611419565b34801561056d57600080fd5b5061030e61057c3660046147c1565b611596565b34801561058d57600080fd5b50610297611647565b3480156105a257600080fd5b5061030e6105b13660046147f1565b6116bb565b3480156105c257600080fd5b506102b96105d13660046147a8565b61182d565b3480156105e257600080fd5b506104f86105f13660046147a8565b611ba9565b34801561060257600080fd5b506103ad7f000000000000000000000000000000000000000000000000000000000000000081565b34801561063657600080fd5b5061029761064536600461462f565b611be3565b34801561065657600080fd5b506103ad7f000000000000000000000000000000000000000000000000000000000000000081565b34801561068a57600080fd5b506103ad7f000000000000000000000000000000000000000000000000000000000000000081565b3480156106be57600080fd5b5061030e6106cd3660046147a8565b611f22565b3480156106de57600080fd5b506103ad7f000000000000000000000000000000000000000000000000000000000000000081565b610297611fcc565b34801561071a57600080fd5b506002546003546104f8565b34801561073257600080fd5b506102b9610741366004614858565b612217565b34801561075257600080fd5b5061030e6107613660046146a7565b6122c0565b34801561077257600080fd5b50610297610781366004614884565b612372565b34801561079257600080fd5b50610297612481565b3480156107a757600080fd5b506102976107b63660046148db565b6125e3565b3480156107c757600080fd5b506102b96107d63660046146a7565b61275c565b6102976107e936600461462f565b612826565b61030e6107fc36600461491d565b612a9f565b61029761080f3660046147a8565b612bcd565b34801561082057600080fd5b50610297612dbf565b34801561083557600080fd5b506103ad7f000000000000000000000000000000000000000000000000000000000000000081565b34801561086957600080fd5b50610872612e07565b604080519889526001600160a01b0397881660208a015296909516958701959095526060860192909252608085015260a08401526001600160801b0391821660c08401521660e0820152610100016102c5565b3480156108d157600080fd5b506108f97f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016102c5565b34801561091757600080fd5b506102b96109263660046146a7565b61302e565b34801561093757600080fd5b5061030e6109463660046147a8565b6130d9565b34801561095757600080fd5b506103ad7f000000000000000000000000000000000000000000000000000000000000000081565b34801561098b57600080fd5b506102976130fa565b61099c613196565b6000828152600460208181526040928390208351610100808201865282548252600183015493820193909352600282015494810194909452600381015460608501529182015460ff80821660808601526001600160a01b0392909104821660a085015260059092015490811660c0840152600160a01b900416151560e08201527318a744de156c9a1a6b6832fd4e47679146f087fa9063d4ce933390610a4290856131c0565b8433856040518563ffffffff1660e01b8152600401610a6494939291906149e6565b61010060405180830381865af4158015610a82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aa69190614b06565b60008381526004602081815260409283902084518155908401516001820155918301516002830155606083015160038301556080830151908201805460a08501516001600160a01b03908116610100026001600160a81b031992831660ff909516949094179390931790915560c08401516005909301805460e0909501511515600160a01b02949091169290911691909117919091179055610b4760018055565b5050565b6000336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610b9657604051631172e7cd60e01b815260040160405180910390fd5b6040516318906bf760e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906318906bf790610be49086908690600401614b23565b6020604051808303816000875af1158015610c03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c279190614b80565b90505b92915050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610c7957604051631172e7cd60e01b815260040160405180910390fd5b600360055460ff166004811115610c9257610c92614762565b14610cb0576040516309b4550360e41b815260040160405180910390fd5b6000816001600160a01b031663200d2ed26040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610cf2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d169190614b9d565b6004811115610d2757610d27614762565b14610d4557604051633cd70f8d60e01b815260040160405180910390fd5b610d4e81613265565b506040516342cef4b160e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301528216602482015273a5de6e2a9503e227ca345f8aeb795a187a32f34e906342cef4b190604401602060405180830381865af4158015610dcf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610df39190614b80565b50806001600160a01b031663e1c7392a6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610e2f57600080fd5b505af1925050508015610e40575060015b610e5d5760405163b75799e960e01b815260040160405180910390fd5b50565b600060025460001480610ea75750600460006003600081548110610e8657610e86614bbe565b90600052602060002001548152602001908152602001600020600101546000145b15610ec55760405163e0d00bd360e01b815260040160405180910390fd5b610ecd613196565b60006003600081548110610ee357610ee3614bbe565b6000918252602080832090910154808352600480835260408085208151610100808201845282548252600183015496820196909652600282015492810192909252600381015460608301529182015460ff80821660808401526001600160a01b0395909104851660a083015260059092015493841660c0820152600160a01b90930416151560e08301529250610f7990836131c0565b9050336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610fc457604051631172e7cd60e01b815260040160405180910390fd5b6000610fd6878360a001518888613307565b60a08301516001600160a01b0316600090815260066020526040812054919250906110019083614bea565b90506000836020015184604001516110199190614bea565b905080821115611027578091505b828211156110b457600061103b8484614bfd565b905061104681614c10565b8560a001516001600160a01b0316600080516020614f7a833981519152600160405161107491815260200190565b60405180910390a360a08501516001600160a01b0316600090815260066020526040812080548392906110a8908490614bfd565b9091555061112c915050565b60006110c08385614bfd565b9050808560a001516001600160a01b0316600080516020614f7a83398151915260016040516110f191815260200190565b60405180910390a360a08501516001600160a01b031660009081526006602052604081208054839290611125908490614bea565b9091555050505b61113984868460006134bc565b600086815260046020818152604080842085518155918501516001830155848101516002830155606085015160038301556080850151928201805460a087015160ff9095166001600160a81b0319918216176101006001600160a01b03968716021790915560c08601516005909301805460e0909701519385169690911695909517600160a01b9215159290920291909117909355915184928c16917f8343d4254deba24e9e0e659e47f973c9705b70baa52a884516698b13af1588bb91a35090935050505061120860018055565b9392505050565b611217613196565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461126057604051631172e7cd60e01b815260040160405180910390fd5b60008181526004602081815260408084208151610100808201845282548252600183015494820194909452600282015492810192909252600381015460608301529283015460ff80821660808401529290046001600160a01b0390811660a083015260059093015492831660c0820152600160a01b90920416151560e08201526112ea90836131c0565b60408101519091506113276113218385847f00000000000000000000000000000000000000000000000000000000000000006134bc565b84613543565b60008481526004602081815260409283902084518155908401516001820155918301516002830155606083015160038301556080830151908201805460a08501516001600160a01b03908116610100026001600160a81b031992831660ff909516949094179390931790915560c08401516005909301805460e0909501511515600160a01b0294909116929091169190911791909117905550610e5d905060018055565b6000600160055460ff1660048111156113e6576113e6614762565b10156113f157600080fd5b6114016113fc613704565b61373d565b905090565b6000806114116137d5565b915091509091565b600060025460001480611460575060046000600360008154811061143f5761143f614bbe565b90600052602060002001548152602001908152602001600020600101546000145b1561147e5760405163e0d00bd360e01b815260040160405180910390fd5b611486613196565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146114cf57604051631172e7cd60e01b815260040160405180910390fd5b60006004600060036000815481106114e9576114e9614bbe565b9060005260206000200154815260200190815260200160002060040160019054906101000a90046001600160a01b03169050600061152986838787613307565b6001600160a01b038316600090815260066020526040812080549293508392909190611556908490614bea565b90915550506040516001815281906001600160a01b03841690600080516020614f7a8339815191529060200160405180910390a391505061120860018055565b6000336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146115e157604051631172e7cd60e01b815260040160405180910390fd5b60026115ee6113fc613704565b60048111156115ff576115ff614762565b1461161d57604051636ef5bcdd60e11b815260040160405180910390fd5b610c27600360008154811061163457611634614bbe565b90600052602060002001548484336139be565b604051631272aa3360e11b81526003600482015273d3ac493cd5f02a5376849ccb494f233323aabe85906324e5546690602401602060405180830381865af4158015611697573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5d9190614b80565b60006116c5613196565b6001600160a01b03831660009081526006602052604081205473a5de6e2a9503e227ca345f8aeb795a187a32f34e906385831bc39087908790879061170b6113fc613704565b7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006040518863ffffffff1660e01b815260040161176f9796959493929190614c2c565b602060405180830381865af415801561178c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117b09190614c7e565b90508015611822576001600160a01b038416600090815260066020526040812080548392906117e0908490614bfd565b909155506117ef905081614c10565b846001600160a01b0316600080516020614f7a833981519152600160405161181991815260200190565b60405180910390a35b905061120860018055565b600060025460001480611874575060046000600360008154811061185357611853614bbe565b90600052602060002001548152602001908152602001600020600101546000145b156118925760405163e0d00bd360e01b815260040160405180910390fd5b600060036000815481106118a8576118a8614bbe565b600091825260208083209091015480835260048083526040938490208451610100808201875282548252600183015495820195909552600282015495810195909552600381015460608601529081015460ff80821660808701526001600160a01b0394909104841660a086015260059091015480841660c0860152600160a01b900416151560e08401529250337f00000000000000000000000000000000000000000000000000000000000000009091161480159061197d57508060c001516001600160a01b0316336001600160a01b031614155b1561199b57604051631172e7cd60e01b815260040160405180910390fd5b806040015181602001516119af9190614bea565b8411156119ef57806040015181602001516119ca9190614bea565b6040516373a50fb160e01b81526004016119e691815260200190565b60405180910390fd5b60a08101516001600160a01b0316600090815260066020526040902054841115611a525760a08101516001600160a01b031660008181526006602052604090819020549051625ea67960e21b8152600481019290925260248201526044016119e6565b60a08101516001600160a01b031660009081526006602052604081208054869290611a7e908490614bfd565b90915550611a8d905084614c10565b8160a001516001600160a01b0316600080516020614f7a8339815191526000604051611abb91815260200190565b60405180910390a3611ad9611ad082846131c0565b838660006134bc565b600083815260046020818152604080842085518155918501516001830155848101516002830155606085015160038301556080850151928201805460a08088015160ff9096166001600160a81b0319928316176101006001600160a01b03978816021790925560c08701516005909401805460e0909801519486169790911696909617600160a01b931515939093029290921790945584015192518793909116917f8343d4254deba24e9e0e659e47f973c9705b70baa52a884516698b13af1588bb91a36001925050505b919050565b6000818152600460205260408120600181015490548291611bc991614bfd565b600093845260046020526040909320600301549293915050565b611beb613196565b600160055460ff166004811115611c0457611c04614762565b14611c2257604051634065aaf160e11b815260040160405180910390fd5b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611c6b57604051631172e7cd60e01b815260040160405180910390fd5b60008281526004602081815260408084208151610100808201845282548252600183015494820194909452600282015492810192909252600381015460608301529283015460ff80821660808401529290046001600160a01b0390811660a083015260059093015492831660c0820152600160a01b90920416151560e0820152611cf590846131c0565b90508060e00151611d195760405163d320557760e01b815260040160405180910390fd5b60208101518151611d2a9190614bfd565b821115611d4a57604051636180f03f60e11b815260040160405180910390fd5b8181602001818151611d5c9190614bea565b9052506000838152600460208181526040928390208451815590840151600180830191909155928401516002820155606084015160038201556080840151918101805460a08601516001600160a01b03908116610100026001600160a81b031992831660ff909616959095179490941790915560c08501516005909201805460e08701511515600160a01b0292169290931691909117179055611e006113fc613704565b6004811115611e1157611e11614762565b14611e2f57604051636b263a7560e01b815260040160405180910390fd5b60a081015160405163d55fad0560e01b81526001600160a01b0391821660048201527f000000000000000000000000000000000000000000000000000000000000000090911660248201526044810183905273ce95035acd9b5ae1304f652412db849f8629f3779063d55fad0590606401602060405180830381865af4158015611ebd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ee19190614b80565b50604051829084907f23a87b7444856c0a68cbd431aaafca806284c2bd83791d761cc6adb66f92075990600090a3611f1883613ab5565b50610b4760018055565b60008181526004602081905260408083209051631ebff4d960e11b81527318a744de156c9a1a6b6832fd4e47679146f087fa92633d7fe9b292611f8b92909187917f00000000000000000000000000000000000000000000000000000000000000009101614d08565b602060405180830381865af4158015611fa8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2a9190614c7e565b611fd4613196565b60025415806120175750600460006003600081548110611ff657611ff6614bbe565b90600052602060002001548152602001908152602001600020600101546000145b156120355760405163e0d00bd360e01b815260040160405180910390fd5b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461207e57604051631172e7cd60e01b815260040160405180910390fd5b6000600360008154811061209457612094614bbe565b6000918252602080832090910154808352600480835260408085208151610100808201845282548252600183015496820196909652600282015492810192909252600381015460608301529182015460ff80821660808401526001600160a01b0395909104851660a083015260059092015493841660c0820152600160a01b90930416151560e0830152925061212a90836131c0565b90506000816040015182602001516121429190614bea565b90506121736113218385847f00000000000000000000000000000000000000000000000000000000000000006134bc565b60009384526004602081815260409586902083518155908301516001820155948201516002860155606082015160038601556080820151908501805460a08401516001600160a01b03908116610100026001600160a81b031992831660ff909516949094179390931790915560c08301516005909601805460e0909401511515600160a01b029390911695909116949094171790925550612215905060018055565b565b6000336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461226257604051631172e7cd60e01b815260040160405180910390fd5b604051633500fa7b60e21b81526001600160e01b03198416600482015282151560248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063d403e9ec90604401610be4565b604051630d63d4af60e41b81526001600160a01b0382811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063d63d4af090602401602060405180830381865afa15801561232b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061234f9190614c7e565b6001600160a01b038316600090815260066020526040902054610c2a9190614bea565b60a48111806123815750600481105b1561239f57604051632c9dd1c960e01b815260040160405180910390fd5b60008282336040516020016123b693929190614d38565b60408051601f1981840301815291815281516020928301206000818152928390529120549091506001600160a01b03168061240457604051630df0f4e960e41b815260040160405180910390fd5b6001600160a01b038116331461242d5760405163110dc63f60e01b815260040160405180910390fd5b6000828152602081815260409182902080546001600160a01b031916905590518381527f2d8bff7bc81a295ee86f540e195478665ae7503c42e2fe02600843e860a1d9db910160405180910390a150505050565b6003546000805b828110156125de57600381815481106124a3576124a3614bbe565b600091825260208083209091015480835260048083526040938490208451610100808201875282548252600183015495820195909552600282015495810195909552600381015460608601529081015460ff80821660808701526001600160a01b0394909104841660a086015260059091015492831660c0850152600160a01b909204909116151560e0830152925061253c81846131c0565b60008481526004602081815260409283902084518155908401516001820155918301516002830155606083015160038301556080830151908201805460a085015160ff9093166001600160a81b0319918216176101006001600160a01b03948516021790915560c08401516005909301805460e09095015193909216931692909217600160a01b91151591909102179055506125d781614d5e565b9050612488565b505050565b600083815260046020526040902060050154839061262b907f0000000000000000000000000000000000000000000000000000000000000000906001600160a01b0316613bf9565b15612756576000848152600460208181526040928390208351610100808201865282548252600183015493820193909352600282015494810194909452600381015460608501529182015460ff80821660808601526001600160a01b0392909104821660a085015260059092015490811660c0840152600160a01b900416151560e08201526126ba90856131c0565b60008581526004602081815260409283902084518155908401516001820155918301516002830155606083015160038301556080830151908201805460a085015160ff9093166001600160a81b0319918216176101006001600160a01b03948516021790915560c08401516005909301805460e09095015193909216931692909217600160a01b91151591909102179055612756848484613d83565b50505050565b600073a5de6e2a9503e227ca345f8aeb795a187a32f34e63306d4f037f0000000000000000000000000000000000000000000000000000000000000000846127a56113fc613704565b7f00000000000000000000000000000000000000000000000000000000000000006040518563ffffffff1660e01b81526004016127e59493929190614d77565b602060405180830381865af4158015612802573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2a9190614b80565b61282e613196565b600160055460ff16600481111561284757612847614762565b1461286557604051634065aaf160e11b815260040160405180910390fd5b60008281526004602052604090206005015482906128ad907f0000000000000000000000000000000000000000000000000000000000000000906001600160a01b0316613bf9565b15611f185760008381526004602081815260408084208151610100808201845282548252600183015494820194909452600282015492810192909252600381015460608301529283015460ff80821660808401529290046001600160a01b0390811660a083015260059093015492831660c0820152600160a01b90920416151560e082015261293c90856131c0565b905082816000018181516129509190614bea565b90525060008481526004602081815260409283902084518155908401516001820155838301516002820155606084015160038201556080840151818301805460a08701516001600160a01b0390811661010081026001600160a81b031993841660ff909616959095179490941790925560c08701516005909401805460e08901511515600160a01b02921694909216938417179055925162f6020f60e71b81529182019290925260248101919091526044810184905273ce95035acd9b5ae1304f652412db849f8629f37790637b01078090606401602060405180830381865af4158015612a42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a669190614b80565b50604051839085907f12a48a2aa5f96e98f382b97b87c5f26c4fa0811d2dc20810972985835540286f90600090a35050610b4760018055565b6000612aa9613196565b600160055460ff166004811115612ac257612ac2614762565b14612ae057604051634065aaf160e11b815260040160405180910390fd5b817f0000000000000000000000000000000000000000000000000000000000000000612b0c8282613bf9565b15612bb9576000612b1e858789613e6c565b9050612b2b818a8a613d83565b60405162f6020f60e71b81526001600160a01b038088166004830152861660248201526044810188905273ce95035acd9b5ae1304f652412db849f8629f37790637b01078090606401602060405180830381865af4158015612b91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bb59190614b80565b5092505b5050612bc460018055565b95945050505050565b612bd5613196565b6002541580612c185750600460006003600081548110612bf757612bf7614bbe565b90600052602060002001548152602001908152602001600020600101546000145b15612c365760405163e0d00bd360e01b815260040160405180910390fd5b60006003600081548110612c4c57612c4c614bbe565b6000918252602080832090910154808352600480835260408085208151610100808201845282548252600183015496820196909652600282015492810192909252600381015460608301529182015460ff80821660808401526001600160a01b0395909104851660a083015260059092015493841660c0820152600160a01b90930416151560e08301529250612ce290836131c0565b905080604001518160200151612cf89190614bea565b831115612d1357806040015181602001516119ca9190614bea565b612d1f818385336134bc565b60009283526004602081815260409485902083518155908301516001820155938201516002850155606082015160038501556080820151908401805460a08401516001600160a01b03908116610100026001600160a81b031992831660ff909516949094179390931790915560c08301516005909501805460e0909401511515600160a01b029390911694909116939093171790915550610e5d60018055565b600060055460ff166004811115612dd857612dd8614762565b14612df55760405162dc149f60e41b815260040160405180910390fd5b612dfd6140d0565b610e5d600161373d565b60008060008060008060008060006003600081548110612e2957612e29614bbe565b6000918252602080832091909101548083526004808352604080852081516101008082018452825482526001830154968201879052600283015482850152600383015460608301528285015460ff80821660808501529190046001600160a01b0390811660a084015260059093015480841660c0840152600160a01b900416151560e0820152915163cc280aaf60e01b815292830194909452602482018390527f0000000000000000000000000000000000000000000000000000000000000000909316604482015290935090919081907318a744de156c9a1a6b6832fd4e47679146f087fa9063cc280aaf906064016040805180830381865af4158015612f35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f599190614dae565b91509150838360c001518460a00151856020015186600001517318a744de156c9a1a6b6832fd4e47679146f087fa633d7fe9b2898b7f00000000000000000000000000000000000000000000000000000000000000006040518463ffffffff1660e01b8152600401612fcd93929190614ddd565b602060405180830381865af4158015612fea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061300e9190614c7e565b87879b509b509b509b509b509b509b509b50505050509091929394959697565b600073a5de6e2a9503e227ca345f8aeb795a187a32f34e63f6be8d107f00000000000000000000000000000000000000000000000000000000000000006130766113fc613704565b7f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000876040518663ffffffff1660e01b81526004016127e5959493929190614dec565b600381815481106130e957600080fd5b600091825260209091200154905081565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316331461314357604051631172e7cd60e01b815260040160405180910390fd5b61314e6113fc613704565b600481111561315f5761315f614762565b60021461317f57604051636ef5bcdd60e11b815260040160405180910390fd5b6131876140e0565b1561221557610e5d600461373d565b6002600154036131b957604051633ee5aeb560e01b815260040160405180910390fd5b6002600155565b6131c86145d3565b604051634a4b6ed160e11b81527318a744de156c9a1a6b6832fd4e47679146f087fa90639496dda29061322390869086907f000000000000000000000000000000000000000000000000000000000000000090600401614ddd565b61010060405180830381865af4158015613241573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c279190614b06565b60405163a2c5925f60e01b81526001600160a01b0382811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063a2c5925f906024016020604051808303816000875af11580156132d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132f69190614b80565b6132ff57600080fd5b506001919050565b6000836001600160a01b0316856001600160a01b0316036133b55760405163f07c570960e01b81526001600160a01b0386811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063f07c5709906024016020604051808303816000875af115801561338a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133ae9190614c7e565b90506134b4565b6001600160a01b03851660009081526006602052604080822054905163b085a00d60e01b8152829173a5de6e2a9503e227ca345f8aeb795a187a32f34e9163b085a00d91613451918b918b917f0000000000000000000000000000000000000000000000000000000000000000917f0000000000000000000000000000000000000000000000000000000000000000918d908d90600401614e2c565b6040805180830381865af415801561346d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134919190614e92565b6001600160a01b03891660009081526006602052604090205592506134b4915050565b949350505050565b6134c46145d3565b60405163d7c637c960e01b81527318a744de156c9a1a6b6832fd4e47679146f087fa9063d7c637c990613501908890889088908890600401614eb6565b61010060405180830381865af415801561351f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bc49190614b06565b61354b6145d3565b8260e0015161356d5760405163d320557760e01b815260040160405180910390fd5b6020830151156135905760405163131689dd60e01b815260040160405180910390fd5b600060e08401526040516299c7a960e21b8152600360048201526024810183905273d3ac493cd5f02a5376849ccb494f233323aabe85906302671ea490604401602060405180830381865af41580156135ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136119190614b80565b506000801b600360008154811061362a5761362a614bbe565b9060005260206000200154036136b057604051631272aa3360e11b81526003600482015273d3ac493cd5f02a5376849ccb494f233323aabe85906324e5546690602401602060405180830381865af415801561368a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136ae9190614b80565b505b6002805460001981019091556000036001016136d2576136d0600361373d565b505b60405182907f7a6d4520711ed1189e0417ec7e099096e5ac3c188863274446a6d3dafded499890600090a25090919050565b60008061370f6140f9565b9050600181600481111561372557613725614762565b1461372f57919050565b6137376141ca565b91505090565b600081600481111561375157613751614762565b60055460ff16600481111561376857613768614762565b03613771575090565b81600481111561378357613783614762565b6040517f2125b4f86fd7a6a349e4e174a768e7c98ec9b7d124822556b128e40a16b642f790600090a26005805483919060ff191660018360048111156137cb576137cb614762565b0217905592915050565b60035460009081908082036137ef57506000928392509050565b6000805b828110156139b7576003818154811061380e5761380e614bbe565b600091825260209091200154915081156139a75760008281526004602081905260408083209051636b579f7b60e11b8152839283927318a744de156c9a1a6b6832fd4e47679146f087fa9263d6af3ef6926138b092918a917f0000000000000000000000000000000000000000000000000000000000000000917f00000000000000000000000000000000000000000000000000000000000000009101614eec565b61014060405180830381865af41580156138ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138f29190614f23565b919450925090506139038289614bea565b975061390f8188614bea565b60008681526004602081815260409283902087518155908701516001820155918601516002830155606086015160038301556080860151908201805460a088015160ff9093166001600160a81b0319918216176101006001600160a01b03948516021790915560c08701516005909301805460e09098015193909216961695909517600160a01b911515919091021790935550909450505b6139b081614d5e565b90506137f3565b5050509091565b604051630180cdbb60e21b8152600481018490526001600160a01b03838116602483015282811660448301526000917f00000000000000000000000000000000000000000000000000000000000000009182169063060336ec906064016020604051808303816000875af1158015613a3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a5e9190614b80565b613a6757600080fd5b6040516001600160a01b038281168252851690869088907feb18a64f5be27a04f549ac2ef204545abfbd24f9274dde558c5321804b08f2629060200160405180910390a45092949350505050565b600354600090613ac790600190614bfd565b9050806000805b838111613bf25760038181548110613ae857613ae8614bbe565b90600052602060002001549150818514613b3057811580613b095750838314155b80613b24575060008281526004602052604090206001015415155b613be257809250613be2565b838303613b3e575050505050565b600060038481548110613b5357613b53614bbe565b906000526020600020015490508060038381548110613b7457613b74614bbe565b90600052602060002001819055508560038581548110613b9657613b96614bbe565b90600052602060002001819055508184877fee3e50299ece6c644a824740e6a8ff24a83a4b747cbf3796a156def7107a8feb84604051613bd891815260200190565b60405180910390a4505b613beb81614d5e565b9050613ace565b5050505050565b6000336001600160a01b03841614801590613c1d5750336001600160a01b03831614155b15613c3a576040516282b42960e81b815260040160405180910390fd5b6000613c468484614261565b90506000803683604051602001613c5f93929190614d38565b60408051601f1981840301815291815281516020928301206000818152928390529120549091506001600160a01b0316613d2a576000803633604051602001613caa93929190614d38565b60408051601f198184030181528282528051602091820120600081815280835283902080546001600160a01b031916331790558084526001600160a01b0387169184019190915292507f1396bca9f810b74b131a089057c919e38ad204e3df53fec73db0e1f3be4caa0d910160405180910390a160009350505050610c2a565b6000818152602081815260409182902080546001600160a01b031916905590518281527f590cb0fad602b541ce3d8b8de5ea73f5db52e7bafb4fab2595dca5e904009ff1910160405180910390a1506001949350505050565b604051639412647f60e01b8152600481018490526001600160801b038084166024830152821660448201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690639412647f906064016020604051808303816000875af1158015613e01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e259190614b80565b50806001600160801b0316826001600160801b0316847feb29c2b3e23350f884045ab45689641c7503ccc09063055895c6a8edcf0a7ad160405160405180910390a4505050565b6040516310ccadff60e21b81523060048201526001600160a01b038085166024830152831660448201526000907318a744de156c9a1a6b6832fd4e47679146f087fa90634332b7fc90606401602060405180830381865af4158015613ed5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ef99190614c7e565b6000818152600460205260409020600501549091506001600160a01b031615613f355760405163384e583f60e11b815260040160405180910390fd5b60405163beccf5db60e01b815260048101829052602481018390526001600160a01b03808616604483015280851660648301527f00000000000000000000000000000000000000000000000000000000000000001660848201527318a744de156c9a1a6b6832fd4e47679146f087fa9063beccf5db9060a40161010060405180830381865af4158015613fcc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ff09190614b06565b600082815260046020818152604080842085518155918501516001808401919091559085015160028084019190915560608601516003808501919091556080870151948401805460a08901516001600160a01b03908116610100026001600160a81b031992831660ff909916989098179790971790915560c08801516005909501805460e0909901511515600160a01b02989091169490951693909317959095179092558054808301825592527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b90910183905581540190559392505050565b6140d861427f565b61221561435d565b60006140ea61443d565b8015611401575061140161450c565b60055460009060ff16600381600481111561411657614116614762565b14806141335750600481600481111561413157614131614762565b145b1561413d57919050565b7f0000000000000000000000000000000000000000000000000000000000000000421015801561416e575060025415155b156141c257600360008154811061418757614187614bbe565b600091825260208220015460405190917f02a583914117d56eb883091ec1c35f0dfc356386054377ef6914e54eac90fe7c91a2600291505090565b600191505090565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663578c65e46040518163ffffffff1660e01b81526004016020604051808303816000875af115801561422c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142509190614b80565b1561425b5750600290565b50600190565b6000336001600160a01b038416146142795782610c27565b50919050565b306001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156142e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061430b9190614f5c565b6001600160a01b0316146122155760405163219f710160e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526024016119e6565b306001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b56b83536040518163ffffffff1660e01b81526004016020604051808303816000875af11580156143c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143eb9190614f5c565b6001600160a01b0316146122155760405163219f710160e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526024016119e6565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c047e5636040518163ffffffff1660e01b81526004016020604051808303816000875af115801561449f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144c39190614c7e565b1561425b576040516368f5afab60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201526024016119e6565b60405163121ee17760e11b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301527f000000000000000000000000000000000000000000000000000000000000000016602482015260009073a5de6e2a9503e227ca345f8aeb795a187a32f34e9063243dc2ee90604401602060405180830381865af41580156145af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114019190614b80565b60405180610100016040528060008152602001600081526020016000815260200160008152602001600060ff16815260200160006001600160a01b0316815260200160006001600160a01b031681526020016000151581525090565b6000806040838503121561464257600080fd5b50508035926020909101359150565b6001600160a01b0381168114610e5d57600080fd5b600080828403608081121561467a57600080fd5b833561468581614651565b92506060601f198201121561469957600080fd5b506020830190509250929050565b6000602082840312156146b957600080fd5b813561120881614651565b60008083601f8401126146d657600080fd5b50813567ffffffffffffffff8111156146ee57600080fd5b60208301915083602082850101111561470657600080fd5b9250929050565b60008060006040848603121561472257600080fd5b833561472d81614651565b9250602084013567ffffffffffffffff81111561474957600080fd5b614755868287016146c4565b9497909650939450505050565b634e487b7160e01b600052602160045260246000fd5b6005811061479657634e487b7160e01b600052602160045260246000fd5b9052565b60208101610c2a8284614778565b6000602082840312156147ba57600080fd5b5035919050565b600080604083850312156147d457600080fd5b8235915060208301356147e681614651565b809150509250929050565b60008060006060848603121561480657600080fd5b833561481181614651565b9250602084013561482181614651565b929592945050506040919091013590565b80356001600160e01b031981168114611ba457600080fd5b8015158114610e5d57600080fd5b6000806040838503121561486b57600080fd5b61487483614832565b915060208301356147e68161484a565b6000806020838503121561489757600080fd5b823567ffffffffffffffff8111156148ae57600080fd5b6148ba858286016146c4565b90969095509350505050565b6001600160801b0381168114610e5d57600080fd5b6000806000606084860312156148f057600080fd5b833592506020840135614902816148c6565b91506040840135614912816148c6565b809150509250925092565b600080600080600060a0868803121561493557600080fd5b8535614940816148c6565b94506020860135614950816148c6565b935060408601359250606086013561496781614651565b9150608086013561497781614651565b809150509295509295909350565b8051825260208101516020830152604081015160408301526060810151606083015260ff608082015116608083015260a081015160018060a01b0380821660a08501528060c08401511660c0850152505060e0810151151560e08301525050565b61016081016149f58287614985565b6101008201949094526001600160a01b039290921661012083015261014090910152919050565b60ff81168114610e5d57600080fd5b8051611ba481614a1c565b8051611ba481614651565b8051611ba48161484a565b6000610100808385031215614a6057600080fd5b6040519081019067ffffffffffffffff82118183101715614a9157634e487b7160e01b600052604160045260246000fd5b8160405280925083518152602084015160208201526040840151604082015260608401516060820152614ac660808501614a2b565b6080820152614ad760a08501614a36565b60a0820152614ae860c08501614a36565b60c0820152614af960e08501614a41565b60e0820152505092915050565b60006101008284031215614b1957600080fd5b610c278383614a4c565b6001600160a01b0383168152608081018235614b3e81614a1c565b60ff8116602084015250614b5460208401614832565b63ffffffff60e01b808216604085015280614b7160408701614832565b16606085015250509392505050565b600060208284031215614b9257600080fd5b81516112088161484a565b600060208284031215614baf57600080fd5b81516005811061120857600080fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115610c2a57610c2a614bd4565b81810381811115610c2a57610c2a614bd4565b6000600160ff1b8201614c2557614c25614bd4565b5060000390565b6001600160a01b0388811682528781166020830152604082018790526060820186905260e0820190614c616080840187614778565b80851660a084015280841660c08401525098975050505050505050565b600060208284031215614c9057600080fd5b5051919050565b80548252600181015460208301526002810154604083015260038101546060830152600481015460ff811660808401526001600160a01b03600882901c811660a085015260058301546001600160a01b038282161660c08601529150506125de60e0840160ff8360a01c1615159052565b6101408101614d178286614c97565b6101008201939093526001600160a01b039190911661012090910152919050565b8284823760609190911b6bffffffffffffffffffffffff19169101908152601401919050565b600060018201614d7057614d70614bd4565b5060010190565b6001600160a01b0385811682528416602082015260808101614d9c6040830185614778565b60ff8316606083015295945050505050565b60008060408385031215614dc157600080fd5b8251614dcc816148c6565b60208401519092506147e6816148c6565b6101408101614d178286614985565b6001600160a01b03868116825260a0820190614e0b6020840188614778565b94851660408301529284166060820152921660809092019190915292915050565b6001600160a01b03888116825287811660208301528681166040830152851660608201526080810184905260c060a0820181905281018290526000828460e0840137600060e0848401015260e0601f19601f850116830101905098975050505050505050565b60008060408385031215614ea557600080fd5b505080516020909101519092909150565b6101608101614ec58287614985565b6101008201949094526101208101929092526001600160a01b031661014090910152919050565b6101608101614efb8287614c97565b6101008201949094526001600160a01b03928316610120820152911661014090910152919050565b60008060006101408486031215614f3957600080fd5b614f438585614a4c565b9250610100840151915061012084015190509250925092565b600060208284031215614f6e57600080fd5b81516112088161465156fe7dcdf49c7e92795e08b749f8dd551c92f7be794148ac2f9db089e2e67cbdd519a26469706673582212201162845487a40bce9bd2ce925ac8f5c45d0fe796c4450496b2f0b290014edd9164736f6c6343000814003360a060405234801561001057600080fd5b50336080526080516104ff6100376000396000818161017c015261023a01526104ff6000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80637443c9071461005c5780639412647f146100b6578063b6973283146100d9578063c6198352146100fa578063dc7262051461010d575b600080fd5b61009161006a36600461036c565b6000908152602081905260409020546001600160801b0380821692600160801b9092041690565b604080516001600160801b039384168152929091166020830152015b60405180910390f35b6100c96100c43660046103a1565b61016f565b60405190151581526020016100ad565b6100ec6100e73660046103dd565b610218565b6040519081526020016100ad565b6100ec6101083660046103dd565b61022d565b61014961011b36600461036c565b600060208190529081526040902080546001909101546001600160801b0380831692600160801b9004169083565b604080516001600160801b039485168152939092166020840152908201526060016100ad565b6000336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146101c25760405162461bcd60e51b81526004016101b990610409565b60405180910390fd5b50604080516060810182526001600160801b038085168252838116602080840191825242848601908152600089815291829052949020925190518216600160801b02911617815590516001918201559392505050565b60006102258484846102a0565b949350505050565b6000336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146102775760405162461bcd60e51b81526004016101b990610409565b6102828484846102a0565b60009485526020859052604090942042600190910155509192915050565b600083815260208181526040808320815160608101835281546001600160801b038082168352600160801b90910416938101939093526001015490820181905282906102ec9042610464565b905061031182602001516001600160801b0316868661030b9190610464565b8361033b565b8251610327906001600160801b0316878461033b565b610331919061047d565b9695505050505050565b600061034d6127106301e187e0610490565b826103588587610490565b6103629190610490565b61022591906104a7565b60006020828403121561037e57600080fd5b5035919050565b80356001600160801b038116811461039c57600080fd5b919050565b6000806000606084860312156103b657600080fd5b833592506103c660208501610385565b91506103d460408501610385565b90509250925092565b6000806000606084860312156103f257600080fd5b505081359360208301359350604090920135919050565b60208082526025908201527f496e74657265737452617465437265643a206f6e6c79206c696e6520636f6e746040820152643930b1ba1760d91b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b818103818111156104775761047761044e565b92915050565b808201808211156104775761047761044e565b80820281158282048414176104775761047761044e565b6000826104c457634e487b7160e01b600052601260045260246000fd5b50049056fea2646970667358221220129feb56738570faed4037f58902f1cd55d4f6eac98183fba6898af4ceda9c0364736f6c63430008140033000000000000000000000000274946031d204567281f7616718b4abb940ef78400000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc4700000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc47000000000000000000000000def1c0ded9bec7f1a1670819833240f027b25eff0000000000000000000000002bc0feebc240a3adb11b0cebb1a8d35402a8b78200000000000000000000000086efaec7c123d613698daabd739e46102a410535000000000000000000000000000000000000000000000000000000000083d6000000000000000000000000000000000000000000000000000000000000000064

Deployed Bytecode

0x60806040526004361061026b5760003560e01c80637df1f1b911610144578063c3651574116100b6578063e701af991161007a578063e701af991461085d578063e96b496f146108c5578063ebb65a0f1461090b578063fac333ac1461092b578063fe25e00a1461094b578063feccf26f1461097f57600080fd5b8063c3651574146107db578063cb836209146107ee578063dfe9608314610801578063e1c7392a14610814578063e2fdcc171461082957600080fd5b8063921c5fcb11610108578063921c5fcb146107265780639637b5d314610746578063a4e7d38f14610766578063a6afed9514610786578063ac856fac1461079b578063b57cb9fc146107bb57600080fd5b80637df1f1b91461067e5780637df33c20146106b25780637e7f0cde146106d25780637eef544d14610706578063817cc1ea1461070e57600080fd5b8063501b56d8116101dd57806362c06767116101a157806362c067671461059657806367828fbd146105b65780636932854f146105d65780637c3a00fd146105f65780637c93ec301461062a5780637dc0d1d01461064a57600080fd5b8063501b56d8146104e35780635e235d541461050d5780635f618768146105415780635fae8b3d1461056157806362bfcc681461058157600080fd5b806329dcb0cf1161022f57806329dcb0cf1461034357806338deee8f1461037757806339c79e0c146103c557806342997913146103d8578063461035af146104985780634f42fc02146104ce57600080fd5b8063040cf0201461027757806318906bf7146102995780631a5bb098146102ce5780631acb7525146102ee578063200d2ed21461031c57600080fd5b3661027257005b600080fd5b34801561028357600080fd5b5061029761029236600461462f565b610994565b005b3480156102a557600080fd5b506102b96102b4366004614666565b610b4b565b60405190151581526020015b60405180910390f35b3480156102da57600080fd5b506102976102e93660046146a7565b610c30565b3480156102fa57600080fd5b5061030e61030936600461470d565b610e60565b6040519081526020016102c5565b34801561032857600080fd5b506005546103369060ff1681565b6040516102c5919061479a565b34801561034f57600080fd5b5061030e7f0000000000000000000000000000000000000000000000000000000065fce73481565b34801561038357600080fd5b506103ad6103923660046147a8565b6000602081905290815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016102c5565b6102976103d33660046147a8565b61120f565b3480156103e457600080fd5b5061044c6103f33660046147a8565b600460208190526000918252604090912080546001820154600283015460038401549484015460059094015492949193909260ff808316926001600160a01b0361010090910481169290811691600160a01b9091041688565b60408051988952602089019790975295870194909452606086019290925260ff1660808501526001600160a01b0390811660a08501521660c0830152151560e0820152610100016102c5565b3480156104a457600080fd5b5061030e6104b33660046146a7565b6001600160a01b031660009081526006602052604090205490565b3480156104da57600080fd5b506103366113cb565b3480156104ef57600080fd5b506104f8611406565b604080519283526020830191909152016102c5565b34801561051957600080fd5b506103ad7f0000000000000000000000002bc0feebc240a3adb11b0cebb1a8d35402a8b78281565b34801561054d57600080fd5b5061030e61055c36600461470d565b611419565b34801561056d57600080fd5b5061030e61057c3660046147c1565b611596565b34801561058d57600080fd5b50610297611647565b3480156105a257600080fd5b5061030e6105b13660046147f1565b6116bb565b3480156105c257600080fd5b506102b96105d13660046147a8565b61182d565b3480156105e257600080fd5b506104f86105f13660046147a8565b611ba9565b34801561060257600080fd5b506103ad7f00000000000000000000000086d55bbdbf6a9f8fb77cc70fa97217d358f7fe4781565b34801561063657600080fd5b5061029761064536600461462f565b611be3565b34801561065657600080fd5b506103ad7f000000000000000000000000274946031d204567281f7616718b4abb940ef78481565b34801561068a57600080fd5b506103ad7f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc4781565b3480156106be57600080fd5b5061030e6106cd3660046147a8565b611f22565b3480156106de57600080fd5b506103ad7f000000000000000000000000def1c0ded9bec7f1a1670819833240f027b25eff81565b610297611fcc565b34801561071a57600080fd5b506002546003546104f8565b34801561073257600080fd5b506102b9610741366004614858565b612217565b34801561075257600080fd5b5061030e6107613660046146a7565b6122c0565b34801561077257600080fd5b50610297610781366004614884565b612372565b34801561079257600080fd5b50610297612481565b3480156107a757600080fd5b506102976107b63660046148db565b6125e3565b3480156107c757600080fd5b506102b96107d63660046146a7565b61275c565b6102976107e936600461462f565b612826565b61030e6107fc36600461491d565b612a9f565b61029761080f3660046147a8565b612bcd565b34801561082057600080fd5b50610297612dbf565b34801561083557600080fd5b506103ad7f00000000000000000000000086efaec7c123d613698daabd739e46102a41053581565b34801561086957600080fd5b50610872612e07565b604080519889526001600160a01b0397881660208a015296909516958701959095526060860192909252608085015260a08401526001600160801b0391821660c08401521660e0820152610100016102c5565b3480156108d157600080fd5b506108f97f000000000000000000000000000000000000000000000000000000000000006481565b60405160ff90911681526020016102c5565b34801561091757600080fd5b506102b96109263660046146a7565b61302e565b34801561093757600080fd5b5061030e6109463660046147a8565b6130d9565b34801561095757600080fd5b506103ad7f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc4781565b34801561098b57600080fd5b506102976130fa565b61099c613196565b6000828152600460208181526040928390208351610100808201865282548252600183015493820193909352600282015494810194909452600381015460608501529182015460ff80821660808601526001600160a01b0392909104821660a085015260059092015490811660c0840152600160a01b900416151560e08201527318a744de156c9a1a6b6832fd4e47679146f087fa9063d4ce933390610a4290856131c0565b8433856040518563ffffffff1660e01b8152600401610a6494939291906149e6565b61010060405180830381865af4158015610a82573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aa69190614b06565b60008381526004602081815260409283902084518155908401516001820155918301516002830155606083015160038301556080830151908201805460a08501516001600160a01b03908116610100026001600160a81b031992831660ff909516949094179390931790915560c08401516005909301805460e0909501511515600160a01b02949091169290911691909117919091179055610b4760018055565b5050565b6000336001600160a01b037f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc471614610b9657604051631172e7cd60e01b815260040160405180910390fd5b6040516318906bf760e01b81526001600160a01b037f0000000000000000000000002bc0feebc240a3adb11b0cebb1a8d35402a8b78216906318906bf790610be49086908690600401614b23565b6020604051808303816000875af1158015610c03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c279190614b80565b90505b92915050565b336001600160a01b037f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc471614610c7957604051631172e7cd60e01b815260040160405180910390fd5b600360055460ff166004811115610c9257610c92614762565b14610cb0576040516309b4550360e41b815260040160405180910390fd5b6000816001600160a01b031663200d2ed26040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610cf2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d169190614b9d565b6004811115610d2757610d27614762565b14610d4557604051633cd70f8d60e01b815260040160405180910390fd5b610d4e81613265565b506040516342cef4b160e01b81526001600160a01b037f0000000000000000000000002bc0feebc240a3adb11b0cebb1a8d35402a8b782811660048301528216602482015273a5de6e2a9503e227ca345f8aeb795a187a32f34e906342cef4b190604401602060405180830381865af4158015610dcf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610df39190614b80565b50806001600160a01b031663e1c7392a6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610e2f57600080fd5b505af1925050508015610e40575060015b610e5d5760405163b75799e960e01b815260040160405180910390fd5b50565b600060025460001480610ea75750600460006003600081548110610e8657610e86614bbe565b90600052602060002001548152602001908152602001600020600101546000145b15610ec55760405163e0d00bd360e01b815260040160405180910390fd5b610ecd613196565b60006003600081548110610ee357610ee3614bbe565b6000918252602080832090910154808352600480835260408085208151610100808201845282548252600183015496820196909652600282015492810192909252600381015460608301529182015460ff80821660808401526001600160a01b0395909104851660a083015260059092015493841660c0820152600160a01b90930416151560e08301529250610f7990836131c0565b9050336001600160a01b037f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc471614610fc457604051631172e7cd60e01b815260040160405180910390fd5b6000610fd6878360a001518888613307565b60a08301516001600160a01b0316600090815260066020526040812054919250906110019083614bea565b90506000836020015184604001516110199190614bea565b905080821115611027578091505b828211156110b457600061103b8484614bfd565b905061104681614c10565b8560a001516001600160a01b0316600080516020614f7a833981519152600160405161107491815260200190565b60405180910390a360a08501516001600160a01b0316600090815260066020526040812080548392906110a8908490614bfd565b9091555061112c915050565b60006110c08385614bfd565b9050808560a001516001600160a01b0316600080516020614f7a83398151915260016040516110f191815260200190565b60405180910390a360a08501516001600160a01b031660009081526006602052604081208054839290611125908490614bea565b9091555050505b61113984868460006134bc565b600086815260046020818152604080842085518155918501516001830155848101516002830155606085015160038301556080850151928201805460a087015160ff9095166001600160a81b0319918216176101006001600160a01b03968716021790915560c08601516005909301805460e0909701519385169690911695909517600160a01b9215159290920291909117909355915184928c16917f8343d4254deba24e9e0e659e47f973c9705b70baa52a884516698b13af1588bb91a35090935050505061120860018055565b9392505050565b611217613196565b336001600160a01b037f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc47161461126057604051631172e7cd60e01b815260040160405180910390fd5b60008181526004602081815260408084208151610100808201845282548252600183015494820194909452600282015492810192909252600381015460608301529283015460ff80821660808401529290046001600160a01b0390811660a083015260059093015492831660c0820152600160a01b90920416151560e08201526112ea90836131c0565b60408101519091506113276113218385847f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc476134bc565b84613543565b60008481526004602081815260409283902084518155908401516001820155918301516002830155606083015160038301556080830151908201805460a08501516001600160a01b03908116610100026001600160a81b031992831660ff909516949094179390931790915560c08401516005909301805460e0909501511515600160a01b0294909116929091169190911791909117905550610e5d905060018055565b6000600160055460ff1660048111156113e6576113e6614762565b10156113f157600080fd5b6114016113fc613704565b61373d565b905090565b6000806114116137d5565b915091509091565b600060025460001480611460575060046000600360008154811061143f5761143f614bbe565b90600052602060002001548152602001908152602001600020600101546000145b1561147e5760405163e0d00bd360e01b815260040160405180910390fd5b611486613196565b336001600160a01b037f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc4716146114cf57604051631172e7cd60e01b815260040160405180910390fd5b60006004600060036000815481106114e9576114e9614bbe565b9060005260206000200154815260200190815260200160002060040160019054906101000a90046001600160a01b03169050600061152986838787613307565b6001600160a01b038316600090815260066020526040812080549293508392909190611556908490614bea565b90915550506040516001815281906001600160a01b03841690600080516020614f7a8339815191529060200160405180910390a391505061120860018055565b6000336001600160a01b037f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc4716146115e157604051631172e7cd60e01b815260040160405180910390fd5b60026115ee6113fc613704565b60048111156115ff576115ff614762565b1461161d57604051636ef5bcdd60e11b815260040160405180910390fd5b610c27600360008154811061163457611634614bbe565b90600052602060002001548484336139be565b604051631272aa3360e11b81526003600482015273d3ac493cd5f02a5376849ccb494f233323aabe85906324e5546690602401602060405180830381865af4158015611697573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5d9190614b80565b60006116c5613196565b6001600160a01b03831660009081526006602052604081205473a5de6e2a9503e227ca345f8aeb795a187a32f34e906385831bc39087908790879061170b6113fc613704565b7f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc477f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc476040518863ffffffff1660e01b815260040161176f9796959493929190614c2c565b602060405180830381865af415801561178c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117b09190614c7e565b90508015611822576001600160a01b038416600090815260066020526040812080548392906117e0908490614bfd565b909155506117ef905081614c10565b846001600160a01b0316600080516020614f7a833981519152600160405161181991815260200190565b60405180910390a35b905061120860018055565b600060025460001480611874575060046000600360008154811061185357611853614bbe565b90600052602060002001548152602001908152602001600020600101546000145b156118925760405163e0d00bd360e01b815260040160405180910390fd5b600060036000815481106118a8576118a8614bbe565b600091825260208083209091015480835260048083526040938490208451610100808201875282548252600183015495820195909552600282015495810195909552600381015460608601529081015460ff80821660808701526001600160a01b0394909104841660a086015260059091015480841660c0860152600160a01b900416151560e08401529250337f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc479091161480159061197d57508060c001516001600160a01b0316336001600160a01b031614155b1561199b57604051631172e7cd60e01b815260040160405180910390fd5b806040015181602001516119af9190614bea565b8411156119ef57806040015181602001516119ca9190614bea565b6040516373a50fb160e01b81526004016119e691815260200190565b60405180910390fd5b60a08101516001600160a01b0316600090815260066020526040902054841115611a525760a08101516001600160a01b031660008181526006602052604090819020549051625ea67960e21b8152600481019290925260248201526044016119e6565b60a08101516001600160a01b031660009081526006602052604081208054869290611a7e908490614bfd565b90915550611a8d905084614c10565b8160a001516001600160a01b0316600080516020614f7a8339815191526000604051611abb91815260200190565b60405180910390a3611ad9611ad082846131c0565b838660006134bc565b600083815260046020818152604080842085518155918501516001830155848101516002830155606085015160038301556080850151928201805460a08088015160ff9096166001600160a81b0319928316176101006001600160a01b03978816021790925560c08701516005909401805460e0909801519486169790911696909617600160a01b931515939093029290921790945584015192518793909116917f8343d4254deba24e9e0e659e47f973c9705b70baa52a884516698b13af1588bb91a36001925050505b919050565b6000818152600460205260408120600181015490548291611bc991614bfd565b600093845260046020526040909320600301549293915050565b611beb613196565b600160055460ff166004811115611c0457611c04614762565b14611c2257604051634065aaf160e11b815260040160405180910390fd5b336001600160a01b037f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc471614611c6b57604051631172e7cd60e01b815260040160405180910390fd5b60008281526004602081815260408084208151610100808201845282548252600183015494820194909452600282015492810192909252600381015460608301529283015460ff80821660808401529290046001600160a01b0390811660a083015260059093015492831660c0820152600160a01b90920416151560e0820152611cf590846131c0565b90508060e00151611d195760405163d320557760e01b815260040160405180910390fd5b60208101518151611d2a9190614bfd565b821115611d4a57604051636180f03f60e11b815260040160405180910390fd5b8181602001818151611d5c9190614bea565b9052506000838152600460208181526040928390208451815590840151600180830191909155928401516002820155606084015160038201556080840151918101805460a08601516001600160a01b03908116610100026001600160a81b031992831660ff909616959095179490941790915560c08501516005909201805460e08701511515600160a01b0292169290931691909117179055611e006113fc613704565b6004811115611e1157611e11614762565b14611e2f57604051636b263a7560e01b815260040160405180910390fd5b60a081015160405163d55fad0560e01b81526001600160a01b0391821660048201527f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc4790911660248201526044810183905273ce95035acd9b5ae1304f652412db849f8629f3779063d55fad0590606401602060405180830381865af4158015611ebd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ee19190614b80565b50604051829084907f23a87b7444856c0a68cbd431aaafca806284c2bd83791d761cc6adb66f92075990600090a3611f1883613ab5565b50610b4760018055565b60008181526004602081905260408083209051631ebff4d960e11b81527318a744de156c9a1a6b6832fd4e47679146f087fa92633d7fe9b292611f8b92909187917f00000000000000000000000086d55bbdbf6a9f8fb77cc70fa97217d358f7fe479101614d08565b602060405180830381865af4158015611fa8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2a9190614c7e565b611fd4613196565b60025415806120175750600460006003600081548110611ff657611ff6614bbe565b90600052602060002001548152602001908152602001600020600101546000145b156120355760405163e0d00bd360e01b815260040160405180910390fd5b336001600160a01b037f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc47161461207e57604051631172e7cd60e01b815260040160405180910390fd5b6000600360008154811061209457612094614bbe565b6000918252602080832090910154808352600480835260408085208151610100808201845282548252600183015496820196909652600282015492810192909252600381015460608301529182015460ff80821660808401526001600160a01b0395909104851660a083015260059092015493841660c0820152600160a01b90930416151560e0830152925061212a90836131c0565b90506000816040015182602001516121429190614bea565b90506121736113218385847f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc476134bc565b60009384526004602081815260409586902083518155908301516001820155948201516002860155606082015160038601556080820151908501805460a08401516001600160a01b03908116610100026001600160a81b031992831660ff909516949094179390931790915560c08301516005909601805460e0909401511515600160a01b029390911695909116949094171790925550612215905060018055565b565b6000336001600160a01b037f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc47161461226257604051631172e7cd60e01b815260040160405180910390fd5b604051633500fa7b60e21b81526001600160e01b03198416600482015282151560248201527f0000000000000000000000002bc0feebc240a3adb11b0cebb1a8d35402a8b7826001600160a01b03169063d403e9ec90604401610be4565b604051630d63d4af60e41b81526001600160a01b0382811660048301526000917f0000000000000000000000002bc0feebc240a3adb11b0cebb1a8d35402a8b7829091169063d63d4af090602401602060405180830381865afa15801561232b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061234f9190614c7e565b6001600160a01b038316600090815260066020526040902054610c2a9190614bea565b60a48111806123815750600481105b1561239f57604051632c9dd1c960e01b815260040160405180910390fd5b60008282336040516020016123b693929190614d38565b60408051601f1981840301815291815281516020928301206000818152928390529120549091506001600160a01b03168061240457604051630df0f4e960e41b815260040160405180910390fd5b6001600160a01b038116331461242d5760405163110dc63f60e01b815260040160405180910390fd5b6000828152602081815260409182902080546001600160a01b031916905590518381527f2d8bff7bc81a295ee86f540e195478665ae7503c42e2fe02600843e860a1d9db910160405180910390a150505050565b6003546000805b828110156125de57600381815481106124a3576124a3614bbe565b600091825260208083209091015480835260048083526040938490208451610100808201875282548252600183015495820195909552600282015495810195909552600381015460608601529081015460ff80821660808701526001600160a01b0394909104841660a086015260059091015492831660c0850152600160a01b909204909116151560e0830152925061253c81846131c0565b60008481526004602081815260409283902084518155908401516001820155918301516002830155606083015160038301556080830151908201805460a085015160ff9093166001600160a81b0319918216176101006001600160a01b03948516021790915560c08401516005909301805460e09095015193909216931692909217600160a01b91151591909102179055506125d781614d5e565b9050612488565b505050565b600083815260046020526040902060050154839061262b907f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc47906001600160a01b0316613bf9565b15612756576000848152600460208181526040928390208351610100808201865282548252600183015493820193909352600282015494810194909452600381015460608501529182015460ff80821660808601526001600160a01b0392909104821660a085015260059092015490811660c0840152600160a01b900416151560e08201526126ba90856131c0565b60008581526004602081815260409283902084518155908401516001820155918301516002830155606083015160038301556080830151908201805460a085015160ff9093166001600160a81b0319918216176101006001600160a01b03948516021790915560c08401516005909301805460e09095015193909216931692909217600160a01b91151591909102179055612756848484613d83565b50505050565b600073a5de6e2a9503e227ca345f8aeb795a187a32f34e63306d4f037f0000000000000000000000002bc0feebc240a3adb11b0cebb1a8d35402a8b782846127a56113fc613704565b7f00000000000000000000000000000000000000000000000000000000000000646040518563ffffffff1660e01b81526004016127e59493929190614d77565b602060405180830381865af4158015612802573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2a9190614b80565b61282e613196565b600160055460ff16600481111561284757612847614762565b1461286557604051634065aaf160e11b815260040160405180910390fd5b60008281526004602052604090206005015482906128ad907f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc47906001600160a01b0316613bf9565b15611f185760008381526004602081815260408084208151610100808201845282548252600183015494820194909452600282015492810192909252600381015460608301529283015460ff80821660808401529290046001600160a01b0390811660a083015260059093015492831660c0820152600160a01b90920416151560e082015261293c90856131c0565b905082816000018181516129509190614bea565b90525060008481526004602081815260409283902084518155908401516001820155838301516002820155606084015160038201556080840151818301805460a08701516001600160a01b0390811661010081026001600160a81b031993841660ff909616959095179490941790925560c08701516005909401805460e08901511515600160a01b02921694909216938417179055925162f6020f60e71b81529182019290925260248101919091526044810184905273ce95035acd9b5ae1304f652412db849f8629f37790637b01078090606401602060405180830381865af4158015612a42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a669190614b80565b50604051839085907f12a48a2aa5f96e98f382b97b87c5f26c4fa0811d2dc20810972985835540286f90600090a35050610b4760018055565b6000612aa9613196565b600160055460ff166004811115612ac257612ac2614762565b14612ae057604051634065aaf160e11b815260040160405180910390fd5b817f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc47612b0c8282613bf9565b15612bb9576000612b1e858789613e6c565b9050612b2b818a8a613d83565b60405162f6020f60e71b81526001600160a01b038088166004830152861660248201526044810188905273ce95035acd9b5ae1304f652412db849f8629f37790637b01078090606401602060405180830381865af4158015612b91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bb59190614b80565b5092505b5050612bc460018055565b95945050505050565b612bd5613196565b6002541580612c185750600460006003600081548110612bf757612bf7614bbe565b90600052602060002001548152602001908152602001600020600101546000145b15612c365760405163e0d00bd360e01b815260040160405180910390fd5b60006003600081548110612c4c57612c4c614bbe565b6000918252602080832090910154808352600480835260408085208151610100808201845282548252600183015496820196909652600282015492810192909252600381015460608301529182015460ff80821660808401526001600160a01b0395909104851660a083015260059092015493841660c0820152600160a01b90930416151560e08301529250612ce290836131c0565b905080604001518160200151612cf89190614bea565b831115612d1357806040015181602001516119ca9190614bea565b612d1f818385336134bc565b60009283526004602081815260409485902083518155908301516001820155938201516002850155606082015160038501556080820151908401805460a08401516001600160a01b03908116610100026001600160a81b031992831660ff909516949094179390931790915560c08301516005909501805460e0909401511515600160a01b029390911694909116939093171790915550610e5d60018055565b600060055460ff166004811115612dd857612dd8614762565b14612df55760405162dc149f60e41b815260040160405180910390fd5b612dfd6140d0565b610e5d600161373d565b60008060008060008060008060006003600081548110612e2957612e29614bbe565b6000918252602080832091909101548083526004808352604080852081516101008082018452825482526001830154968201879052600283015482850152600383015460608301528285015460ff80821660808501529190046001600160a01b0390811660a084015260059093015480841660c0840152600160a01b900416151560e0820152915163cc280aaf60e01b815292830194909452602482018390527f00000000000000000000000086d55bbdbf6a9f8fb77cc70fa97217d358f7fe47909316604482015290935090919081907318a744de156c9a1a6b6832fd4e47679146f087fa9063cc280aaf906064016040805180830381865af4158015612f35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f599190614dae565b91509150838360c001518460a00151856020015186600001517318a744de156c9a1a6b6832fd4e47679146f087fa633d7fe9b2898b7f00000000000000000000000086d55bbdbf6a9f8fb77cc70fa97217d358f7fe476040518463ffffffff1660e01b8152600401612fcd93929190614ddd565b602060405180830381865af4158015612fea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061300e9190614c7e565b87879b509b509b509b509b509b509b509b50505050509091929394959697565b600073a5de6e2a9503e227ca345f8aeb795a187a32f34e63f6be8d107f0000000000000000000000002bc0feebc240a3adb11b0cebb1a8d35402a8b7826130766113fc613704565b7f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc477f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc47876040518663ffffffff1660e01b81526004016127e5959493929190614dec565b600381815481106130e957600080fd5b600091825260209091200154905081565b7f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc476001600160a01b0316331461314357604051631172e7cd60e01b815260040160405180910390fd5b61314e6113fc613704565b600481111561315f5761315f614762565b60021461317f57604051636ef5bcdd60e11b815260040160405180910390fd5b6131876140e0565b1561221557610e5d600461373d565b6002600154036131b957604051633ee5aeb560e01b815260040160405180910390fd5b6002600155565b6131c86145d3565b604051634a4b6ed160e11b81527318a744de156c9a1a6b6832fd4e47679146f087fa90639496dda29061322390869086907f00000000000000000000000086d55bbdbf6a9f8fb77cc70fa97217d358f7fe4790600401614ddd565b61010060405180830381865af4158015613241573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c279190614b06565b60405163a2c5925f60e01b81526001600160a01b0382811660048301526000917f00000000000000000000000086efaec7c123d613698daabd739e46102a4105359091169063a2c5925f906024016020604051808303816000875af11580156132d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132f69190614b80565b6132ff57600080fd5b506001919050565b6000836001600160a01b0316856001600160a01b0316036133b55760405163f07c570960e01b81526001600160a01b0386811660048301527f0000000000000000000000002bc0feebc240a3adb11b0cebb1a8d35402a8b782169063f07c5709906024016020604051808303816000875af115801561338a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133ae9190614c7e565b90506134b4565b6001600160a01b03851660009081526006602052604080822054905163b085a00d60e01b8152829173a5de6e2a9503e227ca345f8aeb795a187a32f34e9163b085a00d91613451918b918b917f000000000000000000000000def1c0ded9bec7f1a1670819833240f027b25eff917f0000000000000000000000002bc0feebc240a3adb11b0cebb1a8d35402a8b782918d908d90600401614e2c565b6040805180830381865af415801561346d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134919190614e92565b6001600160a01b03891660009081526006602052604090205592506134b4915050565b949350505050565b6134c46145d3565b60405163d7c637c960e01b81527318a744de156c9a1a6b6832fd4e47679146f087fa9063d7c637c990613501908890889088908890600401614eb6565b61010060405180830381865af415801561351f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bc49190614b06565b61354b6145d3565b8260e0015161356d5760405163d320557760e01b815260040160405180910390fd5b6020830151156135905760405163131689dd60e01b815260040160405180910390fd5b600060e08401526040516299c7a960e21b8152600360048201526024810183905273d3ac493cd5f02a5376849ccb494f233323aabe85906302671ea490604401602060405180830381865af41580156135ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136119190614b80565b506000801b600360008154811061362a5761362a614bbe565b9060005260206000200154036136b057604051631272aa3360e11b81526003600482015273d3ac493cd5f02a5376849ccb494f233323aabe85906324e5546690602401602060405180830381865af415801561368a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136ae9190614b80565b505b6002805460001981019091556000036001016136d2576136d0600361373d565b505b60405182907f7a6d4520711ed1189e0417ec7e099096e5ac3c188863274446a6d3dafded499890600090a25090919050565b60008061370f6140f9565b9050600181600481111561372557613725614762565b1461372f57919050565b6137376141ca565b91505090565b600081600481111561375157613751614762565b60055460ff16600481111561376857613768614762565b03613771575090565b81600481111561378357613783614762565b6040517f2125b4f86fd7a6a349e4e174a768e7c98ec9b7d124822556b128e40a16b642f790600090a26005805483919060ff191660018360048111156137cb576137cb614762565b0217905592915050565b60035460009081908082036137ef57506000928392509050565b6000805b828110156139b7576003818154811061380e5761380e614bbe565b600091825260209091200154915081156139a75760008281526004602081905260408083209051636b579f7b60e11b8152839283927318a744de156c9a1a6b6832fd4e47679146f087fa9263d6af3ef6926138b092918a917f000000000000000000000000274946031d204567281f7616718b4abb940ef784917f00000000000000000000000086d55bbdbf6a9f8fb77cc70fa97217d358f7fe479101614eec565b61014060405180830381865af41580156138ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138f29190614f23565b919450925090506139038289614bea565b975061390f8188614bea565b60008681526004602081815260409283902087518155908701516001820155918601516002830155606086015160038301556080860151908201805460a088015160ff9093166001600160a81b0319918216176101006001600160a01b03948516021790915560c08701516005909301805460e09098015193909216961695909517600160a01b911515919091021790935550909450505b6139b081614d5e565b90506137f3565b5050509091565b604051630180cdbb60e21b8152600481018490526001600160a01b03838116602483015282811660448301526000917f00000000000000000000000086efaec7c123d613698daabd739e46102a4105359182169063060336ec906064016020604051808303816000875af1158015613a3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a5e9190614b80565b613a6757600080fd5b6040516001600160a01b038281168252851690869088907feb18a64f5be27a04f549ac2ef204545abfbd24f9274dde558c5321804b08f2629060200160405180910390a45092949350505050565b600354600090613ac790600190614bfd565b9050806000805b838111613bf25760038181548110613ae857613ae8614bbe565b90600052602060002001549150818514613b3057811580613b095750838314155b80613b24575060008281526004602052604090206001015415155b613be257809250613be2565b838303613b3e575050505050565b600060038481548110613b5357613b53614bbe565b906000526020600020015490508060038381548110613b7457613b74614bbe565b90600052602060002001819055508560038581548110613b9657613b96614bbe565b90600052602060002001819055508184877fee3e50299ece6c644a824740e6a8ff24a83a4b747cbf3796a156def7107a8feb84604051613bd891815260200190565b60405180910390a4505b613beb81614d5e565b9050613ace565b5050505050565b6000336001600160a01b03841614801590613c1d5750336001600160a01b03831614155b15613c3a576040516282b42960e81b815260040160405180910390fd5b6000613c468484614261565b90506000803683604051602001613c5f93929190614d38565b60408051601f1981840301815291815281516020928301206000818152928390529120549091506001600160a01b0316613d2a576000803633604051602001613caa93929190614d38565b60408051601f198184030181528282528051602091820120600081815280835283902080546001600160a01b031916331790558084526001600160a01b0387169184019190915292507f1396bca9f810b74b131a089057c919e38ad204e3df53fec73db0e1f3be4caa0d910160405180910390a160009350505050610c2a565b6000818152602081815260409182902080546001600160a01b031916905590518281527f590cb0fad602b541ce3d8b8de5ea73f5db52e7bafb4fab2595dca5e904009ff1910160405180910390a1506001949350505050565b604051639412647f60e01b8152600481018490526001600160801b038084166024830152821660448201527f00000000000000000000000086d55bbdbf6a9f8fb77cc70fa97217d358f7fe476001600160a01b031690639412647f906064016020604051808303816000875af1158015613e01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e259190614b80565b50806001600160801b0316826001600160801b0316847feb29c2b3e23350f884045ab45689641c7503ccc09063055895c6a8edcf0a7ad160405160405180910390a4505050565b6040516310ccadff60e21b81523060048201526001600160a01b038085166024830152831660448201526000907318a744de156c9a1a6b6832fd4e47679146f087fa90634332b7fc90606401602060405180830381865af4158015613ed5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ef99190614c7e565b6000818152600460205260409020600501549091506001600160a01b031615613f355760405163384e583f60e11b815260040160405180910390fd5b60405163beccf5db60e01b815260048101829052602481018390526001600160a01b03808616604483015280851660648301527f000000000000000000000000274946031d204567281f7616718b4abb940ef7841660848201527318a744de156c9a1a6b6832fd4e47679146f087fa9063beccf5db9060a40161010060405180830381865af4158015613fcc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ff09190614b06565b600082815260046020818152604080842085518155918501516001808401919091559085015160028084019190915560608601516003808501919091556080870151948401805460a08901516001600160a01b03908116610100026001600160a81b031992831660ff909916989098179790971790915560c08801516005909501805460e0909901511515600160a01b02989091169490951693909317959095179092558054808301825592527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b90910183905581540190559392505050565b6140d861427f565b61221561435d565b60006140ea61443d565b8015611401575061140161450c565b60055460009060ff16600381600481111561411657614116614762565b14806141335750600481600481111561413157614131614762565b145b1561413d57919050565b7f0000000000000000000000000000000000000000000000000000000065fce734421015801561416e575060025415155b156141c257600360008154811061418757614187614bbe565b600091825260208220015460405190917f02a583914117d56eb883091ec1c35f0dfc356386054377ef6914e54eac90fe7c91a2600291505090565b600191505090565b60007f00000000000000000000000086efaec7c123d613698daabd739e46102a4105356001600160a01b031663578c65e46040518163ffffffff1660e01b81526004016020604051808303816000875af115801561422c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142509190614b80565b1561425b5750600290565b50600190565b6000336001600160a01b038416146142795782610c27565b50919050565b306001600160a01b03167f0000000000000000000000002bc0feebc240a3adb11b0cebb1a8d35402a8b7826001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156142e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061430b9190614f5c565b6001600160a01b0316146122155760405163219f710160e11b81526001600160a01b037f0000000000000000000000002bc0feebc240a3adb11b0cebb1a8d35402a8b7821660048201526024016119e6565b306001600160a01b03167f00000000000000000000000086efaec7c123d613698daabd739e46102a4105356001600160a01b031663b56b83536040518163ffffffff1660e01b81526004016020604051808303816000875af11580156143c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143eb9190614f5c565b6001600160a01b0316146122155760405163219f710160e11b81526001600160a01b037f00000000000000000000000086efaec7c123d613698daabd739e46102a4105351660048201526024016119e6565b60007f00000000000000000000000086efaec7c123d613698daabd739e46102a4105356001600160a01b031663c047e5636040518163ffffffff1660e01b81526004016020604051808303816000875af115801561449f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144c39190614c7e565b1561425b576040516368f5afab60e01b81526001600160a01b037f00000000000000000000000086efaec7c123d613698daabd739e46102a4105351660048201526024016119e6565b60405163121ee17760e11b81526001600160a01b037f0000000000000000000000002bc0feebc240a3adb11b0cebb1a8d35402a8b782811660048301527f00000000000000000000000006dae7ba3958ef288adb0b9b3732ec204e48bc4716602482015260009073a5de6e2a9503e227ca345f8aeb795a187a32f34e9063243dc2ee90604401602060405180830381865af41580156145af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114019190614b80565b60405180610100016040528060008152602001600081526020016000815260200160008152602001600060ff16815260200160006001600160a01b0316815260200160006001600160a01b031681526020016000151581525090565b6000806040838503121561464257600080fd5b50508035926020909101359150565b6001600160a01b0381168114610e5d57600080fd5b600080828403608081121561467a57600080fd5b833561468581614651565b92506060601f198201121561469957600080fd5b506020830190509250929050565b6000602082840312156146b957600080fd5b813561120881614651565b60008083601f8401126146d657600080fd5b50813567ffffffffffffffff8111156146ee57600080fd5b60208301915083602082850101111561470657600080fd5b9250929050565b60008060006040848603121561472257600080fd5b833561472d81614651565b9250602084013567ffffffffffffffff81111561474957600080fd5b614755868287016146c4565b9497909650939450505050565b634e487b7160e01b600052602160045260246000fd5b6005811061479657634e487b7160e01b600052602160045260246000fd5b9052565b60208101610c2a8284614778565b6000602082840312156147ba57600080fd5b5035919050565b600080604083850312156147d457600080fd5b8235915060208301356147e681614651565b809150509250929050565b60008060006060848603121561480657600080fd5b833561481181614651565b9250602084013561482181614651565b929592945050506040919091013590565b80356001600160e01b031981168114611ba457600080fd5b8015158114610e5d57600080fd5b6000806040838503121561486b57600080fd5b61487483614832565b915060208301356147e68161484a565b6000806020838503121561489757600080fd5b823567ffffffffffffffff8111156148ae57600080fd5b6148ba858286016146c4565b90969095509350505050565b6001600160801b0381168114610e5d57600080fd5b6000806000606084860312156148f057600080fd5b833592506020840135614902816148c6565b91506040840135614912816148c6565b809150509250925092565b600080600080600060a0868803121561493557600080fd5b8535614940816148c6565b94506020860135614950816148c6565b935060408601359250606086013561496781614651565b9150608086013561497781614651565b809150509295509295909350565b8051825260208101516020830152604081015160408301526060810151606083015260ff608082015116608083015260a081015160018060a01b0380821660a08501528060c08401511660c0850152505060e0810151151560e08301525050565b61016081016149f58287614985565b6101008201949094526001600160a01b039290921661012083015261014090910152919050565b60ff81168114610e5d57600080fd5b8051611ba481614a1c565b8051611ba481614651565b8051611ba48161484a565b6000610100808385031215614a6057600080fd5b6040519081019067ffffffffffffffff82118183101715614a9157634e487b7160e01b600052604160045260246000fd5b8160405280925083518152602084015160208201526040840151604082015260608401516060820152614ac660808501614a2b565b6080820152614ad760a08501614a36565b60a0820152614ae860c08501614a36565b60c0820152614af960e08501614a41565b60e0820152505092915050565b60006101008284031215614b1957600080fd5b610c278383614a4c565b6001600160a01b0383168152608081018235614b3e81614a1c565b60ff8116602084015250614b5460208401614832565b63ffffffff60e01b808216604085015280614b7160408701614832565b16606085015250509392505050565b600060208284031215614b9257600080fd5b81516112088161484a565b600060208284031215614baf57600080fd5b81516005811061120857600080fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115610c2a57610c2a614bd4565b81810381811115610c2a57610c2a614bd4565b6000600160ff1b8201614c2557614c25614bd4565b5060000390565b6001600160a01b0388811682528781166020830152604082018790526060820186905260e0820190614c616080840187614778565b80851660a084015280841660c08401525098975050505050505050565b600060208284031215614c9057600080fd5b5051919050565b80548252600181015460208301526002810154604083015260038101546060830152600481015460ff811660808401526001600160a01b03600882901c811660a085015260058301546001600160a01b038282161660c08601529150506125de60e0840160ff8360a01c1615159052565b6101408101614d178286614c97565b6101008201939093526001600160a01b039190911661012090910152919050565b8284823760609190911b6bffffffffffffffffffffffff19169101908152601401919050565b600060018201614d7057614d70614bd4565b5060010190565b6001600160a01b0385811682528416602082015260808101614d9c6040830185614778565b60ff8316606083015295945050505050565b60008060408385031215614dc157600080fd5b8251614dcc816148c6565b60208401519092506147e6816148c6565b6101408101614d178286614985565b6001600160a01b03868116825260a0820190614e0b6020840188614778565b94851660408301529284166060820152921660809092019190915292915050565b6001600160a01b03888116825287811660208301528681166040830152851660608201526080810184905260c060a0820181905281018290526000828460e0840137600060e0848401015260e0601f19601f850116830101905098975050505050505050565b60008060408385031215614ea557600080fd5b505080516020909101519092909150565b6101608101614ec58287614985565b6101008201949094526101208101929092526001600160a01b031661014090910152919050565b6101608101614efb8287614c97565b6101008201949094526001600160a01b03928316610120820152911661014090910152919050565b60008060006101408486031215614f3957600080fd5b614f438585614a4c565b9250610100840151915061012084015190509250925092565b600060208284031215614f6e57600080fd5b81516112088161465156fe7dcdf49c7e92795e08b749f8dd551c92f7be794148ac2f9db089e2e67cbdd519a26469706673582212201162845487a40bce9bd2ce925ac8f5c45d0fe796c4450496b2f0b290014edd9164736f6c63430008140033

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
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.