Contract Address Details

0x2726f9C56B13E126591A9DdB6484d157b92b0872

Contract Name
RentRegistry
Creator
0xdaf38d–e125de at 0x61b8da–8f0001
Balance
0
Tokens
Fetching tokens...
Transactions
328 Transactions
Transfers
202 Transfers
Gas Used
22,838,923
Last Balance Update
11012736
Contract name:
RentRegistry




Optimization enabled
true
Compiler version
v0.8.17+commit.8df45f5f




Optimization runs
200
EVM Version
london




Verified at
2024-09-05T07:38:37.437692Z

Constructor Arguments

0x0000000000000000000000009f59adf8ca58d0ffd9372af68d747c3dbb4cb02f0000000000000000000000009ab18990c9035f7878a59e134bce5d618cb19d55000000000000000000000000ea6a031bbaf2eadb5ecfd32b8aa39e9a95659e4600000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000099ebd96615a50933c69b435f8a59539caff95d7c000000000000000000000000750449925085a335b58b6706e0cf17039caa606f000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000002bc

Arg [0] (address) : 0x9f59adf8ca58d0ffd9372af68d747c3dbb4cb02f
Arg [1] (address) : 0x9ab18990c9035f7878a59e134bce5d618cb19d55
Arg [2] (address) : 0xea6a031bbaf2eadb5ecfd32b8aa39e9a95659e46
Arg [3] (address[]) : [0x99ebd96615a50933c69b435f8a59539caff95d7c, 0x750449925085a335b58b6706e0cf17039caa606f]
Arg [4] (uint256[]) : [500, 700]

              

project:/contracts/rent/RentRegistry.sol

Sol2uml
new
// SPDX-License-Identifier: PROPRIERTARY
// For development, we used some code from open source with an MIT license
// https://github.com/re-nft/legacy-contracts

pragma solidity =0.8.17;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "./interfaces/IRentResolver.sol";
import "./interfaces/IRentRegistry.sol";
import "../utils/GuardExtension.sol";

error Paused();
error AlreadyRented();
error RentNotCompleted();
error RentAlreadyCompleted();
error LendingNotExists();
error RentingNotExists();
error LendingAlreadyExists();
error RentingAlreadyExists();
error AssertionError();
error NothingToPay();
error LenderRequired();
error RenterRequired();
error InvalidInput(bytes32 parameter);
error SameValue();

