POL Price: $0.368708 (-1.72%)
 

Overview

POL Balance

Polygon PoS Chain LogoPolygon PoS Chain LogoPolygon PoS Chain Logo0 POL

POL Value

$0.00

Token Holdings

Sponsored

Transaction Hash
Method
Block
From
To
Add Self Reputat...409818072023-03-31 14:40:41564 days ago1680273641IN
0xa0a268A0...064dc36Aa
0 POL0.10762435209.26662388
Add Self Reputat...409435412023-03-30 15:26:38565 days ago1680189998IN
0xa0a268A0...064dc36Aa
0 POL0.11635189226.23659535
Add Self Reputat...408994052023-03-29 12:15:45566 days ago1680092145IN
0xa0a268A0...064dc36Aa
0 POL0.08586203166.95158222
Add Reputation408970452023-03-29 10:46:16566 days ago1680086776IN
0xa0a268A0...064dc36Aa
0 POL0.05787709112.49957274
Add Self Reputat...408774482023-03-28 22:18:43566 days ago1680041923IN
0xa0a268A0...064dc36Aa
0 POL0.0430659683.73819029
Add Reputation408716642023-03-28 18:30:49567 days ago1680028249IN
0xa0a268A0...064dc36Aa
0 POL0.06682959129.89811231
Add Reputation408711392023-03-28 18:09:33567 days ago1680026973IN
0xa0a268A0...064dc36Aa
0 POL0.05801168112.76644231
Add Self Reputat...408583342023-03-28 10:02:30567 days ago1679997750IN
0xa0a268A0...064dc36Aa
0 POL0.35024402358.39720397
Add Self Reputat...408582782023-03-28 10:00:00567 days ago1679997600IN
0xa0a268A0...064dc36Aa
0 POL0.14225824276.60933054
Add Self Reputat...408402512023-03-27 22:26:32567 days ago1679955992IN
0xa0a268A0...064dc36Aa
0 POL0.0480708193.46970055
Add Self Reputat...408387472023-03-27 21:27:27567 days ago1679952447IN
0xa0a268A0...064dc36Aa
0 POL0.0479158693.16842685
Add Reputation408359922023-03-27 19:38:25567 days ago1679945905IN
0xa0a268A0...064dc36Aa
0 POL0.050590998.34609451
Add Reputation408359652023-03-27 19:37:27567 days ago1679945847IN
0xa0a268A0...064dc36Aa
0 POL0.05164787100.3867466
Add Reputation408359632023-03-27 19:37:23567 days ago1679945843IN
0xa0a268A0...064dc36Aa
0 POL0.05164787100.3867466
Add Reputation408323842023-03-27 17:15:50568 days ago1679937350IN
0xa0a268A0...064dc36Aa
0 POL0.06222748120.95290397
Add Reputation408323822023-03-27 17:15:44568 days ago1679937344IN
0xa0a268A0...064dc36Aa
0 POL0.06490651126.16901726
Add Self Reputat...408316592023-03-27 16:48:51568 days ago1679935731IN
0xa0a268A0...064dc36Aa
0 POL0.07016545136.43089633
Add Self Reputat...408311662023-03-27 16:30:51568 days ago1679934651IN
0xa0a268A0...064dc36Aa
0 POL0.47051771166.31198067
Add Self Reputat...408308762023-03-27 16:20:03568 days ago1679934003IN
0xa0a268A0...064dc36Aa
0 POL0.26140108267.48612598
Add Reputation408018022023-03-26 21:25:04568 days ago1679865904IN
0xa0a268A0...064dc36Aa
0 POL0.0317851361.78576616
Add Reputation408016592023-03-26 21:19:28568 days ago1679865568IN
0xa0a268A0...064dc36Aa
0 POL0.0476951692.7082731
Add Reputation408001842023-03-26 20:18:40568 days ago1679861920IN
0xa0a268A0...064dc36Aa
0 POL0.06667881125.44717856
Create Profile407959682023-03-26 17:20:36569 days ago1679851236IN
0xa0a268A0...064dc36Aa
0 POL0.02766767183.53718306
Add Self Reputat...407930332023-03-26 15:08:12569 days ago1679843292IN
0xa0a268A0...064dc36Aa
0 POL0.26150492508.47459269
Add Self Reputat...407911012023-03-26 13:25:54569 days ago1679837154IN
0xa0a268A0...064dc36Aa
0 POL0.54208329535.94617254
View all transactions

Latest 1 internal transaction

Parent Transaction Hash Block From To
407892002023-03-26 11:55:08569 days ago1679831708
0xa0a268A0...064dc36Aa
 Contract Creation0 POL
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Profile

Compiler Version
v0.8.18+commit.87f61d96

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 18 : Profile.sol
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.1;

import "@rmrk-team/evm-contracts/contracts/RMRK/nestable/RMRKNestable.sol";
import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import {Ownable} from "./utils/Ownable.sol";
import {Reputation} from "./Reputation.sol";


contract Profile is RMRKNestable, Ownable {
    struct ProfileDetails {
        uint256 parentId;
        bytes32 twitterUidHash;
        bool isOrg;
        mapping(string reputationTokenId => bool) selfRep;
    }

   //  address constant ENS_ADDRESS = 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e;

    bytes32 public merkleRoot; // merkle root of whitelisted users
    uint256 public tokenCounter;
    Reputation public immutable reputationContract;
   //  ENS internal ens;

    mapping(address profile => ProfileDetails profileDetails) public profiles;
    mapping(bytes32 twitterUidHash => bool exists) internal twitterUidHashes;

    event MerkleRootUpdated(bytes32 merkleRoot);
    event ProfileCreated(address indexed owner, uint256 indexed tokenId);
    event OrganizationApproved(uint256 indexed profileId);

    error InvalidOrganization(address profile);
    error ProfileAlreadyExists(address profile);
    error TwitterProfileAlreadyConnected(bytes32 twitterUidHash);

    constructor(
        string memory name,
        string memory symbol,
        address multisigOwner
        // bytes32 _merkleRoot
    ) RMRKNestable(name, symbol) Ownable(multisigOwner, address(0)) {
        // merkleRoot = _merkleRoot;
        tokenCounter = 1;  // Id cannot be zero, because of https://github.com/rmrk-team/evm/blob/dev/contracts/RMRK/nestable/RMRKNestable.sol#LL512-L512C56
        reputationContract = new Reputation("BLSM Reputation", "BLSMR", address(this));
    }

    function createProfile(
        bytes32 twitterUidHash,
        bytes32 whitelistedOrgLeaf,
        bytes32[] memory proof
    ) external {
        if (merkleRoot != "" && !MerkleProof.verify(proof, merkleRoot, whitelistedOrgLeaf)) {
            revert InvalidOrganization(msg.sender);
        }
 
        if(profiles[msg.sender].twitterUidHash != 0x0) {
            revert ProfileAlreadyExists(msg.sender);
        }

        if(twitterUidHashes[twitterUidHash]) {
          revert TwitterProfileAlreadyConnected(twitterUidHash);
        }

        _safeMint(msg.sender, tokenCounter, "0x0"); // we could pass additional data here
        profiles[msg.sender].parentId = tokenCounter;
        profiles[msg.sender].twitterUidHash = twitterUidHash;
        twitterUidHashes[twitterUidHash] = true;

        emit ProfileCreated(msg.sender, tokenCounter);
        unchecked {
            tokenCounter++;
        }
    }

    function getReputations(address profile) external view returns (Child[] memory) {
        return childrenOf(profiles[profile].parentId);
    }

    function addSelfReputations(string[] memory reputationTokenIds) external {
        uint256 parentId = profiles[msg.sender].parentId;
        require(parentId != 0, "Profile does not exist");
        for(uint256 i = 0; i < reputationTokenIds.length; i++) {
            reputationContract.giveCred(parentId, parentId, reputationTokenIds[i], '');
            Child[] memory children = pendingChildrenOf(parentId);
            Child memory lastChild = children[children.length - 1];
            _acceptChild(parentId, children.length - 1, lastChild.contractAddress, lastChild.tokenId);
            profiles[msg.sender].selfRep[reputationTokenIds[i]] = true;
        }
    }

    function addReputation(address receiver, string memory reputationTokenIds, string memory message) external {
        uint256 senderId = profiles[msg.sender].parentId;
        require(senderId != 0, "Sender does not exist");
        uint256 receiverId = profiles[receiver].parentId;
        require(receiverId != 0, "Receiver does not exist");

        reputationContract.giveCred(senderId, receiverId, reputationTokenIds, message);
        Child[] memory children = pendingChildrenOf(receiverId);
        Child memory lastChild = children[children.length - 1];
        _acceptChild(receiverId, children.length - 1, lastChild.contractAddress, lastChild.tokenId);
    }

    function displayReputationInProfile(uint256 childIndex, uint256 childId) external { 
        _acceptChild(profiles[msg.sender].parentId, childIndex, address(reputationContract), childId);
    }

    function hideReputationFromProfile(string memory reputationTokenId) external {
        profiles[msg.sender].selfRep[reputationTokenId] = false;
    }

    function approveOrganization(uint256 profileId) external onlyOwner {
      (address directOwner,,) = directOwnerOf(profileId);

      profiles[directOwner].isOrg = true;

      emit OrganizationApproved(profileId);
    }

    function setMerkleRoot(bytes32 _merkleRoot) external onlyOwner {
        merkleRoot = _merkleRoot;

        emit MerkleRootUpdated(_merkleRoot);
    }

    // // helper function
    // function computeNamehash(string memory name) public pure returns (bytes32 namehash) {
    //     namehash = 0x0000000000000000000000000000000000000000000000000000000000000000;
    //     namehash = keccak256(abi.encodePacked(namehash, keccak256(abi.encodePacked("eth"))));
    //     namehash = keccak256(abi.encodePacked(namehash, keccak256(abi.encodePacked(name))));
    // }

    // helper function
    // Today, Twitter IDs are unique 64-bit unsigned integers,
    function hashTwitterUid(uint64 twitterUid) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(twitterUid));
    }

    function _beforeNestedTokenTransfer(
        address from,
        address to,
        uint256 fromTokenId,
        uint256 toTokenId,
        uint256 tokenId,
        bytes memory data
    ) internal pure override {
        require(from == address(0) || to == address(0), "Soulbound token");
    }

    // ovo isto treba i za _beforeBurn da se preventuje burning
}

File 2 of 18 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

File 3 of 18 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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);
}

File 4 of 18 : 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);
}

File 5 of 18 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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
     * ====
     *
     * [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://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (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);
        }
    }
}

File 6 of 18 : Base64.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides a set of functions to operate with Base64 strings.
 *
 * _Available since v4.5._
 */
library Base64 {
    /**
     * @dev Base64 Encoding/Decoding Table
     */
    string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    /**
     * @dev Converts a `bytes` to its Bytes64 `string` representation.
     */
    function encode(bytes memory data) internal pure returns (string memory) {
        /**
         * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
         * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
         */
        if (data.length == 0) return "";

        // Loads the table into memory
        string memory table = _TABLE;

        // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
        // and split into 4 numbers of 6 bits.
        // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
        // - `data.length + 2`  -> Round up
        // - `/ 3`              -> Number of 3-bytes chunks
        // - `4 *`              -> 4 characters for each chunk
        string memory result = new string(4 * ((data.length + 2) / 3));

        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the lookup table (skip the first "length" byte)
            let tablePtr := add(table, 1)

            // Prepare result pointer, jump over length
            let resultPtr := add(result, 32)

            // Run over the input, 3 bytes at a time
            for {
                let dataPtr := data
                let endPtr := add(data, mload(data))
            } lt(dataPtr, endPtr) {

            } {
                // Advance 3 bytes
                dataPtr := add(dataPtr, 3)
                let input := mload(dataPtr)

                // To write each character, shift the 3 bytes (18 bits) chunk
                // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                // and apply logical AND with 0x3F which is the number of
                // the previous character in the ASCII table prior to the Base64 Table
                // The result is then added to the table to get the character to write,
                // and finally write it in the result pointer but with a left shift
                // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits

                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance
            }

            // When data `bytes` is not exactly 3 bytes long
            // it is padded with `=` characters at the end
            switch mod(mload(data), 3)
            case 1 {
                mstore8(sub(resultPtr, 1), 0x3d)
                mstore8(sub(resultPtr, 2), 0x3d)
            }
            case 2 {
                mstore8(sub(resultPtr, 1), 0x3d)
            }
        }

        return result;
    }
}

File 7 of 18 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (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;
    }
}

File 8 of 18 : MerkleProof.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.0;

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The tree and the proofs can be generated using our
 * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
 * You will find a quickstart guide in the readme.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the merkle tree could be reinterpreted as a leaf value.
 * OpenZeppelin's JavaScript library generates merkle trees that are safe
 * against this attack out of the box.
 */
