ETH Price: $3,416.76 (-0.64%)
Gas: 2 Gwei

Contract

0x478f6F994C6fb3cf3e444a489b3AD9edB8cCaE16
 
Transaction Hash
Method
Block
From
To
Value
Repay Loan202217092024-07-02 22:08:351 hr ago1719958115IN
0x478f6F99...dB8cCaE16
0 ETH0.001608819.63663637
Repay Loan202216552024-07-02 21:57:471 hr ago1719957467IN
0x478f6F99...dB8cCaE16
0 ETH0.001586539.10505083
Emit Loan202210852024-07-02 20:02:593 hrs ago1719950579IN
0x478f6F99...dB8cCaE16
0 ETH0.001406576.01145321
Emit Loan202210682024-07-02 19:59:233 hrs ago1719950363IN
0x478f6F99...dB8cCaE16
0 ETH0.001231084.8873694
Refinance Full202205722024-07-02 18:18:595 hrs ago1719944339IN
0x478f6F99...dB8cCaE16
0 ETH0.000659415.30201016
Repay Loan202205502024-07-02 18:14:355 hrs ago1719944075IN
0x478f6F99...dB8cCaE16
0 ETH0.000717375.85466343
Repay Loan202205472024-07-02 18:13:595 hrs ago1719944039IN
0x478f6F99...dB8cCaE16
0 ETH0.000578965.76368457
Liquidate Loan202204522024-07-02 17:54:596 hrs ago1719942899IN
0x478f6F99...dB8cCaE16
0 ETH0.000308493.0472595
Liquidate Loan202191602024-07-02 13:34:2310 hrs ago1719927263IN
0x478f6F99...dB8cCaE16
0 ETH0.000696256.59056274
Refinance Partia...202190662024-07-02 13:15:2310 hrs ago1719926123IN
0x478f6F99...dB8cCaE16
0 ETH0.000816816.40802364
Refinance Full202156222024-07-02 1:42:2322 hrs ago1719884543IN
0x478f6F99...dB8cCaE16
0 ETH0.000349312.77385954
Refinance Full202156012024-07-02 1:38:1122 hrs ago1719884291IN
0x478f6F99...dB8cCaE16
0 ETH0.000352462.81803491
Repay Loan202155602024-07-02 1:29:5922 hrs ago1719883799IN
0x478f6F99...dB8cCaE16
0 ETH0.000202161.85945991
Refinance Full202152492024-07-02 0:27:3523 hrs ago1719880055IN
0x478f6F99...dB8cCaE16
0 ETH0.000272312.12882889
Emit Loan202152442024-07-02 0:26:3523 hrs ago1719879995IN
0x478f6F99...dB8cCaE16
0 ETH0.000487162.22222116
Refinance Full202150042024-07-01 23:38:2324 hrs ago1719877103IN
0x478f6F99...dB8cCaE16
0 ETH0.000341792.86082062
Repay Loan202149382024-07-01 23:25:1124 hrs ago1719876311IN
0x478f6F99...dB8cCaE16
0 ETH0.000355432.85535587
Refinance Full202147232024-07-01 22:41:3525 hrs ago1719873695IN
0x478f6F99...dB8cCaE16
0 ETH0.000527634.2457415
Refinance Full202146872024-07-01 22:34:2325 hrs ago1719873263IN
0x478f6F99...dB8cCaE16
0 ETH0.000590894.80085543
Refinance Full202136302024-07-01 19:02:3528 hrs ago1719860555IN
0x478f6F99...dB8cCaE16
0 ETH0.0013473110.83719172
Emit Loan202132102024-07-01 17:38:1130 hrs ago1719855491IN
0x478f6F99...dB8cCaE16
0 ETH0.002197858.75353224
Emit Loan202129932024-07-01 16:54:3531 hrs ago1719852875IN
0x478f6F99...dB8cCaE16
0 ETH0.00186048.53846085
Liquidate Loan202129932024-07-01 16:54:3531 hrs ago1719852875IN
0x478f6F99...dB8cCaE16
0 ETH0.000689638.49655156
Repay Loan202129282024-07-01 16:41:3531 hrs ago1719852095IN
0x478f6F99...dB8cCaE16
0 ETH0.001012178.13116038
Emit Loan202128322024-07-01 16:22:2331 hrs ago1719850943IN
0x478f6F99...dB8cCaE16
0 ETH0.002364510.00589026
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MultiSourceLoan

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 2000 runs

Other Settings:
paris EvmVersion, GNU AGPLv3 license
File 1 of 29 : MultiSourceLoan.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;

import "@delegate/IDelegateRegistry.sol";
import "@solmate/tokens/ERC20.sol";
import "@solmate/tokens/ERC721.sol";
import "@solmate/utils/FixedPointMathLib.sol";
import "@solmate/utils/ReentrancyGuard.sol";
import "@solmate/utils/SafeTransferLib.sol";

import "../../interfaces/INFTFlashAction.sol";
import "../../interfaces/loans/IMultiSourceLoan.sol";
import "../../interfaces/ILoanLiquidator.sol";
import "../utils/Hash.sol";
import "../utils/Interest.sol";
import "../Multicall.sol";
import "./WithCallbacks.sol";

contract MultiSourceLoan is IMultiSourceLoan, Multicall, ReentrancyGuard, WithCallbacks {
    using FixedPointMathLib for uint256;
    using Hash for Loan;
    using Hash for SignableRepaymentData;
    using Hash for RenegotiationOffer;
    using Interest for uint256;
    using MessageHashUtils for bytes32;
    using SafeTransferLib for ERC20;

    /// @notice Loan Id to hash
    mapping(uint256 => bytes32) private _loans;

    /// @notice Maximum number of sources per loan
    uint256 private _maxSources;

    /// @notice Min lock period for a source
    uint256 private _minLockPeriod;

    /// @notice If we have N max sources, then the min principal of any given source
    /// at the time of repayment needs to be Total Principal / (N * _MAX_RATIO_SOURCE_MIN_PRINCIPAL)
    /// This is captured in _getMinSourcePrincipal.
    uint256 private constant _MAX_RATIO_SOURCE_MIN_PRINCIPAL = 2;

    /// @notice delegate registry
    IDelegateRegistry private _delegateRegistry;

    /// @notice Contract to execute flash actions.
    INFTFlashAction private _flashActionContract;

    event MaxSourcesUpdated(uint256 newMax);

    event LoanEmitted(uint256 loanId, uint256 offerId, Loan loan, address lender, address borrower, uint256 fee);

    event LoanRefinanced(uint256 renegotiationId, uint256 oldLoanId, uint256 newLoanId, Loan loan, uint256 fee);

    event LoanRepaid(uint256 loanId, uint256 totalRepayment, uint256 fee);

    event DelegateRegistryUpdated(address newdelegateRegistry);

    event Delegated(uint256 loanId, address delegate, bool value);

    event FlashActionContractUpdated(address newFlashActionContract);

    event FlashActionExecuted(uint256 loanId, address target, bytes data);

    event RevokeDelegate(address delegate, address collection, uint256 tokenId);

    event LoanExtended(uint256 oldLoanId, uint256 newLoanId, Loan loan, uint256 _extension);

    event MinLockPeriodUpdated(uint256 minLockPeriod);

    error InvalidMethodError();

    error InvalidRenegotiationOfferError();

    error TooManySourcesError(uint256 sources);

    error MinLockPeriodTooHighError(uint256 minLockPeriod);

    error PartialOfferCannotChangeDurationError();

    error PartialOfferCannotHaveFeeError();

    error LoanExpiredError();

    error RefinanceFullError();

    error LengthMismatchError();

    error TargetPrincipalTooLowError(uint256 sourcePrincipal, uint256 loanPrincipal);

    error NFTNotReturnedError();

    error ExtensionNotAvailableError();

    error SourceCannotBeRefinancedError(uint256 minTimestamp);

    /// @param loanLiquidator Address of the liquidator contract.
    /// @param protocolFee Protocol fee charged on gains.
    /// @param currencyManager Address of the currency manager.
    /// @param collectionManager Address of the collection manager.
    /// @param maxSources Maximum number of sources per loan.
    /// @param delegateRegistry Address of the delegate registry (Delegate.xyz).
    /// @param flashActionContract Address of the flash action contract.
    constructor(
        address loanLiquidator,
        ProtocolFee memory protocolFee,
        address currencyManager,
        address collectionManager,
        uint256 maxSources,
        uint256 minLockPeriod,
        address delegateRegistry,
        address flashActionContract
    ) WithCallbacks("GONDI_MULTI_SOURCE_LOAN", currencyManager, collectionManager) {
        _checkAddressNotZero(loanLiquidator);

        _loanLiquidator = ILoanLiquidator(loanLiquidator);
        _protocolFee = protocolFee;
        _maxSources = maxSources;
        _minLockPeriod = minLockPeriod;
        _delegateRegistry = IDelegateRegistry(delegateRegistry);
        _flashActionContract = INFTFlashAction(flashActionContract);
    }

    /// @inheritdoc IMultiSourceLoan
    function emitLoan(LoanExecutionData calldata _executionData) external nonReentrant returns (uint256, Loan memory) {
        address lender = _executionData.lender;
        address borrower = _executionData.borrower;
        LoanOffer calldata offer = _executionData.executionData.offer;
        address offerer = offer.lender == address(0) ? borrower : lender;
        _validateExecutionData(
            _executionData.executionData,
            lender,
            borrower,
            offerer,
            _executionData.lenderOfferSignature,
            _executionData.borrowerOfferSignature
        );

        uint256 loanId = _getAndSetNewLoanId();
        uint256 amount = _executionData.executionData.amount;

        Source[] memory source = new Source[](1);
        source[0] = Source(loanId, lender, amount, 0, block.timestamp, offer.aprBps);
        Loan memory loan = Loan(
            borrower,
            _executionData.executionData.tokenId,
            offer.nftCollateralAddress,
            offer.principalAddress,
            amount,
            block.timestamp,
            offer.duration,
            source
        );

        _loans[loanId] = loan.hash();
        uint256 fee = offer.fee.mulDivUp(amount, offer.principalAmount);
        ProtocolFee memory protocolFee = _protocolFee;
        _handleProtocolFeeForFee(
            offer.principalAddress, lender, fee.mulDivUp(protocolFee.fraction, _PRECISION), protocolFee
        );

        ERC20(offer.principalAddress).safeTransferFrom(lender, borrower, amount - fee);

        /// @dev After sending the principal to the borrower, check if there's an action to be taken (eg: use it to buy the collateral).
        uint128 tax = _handleAfterPrincipalTransferCallback(loan, _executionData.executionData.callbackData, fee);
        if (tax > 0) {
            uint256 taxCost = amount.mulDivUp(tax, _PRECISION);
            uint256 feeTax = taxCost.mulDivUp(protocolFee.fraction, _PRECISION);

            ERC20(offer.principalAddress).safeTransferFrom(borrower, lender, taxCost - feeTax);
            if (feeTax > 0) {
                ERC20(offer.principalAddress).safeTransferFrom(borrower, protocolFee.recipient, feeTax);
            }
        }

        ERC721(offer.nftCollateralAddress).transferFrom(borrower, address(this), _executionData.executionData.tokenId);

        emit LoanEmitted(loanId, offer.offerId, loan, lender, borrower, offer.fee);

        if (offer.capacity > 0) {
            _used[offerer][offer.offerId] += amount;
        } else {
            isOfferCancelled[offerer][offer.offerId] = true;
        }

        return (loanId, loan);
    }

    /// @inheritdoc IMultiSourceLoan
    function refinanceFull(
        RenegotiationOffer calldata _renegotiationOffer,
        Loan memory _loan,
        bytes calldata _renegotiationOfferSignature
    ) external returns (uint256, Loan memory) {
        uint256 loanId = _renegotiationOffer.loanId;
        address sender = msg.sender;
        bool clearsInterest = false;

        _baseLoanChecks(loanId, _loan);

        _baseRenegotiationChecks(_renegotiationOffer, _loan);

        bool strictImprovement = msg.sender == _renegotiationOffer.lender;

        (uint256 totalDelta, uint256 totalAccruedInterest, uint256 totalNewSources, uint256 totalAnnualInterest) =
            _processOldSources(_renegotiationOffer, _loan, strictImprovement);
        if (totalNewSources > 1) {
            revert RefinanceFullError();
        }
        /// @dev If it's lender initiated, needs to be strictly better.
        if (strictImprovement) {
            _checkStrictlyBetter(
                _renegotiationOffer.principalAmount,
                totalDelta,
                _renegotiationOffer.duration + block.timestamp,
                _loan.duration + _loan.startTime,
                _renegotiationOffer.aprBps,
                totalAnnualInterest / _loan.principalAmount,
                _renegotiationOffer.fee
            );
        } else if (sender != _loan.borrower) {
            revert OnlyLenderOrBorrowerCallableError();
        } else {
            clearsInterest = true;
            _checkSignature(_renegotiationOffer.lender, _renegotiationOffer.hash(), _renegotiationOfferSignature);
        }

        uint256 netNewLender = _renegotiationOffer.principalAmount - _renegotiationOffer.fee;

        if (clearsInterest) {
            netNewLender -= totalAccruedInterest;
            totalAccruedInterest = 0;
        }

        if (totalDelta > netNewLender) {
            ERC20(_loan.principalAddress).safeTransferFrom(
                _loan.borrower, _renegotiationOffer.lender, totalDelta - netNewLender
            );
        } else if (totalDelta < netNewLender) {
            ERC20(_loan.principalAddress).safeTransferFrom(
                _renegotiationOffer.lender, _loan.borrower, netNewLender - totalDelta
            );
        }

        uint256 newLoanId = _getAndSetNewLoanId();
        Source[] memory newSources = new Source[](1);

        newSources[0] = _getSourceFromOffer(_renegotiationOffer, totalAccruedInterest, newLoanId);
        _loan.source = newSources;
        _loan.duration = (block.timestamp - _loan.startTime) + _renegotiationOffer.duration;
        _loan.principalAmount = _renegotiationOffer.principalAmount;

        _loans[newLoanId] = _loan.hash();
        delete _loans[loanId];

        emit LoanRefinanced(_renegotiationOffer.renegotiationId, loanId, newLoanId, _loan, _renegotiationOffer.fee);

        return (newLoanId, _loan);
    }

    /// @inheritdoc IMultiSourceLoan
    function refinancePartial(RenegotiationOffer calldata _renegotiationOffer, Loan memory _loan)
        external
        returns (uint256, Loan memory)
    {
        uint256 loanId = _renegotiationOffer.loanId;
        if (_renegotiationOffer.principalAmount < _getMinSourcePrincipal(_loan.principalAmount)) {
            revert TargetPrincipalTooLowError(_renegotiationOffer.principalAmount, _loan.principalAmount);
        }
        if (msg.sender != _renegotiationOffer.lender) {
            revert OnlyLenderCallableError();
        }

        _baseLoanChecks(loanId, _loan);

        _baseRenegotiationChecks(_renegotiationOffer, _loan);

        if (_renegotiationOffer.duration > 0) {
            revert PartialOfferCannotChangeDurationError();
        }
        if (_renegotiationOffer.fee > 0) {
            revert PartialOfferCannotHaveFeeError();
        }

        (uint256 totalDelta, uint256 totalAccruedInterest, uint256 totalNewSources,) =
            _processOldSources(_renegotiationOffer, _loan, true);

        if (totalDelta != _renegotiationOffer.principalAmount) {
            revert InvalidRenegotiationOfferError();
        }
        if (totalNewSources > _maxSources) {
            revert TooManySourcesError(totalNewSources);
        }

        uint256 newLoanId = _getAndSetNewLoanId();
        Source[] memory newSources = new Source[](totalNewSources);
        newSources[0] = _getSourceFromOffer(_renegotiationOffer, totalAccruedInterest, newLoanId);
        /// @dev Index = 0 is taken by the new source
        uint256 j = 1;
        for (uint256 i = 0; i < _renegotiationOffer.targetPrincipal.length;) {
            if (_renegotiationOffer.targetPrincipal[i] > 0) {
                newSources[j] = _loan.source[i];
                newSources[j].principalAmount = _renegotiationOffer.targetPrincipal[i];
                unchecked {
                    ++j;
                }
            }

            unchecked {
                ++i;
            }
        }

        _loan.source = newSources;

        _loans[newLoanId] = _loan.hash();
        delete _loans[loanId];

        /// @dev Here fee is always 0
        emit LoanRefinanced(_renegotiationOffer.renegotiationId, loanId, newLoanId, _loan, 0);

        return (newLoanId, _loan);
    }

    /// @inheritdoc IMultiSourceLoan
    function extendLoan(uint256 _loanId, Loan memory _loan, uint256 _extension)
        external
        returns (uint256, Loan memory)
    {
        _baseLoanChecks(_loanId, _loan);

        if (_loan.source.length > 1) {
            revert ExtensionNotAvailableError();
        }
        uint256 unlockedTime = _getUnlockedTime(_loan.source[0].startTime, _loan.startTime + _loan.duration);
        if (unlockedTime > block.timestamp) {
            revert SourceCannotBeRefinancedError(unlockedTime);
        }
        if (_loan.source[0].lender != msg.sender) {
            revert OnlyLenderCallableError();
        }
        _loan.duration += _extension;
        uint256 newLoanId = _getAndSetNewLoanId();
        _loans[newLoanId] = _loan.hash();

        delete _loans[_loanId];

        emit LoanExtended(_loanId, newLoanId, _loan, _extension);

        return (newLoanId, _loan);
    }

    /// @inheritdoc IMultiSourceLoan
    function repayLoan(LoanRepaymentData calldata _repaymentData) external override nonReentrant {
        uint256 loanId = _repaymentData.data.loanId;
        Loan calldata loan = _repaymentData.loan;
        /// @dev If the caller is not the borrower itself, check the signature to avoid someone else forcing an unwanted repayment.
        if (msg.sender != loan.borrower) {
            _checkSignature(loan.borrower, _repaymentData.data.hash(), _repaymentData.borrowerSignature);
        }

        _baseLoanChecks(loanId, loan);

        /// @dev Unlikely this is used outside of the callback with a seaport sell, but leaving here in case that's not correct.
        if (_repaymentData.data.shouldDelegate) {
            _delegateRegistry.delegateERC721(
                loan.borrower, loan.nftCollateralAddress, loan.nftCollateralTokenId, bytes32(""), true
            );
        }

        ERC721(loan.nftCollateralAddress).transferFrom(address(this), loan.borrower, loan.nftCollateralTokenId);

        /// @dev After returning the NFT to the borrower, check if there's an action to be taken (eg: sell it to cover repayment).
        uint128 taxBps = _handleAfterNFTTransferCallback(loan, _repaymentData.data.callbackData);

        /// @dev Bring to memory
        ProtocolFee memory protocolFee = _protocolFee;
        bool withProtocolFee = protocolFee.fraction > 0;
        uint256 totalProtocolFee = 0;

        ERC20 asset = ERC20(loan.principalAddress);
        uint256 totalRepayment = 0;
        for (uint256 i = 0; i < loan.source.length;) {
            Source memory source = loan.source[i];

            uint256 newInterest = source.principalAmount.getInterest(source.aprBps, block.timestamp - source.startTime);
            uint256 tax = source.principalAmount.mulDivUp(taxBps, _PRECISION);

            uint256 thisProtocolFee = 0;
            uint256 thisTaxFee = 0;
            if (withProtocolFee) {
                thisProtocolFee = newInterest.mulDivUp(protocolFee.fraction, _PRECISION);
                thisTaxFee = tax.mulDivUp(protocolFee.fraction, _PRECISION);
                totalProtocolFee += thisProtocolFee + thisTaxFee;
            }

            uint256 repayment =
                source.principalAmount + source.accruedInterest + newInterest - thisProtocolFee + tax - thisTaxFee;
            asset.safeTransferFrom(loan.borrower, source.lender, repayment);

            totalRepayment += repayment;

            unchecked {
                ++i;
            }
        }

        emit LoanRepaid(loanId, totalRepayment, totalProtocolFee);

        if (withProtocolFee) {
            asset.safeTransferFrom(loan.borrower, protocolFee.recipient, totalProtocolFee);
        }

        /// @dev Reclaim space.
        delete _loans[loanId];
    }

    /// @inheritdoc IMultiSourceLoan
    function liquidateLoan(uint256 _loanId, Loan calldata _loan)
        external
        override
        nonReentrant
        returns (bytes memory)
    {
        if (_loan.hash() != _loans[_loanId]) {
            revert InvalidLoanError(_loanId);
        }
        uint256 expirationTime = _loan.startTime + _loan.duration;
        address collateralAddress = _loan.nftCollateralAddress;
        ERC721 collateralCollection = ERC721(collateralAddress);

        if (expirationTime > block.timestamp) {
            revert LoanNotDueError(expirationTime);
        }
        bytes memory liquidation;
        if (_loan.source.length == 1) {
            collateralCollection.transferFrom(address(this), _loan.source[0].lender, _loan.nftCollateralTokenId);

            emit LoanForeclosed(_loanId);

            /// @dev Reclaim space.
            delete _loans[_loanId];
        } else {
            collateralCollection.transferFrom(address(this), address(_loanLiquidator), _loan.nftCollateralTokenId);
            liquidation = _loanLiquidator.liquidateLoan(
                _loanId,
                collateralAddress,
                _loan.nftCollateralTokenId,
                _loan.principalAddress,
                _liquidationAuctionDuration,
                msg.sender
            );

            emit LoanSentToLiquidator(_loanId, address(_loanLiquidator));
        }
        return liquidation;
    }

    /// @inheritdoc IMultiSourceLoan
    function loanLiquidated(uint256 _loanId, Loan calldata _loan) external override onlyLiquidator {
        if (_loan.hash() != _loans[_loanId]) {
            revert InvalidLoanError(_loanId);
        }

        emit LoanLiquidated(_loanId);

        /// @dev Reclaim space.
        delete _loans[_loanId];
    }

    function getMinSourcePrincipal(uint256 _loanPrincipal) external view returns (uint256) {
        return _getMinSourcePrincipal(_loanPrincipal);
    }

    /// @inheritdoc IMultiSourceLoan
    function delegate(uint256 _loanId, Loan calldata loan, address _delegate, bytes32 _rights, bool _value) external {
        if (loan.hash() != _loans[_loanId]) {
            revert InvalidLoanError(_loanId);
        }
        if (msg.sender != loan.borrower) {
            revert OnlyBorrowerCallableError();
        }
        _delegateRegistry.delegateERC721(
            _delegate, loan.nftCollateralAddress, loan.nftCollateralTokenId, _rights, _value
        );

        emit Delegated(_loanId, _delegate, _value);
    }

    /// @inheritdoc IMultiSourceLoan
    function revokeDelegate(address _delegate, address _collection, uint256 _tokenId) external {
        if (ERC721(_collection).ownerOf(_tokenId) == address(this)) {
            revert InvalidMethodError();
        }

        _delegateRegistry.delegateERC721(_delegate, _collection, _tokenId, "", false);

        emit RevokeDelegate(_delegate, _collection, _tokenId);
    }

    /// @inheritdoc IMultiSourceLoan
    function getDelegateRegistry() external view returns (address) {
        return address(_delegateRegistry);
    }

    /// @inheritdoc IMultiSourceLoan
    function setDelegateRegistry(address _newDelegateRegistry) external onlyOwner {
        _delegateRegistry = IDelegateRegistry(_newDelegateRegistry);

        emit DelegateRegistryUpdated(_newDelegateRegistry);
    }

    /// @inheritdoc IMultiSourceLoan
    function getMaxSources() external view returns (uint256) {
        return _maxSources;
    }

    /// @inheritdoc IMultiSourceLoan
    function setMaxSources(uint256 __maxSources) external onlyOwner {
        _maxSources = __maxSources;

        emit MaxSourcesUpdated(__maxSources);
    }

    /// @inheritdoc IMultiSourceLoan
    function getMinLockPeriod() external view returns (uint256) {
        return _minLockPeriod;
    }

    /// @inheritdoc IMultiSourceLoan
    function setMinLockPeriod(uint256 __minLockPeriod) external onlyOwner {
        _minLockPeriod = __minLockPeriod;

        emit MinLockPeriodUpdated(__minLockPeriod);
    }

    /// @inheritdoc IMultiSourceLoan
    function getLoanHash(uint256 _loanId) external view returns (bytes32) {
        return _loans[_loanId];
    }

    /// @inheritdoc IMultiSourceLoan
    function executeFlashAction(uint256 _loanId, Loan calldata _loan, address _target, bytes calldata _data) external {
        if (_loan.hash() != _loans[_loanId]) {
            revert InvalidLoanError(_loanId);
        }
        if (msg.sender != _loan.borrower) {
            revert OnlyBorrowerCallableError();
        }

        ERC721(_loan.nftCollateralAddress).transferFrom(
            address(this), address(_flashActionContract), _loan.nftCollateralTokenId
        );
        _flashActionContract.execute(_loan.nftCollateralAddress, _loan.nftCollateralTokenId, _target, _data);

        if (ERC721(_loan.nftCollateralAddress).ownerOf(_loan.nftCollateralTokenId) != address(this)) {
            revert NFTNotReturnedError();
        }

        emit FlashActionExecuted(_loanId, _target, _data);
    }

    /// @inheritdoc IMultiSourceLoan
    function getFlashActionContract() external view returns (address) {
        return address(_flashActionContract);
    }

    /// @inheritdoc IMultiSourceLoan
    function setFlashActionContract(address _newFlashActionContract) external onlyOwner {
        _flashActionContract = INFTFlashAction(_newFlashActionContract);

        emit FlashActionContractUpdated(_newFlashActionContract);
    }

    /// @notice Update old sources and return the total delta, accrued interest, new sources and
    /// transfer the protocol fee.
    /// @param _renegotiationOffer The refinance offer.
    /// @param _loan The loan to be refinanced.
    /// @param _isStrictlyBetter Every source's apr needs to be improved.
    /// @return totalDelta The total delta is the sum of all deltas across existing sources. This must be equal
    /// to the new supplied (the total principal cannot change).
    /// @return totalAccruedInterest Total accrued interest across all sources paid.
    /// @return totalNewSources Total new sources, including new lender, left after the refinance.
    /// @return totalAnnualInterest Total annual interest across all sources.
    function _processOldSources(
        RenegotiationOffer calldata _renegotiationOffer,
        Loan memory _loan,
        bool _isStrictlyBetter
    )
        private
        returns (uint256 totalDelta, uint256 totalAccruedInterest, uint256 totalNewSources, uint256 totalAnnualInterest)
    {
        /// @dev Bring var to memory
        ProtocolFee memory protocolFee = _protocolFee;

        uint256 totalProtocolFee = 0;
        if (protocolFee.fraction > 0 && _renegotiationOffer.fee > 0) {
            totalProtocolFee = _renegotiationOffer.fee.mulDivUp(protocolFee.fraction, _PRECISION);
        }

        totalNewSources = 1;
        for (uint256 i = 0; i < _renegotiationOffer.targetPrincipal.length;) {
            Source memory source = _loan.source[i];
            uint256 targetPrincipal = _renegotiationOffer.targetPrincipal[i];
            (
                uint256 delta,
                uint256 accruedInterest,
                uint256 isNewSource,
                uint256 annualInterest,
                uint256 thisProtocolFee
            ) = _processOldSource(
                _renegotiationOffer.lender,
                _loan.principalAddress,
                source,
                _loan.startTime + _loan.duration,
                targetPrincipal,
                protocolFee
            );

            _checkSourceStrictly(_isStrictlyBetter, delta, source.aprBps, _renegotiationOffer.aprBps, _minimum.interest);

            totalAnnualInterest += annualInterest;
            totalDelta += delta;
            totalAccruedInterest += accruedInterest;
            totalProtocolFee += thisProtocolFee;
            totalNewSources += isNewSource;

            unchecked {
                ++i;
            }
        }

        _handleProtocolFeeForFee(_loan.principalAddress, _renegotiationOffer.lender, totalProtocolFee, protocolFee);
    }

    /// @notice Process the current source during a renegotiation.
    /// @param _lender The new lender.
    /// @param _principalAddress The principal address of the loan.
    /// @param _source The source to be processed.
    /// @param _endTime The end time of the loan.
    /// @param _targetPrincipal The target principal of the source.
    /// @param protocolFee The protocol fee.
    /// @return delta The delta between the old and new principal.
    /// @return accruedInterest The accrued interest paid.
    /// @return isNewSource Whether the source is kept.
    /// @return annualInterest The total annual interest paid (times 10000 since we have it in BPS)
    /// @return thisProtocolFee The protocol fee paid for this source.
    function _processOldSource(
        address _lender,
        address _principalAddress,
        Source memory _source,
        uint256 _endTime,
        uint256 _targetPrincipal,
        ProtocolFee memory protocolFee
    )
        private
        returns (
            uint256 delta,
            uint256 accruedInterest,
            uint256 isNewSource,
            uint256 annualInterest,
            uint256 thisProtocolFee
        )
    {
        uint256 unlockedTime = _getUnlockedTime(_source.startTime, _endTime);
        if (unlockedTime > block.timestamp) {
            revert SourceCannotBeRefinancedError(unlockedTime);
        }
        delta = _source.principalAmount - _targetPrincipal;
        annualInterest = _source.principalAmount * _source.aprBps;
        if (delta == 0) {
            return (0, 0, 1, annualInterest, 0);
        }
        accruedInterest = delta.getInterest(_source.aprBps, block.timestamp - _source.startTime);

        if (protocolFee.fraction > 0) {
            thisProtocolFee = accruedInterest.mulDivUp(protocolFee.fraction, _PRECISION);
        }

        uint256 proportionalAccrued = _source.accruedInterest.mulDivDown(delta, _source.principalAmount);
        if (_targetPrincipal > 0) {
            _source.accruedInterest -= proportionalAccrued;
            isNewSource = 1;
        }

        accruedInterest += proportionalAccrued;

        ERC20(_principalAddress).safeTransferFrom(_lender, _source.lender, delta + accruedInterest - thisProtocolFee);
    }

    function _baseLoanChecks(uint256 _loanId, Loan memory _loan) private view {
        if (_loan.hash() != _loans[_loanId]) {
            revert InvalidLoanError(_loanId);
        }
        if (_loan.startTime + _loan.duration < block.timestamp) {
            revert LoanExpiredError();
        }
    }

    function _baseRenegotiationChecks(RenegotiationOffer calldata _renegotiationOffer, Loan memory _loan)
        private
        view
    {
        if (
            (_renegotiationOffer.principalAmount == 0)
                || (_loan.source.length != _renegotiationOffer.targetPrincipal.length)
        ) {
            revert InvalidRenegotiationOfferError();
        }
        if (block.timestamp > _renegotiationOffer.expirationTime) {
            revert ExpiredRenegotiationOfferError(_renegotiationOffer.expirationTime);
        }
        uint256 renegotiationId = _renegotiationOffer.renegotiationId;
        address lender = _renegotiationOffer.lender;
        if (
            isRenegotiationOfferCancelled[lender][renegotiationId]
                || lenderMinRenegotiationOfferId[lender] >= renegotiationId
        ) {
            revert CancelledRenegotiationOfferError(lender, renegotiationId);
        }
    }

    function _getSourceFromOffer(
        RenegotiationOffer memory _renegotiationOffer,
        uint256 _accruedInterest,
        uint256 _loanId
    ) private view returns (Source memory) {
        return Source({
            loanId: _loanId,
            lender: _renegotiationOffer.lender,
            principalAmount: _renegotiationOffer.principalAmount,
            accruedInterest: _accruedInterest,
            startTime: block.timestamp,
            aprBps: _renegotiationOffer.aprBps
        });
    }

    function _getMinSourcePrincipal(uint256 _loanPrincipal) private view returns (uint256) {
        return _loanPrincipal / (_MAX_RATIO_SOURCE_MIN_PRINCIPAL * _maxSources);
    }

    /// @notice Protocol fee for fees charged on offers/renegotationOffers.
    /// @param _principalAddress The principal address of the loan.
    /// @param _lender The lender of the loan.
    /// @param _fee The fee to be charged.
    /// @param protocolFee The protocol fee variable brought to memory.
    function _handleProtocolFeeForFee(
        address _principalAddress,
        address _lender,
        uint256 _fee,
        ProtocolFee memory protocolFee
    ) private {
        if (protocolFee.fraction > 0 && _fee > 0) {
            ERC20(_principalAddress).safeTransferFrom(_lender, protocolFee.recipient, _fee);
        }
    }

    /// @notice Check condition for strictly better sources
    /// @param _isStrictlyBetter Whether the new source needs to be strictly better than the old one.
    /// @param _delta The delta between the old and new principal. 0 if unchanged.
    /// @param _currentAprBps The current apr of the source.
    /// @param _targetAprBps The target apr of the source.
    /// @param _minImprovement The minimum improvement required.
    function _checkSourceStrictly(
        bool _isStrictlyBetter,
        uint256 _delta,
        uint256 _currentAprBps,
        uint256 _targetAprBps,
        uint256 _minImprovement
    ) private pure {
        /// @dev If _isStrictlyBetter is set, and the new apr is higher, then it'll underflow.
        if (
            _isStrictlyBetter && _delta > 0
                && ((_currentAprBps - _targetAprBps).mulDivDown(_PRECISION, _currentAprBps) < _minImprovement)
        ) {
            revert InvalidRenegotiationOfferError();
        }
    }

    function _getUnlockedTime(uint256 _sourceStartTime, uint256 _loanEndTime) private view returns (uint256) {
        return _sourceStartTime + (_loanEndTime - _sourceStartTime).mulDivUp(_minLockPeriod, _PRECISION);
    }
}

File 2 of 29 : IDelegateRegistry.sol
// SPDX-License-Identifier: CC0-1.0
pragma solidity >=0.8.13;

/**
 * @title IDelegateRegistry
 * @custom:version 2.0
 * @custom:author foobar (0xfoobar)
 * @notice A standalone immutable registry storing delegated permissions from one address to another
 */
interface IDelegateRegistry {
    /// @notice Delegation type, NONE is used when a delegation does not exist or is revoked
    enum DelegationType {
        NONE,
        ALL,
        CONTRACT,
        ERC721,
        ERC20,
        ERC1155
    }

    /// @notice Struct for returning delegations
    struct Delegation {
        DelegationType type_;
        address to;
        address from;
        bytes32 rights;
        address contract_;
        uint256 tokenId;
        uint256 amount;
    }

    /// @notice Emitted when an address delegates or revokes rights for their entire wallet
    event DelegateAll(address indexed from, address indexed to, bytes32 rights, bool enable);

    /// @notice Emitted when an address delegates or revokes rights for a contract address
    event DelegateContract(address indexed from, address indexed to, address indexed contract_, bytes32 rights, bool enable);

    /// @notice Emitted when an address delegates or revokes rights for an ERC721 tokenId
    event DelegateERC721(address indexed from, address indexed to, address indexed contract_, uint256 tokenId, bytes32 rights, bool enable);

    /// @notice Emitted when an address delegates or revokes rights for an amount of ERC20 tokens
    event DelegateERC20(address indexed from, address indexed to, address indexed contract_, bytes32 rights, uint256 amount);

    /// @notice Emitted when an address delegates or revokes rights for an amount of an ERC1155 tokenId
    event DelegateERC1155(address indexed from, address indexed to, address indexed contract_, uint256 tokenId, bytes32 rights, uint256 amount);

    /// @notice Thrown if multicall calldata is malformed
    error MulticallFailed();

    /**
     * -----------  WRITE -----------
     */

