ETH Price: $2,590.89 (+0.96%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

More Info

Private Name Tags

ContractCreator

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Repay Loan227184572025-06-16 17:05:3521 days ago1750093535IN
0x0Cf14D29...3e8Ad259F
0 ETH0.000832513.57461175
Accept Loan227184482025-06-16 17:03:4721 days ago1750093427IN
0x0Cf14D29...3e8Ad259F
0 ETH0.00030693.68097003
Place Bid227184412025-06-16 17:01:5921 days ago1750093319IN
0x0Cf14D29...3e8Ad259F
0 ETH0.000491293.14676597
List Loan227183722025-06-16 16:47:5921 days ago1750092479IN
0x0Cf14D29...3e8Ad259F
0 ETH0.001252332.97217405
Delist Loan227144072025-06-16 3:29:1122 days ago1750044551IN
0x0Cf14D29...3e8Ad259F
0 ETH0.000098620.81731604
List Loan226986782025-06-13 22:39:5924 days ago1749854399IN
0x0Cf14D29...3e8Ad259F
0 ETH0.000440921.15806501
Update Allowed N...226244392025-06-03 13:30:4735 days ago1748957447IN
0x0Cf14D29...3e8Ad259F
0 ETH0.000269035.18089942
Update Allowed E...226244382025-06-03 13:30:3535 days ago1748957435IN
0x0Cf14D29...3e8Ad259F
0 ETH0.000252355.23618907

View more zero value Internal Transactions in Advanced View mode

Advanced mode:

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Contract Source Code Verified (Exact Match)

Contract Name:
NFTLendMarket

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; // For ERC‑20 principal token
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";

/**
 * @title NFTLendMarket
 * @notice A decentralized NFT lending platform where borrowers list NFTs as collateral,
 *         and loans are funded using one of a list of allowed ERC‑20 tokens as the principal.
 *         Lenders can either bid by transferring tokens on the fly or use their deposited funds.
 *
 * SECURITY NOTE:
 * This version follows the checks‑effects‑interactions pattern.
 */
contract NFTLendMarket is ReentrancyGuard, AccessControl {
    // Roles
    bytes32 public constant OWNER_ROLE = keccak256("OWNER_ROLE");
    bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");

    // Enum for loan status
    enum LoanStatus {
        Listed,     // Newly listed, awaiting bids
        Accepted,   // A bid has been accepted
        Repaid,     // Loan repaid
        Defaulted,  // Loan defaulted and NFT claimed by lender
        Delisted,   // Loan canceled by borrower before acceptance
        Expired     // Loan listing expired (admin action)
    }

    // Enum for loan type remains unchanged
    enum LoanType {
        FIXED,
        APR
    }

    struct Loan {
        address borrower;           // Borrower's address
        address lender;             // Current lender (lowest bid)
        address nftAddress;         // NFT contract address
        uint256 tokenId;            // NFT token ID used as collateral
        uint256 loanAmount;         // Loan principal amount (in ERC20 token smallest units)
        uint256 maxInterestRate;    // Maximum acceptable interest rate (in basis points)
        uint256 currentInterestRate;// Current best bid interest rate (in basis points)
        uint256 duration;           // Loan duration (in seconds)
        uint256 startTime;          // Start time once accepted (0 if not started)
        LoanType loanType;          // FIXED or APR
        LoanStatus status;          // Current status of the loan
        address principalToken;     // The ERC20 token used as the loan principal
        uint256 listingTime;        // Timestamp when the loan was listed
    }

    uint256 public loanCounter;
    mapping(uint256 => Loan) public loans;
    // Escrowed lender funds per loan (in ERC20 token units)
    mapping(uint256 => uint256) public escrowedFunds;

    // Allowed NFT and ERC20 contracts
    mapping(address => bool) public allowedNFTContracts;
    mapping(address => bool) public allowedERC20Contracts;

    // Tracking which NFTs are currently collateralized
    mapping(address => mapping(uint256 => bool)) public isCollateralized;
    /// @notice maps an NFT to its current active loanId+1 (0 means “no active loan”)
    mapping(address => mapping(uint256 => uint256)) private _nftLoanPlusOne;
    /// @notice true if this NFT has ever defaulted (borrower failed to repay on time)
    mapping(address => mapping(uint256 => bool))     public everDefaultedLoan;

    // User balances held in the contract (available funds for bids or withdrawal)
    mapping(address => mapping(address => uint256)) public userBalances;
    // Global tracking of user funds per token
    mapping(address => uint256) public totalUserBalances;

    // Protocol fees collected per ERC20 token
    mapping(address => uint256) public protocolFeeBalance;

    // Active loan tracking
    uint256 public maxActiveLoans = 1000;
    uint256[] public activeLoanIds;
    mapping(uint256 => bool) public activeLoans;
    // Expiration time for listed loans (e.g., 7 days)
    uint256 public loanListExpiration = 7 days;


    uint256 public protocolFeeRate = 200; // In basis points (e.g., 200 = 2%)
    // Timestamp of the last bid for each loan
    mapping(uint256 => uint256) public bidTimestamps;
    uint256 public bidCancelPeriod = 1 days;

    // EVENTS
    event LoanListed(
        uint256 indexed loanId,
        address indexed borrower,
        address nftAddress,
        uint256 tokenId,
        uint256 loanAmount,
        uint256 maxInterestRate,
        uint256 duration,
        LoanType loanType,
        address principalToken
    );
    event LoanDelisted(uint256 indexed loanId, address indexed borrower);
    event LoanBidPlaced(uint256 indexed loanId, address indexed lender, uint256 currentInterestRate);
    event LoanBidCancelled(uint256 indexed loanId, address indexed lender);
    event LoanAccepted(
        uint256 indexed loanId,
        address indexed borrower,
        address indexed lender,
        uint256 startTime,
        address principalToken
    );
    event LoanRepaid(uint256 indexed loanId, address indexed borrower, uint256 repaymentAmount);
    event LoanDefaulted(uint256 indexed loanId, address indexed lender);
    event AllowedNFTUpdated(address indexed nftAddress, bool allowed);
    event AllowedERC20Updated(address indexed token, bool allowed);
    event ProtocolFeeRateUpdated(uint256 newFeeRate);
    event ProtocolFeesWithdrawn(address to, uint256 amount, address token);
    event MaxActiveLoansUpdated(uint256 newMaxActiveLoans);
    event BidCancelPeriodUpdated(uint256 newBidCancelPeriod);
    event UserBalanceUpdated(address indexed user, uint256 amount, address token);
    event FundsWithdrawn(address indexed user, uint256 amount, address indexed to, address token);
    event FundsDeposited(address indexed user, uint256 amount, address token);
    event LoanListExpirationUpdated(uint256 newExpiration);

    // MODIFIERS
    modifier onlyBorrower(uint256 loanId) {
        require(msg.sender == loans[loanId].borrower, "Not loan borrower");
        _;
    }

    modifier onlyLender(uint256 loanId) {
        require(msg.sender == loans[loanId].lender, "Not loan lender");
        _;
    }

    modifier onlyNftOwner(address nftAddress, uint256 tokenId) {
        require(IERC721(nftAddress).ownerOf(tokenId) == msg.sender, "Not NFT owner");
        _;
    }

    // Instead of isNotAccepted, we now require the loan be in Listed status.
    modifier isListed(uint256 loanId) {
        require(loans[loanId].status == LoanStatus.Listed, "Loan not in Listed status");
        _;
    }

    modifier loanExists(uint256 loanId) {
        require(loans[loanId].borrower != address(0), "Loan does not exist");
        _;
    }

    modifier isAllowedNFT(address nftAddress) {
        require(allowedNFTContracts[nftAddress], "NFT contract not allowed");
        _;
    }

    modifier isAllowedERC20(address token) {
        require(allowedERC20Contracts[token], "ERC20 token not allowed");
        _;
    }

    // CONSTRUCTOR
    constructor(address _govAddress, address _manager) {
        _grantRole(DEFAULT_ADMIN_ROLE, _govAddress);
        _grantRole(OWNER_ROLE, _govAddress);
        _grantRole(MANAGER_ROLE, _manager);
    }

    // SETTER: Update default loan expiration
    function setLoanListExpiration(uint256 newExpiration) external onlyRole(OWNER_ROLE) {
        loanListExpiration = newExpiration;
        emit LoanListExpirationUpdated(newExpiration);
    }

    // Getter for active loan IDs
    function getActiveLoans() external view returns (uint256[] memory) {
        return activeLoanIds;
    }

    // ADMIN FUNCTIONS

    function grantManagerRole(address account) external onlyRole(DEFAULT_ADMIN_ROLE) {
        grantRole(MANAGER_ROLE, account);
    }

    function setMaxActiveLoans(uint256 newMax) external onlyRole(OWNER_ROLE) {
        maxActiveLoans = newMax;
        emit MaxActiveLoansUpdated(newMax);
    }

    function updateAllowedNFT(address nftAddress, bool allowed) external onlyRole(MANAGER_ROLE) {
        require(nftAddress != address(0), "Invalid NFT address");
        require(nftAddress.code.length > 0, "Address is not a contract");
        try IERC721(nftAddress).supportsInterface(0x80ac58cd) returns (bool isERC721) {
            require(isERC721, "Contract not ERC721");
        } catch {
            revert("Failed to verify ERC721");
        }
        allowedNFTContracts[nftAddress] = allowed;
        emit AllowedNFTUpdated(nftAddress, allowed);
    }

    function updateAllowedERC20(address token, bool allowed) external onlyRole(MANAGER_ROLE) {
        require(token != address(0), "Invalid token address");
        allowedERC20Contracts[token] = allowed;
        emit AllowedERC20Updated(token, allowed);
    }

    function revokeManagerRole(address account) external onlyRole(DEFAULT_ADMIN_ROLE) {
        revokeRole(MANAGER_ROLE, account);
    }

    function setProtocolFeeRate(uint256 newFeeRate) external onlyRole(OWNER_ROLE) {
        require(newFeeRate <= 1000, "Fee rate too high");
        protocolFeeRate = newFeeRate;
        emit ProtocolFeeRateUpdated(newFeeRate);
    }

    function setBidCancelPeriod(uint256 newBidCancelPeriod) external onlyRole(OWNER_ROLE) {
        require(newBidCancelPeriod > 1 hours, "BidCancelPeriod too short");
        bidCancelPeriod = newBidCancelPeriod;
        emit BidCancelPeriodUpdated(newBidCancelPeriod);
    }

    // INTERNAL: Update user balance (for deposits, refunds, etc.)
    function _addUserBalance(address user, uint256 amount, address token) internal {
        userBalances[user][token] += amount;
        totalUserBalances[token] += amount;
        emit UserBalanceUpdated(user, userBalances[user][token], token);
    }

    // INTERNAL: Deduct user balance
    function _deductUserBalance(address user, uint256 amount, address token) internal {
        require(userBalances[user][token] >= amount, "Insufficient balance");
        userBalances[user][token] -= amount;
        totalUserBalances[token] -= amount;
        emit UserBalanceUpdated(user, userBalances[user][token], token);
    }

    // INTERNAL: Update active loans list (removes loanId from activeLoanIds)
    function _removeActiveLoan(uint256 loanId) private {
        delete activeLoans[loanId];
        for (uint256 i = 0; i < activeLoanIds.length; ) {
            if (activeLoanIds[i] == loanId) {
                activeLoanIds[i] = activeLoanIds[activeLoanIds.length - 1];
                activeLoanIds.pop();
                break;
            }
            unchecked { i++; }
        }
    }

    // LOAN FUNCTIONS

    /**
     * @notice Lists a new loan by depositing an NFT as collateral.
     */
    function listLoan(
        address nftAddress,
        uint256 tokenId,
        uint256 loanAmount,
        uint256 maxInterestRate,
        uint256 duration,
        LoanType loanType,
        address principalToken
    )
        external
        nonReentrant
        isAllowedNFT(nftAddress)
        isAllowedERC20(principalToken)
        onlyNftOwner(nftAddress, tokenId)
    {
        require(activeLoanIds.length < maxActiveLoans, "Active loan limit reached");
        require(!isCollateralized[nftAddress][tokenId], "NFT already collateralized");
        require(loanAmount > 0, "Loan amount must be > 0");
        require(maxInterestRate > 0, "Interest rate must be > 0");
        require(duration > 0, "Duration must be > 0");
        require(uint256(loanType) <= uint256(type(LoanType).max), "Invalid loan type");

        uint256 loanId = loanCounter;
        loans[loanId] = Loan({
            borrower: msg.sender,
            lender: address(0),
            nftAddress: nftAddress,
            tokenId: tokenId,
            loanAmount: loanAmount,
            maxInterestRate: maxInterestRate,
            currentInterestRate: maxInterestRate,
            duration: duration,
            startTime: 0,
            loanType: loanType,
            status: LoanStatus.Listed,
            principalToken: principalToken,
            listingTime: block.timestamp
        });

        activeLoans[loanId] = true;
        activeLoanIds.push(loanId);
        _nftLoanPlusOne[nftAddress][tokenId] = loanId + 1;

        loanCounter++;

        isCollateralized[nftAddress][tokenId] = true;

        try IERC721(nftAddress).transferFrom(msg.sender, address(this), tokenId) {
            emit LoanListed(loanId, msg.sender, nftAddress, tokenId, loanAmount, maxInterestRate, duration, loanType, principalToken);
        } catch {
            revert("NFT transfer failed");
        }
    }

    /**
     * @notice Allows a user to deposit ERC20 tokens into their balance.
     */
    function depositFunds(address token, uint256 amount) external nonReentrant isAllowedERC20(token) {
        require(amount > 0, "Amount must be > 0");
        _addUserBalance(msg.sender, amount, token);
        require(IERC20(token).transferFrom(msg.sender, address(this), amount), "Token transfer failed");
        emit FundsDeposited(msg.sender, amount, token);
    }

    /**
     * @notice Withdraw funds from the user's balance.
     */
    function withdrawFunds(address token, address to) external nonReentrant {
        require(to != address(0), "Invalid address");
        uint256 amount = userBalances[msg.sender][token];
        require(amount > 0, "No funds to withdraw");
        userBalances[msg.sender][token] = 0;
        totalUserBalances[token] -= amount;
        require(IERC20(token).transfer(to, amount), "Transfer failed");
        emit FundsWithdrawn(msg.sender, amount, to, token);
    }

    /**
     * @notice Places a bid by transferring ERC20 tokens directly.
     */
    function placeBid(uint256 loanId, uint256 interestRate) external nonReentrant loanExists(loanId) isListed(loanId) {
        Loan storage loan = loans[loanId];
        require(interestRate < loan.currentInterestRate && interestRate <= loan.maxInterestRate, "Bid interest rate invalid");

        address previousBidder = loan.lender;
        uint256 escrowRefund = escrowedFunds[loanId];
        loan.lender = msg.sender;
        loan.currentInterestRate = interestRate;
        escrowedFunds[loanId] = loan.loanAmount;
        bidTimestamps[loanId] = block.timestamp;

        if (previousBidder != address(0) && escrowRefund > 0) {
            _addUserBalance(previousBidder, escrowRefund, loan.principalToken);
        }
        require(IERC20(loan.principalToken).transferFrom(msg.sender, address(this), loan.loanAmount), "Token transfer failed");
        emit LoanBidPlaced(loanId, msg.sender, interestRate);
    }

    /**
     * @notice Alternative bid function that uses the lender's deposited balance.
     */
    function placeBidUsingBalance(uint256 loanId, uint256 interestRate) external nonReentrant loanExists(loanId) isListed(loanId) {
        Loan storage loan = loans[loanId];
        require(interestRate < loan.currentInterestRate && interestRate <= loan.maxInterestRate, "Bid interest rate invalid");

        _deductUserBalance(msg.sender, loan.loanAmount, loan.principalToken);

        address previousBidder = loan.lender;
        uint256 escrowRefund = escrowedFunds[loanId];
        loan.lender = msg.sender;
        loan.currentInterestRate = interestRate;
        escrowedFunds[loanId] = loan.loanAmount;
        bidTimestamps[loanId] = block.timestamp;

        if (previousBidder != address(0) && escrowRefund > 0) {
            _addUserBalance(previousBidder, escrowRefund, loan.principalToken);
        }
        emit LoanBidPlaced(loanId, msg.sender, interestRate);
    }

    /**
     * @notice Delists a loan that hasn't been accepted.
     */
    function delistLoan(uint256 loanId) external nonReentrant loanExists(loanId) isListed(loanId) onlyBorrower(loanId) {
        Loan memory loan = loans[loanId];
        address previousBidder = loan.lender;
        uint256 escrowRefund = 0;
        if (previousBidder != address(0)) {
            escrowRefund = escrowedFunds[loanId];
            escrowedFunds[loanId] = 0;
            if (escrowRefund > 0) {
                _addUserBalance(previousBidder, escrowRefund, loan.principalToken);
            }
        }
        loans[loanId].status = LoanStatus.Delisted;
        _nftLoanPlusOne[loan.nftAddress][loan.tokenId] = 0; // Reset the loan ID for this NFT
        delete loans[loanId];
        _removeActiveLoan(loanId);
        isCollateralized[loan.nftAddress][loan.tokenId] = false;
        try IERC721(loan.nftAddress).safeTransferFrom(address(this), loan.borrower, loan.tokenId) {
            emit LoanDelisted(loanId, loan.borrower);
        } catch {
            revert("NFT transfer failed");
        }
    }

    /**
     * @notice delist an expired loan (not accepted within loanListExpiration).
     */
    function delistExpiredLoan(uint256 loanId) external nonReentrant loanExists(loanId) isListed(loanId) {
        Loan memory loan = loans[loanId];
        require(block.timestamp >= loan.listingTime + loanListExpiration, "Loan not expired yet");

        address previousBidder = loan.lender;
        uint256 escrowRefund = escrowedFunds[loanId];
        if (previousBidder != address(0) && escrowRefund > 0) {
            _addUserBalance(previousBidder, escrowRefund, loan.principalToken);
        }
        loans[loanId].status = LoanStatus.Expired;
        _nftLoanPlusOne[loan.nftAddress][loan.tokenId] = 0; // Reset the loan ID for this NFT
        delete loans[loanId];
        _removeActiveLoan(loanId);
        isCollateralized[loan.nftAddress][loan.tokenId] = false;
        try IERC721(loan.nftAddress).safeTransferFrom(address(this), loan.borrower, loan.tokenId) {
            emit LoanDelisted(loanId, loan.borrower);
        } catch {
            revert("NFT transfer failed");
        }
    }

    /**
     * @notice Accepts a lender’s bid to start the loan.
     */
    function acceptLoan(uint256 loanId) external nonReentrant loanExists(loanId) isListed(loanId) onlyBorrower(loanId) {
        Loan storage loan = loans[loanId];
        require(loan.lender != address(0), "No lender bid");
        require(escrowedFunds[loanId] == loan.loanAmount, "Escrowed funds mismatch");
        require(loan.startTime == 0, "Loan already started");
        loan.startTime = block.timestamp;
        loan.status = LoanStatus.Accepted;
        uint256 amount = escrowedFunds[loanId];
        escrowedFunds[loanId] = 0;
        require(IERC20(loan.principalToken).transfer(loan.borrower, amount), "Transfer failed");
        emit LoanAccepted(loanId, loan.borrower, loan.lender, loan.startTime, loan.principalToken);
    }

    /**
     * @notice Computes the total repayment (principal + interest) for a loan.
     */
    function getTotalRepayment(uint256 loanId) public view loanExists(loanId) returns (uint256) {
        Loan storage loan = loans[loanId];
        uint256 interestAmount;
        if (loan.loanType == LoanType.FIXED) {
            interestAmount = (loan.loanAmount * loan.currentInterestRate) / 10000;
        } else if (loan.loanType == LoanType.APR) {
            uint256 annualizedInterest = (loan.loanAmount * loan.currentInterestRate) / 10000;
            if (loan.status == LoanStatus.Accepted) {
                uint256 elapsedDays = (block.timestamp - loan.startTime) / 1 days;
                interestAmount = (annualizedInterest * (elapsedDays > 0 ? elapsedDays : 1)) / 365;
            } else {
                uint256 durationDays = loan.duration / 1 days;
                interestAmount = (annualizedInterest * (durationDays > 0 ? durationDays : 1)) / 365;
            }
        }
        return loan.loanAmount + interestAmount;
    }

    /**
     * @notice Repays an active loan, returning the NFT collateral and crediting the lender.
     */
    function repayLoan(uint256 loanId) external nonReentrant loanExists(loanId) onlyBorrower(loanId) {
        Loan storage loan = loans[loanId];
        require(loan.status == LoanStatus.Accepted, "Loan not active");
        require(block.timestamp >= loan.startTime, "Repayment before start");
        require(block.timestamp <= loan.startTime + loan.duration, "Loan duration expired");

        uint256 totalRepayment = getTotalRepayment(loanId);
        uint256 borrowerFee = (totalRepayment * protocolFeeRate) / 10000;
        uint256 lenderFee = (totalRepayment * protocolFeeRate) / 10000;
        uint256 requiredRepayment = totalRepayment + borrowerFee;
        uint256 lenderPayout = totalRepayment - lenderFee;

        protocolFeeBalance[loan.principalToken] += (lenderFee + borrowerFee);
        loan.status = LoanStatus.Repaid;
        _nftLoanPlusOne[loan.nftAddress][loan.tokenId] = 0; // Reset the loan ID for this NFT
        _removeActiveLoan(loanId);
        isCollateralized[loan.nftAddress][loan.tokenId] = false;
        _addUserBalance(loan.lender, lenderPayout, loan.principalToken);

        require(IERC20(loan.principalToken).transferFrom(msg.sender, address(this), requiredRepayment), "Token transfer failed");
        
        try IERC721(loan.nftAddress).safeTransferFrom(address(this), loan.borrower, loan.tokenId) {
            emit LoanRepaid(loanId, loan.borrower, requiredRepayment);
        } catch {
            revert("NFT transfer failed");
        }
    }

    /**
     * @notice Cancels a lender's bid if the loan is still listed.
     */
    function cancelBid(uint256 loanId) external nonReentrant loanExists(loanId) isListed(loanId) onlyLender(loanId) {
        Loan storage loan = loans[loanId];
        require(block.timestamp >= bidTimestamps[loanId] + bidCancelPeriod, "Bid cancel period not met");

        address bidder = loan.lender;
        uint256 refundAmount = escrowedFunds[loanId];
        escrowedFunds[loanId] = 0;
        loan.lender = address(0);
        loan.currentInterestRate = loan.maxInterestRate;
        require(IERC20(loan.principalToken).transfer(bidder, refundAmount), "Refund failed");
        emit LoanBidCancelled(loanId, bidder);
    }

    /**
     * @notice Claims the NFT collateral after a defaulted loan.
     */
    function claimDefaultedLoan(uint256 loanId) external nonReentrant loanExists(loanId) {
        Loan storage loan = loans[loanId];
        require(loan.status == LoanStatus.Accepted, "Loan not active");
        require(block.timestamp > loan.startTime + loan.duration, "Loan not expired");

        uint256 totalRepayment = getTotalRepayment(loanId);
        uint256 lenderFee = (totalRepayment * protocolFeeRate) / 10000;

        require(IERC20(loan.principalToken).transferFrom(msg.sender, address(this), lenderFee), "Protocol fee transfer failed");

        protocolFeeBalance[loan.principalToken] += lenderFee;
        loan.status = LoanStatus.Defaulted;
        _nftLoanPlusOne[loan.nftAddress][loan.tokenId] = 0; // Reset the loan ID for this NFT
        _removeActiveLoan(loanId);
        isCollateralized[loan.nftAddress][loan.tokenId] = false;
        // —— Mark NFT as having defaulted —— 
        everDefaultedLoan[loan.nftAddress][loan.tokenId] = true;
        

        try IERC721(loan.nftAddress).safeTransferFrom(address(this), loan.lender, loan.tokenId) {
            emit LoanDefaulted(loanId, loan.lender);
        } catch {
            revert("NFT transfer failed");
        }
    }

    /**
     * @notice Returns the active loanId for a given NFT, or reverts if none.
     * @dev we store loanId+1 so that a 0 mapping means “no active loan”.
     */
    function getLoanIdForNFT(address nftAddress, uint256 tokenId)
      external
      view
      returns (uint256)
    {
      uint256 plusOne = _nftLoanPlusOne[nftAddress][tokenId];
      require(plusOne != 0, "No active loan for this NFT");
      return plusOne - 1;
    }

    /**
     * @notice Returns true if the NFT:
     *   - is not currently collateralized, AND
     *   - has never defaulted on a loan.
     * In other words, it’s valid as new collateral.
     */
    function isValidCollateral(address nftAddress, uint256 tokenId)
        external
        view
        returns (bool)
    {
        return
            !isCollateralized[nftAddress][tokenId] &&
            !everDefaultedLoan[nftAddress][tokenId];
    }


    /**
     * @notice Withdraws protocol fees (in a given ERC20) to a specified address.
     */
    function withdrawProtocolFees(address token, address to) external nonReentrant onlyRole(OWNER_ROLE) {
        require(to != address(0), "Invalid address");
        uint256 tokenBalance = IERC20(token).balanceOf(address(this));

        uint256 totalEscrowed = 0;
        for (uint256 i = 0; i < activeLoanIds.length; i++) {
            uint256 loanId = activeLoanIds[i];
            if (loans[loanId].principalToken == token) {
                totalEscrowed += escrowedFunds[loanId];
            }
        }

        uint256 withdrawable = tokenBalance - totalEscrowed - totalUserBalances[token];
        require(withdrawable > 0, "No funds to withdraw");

        protocolFeeBalance[token] = 0;
        require(IERC20(token).transfer(to, withdrawable), "Transfer failed");
        emit ProtocolFeesWithdrawn(to, withdrawable, token);
    }
}

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

pragma solidity ^0.8.20;

import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    mapping(bytes32 role => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        return _roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        if (!hasRole(role, account)) {
            _roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        if (hasRole(role, account)) {
            _roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC-165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
     * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}

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

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

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

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

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

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC-721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC-721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
     *   {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

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

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

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

pragma solidity ^0.8.20;

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

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

    uint256 private _status;

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

    constructor() {
        _status = NOT_ENTERED;
    }

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

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

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

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

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

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_govAddress","type":"address"},{"internalType":"address","name":"_manager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"AllowedERC20Updated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nftAddress","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"AllowedNFTUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newBidCancelPeriod","type":"uint256"}],"name":"BidCancelPeriodUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"FundsDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"FundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":true,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"uint256","name":"startTime","type":"uint256"},{"indexed":false,"internalType":"address","name":"principalToken","type":"address"}],"name":"LoanAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":true,"internalType":"address","name":"lender","type":"address"}],"name":"LoanBidCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":true,"internalType":"address","name":"lender","type":"address"},{"indexed":false,"internalType":"uint256","name":"currentInterestRate","type":"uint256"}],"name":"LoanBidPlaced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":true,"internalType":"address","name":"lender","type":"address"}],"name":"LoanDefaulted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"}],"name":"LoanDelisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newExpiration","type":"uint256"}],"name":"LoanListExpirationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"address","name":"nftAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"loanAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxInterestRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"},{"indexed":false,"internalType":"enum NFTLendMarket.LoanType","name":"loanType","type":"uint8"},{"indexed":false,"internalType":"address","name":"principalToken","type":"address"}],"name":"LoanListed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repaymentAmount","type":"uint256"}],"name":"LoanRepaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMaxActiveLoans","type":"uint256"}],"name":"MaxActiveLoansUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newFeeRate","type":"uint256"}],"name":"ProtocolFeeRateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"ProtocolFeesWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"UserBalanceUpdated","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OWNER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"acceptLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"activeLoanIds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"activeLoans","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"allowedERC20Contracts","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"allowedNFTContracts","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bidCancelPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"bidTimestamps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"cancelBid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"claimDefaultedLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"delistExpiredLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"delistLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"escrowedFunds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"everDefaultedLoan","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getActiveLoans","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getLoanIdForNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"getTotalRepayment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"grantManagerRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"isCollateralized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"isValidCollateral","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nftAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"loanAmount","type":"uint256"},{"internalType":"uint256","name":"maxInterestRate","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"enum NFTLendMarket.LoanType","name":"loanType","type":"uint8"},{"internalType":"address","name":"principalToken","type":"address"}],"name":"listLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"loanCounter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"loanListExpiration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"loans","outputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"address","name":"lender","type":"address"},{"internalType":"address","name":"nftAddress","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"loanAmount","type":"uint256"},{"internalType":"uint256","name":"maxInterestRate","type":"uint256"},{"internalType":"uint256","name":"currentInterestRate","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"enum NFTLendMarket.LoanType","name":"loanType","type":"uint8"},{"internalType":"enum NFTLendMarket.LoanStatus","name":"status","type":"uint8"},{"internalType":"address","name":"principalToken","type":"address"},{"internalType":"uint256","name":"listingTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxActiveLoans","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"uint256","name":"interestRate","type":"uint256"}],"name":"placeBid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"uint256","name":"interestRate","type":"uint256"}],"name":"placeBidUsingBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"protocolFeeBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFeeRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"repayLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"revokeManagerRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newBidCancelPeriod","type":"uint256"}],"name":"setBidCancelPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newExpiration","type":"uint256"}],"name":"setLoanListExpiration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMax","type":"uint256"}],"name":"setMaxActiveLoans","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newFeeRate","type":"uint256"}],"name":"setProtocolFeeRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"totalUserBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"updateAllowedERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftAddress","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"updateAllowedNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"userBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"withdrawProtocolFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040526103e8600d5562093a8060105560c860115562015180601355348015610028575f5ffd5b5060405161413c38038061413c83398101604081905261004791610162565b60015f90815561005790836100b5565b506100827fb19546dff01e856fb3f010c267a7b1c60363cf8a4664e21cc89c26224620214e836100b5565b506100ad7f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b08826100b5565b505050610193565b5f8281526001602090815260408083206001600160a01b038516845290915281205460ff1661013e575f8381526001602081815260408084206001600160a01b0387168086529252808420805460ff19169093179092559051339286917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a4506001610141565b505f5b92915050565b80516001600160a01b038116811461015d575f5ffd5b919050565b5f5f60408385031215610173575f5ffd5b61017c83610147565b915061018a60208401610147565b90509250929050565b613f9c806101a05f395ff3fe608060405234801561000f575f5ffd5b50600436106102ca575f3560e01c8063921b004b1161017b578063c153a234116100e4578063e1ec3c681161009e578063e58378bb11610079578063e58378bb14610748578063e5a7bfd01461075c578063e5e05bd714610789578063ec87621c146107a8575f5ffd5b8063e1ec3c681461067a578063e2cb0e4d14610722578063e505d0c514610735575f5ffd5b8063c153a234146105f8578063c9a759cb14610625578063cf7b287f1461062e578063d547741f14610641578063deb0ef4014610654578063deb298c314610667575f5ffd5b8063adfbe22f11610135578063adfbe22f1461056b578063b13aa2d61461057e578063b489e14714610591578063b93186ba146105b0578063be4dc94f146105c3578063c0f6ae97146105d6575f5ffd5b8063921b004b146104e25780639703ef35146104f55780639b087af414610508578063a217fddf14610527578063ab7b1c891461052e578063adb5198014610541575f5ffd5b80633360874c116102375780635d54f5c1116101f157806374a3f8fc116101cc57806374a3f8fc146104965780638f112248146104a95780638fd4f224146104bc57806391d14854146104cf575f5ffd5b80635d54f5c1146104585780636729629f1461046b5780636f504fd714610474575f5ffd5b80633360874c146103eb57806334d9289e146103fe57806336568abe1461040757806347126f621461041a57806357c90de51461043c57806358f858801461044f575f5ffd5b8063248a9ca311610288578063248a9ca31461037357806326e885e31461039657806327f8dce3146103a95780632f010b43146103bc5780632f2ff15d146103cf57806332b814ba146103e2575f5ffd5b80620fa9fb146102ce57806301ffc9a7146102e3578063079712b61461030b578063121ed2a714610338578063158c6bdb1461034d5780631e837ef314610360575b5f5ffd5b6102e16102dc3660046139eb565b6107bc565b005b6102f66102f1366004613a22565b61099a565b60405190151581526020015b60405180910390f35b61032a610319366004613a49565b600b6020525f908152604090205481565b604051908152602001610302565b6103406109d0565b6040516103029190613a64565b6102e161035b366004613aa6565b610a26565b6102e161036e366004613ac6565b610bff565b61032a610381366004613ac6565b5f908152600160208190526040909120015490565b6102e16103a4366004613a49565b610c53565b6102e16103b7366004613ac6565b610c74565b6102f66103ca366004613add565b610ffc565b6102e16103dd366004613b07565b611059565b61032a60135481565b6102e16103f9366004613b37565b611084565b61032a60025481565b6102e1610415366004613b07565b611297565b6102f6610428366004613ac6565b600f6020525f908152604090205460ff1681565b6102e161044a366004613aa6565b6112cf565b61032a60115481565b6102e1610466366004613ac6565b6114d8565b61032a60105481565b6102f6610482366004613a49565b60066020525f908152604090205460ff1681565b6102e16104a4366004613ac6565b6118ab565b6102e16104b7366004613ac6565b6118f7565b6102e16104ca366004613b37565b611994565b6102f66104dd366004613b07565b611a50565b6102e16104f0366004613add565b611a7a565b6102e1610503366004613ac6565b611c0d565b61032a610516366004613ac6565b60126020525f908152604090205481565b61032a5f81565b6102e161053c366004613ac6565b611e98565b61032a61054f3660046139eb565b600a60209081525f928352604080842090915290825290205481565b6102e1610579366004613ac6565b6122c8565b6102e161058c366004613ac6565b6125c2565b61032a61059f366004613ac6565b60046020525f908152604090205481565b61032a6105be366004613ac6565b612654565b6102e16105d1366004613a49565b612673565b6102f66105e4366004613a49565b60056020525f908152604090205460ff1681565b6102f6610606366004613add565b600960209081525f928352604080842090915290825290205460ff1681565b61032a600d5481565b6102e161063c3660046139eb565b612694565b6102e161064f366004613b07565b61294c565b6102e1610662366004613b63565b612971565b6102e1610675366004613ac6565b61306a565b610709610688366004613ac6565b600360208190525f918252604090912080546001820154600283015493830154600484015460058501546006860154600787015460088801546009890154600a909901546001600160a01b039889169a978916999789169896979596949593949293919260ff8084169361010081049091169262010000909104909116908d565b6040516103029d9c9b9a99989796959493929190613bfb565b61032a610730366004613add565b61341b565b61032a610743366004613ac6565b6134a0565b61032a5f516020613f275f395f51905f5281565b6102f661076a366004613add565b600760209081525f928352604080842090915290825290205460ff1681565b61032a610797366004613a49565b600c6020525f908152604090205481565b61032a5f516020613f475f395f51905f5281565b6107c461363f565b6001600160a01b0381166108115760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b60448201526064015b60405180910390fd5b335f908152600a602090815260408083206001600160a01b0386168452909152902054806108785760405162461bcd60e51b81526020600482015260146024820152734e6f2066756e647320746f20776974686472617760601b6044820152606401610808565b335f908152600a602090815260408083206001600160a01b03871684528252808320839055600b909152812080548392906108b4908490613caf565b909155505060405163a9059cbb60e01b81526001600160a01b0383811660048301526024820183905284169063a9059cbb906044016020604051808303815f875af1158015610905573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109299190613cc2565b6109455760405162461bcd60e51b815260040161080890613cdd565b604080518281526001600160a01b03858116602083015284169133917fc6d7522ade22416d5f000975be266c973d54b87b3bd997daeba6258d2de358d1910160405180910390a35061099660015f55565b5050565b5f6001600160e01b03198216637965db0b60e01b14806109ca57506301ffc9a760e01b6001600160e01b03198316145b92915050565b6060600e805480602002602001604051908101604052809291908181526020018280548015610a1c57602002820191905f5260205f20905b815481526020019060010190808311610a08575b5050505050905090565b610a2e61363f565b5f8281526003602052604090205482906001600160a01b0316610a635760405162461bcd60e51b815260040161080890613d06565b825f5f82815260036020526040902060090154610100900460ff166005811115610a8f57610a8f613bd3565b14610aac5760405162461bcd60e51b815260040161080890613d33565b5f848152600360205260409020600681015484108015610ad0575080600501548411155b610b185760405162461bcd60e51b8152602060048201526019602482015278109a59081a5b9d195c995cdd081c985d19481a5b9d985b1a59603a1b6044820152606401610808565b60048101546009820154610b3c9133916201000090046001600160a01b0316613667565b6001810180545f87815260046020818152604080842080546001600160a01b031987163317909755600688018b9055928701549092556012905290204290556001600160a01b0316908115801590610b9357505f81115b15610bba57610bba82828560090160029054906101000a90046001600160a01b0316613798565b604051868152339088907fd1b9c752eb85783f53e7669e993e44d05db062782a8dcd72a7757746cc981f1d9060200160405180910390a3505050505061099660015f55565b5f516020613f275f395f51905f52610c16816137fa565b600d8290556040518281527fc59dcd6979a73a87266c1f2a5d4c4108daee42034d779ef00e960a510c789625906020015b60405180910390a15050565b5f610c5d816137fa565b6109965f516020613f475f395f51905f5283611059565b610c7c61363f565b5f8181526003602052604090205481906001600160a01b0316610cb15760405162461bcd60e51b815260040161080890613d06565b5f82815260036020526040902060016009820154610100900460ff166005811115610cde57610cde613bd3565b14610d1d5760405162461bcd60e51b815260206004820152600f60248201526e4c6f616e206e6f742061637469766560881b6044820152606401610808565b80600701548160080154610d319190613d6a565b4211610d725760405162461bcd60e51b815260206004820152601060248201526f131bd85b881b9bdd08195e1c1a5c995960821b6044820152606401610808565b5f610d7c846134a0565b90505f61271060115483610d909190613d7d565b610d9a9190613d94565b60098401546040516323b872dd60e01b81529192506201000090046001600160a01b0316906323b872dd90610dd790339030908690600401613db3565b6020604051808303815f875af1158015610df3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e179190613cc2565b610e635760405162461bcd60e51b815260206004820152601c60248201527f50726f746f636f6c20666565207472616e73666572206661696c6564000000006044820152606401610808565b60098301546201000090046001600160a01b03165f908152600c602052604081208054839290610e94908490613d6a565b909155505060098301805461ff00191661030017905560028301546001600160a01b03165f90815260086020908152604080832060038701548452909152812055610ede85613804565b6002830180546001600160a01b039081165f9081526007602090815260408083206003890180548552908352818420805460ff199081169091558654861685526009845282852082548652909352928190208054909216600190811790925593549087015491549351632142170760e11b8152908316936342842e0e93610f6d93309391169190600401613db3565b5f604051808303815f87803b158015610f84575f5ffd5b505af1925050508015610f95575060015b610fb15760405162461bcd60e51b815260040161080890613dd7565b60018301546040516001600160a01b039091169086907f13b88e6866f0156d706fecfa22b678de5fc2b749c1d2307f6f47eb541385f1ec905f90a350505050610ff960015f55565b50565b6001600160a01b0382165f90815260076020908152604080832084845290915281205460ff1615801561105257506001600160a01b0383165f90815260096020908152604080832085845290915290205460ff16155b9392505050565b5f8281526001602081905260409091200154611074816137fa565b61107e83836138bd565b50505050565b5f516020613f475f395f51905f5261109b816137fa565b6001600160a01b0383166110e75760405162461bcd60e51b8152602060048201526013602482015272496e76616c6964204e4654206164647265737360681b6044820152606401610808565b5f836001600160a01b03163b116111405760405162461bcd60e51b815260206004820152601960248201527f41646472657373206973206e6f74206120636f6e7472616374000000000000006044820152606401610808565b6040516301ffc9a760e01b81526380ac58cd60e01b60048201526001600160a01b038416906301ffc9a790602401602060405180830381865afa9250505080156111a7575060408051601f3d908101601f191682019092526111a491810190613cc2565b60015b6111f35760405162461bcd60e51b815260206004820152601760248201527f4661696c656420746f20766572696679204552433732310000000000000000006044820152606401610808565b806112365760405162461bcd60e51b8152602060048201526013602482015272436f6e7472616374206e6f742045524337323160681b6044820152606401610808565b506001600160a01b0383165f81815260056020908152604091829020805460ff191686151590811790915591519182527f7fe996db267f9f17db8865f67d54879fc39a38a2a37afa158a9ea52357e794ac91015b60405180910390a2505050565b6001600160a01b03811633146112c05760405163334bd91960e11b815260040160405180910390fd5b6112ca8282613933565b505050565b6112d761363f565b5f8281526003602052604090205482906001600160a01b031661130c5760405162461bcd60e51b815260040161080890613d06565b825f5f82815260036020526040902060090154610100900460ff16600581111561133857611338613bd3565b146113555760405162461bcd60e51b815260040161080890613d33565b5f848152600360205260409020600681015484108015611379575080600501548411155b6113c15760405162461bcd60e51b8152602060048201526019602482015278109a59081a5b9d195c995cdd081c985d19481a5b9d985b1a59603a1b6044820152606401610808565b6001810180545f87815260046020818152604080842080546001600160a01b031987163317909755600688018b9055928701549092556012905290204290556001600160a01b031690811580159061141857505f81115b1561143f5761143f82828560090160029054906101000a90046001600160a01b0316613798565b60098301546004808501546040516323b872dd60e01b8152620100009093046001600160a01b0316926323b872dd9261147c923392309201613db3565b6020604051808303815f875af1158015611498573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114bc9190613cc2565b610bba5760405162461bcd60e51b815260040161080890613e04565b6114e061363f565b5f8181526003602052604090205481906001600160a01b03166115155760405162461bcd60e51b815260040161080890613d06565b815f5f82815260036020526040902060090154610100900460ff16600581111561154157611541613bd3565b1461155e5760405162461bcd60e51b815260040161080890613d33565b5f83815260036020818152604080842081516101a08101835281546001600160a01b0390811682526001808401548216958301959095526002830154169281019290925292830154606082015260048301546080820152600583015460a0820152600683015460c0820152600783015460e08201526008830154610100820152600983015490929161012084019160ff16908111156115ff576115ff613bd3565b600181111561161057611610613bd3565b81526020016009820160019054906101000a900460ff16600581111561163857611638613bd3565b600581111561164957611649613bd3565b815260098201546201000090046001600160a01b03166020820152600a9091015460409091015260105461018082015191925061168591613d6a565b4210156116cb5760405162461bcd60e51b8152602060048201526014602482015273131bd85b881b9bdd08195e1c1a5c9959081e595d60621b6044820152606401610808565b6020808201515f86815260049092526040909120546001600160a01b038216158015906116f757505f81115b1561170c5761170c8282856101600151613798565b5f86815260036020818152604080842060098101805461ff001916610500178155888301516001600160a01b03168652600880855283872060608b0151885285529286208690558b86529284905280546001600160a01b0319908116825560018201805482169055600282018054909116905592830184905560048301849055600583018490556006830184905560078301849055820183905580546001600160b01b0319169055600a01556117c186613804565b604080840180516001600160a01b039081165f908152600760209081528482206060890180518452915290849020805460ff191690559151865192519351632142170760e11b81529116926342842e0e9261182192309290600401613db3565b5f604051808303815f87803b158015611838575f5ffd5b505af1925050508015611849575060015b6118655760405162461bcd60e51b815260040161080890613dd7565b82516040516001600160a01b039091169087907f94140863d1b77b2db4c5904c78e4343ab9c2a51a27e3e6beef9f21109162d93e905f90a35050505050610ff960015f55565b5f516020613f275f395f51905f526118c2816137fa565b60108290556040518281527f67d1057b745e2dfa75389ae5cc1305e8e8cad2ae31b991160a93762ab679af5990602001610c47565b5f516020613f275f395f51905f5261190e816137fa565b610e10821161195f5760405162461bcd60e51b815260206004820152601960248201527f42696443616e63656c506572696f6420746f6f2073686f7274000000000000006044820152606401610808565b60138290556040518281527f1d44ca6a9e035c33fe61047920d46da987cf827176af06973df6ff23a92f9d8890602001610c47565b5f516020613f475f395f51905f526119ab816137fa565b6001600160a01b0383166119f95760405162461bcd60e51b8152602060048201526015602482015274496e76616c696420746f6b656e206164647265737360581b6044820152606401610808565b6001600160a01b0383165f81815260066020908152604091829020805460ff191686151590811790915591519182527f5d6e86e5341d57a92c49934296c51542a25015c9b1782a1c2722a940131c3d9a910161128a565b5f9182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b611a8261363f565b6001600160a01b0382165f90815260066020526040902054829060ff16611ae55760405162461bcd60e51b8152602060048201526017602482015276115490cc8c081d1bdad95b881b9bdd08185b1b1bddd959604a1b6044820152606401610808565b5f8211611b295760405162461bcd60e51b81526020600482015260126024820152710416d6f756e74206d757374206265203e20360741b6044820152606401610808565b611b34338385613798565b6040516323b872dd60e01b81526001600160a01b038416906323b872dd90611b6490339030908790600401613db3565b6020604051808303815f875af1158015611b80573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ba49190613cc2565b611bc05760405162461bcd60e51b815260040161080890613e04565b604080518381526001600160a01b038516602082015233917fdf4dcf17030d087ed1d90ed3c90719c7260706c1c4a88505dd2d0a982887cf43910160405180910390a25061099660015f55565b611c1561363f565b5f8181526003602052604090205481906001600160a01b0316611c4a5760405162461bcd60e51b815260040161080890613d06565b815f5f82815260036020526040902060090154610100900460ff166005811115611c7657611c76613bd3565b14611c935760405162461bcd60e51b815260040161080890613d33565b5f8381526003602052604090206001015483906001600160a01b03163314611cef5760405162461bcd60e51b815260206004820152600f60248201526e2737ba103637b0b7103632b73232b960891b6044820152606401610808565b5f8481526003602090815260408083206013546012909352922054611d149190613d6a565b421015611d635760405162461bcd60e51b815260206004820152601960248201527f4269642063616e63656c20706572696f64206e6f74206d6574000000000000006044820152606401610808565b6001810180545f878152600460208190526040808320805493905584546001600160a01b031916909455600585015460068601556009850154935163a9059cbb60e01b81526001600160a01b039384169181018290526024810183905290939192620100009092049091169063a9059cbb906044016020604051808303815f875af1158015611df4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e189190613cc2565b611e545760405162461bcd60e51b815260206004820152600d60248201526c1499599d5b990819985a5b1959609a1b6044820152606401610808565b6040516001600160a01b0383169088907f5677b1e812c0884dd700a8657ca2619da856a4940cb378aa593b217849a21a8a905f90a3505050505050610ff960015f55565b611ea061363f565b5f8181526003602052604090205481906001600160a01b0316611ed55760405162461bcd60e51b815260040161080890613d06565b5f8281526003602052604090205482906001600160a01b03163314611f0c5760405162461bcd60e51b815260040161080890613e33565b5f83815260036020526040902060016009820154610100900460ff166005811115611f3957611f39613bd3565b14611f785760405162461bcd60e51b815260206004820152600f60248201526e4c6f616e206e6f742061637469766560881b6044820152606401610808565b8060080154421015611fc55760405162461bcd60e51b815260206004820152601660248201527514995c185e5b595b9d081899599bdc99481cdd185c9d60521b6044820152606401610808565b80600701548160080154611fd99190613d6a565b4211156120205760405162461bcd60e51b8152602060048201526015602482015274131bd85b88191d5c985d1a5bdb88195e1c1a5c9959605a1b6044820152606401610808565b5f61202a856134a0565b90505f6127106011548361203e9190613d7d565b6120489190613d94565b90505f6127106011548461205c9190613d7d565b6120669190613d94565b90505f6120738385613d6a565b90505f6120808386613caf565b905061208c8484613d6a565b60098701546201000090046001600160a01b03165f908152600c6020526040812080549091906120bd908490613d6a565b909155505060098601805461ff00191661020017905560028601546001600160a01b03165f90815260086020908152604080832060038a0154845290915281205561210789613804565b60028601546001600160a01b039081165f90815260076020908152604080832060038b015484529091529020805460ff191690556001870154600988015461215b9291821691849162010000900416613798565b60098601546040516323b872dd60e01b8152620100009091046001600160a01b0316906323b872dd9061219690339030908790600401613db3565b6020604051808303815f875af11580156121b2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121d69190613cc2565b6121f25760405162461bcd60e51b815260040161080890613e04565b600286015486546003880154604051632142170760e11b81526001600160a01b03938416936342842e0e936122309330939290911691600401613db3565b5f604051808303815f87803b158015612247575f5ffd5b505af1925050508015612258575060015b6122745760405162461bcd60e51b815260040161080890613dd7565b85546040518381526001600160a01b03909116908a907f512d3e65b3e58c2187bb1872aa435dba5bd09c1c03823ba56ab70aac411e4a219060200160405180910390a35050505050505050610ff960015f55565b6122d061363f565b5f8181526003602052604090205481906001600160a01b03166123055760405162461bcd60e51b815260040161080890613d06565b815f5f82815260036020526040902060090154610100900460ff16600581111561233157612331613bd3565b1461234e5760405162461bcd60e51b815260040161080890613d33565b5f8381526003602052604090205483906001600160a01b031633146123855760405162461bcd60e51b815260040161080890613e33565b5f84815260036020526040902060018101546001600160a01b03166123dc5760405162461bcd60e51b815260206004820152600d60248201526c139bc81b195b99195c88189a59609a1b6044820152606401610808565b806004015460045f8781526020019081526020015f2054146124405760405162461bcd60e51b815260206004820152601760248201527f457363726f7765642066756e6473206d69736d617463680000000000000000006044820152606401610808565b6008810154156124895760405162461bcd60e51b8152602060048201526014602482015273131bd85b88185b1c9958591e481cdd185c9d195960621b6044820152606401610808565b42600882015560098101805461010061ff00199091161781555f868152600460208190526040808320805493905592548454935163a9059cbb60e01b81526001600160a01b0394851692810192909252602482018390529192620100009092049091169063a9059cbb906044016020604051808303815f875af1158015612512573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125369190613cc2565b6125525760405162461bcd60e51b815260040161080890613cdd565b6001820154825460088401546009850154604080519283526001600160a01b0362010000909204821660208401529381169392169189917f520af44c0202f573865f02f53109b8768072746c32dc4eb57c89c27067d8644c910160405180910390a45050505050610ff960015f55565b5f516020613f275f395f51905f526125d9816137fa565b6103e882111561261f5760405162461bcd60e51b815260206004820152601160248201527008ccaca40e4c2e8ca40e8dede40d0d2ced607b1b6044820152606401610808565b60118290556040518281527f9d2a5010795914ccbdd5a4cdee6a9aa2addac5e44443b519897523b67ddfe9fc90602001610c47565b600e8181548110612663575f80fd5b5f91825260209091200154905081565b5f61267d816137fa565b6109965f516020613f475f395f51905f528361294c565b61269c61363f565b5f516020613f275f395f51905f526126b3816137fa565b6001600160a01b0382166126fb5760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606401610808565b6040516370a0823160e01b81523060048201525f906001600160a01b038516906370a0823190602401602060405180830381865afa15801561273f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127639190613e5e565b90505f805b600e548110156127e3575f600e828154811061278657612786613e75565b5f91825260208083209091015480835260039091526040909120600901549091506001600160a01b038089166201000090920416036127da575f818152600460205260409020546127d79084613d6a565b92505b50600101612768565b506001600160a01b0385165f908152600b60205260408120546128068385613caf565b6128109190613caf565b90505f81116128585760405162461bcd60e51b81526020600482015260146024820152734e6f2066756e647320746f20776974686472617760601b6044820152606401610808565b6001600160a01b038681165f818152600c6020526040808220919091555163a9059cbb60e01b81529187166004830152602482018390529063a9059cbb906044016020604051808303815f875af11580156128b5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128d99190613cc2565b6128f55760405162461bcd60e51b815260040161080890613cdd565b604080516001600160a01b0387811682526020820184905288168183015290517f66e1599cb4e6f8b24e2216b3ba87495e0bb4b22a948c322997a50b692ebe32329181900360600190a15050505061099660015f55565b5f8281526001602081905260409091200154612967816137fa565b61107e8383613933565b61297961363f565b6001600160a01b0387165f90815260056020526040902054879060ff166129e25760405162461bcd60e51b815260206004820152601860248201527f4e465420636f6e7472616374206e6f7420616c6c6f77656400000000000000006044820152606401610808565b6001600160a01b0382165f90815260066020526040902054829060ff16612a455760405162461bcd60e51b8152602060048201526017602482015276115490cc8c081d1bdad95b881b9bdd08185b1b1bddd959604a1b6044820152606401610808565b6040516331a9108f60e11b8152600481018990528990899033906001600160a01b03841690636352211e90602401602060405180830381865afa158015612a8e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ab29190613e89565b6001600160a01b031614612af85760405162461bcd60e51b815260206004820152600d60248201526c2737ba1027232a1037bbb732b960991b6044820152606401610808565b600d54600e5410612b4b5760405162461bcd60e51b815260206004820152601960248201527f416374697665206c6f616e206c696d69742072656163686564000000000000006044820152606401610808565b6001600160a01b038b165f9081526007602090815260408083208d845290915290205460ff1615612bbe5760405162461bcd60e51b815260206004820152601a60248201527f4e465420616c726561647920636f6c6c61746572616c697a65640000000000006044820152606401610808565b5f8911612c0d5760405162461bcd60e51b815260206004820152601760248201527f4c6f616e20616d6f756e74206d757374206265203e20300000000000000000006044820152606401610808565b5f8811612c5c5760405162461bcd60e51b815260206004820152601960248201527f496e7465726573742072617465206d757374206265203e2030000000000000006044820152606401610808565b5f8711612ca25760405162461bcd60e51b815260206004820152601460248201527304475726174696f6e206d757374206265203e20360641b6044820152606401610808565b6001866001811115612cb657612cb6613bd3565b1115612cf85760405162461bcd60e51b8152602060048201526011602482015270496e76616c6964206c6f616e207479706560781b6044820152606401610808565b5f6002549050604051806101a00160405280336001600160a01b031681526020015f6001600160a01b031681526020018d6001600160a01b031681526020018c81526020018b81526020018a81526020018a81526020018981526020015f8152602001886001811115612d6d57612d6d613bd3565b81526020015f81526001600160a01b03888116602080840191909152426040938401525f858152600380835290849020855181546001600160a01b03199081169186169190911782559286015160018083018054861692871692909217909155948601516002820180549094169416939093179091556060840151908201556080830151600482015560a0830151600582015560c0830151600682015560e08301516007820155610100830151600882015561012083015160098201805492939192909160ff19909116908381811115612e4957612e49613bd3565b021790555061014082015160098201805461ff001916610100836005811115612e7457612e74613bd3565b02179055506101608201516009820180546001600160a01b03909216620100000262010000600160b01b031990921691909117905561018090910151600a909101555f818152600f60205260408120805460ff19166001908117909155600e8054808301825592527fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fd909101829055612f0e908290613d6a565b6001600160a01b038d165f9081526008602090815260408083208f84529091528120919091556002805491612f4283613ea4565b9190505550600160075f8e6001600160a01b03166001600160a01b031681526020019081526020015f205f8d81526020019081526020015f205f6101000a81548160ff0219169083151502179055508b6001600160a01b03166323b872dd33308e6040518463ffffffff1660e01b8152600401612fc193929190613db3565b5f604051808303815f87803b158015612fd8575f5ffd5b505af1925050508015612fe9575060015b6130055760405162461bcd60e51b815260040161080890613dd7565b336001600160a01b0316817ff7d0570a1cc7150abafdd7f18056e9d7696c7c4cd65d4e6e45b3ff0d1f7cf6fc8e8e8e8e8e8e8e60405161304b9796959493929190613ebc565b60405180910390a3505050505061306160015f55565b50505050505050565b61307261363f565b5f8181526003602052604090205481906001600160a01b03166130a75760405162461bcd60e51b815260040161080890613d06565b815f5f82815260036020526040902060090154610100900460ff1660058111156130d3576130d3613bd3565b146130f05760405162461bcd60e51b815260040161080890613d33565b5f8381526003602052604090205483906001600160a01b031633146131275760405162461bcd60e51b815260040161080890613e33565b5f84815260036020818152604080842081516101a08101835281546001600160a01b0390811682526001808401548216958301959095526002830154169281019290925292830154606082015260048301546080820152600583015460a0820152600683015460c0820152600783015460e08201526008830154610100820152600983015490929161012084019160ff16908111156131c8576131c8613bd3565b60018111156131d9576131d9613bd3565b81526020016009820160019054906101000a900460ff16600581111561320157613201613bd3565b600581111561321257613212613bd3565b815260098201546001600160a01b03620100009091048116602080840191909152600a90930154604090920191909152908201519192505f9082161561327b57505f8681526004602052604081208054919055801561327b5761327b8282856101600151613798565b5f87815260036020818152604080842060098101805461ff001916610400178155888301516001600160a01b03168652600880855283872060608b0151885285529286208690558c86529284905280546001600160a01b0319908116825560018201805482169055600282018054909116905592830184905560048301849055600583018490556006830184905560078301849055820183905580546001600160b01b0319169055600a015561333087613804565b604080840180516001600160a01b039081165f908152600760209081528482206060890180518452915290849020805460ff191690559151865192519351632142170760e11b81529116926342842e0e9261339092309290600401613db3565b5f604051808303815f87803b1580156133a7575f5ffd5b505af19250505080156133b8575060015b6133d45760405162461bcd60e51b815260040161080890613dd7565b82516040516001600160a01b039091169088907f94140863d1b77b2db4c5904c78e4343ab9c2a51a27e3e6beef9f21109162d93e905f90a3505050505050610ff960015f55565b6001600160a01b0382165f90815260086020908152604080832084845290915281205480820361348d5760405162461bcd60e51b815260206004820152601b60248201527f4e6f20616374697665206c6f616e20666f722074686973204e465400000000006044820152606401610808565b613498600182613caf565b949350505050565b5f8181526003602052604081205482906001600160a01b03166134d55760405162461bcd60e51b815260040161080890613d06565b5f8381526003602052604081209080600983015460ff1660018111156134fd576134fd613bd3565b0361352a57612710826006015483600401546135199190613d7d565b6135239190613d94565b9050613626565b6001600983015460ff16600181111561354557613545613bd3565b03613626575f612710836006015484600401546135629190613d7d565b61356c9190613d94565b905060016009840154610100900460ff16600581111561358e5761358e613bd3565b036135e3575f620151808460080154426135a89190613caf565b6135b29190613d94565b905061016d5f82116135c55760016135c7565b815b6135d19084613d7d565b6135db9190613d94565b925050613624565b5f6201518084600701546135f79190613d94565b905061016d5f821161360a57600161360c565b815b6136169084613d7d565b6136209190613d94565b9250505b505b8082600401546136369190613d6a565b95945050505050565b60025f540361366157604051633ee5aeb560e01b815260040160405180910390fd5b60025f55565b6001600160a01b038084165f908152600a60209081526040808320938516835292905220548211156136d25760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b6044820152606401610808565b6001600160a01b038084165f908152600a6020908152604080832093851683529290529081208054849290613708908490613caf565b90915550506001600160a01b0381165f908152600b602052604081208054849290613734908490613caf565b90915550506001600160a01b038381165f818152600a60209081526040808320948616808452948252918290205482519081529081019390935290917f8b45c1c793e9a443eda263a15f880b1367ef5e2fb442a38fdd8f813b05e32c9b910161128a565b6001600160a01b038084165f908152600a60209081526040808320938516835292905290812080548492906137ce908490613d6a565b90915550506001600160a01b0381165f908152600b602052604081208054849290613734908490613d6a565b610ff9813361399e565b5f818152600f60205260408120805460ff191690555b600e548110156109965781600e828154811061383857613838613e75565b905f5260205f200154036138b557600e805461385690600190613caf565b8154811061386657613866613e75565b905f5260205f200154600e828154811061388257613882613e75565b5f91825260209091200155600e80548061389e5761389e613f12565b600190038181905f5260205f20015f905590555050565b60010161381a565b5f6138c88383611a50565b61392c575f8381526001602081815260408084206001600160a01b0387168086529252808420805460ff19169093179092559051339286917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45060016109ca565b505f6109ca565b5f61393e8383611a50565b1561392c575f8381526001602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45060016109ca565b6139a88282611a50565b6109965760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610808565b6001600160a01b0381168114610ff9575f5ffd5b5f5f604083850312156139fc575f5ffd5b8235613a07816139d7565b91506020830135613a17816139d7565b809150509250929050565b5f60208284031215613a32575f5ffd5b81356001600160e01b031981168114611052575f5ffd5b5f60208284031215613a59575f5ffd5b8135611052816139d7565b602080825282518282018190525f918401906040840190835b81811015613a9b578351835260209384019390920191600101613a7d565b509095945050505050565b5f5f60408385031215613ab7575f5ffd5b50508035926020909101359150565b5f60208284031215613ad6575f5ffd5b5035919050565b5f5f60408385031215613aee575f5ffd5b8235613af9816139d7565b946020939093013593505050565b5f5f60408385031215613b18575f5ffd5b823591506020830135613a17816139d7565b8015158114610ff9575f5ffd5b5f5f60408385031215613b48575f5ffd5b8235613b53816139d7565b91506020830135613a1781613b2a565b5f5f5f5f5f5f5f60e0888a031215613b79575f5ffd5b8735613b84816139d7565b96506020880135955060408801359450606088013593506080880135925060a088013560028110613bb3575f5ffd5b915060c0880135613bc3816139d7565b8091505092959891949750929550565b634e487b7160e01b5f52602160045260245ffd5b60028110613bf757613bf7613bd3565b9052565b6001600160a01b038e811682528d811660208301528c166040820152606081018b9052608081018a905260a0810189905260c0810188905260e0810187905261010081018690526101a08101613c55610120830187613be7565b60068510613c6557613c65613bd3565b84610140830152613c826101608301856001600160a01b03169052565b826101808301529e9d5050505050505050505050505050565b634e487b7160e01b5f52601160045260245ffd5b818103818111156109ca576109ca613c9b565b5f60208284031215613cd2575f5ffd5b815161105281613b2a565b6020808252600f908201526e151c985b9cd9995c8819985a5b1959608a1b604082015260600190565b602080825260139082015272131bd85b88191bd95cc81b9bdd08195e1a5cdd606a1b604082015260600190565b60208082526019908201527f4c6f616e206e6f7420696e204c69737465642073746174757300000000000000604082015260600190565b808201808211156109ca576109ca613c9b565b80820281158282048414176109ca576109ca613c9b565b5f82613dae57634e487b7160e01b5f52601260045260245ffd5b500490565b6001600160a01b039384168152919092166020820152604081019190915260600190565b602080825260139082015272139195081d1c985b9cd9995c8819985a5b1959606a1b604082015260600190565b602080825260159082015274151bdad95b881d1c985b9cd9995c8819985a5b1959605a1b604082015260600190565b6020808252601190820152702737ba103637b0b7103137b93937bbb2b960791b604082015260600190565b5f60208284031215613e6e575f5ffd5b5051919050565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215613e99575f5ffd5b8151611052816139d7565b5f60018201613eb557613eb5613c9b565b5060010190565b6001600160a01b03881681526020810187905260408101869052606081018590526080810184905260e08101613ef560a0830185613be7565b6001600160a01b039290921660c091909101529695505050505050565b634e487b7160e01b5f52603160045260245ffdfeb19546dff01e856fb3f010c267a7b1c60363cf8a4664e21cc89c26224620214e241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b08a2646970667358221220453c1b00b692d14815345b06344c033036ccec610be965dd7275d35dbc1d393b64736f6c634300081c00330000000000000000000000002190a1dba3e0a9f7216efc929e6faead44ad6131000000000000000000000000d9d96fb150136798861363d8ad9fe4033cfc32b3