library MerkleProof {
    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Calldata version of {verify}
     *
     * _Available since v4.7._
     */
    function verifyCalldata(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProofCalldata(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     *
     * _Available since v4.4._
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Calldata version of {processProof}
     *
     * _Available since v4.7._
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Calldata version of {multiProofVerify}
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * _Available since v4.7._
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            return hashes[totalHashes - 1];
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Calldata version of {processMultiProof}.
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            return hashes[totalHashes - 1];
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

File 9 of 18 : 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);
}

File 10 of 18 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

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

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

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

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

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

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

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

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

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

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 11 of 18 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

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

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

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

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

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

File 12 of 18 : IRMRKCore.sol
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

/**
 * @title IRMRKCore
 * @author RMRK team
 * @notice Interface smart contract for RMRK core module.
 */
interface IRMRKCore {
    /**
     * @notice Used to retrieve the collection name.
     * @return Name of the collection
     */
    function name() external view returns (string memory);

    /**
     * @notice Used to retrieve the collection symbol.
     * @return Symbol of the collection
     */
    function symbol() external view returns (string memory);
}

File 13 of 18 : RMRKCore.sol
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

import "./IRMRKCore.sol";

/**
 * @title RMRKCore
 * @author RMRK team
 * @notice Smart contract of the RMRK core module.
 * @dev This is currently just a passthrough contract which allows for granular editing of base-level ERC721 functions.
 */
contract RMRKCore is IRMRKCore {
    /**
     * @notice Version of the @rmrk-team/evm-contracts package
     * @return Version identifier of the smart contract
     */
    string public constant VERSION = "0.27.1";

    /**
     * @notice Used to initialize the smart contract.
     * @param name_ Name of the token collection
     * @param symbol_ Symbol of the token collection
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /// Token name
    string private _name;

    /// Token symbol
    string private _symbol;

    /**
     * @notice Used to retrieve the collection name.
     * @return Name of the collection
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @notice Used to retrieve the collection symbol.
     * @return Symbol of the collection
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @notice Hook that is called before any token transfer. This includes minting and burning.
     * @dev Calling conditions:
     *
     *  - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be transferred to `to`.
     *  - When `from` is zero, `tokenId` will be minted to `to`.
     *  - When `to` is zero, ``from``'s `tokenId` will be burned.
     *  - `from` and `to` are never zero at the same time.
     *
     *  To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param from Address from which the token is being transferred
     * @param to Address to which the token is being transferred
     * @param tokenId ID of the token being transferred
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}

    /**
     * @notice Hook that is called after any transfer of tokens. This includes minting and burning.
     * @dev Calling conditions:
     *
     *  - When `from` and `to` are both non-zero.
     *  - `from` and `to` are never zero at the same time.
     *
     *  To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param from Address from which the token has been transferred
     * @param to Address to which the token has been transferred
     * @param tokenId ID of the token that has been transferred
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}
}

File 14 of 18 : RMRKErrors.sol
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

/// @title RMRKErrors
/// @author RMRK team
/// @notice A collection of errors used in the RMRK suite
/// @dev Errors are kept in a centralised file in order to provide a central point of reference and to avoid error
///  naming collisions due to inheritance

/// Attempting to grant the token to 0x0 address
error ERC721AddressZeroIsNotaValidOwner();
/// Attempting to grant approval to the current owner of the token
error ERC721ApprovalToCurrentOwner();
/// Attempting to grant approval when not being owner or approved for all should not be permitted
error ERC721ApproveCallerIsNotOwnerNorApprovedForAll();
/// Attempting to get approvals for a token owned by 0x0 (considered non-existent)
error ERC721ApprovedQueryForNonexistentToken();
/// Attempting to grant approval to self
error ERC721ApproveToCaller();
/// Attempting to use an invalid token ID
error ERC721InvalidTokenId();
/// Attempting to mint to 0x0 address
error ERC721MintToTheZeroAddress();
/// Attempting to manage a token without being its owner or approved by the owner
error ERC721NotApprovedOrOwner();
/// Attempting to mint an already minted token
error ERC721TokenAlreadyMinted();
/// Attempting to transfer the token from an address that is not the owner
error ERC721TransferFromIncorrectOwner();
/// Attempting to safe transfer to an address that is unable to receive the token
error ERC721TransferToNonReceiverImplementer();
/// Attempting to transfer the token to a 0x0 address
error ERC721TransferToTheZeroAddress();
/// Attempting to grant approval of assets to their current owner
error RMRKApprovalForAssetsToCurrentOwner();
/// Attempting to grant approval of assets without being the caller or approved for all
error RMRKApproveForAssetsCallerIsNotOwnerNorApprovedForAll();
/// Attempting to incorrectly configue a Catalog item
error RMRKBadConfig();
/// Attempting to set the priorities with an array of length that doesn't match the length of active assets array
error RMRKBadPriorityListLength();
/// Attempting to add an asset entry with `Part`s, without setting the `Catalog` address
error RMRKCatalogRequiredForParts();
/// Attempting to transfer a soulbound (non-transferrable) token
error RMRKCannotTransferSoulbound();
/// Attempting to accept a child that has already been accepted
error RMRKChildAlreadyExists();
/// Attempting to interact with a child, using index that is higher than the number of children
error RMRKChildIndexOutOfRange();
/// Attempting to find the index of a child token on a parent which does not own it.
error RMRKChildNotFoundInParent();
/// Attempting to equip a `Part` with a child not approved by the Catalog
error RMRKEquippableEquipNotAllowedByCatalog();
/// Attempting to use ID 0, which is not supported
/// @dev The ID 0 in RMRK suite is reserved for empty values. Guarding against its use ensures the expected operation
error RMRKIdZeroForbidden();
/// Attempting to interact with an asset, using index greater than number of assets
error RMRKIndexOutOfRange();
/// Attempting to reclaim a child that can't be reclaimed
error RMRKInvalidChildReclaim();
/// Attempting to interact with an end-user account when the contract account is expected
error RMRKIsNotContract();
/// Attempting to interact with a contract that had its operation locked
error RMRKLocked();
/// Attempting to add a pending child after the number of pending children has reached the limit (default limit is 128)
error RMRKMaxPendingChildrenReached();
/// Attempting to add a pending asset after the number of pending assets has reached the limit (default limit is
///  128)
error RMRKMaxPendingAssetsReached();
/// Attempting to burn a total number of recursive children higher than maximum set
/// @param childContract Address of the collection smart contract in which the maximum number of recursive burns was reached
/// @param childId ID of the child token at which the maximum number of recursive burns was reached
error RMRKMaxRecursiveBurnsReached(address childContract, uint256 childId);
/// Attempting to mint a number of tokens that would cause the total supply to be greater than maximum supply
error RMRKMintOverMax();
/// Attempting to mint a nested token to a smart contract that doesn't support nesting
error RMRKMintToNonRMRKNestableImplementer();
/// Attempting to transfer a child before it is unequipped
error RMRKMustUnequipFirst();
/// Attempting to nest a child over the nestable limit (current limit is 100 levels of nesting)
error RMRKNestableTooDeep();
/// Attempting to nest the token to own descendant, which would create a loop and leave the looped tokens in limbo
error RMRKNestableTransferToDescendant();
/// Attempting to nest the token to a smart contract that doesn't support nesting
error RMRKNestableTransferToNonRMRKNestableImplementer();
/// Attempting to nest the token into itself
error RMRKNestableTransferToSelf();
/// Attempting to interact with an asset that can not be found
error RMRKNoAssetMatchingId();
/// Attempting to manage an asset without owning it or having been granted permission by the owner to do so
error RMRKNotApprovedForAssetsOrOwner();
/// Attempting to interact with a token without being its owner or having been granted permission by the
///  owner to do so
/// @dev When a token is nested, only the direct owner (NFT parent) can mange it. In that case, approved addresses are
///  not allowed to manage it, in order to ensure the expected behaviour
error RMRKNotApprovedOrDirectOwner();
/// Attempting to compose an asset wihtout having an associated Catalog
error RMRKNotComposableAsset();
/// Attempting to unequip an item that isn't equipped
error RMRKNotEquipped();
/// Attempting to interact with a management function without being the smart contract's owner
error RMRKNotOwner();
/// Attempting to interact with a function without being the owner or contributor of the collection
error RMRKNotOwnerOrContributor();
/// Attempting to transfer the ownership to the 0x0 address
error RMRKNewOwnerIsZeroAddress();
/// Attempting to assign a 0x0 address as a contributor
error RMRKNewContributorIsZeroAddress();
/// Attempting an operation requiring the token being nested, while it is not
error RMRKParentIsNotNFT();
/// Attempting to add a `Part` with an ID that is already used
error RMRKPartAlreadyExists();
/// Attempting to use a `Part` that doesn't exist
error RMRKPartDoesNotExist();
/// Attempting to use a `Part` that is `Fixed` when `Slot` kind of `Part` should be used
error RMRKPartIsNotSlot();
/// Attempting to interact with a pending child using an index greater than the size of pending array
error RMRKPendingChildIndexOutOfRange();
/// Attempting to add an asset using an ID that has already been used
error RMRKAssetAlreadyExists();
/// Attempting to equip an item into a slot that already has an item equipped
error RMRKSlotAlreadyUsed();
/// Attempting to equip an item into a `Slot` that the target asset does not implement
error RMRKTargetAssetCannotReceiveSlot();
/// Attempting to equip a child into a `Slot` and parent that the child's collection doesn't support
error RMRKTokenCannotBeEquippedWithAssetIntoSlot();
/// Attempting to compose a NFT of a token without active assets
error RMRKTokenDoesNotHaveAsset();
/// Attempting to determine the asset with the top priority on a token without assets
error RMRKTokenHasNoAssets();
/// Attempting to accept or transfer a child which does not match the one at the specified index
error RMRKUnexpectedChildId();
/// Attempting to reject all pending assets but more assets than expected are pending
error RMRKUnexpectedNumberOfAssets();
/// Attempting to reject all pending children but children assets than expected are pending
error RMRKUnexpectedNumberOfChildren();
/// Attempting to accept or reject an asset which does not match the one at the specified index
error RMRKUnexpectedAssetId();
/// Attempting an operation expecting a parent to the token which is not the actual one
error RMRKUnexpectedParent();
/// Attempting not to pass an empty array of equippable addresses when adding or setting the equippable addresses
error RMRKZeroLengthIdsPassed();
/// Attempting to set the royalties to a value higher than 100% (10000 in base points)
error RMRKRoyaltiesTooHigh();

File 15 of 18 : IRMRKNestable.sol
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.18;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/**
 * @title IRMRKNestable
 * @author RMRK team
 * @notice Interface smart contract of the RMRK nestable module.
 */
interface IRMRKNestable is IERC165 {
    /**
     * @notice The core struct of RMRK ownership.
     * @dev The `DirectOwner` struct is used to store information of the next immediate owner, be it the parent token or
     *  the externally owned account.
     * @dev If the token is owned by the externally owned account, the `tokenId` should equal `0`.
     * @param tokenId ID of the parent token
     * @param ownerAddress Address of the owner of the token. If the owner is another token, then the address should be
     *  the one of the parent token's collection smart contract. If the owner is externally owned account, the address
     *  should be the address of this account
     * @param isNft A boolean value signifying whether the token is owned by another token (`true`) or by an externally
     *  owned account (`false`)
     */
    struct DirectOwner {
        uint256 tokenId;
        address ownerAddress;
        bool isNft;
    }

    /**
     * @notice Used to notify listeners that the token is being transferred.
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     * @param from Address of the previous immediate owner, which is a smart contract if the token was nested.
     * @param to Address of the new immediate owner, which is a smart contract if the token is being nested.
     * @param fromTokenId ID of the previous parent token. If the token was not nested before, the value should be `0`
     * @param toTokenId ID of the new parent token. If the token is not being nested, the value should be `0`
     * @param tokenId ID of the token being transferred
     */
    event NestTransfer(
        address indexed from,
        address indexed to,
        uint256 fromTokenId,
        uint256 toTokenId,
        uint256 indexed tokenId
    );

    /**
     * @notice Used to notify listeners that a new token has been added to a given token's pending children array.
     * @dev Emitted when a child NFT is added to a token's pending array.
     * @param tokenId ID of the token that received a new pending child token
     * @param childIndex Index of the proposed child token in the parent token's pending children array
     * @param childAddress Address of the proposed child token's collection smart contract
     * @param childId ID of the child token in the child token's collection smart contract
     */
    event ChildProposed(
        uint256 indexed tokenId,
        uint256 childIndex,
        address indexed childAddress,
        uint256 indexed childId
    );

    /**
     * @notice Used to notify listeners that a new child token was accepted by the parent token.
     * @dev Emitted when a parent token accepts a token from its pending array, migrating it to the active array.
     * @param tokenId ID of the token that accepted a new child token
     * @param childIndex Index of the newly accepted child token in the parent token's active children array
     * @param childAddress Address of the child token's collection smart contract
     * @param childId ID of the child token in the child token's collection smart contract
     */
    event ChildAccepted(
        uint256 indexed tokenId,
        uint256 childIndex,
        address indexed childAddress,
        uint256 indexed childId
    );

    /**
     * @notice Used to notify listeners that all pending child tokens of a given token have been rejected.
     * @dev Emitted when a token removes all a child tokens from its pending array.
     * @param tokenId ID of the token that rejected all of the pending children
     */
    event AllChildrenRejected(uint256 indexed tokenId);

    /**
     * @notice Used to notify listeners a child token has been transferred from parent token.
     * @dev Emitted when a token transfers a child from itself, transferring ownership to the root owner.
     * @param tokenId ID of the token that transferred a child token
     * @param childIndex Index of a child in the array from which it is being transferred
     * @param childAddress Address of the child token's collection smart contract
     * @param childId ID of the child token in the child token's collection smart contract
     * @param fromPending A boolean value signifying whether the token was in the pending child tokens array (`true`) or
     *  in the active child tokens array (`false`)
     * @param toZero A boolean value signifying whether the token is being transferred to the `0x0` address (`true`) or
     *  not (`false`)
     */
    event ChildTransferred(
        uint256 indexed tokenId,
        uint256 childIndex,
        address indexed childAddress,
        uint256 indexed childId,
        bool fromPending,
        bool toZero
    );

    /**
     * @notice The core child token struct, holding the information about the child tokens.
     * @return tokenId ID of the child token in the child token's collection smart contract
     * @return contractAddress Address of the child token's smart contract
     */
    struct Child {
        uint256 tokenId;
        address contractAddress;
    }

    /**
     * @notice Used to retrieve the *root* owner of a given token.
     * @dev The *root* owner of the token is an externally owned account (EOA). If the given token is child of another
     *  NFT, this will return an EOA address. Otherwise, if the token is owned by an EOA, this EOA wil be returned.
     * @param tokenId ID of the token for which the *root* owner has been retrieved
     * @return owner The *root* owner of the token
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @notice Used to retrieve the immediate owner of the given token.
     * @dev If the immediate owner is another token, the address returned, should be the one of the parent token's
     *  collection smart contract.
     * @param tokenId ID of the token for which the RMRK owner is being retrieved
     * @return Address of the given token's owner
     * @return The ID of the parent token. Should be `0` if the owner is an externally owned account
     * @return The boolean value signifying whether the owner is an NFT or not
     */
    function directOwnerOf(
        uint256 tokenId
    ) external view returns (address, uint256, bool);

    /**
     * @notice Used to burn a given token.
     * @dev When a token is burned, all of its child tokens are recursively burned as well.
     * @dev When specifying the maximum recursive burns, the execution will be reverted if there are more children to be
     *  burned.
     * @dev Setting the `maxRecursiveBurn` value to 0 will only attempt to burn the specified token and revert if there
     *  are any child tokens present.
     * @dev The approvals are cleared when the token is burned.
     * @dev Requirements:
     *
     *  - `tokenId` must exist.
     * @dev Emits a {Transfer} event.
     * @param tokenId ID of the token to burn
     * @param maxRecursiveBurns Maximum number of tokens to recursively burn
     * @return Number of recursively burned children
     */
    function burn(
        uint256 tokenId,
        uint256 maxRecursiveBurns
    ) external returns (uint256);

    /**
     * @notice Used to add a child token to a given parent token.
     * @dev This adds the child token into the given parent token's pending child tokens array.
     * @dev Requirements:
     *
     *  - `directOwnerOf` on the child contract must resolve to the called contract.
     *  - the pending array of the parent contract must not be full.
     * @param parentId ID of the parent token to receive the new child token
     * @param childId ID of the new proposed child token
     * @param data Additional data with no specified format
     */
    function addChild(
        uint256 parentId,
        uint256 childId,
        bytes memory data
    ) external;

    /**
     * @notice Used to accept a pending child token for a given parent token.
     * @dev This moves the child token from parent token's pending child tokens array into the active child tokens
     *  array.
     * @param parentId ID of the parent token for which the child token is being accepted
     * @param childIndex Index of a child tokem in the given parent's pending children array
     * @param childAddress Address of the collection smart contract of the child token expected to be located at the
     *  specified index of the given parent token's pending children array
     * @param childId ID of the child token expected to be located at the specified index of the given parent token's
     *  pending children array
     */
    function acceptChild(
        uint256 parentId,
        uint256 childIndex,
        address childAddress,
        uint256 childId
    ) external;

    /**
     * @notice Used to reject all pending children of a given parent token.
     * @dev Removes the children from the pending array mapping.
     * @dev This does not update the ownership storage data on children. If necessary, ownership can be reclaimed by the
     *  rootOwner of the previous parent.
     * @dev Requirements:
     *
     * Requirements:
     *
     * - `parentId` must exist
     * @param parentId ID of the parent token for which to reject all of the pending tokens.
     * @param maxRejections Maximum number of expected children to reject, used to prevent from rejecting children which
     *  arrive just before this operation.
     */
    function rejectAllChildren(
        uint256 parentId,
        uint256 maxRejections
    ) external;

    /**
     * @notice Used to transfer a child token from a given parent token.
     * @dev When transferring a child token, the owner of the token is set to `to`, or is not updated in the event of
     *  `to` being the `0x0` address.
     * @param tokenId ID of the parent token from which the child token is being transferred
     * @param to Address to which to transfer the token to
     * @param destinationId ID of the token to receive this child token (MUST be 0 if the destination is not a token)
     * @param childIndex Index of a token we are transferring, in the array it belongs to (can be either active array or
     *  pending array)
     * @param childAddress Address of the child token's collection smart contract.
     * @param childId ID of the child token in its own collection smart contract.
     * @param isPending A boolean value indicating whether the child token being transferred is in the pending array of
     *  the parent token (`true`) or in the active array (`false`)
     * @param data Additional data with no specified format, sent in call to `_to`
     */
    function transferChild(
        uint256 tokenId,
        address to,
        uint256 destinationId,
        uint256 childIndex,
        address childAddress,
        uint256 childId,
        bool isPending,
        bytes memory data
    ) external;

    /**
     * @notice Used to retrieve the active child tokens of a given parent token.
     * @dev Returns array of Child structs existing for parent token.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @param parentId ID of the parent token for which to retrieve the active child tokens
     * @return An array of Child structs containing the parent token's active child tokens
     */
    function childrenOf(
        uint256 parentId
    ) external view returns (Child[] memory);

    /**
     * @notice Used to retrieve the pending child tokens of a given parent token.
     * @dev Returns array of pending Child structs existing for given parent.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @param parentId ID of the parent token for which to retrieve the pending child tokens
     * @return An array of Child structs containing the parent token's pending child tokens
     */
    function pendingChildrenOf(
        uint256 parentId
    ) external view returns (Child[] memory);

    /**
     * @notice Used to retrieve a specific active child token for a given parent token.
     * @dev Returns a single Child struct locating at `index` of parent token's active child tokens array.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @param parentId ID of the parent token for which the child is being retrieved
     * @param index Index of the child token in the parent token's active child tokens array
     * @return A Child struct containing data about the specified child
     */
    function childOf(
        uint256 parentId,
        uint256 index
    ) external view returns (Child memory);

    /**
     * @notice Used to retrieve a specific pending child token from a given parent token.
     * @dev Returns a single Child struct locating at `index` of parent token's active child tokens array.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @param parentId ID of the parent token for which the pending child token is being retrieved
     * @param index Index of the child token in the parent token's pending child tokens array
     * @return A Child struct containting data about the specified child
     */
    function pendingChildOf(
        uint256 parentId,
        uint256 index
    ) external view returns (Child memory);

    /**
     * @notice Used to transfer the token into another token.
     * @param from Address of the direct owner of the token to be transferred
     * @param to Address of the receiving token's collection smart contract
     * @param tokenId ID of the token being transferred
     * @param destinationId ID of the token to receive the token being transferred
     * @param data Additional data with no specified format, sent in the addChild call
     */
    function nestTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        uint256 destinationId,
        bytes memory data
    ) external;
}

File 16 of 18 : RMRKNestable.sol
// SPDX-License-Identifier: Apache-2.0

//Generally all interactions should propagate downstream

pragma solidity ^0.8.18;

import "./IRMRKNestable.sol";
import "../core/RMRKCore.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "../library/RMRKErrors.sol";

/**
 * @title RMRKNestable
 * @author RMRK team
 * @notice Smart contract of the RMRK Nestable module.
 * @dev This contract is hierarchy agnostic and can support an arbitrary number of nested levels up and down, as long as
 *  gas limits allow it.
 */
contract RMRKNestable is Context, IERC165, IERC721, IRMRKNestable, RMRKCore {
    using Address for address;

    uint256 private constant _MAX_LEVELS_TO_CHECK_FOR_INHERITANCE_LOOP = 100;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approver address to approved address
    // The approver is necessary so approvals are invalidated for nested children on transfer
    // WARNING: If a child NFT returns to a previous root owner, old permissions would be active again
    mapping(uint256 => mapping(address => address)) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // ------------------- NESTABLE --------------

    // Mapping from token ID to DirectOwner struct
    mapping(uint256 => DirectOwner) private _RMRKOwners;

    // Mapping of tokenId to array of active children structs
    mapping(uint256 => Child[]) internal _activeChildren;

    // Mapping of tokenId to array of pending children structs
    mapping(uint256 => Child[]) internal _pendingChildren;

    // Mapping of child token address to child token ID to whether they are pending or active on any token
    // We might have a first extra mapping from token ID, but since the same child cannot be nested into multiple tokens
    //  we can strip it for size/gas savings.
    mapping(address => mapping(uint256 => uint256)) private _childIsInActive;

    // -------------------------- MODIFIERS ----------------------------

    /**
     * @notice Used to verify that the caller is either the owner of the token or approved to manage it by its owner.
     * @dev If the caller is not the owner of the token or approved to manage it by its owner, the execution will be
     *  reverted.
     * @param tokenId ID of the token to check
     */
    function _onlyApprovedOrOwner(uint256 tokenId) private view {
        if (!_isApprovedOrOwner(_msgSender(), tokenId))
            revert ERC721NotApprovedOrOwner();
    }

    /**
     * @notice Used to verify that the caller is either the owner of the token or approved to manage it by its owner.
     * @param tokenId ID of the token to check
     */
    modifier onlyApprovedOrOwner(uint256 tokenId) {
        _onlyApprovedOrOwner(tokenId);
        _;
    }

    /**
     * @notice Used to verify that the caller is approved to manage the given token or it its direct owner.
     * @dev This does not delegate to ownerOf, which returns the root owner, but rater uses an owner from DirectOwner
     *  struct.
     * @dev The execution is reverted if the caller is not immediate owner or approved to manage the given token.
     * @dev Used for parent-scoped transfers.
     * @param tokenId ID of the token to check.
     */
    function _onlyApprovedOrDirectOwner(uint256 tokenId) private view {
        if (!_isApprovedOrDirectOwner(_msgSender(), tokenId))
            revert RMRKNotApprovedOrDirectOwner();
    }

    /**
     * @notice Used to verify that the caller is approved to manage the given token or is its direct owner.
     * @param tokenId ID of the token to check
     */
    modifier onlyApprovedOrDirectOwner(uint256 tokenId) {
        _onlyApprovedOrDirectOwner(tokenId);
        _;
    }

    // ----------------------------- CONSTRUCTOR ------------------------------

    /**
     * @notice Initializes the contract by setting a `name` and a `symbol` to the token collection.
     * @param name_ Name of the token collection
     * @param symbol_ Symbol of the token collection
     */
    constructor(
        string memory name_,
        string memory symbol_
    ) RMRKCore(name_, symbol_) {}

    // ------------------------------- ERC721 ---------------------------------
    /**
     * @inheritdoc IERC165
     */
    function supportsInterface(
        bytes4 interfaceId
    ) public view virtual returns (bool) {
        return
            interfaceId == type(IERC165).interfaceId ||
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            interfaceId == type(IRMRKNestable).interfaceId;
    }

    /**
     * @notice Used to retrieve the number of tokens in `owner`'s account.
     * @param owner Address of the account being checked
     * @return The balance of the given account
     */
    function balanceOf(address owner) public view virtual returns (uint256) {
        if (owner == address(0)) revert ERC721AddressZeroIsNotaValidOwner();
        return _balances[owner];
    }

    ////////////////////////////////////////
    //              TRANSFERS
    ////////////////////////////////////////

    /**
     * @notice Transfers a given token from `from` to `to`.
     * @dev 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}.
     * @dev Emits a {Transfer} event.
     * @param from Address from which to transfer the token from
     * @param to Address to which to transfer the token to
     * @param tokenId ID of the token to transfer
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual onlyApprovedOrDirectOwner(tokenId) {
        _transfer(from, to, tokenId, "");
    }

    /**
     * @notice Used to safely transfer a given token token from `from` to `to`.
     * @dev 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.
     * @dev Emits a {Transfer} event.
     * @param from Address to transfer the tokens from
     * @param to Address to transfer the tokens to
     * @param tokenId ID of the token to transfer
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @notice Used to safely transfer a given token token from `from` to `to`.
     * @dev 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.
     * @dev Emits a {Transfer} event.
     * @param from Address to transfer the tokens from
     * @param to Address to transfer the tokens to
     * @param tokenId ID of the token to transfer
     * @param data Additional data without a specified format to be sent along with the token transaction
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) public virtual onlyApprovedOrDirectOwner(tokenId) {
        _safeTransfer(from, to, tokenId, data);
    }

    /**
     * @inheritdoc IRMRKNestable
     */
    function nestTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        uint256 destinationId,
        bytes memory data
    ) public virtual onlyApprovedOrDirectOwner(tokenId) {
        _nestTransfer(from, to, tokenId, destinationId, data);
    }

    /**
     * @notice Used to safely transfer the token form `from` to `to`.
     * @dev The function checks that contract recipients are aware of the ERC721 protocol to prevent tokens from being
     *  forever locked.
     * @dev This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. implement alternative
     *  mechanisms to perform token transfer, such as signature-based.
     * @dev Requirements:
     *
     *  - `from` cannot be the zero address.
     *  - `to` cannot be the zero address.
     *  - `tokenId` token must exist and be owned by `from`.
     *  - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     * @dev Emits a {Transfer} event.
     * @param from Address of the account currently owning the given token
     * @param to Address to transfer the token to
     * @param tokenId ID of the token to transfer
     * @param data Additional data with no specified format, sent in call to `to`
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _transfer(from, to, tokenId, data);
        if (!_checkOnERC721Received(from, to, tokenId, data))
            revert ERC721TransferToNonReceiverImplementer();
    }

    /**
     * @notice Used to transfer the token from `from` to `to`.
     * @dev As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     * @dev Requirements:
     *
     *  - `to` cannot be the zero address.
     *  - `tokenId` token must be owned by `from`.
     * @dev Emits a {Transfer} event.
     * @param from Address of the account currently owning the given token
     * @param to Address to transfer the token to
     * @param tokenId ID of the token to transfer
     * @param data Additional data with no specified format, sent in call to `to`
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        (address immediateOwner, uint256 parentId, ) = directOwnerOf(tokenId);
        if (immediateOwner != from) revert ERC721TransferFromIncorrectOwner();
        if (to == address(0)) revert ERC721TransferToTheZeroAddress();

        _beforeTokenTransfer(from, to, tokenId);
        _beforeNestedTokenTransfer(
            immediateOwner,
            to,
            parentId,
            0,
            tokenId,
            data
        );

        _balances[from] -= 1;
        _updateOwnerAndClearApprovals(tokenId, 0, to, false);
        _balances[to] += 1;

        emit Transfer(from, to, tokenId);
        emit NestTransfer(immediateOwner, to, parentId, 0, tokenId);

        _afterTokenTransfer(from, to, tokenId);
        _afterNestedTokenTransfer(
            immediateOwner,
            to,
            parentId,
            0,
            tokenId,
            data
        );
    }

    /**
     * @notice Used to transfer a token into another token.
     * @dev Attempting to nest a token into `0x0` address will result in reverted transaction.
     * @dev Attempting to nest a token into itself will result in reverted transaction.
     * @param from Address of the account currently owning the given token
     * @param to Address of the receiving token's collection smart contract
     * @param tokenId ID of the token to transfer
     * @param destinationId ID of the token receiving the given token
     * @param data Additional data with no specified format, sent in the addChild call
     */
    function _nestTransfer(
        address from,
        address to,
        uint256 tokenId,
        uint256 destinationId,
        bytes memory data
    ) internal virtual {
        (address immediateOwner, uint256 parentId, ) = directOwnerOf(tokenId);
        if (immediateOwner != from) revert ERC721TransferFromIncorrectOwner();
        if (to == address(0)) revert ERC721TransferToTheZeroAddress();
        if (to == address(this) && tokenId == destinationId)
            revert RMRKNestableTransferToSelf();

        // Destination contract checks:
        // It seems redundant, but otherwise it would revert with no error
        if (!to.isContract()) revert RMRKIsNotContract();
        if (!IERC165(to).supportsInterface(type(IRMRKNestable).interfaceId))
            revert RMRKNestableTransferToNonRMRKNestableImplementer();
        _checkForInheritanceLoop(tokenId, to, destinationId);

        _beforeTokenTransfer(from, to, tokenId);
        _beforeNestedTokenTransfer(
            immediateOwner,
            to,
            parentId,
            destinationId,
            tokenId,
            data
        );
        _balances[from] -= 1;
        _updateOwnerAndClearApprovals(tokenId, destinationId, to, true);
        _balances[to] += 1;

        // Sending to NFT:
        _sendToNFT(immediateOwner, to, parentId, destinationId, tokenId, data);
    }

    /**
     * @notice Used to send a token to another token.
     * @dev If the token being sent is currently owned by an externally owned account, the `parentId` should equal `0`.
     * @dev Emits {Transfer} event.
     * @dev Emits {NestTransfer} event.
     * @param from Address from which the token is being sent
     * @param to Address of the collection smart contract of the token to receive the given token
     * @param parentId ID of the current parent token of the token being sent
     * @param destinationId ID of the tokento receive the token being sent
     * @param tokenId ID of the token being sent
     * @param data Additional data with no specified format, sent in the addChild call
     */
    function _sendToNFT(
        address from,
        address to,
        uint256 parentId,
        uint256 destinationId,
        uint256 tokenId,
        bytes memory data
    ) private {
        IRMRKNestable destContract = IRMRKNestable(to);
        destContract.addChild(destinationId, tokenId, data);

        emit Transfer(from, to, tokenId);
        emit NestTransfer(from, to, parentId, destinationId, tokenId);

        _afterTokenTransfer(from, to, tokenId);
        _afterNestedTokenTransfer(
            from,
            to,
            parentId,
            destinationId,
            tokenId,
            data
        );
    }

    /**
     * @notice Used to check if nesting a given token into a specified token would create an inheritance loop.
     * @dev If a loop would occur, the tokens would be unmanageable, so the execution is reverted if one is detected.
     * @dev The check for inheritance loop is bounded to guard against too much gas being consumed.
     * @param currentId ID of the token that would be nested
     * @param targetContract Address of the collection smart contract of the token into which the given token would be
     *  nested
     * @param targetId ID of the token into which the given token would be nested
     */
    function _checkForInheritanceLoop(
        uint256 currentId,
        address targetContract,
        uint256 targetId
    ) private view {
        for (uint256 i; i < _MAX_LEVELS_TO_CHECK_FOR_INHERITANCE_LOOP; ) {
            (
                address nextOwner,
                uint256 nextOwnerTokenId,
                bool isNft
            ) = IRMRKNestable(targetContract).directOwnerOf(targetId);
            // If there's a final address, we're good. There's no loop.
            if (!isNft) {
                return;
            }
            // Ff the current nft is an ancestor at some point, there is an inheritance loop
            if (nextOwner == address(this) && nextOwnerTokenId == currentId) {
                revert RMRKNestableTransferToDescendant();
            }
            // We reuse the parameters to save some contract size
            targetContract = nextOwner;
            targetId = nextOwnerTokenId;
            unchecked {
                ++i;
            }
        }
        revert RMRKNestableTooDeep();
    }

    ////////////////////////////////////////
    //              MINTING
    ////////////////////////////////////////

    /**
     * @notice Used to safely mint the token to the specified address while passing the additional data to contract
     *  recipients.
     * @param to Address to which to mint the token
     * @param tokenId ID of the token to mint
     * @param data Additional data to send with the tokens
     */
    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _mint(to, tokenId, data);
        if (!_checkOnERC721Received(address(0), to, tokenId, data))
            revert ERC721TransferToNonReceiverImplementer();
    }

    /**
     * @notice Used to mint a specified token to a given address.
     * @dev WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible.
     * @dev Requirements:
     *
     *  - `tokenId` must not exist.
     *  - `to` cannot be the zero address.
     * @dev Emits a {Transfer} event.
     * @dev Emits a {NestTransfer} event.
     * @param to Address to mint the token to
     * @param tokenId ID of the token to mint
     * @param data Additional data with no specified format, sent in call to `to`
     */
    function _mint(
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _innerMint(to, tokenId, 0, data);

        emit Transfer(address(0), to, tokenId);
        emit NestTransfer(address(0), to, 0, 0, tokenId);

        _afterTokenTransfer(address(0), to, tokenId);
        _afterNestedTokenTransfer(address(0), to, 0, 0, tokenId, data);
    }

    /**
     * @notice Used to mint a child token to a given parent token.
     * @param to Address of the collection smart contract of the token into which to mint the child token
     * @param tokenId ID of the token to mint
     * @param destinationId ID of the token into which to mint the new child token
     * @param data Additional data with no specified format, sent in the addChild call
     */
    function _nestMint(
        address to,
        uint256 tokenId,
        uint256 destinationId,
        bytes memory data
    ) internal virtual {
        // It seems redundant, but otherwise it would revert with no error
        if (!to.isContract()) revert RMRKIsNotContract();
        if (!IERC165(to).supportsInterface(type(IRMRKNestable).interfaceId))
            revert RMRKMintToNonRMRKNestableImplementer();

        _innerMint(to, tokenId, destinationId, data);
        _sendToNFT(address(0), to, 0, destinationId, tokenId, data);
    }

    /**
     * @notice Used to mint a child token into a given parent token.
     * @dev Requirements:
     *
     *  - `to` cannot be the zero address.
     *  - `tokenId` must not exist.
     *  - `tokenId` must not be `0`.
     * @param to Address of the collection smart contract of the token into which to mint the child token
     * @param tokenId ID of the token to mint
     * @param destinationId ID of the token into which to mint the new token
     * @param data Additional data with no specified format, sent in call to `to`
     */
    function _innerMint(
        address to,
        uint256 tokenId,
        uint256 destinationId,
        bytes memory data
    ) private {
        if (to == address(0)) revert ERC721MintToTheZeroAddress();
        if (_exists(tokenId)) revert ERC721TokenAlreadyMinted();
        if (tokenId == 0) revert RMRKIdZeroForbidden();

        _beforeTokenTransfer(address(0), to, tokenId);
        _beforeNestedTokenTransfer(
            address(0),
            to,
            0,
            destinationId,
            tokenId,
            data
        );

        _balances[to] += 1;
        _RMRKOwners[tokenId] = DirectOwner({
            ownerAddress: to,
            tokenId: destinationId,
            isNft: destinationId != 0
        });
    }

    ////////////////////////////////////////
    //              Ownership
    ////////////////////////////////////////

    /**
     * @inheritdoc IRMRKNestable
     */
    function ownerOf(
        uint256 tokenId
    ) public view virtual override(IRMRKNestable, IERC721) returns (address) {
        (address owner, uint256 ownerTokenId, bool isNft) = directOwnerOf(
            tokenId
        );
        if (isNft) {
            owner = IRMRKNestable(owner).ownerOf(ownerTokenId);
        }
        return owner;
    }

    /**
     * @inheritdoc IRMRKNestable
     */
    function directOwnerOf(
        uint256 tokenId
    ) public view virtual returns (address, uint256, bool) {
        DirectOwner memory owner = _RMRKOwners[tokenId];
        if (owner.ownerAddress == address(0)) revert ERC721InvalidTokenId();

        return (owner.ownerAddress, owner.tokenId, owner.isNft);
    }

    ////////////////////////////////////////
    //              BURNING
    ////////////////////////////////////////

    /**
     * @notice Used to burn a given token.
     * @dev In case the token has any child tokens, the execution will be reverted.
     * @param tokenId ID of the token to burn
     */
    function burn(uint256 tokenId) public virtual {
        burn(tokenId, 0);
    }

    /**
     * @inheritdoc IRMRKNestable
     */
    function burn(
        uint256 tokenId,
        uint256 maxChildrenBurns
    ) public virtual onlyApprovedOrDirectOwner(tokenId) returns (uint256) {
        return _burn(tokenId, maxChildrenBurns);
    }

    /**
     * @notice Used to burn a token.
     * @dev When a token is burned, its children are recursively burned as well.
     * @dev The approvals are cleared when the token is burned.
     * @dev Requirements:
     *
     *  - `tokenId` must exist.
     * @dev Emits a {Transfer} event.
     * @dev Emits a {NestTransfer} event.
     * @param tokenId ID of the token to burn
     * @param maxChildrenBurns Maximum children to recursively burn
     * @return The number of recursive burns it took to burn all of the children
     */
    function _burn(
        uint256 tokenId,
        uint256 maxChildrenBurns
    ) internal virtual returns (uint256) {
        (address immediateOwner, uint256 parentId, ) = directOwnerOf(tokenId);
        address owner = ownerOf(tokenId);
        _balances[immediateOwner] -= 1;

        _beforeTokenTransfer(owner, address(0), tokenId);
        _beforeNestedTokenTransfer(
            immediateOwner,
            address(0),
            parentId,
            0,
            tokenId,
            ""
        );

        _approve(address(0), tokenId);
        _cleanApprovals(tokenId);

        Child[] memory children = childrenOf(tokenId);

        delete _activeChildren[tokenId];
        delete _pendingChildren[tokenId];
        delete _tokenApprovals[tokenId][owner];

        uint256 pendingRecursiveBurns;
        uint256 totalChildBurns;

        uint256 length = children.length; //gas savings
        for (uint256 i; i < length; ) {
            if (totalChildBurns >= maxChildrenBurns)
                revert RMRKMaxRecursiveBurnsReached(
                    children[i].contractAddress,
                    children[i].tokenId
                );
            delete _childIsInActive[children[i].contractAddress][
                children[i].tokenId
            ];
            unchecked {
                // At this point we know pendingRecursiveBurns must be at least 1
                pendingRecursiveBurns = maxChildrenBurns - totalChildBurns;
            }
            // We substract one to the next level to count for the token being burned, then add it again on returns
            // This is to allow the behavior of 0 recursive burns meaning only the current token is deleted.
            totalChildBurns +=
                IRMRKNestable(children[i].contractAddress).burn(
                    children[i].tokenId,
                    pendingRecursiveBurns - 1
                ) +
                1;
            unchecked {
                ++i;
            }
        }
        // Can't remove before burning child since child will call back to get root owner
        delete _RMRKOwners[tokenId];

        emit Transfer(owner, address(0), tokenId);
        emit NestTransfer(immediateOwner, address(0), parentId, 0, tokenId);

        _afterTokenTransfer(owner, address(0), tokenId);
        _afterNestedTokenTransfer(
            immediateOwner,
            address(0),
            parentId,
            0,
            tokenId,
            ""
        );

        return totalChildBurns;
    }

    ////////////////////////////////////////
    //              APPROVALS
    ////////////////////////////////////////

    /**
     * @notice Used to grant a one-time approval to manage one's token.
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * @dev The approval is cleared when the token is transferred.
     * @dev Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     * @dev Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     * @dev Emits an {Approval} event.
     * @param to Address receiving the approval
     * @param tokenId ID of the token for which the approval is being granted
     */
    function approve(address to, uint256 tokenId) public virtual {
        address owner = ownerOf(tokenId);
        if (to == owner) revert ERC721ApprovalToCurrentOwner();

        if (_msgSender() != owner && !isApprovedForAll(owner, _msgSender()))
            revert ERC721ApproveCallerIsNotOwnerNorApprovedForAll();

        _approve(to, tokenId);
    }

    /**
     * @notice Used to retrieve the account approved to manage given token.
     * @dev Requirements:
     *
     *  - `tokenId` must exist.
     * @param tokenId ID of the token to check for approval
     * @return Address of the account approved to manage the token
     */
    function getApproved(
        uint256 tokenId
    ) public view virtual returns (address) {
        _requireMinted(tokenId);

        return _tokenApprovals[tokenId][ownerOf(tokenId)];
    }

    /**
     * @notice Used to approve or remove `operator` as an operator for the caller.
     * @dev Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     * @dev Requirements:
     *
     * - The `operator` cannot be the caller.
     * @dev Emits an {ApprovalForAll} event.
     * @param operator Address of the operator being managed
     * @param approved A boolean value signifying whether the approval is being granted (`true`) or (`revoked`)
     */
    function setApprovalForAll(address operator, bool approved) public virtual {
        if (_msgSender() == operator) revert ERC721ApproveToCaller();
        _operatorApprovals[_msgSender()][operator] = approved;
        emit ApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @notice Used to check if the given address is allowed to manage the tokens of the specified address.
     * @param owner Address of the owner of the tokens
     * @param operator Address being checked for approval
     * @return A boolean value signifying whether the *operator* is allowed to manage the tokens of the *owner* (`true`)
     *  or not (`false`)
     */
    function isApprovedForAll(
        address owner,
        address operator
    ) public view virtual returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @notice Used to grant an approval to manage a given token.
     * @dev Emits an {Approval} event.
     * @param to Address to which the approval is being granted
     * @param tokenId ID of the token for which the approval is being granted
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        address owner = ownerOf(tokenId);
        _tokenApprovals[tokenId][owner] = to;
        emit Approval(owner, to, tokenId);
    }

    /**
     * @notice Used to update the owner of the token and clear the approvals associated with the previous owner.
     * @dev The `destinationId` should equal `0` if the new owner is an externally owned account.
     * @param tokenId ID of the token being updated
     * @param destinationId ID of the token to receive the given token
     * @param to Address of account to receive the token
     * @param isNft A boolean value signifying whether the new owner is a token (`true`) or externally owned account
     *  (`false`)
     */
    function _updateOwnerAndClearApprovals(
        uint256 tokenId,
        uint256 destinationId,
        address to,
        bool isNft
    ) internal {
        _RMRKOwners[tokenId] = DirectOwner({
            ownerAddress: to,
            tokenId: destinationId,
            isNft: isNft
        });

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);
        _cleanApprovals(tokenId);
    }

    /**
     * @notice Used to remove approvals for the current owner of the given token.
     * @param tokenId ID of the token to clear the approvals for
     */
    function _cleanApprovals(uint256 tokenId) internal virtual {}

    ////////////////////////////////////////
    //              UTILS
    ////////////////////////////////////////

    /**
     * @notice Used to check whether the given account is allowed to manage the given token.
     * @dev Requirements:
     *
     *  - `tokenId` must exist.
     * @param spender Address that is being checked for approval
     * @param tokenId ID of the token being checked
     * @return A boolean value indicating whether the `spender` is approved to manage the given token
     */
    function _isApprovedOrOwner(
        address spender,
        uint256 tokenId
    ) internal view virtual returns (bool) {
        address owner = ownerOf(tokenId);
        return (spender == owner ||
            isApprovedForAll(owner, spender) ||
            getApproved(tokenId) == spender);
    }

    /**
     * @notice Used to check whether the account is approved to manage the token or its direct owner.
     * @param spender Address that is being checked for approval or direct ownership
     * @param tokenId ID of the token being checked
     * @return A boolean value indicating whether the `spender` is approved to manage the given token or its
     *  direct owner
     */
    function _isApprovedOrDirectOwner(
        address spender,
        uint256 tokenId
    ) internal view virtual returns (bool) {
        (address owner, uint256 parentId, ) = directOwnerOf(tokenId);
        // When the parent is an NFT, only it can do operations
        if (parentId != 0) {
            return (spender == owner);
        }
        // Otherwise, the owner or approved address can
        return (spender == owner ||
            isApprovedForAll(owner, spender) ||
            getApproved(tokenId) == spender);
    }

    /**
     * @notice Used to enforce that the given token has been minted.
     * @dev Reverts if the `tokenId` has not been minted yet.
     * @dev The validation checks whether the owner of a given token is a `0x0` address and considers it not minted if
     *  it is. This means that both tokens that haven't been minted yet as well as the ones that have already been
     *  burned will cause the transaction to be reverted.
     * @param tokenId ID of the token to check
     */
    function _requireMinted(uint256 tokenId) internal view virtual {
        if (!_exists(tokenId)) revert ERC721InvalidTokenId();
    }

    /**
     * @notice Used to check whether the given token exists.
     * @dev Tokens start existing when they are minted (`_mint`) and stop existing when they are burned (`_burn`).
     * @param tokenId ID of the token being checked
     * @return A boolean value signifying whether the token exists
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _RMRKOwners[tokenId].ownerAddress != address(0);
    }

    /**
     * @notice Used to invoke {IERC721Receiver-onERC721Received} on a target address.
     * @dev The call is not executed if the target address is not a contract.
     * @param from Address representing the previous owner of the given token
     * @param to Yarget address that will receive the tokens
     * @param tokenId ID of the token to be transferred
     * @param data Optional data to send along with the call
     * @return Boolean value signifying whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) private returns (bool) {
        if (to.isContract()) {
            try
                IERC721Receiver(to).onERC721Received(
                    _msgSender(),
                    from,
                    tokenId,
                    data
                )
            returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert ERC721TransferToNonReceiverImplementer();
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    ////////////////////////////////////////
    //      CHILD MANAGEMENT PUBLIC
    ////////////////////////////////////////

    /**
     * @inheritdoc IRMRKNestable
     */
    function addChild(
        uint256 parentId,
        uint256 childId,
        bytes memory data
    ) public virtual {
        _requireMinted(parentId);

        address childAddress = _msgSender();
        if (!childAddress.isContract()) revert RMRKIsNotContract();

        Child memory child = Child({
            contractAddress: childAddress,
            tokenId: childId
        });

        _beforeAddChild(parentId, childAddress, childId, data);

        uint256 length = pendingChildrenOf(parentId).length;

        if (length < 128) {
            _pendingChildren[parentId].push(child);
        } else {
            revert RMRKMaxPendingChildrenReached();
        }

        // Previous length matches the index for the new child
        emit ChildProposed(parentId, length, childAddress, childId);

        _afterAddChild(parentId, childAddress, childId, data);
    }

    /**
     * @inheritdoc IRMRKNestable
     */
    function acceptChild(
        uint256 parentId,
        uint256 childIndex,
        address childAddress,
        uint256 childId
    ) public virtual onlyApprovedOrOwner(parentId) {
        _acceptChild(parentId, childIndex, childAddress, childId);
    }

    /**
     * @notice Used to accept a pending child token for a given parent token.
     * @dev This moves the child token from parent token's pending child tokens array into the active child tokens
     *  array.
     * @dev Requirements:
     *
     *  - `tokenId` must exist
     *  - `index` must be in range of the pending children array
     * @dev Emits ***ChildAccepted*** event.
     * @param parentId ID of the parent token for which the child token is being accepted
     * @param childIndex Index of a child tokem in the given parent's pending children array
     * @param childAddress Address of the collection smart contract of the child token expected to be located at the
     *  specified index of the given parent token's pending children array
     * @param childId ID of the child token expected to be located at the specified index of the given parent token's
     *  pending children array
     */
    function _acceptChild(
        uint256 parentId,
        uint256 childIndex,
        address childAddress,
        uint256 childId
    ) internal virtual {
        Child memory child = pendingChildOf(parentId, childIndex);
        _checkExpectedChild(child, childAddress, childId);
        if (_childIsInActive[childAddress][childId] != 0)
            revert RMRKChildAlreadyExists();

        _beforeAcceptChild(parentId, childIndex, childAddress, childId);

        // Remove from pending:
        _removeChildByIndex(_pendingChildren[parentId], childIndex);

        // Add to active:
        _activeChildren[parentId].push(child);
        _childIsInActive[childAddress][childId] = 1; // We use 1 as true

        emit ChildAccepted(parentId, childIndex, childAddress, childId);

        _afterAcceptChild(parentId, childIndex, childAddress, childId);
    }

    /**
     * @inheritdoc IRMRKNestable
     */
    function rejectAllChildren(
        uint256 tokenId,
        uint256 maxRejections
    ) public virtual onlyApprovedOrOwner(tokenId) {
        _rejectAllChildren(tokenId, maxRejections);
    }

    /**
     * @notice Used to reject all pending children of a given parent token.
     * @dev Removes the children from the pending array mapping.
     * @dev This does not update the ownership storage data on children. If necessary, ownership can be reclaimed by the
     *  rootOwner of the previous parent.
     * @dev Requirements:
     *
     *  - `tokenId` must exist
     * @dev Emits ***AllChildrenRejected*** event.
     * @param tokenId ID of the parent token for which to reject all of the pending tokens.
     * @param maxRejections Maximum number of expected children to reject, used to prevent from rejecting children which
     *  arrive just before this operation.
     */
    function _rejectAllChildren(
        uint256 tokenId,
        uint256 maxRejections
    ) internal virtual {
        if (_pendingChildren[tokenId].length > maxRejections)
            revert RMRKUnexpectedNumberOfChildren();

        _beforeRejectAllChildren(tokenId);
        delete _pendingChildren[tokenId];
        emit AllChildrenRejected(tokenId);
        _afterRejectAllChildren(tokenId);
    }

    /**
     * @inheritdoc IRMRKNestable
     */
    function transferChild(
        uint256 tokenId,
        address to,
        uint256 destinationId,
        uint256 childIndex,
        address childAddress,
        uint256 childId,
        bool isPending,
        bytes memory data
    ) public virtual onlyApprovedOrOwner(tokenId) {
        _transferChild(
            tokenId,
            to,
            destinationId,
            childIndex,
            childAddress,
            childId,
            isPending,
            data
        );
    }

    /**
     * @notice Used to transfer a child token from a given parent token.
     * @dev When transferring a child token, the owner of the token is set to `to`, or is not updated in the event of
     *  `to` being the `0x0` address.
     * @dev Requirements:
     *
     *  - `tokenId` must exist.
     * @dev Emits {ChildTransferred} event.
     * @param tokenId ID of the parent token from which the child token is being transferred
     * @param to Address to which to transfer the token to
     * @param destinationId ID of the token to receive this child token (MUST be 0 if the destination is not a token)
     * @param childIndex Index of a token we are transferring, in the array it belongs to (can be either active array or
     *  pending array)
     * @param childAddress Address of the child token's collection smart contract.
     * @param childId ID of the child token in its own collection smart contract.
     * @param isPending A boolean value indicating whether the child token being transferred is in the pending array of
     *  the parent token (`true`) or in the active array (`false`)
     * @param data Additional data with no specified format, sent in call to `_to`
     */
    function _transferChild(
        uint256 tokenId,
        address to,
        uint256 destinationId, // newParentId
        uint256 childIndex,
        address childAddress,
        uint256 childId,
        bool isPending,
        bytes memory data
    ) internal virtual {
        Child memory child;
        if (isPending) {
            child = pendingChildOf(tokenId, childIndex);
        } else {
            child = childOf(tokenId, childIndex);
        }
        _checkExpectedChild(child, childAddress, childId);

        _beforeTransferChild(
            tokenId,
            childIndex,
            childAddress,
            childId,
            isPending,
            data
        );

        if (isPending) {
            _removeChildByIndex(_pendingChildren[tokenId], childIndex);
        } else {
            delete _childIsInActive[childAddress][childId];
            _removeChildByIndex(_activeChildren[tokenId], childIndex);
        }

        if (to != address(0)) {
            if (destinationId == 0) {
                IERC721(childAddress).safeTransferFrom(
                    address(this),
                    to,
                    childId,
                    data
                );
            } else {
                // Destination is an NFT
                IRMRKNestable(child.contractAddress).nestTransferFrom(
                    address(this),
                    to,
                    child.tokenId,
                    destinationId,
                    data
                );
            }
        }

        emit ChildTransferred(
            tokenId,
            childIndex,
            childAddress,
            childId,
            isPending,
            to == address(0)
        );
        _afterTransferChild(
            tokenId,
            childIndex,
            childAddress,
            childId,
            isPending,
            data
        );
    }

    /**
     * @notice Used to verify that the child being accessed is the intended child.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @param child A Child struct of a child being accessed
     * @param expectedAddress The address expected to be the one of the child
     * @param expectedId The token ID expected to be the one of the child
     */
    function _checkExpectedChild(
        Child memory child,
        address expectedAddress,
        uint256 expectedId
    ) private pure {
        if (
            expectedAddress != child.contractAddress ||
            expectedId != child.tokenId
        ) revert RMRKUnexpectedChildId();
    }

    ////////////////////////////////////////
    //      CHILD MANAGEMENT GETTERS
    ////////////////////////////////////////

    /**
     * @inheritdoc IRMRKNestable
     */

    function childrenOf(
        uint256 parentId
    ) public view virtual returns (Child[] memory) {
        Child[] memory children = _activeChildren[parentId];
        return children;
    }

    /**
     * @inheritdoc IRMRKNestable
     */

    function pendingChildrenOf(
        uint256 parentId
    ) public view virtual returns (Child[] memory) {
        Child[] memory pendingChildren = _pendingChildren[parentId];
        return pendingChildren;
    }

    /**
     * @inheritdoc IRMRKNestable
     */
    function childOf(
        uint256 parentId,
        uint256 index
    ) public view virtual returns (Child memory) {
        if (childrenOf(parentId).length <= index)
            revert RMRKChildIndexOutOfRange();
        Child memory child = _activeChildren[parentId][index];
        return child;
    }

    /**
     * @inheritdoc IRMRKNestable
     */
    function pendingChildOf(
        uint256 parentId,
        uint256 index
    ) public view virtual returns (Child memory) {
        if (pendingChildrenOf(parentId).length <= index)
            revert RMRKPendingChildIndexOutOfRange();
        Child memory child = _pendingChildren[parentId][index];
        return child;
    }

    /**
     * @notice Used to verify that the given child tokwn is included in an active array of a token.
     * @param childAddress Address of the given token's collection smart contract
     * @param childId ID of the child token being checked
     * @return A boolean value signifying whether the given child token is included in an active child tokens array of a
     *  token (`true`) or not (`false`)
     */
    function childIsInActive(
        address childAddress,
        uint256 childId
    ) public view virtual returns (bool) {
        return _childIsInActive[childAddress][childId] != 0;
    }

    // HOOKS

    /**
     * @notice Hook that is called before nested token transfer.
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param from Address from which the token is being transferred
     * @param to Address to which the token is being transferred
     * @param fromTokenId ID of the token from which the given token is being transferred
     * @param toTokenId ID of the token to which the given token is being transferred
     * @param tokenId ID of the token being transferred
     * @param data Additional data with no specified format, sent in the addChild call
     */
    function _beforeNestedTokenTransfer(
        address from,
        address to,
        uint256 fromTokenId,
        uint256 toTokenId,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {}

    /**
     * @notice Hook that is called after nested token transfer.
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param from Address from which the token was transferred
     * @param to Address to which the token was transferred
     * @param fromTokenId ID of the token from which the given token was transferred
     * @param toTokenId ID of the token to which the given token was transferred
     * @param tokenId ID of the token that was transferred
     * @param data Additional data with no specified format, sent in the addChild call
     */
    function _afterNestedTokenTransfer(
        address from,
        address to,
        uint256 fromTokenId,
        uint256 toTokenId,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {}

    /**
     * @notice Hook that is called before a child is added to the pending tokens array of a given token.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param tokenId ID of the token that will receive a new pending child token
     * @param childAddress Address of the collection smart contract of the child token expected to be located at the
     *  specified index of the given parent token's pending children array
     * @param childId ID of the child token expected to be located at the specified index of the given parent token's
     *  pending children array
     * @param data Additional data with no specified format
     */
    function _beforeAddChild(
        uint256 tokenId,
        address childAddress,
        uint256 childId,
        bytes memory data
    ) internal virtual {}

    /**
     * @notice Hook that is called after a child is added to the pending tokens array of a given token.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param tokenId ID of the token that has received a new pending child token
     * @param childAddress Address of the collection smart contract of the child token expected to be located at the
     *  specified index of the given parent token's pending children array
     * @param childId ID of the child token expected to be located at the specified index of the given parent token's
     *  pending children array
     * @param data Additional data with no specified format
     */
    function _afterAddChild(
        uint256 tokenId,
        address childAddress,
        uint256 childId,
        bytes memory data
    ) internal virtual {}

    /**
     * @notice Hook that is called before a child is accepted to the active tokens array of a given token.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param parentId ID of the token that will accept a pending child token
     * @param childIndex Index of the child token to accept in the given parent token's pending children array
     * @param childAddress Address of the collection smart contract of the child token expected to be located at the
     *  specified index of the given parent token's pending children array
     * @param childId ID of the child token expected to be located at the specified index of the given parent token's
     *  pending children array
     */
    function _beforeAcceptChild(
        uint256 parentId,
        uint256 childIndex,
        address childAddress,
        uint256 childId
    ) internal virtual {}

    /**
     * @notice Hook that is called after a child is accepted to the active tokens array of a given token.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param parentId ID of the token that has accepted a pending child token
     * @param childIndex Index of the child token that was accpeted in the given parent token's pending children array
     * @param childAddress Address of the collection smart contract of the child token that was expected to be located
     *  at the specified index of the given parent token's pending children array
     * @param childId ID of the child token that was expected to be located at the specified index of the given parent
     *  token's pending children array
     */
    function _afterAcceptChild(
        uint256 parentId,
        uint256 childIndex,
        address childAddress,
        uint256 childId
    ) internal virtual {}

    /**
     * @notice Hook that is called before a child is transferred from a given child token array of a given token.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param tokenId ID of the token that will transfer a child token
     * @param childIndex Index of the child token that will be transferred from the given parent token's children array
     * @param childAddress Address of the collection smart contract of the child token that is expected to be located
     *  at the specified index of the given parent token's children array
     * @param childId ID of the child token that is expected to be located at the specified index of the given parent
     *  token's children array
     * @param isPending A boolean value signifying whether the child token is being transferred from the pending child
     *  tokens array (`true`) or from the active child tokens array (`false`)
     * @param data Additional data with no specified format, sent in the addChild call
     */
    function _beforeTransferChild(
        uint256 tokenId,
        uint256 childIndex,
        address childAddress,
        uint256 childId,
        bool isPending,
        bytes memory data
    ) internal virtual {}

    /**
     * @notice Hook that is called after a child is transferred from a given child token array of a given token.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param tokenId ID of the token that has transferred a child token
     * @param childIndex Index of the child token that was transferred from the given parent token's children array
     * @param childAddress Address of the collection smart contract of the child token that was expected to be located
     *  at the specified index of the given parent token's children array
     * @param childId ID of the child token that was expected to be located at the specified index of the given parent
     *  token's children array
     * @param isPending A boolean value signifying whether the child token was transferred from the pending child tokens
     *  array (`true`) or from the active child tokens array (`false`)
     * @param data Additional data with no specified format, sent in the addChild call
     */
    function _afterTransferChild(
        uint256 tokenId,
        uint256 childIndex,
        address childAddress,
        uint256 childId,
        bool isPending,
        bytes memory data
    ) internal virtual {}

    /**
     * @notice Hook that is called before a pending child tokens array of a given token is cleared.
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param tokenId ID of the token that will reject all of the pending child tokens
     */
    function _beforeRejectAllChildren(uint256 tokenId) internal virtual {}

    /**
     * @notice Hook that is called after a pending child tokens array of a given token is cleared.
     * @dev To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     * @param tokenId ID of the token that has rejected all of the pending child tokens
     */
    function _afterRejectAllChildren(uint256 tokenId) internal virtual {}

    // HELPERS

    /**
     * @notice Used to remove a specified child token form an array using its index within said array.
     * @dev The caller must ensure that the length of the array is valid compared to the index passed.
     * @dev The Child struct consists of the following values:
     *  [
     *      tokenId,
     *      contractAddress
     *  ]
     * @param array An array od Child struct containing info about the child tokens in a given child tokens array
     * @param index An index of the child token to remove in the accompanying array
     */
    function _removeChildByIndex(Child[] storage array, uint256 index) private {
        array[index] = array[array.length - 1];
        array.pop();
    }
}

File 17 of 18 : Reputation.sol
// SPDX-License-Identifier: Apache-2.0

import "@rmrk-team/evm-contracts/contracts/RMRK/nestable/RMRKNestable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Base64.sol";
import {Ownable} from "./utils/Ownable.sol";

pragma solidity ^0.8.18;

// gatekeeping
// optional emits
// categories logic
// optional comment from issuer

contract Reputation is RMRKNestable, Ownable {
    uint256 private tokenCounter;

    /* 
    =====================================
    Things we wanna have in ReputationNFT
    =====================================
        issuerNftId - id of profile nft that gave cred
        receiverNftId - id of profile NFT that gets cred
        categoryId - id of category this person is getting cred for
        bytes message - comment related to the reputation, better to store on ipfs
    */

    struct Cred {
        uint256 credTokenId;
        uint256 issuerNftId;
        uint256 receiverNftId;
        string categoryId;
        string message;
    }

    mapping(uint256 credTokenId => Cred givenCreds) public givenCreds;
    mapping(uint256 tokenId => string tokenUri) public tokenUris;
    mapping(bytes32 hashed => bool used) internal usedHashes;

    event ReputationMinted(address indexed recipient, uint256 indexed tokenId);

    // // TODO
    // string public constant TOKEN_URI =
    //     "ipfs://bafybeig37ioir76s7mg5oobetncojcm3c3hxasyd4rvid4jqhy4gkaheg4/?filename=0-PUG.json";

    constructor(
        string memory name,
        string memory symbol,
        address owner
    )
        RMRKNestable(name, symbol) Ownable(owner, address(0))
    {
        // Custom optional: constructor logic
        tokenCounter = 1;
    }

    // minting reputation NFT as nested NFT
    function giveCred(uint256 issuerId, uint256 recipientId, string memory categoryId, string memory message) public {
        _checkOwner();

        bytes32 hashInput = hash(issuerId, recipientId, categoryId);
        require(!usedHashes[hashInput], "Reputation already minted");
        usedHashes[hashInput] = true;

        _nestMint(owner(), tokenCounter, recipientId, bytes(message));
    
        string memory json = Base64.encode(
            bytes(
                string(
                    abi.encodePacked(
                    '{"name": "',
                    categoryId, // #TODO: check if we want this for name
                    '", "description": "Blossoms", "image": "https://ipfs.io/ipfs/QmSsYRx3LpDAb1GZQm7zZ1AuHZjfbPkD6J7s9r41xu1mf8?filename=pug.png"}'
                    )
                )
            )
        );
        string memory tokenUri = string(
            abi.encodePacked("data:application/json;base64,", json)
        );
        tokenUris[tokenCounter] = tokenUri;

        givenCreds[tokenCounter] = Cred(tokenCounter, issuerId, recipientId, categoryId, message);

        emit ReputationMinted(owner(), tokenCounter);
        unchecked {tokenCounter++;}
    }

    function hash(uint256 issuerId, uint256 reciverId, string memory categoryId) internal pure returns(bytes32) {
        return keccak256(abi.encodePacked(issuerId, reciverId, categoryId));
    }

    function getTokenUri(uint256 tokenId) public view returns (string memory) {
        return tokenUris[tokenId];
    }
}

File 18 of 18 : Ownable.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.18;

/**
 * @title The Ownable contract
 * @notice An abstract contract for ownership managment
 */
abstract contract Ownable {
    address private _owner;
    address private _pendingOwner;

    event OwnershipTransferRequested(address indexed from, address indexed to);
    event OwnershipTransferred(address indexed from, address indexed to);
    event OwnershipTransferCanceled(address indexed from, address indexed to);

    error CannotSetOwnerToZeroAddress();
    error MustBeProposedOwner();
    error CallerIsNotOwner();
    error CannotTransferToSelf();

    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    constructor(address newOwner, address pendingOwner) {
        if (newOwner == address(0)) revert CannotSetOwnerToZeroAddress();

        _owner = newOwner;

        if (pendingOwner != address(0)) _transferOwnership(pendingOwner);
    }

    /**
     * @notice Requests ownership transfer to the new address which needs to accept it.
     *
     * @dev Only owner can call.
     *
     * @param newOwner - address of proposed new owner
     *
     * No return, reverts on error.
     */
    function transferOwnership(address newOwner) external onlyOwner {
        _transferOwnership(newOwner);
    }

    /**
     * @notice Accepts pending ownership transfer request.
     *
     * @dev Only proposed new owner can call.
     *
     * No return, revets on error.
     */
    function acceptOwnership() external {
        if (msg.sender != _pendingOwner) revert MustBeProposedOwner();

        address oldOwner = _owner;
        _owner = msg.sender;
        _pendingOwner = address(0);

        emit OwnershipTransferred(oldOwner, msg.sender);
    }

    /**
     * @notice Cancels ownership request transfer.
     *
     * @dev Only owner can call.
     *
     * No return, reverts on error.
     */
    function cancelOwnershipTransfer() external onlyOwner {
        address oldPendingOwner = _pendingOwner;
        _pendingOwner = address(0);

        emit OwnershipTransferCanceled(msg.sender, oldPendingOwner);
    }

    /**
     * @notice Gets current owner address.
     *
     * @return owner
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @notice Gets pending owner address.
     *
     * @return pendingOwner
     */
    function getPendingOwner() public view returns (address) {
        return _pendingOwner;
    }

    function _checkOwner() internal view {
        if (msg.sender != owner()) revert CallerIsNotOwner();
    }

    function _transferOwnership(address newOwner) private {
        if (newOwner == address(0)) revert CannotSetOwnerToZeroAddress();
        if (newOwner == msg.sender) revert CannotTransferToSelf();

        _pendingOwner = newOwner;

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

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"address","name":"multisigOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CallerIsNotOwner","type":"error"},{"inputs":[],"name":"CannotSetOwnerToZeroAddress","type":"error"},{"inputs":[],"name":"CannotTransferToSelf","type":"error"},{"inputs":[],"name":"ERC721AddressZeroIsNotaValidOwner","type":"error"},{"inputs":[],"name":"ERC721ApprovalToCurrentOwner","type":"error"},{"inputs":[],"name":"ERC721ApproveCallerIsNotOwnerNorApprovedForAll","type":"error"},{"inputs":[],"name":"ERC721ApproveToCaller","type":"error"},{"inputs":[],"name":"ERC721InvalidTokenId","type":"error"},{"inputs":[],"name":"ERC721MintToTheZeroAddress","type":"error"},{"inputs":[],"name":"ERC721NotApprovedOrOwner","type":"error"},{"inputs":[],"name":"ERC721TokenAlreadyMinted","type":"error"},{"inputs":[],"name":"ERC721TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"ERC721TransferToNonReceiverImplementer","type":"error"},{"inputs":[],"name":"ERC721TransferToTheZeroAddress","type":"error"},{"inputs":[{"internalType":"address","name":"profile","type":"address"}],"name":"InvalidOrganization","type":"error"},{"inputs":[],"name":"MustBeProposedOwner","type":"error"},{"inputs":[{"internalType":"address","name":"profile","type":"address"}],"name":"ProfileAlreadyExists","type":"error"},{"inputs":[],"name":"RMRKChildAlreadyExists","type":"error"},{"inputs":[],"name":"RMRKChildIndexOutOfRange","type":"error"},{"inputs":[],"name":"RMRKIdZeroForbidden","type":"error"},{"inputs":[],"name":"RMRKIsNotContract","type":"error"},{"inputs":[],"name":"RMRKMaxPendingChildrenReached","type":"error"},{"inputs":[{"internalType":"address","name":"childContract","type":"address"},{"internalType":"uint256","name":"childId","type":"uint256"}],"name":"RMRKMaxRecursiveBurnsReached","type":"error"},{"inputs":[],"name":"RMRKNestableTooDeep","type":"error"},{"inputs":[],"name":"RMRKNestableTransferToDescendant","type":"error"},{"inputs":[],"name":"RMRKNestableTransferToNonRMRKNestableImplementer","type":"error"},{"inputs":[],"name":"RMRKNestableTransferToSelf","type":"error"},{"inputs":[],"name":"RMRKNotApprovedOrDirectOwner","type":"error"},{"inputs":[],"name":"RMRKPendingChildIndexOutOfRange","type":"error"},{"inputs":[],"name":"RMRKUnexpectedChildId","type":"error"},{"inputs":[],"name":"RMRKUnexpectedNumberOfChildren","type":"error"},{"inputs":[{"internalType":"bytes32","name":"twitterUidHash","type":"bytes32"}],"name":"TwitterProfileAlreadyConnected","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"AllChildrenRejected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"childIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"childAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"childId","type":"uint256"}],"name":"ChildAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"childIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"childAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"childId","type":"uint256"}],"name":"ChildProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"childIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"childAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"childId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"fromPending","type":"bool"},{"indexed":false,"internalType":"bool","name":"toZero","type":"bool"}],"name":"ChildTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"name":"MerkleRootUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NestTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"OrganizationApproved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ProfileCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"},{"internalType":"uint256","name":"childIndex","type":"uint256"},{"internalType":"address","name":"childAddress","type":"address"},{"internalType":"uint256","name":"childId","type":"uint256"}],"name":"acceptChild","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"},{"internalType":"uint256","name":"childId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"addChild","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"string","name":"reputationTokenIds","type":"string"},{"internalType":"string","name":"message","type":"string"}],"name":"addReputation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string[]","name":"reputationTokenIds","type":"string[]"}],"name":"addSelfReputations","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"profileId","type":"uint256"}],"name":"approveOrganization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"maxChildrenBurns","type":"uint256"}],"name":"burn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelOwnershipTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"childAddress","type":"address"},{"internalType":"uint256","name":"childId","type":"uint256"}],"name":"childIsInActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"childOf","outputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"contractAddress","type":"address"}],"internalType":"struct IRMRKNestable.Child","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"}],"name":"childrenOf","outputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"contractAddress","type":"address"}],"internalType":"struct IRMRKNestable.Child[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"twitterUidHash","type":"bytes32"},{"internalType":"bytes32","name":"whitelistedOrgLeaf","type":"bytes32"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"createProfile","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"directOwnerOf","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"childIndex","type":"uint256"},{"internalType":"uint256","name":"childId","type":"uint256"}],"name":"displayReputationInProfile","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"profile","type":"address"}],"name":"getReputations","outputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"contractAddress","type":"address"}],"internalType":"struct IRMRKNestable.Child[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"twitterUid","type":"uint64"}],"name":"hashTwitterUid","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"reputationTokenId","type":"string"}],"name":"hideReputationFromProfile","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"merkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"destinationId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"nestTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"pendingChildOf","outputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"contractAddress","type":"address"}],"internalType":"struct IRMRKNestable.Child","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"parentId","type":"uint256"}],"name":"pendingChildrenOf","outputs":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"contractAddress","type":"address"}],"internalType":"struct IRMRKNestable.Child[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"profile","type":"address"}],"name":"profiles","outputs":[{"internalType":"uint256","name":"parentId","type":"uint256"},{"internalType":"bytes32","name":"twitterUidHash","type":"bytes32"},{"internalType":"bool","name":"isOrg","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"maxRejections","type":"uint256"}],"name":"rejectAllChildren","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reputationContract","outputs":[{"internalType":"contract Reputation","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"}],"name":"setMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenCounter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"destinationId","type":"uint256"},{"internalType":"uint256","name":"childIndex","type":"uint256"},{"internalType":"address","name":"childAddress","type":"address"},{"internalType":"uint256","name":"childId","type":"uint256"},{"internalType":"bool","name":"isPending","type":"bool"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"transferChild","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a06040523480156200001157600080fd5b5060405162006f8638038062006f868339810160408190526200003491620002c8565b8060008484818184620000488382620003e4565b506001620000578282620003e4565b5050506001600160a01b0384169150620000869050576040516342b66a3d60e01b815260040160405180910390fd5b600980546001600160a01b0319166001600160a01b0384811691909117909155811615620000b957620000b98162000157565b50506001600c556040513090620000d090620001f5565b6060808252600f908201526e212629a6902932b83aba30ba34b7b760891b608082015260a06020820181905260059082015264212629a6a960d91b60c08201526001600160a01b03909116604082015260e001604051809103906000f08015801562000140573d6000803e3d6000fd5b506001600160a01b031660805250620004b0915050565b6001600160a01b0381166200017f576040516342b66a3d60e01b815260040160405180910390fd5b336001600160a01b03821603620001a957604051636d6c4ee560e11b815260040160405180910390fd5b600a80546001600160a01b0319166001600160a01b03831690811790915560405133907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b61368a80620038fc83390190565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200022b57600080fd5b81516001600160401b038082111562000248576200024862000203565b604051601f8301601f19908116603f0116810190828211818310171562000273576200027362000203565b816040528381526020925086838588010111156200029057600080fd5b600091505b83821015620002b4578582018301518183018401529082019062000295565b600093810190920192909252949350505050565b600080600060608486031215620002de57600080fd5b83516001600160401b0380821115620002f657600080fd5b620003048783880162000219565b945060208601519150808211156200031b57600080fd5b506200032a8682870162000219565b604086015190935090506001600160a01b03811681146200034a57600080fd5b809150509250925092565b600181811c908216806200036a57607f821691505b6020821081036200038b57634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620003df57600081815260208120601f850160051c81016020861015620003ba5750805b601f850160051c820191505b81811015620003db57828155600101620003c6565b5050505b505050565b81516001600160401b0381111562000400576200040062000203565b620004188162000411845462000355565b8462000391565b602080601f831160018114620004505760008415620004375750858301515b600019600386901b1c1916600185901b178555620003db565b600085815260208120601f198616915b82811015620004815788860151825594840194600190910190840162000460565b5085821015620004a05787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805161341b620004e1600039600081816104b70152818161095601528181610b2b0152611082015261341b6000f3fe608060405234801561001057600080fd5b50600436106102745760003560e01c8063736d7f8811610151578063b390c0ab116100c3578063defa80c311610087578063defa80c314610615578063e97ceaa814610628578063e985e9c51461063b578063f2fde38b1461064e578063fb25fb7a14610661578063ffa1ad741461069b57600080fd5b8063b390c0ab14610584578063b88d4fde14610597578063bbe15627146105aa578063d082e381146105f9578063d7e29cd51461060257600080fd5b806393596c7b1161011557806393596c7b146104ea57806395d89b41146104fb57806397961336146105035780639813c5b614610516578063a22cb4651461055e578063a898e3641461057157600080fd5b8063736d7f881461048457806379ba5097146104975780637cb647591461049f57806387bc1425146104b25780638da5cb5b146104d957600080fd5b80632f32f937116101ea57806344ec9344116101ae57806344ec9344146104125780636352211e14610425578063635490cc146104385780636bed127f1461044b5780636f19951c1461045e57806370a082311461047157600080fd5b80632f32f937146103815780634182e4a3146103a157806342842e0e146103d957806342966c68146103ec57806344305f2c146103ff57600080fd5b8063095ea7b31161023c578063095ea7b3146103165780630dddf0631461032957806321a8be9d1461033c57806323452b9c1461034f57806323b872dd146103575780632eb4a7ab1461036a57600080fd5b806301ffc9a714610279578063064c0a3a146102a157806306fdde03146102b6578063081812fc146102cb5780630846b3bc146102f6575b600080fd5b61028c6102873660046129be565b6106c0565b60405190151581526020015b60405180910390f35b6102b46102af366004612aa7565b61072d565b005b6102be61074c565b6040516102989190612b6d565b6102de6102d9366004612b80565b6107de565b6040516001600160a01b039091168152602001610298565b610309610304366004612b99565b610824565b6040516102989190612bb6565b6102b4610324366004612c16565b610849565b6102b4610337366004612b80565b6108d5565b6102b461034a366004612c42565b61093f565b6102b461097f565b6102b4610365366004612c64565b6109d5565b610373600b5481565b604051908152602001610298565b61039461038f366004612c42565b610a00565b6040516102989190612ca5565b61028c6103af366004612c16565b6001600160a01b039190911660009081526008602090815260408083209383529290522054151590565b6102b46103e7366004612c64565b610a99565b6102b46103fa366004612b80565b610ab4565b6102b461040d366004612ce9565b610abf565b610309610420366004612b80565b610c8e565b6102de610433366004612b80565b610d0d565b6102b4610446366004612d92565b610d9d565b6102b4610459366004612de2565b610eaf565b61030961046c366004612b80565b610ef5565b61037361047f366004612b99565b610f65565b6102b4610492366004612e17565b610faa565b6102b4611142565b6102b46104ad366004612b80565b6111c8565b6102de7f000000000000000000000000000000000000000000000000000000000000000081565b6009546001600160a01b03166102de565b600a546001600160a01b03166102de565b6102be61120b565b6102b4610511366004612c42565b61121a565b610373610524366004612e83565b6040516001600160c01b031960c083901b166020820152600090602801604051602081830303815290604052805190602001209050919050565b6102b461056c366004612ebb565b61122e565b61039461057f366004612c42565b6112c3565b610373610592366004612c42565b611320565b6102b46105a5366004612ef4565b61133e565b6105dc6105b8366004612b99565b600d6020526000908152604090208054600182015460029092015490919060ff1683565b604080519384526020840192909252151590820152606001610298565b610373600c5481565b6102b4610610366004612f60565b61135b565b6102b4610623366004613006565b611496565b6102b46106363660046130a2565b6114bb565b61028c6106493660046130e1565b6114d1565b6102b461065c366004612b99565b6114ff565b61067461066f366004612b80565b611513565b604080516001600160a01b0390941684526020840192909252151590820152606001610298565b6102be60405180604001604052806006815260200165302e32372e3160d01b81525081565b60006001600160e01b031982166301ffc9a760e01b14806106f157506001600160e01b031982166380ac58cd60e01b145b8061070c57506001600160e01b03198216635b5e139f60e01b145b8061072757506001600160e01b031982166342b0e56f60e01b145b92915050565b8261073781611594565b61074486868686866115bb565b505050505050565b60606000805461075b9061310f565b80601f01602080910402602001604051908101604052809291908181526020018280546107879061310f565b80156107d45780601f106107a9576101008083540402835291602001916107d4565b820191906000526020600020905b8154815290600101906020018083116107b757829003601f168201915b5050505050905090565b60006107e9826117a0565b60008281526003602052604081209061080184610d0d565b6001600160a01b0390811682526020820192909252604001600020541692915050565b6001600160a01b0381166000908152600d602052604090205460609061072790610ef5565b600061085482610d0d565b9050806001600160a01b0316836001600160a01b03160361088857604051630591db6d60e01b815260040160405180910390fd5b336001600160a01b038216148015906108a857506108a681336114d1565b155b156108c657604051634c12315960e11b815260040160405180910390fd5b6108d083836117d8565b505050565b6108dd611851565b60006108e882611513565b50506001600160a01b0381166000908152600d6020526040808220600201805460ff191660011790555191925083917f574b2683ce92a856b9955684fd27d399f1c64effd906c166d1939864c24b07299190a25050565b336000908152600d602052604090205461097b90837f00000000000000000000000000000000000000000000000000000000000000008461187e565b5050565b610987611851565b600a80546001600160a01b031981169091556040516001600160a01b0390911690819033907fe83a760af9d3c86797ea13c8979010086f067cfe3c985b2d03d951248600c50f90600090a350565b806109df81611594565b6109fa8484846040518060200160405280600081525061198c565b50505050565b604080518082019091526000808252602082015281610a1e84610ef5565b5111610a3d5760405163653e642560e11b815260040160405180910390fd5b6000838152600660205260408120805484908110610a5d57610a5d613149565b60009182526020918290206040805180820190915260029092020180548252600101546001600160a01b03169181019190915291505092915050565b6108d08383836040518060200160405280600081525061133e565b61097b816000611320565b336000908152600d602052604081205490819003610b1d5760405162461bcd60e51b8152602060048201526016602482015275141c9bd99a5b1948191bd95cc81b9bdd08195e1a5cdd60521b60448201526064015b60405180910390fd5b60005b82518110156108d0577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316631ffeaf968384868581518110610b6c57610b6c613149565b60200260200101516040518463ffffffff1660e01b8152600401610b929392919061315f565b600060405180830381600087803b158015610bac57600080fd5b505af1158015610bc0573d6000803e3d6000fd5b505050506000610bcf83610c8e565b905060008160018351610be291906131af565b81518110610bf257610bf2613149565b60200260200101519050610c1a8460018451610c0e91906131af565b6020840151845161187e565b336000908152600d60205260409020855160019160030190879086908110610c4457610c44613149565b6020026020010151604051610c5991906131c2565b908152604051908190036020019020805491151560ff1990921691909117905550819050610c86816131de565b915050610b20565b6000818152600760209081526040808320805482518185028101850190935280835260609493849084015b82821015610d015760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b0316828401529083529092019101610cb9565b50929695505050505050565b600080600080610d1c85611513565b9250925092508015610d94576040516331a9108f60e11b8152600481018390526001600160a01b03841690636352211e90602401602060405180830381865afa158015610d6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d9191906131f7565b92505b50909392505050565b610da6836117a0565b33803b610dc65760405163b9d3114760e01b815260040160405180910390fd5b604080518082019091528381526001600160a01b03821660208201526000610ded86610c8e565b5190506080811015610e4c576000868152600760209081526040822080546001808201835591845292829020855160029094020192835590840151910180546001600160a01b0319166001600160a01b03909216919091179055610e65565b60405163a53c8c0560e01b815260040160405180910390fd5b84836001600160a01b0316877fe65085e689b77b126ba0bac3b079aa8288f19f4d5445af11c76003f8ab3075dd84604051610ea291815260200190565b60405180910390a4610744565b336000908152600d60205260408082209051600390910190610ed29084906131c2565b908152604051908190036020019020805491151560ff1990921691909117905550565b6000818152600660209081526040808320805482518185028101850190935280835260609493849084018215610d015760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b0316828401529083529092019101610cb9565b60006001600160a01b038216610f8e57604051633bb9143360e11b815260040160405180910390fd5b506001600160a01b031660009081526002602052604090205490565b336000908152600d6020526040812054908190036110025760405162461bcd60e51b815260206004820152601560248201527414d95b99195c88191bd95cc81b9bdd08195e1a5cdd605a1b6044820152606401610b14565b6001600160a01b0384166000908152600d60205260408120549081900361106b5760405162461bcd60e51b815260206004820152601760248201527f526563656976657220646f6573206e6f742065786973740000000000000000006044820152606401610b14565b604051630fff57cb60e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690631ffeaf96906110bd908590859089908990600401613214565b600060405180830381600087803b1580156110d757600080fd5b505af11580156110eb573d6000803e3d6000fd5b5050505060006110fa82610c8e565b90506000816001835161110d91906131af565b8151811061111d5761111d613149565b602002602001015190506111398360018451610c0e91906131af565b50505050505050565b600a546001600160a01b0316331461116d5760405163015aa1e360e11b815260040160405180910390fd5b600980546001600160a01b031980821633908117909355600a805490911690556040516001600160a01b03909116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6111d0611851565b600b8190556040518181527f90004c04698bc3322499a575ed3752dd4abf33e0a7294c06a787a0fe01bea9419060200160405180910390a150565b60606001805461075b9061310f565b8161122481611ad6565b6108d08383611afd565b6001600160a01b038216330361125757604051630b7b99b960e21b815260040160405180910390fd5b3360008181526004602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b6040805180820190915260008082526020820152816112e184610c8e565b51116113005760405163da22687f60e01b815260040160405180910390fd5b6000838152600760205260408120805484908110610a5d57610a5d613149565b60008261132c81611594565b6113368484611b72565b949350505050565b8161134881611594565b61135485858585611ecc565b5050505050565b600b5415801590611376575061137481600b5484611f01565b155b15611396576040516313e5138760e01b8152336004820152602401610b14565b336000908152600d6020526040902060010154156113c95760405163a760ecd560e01b8152336004820152602401610b14565b6000838152600e602052604090205460ff16156113fc576040516306a4b6d160e51b815260048101849052602401610b14565b61142433600c546040518060400160405280600381526020016203078360ec1b815250611f17565b600c8054336000818152600d602090815260408083209485556001948501899055888352600e909152808220805460ff191690941790935592549151919290917fcb939889322fed25104a5d8945bde5162563412a1fd718946cdcacb03088ea089190a35050600c8054600101905550565b876114a081611ad6565b6114b08989898989898989611f4c565b505050505050505050565b836114c581611ad6565b6113548585858561187e565b6001600160a01b03918216600090815260046020908152604080832093909416825291909152205460ff1690565b611507611851565b61151081612130565b50565b60008181526005602090815260408083208151606081018352815481526001909101546001600160a01b038116938201849052600160a01b900460ff16151591810191909152829182919061157b5760405163089ba7e160e41b815260040160405180910390fd5b6020810151815160409092015190969195509350915050565b61159e33826121cc565b611510576040516345f3c98360e11b815260040160405180910390fd5b6000806115c785611513565b5091509150866001600160a01b0316826001600160a01b0316146115fe5760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b038616611625576040516338f646ff60e21b815260040160405180910390fd5b6001600160a01b0386163014801561163c57508385145b1561165a57604051633d76b10760e01b815260040160405180910390fd5b6001600160a01b0386163b6116825760405163b9d3114760e01b815260040160405180910390fd5b6040516301ffc9a760e01b81526342b0e56f60e01b60048201526001600160a01b038716906301ffc9a790602401602060405180830381865afa1580156116cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116f19190613250565b61170e57604051631784ec7360e21b815260040160405180910390fd5b61171985878661224f565b611727828783878988612340565b6001600160a01b03871660009081526002602052604081208054600192906117509084906131af565b909155506117639050858588600161239b565b6001600160a01b038616600090815260026020526040812080546001929061178c90849061326d565b909155506111399050828783878988612412565b6000818152600560205260409020600101546001600160a01b03166115105760405163089ba7e160e41b815260040160405180910390fd5b60006117e382610d0d565b60008381526003602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a4505050565b6009546001600160a01b0316331461187c57604051636db2465f60e01b815260040160405180910390fd5b565b600061188a85856112c3565b90506118978184846124ef565b6001600160a01b0383166000908152600860209081526040808320858452909152902054156118d95760405163188a497360e01b815260040160405180910390fd5b60008581526007602052604090206118f19085612533565b600085815260066020908152604080832080546001808201835591855283852086516002909202019081558584015190820180546001600160a01b0319166001600160a01b03928316179055871680855260088452828520878652845293829020555186815284929188917f29486b9e2ae569b440933a9b1b421467306fa21f3dcad439c262910a634963a9910160405180910390a4611354565b60008061199884611513565b5091509150856001600160a01b0316826001600160a01b0316146119cf5760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b0385166119f6576040516338f646ff60e21b815260040160405180910390fd5b611a0582868360008888612340565b6001600160a01b0386166000908152600260205260408120805460019290611a2e9084906131af565b90915550611a419050846000878161239b565b6001600160a01b0385166000908152600260205260408120805460019290611a6a90849061326d565b909155505060405184906001600160a01b0380881691908916906000805160206133c683398151915290600090a483856001600160a01b0316836001600160a01b03166000805160206133a6833981519152846000604051610ea2929190918252602082015260400190565b611ae033826125ed565b611510576040516302728a9d60e41b815260040160405180910390fd5b600082815260076020526040902054811015611b2c57604051631e73178b60e11b815260040160405180910390fd5b6000828152600760205260408120611b4391612961565b60405182907f8ac4a0d65950c3e40448afb2260e2e0ec36ea15644d9b39e37e85472e5f9445190600090a25050565b6000806000611b8085611513565b50915091506000611b9086610d0d565b6001600160a01b03841660009081526002602052604081208054929350600192909190611bbe9084906131af565b90915550611bc99050565b611be88360008460008a60405180602001604052806000815250612340565b611bf36000876117d8565b6000611bfe87610ef5565b6000888152600660205260408120919250611c199190612961565b6000878152600760205260408120611c3091612961565b60008781526003602090815260408083206001600160a01b0386168452909152812080546001600160a01b031916905581518190815b81811015611e3157898310611cdb57848181518110611c8757611c87613149565b602002602001015160200151858281518110611ca557611ca5613149565b6020908102919091010151516040516306177b2560e41b81526001600160a01b0390921660048301526024820152604401610b14565b60086000868381518110611cf157611cf1613149565b6020026020010151602001516001600160a01b03166001600160a01b031681526020019081526020016000206000868381518110611d3157611d31613149565b602002602001015160000151815260200190815260200160002060009055828a039350848181518110611d6657611d66613149565b6020026020010151602001516001600160a01b031663b390c0ab868381518110611d9257611d92613149565b602002602001015160000151600187611dab91906131af565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af1158015611dee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e129190613280565b611e1d90600161326d565b611e27908461326d565b9250600101611c66565b5060008a81526005602052604080822082815560010180546001600160a81b0319169055518b91906001600160a01b038816906000805160206133c6833981519152908390a4604080518781526000602082018190528c9290916001600160a01b038b16916000805160206133a6833981519152910160405180910390a46040805160208101909152600090525b5098975050505050505050565b611ed88484848461198c565b611ee48484848461264b565b6109fa5760405163bcb5663760e01b815260040160405180910390fd5b600082611f0e858461274d565b14949350505050565b611f2283838361279a565b611f2f600084848461264b565b6108d05760405163bcb5663760e01b815260040160405180910390fd5b60408051808201909152600080825260208201528215611f7757611f7089876112c3565b9050611f84565b611f818987610a00565b90505b611f8f8186866124ef565b8215611fb2576000898152600760205260409020611fad9087612533565b611fec565b6001600160a01b038516600090815260086020908152604080832087845282528083208390558b835260069091529020611fec9087612533565b6001600160a01b038816156120d9578660000361206c57604051635c46a7ef60e11b81526001600160a01b0386169063b88d4fde906120359030908c9089908890600401613299565b600060405180830381600087803b15801561204f57600080fd5b505af1158015612063573d6000803e3d6000fd5b505050506120d9565b60208101518151604051630326051d60e11b81526001600160a01b039092169163064c0a3a916120a69130918d918d9089906004016132d6565b600060405180830381600087803b1580156120c057600080fd5b505af11580156120d4573d6000803e3d6000fd5b505050505b6040805187815284151560208201526001600160a01b038a81161582840152915186928816918c917f02d6d6dbcb604d5e1e8c7886456e82a9cdce88b0a580071358f206b5a4d58f709181900360600190a46114b0565b6001600160a01b038116612157576040516342b66a3d60e01b815260040160405180910390fd5b336001600160a01b0382160361218057604051636d6c4ee560e11b815260040160405180910390fd5b600a80546001600160a01b0319166001600160a01b03831690811790915560405133907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b60008060006121da84611513565b5091509150806000146121fd57506001600160a01b038481169116149050610727565b816001600160a01b0316856001600160a01b03161480612222575061222282866114d1565b806122465750846001600160a01b031661223b856107de565b6001600160a01b0316145b95945050505050565b60005b6064811015612326576000806000856001600160a01b031663fb25fb7a866040518263ffffffff1660e01b815260040161228e91815260200190565b606060405180830381865afa1580156122ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122cf9190613310565b925092509250806122e35750505050505050565b6001600160a01b038316301480156122fa57508682145b15612318576040516324543e6d60e11b815260040160405180910390fd5b509093509150600101612252565b50604051630349a6bd60e51b815260040160405180910390fd5b6001600160a01b038616158061235d57506001600160a01b038516155b6107445760405162461bcd60e51b815260206004820152600f60248201526e29b7bab63137bab732103a37b5b2b760891b6044820152606401610b14565b604080516060810182528481526001600160a01b03808516602080840191825285151584860190815260008a815260059092529481209351845590516001909301805494511515600160a01b026001600160a81b031990951693909216929092179290921790915561240d90856117d8565b6109fa565b6040516318d5243360e21b815285906001600160a01b0382169063635490cc9061244490879087908790600401613353565b600060405180830381600087803b15801561245e57600080fd5b505af1158015612472573d6000803e3d6000fd5b5050505082866001600160a01b0316886001600160a01b03166000805160206133c683398151915260405160405180910390a482866001600160a01b0316886001600160a01b03166000805160206133a683398151915288886040516124e2929190918252602082015260400190565b60405180910390a4611139565b82602001516001600160a01b0316826001600160a01b0316141580612515575082518114155b156108d057604051637383f2c160e11b815260040160405180910390fd5b81548290612543906001906131af565b8154811061255357612553613149565b906000526020600020906002020182828154811061257357612573613149565b600091825260209091208254600290920201908155600191820154910180546001600160a01b0319166001600160a01b0390921691909117905581548290806125be576125be613372565b60008281526020812060026000199093019283020190815560010180546001600160a01b031916905590555050565b6000806125f983610d0d565b9050806001600160a01b0316846001600160a01b03161480612620575061262081856114d1565b806113365750836001600160a01b0316612639846107de565b6001600160a01b031614949350505050565b60006001600160a01b0384163b1561274257604051630a85bd0160e11b81526001600160a01b0385169063150b7a029061268f903390899088908890600401613299565b6020604051808303816000875af19250505080156126ca575060408051601f3d908101601f191682019092526126c791810190613388565b60015b612728573d8080156126f8576040519150601f19603f3d011682016040523d82523d6000602084013e6126fd565b606091505b5080516000036127205760405163bcb5663760e01b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050611336565b506001949350505050565b600081815b84518110156127925761277e8286838151811061277157612771613149565b6020026020010151612808565b91508061278a816131de565b915050612752565b509392505050565b6127a7838360008461283a565b60405182906001600160a01b038516906000906000805160206133c6833981519152908290a46040805160008082526020820181905284926001600160a01b038716926000805160206133a6833981519152910160405180910390a4505050565b6000818310612824576000828152602084905260409020612833565b60008381526020839052604090205b9392505050565b6001600160a01b038416612861576040516325bd6bd360e01b815260040160405180910390fd5b6000838152600560205260409020600101546001600160a01b03161561289a5760405163c5a8d37160e01b815260040160405180910390fd5b826000036128bb576040516312c33ce360e01b815260040160405180910390fd5b6128cb6000856000858786612340565b6001600160a01b03841660009081526002602052604081208054600192906128f490849061326d565b9091555050604080516060810182528381526001600160a01b039586166020808301918252941515828401908152600096875260059095529190942093518455516001909301805492511515600160a01b026001600160a81b031990931693909416929092171790915550565b508054600082556002029060005260206000209081019061151091905b808211156129a457600081556001810180546001600160a01b031916905560020161297e565b5090565b6001600160e01b03198116811461151057600080fd5b6000602082840312156129d057600080fd5b8135612833816129a8565b6001600160a01b038116811461151057600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612a2f57612a2f6129f0565b604052919050565b600082601f830112612a4857600080fd5b813567ffffffffffffffff811115612a6257612a626129f0565b612a75601f8201601f1916602001612a06565b818152846020838601011115612a8a57600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600060a08688031215612abf57600080fd5b8535612aca816129db565b94506020860135612ada816129db565b93506040860135925060608601359150608086013567ffffffffffffffff811115612b0457600080fd5b612b1088828901612a37565b9150509295509295909350565b60005b83811015612b38578181015183820152602001612b20565b50506000910152565b60008151808452612b59816020860160208601612b1d565b601f01601f19169290920160200192915050565b6020815260006128336020830184612b41565b600060208284031215612b9257600080fd5b5035919050565b600060208284031215612bab57600080fd5b8135612833816129db565b602080825282518282018190526000919060409081850190868401855b82811015612c0957612bf9848351805182526020908101516001600160a01b0316910152565b9284019290850190600101612bd3565b5091979650505050505050565b60008060408385031215612c2957600080fd5b8235612c34816129db565b946020939093013593505050565b60008060408385031215612c5557600080fd5b50508035926020909101359150565b600080600060608486031215612c7957600080fd5b8335612c84816129db565b92506020840135612c94816129db565b929592945050506040919091013590565b815181526020808301516001600160a01b03169082015260408101610727565b600067ffffffffffffffff821115612cdf57612cdf6129f0565b5060051b60200190565b60006020808385031215612cfc57600080fd5b823567ffffffffffffffff80821115612d1457600080fd5b818501915085601f830112612d2857600080fd5b8135612d3b612d3682612cc5565b612a06565b81815260059190911b83018401908481019088831115612d5a57600080fd5b8585015b83811015611ebf57803585811115612d765760008081fd5b612d848b89838a0101612a37565b845250918601918601612d5e565b600080600060608486031215612da757600080fd5b8335925060208401359150604084013567ffffffffffffffff811115612dcc57600080fd5b612dd886828701612a37565b9150509250925092565b600060208284031215612df457600080fd5b813567ffffffffffffffff811115612e0b57600080fd5b61133684828501612a37565b600080600060608486031215612e2c57600080fd5b8335612e37816129db565b9250602084013567ffffffffffffffff80821115612e5457600080fd5b612e6087838801612a37565b93506040860135915080821115612e7657600080fd5b50612dd886828701612a37565b600060208284031215612e9557600080fd5b813567ffffffffffffffff8116811461283357600080fd5b801515811461151057600080fd5b60008060408385031215612ece57600080fd5b8235612ed9816129db565b91506020830135612ee981612ead565b809150509250929050565b60008060008060808587031215612f0a57600080fd5b8435612f15816129db565b93506020850135612f25816129db565b925060408501359150606085013567ffffffffffffffff811115612f4857600080fd5b612f5487828801612a37565b91505092959194509250565b600080600060608486031215612f7557600080fd5b833592506020808501359250604085013567ffffffffffffffff811115612f9b57600080fd5b8501601f81018713612fac57600080fd5b8035612fba612d3682612cc5565b81815260059190911b82018301908381019089831115612fd957600080fd5b928401925b82841015612ff757833582529284019290840190612fde565b80955050505050509250925092565b600080600080600080600080610100898b03121561302357600080fd5b883597506020890135613035816129db565b965060408901359550606089013594506080890135613053816129db565b935060a0890135925060c089013561306a81612ead565b915060e089013567ffffffffffffffff81111561308657600080fd5b6130928b828c01612a37565b9150509295985092959890939650565b600080600080608085870312156130b857600080fd5b843593506020850135925060408501356130d1816129db565b9396929550929360600135925050565b600080604083850312156130f457600080fd5b82356130ff816129db565b91506020830135612ee9816129db565b600181811c9082168061312357607f821691505b60208210810361314357634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052603260045260246000fd5b83815282602082015260806040820152600061317e6080830184612b41565b82810360608401526000815260208101915050949350505050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561072757610727613199565b600082516131d4818460208701612b1d565b9190910192915050565b6000600182016131f0576131f0613199565b5060010190565b60006020828403121561320957600080fd5b8151612833816129db565b8481528360208201526080604082015260006132336080830185612b41565b82810360608401526132458185612b41565b979650505050505050565b60006020828403121561326257600080fd5b815161283381612ead565b8082018082111561072757610727613199565b60006020828403121561329257600080fd5b5051919050565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906132cc90830184612b41565b9695505050505050565b6001600160a01b03868116825285166020820152604081018490526060810183905260a06080820181905260009061324590830184612b41565b60008060006060848603121561332557600080fd5b8351613330816129db565b60208501516040860151919450925061334881612ead565b809150509250925092565b8381528260208201526060604082015260006122466060830184612b41565b634e487b7160e01b600052603160045260246000fd5b60006020828403121561339a57600080fd5b8151612833816129a856fe04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552dddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220cc09a863b0d2bb13cb76e1f7d2dc71b68e2e23f891786da65fa3d78561e30e4d64736f6c6343000812003360806040523480156200001157600080fd5b506040516200368a3803806200368a83398101604081905262000034916200022d565b806000848481818462000048838262000349565b50600162000057828262000349565b5050506001600160a01b0384169150620000869050576040516342b66a3d60e01b815260040160405180910390fd5b600980546001600160a01b0319166001600160a01b0384811691909117909155811615620000b957620000b981620000ca565b50506001600b555062000415915050565b6001600160a01b038116620000f2576040516342b66a3d60e01b815260040160405180910390fd5b336001600160a01b038216036200011c57604051636d6c4ee560e11b815260040160405180910390fd5b600a80546001600160a01b0319166001600160a01b03831690811790915560405133907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200019057600080fd5b81516001600160401b0380821115620001ad57620001ad62000168565b604051601f8301601f19908116603f01168101908282118183101715620001d857620001d862000168565b81604052838152602092508683858801011115620001f557600080fd5b600091505b83821015620002195785820183015181830184015290820190620001fa565b600093810190920192909252949350505050565b6000806000606084860312156200024357600080fd5b83516001600160401b03808211156200025b57600080fd5b62000269878388016200017e565b945060208601519150808211156200028057600080fd5b506200028f868287016200017e565b604086015190935090506001600160a01b0381168114620002af57600080fd5b809150509250925092565b600181811c90821680620002cf57607f821691505b602082108103620002f057634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200034457600081815260208120601f850160051c810160208610156200031f5750805b601f850160051c820191505b8181101562000340578281556001016200032b565b5050505b505050565b81516001600160401b0381111562000365576200036562000168565b6200037d81620003768454620002ba565b84620002f6565b602080601f831160018114620003b557600084156200039c5750858301515b600019600386901b1c1916600185901b17855562000340565b600085815260208120601f198616915b82811015620003e657888601518255948401946001909101908401620003c5565b5085821015620004055787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b61326580620004256000396000f3fe608060405234801561001057600080fd5b50600436106102115760003560e01c806379ba509711610125578063b390c0ab116100ad578063e97ceaa81161007c578063e97ceaa8146104ba578063e985e9c5146104cd578063f2fde38b146104e0578063fb25fb7a146104f3578063ffa1ad741461052d57600080fd5b8063b390c0ab1461045d578063b88d4fde14610470578063defa80c314610483578063e343537d1461049657600080fd5b806395d89b41116100f457806395d89b41146104095780639796133614610411578063a22cb46514610424578063a898e36414610437578063ae882b651461044a57600080fd5b806379ba5097146103cc5780638ad91345146103d45780638da5cb5b146103e757806393596c7b146103f857600080fd5b80632f32f937116101a857806344ec93441161017757806344ec9344146103525780636352211e14610372578063635490cc146103855780636f19951c1461039857806370a08231146103ab57600080fd5b80632f32f937146102d45780634182e4a3146102f457806342842e0e1461032c57806342966c681461033f57600080fd5b8063095ea7b3116101e4578063095ea7b3146102935780631ffeaf96146102a657806323452b9c146102b957806323b872dd146102c157600080fd5b806301ffc9a714610216578063064c0a3a1461023e57806306fdde0314610253578063081812fc14610268575b600080fd5b6102296102243660046127c8565b610552565b60405190151581526020015b60405180910390f35b61025161024c3660046128a4565b6105bf565b005b61025b6105de565b604051610235919061296a565b61027b61027636600461297d565b610670565b6040516001600160a01b039091168152602001610235565b6102516102a1366004612996565b6106b6565b6102516102b43660046129c2565b610742565b610251610936565b6102516102cf366004612a39565b61098c565b6102e76102e2366004612a7a565b6109b7565b6040516102359190612a9c565b610229610302366004612996565b6001600160a01b039190911660009081526008602090815260408083209383529290522054151590565b61025161033a366004612a39565b610a50565b61025161034d36600461297d565b610a6b565b61036561036036600461297d565b610a7a565b6040516102359190612abc565b61027b61038036600461297d565b610af9565b610251610393366004612b1c565b610b89565b6103656103a636600461297d565b610c9b565b6103be6103b9366004612b6c565b610d0b565b604051908152602001610235565b610251610d50565b61025b6103e236600461297d565b610dd6565b6009546001600160a01b031661027b565b600a546001600160a01b031661027b565b61025b610e78565b61025161041f366004612a7a565b610e87565b610251610432366004612b97565b610e9b565b6102e7610445366004612a7a565b610f30565b61025b61045836600461297d565b610f8d565b6103be61046b366004612a7a565b611027565b61025161047e366004612bd0565b611045565b610251610491366004612c30565b611062565b6104a96104a436600461297d565b611087565b604051610235959493929190612ccc565b6102516104c8366004612d0f565b6111c8565b6102296104db366004612d4e565b6111de565b6102516104ee366004612b6c565b61120c565b61050661050136600461297d565b611220565b604080516001600160a01b0390941684526020840192909252151590820152606001610235565b61025b60405180604001604052806006815260200165302e32372e3160d01b81525081565b60006001600160e01b031982166301ffc9a760e01b148061058357506001600160e01b031982166380ac58cd60e01b145b8061059e57506001600160e01b03198216635b5e139f60e01b145b806105b957506001600160e01b031982166342b0e56f60e01b145b92915050565b826105c9816112a1565b6105d686868686866112c8565b505050505050565b6060600080546105ed90612d7c565b80601f016020809104026020016040519081016040528092919081815260200182805461061990612d7c565b80156106665780601f1061063b57610100808354040283529160200191610666565b820191906000526020600020905b81548152906001019060200180831161064957829003601f168201915b5050505050905090565b600061067b826114a8565b60008281526003602052604081209061069384610af9565b6001600160a01b0390811682526020820192909252604001600020541692915050565b60006106c182610af9565b9050806001600160a01b0316836001600160a01b0316036106f557604051630591db6d60e01b815260040160405180910390fd5b336001600160a01b03821614801590610715575061071381336111de565b155b1561073357604051634c12315960e11b815260040160405180910390fd5b61073d83836114e0565b505050565b61074a611559565b6000610757858585611586565b6000818152600e602052604090205490915060ff16156107be5760405162461bcd60e51b815260206004820152601960248201527f52657075746174696f6e20616c7265616479206d696e7465640000000000000060448201526064015b60405180910390fd5b6000818152600e60205260409020805460ff191660011790556107f66107ec6009546001600160a01b031690565b600b5486856115bc565b60006108208460405160200161080c9190612db6565b60405160208183030381529060405261168c565b90506000816040516020016108359190612e82565b60408051601f19818403018152918152600b546000908152600d602052209091506108608282612f0d565b506040805160a081018252600b5480825260208083018b81528385018b8152606085018b8152608086018b90526000948552600c90935294909220835181559151600183015592516002820155915190919060038201906108c19082612f0d565b50608082015160048201906108d69082612f0d565b50905050600b546108ef6009546001600160a01b031690565b6001600160a01b03167f42bcf4689d0211adce4e556ee455319d34e671e8b8a762034e146461fb1be82d60405160405180910390a35050600b805460010190555050505050565b61093e611559565b600a80546001600160a01b031981169091556040516001600160a01b0390911690819033907fe83a760af9d3c86797ea13c8979010086f067cfe3c985b2d03d951248600c50f90600090a350565b80610996816112a1565b6109b1848484604051806020016040528060008152506117df565b50505050565b6040805180820190915260008082526020820152816109d584610c9b565b51116109f45760405163653e642560e11b815260040160405180910390fd5b6000838152600660205260408120805484908110610a1457610a14612fcd565b60009182526020918290206040805180820190915260029092020180548252600101546001600160a01b03169181019190915291505092915050565b61073d83838360405180602001604052806000815250611045565b610a76816000611027565b5050565b6000818152600760209081526040808320805482518185028101850190935280835260609493849084015b82821015610aed5760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b0316828401529083529092019101610aa5565b50929695505050505050565b600080600080610b0885611220565b9250925092508015610b80576040516331a9108f60e11b8152600481018390526001600160a01b03841690636352211e90602401602060405180830381865afa158015610b59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7d9190612fe3565b92505b50909392505050565b610b92836114a8565b33803b610bb25760405163b9d3114760e01b815260040160405180910390fd5b604080518082019091528381526001600160a01b03821660208201526000610bd986610a7a565b5190506080811015610c38576000868152600760209081526040822080546001808201835591845292829020855160029094020192835590840151910180546001600160a01b0319166001600160a01b03909216919091179055610c51565b60405163a53c8c0560e01b815260040160405180910390fd5b84836001600160a01b0316877fe65085e689b77b126ba0bac3b079aa8288f19f4d5445af11c76003f8ab3075dd84604051610c8e91815260200190565b60405180910390a46105d6565b6000818152600660209081526040808320805482518185028101850190935280835260609493849084018215610aed5760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b0316828401529083529092019101610aa5565b60006001600160a01b038216610d3457604051633bb9143360e11b815260040160405180910390fd5b506001600160a01b031660009081526002602052604090205490565b600a546001600160a01b03163314610d7b5760405163015aa1e360e11b815260040160405180910390fd5b600980546001600160a01b031980821633908117909355600a805490911690556040516001600160a01b03909116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6000818152600d60205260409020805460609190610df390612d7c565b80601f0160208091040260200160405190810160405280929190818152602001828054610e1f90612d7c565b8015610e6c5780601f10610e4157610100808354040283529160200191610e6c565b820191906000526020600020905b815481529060010190602001808311610e4f57829003601f168201915b50505050509050919050565b6060600180546105ed90612d7c565b81610e918161193e565b61073d8383611965565b6001600160a01b0382163303610ec457604051630b7b99b960e21b815260040160405180910390fd5b3360008181526004602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b604080518082019091526000808252602082015281610f4e84610a7a565b5111610f6d5760405163da22687f60e01b815260040160405180910390fd5b6000838152600760205260408120805484908110610a1457610a14612fcd565b600d6020526000908152604090208054610fa690612d7c565b80601f0160208091040260200160405190810160405280929190818152602001828054610fd290612d7c565b801561101f5780601f10610ff45761010080835404028352916020019161101f565b820191906000526020600020905b81548152906001019060200180831161100257829003601f168201915b505050505081565b600082611033816112a1565b61103d84846119da565b949350505050565b8161104f816112a1565b61105b85858585611d47565b5050505050565b8761106c8161193e565b61107c8989898989898989611d7c565b505050505050505050565b600c602052600090815260409020805460018201546002830154600384018054939492939192916110b790612d7c565b80601f01602080910402602001604051908101604052809291908181526020018280546110e390612d7c565b80156111305780601f1061110557610100808354040283529160200191611130565b820191906000526020600020905b81548152906001019060200180831161111357829003601f168201915b50505050509080600401805461114590612d7c565b80601f016020809104026020016040519081016040528092919081815260200182805461117190612d7c565b80156111be5780601f10611193576101008083540402835291602001916111be565b820191906000526020600020905b8154815290600101906020018083116111a157829003601f168201915b5050505050905085565b836111d28161193e565b61105b85858585611f60565b6001600160a01b03918216600090815260046020908152604080832093909416825291909152205460ff1690565b611214611559565b61121d8161206e565b50565b60008181526005602090815260408083208151606081018352815481526001909101546001600160a01b038116938201849052600160a01b900460ff1615159181019190915282918291906112885760405163089ba7e160e41b815260040160405180910390fd5b6020810151815160409092015190969195509350915050565b6112ab338261210a565b61121d576040516345f3c98360e11b815260040160405180910390fd5b6000806112d485611220565b5091509150866001600160a01b0316826001600160a01b03161461130b5760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b038616611332576040516338f646ff60e21b815260040160405180910390fd5b6001600160a01b0386163014801561134957508385145b1561136757604051633d76b10760e01b815260040160405180910390fd5b6001600160a01b0386163b61138f5760405163b9d3114760e01b815260040160405180910390fd5b6040516301ffc9a760e01b81526342b0e56f60e01b60048201526001600160a01b038716906301ffc9a790602401602060405180830381865afa1580156113da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113fe9190613000565b61141b57604051631784ec7360e21b815260040160405180910390fd5b61142685878661218d565b6001600160a01b038716600090815260026020526040812080546001929061144f908490613033565b909155506114629050858588600161227e565b6001600160a01b038616600090815260026020526040812080546001929061148b908490613046565b9091555061149f90508287838789886122f5565b50505050505050565b6000818152600560205260409020600101546001600160a01b031661121d5760405163089ba7e160e41b815260040160405180910390fd5b60006114eb82610af9565b60008381526003602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a4505050565b6009546001600160a01b0316331461158457604051636db2465f60e01b815260040160405180910390fd5b565b600083838360405160200161159d93929190613059565b6040516020818303038152906040528051906020012090509392505050565b6001600160a01b0384163b6115e45760405163b9d3114760e01b815260040160405180910390fd5b6040516301ffc9a760e01b81526342b0e56f60e01b60048201526001600160a01b038516906301ffc9a790602401602060405180830381865afa15801561162f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116539190613000565b6116705760405163fbd5d8b960e01b815260040160405180910390fd5b61167c848484846123f6565b6109b160008560008587866122f5565b606081516000036116ab57505060408051602081019091526000815290565b60006040518060600160405280604081526020016131f060409139905060006003845160026116da9190613046565b6116e49190613086565b6116ef9060046130a8565b67ffffffffffffffff81111561170757611707612801565b6040519080825280601f01601f191660200182016040528015611731576020820181803683370190505b509050600182016020820185865187015b8082101561179d576003820191508151603f8160121c168501518453600184019350603f81600c1c168501518453600184019350603f8160061c168501518453600184019350603f8116850151845350600183019250611742565b50506003865106600181146117b957600281146117cc576117d4565b603d6001830353603d60028303536117d4565b603d60018303535b509195945050505050565b6000806117eb84611220565b5091509150856001600160a01b0316826001600160a01b0316146118225760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b038516611849576040516338f646ff60e21b815260040160405180910390fd5b6001600160a01b0386166000908152600260205260408120805460019290611872908490613033565b909155506118859050846000878161227e565b6001600160a01b03851660009081526002602052604081208054600192906118ae908490613046565b909155505060405184906001600160a01b0380881691908916907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90600090a483856001600160a01b0316836001600160a01b03167f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d846000604051610c8e929190918252602082015260400190565b611948338261250d565b61121d576040516302728a9d60e41b815260040160405180910390fd5b60008281526007602052604090205481101561199457604051631e73178b60e11b815260040160405180910390fd5b60008281526007602052604081206119ab9161276b565b60405182907f8ac4a0d65950c3e40448afb2260e2e0ec36ea15644d9b39e37e85472e5f9445190600090a25050565b60008060006119e885611220565b509150915060006119f886610af9565b6001600160a01b03841660009081526002602052604081208054929350600192909190611a26908490613033565b90915550611a319050565b604080516020810190915260009052611a4b6000876114e0565b6000611a5687610c9b565b6000888152600660205260408120919250611a71919061276b565b6000878152600760205260408120611a889161276b565b60008781526003602090815260408083206001600160a01b0386168452909152812080546001600160a01b031916905581518190815b81811015611c8957898310611b3357848181518110611adf57611adf612fcd565b602002602001015160200151858281518110611afd57611afd612fcd565b6020908102919091010151516040516306177b2560e41b81526001600160a01b03909216600483015260248201526044016107b5565b60086000868381518110611b4957611b49612fcd565b6020026020010151602001516001600160a01b03166001600160a01b031681526020019081526020016000206000868381518110611b8957611b89612fcd565b602002602001015160000151815260200190815260200160002060009055828a039350848181518110611bbe57611bbe612fcd565b6020026020010151602001516001600160a01b031663b390c0ab868381518110611bea57611bea612fcd565b602002602001015160000151600187611c039190613033565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af1158015611c46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c6a91906130bf565b611c75906001613046565b611c7f9084613046565b9250600101611abe565b5060008a81526005602052604080822082815560010180546001600160a81b0319169055518b91906001600160a01b038816907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a4604080518781526000602082018190528c9290916001600160a01b038b16917f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d910160405180910390a46040805160208101909152600090525098975050505050505050565b611d53848484846117df565b611d5f8484848461256b565b6109b15760405163bcb5663760e01b815260040160405180910390fd5b60408051808201909152600080825260208201528215611da757611da08987610f30565b9050611db4565b611db189876109b7565b90505b611dbf81868661266d565b8215611de2576000898152600760205260409020611ddd90876126b1565b611e1c565b6001600160a01b038516600090815260086020908152604080832087845282528083208390558b835260069091529020611e1c90876126b1565b6001600160a01b03881615611f095786600003611e9c57604051635c46a7ef60e11b81526001600160a01b0386169063b88d4fde90611e659030908c90899088906004016130d8565b600060405180830381600087803b158015611e7f57600080fd5b505af1158015611e93573d6000803e3d6000fd5b50505050611f09565b60208101518151604051630326051d60e11b81526001600160a01b039092169163064c0a3a91611ed69130918d918d908990600401613115565b600060405180830381600087803b158015611ef057600080fd5b505af1158015611f04573d6000803e3d6000fd5b505050505b6040805187815284151560208201526001600160a01b038a81161582840152915186928816918c917f02d6d6dbcb604d5e1e8c7886456e82a9cdce88b0a580071358f206b5a4d58f709181900360600190a461107c565b6000611f6c8585610f30565b9050611f7981848461266d565b6001600160a01b038316600090815260086020908152604080832085845290915290205415611fbb5760405163188a497360e01b815260040160405180910390fd5b6000858152600760205260409020611fd390856126b1565b600085815260066020908152604080832080546001808201835591855283852086516002909202019081558584015190820180546001600160a01b0319166001600160a01b03928316179055871680855260088452828520878652845293829020555186815284929188917f29486b9e2ae569b440933a9b1b421467306fa21f3dcad439c262910a634963a9910160405180910390a461105b565b6001600160a01b038116612095576040516342b66a3d60e01b815260040160405180910390fd5b336001600160a01b038216036120be57604051636d6c4ee560e11b815260040160405180910390fd5b600a80546001600160a01b0319166001600160a01b03831690811790915560405133907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b600080600061211884611220565b50915091508060001461213b57506001600160a01b0384811691161490506105b9565b816001600160a01b0316856001600160a01b03161480612160575061216082866111de565b806121845750846001600160a01b031661217985610670565b6001600160a01b0316145b95945050505050565b60005b6064811015612264576000806000856001600160a01b031663fb25fb7a866040518263ffffffff1660e01b81526004016121cc91815260200190565b606060405180830381865afa1580156121e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061220d919061315a565b925092509250806122215750505050505050565b6001600160a01b0383163014801561223857508682145b15612256576040516324543e6d60e11b815260040160405180910390fd5b509093509150600101612190565b50604051630349a6bd60e51b815260040160405180910390fd5b604080516060810182528481526001600160a01b03808516602080840191825285151584860190815260008a815260059092529481209351845590516001909301805494511515600160a01b026001600160a81b03199095169390921692909217929092179091556122f090856114e0565b6109b1565b6040516318d5243360e21b815285906001600160a01b0382169063635490cc906123279087908790879060040161319d565b600060405180830381600087803b15801561234157600080fd5b505af1158015612355573d6000803e3d6000fd5b5050505082866001600160a01b0316886001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a482866001600160a01b0316886001600160a01b03167f04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552d88886040516123e9929190918252602082015260400190565b60405180910390a461149f565b6001600160a01b03841661241d576040516325bd6bd360e01b815260040160405180910390fd5b6000838152600560205260409020600101546001600160a01b0316156124565760405163c5a8d37160e01b815260040160405180910390fd5b82600003612477576040516312c33ce360e01b815260040160405180910390fd5b6001600160a01b03841660009081526002602052604081208054600192906124a0908490613046565b9091555050604080516060810182528381526001600160a01b039586166020808301918252941515828401908152600096875260059095529190942093518455516001909301805492511515600160a01b026001600160a81b031990931693909416929092171790915550565b60008061251983610af9565b9050806001600160a01b0316846001600160a01b03161480612540575061254081856111de565b8061103d5750836001600160a01b031661255984610670565b6001600160a01b031614949350505050565b60006001600160a01b0384163b1561266257604051630a85bd0160e11b81526001600160a01b0385169063150b7a02906125af9033908990889088906004016130d8565b6020604051808303816000875af19250505080156125ea575060408051601f3d908101601f191682019092526125e7918101906131bc565b60015b612648573d808015612618576040519150601f19603f3d011682016040523d82523d6000602084013e61261d565b606091505b5080516000036126405760405163bcb5663760e01b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b14905061103d565b506001949350505050565b82602001516001600160a01b0316826001600160a01b0316141580612693575082518114155b1561073d57604051637383f2c160e11b815260040160405180910390fd5b815482906126c190600190613033565b815481106126d1576126d1612fcd565b90600052602060002090600202018282815481106126f1576126f1612fcd565b600091825260209091208254600290920201908155600191820154910180546001600160a01b0319166001600160a01b03909216919091179055815482908061273c5761273c6131d9565b60008281526020812060026000199093019283020190815560010180546001600160a01b031916905590555050565b508054600082556002029060005260206000209081019061121d91905b808211156127ae57600081556001810180546001600160a01b0319169055600201612788565b5090565b6001600160e01b03198116811461121d57600080fd5b6000602082840312156127da57600080fd5b81356127e5816127b2565b9392505050565b6001600160a01b038116811461121d57600080fd5b634e487b7160e01b600052604160045260246000fd5b600082601f83011261282857600080fd5b813567ffffffffffffffff8082111561284357612843612801565b604051601f8301601f19908116603f0116810190828211818310171561286b5761286b612801565b8160405283815286602085880101111561288457600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600080600060a086880312156128bc57600080fd5b85356128c7816127ec565b945060208601356128d7816127ec565b93506040860135925060608601359150608086013567ffffffffffffffff81111561290157600080fd5b61290d88828901612817565b9150509295509295909350565b60005b8381101561293557818101518382015260200161291d565b50506000910152565b6000815180845261295681602086016020860161291a565b601f01601f19169290920160200192915050565b6020815260006127e5602083018461293e565b60006020828403121561298f57600080fd5b5035919050565b600080604083850312156129a957600080fd5b82356129b4816127ec565b946020939093013593505050565b600080600080608085870312156129d857600080fd5b8435935060208501359250604085013567ffffffffffffffff808211156129fe57600080fd5b612a0a88838901612817565b93506060870135915080821115612a2057600080fd5b50612a2d87828801612817565b91505092959194509250565b600080600060608486031215612a4e57600080fd5b8335612a59816127ec565b92506020840135612a69816127ec565b929592945050506040919091013590565b60008060408385031215612a8d57600080fd5b50508035926020909101359150565b815181526020808301516001600160a01b031690820152604081016105b9565b602080825282518282018190526000919060409081850190868401855b82811015612b0f57612aff848351805182526020908101516001600160a01b0316910152565b9284019290850190600101612ad9565b5091979650505050505050565b600080600060608486031215612b3157600080fd5b8335925060208401359150604084013567ffffffffffffffff811115612b5657600080fd5b612b6286828701612817565b9150509250925092565b600060208284031215612b7e57600080fd5b81356127e5816127ec565b801515811461121d57600080fd5b60008060408385031215612baa57600080fd5b8235612bb5816127ec565b91506020830135612bc581612b89565b809150509250929050565b60008060008060808587031215612be657600080fd5b8435612bf1816127ec565b93506020850135612c01816127ec565b925060408501359150606085013567ffffffffffffffff811115612c2457600080fd5b612a2d87828801612817565b600080600080600080600080610100898b031215612c4d57600080fd5b883597506020890135612c5f816127ec565b965060408901359550606089013594506080890135612c7d816127ec565b935060a0890135925060c0890135612c9481612b89565b915060e089013567ffffffffffffffff811115612cb057600080fd5b612cbc8b828c01612817565b9150509295985092959890939650565b85815284602082015283604082015260a060608201526000612cf160a083018561293e565b8281036080840152612d03818561293e565b98975050505050505050565b60008060008060808587031215612d2557600080fd5b84359350602085013592506040850135612d3e816127ec565b9396929550929360600135925050565b60008060408385031215612d6157600080fd5b8235612d6c816127ec565b91506020830135612bc5816127ec565b600181811c90821680612d9057607f821691505b602082108103612db057634e487b7160e01b600052602260045260246000fd5b50919050565b693d913730b6b2911d101160b11b81528151600090612ddc81600a85016020870161291a565b7f222c20226465736372697074696f6e223a2022426c6f73736f6d73222c202269600a9390910192830152507f6d616765223a202268747470733a2f2f697066732e696f2f697066732f516d53602a8201527f73595278334c7044416231475a516d377a5a314175485a6a6662506b44364a37604a8201527f73397234317875316d66383f66696c656e616d653d7075672e706e67227d0000606a820152608801919050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000815260008251612eba81601d85016020870161291a565b91909101601d0192915050565b601f82111561073d57600081815260208120601f850160051c81016020861015612eee5750805b601f850160051c820191505b818110156105d657828155600101612efa565b815167ffffffffffffffff811115612f2757612f27612801565b612f3b81612f358454612d7c565b84612ec7565b602080601f831160018114612f705760008415612f585750858301515b600019600386901b1c1916600185901b1785556105d6565b600085815260208120601f198616915b82811015612f9f57888601518255948401946001909101908401612f80565b5085821015612fbd5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052603260045260246000fd5b600060208284031215612ff557600080fd5b81516127e5816127ec565b60006020828403121561301257600080fd5b81516127e581612b89565b634e487b7160e01b600052601160045260246000fd5b818103818111156105b9576105b961301d565b808201808211156105b9576105b961301d565b8381528260208201526000825161307781604085016020870161291a565b91909101604001949350505050565b6000826130a357634e487b7160e01b600052601260045260246000fd5b500490565b80820281158282048414176105b9576105b961301d565b6000602082840312156130d157600080fd5b5051919050565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061310b9083018461293e565b9695505050505050565b6001600160a01b03868116825285166020820152604081018490526060810183905260a06080820181905260009061314f9083018461293e565b979650505050505050565b60008060006060848603121561316f57600080fd5b835161317a816127ec565b60208501516040860151919450925061319281612b89565b809150509250925092565b838152826020820152606060408201526000612184606083018461293e565b6000602082840312156131ce57600080fd5b81516127e5816127b2565b634e487b7160e01b600052603160045260246000fdfe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa26469706673582212200fb860fb98ef181780626a0702f4c0a4c8e29b9735703edb49d2f5bdd1b5ffcd64736f6c63430008120033000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000003425514b0d1e6b4583cee39fee8cfcaa13960626000000000000000000000000000000000000000000000000000000000000000c424c534d2d50726f66696c6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005424c534d50000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102745760003560e01c8063736d7f8811610151578063b390c0ab116100c3578063defa80c311610087578063defa80c314610615578063e97ceaa814610628578063e985e9c51461063b578063f2fde38b1461064e578063fb25fb7a14610661578063ffa1ad741461069b57600080fd5b8063b390c0ab14610584578063b88d4fde14610597578063bbe15627146105aa578063d082e381146105f9578063d7e29cd51461060257600080fd5b806393596c7b1161011557806393596c7b146104ea57806395d89b41146104fb57806397961336146105035780639813c5b614610516578063a22cb4651461055e578063a898e3641461057157600080fd5b8063736d7f881461048457806379ba5097146104975780637cb647591461049f57806387bc1425146104b25780638da5cb5b146104d957600080fd5b80632f32f937116101ea57806344ec9344116101ae57806344ec9344146104125780636352211e14610425578063635490cc146104385780636bed127f1461044b5780636f19951c1461045e57806370a082311461047157600080fd5b80632f32f937146103815780634182e4a3146103a157806342842e0e146103d957806342966c68146103ec57806344305f2c146103ff57600080fd5b8063095ea7b31161023c578063095ea7b3146103165780630dddf0631461032957806321a8be9d1461033c57806323452b9c1461034f57806323b872dd146103575780632eb4a7ab1461036a57600080fd5b806301ffc9a714610279578063064c0a3a146102a157806306fdde03146102b6578063081812fc146102cb5780630846b3bc146102f6575b600080fd5b61028c6102873660046129be565b6106c0565b60405190151581526020015b60405180910390f35b6102b46102af366004612aa7565b61072d565b005b6102be61074c565b6040516102989190612b6d565b6102de6102d9366004612b80565b6107de565b6040516001600160a01b039091168152602001610298565b610309610304366004612b99565b610824565b6040516102989190612bb6565b6102b4610324366004612c16565b610849565b6102b4610337366004612b80565b6108d5565b6102b461034a366004612c42565b61093f565b6102b461097f565b6102b4610365366004612c64565b6109d5565b610373600b5481565b604051908152602001610298565b61039461038f366004612c42565b610a00565b6040516102989190612ca5565b61028c6103af366004612c16565b6001600160a01b039190911660009081526008602090815260408083209383529290522054151590565b6102b46103e7366004612c64565b610a99565b6102b46103fa366004612b80565b610ab4565b6102b461040d366004612ce9565b610abf565b610309610420366004612b80565b610c8e565b6102de610433366004612b80565b610d0d565b6102b4610446366004612d92565b610d9d565b6102b4610459366004612de2565b610eaf565b61030961046c366004612b80565b610ef5565b61037361047f366004612b99565b610f65565b6102b4610492366004612e17565b610faa565b6102b4611142565b6102b46104ad366004612b80565b6111c8565b6102de7f00000000000000000000000027dba89e927d708cb276a98f71236f8222ec278f81565b6009546001600160a01b03166102de565b600a546001600160a01b03166102de565b6102be61120b565b6102b4610511366004612c42565b61121a565b610373610524366004612e83565b6040516001600160c01b031960c083901b166020820152600090602801604051602081830303815290604052805190602001209050919050565b6102b461056c366004612ebb565b61122e565b61039461057f366004612c42565b6112c3565b610373610592366004612c42565b611320565b6102b46105a5366004612ef4565b61133e565b6105dc6105b8366004612b99565b600d6020526000908152604090208054600182015460029092015490919060ff1683565b604080519384526020840192909252151590820152606001610298565b610373600c5481565b6102b4610610366004612f60565b61135b565b6102b4610623366004613006565b611496565b6102b46106363660046130a2565b6114bb565b61028c6106493660046130e1565b6114d1565b6102b461065c366004612b99565b6114ff565b61067461066f366004612b80565b611513565b604080516001600160a01b0390941684526020840192909252151590820152606001610298565b6102be60405180604001604052806006815260200165302e32372e3160d01b81525081565b60006001600160e01b031982166301ffc9a760e01b14806106f157506001600160e01b031982166380ac58cd60e01b145b8061070c57506001600160e01b03198216635b5e139f60e01b145b8061072757506001600160e01b031982166342b0e56f60e01b145b92915050565b8261073781611594565b61074486868686866115bb565b505050505050565b60606000805461075b9061310f565b80601f01602080910402602001604051908101604052809291908181526020018280546107879061310f565b80156107d45780601f106107a9576101008083540402835291602001916107d4565b820191906000526020600020905b8154815290600101906020018083116107b757829003601f168201915b5050505050905090565b60006107e9826117a0565b60008281526003602052604081209061080184610d0d565b6001600160a01b0390811682526020820192909252604001600020541692915050565b6001600160a01b0381166000908152600d602052604090205460609061072790610ef5565b600061085482610d0d565b9050806001600160a01b0316836001600160a01b03160361088857604051630591db6d60e01b815260040160405180910390fd5b336001600160a01b038216148015906108a857506108a681336114d1565b155b156108c657604051634c12315960e11b815260040160405180910390fd5b6108d083836117d8565b505050565b6108dd611851565b60006108e882611513565b50506001600160a01b0381166000908152600d6020526040808220600201805460ff191660011790555191925083917f574b2683ce92a856b9955684fd27d399f1c64effd906c166d1939864c24b07299190a25050565b336000908152600d602052604090205461097b90837f00000000000000000000000027dba89e927d708cb276a98f71236f8222ec278f8461187e565b5050565b610987611851565b600a80546001600160a01b031981169091556040516001600160a01b0390911690819033907fe83a760af9d3c86797ea13c8979010086f067cfe3c985b2d03d951248600c50f90600090a350565b806109df81611594565b6109fa8484846040518060200160405280600081525061198c565b50505050565b604080518082019091526000808252602082015281610a1e84610ef5565b5111610a3d5760405163653e642560e11b815260040160405180910390fd5b6000838152600660205260408120805484908110610a5d57610a5d613149565b60009182526020918290206040805180820190915260029092020180548252600101546001600160a01b03169181019190915291505092915050565b6108d08383836040518060200160405280600081525061133e565b61097b816000611320565b336000908152600d602052604081205490819003610b1d5760405162461bcd60e51b8152602060048201526016602482015275141c9bd99a5b1948191bd95cc81b9bdd08195e1a5cdd60521b60448201526064015b60405180910390fd5b60005b82518110156108d0577f00000000000000000000000027dba89e927d708cb276a98f71236f8222ec278f6001600160a01b0316631ffeaf968384868581518110610b6c57610b6c613149565b60200260200101516040518463ffffffff1660e01b8152600401610b929392919061315f565b600060405180830381600087803b158015610bac57600080fd5b505af1158015610bc0573d6000803e3d6000fd5b505050506000610bcf83610c8e565b905060008160018351610be291906131af565b81518110610bf257610bf2613149565b60200260200101519050610c1a8460018451610c0e91906131af565b6020840151845161187e565b336000908152600d60205260409020855160019160030190879086908110610c4457610c44613149565b6020026020010151604051610c5991906131c2565b908152604051908190036020019020805491151560ff1990921691909117905550819050610c86816131de565b915050610b20565b6000818152600760209081526040808320805482518185028101850190935280835260609493849084015b82821015610d015760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b0316828401529083529092019101610cb9565b50929695505050505050565b600080600080610d1c85611513565b9250925092508015610d94576040516331a9108f60e11b8152600481018390526001600160a01b03841690636352211e90602401602060405180830381865afa158015610d6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d9191906131f7565b92505b50909392505050565b610da6836117a0565b33803b610dc65760405163b9d3114760e01b815260040160405180910390fd5b604080518082019091528381526001600160a01b03821660208201526000610ded86610c8e565b5190506080811015610e4c576000868152600760209081526040822080546001808201835591845292829020855160029094020192835590840151910180546001600160a01b0319166001600160a01b03909216919091179055610e65565b60405163a53c8c0560e01b815260040160405180910390fd5b84836001600160a01b0316877fe65085e689b77b126ba0bac3b079aa8288f19f4d5445af11c76003f8ab3075dd84604051610ea291815260200190565b60405180910390a4610744565b336000908152600d60205260408082209051600390910190610ed29084906131c2565b908152604051908190036020019020805491151560ff1990921691909117905550565b6000818152600660209081526040808320805482518185028101850190935280835260609493849084018215610d015760008481526020908190206040805180820190915260028502909101805482526001908101546001600160a01b0316828401529083529092019101610cb9565b60006001600160a01b038216610f8e57604051633bb9143360e11b815260040160405180910390fd5b506001600160a01b031660009081526002602052604090205490565b336000908152600d6020526040812054908190036110025760405162461bcd60e51b815260206004820152601560248201527414d95b99195c88191bd95cc81b9bdd08195e1a5cdd605a1b6044820152606401610b14565b6001600160a01b0384166000908152600d60205260408120549081900361106b5760405162461bcd60e51b815260206004820152601760248201527f526563656976657220646f6573206e6f742065786973740000000000000000006044820152606401610b14565b604051630fff57cb60e11b81526001600160a01b037f00000000000000000000000027dba89e927d708cb276a98f71236f8222ec278f1690631ffeaf96906110bd908590859089908990600401613214565b600060405180830381600087803b1580156110d757600080fd5b505af11580156110eb573d6000803e3d6000fd5b5050505060006110fa82610c8e565b90506000816001835161110d91906131af565b8151811061111d5761111d613149565b602002602001015190506111398360018451610c0e91906131af565b50505050505050565b600a546001600160a01b0316331461116d5760405163015aa1e360e11b815260040160405180910390fd5b600980546001600160a01b031980821633908117909355600a805490911690556040516001600160a01b03909116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6111d0611851565b600b8190556040518181527f90004c04698bc3322499a575ed3752dd4abf33e0a7294c06a787a0fe01bea9419060200160405180910390a150565b60606001805461075b9061310f565b8161122481611ad6565b6108d08383611afd565b6001600160a01b038216330361125757604051630b7b99b960e21b815260040160405180910390fd5b3360008181526004602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b6040805180820190915260008082526020820152816112e184610c8e565b51116113005760405163da22687f60e01b815260040160405180910390fd5b6000838152600760205260408120805484908110610a5d57610a5d613149565b60008261132c81611594565b6113368484611b72565b949350505050565b8161134881611594565b61135485858585611ecc565b5050505050565b600b5415801590611376575061137481600b5484611f01565b155b15611396576040516313e5138760e01b8152336004820152602401610b14565b336000908152600d6020526040902060010154156113c95760405163a760ecd560e01b8152336004820152602401610b14565b6000838152600e602052604090205460ff16156113fc576040516306a4b6d160e51b815260048101849052602401610b14565b61142433600c546040518060400160405280600381526020016203078360ec1b815250611f17565b600c8054336000818152600d602090815260408083209485556001948501899055888352600e909152808220805460ff191690941790935592549151919290917fcb939889322fed25104a5d8945bde5162563412a1fd718946cdcacb03088ea089190a35050600c8054600101905550565b876114a081611ad6565b6114b08989898989898989611f4c565b505050505050505050565b836114c581611ad6565b6113548585858561187e565b6001600160a01b03918216600090815260046020908152604080832093909416825291909152205460ff1690565b611507611851565b61151081612130565b50565b60008181526005602090815260408083208151606081018352815481526001909101546001600160a01b038116938201849052600160a01b900460ff16151591810191909152829182919061157b5760405163089ba7e160e41b815260040160405180910390fd5b6020810151815160409092015190969195509350915050565b61159e33826121cc565b611510576040516345f3c98360e11b815260040160405180910390fd5b6000806115c785611513565b5091509150866001600160a01b0316826001600160a01b0316146115fe5760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b038616611625576040516338f646ff60e21b815260040160405180910390fd5b6001600160a01b0386163014801561163c57508385145b1561165a57604051633d76b10760e01b815260040160405180910390fd5b6001600160a01b0386163b6116825760405163b9d3114760e01b815260040160405180910390fd5b6040516301ffc9a760e01b81526342b0e56f60e01b60048201526001600160a01b038716906301ffc9a790602401602060405180830381865afa1580156116cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116f19190613250565b61170e57604051631784ec7360e21b815260040160405180910390fd5b61171985878661224f565b611727828783878988612340565b6001600160a01b03871660009081526002602052604081208054600192906117509084906131af565b909155506117639050858588600161239b565b6001600160a01b038616600090815260026020526040812080546001929061178c90849061326d565b909155506111399050828783878988612412565b6000818152600560205260409020600101546001600160a01b03166115105760405163089ba7e160e41b815260040160405180910390fd5b60006117e382610d0d565b60008381526003602090815260408083206001600160a01b038581168086529190935281842080546001600160a01b031916938916938417905590519394508593919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a4505050565b6009546001600160a01b0316331461187c57604051636db2465f60e01b815260040160405180910390fd5b565b600061188a85856112c3565b90506118978184846124ef565b6001600160a01b0383166000908152600860209081526040808320858452909152902054156118d95760405163188a497360e01b815260040160405180910390fd5b60008581526007602052604090206118f19085612533565b600085815260066020908152604080832080546001808201835591855283852086516002909202019081558584015190820180546001600160a01b0319166001600160a01b03928316179055871680855260088452828520878652845293829020555186815284929188917f29486b9e2ae569b440933a9b1b421467306fa21f3dcad439c262910a634963a9910160405180910390a4611354565b60008061199884611513565b5091509150856001600160a01b0316826001600160a01b0316146119cf5760405163e146af6f60e01b815260040160405180910390fd5b6001600160a01b0385166119f6576040516338f646ff60e21b815260040160405180910390fd5b611a0582868360008888612340565b6001600160a01b0386166000908152600260205260408120805460019290611a2e9084906131af565b90915550611a419050846000878161239b565b6001600160a01b0385166000908152600260205260408120805460019290611a6a90849061326d565b909155505060405184906001600160a01b0380881691908916906000805160206133c683398151915290600090a483856001600160a01b0316836001600160a01b03166000805160206133a6833981519152846000604051610ea2929190918252602082015260400190565b611ae033826125ed565b611510576040516302728a9d60e41b815260040160405180910390fd5b600082815260076020526040902054811015611b2c57604051631e73178b60e11b815260040160405180910390fd5b6000828152600760205260408120611b4391612961565b60405182907f8ac4a0d65950c3e40448afb2260e2e0ec36ea15644d9b39e37e85472e5f9445190600090a25050565b6000806000611b8085611513565b50915091506000611b9086610d0d565b6001600160a01b03841660009081526002602052604081208054929350600192909190611bbe9084906131af565b90915550611bc99050565b611be88360008460008a60405180602001604052806000815250612340565b611bf36000876117d8565b6000611bfe87610ef5565b6000888152600660205260408120919250611c199190612961565b6000878152600760205260408120611c3091612961565b60008781526003602090815260408083206001600160a01b0386168452909152812080546001600160a01b031916905581518190815b81811015611e3157898310611cdb57848181518110611c8757611c87613149565b602002602001015160200151858281518110611ca557611ca5613149565b6020908102919091010151516040516306177b2560e41b81526001600160a01b0390921660048301526024820152604401610b14565b60086000868381518110611cf157611cf1613149565b6020026020010151602001516001600160a01b03166001600160a01b031681526020019081526020016000206000868381518110611d3157611d31613149565b602002602001015160000151815260200190815260200160002060009055828a039350848181518110611d6657611d66613149565b6020026020010151602001516001600160a01b031663b390c0ab868381518110611d9257611d92613149565b602002602001015160000151600187611dab91906131af565b6040516001600160e01b031960e085901b168152600481019290925260248201526044016020604051808303816000875af1158015611dee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e129190613280565b611e1d90600161326d565b611e27908461326d565b9250600101611c66565b5060008a81526005602052604080822082815560010180546001600160a81b0319169055518b91906001600160a01b038816906000805160206133c6833981519152908390a4604080518781526000602082018190528c9290916001600160a01b038b16916000805160206133a6833981519152910160405180910390a46040805160208101909152600090525b5098975050505050505050565b611ed88484848461198c565b611ee48484848461264b565b6109fa5760405163bcb5663760e01b815260040160405180910390fd5b600082611f0e858461274d565b14949350505050565b611f2283838361279a565b611f2f600084848461264b565b6108d05760405163bcb5663760e01b815260040160405180910390fd5b60408051808201909152600080825260208201528215611f7757611f7089876112c3565b9050611f84565b611f818987610a00565b90505b611f8f8186866124ef565b8215611fb2576000898152600760205260409020611fad9087612533565b611fec565b6001600160a01b038516600090815260086020908152604080832087845282528083208390558b835260069091529020611fec9087612533565b6001600160a01b038816156120d9578660000361206c57604051635c46a7ef60e11b81526001600160a01b0386169063b88d4fde906120359030908c9089908890600401613299565b600060405180830381600087803b15801561204f57600080fd5b505af1158015612063573d6000803e3d6000fd5b505050506120d9565b60208101518151604051630326051d60e11b81526001600160a01b039092169163064c0a3a916120a69130918d918d9089906004016132d6565b600060405180830381600087803b1580156120c057600080fd5b505af11580156120d4573d6000803e3d6000fd5b505050505b6040805187815284151560208201526001600160a01b038a81161582840152915186928816918c917f02d6d6dbcb604d5e1e8c7886456e82a9cdce88b0a580071358f206b5a4d58f709181900360600190a46114b0565b6001600160a01b038116612157576040516342b66a3d60e01b815260040160405180910390fd5b336001600160a01b0382160361218057604051636d6c4ee560e11b815260040160405180910390fd5b600a80546001600160a01b0319166001600160a01b03831690811790915560405133907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b60008060006121da84611513565b5091509150806000146121fd57506001600160a01b038481169116149050610727565b816001600160a01b0316856001600160a01b03161480612222575061222282866114d1565b806122465750846001600160a01b031661223b856107de565b6001600160a01b0316145b95945050505050565b60005b6064811015612326576000806000856001600160a01b031663fb25fb7a866040518263ffffffff1660e01b815260040161228e91815260200190565b606060405180830381865afa1580156122ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122cf9190613310565b925092509250806122e35750505050505050565b6001600160a01b038316301480156122fa57508682145b15612318576040516324543e6d60e11b815260040160405180910390fd5b509093509150600101612252565b50604051630349a6bd60e51b815260040160405180910390fd5b6001600160a01b038616158061235d57506001600160a01b038516155b6107445760405162461bcd60e51b815260206004820152600f60248201526e29b7bab63137bab732103a37b5b2b760891b6044820152606401610b14565b604080516060810182528481526001600160a01b03808516602080840191825285151584860190815260008a815260059092529481209351845590516001909301805494511515600160a01b026001600160a81b031990951693909216929092179290921790915561240d90856117d8565b6109fa565b6040516318d5243360e21b815285906001600160a01b0382169063635490cc9061244490879087908790600401613353565b600060405180830381600087803b15801561245e57600080fd5b505af1158015612472573d6000803e3d6000fd5b5050505082866001600160a01b0316886001600160a01b03166000805160206133c683398151915260405160405180910390a482866001600160a01b0316886001600160a01b03166000805160206133a683398151915288886040516124e2929190918252602082015260400190565b60405180910390a4611139565b82602001516001600160a01b0316826001600160a01b0316141580612515575082518114155b156108d057604051637383f2c160e11b815260040160405180910390fd5b81548290612543906001906131af565b8154811061255357612553613149565b906000526020600020906002020182828154811061257357612573613149565b600091825260209091208254600290920201908155600191820154910180546001600160a01b0319166001600160a01b0390921691909117905581548290806125be576125be613372565b60008281526020812060026000199093019283020190815560010180546001600160a01b031916905590555050565b6000806125f983610d0d565b9050806001600160a01b0316846001600160a01b03161480612620575061262081856114d1565b806113365750836001600160a01b0316612639846107de565b6001600160a01b031614949350505050565b60006001600160a01b0384163b1561274257604051630a85bd0160e11b81526001600160a01b0385169063150b7a029061268f903390899088908890600401613299565b6020604051808303816000875af19250505080156126ca575060408051601f3d908101601f191682019092526126c791810190613388565b60015b612728573d8080156126f8576040519150601f19603f3d011682016040523d82523d6000602084013e6126fd565b606091505b5080516000036127205760405163bcb5663760e01b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050611336565b506001949350505050565b600081815b84518110156127925761277e8286838151811061277157612771613149565b6020026020010151612808565b91508061278a816131de565b915050612752565b509392505050565b6127a7838360008461283a565b60405182906001600160a01b038516906000906000805160206133c6833981519152908290a46040805160008082526020820181905284926001600160a01b038716926000805160206133a6833981519152910160405180910390a4505050565b6000818310612824576000828152602084905260409020612833565b60008381526020839052604090205b9392505050565b6001600160a01b038416612861576040516325bd6bd360e01b815260040160405180910390fd5b6000838152600560205260409020600101546001600160a01b03161561289a5760405163c5a8d37160e01b815260040160405180910390fd5b826000036128bb576040516312c33ce360e01b815260040160405180910390fd5b6128cb6000856000858786612340565b6001600160a01b03841660009081526002602052604081208054600192906128f490849061326d565b9091555050604080516060810182528381526001600160a01b039586166020808301918252941515828401908152600096875260059095529190942093518455516001909301805492511515600160a01b026001600160a81b031990931693909416929092171790915550565b508054600082556002029060005260206000209081019061151091905b808211156129a457600081556001810180546001600160a01b031916905560020161297e565b5090565b6001600160e01b03198116811461151057600080fd5b6000602082840312156129d057600080fd5b8135612833816129a8565b6001600160a01b038116811461151057600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715612a2f57612a2f6129f0565b604052919050565b600082601f830112612a4857600080fd5b813567ffffffffffffffff811115612a6257612a626129f0565b612a75601f8201601f1916602001612a06565b818152846020838601011115612a8a57600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600060a08688031215612abf57600080fd5b8535612aca816129db565b94506020860135612ada816129db565b93506040860135925060608601359150608086013567ffffffffffffffff811115612b0457600080fd5b612b1088828901612a37565b9150509295509295909350565b60005b83811015612b38578181015183820152602001612b20565b50506000910152565b60008151808452612b59816020860160208601612b1d565b601f01601f19169290920160200192915050565b6020815260006128336020830184612b41565b600060208284031215612b9257600080fd5b5035919050565b600060208284031215612bab57600080fd5b8135612833816129db565b602080825282518282018190526000919060409081850190868401855b82811015612c0957612bf9848351805182526020908101516001600160a01b0316910152565b9284019290850190600101612bd3565b5091979650505050505050565b60008060408385031215612c2957600080fd5b8235612c34816129db565b946020939093013593505050565b60008060408385031215612c5557600080fd5b50508035926020909101359150565b600080600060608486031215612c7957600080fd5b8335612c84816129db565b92506020840135612c94816129db565b929592945050506040919091013590565b815181526020808301516001600160a01b03169082015260408101610727565b600067ffffffffffffffff821115612cdf57612cdf6129f0565b5060051b60200190565b60006020808385031215612cfc57600080fd5b823567ffffffffffffffff80821115612d1457600080fd5b818501915085601f830112612d2857600080fd5b8135612d3b612d3682612cc5565b612a06565b81815260059190911b83018401908481019088831115612d5a57600080fd5b8585015b83811015611ebf57803585811115612d765760008081fd5b612d848b89838a0101612a37565b845250918601918601612d5e565b600080600060608486031215612da757600080fd5b8335925060208401359150604084013567ffffffffffffffff811115612dcc57600080fd5b612dd886828701612a37565b9150509250925092565b600060208284031215612df457600080fd5b813567ffffffffffffffff811115612e0b57600080fd5b61133684828501612a37565b600080600060608486031215612e2c57600080fd5b8335612e37816129db565b9250602084013567ffffffffffffffff80821115612e5457600080fd5b612e6087838801612a37565b93506040860135915080821115612e7657600080fd5b50612dd886828701612a37565b600060208284031215612e9557600080fd5b813567ffffffffffffffff8116811461283357600080fd5b801515811461151057600080fd5b60008060408385031215612ece57600080fd5b8235612ed9816129db565b91506020830135612ee981612ead565b809150509250929050565b60008060008060808587031215612f0a57600080fd5b8435612f15816129db565b93506020850135612f25816129db565b925060408501359150606085013567ffffffffffffffff811115612f4857600080fd5b612f5487828801612a37565b91505092959194509250565b600080600060608486031215612f7557600080fd5b833592506020808501359250604085013567ffffffffffffffff811115612f9b57600080fd5b8501601f81018713612fac57600080fd5b8035612fba612d3682612cc5565b81815260059190911b82018301908381019089831115612fd957600080fd5b928401925b82841015612ff757833582529284019290840190612fde565b80955050505050509250925092565b600080600080600080600080610100898b03121561302357600080fd5b883597506020890135613035816129db565b965060408901359550606089013594506080890135613053816129db565b935060a0890135925060c089013561306a81612ead565b915060e089013567ffffffffffffffff81111561308657600080fd5b6130928b828c01612a37565b9150509295985092959890939650565b600080600080608085870312156130b857600080fd5b843593506020850135925060408501356130d1816129db565b9396929550929360600135925050565b600080604083850312156130f457600080fd5b82356130ff816129db565b91506020830135612ee9816129db565b600181811c9082168061312357607f821691505b60208210810361314357634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052603260045260246000fd5b83815282602082015260806040820152600061317e6080830184612b41565b82810360608401526000815260208101915050949350505050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561072757610727613199565b600082516131d4818460208701612b1d565b9190910192915050565b6000600182016131f0576131f0613199565b5060010190565b60006020828403121561320957600080fd5b8151612833816129db565b8481528360208201526080604082015260006132336080830185612b41565b82810360608401526132458185612b41565b979650505050505050565b60006020828403121561326257600080fd5b815161283381612ead565b8082018082111561072757610727613199565b60006020828403121561329257600080fd5b5051919050565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906132cc90830184612b41565b9695505050505050565b6001600160a01b03868116825285166020820152604081018490526060810183905260a06080820181905260009061324590830184612b41565b60008060006060848603121561332557600080fd5b8351613330816129db565b60208501516040860151919450925061334881612ead565b809150509250925092565b8381528260208201526060604082015260006122466060830184612b41565b634e487b7160e01b600052603160045260246000fd5b60006020828403121561339a57600080fd5b8151612833816129a856fe04444026cefd1b05506559cab59d1b865ae3ba4ed2fe5c894f04e522776c552dddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220cc09a863b0d2bb13cb76e1f7d2dc71b68e2e23f891786da65fa3d78561e30e4d64736f6c63430008120033

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

000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000003425514b0d1e6b4583cee39fee8cfcaa13960626000000000000000000000000000000000000000000000000000000000000000c424c534d2d50726f66696c6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005424c534d50000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : name (string): BLSM-Profile
Arg [1] : symbol (string): BLSMP
Arg [2] : multisigOwner (address): 0x3425514B0d1E6b4583cEE39feE8CFcAa13960626

-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [2] : 0000000000000000000000003425514b0d1e6b4583cee39fee8cfcaa13960626
Arg [3] : 000000000000000000000000000000000000000000000000000000000000000c
Arg [4] : 424c534d2d50726f66696c650000000000000000000000000000000000000000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [6] : 424c534d50000000000000000000000000000000000000000000000000000000


Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

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

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