    /**
     * @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
     * @param data The encoded function data for each of the calls to make to this contract
     * @return results The results from each of the calls passed in via data
     */
    function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);

    /**
     * @notice Allow the delegate to act on behalf of `msg.sender` for all contracts
     * @param to The address to act as delegate
     * @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
     * @param enable Whether to enable or disable this delegation, true delegates and false revokes
     * @return delegationHash The unique identifier of the delegation
     */
    function delegateAll(address to, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);

    /**
     * @notice Allow the delegate to act on behalf of `msg.sender` for a specific contract
     * @param to The address to act as delegate
     * @param contract_ The contract whose rights are being delegated
     * @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
     * @param enable Whether to enable or disable this delegation, true delegates and false revokes
     * @return delegationHash The unique identifier of the delegation
     */
    function delegateContract(address to, address contract_, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);

    /**
     * @notice Allow the delegate to act on behalf of `msg.sender` for a specific ERC721 token
     * @param to The address to act as delegate
     * @param contract_ The contract whose rights are being delegated
     * @param tokenId The token id to delegate
     * @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
     * @param enable Whether to enable or disable this delegation, true delegates and false revokes
     * @return delegationHash The unique identifier of the delegation
     */
    function delegateERC721(address to, address contract_, uint256 tokenId, bytes32 rights, bool enable) external payable returns (bytes32 delegationHash);

    /**
     * @notice Allow the delegate to act on behalf of `msg.sender` for a specific amount of ERC20 tokens
     * @dev The actual amount is not encoded in the hash, just the existence of a amount (since it is an upper bound)
     * @param to The address to act as delegate
     * @param contract_ The address for the fungible token contract
     * @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
     * @param amount The amount to delegate, > 0 delegates and 0 revokes
     * @return delegationHash The unique identifier of the delegation
     */
    function delegateERC20(address to, address contract_, bytes32 rights, uint256 amount) external payable returns (bytes32 delegationHash);

    /**
     * @notice Allow the delegate to act on behalf of `msg.sender` for a specific amount of ERC1155 tokens
     * @dev The actual amount is not encoded in the hash, just the existence of a amount (since it is an upper bound)
     * @param to The address to act as delegate
     * @param contract_ The address of the contract that holds the token
     * @param tokenId The token id to delegate
     * @param rights Specific subdelegation rights granted to the delegate, pass an empty bytestring to encompass all rights
     * @param amount The amount of that token id to delegate, > 0 delegates and 0 revokes
     * @return delegationHash The unique identifier of the delegation
     */
    function delegateERC1155(address to, address contract_, uint256 tokenId, bytes32 rights, uint256 amount) external payable returns (bytes32 delegationHash);

    /**
     * ----------- CHECKS -----------
     */

    /**
     * @notice Check if `to` is a delegate of `from` for the entire wallet
     * @param to The potential delegate address
     * @param from The potential address who delegated rights
     * @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
     * @return valid Whether delegate is granted to act on the from's behalf
     */
    function checkDelegateForAll(address to, address from, bytes32 rights) external view returns (bool);

    /**
     * @notice Check if `to` is a delegate of `from` for the specified `contract_` or the entire wallet
     * @param to The delegated address to check
     * @param contract_ The specific contract address being checked
     * @param from The cold wallet who issued the delegation
     * @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
     * @return valid Whether delegate is granted to act on from's behalf for entire wallet or that specific contract
     */
    function checkDelegateForContract(address to, address from, address contract_, bytes32 rights) external view returns (bool);

    /**
     * @notice Check if `to` is a delegate of `from` for the specific `contract` and `tokenId`, the entire `contract_`, or the entire wallet
     * @param to The delegated address to check
     * @param contract_ The specific contract address being checked
     * @param tokenId The token id for the token to delegating
     * @param from The wallet that issued the delegation
     * @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
     * @return valid Whether delegate is granted to act on from's behalf for entire wallet, that contract, or that specific tokenId
     */
    function checkDelegateForERC721(address to, address from, address contract_, uint256 tokenId, bytes32 rights) external view returns (bool);

    /**
     * @notice Returns the amount of ERC20 tokens the delegate is granted rights to act on the behalf of
     * @param to The delegated address to check
     * @param contract_ The address of the token contract
     * @param from The cold wallet who issued the delegation
     * @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
     * @return balance The delegated balance, which will be 0 if the delegation does not exist
     */
    function checkDelegateForERC20(address to, address from, address contract_, bytes32 rights) external view returns (uint256);

    /**
     * @notice Returns the amount of a ERC1155 tokens the delegate is granted rights to act on the behalf of
     * @param to The delegated address to check
     * @param contract_ The address of the token contract
     * @param tokenId The token id to check the delegated amount of
     * @param from The cold wallet who issued the delegation
     * @param rights Specific rights to check for, pass the zero value to ignore subdelegations and check full delegations only
     * @return balance The delegated balance, which will be 0 if the delegation does not exist
     */
    function checkDelegateForERC1155(address to, address from, address contract_, uint256 tokenId, bytes32 rights) external view returns (uint256);

    /**
     * ----------- ENUMERATIONS -----------
     */

    /**
     * @notice Returns all enabled delegations a given delegate has received
     * @param to The address to retrieve delegations for
     * @return delegations Array of Delegation structs
     */
    function getIncomingDelegations(address to) external view returns (Delegation[] memory delegations);

    /**
     * @notice Returns all enabled delegations an address has given out
     * @param from The address to retrieve delegations for
     * @return delegations Array of Delegation structs
     */
    function getOutgoingDelegations(address from) external view returns (Delegation[] memory delegations);

    /**
     * @notice Returns all hashes associated with enabled delegations an address has received
     * @param to The address to retrieve incoming delegation hashes for
     * @return delegationHashes Array of delegation hashes
     */
    function getIncomingDelegationHashes(address to) external view returns (bytes32[] memory delegationHashes);

    /**
     * @notice Returns all hashes associated with enabled delegations an address has given out
     * @param from The address to retrieve outgoing delegation hashes for
     * @return delegationHashes Array of delegation hashes
     */
    function getOutgoingDelegationHashes(address from) external view returns (bytes32[] memory delegationHashes);

    /**
     * @notice Returns the delegations for a given array of delegation hashes
     * @param delegationHashes is an array of hashes that correspond to delegations
     * @return delegations Array of Delegation structs, return empty structs for nonexistent or revoked delegations
     */
    function getDelegationsFromHashes(bytes32[] calldata delegationHashes) external view returns (Delegation[] memory delegations);

    /**
     * ----------- STORAGE ACCESS -----------
     */

    /**
     * @notice Allows external contracts to read arbitrary storage slots
     */
    function readSlot(bytes32 location) external view returns (bytes32);

    /**
     * @notice Allows external contracts to read an arbitrary array of storage slots
     */
    function readSlots(bytes32[] calldata locations) external view returns (bytes32[] memory);
}

File 3 of 29 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 4 of 29 : ERC721.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 indexed id);

    event Approval(address indexed owner, address indexed spender, uint256 indexed id);

    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /*//////////////////////////////////////////////////////////////
                         METADATA STORAGE/LOGIC
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    function tokenURI(uint256 id) public view virtual returns (string memory);

    /*//////////////////////////////////////////////////////////////
                      ERC721 BALANCE/OWNER STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => address) internal _ownerOf;

    mapping(address => uint256) internal _balanceOf;

    function ownerOf(uint256 id) public view virtual returns (address owner) {
        require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
    }

    function balanceOf(address owner) public view virtual returns (uint256) {
        require(owner != address(0), "ZERO_ADDRESS");

        return _balanceOf[owner];
    }

    /*//////////////////////////////////////////////////////////////
                         ERC721 APPROVAL STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(uint256 => address) public getApproved;

    mapping(address => mapping(address => bool)) public isApprovedForAll;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(string memory _name, string memory _symbol) {
        name = _name;
        symbol = _symbol;
    }

    /*//////////////////////////////////////////////////////////////
                              ERC721 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 id) public virtual {
        address owner = _ownerOf[id];

        require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");

        getApproved[id] = spender;

        emit Approval(owner, spender, id);
    }

    function setApprovalForAll(address operator, bool approved) public virtual {
        isApprovedForAll[msg.sender][operator] = approved;

        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function transferFrom(
        address from,
        address to,
        uint256 id
    ) public virtual {
        require(from == _ownerOf[id], "WRONG_FROM");

        require(to != address(0), "INVALID_RECIPIENT");

        require(
            msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
            "NOT_AUTHORIZED"
        );

        // Underflow of the sender's balance is impossible because we check for
        // ownership above and the recipient's balance can't realistically overflow.
        unchecked {
            _balanceOf[from]--;

            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        delete getApproved[id];

        emit Transfer(from, to, id);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id
    ) public virtual {
        transferFrom(from, to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        bytes calldata data
    ) public virtual {
        transferFrom(from, to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    /*//////////////////////////////////////////////////////////////
                              ERC165 LOGIC
    //////////////////////////////////////////////////////////////*/

    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return
            interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
            interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
            interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 id) internal virtual {
        require(to != address(0), "INVALID_RECIPIENT");

        require(_ownerOf[id] == address(0), "ALREADY_MINTED");

        // Counter overflow is incredibly unrealistic.
        unchecked {
            _balanceOf[to]++;
        }

        _ownerOf[id] = to;

        emit Transfer(address(0), to, id);
    }

    function _burn(uint256 id) internal virtual {
        address owner = _ownerOf[id];

        require(owner != address(0), "NOT_MINTED");

        // Ownership check above ensures no underflow.
        unchecked {
            _balanceOf[owner]--;
        }

        delete _ownerOf[id];

        delete getApproved[id];

        emit Transfer(owner, address(0), id);
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL SAFE MINT LOGIC
    //////////////////////////////////////////////////////////////*/

    function _safeMint(address to, uint256 id) internal virtual {
        _mint(to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }

    function _safeMint(
        address to,
        uint256 id,
        bytes memory data
    ) internal virtual {
        _mint(to, id);

        require(
            to.code.length == 0 ||
                ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
                ERC721TokenReceiver.onERC721Received.selector,
            "UNSAFE_RECIPIENT"
        );
    }
}

/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721TokenReceiver {
    function onERC721Received(
        address,
        address,
        uint256,
        bytes calldata
    ) external virtual returns (bytes4) {
        return ERC721TokenReceiver.onERC721Received.selector;
    }
}

File 5 of 29 : FixedPointMathLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant MAX_UINT256 = 2**256 - 1;

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}

File 6 of 29 : ReentrancyGuard.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    uint256 private locked = 1;

    modifier nonReentrant() virtual {
        require(locked == 1, "REENTRANCY");

        locked = 2;

        _;

        locked = 1;
    }
}

File 7 of 29 : SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

File 8 of 29 : INFTFlashAction.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;

interface INFTFlashAction {
    error InvalidOwnerError();

    /// @notice Execute an arbitrary flash action on a given NFT. This contract owns it and must return it.
    /// @param _collection The NFT collection.
    /// @param _tokenId The NFT token ID.
    /// @param _target The target contract.
    /// @param _data The data to send to the target.
    function execute(address _collection, uint256 _tokenId, address _target, bytes calldata _data) external;
}

File 9 of 29 : IMultiSourceLoan.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;

import "./IBaseLoan.sol";

interface IMultiSourceLoan {
    /// @param executionData Execution data.
    /// @param lender Lender address.
    /// @param borrower Address that owns the NFT and will take over the loan.
    /// @param lenderOfferSignature Signature of the offer (signed by lender).
    /// @param borrowerOfferSignature Signature of the offer (signed by borrower).
    /// @param callbackData Whether to call the afterPrincipalTransfer callback
    struct LoanExecutionData {
        IBaseLoan.ExecutionData executionData;
        address lender;
        address borrower;
        bytes lenderOfferSignature;
        bytes borrowerOfferSignature;
    }

    /// @param loanId Loan ID.
    /// @param callbackData Whether to call the afterNFTTransfer callback
    /// @param shouldDelegate Whether to delegate ownership of the NFT (avoid seaport flags).
    struct SignableRepaymentData {
        uint256 loanId;
        bytes callbackData;
        bool shouldDelegate;
    }

    /// @param loan Loan.
    /// @param borrowerLoanSignature Signature of the loan (signed by borrower).
    struct LoanRepaymentData {
        SignableRepaymentData data;
        Loan loan;
        bytes borrowerSignature;
    }

    /// @notice When a loan is initiated, there's one source, the original lender. After each refinance,
    /// a new source is created to represent the new lender, and potentially others dissapear (if a tranche
    /// is fully refinanced)
    /// @dev No need to have principal address here since it's the same across all, so it can live in the Loan.
    /// @param loanId Loan ID.
    /// @param lender Lender for this given source.
    /// @param principalAmount Principal Amount.
    /// @param accruedInterest Accrued Interest.
    /// @param startTime Start Time. Either the time at which the loan initiated / was refinanced.
    /// @param aprBps APR in basis points.
    struct Source {
        uint256 loanId;
        address lender;
        uint256 principalAmount;
        uint256 accruedInterest;
        uint256 startTime;
        uint256 aprBps;
    }

    /// @dev Principal Amount is equal to the sum of all sources principalAmount.
    /// We keep it for caching purposes. Since we are not saving this on chain but the hash,
    /// it does not have a huge impact on gas.
    /// @param borrower Borrower.
    /// @param nftCollateralTokenId NFT Collateral Token ID.
    /// @param nftCollateralAddress NFT Collateral Address.
    /// @param principalAddress Principal Address.
    /// @param principalAmount Principal Amount.
    /// @param startTime Start Time.
    /// @param duration Duration.
    /// @param source Sources
    struct Loan {
        address borrower;
        uint256 nftCollateralTokenId;
        address nftCollateralAddress;
        address principalAddress;
        uint256 principalAmount;
        uint256 startTime;
        uint256 duration;
        Source[] source;
    }

    struct RenegotiationOffer {
        uint256 renegotiationId;
        uint256 loanId;
        address lender;
        uint256 fee;
        uint256[] targetPrincipal;
        uint256 principalAmount;
        uint256 aprBps;
        uint256 expirationTime;
        uint256 duration;
    }

    /// @notice Call by the borrower when emiting a new loan.
    /// @param _executionData Loan execution data.
    /// @return loanId Loan ID.
    /// @return loan Loan.
    function emitLoan(LoanExecutionData calldata _executionData) external returns (uint256, Loan memory);

    /// @notice Refinance whole loan (leaving just one source).
    /// @param _renegotiationOffer Offer to refinance a loan.
    /// @param _loan Current loan.
    /// @param _renegotiationOfferSignature Signature of the offer.
    /// @return loanId New Loan Id, New Loan.
    function refinanceFull(
        RenegotiationOffer calldata _renegotiationOffer,
        Loan memory _loan,
        bytes calldata _renegotiationOfferSignature
    ) external returns (uint256, Loan memory);

    /// @notice Refinance a loan partially. It can only be called by the new lender
    /// (they are always a strict improvement on apr).
    /// @param _renegotiationOffer Offer to refinance a loan partially.
    /// @param _loan Current loan.
    /// @return loanId New Loan Id, New Loan.
    function refinancePartial(RenegotiationOffer calldata _renegotiationOffer, Loan memory _loan)
        external
        returns (uint256, Loan memory);

    /// @notice Repay loan. Interest is calculated pro-rata based on time. Lender is defined by nft ownership.
    /// @param _repaymentData Repayment data.
    function repayLoan(LoanRepaymentData calldata _repaymentData) external;

    /// @notice Call when a loan is past its due date.
    /// @param _loanId Loan ID.
    /// @param _loan Loan.
    /// @return Liquidation Struct of the liquidation.
    function liquidateLoan(uint256 _loanId, Loan calldata _loan) external returns (bytes memory);

    /// @return maxSources Max sources per loan.
    function getMaxSources() external view returns (uint256);

    /// @notice Update the maximum number of sources per loan.
    /// @param maxSources Maximum number of sources.
    function setMaxSources(uint256 maxSources) external;

    /// @notice Set min lock period (in BPS).
    /// @param _minLockPeriod Min lock period.
    function setMinLockPeriod(uint256 _minLockPeriod) external;

    /// @notice Get min lock period (in BPS).
    /// @return minLockPeriod Min lock period.
    function getMinLockPeriod() external view returns (uint256);

    /// @notice Get delegation registry.
    /// @return delegateRegistry Delegate registry.
    function getDelegateRegistry() external view returns (address);

    /// @notice Update delegation registry.
    /// @param _newDelegationRegistry Delegation registry.
    function setDelegateRegistry(address _newDelegationRegistry) external;

    /// @notice Delegate ownership.
    /// @param _loanId Loan ID.
    /// @param _loan Loan.
    /// @param _rights Delegation Rights. Empty for all.
    /// @param _delegate Delegate address.
    /// @param _value True if delegate, false if undelegate.
    function delegate(uint256 _loanId, Loan calldata _loan, address _delegate, bytes32 _rights, bool _value) external;

    /// @notice Anyone can reveke a delegation on an NFT that's no longer in escrow.
    /// @param _delegate Delegate address.
    /// @param _collection Collection address.
    /// @param _tokenId Token ID.
    function revokeDelegate(address _delegate, address _collection, uint256 _tokenId) external;

    /// @notice Get Flash Action Contract.
    /// @return flashActionContract Flash Action Contract.
    function getFlashActionContract() external view returns (address);

    /// @notice Update Flash Action Contract.
    /// @param _newFlashActionContract Flash Action Contract.
    function setFlashActionContract(address _newFlashActionContract) external;

    /// @notice Get Loan Hash.
    /// @param _loanId Loan ID.
    /// @return loanHash Loan Hash.
    function getLoanHash(uint256 _loanId) external view returns (bytes32);

    /// @notice Transfer NFT to the flash action contract (expected use cases here are for airdrops and similar scenarios).
    /// The flash action contract would implement specific interactions with given contracts.
    /// Only the the borrower can call this function for a given loan. By the end of the transaction, the NFT must have
    /// been returned to escrow.
    /// @param _loanId Loan ID.
    /// @param _loan Loan.
    /// @param _target Target address for the flash action contract to interact with.
    /// @param _data Data to be passed to be passed to the ultimate contract.
    function executeFlashAction(uint256 _loanId, Loan calldata _loan, address _target, bytes calldata _data) external;

    /// @notice Extend loan duration. Can only be called by the lender on loans that have just one source.
    /// @param _loanId Loan ID.
    /// @param _loan Loan.
    /// @param _extension Extension in seconds.
    /// @return newLoanId New Loan Id
    /// @return newLoan New Loan.
    function extendLoan(uint256 _loanId, Loan memory _loan, uint256 _extension)
        external
        returns (uint256, Loan memory);

    /// @notice Called by the liquidator for accounting purposes.
    /// @param _loanId The id of the loan.
    /// @param _loan The loan object.
    function loanLiquidated(uint256 _loanId, Loan calldata _loan) external;
}

File 10 of 29 : ILoanLiquidator.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;

/// @title Liquidates Collateral for Defaulted Loans
/// @author Florida St
/// @notice It liquidates collateral corresponding to defaulted loans
///         and sends back the proceeds to the loan contract for distribution.
interface ILoanLiquidator {
    /// @notice Given a loan, it takes posession of the NFT and liquidates it.
    /// @param _loanId The loan id.
    /// @param _contract The loan contract address.
    /// @param _tokenId The NFT id.
    /// @param _asset The asset address.
    /// @param _duration The liquidation duration.
    /// @param _originator The address that trigger the liquidation.
    /// @return encodedAuction Encoded struct.
    function liquidateLoan(
        uint256 _loanId,
        address _contract,
        uint256 _tokenId,
        address _asset,
        uint96 _duration,
        address _originator
    ) external returns (bytes memory);
}

File 11 of 29 : Hash.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;

import "../../interfaces/loans/IMultiSourceLoan.sol";
import "../../interfaces/loans/IBaseLoan.sol";
import "../../interfaces/IAuctionLoanLiquidator.sol";

library Hash {
    // keccak256("OfferValidator(address validator,bytes arguments)")
    bytes32 private constant _VALIDATOR_HASH = 0x4def3e04bd42194484d5f8a5b268ec0df03b9d9d0402606fe3100023c5d79ac4;

    // keccak256("LoanOffer(uint256 offerId,address lender,uint256 fee,address borrower,uint256 capacity,address nftCollateralAddress,uint256 nftCollateralTokenId,address principalAddress,uint256 principalAmount,uint256 aprBps,uint256 expirationTime,uint256 duration,OfferValidator[] validators)OfferValidator(address validator,bytes arguments)")
    bytes32 private constant _LOAN_OFFER_HASH = 0x891e530ed2768a9decac48f4b7beec447f755ce23feeeeb952e429145b44ba91;

    /// keccak256("ExecutionData(LoanOffer offer,uint256 tokenId,uint256 amount,uint256 expirationTime,bytes callbackData)LoanOffer(uint256 offerId,address lender,uint256 fee,address borrower,uint256 capacity,address nftCollateralAddress,uint256 nftCollateralTokenId,address principalAddress,uint256 principalAmount,uint256 aprBps,uint256 expirationTime,uint256 duration,OfferValidator[] validators)OfferValidator(address validator,bytes arguments)")
    bytes32 private constant _EXECUTION_DATA_HASH = 0x7e90717662b6dd110797922ef6d6701d92bfd4164783966933e092ea21a74c5a;

    /// keccak256("SignableRepaymentData(uint256 loanId,bytes callbackData,bool shouldDelegate)")
    bytes32 private constant _SIGNABLE_REPAYMENT_DATA_HASH =
        0x41277b3c1cbe08ea7bbdd10a13f24dc956f3936bf46526f904c73697d9958e0c;

    // keccak256("Loan(address borrower,uint256 nftCollateralTokenId,address nftCollateralAddress,address principalAddress,uint256 principalAmount,uint256 startTime,uint256 duration,Source[] source)Source(uint256 loanId,address lender,uint256 principalAmount,uint256 accruedInterest,uint256 startTime,uint256 aprBps)")
    bytes32 private constant _MULTI_SOURCE_LOAN_HASH =
        0x35f73c5cb07b3fa605378d4f576769166fed212ec3813ac1f1d73ef1c537eb0e;

    // keccak256("Source(uint256 loanId,address lender,uint256 principalAmount,uint256 accruedInterest,uint256 startTime,uint256 aprBps)")
    bytes32 private constant _SOURCE_HASH = 0x8ca047c2f10359bf4a27bd2c623674be3801153b6b2646ba08593dc96ad7bb44;

    /// keccak256("RenegotiationOffer(uint256 renegotiationId,uint256 loanId,address lender,uint256 fee,uint256[] targetPrincipal,uint256 principalAmount,uint256 aprBps,uint256 expirationTime,uint256 duration)")
    bytes32 private constant _MULTI_RENEGOTIATION_OFFER_HASH =
        0xdb613ea3383336cd787d929ccfc21ab7cd87bf1d588780c80ce5f970dd79c348;

    /// keccak256("Auction(address loanAddress,uint256 loanId,uint256 highestBid,uint256 triggerFee,address highestBidder,uint96 duration,address asset,uint96 startTime,address originator,uint96 lastBidTime)")
    bytes32 private constant _AUCTION_HASH = 0xd1912299766a3d3ca1ad2e2135d884e08d798009860146382d22f8c389905b34;

    function hash(IBaseLoan.LoanOffer memory _loanOffer) internal pure returns (bytes32) {
        bytes memory encodedValidators;
        for (uint256 i = 0; i < _loanOffer.validators.length;) {
            encodedValidators = abi.encodePacked(encodedValidators, _hashValidator(_loanOffer.validators[i]));

            unchecked {
                ++i;
            }
        }
        return keccak256(
            abi.encode(
                _LOAN_OFFER_HASH,
                _loanOffer.offerId,
                _loanOffer.lender,
                _loanOffer.fee,
                _loanOffer.borrower,
                _loanOffer.capacity,
                _loanOffer.nftCollateralAddress,
                _loanOffer.nftCollateralTokenId,
                _loanOffer.principalAddress,
                _loanOffer.principalAmount,
                _loanOffer.aprBps,
                _loanOffer.expirationTime,
                _loanOffer.duration,
                keccak256(encodedValidators)
            )
        );
    }

    function hash(IBaseLoan.ExecutionData memory _executionData) internal pure returns (bytes32) {
        return keccak256(
            abi.encode(
                _EXECUTION_DATA_HASH,
                hash(_executionData.offer),
                _executionData.tokenId,
                _executionData.amount,
                _executionData.expirationTime,
                keccak256(_executionData.callbackData)
            )
        );
    }

    function hash(IMultiSourceLoan.SignableRepaymentData memory _repaymentData) internal pure returns (bytes32) {
        return keccak256(
            abi.encode(
                _SIGNABLE_REPAYMENT_DATA_HASH,
                _repaymentData.loanId,
                keccak256(_repaymentData.callbackData),
                _repaymentData.shouldDelegate
            )
        );
    }

    function hash(IMultiSourceLoan.Loan memory _loan) internal pure returns (bytes32) {
        bytes memory sourceHashes;
        for (uint256 i = 0; i < _loan.source.length;) {
            sourceHashes = abi.encodePacked(sourceHashes, _hashSource(_loan.source[i]));
            unchecked {
                ++i;
            }
        }
        return keccak256(
            abi.encode(
                _MULTI_SOURCE_LOAN_HASH,
                _loan.borrower,
                _loan.nftCollateralTokenId,
                _loan.nftCollateralAddress,
                _loan.principalAddress,
                _loan.principalAmount,
                _loan.startTime,
                _loan.duration,
                keccak256(sourceHashes)
            )
        );
    }

    function hash(IMultiSourceLoan.RenegotiationOffer memory _refinanceOffer) internal pure returns (bytes32) {
        bytes memory encodedPrincipals;
        for (uint256 i = 0; i < _refinanceOffer.targetPrincipal.length;) {
            encodedPrincipals = abi.encodePacked(encodedPrincipals, _refinanceOffer.targetPrincipal[i]);
            unchecked {
                ++i;
            }
        }
        return keccak256(
            abi.encode(
                _MULTI_RENEGOTIATION_OFFER_HASH,
                _refinanceOffer.renegotiationId,
                _refinanceOffer.loanId,
                _refinanceOffer.lender,
                _refinanceOffer.fee,
                keccak256(encodedPrincipals),
                _refinanceOffer.principalAmount,
                _refinanceOffer.aprBps,
                _refinanceOffer.expirationTime,
                _refinanceOffer.duration
            )
        );
    }

    function hash(IAuctionLoanLiquidator.Auction memory _auction) internal pure returns (bytes32) {
        return keccak256(
            abi.encode(
                _AUCTION_HASH,
                _auction.loanAddress,
                _auction.loanId,
                _auction.highestBid,
                _auction.triggerFee,
                _auction.highestBidder,
                _auction.duration,
                _auction.asset,
                _auction.startTime,
                _auction.originator,
                _auction.lastBidTime
            )
        );
    }

    function _hashSource(IMultiSourceLoan.Source memory _source) private pure returns (bytes32) {
        return keccak256(
            abi.encode(
                _SOURCE_HASH,
                _source.lender,
                _source.principalAmount,
                _source.accruedInterest,
                _source.startTime,
                _source.aprBps
            )
        );
    }

    function _hashValidator(IBaseLoan.OfferValidator memory _validator) private pure returns (bytes32) {
        return keccak256(abi.encode(_VALIDATOR_HASH, _validator.validator, keccak256(_validator.arguments)));
    }
}

File 12 of 29 : Interest.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;

import "@solmate/utils/FixedPointMathLib.sol";
import "../../interfaces/loans/IMultiSourceLoan.sol";
import "../../interfaces/loans/IBaseLoan.sol";

library Interest {
    using FixedPointMathLib for uint256;

    uint256 private constant _PRECISION = 10000;

    uint256 private constant _SECONDS_PER_YEAR = 31536000;

    function getInterest(IBaseLoan.LoanOffer memory _loanOffer) internal pure returns (uint256) {
        return _getInterest(_loanOffer.principalAmount, _loanOffer.aprBps, _loanOffer.duration);
    }

    function getInterest(uint256 _amount, uint256 _aprBps, uint256 _duration) internal pure returns (uint256) {
        return _getInterest(_amount, _aprBps, _duration);
    }

    function getTotalOwed(IMultiSourceLoan.Loan memory _loan, uint256 _timestamp) internal pure returns (uint256) {
        uint256 owed = 0;
        for (uint256 i = 0; i < _loan.source.length;) {
            IMultiSourceLoan.Source memory source = _loan.source[i];
            owed += source.principalAmount + source.accruedInterest
                + _getInterest(source.principalAmount, source.aprBps, _timestamp - source.startTime);
            unchecked {
                ++i;
            }
        }
        return owed;
    }

    function _getInterest(uint256 _amount, uint256 _aprBps, uint256 _duration) private pure returns (uint256) {
        return _amount.mulDivUp(_aprBps * _duration, _PRECISION * _SECONDS_PER_YEAR);
    }
}

File 13 of 29 : Multicall.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;

import "../interfaces/IMulticall.sol";

/// @title Multicall
/// @author Florida St
/// @notice Base implementation for multicall.
abstract contract Multicall is IMulticall {
    function multicall(bytes[] calldata data) external payable override returns (bytes[] memory results) {
        results = new bytes[](data.length);
        bool success;
        for (uint256 i = 0; i < data.length;) {
            //slither-disable-next-line calls-loop,delegatecall-loop
            (success, results[i]) = address(this).delegatecall(data[i]);
            if (!success) revert MulticallFailed(i, results[i]);
            unchecked {
                ++i;
            }
        }
    }
}

File 14 of 29 : WithCallbacks.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;

import "../../interfaces/callbacks/ILoanCallback.sol";
import "./BaseLoan.sol";

abstract contract WithCallbacks is BaseLoan {
    struct Taxes {
        uint128 buyTax;
        uint128 sellTax;
    }

    event WhitelistedCallbackContractAdded(address contractAdded, Taxes tax);

    event WhitelistedCallbackContractRemoved(address contractRemoved);

    mapping(address => Taxes) private _callbackTaxes;

    constructor(string memory _name, address __currencyManager, address __collectionManager)
        BaseLoan(_name, __currencyManager, __collectionManager)
    {}

    /// @notice Add a whitelisted callback contract / update an existing one with different taxes.
    /// @param _contract Address of the contract.
    function addWhitelistedCallbackContract(address _contract, Taxes calldata _tax) external onlyOwner {
        _checkAddressNotZero(_contract);
        if (_tax.buyTax > _PRECISION || _tax.sellTax > _PRECISION) {
            revert InvalidValueError();
        }
        _isWhitelistedCallbackContract[_contract] = true;
        _callbackTaxes[_contract] = _tax;

        emit WhitelistedCallbackContractAdded(_contract, _tax);
    }

    /// @notice Remove a whitelisted callback contract.
    /// @param _contract Address of the contract.
    function removeWhitelistedCallbackContract(address _contract) external onlyOwner {
        _isWhitelistedCallbackContract[_contract] = false;
        delete _callbackTaxes[_contract];

        emit WhitelistedCallbackContractRemoved(_contract);
    }

    /// @return Whether a callback contract is whitelisted
    function isWhitelistedCallbackContract(address _contract) external view returns (bool) {
        return _isWhitelistedCallbackContract[_contract];
    }

    /// @notice Handle the afterPrincipalTransfer callback.
    /// @param _loan Loan.
    /// @param _callbackData Callback data.
    /// @param _fee Fee.
    /// @return buyTax
    function _handleAfterPrincipalTransferCallback(
        IMultiSourceLoan.Loan memory _loan,
        bytes memory _callbackData,
        uint256 _fee
    ) internal returns (uint128) {
        if (_noCallback(_callbackData)) {
            return 0;
        }
        if (
            !_isWhitelistedCallbackContract[msg.sender]
                || ILoanCallback(msg.sender).afterPrincipalTransfer(_loan, _fee, _callbackData)
                    != ILoanCallback.afterPrincipalTransfer.selector
        ) {
            revert ILoanCallback.InvalidCallbackError();
        }
        return _callbackTaxes[msg.sender].buyTax;
    }

    /// @notice Handle the afterNFTTransfer callback.
    /// @param _loan Loan.
    /// @param _callbackData Callback data.
    /// @return sellTax
    function _handleAfterNFTTransferCallback(IMultiSourceLoan.Loan memory _loan, bytes calldata _callbackData)
        internal
        returns (uint128)
    {
        if (_noCallback(_callbackData)) {
            return 0;
        }
        if (
            !_isWhitelistedCallbackContract[msg.sender]
                || ILoanCallback(msg.sender).afterNFTTransfer(_loan, _callbackData)
                    != ILoanCallback.afterNFTTransfer.selector
        ) {
            revert ILoanCallback.InvalidCallbackError();
        }
        return _callbackTaxes[msg.sender].sellTax;
    }

    function _noCallback(bytes memory _callbackData) private pure returns (bool) {
        return _callbackData.length == 0;
    }
}

File 15 of 29 : IBaseLoan.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;

import "../../interfaces/ILoanLiquidator.sol";

/// @title Interface for Loans.
/// @author Florida St
/// @notice Basic Loan
interface IBaseLoan {
    /// @notice Minimum improvement (in BPS) required for a strict improvement.
    /// @param principalAmount Minimum delta of principal amount.
    /// @param interest Minimum delta of interest.
    /// @param duration Minimum delta of duration.
    struct ImprovementMinimum {
        uint256 principalAmount;
        uint256 interest;
        uint256 duration;
    }

    /// @notice Arbitrary contract to validate offers implementing `IBaseOfferValidator`.
    /// @param validator Address of the validator contract.
    /// @param arguments Arguments to pass to the validator.
    struct OfferValidator {
        address validator;
        bytes arguments;
    }

    /// @notice Borrowers receive offers that are then validated.
    /// @dev Setting the nftCollateralTokenId to 0 triggers validation through `validators`.
    /// @param offerId Offer ID. Used for canceling/setting as executed.
    /// @param lender Lender of the offer.
    /// @param fee Origination fee.
    /// @param borrower Borrower of the offer. Can be set to 0 (any borrower).
    /// @param capacity Capacity of the offer.
    /// @param nftCollateralAddress Address of the NFT collateral.
    /// @param nftCollateralTokenId NFT collateral token ID.
    /// @param principalAddress Address of the principal.
    /// @param principalAmount Principal amount of the loan.
    /// @param aprBps APR in BPS.
    /// @param expirationTime Expiration time of the offer.
    /// @param duration Duration of the loan in seconds.
    /// @param validators Arbitrary contract to validate offers implementing `IBaseOfferValidator`.
    struct LoanOffer {
        uint256 offerId;
        address lender;
        uint256 fee;
        address borrower;
        uint256 capacity;
        address nftCollateralAddress;
        uint256 nftCollateralTokenId;
        address principalAddress;
        uint256 principalAmount;
        uint256 aprBps;
        uint256 expirationTime;
        uint256 duration;
        OfferValidator[] validators;
    }

    /// @notice Offer + necessary fields to execute a specific loan. This has a separate expirationTime to avoid
    /// someone holding an offer and executing much later, without the borrower's awareness.
    /// @param offer Loan offer. It can be executed potentially for multiple ids / amounts < principalAmount.
    /// @param tokenId NFT collateral token ID.
    /// @param amount The amount the borrower is willing to take (must be <= _loanOffer principalAmount)
    /// @param expirationTime Expiration time of the signed offer by the borrower.
    /// @param callbackData Data to pass to the callback.
    struct ExecutionData {
        LoanOffer offer;
        uint256 tokenId;
        uint256 amount;
        uint256 expirationTime;
        bytes callbackData;
    }

    /// @notice Recipient address and fraction of gains charged by the protocol.
    struct ProtocolFee {
        address recipient;
        uint256 fraction;
    }

    /// @notice Total number of loans issued by this contract.
    function getTotalLoansIssued() external view returns (uint256);

    /// @notice Cancel offer for `msg.sender`. Each lender has unique offerIds.
    /// @param _offerId Offer ID.
    function cancelOffer(uint256 _offerId) external;

    /// @notice Cancel multiple offers.
    /// @param _offerIds Offer IDs.
    function cancelOffers(uint256[] calldata _offerIds) external;

    /// @notice Cancell all offers with offerId < _minOfferId
    /// @param _minOfferId Minimum offer ID.
    function cancelAllOffers(uint256 _minOfferId) external;

    /// @notice Cancel renegotiation offer. Similar to offers.
    /// @param _renegotiationId Renegotiation offer ID.
    function cancelRenegotiationOffer(uint256 _renegotiationId) external;

    /// @notice Cancel multiple renegotiation offers.
    /// @param _renegotiationIds Renegotiation offer IDs.
    function cancelRenegotiationOffers(uint256[] calldata _renegotiationIds) external;

    /// @notice Cancell all renegotiation offers with renegotiationId < _minRenegotiationId
    /// @param _minRenegotiationId Minimum renegotiation offer ID.
    function cancelAllRenegotiationOffers(uint256 _minRenegotiationId) external;

    /// @return protocolFee The Protocol fee.
    function getProtocolFee() external view returns (ProtocolFee memory);

    /// @return pendingProtocolFee The pending protocol fee.
    function getPendingProtocolFee() external view returns (ProtocolFee memory);

    /// @return protocolFeeSetTime Time when the protocol fee was set to be changed.
    function getPendingProtocolFeeSetTime() external view returns (uint256);

    /// @notice Kicks off the process to update the protocol fee.
    /// @param _newProtocolFee New protocol fee.
    function updateProtocolFee(ProtocolFee calldata _newProtocolFee) external;

    /// @notice Set the protocol fee if enough notice has been given.
    function setProtocolFee() external;

    /// @return Liquidator contract address
    function getLiquidator() external returns (address);

    /// @notice Updates the liquidation contract.
    /// @param loanLiquidator New liquidation contract.
    function updateLiquidationContract(ILoanLiquidator loanLiquidator) external;

    /// @notice Updates the auction duration for liquidations.
    /// @param _newDuration New auction duration.
    function updateLiquidationAuctionDuration(uint48 _newDuration) external;

    /// @return auctionDuration Returns the auction's duration for liquidations.
    function getLiquidationAuctionDuration() external returns (uint48);
}

File 16 of 29 : IAuctionLoanLiquidator.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;

import "./loans/IMultiSourceLoan.sol";

/// @title Liquidates Collateral for Defaulted Loans using English Auctions.
/// @author Florida St
/// @notice It liquidates collateral corresponding to defaulted loans
///         and sends back the proceeds to the loan contract for distribution.
interface IAuctionLoanLiquidator {
    /// @notice The auction struct.
    /// @param loanAddress The loan contract address.
    /// @param loanId The loan id.
    /// @param highestBid The highest bid.
    /// @param highestBidder The highest bidder.
    /// @param duration The auction duration.
    /// @param asset The asset address.
    /// @param startTime The auction start time.
    /// @param originator The address that triggered the liquidation.
    /// @param lastBidTime The last bid time.
    struct Auction {
        address loanAddress;
        uint256 loanId;
        uint256 highestBid;
        uint256 triggerFee;
        address highestBidder;
        uint96 duration;
        address asset;
        uint96 startTime;
        address originator;
        uint96 lastBidTime;
    }

    /// @notice Add a loan contract to the list of accepted contracts.
    /// @param _loanContract The loan contract to be added.
    function addLoanContract(address _loanContract) external;

    /// @notice Remove a loan contract from the list of accepted contracts.
    /// @param _loanContract The loan contract to be removed.
    function removeLoanContract(address _loanContract) external;

    /// @return The loan contracts that are accepted by this liquidator.
    function getValidLoanContracts() external view returns (address[] memory);

    /// @notice Update liquidation distributor.
    /// @param _liquidationDistributor The new liquidation distributor.
    function updateLiquidationDistributor(address _liquidationDistributor) external;

    /// @return liquidationDistributor The liquidation distributor address.
    function getLiquidationDistributor() external view returns (address);

    /// @notice Called by the owner to update the trigger fee.
    /// @param triggerFee The new trigger fee.
    function updateTriggerFee(uint256 triggerFee) external;

    /// @return triggerFee The trigger fee.
    function getTriggerFee() external view returns (uint256);

    /// @notice When a bid is placed, the contract takes possesion of the bid, and
    ///         if there was a previous bid, it returns that capital to the original
    ///         bidder.
    /// @param _contract The nft contract address.
    /// @param _tokenId The nft id.
    /// @param _auction The auction struct.
    /// @param _bid The bid amount.
    /// @return auction The updated auction struct.
    function placeBid(address _contract, uint256 _tokenId, Auction memory _auction, uint256 _bid)
        external
        returns (Auction memory);

    /// @notice On settlement, the NFT is sent to the highest bidder.
    ///         Calls loan liquidated for accounting purposes.
    /// @param _auction The auction struct.
    /// @param _loan The loan struct.
    function settleAuction(Auction calldata _auction, IMultiSourceLoan.Loan calldata _loan) external;

    /// @notice The contract has hashes of all auctions to save space (not the actual struct)
    /// @param _contract The nft contract address.
    /// @param _tokenId The nft id.
    /// @return auctionHash The auction hash.
    function getAuctionHash(address _contract, uint256 _tokenId) external view returns (bytes32);
}

File 17 of 29 : IMulticall.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;

interface IMulticall {
    error MulticallFailed(uint256 i, bytes returndata);

    /// @notice Call multiple functions in the contract. Revert if one of them fails, return results otherwise.
    /// @param data Encoded function calls.
    /// @return results The results of the function calls.
    function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
}

File 18 of 29 : ILoanCallback.sol
// SPDX-License-Identifier: AGPL-3.0

pragma solidity ^0.8.20;

import "../loans/IMultiSourceLoan.sol";

interface ILoanCallback {
    error InvalidCallbackError();

    /// @notice Called by the MSL contract after the principal of loan has been tranfered (when a loan is initiated)
    /// but before it tries to transfer the NFT into escrow.
    /// @param _loan The loan.
    /// @param _fee The origination fee.
    /// @param _executionData Execution data for purchase.
    /// @return The bytes4 magic value.
    function afterPrincipalTransfer(IMultiSourceLoan.Loan memory _loan, uint256 _fee, bytes calldata _executionData)
        external
        returns (bytes4);

    /// @notice Call by the MSL contract after the NFT has been transfered to the borrower repaying the loan, but before
    /// transfering the principal to the lender.
    /// @param _loan The loan.
    /// @param _executionData Execution data for the offer.
    /// @return The bytes4 magic value.
    function afterNFTTransfer(IMultiSourceLoan.Loan memory _loan, bytes calldata _executionData)
        external
        returns (bytes4);
}

File 19 of 29 : BaseLoan.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;

import "@openzeppelin/utils/cryptography/ECDSA.sol";
import "@openzeppelin/utils/cryptography/MessageHashUtils.sol";
import "@openzeppelin/interfaces/IERC1271.sol";
import "@solmate/tokens/ERC721.sol";
import "@solmate/utils/FixedPointMathLib.sol";

import "../AddressManager.sol";
import "../utils/Hash.sol";
import "../../interfaces/loans/IBaseLoan.sol";
import "../../interfaces/validators/IBaseOfferValidator.sol";
import "../InputChecker.sol";

/// @title BaseLoan
/// @author Florida St
/// @notice Base implementation that we expect all loans to share. Offers can either be
///         for new loans or renegotiating existing ones.
///         Offers are signed off-chain.
///         Offers have a nonce associated that is used for cancelling and
///         marking as executed.
abstract contract BaseLoan is ERC721TokenReceiver, IBaseLoan, InputChecker, Owned {
    using FixedPointMathLib for uint256;
    using ECDSA for bytes32;
    using MessageHashUtils for bytes32;
    using Hash for LoanOffer;
    using Hash for ExecutionData;

    /// @notice Used in compliance with EIP712
    uint256 internal immutable INITIAL_CHAIN_ID;
    bytes32 public immutable INITIAL_DOMAIN_SEPARATOR;

    uint256 public constant MAX_PROTOCOL_FEE = 2500;
    uint256 public constant FEE_UPDATE_NOTICE = 30 days;
    uint48 public constant MIN_AUCTION_DURATION = 1 days;
    bytes4 private constant MAGICVALUE_1271 = 0x1626ba7e;

    /// @notice Precision used for calculating interests.
    uint256 internal constant _PRECISION = 10000;

    /// @notice Minimum improvement (in BPS) required for a strict improvement.
    ImprovementMinimum internal _minimum = ImprovementMinimum(500, 100, 100);

    string public name;

    /// @notice Duration of the auction when a loan defaults requires a liquidation.
    uint48 internal _liquidationAuctionDuration = 3 days;

    /// @notice Liquidator used defaulted loans that requires liquidation.
    ILoanLiquidator internal _loanLiquidator;

    /// @notice Protocol fee charged on gains.
    ProtocolFee internal _protocolFee;
    /// @notice Set as the target new protocol fee.
    ProtocolFee internal _pendingProtocolFee;
    /// @notice Set when the protocol fee updating mechanisms starts.
    uint256 internal _pendingProtocolFeeSetTime;

    /// @notice Total number of loans issued. Given it's a serial value, we use it
    ///         as loan id.
    uint256 public override getTotalLoansIssued;

    /// @notice Offer capacity
    mapping(address => mapping(uint256 => uint256)) internal _used;

    /// @notice Used for validate off chain maker offers / canceling one
    mapping(address => mapping(uint256 => bool)) public isOfferCancelled;
    /// @notice Used for validating off chain maker offers / canceling all
    mapping(address => uint256) public minOfferId;

    /// @notice Used in a similar way as `isOfferCancelled` to handle renegotiations.
    mapping(address => mapping(uint256 => bool)) public isRenegotiationOfferCancelled;
    /// @notice Used in a similar way as `minOfferId` to handle renegotiations.
    mapping(address => uint256) public lenderMinRenegotiationOfferId;

    /// @notice Loans are only denominated in whitelisted addresses. Within each struct,
    ///         we save those as their `uint` representation.
    AddressManager internal immutable _currencyManager;

    /// @notice Only whilteslited collections are accepted as collateral. Within each struct,
    ///         we save those as their `uint` representation.
    AddressManager internal immutable _collectionManager;

    /// @notice For security reasons we only allow a whitelisted set of callback contracts.
    mapping(address => bool) internal _isWhitelistedCallbackContract;

    event OfferCancelled(address lender, uint256 offerId);

    event BorrowerOfferCancelled(address borrower, uint256 offerId);

    event AllOffersCancelled(address lender, uint256 minOfferId);

    event RenegotiationOfferCancelled(address lender, uint256 renegotiationId);

    event AllRenegotiationOffersCancelled(address lender, uint256 minRenegotiationId);

    event ProtocolFeeUpdated(ProtocolFee fee);

    event ProtocolFeePendingUpdate(ProtocolFee fee);

    event LoanSentToLiquidator(uint256 loanId, address liquidator);

    event LoanLiquidated(uint256 loanId);

    event LoanForeclosed(uint256 loanId);

    event ImprovementMinimumUpdated(ImprovementMinimum minimum);

    event LiquidationContractUpdated(address liquidator);

    event LiquidationAuctionDurationUpdated(uint256 newDuration);

    error InvalidValueError();

    error LiquidatorOnlyError(address _liquidator);

    error CancelledOrExecutedOfferError(address _lender, uint256 _offerId);

    error CancelledRenegotiationOfferError(address _lender, uint256 _renegotiationId);

    error ExpiredOfferError(uint256 _expirationTime);

    error ExpiredRenegotiationOfferError(uint256 _expirationTime);

    error LowOfferIdError(address _lender, uint256 _newMinOfferId, uint256 _minOfferId);

    error LowRenegotiationOfferIdError(address _lender, uint256 _newMinRenegotiationOfferId, uint256 _minOfferId);

    error CannotLiquidateError();

    error LoanNotDueError(uint256 _expirationTime);

    error InvalidLenderError();

    error InvalidBorrowerError();

    error ZeroDurationError();

    error ZeroInterestError();

    error InvalidSignatureError();

    error InvalidLiquidationError();

    error CurrencyNotWhitelistedError();

    error CollectionNotWhitelistedError();

    error InvalidProtocolFeeError(uint256 _fraction);

    error TooEarlyError(uint256 _pendingProtocolFeeSetTime);

    error MaxCapacityExceededError();

    error InvalidLoanError(uint256 _loanId);

    error InvalidCollateralIdError();

    error OnlyLenderOrBorrowerCallableError();

    error OnlyBorrowerCallableError();

    error OnlyLenderCallableError();

    error NotStrictlyImprovedError();

    error InvalidAmountError(uint256 _amount, uint256 _principalAmount);

    error InvalidDurationError();

    constructor(string memory _name, address currencyManager, address collectionManager) Owned(tx.origin) {
        name = _name;
        _checkAddressNotZero(currencyManager);
        _checkAddressNotZero(collectionManager);

        _currencyManager = AddressManager(currencyManager);
        _collectionManager = AddressManager(collectionManager);
        _pendingProtocolFeeSetTime = type(uint256).max;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = _computeDomainSeparator();
    }

    modifier onlyLiquidator() {
        if (msg.sender != address(_loanLiquidator)) {
            revert LiquidatorOnlyError(address(_loanLiquidator));
        }
        _;
    }

    /// @notice Get the domain separator requried to comply with EIP-712.
    function DOMAIN_SEPARATOR() public view returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : _computeDomainSeparator();
    }

    /// @return The minimum improvement for a loan to be considered strictly better.
    function getImprovementMinimum() external view returns (ImprovementMinimum memory) {
        return _minimum;
    }

    /// @notice Updates the minimum improvement for a loan to be considered strictly better.
    ///         Only the owner can call this function.
    /// @param _newMinimum The new minimum improvement.
    function updateImprovementMinimum(ImprovementMinimum calldata _newMinimum) external onlyOwner {
        _minimum = _newMinimum;

        emit ImprovementMinimumUpdated(_newMinimum);
    }

    /// @return Address of the currency manager.
    function getCurrencyManager() external view returns (address) {
        return address(_currencyManager);
    }

    /// @return Address of the collection manager.
    function getCollectionManager() external view returns (address) {
        return address(_collectionManager);
    }

    /// @inheritdoc IBaseLoan
    function cancelOffer(uint256 _offerId) external {
        address user = msg.sender;
        isOfferCancelled[user][_offerId] = true;

        emit OfferCancelled(user, _offerId);
    }

    /// @inheritdoc IBaseLoan
    function cancelOffers(uint256[] calldata _offerIds) external virtual {
        address user = msg.sender;
        uint256 total = _offerIds.length;
        for (uint256 i = 0; i < total;) {
            uint256 offerId = _offerIds[i];
            isOfferCancelled[user][offerId] = true;

            emit OfferCancelled(user, offerId);
            unchecked {
                ++i;
            }
        }
    }

    /// @inheritdoc IBaseLoan
    function cancelAllOffers(uint256 _minOfferId) external virtual {
        address user = msg.sender;
        uint256 currentMinOfferId = minOfferId[user];
        if (currentMinOfferId >= _minOfferId) {
            revert LowOfferIdError(user, _minOfferId, currentMinOfferId);
        }
        minOfferId[user] = _minOfferId;

        emit AllOffersCancelled(user, _minOfferId);
    }

    /// @inheritdoc IBaseLoan
    function cancelRenegotiationOffer(uint256 _renegotiationId) external virtual {
        address lender = msg.sender;
        isRenegotiationOfferCancelled[lender][_renegotiationId] = true;

        emit RenegotiationOfferCancelled(lender, _renegotiationId);
    }

    /// @inheritdoc IBaseLoan
    function cancelRenegotiationOffers(uint256[] calldata _renegotiationIds) external virtual {
        address lender = msg.sender;
        uint256 total = _renegotiationIds.length;
        for (uint256 i = 0; i < total;) {
            uint256 renegotiationId = _renegotiationIds[i];
            isRenegotiationOfferCancelled[lender][renegotiationId] = true;

            emit RenegotiationOfferCancelled(lender, renegotiationId);
            unchecked {
                ++i;
            }
        }
    }

    /// @inheritdoc IBaseLoan
    function cancelAllRenegotiationOffers(uint256 _minRenegotiationId) external virtual {
        address lender = msg.sender;
        uint256 currentMinRenegotiationOfferId = lenderMinRenegotiationOfferId[lender];
        if (currentMinRenegotiationOfferId >= _minRenegotiationId) {
            revert LowRenegotiationOfferIdError(lender, _minRenegotiationId, currentMinRenegotiationOfferId);
        }
        lenderMinRenegotiationOfferId[lender] = _minRenegotiationId;

        emit AllRenegotiationOffersCancelled(lender, _minRenegotiationId);
    }

    /// @notice Returns the remaining capacity for a given loan offer.
    /// @param _lender The address of the lender.
    /// @param _offerId The id of the offer.
    /// @return The amount lent out.
    function getUsedCapacity(address _lender, uint256 _offerId) external view returns (uint256) {
        return _used[_lender][_offerId];
    }

    /// @inheritdoc IBaseLoan
    function getProtocolFee() external view returns (ProtocolFee memory) {
        return _protocolFee;
    }

    /// @inheritdoc IBaseLoan
    function getPendingProtocolFee() external view returns (ProtocolFee memory) {
        return _pendingProtocolFee;
    }

    /// @inheritdoc IBaseLoan
    function getPendingProtocolFeeSetTime() external view returns (uint256) {
        return _pendingProtocolFeeSetTime;
    }

    /// @inheritdoc IBaseLoan
    function setProtocolFee() external onlyOwner {
        if (block.timestamp < _pendingProtocolFeeSetTime + FEE_UPDATE_NOTICE) {
            revert TooEarlyError(_pendingProtocolFeeSetTime);
        }
        _protocolFee = _pendingProtocolFee;

        emit ProtocolFeeUpdated(_pendingProtocolFee);
    }

    /// @inheritdoc IBaseLoan
    function updateProtocolFee(ProtocolFee calldata _newProtocolFee) external onlyOwner {
        if (_newProtocolFee.fraction > MAX_PROTOCOL_FEE) {
            revert InvalidProtocolFeeError(_newProtocolFee.fraction);
        }
        _checkAddressNotZero(_newProtocolFee.recipient);

        _pendingProtocolFee = _newProtocolFee;
        _pendingProtocolFeeSetTime = block.timestamp;

        emit ProtocolFeePendingUpdate(_pendingProtocolFee);
    }

    /// @inheritdoc IBaseLoan
    function getLiquidator() external view returns (address) {
        return address(_loanLiquidator);
    }

    /// @inheritdoc IBaseLoan
    function updateLiquidationContract(ILoanLiquidator loanLiquidator) external onlyOwner {
        _checkAddressNotZero(address(loanLiquidator));
        _loanLiquidator = loanLiquidator;

        emit LiquidationContractUpdated(address(loanLiquidator));
    }

    /// @inheritdoc IBaseLoan
    function updateLiquidationAuctionDuration(uint48 _newDuration) external onlyOwner {
        if (_newDuration < MIN_AUCTION_DURATION) {
            revert InvalidDurationError();
        }
        _liquidationAuctionDuration = _newDuration;

        emit LiquidationAuctionDurationUpdated(_newDuration);
    }

    /// @inheritdoc IBaseLoan
    function getLiquidationAuctionDuration() external view returns (uint48) {
        return _liquidationAuctionDuration;
    }

    /// @notice Call when issuing a new loan to get/set a unique serial id.
    /// @dev This id should never be 0.
    /// @return The new loan id.
    function _getAndSetNewLoanId() internal returns (uint256) {
        unchecked {
            return ++getTotalLoansIssued;
        }
    }

    /// @notice Base ExecutionData Checks
    /// @dev Note that we do not validate fee < principalAmount since this is done in the child class in this case.
    /// @param _executionData Loan execution data.
    /// @param _lender The lender.
    /// @param _borrower The borrower.
    /// @param _offerer The offerrer (either lender or borrower)
    /// @param _lenderOfferSignature The signature of the lender of LoanOffer.
    /// @param _borrowerOfferSignature The signature of the borrower of ExecutionData.
    function _validateExecutionData(
        ExecutionData calldata _executionData,
        address _lender,
        address _borrower,
        address _offerer,
        bytes calldata _lenderOfferSignature,
        bytes calldata _borrowerOfferSignature
    ) internal {
        address lender = _executionData.offer.lender;
        address borrower = _executionData.offer.borrower;
        LoanOffer calldata offer = _executionData.offer;
        uint256 offerId = offer.offerId;

        if (msg.sender != _lender) {
            _checkSignature(lender, offer.hash(), _lenderOfferSignature);
        }
        if (msg.sender != _borrower) {
            _checkSignature(_borrower, _executionData.hash(), _borrowerOfferSignature);
        }

        if (block.timestamp > offer.expirationTime) {
            revert ExpiredOfferError(offer.expirationTime);
        }
        if (block.timestamp > _executionData.expirationTime) {
            revert ExpiredOfferError(_executionData.expirationTime);
        }

        if (isOfferCancelled[_offerer][offerId] || (offerId <= minOfferId[_offerer])) {
            revert CancelledOrExecutedOfferError(_offerer, offerId);
        }

        if (_executionData.amount > offer.principalAmount) {
            revert InvalidAmountError(_executionData.amount, offer.principalAmount);
        }

        if (!_currencyManager.isWhitelisted(offer.principalAddress)) {
            revert CurrencyNotWhitelistedError();
        }
        if (!_collectionManager.isWhitelisted(offer.nftCollateralAddress)) {
            revert CollectionNotWhitelistedError();
        }

        if (lender != address(0) && (lender != _lender)) {
            revert InvalidLenderError();
        }
        if (borrower != address(0) && (borrower != _borrower)) {
            revert InvalidBorrowerError();
        }
        if (offer.duration == 0) {
            revert ZeroDurationError();
        }
        if (offer.aprBps == 0) {
            revert ZeroInterestError();
        }
        if ((offer.capacity > 0) && (_used[_offerer][offer.offerId] + _executionData.amount > offer.capacity)) {
            revert MaxCapacityExceededError();
        }

        _checkValidators(offer, _executionData.tokenId);
    }

    /// @notice Check generic offer validators for a given offer or
    ///         an exact match if no validators are given. The validators
    ///         check is performed only if tokenId is set to 0.
    ///         Having one empty validator is used for collection offers (all IDs match).
    /// @param _loanOffer The loan offer to check.
    /// @param _tokenId The token ID to check.
    function _checkValidators(LoanOffer calldata _loanOffer, uint256 _tokenId) internal {
        uint256 offerTokenId = _loanOffer.nftCollateralTokenId;
        if (_loanOffer.nftCollateralTokenId != 0) {
            if (offerTokenId != _tokenId) {
                revert InvalidCollateralIdError();
            }
        } else {
            uint256 totalValidators = _loanOffer.validators.length;
            if (totalValidators == 0 && _tokenId != 0) {
                revert InvalidCollateralIdError();
            } else if ((totalValidators == 1) && (_loanOffer.validators[0].validator == address(0))) {
                return;
            }
            for (uint256 i = 0; i < totalValidators;) {
                OfferValidator memory thisValidator = _loanOffer.validators[i];
                IBaseOfferValidator(thisValidator.validator).validateOffer(
                    _loanOffer, _tokenId, thisValidator.arguments
                );
                unchecked {
                    ++i;
                }
            }
        }
    }

    /// @notice Check a signature is valid given a hash and signer.
    /// @dev Comply with IERC1271 and EIP-712.
    function _checkSignature(address _signer, bytes32 _hash, bytes calldata _signature) internal view {
        bytes32 offerHash = DOMAIN_SEPARATOR().toTypedDataHash(_hash);

        if (_signer.code.length > 0) {
            if (IERC1271(_signer).isValidSignature(offerHash, _signature) != MAGICVALUE_1271) {
                revert InvalidSignatureError();
            }
        } else {
            address recovered = offerHash.recover(_signature);
            if (_signer != recovered) {
                revert InvalidSignatureError();
            }
        }
    }

    /// @dev Check whether an offer is strictly better than a loan/source.
    function _checkStrictlyBetter(
        uint256 _offerPrincipalAmount,
        uint256 _loanPrincipalAmount,
        uint256 _offerEndTime,
        uint256 _loanEndTime,
        uint256 _offerAprBps,
        uint256 _loanAprBps,
        uint256 _offerFee
    ) internal view {
        ImprovementMinimum memory minimum = _minimum;

        /// @dev If principal is increased, then we need to check net daily interest is better.
        /// interestDelta = (_loanAprBps * _loanPrincipalAmount - _offerAprBps * _offerPrincipalAmount)
        /// We already checked that all sources are strictly better.
        /// We check that the duration is not decreased or the offer charges a fee.
        if (
            (
                (_offerPrincipalAmount - _loanPrincipalAmount > 0)
                    && (
                        (_loanAprBps * _loanPrincipalAmount - _offerAprBps * _offerPrincipalAmount).mulDivDown(
                            _PRECISION, _loanAprBps * _loanPrincipalAmount
                        ) < minimum.interest
                    )
            ) || (_offerFee > 0) || (_offerEndTime < _loanEndTime)
        ) {
            revert NotStrictlyImprovedError();
        }
    }

    /// @notice Compute domain separator for EIP-712.
    /// @return The domain separator.
    function _computeDomainSeparator() private view returns (bytes32) {
        return keccak256(
            abi.encode(
                keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                keccak256(bytes(name)),
                keccak256("2"),
                block.chainid,
                address(this)
            )
        );
    }
}

File 20 of 29 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.20;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS
    }

    /**
     * @dev The signature derives the `address(0)`.
     */
    error ECDSAInvalidSignature();

    /**
     * @dev The signature has an invalid length.
     */
    error ECDSAInvalidSignatureLength(uint256 length);

    /**
     * @dev The signature has an S value that is in the upper half order.
     */
    error ECDSAInvalidSignatureS(bytes32 s);

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
     * return address(0) without also returning an error description. Errors are documented using an enum (error type)
     * and a bytes32 providing additional information about the error.
     *
     * If no error is returned, then the address can be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
        unchecked {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError, bytes32) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS, s);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature, bytes32(0));
        }

        return (signer, RecoverError.NoError, bytes32(0));
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
     */
    function _throwError(RecoverError error, bytes32 errorArg) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert ECDSAInvalidSignature();
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert ECDSAInvalidSignatureLength(uint256(errorArg));
        } else if (error == RecoverError.InvalidSignatureS) {
            revert ECDSAInvalidSignatureS(errorArg);
        }
    }
}