Deployed Bytecode

0x608060405234801561000f575f5ffd5b50600436106102ca575f3560e01c8063921b004b1161017b578063c153a234116100e4578063e1ec3c681161009e578063e58378bb11610079578063e58378bb14610748578063e5a7bfd01461075c578063e5e05bd714610789578063ec87621c146107a8575f5ffd5b8063e1ec3c681461067a578063e2cb0e4d14610722578063e505d0c514610735575f5ffd5b8063c153a234146105f8578063c9a759cb14610625578063cf7b287f1461062e578063d547741f14610641578063deb0ef4014610654578063deb298c314610667575f5ffd5b8063adfbe22f11610135578063adfbe22f1461056b578063b13aa2d61461057e578063b489e14714610591578063b93186ba146105b0578063be4dc94f146105c3578063c0f6ae97146105d6575f5ffd5b8063921b004b146104e25780639703ef35146104f55780639b087af414610508578063a217fddf14610527578063ab7b1c891461052e578063adb5198014610541575f5ffd5b80633360874c116102375780635d54f5c1116101f157806374a3f8fc116101cc57806374a3f8fc146104965780638f112248146104a95780638fd4f224146104bc57806391d14854146104cf575f5ffd5b80635d54f5c1146104585780636729629f1461046b5780636f504fd714610474575f5ffd5b80633360874c146103eb57806334d9289e146103fe57806336568abe1461040757806347126f621461041a57806357c90de51461043c57806358f858801461044f575f5ffd5b8063248a9ca311610288578063248a9ca31461037357806326e885e31461039657806327f8dce3146103a95780632f010b43146103bc5780632f2ff15d146103cf57806332b814ba146103e2575f5ffd5b80620fa9fb146102ce57806301ffc9a7146102e3578063079712b61461030b578063121ed2a714610338578063158c6bdb1461034d5780631e837ef314610360575b5f5ffd5b6102e16102dc3660046139eb565b6107bc565b005b6102f66102f1366004613a22565b61099a565b60405190151581526020015b60405180910390f35b61032a610319366004613a49565b600b6020525f908152604090205481565b604051908152602001610302565b6103406109d0565b6040516103029190613a64565b6102e161035b366004613aa6565b610a26565b6102e161036e366004613ac6565b610bff565b61032a610381366004613ac6565b5f908152600160208190526040909120015490565b6102e16103a4366004613a49565b610c53565b6102e16103b7366004613ac6565b610c74565b6102f66103ca366004613add565b610ffc565b6102e16103dd366004613b07565b611059565b61032a60135481565b6102e16103f9366004613b37565b611084565b61032a60025481565b6102e1610415366004613b07565b611297565b6102f6610428366004613ac6565b600f6020525f908152604090205460ff1681565b6102e161044a366004613aa6565b6112cf565b61032a60115481565b6102e1610466366004613ac6565b6114d8565b61032a60105481565b6102f6610482366004613a49565b60066020525f908152604090205460ff1681565b6102e16104a4366004613ac6565b6118ab565b6102e16104b7366004613ac6565b6118f7565b6102e16104ca366004613b37565b611994565b6102f66104dd366004613b07565b611a50565b6102e16104f0366004613add565b611a7a565b6102e1610503366004613ac6565b611c0d565b61032a610516366004613ac6565b60126020525f908152604090205481565b61032a5f81565b6102e161053c366004613ac6565b611e98565b61032a61054f3660046139eb565b600a60209081525f928352604080842090915290825290205481565b6102e1610579366004613ac6565b6122c8565b6102e161058c366004613ac6565b6125c2565b61032a61059f366004613ac6565b60046020525f908152604090205481565b61032a6105be366004613ac6565b612654565b6102e16105d1366004613a49565b612673565b6102f66105e4366004613a49565b60056020525f908152604090205460ff1681565b6102f6610606366004613add565b600960209081525f928352604080842090915290825290205460ff1681565b61032a600d5481565b6102e161063c3660046139eb565b612694565b6102e161064f366004613b07565b61294c565b6102e1610662366004613b63565b612971565b6102e1610675366004613ac6565b61306a565b610709610688366004613ac6565b600360208190525f918252604090912080546001820154600283015493830154600484015460058501546006860154600787015460088801546009890154600a909901546001600160a01b039889169a978916999789169896979596949593949293919260ff8084169361010081049091169262010000909104909116908d565b6040516103029d9c9b9a99989796959493929190613bfb565b61032a610730366004613add565b61341b565b61032a610743366004613ac6565b6134a0565b61032a5f516020613f275f395f51905f5281565b6102f661076a366004613add565b600760209081525f928352604080842090915290825290205460ff1681565b61032a610797366004613a49565b600c6020525f908152604090205481565b61032a5f516020613f475f395f51905f5281565b6107c461363f565b6001600160a01b0381166108115760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b60448201526064015b60405180910390fd5b335f908152600a602090815260408083206001600160a01b0386168452909152902054806108785760405162461bcd60e51b81526020600482015260146024820152734e6f2066756e647320746f20776974686472617760601b6044820152606401610808565b335f908152600a602090815260408083206001600160a01b03871684528252808320839055600b909152812080548392906108b4908490613caf565b909155505060405163a9059cbb60e01b81526001600160a01b0383811660048301526024820183905284169063a9059cbb906044016020604051808303815f875af1158015610905573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109299190613cc2565b6109455760405162461bcd60e51b815260040161080890613cdd565b604080518281526001600160a01b03858116602083015284169133917fc6d7522ade22416d5f000975be266c973d54b87b3bd997daeba6258d2de358d1910160405180910390a35061099660015f55565b5050565b5f6001600160e01b03198216637965db0b60e01b14806109ca57506301ffc9a760e01b6001600160e01b03198316145b92915050565b6060600e805480602002602001604051908101604052809291908181526020018280548015610a1c57602002820191905f5260205f20905b815481526020019060010190808311610a08575b5050505050905090565b610a2e61363f565b5f8281526003602052604090205482906001600160a01b0316610a635760405162461bcd60e51b815260040161080890613d06565b825f5f82815260036020526040902060090154610100900460ff166005811115610a8f57610a8f613bd3565b14610aac5760405162461bcd60e51b815260040161080890613d33565b5f848152600360205260409020600681015484108015610ad0575080600501548411155b610b185760405162461bcd60e51b8152602060048201526019602482015278109a59081a5b9d195c995cdd081c985d19481a5b9d985b1a59603a1b6044820152606401610808565b60048101546009820154610b3c9133916201000090046001600160a01b0316613667565b6001810180545f87815260046020818152604080842080546001600160a01b031987163317909755600688018b9055928701549092556012905290204290556001600160a01b0316908115801590610b9357505f81115b15610bba57610bba82828560090160029054906101000a90046001600160a01b0316613798565b604051868152339088907fd1b9c752eb85783f53e7669e993e44d05db062782a8dcd72a7757746cc981f1d9060200160405180910390a3505050505061099660015f55565b5f516020613f275f395f51905f52610c16816137fa565b600d8290556040518281527fc59dcd6979a73a87266c1f2a5d4c4108daee42034d779ef00e960a510c789625906020015b60405180910390a15050565b5f610c5d816137fa565b6109965f516020613f475f395f51905f5283611059565b610c7c61363f565b5f8181526003602052604090205481906001600160a01b0316610cb15760405162461bcd60e51b815260040161080890613d06565b5f82815260036020526040902060016009820154610100900460ff166005811115610cde57610cde613bd3565b14610d1d5760405162461bcd60e51b815260206004820152600f60248201526e4c6f616e206e6f742061637469766560881b6044820152606401610808565b80600701548160080154610d319190613d6a565b4211610d725760405162461bcd60e51b815260206004820152601060248201526f131bd85b881b9bdd08195e1c1a5c995960821b6044820152606401610808565b5f610d7c846134a0565b90505f61271060115483610d909190613d7d565b610d9a9190613d94565b60098401546040516323b872dd60e01b81529192506201000090046001600160a01b0316906323b872dd90610dd790339030908690600401613db3565b6020604051808303815f875af1158015610df3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e179190613cc2565b610e635760405162461bcd60e51b815260206004820152601c60248201527f50726f746f636f6c20666565207472616e73666572206661696c6564000000006044820152606401610808565b60098301546201000090046001600160a01b03165f908152600c602052604081208054839290610e94908490613d6a565b909155505060098301805461ff00191661030017905560028301546001600160a01b03165f90815260086020908152604080832060038701548452909152812055610ede85613804565b6002830180546001600160a01b039081165f9081526007602090815260408083206003890180548552908352818420805460ff199081169091558654861685526009845282852082548652909352928190208054909216600190811790925593549087015491549351632142170760e11b8152908316936342842e0e93610f6d93309391169190600401613db3565b5f604051808303815f87803b158015610f84575f5ffd5b505af1925050508015610f95575060015b610fb15760405162461bcd60e51b815260040161080890613dd7565b60018301546040516001600160a01b039091169086907f13b88e6866f0156d706fecfa22b678de5fc2b749c1d2307f6f47eb541385f1ec905f90a350505050610ff960015f55565b50565b6001600160a01b0382165f90815260076020908152604080832084845290915281205460ff1615801561105257506001600160a01b0383165f90815260096020908152604080832085845290915290205460ff16155b9392505050565b5f8281526001602081905260409091200154611074816137fa565b61107e83836138bd565b50505050565b5f516020613f475f395f51905f5261109b816137fa565b6001600160a01b0383166110e75760405162461bcd60e51b8152602060048201526013602482015272496e76616c6964204e4654206164647265737360681b6044820152606401610808565b5f836001600160a01b03163b116111405760405162461bcd60e51b815260206004820152601960248201527f41646472657373206973206e6f74206120636f6e7472616374000000000000006044820152606401610808565b6040516301ffc9a760e01b81526380ac58cd60e01b60048201526001600160a01b038416906301ffc9a790602401602060405180830381865afa9250505080156111a7575060408051601f3d908101601f191682019092526111a491810190613cc2565b60015b6111f35760405162461bcd60e51b815260206004820152601760248201527f4661696c656420746f20766572696679204552433732310000000000000000006044820152606401610808565b806112365760405162461bcd60e51b8152602060048201526013602482015272436f6e7472616374206e6f742045524337323160681b6044820152606401610808565b506001600160a01b0383165f81815260056020908152604091829020805460ff191686151590811790915591519182527f7fe996db267f9f17db8865f67d54879fc39a38a2a37afa158a9ea52357e794ac91015b60405180910390a2505050565b6001600160a01b03811633146112c05760405163334bd91960e11b815260040160405180910390fd5b6112ca8282613933565b505050565b6112d761363f565b5f8281526003602052604090205482906001600160a01b031661130c5760405162461bcd60e51b815260040161080890613d06565b825f5f82815260036020526040902060090154610100900460ff16600581111561133857611338613bd3565b146113555760405162461bcd60e51b815260040161080890613d33565b5f848152600360205260409020600681015484108015611379575080600501548411155b6113c15760405162461bcd60e51b8152602060048201526019602482015278109a59081a5b9d195c995cdd081c985d19481a5b9d985b1a59603a1b6044820152606401610808565b6001810180545f87815260046020818152604080842080546001600160a01b031987163317909755600688018b9055928701549092556012905290204290556001600160a01b031690811580159061141857505f81115b1561143f5761143f82828560090160029054906101000a90046001600160a01b0316613798565b60098301546004808501546040516323b872dd60e01b8152620100009093046001600160a01b0316926323b872dd9261147c923392309201613db3565b6020604051808303815f875af1158015611498573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114bc9190613cc2565b610bba5760405162461bcd60e51b815260040161080890613e04565b6114e061363f565b5f8181526003602052604090205481906001600160a01b03166115155760405162461bcd60e51b815260040161080890613d06565b815f5f82815260036020526040902060090154610100900460ff16600581111561154157611541613bd3565b1461155e5760405162461bcd60e51b815260040161080890613d33565b5f83815260036020818152604080842081516101a08101835281546001600160a01b0390811682526001808401548216958301959095526002830154169281019290925292830154606082015260048301546080820152600583015460a0820152600683015460c0820152600783015460e08201526008830154610100820152600983015490929161012084019160ff16908111156115ff576115ff613bd3565b600181111561161057611610613bd3565b81526020016009820160019054906101000a900460ff16600581111561163857611638613bd3565b600581111561164957611649613bd3565b815260098201546201000090046001600160a01b03166020820152600a9091015460409091015260105461018082015191925061168591613d6a565b4210156116cb5760405162461bcd60e51b8152602060048201526014602482015273131bd85b881b9bdd08195e1c1a5c9959081e595d60621b6044820152606401610808565b6020808201515f86815260049092526040909120546001600160a01b038216158015906116f757505f81115b1561170c5761170c8282856101600151613798565b5f86815260036020818152604080842060098101805461ff001916610500178155888301516001600160a01b03168652600880855283872060608b0151885285529286208690558b86529284905280546001600160a01b0319908116825560018201805482169055600282018054909116905592830184905560048301849055600583018490556006830184905560078301849055820183905580546001600160b01b0319169055600a01556117c186613804565b604080840180516001600160a01b039081165f908152600760209081528482206060890180518452915290849020805460ff191690559151865192519351632142170760e11b81529116926342842e0e9261182192309290600401613db3565b5f604051808303815f87803b158015611838575f5ffd5b505af1925050508015611849575060015b6118655760405162461bcd60e51b815260040161080890613dd7565b82516040516001600160a01b039091169087907f94140863d1b77b2db4c5904c78e4343ab9c2a51a27e3e6beef9f21109162d93e905f90a35050505050610ff960015f55565b5f516020613f275f395f51905f526118c2816137fa565b60108290556040518281527f67d1057b745e2dfa75389ae5cc1305e8e8cad2ae31b991160a93762ab679af5990602001610c47565b5f516020613f275f395f51905f5261190e816137fa565b610e10821161195f5760405162461bcd60e51b815260206004820152601960248201527f42696443616e63656c506572696f6420746f6f2073686f7274000000000000006044820152606401610808565b60138290556040518281527f1d44ca6a9e035c33fe61047920d46da987cf827176af06973df6ff23a92f9d8890602001610c47565b5f516020613f475f395f51905f526119ab816137fa565b6001600160a01b0383166119f95760405162461bcd60e51b8152602060048201526015602482015274496e76616c696420746f6b656e206164647265737360581b6044820152606401610808565b6001600160a01b0383165f81815260066020908152604091829020805460ff191686151590811790915591519182527f5d6e86e5341d57a92c49934296c51542a25015c9b1782a1c2722a940131c3d9a910161128a565b5f9182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b611a8261363f565b6001600160a01b0382165f90815260066020526040902054829060ff16611ae55760405162461bcd60e51b8152602060048201526017602482015276115490cc8c081d1bdad95b881b9bdd08185b1b1bddd959604a1b6044820152606401610808565b5f8211611b295760405162461bcd60e51b81526020600482015260126024820152710416d6f756e74206d757374206265203e20360741b6044820152606401610808565b611b34338385613798565b6040516323b872dd60e01b81526001600160a01b038416906323b872dd90611b6490339030908790600401613db3565b6020604051808303815f875af1158015611b80573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ba49190613cc2565b611bc05760405162461bcd60e51b815260040161080890613e04565b604080518381526001600160a01b038516602082015233917fdf4dcf17030d087ed1d90ed3c90719c7260706c1c4a88505dd2d0a982887cf43910160405180910390a25061099660015f55565b611c1561363f565b5f8181526003602052604090205481906001600160a01b0316611c4a5760405162461bcd60e51b815260040161080890613d06565b815f5f82815260036020526040902060090154610100900460ff166005811115611c7657611c76613bd3565b14611c935760405162461bcd60e51b815260040161080890613d33565b5f8381526003602052604090206001015483906001600160a01b03163314611cef5760405162461bcd60e51b815260206004820152600f60248201526e2737ba103637b0b7103632b73232b960891b6044820152606401610808565b5f8481526003602090815260408083206013546012909352922054611d149190613d6a565b421015611d635760405162461bcd60e51b815260206004820152601960248201527f4269642063616e63656c20706572696f64206e6f74206d6574000000000000006044820152606401610808565b6001810180545f878152600460208190526040808320805493905584546001600160a01b031916909455600585015460068601556009850154935163a9059cbb60e01b81526001600160a01b039384169181018290526024810183905290939192620100009092049091169063a9059cbb906044016020604051808303815f875af1158015611df4573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e189190613cc2565b611e545760405162461bcd60e51b815260206004820152600d60248201526c1499599d5b990819985a5b1959609a1b6044820152606401610808565b6040516001600160a01b0383169088907f5677b1e812c0884dd700a8657ca2619da856a4940cb378aa593b217849a21a8a905f90a3505050505050610ff960015f55565b611ea061363f565b5f8181526003602052604090205481906001600160a01b0316611ed55760405162461bcd60e51b815260040161080890613d06565b5f8281526003602052604090205482906001600160a01b03163314611f0c5760405162461bcd60e51b815260040161080890613e33565b5f83815260036020526040902060016009820154610100900460ff166005811115611f3957611f39613bd3565b14611f785760405162461bcd60e51b815260206004820152600f60248201526e4c6f616e206e6f742061637469766560881b6044820152606401610808565b8060080154421015611fc55760405162461bcd60e51b815260206004820152601660248201527514995c185e5b595b9d081899599bdc99481cdd185c9d60521b6044820152606401610808565b80600701548160080154611fd99190613d6a565b4211156120205760405162461bcd60e51b8152602060048201526015602482015274131bd85b88191d5c985d1a5bdb88195e1c1a5c9959605a1b6044820152606401610808565b5f61202a856134a0565b90505f6127106011548361203e9190613d7d565b6120489190613d94565b90505f6127106011548461205c9190613d7d565b6120669190613d94565b90505f6120738385613d6a565b90505f6120808386613caf565b905061208c8484613d6a565b60098701546201000090046001600160a01b03165f908152600c6020526040812080549091906120bd908490613d6a565b909155505060098601805461ff00191661020017905560028601546001600160a01b03165f90815260086020908152604080832060038a0154845290915281205561210789613804565b60028601546001600160a01b039081165f90815260076020908152604080832060038b015484529091529020805460ff191690556001870154600988015461215b9291821691849162010000900416613798565b60098601546040516323b872dd60e01b8152620100009091046001600160a01b0316906323b872dd9061219690339030908790600401613db3565b6020604051808303815f875af11580156121b2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121d69190613cc2565b6121f25760405162461bcd60e51b815260040161080890613e04565b600286015486546003880154604051632142170760e11b81526001600160a01b03938416936342842e0e936122309330939290911691600401613db3565b5f604051808303815f87803b158015612247575f5ffd5b505af1925050508015612258575060015b6122745760405162461bcd60e51b815260040161080890613dd7565b85546040518381526001600160a01b03909116908a907f512d3e65b3e58c2187bb1872aa435dba5bd09c1c03823ba56ab70aac411e4a219060200160405180910390a35050505050505050610ff960015f55565b6122d061363f565b5f8181526003602052604090205481906001600160a01b03166123055760405162461bcd60e51b815260040161080890613d06565b815f5f82815260036020526040902060090154610100900460ff16600581111561233157612331613bd3565b1461234e5760405162461bcd60e51b815260040161080890613d33565b5f8381526003602052604090205483906001600160a01b031633146123855760405162461bcd60e51b815260040161080890613e33565b5f84815260036020526040902060018101546001600160a01b03166123dc5760405162461bcd60e51b815260206004820152600d60248201526c139bc81b195b99195c88189a59609a1b6044820152606401610808565b806004015460045f8781526020019081526020015f2054146124405760405162461bcd60e51b815260206004820152601760248201527f457363726f7765642066756e6473206d69736d617463680000000000000000006044820152606401610808565b6008810154156124895760405162461bcd60e51b8152602060048201526014602482015273131bd85b88185b1c9958591e481cdd185c9d195960621b6044820152606401610808565b42600882015560098101805461010061ff00199091161781555f868152600460208190526040808320805493905592548454935163a9059cbb60e01b81526001600160a01b0394851692810192909252602482018390529192620100009092049091169063a9059cbb906044016020604051808303815f875af1158015612512573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125369190613cc2565b6125525760405162461bcd60e51b815260040161080890613cdd565b6001820154825460088401546009850154604080519283526001600160a01b0362010000909204821660208401529381169392169189917f520af44c0202f573865f02f53109b8768072746c32dc4eb57c89c27067d8644c910160405180910390a45050505050610ff960015f55565b5f516020613f275f395f51905f526125d9816137fa565b6103e882111561261f5760405162461bcd60e51b815260206004820152601160248201527008ccaca40e4c2e8ca40e8dede40d0d2ced607b1b6044820152606401610808565b60118290556040518281527f9d2a5010795914ccbdd5a4cdee6a9aa2addac5e44443b519897523b67ddfe9fc90602001610c47565b600e8181548110612663575f80fd5b5f91825260209091200154905081565b5f61267d816137fa565b6109965f516020613f475f395f51905f528361294c565b61269c61363f565b5f516020613f275f395f51905f526126b3816137fa565b6001600160a01b0382166126fb5760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606401610808565b6040516370a0823160e01b81523060048201525f906001600160a01b038516906370a0823190602401602060405180830381865afa15801561273f573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127639190613e5e565b90505f805b600e548110156127e3575f600e828154811061278657612786613e75565b5f91825260208083209091015480835260039091526040909120600901549091506001600160a01b038089166201000090920416036127da575f818152600460205260409020546127d79084613d6a565b92505b50600101612768565b506001600160a01b0385165f908152600b60205260408120546128068385613caf565b6128109190613caf565b90505f81116128585760405162461bcd60e51b81526020600482015260146024820152734e6f2066756e647320746f20776974686472617760601b6044820152606401610808565b6001600160a01b038681165f818152600c6020526040808220919091555163a9059cbb60e01b81529187166004830152602482018390529063a9059cbb906044016020604051808303815f875af11580156128b5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128d99190613cc2565b6128f55760405162461bcd60e51b815260040161080890613cdd565b604080516001600160a01b0387811682526020820184905288168183015290517f66e1599cb4e6f8b24e2216b3ba87495e0bb4b22a948c322997a50b692ebe32329181900360600190a15050505061099660015f55565b5f8281526001602081905260409091200154612967816137fa565b61107e8383613933565b61297961363f565b6001600160a01b0387165f90815260056020526040902054879060ff166129e25760405162461bcd60e51b815260206004820152601860248201527f4e465420636f6e7472616374206e6f7420616c6c6f77656400000000000000006044820152606401610808565b6001600160a01b0382165f90815260066020526040902054829060ff16612a455760405162461bcd60e51b8152602060048201526017602482015276115490cc8c081d1bdad95b881b9bdd08185b1b1bddd959604a1b6044820152606401610808565b6040516331a9108f60e11b8152600481018990528990899033906001600160a01b03841690636352211e90602401602060405180830381865afa158015612a8e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ab29190613e89565b6001600160a01b031614612af85760405162461bcd60e51b815260206004820152600d60248201526c2737ba1027232a1037bbb732b960991b6044820152606401610808565b600d54600e5410612b4b5760405162461bcd60e51b815260206004820152601960248201527f416374697665206c6f616e206c696d69742072656163686564000000000000006044820152606401610808565b6001600160a01b038b165f9081526007602090815260408083208d845290915290205460ff1615612bbe5760405162461bcd60e51b815260206004820152601a60248201527f4e465420616c726561647920636f6c6c61746572616c697a65640000000000006044820152606401610808565b5f8911612c0d5760405162461bcd60e51b815260206004820152601760248201527f4c6f616e20616d6f756e74206d757374206265203e20300000000000000000006044820152606401610808565b5f8811612c5c5760405162461bcd60e51b815260206004820152601960248201527f496e7465726573742072617465206d757374206265203e2030000000000000006044820152606401610808565b5f8711612ca25760405162461bcd60e51b815260206004820152601460248201527304475726174696f6e206d757374206265203e20360641b6044820152606401610808565b6001866001811115612cb657612cb6613bd3565b1115612cf85760405162461bcd60e51b8152602060048201526011602482015270496e76616c6964206c6f616e207479706560781b6044820152606401610808565b5f6002549050604051806101a00160405280336001600160a01b031681526020015f6001600160a01b031681526020018d6001600160a01b031681526020018c81526020018b81526020018a81526020018a81526020018981526020015f8152602001886001811115612d6d57612d6d613bd3565b81526020015f81526001600160a01b03888116602080840191909152426040938401525f858152600380835290849020855181546001600160a01b03199081169186169190911782559286015160018083018054861692871692909217909155948601516002820180549094169416939093179091556060840151908201556080830151600482015560a0830151600582015560c0830151600682015560e08301516007820155610100830151600882015561012083015160098201805492939192909160ff19909116908381811115612e4957612e49613bd3565b021790555061014082015160098201805461ff001916610100836005811115612e7457612e74613bd3565b02179055506101608201516009820180546001600160a01b03909216620100000262010000600160b01b031990921691909117905561018090910151600a909101555f818152600f60205260408120805460ff19166001908117909155600e8054808301825592527fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fd909101829055612f0e908290613d6a565b6001600160a01b038d165f9081526008602090815260408083208f84529091528120919091556002805491612f4283613ea4565b9190505550600160075f8e6001600160a01b03166001600160a01b031681526020019081526020015f205f8d81526020019081526020015f205f6101000a81548160ff0219169083151502179055508b6001600160a01b03166323b872dd33308e6040518463ffffffff1660e01b8152600401612fc193929190613db3565b5f604051808303815f87803b158015612fd8575f5ffd5b505af1925050508015612fe9575060015b6130055760405162461bcd60e51b815260040161080890613dd7565b336001600160a01b0316817ff7d0570a1cc7150abafdd7f18056e9d7696c7c4cd65d4e6e45b3ff0d1f7cf6fc8e8e8e8e8e8e8e60405161304b9796959493929190613ebc565b60405180910390a3505050505061306160015f55565b50505050505050565b61307261363f565b5f8181526003602052604090205481906001600160a01b03166130a75760405162461bcd60e51b815260040161080890613d06565b815f5f82815260036020526040902060090154610100900460ff1660058111156130d3576130d3613bd3565b146130f05760405162461bcd60e51b815260040161080890613d33565b5f8381526003602052604090205483906001600160a01b031633146131275760405162461bcd60e51b815260040161080890613e33565b5f84815260036020818152604080842081516101a08101835281546001600160a01b0390811682526001808401548216958301959095526002830154169281019290925292830154606082015260048301546080820152600583015460a0820152600683015460c0820152600783015460e08201526008830154610100820152600983015490929161012084019160ff16908111156131c8576131c8613bd3565b60018111156131d9576131d9613bd3565b81526020016009820160019054906101000a900460ff16600581111561320157613201613bd3565b600581111561321257613212613bd3565b815260098201546001600160a01b03620100009091048116602080840191909152600a90930154604090920191909152908201519192505f9082161561327b57505f8681526004602052604081208054919055801561327b5761327b8282856101600151613798565b5f87815260036020818152604080842060098101805461ff001916610400178155888301516001600160a01b03168652600880855283872060608b0151885285529286208690558c86529284905280546001600160a01b0319908116825560018201805482169055600282018054909116905592830184905560048301849055600583018490556006830184905560078301849055820183905580546001600160b01b0319169055600a015561333087613804565b604080840180516001600160a01b039081165f908152600760209081528482206060890180518452915290849020805460ff191690559151865192519351632142170760e11b81529116926342842e0e9261339092309290600401613db3565b5f604051808303815f87803b1580156133a7575f5ffd5b505af19250505080156133b8575060015b6133d45760405162461bcd60e51b815260040161080890613dd7565b82516040516001600160a01b039091169088907f94140863d1b77b2db4c5904c78e4343ab9c2a51a27e3e6beef9f21109162d93e905f90a3505050505050610ff960015f55565b6001600160a01b0382165f90815260086020908152604080832084845290915281205480820361348d5760405162461bcd60e51b815260206004820152601b60248201527f4e6f20616374697665206c6f616e20666f722074686973204e465400000000006044820152606401610808565b613498600182613caf565b949350505050565b5f8181526003602052604081205482906001600160a01b03166134d55760405162461bcd60e51b815260040161080890613d06565b5f8381526003602052604081209080600983015460ff1660018111156134fd576134fd613bd3565b0361352a57612710826006015483600401546135199190613d7d565b6135239190613d94565b9050613626565b6001600983015460ff16600181111561354557613545613bd3565b03613626575f612710836006015484600401546135629190613d7d565b61356c9190613d94565b905060016009840154610100900460ff16600581111561358e5761358e613bd3565b036135e3575f620151808460080154426135a89190613caf565b6135b29190613d94565b905061016d5f82116135c55760016135c7565b815b6135d19084613d7d565b6135db9190613d94565b925050613624565b5f6201518084600701546135f79190613d94565b905061016d5f821161360a57600161360c565b815b6136169084613d7d565b6136209190613d94565b9250505b505b8082600401546136369190613d6a565b95945050505050565b60025f540361366157604051633ee5aeb560e01b815260040160405180910390fd5b60025f55565b6001600160a01b038084165f908152600a60209081526040808320938516835292905220548211156136d25760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b6044820152606401610808565b6001600160a01b038084165f908152600a6020908152604080832093851683529290529081208054849290613708908490613caf565b90915550506001600160a01b0381165f908152600b602052604081208054849290613734908490613caf565b90915550506001600160a01b038381165f818152600a60209081526040808320948616808452948252918290205482519081529081019390935290917f8b45c1c793e9a443eda263a15f880b1367ef5e2fb442a38fdd8f813b05e32c9b910161128a565b6001600160a01b038084165f908152600a60209081526040808320938516835292905290812080548492906137ce908490613d6a565b90915550506001600160a01b0381165f908152600b602052604081208054849290613734908490613d6a565b610ff9813361399e565b5f818152600f60205260408120805460ff191690555b600e548110156109965781600e828154811061383857613838613e75565b905f5260205f200154036138b557600e805461385690600190613caf565b8154811061386657613866613e75565b905f5260205f200154600e828154811061388257613882613e75565b5f91825260209091200155600e80548061389e5761389e613f12565b600190038181905f5260205f20015f905590555050565b60010161381a565b5f6138c88383611a50565b61392c575f8381526001602081815260408084206001600160a01b0387168086529252808420805460ff19169093179092559051339286917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45060016109ca565b505f6109ca565b5f61393e8383611a50565b1561392c575f8381526001602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45060016109ca565b6139a88282611a50565b6109965760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610808565b6001600160a01b0381168114610ff9575f5ffd5b5f5f604083850312156139fc575f5ffd5b8235613a07816139d7565b91506020830135613a17816139d7565b809150509250929050565b5f60208284031215613a32575f5ffd5b81356001600160e01b031981168114611052575f5ffd5b5f60208284031215613a59575f5ffd5b8135611052816139d7565b602080825282518282018190525f918401906040840190835b81811015613a9b578351835260209384019390920191600101613a7d565b509095945050505050565b5f5f60408385031215613ab7575f5ffd5b50508035926020909101359150565b5f60208284031215613ad6575f5ffd5b5035919050565b5f5f60408385031215613aee575f5ffd5b8235613af9816139d7565b946020939093013593505050565b5f5f60408385031215613b18575f5ffd5b823591506020830135613a17816139d7565b8015158114610ff9575f5ffd5b5f5f60408385031215613b48575f5ffd5b8235613b53816139d7565b91506020830135613a1781613b2a565b5f5f5f5f5f5f5f60e0888a031215613b79575f5ffd5b8735613b84816139d7565b96506020880135955060408801359450606088013593506080880135925060a088013560028110613bb3575f5ffd5b915060c0880135613bc3816139d7565b8091505092959891949750929550565b634e487b7160e01b5f52602160045260245ffd5b60028110613bf757613bf7613bd3565b9052565b6001600160a01b038e811682528d811660208301528c166040820152606081018b9052608081018a905260a0810189905260c0810188905260e0810187905261010081018690526101a08101613c55610120830187613be7565b60068510613c6557613c65613bd3565b84610140830152613c826101608301856001600160a01b03169052565b826101808301529e9d5050505050505050505050505050565b634e487b7160e01b5f52601160045260245ffd5b818103818111156109ca576109ca613c9b565b5f60208284031215613cd2575f5ffd5b815161105281613b2a565b6020808252600f908201526e151c985b9cd9995c8819985a5b1959608a1b604082015260600190565b602080825260139082015272131bd85b88191bd95cc81b9bdd08195e1a5cdd606a1b604082015260600190565b60208082526019908201527f4c6f616e206e6f7420696e204c69737465642073746174757300000000000000604082015260600190565b808201808211156109ca576109ca613c9b565b80820281158282048414176109ca576109ca613c9b565b5f82613dae57634e487b7160e01b5f52601260045260245ffd5b500490565b6001600160a01b039384168152919092166020820152604081019190915260600190565b602080825260139082015272139195081d1c985b9cd9995c8819985a5b1959606a1b604082015260600190565b602080825260159082015274151bdad95b881d1c985b9cd9995c8819985a5b1959605a1b604082015260600190565b6020808252601190820152702737ba103637b0b7103137b93937bbb2b960791b604082015260600190565b5f60208284031215613e6e575f5ffd5b5051919050565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215613e99575f5ffd5b8151611052816139d7565b5f60018201613eb557613eb5613c9b565b5060010190565b6001600160a01b03881681526020810187905260408101869052606081018590526080810184905260e08101613ef560a0830185613be7565b6001600160a01b039290921660c091909101529695505050505050565b634e487b7160e01b5f52603160045260245ffdfeb19546dff01e856fb3f010c267a7b1c60363cf8a4664e21cc89c26224620214e241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b08a2646970667358221220453c1b00b692d14815345b06344c033036ccec610be965dd7275d35dbc1d393b64736f6c634300081c0033

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

0000000000000000000000002190a1dba3e0a9f7216efc929e6faead44ad6131000000000000000000000000d9d96fb150136798861363d8ad9fe4033cfc32b3

-----Decoded View---------------
Arg [0] : _govAddress (address): 0x2190A1dba3e0A9f7216eFC929E6FaEAD44aD6131
Arg [1] : _manager (address): 0xD9D96fb150136798861363d8Ad9Fe4033cfC32b3

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000002190a1dba3e0a9f7216efc929e6faead44ad6131
Arg [1] : 000000000000000000000000d9d96fb150136798861363d8ad9fe4033cfc32b3


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]

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