contract RentRegistry is
    IRentRegistry,
    ERC721Holder,
    ERC1155Holder,
    GuardExtension,
    ReentrancyGuard
{
    using SafeERC20 for ERC20;

    bytes1 private constant DOMAIN_SEPARATOR_LENDING_ID = 0x01;
    bytes1 private constant DOMAIN_SEPARATOR_RENTING_ID = 0x02;

    IRentResolver private _resolver;
    address private _beneficiary;
    uint256 private _lendingID = 1;
    uint256 private _rentingID = 1;
    bool public _paused = false;
    mapping(address => uint256) private _rentFee;
    mapping(bytes32 => Lending) private _lendings;
    mapping(bytes32 => Renting) private _rentings;

    modifier notPaused() {
        if (_paused) {
            revert Paused();
        }
        _;
    }

    constructor(
        address resolver_,
        address beneficiary_,
        address rights_,
        address[] memory feeTokens_,
        uint256[] memory feePercents_
    ) GuardExtension(rights_) ReentrancyGuard() {
        if (feeTokens_.length != feePercents_.length) {
            revert InvalidInput("feeTokens_");
        }
        ensureIsNotZeroAddr(resolver_);
        ensureIsNotZeroAddr(beneficiary_);
        ensureIsNotZeroAddr(rights_);
        _resolver = IRentResolver(resolver_);
        _beneficiary = beneficiary_;
        for (uint256 i = 0; i < feeTokens_.length; ++i) {
            ensureIsNotZeroAddr(feeTokens_[i]);
            _setRentFee(feeTokens_[i], feePercents_[i]);
        }
    }

    function lend(
        NFTStandard[] memory nftStandard_,
        address[] memory nftAddress_,
        uint256[] memory tokenId_,
        uint256[] memory lendAmount_,
        uint8[] memory maxRentDuration_,
        bytes4[] memory dailyRentPrice_,
        uint8[] memory paymentToken_,
        bool[] memory willAutoRenew_
    ) external override notPaused nonReentrant {
        bundleCall(
            handleLend,
            createLendCallData(
                nftStandard_,
                nftAddress_,
                tokenId_,
                lendAmount_,
                maxRentDuration_,
                dailyRentPrice_,
                paymentToken_,
                willAutoRenew_
            )
        );
    }

    function stopLend(
        NFTStandard[] memory nftStandard_,
        address[] memory nftAddress_,
        uint256[] memory tokenId_,
        uint256[] memory lendingId_
    ) external override notPaused nonReentrant {
        bundleCall(
            handleStopLend,
            createActionCallData(
                nftStandard_,
                nftAddress_,
                tokenId_,
                lendingId_,
                new uint256[](0)
            )
        );
    }

    /**
     * @param nftStandard_ NFT standart (0 - ERC721, 1 - ERC1155)
     * @param nftAddress_ NFT contract address
     * @param tokenId_ NFT token Id
     * @param lendingId_ The landing Id
     * @param rentDuration_ Rent duration in days
     * @param rentAmount_ Token amount (for ERC721 always 1)
     */
    function rent(
        NFTStandard[] memory nftStandard_,
        address[] memory nftAddress_,
        uint256[] memory tokenId_,
        uint256[] memory lendingId_,
        uint8[] memory rentDuration_,
        uint256[] memory rentAmount_
    ) external override notPaused nonReentrant {
        bundleCall(
            handleRent,
            createRentCallData(
                nftStandard_,
                nftAddress_,
                tokenId_,
                lendingId_,
                rentDuration_,
                rentAmount_
            )
        );
    }

    function stopRent(
        NFTStandard[] memory nftStandard_,
        address[] memory nftAddress_,
        uint256[] memory tokenId_,
        uint256[] memory lendingId_,
        uint256[] memory rentingId_
    ) external override notPaused nonReentrant {
        bundleCall(
            handleStopRent,
            createActionCallData(
                nftStandard_,
                nftAddress_,
                tokenId_,
                lendingId_,
                rentingId_
            )
        );
    }

    function claimRent(
        NFTStandard[] memory nftStandard_,
        address[] memory nftAddress_,
        uint256[] memory tokenId_,
        uint256[] memory lendingId_,
        uint256[] memory rentingId_
    ) external override notPaused nonReentrant {
        bundleCall(
            handleClaimRent,
            createActionCallData(
                nftStandard_,
                nftAddress_,
                tokenId_,
                lendingId_,
                rentingId_
            )
        );
    }

    function handleLend(CallData memory cd_) private {
        for (uint256 i = cd_.left; i < cd_.right; i++) {
            ensureIsLendable(cd_, i);
            bytes32 identifier = getLendingId(
                cd_.nftAddress[cd_.left],
                cd_.tokenID[i],
                _lendingID
            );
            Lending storage lending = _lendings[identifier];
            ensureIsNull(lending);
            ensureTokenNotSentinel(uint8(cd_.paymentToken[i]));
            bool is721 = cd_.nftStandard[i] == NFTStandard.E721;
            uint16 _lendAmount = uint16(cd_.lendAmount[i]);
            if (is721 && _lendAmount != 1) {
                revert InvalidInput("_lendAmount");
            }
            _lendings[identifier] = Lending({
                nftStandard: cd_.nftStandard[i],
                lenderAddress: msg.sender,
                maxRentDuration: cd_.maxRentDuration[i],
                dailyRentPrice: cd_.dailyRentPrice[i],
                lendAmount: _lendAmount,
                availableAmount: _lendAmount,
                paymentToken: cd_.paymentToken[i],
                willAutoRenew: cd_.willAutoRenew[i]
            });
            emit Lend(
                is721,
                msg.sender,
                cd_.nftAddress[cd_.left],
                cd_.tokenID[i],
                _lendingID,
                cd_.maxRentDuration[i],
                cd_.dailyRentPrice[i],
                _lendAmount,
                cd_.paymentToken[i],
                cd_.willAutoRenew[i]
            );
            _lendingID++;
        }
        safeTransfer(
            cd_,
            msg.sender,
            address(this),
            sliceArr(cd_.tokenID, cd_.left, cd_.right, 0),
            sliceArr(cd_.lendAmount, cd_.left, cd_.right, 0)
        );
    }

    function handleStopLend(CallData memory cd_) private {
        uint256[] memory lentAmounts = new uint256[](cd_.right - cd_.left);
        for (uint256 i = cd_.left; i < cd_.right; i++) {
            bytes32 lendingIdentifier = getLendingId(
                cd_.nftAddress[cd_.left],
                cd_.tokenID[i],
                cd_.lendingID[i]
            );
            Lending storage lending = _lendings[lendingIdentifier];
            ensureIsNotNull(lending);
            ensureIsStoppable(lending, msg.sender);
            if (cd_.nftStandard[i] != lending.nftStandard) {
                revert InvalidInput("nftStandard");
            }
            if (lending.lendAmount != lending.availableAmount) {
                revert AlreadyRented();
            }
            lentAmounts[i - cd_.left] = lending.lendAmount;
            emit StopLend(
                cd_.lendingID[i],
                uint32(block.timestamp),
                lending.lendAmount
            );
            delete _lendings[lendingIdentifier];
        }
        safeTransfer(
            cd_,
            address(this),
            msg.sender,
            sliceArr(cd_.tokenID, cd_.left, cd_.right, 0),
            sliceArr(lentAmounts, cd_.left, cd_.right, cd_.left)
        );
    }

    function handleRent(CallData memory cd_) private {
        for (uint256 i = cd_.left; i < cd_.right; i++) {
            bytes32 lendingIdentifier = getLendingId(
                cd_.nftAddress[cd_.left],
                cd_.tokenID[i],
                cd_.lendingID[i]
            );
            bytes32 rentingIdentifier = getRentingId(
                cd_.nftAddress[cd_.left],
                cd_.tokenID[i],
                _rentingID
            );
            Lending storage lending = _lendings[lendingIdentifier];
            Renting storage renting = _rentings[rentingIdentifier];
            ensureIsNotNull(lending);
            ensureIsNull(renting);
            ensureIsRentable(lending, cd_, i, msg.sender);
            if (cd_.nftStandard[i] != lending.nftStandard) {
                revert InvalidInput("nftStandard");
            }
            if (cd_.rentAmount[i] > lending.availableAmount) {
                revert InvalidInput("rentAmount");
            }
            uint8 paymentTokenIx = uint8(lending.paymentToken);
            ERC20 paymentToken = ERC20(
                _resolver.getPaymentToken(paymentTokenIx)
            );
            uint256 decimals = paymentToken.decimals();
            {
                uint256 scale = 10 ** decimals;
                uint256 rentPrice = cd_.rentAmount[i] *
                    cd_.rentDuration[i] *
                    unpackPrice(lending.dailyRentPrice, scale);
                if (rentPrice == 0) {
                    revert InvalidInput("rentDuration");
                }
                paymentToken.safeTransferFrom(
                    msg.sender,
                    address(this),
                    rentPrice
                );
            }
            _rentings[rentingIdentifier] = Renting({
                renterAddress: msg.sender,
                rentAmount: uint16(cd_.rentAmount[i]),
                rentDuration: cd_.rentDuration[i],
                rentedAt: uint32(block.timestamp)
            });
            _lendings[lendingIdentifier].availableAmount -= uint16(
                cd_.rentAmount[i]
            );
            emit Rent(
                msg.sender,
                cd_.lendingID[i],
                _rentingID,
                uint16(cd_.rentAmount[i]),
                cd_.rentDuration[i],
                renting.rentedAt
            );
            _rentingID++;
        }
    }

    function handleStopRent(CallData memory cd_) private {
        for (uint256 i = cd_.left; i < cd_.right; i++) {
            bytes32 lendingIdentifier = getLendingId(
                cd_.nftAddress[cd_.left],
                cd_.tokenID[i],
                cd_.lendingID[i]
            );
            bytes32 rentingIdentifier = getRentingId(
                cd_.nftAddress[cd_.left],
                cd_.tokenID[i],
                cd_.rentingID[i]
            );
            Lending storage lending = _lendings[lendingIdentifier];
            Renting memory renting = _rentings[rentingIdentifier];
            // Remove renting to avoid reentry attack
            delete _rentings[rentingIdentifier];
            ensureIsNotNull(lending);
            ensureIsNotNull(renting);
            ensureIsReturnable(renting, msg.sender, block.timestamp);
            if (cd_.nftStandard[i] != lending.nftStandard) {
                revert InvalidInput("nftStandard");
            }
            if (renting.rentAmount > lending.lendAmount) {
                revert AssertionError();
            }
            uint256 secondsSinceRentStart = block.timestamp - renting.rentedAt;
            (
                uint256 sendRenterAmt,
                uint256 sendLenderAmt,
                uint256 fee
            ) = distributePayments(lending, renting, secondsSinceRentStart);
            manageWillAutoRenew(
                lending,
                renting,
                cd_.nftAddress[cd_.left],
                cd_.nftStandard[cd_.left],
                cd_.tokenID[i],
                cd_.lendingID[i]
            );
            emit StopRent(
                cd_.rentingID[i],
                sendRenterAmt,
                sendLenderAmt,
                fee,
                uint32(block.timestamp)
            );
        }
    }

    function handleClaimRent(CallData memory cd_) private {
        for (uint256 i = cd_.left; i < cd_.right; i++) {
            bytes32 lendingIdentifier = getLendingId(
                cd_.nftAddress[cd_.left],
                cd_.tokenID[i],
                cd_.lendingID[i]
            );
            bytes32 rentingIdentifier = getRentingId(
                cd_.nftAddress[cd_.left],
                cd_.tokenID[i],
                cd_.rentingID[i]
            );
            Lending storage lending = _lendings[lendingIdentifier];
            Renting memory renting = _rentings[rentingIdentifier];
            // Remove renting to avoid reentry attack
            delete _rentings[rentingIdentifier];
            ensureIsNotNull(lending);
            ensureIsNotNull(renting);
            ensureIsClaimable(renting, block.timestamp);
            (uint256 amount, uint256 fee) = distributeClaimPayment(
                lending,
                renting
            );
            manageWillAutoRenew(
                lending,
                renting,
                cd_.nftAddress[cd_.left],
                cd_.nftStandard[cd_.left],
                cd_.tokenID[i],
                cd_.lendingID[i]
            );
            emit RentClaimed(
                cd_.rentingID[i],
                amount,
                fee,
                uint32(block.timestamp)
            );
        }
    }

    function manageWillAutoRenew(
        Lending storage lending_,
        Renting memory renting_,
        address nftAddress_,
        NFTStandard nftStandard_,
        uint256 tokenId_,
        uint256 lendingId_
    ) private {
        if (lending_.willAutoRenew == false) {
            // No automatic renewal, stop the lending (or a portion of it) completely!

            // We must be careful here, because the lending might be for an ERC1155 token, which means
            // that the renting.rentAmount might not be the same as the lending.lendAmount. In this case, we
            // must NOT delete the lending, but only decrement the lending.lendAmount by the renting.rentAmount.
            // Notice: this is only possible for an ERC1155 tokens!
            if (lending_.lendAmount > renting_.rentAmount) {
                // update lending lendAmount to reflect NOT renewing the lending
                // Do not update lending.availableAmount, because the assets will not be lent out again
                lending_.lendAmount -= renting_.rentAmount;
                // return the assets to the lender
                IERC1155(nftAddress_).safeTransferFrom(
                    address(this),
                    lending_.lenderAddress,
                    tokenId_,
                    uint256(renting_.rentAmount),
                    ""
                );
            }
            // If the lending is for an ERC721 token, then the renting.rentAmount is always the same as the
            // lending.lendAmount, and we can delete the lending. If the lending is for an ERC1155 token and
            // the renting.rentAmount is the same as the lending.lendAmount, then we can also delete the
            // lending.
            else if (lending_.lendAmount == renting_.rentAmount) {
                // return the assets to the lender
                if (nftStandard_ == NFTStandard.E721) {
                    IERC721(nftAddress_).safeTransferFrom(
                        address(this),
                        lending_.lenderAddress,
                        tokenId_
                    );
                } else {
                    IERC1155(nftAddress_).safeTransferFrom(
                        address(this),
                        lending_.lenderAddress,
                        tokenId_,
                        uint256(renting_.rentAmount),
                        ""
                    );
                }
                delete _lendings[
                    getLendingId(nftAddress_, tokenId_, lendingId_)
                ];
            }
            // StopLend event but only the amount that was not renewed (or all of it)
            emit StopLend(
                lendingId_,
                uint32(block.timestamp),
                renting_.rentAmount
            );
        } else {
            // automatic renewal, make the assets available to be lent out again
            lending_.availableAmount += renting_.rentAmount;
        }
    }

    function bundleCall(
        function(CallData memory) handler_,
        CallData memory cd_
    ) private {
        if (cd_.nftAddress.length == 0) {
            revert InvalidInput("bundle");
        }
        while (cd_.right != cd_.nftAddress.length) {
            if (
                (cd_.nftAddress[cd_.left] == cd_.nftAddress[cd_.right]) &&
                (cd_.nftStandard[cd_.right] == NFTStandard.E1155)
            ) {
                cd_.right++;
            } else {
                handler_(cd_);
                cd_.left = cd_.right;
                cd_.right++;
            }
        }
        handler_(cd_);
    }

    function takeFee(
        uint256 rentAmount_,
        ERC20 token_
    ) private returns (uint256 fee) {
        uint256 feePercent = _rentFee[address(token_)];
        if (feePercent > 0) {
            fee = rentAmount_ * feePercent;
            fee /= 10000;
            token_.safeTransfer(_beneficiary, fee);
        } else {
            fee = 0;
        }
    }

    function distributePayments(
        Lending memory lending_,
        Renting memory renting_,
        uint256 secondsSinceRentStart
    )
        private
        returns (uint256 sendRenterAmt, uint256 sendLenderAmt, uint256 takenFee)
    {
        uint8 paymentTokenIx = uint8(lending_.paymentToken);
        ERC20 paymentToken = ERC20(_resolver.getPaymentToken(paymentTokenIx));
        uint256 decimals = paymentToken.decimals();
        uint256 scale = 10 ** decimals;
        uint256 rentPrice = renting_.rentAmount *
            unpackPrice(lending_.dailyRentPrice, scale);
        uint256 totalRenterPmt = rentPrice * renting_.rentDuration;
        sendLenderAmt = (secondsSinceRentStart * rentPrice) / 1 days;
        if (totalRenterPmt == 0 || sendLenderAmt == 0) {
            revert NothingToPay();
        }
        sendRenterAmt = totalRenterPmt - sendLenderAmt;
        takenFee = takeFee(sendLenderAmt, paymentToken);
        sendLenderAmt -= takenFee;
        paymentToken.safeTransfer(lending_.lenderAddress, sendLenderAmt);
        if (sendRenterAmt > 0) {
            paymentToken.safeTransfer(renting_.renterAddress, sendRenterAmt);
        }
    }

    function distributeClaimPayment(
        Lending memory lending_,
        Renting memory renting_
    ) private returns (uint256 amount, uint256 fee) {
        uint8 paymentTokenIx = uint8(lending_.paymentToken);
        ERC20 paymentToken = ERC20(_resolver.getPaymentToken(paymentTokenIx));
        uint256 decimals = paymentToken.decimals();
        uint256 scale = 10 ** decimals;
        uint256 rentPrice = renting_.rentAmount *
            unpackPrice(lending_.dailyRentPrice, scale);
        uint256 fullAmount = rentPrice * renting_.rentDuration;
        fee = takeFee(fullAmount, paymentToken);
        amount = fullAmount - fee;
        paymentToken.safeTransfer(lending_.lenderAddress, amount);
    }

    function safeTransfer(
        CallData memory cd_,
        address from_,
        address to_,
        uint256[] memory tokenId_,
        uint256[] memory lendAmount_
    ) private {
        if (cd_.nftStandard[cd_.left] == NFTStandard.E721) {
            IERC721(cd_.nftAddress[cd_.left]).safeTransferFrom(
                from_,
                to_,
                cd_.tokenID[cd_.left]
            );
        } else {
            IERC1155(cd_.nftAddress[cd_.left]).safeBatchTransferFrom(
                from_,
                to_,
                tokenId_,
                lendAmount_,
                ""
            );
        }
    }

    function getLending(
        address nftAddress_,
        uint256 tokenId_,
        uint256 lendingId_
    ) external view returns (Lending memory lending) {
        lending = _lendings[getLendingId(nftAddress_, tokenId_, lendingId_)];
    }

    function getRenting(
        address nftAddress_,
        uint256 tokenId_,
        uint256 rentingId_
    ) external view returns (Renting memory renting) {
        renting = _rentings[getRentingId(nftAddress_, tokenId_, rentingId_)];
    }

    function createLendCallData(
        NFTStandard[] memory nftStandard_,
        address[] memory nftAddress_,
        uint256[] memory tokenId_,
        uint256[] memory lendAmount_,
        uint8[] memory maxRentDuration_,
        bytes4[] memory dailyRentPrice_,
        uint8[] memory paymentToken_,
        bool[] memory willAutoRenew_
    ) private pure returns (CallData memory cd) {
        cd = CallData({
            left: 0,
            right: 1,
            nftStandard: nftStandard_,
            nftAddress: nftAddress_,
            tokenID: tokenId_,
            lendAmount: lendAmount_,
            lendingID: new uint256[](0),
            rentingID: new uint256[](0),
            rentDuration: new uint8[](0),
            rentAmount: new uint256[](0),
            maxRentDuration: maxRentDuration_,
            dailyRentPrice: dailyRentPrice_,
            paymentToken: paymentToken_,
            willAutoRenew: willAutoRenew_
        });
    }

    function createRentCallData(
        NFTStandard[] memory nftStandard_,
        address[] memory nftAddress_,
        uint256[] memory tokenId_,
        uint256[] memory lendingId_,
        uint8[] memory rentDuration_,
        uint256[] memory rentAmount_
    ) private pure returns (CallData memory cd) {
        cd = CallData({
            left: 0,
            right: 1,
            nftStandard: nftStandard_,
            nftAddress: nftAddress_,
            tokenID: tokenId_,
            lendAmount: new uint256[](0),
            lendingID: lendingId_,
            rentingID: new uint256[](0),
            rentDuration: rentDuration_,
            rentAmount: rentAmount_,
            maxRentDuration: new uint8[](0),
            dailyRentPrice: new bytes4[](0),
            paymentToken: new uint8[](0),
            willAutoRenew: new bool[](0)
        });
    }

    function createActionCallData(
        NFTStandard[] memory nftStandard_,
        address[] memory nftAddress_,
        uint256[] memory tokenId_,
        uint256[] memory lendingId_,
        uint256[] memory rentingId_
    ) private pure returns (CallData memory cd) {
        cd = CallData({
            left: 0,
            right: 1,
            nftStandard: nftStandard_,
            nftAddress: nftAddress_,
            tokenID: tokenId_,
            lendAmount: new uint256[](0),
            lendingID: lendingId_,
            rentingID: rentingId_,
            rentDuration: new uint8[](0),
            rentAmount: new uint256[](0),
            maxRentDuration: new uint8[](0),
            dailyRentPrice: new bytes4[](0),
            paymentToken: new uint8[](0),
            willAutoRenew: new bool[](0)
        });
    }

    function unpackPrice(
        bytes4 price_,
        uint256 scale_
    ) private pure returns (uint256 fullPrice) {
        ensureIsUnpackablePrice(price_, scale_);
        uint16 whole = uint16(bytes2(price_));
        uint16 decimal = uint16(bytes2(price_ << 16));
        uint256 decimalScale = scale_ / 10000;
        if (whole > 9999) {
            whole = 9999;
        }
        if (decimal > 9999) {
            decimal = 9999;
        }
        uint256 w = whole * scale_;
        uint256 d = decimal * decimalScale;
        fullPrice = w + d;
    }

    function sliceArr(
        uint256[] memory arr_,
        uint256 fromIx_,
        uint256 toIx_,
        uint256 arrOffset_
    ) private pure returns (uint256[] memory r) {
        r = new uint256[](toIx_ - fromIx_);
        for (uint256 i = fromIx_; i < toIx_; i++) {
            r[i - fromIx_] = arr_[i - arrOffset_];
        }
    }

    function ensureIsNotZeroAddr(address addr_) private pure {
        if (addr_ == address(0)) {
            revert InvalidInput("address");
        }
    }

    function ensureIsNull(Lending memory lending_) private pure {
        if (
            lending_.lenderAddress != address(0) ||
            lending_.maxRentDuration != 0 ||
            lending_.dailyRentPrice != 0
        ) {
            revert LendingAlreadyExists();
        }
    }

    function ensureIsNotNull(Lending memory lending_) private pure {
        if (
            lending_.lenderAddress == address(0) ||
            lending_.maxRentDuration == 0 ||
            lending_.dailyRentPrice == 0
        ) {
            revert LendingNotExists();
        }
    }

    function ensureIsNull(Renting memory renting_) private pure {
        if (
            renting_.renterAddress != address(0) ||
            renting_.rentDuration != 0 ||
            renting_.rentedAt != 0
        ) {
            revert RentingAlreadyExists();
        }
    }

    function ensureIsNotNull(Renting memory renting_) private pure {
        if (
            renting_.renterAddress == address(0) ||
            renting_.rentDuration == 0 ||
            renting_.rentedAt == 0
        ) {
            revert RentingNotExists();
        }
    }

    function ensureIsLendable(
        CallData memory cd_,
        uint256 index_
    ) private pure {
        if (
            cd_.lendAmount[index_] == 0 ||
            cd_.lendAmount[index_] > type(uint16).max
        ) {
            revert InvalidInput("lendAmount");
        }
        if (
            cd_.maxRentDuration[index_] == 0 ||
            cd_.maxRentDuration[index_] > type(uint8).max
        ) {
            revert InvalidInput("maxRentDuration");
        }
        if (uint32(cd_.dailyRentPrice[index_]) == 0) {
            revert InvalidInput("dailyRentPrice");
        }
    }

    function ensureIsRentable(
        Lending memory lending_,
        CallData memory cd_,
        uint256 index_,
        address msgSender
    ) private pure {
        if (msgSender == lending_.lenderAddress) {
            revert InvalidInput("lenderAddress");
        }
        if (
            cd_.rentDuration[index_] == 0 ||
            cd_.rentDuration[index_] > type(uint8).max
        ) {
            revert InvalidInput("rentDuration");
        }
        if (
            cd_.rentAmount[index_] == 0 ||
            cd_.rentAmount[index_] > type(uint16).max
        ) {
            revert InvalidInput("rentAmount");
        }
        if (cd_.rentDuration[index_] > lending_.maxRentDuration) {
            revert InvalidInput("rentDuration");
        }
    }

    function ensureIsReturnable(
        Renting memory renting_,
        address msgSender_,
        uint256 blockTimestamp_
    ) private pure {
        if (renting_.renterAddress != msgSender_) {
            revert RenterRequired();
        }
        if (isPastReturnDate(renting_, blockTimestamp_)) {
            revert RentAlreadyCompleted();
        }
    }

    function ensureIsStoppable(
        Lending memory lending_,
        address msgSender_
    ) private pure {
        if (lending_.lenderAddress != msgSender_) {
            revert LenderRequired();
        }
    }

    function ensureIsUnpackablePrice(
        bytes4 price_,
        uint256 scale_
    ) private pure {
        if (uint32(price_) == 0 || scale_ < 10000) {
            revert InvalidInput("price");
        }
    }

    function ensureTokenNotSentinel(uint8 paymentIx_) private pure {
        if (paymentIx_ == 0) {
            revert InvalidInput("paymentIx_");
        }
    }

    function ensureIsClaimable(
        Renting memory renting_,
        uint256 blockTimestamp_
    ) private pure {
        if (!isPastReturnDate(renting_, blockTimestamp_)) {
            revert RentNotCompleted();
        }
    }

    function isPastReturnDate(
        Renting memory renting_,
        uint256 nowTime_
    ) private pure returns (bool isPast) {
        if (nowTime_ <= renting_.rentedAt) {
            revert AssertionError();
        }
        isPast = nowTime_ - renting_.rentedAt > renting_.rentDuration * 1 days;
    }

    function getRentFee(address token_) external view returns (uint256 fee) {
        fee = _rentFee[token_];
    }

    function getBeneficiary()
        external
        view
        returns (address beneficiaryAddress)
    {
        beneficiaryAddress = _beneficiary;
    }

    function setRentFee(
        address token_,
        uint256 newRentFee_
    ) external haveRights {
        _setRentFee(token_, newRentFee_);
    }

    function setBeneficiary(address newBeneficiary_) external haveRights {
        if (_beneficiary != newBeneficiary_) {
            _beneficiary = newBeneficiary_;
        } else {
            revert SameValue();
        }
    }

    function setPaused(bool newPaused_) external haveRights {
        if (_paused != newPaused_) {
            _paused = newPaused_;
        } else {
            revert SameValue();
        }
    }

    function _setRentFee(address token_, uint256 newRentFee_) private {
        if (newRentFee_ > 2000) {
            revert InvalidInput("newRentFee_");
        }
        if (_rentFee[token_] != newRentFee_) {
            _rentFee[token_] = newRentFee_;
            emit RentFeeUpdated(token_, newRentFee_);
        } else {
            revert SameValue();
        }
    }

    function getRentingId(
        address nftAddress_,
        uint256 tokenId_,
        uint256 rentingId_
    ) private pure returns (bytes32 rentId) {
        rentId = keccak256(
            abi.encodePacked(
                DOMAIN_SEPARATOR_RENTING_ID,
                nftAddress_,
                tokenId_,
                rentingId_
            )
        );
    }

    function getLendingId(
        address nftAddress_,
        uint256 tokenId_,
        uint256 lendingId_
    ) private pure returns (bytes32 lendId) {
        lendId = keccak256(
            abi.encodePacked(
                DOMAIN_SEPARATOR_LENDING_ID,
                nftAddress_,
                tokenId_,
                lendingId_
            )
        );
    }
}
        