File 21 of 29 : MessageHashUtils.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
 *
 * The library provides methods for generating a hash of a message that conforms to the
 * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
 * specifications.
 */
library MessageHashUtils {
    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing a bytes32 `messageHash` with
     * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
     * keccak256, although any bytes32 value can be safely used because the final digest will
     * be re-hashed.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
            mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
            digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
        }
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing an arbitrary `message` with
     * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
        return
            keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x00` (data with intended validator).
     *
     * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
     * `validator` address. Then hashing the result.
     *
     * See {ECDSA-recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(hex"19_00", validator, data));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
     *
     * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
     * `\x19\x01` and hashing the result. It corresponds to the hash signed by the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
     *
     * See {ECDSA-recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, hex"19_01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            digest := keccak256(ptr, 0x42)
        }
    }
}

File 22 of 29 : IERC1271.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC1271 standard signature validation method for
 * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
 */
interface IERC1271 {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param hash      Hash of the data to be signed
     * @param signature Signature byte array associated with _data
     */
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}

File 23 of 29 : AddressManager.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;

import "@solmate/auth/Owned.sol";
import "@solmate/utils/ReentrancyGuard.sol";

import "./InputChecker.sol";

/// @title AddressManager
/// @notice A contract that handles a whitelist of addresses and their indexes.
/// @dev We assume no more than 65535 addresses will be added to the directory.
contract AddressManager is InputChecker, Owned, ReentrancyGuard {
    event AddressAdded(address address_added);

    event AddressRemovedFromWhitelist(address address_removed);

    event AddressWhitelisted(address address_whitelisted);

    error AddressAlreadyAddedError(address _address);

    error AddressNotAddedError(address _address);

    mapping(address => uint16) private _directory;

    mapping(uint16 => address) private _inverseDirectory;

    mapping(address => bool) private _whitelist;

    uint16 private _lastAdded;

    constructor(address[] memory _original) Owned(tx.origin) {
        uint256 total = _original.length;
        for (uint256 i; i < total;) {
            _add(_original[i]);
            unchecked {
                ++i;
            }
        }
    }

    /// @notice Adds an address to the directory. If it already exists,
    ///        reverts. It assumes it's whitelisted.
    function add(address _entry) external onlyOwner returns (uint16) {
        return _add(_entry);
    }

    /// @notice Whitelist an address that's already part of the directory.
    function addToWhitelist(address _entry) external onlyOwner {
        if (_directory[_entry] == 0) {
            revert AddressNotAddedError(_entry);
        }
        _whitelist[_entry] = true;

        emit AddressWhitelisted(_entry);
    }

    /// @notice Removes an address from the whitelist. We still keep it
    ///         in the directory since this mapping is relevant across time.
    /// @param _entry The address to remove from the whitelist.
    function removeFromWhitelist(address _entry) external onlyOwner {
        _whitelist[_entry] = false;

        emit AddressRemovedFromWhitelist(_entry);
    }

    /// @param _address The address to get the index for.
    /// @return The index for a given address.
    function addressToIndex(address _address) external view returns (uint16) {
        return _directory[_address];
    }

    /// @param _index The index to get the address for.
    /// @return The address for a given index.
    function indexToAddress(uint16 _index) external view returns (address) {
        return _inverseDirectory[_index];
    }

    /// @param _entry The address to check if it's whitelisted.
    /// @return Whether the address is whitelisted or not.
    function isWhitelisted(address _entry) external view returns (bool) {
        return _whitelist[_entry];
    }

    function _add(address _entry) private returns (uint16) {
        _checkAddressNotZero(_entry);
        if (_directory[_entry] != 0) {
            revert AddressAlreadyAddedError(_entry);
        }
        unchecked {
            ++_lastAdded;
        }
        _directory[_entry] = _lastAdded;
        _inverseDirectory[_lastAdded] = _entry;
        _whitelist[_entry] = true;

        emit AddressAdded(_entry);

        return _lastAdded;
    }
}

File 24 of 29 : IBaseOfferValidator.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;

import "../loans/IBaseLoan.sol";

/// @title Interface for  Loan Offer Validators.
/// @author Florida St
/// @notice Verify the given `_offer` is valid for `_tokenId` and `_validatorData`.
interface IBaseOfferValidator {
    /// @notice Validate a loan offer.
    function validateOffer(IBaseLoan.LoanOffer calldata _offer, uint256 _tokenId, bytes calldata _validatorData)
        external;
}

File 25 of 29 : InputChecker.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;

/// @title InputChecker
/// @author Florida St
/// @notice Some basic input checks.
abstract contract InputChecker {
    error AddressZeroError();

    function _checkAddressNotZero(address _address) internal pure {
        if (_address == address(0)) {
            revert AddressZeroError();
        }
    }
}

File 26 of 29 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    uint8 private constant ADDRESS_LENGTH = 20;

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        uint256 localValue = value;
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
     * representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 27 of 29 : Owned.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

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

    /*//////////////////////////////////////////////////////////////
                            OWNERSHIP STORAGE
    //////////////////////////////////////////////////////////////*/

    address public owner;

    modifier onlyOwner() virtual {
        require(msg.sender == owner, "UNAUTHORIZED");

        _;
    }

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address _owner) {
        owner = _owner;

        emit OwnershipTransferred(address(0), _owner);
    }

    /*//////////////////////////////////////////////////////////////
                             OWNERSHIP LOGIC
    //////////////////////////////////////////////////////////////*/

    function transferOwnership(address newOwner) public virtual onlyOwner {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
}

File 28 of 29 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

File 29 of 29 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

Settings
{
  "remappings": [
    "@forge-std/=lib/forge-std/src/",
    "@openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "@solmate/=lib/solmate/src/",
    "@zora/=lib/v3/contracts/",
    "@chainlink/=lib/chainlink/contracts/src/v0.8/",
    "@delegate/=lib/delegate-registry/src/",
    "test/=test/",
    "@manifoldxyz/=lib/v3/node_modules/@manifoldxyz/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@rari-capital/=lib/v3/node_modules/@rari-capital/",
    "chainlink/=lib/chainlink/",
    "delegate-registry/=lib/delegate-registry/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "murky/=lib/delegate-registry/lib/murky/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/delegate-registry/lib/openzeppelin-contracts/contracts/",
    "solmate/=lib/solmate/src/",
    "v3/=lib/v3/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 2000
  },
  "viaIR": true,
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"loanLiquidator","type":"address"},{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"internalType":"struct IBaseLoan.ProtocolFee","name":"protocolFee","type":"tuple"},{"internalType":"address","name":"currencyManager","type":"address"},{"internalType":"address","name":"collectionManager","type":"address"},{"internalType":"uint256","name":"maxSources","type":"uint256"},{"internalType":"uint256","name":"minLockPeriod","type":"uint256"},{"internalType":"address","name":"delegateRegistry","type":"address"},{"internalType":"address","name":"flashActionContract","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressZeroError","type":"error"},{"inputs":[{"internalType":"address","name":"_lender","type":"address"},{"internalType":"uint256","name":"_offerId","type":"uint256"}],"name":"CancelledOrExecutedOfferError","type":"error"},{"inputs":[{"internalType":"address","name":"_lender","type":"address"},{"internalType":"uint256","name":"_renegotiationId","type":"uint256"}],"name":"CancelledRenegotiationOfferError","type":"error"},{"inputs":[],"name":"CannotLiquidateError","type":"error"},{"inputs":[],"name":"CollectionNotWhitelistedError","type":"error"},{"inputs":[],"name":"CurrencyNotWhitelistedError","type":"error"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[{"internalType":"uint256","name":"_expirationTime","type":"uint256"}],"name":"ExpiredOfferError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_expirationTime","type":"uint256"}],"name":"ExpiredRenegotiationOfferError","type":"error"},{"inputs":[],"name":"ExtensionNotAvailableError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_principalAmount","type":"uint256"}],"name":"InvalidAmountError","type":"error"},{"inputs":[],"name":"InvalidBorrowerError","type":"error"},{"inputs":[],"name":"InvalidCallbackError","type":"error"},{"inputs":[],"name":"InvalidCollateralIdError","type":"error"},{"inputs":[],"name":"InvalidDurationError","type":"error"},{"inputs":[],"name":"InvalidLenderError","type":"error"},{"inputs":[],"name":"InvalidLiquidationError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"}],"name":"InvalidLoanError","type":"error"},{"inputs":[],"name":"InvalidMethodError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_fraction","type":"uint256"}],"name":"InvalidProtocolFeeError","type":"error"},{"inputs":[],"name":"InvalidRenegotiationOfferError","type":"error"},{"inputs":[],"name":"InvalidSignatureError","type":"error"},{"inputs":[],"name":"InvalidValueError","type":"error"},{"inputs":[],"name":"LengthMismatchError","type":"error"},{"inputs":[{"internalType":"address","name":"_liquidator","type":"address"}],"name":"LiquidatorOnlyError","type":"error"},{"inputs":[],"name":"LoanExpiredError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_expirationTime","type":"uint256"}],"name":"LoanNotDueError","type":"error"},{"inputs":[{"internalType":"address","name":"_lender","type":"address"},{"internalType":"uint256","name":"_newMinOfferId","type":"uint256"},{"internalType":"uint256","name":"_minOfferId","type":"uint256"}],"name":"LowOfferIdError","type":"error"},{"inputs":[{"internalType":"address","name":"_lender","type":"address"},{"internalType":"uint256","name":"_newMinRenegotiationOfferId","type":"uint256"},{"internalType":"uint256","name":"_minOfferId","type":"uint256"}],"name":"LowRenegotiationOfferIdError","type":"error"},{"inputs":[],"name":"MaxCapacityExceededError","type":"error"},{"inputs":[{"internalType":"uint256","name":"minLockPeriod","type":"uint256"}],"name":"MinLockPeriodTooHighError","type":"error"},{"inputs":[{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"bytes","name":"returndata","type":"bytes"}],"name":"MulticallFailed","type":"error"},{"inputs":[],"name":"NFTNotReturnedError","type":"error"},{"inputs":[],"name":"NotStrictlyImprovedError","type":"error"},{"inputs":[],"name":"OnlyBorrowerCallableError","type":"error"},{"inputs":[],"name":"OnlyLenderCallableError","type":"error"},{"inputs":[],"name":"OnlyLenderOrBorrowerCallableError","type":"error"},{"inputs":[],"name":"PartialOfferCannotChangeDurationError","type":"error"},{"inputs":[],"name":"PartialOfferCannotHaveFeeError","type":"error"},{"inputs":[],"name":"RefinanceFullError","type":"error"},{"inputs":[{"internalType":"uint256","name":"minTimestamp","type":"uint256"}],"name":"SourceCannotBeRefinancedError","type":"error"},{"inputs":[{"internalType":"uint256","name":"sourcePrincipal","type":"uint256"},{"internalType":"uint256","name":"loanPrincipal","type":"uint256"}],"name":"TargetPrincipalTooLowError","type":"error"},{"inputs":[{"internalType":"uint256","name":"_pendingProtocolFeeSetTime","type":"uint256"}],"name":"TooEarlyError","type":"error"},{"inputs":[{"internalType":"uint256","name":"sources","type":"uint256"}],"name":"TooManySourcesError","type":"error"},{"inputs":[],"name":"ZeroDurationError","type":"error"},{"inputs":[],"name":"ZeroInterestError","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"uint256","name":"minOfferId","type":"uint256"}],"name":"AllOffersCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"uint256","name":"minRenegotiationId","type":"uint256"}],"name":"AllRenegotiationOffersCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"BorrowerOfferCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newdelegateRegistry","type":"address"}],"name":"DelegateRegistryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"Delegated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newFlashActionContract","type":"address"}],"name":"FlashActionContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"FlashActionExecuted","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"interest","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"indexed":false,"internalType":"struct IBaseLoan.ImprovementMinimum","name":"minimum","type":"tuple"}],"name":"ImprovementMinimumUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newDuration","type":"uint256"}],"name":"LiquidationAuctionDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator","type":"address"}],"name":"LiquidationContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"offerId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"indexed":false,"internalType":"struct IMultiSourceLoan.Loan","name":"loan","type":"tuple"},{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"LoanEmitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldLoanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLoanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"indexed":false,"internalType":"struct IMultiSourceLoan.Loan","name":"loan","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"_extension","type":"uint256"}],"name":"LoanExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"LoanForeclosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"LoanLiquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"renegotiationId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldLoanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLoanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"indexed":false,"internalType":"struct IMultiSourceLoan.Loan","name":"loan","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"LoanRefinanced","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalRepayment","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"LoanRepaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":false,"internalType":"address","name":"liquidator","type":"address"}],"name":"LoanSentToLiquidator","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMax","type":"uint256"}],"name":"MaxSourcesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minLockPeriod","type":"uint256"}],"name":"MinLockPeriodUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"uint256","name":"offerId","type":"uint256"}],"name":"OfferCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"indexed":false,"internalType":"struct IBaseLoan.ProtocolFee","name":"fee","type":"tuple"}],"name":"ProtocolFeePendingUpdate","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"indexed":false,"internalType":"struct IBaseLoan.ProtocolFee","name":"fee","type":"tuple"}],"name":"ProtocolFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"uint256","name":"renegotiationId","type":"uint256"}],"name":"RenegotiationOfferCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"address","name":"collection","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"RevokeDelegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"contractAdded","type":"address"},{"components":[{"internalType":"uint128","name":"buyTax","type":"uint128"},{"internalType":"uint128","name":"sellTax","type":"uint128"}],"indexed":false,"internalType":"struct WithCallbacks.Taxes","name":"tax","type":"tuple"}],"name":"WhitelistedCallbackContractAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"contractRemoved","type":"address"}],"name":"WhitelistedCallbackContractRemoved","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_UPDATE_NOTICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INITIAL_DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PROTOCOL_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_AUCTION_DURATION","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"},{"components":[{"internalType":"uint128","name":"buyTax","type":"uint128"},{"internalType":"uint128","name":"sellTax","type":"uint128"}],"internalType":"struct WithCallbacks.Taxes","name":"_tax","type":"tuple"}],"name":"addWhitelistedCallbackContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minOfferId","type":"uint256"}],"name":"cancelAllOffers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minRenegotiationId","type":"uint256"}],"name":"cancelAllRenegotiationOffers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_offerId","type":"uint256"}],"name":"cancelOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_offerIds","type":"uint256[]"}],"name":"cancelOffers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_renegotiationId","type":"uint256"}],"name":"cancelRenegotiationOffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_renegotiationIds","type":"uint256[]"}],"name":"cancelRenegotiationOffers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"loan","type":"tuple"},{"internalType":"address","name":"_delegate","type":"address"},{"internalType":"bytes32","name":"_rights","type":"bytes32"},{"internalType":"bool","name":"_value","type":"bool"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"components":[{"internalType":"uint256","name":"offerId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"capacity","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"},{"internalType":"uint256","name":"expirationTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"address","name":"validator","type":"address"},{"internalType":"bytes","name":"arguments","type":"bytes"}],"internalType":"struct IBaseLoan.OfferValidator[]","name":"validators","type":"tuple[]"}],"internalType":"struct IBaseLoan.LoanOffer","name":"offer","type":"tuple"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expirationTime","type":"uint256"},{"internalType":"bytes","name":"callbackData","type":"bytes"}],"internalType":"struct IBaseLoan.ExecutionData","name":"executionData","type":"tuple"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"bytes","name":"lenderOfferSignature","type":"bytes"},{"internalType":"bytes","name":"borrowerOfferSignature","type":"bytes"}],"internalType":"struct IMultiSourceLoan.LoanExecutionData","name":"_executionData","type":"tuple"}],"name":"emitLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"},{"internalType":"address","name":"_target","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"executeFlashAction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"},{"internalType":"uint256","name":"_extension","type":"uint256"}],"name":"extendLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getCollectionManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrencyManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDelegateRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFlashActionContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getImprovementMinimum","outputs":[{"components":[{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"interest","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"internalType":"struct IBaseLoan.ImprovementMinimum","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidationAuctionDuration","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"}],"name":"getLoanHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxSources","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMinLockPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanPrincipal","type":"uint256"}],"name":"getMinSourcePrincipal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPendingProtocolFee","outputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"internalType":"struct IBaseLoan.ProtocolFee","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPendingProtocolFeeSetTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProtocolFee","outputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"internalType":"struct IBaseLoan.ProtocolFee","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalLoansIssued","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_lender","type":"address"},{"internalType":"uint256","name":"_offerId","type":"uint256"}],"name":"getUsedCapacity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"isOfferCancelled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"isRenegotiationOfferCancelled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"isWhitelistedCallbackContract","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lenderMinRenegotiationOfferId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"}],"name":"liquidateLoan","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_loanId","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"}],"name":"loanLiquidated","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"minOfferId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"renegotiationId","type":"uint256"},{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256[]","name":"targetPrincipal","type":"uint256[]"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"},{"internalType":"uint256","name":"expirationTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"internalType":"struct IMultiSourceLoan.RenegotiationOffer","name":"_renegotiationOffer","type":"tuple"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"},{"internalType":"bytes","name":"_renegotiationOfferSignature","type":"bytes"}],"name":"refinanceFull","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"renegotiationId","type":"uint256"},{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256[]","name":"targetPrincipal","type":"uint256[]"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"},{"internalType":"uint256","name":"expirationTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"internalType":"struct IMultiSourceLoan.RenegotiationOffer","name":"_renegotiationOffer","type":"tuple"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"_loan","type":"tuple"}],"name":"refinancePartial","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_contract","type":"address"}],"name":"removeWhitelistedCallbackContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"bytes","name":"callbackData","type":"bytes"},{"internalType":"bool","name":"shouldDelegate","type":"bool"}],"internalType":"struct IMultiSourceLoan.SignableRepaymentData","name":"data","type":"tuple"},{"components":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"nftCollateralTokenId","type":"uint256"},{"internalType":"address","name":"nftCollateralAddress","type":"address"},{"internalType":"address","name":"principalAddress","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"components":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"accruedInterest","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"aprBps","type":"uint256"}],"internalType":"struct IMultiSourceLoan.Source[]","name":"source","type":"tuple[]"}],"internalType":"struct IMultiSourceLoan.Loan","name":"loan","type":"tuple"},{"internalType":"bytes","name":"borrowerSignature","type":"bytes"}],"internalType":"struct IMultiSourceLoan.LoanRepaymentData","name":"_repaymentData","type":"tuple"}],"name":"repayLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_delegate","type":"address"},{"internalType":"address","name":"_collection","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"revokeDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newDelegateRegistry","type":"address"}],"name":"setDelegateRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newFlashActionContract","type":"address"}],"name":"setFlashActionContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"__maxSources","type":"uint256"}],"name":"setMaxSources","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"__minLockPeriod","type":"uint256"}],"name":"setMinLockPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"principalAmount","type":"uint256"},{"internalType":"uint256","name":"interest","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"internalType":"struct IBaseLoan.ImprovementMinimum","name":"_newMinimum","type":"tuple"}],"name":"updateImprovementMinimum","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"_newDuration","type":"uint48"}],"name":"updateLiquidationAuctionDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ILoanLiquidator","name":"loanLiquidator","type":"address"}],"name":"updateLiquidationContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"fraction","type":"uint256"}],"internalType":"struct IBaseLoan.ProtocolFee","name":"_newProtocolFee","type":"tuple"}],"name":"updateProtocolFee","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101008060405234620005175780620064eb803803809162000022828562000538565b8339810103906101208212620005175760406200003f826200055c565b92601f19011262000517576040519162000059836200051c565b62000067602083016200055c565b83526040820151602084015262000081606083016200055c565b9262000090608084016200055c565b9160a08401519160c085015193620000ba610100620000b260e089016200055c565b97016200055c565b96604051620000c9816200051c565b601781527f474f4e44495f4d554c54495f534f555243455f4c4f414e000000000000000000602082015260016000553260018060a01b031960015416176001556040513260007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a3606081016001600160401b03811182821017620003ad57604052606460406101f4928381528260208201520152600255606460035560646004556203f48065ffffffffffff19600654161760065580519060018060401b038211620003ad578190620001a160055462000571565b601f8111620004c1575b50602090601f8311600114620004435760009262000437575b50508160011b916000199060031b1c1916176005555b620001e581620005ae565b620001f082620005ae565b6001600160a01b0390811660c0521660e052600019600b5546608052604051600554816000620002208362000571565b8083529260018116908115620004165750600114620003c3575b620002489250038262000538565b60208151910120916040519260208401907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f825260408501527fad7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a560608501524660808501523060a085015260a0845260c084019380851060018060401b03861117620003ad5760209460405251902060a052620002e581620005ae565b60068054600160301b600160d01b03191660309290921b600160301b600160d01b03169190911790558051600780546001600160a01b03199081166001600160a01b03938416179091559290910151600855601592909255601692909255601780548316938216939093179092556018805490911692909116919091179055604051615ef99081620005d28239608051816145be015260a05181818161283101526145e5015260c051818181611c2a0152612c49015260e051818181610f9d0152611cb10152f35b634e487b7160e01b600052604160045260246000fd5b50600560009081529091600080516020620064cb8339815191525b818310620003f957505090602062000248928201016200023a565b6020919350806001915483858801015201910190918392620003de565b602092506200024894915060ff191682840152151560051b8201016200023a565b015190503880620001c4565b600560009081529350600080516020620064cb83398151915291905b601f1984168510620004a5576001945083601f198116106200048b575b505050811b01600555620001da565b015160001960f88460031b161c191690553880806200047c565b818101518355602094850194600190930192909101906200045f565b90915060056000526020600020601f840160051c8101602085106200050f575b90849392915b601f830160051c82018110620004ff575050620001ab565b60008155859450600101620004e7565b5080620004e1565b600080fd5b604081019081106001600160401b03821117620003ad57604052565b601f909101601f19168101906001600160401b03821190821017620003ad57604052565b51906001600160a01b03821682036200051757565b90600182811c92168015620005a3575b60208310146200058d57565b634e487b7160e01b600052602260045260246000fd5b91607f169162000581565b6001600160a01b031615620005bf57565b6040516349431df560e11b8152600490fdfe61014080604052600436101561001457600080fd5b600090813560e01c90816306fdde0314613fc157508063150b7a0214613f5157806316ec9bc614613f195780631c31cac314613e9a5780632513a5ae146136c45780632891433d1461368c5780632c0a286e146130ef5780633644e515146130d45780633687121b146130ad57806344af681c1461308f5780634ae25a8614612fd05780634ec8198114612f745780634fd930ba14612ef75780635122afb114612e1c5780635448608614612c94578063584d158f14612c6d5780635a32989414612c29578063665458a714612be257806367562c641461285457806367e22404146128195780636ccc9dde146127625780637320ca261461270657806373b99f101461262657806373c2390c146126005780637795960b1461254d5780637b8d1a0b146125235780637d4e41ed14611a5b5780638617780b14611a3d5780638da5cb5b14611a16578063912d819e146119cf578063931c56e9146119b1578063986a7ace14611934578063995fe7201461190d578063a5a41031146118b6578063aa29dad11461188c578063ab250a3a146117cd578063ac9650d814611618578063acb1dfdb146115d6578063b1f0c786146112e7578063b8ca3b83146112ca578063b97e527a14611203578063baab19941461118e578063c2f50a7a14611170578063c833796d14611152578063cc37ef4f14611115578063cf23797d146110f7578063da144a5114611040578063db540a8a14610fc1578063dd7d978214610f7d578063e51e104014610dbd578063e621ffa914610cf4578063e69d8c4714610c8d578063ed21cbd214610ae4578063ed5a42841461095f578063ef706adf146108dc578063f0ad206a1461088d578063f2fde38b146108125763f95cc78f146102a057600080fd5b3461080f576003196040813601126108075767ffffffffffffffff90816004351161080b57610120906004353603011261080757602435908111610807576102ec9036906004016142d9565b6102f4614820565b5060808101610303815161583d565b60a46004350135106107c957506001600160a01b036103266044600435016147d2565b16330361079f5761033d8160246004350135615674565b61034c816004356004016156d8565b61010460043501356107755760646004350135918261074b57600092839081610373614774565b95839260208801519081151580610744575b610731575b5050919290600182935b6103a8608460043501600435600401614c65565b90508510156104ec576103bf8560e08a01516145a5565b5160a061041d8b6103e4896103de608460043501600435600401614c65565b90614764565b358c856103f56044600435016147d2565b916104176001600160a01b036060830151169160c08982015191015190614799565b92615537565b9296919490950151600354841515918261049a575b50506104705760019561045861045e946104526104649861045895614799565b9a614799565b99614799565b93614799565b94019390949291610394565b60046040517f25444587000000000000000000000000000000000000000000000000000000008152fd5b9091506104ad60c460043501358261497c565b612710907e068db8bac710cb295e9e1b089a027525460aa64c2f837b4a2339c0ebedfa4381118202158302156104e7570204103880610432565b600080fd5b508686926105178a886001600160a01b036060860151166105116044600435016147d2565b90615877565b60a4600435013503610470576015548211610700576001600c54019384600c5584601f1961055d6105478661426a565b95610555604051978861417b565b80875261426a565b0160005b8181106106e45750506105829161057d366004356004016153aa565b6157ed565b61058b83614598565b5261059582614598565b50600193835b6105af608460043501600435600401614c65565b9050811015610639576105d0816103de608460043501600435600401614c65565b356105de575b60010161059b565b94600180916105f18860e08701516145a5565b516105fc82886145a5565b5261060781876145a5565b50610620886103de608460043501600435600401614c65565b35604061062d83896145a5565b510152019590506105d6565b507f9190f728135a4ce2ee0c985cc2c2ce55169f358d6a979e6d48add4743482621c848460e085015261066b84615d37565b83825260146020526040822055602460043501358152806040812055604051809160043560040135825260246004350135602083015284604083015260a060608301526106bb60a08301876143c9565b9060808301520390a16106e060405192839283526040602084015260408301906143c9565b0390f35b60209192506106f161490e565b82828801015201908691610561565b602482604051907fc56ee9910000000000000000000000000000000000000000000000000000000082526004820152fd5b61073c9294506149e6565b91388061038a565b5085610385565b60046040517fe791b76b000000000000000000000000000000000000000000000000000000008152fd5b60046040517f32259e57000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3f9b6963000000000000000000000000000000000000000000000000000000008152fd5b60449051604051907f2e67bbe300000000000000000000000000000000000000000000000000000000825260a4600435013560048301526024820152fd5b5080fd5b8280fd5b80fd5b503461080f57602060031936011261080f5773ffffffffffffffffffffffffffffffffffffffff196108426141e6565b600154906001600160a01b039061085c82841633146144fe565b169182911617600155337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b503461080f578060031936011261080f57604080516108ab816140da565b828152826020820152015260606108c0614740565b6040805191805183526020810151602084015201516040820152f35b503461080f57602060031936011261080f577f899cab278284ae4a91172caa0943607a0bcb19766254c3ebe1139be00650b102610959600435338452600e6020526040842081855260205260408420600160ff198254161790556040519182913383602090939291936001600160a01b0360408201951681520152565b0390a180f35b503461080f57606060031936011261080f576109796141e6565b60407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc360112610807576001600160a01b036109ba816001541633146144fe565b6109c3826147e6565b6fffffffffffffffffffffffffffffffff9161271080846109e26158b9565b1611908115610ad1575b50610aa7571690818352601260205260408320600160ff1982541617905560136020526040832081610a1c6158b9565b16815490857fffffffffffffffffffffffffffffffff00000000000000000000000000000000610a4a6158d8565b60801b169216171790556040519182526024358181168091036104e75760208301526044359081168091036104e7578160609160407f58b1f18b665c4c540aef11f1c3dec18c43824ab0e2e8057a7a9bc6ad3653be24940152a180f35b60046040517fb53b7b15000000000000000000000000000000000000000000000000000000008152fd5b905083610adc6158d8565b1611386109ec565b503461080f5760031960a081360112610807576004356024359167ffffffffffffffff8311610c895761010083600401918436030112610c8957610b26614212565b90608435918215158093036104e757610b47610b4236846142d9565b615d37565b9184875260209260148452604088205403610c715790610b71916001600160a01b039283916147d2565b163303610c47578160a484928983601754166024610b9160448d016147d2565b9b604051988997889663b18e2bbb60e01b8852169d8e600488015216828601520135604484015260643560648401528860848401525af18015610c3c57610c0d575b50907f696a99e04dd3d11d9ec4fe68917db2c142d3a55e1bff074c34ad74c1d515547d93606093926040519384528301526040820152a180f35b9080939291813d8311610c35575b610c25818361417b565b810103126104e757909138610bd3565b503d610c1b565b6040513d88823e3d90fd5b60046040517fdbd91fb4000000000000000000000000000000000000000000000000000000008152fd5b6024856040519063047e261360e21b82526004820152fd5b8380fd5b503461080f578060031936011261080f5760006020604051610cae81614143565b82815201526106e0604051610cc281614143565b6009546001600160a01b0316808252600a54602092830190815260408051928352905192820192909252918291820190565b503461080f57610d03366144c7565b6001600160a01b0360065460301c16803303610d8c5750610b42610d289136906142d9565b8183526014602052604083205403610d74577faf91ffe368225045d42f7c2b4b90feb438133ac1e375e73bc9066652b70a1d776020604051838152a18152601460205280604081205580f35b6024906040519063047e261360e21b82526004820152fd5b602490604051907f8afaa9b50000000000000000000000000000000000000000000000000000000082526004820152fd5b503461080f57606060031936011261080f576004359060243567ffffffffffffffff811161080757610df39036906004016142d9565b91604435610dff614820565b50610e0a8483615674565b60e0840191600183515111610f53576080610e258451614598565b51015192610e5d60a087015194610e57610e4e82610e4960c08c01998a5190614799565b61497c565b601654906149e6565b90614799565b428111610f2257506020610e796001600160a01b039251614598565b51015116330361079f5782610eb0837f611e850fd4909c086538320a1f747a380fdaff020cc4e6649b9b4dd0dbd383219551614799565b90526001600c54019384600c55610ec686615d37565b858252601460205260408220558181526040812055604051918291825284602083015260806040830152610efd60808301876143c9565b9060608301520390a16106e060405192839283526040602084015260408301906143c9565b602490604051907f4d321dfb0000000000000000000000000000000000000000000000000000000082526004820152fd5b60046040517fcd23b291000000000000000000000000000000000000000000000000000000008152fd5b503461080f578060031936011261080f5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461080f57602060031936011261080f577faed358a2bd4ca37fb72362cad5ae66b0b49a2a2031f9474a656e47b1f4afc4196020610ffe6141e6565b6001600160a01b0390611016826001541633146144fe565b168073ffffffffffffffffffffffffffffffffffffffff196018541617601855604051908152a180f35b503461080f57602060031936011261080f5760043533825260116020526040822054818110156110b55750338083526011602090815260408085208490558051928352908201929092527f5009c2e03e218242def2d459330d4f8b8b7eb7289ca08c47489aeef06672c63c9181908101610959565b6040517f997e86d600000000000000000000000000000000000000000000000000000000815233600482015260248101929092526044820152606490fd5b0390fd5b503461080f578060031936011261080f576020601654604051908152f35b503461080f57602060031936011261080f5760ff60406020926001600160a01b0361113e6141e6565b168152601284522054166040519015158152f35b503461080f578060031936011261080f576020601554604051908152f35b503461080f578060031936011261080f576020604051620151808152f35b503461080f57606060031936011261080f576111b66001600160a01b036001541633146144fe565b7f36e7ba2b68a5f2a92d31c35c23faa8f6fb2559d4406bb30eef08bdbab0d6f10960606004358060025560243580600355604435908160045560405192835260208301526040820152a180f35b503461080f57602060031936011261080f5760043565ffffffffffff81168091036108075761123e6001600160a01b036001541633146144fe565b6201518081106112a0576020817f9b0306e96c09148e30f9acd9a1ebc2c7cb1bc0348adbf320530ead875f78292c927fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000006006541617600655604051908152a180f35b60046040517fa922f92c000000000000000000000000000000000000000000000000000000008152fd5b503461080f578060031936011261080f5760206040516109c48152f35b503461080f576003196080813601126108075767ffffffffffffffff6004356024358281116115d257610100816004019482360301126115d257611329614212565b926064359081116115ce5761134290369060040161423c565b919094611352610b4236836142d9565b84885260146020526040882054036115b657611378906001600160a01b039182916147d2565b163303610c475760448201878261138e836147d2565b16936024846018541691013594803b1561080b576040516323b872dd60e01b81523060048201526001600160a01b039290921660248301526044820186905282908290606490829084905af180156115ab57611597575b50508160185416886113f6836147d2565b823b1561080757899885938484809461145b604051988996879586947fee534a3f0000000000000000000000000000000000000000000000000000000086521660048501528d6024850152169e8f6044840152608060648401528d6084840191614c9b565b03925af191821561158a57849261156f575b505061147a6020926147d2565b16926024604051809581937f6352211e00000000000000000000000000000000000000000000000000000000835260048301525afa918215611564578792611534575b503091160361150a577f13ab7606a034e1d4320c1129442ffa929fe2789f8cd0d03d147ff4f4707567a5936109599160405194859485526020850152606060408501526060840191614c9b565b60046040517f807a2b8f000000000000000000000000000000000000000000000000000000008152fd5b61155691925060203d811161155d575b61154e818361417b565b810190615518565b90386114bd565b503d611544565b6040513d89823e3d90fd5b61157a9192506140f6565b6115865781883861146d565b8780fd5b50604051903d90823e3d90fd5b6115a0906140f6565b6115865787386113e5565b6040513d84823e3d90fd5b6024846040519063047e261360e21b82526004820152fd5b8580fd5b8480fd5b503461080f57604060031936011261080f5760406020916001600160a01b036115fd6141e6565b168152600d8352818120602435825283522054604051908152f35b506020806003193601126108075760043567ffffffffffffffff811161080b5761164783913690600401614496565b6116508161426a565b9161165e604051938461417b565b818352601f1961166d8361426a565b01845b8181106117be575050835b8281106116ff5750505060405191838301848452825180915260408401948060408360051b870101940192955b8287106116b55785850386f35b9091929382806116ef837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08a6001960301865288516141c1565b96019201960195929190926116a8565b84806117138360059998991b850185614549565b9081604051928392833781018381520390305af43d156117b7573d6117378161457c565b90611745604051928361417b565b81523d888883013e5b61175883876145a5565b52156117695760010194939461167b565b61177381856145a5565b51906110f36040519283927f6cf73d1400000000000000000000000000000000000000000000000000000000845260048401526040602484015260448301906141c1565b606061174e565b60608582018801528601611670565b503461080f576020806003193601126108075760043567ffffffffffffffff811161080b57611800903690600401614496565b835b81811061180d578480f35b611818818385614764565b3590338652601085526040862082875285527fa39a039982552bc613a549c5a31d7c9a911b4e57c7da274435fca0e156c9c34061188360408820936001948560ff198254161790556040519182913383602090939291936001600160a01b0360408201951681520152565b0390a101611802565b503461080f57602060031936011261080f5760406020916004358152601483522054604051908152f35b503461080f578060031936011261080f57600060206040516118d781614143565b82815201526106e06118e7614774565b6040519182918291909160208060408301946001600160a01b0381511684520151910152565b503461080f578060031936011261080f5760206001600160a01b0360175416604051908152f35b503461080f57602060031936011261080f577fa39a039982552bc613a549c5a31d7c9a911b4e57c7da274435fca0e156c9c34061095960043533845260106020526040842081855260205260408420600160ff198254161790556040519182913383602090939291936001600160a01b0360408201951681520152565b503461080f578060031936011261080f57602060405162278d008152f35b503461080f57604060031936011261080f5760ff60406020926001600160a01b036119f86141e6565b16815260108452818120602435825284522054166040519015158152f35b503461080f578060031936011261080f5760206001600160a01b0360015416604051908152f35b503461080f578060031936011261080f576020600c54604051908152f35b503461080f576003199060208236011261080f576004359167ffffffffffffffff83116108075760a090833603011261080f57611a96614820565b50611aa4600182541461485d565b60028155611ab4602483016147d2565b611ac0604484016147d2565b91611ad7611ad160048601806148a8565b806148db565b936001600160a01b03611aec602087016147d2565b1661251c57835b611b0060048301806148a8565b94611b116064840184600401614549565b9690611b236084860186600401614549565b611b386020611b3286806148db565b016147d2565b91611b486060611b3287806148db565b93611b5386806148db565b9b8c916001600160a01b038d1633036124f8575b5050506001600160a01b03871633036123b9575b50506101408901358042116123885750606083013580421161238857506001600160a01b0384168752600e602052604087208935885260205260ff6040882054168015612368575b61232757610100890135806040850135116122ed5750611be560e08a016147d2565b6001600160a01b03604051917f3af32abf0000000000000000000000000000000000000000000000000000000083521660048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156122995788916122ce575b50156122a457611c6c60a08a016147d2565b6001600160a01b03604051917f3af32abf0000000000000000000000000000000000000000000000000000000083521660048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa90811561229957889161226a575b5015612240576001600160a01b0316801515908161222c575b50612202576001600160a01b031680151590816121ee575b506121c4576101608701351561219a57610120870135156121705760808701358015159081612139575b5061210f5760209687611d5192013590614cbc565b6001600c54019485600c556040611d6e85600401866004016148a8565b013593611d79614940565b604051611d858161410a565b8881526001600160a01b0384168a8201528660408201528760608201524260808201526101208b013560a0820152611dbc82614598565b52611dc681614598565b506001600160a01b0389611ddd60048501806148a8565b0135818c611df960e0611df260a084016147d2565b92016147d2565b926040519d8e611e0881614126565b848c16815201521660408c01521660608a01528560808a01524260a08a01526101608a013560c08a015260e0890152611e4088615d37565b87875260146020526040872055610100890135856000190460408b0135118602158102156104e757899086604083013502908082049106151501611e82614774565b611e8e60e084016147d2565b91602082019282878551611ea290856149e6565b90611eac93615877565b611eb860e085016147d2565b6001600160a01b03168689611ecd848d61497c565b91611ed793614a09565b611ee460048601806148a8565b60808101611ef191614549565b3690611efc92614989565b90611f07918d6158f7565b6fffffffffffffffffffffffffffffffff168061209d575b505050611f466001600160a01b03611f3b60a0602094016147d2565b1692600401806148a8565b0135813b15612099576040516323b872dd60e01b81526001600160a01b038616600482015230602482015260448101919091529086908290606490829084905af18015610c3c57612085575b50877f2b81fc2f914e99b38053262ace9e49b2f47f712ef594be1ea005de4f3baeec9d88886080946001600160a01b0360019b9c9d359881611fec60405196879687528c602088015260c0604088015260c08701906143c9565b931660608501521686830152604085013560a08301520390a1013515612055576001600160a01b03168352600d6020526040832090835260205261203560408320918254614799565b90555b556106e060405192839283526040602084015260408301906143c9565b6001600160a01b03919250168252600e60205260408220908252602052604081208260ff19825416179055612038565b61208f86916140f6565b6115d25738611f92565b8680fd5b6120b56120ad6120dd928b6149e6565b9351846149e6565b9286896120d7866001600160a01b036120d060e08b016147d2565b169461497c565b92614a09565b868215611f1f576001600160a01b03806120fc60e061210697016147d2565b1692511691614a09565b88388086611f1f565b60046040517fdab69d5e000000000000000000000000000000000000000000000000000000008152fd5b90506001600160a01b0383168652600d602052604086208835875260205261216960408301356040882054614799565b1138611d3c565b60046040517f20dac71b000000000000000000000000000000000000000000000000000000008152fd5b60046040517fecd83f88000000000000000000000000000000000000000000000000000000008152fd5b60046040517f1548268b000000000000000000000000000000000000000000000000000000008152fd5b90506001600160a01b038416141538611d12565b60046040517f62257e48000000000000000000000000000000000000000000000000000000008152fd5b90506001600160a01b038816141538611cfa565b60046040517ff1a638e6000000000000000000000000000000000000000000000000000000008152fd5b61228c915060203d602011612292575b612284818361417b565b810190614c4d565b38611ce1565b503d61227a565b6040513d8a823e3d90fd5b60046040517fa212b5d3000000000000000000000000000000000000000000000000000000008152fd5b6122e7915060203d60201161229257612284818361417b565b38611c5a565b8360449160408051927f111c5f29000000000000000000000000000000000000000000000000000000008452013560048301526024820152fd5b6040517ffc4a5c250000000000000000000000000000000000000000000000000000000081526001600160a01b038516600482015289356024820152604490fd5b506001600160a01b0384168752600f602052604087205489351115611bc3565b602490604051907f5929d5bb0000000000000000000000000000000000000000000000000000000082526004820152fd5b60a0853603126124f4576040518060a081011067ffffffffffffffff60a0830111176124de5760a0810160405267ffffffffffffffff8635116124da576124033687358801614b04565b815260208601356020820152604086013560408201526060860135606082015267ffffffffffffffff6080870135116124da57906124d3929161244c3660808901358901614a99565b6080820190815261245d8251615b6f565b9160208101519160606040830151920151905160208151910120916040519360208501957f7e90717662b6dd110797922ef6d6701d92bfd4164783966933e092ea21a74c5a875260408601526060850152608084015260a083015260c082015260c081526124ca8161415f565b51902088615095565b3880611b7b565b8980fd5b634e487b7160e01b600052604160045260246000fd5b8880fd5b61250e612509612514943690614b04565b615b6f565b86615095565b8a3880611b67565b8293611af3565b503461080f578060031936011261080f5760206001600160a01b0360065460301c16604051908152f35b503461080f57602060031936011261080f57600435338252600f6020526040822054818110156125c2575033808352600f602090815260408085208490558051928352908201929092527feaacd06746bfd850a83bbe929f94f4e1413f9aac82dc76e4331418aaf8bba4689181908101610959565b6040517f570f5b7800000000000000000000000000000000000000000000000000000000815233600482015260248101929092526044820152606490fd5b503461080f578060031936011261080f57602065ffffffffffff60065416604051908152f35b503461080f578060031936011261080f576001600160a01b0361264e816001541633146144fe565b600b5462278d0081018082116126f25742106126c1575060407fb3c1d38dbdc9199d0ce01f386d70e29014ed4af7af1d321ca6641a91f4b4dc0c91600954168073ffffffffffffffffffffffffffffffffffffffff196007541617600755600a548060085582519182526020820152a180f35b602490604051907f5c269db50000000000000000000000000000000000000000000000000000000082526004820152fd5b602484634e487b7160e01b81526011600452fd5b503461080f57602060031936011261080f577fe818ff6972bf8970ca14a893539e57452588b197b76e6afe29c40a31b3363c6160206004356127546001600160a01b036001541633146144fe565b80601655604051908152a180f35b503461080f57602060031936011261080f576004356001600160a01b03808216808303610c89577f0656dc0fa3e59bca53331c14b87a4778ffac8c184915152fdd0f103838a4f378926127bc6020936001541633146144fe565b6127c5826147e6565b7fffffffffffff0000000000000000000000000000000000000000ffffffffffff79ffffffffffffffffffffffffffffffffffffffff0000000000006006549260301b16911617600655604051908152a180f35b503461080f578060031936011261080f5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461080f57612863366144c7565b91612871600182541461485d565b60028155612882610b4236856142d9565b9180825260209260148452604083205403610d74576128a960c085013560a0860135614799565b936001600160a01b0394856128c0604084016147d2565b1690428111612bb1575060609560e083019060016128de83866154e2565b9050146000146129d257506128f390836154e2565b156129be578561290391016147d2565b91813b156115d2576040516323b872dd60e01b81523060048201526001600160a01b03939093166024840152850135604483015283908290606490829084905af180156129b35761299f575b50906001917f5985c1ea46d5ed2be4837f88c0563e18ed022c3bfe10dee15f6ce74daf897d6384604051838152a18152601483528060408120555b556106e06040519282849384528301906141c1565b6129a983916140f6565b610807573861294f565b6040513d85823e3d90fd5b602485634e487b7160e01b81526032600452fd5b96919050600654918684013592823b15612099576040516323b872dd60e01b815230600482015260309190911c89166001600160a01b0316602482015260448101849052868160648183875af180156115645791899188959493612b94575b5060c49291612a45869260065498016147d2565b968160405198899788967f762d7d740000000000000000000000000000000000000000000000000000000088528c60048901526024880152604487015216606485015265ffffffffffff811660848501523360a485015260301c165af19081156129b3578391612af0575b506040600193927f0c0f58e3bcc7b4b6f90dac7e83ad948f2dfe00aff9393c51aa2181fa08fb94e1929660065460301c16825191825286820152a161298a565b90503d8084833e612b01818361417b565b8101908481830312610c895780519067ffffffffffffffff82116115d2570181601f82011215610c8957805190612b378261457c565b92612b45604051948561417b565b8284528683830101116115d2579282612b8a6040936001979689807f0c0f58e3bcc7b4b6f90dac7e83ad948f2dfe00aff9393c51aa2181fa08fb94e19801910161419e565b9250929350612ab0565b612ba29193949592506140f6565b6115ce57908786939238612a31565b602490604051907fb73a6a130000000000000000000000000000000000000000000000000000000082526004820152fd5b503461080f57604060031936011261080f5760ff60406020926001600160a01b03612c0b6141e6565b168152600e8452818120602435825284522054166040519015158152f35b503461080f578060031936011261080f5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461080f578060031936011261080f5760206001600160a01b0360185416604051908152f35b503461080f57606060031936011261080f57612cae6141e6565b612cb66141fc565b604435916001600160a01b0380831690604051917f6352211e0000000000000000000000000000000000000000000000000000000083528560048401526020928381602481855afa908115612299578891612dff575b5082163014612dd557829060a483601754169389604051958694859363b18e2bbb60e01b85528a16600485015260248401528a6044840152600060648401528160848401525af18015610c3c57612dac575b5050604080516001600160a01b0392831681529290911660208301528101919091527f6ebfd923dee76d83a8a77f3f29fe17755e74e5322d953678e35381cb63709199908060608101610959565b813d8311612dce575b612dbf818361417b565b810103126104e7573880612d5e565b503d612db5565b60046040517f64436547000000000000000000000000000000000000000000000000000000008152fd5b612e169150843d861161155d5761154e818361417b565b38612d0c565b503461080f57604060031936011261080f576001600160a01b03612e45816001541633146144fe565b602435906109c48211612ec6577f160fef22fef07b45037a807beef2c89408a81168d1055cf34024c85396b882af91604091612e87612e826147bc565b6147e6565b612e8f6147bc565b16908173ffffffffffffffffffffffffffffffffffffffff19600954161760095580600a5542600b5582519182526020820152a180f35b602482604051907f8b902c630000000000000000000000000000000000000000000000000000000082526004820152fd5b503461080f57602060031936011261080f577f924773930b2679ab8bf328330b211bedfb8f917551fd856f536bec008d17f9a66020612f346141e6565b6001600160a01b0390612f4c826001541633146144fe565b16808452601282526040842060ff19815416905560138252836040812055604051908152a180f35b503461080f57602060031936011261080f577f759c526639cdac7b56455b1bb335d0edb0ea487d414f1fa706cedf5532f09fc46020600435612fc26001600160a01b036001541633146144fe565b80601555604051908152a180f35b503461080f576020806003193601126108075760043567ffffffffffffffff811161080b57613003903690600401614496565b835b818110613010578480f35b61301b818385614764565b3590338652600e85526040862082875285527f899cab278284ae4a91172caa0943607a0bcb19766254c3ebe1139be00650b10261308660408820936001948560ff198254161790556040519182913383602090939291936001600160a01b0360408201951681520152565b0390a101613005565b503461080f578060031936011261080f576020600b54604051908152f35b503461080f57602060031936011261080f5760206130cc60043561583d565b604051908152f35b503461080f578060031936011261080f5760206130cc6145b9565b503461080f576003199060208236011261080f5767ffffffffffffffff6004351161080f57606060043536039283011261080f57613130600182541461485d565b600281556131426004803501806154cd565b35907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefd60246004350135930183121561080f576001600160a01b0361318c600485813501016147d2565b16330361356a575b6131ad6131a736600486813501016142d9565b83615674565b60406131bd6004803501806154cd565b01358015158103610807576134b9575b6001600160a01b036131e560448560043501016147d2565b166131f5600485813501016147d2565b813b1561080b576040516323b872dd60e01b8152306004808301919091526001600160a01b0392909216602480830191909152913586019091013560448201529082908290606490829084905af180156115ab576134aa575b5061328361326d6132636004803501806154cd565b6020810190614549565b9061327e36600488813501016142d9565b615a38565b9261328c614774565b93602085015115159183906001600160a01b036132af60648360043501016147d2565b16859386945b6132c960048035860160e4810191016154e2565b9050861015613425576132e660048035860160e4810191016154e2565b87101561340f576132fd90369060c0890201614282565b9087613329604084015161332360a086015161331d60808801514261497c565b906154ba565b906149c0565b8c61334a6fffffffffffffffffffffffffffffffff871660408701516149e6565b918c918d946133b5575b508593610e496133ad969461338261338295610e4961338796613382604060019e015160608d015190614799565b614799565b80936001600160a01b0360206133a260048d813501016147d2565b920151169089614a09565b9501946132b5565b6001969450613382925099610e496133ad969461338284610e49613387969f6133f06133e960206133fb93015180956149e6565b93866149e6565b96610e578885614799565b9f965050955050949650509395508e613354565b634e487b7160e01b600052603260045260246000fd5b888486858d8b7fd9cfbcfb9ae5d65b8d34072de4575f602ee25cf3715bc05c291701fe9e258f9a60608f98604051908982526020820152866040820152a161347e575b8585815260146020528060408120556001815580f35b6001600160a01b0361349860046134a196813501016147d2565b91511691614a09565b82808080613468565b6134b3906140f6565b3861324e565b6001600160a01b036017541660206001600160a01b0360a46134e0600488813501016147d2565b856134f160448a60043501016147d2565b9584604051978896879563b18e2bbb60e01b875216600486015216602484015260248a6004350101356044840152816064840152600160848401525af180156115ab5761353f575b506131cd565b602090813d8311613563575b613555818361417b565b8101031261080f5738613539565b503d61354b565b613579600484813501016147d2565b6135876004803501806154cd565b60608136031261080b576040519061359e826140da565b80358252602081013567ffffffffffffffff81116115d2576135c39036908301614a99565b6020830181905291604082013580151590036115d2579060409182820135838201525192602081519101208251937f41277b3c1cbe08ea7bbdd10a13f24dc956f3936bf46526f904c73697d9958e0c6020860152838501526060840152013515156080820152608081528060a081011067ffffffffffffffff60a0830111176136785760a0810160405280516020909101206136739161366b60048035604481019101614549565b929091615095565b613194565b602483634e487b7160e01b81526041600452fd5b503461080f57602060031936011261080f5760406020916001600160a01b036136b36141e6565b168152600f83522054604051908152f35b503461080f576003196060813601126108075767ffffffffffffffff6004351161080757610120906004353603011261080f5760243567ffffffffffffffff8111610807576137179036906004016142d9565b6101205260443567ffffffffffffffff81116108075761373b90369060040161423c565b60e052613746614820565b50819061375c6101205160246004350135615674565b61376e610120516004356004016156d8565b6001600160a01b036137846044600435016147d2565b16331460c052826080528283608052839261379d614774565b610100528490602061010051015180151580613e8b575b613e72575b506001949294918660a0525b6137d9608460043501600435600401614c65565b905060a051101561391d5760a06137f8815160e06101205101516145a5565b519161385d61381c613814608460043501600435600401614c65565b855191614764565b3561382b6044600435016147d2565b906001600160a01b0360606101205101511691868661012051015193610417610100519560c061012051015190614799565b9394929991959096015160035460c0519160c051613913575b826138c2575b50506104705761389f61045e946138996138b0986138a894614799565b9c614799565b98608051614799565b608052614799565b60a080516001019052929492916137c5565b9091506138d560c460043501358261497c565b612710907e068db8bac710cb295e9e1b089a027525460aa64c2f837b4a2339c0ebedfa43811182021583021561390f57020410388061387c565b8f80fd5b8615159250613876565b8692938561394b88936001600160a01b036060610120510151169061010051916105116044600435016147d2565b60016080519611613e485760c05115613c83575060a4600435013590613978426101046004350135614799565b6139a361399460c061012051015160a061012051015190614799565b9260806101205101519061549a565b6139ab614740565b856139b6818761497c565b15159586613c0c575b505050508215613bfc575b8215613bf2575b5050613bc8575b6139ee6064600435013560a4600435013561497c565b91613bb2575b81811115613b605790613a39916001600160a01b03606061012051015116916120d76001600160a01b0361012051511691613a336044600435016147d2565b9361497c565b6001600c54019182600c55613a6083613a50614940565b9261057d366004356004016153aa565b613a6982614598565b52613a7381614598565b5060e0610120510152613a9e613a9060a06101205101514261497c565b610104600435013590614799565b60c061012051015260a460043501356080610120510152613ac161012051615d37565b8282526014602052604082205560246004350135815260408120557f9190f728135a4ce2ee0c985cc2c2ce55169f358d6a979e6d48add4743482621c60405160043560040135815260246004350135602082015282604082015260a0606082015280613b3360a08201610120516143c9565b6064600435013560808301520390a160405190815260406020820152806106e060408201610120516143c9565b818110613b6f575b5050613a39565b613bab916001600160a01b03606061012051015116916120d7613b966044600435016147d2565b916001600160a01b036101205151169361497c565b8280613b68565b9250613bc1906080519061497c565b81926139f4565b60046040517fc9031bb4000000000000000000000000000000000000000000000000000000008152fd5b10905085806139d1565b60043560640135151592506139ca565b83949650613c3890613c32613c2584613c3e96976154ba565b9160c460043501356154ba565b9061497c565b936154ba565b907e068db8bac710cb295e9e1b089a027525460aa64c2f837b4a2339c0ebedfa43831161271002158202156115865760206127109101519202041091878085816139bf565b9250506001600160a01b03610120515116331415600014613cc85760046040517fd1988c2d000000000000000000000000000000000000000000000000000000008152fd5b600191613cd96044600435016147d2565b90613ce9366004356004016153aa565b9160608686805b613de5575b50508351936020810151916001600160a01b03604083015116906060830151906020815191012060a08401519160c08501519361010060e0870151960151966040519a7fdb613ea3383336cd787d929ccfc21ab7cd87bf1d588780c80ce5f970dd79c34860208d015260408c015260608b015260808a015260a089015260c088015260e0870152610100860152610120850152610140908185015283528261016081011067ffffffffffffffff61016085011117613dd157908291610160613dcc940160405260e051926020815191012090615095565b6139d8565b602486634e487b7160e01b81526041600452fd5b909160808601518051841015613e3f5790613e366040613e068686956145a5565b519281519381613e2086935180926020808701910161419e565b820190602082015203602081018452018261417b565b92019080613cf0565b50915080613cf5565b60046040517fbf85e051000000000000000000000000000000000000000000000000000000008152fd5b613e84919250606460043501356149e6565b90386137b9565b506064600435013515156137b4565b503461080f57602060031936011261080f577f47487988ae8c1417174bb9698afe3a54ef641a5255053a53eabc4d188fe6875d6020613ed76141e6565b6001600160a01b0390613eef826001541633146144fe565b168073ffffffffffffffffffffffffffffffffffffffff196017541617601755604051908152a180f35b503461080f57602060031936011261080f5760406020916001600160a01b03613f406141e6565b168152601183522054604051908152f35b503461080f57608060031936011261080f57613f6b6141e6565b50613f746141fc565b5060643567ffffffffffffffff811161080757613f9590369060040161423c565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b823461080f578060031936011261080f576005548282613fe0836140a0565b9283835260209460019186838216918260001461407d575050600114614023575b505061400f9250038361417b565b6106e06040519282849384528301906141c1565b90859250600582527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db05b85831061406557505061400f93508201018580614001565b8054838901850152879450869390920191810161404d565b9150935061400f95925060ff1991501682840152151560051b8201018580614001565b90600182811c921680156140d0575b60208310146140ba57565b634e487b7160e01b600052602260045260246000fd5b91607f16916140af565b6060810190811067ffffffffffffffff8211176124de57604052565b67ffffffffffffffff81116124de57604052565b60c0810190811067ffffffffffffffff8211176124de57604052565b610100810190811067ffffffffffffffff8211176124de57604052565b6040810190811067ffffffffffffffff8211176124de57604052565b60e0810190811067ffffffffffffffff8211176124de57604052565b90601f601f19910116810190811067ffffffffffffffff8211176124de57604052565b60005b8381106141b15750506000910152565b81810151838201526020016141a1565b90601f19601f6020936141df8151809281875287808801910161419e565b0116010190565b600435906001600160a01b03821682036104e757565b602435906001600160a01b03821682036104e757565b604435906001600160a01b03821682036104e757565b35906001600160a01b03821682036104e757565b9181601f840112156104e75782359167ffffffffffffffff83116104e757602083818601950101116104e757565b67ffffffffffffffff81116124de5760051b60200190565b91908260c09103126104e75760405161429a8161410a565b60a0808294803584526142af60208201614228565b60208501526040810135604085015260608101356060850152608081013560808501520135910152565b9190610100838203126104e7576040516142f281614126565b80936142fd81614228565b82526020808201358184015261431560408301614228565b604084015261432660608301614228565b60608401526080820135608084015260a082013560a084015260c091828101358385015260e08101359067ffffffffffffffff82116104e757019084601f830112156104e75781356143778161426a565b95614385604051978861417b565b81875282858189019302850101938185116104e7578301915b8483106143b15750505050505060e00152565b8386916143be8486614282565b81520192019161439e565b610100918281016001600160a01b03918284511681526020908185015182820152604094848682015116868301526060918583830151168382015260809586830151878301528461012060a093848601518582015260e060c096878101518884015201519b60e08201528b51809952019901966000945b878610614454575050505050505050505090565b885180518c528088015184168c890152808b01518c8c0152808201518c830152808301518c8401528401518b8501529984019997860197600190950194614440565b9181601f840112156104e75782359167ffffffffffffffff83116104e7576020808501948460051b0101116104e757565b906003196040818401126104e757600435926024359167ffffffffffffffff83116104e75782610100920301126104e75760040190565b1561450557565b606460405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152fd5b903590601e19813603018212156104e7570180359067ffffffffffffffff82116104e7576020019181360383136104e757565b67ffffffffffffffff81116124de57601f01601f191660200190565b80511561340f5760200190565b805182101561340f5760209160051b010190565b6000467f00000000000000000000000000000000000000000000000000000000000000000361460757507f000000000000000000000000000000000000000000000000000000000000000090565b6040516005549190818161461a856140a0565b918282526020958683019560019188838216918260001461471f5750506001146146c5575b505061464d9250038261417b565b51902090604051908101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260408201527fad7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a560608201524660808201523060a082015260a081526146bf8161410a565b51902090565b90879250600582527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db05b85831061470757505061464d9350820101388061463f565b805483880185015286945088939092019181016146ef565b9150935061464d95925060ff199150168652151560051b820101388061463f565b6040519061474d826140da565b600254825260035460208301526004546040830152565b919081101561340f5760051b0190565b6040519061478182614143565b6007546001600160a01b031682526008546020830152565b919082018092116147a657565b634e487b7160e01b600052601160045260246000fd5b6004356001600160a01b03811681036104e75790565b356001600160a01b03811681036104e75790565b6001600160a01b0316156147f657565b60046040517f92863bea000000000000000000000000000000000000000000000000000000008152fd5b6040519061482d82614126565b606060e083600080825280602083015280604083015280848301528060808301528060a083015260c08201520152565b1561486457565b606460405162461bcd60e51b815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61813603018212156104e7570190565b9035907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe61813603018212156104e7570190565b6040519061491b8261410a565b8160a06000918281528260208201528260408201528260608201528260808201520152565b6040519061494d82614143565b600182528160005b602090818110156149775760209161496b61490e565b90828501015201614955565b505050565b919082039182116147a657565b9291926149958261457c565b916149a3604051938461417b565b8294818452818301116104e7578281602093846000960137010152565b9064496cebb80091816000190481118202158302156104e7570290808204910615150190565b9061271091816000190481118202158302156104e7570290808204910615150190565b9160008093602095606494604051946323b872dd60e01b86526001600160a01b03809216600487015216602485015260448401525af13d15601f3d1160016000511416171615614a5557565b606460405162461bcd60e51b815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b9080601f830112156104e757816020614ab493359101614989565b90565b91906040838203126104e75760405190614ad082614143565b8193614adb81614228565b835260208101359167ffffffffffffffff83116104e757602092614aff9201614a99565b910152565b9190916101a09081818503126104e75760405167ffffffffffffffff928101838111828210176124de57604052809482358252602090614b45828501614228565b8284015260408401356040840152614b5f60608501614228565b606084015260808401356080840152614b7a60a08501614228565b60a084015260c084013560c0840152614b9560e08501614228565b60e084015261010080850135908401526101208085013590840152610140808501359084015261016080850135908401526101809384810135908682116104e7570181601f820112156104e7578035614bed8161426a565b96614bfb604051988961417b565b818852848089019260051b840101938085116104e757858401925b858410614c2857505050505050500152565b83358381116104e7578791614c42848480948a0101614ab7565b815201930192614c16565b908160209103126104e7575180151581036104e75790565b903590601e19813603018212156104e7570180359067ffffffffffffffff82116104e757602001918160051b360383136104e757565b601f8260209493601f19938186528686013760008582860101520116010190565b60c0810135908115614cfa575003614cd057565b60046040517fe69ab6be000000000000000000000000000000000000000000000000000000008152fd5b9190610180830191614d0c8385614c65565b8091501580615054575b15614d455760046040517fe69ab6be000000000000000000000000000000000000000000000000000000008152fd5b6001811480615019575b6150125791909493926000908035915b848110614d70575050505050509050565b614d7a8287614c65565b82101561340f5760059082821b810135603e199182813603018212156104e757614da691369101614ab7565b906001600160a01b03808351169160208094015190833b156104e75789958c601e1996604097889283519a7fdbe33d66000000000000000000000000000000000000000000000000000000008c5288614e308d60609081600482015260a46102048201998735606484015285614e1d8b8a01614228565b1660848401528701359101528401614228565b1660c48d0152608082013560e48d015288614e4d60a08401614228565b166101048d01526101248c015287614e6760e08301614228565b166101448c01526101008101356101648c01526101208101356101848c01526101408101356101a48c01526101608101356101c48c01523603018c12156104e7578e8c018035928282019767ffffffffffffffff958686116104e75785901b918236038a136104e757948094939296956101a06101e48f9a949a015252610224808d01968d01019896600091605e1984360301935b868410614f72575050505050505050505091846000818f9b9c9d9e9f81958397614f369260248501526003198483030160448501526141c1565b03925af1908115614f6857509060019291614f59575b5001949392969594614d5f565b614f62906140f6565b38614f4c565b513d6000823e3d90fd5b9091929394959697989a7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffddc8f8203018a528b35868112156104e75782018e85614fbc8a8401614228565b1683528101359084813603018212156104e7570187810135908f018a82116104e75781360381136104e7578f89929183928286615000948660019901520191614c9b565b9d019a01989796959401929190614efc565b5050505050565b506150248486614c65565b1561340f57803590603e19813603018212156104e7576001600160a01b039161504d91016147d2565b1615614d4f565b50811515614d16565b908160209103126104e757517fffffffff00000000000000000000000000000000000000000000000000000000811681036104e75790565b9291909260426150a36145b9565b6040958651917f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201522092813b15156000146151d85791602091615135936001600160a01b0387518096819582947f1626ba7e000000000000000000000000000000000000000000000000000000009a8b855260048501528b60248501526044840191614c9b565b0392165afa9081156151cd57907fffffffff000000000000000000000000000000000000000000000000000000009160009161519f575b5016036151765750565b600490517fd9681a86000000000000000000000000000000000000000000000000000000008152fd5b6151c0915060203d81116151c6575b6151b8818361417b565b81019061505d565b3861516c565b503d6151ae565b83513d6000823e3d90fd5b926151ec6151fb936151f293953691614989565b90615210565b909291926152dc565b6001600160a01b038091169116036151765750565b81519190604183036152415761523a92506020820151906060604084015193015160001a9061524c565b9192909190565b505060009160029190565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084116152d057926020929160ff608095604051948552168484015260408301526060820152600092839182805260015afa156152c45780516001600160a01b038116156152bb57918190565b50809160019190565b604051903d90823e3d90fd5b50505060009160039190565b600481101561539457806152ee575050565b600181036153205760046040517ff645eedf000000000000000000000000000000000000000000000000000000008152fd5b6002810361535957602482604051907ffce698f70000000000000000000000000000000000000000000000000000000082526004820152fd5b6003146153635750565b602490604051907fd78bce0c0000000000000000000000000000000000000000000000000000000082526004820152fd5b634e487b7160e01b600052602160045260246000fd5b9190916101209081818503126104e7576040519182019367ffffffffffffffff94838110868211176124de5760405282948235845260209081840135828601526153f660408501614228565b60408601526060840135606086015260808401359081116104e75783019180601f840112156104e757823561542a8161426a565b93615438604051958661417b565b818552838086019260051b8201019283116104e7578301905b82821061548b5750505050608083015260a081013560a083015260c081013560c083015260e081013560e083015261010080910135910152565b81358152908301908301615451565b81156154a4570490565b634e487b7160e01b600052601260045260246000fd5b818102929181159184041417156147a657565b903590605e19813603018212156104e7570190565b903590601e19813603018212156104e7570180359067ffffffffffffffff82116104e7576020019160c08202360383136104e757565b908160209103126104e757516001600160a01b03811681036104e75790565b939590949194600094600097889461555c60808a0197610e57610e4e8a51809361497c565b428111610f2257506040890199615574838c5161497c565b9a80519861558860a08d019a8b51906154ba565b998d1561565e578d61332360209361331d6155a6945191514261497c565b9301518061564b575b5060608b019182519151908d80600019048411021582021561080f5750908c95949392918682020493615617575b505061560e87610e496155f861561598979695602095614799565b809d6001600160a01b0395869101511696614799565b9316614a09565b565b6020929a50610e496155f88561560e946156386156159b9a99988e9661497c565b905260019d9550959697985050506155dd565b615657919850836149e6565b96386155af565b50919b508b9a50600199508a9750505050505050565b61567d82615d37565b81600052601460205260406000205403610d7457508060c060a06156a693015191015190614799565b42116156ae57565b60046040517f6d4241c0000000000000000000000000000000000000000000000000000000008152fd5b9060a0820135159081156157ce575b506104705760e081013580421161579d57506157076040823592016147d2565b6001600160a01b03811682600082815260106020526040812082825260205260ff604082205416928315615785575b505050615741575050565b6040517f0d7043a00000000000000000000000000000000000000000000000000000000081526001600160a01b039190911660048201526024810191909152604490fd5b60409293508152601160205220541015823880615736565b602490604051907f2f06d7780000000000000000000000000000000000000000000000000000000082526004820152fd5b60e091500151516157e26080830183614c65565b9190501415386156e7565b906157f661490e565b506001600160a01b036040830151169160c060a0820151910151926040519461581e8661410a565b855260208501526040840152606083015242608083015260a082015290565b601554907f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821682036147a657614ab49160011b9061549a565b9060208401511515806158b0575b615890575b50505050565b6158a7936001600160a01b03809151169216614a09565b3880808061588a565b50821515615885565b6024356fffffffffffffffffffffffffffffffff811681036104e75790565b6044356fffffffffffffffffffffffffffffffff811681036104e75790565b815115615a3057600092338452601260205260ff6040852054161592831561596d575b5050506159435760406fffffffffffffffffffffffffffffffff91338152601360205220541690565b60046040517f4eba9946000000000000000000000000000000000000000000000000000000008152fd5b6159cf9293509060209160405193849283926159b87f628b220700000000000000000000000000000000000000000000000000000000988986526060600487015260648601906143c9565b9160248501526003198483030160448501526141c1565b038186335af19081156129b357907fffffffff00000000000000000000000000000000000000000000000000000000918491615a12575b5016141538808061591a565b615a2a915060203d81116151c6576151b8818361417b565b38615a06565b505050600090565b9091615a45368285614989565b5115615a3057600091338352601260205260409360ff858520541615928315615aa9575b505050615a80573381526013602052205460801c90565b600482517f4eba9946000000000000000000000000000000000000000000000000000000008152fd5b615b049293509060209186519384928392615af27f94a2d3d500000000000000000000000000000000000000000000000000000000988986528b600487015260448601906143c9565b91600319858403016024860152614c9b565b038186335af1908115615b6557907fffffffff00000000000000000000000000000000000000000000000000000000918491615b47575b50161415388080615a69565b615b5f915060203d81116151c6576151b8818361417b565b38615b3b565b84513d85823e3d90fd5b6000919060605b6101808201518051851015615c385784615b8f916145a5565b516001600160a01b0381511660208092015182815191012091604090815193818501937f4def3e04bd42194484d5f8a5b268ec0df03b9d9d0402606fe3100023c5d79ac4855283860152606085015260608452608084019380851067ffffffffffffffff8611176124de57600195615c30948685528251902060a091615c1d8151809287868801910161419e565b830191820152039081018452018261417b565b930192615b76565b509192508051916001600160a01b0391826020820151169260408201519080606084015116906080840151908060a0860151169160c08601519160e08701511692610100918288015195610120890151976101606101408b01519a01519a602081519101209b60606040519e8f907f891e530ed2768a9decac48f4b7beec447f755ce23feeeeb952e429145b44ba9160208301526040820152015260808d015260a08c015260c08b015260e08a01528801526101208701526101408601526101608501526101808401526101a08301526101c0908183015281526101e0810181811067ffffffffffffffff8211176124de576040526020815191012090565b90600060605b60e08401518051831015615e095790615e01615d5b846001946145a5565b5160206001600160a01b03818301511660409283810151906060810151608091828101519160a08092015193885195888701977f8ca047c2f10359bf4a27bd2c623674be3801153b6b2646ba08593dc96ad7bb4489528a880152606087015285015283015260c090818301528152615dd28161415f565b5190209382519481615ded879351809286808701910161419e565b82019083820152039081018452018261417b565b910190615d3d565b50919290506001600160a01b038082511692602083015192826040820151169260608201511660808201519060c060a08401519301519360208151910120946040519660208801987f35f73c5cb07b3fa605378d4f576769166fed212ec3813ac1f1d73ef1c537eb0e8a5260408901526060880152608087015260a086015260c085015260e084015261010083015261012090818301528152610140810181811067ffffffffffffffff8211176124de576040525190209056fea2646970667358221220eba2315c8271639a56006b67768be0c0c6e366fb6841900f2b4b05edcbe1d25864736f6c63430008150033036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db000000000000000000000000097d34635b605c2f1630d6b4c6c5d222b8a2ca47d000000000000000000000000d07a25e6a22e9158162699490aa6a9464e14c50a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000004150ded32a6d3bfecae76e7558af48019034492700000000000000000000000052ac424ef7b283aa5badb8c6254832e3280d7398000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000447e69651d841bd8d104bed4930000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x61014080604052600436101561001457600080fd5b600090813560e01c90816306fdde0314613fc157508063150b7a0214613f5157806316ec9bc614613f195780631c31cac314613e9a5780632513a5ae146136c45780632891433d1461368c5780632c0a286e146130ef5780633644e515146130d45780633687121b146130ad57806344af681c1461308f5780634ae25a8614612fd05780634ec8198114612f745780634fd930ba14612ef75780635122afb114612e1c5780635448608614612c94578063584d158f14612c6d5780635a32989414612c29578063665458a714612be257806367562c641461285457806367e22404146128195780636ccc9dde146127625780637320ca261461270657806373b99f101461262657806373c2390c146126005780637795960b1461254d5780637b8d1a0b146125235780637d4e41ed14611a5b5780638617780b14611a3d5780638da5cb5b14611a16578063912d819e146119cf578063931c56e9146119b1578063986a7ace14611934578063995fe7201461190d578063a5a41031146118b6578063aa29dad11461188c578063ab250a3a146117cd578063ac9650d814611618578063acb1dfdb146115d6578063b1f0c786146112e7578063b8ca3b83146112ca578063b97e527a14611203578063baab19941461118e578063c2f50a7a14611170578063c833796d14611152578063cc37ef4f14611115578063cf23797d146110f7578063da144a5114611040578063db540a8a14610fc1578063dd7d978214610f7d578063e51e104014610dbd578063e621ffa914610cf4578063e69d8c4714610c8d578063ed21cbd214610ae4578063ed5a42841461095f578063ef706adf146108dc578063f0ad206a1461088d578063f2fde38b146108125763f95cc78f146102a057600080fd5b3461080f576003196040813601126108075767ffffffffffffffff90816004351161080b57610120906004353603011261080757602435908111610807576102ec9036906004016142d9565b6102f4614820565b5060808101610303815161583d565b60a46004350135106107c957506001600160a01b036103266044600435016147d2565b16330361079f5761033d8160246004350135615674565b61034c816004356004016156d8565b61010460043501356107755760646004350135918261074b57600092839081610373614774565b95839260208801519081151580610744575b610731575b5050919290600182935b6103a8608460043501600435600401614c65565b90508510156104ec576103bf8560e08a01516145a5565b5160a061041d8b6103e4896103de608460043501600435600401614c65565b90614764565b358c856103f56044600435016147d2565b916104176001600160a01b036060830151169160c08982015191015190614799565b92615537565b9296919490950151600354841515918261049a575b50506104705760019561045861045e946104526104649861045895614799565b9a614799565b99614799565b93614799565b94019390949291610394565b60046040517f25444587000000000000000000000000000000000000000000000000000000008152fd5b9091506104ad60c460043501358261497c565b612710907e068db8bac710cb295e9e1b089a027525460aa64c2f837b4a2339c0ebedfa4381118202158302156104e7570204103880610432565b600080fd5b508686926105178a886001600160a01b036060860151166105116044600435016147d2565b90615877565b60a4600435013503610470576015548211610700576001600c54019384600c5584601f1961055d6105478661426a565b95610555604051978861417b565b80875261426a565b0160005b8181106106e45750506105829161057d366004356004016153aa565b6157ed565b61058b83614598565b5261059582614598565b50600193835b6105af608460043501600435600401614c65565b9050811015610639576105d0816103de608460043501600435600401614c65565b356105de575b60010161059b565b94600180916105f18860e08701516145a5565b516105fc82886145a5565b5261060781876145a5565b50610620886103de608460043501600435600401614c65565b35604061062d83896145a5565b510152019590506105d6565b507f9190f728135a4ce2ee0c985cc2c2ce55169f358d6a979e6d48add4743482621c848460e085015261066b84615d37565b83825260146020526040822055602460043501358152806040812055604051809160043560040135825260246004350135602083015284604083015260a060608301526106bb60a08301876143c9565b9060808301520390a16106e060405192839283526040602084015260408301906143c9565b0390f35b60209192506106f161490e565b82828801015201908691610561565b602482604051907fc56ee9910000000000000000000000000000000000000000000000000000000082526004820152fd5b61073c9294506149e6565b91388061038a565b5085610385565b60046040517fe791b76b000000000000000000000000000000000000000000000000000000008152fd5b60046040517f32259e57000000000000000000000000000000000000000000000000000000008152fd5b60046040517f3f9b6963000000000000000000000000000000000000000000000000000000008152fd5b60449051604051907f2e67bbe300000000000000000000000000000000000000000000000000000000825260a4600435013560048301526024820152fd5b5080fd5b8280fd5b80fd5b503461080f57602060031936011261080f5773ffffffffffffffffffffffffffffffffffffffff196108426141e6565b600154906001600160a01b039061085c82841633146144fe565b169182911617600155337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b503461080f578060031936011261080f57604080516108ab816140da565b828152826020820152015260606108c0614740565b6040805191805183526020810151602084015201516040820152f35b503461080f57602060031936011261080f577f899cab278284ae4a91172caa0943607a0bcb19766254c3ebe1139be00650b102610959600435338452600e6020526040842081855260205260408420600160ff198254161790556040519182913383602090939291936001600160a01b0360408201951681520152565b0390a180f35b503461080f57606060031936011261080f576109796141e6565b60407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc360112610807576001600160a01b036109ba816001541633146144fe565b6109c3826147e6565b6fffffffffffffffffffffffffffffffff9161271080846109e26158b9565b1611908115610ad1575b50610aa7571690818352601260205260408320600160ff1982541617905560136020526040832081610a1c6158b9565b16815490857fffffffffffffffffffffffffffffffff00000000000000000000000000000000610a4a6158d8565b60801b169216171790556040519182526024358181168091036104e75760208301526044359081168091036104e7578160609160407f58b1f18b665c4c540aef11f1c3dec18c43824ab0e2e8057a7a9bc6ad3653be24940152a180f35b60046040517fb53b7b15000000000000000000000000000000000000000000000000000000008152fd5b905083610adc6158d8565b1611386109ec565b503461080f5760031960a081360112610807576004356024359167ffffffffffffffff8311610c895761010083600401918436030112610c8957610b26614212565b90608435918215158093036104e757610b47610b4236846142d9565b615d37565b9184875260209260148452604088205403610c715790610b71916001600160a01b039283916147d2565b163303610c47578160a484928983601754166024610b9160448d016147d2565b9b604051988997889663b18e2bbb60e01b8852169d8e600488015216828601520135604484015260643560648401528860848401525af18015610c3c57610c0d575b50907f696a99e04dd3d11d9ec4fe68917db2c142d3a55e1bff074c34ad74c1d515547d93606093926040519384528301526040820152a180f35b9080939291813d8311610c35575b610c25818361417b565b810103126104e757909138610bd3565b503d610c1b565b6040513d88823e3d90fd5b60046040517fdbd91fb4000000000000000000000000000000000000000000000000000000008152fd5b6024856040519063047e261360e21b82526004820152fd5b8380fd5b503461080f578060031936011261080f5760006020604051610cae81614143565b82815201526106e0604051610cc281614143565b6009546001600160a01b0316808252600a54602092830190815260408051928352905192820192909252918291820190565b503461080f57610d03366144c7565b6001600160a01b0360065460301c16803303610d8c5750610b42610d289136906142d9565b8183526014602052604083205403610d74577faf91ffe368225045d42f7c2b4b90feb438133ac1e375e73bc9066652b70a1d776020604051838152a18152601460205280604081205580f35b6024906040519063047e261360e21b82526004820152fd5b602490604051907f8afaa9b50000000000000000000000000000000000000000000000000000000082526004820152fd5b503461080f57606060031936011261080f576004359060243567ffffffffffffffff811161080757610df39036906004016142d9565b91604435610dff614820565b50610e0a8483615674565b60e0840191600183515111610f53576080610e258451614598565b51015192610e5d60a087015194610e57610e4e82610e4960c08c01998a5190614799565b61497c565b601654906149e6565b90614799565b428111610f2257506020610e796001600160a01b039251614598565b51015116330361079f5782610eb0837f611e850fd4909c086538320a1f747a380fdaff020cc4e6649b9b4dd0dbd383219551614799565b90526001600c54019384600c55610ec686615d37565b858252601460205260408220558181526040812055604051918291825284602083015260806040830152610efd60808301876143c9565b9060608301520390a16106e060405192839283526040602084015260408301906143c9565b602490604051907f4d321dfb0000000000000000000000000000000000000000000000000000000082526004820152fd5b60046040517fcd23b291000000000000000000000000000000000000000000000000000000008152fd5b503461080f578060031936011261080f5760206040516001600160a01b037f00000000000000000000000052ac424ef7b283aa5badb8c6254832e3280d7398168152f35b503461080f57602060031936011261080f577faed358a2bd4ca37fb72362cad5ae66b0b49a2a2031f9474a656e47b1f4afc4196020610ffe6141e6565b6001600160a01b0390611016826001541633146144fe565b168073ffffffffffffffffffffffffffffffffffffffff196018541617601855604051908152a180f35b503461080f57602060031936011261080f5760043533825260116020526040822054818110156110b55750338083526011602090815260408085208490558051928352908201929092527f5009c2e03e218242def2d459330d4f8b8b7eb7289ca08c47489aeef06672c63c9181908101610959565b6040517f997e86d600000000000000000000000000000000000000000000000000000000815233600482015260248101929092526044820152606490fd5b0390fd5b503461080f578060031936011261080f576020601654604051908152f35b503461080f57602060031936011261080f5760ff60406020926001600160a01b0361113e6141e6565b168152601284522054166040519015158152f35b503461080f578060031936011261080f576020601554604051908152f35b503461080f578060031936011261080f576020604051620151808152f35b503461080f57606060031936011261080f576111b66001600160a01b036001541633146144fe565b7f36e7ba2b68a5f2a92d31c35c23faa8f6fb2559d4406bb30eef08bdbab0d6f10960606004358060025560243580600355604435908160045560405192835260208301526040820152a180f35b503461080f57602060031936011261080f5760043565ffffffffffff81168091036108075761123e6001600160a01b036001541633146144fe565b6201518081106112a0576020817f9b0306e96c09148e30f9acd9a1ebc2c7cb1bc0348adbf320530ead875f78292c927fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000006006541617600655604051908152a180f35b60046040517fa922f92c000000000000000000000000000000000000000000000000000000008152fd5b503461080f578060031936011261080f5760206040516109c48152f35b503461080f576003196080813601126108075767ffffffffffffffff6004356024358281116115d257610100816004019482360301126115d257611329614212565b926064359081116115ce5761134290369060040161423c565b919094611352610b4236836142d9565b84885260146020526040882054036115b657611378906001600160a01b039182916147d2565b163303610c475760448201878261138e836147d2565b16936024846018541691013594803b1561080b576040516323b872dd60e01b81523060048201526001600160a01b039290921660248301526044820186905282908290606490829084905af180156115ab57611597575b50508160185416886113f6836147d2565b823b1561080757899885938484809461145b604051988996879586947fee534a3f0000000000000000000000000000000000000000000000000000000086521660048501528d6024850152169e8f6044840152608060648401528d6084840191614c9b565b03925af191821561158a57849261156f575b505061147a6020926147d2565b16926024604051809581937f6352211e00000000000000000000000000000000000000000000000000000000835260048301525afa918215611564578792611534575b503091160361150a577f13ab7606a034e1d4320c1129442ffa929fe2789f8cd0d03d147ff4f4707567a5936109599160405194859485526020850152606060408501526060840191614c9b565b60046040517f807a2b8f000000000000000000000000000000000000000000000000000000008152fd5b61155691925060203d811161155d575b61154e818361417b565b810190615518565b90386114bd565b503d611544565b6040513d89823e3d90fd5b61157a9192506140f6565b6115865781883861146d565b8780fd5b50604051903d90823e3d90fd5b6115a0906140f6565b6115865787386113e5565b6040513d84823e3d90fd5b6024846040519063047e261360e21b82526004820152fd5b8580fd5b8480fd5b503461080f57604060031936011261080f5760406020916001600160a01b036115fd6141e6565b168152600d8352818120602435825283522054604051908152f35b506020806003193601126108075760043567ffffffffffffffff811161080b5761164783913690600401614496565b6116508161426a565b9161165e604051938461417b565b818352601f1961166d8361426a565b01845b8181106117be575050835b8281106116ff5750505060405191838301848452825180915260408401948060408360051b870101940192955b8287106116b55785850386f35b9091929382806116ef837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08a6001960301865288516141c1565b96019201960195929190926116a8565b84806117138360059998991b850185614549565b9081604051928392833781018381520390305af43d156117b7573d6117378161457c565b90611745604051928361417b565b81523d888883013e5b61175883876145a5565b52156117695760010194939461167b565b61177381856145a5565b51906110f36040519283927f6cf73d1400000000000000000000000000000000000000000000000000000000845260048401526040602484015260448301906141c1565b606061174e565b60608582018801528601611670565b503461080f576020806003193601126108075760043567ffffffffffffffff811161080b57611800903690600401614496565b835b81811061180d578480f35b611818818385614764565b3590338652601085526040862082875285527fa39a039982552bc613a549c5a31d7c9a911b4e57c7da274435fca0e156c9c34061188360408820936001948560ff198254161790556040519182913383602090939291936001600160a01b0360408201951681520152565b0390a101611802565b503461080f57602060031936011261080f5760406020916004358152601483522054604051908152f35b503461080f578060031936011261080f57600060206040516118d781614143565b82815201526106e06118e7614774565b6040519182918291909160208060408301946001600160a01b0381511684520151910152565b503461080f578060031936011261080f5760206001600160a01b0360175416604051908152f35b503461080f57602060031936011261080f577fa39a039982552bc613a549c5a31d7c9a911b4e57c7da274435fca0e156c9c34061095960043533845260106020526040842081855260205260408420600160ff198254161790556040519182913383602090939291936001600160a01b0360408201951681520152565b503461080f578060031936011261080f57602060405162278d008152f35b503461080f57604060031936011261080f5760ff60406020926001600160a01b036119f86141e6565b16815260108452818120602435825284522054166040519015158152f35b503461080f578060031936011261080f5760206001600160a01b0360015416604051908152f35b503461080f578060031936011261080f576020600c54604051908152f35b503461080f576003199060208236011261080f576004359167ffffffffffffffff83116108075760a090833603011261080f57611a96614820565b50611aa4600182541461485d565b60028155611ab4602483016147d2565b611ac0604484016147d2565b91611ad7611ad160048601806148a8565b806148db565b936001600160a01b03611aec602087016147d2565b1661251c57835b611b0060048301806148a8565b94611b116064840184600401614549565b9690611b236084860186600401614549565b611b386020611b3286806148db565b016147d2565b91611b486060611b3287806148db565b93611b5386806148db565b9b8c916001600160a01b038d1633036124f8575b5050506001600160a01b03871633036123b9575b50506101408901358042116123885750606083013580421161238857506001600160a01b0384168752600e602052604087208935885260205260ff6040882054168015612368575b61232757610100890135806040850135116122ed5750611be560e08a016147d2565b6001600160a01b03604051917f3af32abf0000000000000000000000000000000000000000000000000000000083521660048201526020816024816001600160a01b037f0000000000000000000000004150ded32a6d3bfecae76e7558af480190344927165afa9081156122995788916122ce575b50156122a457611c6c60a08a016147d2565b6001600160a01b03604051917f3af32abf0000000000000000000000000000000000000000000000000000000083521660048201526020816024816001600160a01b037f00000000000000000000000052ac424ef7b283aa5badb8c6254832e3280d7398165afa90811561229957889161226a575b5015612240576001600160a01b0316801515908161222c575b50612202576001600160a01b031680151590816121ee575b506121c4576101608701351561219a57610120870135156121705760808701358015159081612139575b5061210f5760209687611d5192013590614cbc565b6001600c54019485600c556040611d6e85600401866004016148a8565b013593611d79614940565b604051611d858161410a565b8881526001600160a01b0384168a8201528660408201528760608201524260808201526101208b013560a0820152611dbc82614598565b52611dc681614598565b506001600160a01b0389611ddd60048501806148a8565b0135818c611df960e0611df260a084016147d2565b92016147d2565b926040519d8e611e0881614126565b848c16815201521660408c01521660608a01528560808a01524260a08a01526101608a013560c08a015260e0890152611e4088615d37565b87875260146020526040872055610100890135856000190460408b0135118602158102156104e757899086604083013502908082049106151501611e82614774565b611e8e60e084016147d2565b91602082019282878551611ea290856149e6565b90611eac93615877565b611eb860e085016147d2565b6001600160a01b03168689611ecd848d61497c565b91611ed793614a09565b611ee460048601806148a8565b60808101611ef191614549565b3690611efc92614989565b90611f07918d6158f7565b6fffffffffffffffffffffffffffffffff168061209d575b505050611f466001600160a01b03611f3b60a0602094016147d2565b1692600401806148a8565b0135813b15612099576040516323b872dd60e01b81526001600160a01b038616600482015230602482015260448101919091529086908290606490829084905af18015610c3c57612085575b50877f2b81fc2f914e99b38053262ace9e49b2f47f712ef594be1ea005de4f3baeec9d88886080946001600160a01b0360019b9c9d359881611fec60405196879687528c602088015260c0604088015260c08701906143c9565b931660608501521686830152604085013560a08301520390a1013515612055576001600160a01b03168352600d6020526040832090835260205261203560408320918254614799565b90555b556106e060405192839283526040602084015260408301906143c9565b6001600160a01b03919250168252600e60205260408220908252602052604081208260ff19825416179055612038565b61208f86916140f6565b6115d25738611f92565b8680fd5b6120b56120ad6120dd928b6149e6565b9351846149e6565b9286896120d7866001600160a01b036120d060e08b016147d2565b169461497c565b92614a09565b868215611f1f576001600160a01b03806120fc60e061210697016147d2565b1692511691614a09565b88388086611f1f565b60046040517fdab69d5e000000000000000000000000000000000000000000000000000000008152fd5b90506001600160a01b0383168652600d602052604086208835875260205261216960408301356040882054614799565b1138611d3c565b60046040517f20dac71b000000000000000000000000000000000000000000000000000000008152fd5b60046040517fecd83f88000000000000000000000000000000000000000000000000000000008152fd5b60046040517f1548268b000000000000000000000000000000000000000000000000000000008152fd5b90506001600160a01b038416141538611d12565b60046040517f62257e48000000000000000000000000000000000000000000000000000000008152fd5b90506001600160a01b038816141538611cfa565b60046040517ff1a638e6000000000000000000000000000000000000000000000000000000008152fd5b61228c915060203d602011612292575b612284818361417b565b810190614c4d565b38611ce1565b503d61227a565b6040513d8a823e3d90fd5b60046040517fa212b5d3000000000000000000000000000000000000000000000000000000008152fd5b6122e7915060203d60201161229257612284818361417b565b38611c5a565b8360449160408051927f111c5f29000000000000000000000000000000000000000000000000000000008452013560048301526024820152fd5b6040517ffc4a5c250000000000000000000000000000000000000000000000000000000081526001600160a01b038516600482015289356024820152604490fd5b506001600160a01b0384168752600f602052604087205489351115611bc3565b602490604051907f5929d5bb0000000000000000000000000000000000000000000000000000000082526004820152fd5b60a0853603126124f4576040518060a081011067ffffffffffffffff60a0830111176124de5760a0810160405267ffffffffffffffff8635116124da576124033687358801614b04565b815260208601356020820152604086013560408201526060860135606082015267ffffffffffffffff6080870135116124da57906124d3929161244c3660808901358901614a99565b6080820190815261245d8251615b6f565b9160208101519160606040830151920151905160208151910120916040519360208501957f7e90717662b6dd110797922ef6d6701d92bfd4164783966933e092ea21a74c5a875260408601526060850152608084015260a083015260c082015260c081526124ca8161415f565b51902088615095565b3880611b7b565b8980fd5b634e487b7160e01b600052604160045260246000fd5b8880fd5b61250e612509612514943690614b04565b615b6f565b86615095565b8a3880611b67565b8293611af3565b503461080f578060031936011261080f5760206001600160a01b0360065460301c16604051908152f35b503461080f57602060031936011261080f57600435338252600f6020526040822054818110156125c2575033808352600f602090815260408085208490558051928352908201929092527feaacd06746bfd850a83bbe929f94f4e1413f9aac82dc76e4331418aaf8bba4689181908101610959565b6040517f570f5b7800000000000000000000000000000000000000000000000000000000815233600482015260248101929092526044820152606490fd5b503461080f578060031936011261080f57602065ffffffffffff60065416604051908152f35b503461080f578060031936011261080f576001600160a01b0361264e816001541633146144fe565b600b5462278d0081018082116126f25742106126c1575060407fb3c1d38dbdc9199d0ce01f386d70e29014ed4af7af1d321ca6641a91f4b4dc0c91600954168073ffffffffffffffffffffffffffffffffffffffff196007541617600755600a548060085582519182526020820152a180f35b602490604051907f5c269db50000000000000000000000000000000000000000000000000000000082526004820152fd5b602484634e487b7160e01b81526011600452fd5b503461080f57602060031936011261080f577fe818ff6972bf8970ca14a893539e57452588b197b76e6afe29c40a31b3363c6160206004356127546001600160a01b036001541633146144fe565b80601655604051908152a180f35b503461080f57602060031936011261080f576004356001600160a01b03808216808303610c89577f0656dc0fa3e59bca53331c14b87a4778ffac8c184915152fdd0f103838a4f378926127bc6020936001541633146144fe565b6127c5826147e6565b7fffffffffffff0000000000000000000000000000000000000000ffffffffffff79ffffffffffffffffffffffffffffffffffffffff0000000000006006549260301b16911617600655604051908152a180f35b503461080f578060031936011261080f5760206040517f24fc88ca4cc577be38175f01dd694014086415b93c7410ba2b863edc7b9e87648152f35b503461080f57612863366144c7565b91612871600182541461485d565b60028155612882610b4236856142d9565b9180825260209260148452604083205403610d74576128a960c085013560a0860135614799565b936001600160a01b0394856128c0604084016147d2565b1690428111612bb1575060609560e083019060016128de83866154e2565b9050146000146129d257506128f390836154e2565b156129be578561290391016147d2565b91813b156115d2576040516323b872dd60e01b81523060048201526001600160a01b03939093166024840152850135604483015283908290606490829084905af180156129b35761299f575b50906001917f5985c1ea46d5ed2be4837f88c0563e18ed022c3bfe10dee15f6ce74daf897d6384604051838152a18152601483528060408120555b556106e06040519282849384528301906141c1565b6129a983916140f6565b610807573861294f565b6040513d85823e3d90fd5b602485634e487b7160e01b81526032600452fd5b96919050600654918684013592823b15612099576040516323b872dd60e01b815230600482015260309190911c89166001600160a01b0316602482015260448101849052868160648183875af180156115645791899188959493612b94575b5060c49291612a45869260065498016147d2565b968160405198899788967f762d7d740000000000000000000000000000000000000000000000000000000088528c60048901526024880152604487015216606485015265ffffffffffff811660848501523360a485015260301c165af19081156129b3578391612af0575b506040600193927f0c0f58e3bcc7b4b6f90dac7e83ad948f2dfe00aff9393c51aa2181fa08fb94e1929660065460301c16825191825286820152a161298a565b90503d8084833e612b01818361417b565b8101908481830312610c895780519067ffffffffffffffff82116115d2570181601f82011215610c8957805190612b378261457c565b92612b45604051948561417b565b8284528683830101116115d2579282612b8a6040936001979689807f0c0f58e3bcc7b4b6f90dac7e83ad948f2dfe00aff9393c51aa2181fa08fb94e19801910161419e565b9250929350612ab0565b612ba29193949592506140f6565b6115ce57908786939238612a31565b602490604051907fb73a6a130000000000000000000000000000000000000000000000000000000082526004820152fd5b503461080f57604060031936011261080f5760ff60406020926001600160a01b03612c0b6141e6565b168152600e8452818120602435825284522054166040519015158152f35b503461080f578060031936011261080f5760206040516001600160a01b037f0000000000000000000000004150ded32a6d3bfecae76e7558af480190344927168152f35b503461080f578060031936011261080f5760206001600160a01b0360185416604051908152f35b503461080f57606060031936011261080f57612cae6141e6565b612cb66141fc565b604435916001600160a01b0380831690604051917f6352211e0000000000000000000000000000000000000000000000000000000083528560048401526020928381602481855afa908115612299578891612dff575b5082163014612dd557829060a483601754169389604051958694859363b18e2bbb60e01b85528a16600485015260248401528a6044840152600060648401528160848401525af18015610c3c57612dac575b5050604080516001600160a01b0392831681529290911660208301528101919091527f6ebfd923dee76d83a8a77f3f29fe17755e74e5322d953678e35381cb63709199908060608101610959565b813d8311612dce575b612dbf818361417b565b810103126104e7573880612d5e565b503d612db5565b60046040517f64436547000000000000000000000000000000000000000000000000000000008152fd5b612e169150843d861161155d5761154e818361417b565b38612d0c565b503461080f57604060031936011261080f576001600160a01b03612e45816001541633146144fe565b602435906109c48211612ec6577f160fef22fef07b45037a807beef2c89408a81168d1055cf34024c85396b882af91604091612e87612e826147bc565b6147e6565b612e8f6147bc565b16908173ffffffffffffffffffffffffffffffffffffffff19600954161760095580600a5542600b5582519182526020820152a180f35b602482604051907f8b902c630000000000000000000000000000000000000000000000000000000082526004820152fd5b503461080f57602060031936011261080f577f924773930b2679ab8bf328330b211bedfb8f917551fd856f536bec008d17f9a66020612f346141e6565b6001600160a01b0390612f4c826001541633146144fe565b16808452601282526040842060ff19815416905560138252836040812055604051908152a180f35b503461080f57602060031936011261080f577f759c526639cdac7b56455b1bb335d0edb0ea487d414f1fa706cedf5532f09fc46020600435612fc26001600160a01b036001541633146144fe565b80601555604051908152a180f35b503461080f576020806003193601126108075760043567ffffffffffffffff811161080b57613003903690600401614496565b835b818110613010578480f35b61301b818385614764565b3590338652600e85526040862082875285527f899cab278284ae4a91172caa0943607a0bcb19766254c3ebe1139be00650b10261308660408820936001948560ff198254161790556040519182913383602090939291936001600160a01b0360408201951681520152565b0390a101613005565b503461080f578060031936011261080f576020600b54604051908152f35b503461080f57602060031936011261080f5760206130cc60043561583d565b604051908152f35b503461080f578060031936011261080f5760206130cc6145b9565b503461080f576003199060208236011261080f5767ffffffffffffffff6004351161080f57606060043536039283011261080f57613130600182541461485d565b600281556131426004803501806154cd565b35907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefd60246004350135930183121561080f576001600160a01b0361318c600485813501016147d2565b16330361356a575b6131ad6131a736600486813501016142d9565b83615674565b60406131bd6004803501806154cd565b01358015158103610807576134b9575b6001600160a01b036131e560448560043501016147d2565b166131f5600485813501016147d2565b813b1561080b576040516323b872dd60e01b8152306004808301919091526001600160a01b0392909216602480830191909152913586019091013560448201529082908290606490829084905af180156115ab576134aa575b5061328361326d6132636004803501806154cd565b6020810190614549565b9061327e36600488813501016142d9565b615a38565b9261328c614774565b93602085015115159183906001600160a01b036132af60648360043501016147d2565b16859386945b6132c960048035860160e4810191016154e2565b9050861015613425576132e660048035860160e4810191016154e2565b87101561340f576132fd90369060c0890201614282565b9087613329604084015161332360a086015161331d60808801514261497c565b906154ba565b906149c0565b8c61334a6fffffffffffffffffffffffffffffffff871660408701516149e6565b918c918d946133b5575b508593610e496133ad969461338261338295610e4961338796613382604060019e015160608d015190614799565b614799565b80936001600160a01b0360206133a260048d813501016147d2565b920151169089614a09565b9501946132b5565b6001969450613382925099610e496133ad969461338284610e49613387969f6133f06133e960206133fb93015180956149e6565b93866149e6565b96610e578885614799565b9f965050955050949650509395508e613354565b634e487b7160e01b600052603260045260246000fd5b888486858d8b7fd9cfbcfb9ae5d65b8d34072de4575f602ee25cf3715bc05c291701fe9e258f9a60608f98604051908982526020820152866040820152a161347e575b8585815260146020528060408120556001815580f35b6001600160a01b0361349860046134a196813501016147d2565b91511691614a09565b82808080613468565b6134b3906140f6565b3861324e565b6001600160a01b036017541660206001600160a01b0360a46134e0600488813501016147d2565b856134f160448a60043501016147d2565b9584604051978896879563b18e2bbb60e01b875216600486015216602484015260248a6004350101356044840152816064840152600160848401525af180156115ab5761353f575b506131cd565b602090813d8311613563575b613555818361417b565b8101031261080f5738613539565b503d61354b565b613579600484813501016147d2565b6135876004803501806154cd565b60608136031261080b576040519061359e826140da565b80358252602081013567ffffffffffffffff81116115d2576135c39036908301614a99565b6020830181905291604082013580151590036115d2579060409182820135838201525192602081519101208251937f41277b3c1cbe08ea7bbdd10a13f24dc956f3936bf46526f904c73697d9958e0c6020860152838501526060840152013515156080820152608081528060a081011067ffffffffffffffff60a0830111176136785760a0810160405280516020909101206136739161366b60048035604481019101614549565b929091615095565b613194565b602483634e487b7160e01b81526041600452fd5b503461080f57602060031936011261080f5760406020916001600160a01b036136b36141e6565b168152600f83522054604051908152f35b503461080f576003196060813601126108075767ffffffffffffffff6004351161080757610120906004353603011261080f5760243567ffffffffffffffff8111610807576137179036906004016142d9565b6101205260443567ffffffffffffffff81116108075761373b90369060040161423c565b60e052613746614820565b50819061375c6101205160246004350135615674565b61376e610120516004356004016156d8565b6001600160a01b036137846044600435016147d2565b16331460c052826080528283608052839261379d614774565b610100528490602061010051015180151580613e8b575b613e72575b506001949294918660a0525b6137d9608460043501600435600401614c65565b905060a051101561391d5760a06137f8815160e06101205101516145a5565b519161385d61381c613814608460043501600435600401614c65565b855191614764565b3561382b6044600435016147d2565b906001600160a01b0360606101205101511691868661012051015193610417610100519560c061012051015190614799565b9394929991959096015160035460c0519160c051613913575b826138c2575b50506104705761389f61045e946138996138b0986138a894614799565b9c614799565b98608051614799565b608052614799565b60a080516001019052929492916137c5565b9091506138d560c460043501358261497c565b612710907e068db8bac710cb295e9e1b089a027525460aa64c2f837b4a2339c0ebedfa43811182021583021561390f57020410388061387c565b8f80fd5b8615159250613876565b8692938561394b88936001600160a01b036060610120510151169061010051916105116044600435016147d2565b60016080519611613e485760c05115613c83575060a4600435013590613978426101046004350135614799565b6139a361399460c061012051015160a061012051015190614799565b9260806101205101519061549a565b6139ab614740565b856139b6818761497c565b15159586613c0c575b505050508215613bfc575b8215613bf2575b5050613bc8575b6139ee6064600435013560a4600435013561497c565b91613bb2575b81811115613b605790613a39916001600160a01b03606061012051015116916120d76001600160a01b0361012051511691613a336044600435016147d2565b9361497c565b6001600c54019182600c55613a6083613a50614940565b9261057d366004356004016153aa565b613a6982614598565b52613a7381614598565b5060e0610120510152613a9e613a9060a06101205101514261497c565b610104600435013590614799565b60c061012051015260a460043501356080610120510152613ac161012051615d37565b8282526014602052604082205560246004350135815260408120557f9190f728135a4ce2ee0c985cc2c2ce55169f358d6a979e6d48add4743482621c60405160043560040135815260246004350135602082015282604082015260a0606082015280613b3360a08201610120516143c9565b6064600435013560808301520390a160405190815260406020820152806106e060408201610120516143c9565b818110613b6f575b5050613a39565b613bab916001600160a01b03606061012051015116916120d7613b966044600435016147d2565b916001600160a01b036101205151169361497c565b8280613b68565b9250613bc1906080519061497c565b81926139f4565b60046040517fc9031bb4000000000000000000000000000000000000000000000000000000008152fd5b10905085806139d1565b60043560640135151592506139ca565b83949650613c3890613c32613c2584613c3e96976154ba565b9160c460043501356154ba565b9061497c565b936154ba565b907e068db8bac710cb295e9e1b089a027525460aa64c2f837b4a2339c0ebedfa43831161271002158202156115865760206127109101519202041091878085816139bf565b9250506001600160a01b03610120515116331415600014613cc85760046040517fd1988c2d000000000000000000000000000000000000000000000000000000008152fd5b600191613cd96044600435016147d2565b90613ce9366004356004016153aa565b9160608686805b613de5575b50508351936020810151916001600160a01b03604083015116906060830151906020815191012060a08401519160c08501519361010060e0870151960151966040519a7fdb613ea3383336cd787d929ccfc21ab7cd87bf1d588780c80ce5f970dd79c34860208d015260408c015260608b015260808a015260a089015260c088015260e0870152610100860152610120850152610140908185015283528261016081011067ffffffffffffffff61016085011117613dd157908291610160613dcc940160405260e051926020815191012090615095565b6139d8565b602486634e487b7160e01b81526041600452fd5b909160808601518051841015613e3f5790613e366040613e068686956145a5565b519281519381613e2086935180926020808701910161419e565b820190602082015203602081018452018261417b565b92019080613cf0565b50915080613cf5565b60046040517fbf85e051000000000000000000000000000000000000000000000000000000008152fd5b613e84919250606460043501356149e6565b90386137b9565b506064600435013515156137b4565b503461080f57602060031936011261080f577f47487988ae8c1417174bb9698afe3a54ef641a5255053a53eabc4d188fe6875d6020613ed76141e6565b6001600160a01b0390613eef826001541633146144fe565b168073ffffffffffffffffffffffffffffffffffffffff196017541617601755604051908152a180f35b503461080f57602060031936011261080f5760406020916001600160a01b03613f406141e6565b168152601183522054604051908152f35b503461080f57608060031936011261080f57613f6b6141e6565b50613f746141fc565b5060643567ffffffffffffffff811161080757613f9590369060040161423c565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b823461080f578060031936011261080f576005548282613fe0836140a0565b9283835260209460019186838216918260001461407d575050600114614023575b505061400f9250038361417b565b6106e06040519282849384528301906141c1565b90859250600582527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db05b85831061406557505061400f93508201018580614001565b8054838901850152879450869390920191810161404d565b9150935061400f95925060ff1991501682840152151560051b8201018580614001565b90600182811c921680156140d0575b60208310146140ba57565b634e487b7160e01b600052602260045260246000fd5b91607f16916140af565b6060810190811067ffffffffffffffff8211176124de57604052565b67ffffffffffffffff81116124de57604052565b60c0810190811067ffffffffffffffff8211176124de57604052565b610100810190811067ffffffffffffffff8211176124de57604052565b6040810190811067ffffffffffffffff8211176124de57604052565b60e0810190811067ffffffffffffffff8211176124de57604052565b90601f601f19910116810190811067ffffffffffffffff8211176124de57604052565b60005b8381106141b15750506000910152565b81810151838201526020016141a1565b90601f19601f6020936141df8151809281875287808801910161419e565b0116010190565b600435906001600160a01b03821682036104e757565b602435906001600160a01b03821682036104e757565b604435906001600160a01b03821682036104e757565b35906001600160a01b03821682036104e757565b9181601f840112156104e75782359167ffffffffffffffff83116104e757602083818601950101116104e757565b67ffffffffffffffff81116124de5760051b60200190565b91908260c09103126104e75760405161429a8161410a565b60a0808294803584526142af60208201614228565b60208501526040810135604085015260608101356060850152608081013560808501520135910152565b9190610100838203126104e7576040516142f281614126565b80936142fd81614228565b82526020808201358184015261431560408301614228565b604084015261432660608301614228565b60608401526080820135608084015260a082013560a084015260c091828101358385015260e08101359067ffffffffffffffff82116104e757019084601f830112156104e75781356143778161426a565b95614385604051978861417b565b81875282858189019302850101938185116104e7578301915b8483106143b15750505050505060e00152565b8386916143be8486614282565b81520192019161439e565b610100918281016001600160a01b03918284511681526020908185015182820152604094848682015116868301526060918583830151168382015260809586830151878301528461012060a093848601518582015260e060c096878101518884015201519b60e08201528b51809952019901966000945b878610614454575050505050505050505090565b885180518c528088015184168c890152808b01518c8c0152808201518c830152808301518c8401528401518b8501529984019997860197600190950194614440565b9181601f840112156104e75782359167ffffffffffffffff83116104e7576020808501948460051b0101116104e757565b906003196040818401126104e757600435926024359167ffffffffffffffff83116104e75782610100920301126104e75760040190565b1561450557565b606460405162461bcd60e51b815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152fd5b903590601e19813603018212156104e7570180359067ffffffffffffffff82116104e7576020019181360383136104e757565b67ffffffffffffffff81116124de57601f01601f191660200190565b80511561340f5760200190565b805182101561340f5760209160051b010190565b6000467f00000000000000000000000000000000000000000000000000000000000000010361460757507f24fc88ca4cc577be38175f01dd694014086415b93c7410ba2b863edc7b9e876490565b6040516005549190818161461a856140a0565b918282526020958683019560019188838216918260001461471f5750506001146146c5575b505061464d9250038261417b565b51902090604051908101917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f835260408201527fad7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a560608201524660808201523060a082015260a081526146bf8161410a565b51902090565b90879250600582527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db05b85831061470757505061464d9350820101388061463f565b805483880185015286945088939092019181016146ef565b9150935061464d95925060ff199150168652151560051b820101388061463f565b6040519061474d826140da565b600254825260035460208301526004546040830152565b919081101561340f5760051b0190565b6040519061478182614143565b6007546001600160a01b031682526008546020830152565b919082018092116147a657565b634e487b7160e01b600052601160045260246000fd5b6004356001600160a01b03811681036104e75790565b356001600160a01b03811681036104e75790565b6001600160a01b0316156147f657565b60046040517f92863bea000000000000000000000000000000000000000000000000000000008152fd5b6040519061482d82614126565b606060e083600080825280602083015280604083015280848301528060808301528060a083015260c08201520152565b1561486457565b606460405162461bcd60e51b815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61813603018212156104e7570190565b9035907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe61813603018212156104e7570190565b6040519061491b8261410a565b8160a06000918281528260208201528260408201528260608201528260808201520152565b6040519061494d82614143565b600182528160005b602090818110156149775760209161496b61490e565b90828501015201614955565b505050565b919082039182116147a657565b9291926149958261457c565b916149a3604051938461417b565b8294818452818301116104e7578281602093846000960137010152565b9064496cebb80091816000190481118202158302156104e7570290808204910615150190565b9061271091816000190481118202158302156104e7570290808204910615150190565b9160008093602095606494604051946323b872dd60e01b86526001600160a01b03809216600487015216602485015260448401525af13d15601f3d1160016000511416171615614a5557565b606460405162461bcd60e51b815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b9080601f830112156104e757816020614ab493359101614989565b90565b91906040838203126104e75760405190614ad082614143565b8193614adb81614228565b835260208101359167ffffffffffffffff83116104e757602092614aff9201614a99565b910152565b9190916101a09081818503126104e75760405167ffffffffffffffff928101838111828210176124de57604052809482358252602090614b45828501614228565b8284015260408401356040840152614b5f60608501614228565b606084015260808401356080840152614b7a60a08501614228565b60a084015260c084013560c0840152614b9560e08501614228565b60e084015261010080850135908401526101208085013590840152610140808501359084015261016080850135908401526101809384810135908682116104e7570181601f820112156104e7578035614bed8161426a565b96614bfb604051988961417b565b818852848089019260051b840101938085116104e757858401925b858410614c2857505050505050500152565b83358381116104e7578791614c42848480948a0101614ab7565b815201930192614c16565b908160209103126104e7575180151581036104e75790565b903590601e19813603018212156104e7570180359067ffffffffffffffff82116104e757602001918160051b360383136104e757565b601f8260209493601f19938186528686013760008582860101520116010190565b60c0810135908115614cfa575003614cd057565b60046040517fe69ab6be000000000000000000000000000000000000000000000000000000008152fd5b9190610180830191614d0c8385614c65565b8091501580615054575b15614d455760046040517fe69ab6be000000000000000000000000000000000000000000000000000000008152fd5b6001811480615019575b6150125791909493926000908035915b848110614d70575050505050509050565b614d7a8287614c65565b82101561340f5760059082821b810135603e199182813603018212156104e757614da691369101614ab7565b906001600160a01b03808351169160208094015190833b156104e75789958c601e1996604097889283519a7fdbe33d66000000000000000000000000000000000000000000000000000000008c5288614e308d60609081600482015260a46102048201998735606484015285614e1d8b8a01614228565b1660848401528701359101528401614228565b1660c48d0152608082013560e48d015288614e4d60a08401614228565b166101048d01526101248c015287614e6760e08301614228565b166101448c01526101008101356101648c01526101208101356101848c01526101408101356101a48c01526101608101356101c48c01523603018c12156104e7578e8c018035928282019767ffffffffffffffff958686116104e75785901b918236038a136104e757948094939296956101a06101e48f9a949a015252610224808d01968d01019896600091605e1984360301935b868410614f72575050505050505050505091846000818f9b9c9d9e9f81958397614f369260248501526003198483030160448501526141c1565b03925af1908115614f6857509060019291614f59575b5001949392969594614d5f565b614f62906140f6565b38614f4c565b513d6000823e3d90fd5b9091929394959697989a7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffddc8f8203018a528b35868112156104e75782018e85614fbc8a8401614228565b1683528101359084813603018212156104e7570187810135908f018a82116104e75781360381136104e7578f89929183928286615000948660019901520191614c9b565b9d019a01989796959401929190614efc565b5050505050565b506150248486614c65565b1561340f57803590603e19813603018212156104e7576001600160a01b039161504d91016147d2565b1615614d4f565b50811515614d16565b908160209103126104e757517fffffffff00000000000000000000000000000000000000000000000000000000811681036104e75790565b9291909260426150a36145b9565b6040958651917f19010000000000000000000000000000000000000000000000000000000000008352600283015260228201522092813b15156000146151d85791602091615135936001600160a01b0387518096819582947f1626ba7e000000000000000000000000000000000000000000000000000000009a8b855260048501528b60248501526044840191614c9b565b0392165afa9081156151cd57907fffffffff000000000000000000000000000000000000000000000000000000009160009161519f575b5016036151765750565b600490517fd9681a86000000000000000000000000000000000000000000000000000000008152fd5b6151c0915060203d81116151c6575b6151b8818361417b565b81019061505d565b3861516c565b503d6151ae565b83513d6000823e3d90fd5b926151ec6151fb936151f293953691614989565b90615210565b909291926152dc565b6001600160a01b038091169116036151765750565b81519190604183036152415761523a92506020820151906060604084015193015160001a9061524c565b9192909190565b505060009160029190565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084116152d057926020929160ff608095604051948552168484015260408301526060820152600092839182805260015afa156152c45780516001600160a01b038116156152bb57918190565b50809160019190565b604051903d90823e3d90fd5b50505060009160039190565b600481101561539457806152ee575050565b600181036153205760046040517ff645eedf000000000000000000000000000000000000000000000000000000008152fd5b6002810361535957602482604051907ffce698f70000000000000000000000000000000000000000000000000000000082526004820152fd5b6003146153635750565b602490604051907fd78bce0c0000000000000000000000000000000000000000000000000000000082526004820152fd5b634e487b7160e01b600052602160045260246000fd5b9190916101209081818503126104e7576040519182019367ffffffffffffffff94838110868211176124de5760405282948235845260209081840135828601526153f660408501614228565b60408601526060840135606086015260808401359081116104e75783019180601f840112156104e757823561542a8161426a565b93615438604051958661417b565b818552838086019260051b8201019283116104e7578301905b82821061548b5750505050608083015260a081013560a083015260c081013560c083015260e081013560e083015261010080910135910152565b81358152908301908301615451565b81156154a4570490565b634e487b7160e01b600052601260045260246000fd5b818102929181159184041417156147a657565b903590605e19813603018212156104e7570190565b903590601e19813603018212156104e7570180359067ffffffffffffffff82116104e7576020019160c08202360383136104e757565b908160209103126104e757516001600160a01b03811681036104e75790565b939590949194600094600097889461555c60808a0197610e57610e4e8a51809361497c565b428111610f2257506040890199615574838c5161497c565b9a80519861558860a08d019a8b51906154ba565b998d1561565e578d61332360209361331d6155a6945191514261497c565b9301518061564b575b5060608b019182519151908d80600019048411021582021561080f5750908c95949392918682020493615617575b505061560e87610e496155f861561598979695602095614799565b809d6001600160a01b0395869101511696614799565b9316614a09565b565b6020929a50610e496155f88561560e946156386156159b9a99988e9661497c565b905260019d9550959697985050506155dd565b615657919850836149e6565b96386155af565b50919b508b9a50600199508a9750505050505050565b61567d82615d37565b81600052601460205260406000205403610d7457508060c060a06156a693015191015190614799565b42116156ae57565b60046040517f6d4241c0000000000000000000000000000000000000000000000000000000008152fd5b9060a0820135159081156157ce575b506104705760e081013580421161579d57506157076040823592016147d2565b6001600160a01b03811682600082815260106020526040812082825260205260ff604082205416928315615785575b505050615741575050565b6040517f0d7043a00000000000000000000000000000000000000000000000000000000081526001600160a01b039190911660048201526024810191909152604490fd5b60409293508152601160205220541015823880615736565b602490604051907f2f06d7780000000000000000000000000000000000000000000000000000000082526004820152fd5b60e091500151516157e26080830183614c65565b9190501415386156e7565b906157f661490e565b506001600160a01b036040830151169160c060a0820151910151926040519461581e8661410a565b855260208501526040840152606083015242608083015260a082015290565b601554907f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821682036147a657614ab49160011b9061549a565b9060208401511515806158b0575b615890575b50505050565b6158a7936001600160a01b03809151169216614a09565b3880808061588a565b50821515615885565b6024356fffffffffffffffffffffffffffffffff811681036104e75790565b6044356fffffffffffffffffffffffffffffffff811681036104e75790565b815115615a3057600092338452601260205260ff6040852054161592831561596d575b5050506159435760406fffffffffffffffffffffffffffffffff91338152601360205220541690565b60046040517f4eba9946000000000000000000000000000000000000000000000000000000008152fd5b6159cf9293509060209160405193849283926159b87f628b220700000000000000000000000000000000000000000000000000000000988986526060600487015260648601906143c9565b9160248501526003198483030160448501526141c1565b038186335af19081156129b357907fffffffff00000000000000000000000000000000000000000000000000000000918491615a12575b5016141538808061591a565b615a2a915060203d81116151c6576151b8818361417b565b38615a06565b505050600090565b9091615a45368285614989565b5115615a3057600091338352601260205260409360ff858520541615928315615aa9575b505050615a80573381526013602052205460801c90565b600482517f4eba9946000000000000000000000000000000000000000000000000000000008152fd5b615b049293509060209186519384928392615af27f94a2d3d500000000000000000000000000000000000000000000000000000000988986528b600487015260448601906143c9565b91600319858403016024860152614c9b565b038186335af1908115615b6557907fffffffff00000000000000000000000000000000000000000000000000000000918491615b47575b50161415388080615a69565b615b5f915060203d81116151c6576151b8818361417b565b38615b3b565b84513d85823e3d90fd5b6000919060605b6101808201518051851015615c385784615b8f916145a5565b516001600160a01b0381511660208092015182815191012091604090815193818501937f4def3e04bd42194484d5f8a5b268ec0df03b9d9d0402606fe3100023c5d79ac4855283860152606085015260608452608084019380851067ffffffffffffffff8611176124de57600195615c30948685528251902060a091615c1d8151809287868801910161419e565b830191820152039081018452018261417b565b930192615b76565b509192508051916001600160a01b0391826020820151169260408201519080606084015116906080840151908060a0860151169160c08601519160e08701511692610100918288015195610120890151976101606101408b01519a01519a602081519101209b60606040519e8f907f891e530ed2768a9decac48f4b7beec447f755ce23feeeeb952e429145b44ba9160208301526040820152015260808d015260a08c015260c08b015260e08a01528801526101208701526101408601526101608501526101808401526101a08301526101c0908183015281526101e0810181811067ffffffffffffffff8211176124de576040526020815191012090565b90600060605b60e08401518051831015615e095790615e01615d5b846001946145a5565b5160206001600160a01b03818301511660409283810151906060810151608091828101519160a08092015193885195888701977f8ca047c2f10359bf4a27bd2c623674be3801153b6b2646ba08593dc96ad7bb4489528a880152606087015285015283015260c090818301528152615dd28161415f565b5190209382519481615ded879351809286808701910161419e565b82019083820152039081018452018261417b565b910190615d3d565b50919290506001600160a01b038082511692602083015192826040820151169260608201511660808201519060c060a08401519301519360208151910120946040519660208801987f35f73c5cb07b3fa605378d4f576769166fed212ec3813ac1f1d73ef1c537eb0e8a5260408901526060880152608087015260a086015260c085015260e084015261010083015261012090818301528152610140810181811067ffffffffffffffff8211176124de576040525190209056fea2646970667358221220eba2315c8271639a56006b67768be0c0c6e366fb6841900f2b4b05edcbe1d25864736f6c63430008150033

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

00000000000000000000000097d34635b605c2f1630d6b4c6c5d222b8a2ca47d000000000000000000000000d07a25e6a22e9158162699490aa6a9464e14c50a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000004150ded32a6d3bfecae76e7558af48019034492700000000000000000000000052ac424ef7b283aa5badb8c6254832e3280d7398000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000447e69651d841bd8d104bed4930000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : loanLiquidator (address): 0x97D34635b605C2F1630D6b4c6C5D222B8a2ca47D
Arg [1] : protocolFee (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [2] : currencyManager (address): 0x4150deD32A6D3bfecAE76e7558Af480190344927
Arg [3] : collectionManager (address): 0x52Ac424eF7B283aA5bADB8c6254832E3280d7398
Arg [4] : maxSources (uint256): 10
Arg [5] : minLockPeriod (uint256): 500
Arg [6] : delegateRegistry (address): 0x00000000000000447e69651d841bD8D104Bed493
Arg [7] : flashActionContract (address): 0x0000000000000000000000000000000000000000

-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 00000000000000000000000097d34635b605c2f1630d6b4c6c5d222b8a2ca47d
Arg [1] : 000000000000000000000000d07a25e6a22e9158162699490aa6a9464e14c50a
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [3] : 0000000000000000000000004150ded32a6d3bfecae76e7558af480190344927
Arg [4] : 00000000000000000000000052ac424ef7b283aa5badb8c6254832e3280d7398
Arg [5] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [6] : 00000000000000000000000000000000000000000000000000000000000001f4
Arg [7] : 00000000000000000000000000000000000000447e69651d841bd8d104bed493
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000000


Deployed Bytecode Sourcemap

582:29526:25:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::i;:::-;;10246:21;;;10223:45;582:29526;;10223:45;:::i;:::-;10185:35;582:29526;;10185:35;582:29526;10185:83;10181:207;;582:29526;-1:-1:-1;;;;;10415:26:25;;582:29526;;10415:26;;:::i;:::-;582:29526;10401:10;:40;10397:103;;10534:5;582:29526;;;;10145:26;582:29526;10534:5;:::i;:::-;10597;582:29526;;;;;10597:5;:::i;:::-;10618:28;582:29526;;10618:28;582:29526;10614:109;;10736:23;582:29526;;10736:23;582:29526;10736:27;;10732:97;;582:29526;;;;;;;:::i;:::-;22612:28;;22654:20;;;;582:29526;22654:24;;;;:55;;;582:29526;22650:171;;582:29526;22831:19;;;;;10977:4;22865:13;22860:1096;22865:13;22884:35;;582:29526;;22884:35;582:29526;;;;22884:35;:::i;:::-;22880:46;;;;;;;22966:15;:12;582:29526;22966:12;;;:15;:::i;:::-;;23423;23280:251;582:29526;23021:38;582:29526;23021:35;22884;582:29526;;22884:35;582:29526;;;;23021:35;:::i;:::-;:38;;:::i;:::-;582:29526;;;23315:26;10415;582:29526;;10415:26;23315;:::i;:::-;23359:22;23423:32;-1:-1:-1;;;;;23359:22:25;;;1590:5:24;582:29526:25;23423:15;23441:14;23423:15;;;582:29526;23441:14;;582:29526;23423:32;;:::i;:::-;23280:251;;:::i;:::-;23593:13;;;;;;;582:29526;23636:17;582:29526;29680:10;;;29659:142;;;;22865:13;29642:234;;;;10977:4;23669:37;23720:19;23806:35;23669:37;;23855:30;23669:37;23753:39;23669:37;;:::i;:::-;23720:19;;:::i;:::-;23753:39;;:::i;23806:35::-;23855:30;;:::i;:::-;582:29526;;22865:13;;;;;;;29642:234;582:29526;;;29833:32;;;;29659:142;582:29526;;;29712:30;23608:26;582:29526;;23608:26;582:29526;29712:30;;:::i;:::-;1590:5:24;1744:340:10;;;;;;;;;;;;;;29711:89:25;29659:142;;;;1744:340:10;582:29526:25;1744:340:10;;22880:46:25;;;;;24061:11;22880:46;;-1:-1:-1;;;;;23359:22:25;;;1590:5:24;582:29526:25;24015:26;10415;582:29526;;10415:26;24015;:::i;:::-;24061:11;;:::i;:::-;10185:35;582:29526;;10185:35;582:29526;10997:49;10993:119;;11143:11;582:29526;11125:29;;11121:103;;10977:4;13301:21:24;582:29526:25;;;;13301:21:24;582:29526:25;;-1:-1:-1;;582:29526:25;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;;11369:73;582:29526;;;;;;;;:::i;:::-;11369:73;:::i;:::-;11353:89;;;:::i;:::-;;;;;:::i;:::-;;10977:4;11534:13;;;11553:35;22884;582:29526;;22884:35;582:29526;;;;11553:35;:::i;:::-;11549:46;;;;;;;11616:38;582:29526;11616:35;22884;582:29526;;22884:35;582:29526;;;;11616:35;:::i;:38::-;582:29526;11612:271;;11534:13;10977:4;582:29526;11534:13;;11612:271;22966:12;10977:4;22966:12;;11694:15;22966:12;582:29526;22966:12;;11694;:15;:::i;:::-;;11678:31;;;;:::i;:::-;;;;;;:::i;:::-;;11759:38;582:29526;11759:35;22884;582:29526;;22884:35;582:29526;;;;11759:35;:::i;:38::-;582:29526;;11727:13;;;;:::i;:::-;;:29;582:29526;;11612:271;;;;;11549:46;;12116:80;11549:46;;582:29526;22966:12;;11963:25;12019:12;;;:::i;:::-;582:29526;;;11999:6;22654:20;582:29526;;;;;;;;10145:26;582:29526;;;;;;;;;;;;;;;;;;;;;;10145:26;582:29526;22654:20;582:29526;;;;;;;;23423:15;23359:22;582:29526;;;;23423:15;582:29526;;;;:::i;:::-;;10246:21;582:29526;;;12116:80;;;582:29526;;;;;;;;;22654:20;582:29526;;;;;;;;:::i;:::-;;;;;22654:20;582:29526;;;;;:::i;:::-;;;;;;;;;;;;;11121:103;582:29526;;;;11177:36;;;;582:29526;11177:36;;582:29526;11177:36;22650:171;22744:66;;;;;:::i;:::-;22650:171;;;;;22654:55;;;;;10732:97;582:29526;;;10786:32;;;;10614:109;582:29526;;;10673:39;;;;10397:103;582:29526;;;10464:25;;;;10181:207;582:29526;;;;;10291:86;;;;10185:35;582:29526;;10185:35;582:29526;;10291:86;;582:29526;;;;;10291:86;582:29526;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;-1:-1:-1;;582:29526:25;;:::i;:::-;778:5:7;582:29526:25;;-1:-1:-1;;;;;582:29526:25;756:44:7;582:29526:25;;;764:10:7;:19;756:44;:::i;:::-;582:29526:25;;;;;;778:5:7;582:29526:25;764:10:7;1424:42;;;;582:29526:25;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;8158:30:24;;582:29526:25;;8083:10:24;582:29526:25;;8103:16:24;582:29526:25;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;8083:10:24;;;;8158:30;582:29526:25;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;;;;8158:30:24;;;;582:29526:25;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;:::i;:::-;;;;;;;;-1:-1:-1;;;;;756:44:7;582:29526:25;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;919:9:26;;;:::i;:::-;1590:5:24;;;;;943:11:26;;:::i;:::-;1590:5:24;943:24:26;:53;;;;;582:29526:25;939:110:26;;;582:29526:25;;;;;1058:30:26;582:29526:25;;;;;778:5:7;-1:-1:-1;;582:29526:25;;;;;;1116:14:26;582:29526:25;;;;;;;;:::i;:::-;1590:5:24;582:29526:25;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;1590:5:24;;;582:29526:25;;;;;;;;;;;1590:5:24;;;582:29526:25;;;;;;;;;1164:49:26;582:29526:25;;;1164:49:26;582:29526:25;;939:110:26;582:29526:25;;;1019:19:26;;;;943:53;971:12;;;;;:::i;:::-;1590:5:24;971:25:26;943:53;;;582:29526:25;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;18115:11;582:29526;;;;:::i;:::-;18115:11;:::i;:::-;582:29526;;;;;;18130:6;582:29526;;;;;;18115:30;18111:93;;582:29526;18231:13;582:29526;-1:-1:-1;;;;;18231:13:25;;;;:::i;:::-;582:29526;18217:10;:27;18213:92;;582:29526;;;;;;18314:17;582:29526;;;18371:25;582:29526;18371:25;;;:::i;:::-;582:29526;;;18314:136;;;;;-1:-1:-1;;;18314:136:25;;582:29526;18314:136;;582:29526;18314:136;;582:29526;;;;;;18398:25;582:29526;;;;;;;;;;;;;;;;18314:136;;;;;;;;582:29526;;;18466:37;582:29526;;;;;;;;;;;;;;;;18466:37;582:29526;;18314:136;;;;;;;;;;;;;;;;;:::i;:::-;;;582:29526;;;;18314:136;;;;;;;;;;;582:29526;;1590:5:24;582:29526:25;;1590:5:24;;;;18213:92:25;582:29526;;;18267:27;;;;18111:93;582:29526;;;;18168:25;-1:-1:-1;;;18168:25:25;;582:29526;18168:25;;582:29526;18168:25;582:29526;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;11101:19:24;582:29526:25;-1:-1:-1;;;;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;6671:15:24;582:29526:25;;;;6649:10:24;;:38;6645:121;;582:29526:25;;17590:12;582:29526;;;;:::i;17590:12::-;582:29526;;;17606:6;582:29526;;;;;;17590:31;17586:94;;17695:23;582:29526;;;;;;17695:23;582:29526;;17606:6;582:29526;;;;;;;;;17586:94;582:29526;;;;17644:25;-1:-1:-1;;;17644:25:25;;582:29526;17644:25;;582:29526;17644:25;6645:121:24;582:29526:25;;;;6710:45:24;;;;582:29526:25;6710:45:24;;582:29526:25;6710:45:24;582:29526:25;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;12453:5;;;;:::i;:::-;582:29526;12474:12;;;582:29526;12474:12;;582:29526;12474:23;12470:89;;12608:25;:15;:12;;:15;:::i;:::-;;:25;582:29526;12635:15;30010:89;12635:15;;;582:29526;12653:14;30029:70;30030:31;12653:14;12635:32;12653:14;;;582:29526;;;12635:32;;:::i;:::-;30030:31;:::i;:::-;30072:14;582:29526;30029:70;;:::i;:::-;30010:89;;:::i;:::-;12697:15;12682:30;;12678:111;;12802:12;582:29526;12802:15;-1:-1:-1;;;;;12802:12:25;;:15;:::i;:::-;;:22;1590:5:24;582:29526:25;12828:10;12802:36;12798:99;;582:29526;12906:28;582:29526;13076:51;582:29526;;12906:28;:::i;:::-;582:29526;;;13301:21:24;582:29526:25;;;;13301:21:24;582:29526:25;13015:12;;;:::i;:::-;582:29526;;;12995:6;582:29526;;;;;;;;;;;;;;;;;;;;;;;;;12608:25;582:29526;;;;;12608:25;582:29526;;;;:::i;:::-;;;;;;13076:51;;;582:29526;;;;;;;;;;;;;;;;;;:::i;12678:111::-;582:29526;;;;12735:43;;;;582:29526;12735:43;;582:29526;12735:43;12470:89;582:29526;;;12520:28;;;;582:29526;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;-1:-1:-1;;;;;7948:18:24;582:29526:25;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;21405:51;582:29526;;;:::i;:::-;-1:-1:-1;;;;;582:29526:25;756:44:7;582:29526:25;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;582:29526:25;;-1:-1:-1;;21326:63:25;582:29526;;;21326:63;582:29526;;;;;;21405:51;582:29526;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;10043:10:24;582:29526:25;;10104:29:24;582:29526:25;;;;;;10155:53:24;;;;10151:180;;-1:-1:-1;10043:10:24;582:29526:25;;;10104:29:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;10415:60:24;;582:29526:25;;;;10415:60:24;582:29526:25;10151:180:24;582:29526:25;;10231:89:24;;;10043:10;582:29526:25;10231:89:24;;582:29526:25;;;;;;;;;;;;;;10231:89:24;;;;;582:29526:25;;;;;;-1:-1:-1;;582:29526:25;;;;;;19790:14;582:29526;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;-1:-1:-1;;;;;582:29526:25;;:::i;:::-;;;;1751:30:26;582:29526:25;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;19455:11;582:29526;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;1422:6:24;582:29526:25;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;756:44:7;-1:-1:-1;;;;;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;7591:38:24;582:29526:25;;;;7553:22:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;7591:38:24;582:29526:25;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;756:44:7;-1:-1:-1;;;;;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;1422:6:24;12676:35;;12672:95;;582:29526:25;;12834:47:24;582:29526:25;;12776:42:24;582:29526:25;;;12776:42:24;582:29526:25;;;;;;12834:47:24;582:29526:25;;12672:95:24;582:29526:25;;;12734:22:24;;;;582:29526:25;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;1309:4:24;582:29526:25;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;20349:12;582:29526;;;;:::i;20349:12::-;582:29526;;;20365:6;582:29526;;;;;;20349:31;20345:94;;20466:14;582:29526;-1:-1:-1;;;;;20466:14:25;;;;:::i;:::-;582:29526;20452:10;:28;20448:93;;582:29526;20558:26;;;;;;;:::i;:::-;582:29526;;;;20635:20;582:29526;;20658:26;;582:29526;20551:143;;;;;;582:29526;;-1:-1:-1;;;20551:143:25;;20620:4;582:29526;20551:143;;582:29526;-1:-1:-1;;;;;582:29526:25;;;;1590:5:24;;;582:29526:25;1590:5:24;;;582:29526:25;;;;;;;1590:5:24;;582:29526:25;;;;20551:143;;;;;;;;582:29526;;;;20635:20;582:29526;;20733:26;;;;:::i;:::-;20704:100;;;;;582:29526;;;;;;;;;;;20704:100;;;;;;;1590:5:24;20704:100:25;;582:29526;;20704:100;;582:29526;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;20704:100;;;;;;;;;;;;;582:29526;20826:26;;;582:29526;20826:26;;:::i;:::-;582:29526;;;;;20819:70;;;;1590:5:24;20819:70:25;;582:29526;20819:70;;582:29526;20819:70;;;;;;;;;;;582:29526;20620:4;;582:29526;;20819:87;20815:146;;20976:44;582:29526;;;;;;;;;;;;;;;;;;;;;;;;:::i;20815:146::-;582:29526;;;20929:21;;;;20819:70;;;;;582:29526;20819:70;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;;582:29526;;1590:5:24;582:29526:25;;1590:5:24;;;;20704:100:25;;;;;;:::i;:::-;582:29526;;20704:100;;;;;582:29526;;;;20704:100;582:29526;;;1590:5:24;;;;;;;;20551:143:25;;;;:::i;:::-;582:29526;;20551:143;;;;;582:29526;;1590:5:24;582:29526:25;;1590:5:24;;;;20345:94:25;582:29526;;;;20403:25;-1:-1:-1;;;20403:25:25;;582:29526;20403:25;;582:29526;20403:25;582:29526;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;-1:-1:-1;;;;;582:29526:25;;:::i;:::-;;;;10800:5:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;-1:-1:-1;;582:29526:25;;;:::i;:::-;;;;;;;;;424:13:23;;;439:15;;;;;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;424:13:23;582:29526:25;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;564:35:23;572:4;;564:35;;582:29526:25;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;540:59:23;;;;:::i;:::-;;617:8;613:51;;582:29526:25;;424:13:23;;;;;613:51;653:10;;;;:::i;:::-;;582:29526:25;;;;634:30:23;;;;;;582:29526:25;634:30:23;;582:29526:25;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;:::i;:::-;9586:13:24;9601:9;;;;;;582:29526:25;;;9586:13:24;9653:20;;;;;:::i;:::-;582:29526:25;9511:10:24;;582:29526:25;;9687:29:24;582:29526:25;;;;;;;;;;9768:52:24;;582:29526:25;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;9511:10:24;;;;9768:52;582:29526:25;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;;;;9768:52:24;;;;582:29526:25;9586:13:24;;582:29526:25;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;20156:6;582:29526;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;-1:-1:-1;;;;;19055:17:25;582:29526;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;9298:53:24;;582:29526:25;;9200:10:24;582:29526:25;;9220:29:24;582:29526:25;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;9200:10:24;;;;9298:53;582:29526:25;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;1363:7:24;582:29526:25;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;-1:-1:-1;;;;;582:29526:25;;:::i;:::-;;;;2977:81:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;-1:-1:-1;;;;;690:20:7;582:29526:25;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;2467:43:24;582:29526:25;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;504:34:11;522:1;582:29526:25;;512:11:11;504:34;:::i;:::-;558:1;582:29526:25;;4522:21;;;;;:::i;:::-;4572:23;;;;;:::i;:::-;582:29526;4632:34;:28;582:29526;;;;4632:28;:::i;:::-;:34;;:::i;:::-;4694:12;-1:-1:-1;;;;;4694:12:25;582:29526;4694:12;;;:::i;:::-;582:29526;4694:46;;;;4786:28;582:29526;;;;4786:28;:::i;:::-;4891:35;;;;;582:29526;;;4891:35;:::i;:::-;4940:37;;;;;;582:29526;;;4940:37;:::i;:::-;14152:27:24;582:29526:25;14152:20:24;;;;:::i;:::-;:27;;:::i;:::-;14208:20;:29;4891:35:25;14208:20:24;;;;:::i;:29::-;14274:20;;;;;:::i;:::-;582:29526:25;;;-1:-1:-1;;;;;582:29526:25;;14350:10:24;:21;14346:112;;4694:46:25;582:29526;;;-1:-1:-1;;;;;582:29526:25;;14350:10:24;14471:23;14467:128;;4694:46:25;14627:20:24;;;;;582:29526:25;14609:15:24;;:38;14605:115;;14751:29;4891:35:25;14751:29:24;;582:29526:25;14609:15:24;;14733:47;14729:133;;582:29526:25;-1:-1:-1;;;;;582:29526:25;;;;14876:16:24;582:29526:25;;;;;;;;;;;;;;;;;14876:72:24;;;;4694:46:25;14872:158:24;;15068:21;;;582:29526:25;15044:21:24;582:29526:25;15044:21:24;;582:29526:25;15044:45:24;15040:147;;15233:22;;582:29526:25;15233:22:24;;;:::i;:::-;-1:-1:-1;;;;;582:29526:25;;15202:54:24;1590:5;15202:54;;582:29526:25;;15202:54:24;;582:29526:25;;15202:16:24;4522:21:25;15202:16:24;-1:-1:-1;;;;;15202:16:24;582:29526:25;15202:54:24;;;;;;;;;;;4694:46:25;15201:55:24;;15197:122;;15366:26;582:29526:25;15366:26:24;;;:::i;:::-;-1:-1:-1;;;;;582:29526:25;;15333:60:24;1590:5;15333:60;;582:29526:25;;15333:60:24;;582:29526:25;;15333:18:24;4522:21:25;15333:18:24;-1:-1:-1;;;;;15333:18:24;582:29526:25;15333:60:24;;;;;;;;;;;4694:46:25;15332:61:24;;15328:130;;-1:-1:-1;;;;;582:29526:25;15472:20:24;;;:43;;;;4694:46:25;15468:101:24;;;-1:-1:-1;;;;;582:29526:25;15582:22:24;;;:49;;;;4694:46:25;15578:109:24;;;15700:14;;;582:29526:25;15700:19:24;15696:76;;15785:12;;;582:29526:25;15785:17:24;15781:74;;4940:37:25;15869:14:24;;582:29526:25;15869:18:24;;;15868:97;;;;4694:46:25;15864:161:24;;;582:29526:25;16059:22:24;;;;;582:29526:25;16059:22:24;;:::i;:::-;522:1:11;13301:21:24;582:29526:25;;;;13301:21:24;582:29526:25;;5063:28;582:29526;;;;;;5063:28;:::i;:::-;:35;582:29526;5134:15;;;:::i;:::-;582:29526;;;;;:::i;:::-;;;;-1:-1:-1;;;;;582:29526:25;;5171:64;;;582:29526;5171:64;582:29526;5171:64;;582:29526;5171:64;4891:35;5171:64;;582:29526;14609:15:24;4940:37:25;5171:64;;582:29526;15785:12:24;5222::25;;582:29526;;5171:64;;582:29526;5159:76;;;:::i;:::-;;;;;:::i;:::-;-1:-1:-1;;;;;;582:29526:25;5304:28;582:29526;;;;5304:28;:::i;:::-;:36;582:29526;5354:26;;5394:22;582:29526;5354:26;582:29526;5354:26;;;:::i;:::-;5394:22;;;:::i;:::-;582:29526;;;;;;;;:::i;:::-;;;;;;5264:259;582:29526;;;5264:259;;582:29526;;4891:35;5264:259;;582:29526;5264:259;4940:37;5264:259;;582:29526;14609:15:24;582:29526:25;5264:259;;582:29526;15700:14:24;5479::25;;582:29526;;5264:259;;582:29526;;5264:259;;582:29526;5551:11;;;:::i;:::-;582:29526;;;5534:6;582:29526;;;;;;15068:21:24;5613::25;;582:29526;2274:488:10;-1:-1:-1;;2274:488:10;582:29526:25;5586:9;;582:29526;2274:488:10;;;;;;;;;5586:9:25;;;582:29526;5586:9;;582:29526;2274:488:10;;;;;;;;;;582:29526:25;;:::i;:::-;5738:22;582:29526;5394:22;;5738;:::i;:::-;5783:20;582:29526;5783:20;;582:29526;;;;;5770:46;;;;:::i;:::-;5818:11;;;;:::i;:::-;5856:22;582:29526;5394:22;;5856;:::i;:::-;-1:-1:-1;;;;;582:29526:25;5915:12;;;;;;:::i;:::-;;;;;:::i;:::-;6134:28;582:29526;;;;6134:28;:::i;:::-;4940:37;6134:41;;;;;:::i;:::-;582:29526;1590:5:24;;;;:::i;:::-;6090:91:25;;;;;:::i;:::-;1590:5:24;;6195:7:25;6191:415;;4694:46;5354:26;;;6689:28;-1:-1:-1;;;;;6623:26:25;582:29526;;5354:26;;6623;:::i;:::-;582:29526;;;;;6689:28;:::i;:::-;:36;582:29526;6616:110;;;;;582:29526;;-1:-1:-1;;;6616:110:25;;-1:-1:-1;;;;;582:29526:25;;;6616:110;;582:29526;6682:4;1590:5:24;;;582:29526:25;1590:5:24;;;582:29526:25;;;;;;;;;1590:5:24;;582:29526:25;;;;6616:110;;;;;;;;4694:46;582:29526;;6742:69;582:29526;;4940:37;582:29526;-1:-1:-1;;;;;522:1:11;582:29526:25;;;;;;1590:5:24;582:29526:25;;;;;;;1590:5:24;582:29526:25;1590:5:24;;582:29526:25;;;1590:5:24;;;582:29526:25;1590:5:24;;;;:::i;:::-;582:29526:25;;4891:35;1590:5:24;;582:29526:25;;1590:5:24;;;582:29526:25;;5586:9;;582:29526;;1590:5:24;;582:29526:25;6742:69;;;6826:14;582:29526;6826:18;4940:37;;-1:-1:-1;;;;;582:29526:25;;;6860:5;582:29526;;;;;;;;;;6860:39;582:29526;;;;;;6860:39;:::i;:::-;582:29526;;6822:166;582:29526;;;;;;;;;;;;;;;;;;;:::i;6822:166::-;-1:-1:-1;;;;;582:29526:25;;;;;;14876:16:24;582:29526:25;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;6822:166;;6616:110;;;;;:::i;:::-;582:29526;;6616:110;;;;582:29526;;;6191:415;6299:50;6236:32;6429:16;6236:32;;;:::i;:::-;582:29526;;6299:50;;:::i;:::-;5394:22;;;6429:16;5394:22;-1:-1:-1;;;;;6370:22:25;582:29526;5394:22;;6370;:::i;:::-;582:29526;6429:16;;:::i;:::-;;;:::i;:::-;6464:10;;6460:136;6191:415;6460:136;-1:-1:-1;;;;;5394:22:25;6500;582:29526;6574:6;5394:22;;6500;:::i;:::-;582:29526;1590:5:24;;582:29526:25;6574:6;;:::i;:::-;6460:136;;;;6191:415;;15864:161:24;582:29526:25;;;15988:26:24;;;;15868:97;582:29526:25;;-1:-1:-1;;;;;582:29526:25;;;;15893:5:24;582:29526:25;;;;;;;;;;;15893:54:24;582:29526:25;15044:21:24;;582:29526:25;;;;;15893:54:24;:::i;:::-;:71;15868:97;;;15781:74;582:29526:25;;;15825:19:24;;;;15696:76;582:29526:25;;;15742:19:24;;;;15578:109;582:29526:25;;;15654:22:24;;;;15582:49;582:29526:25;;-1:-1:-1;;;;;582:29526:25;;15609:21:24;;15582:49;;;15468:101;582:29526:25;;;15538:20:24;;;;15472:43;582:29526:25;;-1:-1:-1;;;;;582:29526:25;;15497:17:24;;15472:43;;;15328:130;582:29526:25;;;15416:31:24;;;;15333:60;;;;582:29526:25;15333:60:24;582:29526:25;15333:60:24;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;582:29526:25;;1590:5:24;582:29526:25;;1590:5:24;;;;15197:122;582:29526:25;;;15279:29:24;;;;15202:54;;;;582:29526:25;15202:54:24;582:29526:25;15202:54:24;;;;;;;:::i;:::-;;;;15040:147;582:29526:25;4572:23;582:29526;;;;15112:64:24;;;;15044:21;582:29526:25;;15112:64:24;;582:29526:25;4522:21;582:29526;;;15112:64:24;14872:158;582:29526:25;;14971:48:24;;;-1:-1:-1;;;;;582:29526:25;;;14971:48:24;;582:29526:25;;;;;;;;;10231:89:24;14876:72;582:29526:25;-1:-1:-1;;;;;582:29526:25;;;;14927:10:24;582:29526:25;;;;;;;;14916:31:24;;14876:72;;14729:133;4522:21:25;582:29526;;;14803:48:24;;;;582:29526:25;14803:48:24;;582:29526:25;14803:48:24;14467:128;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;4891:35;582:29526;;;4891:35;582:29526;;;;4940:37;582:29526;;;;;;;14560:23:24;582:29526:25;;;;4940:37;582:29526;;;;;;:::i;:::-;4940:37;582:29526;;;;;4291:26:27;4296:20;;4291:26;:::i;:::-;582:29526:25;;;;;;4891:35;582:29526;;;;;;;4471:27:27;;582:29526:25;;;;;4461:38:27;582:29526:25;;;4225:288:27;582:29526:25;4225:288:27;;582:29526:25;1387:66:27;582:29526:25;;;1387:66:27;;582:29526:25;4891:35;1387:66:27;;582:29526:25;4940:37;1387:66:27;;582:29526:25;;1387:66:27;;582:29526:25;1387:66:27;;;582:29526:25;1387:66:27;4225:288;;;;;:::i;:::-;582:29526:25;4202:321:27;;14560:23:24;;:::i;:::-;14467:128;;;;582:29526:25;;;;;-1:-1:-1;;;582:29526:25;;;;;4522:21;582:29526;;;;;;14346:112:24;14411:12;582:29526:25;14425:21:24;582:29526:25;;;;:::i;:::-;14411:12:24;:::i;:::-;14425:21;;:::i;:::-;14346:112;;;;;4694:46:25;;;;;582:29526;;;;;;-1:-1:-1;;582:29526:25;;;;;;-1:-1:-1;;;;;12228:15:24;582:29526:25;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;8764:10:24;582:29526:25;;8812:10:24;582:29526:25;;;;;;8842:32:24;;;;8838:123;;-1:-1:-1;8764:10:24;582:29526:25;;;8812:10:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;9016:37:24;;582:29526:25;;;;9016:37:24;582:29526:25;8838:123:24;582:29526:25;;8897:53:24;;;8764:10;582:29526:25;8897:53:24;;582:29526:25;;;;;;;;;;;;;;10231:89:24;582:29526:25;;;;;;-1:-1:-1;;582:29526:25;;;;;;;13013:27:24;582:29526:25;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;-1:-1:-1;;;;;756:44:7;582:29526:25;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;11398:26:24;582:29526:25;1363:7:24;582:29526:25;;;;;;;11380:15:24;:64;11376:143;;582:29526:25;;11578:39:24;582:29526:25;11543:19:24;582:29526:25;;;-1:-1:-1;;11528:34:24;582:29526:25;;;11528:34:24;582:29526:25;;;;;;;;;;;;;;;11578:39:24;582:29526:25;;11376:143:24;582:29526:25;;;;11467:41:24;;;;582:29526:25;11467:41:24;;582:29526:25;11467:41:24;582:29526:25;;;-1:-1:-1;;;582:29526:25;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;19982:37;582:29526;;;756:44:7;-1:-1:-1;;;;;582:29526:25;;;764:10:7;:19;756:44;:::i;:::-;582:29526:25;19934:32;582:29526;;;;;;19982:37;582:29526;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;;12486:51:24;582:29526:25;756:44:7;582:29526:25;;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;12404:23:24;;;:::i;:::-;582:29526:25;;12438:32:24;582:29526:25;;;;;;;;12438:32:24;582:29526:25;;;;;;12486:51:24;582:29526:25;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;1210:49:24;582:29526:25;;;;;;;;;;;:::i;:::-;;504:34:11;522:1;582:29526:25;;512:11:11;504:34;:::i;:::-;558:1;582:29526:25;;16197:12;582:29526;;;;:::i;16197:12::-;582:29526;;;;;;16213:6;582:29526;;;;;;16197:31;16193:94;;16321:32;16339:14;;;582:29526;16321:15;;;582:29526;16321:32;:::i;:::-;582:29526;-1:-1:-1;;;;;16391:26:25;;;582:29526;16391:26;;;:::i;:::-;582:29526;16514:15;;16497:32;;16493:101;;16603:24;582:29526;16641:12;582:29526;16641:12;;;522:1:11;16641:12:25;;;;:::i;:::-;:24;;;16637:767;522:1:11;;;16730:12:25;;;;;:::i;:::-;582:29526;;;16730:22;;;;;:::i;:::-;16681:100;;;;;;582:29526;;-1:-1:-1;;;16681:100:25;;16723:4;582:29526;16681:100;;582:29526;-1:-1:-1;;;;;582:29526:25;;;;1590:5:24;;;582:29526:25;16754:26;;582:29526;1590:5:24;;;582:29526:25;;;;;1590:5:24;;582:29526:25;;;;16681:100;;;;;;;;16637:767;582:29526;;522:1:11;582:29526:25;16801:23;582:29526;;;;;;16801:23;582:29526;;16213:6;582:29526;;;;;;;16637:767;582:29526;;;;;;;;;;;;;;:::i;16681:100::-;;;;;:::i;:::-;582:29526;;16681:100;;;;582:29526;;1590:5:24;582:29526:25;;1590:5:24;;;;582:29526:25;;;-1:-1:-1;;;582:29526:25;;;;;;16637:767;582:29526;;;;16985:15;582:29526;17003:26;;;;582:29526;16928:102;;;;;;582:29526;;-1:-1:-1;;;16928:102:25;;16970:4;582:29526;16928:102;;582:29526;;;;;;;;-1:-1:-1;;;;;582:29526:25;1590:5:24;;;582:29526:25;1590:5:24;;;582:29526:25;;;;;1590:5:24;582:29526:25;;16928:102;;;;;;;;;;;;;;;;16637:767;582:29526;;;;17209:22;582:29526;;16985:15;582:29526;17209:22;;;:::i;:::-;582:29526;;;;17058:260;;;;;1590:5:24;17058:260:25;;;582:29526;17058:260;;582:29526;;;;;;;;;;;;;;;;;;;;;17294:10;582:29526;;;;;;;17058:260;;;;;;;;;;;16637:767;17044:274;582:29526;522:1:11;17044:274:25;;17338:55;17044:274;582:29526;16985:15;582:29526;;;;;;;;;;;;;17338:55;16637:767;;17058:260;;;;;;;;;;;;:::i;:::-;;;582:29526;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;522:1:11;582:29526:25;;;;17338:55;582:29526;;;;;:::i;:::-;17058:260;;;;;;;16928:102;;;;;;;;;:::i;:::-;582:29526;;16928:102;;;;;;;;16493:101;582:29526;;;;16552:31;;;;582:29526;16552:31;;582:29526;16552:31;582:29526;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;-1:-1:-1;;;;;582:29526:25;;:::i;:::-;;;;2690:68:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;-1:-1:-1;;;;;7778:16:24;582:29526:25;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;-1:-1:-1;;;;;21161:20:25;582:29526;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;:::i;:::-;;;:::i;:::-;;;;-1:-1:-1;;;;;582:29526:25;;;;;;18658:37;1590:5:24;18658:37:25;;;582:29526;18658:37;;582:29526;;18658:37;;;582:29526;18658:37;;;;;;;;;;;;;582:29526;-1:-1:-1;582:29526:25;;18707:4;18658:54;18654:112;;582:29526;;;;18776:17;582:29526;;;;;;18776:77;;;;;-1:-1:-1;;;18776:77:25;;582:29526;;;18776:77;;582:29526;;;;;;;;;;;;;;;;;;;;18776:77;;;;;;;;582:29526;-1:-1:-1;;582:29526:25;;;-1:-1:-1;;;;;582:29526:25;;;;;;;;;1590:5:24;;;582:29526:25;1590:5:24;;582:29526:25;;;;18869:48;;582:29526;1590:5:24;;;18869:48:25;1590:5:24;18776:77:25;;;;;;;;;;;;:::i;:::-;;;582:29526;;;;18776:77;;;;;;;;;18654:112;582:29526;;;18735:20;;;;18658:37;;;;;;;;;;;;;;:::i;:::-;;;;582:29526;;;;;;-1:-1:-1;;582:29526:25;;;;;-1:-1:-1;;;;;756:44:7;582:29526:25;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;11758:24:24;582:29526:25;11758:43:24;1309:4;11758:43;;11754:130;;12058:45;11914:25;582:29526:25;11914:25:24;;;;:::i;:::-;;:::i;:::-;582:29526:25;;:::i;:::-;;;;-1:-1:-1;;11951:37:24;582:29526:25;;;11951:37:24;582:29526:25;;;;12027:15:24;11998:44;582:29526:25;;;;;;11758:24:24;582:29526:25;;;12058:45:24;582:29526:25;;11754:130:24;11758:24;582:29526:25;;;11824:49:24;;;;582:29526:25;11824:49:24;;582:29526:25;11824:49:24;582:29526:25;;;;;;-1:-1:-1;;582:29526:25;;;;;1530:45:26;582:29526:25;;;:::i;:::-;-1:-1:-1;;;;;582:29526:25;756:44:7;582:29526:25;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;582:29526:25;;;;1423:30:26;582:29526:25;;;;;-1:-1:-1;;582:29526:25;;;;;1489:14:26;582:29526:25;;;;;;;;;;;;1530:45:26;582:29526:25;;;;;;;;-1:-1:-1;;582:29526:25;;;;;19632:31;582:29526;;;756:44:7;-1:-1:-1;;;;;582:29526:25;;;764:10:7;:19;756:44;:::i;:::-;582:29526:25;19590:26;582:29526;;;;;;19632:31;582:29526;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;:::i;:::-;8392:13:24;8407:9;;;;;;582:29526:25;;;8392:13:24;8451:12;;;;;:::i;:::-;582:29526:25;8325:10:24;;582:29526:25;;8477:16:24;582:29526:25;;;;;;;;;;8535:29:24;;582:29526:25;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;8325:10:24;;;;8535:29;582:29526:25;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;;;;8535:29:24;;;;582:29526:25;8392:13:24;;582:29526:25;;;;;;-1:-1:-1;;582:29526:25;;;;;;11252:26:24;582:29526:25;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;17900:38;582:29526;;17900:38;:::i;:::-;582:29526;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;:::i;:::-;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;504:34:11;522:1;582:29526:25;;512:11:11;504:34;:::i;:::-;558:1;582:29526:25;;13333:19;582:29526;;;;;13333:19;:::i;:::-;582:29526;;;13390:19;582:29526;;13390:19;582:29526;;;;;;;;-1:-1:-1;;;;;13569:13:25;582:29526;;;;;;13569:13;:::i;:::-;582:29526;13555:10;:27;13551:150;;582:29526;13711:29;582:29526;;;;;;;;;:::i;:::-;13711:29;;:::i;:::-;582:29526;13884:19;582:29526;;;;;13884:19;:::i;:::-;:34;582:29526;;;;;;;;13880:215;;582:29526;-1:-1:-1;;;;;14112:25:25;;582:29526;;;;14112:25;;:::i;:::-;582:29526;14167:13;582:29526;;;;;;14167:13;:::i;:::-;14105:103;;;;;582:29526;;-1:-1:-1;;;14105:103:25;;14160:4;582:29526;14105:103;;;582:29526;;;;-1:-1:-1;;;;;582:29526:25;;;;13390:19;1590:5:24;;;582:29526:25;;;;;;;;14182:25;;;582:29526;1590:5:24;;;582:29526:25;;;;;;1590:5:24;;582:29526:25;;;;14105:103;;;;;;;;582:29526;-1:-1:-1;14367:71:25;14405:32;:19;582:29526;;;;;14405:19;:::i;:::-;582:29526;14405:32;;;;:::i;:::-;582:29526;;;;;;;;;;:::i;:::-;14367:71;:::i;:::-;582:29526;;;:::i;:::-;14560:20;582:29526;14560:20;;582:29526;14560:24;;14594:28;;582:29526;-1:-1:-1;;;;;14653:21:25;;582:29526;;;;14653:21;;:::i;:::-;582:29526;14685:26;14726:13;;14721:996;14726:13;14745:11;582:29526;;;;;14745:11;;;;582:29526;14745:11;:::i;:::-;14741:22;;;;;;;14803:11;582:29526;;;;;14745:11;;;;582:29526;14803:11;:::i;:::-;582:29526;;;;;;;;;;;;;;:::i;:::-;14854:22;;1423:69:28;582:29526:25;14854:22;;582:29526;1440:19:28;14889:13:25;;;582:29526;14904:34;14922:16;;;582:29526;14904:15;:34;:::i;:::-;1440:19:28;;:::i;:::-;1423:69;;:::i;:::-;1590:5:24;14967:51:25;1590:5:24;;;582:29526:25;14854:22;;582:29526;14967:51;:::i;:::-;15033:27;;15074:22;;15110:269;;;14726:13;14854:22;;;15429:85;15619:27;14854:22;;15429:79;15594:9;14854:22;15429:61;:98;14854:22;15429:47;582:29526;522:1:11;14854:22:25;;582:29526;;15454:22;;582:29526;15429:47;;:::i;:::-;:61;:::i;:98::-;582:29526;;-1:-1:-1;;;;;582:29526:25;15564:13;582:29526;;;;;;15564:13;:::i;:::-;15579;;1590:5:24;582:29526:25;15594:9;;;:::i;15619:27::-;582:29526;;14726:13;;;15110:269;522:1:11;14560:20:25;;;15594:9;14560:20;;;15429:85;15619:27;14560:20;;15429:79;14560:20;15429:61;:98;14560:20;;15252:46;15167:54;582:29526;15316:48;14560:20;;582:29526;15167:54;;;:::i;:::-;15252:46;;;:::i;:::-;15336:28;;;;;:::i;15316:48::-;15110:269;;;;;;;;;;;;;;;;;582:29526;-1:-1:-1;;;582:29526:25;;;;;13390:19;582:29526;;14741:22;;;;;;;15732:52;582:29526;14741:22;;582:29526;;;;;;;;;;;;;;;15732:52;15795:124;;14721:996;582:29526;;;;15968:6;582:29526;;;;;;;522:1:11;582:29526:25;;;;15795:124;-1:-1:-1;;;;;15853:13:25;582:29526;15891:16;582:29526;;;;;15853:13;:::i;:::-;1590:5:24;;582:29526:25;15891:16;;:::i;:::-;15795:124;;;;;;14105:103;;;;:::i;:::-;;;;13880:215;-1:-1:-1;;;;;13934:17:25;582:29526;;;-1:-1:-1;;;;;582:29526:25;13984:13;582:29526;;;;;;13984:13;:::i;:::-;582:29526;13999:25;;582:29526;;;;13999:25;;:::i;:::-;582:29526;;;;13934:150;;;;;-1:-1:-1;;;13934:150:25;;582:29526;;13934:150;;582:29526;;13390:19;582:29526;;;13390:19;582:29526;;;;14026:25;582:29526;13999:25;582:29526;;;;;;;;522:1:11;582:29526:25;;;;13934:150;;;;;;;;13880:215;;;;13934:150;582:29526;13934:150;;;;;;;;;;;;:::i;:::-;;;582:29526;;;;13934:150;;;;;;;;13551;13614:13;582:29526;;;;;;13614:13;:::i;:::-;13629:19;582:29526;;;;;13629:19;:::i;:::-;582:29526;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4798:38:27;582:29526:25;;4684:213:27;1623:66;582:29526:25;4684:213:27;;582:29526:25;1623:66:27;;;582:29526:25;;1623:66:27;;582:29526:25;;;;;1623:66:27;;;582:29526:25;1623:66:27;4684:213;;582:29526:25;1623:66:27;582:29526:25;;;;1623:66:27;582:29526:25;;;;;;1623:66:27;582:29526:25;;;;;;;4684:213:27;;;4661:246;13657:32:25;;;582:29526;;;13657:32;;;;582:29526;13657:32;:::i;:::-;;;;;:::i;:::-;13551:150;;582:29526;13390:19;582:29526;-1:-1:-1;;;582:29526:25;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;-1:-1:-1;;;;;582:29526:25;;:::i;:::-;;;;2839:45:24;582:29526:25;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;7375:27;7437:5;;;;582:29526;;;7302:26;582:29526;7437:5;:::i;:::-;7500;;;582:29526;;;;7500:5;:::i;:::-;-1:-1:-1;;;;;7556:26:25;582:29526;;;7556:26;;:::i;:::-;582:29526;7355:10;7542:40;;;7712:65;;;22401:18;22421:28;;;22476:27;582:29526;;;:::i;:::-;22556:45;;22612:28;22654:20;;;;;582:29526;22654:24;;;:55;;;582:29526;22650:171;;582:29526;22831:19;22849:1;22831:19;;;22865:13;;;;;22884:35;;582:29526;;22884:35;582:29526;;;;22884:35;:::i;:::-;22880:46;;;;;;;;23423:15;22966;;;582:29526;22966:12;;;;:15;:::i;:::-;;582:29526;23280:251;23021:38;:35;22884;582:29526;;22884:35;582:29526;;;;23021:35;:::i;:::-;:38;;;;:::i;:::-;582:29526;23315:26;582:29526;;;7556:26;23315;:::i;:::-;23359:22;-1:-1:-1;;;;;582:29526:25;23359:22;;;1590:5:24;582:29526:25;23423:15;;;;;;582:29526;23280:251;23423:32;23280:251;;23441:14;;;;;582:29526;23423:32;;:::i;23280:251::-;23593:13;;;;;;;;;582:29526;23636:17;582:29526;29659:31;;;;;;;22865:13;29659:142;;;22865:13;29642:234;;;;23720:19;23806:35;23669:37;;23855:30;23669:37;23753:39;23669:37;;:::i;:::-;23720:19;;:::i;:::-;23753:39;;;;:::i;:::-;;;23806:35;:::i;23855:30::-;23928:3;;;22849:1;582:29526;23928:3;;22865:13;;;23928:3;22865:13;;29659:142;582:29526;;;29712:30;23608:26;582:29526;;23608:26;582:29526;29712:30;;:::i;:::-;1590:5:24;1744:340:10;;;;;;;;;;;;;;29711:89:25;29659:142;;;;1744:340:10;;;;29659:31:25;29680:10;;;;-1:-1:-1;29659:31:25;;22880:46;;;;;24061:11;22880:46;;-1:-1:-1;;;;;582:29526:25;23359:22;;;1590:5:24;582:29526:25;24061:11;;;582:29526;24015:26;582:29526;;;7556:26;24015;:::i;24061:11::-;22849:1;7593:184;;7791:19;;7787:77;;7945:693;;;;;582:29526;8020:35;582:29526;;8020:35;582:29526;8132:15;8101:46;8132:15;8101:28;582:29526;;8101:28;582:29526;8101:46;:::i;:::-;8259:43;8165:32;23441:14;;;;582:29526;23423:15;;;;582:29526;8165:32;;:::i;:::-;8281:21;;;;;582:29526;8259:43;;:::i;:::-;582:29526;;:::i;:::-;19027:44:24;;;;;:::i;:::-;:48;;19026:329;;;;7945:693:25;19008:380:24;;;;;;;;7945:693:25;19008:414:24;;;;7945:693:25;18991:500:24;;;;7945:693:25;8671:61;8709:23;582:29526;;8709:23;582:29526;8671:35;582:29526;;8671:35;582:29526;8671:61;:::i;:::-;8743:119;;;7945:693;8876:25;;;;;;23359:22;9025:25;23359:22;-1:-1:-1;;;;;582:29526:25;23359:22;;;1590:5:24;582:29526:25;8981:14;9025:25;-1:-1:-1;;;;;8981:14:25;;1590:5:24;582:29526:25;;8997:26;582:29526;;;7556:26;8997;:::i;:::-;9025:25;;:::i;:::-;22849:1;13301:21:24;582:29526:25;;;;13301:21:24;582:29526:25;9416:73;9374:15;;;:::i;:::-;582:29526;;;;;;;;:::i;9416:73::-;9400:89;;;:::i;:::-;;;;;:::i;:::-;;582:29526;22966:12;;;9499:25;9551:66;9552:33;23423:15;;;;582:29526;9552:15;:33;:::i;:::-;9589:28;582:29526;;9589:28;582:29526;9551:66;;:::i;:::-;23441:14;;;;582:29526;8671:35;582:29526;;8671:35;582:29526;9627:21;;;;582:29526;9717:12;;;;:::i;:::-;582:29526;;;9697:6;22654:20;582:29526;;;;;;;;7302:26;582:29526;;;;;;;9776:102;582:29526;;;;;;;;;;;;7302:26;582:29526;22654:20;582:29526;;;;;;;;23423:15;582:29526;;;;;;23423:15;582:29526;;;;;:::i;:::-;8709:23;582:29526;;8709:23;582:29526;9627:21;582:29526;;;9776:102;;;582:29526;;;;;;22654:20;582:29526;;;;;;;;;;;:::i;8872:412::-;9085:25;;;9081:203;;8872:412;;;;;9081:203;9234:25;23359:22;-1:-1:-1;;;;;582:29526:25;23359:22;;;1590:5:24;582:29526:25;;9234:25;9190:26;582:29526;;;7556:26;9190;:::i;:::-;9218:14;-1:-1:-1;;;;;9218:14:25;;1590:5:24;582:29526:25;9234:25;;:::i;:::-;9081:203;;;;8743:119;8777:36;;;;;;;;:::i;:::-;8827:24;8743:119;;;18991:500:24;582:29526:25;;;19454:26:24;;;;19008:414;19393:28;;-1:-1:-1;19008:414:24;;;;:380;582:29526:25;;8320:23;;582:29526;19374:13:24;;;-1:-1:-1;19008:380:24;;19026:329;19127:34;;;;:73;:34;19164:36;19127:34;;19254;19127;;;:::i;:::-;582:29526:25;23608:26;582:29526;;23608:26;582:29526;19164:36:24;:::i;:::-;19127:73;;:::i;:::-;19254:34;;:::i;:::-;1744:340:10;;;;1590:5:24;1744:340:10;;;;;;;22654:20:25;1590:5:24;19317:16;;582:29526:25;1744:340:10;;;19126:207:24;19026:329;;;;;;;7945:693:25;8388:14;;;-1:-1:-1;;;;;8388:14:25;;1590:5:24;582:29526:25;7355:10;8378:24;;8374:264;582:29526;;;;;;8425:35;;;;8374:264;22849:1;582:29526;8542:26;582:29526;;;7556:26;8542;:::i;:::-;582:29526;;;;;;;;:::i;:::-;5800:30:27;582:29526:25;5845:13:27;;;22849:1:25;;;5840:240:27;582:29526:25;;;;6245:22:27;22654:20:25;6245:22:27;;582:29526:25;6285:22:27;-1:-1:-1;;;;;582:29526:25;6285:22:27;;1590:5:24;582:29526:25;6325:19:27;582:29526:25;6325:19:27;;582:29526:25;;22654:20;582:29526;;;;6362:28:27;23423:15:25;6408:31:27;;582:29526:25;6457:22:27;23441:14:25;6457:22:27;;582:29526:25;6497:30:27;6545:24;582:29526:25;6497:30:27;;582:29526:25;6545:24:27;;582:29526:25;;;;6119:464:27;2678:66;22654:20:25;6119:464:27;;582:29526:25;;2678:66:27;;582:29526:25;;2678:66:27;;582:29526:25;5864:31:27;2678:66;;582:29526:25;23423:15;2678:66:27;;582:29526:25;23441:14;2678:66:27;;582:29526:25;;2678:66:27;;582:29526:25;6545:24:27;2678:66;;582:29526:25;;2678:66:27;;582:29526:25;2678:66:27;;;;;582:29526:25;6119:464:27;;582:29526:25;2678:66:27;582:29526:25;;;;2678:66:27;582:29526:25;;;;;;;;;2678:66:27;8598:28:25;582:29526;;;;8598:28;;582:29526;22654:20;582:29526;;6119:464:27;;6096:497;8598:28:25;;:::i;:::-;7945:693;;582:29526;;;-1:-1:-1;;;582:29526:25;;;;;;5845:13:27;5864:31;;;;;;582:29526:25;;5860:42:27;;;;;5975:34;5939:71;582:29526:25;5975:34:27;;;;;:::i;:::-;582:29526:25;;;;;;;;;;5939:71:27;;22654:20:25;5939:71:27;;;582:29526:25;;;:::i;:::-;;;;22654:20;582:29526;;;5939:71:27;22654:20:25;5939:71:27;;;;;;;:::i;:::-;582:29526:25;;5845:13:27;;;;5860:42;;;;;;;7787:77:25;582:29526;;;7833:20;;;;22650:171;22744:66;582:29526;;;22744:23;582:29526;;22744:23;582:29526;22744:66;:::i;:::-;22650:171;;;;22654:55;582:29526;22682:23;582:29526;;22682:23;582:29526;22682:27;;22654:55;;582:29526;;;;;;-1:-1:-1;;582:29526:25;;;;;19286:45;582:29526;;;:::i;:::-;-1:-1:-1;;;;;582:29526:25;756:44:7;582:29526:25;778:5:7;582:29526:25;;764:10:7;:19;756:44;:::i;:::-;582:29526:25;;-1:-1:-1;;19211:59:25;582:29526;;;19211:59;582:29526;;;;;;19286:45;582:29526;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;-1:-1:-1;;;;;582:29526:25;;:::i;:::-;;;;3144:64:24;582:29526:25;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;7310:45:9;582:29526:25;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;1761:18:24;582:29526:25;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;1761:18:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;582:29526:25;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;1761:18:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;15068:21:24;582:29526:25;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;-1:-1:-1;;582:29526:25;;;;:::o;:::-;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;582:29526:25;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;582:29526:25;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;582:29526:25;;;;;;:::o;:::-;;;-1:-1:-1;;;;;582:29526:25;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;:::o;:::-;;;;-1:-1:-1;;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;-1:-1:-1;;582:29526:25;;;;:::o;:::-;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;6863:170:24:-;-1:-1:-1;6938:13:24;6955:16;6938:33;6955:16;;6974:24;;6863:170;:::o;6938:88::-;582:29526:25;;19858:4:24;582:29526:25;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;19842:22:24;;582:29526:25;;;19701:271:24;;;582:29526:25;19729:95:24;582:29526:25;;;;;;19882:14:24;582:29526:25;;;;6938:13:24;582:29526:25;;;;19953:4:24;582:29526:25;;;;;19701:271:24;;;;;:::i;:::-;582:29526:25;19678:304:24;;6863:170;:::o;582:29526:25:-;;;;;19858:4:24;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;582:29526:25;;;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;19858:4:24;582:29526:25;;;;;;;;;;;;;;;:::i;:::-;18619:8:24;582:29526:25;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;14515:12;582:29526;-1:-1:-1;;;;;582:29526:25;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;-1:-1:-1;;;582:29526:25;;;;;;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;:::o;:::-;;-1:-1:-1;;;;;582:29526:25;;;;;;;:::o;215:156:22:-;-1:-1:-1;;;;;582:29526:25;291:22:22;287:78;;215:156::o;287:78::-;336:18;582:29526:25;;336:18:22;;;;582:29526:25;;;;;;;:::i;:::-;;;;-1:-1:-1;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;:::o;:::-;;;;-1:-1:-1;;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;;;-1:-1:-1;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;7809:1;582:29526;;;-1:-1:-1;582:29526:25;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::o;1590:5:24:-;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;582:29526:25;;;;;;;:::i;:::-;1590:5:24;;;;;;;;;;;;;;;;-1:-1:-1;1590:5:24;;582:29526:25;;;;1590:5:24:o;2096:672:10:-;;582:29526:25;2274:488:10;;-1:-1:-1;;2274:488:10;;;;;;;;;;;;;;;;;;;;;2096:672;:::o;:::-;;1590:5:24;2274:488:10;;-1:-1:-1;;2274:488:10;;;;;;;;;;;;;;;;;;;;;2096:672;:::o;1328:1782:12:-;;-1:-1:-1;1328:1782:12;;1532:1521;1328:1782;1532:1521;1328:1782;1532:1521;;;-1:-1:-1;;;1532:1521:12;;-1:-1:-1;;;;;1532:1521:12;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1532:1521:12;;;;;582:29526:25;;;1328:1782:12:o;582:29526:25:-;1532:1521:12;;582:29526:25;-1:-1:-1;;;582:29526:25;;1532:1521:12;;582:29526:25;;;;1532:1521:12;582:29526:25;;;;1532:1521:12;582:29526:25;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;:::o;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;-1:-1:-1;582:29526:25;;;;;;;;;;;:::o;16489:1042:24:-;16606:31;;;582:29526:25;;16651:36:24;;;;16707:24;;16703:96;;16489:1042::o;16703:96::-;16758:26;582:29526:25;;16758:26:24;;;;16647:878;16855:21;;;;;;;;;;:::i;:::-;16901:20;;;;:37;;;16647:878;16897:236;;;16965:26;582:29526:25;;16965:26:24;;;;16897:236;582:29526:25;17017:20:24;;17016:76;;;16897:236;17012:121;;16897:236;;;;;-1:-1:-1;582:29526:25;;;17146:369:24;17166:19;;;;;;16647:878;;;;;;;;16489:1042::o;17151:13::-;17244:21;;;;:::i;:::-;582:29526:25;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;:::i;:::-;;-1:-1:-1;;;;;1590:5:24;;;582:29526:25;17388:23:24;;;;;;17286:143;;;;;;582:29526:25;;;-1:-1:-1;;582:29526:25;;;;;;;17286:143:24;1590:5;17286:143;;582:29526:25;;;;17286:143:24;;;;;582:29526:25;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;1590:5:24;582:29526:25;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;582:29526:25;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;582:29526:25;;;;;;;;;;;;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;:::i;:::-;17286:143:24;;;;;;;;;;;582:29526:25;17286:143:24;;;;582:29526:25;;;17151:13:24;;;;;;;;17286:143;;;;:::i;:::-;;;;;582:29526:25;1590:5:24;-1:-1:-1;1590:5:24;;;;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;17012:121:24;17112:7;;;;;:::o;17016:76::-;17043:21;;;;;:::i;:::-;582:29526:25;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;-1:-1:-1;;;;;582:29526:25;17043:34:24;582:29526:25;;17043:34:24;:::i;:::-;582:29526:25;17043:48:24;17016:76;;16901:37;16925:13;;;;16901:37;;582:29526:25;;;;;;;;;;;;;;;;;;:::o;17652:566:24:-;;;;;3515:233:4;17780:18:24;;:::i;:::-;3515:233:4;;;;;;;;;;;;;;;;;17836:19:24;;;:23;;17832:380;17836:19;;;582:29526:25;17879:57:24;582:29526:25;;;-1:-1:-1;;;;;582:29526:25;;1590:5:24;;;;;;;17879:57;;;;;;;582:29526:25;;;;;;;;;;;:::i;:::-;17879:57:24;582:29526:25;;17879:57:24;;;;;;;;582:29526:25;17879:57:24;-1:-1:-1;17879:57:24;;;17832:380;582:29526:25;;17879:76:24;17875:145;;17832:380;17652:566::o;17875:145::-;17879:57;582:29526:25;;17982:23:24;;;;17879:57;;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;582:29526:25;;1590:5:24;-1:-1:-1;1590:5:24;;;;;17832:380;1590:5;;3915:8:3;1590:5:24;3859:27:3;1590:5:24;;;;;:::i;:::-;3859:27:3;;:::i;:::-;3915:8;;;;;:::i;:::-;-1:-1:-1;;;;;582:29526:25;;;;;18117:20:24;18113:89;;17832:380;17652:566::o;2129:766:3:-;582:29526:25;;;2129:766:3;2276:2;2256:22;;2276:2;;2739:25;2539:180;;;;;;;;;;;;;;;-1:-1:-1;2539:180:3;2739:25;;:::i;:::-;2732:32;;;;;:::o;2252:637::-;2795:83;;2811:1;2795:83;2815:35;2795:83;;:::o;5140:1530::-;;;6199:66;6186:79;;6182:164;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;6457:24:3;;;;;;;;;;;;;;-1:-1:-1;;;;;582:29526:25;;6495:20:3;6491:113;;6614:49;;5140:1530;:::o;6491:113::-;6531:62;;;6457:24;6531:62;;:::o;6457:24::-;582:29526:25;;1590:5:24;;;;;;;;6182:164:3;6281:54;;;6297:1;6281:54;6301:30;6281:54;;:::o;7196:532::-;582:29526:25;;;;;;7282:29:3;;;7327:7;;:::o;7278:444::-;582:29526:25;7378:38:3;;582:29526:25;;;;;7439:23:3;;;;7374:348;7492:35;7483:44;;7492:35;;582:29526:25;;;;7550:46:3;;;;582:29526:25;7550:46:3;;582:29526:25;7550:46:3;7479:243;7626:30;7617:39;7613:109;;7479:243;7196:532::o;7613:109::-;582:29526:25;;;;7679:32:3;;;;582:29526:25;7679:32:3;;582:29526:25;7679:32:3;582:29526:25;-1:-1:-1;;;7291:20:3;582:29526:25;;;;;7291:20:3;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;:::o;:::-;;;;-1:-1:-1;;582:29526:25;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;-1:-1:-1;;;;;582:29526:25;;;;;;;:::o;24827:1508::-;;;;;;;582:29526;25161:19;582:29526;25230:23;;25318:17;30010:89;25318:17;;;582:29526;30029:70;30030:31;582:29526;;30030:31;;;:::i;30010:89::-;25375:15;25360:30;;25356:111;;25484:23;;;;582:29526;25484:42;582:29526;;;25484:42;:::i;:::-;582:29526;;;25579:14;25553:40;25579:14;;;582:29526;;;25553:40;;:::i;:::-;25607:10;;;25603:76;;582:29526;1440:19:28;25791:20:25;582:29526;25740:35;1423:69:28;582:29526:25;;;;25375:15;25740:35;:::i;1423:69:28:-;25791:20:25;;582:29526;25791:24;25787:131;;24827:1508;25958:23;;;;582:29526;;;;;1744:340:10;;;-1:-1:-1;;1744:340:10;;;;;;;;;;;;;;;;;;;;;;26038:20:25;26034:126;;24827:1508;26170:38;;26286:41;26170:38;26286:23;26170:38;26286:41;26170:38;;;;25791:20;26170:38;;:::i;:::-;582:29526;;-1:-1:-1;;;;;26270:14:25;;;;1590:5:24;582:29526:25;26286:23;;:::i;:41::-;582:29526;;26286:41;:::i;:::-;24827:1508::o;26034:126::-;25791:20;26074:46;;;26286:23;26170:38;26074:46;26286:41;26074:46;;26286:41;26074:46;;;;;;;:::i;:::-;582:29526;;;26034:126;;;;;;;;;;;;25787:131;25849:58;;;;;;:::i;:::-;25787:131;;;;25603:76;-1:-1:-1;25633:35:25;;-1:-1:-1;25633:35:25;;-1:-1:-1;25647:1:25;;-1:-1:-1;25633:35:25;;-1:-1:-1;;;;;;;25633:35:25:o;26341:299::-;26429:12;;;:::i;:::-;582:29526;-1:-1:-1;582:29526:25;26445:6;582:29526;;;-1:-1:-1;582:29526:25;;26429:31;26425:94;;26532:15;;26550:14;26532:15;:32;:15;;582:29526;26550:14;;582:29526;26532:32;;:::i;:::-;26567:15;-1:-1:-1;26528:106:25;;26341:299::o;26528:106::-;26605:18;582:29526;;26605:18;;;;26646:917;;26809:35;;;582:29526;26809:40;26808:129;;;;;26646:917;26791:221;;;27043:34;;;582:29526;27025:15;;:52;27021:156;;582:29526;27274:26;;582:29526;;27274:26;;;:::i;:::-;-1:-1:-1;;;;;582:29526:25;;-1:-1:-1;;582:29526:25;;;27327:29;582:29526;;27274:26;582:29526;;;;;;;;27274:26;582:29526;;;;27327:130;;;;;26646:917;27310:247;;;;;26646:917;;:::o;27310:247::-;27274:26;582:29526;27489:57;;;-1:-1:-1;;;;;582:29526:25;;;;27489:57;;;582:29526;;;;;;;;;;10231:89:24;27327:130:25;27274:26;582:29526;;;;;27401:29;582:29526;;;;27401:56;;27327:130;;;;;27021:156;582:29526;;;;27100:66;;;;;;;582:29526;27100:66;26808:129;26871:12;;;;;582:29526;26894:35;;;;;;:::i;:::-;26871:65;;;;;26808:129;;;27569:506;;582:29526;;:::i;:::-;;-1:-1:-1;;;;;27830:26:25;;;1590:5:24;582:29526:25;27887:35;28031:26;27887:35;;;582:29526;28031:26;;582:29526;;27830:26;582:29526;;;;;:::i;:::-;;;27772:296;;;582:29526;27830:26;27772:296;;582:29526;27772:296;;;582:29526;27994:15;27772:296;;;582:29526;27887:35;27772:296;;582:29526;27569:506;:::o;28081:175::-;28237:11;582:29526;;;;;;;;;28185:64;582:29526;;;28185:64;;:::i;28568:332::-;;28752:20;;;582:29526;28752:24;;:36;;;28568:332;28748:146;;28568:332;;;;;:::o;28748:146::-;28878:4;582:29526;-1:-1:-1;;;;;1590:5:24;;;582:29526:25;;;28878:4;:::i;:::-;28748:146;;;;;;28752:36;28780:8;;;;28752:36;;582:29526;;;1590:5:24;;;582:29526:25;;;;;:::o;:::-;971:12:26;582:29526:25;1590:5:24;;;582:29526:25;;;;;:::o;1984:626:26:-;582:29526:25;;3446:25:26;2176:65;;-1:-1:-1;2299:10:26;;582:29526:25;;2268:30:26;582:29526:25;;;;;;;;2267:43:26;582:29526:25;;;2267:208:26;;1984:626;2250:304;;;;;582:29526:25;1590:5:24;2299:10:26;;582:29526:25;;2570:14:26;582:29526:25;;;;1590:5:24;1984:626:26;:::o;2250:304::-;2507:36;582:29526:25;;2507:36:26;;;;2267:208;582:29526:25;;;;;;;;;1590:5:24;;;;;582:29526:25;1590:5:24;2330:76:26;;;;582:29526:25;2330:76:26;;;582:29526:25;;;;;;:::i;:::-;;;;;;-1:-1:-1;;582:29526:25;;;;;;;;;:::i;:::-;2330:76:26;2299:10;;;2330:76;;;;;;;;582:29526:25;2330:76:26;;;;;2267:208;582:29526:25;;2330:145:26;;2267:208;;;;;2330:76;;;;582:29526:25;2330:76:26;;;;;;;;;:::i;:::-;;;;2176:65;2222:8;;;-1:-1:-1;2222:8:26;:::o;2765:581::-;;;1590:5:24;;;;;:::i;:::-;582:29526:25;3446:25:26;2929:65;;-1:-1:-1;3052:10:26;;582:29526:25;;3021:30:26;582:29526:25;;;;;;;;;;3020:43:26;582:29526:25;;;3020:190:26;;2765:581;3003:286;;;;;3052:10;582:29526:25;;3305:14:26;582:29526:25;;;;;;2765:581:26;:::o;3003:286::-;3242:36;582:29526:25;;3242:36:26;;;;3020:190;582:29526:25;;;;;;;;;1590:5:24;;;;;582:29526:25;1590:5:24;3083:64:26;;;;;;;;582:29526:25;;;;;;:::i;:::-;;-1:-1:-1;;582:29526:25;;;;;;;;;:::i;:::-;3083:64:26;3052:10;;;3083:64;;;;;;;;582:29526:25;3083:64:26;;;;;3020:190;582:29526:25;;3083:127:26;;3020:190;;;;;3083:64;;;;582:29526:25;3083:64:26;;;;;;;;;:::i;:::-;;;;;582:29526:25;;1590:5:24;582:29526:25;;1590:5:24;;;;3075:1011:27;-1:-1:-1;;3075:1011:27;582:29526:25;3215:13:27;3234:21;;;;582:29526:25;;3230:32:27;;;;;3350:24;;;;:::i;:::-;;-1:-1:-1;;;;;1590:5:24;;582:29526:25;7757:20:27;;;;;582:29526:25;;;;;7747:31:27;582:29526:25;;;;;7697:82:27;;;;582:29526:25;351:66:27;582:29526:25;;351:66:27;;;582:29526:25;;351:66:27;;582:29526:25;;7697:82:27;;351:66;582:29526:25;;;;;;;;;;;;;;3299:77:27;582:29526:25;;;;;;7687:93:27;;3299:77;582:29526:25;;;;3299:77:27;;;;;;582:29526:25;;;:::i;:::-;;;;;;;3299:77:27;;;;;;;;;:::i;:::-;582:29526:25;;3215:13:27;;;3230:32;;;;;582:29526:25;;;-1:-1:-1;;;;;3584:17:27;;7757:20;3584:17;;1590:5:24;582:29526:25;3619:14:27;582:29526:25;3619:14:27;;582:29526:25;3651:19:27;;582:29526:25;3651:19:27;;1590:5:24;582:29526:25;3688:19:27;351:66;3688:19;;582:29526:25;3725:31:27;;3299:77;3725:31;;1590:5:24;582:29526:25;3774:31:27;;;;582:29526:25;3823:27:27;;;;1590:5:24;582:29526:25;3868:26:27;;;;;;582:29526:25;3912:17:27;;;;582:29526:25;3947:25:27;3990:19;3947:25;;;582:29526:25;3990:19:27;;582:29526:25;;7757:20:27;582:29526:25;;;;4027:28:27;582:29526:25;;;;3486:583:27;;;815:66;7757:20;3486:583;;582:29526:25;;815:66:27;;582:29526:25;815:66:27;582:29526:25;351:66:27;815;;582:29526:25;3299:77:27;815:66;;582:29526:25;3774:31:27;815:66;;582:29526:25;3823:27:27;815:66;;582:29526:25;815:66:27;;582:29526:25;3912:17:27;815:66;;582:29526:25;3947:25:27;815:66;;582:29526:25;3990:19:27;815:66;;582:29526:25;3234:21:27;815:66;;582:29526:25;815:66:27;;;582:29526:25;815:66:27;;;;;582:29526:25;3486:583:27;;815:66;582:29526:25;;;;;;;;;;;;;7757:20:27;582:29526:25;;3486:583:27;;3463:616;3075:1011;:::o;4920:758::-;;-1:-1:-1;582:29526:25;5052:13:27;5071:12;;;;582:29526:25;;5067:23:27;;;;;5165:15;5122:60;5165:15;;582:29526:25;5165:15:27;;:::i;:::-;;7371:14;-1:-1:-1;;;;;7371:14:27;;;1590:5:24;582:29526:25;7403:23:27;;;;;582:29526:25;7444:23:27;582:29526:25;7444:23:27;;582:29526:25;7485:17:27;;;;;582:29526:25;7520:14:27;;;;;582:29526:25;;;;7313:235:27;;;;582:29526:25;2326:66:27;582:29526:25;;2326:66:27;;;582:29526:25;;2326:66:27;;582:29526:25;2326:66:27;;582:29526:25;2326:66:27;;582:29526:25;2326:66:27;;;;;582:29526:25;7313:235:27;;;;;:::i;:::-;582:29526:25;7290:268:27;;582:29526:25;;;;;;;;;5122:60:27;;;;;;582:29526:25;;;:::i;:::-;;;;;;;;5122:60:27;;;;;;;;;:::i;:::-;582:29526:25;;5052:13:27;;;5067:23;;;;;;-1:-1:-1;;;;;1590:5:24;;;582:29526:25;5392:26:27;7371:14;5392:26;;582:29526:25;5436:26:27;;7403:23;5436:26;;1590:5:24;582:29526:25;5480:22:27;582:29526:25;5480:22:27;;1590:5:24;582:29526:25;7485:17:27;5520:21;;582:29526:25;5559:15:27;2326:66;7520:14;5559:15;;582:29526:25;5592:14:27;;582:29526:25;;7371:14:27;582:29526:25;;;;5624:23:27;582:29526:25;7403:23:27;582:29526:25;5291:370:27;7371:14;5291:370;;582:29526:25;2074:66:27;582:29526:25;;7403:23:27;2074:66;;582:29526:25;;2074:66:27;;582:29526:25;7485:17:27;2074:66;;582:29526:25;7520:14:27;2074:66;;582:29526:25;2326:66:27;2074;;582:29526:25;5071:12:27;2074:66;;582:29526:25;2074:66:27;;;582:29526:25;2074:66:27;;;;;582:29526:25;5291:370:27;;2074:66;582:29526:25;;;;;;;;;;;7403:23:27;582:29526:25;;5268:403:27;;4920:758;:::o

Swarm Source

ipfs://eba2315c8271639a56006b67768be0c0c6e366fb6841900f2b4b05edcbe1d258

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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