@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
          

@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol

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

pragma solidity ^0.8.0;

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

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

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

@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

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

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

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

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

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

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

@openzeppelin/contracts/token/ERC1155/utils/ERC1155Receiver.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/utils/ERC1155Receiver.sol)

pragma solidity ^0.8.0;

import "../IERC1155Receiver.sol";
import "../../../utils/introspection/ERC165.sol";

/**
 * @dev _Available since v3.1._
 */
abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
    }
}
          

@openzeppelin/contracts/token/ERC20/ERC20.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(address from, address to, uint256 amount) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
          

@openzeppelin/contracts/token/ERC20/IERC20.sol

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

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
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 amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

@openzeppelin/contracts/security/ReentrancyGuard.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    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
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // 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;
    }
}
          

@openzeppelin/contracts/token/ERC1155/IERC1155.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/IERC1155.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155 is IERC165 {
    /**
     * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
     * transfers.
     */
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] values
    );

    /**
     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `approved`.
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

    /**
     * @dev Returns the amount of tokens of token type `id` owned by `account`.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(
        address[] calldata accounts,
        uint256[] calldata ids
    ) external view returns (uint256[] memory);

    /**
     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
     *
     * Emits an {ApprovalForAll} event.
     *
     * Requirements:
     *
     * - `operator` cannot be the caller.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external;
}
          

@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev _Available since v3.1._
 */
interface IERC1155Receiver is IERC165 {
    /**
     * @dev Handles the receipt of a single ERC1155 token type. This function is
     * called at the end of a `safeTransferFrom` after the balance has been updated.
     *
     * NOTE: To accept the transfer, this must return
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     * (i.e. 0xf23a6e61, or its own function selector).
     *
     * @param operator The address which initiated the transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param id The ID of the token being transferred
     * @param value The amount of tokens being transferred
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
     */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    /**
     * @dev Handles the receipt of a multiple ERC1155 token types. This function
     * is called at the end of a `safeBatchTransferFrom` after the balances have
     * been updated.
     *
     * NOTE: To accept the transfer(s), this must return
     * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
     * (i.e. 0xbc197c81, or its own function selector).
     *
     * @param operator The address which initiated the batch transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param ids An array containing ids of each token being transferred (order and length must match values array)
     * @param values An array containing amounts of each token being transferred (order and length must match ids array)
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
     */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}
          

@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/utils/ERC1155Holder.sol)

pragma solidity ^0.8.0;

import "./ERC1155Receiver.sol";

/**
 * Simple implementation of `ERC1155Receiver` that will allow a contract to hold ERC1155 tokens.
 *
 * IMPORTANT: When inheriting this contract, you must include a way to use the received tokens, otherwise they will be
 * stuck.
 *
 * @dev _Available since v3.1._
 */
contract ERC1155Holder is ERC1155Receiver {
    function onERC1155Received(
        address,
        address,
        uint256,
        uint256,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC1155Received.selector;
    }

    function onERC1155BatchReceived(
        address,
        address,
        uint256[] memory,
        uint256[] memory,
        bytes memory
    ) public virtual override returns (bytes4) {
        return this.onERC1155BatchReceived.selector;
    }
}
          

@openzeppelin/contracts/token/ERC721/IERC721.sol

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

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 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 ERC721 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 ERC721
     * 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 caller.
     *
     * 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);
}
          

@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}
          

@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/utils/ERC721Holder.sol)

pragma solidity ^0.8.0;

import "../IERC721Receiver.sol";

/**
 * @dev Implementation of the {IERC721Receiver} interface.
 *
 * Accepts all token transfers.
 * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
 */
contract ERC721Holder is IERC721Receiver {
    /**
     * @dev See {IERC721Receiver-onERC721Received}.
     *
     * Always returns `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
        return this.onERC721Received.selector;
    }
}
          

@openzeppelin/contracts/utils/Address.sol

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

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

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

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

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

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

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

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}
          

@openzeppelin/contracts/utils/Context.sol

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

pragma solidity ^0.8.0;

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

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

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

@openzeppelin/contracts/utils/introspection/ERC165.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 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);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}
          

@openzeppelin/contracts/utils/introspection/IERC165.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * 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[EIP 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);
}
          

project:/contracts/rent/interfaces/IRentRegistry.sol

// SPDX-License-Identifier: PROPRIERTARY

pragma solidity =0.8.17;

interface IRentRegistry {
    event Lend(
        bool is721,
        address indexed lenderAddress,
        address indexed nftAddress,
        uint256 indexed tokenID,
        uint256 lendingID,
        uint8 maxRentDuration,
        bytes4 dailyRentPrice,
        uint16 lendAmount,
        uint8 paymentToken,
        bool willAutoRenew
    );

    event Rent(
        address indexed renterAddress,
        uint256 indexed lendingID,
        uint256 indexed rentingID,
        uint16 rentAmount,
        uint8 rentDuration,
        uint32 rentedAt
    );

    event StopLend(uint256 indexed lendingID, uint32 stoppedAt, uint16 amount);

    event StopRent(
        uint256 indexed rentingID,
        uint256 renterAmount,
        uint256 lenderAmount,
        uint256 fee,
        uint32 stoppedAt
    );

    event RentClaimed(
        uint256 indexed rentingID,
        uint256 amount,
        uint256 fee,
        uint32 collectedAt
    );

    event RentFeeUpdated(address indexed token, uint256 feePercent);

    enum NFTStandard {
        E721,
        E1155
    }

    struct CallData {
        uint256 left;
        uint256 right;
        NFTStandard[] nftStandard;
        address[] nftAddress;
        uint256[] tokenID;
        uint256[] lendAmount;
        uint8[] maxRentDuration;
        bytes4[] dailyRentPrice;
        uint256[] lendingID;
        uint256[] rentingID;
        uint8[] rentDuration;
        uint256[] rentAmount;
        uint8[] paymentToken;
        bool[] willAutoRenew;
    }

    // fits into a single storage slot
    // nftStandard       2
    // lenderAddress   162
    // maxRentDuration 170
    // dailyRentPrice  202
    // lendAmount      218
    // availableAmount 234
    // paymentToken    242
    // willAutoRenew   250
    // leaves a spare byte
    struct Lending {
        NFTStandard nftStandard;
        address lenderAddress;
        uint8 maxRentDuration;
        bytes4 dailyRentPrice;
        uint16 lendAmount;
        uint16 availableAmount;
        uint8 paymentToken;
        bool willAutoRenew;
    }

    // fits into a single storage slot
    // renterAddress 160
    // rentDuration  168
    // rentedAt      216
    // rentAmount    232
    // leaves 3 spare bytes
    struct Renting {
        address renterAddress;
        uint8 rentDuration;
        uint32 rentedAt;
        uint16 rentAmount;
    }

    // creates the lending structs and adds them to the enumerable set
    function lend(
        NFTStandard[] memory nftStandard_,
        address[] memory nftAddress_,
        uint256[] memory tokenId_,
        uint256[] memory lendAmount_,
        uint8[] memory maxRentDuration_,
        bytes4[] memory dailyRentPrice_,
        uint8[] memory paymentToken_,
        bool[] memory willAutoRenew_
    ) external;

    function stopLend(
        NFTStandard[] memory nftStandard_,
        address[] memory nftAddress_,
        uint256[] memory tokenId_,
        uint256[] memory lendingId_
    ) external;

    // creates the renting structs and adds them to the enumerable set
    function rent(
        NFTStandard[] memory nftStandard_,
        address[] memory nftAddress_,
        uint256[] memory tokenID_,
        uint256[] memory lendingId_,
        uint8[] memory rentDuration_,
        uint256[] memory rentAmount
    ) external;

    function stopRent(
        NFTStandard[] memory nftStandard_,
        address[] memory nftAddress_,
        uint256[] memory tokenId_,
        uint256[] memory lendingId_,
        uint256[] memory rentingId
    ) external;

    function claimRent(
        NFTStandard[] memory nftStandard_,
        address[] memory nftAddress_,
        uint256[] memory tokenId_,
        uint256[] memory lendingId_,
        uint256[] memory rentingId
    ) external;
}
          

project:/contracts/rent/interfaces/IRentResolver.sol

// SPDX-License-Identifier: PROPRIERTARY

pragma solidity =0.8.17;

interface IRentResolver {
    event PaymentTokenAdded(uint8 pt, address tokenAddress);
    
    function getPaymentToken(uint8 pt_) external view returns (address);

    function setPaymentToken(uint8 pt_, address v_) external;
}
          

project:/contracts/utils/Guard.sol

// SPDX-License-Identifier: PROPRIERTARY

// Author: Ilya A. Shlyakhovoy
// Email: is@unicsoft.com

pragma solidity 0.8.17;
import "./interfaces/IRights.sol";

abstract contract Guard {
    string constant NO_RIGHTS = "Guard: No rights";

    /// @notice only if person with rights calls the contract
    modifier haveRights() {
        require(_rights().haveRights(address(this), msg.sender), NO_RIGHTS);
        _;
    }

    /// @notice only if someone with rights calls the contract
    modifier haveRightsPerson(address who_) {
        require(_rights().haveRights(address(this), who_), NO_RIGHTS);
        _;
    }

    /// @notice only if who with rights calls the target function
    modifier haveRightsExt(address target_, address who_) {
        require(_rights().haveRights(target_, who_), NO_RIGHTS);
        _;
    }

    function _rights() internal view virtual returns (IRights);

    function setRights(address rights_) external virtual;
}
          

project:/contracts/utils/GuardExtension.sol

// SPDX-License-Identifier: PROPRIERTARY

// Author: Ilya A. Shlyakhovoy
// Email: is@unicsoft.com

pragma solidity 0.8.17;
import "./interfaces/IRights.sol";
import "../utils/Guard.sol";

abstract contract GuardExtension is Guard {
    IRights private _rightsContract;

    string private constant SAME_VALUE = "Guard: same value";
    string private constant ZERO_ADDRESS = "Guard: zero address";

    constructor(address rights_) {
        require(rights_ != address(0), ZERO_ADDRESS);
        _rightsContract = IRights(rights_);
    }

    function _rights() internal view virtual override returns (IRights) {
        return _rightsContract;
    }

    function setRights(address rights_) external virtual override haveRights {
        require(address(_rightsContract) != rights_, SAME_VALUE);
        require(rights_ != address(0), ZERO_ADDRESS);
        _rightsContract = IRights(rights_);
    }
}
          

project:/contracts/utils/interfaces/IRights.sol

// SPDX-License-Identifier: PROPRIERTARY

// Author: Ilya A. Shlyakhovoy
// Email: is@unicsoft.com

pragma solidity 0.8.17;

interface IRights {
    event AdminAdded(address indexed admin);
    event AdminDefined(address indexed admin, address indexed contractHash);
    event AdminRemoved(address indexed admin);
    event AdminCleared(address indexed admin, address indexed contractHash);

    /**
@notice Add a new admin for the Rigths contract
@param admin_ New admin address
*/

    function addAdmin(address admin_) external;

    /**
@notice Add a new admin for the any other contract
@param contract_ Contract address packed into address
@param admin_ New admin address
*/

    function addAdmin(address contract_, address admin_) external;

    /**
@notice Remove the existing admin from the Rigths contract
@param admin_ Admin address
*/

    function removeAdmin(address admin_) external;

    /**
@notice Remove the existing admin from the specified contract
@param contract_ Contract address packed into address
@param admin_ Admin address
*/

    function removeAdmin(address contract_, address admin_) external;

    /**
@notice Get the rights for the contract for the caller
@param contract_ Contract address packed into address
@return have rights or not
*/
    function haveRights(address contract_) external view returns (bool);

    /**
@notice Get the rights for the contract
@param contract_ Contract address packed into address
@param admin_ Admin address
@return have rights or not
*/
    function haveRights(address contract_, address admin_)
        external
        view
        returns (bool);
}
          

Compiler Settings

{"viaIR":true,"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers"]}},"optimizer":{"runs":200,"enabled":true},"libraries":{},"evmVersion":"london"}
              

Contract ABI

[{"type":"constructor","inputs":[{"type":"address","name":"resolver_","internalType":"address"},{"type":"address","name":"beneficiary_","internalType":"address"},{"type":"address","name":"rights_","internalType":"address"},{"type":"address[]","name":"feeTokens_","internalType":"address[]"},{"type":"uint256[]","name":"feePercents_","internalType":"uint256[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"_paused","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimRent","inputs":[{"type":"uint8[]","name":"nftStandard_","internalType":"enum IRentRegistry.NFTStandard[]"},{"type":"address[]","name":"nftAddress_","internalType":"address[]"},{"type":"uint256[]","name":"tokenId_","internalType":"uint256[]"},{"type":"uint256[]","name":"lendingId_","internalType":"uint256[]"},{"type":"uint256[]","name":"rentingId_","internalType":"uint256[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"beneficiaryAddress","internalType":"address"}],"name":"getBeneficiary","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"lending","internalType":"struct IRentRegistry.Lending","components":[{"type":"uint8"},{"type":"address"},{"type":"uint8"},{"type":"bytes4"},{"type":"uint16"},{"type":"uint16"},{"type":"uint8"},{"type":"bool"}]}],"name":"getLending","inputs":[{"type":"address","name":"nftAddress_","internalType":"address"},{"type":"uint256","name":"tokenId_","internalType":"uint256"},{"type":"uint256","name":"lendingId_","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"fee","internalType":"uint256"}],"name":"getRentFee","inputs":[{"type":"address","name":"token_","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"renting","internalType":"struct IRentRegistry.Renting","components":[{"type":"address"},{"type":"uint8"},{"type":"uint32"},{"type":"uint16"}]}],"name":"getRenting","inputs":[{"type":"address","name":"nftAddress_","internalType":"address"},{"type":"uint256","name":"tokenId_","internalType":"uint256"},{"type":"uint256","name":"rentingId_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"lend","inputs":[{"type":"uint8[]","name":"nftStandard_","internalType":"enum IRentRegistry.NFTStandard[]"},{"type":"address[]","name":"nftAddress_","internalType":"address[]"},{"type":"uint256[]","name":"tokenId_","internalType":"uint256[]"},{"type":"uint256[]","name":"lendAmount_","internalType":"uint256[]"},{"type":"uint8[]","name":"maxRentDuration_","internalType":"uint8[]"},{"type":"bytes4[]","name":"dailyRentPrice_","internalType":"bytes4[]"},{"type":"uint8[]","name":"paymentToken_","internalType":"uint8[]"},{"type":"bool[]","name":"willAutoRenew_","internalType":"bool[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes4","name":"","internalType":"bytes4"}],"name":"onERC1155BatchReceived","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"},{"type":"uint256[]","name":"","internalType":"uint256[]"},{"type":"uint256[]","name":"","internalType":"uint256[]"},{"type":"bytes","name":"","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes4","name":"","internalType":"bytes4"}],"name":"onERC1155Received","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"bytes","name":"","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes4","name":"","internalType":"bytes4"}],"name":"onERC721Received","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"bytes","name":"","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"rent","inputs":[{"type":"uint8[]","name":"nftStandard_","internalType":"enum IRentRegistry.NFTStandard[]"},{"type":"address[]","name":"nftAddress_","internalType":"address[]"},{"type":"uint256[]","name":"tokenId_","internalType":"uint256[]"},{"type":"uint256[]","name":"lendingId_","internalType":"uint256[]"},{"type":"uint8[]","name":"rentDuration_","internalType":"uint8[]"},{"type":"uint256[]","name":"rentAmount_","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setBeneficiary","inputs":[{"type":"address","name":"newBeneficiary_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setPaused","inputs":[{"type":"bool","name":"newPaused_","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRentFee","inputs":[{"type":"address","name":"token_","internalType":"address"},{"type":"uint256","name":"newRentFee_","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRights","inputs":[{"type":"address","name":"rights_","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"stopLend","inputs":[{"type":"uint8[]","name":"nftStandard_","internalType":"enum IRentRegistry.NFTStandard[]"},{"type":"address[]","name":"nftAddress_","internalType":"address[]"},{"type":"uint256[]","name":"tokenId_","internalType":"uint256[]"},{"type":"uint256[]","name":"lendingId_","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"stopRent","inputs":[{"type":"uint8[]","name":"nftStandard_","internalType":"enum IRentRegistry.NFTStandard[]"},{"type":"address[]","name":"nftAddress_","internalType":"address[]"},{"type":"uint256[]","name":"tokenId_","internalType":"uint256[]"},{"type":"uint256[]","name":"lendingId_","internalType":"uint256[]"},{"type":"uint256[]","name":"rentingId_","internalType":"uint256[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"supportsInterface","inputs":[{"type":"bytes4","name":"interfaceId","internalType":"bytes4"}]},{"type":"event","name":"Lend","inputs":[{"type":"bool","name":"is721","indexed":false},{"type":"address","name":"lenderAddress","indexed":true},{"type":"address","name":"nftAddress","indexed":true},{"type":"uint256","name":"tokenID","indexed":true},{"type":"uint256","name":"lendingID","indexed":false},{"type":"uint8","name":"maxRentDuration","indexed":false},{"type":"bytes4","name":"dailyRentPrice","indexed":false},{"type":"uint16","name":"lendAmount","indexed":false},{"type":"uint8","name":"paymentToken","indexed":false},{"type":"bool","name":"willAutoRenew","indexed":false}],"anonymous":false},{"type":"event","name":"Rent","inputs":[{"type":"address","name":"renterAddress","indexed":true},{"type":"uint256","name":"lendingID","indexed":true},{"type":"uint256","name":"rentingID","indexed":true},{"type":"uint16","name":"rentAmount","indexed":false},{"type":"uint8","name":"rentDuration","indexed":false},{"type":"uint32","name":"rentedAt","indexed":false}],"anonymous":false},{"type":"event","name":"RentClaimed","inputs":[{"type":"uint256","name":"rentingID","indexed":true},{"type":"uint256","name":"amount","indexed":false},{"type":"uint256","name":"fee","indexed":false},{"type":"uint32","name":"collectedAt","indexed":false}],"anonymous":false},{"type":"event","name":"RentFeeUpdated","inputs":[{"type":"address","name":"token","indexed":true},{"type":"uint256","name":"feePercent","indexed":false}],"anonymous":false},{"type":"event","name":"StopLend","inputs":[{"type":"uint256","name":"lendingID","indexed":true},{"type":"uint32","name":"stoppedAt","indexed":false},{"type":"uint16","name":"amount","indexed":false}],"anonymous":false},{"type":"event","name":"StopRent","inputs":[{"type":"uint256","name":"rentingID","indexed":true},{"type":"uint256","name":"renterAmount","indexed":false},{"type":"uint256","name":"lenderAmount","indexed":false},{"type":"uint256","name":"fee","indexed":false},{"type":"uint32","name":"stoppedAt","indexed":false}],"anonymous":false},{"type":"error","name":"AlreadyRented","inputs":[]},{"type":"error","name":"AssertionError","inputs":[]},{"type":"error","name":"InvalidInput","inputs":[{"type":"bytes32","name":"parameter","internalType":"bytes32"}]},{"type":"error","name":"LenderRequired","inputs":[]},{"type":"error","name":"LendingAlreadyExists","inputs":[]},{"type":"error","name":"LendingNotExists","inputs":[]},{"type":"error","name":"NothingToPay","inputs":[]},{"type":"error","name":"Paused","inputs":[]},{"type":"error","name":"RentAlreadyCompleted","inputs":[]},{"type":"error","name":"RentNotCompleted","inputs":[]},{"type":"error","name":"RenterRequired","inputs":[]},{"type":"error","name":"RentingAlreadyExists","inputs":[]},{"type":"error","name":"RentingNotExists","inputs":[]},{"type":"error","name":"SameValue","inputs":[]}]
              

Contract Creation Code

0x604060808152346200039357620039e990813803806200001f81620003b3565b93843982019160a08184031262000393576200003b81620003d9565b9060206200004b818301620003d9565b9462000059858401620003d9565b60608401516001600160401b039491939190858111620003935781019083601f8301121562000393578151916200009a6200009484620003ee565b620003b3565b92848085838152019160051b83010191868311620003935785809101915b8383106200039857505050506080810151908682116200039357019280601f8501121562000393578351620000f16200009482620003ee565b94848087848152019260051b820101928311620003935784809101915b8383106200038257505088516001600160a01b038781169890949350909150818a01908111828210176200036c578952601381527f47756172643a207a65726f2061646472657373000000000000000000000000008482015286156200030c575060018060a01b0319600096818854161787556001978880556004968988558960055560ff19600654166006558351875103620002e9579a8482620001cb8c9e620001ba849662000431565b620001c58562000431565b62000431565b168360025416176002551690600354161760035585985b620001f7575b87516135849081620004658239f35b8051891015620002e3576200021a82620002128b8462000406565b511662000431565b81620002278a8362000406565b5116620002358a8662000406565b516107d08111620002bf5781885260078086528a8920548214620002af577f5820e211ee8cba31a6b5e7909a12b6dac26ce15657d066c5269d1e60f5abd316918691848b528252808c8b20558b51908152a260001989146200029c579786019786620001e2565b634e487b7160e01b865260118552602486fd5b8a5163c23f6ccb60e01b81528890fd5b895163e1f2fb3d60e01b81526a6e657752656e744665655f60a81b81890152602490fd5b620001e8565b8a5163e1f2fb3d60e01b815269666565546f6b656e735f60b01b818a0152602490fd5b83908951809262461bcd60e51b82528060048301528251908160248401526000935b82851062000352575050604492506000838284010152601f80199101168101030190fd5b84810182015186860160440152938101938593506200032e565b634e487b7160e01b600052604160045260246000fd5b82518152918101918591016200010e565b600080fd5b8190620003a584620003d9565b8152019101908590620000b8565b6040519190601f01601f191682016001600160401b038111838210176200036c57604052565b51906001600160a01b03821682036200039357565b6001600160401b0381116200036c5760051b60200190565b80518210156200041b5760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b6001600160a01b0316156200044257565b60405163e1f2fb3d60e01b8152666164647265737360c81b6004820152602490fdfe608080604052600436101561001357600080fd5b60003560e01c90816301ffc9a7146110755750806314b5685c14610fa5578063150b7a0214610f5057806316c38b3c14610ea757806316c61ccc14610e845780631c31f71014610de0578063296c21b714610d0157806337d2aae914610bfb578063565a2e2c14610bd2578063937821a114610afd578063a4052a9514610ac3578063bc197c8114610a3b578063c9c40183146108b5578063cbe55e541461067e578063d7e0936014610539578063dc68a142146101ca578063f23a6e61146101755763fe46b7cd146100e557600080fd5b346101705761011d6100f6366113fc565b916000606060405161010781611116565b8281528260208201528260408201520152612192565b600052600960205260806101346040600020611605565b61ffff60606040519260018060a01b03815116845260ff602082015116602085015263ffffffff60408201511660408501520151166060820152f35b600080fd5b346101705760a03660031901126101705761018e611219565b5061019761122f565b506084356001600160401b038111610170576101b79036906004016113b5565b5060405163f23a6e6160e01b8152602090f35b3461017057610100366003190112610170576001600160401b03600435818111610170576101fc9036906004016111b2565b9060243581811161017057610215903690600401611245565b906044358181116101705761022e9036906004016112b3565b606435828111610170576102469036906004016112b3565b936084358381116101705761025f903690600401611426565b9160a4359584871161017057366023880112156101705786600401356102848161119b565b97610292604051998a61117a565b818952602460208a019260051b8201019036821161017057602401915b8183106105185750505060c435858111610170576102d1903690600401611426565b9360e4359586116101705736602387011215610170578560040135956102f68761119b565b96610304604051988961117a565b80885260208801913660248360051b83010111610170579160248301925b60248360051b82010184106104f8575050505060ff600654166104e65760209761034a61148e565b610352611e2d565b50604051916103608361115f565b60008352604051936103718561115f565b60008552604051956103828761115f565b60008752604051976103938961115f565b6000895260016040519d8e6103a7816110fa565b60008152015260408d01528a60608d015260808c015260a08b015260c08a015260e08901526101008801526101208701526101408601526101608501526101808401526101a083015251156104c5575b60208101516060820151908151146104b65781516001600160a01b0391829161041f9161153b565b511690610435606084015160208501519061153b565b51161480610473575b1561045a576104506020820151611516565b60208201526103f7565b61046381613027565b6104506020820151808352611516565b5061048f61048a604083015160208401519061153b565b6115eb565b60028110156104a05760011461043e565b634e487b7160e01b600052602160045260246000fd5b6104bf82613027565b60018055005b60405163e1f2fb3d60e01b81526562756e646c6560d01b6004820152602490fd5b6040516313d0ff5960e31b8152600490fd5b833515158435036101705760208060249386358152019401939150610322565b82356001600160e01b031981168103610170578152602092830192016102af565b3461017057604036600319011261017057610552611219565b600054604051630f6266a760e01b8152306004820152336024808301919091526020939035926001600160a01b03929085908290604490829087165afa8015610672576105b091600091610645575b506105aa61215e565b9061218a565b6107d0831161061f571691826000526007815281604060002054141560001461060d577f5820e211ee8cba31a6b5e7909a12b6dac26ce15657d066c5269d1e60f5abd31691836000526007825280604060002055604051908152a2005b60405163c23f6ccb60e01b8152600490fd5b60405163e1f2fb3d60e01b81526a6e657752656e744665655f60a81b6004820152602490fd5b6106659150863d881161066b575b61065d818361117a565b8101906116b8565b866105a1565b503d610653565b6040513d6000823e3d90fd5b346101705760c0366003190112610170576001600160401b03600435818111610170576106af9036906004016111b2565b602435828111610170576106c7903690600401611245565b604435838111610170576106df9036906004016112b3565b91606435848111610170576106f89036906004016112b3565b60843585811161017057610710903690600401611426565b9460a435908111610170576107299036906004016112b3565b60ff600654166104e65760209561073e61148e565b610746611e2d565b50604051926107548461115f565b60008452604051906107658261115f565b600082526040516107758161115f565b60008152604051906107868261115f565b60008252604051966107978861115f565b60008852604051986107a88a61115f565b60008a5260016040519d8e6107bc816110fa565b60008152015260408d015260608c019a8a8c5260808d015260a08c015260c08b015260e08a01526101008901526101208801526101408701526101608601526101808501526101a084015251156104c5575b60208201518151908151146108ac5782516001600160a01b039182916108339161153b565b511690610846835160208601519061153b565b51161480610884575b1561086b576108616020830151611516565b602083015261080e565b61087482612ade565b6108616020830151808452611516565b5061089b61048a604084015160208501519061153b565b60028110156104a05760011461084f565b6104bf83612ade565b34610170576080366003190112610170576001600160401b03600435818111610170576108e69036906004016111b2565b90602491823582811161017057610901903690600401611245565b916044358181116101705761091a9036906004016112b3565b90606435908111610170576109339036906004016112b3565b9060ff600654166104e6576109609361094a61148e565b604051936109578561115f565b60008552611e96565b606081019182515115610a1d5760208201925b8351815190815114610a145783516001600160a01b039182916109959161153b565b5116906109a5835187519061153b565b511614806109da575b156109c4576109bd8451611516565b8452610973565b6109cd836128b2565b6109bd8451808552611516565b506109ee61048a604085015186519061153b565b60028110156109ff576001146109ae565b82634e487b7160e01b60005260216004526000fd5b6104bf846128b2565b60405163e1f2fb3d60e01b81526562756e646c6560d01b6004820152fd5b346101705760a036600319011261017057610a54611219565b50610a5d61122f565b506001600160401b0360443581811161017057610a7e9036906004016112b3565b5060643581811161017057610a979036906004016112b3565b5060843590811161017057610ab09036906004016113b5565b5060405163bc197c8160e01b8152602090f35b34610170576020366003190112610170576001600160a01b03610ae4611219565b1660005260076020526020604060002054604051908152f35b3461017057610b0b36611311565b9260ff600693929354166104e657610b2a94610b2561148e565b611e96565b6060810190815151156104c55760208101915b8251815190815114610bc95782516001600160a01b03918291610b5f9161153b565b511690610b6f835186519061153b565b51161480610ba4575b15610b8e57610b878351611516565b8352610b3d565b610b9782612641565b610b878351808452611516565b50610bb861048a604084015185519061153b565b60028110156104a057600114610b78565b6104bf83612641565b34610170576000366003190112610170576003546040516001600160a01b039091168152602090f35b346101705760208060031936011261017057610c15611219565b600054604051630f6266a760e01b81523060048201523360248201526001600160a01b03808316949293919291908284604481895afa91821561067257610c6d610ca193610cd496600091610ce457506105aa61215e565b16948560405191610c7d83611144565b601183527047756172643a2073616d652076616c756560781b85840152141561218a565b7247756172643a207a65726f206164647265737360681b60405191610cc583611144565b6013835282015283151561218a565b6001600160a01b03191617600055005b610cfb9150863d881161066b5761065d818361117a565b896105a1565b3461017057610d51610d12366113fc565b91600060e0604051610d23816110c8565b8281528260208201528260408201528260608201528260808201528260a08201528260c082015201526121d8565b6000526008602052610d666040600020611571565b604051815160028110156104a0576101009260e091835260018060a01b03602082015116602084015260ff604082015116604084015263ffffffff821b606082015116606084015261ffff80608083015116608085015260a08201511660a084015260ff60c08201511660c08401520151151560e0820152f35b3461017057602036600319011261017057610df9611219565b600054604051630f6266a760e01b81523060048201523360248201526001600160a01b0392916020908290604490829087165afa801561067257610e4791600091610e6657506105aa61215e565b600354908216918116821461060d576001600160a01b03191617600355005b610e7e915060203d811161066b5761065d818361117a565b846105a1565b3461017057600036600319011261017057602060ff600654166040519015158152f35b346101705760203660031901126101705760043580151580910361017057600054604051630f6266a760e01b815230600482015233602482015290602090829060449082906001600160a01b03165afa801561067257610f1191600091610f3257506105aa61215e565b6006549060ff82161515811461060d5760ff169060ff191617600655600080f35b610f4a915060203d811161066b5761065d818361117a565b836105a1565b3461017057608036600319011261017057610f69611219565b50610f7261122f565b506064356001600160401b03811161017057610f929036906004016113b5565b50604051630a85bd0160e11b8152602090f35b3461017057610fb336611311565b9260ff600693929354166104e657610fcd94610b2561148e565b6060810190815151156104c55760208101915b825181519081511461106c5782516001600160a01b039182916110029161153b565b511690611012835186519061153b565b51161480611047575b156110315761102a8351611516565b8352610fe0565b61103a82612218565b61102a8351808452611516565b5061105b61048a604084015185519061153b565b60028110156104a05760011461101b565b6104bf83612218565b34610170576020366003190112610170576004359063ffffffff60e01b821680920361017057602091630271189760e51b81149081156110b7575b5015158152f35b6301ffc9a760e01b149050836110b0565b61010081019081106001600160401b038211176110e457604052565b634e487b7160e01b600052604160045260246000fd5b6101c081019081106001600160401b038211176110e457604052565b608081019081106001600160401b038211176110e457604052565b6001600160401b0381116110e457604052565b604081019081106001600160401b038211176110e457604052565b602081019081106001600160401b038211176110e457604052565b90601f801991011681019081106001600160401b038211176110e457604052565b6001600160401b0381116110e45760051b60200190565b81601f82011215610170578035916111c98361119b565b926111d7604051948561117a565b808452602092838086019260051b820101928311610170578301905b828210611201575050505090565b813560028110156101705781529083019083016111f3565b600435906001600160a01b038216820361017057565b602435906001600160a01b038216820361017057565b81601f820112156101705780359161125c8361119b565b9261126a604051948561117a565b808452602092838086019260051b820101928311610170578301905b828210611294575050505090565b81356001600160a01b0381168103610170578152908301908301611286565b81601f82011215610170578035916112ca8361119b565b926112d8604051948561117a565b808452602092838086019260051b820101928311610170578301905b828210611302575050505090565b813581529083019083016112f4565b60a0600319820112610170576004906001600160401b038235818111610170578261133d9185016111b2565b936024358281116101705783611354918601611245565b93604435838111610170578461136b9183016112b3565b9360643584811161017057816113829184016112b3565b936084359081116101705761139792016112b3565b90565b6001600160401b0381116110e457601f01601f191660200190565b81601f82011215610170578035906113cc8261139a565b926113da604051948561117a565b8284526020838301011161017057816000926020809301838601378301015290565b6060906003190112610170576004356001600160a01b038116810361017057906024359060443590565b81601f820112156101705780359161143d8361119b565b9261144b604051948561117a565b808452602092838086019260051b820101928311610170578301905b828210611475575050505090565b813560ff81168103610170578152908301908301611467565b60026001541461149f576002600155565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b906114ee8261119b565b6114fb604051918261117a565b828152809261150c601f199161119b565b0190602036910137565b60001981146115255760010190565b634e487b7160e01b600052601160045260246000fd5b805182101561154f5760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b60028210156104a05752565b9060405161157e816110c8565b60e081935461159060ff821684611565565b60018060a01b038160081c16602084015260ff8160a81c16604084015263ffffffff821b8160301b16606084015261ffff808260d01c16608085015281831c1660a084015260ff8160f01c1660c084015260f81c1515910152565b5160028110156104a05790565b9190820391821161152557565b9060405161161281611116565b91546001600160a01b038116835260a081901c60ff16602084015260a881901c63ffffffff16604084015260c81c61ffff166060830152565b9081602091031261017057516001600160a01b03811681036101705790565b90816020910312610170575160ff811681036101705790565b604d811161152557600a0a90565b8181029291811591840414171561152557565b61ffff918216908216039190821161152557565b90816020910312610170575180151581036101705790565b60405161174d916001600160a01b03166116e982611144565b6000806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af13d156117d5573d916117318361139a565b9261173f604051948561117a565b83523d60008785013e6117d9565b8051908282159283156117bd575b505050156117665750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b6117cd93508201810191016116b8565b38828161175b565b6060915b9192901561183b57508151156117ed575090565b3b156117f65790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b82519091501561184e5750805190602001fd5b60405162461bcd60e51b815290819061186a906004830161186e565b0390fd5b6020808252825181830181905290939260005b8281106118a357505060409293506000838284010152601f8019910116010190565b818101860151848201604001528501611881565b91939092938254926000938060f81c15600014611bd45750805461ffff966060888360d01c169701968888511690818111600014611a0657505050509061192661190a8787511688855460d01c166116a4565b835461ffff60d01b191660d09190911b61ffff60d01b16178355565b905484518616926001600160a01b0392831692909160081c16823b15611a0257604051637921219560e11b81523060048201526001600160a01b039190911660248201526044810191909152606481019290925260a06084830152600060a48301528290829060c490829084905af180156119f7576119e0575b505060008051602061352f833981519152916119db915b516040805163ffffffff421681529290911661ffff16602083015290918291820190565b0390a2565b6119ea8291611131565b6119f457806119a0565b80fd5b6040513d84823e3d90fd5b8480fd5b14611a2b575b50505050505060008051602061352f833981519152916119db916119b7565b6002811015611bc057611afc5750546001600160a01b038281169160081c16813b15611a0257604051632142170760e11b81523060048201526001600160a01b03919091166024820152604481018490529084908290606490829084905af18015611af1579087939291611ad2575b506119db949260008051602061352f833981519152969492611abb926121d8565b815260086020526040812055918193388080611a0c565b8491929350611ae090611131565b611aed5790859138611a9a565b8280fd5b6040513d86823e3d90fd5b855192949392871691506001600160a01b0380861691823b15611bbc57604051637921219560e11b815230600482015260089190911c919091166001600160a01b0316602482015260448101849052606481019290925260a06084830152600060a48301528390829060c490829084905af18015611bb15760008051602061352f8339815191529694926119db9694928992611b9e575b50611abb92936121d8565b92611bab611abb94611131565b92611b93565b6040513d85823e3d90fd5b8580fd5b634e487b7160e01b86526021600452602486fd5b93979650939150506060935061ffff809481920151169160e01c1601918211611c1a57805461ffff60e01b191660e09290921b61ffff60e01b169190911790559050565b565b634e487b7160e01b84526011600452602484fd5b60405163a9059cbb60e01b60208201526001600160a01b039092166024830152604480830193909352918152611c1891611c6782611116565b6116d0565b6001600160a01b03918216600081815260076020526040902054909392918115611caf57611ca1611c18939261271092611691565b049384916003541690611c2e565b50600093505050565b90815180825260208080930193019160005b828110611cd8575050505090565b835185529381019392810192600101611cca565b9193929092611d0461048a604085015185519061153b565b60028110156104a057611d9857505060608101518151611d40916001600160a01b0391611d309161153b565b511691608081015190519061153b565b5190803b1561017057604051632142170760e11b81526001600160a01b03938416600482015293909216602484015260448301526000908290606490829084905af1801561067257611d8f5750565b611c1890611131565b9092606083015192611db460018060a01b03948592519061153b565b511691823b156101705760206000959186611e10611dfd8295896040519c8d9b8c9a631759616b60e11b8c521660048b015216602489015260a0604489015260a4880190611cb8565b6003199384888303016064890152611cb8565b85810392830160848701525201925af1801561067257611d8f5750565b60405190611e3a826110fa565b8160008152600060208201526101a060609182604082015282808201528260808201528260a08201528260c08201528260e082015282610100820152826101208201528261014082015282610160820152826101808201520152565b9193929093611ea3611e2d565b50604093845192611eb38461115f565b600096878552865193611ec58561115f565b888552875195611ed48761115f565b898752885191611ee38361115f565b8a8352895193611ef28561115f565b8b85528a5199611f018b61115f565b8c8b528b519b611f108d61115f565b8d8d528051809e611f20826110fa565b8152602001600190528d015260608c015260808b015260a08a015260c089015260e08801526101008701526101208601526101408501526101608401526101808301526101a082015290565b8060e01c80158015611ff7575b611fd757611fb99160f01c908361ffff8092169261270f808211611fcf575b808511611fc5575b506127109183611fb09216611691565b94049116611691565b81018091116115255790565b9350612710611fa0565b905080611f98565b60405163e1f2fb3d60e01b815264707269636560d81b6004820152602490fd5b506127108310611f79565b92919061201761201282846115f8565b6114e4565b93815b8381106120275750505050565b8061203561204f928461153b565b5161204961204386846115f8565b8961153b565b52611516565b61201a565b60208101516001600160a01b0316159081156120a2575b811561208b575b5061207957565b604051631c9bac5160e21b8152600490fd5b606001516001600160e01b03191615905038612072565b604081015160ff1615915061206b565b80516001600160a01b0316159081156120f9575b81156120e6575b506120d457565b604051630269a7b160e11b8152600490fd5b63ffffffff9150604001511615386120cd565b602081015160ff161591506120c6565b90604082019063ffffffff8083511682111561214c5761213560ff9260209262015180955116906115f8565b93015116029062ffffff8216918203611525571190565b60405163b009d33760e01b8152600490fd5b6040519061216b82611144565b601082526f47756172643a204e6f2072696768747360801b6020830152565b1561184e5750565b91604051916020830193600160f91b85526bffffffffffffffffffffffff199060601b16602184015260358301526055820152605581526121d281611116565b51902090565b91604051916020830193600160f81b85526bffffffffffffffffffffffff199060601b16602184015260358301526055820152605581526121d281611116565b9081515b602083015181101561263c576060830151835161226c916001600160a01b03916122459161153b565b511661225583608087015161153b565b516122658461010088015161153b565b51916121d8565b606084015184519192916122b3916001600160a01b039161228c9161153b565b511661229c83608088015161153b565b516122ac8461012089015161153b565b5191612192565b9160005260086020526040600020826000526009806020526122d86040600020611605565b93600052602052600060408120556122f76122f282611571565b612054565b612300836120b2565b8251336001600160a01b039091160361262a5761231d4284612109565b6126185761233261048a83604088015161153b565b81549060ff82169060028210156104a05760028110156104a057036125f25761ffff806060860151169160d01c161061214c5761237963ffffffff604085015116426115f8565b9061238381611571565b9160ff60c084015116602060018060a01b03600254169160246040518094819363c6ee427f60e01b835260048301525afa908115610672576000916125c3575b5060405163313ce56760e01b81526020816004816001600160a01b0386165afa80156106725761242f61240960ff612446936201518095600091612594575b5016611683565b61242961ffff60608c0151169163ffffffff60e01b60608b015116611f6c565b90611691565b61244060ff60208b01511682611691565b94611691565b048215801561258c575b61257a576125237fb09142df05002174e1dd0d23b856f0f7df7c6c43a6491a53e7f07c828f4d6939948a6125569961248a856080986115f8565b956124c66124ab6124a46001600160a01b03841689611c6c565b80986115f8565b6020909b01518b906001600160a01b03908116908416611c2e565b868061255b575b5050606082015182516001600160a01b03916124e9919061153b565b51166124fe61048a604085015185519061153b565b9161251c8c610100612513828d89015161153b565b5196015161153b565b51946118b7565b612532866101208b015161153b565b51946040519283526020830152604082015263ffffffff42166060820152a2611516565b61221c565b8251612573926001600160a01b039182169116611c2e565b38866124cd565b6040516326b1e12960e11b8152600490fd5b508015612450565b6125b6915060203d6020116125bc575b6125ae818361117a565b81019061166a565b38612402565b503d6125a4565b6125e5915060203d6020116125eb575b6125dd818361117a565b81019061164b565b386123c3565b503d6125d3565b60405163e1f2fb3d60e01b81526a1b999d14dd185b99185c9960aa1b6004820152602490fd5b604051639d9e029f60e01b8152600490fd5b604051631c962d0560e01b8152600490fd5b509050565b9081515b602083015181101561263c576060830151835161266e916001600160a01b03916122459161153b565b6060840151845191929161268e916001600160a01b039161228c9161153b565b916000526008602052604060002091806000526009806020526126b46040600020611605565b91600052602052600060408120556126ce6122f284611571565b6126d7816120b2565b6126e14282612109565b156128a0576126ef83611571565b9060ff60c083015116906024602060018060a01b03600254166040519283809263c6ee427f60e01b8252600497888301525afa90811561067257600091612881575b5060405163313ce56760e01b8152926020908490816001600160a01b0385165afa9283156106725761287c968960609461280a6127d06127c26127a360ff7fbcc1719994927933c5e16aafb3b01e6ec102fb62f89c2bb8d5832bd00e4619679b61284e99600091612594575016611683565b61242961ffff8b87015116918d8c63ffffffff60e01b91015116611f6c565b60ff60208501511690611691565b986127ee6127e76001600160a01b0389168c611c6c565b809b6115f8565b602090910151909687916001600160a01b039081169116611c2e565b8186015182516001600160a01b0391612823919061153b565b511661283861048a604085015185519061153b565b9161251c8b61010061251382608089015161153b565b61285d856101208a015161153b565b5193604051918252602082015263ffffffff42166040820152a2611516565b612645565b61289a915060203d6020116125eb576125dd818361117a565b38612731565b60405163de50443160e01b8152600490fd5b602091828201916128c961201284518351906115f8565b9381519263ffffffff4216935b8551811015612a6f57606084015184516001600160a01b039182916128fa9161153b565b511661292061290d84608089015161153b565b519161010088019261226586855161153b565b91600092808452600891828852604093848620916129406122f284611571565b33908a61294c85611571565b01511603612a5e5761296461048a88878d015161153b565b91549160ff831690600280831015612a4a57811015612a365703612a115761ffff808360d01c169260e01c168203612a00579060008051602061352f8339815191526129d2888f94846129cb8f976129c56129fb9e9d9c9b9a9951866115f8565b9061153b565b525161153b565b51855163ffffffff8e16815261ffff93909316602084015291604090a283528652812055611516565b6128d6565b8451633c42e29760e11b8152600490fd5b845163e1f2fb3d60e01b81526a1b999d14dd185b99185c9960aa1b6004820152602490fd5b634e487b7160e01b88526021600452602488fd5b634e487b7160e01b89526021600452602489fd5b8451631837e8d760e11b8152600490fd5b5093925050612a8660808201518251845191612002565b9080519251612a9861201285836115f8565b93805b828110612ab457505050611c1893945033903090611cec565b80612acb612ac584612ad9946115f8565b8a61153b565b5161204961204385846115f8565b612a9b565b9081515b602083015181101561263c576060830180518451919291612b62916001600160a01b0391612b0f9161153b565b5116612b5883612b386080890193612b2883865161153b565b51612265846101008d015161153b565b955188516001600160a01b0391612b4f919061153b565b5116925161153b565b5160055491612192565b82600052600860205260406000208160005260096020526040600020612b8a6122f283611571565b612b9381611605565b80516001600160a01b03161580159190613016575b8115613002575b50612ff057612bbd82611571565b60208101516001600160a01b03163314612fc85760ff612be2866101408b015161153b565b5116158015612fad575b612f2657612bff856101608a015161153b565b51158015612f93575b612f6e5760ff604081612c20886101408d015161153b565b51169201511610612f2657612c3c61048a8560408a015161153b565b91549160ff83169060028210156104a05760028110156104a057036125f257612c6a8461016089015161153b565b5161ffff8360e01c1610612f6e5760025460405163c6ee427f60e01b815260f084901c60ff1660048201529290602090849060249082906001600160a01b03165afa92831561067257600093612f4d575b5060405163313ce56760e01b8152906020826004816001600160a01b0388165afa91821561067257612cfe60ff612d3e9461242993600091612594575016611683565b612d2b8b60ff612d238b610140612d1a8261016087015161153b565b5194015161153b565b511690611691565b9260301b6001600160e01b031916611f6c565b8015612f26576040516323b872dd60e01b6020820152336024820152306044820152606480820192909252908152612f2195612f19948994612e9593612d9791612d8960848361117a565b6001600160a01b03166116d0565b61ffff612da98861016088015161153b565b51169060ff612dbd8961014089015161153b565b51169160405192612dcd84611116565b338452602084015263ffffffff421660408401526060830152600052600960205260406000209060018060a01b038151169082549160ff60a01b602083015160a01b1690604083015192606061ffff60c81b91015160c81b169364ffffffffff60d81b1617179063ffffffff60a81b9060a81b161717905561ffff612e578761016087015161153b565b5116906000526008602052612e7960406000209161ffff835460e01c166116a4565b815461ffff60e01b191660e09190911b61ffff60e01b16179055565b612ea48461010084015161153b565b5160055492839263ffffffff60ff612ed58961014061ffff612ecb836101608a015161153b565b511696015161153b565b5116915460a81c1690604051928352602083015260408201527f61e1a1e6f89eaba4ba0119b0023bd32b1bb0412ab96ccd8d0588a3e98a09763160603392a4611516565b600555611516565b612ae2565b60405163e1f2fb3d60e01b81526b3932b73a223ab930ba34b7b760a11b6004820152602490fd5b612f6791935060203d6020116125eb576125dd818361117a565b9138612cbb565b60405163e1f2fb3d60e01b8152691c995b9d105b5bdd5b9d60b21b6004820152602490fd5b5061ffff612fa6866101608b015161153b565b5111612c08565b5060ff80612fc0876101408c015161153b565b511611612bec565b60405163e1f2fb3d60e01b81526c6c656e6465724164647265737360981b6004820152602490fd5b6040516308e15a0360e21b8152600490fd5b63ffffffff91506040015116151538612baf565b602081015160ff1615159150612ba8565b9081515b60208301518110156134f2576130458160a085015161153b565b511580156134d9575b6134b45760ff6130628260c086015161153b565b511615801561349a575b6134705761307e8160e085015161153b565b5160e01c1561344757606083015183516130be916001600160a01b03916130a49161153b565b51166130b483608087015161153b565b51600454916121d8565b908160005260086020526130d56040600020611571565b60208101516001600160a01b03161580159190613436575b811561341e575b5061340c5760ff61310a8261018087015161153b565b5116156133e75761312261048a82604087015161153b565b60028110156104a05761ffff61313c8360a088015161153b565b519115911681806133dc575b6133b65761315d61048a84604089015161153b565b9360ff61316e8560c08a015161153b565b511663ffffffff60e01b6131868660e08b015161153b565b511660ff613199876101808c015161153b565b5116906131ab876101a08c015161153b565b511515926131c5604051996131bf8b6110c8565b8a611565565b3360208a0152604089015260608801528360808801528360a088015260c087015260e0860152600052600860205260406000209184519260028410156104a057805460208701516040880151606089015160808a01516001600160e01b031990941660ff9098169790971760089290921b610100600160a81b03169190911760a89190911b60ff60a81b161760309590951c63ffffffff60b01b169490941760d09490941b61ffff60d01b169390931783556133b1946133a9939060a0810151825461ffff60e01b191660e09190911b61ffff60e01b1617825560c0810151825460e0909201516001600160f01b0390921660f09190911b60ff60f01b161790151560f81b6001600160f81b031916179055606087015187516001600160a01b03916132f1919061153b565b51166133018560808a015161153b565b5191600454938960ff6133188960c084015161153b565b51169163ffffffff60e01b6133318a60e085015161153b565b51166133558a6101a060ff61334b8361018089015161153b565b511695015161153b565b5115159360405195865288602087015260408601526060850152608084015260a083015260c08201527ff8d82fdbb496215c151d63910926322dcb6ba0f8cbd5fa216ef261106bb1ab4660e03392a4611516565b600455611516565b61302b565b60405163e1f2fb3d60e01b81526a17db195b99105b5bdd5b9d60aa1b6004820152602490fd5b506001811415613148565b60405163e1f2fb3d60e01b8152697061796d656e7449785f60b01b6004820152602490fd5b60405163b9a87ba760e01b8152600490fd5b606001516001600160e01b03191615159050386130f4565b604081015160ff16151591506130ed565b60405163e1f2fb3d60e01b81526d6461696c7952656e74507269636560901b6004820152602490fd5b60405163e1f2fb3d60e01b81526e36b0bc2932b73a223ab930ba34b7b760891b6004820152602490fd5b5060ff806134ac8360c087015161153b565b51161161306c565b60405163e1f2fb3d60e01b8152691b195b99105b5bdd5b9d60b21b6004820152602490fd5b5061ffff6134eb8260a086015161153b565b511161304e565b5090611c189060808101516135246135138351926020850193845191612002565b9160a0840151908451905191612002565b9130903390611cec56fefc33a7c95ee492cb6247bd90b28bbe1f459d8f80f3dc65f934cd020cf1e83efba2646970667358221220c275f1e4de3a5e6ad379ae04fda28b286916212950fe140c0e6b057c1807221064736f6c634300081100330000000000000000000000009f59adf8ca58d0ffd9372af68d747c3dbb4cb02f0000000000000000000000009ab18990c9035f7878a59e134bce5d618cb19d55000000000000000000000000ea6a031bbaf2eadb5ecfd32b8aa39e9a95659e4600000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000099ebd96615a50933c69b435f8a59539caff95d7c000000000000000000000000750449925085a335b58b6706e0cf17039caa606f000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000002bc

Deployed ByteCode

0x608080604052600436101561001357600080fd5b60003560e01c90816301ffc9a7146110755750806314b5685c14610fa5578063150b7a0214610f5057806316c38b3c14610ea757806316c61ccc14610e845780631c31f71014610de0578063296c21b714610d0157806337d2aae914610bfb578063565a2e2c14610bd2578063937821a114610afd578063a4052a9514610ac3578063bc197c8114610a3b578063c9c40183146108b5578063cbe55e541461067e578063d7e0936014610539578063dc68a142146101ca578063f23a6e61146101755763fe46b7cd146100e557600080fd5b346101705761011d6100f6366113fc565b916000606060405161010781611116565b8281528260208201528260408201520152612192565b600052600960205260806101346040600020611605565b61ffff60606040519260018060a01b03815116845260ff602082015116602085015263ffffffff60408201511660408501520151166060820152f35b600080fd5b346101705760a03660031901126101705761018e611219565b5061019761122f565b506084356001600160401b038111610170576101b79036906004016113b5565b5060405163f23a6e6160e01b8152602090f35b3461017057610100366003190112610170576001600160401b03600435818111610170576101fc9036906004016111b2565b9060243581811161017057610215903690600401611245565b906044358181116101705761022e9036906004016112b3565b606435828111610170576102469036906004016112b3565b936084358381116101705761025f903690600401611426565b9160a4359584871161017057366023880112156101705786600401356102848161119b565b97610292604051998a61117a565b818952602460208a019260051b8201019036821161017057602401915b8183106105185750505060c435858111610170576102d1903690600401611426565b9360e4359586116101705736602387011215610170578560040135956102f68761119b565b96610304604051988961117a565b80885260208801913660248360051b83010111610170579160248301925b60248360051b82010184106104f8575050505060ff600654166104e65760209761034a61148e565b610352611e2d565b50604051916103608361115f565b60008352604051936103718561115f565b60008552604051956103828761115f565b60008752604051976103938961115f565b6000895260016040519d8e6103a7816110fa565b60008152015260408d01528a60608d015260808c015260a08b015260c08a015260e08901526101008801526101208701526101408601526101608501526101808401526101a083015251156104c5575b60208101516060820151908151146104b65781516001600160a01b0391829161041f9161153b565b511690610435606084015160208501519061153b565b51161480610473575b1561045a576104506020820151611516565b60208201526103f7565b61046381613027565b6104506020820151808352611516565b5061048f61048a604083015160208401519061153b565b6115eb565b60028110156104a05760011461043e565b634e487b7160e01b600052602160045260246000fd5b6104bf82613027565b60018055005b60405163e1f2fb3d60e01b81526562756e646c6560d01b6004820152602490fd5b6040516313d0ff5960e31b8152600490fd5b833515158435036101705760208060249386358152019401939150610322565b82356001600160e01b031981168103610170578152602092830192016102af565b3461017057604036600319011261017057610552611219565b600054604051630f6266a760e01b8152306004820152336024808301919091526020939035926001600160a01b03929085908290604490829087165afa8015610672576105b091600091610645575b506105aa61215e565b9061218a565b6107d0831161061f571691826000526007815281604060002054141560001461060d577f5820e211ee8cba31a6b5e7909a12b6dac26ce15657d066c5269d1e60f5abd31691836000526007825280604060002055604051908152a2005b60405163c23f6ccb60e01b8152600490fd5b60405163e1f2fb3d60e01b81526a6e657752656e744665655f60a81b6004820152602490fd5b6106659150863d881161066b575b61065d818361117a565b8101906116b8565b866105a1565b503d610653565b6040513d6000823e3d90fd5b346101705760c0366003190112610170576001600160401b03600435818111610170576106af9036906004016111b2565b602435828111610170576106c7903690600401611245565b604435838111610170576106df9036906004016112b3565b91606435848111610170576106f89036906004016112b3565b60843585811161017057610710903690600401611426565b9460a435908111610170576107299036906004016112b3565b60ff600654166104e65760209561073e61148e565b610746611e2d565b50604051926107548461115f565b60008452604051906107658261115f565b600082526040516107758161115f565b60008152604051906107868261115f565b60008252604051966107978861115f565b60008852604051986107a88a61115f565b60008a5260016040519d8e6107bc816110fa565b60008152015260408d015260608c019a8a8c5260808d015260a08c015260c08b015260e08a01526101008901526101208801526101408701526101608601526101808501526101a084015251156104c5575b60208201518151908151146108ac5782516001600160a01b039182916108339161153b565b511690610846835160208601519061153b565b51161480610884575b1561086b576108616020830151611516565b602083015261080e565b61087482612ade565b6108616020830151808452611516565b5061089b61048a604084015160208501519061153b565b60028110156104a05760011461084f565b6104bf83612ade565b34610170576080366003190112610170576001600160401b03600435818111610170576108e69036906004016111b2565b90602491823582811161017057610901903690600401611245565b916044358181116101705761091a9036906004016112b3565b90606435908111610170576109339036906004016112b3565b9060ff600654166104e6576109609361094a61148e565b604051936109578561115f565b60008552611e96565b606081019182515115610a1d5760208201925b8351815190815114610a145783516001600160a01b039182916109959161153b565b5116906109a5835187519061153b565b511614806109da575b156109c4576109bd8451611516565b8452610973565b6109cd836128b2565b6109bd8451808552611516565b506109ee61048a604085015186519061153b565b60028110156109ff576001146109ae565b82634e487b7160e01b60005260216004526000fd5b6104bf846128b2565b60405163e1f2fb3d60e01b81526562756e646c6560d01b6004820152fd5b346101705760a036600319011261017057610a54611219565b50610a5d61122f565b506001600160401b0360443581811161017057610a7e9036906004016112b3565b5060643581811161017057610a979036906004016112b3565b5060843590811161017057610ab09036906004016113b5565b5060405163bc197c8160e01b8152602090f35b34610170576020366003190112610170576001600160a01b03610ae4611219565b1660005260076020526020604060002054604051908152f35b3461017057610b0b36611311565b9260ff600693929354166104e657610b2a94610b2561148e565b611e96565b6060810190815151156104c55760208101915b8251815190815114610bc95782516001600160a01b03918291610b5f9161153b565b511690610b6f835186519061153b565b51161480610ba4575b15610b8e57610b878351611516565b8352610b3d565b610b9782612641565b610b878351808452611516565b50610bb861048a604084015185519061153b565b60028110156104a057600114610b78565b6104bf83612641565b34610170576000366003190112610170576003546040516001600160a01b039091168152602090f35b346101705760208060031936011261017057610c15611219565b600054604051630f6266a760e01b81523060048201523360248201526001600160a01b03808316949293919291908284604481895afa91821561067257610c6d610ca193610cd496600091610ce457506105aa61215e565b16948560405191610c7d83611144565b601183527047756172643a2073616d652076616c756560781b85840152141561218a565b7247756172643a207a65726f206164647265737360681b60405191610cc583611144565b6013835282015283151561218a565b6001600160a01b03191617600055005b610cfb9150863d881161066b5761065d818361117a565b896105a1565b3461017057610d51610d12366113fc565b91600060e0604051610d23816110c8565b8281528260208201528260408201528260608201528260808201528260a08201528260c082015201526121d8565b6000526008602052610d666040600020611571565b604051815160028110156104a0576101009260e091835260018060a01b03602082015116602084015260ff604082015116604084015263ffffffff821b606082015116606084015261ffff80608083015116608085015260a08201511660a084015260ff60c08201511660c08401520151151560e0820152f35b3461017057602036600319011261017057610df9611219565b600054604051630f6266a760e01b81523060048201523360248201526001600160a01b0392916020908290604490829087165afa801561067257610e4791600091610e6657506105aa61215e565b600354908216918116821461060d576001600160a01b03191617600355005b610e7e915060203d811161066b5761065d818361117a565b846105a1565b3461017057600036600319011261017057602060ff600654166040519015158152f35b346101705760203660031901126101705760043580151580910361017057600054604051630f6266a760e01b815230600482015233602482015290602090829060449082906001600160a01b03165afa801561067257610f1191600091610f3257506105aa61215e565b6006549060ff82161515811461060d5760ff169060ff191617600655600080f35b610f4a915060203d811161066b5761065d818361117a565b836105a1565b3461017057608036600319011261017057610f69611219565b50610f7261122f565b506064356001600160401b03811161017057610f929036906004016113b5565b50604051630a85bd0160e11b8152602090f35b3461017057610fb336611311565b9260ff600693929354166104e657610fcd94610b2561148e565b6060810190815151156104c55760208101915b825181519081511461106c5782516001600160a01b039182916110029161153b565b511690611012835186519061153b565b51161480611047575b156110315761102a8351611516565b8352610fe0565b61103a82612218565b61102a8351808452611516565b5061105b61048a604084015185519061153b565b60028110156104a05760011461101b565b6104bf83612218565b34610170576020366003190112610170576004359063ffffffff60e01b821680920361017057602091630271189760e51b81149081156110b7575b5015158152f35b6301ffc9a760e01b149050836110b0565b61010081019081106001600160401b038211176110e457604052565b634e487b7160e01b600052604160045260246000fd5b6101c081019081106001600160401b038211176110e457604052565b608081019081106001600160401b038211176110e457604052565b6001600160401b0381116110e457604052565b604081019081106001600160401b038211176110e457604052565b602081019081106001600160401b038211176110e457604052565b90601f801991011681019081106001600160401b038211176110e457604052565b6001600160401b0381116110e45760051b60200190565b81601f82011215610170578035916111c98361119b565b926111d7604051948561117a565b808452602092838086019260051b820101928311610170578301905b828210611201575050505090565b813560028110156101705781529083019083016111f3565b600435906001600160a01b038216820361017057565b602435906001600160a01b038216820361017057565b81601f820112156101705780359161125c8361119b565b9261126a604051948561117a565b808452602092838086019260051b820101928311610170578301905b828210611294575050505090565b81356001600160a01b0381168103610170578152908301908301611286565b81601f82011215610170578035916112ca8361119b565b926112d8604051948561117a565b808452602092838086019260051b820101928311610170578301905b828210611302575050505090565b813581529083019083016112f4565b60a0600319820112610170576004906001600160401b038235818111610170578261133d9185016111b2565b936024358281116101705783611354918601611245565b93604435838111610170578461136b9183016112b3565b9360643584811161017057816113829184016112b3565b936084359081116101705761139792016112b3565b90565b6001600160401b0381116110e457601f01601f191660200190565b81601f82011215610170578035906113cc8261139a565b926113da604051948561117a565b8284526020838301011161017057816000926020809301838601378301015290565b6060906003190112610170576004356001600160a01b038116810361017057906024359060443590565b81601f820112156101705780359161143d8361119b565b9261144b604051948561117a565b808452602092838086019260051b820101928311610170578301905b828210611475575050505090565b813560ff81168103610170578152908301908301611467565b60026001541461149f576002600155565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b906114ee8261119b565b6114fb604051918261117a565b828152809261150c601f199161119b565b0190602036910137565b60001981146115255760010190565b634e487b7160e01b600052601160045260246000fd5b805182101561154f5760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b60028210156104a05752565b9060405161157e816110c8565b60e081935461159060ff821684611565565b60018060a01b038160081c16602084015260ff8160a81c16604084015263ffffffff821b8160301b16606084015261ffff808260d01c16608085015281831c1660a084015260ff8160f01c1660c084015260f81c1515910152565b5160028110156104a05790565b9190820391821161152557565b9060405161161281611116565b91546001600160a01b038116835260a081901c60ff16602084015260a881901c63ffffffff16604084015260c81c61ffff166060830152565b9081602091031261017057516001600160a01b03811681036101705790565b90816020910312610170575160ff811681036101705790565b604d811161152557600a0a90565b8181029291811591840414171561152557565b61ffff918216908216039190821161152557565b90816020910312610170575180151581036101705790565b60405161174d916001600160a01b03166116e982611144565b6000806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af13d156117d5573d916117318361139a565b9261173f604051948561117a565b83523d60008785013e6117d9565b8051908282159283156117bd575b505050156117665750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b6117cd93508201810191016116b8565b38828161175b565b6060915b9192901561183b57508151156117ed575090565b3b156117f65790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b82519091501561184e5750805190602001fd5b60405162461bcd60e51b815290819061186a906004830161186e565b0390fd5b6020808252825181830181905290939260005b8281106118a357505060409293506000838284010152601f8019910116010190565b818101860151848201604001528501611881565b91939092938254926000938060f81c15600014611bd45750805461ffff966060888360d01c169701968888511690818111600014611a0657505050509061192661190a8787511688855460d01c166116a4565b835461ffff60d01b191660d09190911b61ffff60d01b16178355565b905484518616926001600160a01b0392831692909160081c16823b15611a0257604051637921219560e11b81523060048201526001600160a01b039190911660248201526044810191909152606481019290925260a06084830152600060a48301528290829060c490829084905af180156119f7576119e0575b505060008051602061352f833981519152916119db915b516040805163ffffffff421681529290911661ffff16602083015290918291820190565b0390a2565b6119ea8291611131565b6119f457806119a0565b80fd5b6040513d84823e3d90fd5b8480fd5b14611a2b575b50505050505060008051602061352f833981519152916119db916119b7565b6002811015611bc057611afc5750546001600160a01b038281169160081c16813b15611a0257604051632142170760e11b81523060048201526001600160a01b03919091166024820152604481018490529084908290606490829084905af18015611af1579087939291611ad2575b506119db949260008051602061352f833981519152969492611abb926121d8565b815260086020526040812055918193388080611a0c565b8491929350611ae090611131565b611aed5790859138611a9a565b8280fd5b6040513d86823e3d90fd5b855192949392871691506001600160a01b0380861691823b15611bbc57604051637921219560e11b815230600482015260089190911c919091166001600160a01b0316602482015260448101849052606481019290925260a06084830152600060a48301528390829060c490829084905af18015611bb15760008051602061352f8339815191529694926119db9694928992611b9e575b50611abb92936121d8565b92611bab611abb94611131565b92611b93565b6040513d85823e3d90fd5b8580fd5b634e487b7160e01b86526021600452602486fd5b93979650939150506060935061ffff809481920151169160e01c1601918211611c1a57805461ffff60e01b191660e09290921b61ffff60e01b169190911790559050565b565b634e487b7160e01b84526011600452602484fd5b60405163a9059cbb60e01b60208201526001600160a01b039092166024830152604480830193909352918152611c1891611c6782611116565b6116d0565b6001600160a01b03918216600081815260076020526040902054909392918115611caf57611ca1611c18939261271092611691565b049384916003541690611c2e565b50600093505050565b90815180825260208080930193019160005b828110611cd8575050505090565b835185529381019392810192600101611cca565b9193929092611d0461048a604085015185519061153b565b60028110156104a057611d9857505060608101518151611d40916001600160a01b0391611d309161153b565b511691608081015190519061153b565b5190803b1561017057604051632142170760e11b81526001600160a01b03938416600482015293909216602484015260448301526000908290606490829084905af1801561067257611d8f5750565b611c1890611131565b9092606083015192611db460018060a01b03948592519061153b565b511691823b156101705760206000959186611e10611dfd8295896040519c8d9b8c9a631759616b60e11b8c521660048b015216602489015260a0604489015260a4880190611cb8565b6003199384888303016064890152611cb8565b85810392830160848701525201925af1801561067257611d8f5750565b60405190611e3a826110fa565b8160008152600060208201526101a060609182604082015282808201528260808201528260a08201528260c08201528260e082015282610100820152826101208201528261014082015282610160820152826101808201520152565b9193929093611ea3611e2d565b50604093845192611eb38461115f565b600096878552865193611ec58561115f565b888552875195611ed48761115f565b898752885191611ee38361115f565b8a8352895193611ef28561115f565b8b85528a5199611f018b61115f565b8c8b528b519b611f108d61115f565b8d8d528051809e611f20826110fa565b8152602001600190528d015260608c015260808b015260a08a015260c089015260e08801526101008701526101208601526101408501526101608401526101808301526101a082015290565b8060e01c80158015611ff7575b611fd757611fb99160f01c908361ffff8092169261270f808211611fcf575b808511611fc5575b506127109183611fb09216611691565b94049116611691565b81018091116115255790565b9350612710611fa0565b905080611f98565b60405163e1f2fb3d60e01b815264707269636560d81b6004820152602490fd5b506127108310611f79565b92919061201761201282846115f8565b6114e4565b93815b8381106120275750505050565b8061203561204f928461153b565b5161204961204386846115f8565b8961153b565b52611516565b61201a565b60208101516001600160a01b0316159081156120a2575b811561208b575b5061207957565b604051631c9bac5160e21b8152600490fd5b606001516001600160e01b03191615905038612072565b604081015160ff1615915061206b565b80516001600160a01b0316159081156120f9575b81156120e6575b506120d457565b604051630269a7b160e11b8152600490fd5b63ffffffff9150604001511615386120cd565b602081015160ff161591506120c6565b90604082019063ffffffff8083511682111561214c5761213560ff9260209262015180955116906115f8565b93015116029062ffffff8216918203611525571190565b60405163b009d33760e01b8152600490fd5b6040519061216b82611144565b601082526f47756172643a204e6f2072696768747360801b6020830152565b1561184e5750565b91604051916020830193600160f91b85526bffffffffffffffffffffffff199060601b16602184015260358301526055820152605581526121d281611116565b51902090565b91604051916020830193600160f81b85526bffffffffffffffffffffffff199060601b16602184015260358301526055820152605581526121d281611116565b9081515b602083015181101561263c576060830151835161226c916001600160a01b03916122459161153b565b511661225583608087015161153b565b516122658461010088015161153b565b51916121d8565b606084015184519192916122b3916001600160a01b039161228c9161153b565b511661229c83608088015161153b565b516122ac8461012089015161153b565b5191612192565b9160005260086020526040600020826000526009806020526122d86040600020611605565b93600052602052600060408120556122f76122f282611571565b612054565b612300836120b2565b8251336001600160a01b039091160361262a5761231d4284612109565b6126185761233261048a83604088015161153b565b81549060ff82169060028210156104a05760028110156104a057036125f25761ffff806060860151169160d01c161061214c5761237963ffffffff604085015116426115f8565b9061238381611571565b9160ff60c084015116602060018060a01b03600254169160246040518094819363c6ee427f60e01b835260048301525afa908115610672576000916125c3575b5060405163313ce56760e01b81526020816004816001600160a01b0386165afa80156106725761242f61240960ff612446936201518095600091612594575b5016611683565b61242961ffff60608c0151169163ffffffff60e01b60608b015116611f6c565b90611691565b61244060ff60208b01511682611691565b94611691565b048215801561258c575b61257a576125237fb09142df05002174e1dd0d23b856f0f7df7c6c43a6491a53e7f07c828f4d6939948a6125569961248a856080986115f8565b956124c66124ab6124a46001600160a01b03841689611c6c565b80986115f8565b6020909b01518b906001600160a01b03908116908416611c2e565b868061255b575b5050606082015182516001600160a01b03916124e9919061153b565b51166124fe61048a604085015185519061153b565b9161251c8c610100612513828d89015161153b565b5196015161153b565b51946118b7565b612532866101208b015161153b565b51946040519283526020830152604082015263ffffffff42166060820152a2611516565b61221c565b8251612573926001600160a01b039182169116611c2e565b38866124cd565b6040516326b1e12960e11b8152600490fd5b508015612450565b6125b6915060203d6020116125bc575b6125ae818361117a565b81019061166a565b38612402565b503d6125a4565b6125e5915060203d6020116125eb575b6125dd818361117a565b81019061164b565b386123c3565b503d6125d3565b60405163e1f2fb3d60e01b81526a1b999d14dd185b99185c9960aa1b6004820152602490fd5b604051639d9e029f60e01b8152600490fd5b604051631c962d0560e01b8152600490fd5b509050565b9081515b602083015181101561263c576060830151835161266e916001600160a01b03916122459161153b565b6060840151845191929161268e916001600160a01b039161228c9161153b565b916000526008602052604060002091806000526009806020526126b46040600020611605565b91600052602052600060408120556126ce6122f284611571565b6126d7816120b2565b6126e14282612109565b156128a0576126ef83611571565b9060ff60c083015116906024602060018060a01b03600254166040519283809263c6ee427f60e01b8252600497888301525afa90811561067257600091612881575b5060405163313ce56760e01b8152926020908490816001600160a01b0385165afa9283156106725761287c968960609461280a6127d06127c26127a360ff7fbcc1719994927933c5e16aafb3b01e6ec102fb62f89c2bb8d5832bd00e4619679b61284e99600091612594575016611683565b61242961ffff8b87015116918d8c63ffffffff60e01b91015116611f6c565b60ff60208501511690611691565b986127ee6127e76001600160a01b0389168c611c6c565b809b6115f8565b602090910151909687916001600160a01b039081169116611c2e565b8186015182516001600160a01b0391612823919061153b565b511661283861048a604085015185519061153b565b9161251c8b61010061251382608089015161153b565b61285d856101208a015161153b565b5193604051918252602082015263ffffffff42166040820152a2611516565b612645565b61289a915060203d6020116125eb576125dd818361117a565b38612731565b60405163de50443160e01b8152600490fd5b602091828201916128c961201284518351906115f8565b9381519263ffffffff4216935b8551811015612a6f57606084015184516001600160a01b039182916128fa9161153b565b511661292061290d84608089015161153b565b519161010088019261226586855161153b565b91600092808452600891828852604093848620916129406122f284611571565b33908a61294c85611571565b01511603612a5e5761296461048a88878d015161153b565b91549160ff831690600280831015612a4a57811015612a365703612a115761ffff808360d01c169260e01c168203612a00579060008051602061352f8339815191526129d2888f94846129cb8f976129c56129fb9e9d9c9b9a9951866115f8565b9061153b565b525161153b565b51855163ffffffff8e16815261ffff93909316602084015291604090a283528652812055611516565b6128d6565b8451633c42e29760e11b8152600490fd5b845163e1f2fb3d60e01b81526a1b999d14dd185b99185c9960aa1b6004820152602490fd5b634e487b7160e01b88526021600452602488fd5b634e487b7160e01b89526021600452602489fd5b8451631837e8d760e11b8152600490fd5b5093925050612a8660808201518251845191612002565b9080519251612a9861201285836115f8565b93805b828110612ab457505050611c1893945033903090611cec565b80612acb612ac584612ad9946115f8565b8a61153b565b5161204961204385846115f8565b612a9b565b9081515b602083015181101561263c576060830180518451919291612b62916001600160a01b0391612b0f9161153b565b5116612b5883612b386080890193612b2883865161153b565b51612265846101008d015161153b565b955188516001600160a01b0391612b4f919061153b565b5116925161153b565b5160055491612192565b82600052600860205260406000208160005260096020526040600020612b8a6122f283611571565b612b9381611605565b80516001600160a01b03161580159190613016575b8115613002575b50612ff057612bbd82611571565b60208101516001600160a01b03163314612fc85760ff612be2866101408b015161153b565b5116158015612fad575b612f2657612bff856101608a015161153b565b51158015612f93575b612f6e5760ff604081612c20886101408d015161153b565b51169201511610612f2657612c3c61048a8560408a015161153b565b91549160ff83169060028210156104a05760028110156104a057036125f257612c6a8461016089015161153b565b5161ffff8360e01c1610612f6e5760025460405163c6ee427f60e01b815260f084901c60ff1660048201529290602090849060249082906001600160a01b03165afa92831561067257600093612f4d575b5060405163313ce56760e01b8152906020826004816001600160a01b0388165afa91821561067257612cfe60ff612d3e9461242993600091612594575016611683565b612d2b8b60ff612d238b610140612d1a8261016087015161153b565b5194015161153b565b511690611691565b9260301b6001600160e01b031916611f6c565b8015612f26576040516323b872dd60e01b6020820152336024820152306044820152606480820192909252908152612f2195612f19948994612e9593612d9791612d8960848361117a565b6001600160a01b03166116d0565b61ffff612da98861016088015161153b565b51169060ff612dbd8961014089015161153b565b51169160405192612dcd84611116565b338452602084015263ffffffff421660408401526060830152600052600960205260406000209060018060a01b038151169082549160ff60a01b602083015160a01b1690604083015192606061ffff60c81b91015160c81b169364ffffffffff60d81b1617179063ffffffff60a81b9060a81b161717905561ffff612e578761016087015161153b565b5116906000526008602052612e7960406000209161ffff835460e01c166116a4565b815461ffff60e01b191660e09190911b61ffff60e01b16179055565b612ea48461010084015161153b565b5160055492839263ffffffff60ff612ed58961014061ffff612ecb836101608a015161153b565b511696015161153b565b5116915460a81c1690604051928352602083015260408201527f61e1a1e6f89eaba4ba0119b0023bd32b1bb0412ab96ccd8d0588a3e98a09763160603392a4611516565b600555611516565b612ae2565b60405163e1f2fb3d60e01b81526b3932b73a223ab930ba34b7b760a11b6004820152602490fd5b612f6791935060203d6020116125eb576125dd818361117a565b9138612cbb565b60405163e1f2fb3d60e01b8152691c995b9d105b5bdd5b9d60b21b6004820152602490fd5b5061ffff612fa6866101608b015161153b565b5111612c08565b5060ff80612fc0876101408c015161153b565b511611612bec565b60405163e1f2fb3d60e01b81526c6c656e6465724164647265737360981b6004820152602490fd5b6040516308e15a0360e21b8152600490fd5b63ffffffff91506040015116151538612baf565b602081015160ff1615159150612ba8565b9081515b60208301518110156134f2576130458160a085015161153b565b511580156134d9575b6134b45760ff6130628260c086015161153b565b511615801561349a575b6134705761307e8160e085015161153b565b5160e01c1561344757606083015183516130be916001600160a01b03916130a49161153b565b51166130b483608087015161153b565b51600454916121d8565b908160005260086020526130d56040600020611571565b60208101516001600160a01b03161580159190613436575b811561341e575b5061340c5760ff61310a8261018087015161153b565b5116156133e75761312261048a82604087015161153b565b60028110156104a05761ffff61313c8360a088015161153b565b519115911681806133dc575b6133b65761315d61048a84604089015161153b565b9360ff61316e8560c08a015161153b565b511663ffffffff60e01b6131868660e08b015161153b565b511660ff613199876101808c015161153b565b5116906131ab876101a08c015161153b565b511515926131c5604051996131bf8b6110c8565b8a611565565b3360208a0152604089015260608801528360808801528360a088015260c087015260e0860152600052600860205260406000209184519260028410156104a057805460208701516040880151606089015160808a01516001600160e01b031990941660ff9098169790971760089290921b610100600160a81b03169190911760a89190911b60ff60a81b161760309590951c63ffffffff60b01b169490941760d09490941b61ffff60d01b169390931783556133b1946133a9939060a0810151825461ffff60e01b191660e09190911b61ffff60e01b1617825560c0810151825460e0909201516001600160f01b0390921660f09190911b60ff60f01b161790151560f81b6001600160f81b031916179055606087015187516001600160a01b03916132f1919061153b565b51166133018560808a015161153b565b5191600454938960ff6133188960c084015161153b565b51169163ffffffff60e01b6133318a60e085015161153b565b51166133558a6101a060ff61334b8361018089015161153b565b511695015161153b565b5115159360405195865288602087015260408601526060850152608084015260a083015260c08201527ff8d82fdbb496215c151d63910926322dcb6ba0f8cbd5fa216ef261106bb1ab4660e03392a4611516565b600455611516565b61302b565b60405163e1f2fb3d60e01b81526a17db195b99105b5bdd5b9d60aa1b6004820152602490fd5b506001811415613148565b60405163e1f2fb3d60e01b8152697061796d656e7449785f60b01b6004820152602490fd5b60405163b9a87ba760e01b8152600490fd5b606001516001600160e01b03191615159050386130f4565b604081015160ff16151591506130ed565b60405163e1f2fb3d60e01b81526d6461696c7952656e74507269636560901b6004820152602490fd5b60405163e1f2fb3d60e01b81526e36b0bc2932b73a223ab930ba34b7b760891b6004820152602490fd5b5060ff806134ac8360c087015161153b565b51161161306c565b60405163e1f2fb3d60e01b8152691b195b99105b5bdd5b9d60b21b6004820152602490fd5b5061ffff6134eb8260a086015161153b565b511161304e565b5090611c189060808101516135246135138351926020850193845191612002565b9160a0840151908451905191612002565b9130903390611cec56fefc33a7c95ee492cb6247bd90b28bbe1f459d8f80f3dc65f934cd020cf1e83efba2646970667358221220c275f1e4de3a5e6ad379ae04fda28b286916212950fe140c0e6b057c1807221064736f6c63430008110033