ETH Price: $3,596.70 (+3.74%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ScapesERC721

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion
File 1 of 30 : ScapesERC721.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.16;

import {ERC165} from "@solidstate/contracts/introspection/ERC165.sol";
import {OwnableInternal} from "@solidstate/contracts/access/ownable/OwnableInternal.sol";
import {DefaultOperatorFilterer} from "operator-filter-registry/src/DefaultOperatorFilterer.sol";

import {IERC721, ERC721Base, ERC721BaseInternal, ERC721BaseStorage} from "./solidstate/ERC721Base.sol";
import {ScapesERC721MetadataStorage} from "./ScapesERC721MetadataStorage.sol";
import {IChild} from "./IChild.sol";
import {ScapesMerge} from "../metadata/ScapesMerge.sol";

import {WithFees} from "./marketplace/WithFees.sol";
import {ScapesMarketplaceStorage} from "./marketplace/ScapesMarketplaceStorage.sol";
import {IERC721MarketplaceInternal} from "./marketplace/IERC721MarketplaceInternal.sol";

/// @title ScapesERC721
/// @author akuti.eth | scapes.eth
/// @notice The ERC721 functionality for Scapes
/// @dev The facet to add ERC721 functionality and merging to Scapes.
contract ScapesERC721 is
    ERC165,
    OwnableInternal,
    ERC721Base,
    WithFees,
    IERC721MarketplaceInternal,
    DefaultOperatorFilterer
{
    error ScapesERC721__ChildAlreadyAdded();
    error ScapesERC721__InvalidArgument();
    using ERC721BaseStorage for ERC721BaseStorage.Layout;
    using ScapesMerge for ScapesMerge.Merge;

    uint256 internal constant MIN_MERGE_SIZE = 2;
    uint256 internal constant MAX_MERGE_SIZE = 8;

    /**
     * @notice Ceate and mint a merge out of multiple Scapes.
     * @dev Transfer Scape tokens to the contract and mint a merge Scape.
     * @param merge_ Merge settings
     */
    function merge(ScapesMerge.Merge calldata merge_) external {
        if (
            merge_.parts.length < MIN_MERGE_SIZE ||
            merge_.parts.length > MAX_MERGE_SIZE
        ) revert ScapesERC721__InvalidArgument();
        for (uint256 i = 0; i < merge_.parts.length; i++) {
            // transfer all containing tokens, it is supposed to fail when tokens are repeated
            transferFrom(msg.sender, address(this), merge_.parts[i].tokenId);
        }
        _safeMint(msg.sender, merge_.toId());
    }

    /**
     * @notice Burn a merge and receive its Scapes.
     * @dev Burn the merge scape and transfer the contained Scapes form the contract.
     * @param merge_ Merge settings
     */
    function purge(ScapesMerge.Merge calldata merge_) external {
        uint256 tokenId = merge_.toId();
        if (_ownerOf(tokenId) != msg.sender) revert ERC721Base__NotTokenOwner();
        _burn(tokenId);
        for (uint256 i = 0; i < merge_.parts.length; i++) {
            _safeTransfer(
                address(this),
                msg.sender,
                merge_.parts[i].tokenId,
                ""
            );
        }
    }

    /**
     * @inheritdoc IERC721
     */
    function setApprovalForAll(address operator, bool approved)
        public
        override
        onlyAllowedOperatorApproval(operator)
    {
        super.setApprovalForAll(operator, approved);
    }

    /**
     * @inheritdoc IERC721
     */
    function approve(address operator, uint256 tokenId)
        public
        payable
        override
        onlyAllowedOperatorApproval(operator)
    {
        super.approve(operator, tokenId);
    }

    /**
     * @inheritdoc IERC721
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public payable override onlyAllowedOperator(from) {
        super.transferFrom(from, to, tokenId);
    }

    /**
     * @inheritdoc IERC721
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public payable override onlyAllowedOperator(from) {
        super.safeTransferFrom(from, to, tokenId);
    }

    /**
     * @inheritdoc IERC721
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) public payable override onlyAllowedOperator(from) {
        super.safeTransferFrom(from, to, tokenId, data);
    }

    /**
     * @inheritdoc ERC721BaseInternal
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual override(ERC721BaseInternal) {
        if (
            from != address(0) &&
            to != address(0) &&
            ScapesMarketplaceStorage.layout().offers[tokenId].price > 0
        ) {
            ScapesMarketplaceStorage.Layout storage d = ScapesMarketplaceStorage
                .layout();
            d.offers[tokenId].price = 0;
            d.offers[tokenId].specificBuyer = address(0);
            emit OfferWithdrawn(tokenId);
        }
        super._beforeTokenTransfer(from, to, tokenId);
    }

    /**
     * @inheritdoc ERC721BaseInternal
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual override(ERC721BaseInternal) {
        IChild(ScapesERC721MetadataStorage.layout().scapeBound).update(
            from,
            to,
            tokenId
        );
        super._afterTokenTransfer(from, to, tokenId);
    }

    /**
     * @notice The total Scapes supply (ignoring Merges)
     */
    function totalSupply() external pure returns (uint256) {
        return 10_000;
    }
}

File 2 of 30 : IOwnableInternal.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

import { IERC173Internal } from '../../interfaces/IERC173Internal.sol';

interface IOwnableInternal is IERC173Internal {
    error Ownable__NotOwner();
    error Ownable__NotTransitiveOwner();
}

File 3 of 30 : OwnableInternal.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

import { IERC173 } from '../../interfaces/IERC173.sol';
import { AddressUtils } from '../../utils/AddressUtils.sol';
import { IOwnableInternal } from './IOwnableInternal.sol';
import { OwnableStorage } from './OwnableStorage.sol';

abstract contract OwnableInternal is IOwnableInternal {
    using AddressUtils for address;
    using OwnableStorage for OwnableStorage.Layout;

    modifier onlyOwner() {
        if (msg.sender != _owner()) revert Ownable__NotOwner();
        _;
    }

    modifier onlyTransitiveOwner() {
        if (msg.sender != _transitiveOwner())
            revert Ownable__NotTransitiveOwner();
        _;
    }

    function _owner() internal view virtual returns (address) {
        return OwnableStorage.layout().owner;
    }

    function _transitiveOwner() internal view virtual returns (address) {
        address owner = _owner();

        while (owner.isContract()) {
            try IERC173(owner).owner() returns (address transitiveOwner) {
                owner = transitiveOwner;
            } catch {
                return owner;
            }
        }

        return owner;
    }

    function _transferOwnership(address account) internal virtual {
        OwnableStorage.layout().setOwner(account);
        emit OwnershipTransferred(msg.sender, account);
    }
}

File 4 of 30 : OwnableStorage.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

library OwnableStorage {
    struct Layout {
        address owner;
    }

    bytes32 internal constant STORAGE_SLOT =
        keccak256('solidstate.contracts.storage.Ownable');

    function layout() internal pure returns (Layout storage l) {
        bytes32 slot = STORAGE_SLOT;
        assembly {
            l.slot := slot
        }
    }

    function setOwner(Layout storage l, address owner) internal {
        l.owner = owner;
    }
}

File 5 of 30 : EnumerableMap.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

/**
 * @title Map implementation with enumeration functions
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)
 */
library EnumerableMap {
    error EnumerableMap__IndexOutOfBounds();
    error EnumerableMap__NonExistentKey();

    struct MapEntry {
        bytes32 _key;
        bytes32 _value;
    }

    struct Map {
        MapEntry[] _entries;
        // 1-indexed to allow 0 to signify nonexistence
        mapping(bytes32 => uint256) _indexes;
    }

    struct AddressToAddressMap {
        Map _inner;
    }

    struct UintToAddressMap {
        Map _inner;
    }

    function at(
        AddressToAddressMap storage map,
        uint256 index
    ) internal view returns (address, address) {
        (bytes32 key, bytes32 value) = _at(map._inner, index);

        return (
            address(uint160(uint256(key))),
            address(uint160(uint256(value)))
        );
    }

    function at(
        UintToAddressMap storage map,
        uint256 index
    ) internal view returns (uint256, address) {
        (bytes32 key, bytes32 value) = _at(map._inner, index);
        return (uint256(key), address(uint160(uint256(value))));
    }

    function contains(
        AddressToAddressMap storage map,
        address key
    ) internal view returns (bool) {
        return _contains(map._inner, bytes32(uint256(uint160(key))));
    }

    function contains(
        UintToAddressMap storage map,
        uint256 key
    ) internal view returns (bool) {
        return _contains(map._inner, bytes32(key));
    }

    function length(
        AddressToAddressMap storage map
    ) internal view returns (uint256) {
        return _length(map._inner);
    }

    function length(
        UintToAddressMap storage map
    ) internal view returns (uint256) {
        return _length(map._inner);
    }

    function get(
        AddressToAddressMap storage map,
        address key
    ) internal view returns (address) {
        return
            address(
                uint160(
                    uint256(_get(map._inner, bytes32(uint256(uint160(key)))))
                )
            );
    }

    function get(
        UintToAddressMap storage map,
        uint256 key
    ) internal view returns (address) {
        return address(uint160(uint256(_get(map._inner, bytes32(key)))));
    }

    function set(
        AddressToAddressMap storage map,
        address key,
        address value
    ) internal returns (bool) {
        return
            _set(
                map._inner,
                bytes32(uint256(uint160(key))),
                bytes32(uint256(uint160(value)))
            );
    }

    function set(
        UintToAddressMap storage map,
        uint256 key,
        address value
    ) internal returns (bool) {
        return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
    }

    function remove(
        AddressToAddressMap storage map,
        address key
    ) internal returns (bool) {
        return _remove(map._inner, bytes32(uint256(uint160(key))));
    }

    function remove(
        UintToAddressMap storage map,
        uint256 key
    ) internal returns (bool) {
        return _remove(map._inner, bytes32(key));
    }

    function toArray(
        AddressToAddressMap storage map
    )
        internal
        view
        returns (address[] memory keysOut, address[] memory valuesOut)
    {
        uint256 len = map._inner._entries.length;

        keysOut = new address[](len);
        valuesOut = new address[](len);

        unchecked {
            for (uint256 i; i < len; ++i) {
                keysOut[i] = address(
                    uint160(uint256(map._inner._entries[i]._key))
                );
                valuesOut[i] = address(
                    uint160(uint256(map._inner._entries[i]._value))
                );
            }
        }
    }

    function toArray(
        UintToAddressMap storage map
    )
        internal
        view
        returns (uint256[] memory keysOut, address[] memory valuesOut)
    {
        uint256 len = map._inner._entries.length;

        keysOut = new uint256[](len);
        valuesOut = new address[](len);

        unchecked {
            for (uint256 i; i < len; ++i) {
                keysOut[i] = uint256(map._inner._entries[i]._key);
                valuesOut[i] = address(
                    uint160(uint256(map._inner._entries[i]._value))
                );
            }
        }
    }

    function keys(
        AddressToAddressMap storage map
    ) internal view returns (address[] memory keysOut) {
        uint256 len = map._inner._entries.length;

        keysOut = new address[](len);

        unchecked {
            for (uint256 i; i < len; ++i) {
                keysOut[i] = address(
                    uint160(uint256(map._inner._entries[i]._key))
                );
            }
        }
    }

    function keys(
        UintToAddressMap storage map
    ) internal view returns (uint256[] memory keysOut) {
        uint256 len = map._inner._entries.length;

        keysOut = new uint256[](len);

        unchecked {
            for (uint256 i; i < len; ++i) {
                keysOut[i] = uint256(map._inner._entries[i]._key);
            }
        }
    }

    function values(
        AddressToAddressMap storage map
    ) internal view returns (address[] memory valuesOut) {
        uint256 len = map._inner._entries.length;

        valuesOut = new address[](len);

        unchecked {
            for (uint256 i; i < len; ++i) {
                valuesOut[i] = address(
                    uint160(uint256(map._inner._entries[i]._value))
                );
            }
        }
    }

    function values(
        UintToAddressMap storage map
    ) internal view returns (address[] memory valuesOut) {
        uint256 len = map._inner._entries.length;

        valuesOut = new address[](len);

        unchecked {
            for (uint256 i; i < len; ++i) {
                valuesOut[i] = address(
                    uint160(uint256(map._inner._entries[i]._value))
                );
            }
        }
    }

    function _at(
        Map storage map,
        uint256 index
    ) private view returns (bytes32, bytes32) {
        if (index >= map._entries.length)
            revert EnumerableMap__IndexOutOfBounds();

        MapEntry storage entry = map._entries[index];
        return (entry._key, entry._value);
    }

    function _contains(
        Map storage map,
        bytes32 key
    ) private view returns (bool) {
        return map._indexes[key] != 0;
    }

    function _length(Map storage map) private view returns (uint256) {
        return map._entries.length;
    }

    function _get(Map storage map, bytes32 key) private view returns (bytes32) {
        uint256 keyIndex = map._indexes[key];
        if (keyIndex == 0) revert EnumerableMap__NonExistentKey();
        unchecked {
            return map._entries[keyIndex - 1]._value;
        }
    }

    function _set(
        Map storage map,
        bytes32 key,
        bytes32 value
    ) private returns (bool) {
        uint256 keyIndex = map._indexes[key];

        if (keyIndex == 0) {
            map._entries.push(MapEntry({ _key: key, _value: value }));
            map._indexes[key] = map._entries.length;
            return true;
        } else {
            unchecked {
                map._entries[keyIndex - 1]._value = value;
            }
            return false;
        }
    }

    function _remove(Map storage map, bytes32 key) private returns (bool) {
        uint256 keyIndex = map._indexes[key];

        if (keyIndex != 0) {
            unchecked {
                MapEntry storage last = map._entries[map._entries.length - 1];

                // move last entry to now-vacant index
                map._entries[keyIndex - 1] = last;
                map._indexes[last._key] = keyIndex;
            }

            // clear last index
            map._entries.pop();
            delete map._indexes[key];

            return true;
        } else {
            return false;
        }
    }
}

File 6 of 30 : EnumerableSet.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

/**
 * @title Set implementation with enumeration functions
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)
 */
library EnumerableSet {
    error EnumerableSet__IndexOutOfBounds();

    struct Set {
        bytes32[] _values;
        // 1-indexed to allow 0 to signify nonexistence
        mapping(bytes32 => uint256) _indexes;
    }

    struct Bytes32Set {
        Set _inner;
    }

    struct AddressSet {
        Set _inner;
    }

    struct UintSet {
        Set _inner;
    }

    function at(
        Bytes32Set storage set,
        uint256 index
    ) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    function at(
        AddressSet storage set,
        uint256 index
    ) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    function at(
        UintSet storage set,
        uint256 index
    ) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    function contains(
        Bytes32Set storage set,
        bytes32 value
    ) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    function contains(
        AddressSet storage set,
        address value
    ) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    function contains(
        UintSet storage set,
        uint256 value
    ) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    function indexOf(
        Bytes32Set storage set,
        bytes32 value
    ) internal view returns (uint256) {
        return _indexOf(set._inner, value);
    }

    function indexOf(
        AddressSet storage set,
        address value
    ) internal view returns (uint256) {
        return _indexOf(set._inner, bytes32(uint256(uint160(value))));
    }

    function indexOf(
        UintSet storage set,
        uint256 value
    ) internal view returns (uint256) {
        return _indexOf(set._inner, bytes32(value));
    }

    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    function add(
        Bytes32Set storage set,
        bytes32 value
    ) internal returns (bool) {
        return _add(set._inner, value);
    }

    function add(
        AddressSet storage set,
        address value
    ) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    function remove(
        Bytes32Set storage set,
        bytes32 value
    ) internal returns (bool) {
        return _remove(set._inner, value);
    }

    function remove(
        AddressSet storage set,
        address value
    ) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    function remove(
        UintSet storage set,
        uint256 value
    ) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    function toArray(
        Bytes32Set storage set
    ) internal view returns (bytes32[] memory) {
        return set._inner._values;
    }

    function toArray(
        AddressSet storage set
    ) internal view returns (address[] memory) {
        bytes32[] storage values = set._inner._values;
        address[] storage array;

        assembly {
            array.slot := values.slot
        }

        return array;
    }

    function toArray(
        UintSet storage set
    ) internal view returns (uint256[] memory) {
        bytes32[] storage values = set._inner._values;
        uint256[] storage array;

        assembly {
            array.slot := values.slot
        }

        return array;
    }

    function _at(
        Set storage set,
        uint256 index
    ) private view returns (bytes32) {
        if (index >= set._values.length)
            revert EnumerableSet__IndexOutOfBounds();
        return set._values[index];
    }

    function _contains(
        Set storage set,
        bytes32 value
    ) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    function _indexOf(
        Set storage set,
        bytes32 value
    ) private view returns (uint256) {
        unchecked {
            return set._indexes[value] - 1;
        }
    }

    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    function _add(
        Set storage set,
        bytes32 value
    ) private returns (bool status) {
        if (!_contains(set, value)) {
            set._values.push(value);
            set._indexes[value] = set._values.length;
            status = true;
        }
    }

    function _remove(
        Set storage set,
        bytes32 value
    ) private returns (bool status) {
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            unchecked {
                bytes32 last = set._values[set._values.length - 1];

                // move last value to now-vacant index

                set._values[valueIndex - 1] = last;
                set._indexes[last] = valueIndex;
            }
            // clear last index

            set._values.pop();
            delete set._indexes[value];

            status = true;
        }
    }
}

File 7 of 30 : IERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

/**
 * @title ERC165 interface registration interface
 * @dev see https://eips.ethereum.org/EIPS/eip-165
 */
interface IERC165 {
    /**
     * @notice query whether contract has registered support for given interface
     * @param interfaceId interface id
     * @return bool whether interface is supported
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 8 of 30 : IERC173.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

import { IERC173Internal } from './IERC173Internal.sol';

/**
 * @title Contract ownership standard interface
 * @dev see https://eips.ethereum.org/EIPS/eip-173
 */
interface IERC173 is IERC173Internal {
    /**
     * @notice get the ERC173 contract owner
     * @return conrtact owner
     */
    function owner() external view returns (address);

    /**
     * @notice transfer contract ownership to new account
     * @param account address of new owner
     */
    function transferOwnership(address account) external;
}

File 9 of 30 : IERC173Internal.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

/**
 * @title Partial ERC173 interface needed by internal functions
 */
interface IERC173Internal {
    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );
}

File 10 of 30 : IERC721.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

import { IERC165 } from './IERC165.sol';
import { IERC721Internal } from './IERC721Internal.sol';

/**
 * @title ERC721 interface
 * @dev see https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721 is IERC721Internal, IERC165 {
    /**
     * @notice query the balance of given address
     * @return balance quantity of tokens held
     */
    function balanceOf(address account) external view returns (uint256 balance);

    /**
     * @notice query the owner of given token
     * @param tokenId token to query
     * @return owner token owner
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @notice transfer token between given addresses, checking for ERC721Receiver implementation if applicable
     * @param from sender of token
     * @param to receiver of token
     * @param tokenId token id
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external payable;

    /**
     * @notice transfer token between given addresses, checking for ERC721Receiver implementation if applicable
     * @param from sender of token
     * @param to receiver of token
     * @param tokenId token id
     * @param data data payload
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external payable;

    /**
     * @notice transfer token between given addresses, without checking for ERC721Receiver implementation if applicable
     * @param from sender of token
     * @param to receiver of token
     * @param tokenId token id
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external payable;

    /**
     * @notice grant approval to given account to spend token
     * @param operator address to be approved
     * @param tokenId token to approve
     */
    function approve(address operator, uint256 tokenId) external payable;

    /**
     * @notice get approval status for given token
     * @param tokenId token to query
     * @return operator address approved to spend token
     */
    function getApproved(
        uint256 tokenId
    ) external view returns (address operator);

    /**
     * @notice grant approval to or revoke approval from given account to spend all tokens held by sender
     * @param operator address to be approved
     * @param status approval status
     */
    function setApprovalForAll(address operator, bool status) external;

    /**
     * @notice query approval status of given operator with respect to given address
     * @param account address to query for approval granted
     * @param operator address to query for approval received
     * @return status whether operator is approved to spend tokens held by account
     */
    function isApprovedForAll(
        address account,
        address operator
    ) external view returns (bool status);
}

File 11 of 30 : IERC721Internal.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

/**
 * @title Partial ERC721 interface needed by internal functions
 */
interface IERC721Internal {
    event Transfer(
        address indexed from,
        address indexed to,
        uint256 indexed tokenId
    );

    event Approval(
        address indexed owner,
        address indexed operator,
        uint256 indexed tokenId
    );

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

File 12 of 30 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

interface IERC721Receiver {
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 13 of 30 : ERC165.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

import { IERC165 } from '../interfaces/IERC165.sol';
import { ERC165Storage } from './ERC165Storage.sol';

/**
 * @title ERC165 implementation
 */
abstract contract ERC165 is IERC165 {
    using ERC165Storage for ERC165Storage.Layout;

    /**
     * @inheritdoc IERC165
     */
    function supportsInterface(bytes4 interfaceId) public view returns (bool) {
        return ERC165Storage.layout().isSupportedInterface(interfaceId);
    }
}

File 14 of 30 : ERC165Storage.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

library ERC165Storage {
    error ERC165Storage__InvalidInterfaceId();

    struct Layout {
        mapping(bytes4 => bool) supportedInterfaces;
    }

    bytes32 internal constant STORAGE_SLOT =
        keccak256('solidstate.contracts.storage.ERC165');

    function layout() internal pure returns (Layout storage l) {
        bytes32 slot = STORAGE_SLOT;
        assembly {
            l.slot := slot
        }
    }

    function isSupportedInterface(
        Layout storage l,
        bytes4 interfaceId
    ) internal view returns (bool) {
        return l.supportedInterfaces[interfaceId];
    }

    function setSupportedInterface(
        Layout storage l,
        bytes4 interfaceId,
        bool status
    ) internal {
        if (interfaceId == 0xffffffff)
            revert ERC165Storage__InvalidInterfaceId();
        l.supportedInterfaces[interfaceId] = status;
    }
}

File 15 of 30 : IERC721Base.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

import { IERC721 } from '../../../interfaces/IERC721.sol';
import { IERC721BaseInternal } from './IERC721BaseInternal.sol';

/**
 * @title ERC721 base interface
 */
interface IERC721Base is IERC721BaseInternal, IERC721 {

}

File 16 of 30 : IERC721BaseInternal.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

import { IERC721Internal } from '../../../interfaces/IERC721Internal.sol';

/**
 * @title ERC721 base interface
 */
interface IERC721BaseInternal is IERC721Internal {
    error ERC721Base__NotOwnerOrApproved();
    error ERC721Base__SelfApproval();
    error ERC721Base__BalanceQueryZeroAddress();
    error ERC721Base__ERC721ReceiverNotImplemented();
    error ERC721Base__InvalidOwner();
    error ERC721Base__MintToZeroAddress();
    error ERC721Base__NonExistentToken();
    error ERC721Base__NotTokenOwner();
    error ERC721Base__TokenAlreadyMinted();
    error ERC721Base__TransferToZeroAddress();
}

File 17 of 30 : AddressUtils.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

import { UintUtils } from './UintUtils.sol';

library AddressUtils {
    using UintUtils for uint256;

    error AddressUtils__InsufficientBalance();
    error AddressUtils__NotContract();
    error AddressUtils__SendValueFailed();

    function toString(address account) internal pure returns (string memory) {
        return uint256(uint160(account)).toHexString(20);
    }

    function isContract(address account) internal view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    function sendValue(address payable account, uint256 amount) internal {
        (bool success, ) = account.call{ value: amount }('');
        if (!success) revert AddressUtils__SendValueFailed();
    }

    function functionCall(
        address target,
        bytes memory data
    ) internal returns (bytes memory) {
        return
            functionCall(target, data, 'AddressUtils: failed low-level call');
    }

    function functionCall(
        address target,
        bytes memory data,
        string memory error
    ) internal returns (bytes memory) {
        return _functionCallWithValue(target, data, 0, error);
    }

    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return
            functionCallWithValue(
                target,
                data,
                value,
                'AddressUtils: failed low-level call with value'
            );
    }

    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory error
    ) internal returns (bytes memory) {
        if (value > address(this).balance)
            revert AddressUtils__InsufficientBalance();
        return _functionCallWithValue(target, data, value, error);
    }

    /**
     * @notice execute arbitrary external call with limited gas usage and amount of copied return data
     * @dev derived from https://github.com/nomad-xyz/ExcessivelySafeCall (MIT License)
     * @param target recipient of call
     * @param gasAmount gas allowance for call
     * @param value native token value to include in call
     * @param maxCopy maximum number of bytes to copy from return data
     * @param data encoded call data
     * @return success whether call is successful
     * @return returnData copied return data
     */
    function excessivelySafeCall(
        address target,
        uint256 gasAmount,
        uint256 value,
        uint16 maxCopy,
        bytes memory data
    ) internal returns (bool success, bytes memory returnData) {
        returnData = new bytes(maxCopy);

        assembly {
            // execute external call via assembly to avoid automatic copying of return data
            success := call(
                gasAmount,
                target,
                value,
                add(data, 0x20),
                mload(data),
                0,
                0
            )

            // determine whether to limit amount of data to copy
            let toCopy := returndatasize()

            if gt(toCopy, maxCopy) {
                toCopy := maxCopy
            }

            // store the length of the copied bytes
            mstore(returnData, toCopy)

            // copy the bytes from returndata[0:toCopy]
            returndatacopy(add(returnData, 0x20), 0, toCopy)
        }
    }

    function _functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory error
    ) private returns (bytes memory) {
        if (!isContract(target)) revert AddressUtils__NotContract();

        (bool success, bytes memory returnData) = target.call{ value: value }(
            data
        );

        if (success) {
            return returnData;
        } else if (returnData.length > 0) {
            assembly {
                let returnData_size := mload(returnData)
                revert(add(32, returnData), returnData_size)
            }
        } else {
            revert(error);
        }
    }
}

File 18 of 30 : UintUtils.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.8;

/**
 * @title utility functions for uint256 operations
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license)
 */
library UintUtils {
    error UintUtils__InsufficientHexLength();

    bytes16 private constant HEX_SYMBOLS = '0123456789abcdef';

    function add(uint256 a, int256 b) internal pure returns (uint256) {
        return b < 0 ? sub(a, -b) : a + uint256(b);
    }

    function sub(uint256 a, int256 b) internal pure returns (uint256) {
        return b < 0 ? add(a, -b) : a - uint256(b);
    }

    function toString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return '0';
        }

        uint256 temp = value;
        uint256 digits;

        while (temp != 0) {
            digits++;
            temp /= 10;
        }

        bytes memory buffer = new bytes(digits);

        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }

        return string(buffer);
    }

    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return '0x00';
        }

        uint256 length = 0;

        for (uint256 temp = value; temp != 0; temp >>= 8) {
            unchecked {
                length++;
            }
        }

        return toHexString(value, 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';

        unchecked {
            for (uint256 i = 2 * length + 1; i > 1; --i) {
                buffer[i] = HEX_SYMBOLS[value & 0xf];
                value >>= 4;
            }
        }

        if (value != 0) revert UintUtils__InsufficientHexLength();

        return string(buffer);
    }
}

File 19 of 30 : IChild.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.16;

/**
 * @title ERC-721 Non-Fungible Token Standard, optional child extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 *
 * A child token does not store it's own token balances and does not support
 * minting, transfer, approval. All view methods are passed along to the
 * parent contract.
 */
interface IChild {
    error ERC721Child__InvalidCaller();
    error ERC721Child__NonExistentToken();
    error ERC721Child__ApprovalNotSupported();
    error ERC721Child__TransferNotSupported();

    /**
     * @dev Returns the parent collection.
     */
    function parent() external view returns (address);

    /**
     * @dev Initialize token ownership by calling it from the parent contract.
     *
     * Only call this once in case the child contract after the parent contract.
     * Emits a {Transfer} event from ZeroAddress to current owner per token.
     */
    function init(uint256 tokenIdStart, uint256 tokenIdEnd) external;

    /**
     * @dev Update token ownership from by calling it from the parent contract.
     *
     * Emits a {Transfer} event.
     */
    function update(
        address from,
        address to,
        uint256 tokenId
    ) external;
}

File 20 of 30 : IERC721MarketplaceInternal.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

import {IERC721BaseInternal} from "@solidstate/contracts/token/ERC721/base/IERC721BaseInternal.sol";

interface IERC721MarketplaceInternal is IERC721BaseInternal {
    error ERC721Marketplace__NonExistentOffer();
    error ERC721Marketplace__InvalidArguments();
    error ERC721Marketplace__PaymentFailed();
    error ERC721Marketplace__InvalidValue();
    error ERC721Marketplace__InvalidPrice();

    event OfferCreated(
        uint256 indexed tokenId,
        uint256 indexed value,
        address indexed to
    );
    event OfferWithdrawn(uint256 indexed tokenId);
    event Sale(
        uint256 indexed tokenId,
        address indexed from,
        address indexed to,
        uint256 value
    );
}

File 21 of 30 : ScapesMarketplaceStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

library ScapesMarketplaceStorage {
    bytes32 internal constant STORAGE_SLOT =
        keccak256("scapes.storage.Marketplace");

    struct Offer {
        uint80 price;
        uint80 specificBuyerPrice;
        uint80 lastPrice;
        address specificBuyer;
    }

    struct Layout {
        address beneficiary;
        uint256 bps;
        mapping(uint256 => Offer) offers;
    }

    function layout() internal pure returns (Layout storage d) {
        bytes32 slot = STORAGE_SLOT;
        assembly {
            d.slot := slot
        }
    }
}

File 22 of 30 : WithFees.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

import {ERC165} from "@solidstate/contracts/introspection/ERC165.sol";
import {OwnableInternal} from "@solidstate/contracts/access/ownable/OwnableInternal.sol";
import {ScapesMarketplaceStorage} from "./ScapesMarketplaceStorage.sol";

abstract contract HasSecondarySaleFees is ERC165 {
    function getFeeRecipients(uint256 id)
        public
        view
        virtual
        returns (address payable[] memory);

    function getFeeBps(uint256 id)
        public
        view
        virtual
        returns (uint256[] memory);
}

/// @title WithFees
/// @author jalil.eth, akuti.eth | scapes.eth
/// @dev Implements the various fee standards that are floating around. We need a proper standard for this.
abstract contract WithFees is HasSecondarySaleFees, OwnableInternal {
    /// Exposes a way to update the secondary sale beneficiary
    /// @param beneficiary_ the new beneficiary
    function setBeneficiary(address beneficiary_) public onlyOwner {
        ScapesMarketplaceStorage.layout().beneficiary = beneficiary_;
    }

    /// Implement the `HasSecondarySaleFees` Contract
    /// @dev implements the standard pushed by Rarible
    /// @return list of fee recipients, in our case always one
    function getFeeRecipients(uint256)
        public
        view
        override
        returns (address payable[] memory)
    {
        address payable[] memory recipients = new address payable[](1);
        recipients[0] = payable(ScapesMarketplaceStorage.layout().beneficiary);
        return recipients;
    }

    /// Implement the `HasSecondarySaleFees` Contract
    /// @dev implements the standard pushed by Rarible
    /// @return list of fee basis points, in our case always one
    function getFeeBps(uint256)
        public
        view
        override
        returns (uint256[] memory)
    {
        uint256[] memory bpsArray = new uint256[](1);
        bpsArray[0] = ScapesMarketplaceStorage.layout().bps;
        return bpsArray;
    }
}

File 23 of 30 : ScapesERC721MetadataStorage.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.16;

library ScapesERC721MetadataStorage {
    bytes32 internal constant STORAGE_SLOT =
        keccak256("scapes.storage.ERC721Metadata");

    struct Layout {
        string name;
        string symbol;
        string description;
        string externalBaseURI;
        address scapeBound;
    }

    function layout() internal pure returns (Layout storage d) {
        bytes32 slot = STORAGE_SLOT;
        assembly {
            d.slot := slot
        }
    }
}

File 24 of 30 : ERC721Base.sol
// SPDX-License-Identifier: MIT
// Based on solidstate @solidstate/contracts/token/ERC721/base/ERC721Base.sol
// Changes made:
//  - make approval, transferFrom and safeTransferFrom virtual

pragma solidity ^0.8.8;

import {IERC721} from "@solidstate/contracts/interfaces/IERC721.sol";
import {IERC721Receiver} from "@solidstate/contracts/interfaces/IERC721Receiver.sol";
import {EnumerableMap} from "@solidstate/contracts/data/EnumerableMap.sol";
import {EnumerableSet} from "@solidstate/contracts/data/EnumerableSet.sol";
import {AddressUtils} from "@solidstate/contracts/utils/AddressUtils.sol";
import {IERC721Base} from "@solidstate/contracts/token/ERC721/base/IERC721Base.sol";
import {ERC721BaseStorage} from "./ERC721BaseStorage.sol";
import {ERC721BaseInternal} from "./ERC721BaseInternal.sol";

/**
 * @title Base ERC721 implementation, excluding optional extensions
 */
abstract contract ERC721Base is IERC721Base, ERC721BaseInternal {
    using AddressUtils for address;
    using EnumerableMap for EnumerableMap.UintToAddressMap;
    using EnumerableSet for EnumerableSet.UintSet;

    /**
     * @inheritdoc IERC721
     */
    function balanceOf(address account) public view returns (uint256) {
        return _balanceOf(account) + _balanceOfMerges(account);
    }

    /**
     * @notice query the balance of merges of given address
     * @return balance quantity of merge tokens held
     */
    function balanceOfMerges(address account) public view returns (uint256) {
        return _balanceOfMerges(account);
    }

    /**
     * @inheritdoc IERC721
     */
    function ownerOf(uint256 tokenId) public view returns (address) {
        return _ownerOf(tokenId);
    }

    /**
     * @inheritdoc IERC721
     */
    function getApproved(uint256 tokenId) public view returns (address) {
        return _getApproved(tokenId);
    }

    /**
     * @inheritdoc IERC721
     */
    function isApprovedForAll(address account, address operator)
        public
        view
        returns (bool)
    {
        return _isApprovedForAll(account, operator);
    }

    /**
     * @inheritdoc IERC721
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public payable virtual {
        _handleTransferMessageValue(from, to, tokenId, msg.value);
        if (!_isApprovedOrOwner(msg.sender, tokenId))
            revert ERC721Base__NotOwnerOrApproved();
        _transfer(from, to, tokenId);
    }

    /**
     * @inheritdoc IERC721
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public payable virtual {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @inheritdoc IERC721
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) public payable virtual {
        _handleTransferMessageValue(from, to, tokenId, msg.value);
        if (!_isApprovedOrOwner(msg.sender, tokenId))
            revert ERC721Base__NotOwnerOrApproved();
        _safeTransfer(from, to, tokenId, data);
    }

    /**
     * @inheritdoc IERC721
     */
    function approve(address operator, uint256 tokenId) public payable virtual {
        _handleApproveMessageValue(operator, tokenId, msg.value);
        address owner = ownerOf(tokenId);
        if (operator == owner) revert ERC721Base__SelfApproval();
        if (msg.sender != owner && !isApprovedForAll(owner, msg.sender))
            revert ERC721Base__NotOwnerOrApproved();
        _approve(operator, tokenId);
    }

    /**
     * @inheritdoc IERC721
     */
    function setApprovalForAll(address operator, bool status) public virtual {
        if (operator == msg.sender) revert ERC721Base__SelfApproval();
        ERC721BaseStorage.layout().operatorApprovals[msg.sender][
            operator
        ] = status;
        emit ApprovalForAll(msg.sender, operator, status);
    }
}

File 25 of 30 : ERC721BaseInternal.sol
// SPDX-License-Identifier: MIT
// Based on solidstate @solidstate/contracts/token/ERC721/base/ERC721BaseInternal.sol
// Changes made:
//  - store a holder balance instead of the individual tokens, this removes the
//    option to easily upgrade to ERC721Enumerable but lowers gas cost by ~45k per mint
//  - update balance in mint, transfer and burn functions separate balance for normal
//    tokens and merged tokens
//  - update balanceOf to return the combined balance
//  - add _afterTokenTransfer hook

pragma solidity ^0.8.8;

import {IERC721Receiver} from "@solidstate/contracts/interfaces/IERC721Receiver.sol";
import {EnumerableMap} from "@solidstate/contracts/data/EnumerableMap.sol";
import {EnumerableSet} from "@solidstate/contracts/data/EnumerableSet.sol";
import {AddressUtils} from "@solidstate/contracts/utils/AddressUtils.sol";
import {IERC721BaseInternal} from "@solidstate/contracts/token/ERC721/base/IERC721BaseInternal.sol";
import {ERC721BaseStorage} from "./ERC721BaseStorage.sol";

/**
 * @title Base ERC721 internal functions
 */
abstract contract ERC721BaseInternal is IERC721BaseInternal {
    using ERC721BaseStorage for ERC721BaseStorage.Layout;
    using AddressUtils for address;
    using EnumerableMap for EnumerableMap.UintToAddressMap;
    using EnumerableSet for EnumerableSet.UintSet;

    uint256 internal constant MERGES_TRESHOLD = 130_000;

    function _balanceOf(address account)
        internal
        view
        virtual
        returns (uint256)
    {
        if (account == address(0)) revert ERC721Base__BalanceQueryZeroAddress();
        return ERC721BaseStorage.layout().holderBalances[account];
    }

    function _balanceOfMerges(address account)
        internal
        view
        virtual
        returns (uint256)
    {
        if (account == address(0)) revert ERC721Base__BalanceQueryZeroAddress();
        return ERC721BaseStorage.layout().holderBalancesMerges[account];
    }

    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        address owner = ERC721BaseStorage.layout().tokenOwners.get(tokenId);
        if (owner == address(0)) revert ERC721Base__InvalidOwner();
        return owner;
    }

    function _getApproved(uint256 tokenId)
        internal
        view
        virtual
        returns (address)
    {
        ERC721BaseStorage.Layout storage l = ERC721BaseStorage.layout();

        if (!l.exists(tokenId)) revert ERC721Base__NonExistentToken();

        return l.tokenApprovals[tokenId];
    }

    function _isApprovedForAll(address account, address operator)
        internal
        view
        virtual
        returns (bool)
    {
        return ERC721BaseStorage.layout().operatorApprovals[account][operator];
    }

    function _isApprovedOrOwner(address spender, uint256 tokenId)
        internal
        view
        virtual
        returns (bool)
    {
        if (!ERC721BaseStorage.layout().exists(tokenId))
            revert ERC721Base__NonExistentToken();

        address owner = _ownerOf(tokenId);

        return (spender == owner ||
            _getApproved(tokenId) == spender ||
            _isApprovedForAll(owner, spender));
    }

    function _mint(address to, uint256 tokenId) internal virtual {
        if (to == address(0)) revert ERC721Base__MintToZeroAddress();

        ERC721BaseStorage.Layout storage l = ERC721BaseStorage.layout();

        if (l.exists(tokenId)) revert ERC721Base__TokenAlreadyMinted();

        _beforeTokenTransfer(address(0), to, tokenId);
        unchecked {
            if (tokenId > MERGES_TRESHOLD) l.holderBalancesMerges[to] += 1;
            else l.holderBalances[to] += 1;
        }
        l.tokenOwners.set(tokenId, to);

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

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

    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _mint(to, tokenId);
        if (!_checkOnERC721Received(address(0), to, tokenId, data))
            revert ERC721Base__ERC721ReceiverNotImplemented();
    }

    function _burn(uint256 tokenId) internal virtual {
        address owner = _ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId);

        _approve(address(0), tokenId);

        ERC721BaseStorage.Layout storage l = ERC721BaseStorage.layout();
        unchecked {
            if (tokenId > MERGES_TRESHOLD) l.holderBalancesMerges[owner] -= 1;
            else l.holderBalances[owner] -= 1;
        }
        l.tokenOwners.remove(tokenId);

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId);
    }

    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        if (_ownerOf(tokenId) != from) revert ERC721Base__NotTokenOwner();
        if (to == address(0)) revert ERC721Base__TransferToZeroAddress();

        _beforeTokenTransfer(from, to, tokenId);

        _approve(address(0), tokenId);

        ERC721BaseStorage.Layout storage l = ERC721BaseStorage.layout();
        unchecked {
            if (tokenId > MERGES_TRESHOLD) {
                l.holderBalancesMerges[from] -= 1;
                l.holderBalancesMerges[to] += 1;
            } else {
                l.holderBalances[from] -= 1;
                l.holderBalances[to] += 1;
            }
        }
        l.tokenOwners.set(tokenId, to);

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId);
    }

    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual {
        _transfer(from, to, tokenId);
        if (!_checkOnERC721Received(from, to, tokenId, data))
            revert ERC721Base__ERC721ReceiverNotImplemented();
    }

    function _approve(address operator, uint256 tokenId) internal virtual {
        ERC721BaseStorage.layout().tokenApprovals[tokenId] = operator;
        emit Approval(_ownerOf(tokenId), operator, tokenId);
    }

    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal virtual returns (bool) {
        if (!to.isContract()) {
            return true;
        }

        bytes memory returnData = to.functionCall(
            abi.encodeWithSelector(
                IERC721Receiver(to).onERC721Received.selector,
                msg.sender,
                from,
                tokenId,
                data
            ),
            "ERC721: transfer to non ERC721Receiver implementer"
        );

        bytes4 returnValue = abi.decode(returnData, (bytes4));
        return returnValue == type(IERC721Receiver).interfaceId;
    }

    /**
     * @notice ERC721 hook, called before externally called approvals for processing of included message value
     * @param operator beneficiary of approval
     * @param tokenId id of transferred token
     * @param value message value
     */
    function _handleApproveMessageValue(
        address operator,
        uint256 tokenId,
        uint256 value
    ) internal virtual {}

    /**
     * @notice ERC721 hook, called before externally called transfers for processing of included message value
     * @param from sender of token
     * @param to receiver of token
     * @param tokenId id of transferred token
     * @param value message value
     */
    function _handleTransferMessageValue(
        address from,
        address to,
        uint256 tokenId,
        uint256 value
    ) internal virtual {}

    /**
     * @notice ERC721 hook, called before all transfers including mint and burn
     * @dev function should be overridden and new implementation must call super
     * @param from sender of token
     * @param to receiver of token
     * @param tokenId id of transferred token
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}

    /**
     * @notice ERC721 hook, called after all transfers including mint and burn
     * @dev function should be overridden and new implementation must call super
     * @param from sender of token
     * @param to receiver of token
     * @param tokenId id of transferred token
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}
}

File 26 of 30 : ERC721BaseStorage.sol
// SPDX-License-Identifier: MIT
// Based on solidstate @solidstate/contracts/token/ERC721/base/ERC721BaseStorage.sol
// Changes made:
//  - replace holderTokens with holderBalances, this removes the
//    option to easily upgrade to ERC721Enumerable but lowers gas cost by ~45k per mint
//  - add holderBalancesMerges to separetly track the balance of merged scape tokens

pragma solidity ^0.8.8;

import {EnumerableMap} from "@solidstate/contracts/data/EnumerableMap.sol";

library ERC721BaseStorage {
    using EnumerableMap for EnumerableMap.UintToAddressMap;

    bytes32 internal constant STORAGE_SLOT =
        keccak256("scapes.storage.ERC721Base");

    struct Layout {
        EnumerableMap.UintToAddressMap tokenOwners;
        mapping(address => uint256) holderBalances;
        mapping(address => uint256) holderBalancesMerges;
        mapping(uint256 => address) tokenApprovals;
        mapping(address => mapping(address => bool)) operatorApprovals;
    }

    function layout() internal pure returns (Layout storage l) {
        bytes32 slot = STORAGE_SLOT;
        assembly {
            l.slot := slot
        }
    }

    function exists(Layout storage l, uint256 tokenId)
        internal
        view
        returns (bool)
    {
        return l.tokenOwners.contains(tokenId);
    }

    function totalSupply(Layout storage l) internal view returns (uint256) {
        return l.tokenOwners.length();
    }

    function tokenByIndex(Layout storage l, uint256 index)
        internal
        view
        returns (uint256)
    {
        (uint256 tokenId, ) = l.tokenOwners.at(index);
        return tokenId;
    }
}

File 27 of 30 : ScapesMerge.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

library ScapesMerge {
    struct MergePart {
        uint256 tokenId;
        bool flipX;
        bool flipY;
    }

    struct Merge {
        MergePart[] parts;
        bool isFade;
    }

    function toId(Merge calldata merge_)
        internal
        pure
        returns (uint256 mergeId)
    {
        for (uint256 i = 0; i < merge_.parts.length; i++) {
            MergePart memory part = merge_.parts[i];
            uint256 partDNA = part.tokenId;
            if (part.flipX) {
                partDNA |= 1 << 14;
            }
            if (part.flipY) {
                partDNA |= 1 << 15;
            }
            mergeId |= partDNA << (16 * i);
        }
        mergeId = mergeId << 1;
        if (merge_.isFade) {
            mergeId |= 1;
        }
    }

    function fromId(uint256 mergeId)
        internal
        pure
        returns (Merge memory merge_)
    {
        MergePart[15] memory parts;
        merge_.isFade = mergeId & 1 > 0;
        mergeId >>= 1;
        uint256 numParts;
        for (uint256 i = 0; i < 15; i++) {
            MergePart memory part = parts[i];
            uint256 offset = 16 * i;
            uint256 filter = (1 << (offset + 14)) - (1 << offset);
            part.tokenId = (mergeId & filter) >> offset;
            if (part.tokenId == 0) {
                break;
            }
            part.flipX = mergeId & (1 << (offset + 14)) > 0;
            part.flipY = mergeId & (1 << (offset + 15)) > 0;
            numParts++;
        }
        merge_.parts = new MergePart[](numParts);
        for (uint256 i = 0; i < numParts; i++) {
            merge_.parts[i] = parts[i];
        }
    }

    function getSortedTokenIds(Merge memory merge_, bool unique)
        internal
        pure
        returns (uint256[] memory)
    {
        uint256[] memory tokenIds = new uint256[](merge_.parts.length);
        for (uint256 i = 0; i < merge_.parts.length; i++) {
            tokenIds[i] = merge_.parts[i].tokenId;
        }
        _quickSort(tokenIds, int256(0), int256(tokenIds.length - 1));
        if (!unique) {
            return tokenIds;
        }
        uint256 uniqueCounter;
        uint256 lastTokenId;
        for (uint256 i = 0; i < tokenIds.length; i++) {
            if (tokenIds[i] != lastTokenId) uniqueCounter++;
            lastTokenId = tokenIds[i];
        }

        uint256[] memory uniqueTokenIds = new uint256[](uniqueCounter);
        uniqueCounter = 0;
        lastTokenId = 0;
        for (uint256 i = 0; i < tokenIds.length; i++) {
            if (tokenIds[i] != lastTokenId) {
                uniqueTokenIds[uniqueCounter] = tokenIds[i];
                uniqueCounter++;
            }
        }
        return uniqueTokenIds;
    }

    function hasNoFlip(Merge memory merge_) internal pure returns (bool) {
        for (uint256 i = 0; i < merge_.parts.length; i++) {
            if (merge_.parts[i].flipX || merge_.parts[i].flipY) return false;
        }
        return true;
    }

    // Sorts in-place
    function _quickSort(
        uint256[] memory arr,
        int256 left,
        int256 right
    ) internal pure {
        int256 i = left;
        int256 j = right;
        if (i == j) return;
        uint256 pivot = arr[uint256(left + (right - left) / 2)];
        while (i <= j) {
            while (arr[uint256(i)] < pivot) i++;
            while (pivot < arr[uint256(j)]) j--;
            if (i <= j) {
                (arr[uint256(i)], arr[uint256(j)]) = (
                    arr[uint256(j)],
                    arr[uint256(i)]
                );
                i++;
                j--;
            }
        }
        if (left < j) _quickSort(arr, left, j);
        if (i < right) _quickSort(arr, i, right);
    }
}

File 28 of 30 : DefaultOperatorFilterer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

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

/**
 * @title  DefaultOperatorFilterer
 * @notice Inherits from OperatorFilterer and automatically subscribes to the default OpenSea subscription.
 */
abstract contract DefaultOperatorFilterer is OperatorFilterer {
    address constant DEFAULT_SUBSCRIPTION = address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);

    constructor() OperatorFilterer(DEFAULT_SUBSCRIPTION, true) {}
}

File 29 of 30 : IOperatorFilterRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

interface IOperatorFilterRegistry {
    function isOperatorAllowed(address registrant, address operator) external view returns (bool);
    function register(address registrant) external;
    function registerAndSubscribe(address registrant, address subscription) external;
    function registerAndCopyEntries(address registrant, address registrantToCopy) external;
    function unregister(address addr) external;
    function updateOperator(address registrant, address operator, bool filtered) external;
    function updateOperators(address registrant, address[] calldata operators, bool filtered) external;
    function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external;
    function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external;
    function subscribe(address registrant, address registrantToSubscribe) external;
    function unsubscribe(address registrant, bool copyExistingEntries) external;
    function subscriptionOf(address addr) external returns (address registrant);
    function subscribers(address registrant) external returns (address[] memory);
    function subscriberAt(address registrant, uint256 index) external returns (address);
    function copyEntriesOf(address registrant, address registrantToCopy) external;
    function isOperatorFiltered(address registrant, address operator) external returns (bool);
    function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
    function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
    function filteredOperators(address addr) external returns (address[] memory);
    function filteredCodeHashes(address addr) external returns (bytes32[] memory);
    function filteredOperatorAt(address registrant, uint256 index) external returns (address);
    function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
    function isRegistered(address addr) external returns (bool);
    function codeHashOf(address addr) external returns (bytes32);
}

File 30 of 30 : OperatorFilterer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

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

/**
 * @title  OperatorFilterer
 * @notice Abstract contract whose constructor automatically registers and optionally subscribes to or copies another
 *         registrant's entries in the OperatorFilterRegistry.
 * @dev    This smart contract is meant to be inherited by token contracts so they can use the following:
 *         - `onlyAllowedOperator` modifier for `transferFrom` and `safeTransferFrom` methods.
 *         - `onlyAllowedOperatorApproval` modifier for `approve` and `setApprovalForAll` methods.
 */
abstract contract OperatorFilterer {
    error OperatorNotAllowed(address operator);

    IOperatorFilterRegistry public constant OPERATOR_FILTER_REGISTRY =
        IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);

    constructor(address subscriptionOrRegistrantToCopy, bool subscribe) {
        // If an inheriting token contract is deployed to a network without the registry deployed, the modifier
        // will not revert, but the contract will need to be registered with the registry once it is deployed in
        // order for the modifier to filter addresses.
        if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
            if (subscribe) {
                OPERATOR_FILTER_REGISTRY.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
            } else {
                if (subscriptionOrRegistrantToCopy != address(0)) {
                    OPERATOR_FILTER_REGISTRY.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy);
                } else {
                    OPERATOR_FILTER_REGISTRY.register(address(this));
                }
            }
        }
    }

    modifier onlyAllowedOperator(address from) virtual {
        // Allow spending tokens from addresses with balance
        // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
        // from an EOA.
        if (from != msg.sender) {
            _checkFilterOperator(msg.sender);
        }
        _;
    }

    modifier onlyAllowedOperatorApproval(address operator) virtual {
        _checkFilterOperator(operator);
        _;
    }

    function _checkFilterOperator(address operator) internal view virtual {
        // Check registry code length to facilitate testing in environments without a deployed registry.
        if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
            if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
                revert OperatorNotAllowed(operator);
            }
        }
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"AddressUtils__NotContract","type":"error"},{"inputs":[],"name":"ERC721Base__BalanceQueryZeroAddress","type":"error"},{"inputs":[],"name":"ERC721Base__ERC721ReceiverNotImplemented","type":"error"},{"inputs":[],"name":"ERC721Base__InvalidOwner","type":"error"},{"inputs":[],"name":"ERC721Base__MintToZeroAddress","type":"error"},{"inputs":[],"name":"ERC721Base__NonExistentToken","type":"error"},{"inputs":[],"name":"ERC721Base__NotOwnerOrApproved","type":"error"},{"inputs":[],"name":"ERC721Base__NotTokenOwner","type":"error"},{"inputs":[],"name":"ERC721Base__SelfApproval","type":"error"},{"inputs":[],"name":"ERC721Base__TokenAlreadyMinted","type":"error"},{"inputs":[],"name":"ERC721Base__TransferToZeroAddress","type":"error"},{"inputs":[],"name":"ERC721Marketplace__InvalidArguments","type":"error"},{"inputs":[],"name":"ERC721Marketplace__InvalidPrice","type":"error"},{"inputs":[],"name":"ERC721Marketplace__InvalidValue","type":"error"},{"inputs":[],"name":"ERC721Marketplace__NonExistentOffer","type":"error"},{"inputs":[],"name":"ERC721Marketplace__PaymentFailed","type":"error"},{"inputs":[],"name":"EnumerableMap__NonExistentKey","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"OperatorNotAllowed","type":"error"},{"inputs":[],"name":"Ownable__NotOwner","type":"error"},{"inputs":[],"name":"Ownable__NotTransitiveOwner","type":"error"},{"inputs":[],"name":"ScapesERC721__ChildAlreadyAdded","type":"error"},{"inputs":[],"name":"ScapesERC721__InvalidArgument","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","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":true,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OfferCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"OfferWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Sale","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":"OPERATOR_FILTER_REGISTRY","outputs":[{"internalType":"contract IOperatorFilterRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOfMerges","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getFeeBps","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getFeeRecipients","outputs":[{"internalType":"address payable[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bool","name":"flipX","type":"bool"},{"internalType":"bool","name":"flipY","type":"bool"}],"internalType":"struct ScapesMerge.MergePart[]","name":"parts","type":"tuple[]"},{"internalType":"bool","name":"isFade","type":"bool"}],"internalType":"struct ScapesMerge.Merge","name":"merge_","type":"tuple"}],"name":"merge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bool","name":"flipX","type":"bool"},{"internalType":"bool","name":"flipY","type":"bool"}],"internalType":"struct ScapesMerge.MergePart[]","name":"parts","type":"tuple[]"},{"internalType":"bool","name":"isFade","type":"bool"}],"internalType":"struct ScapesMerge.Merge","name":"merge_","type":"tuple"}],"name":"purge","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":"safeTransferFrom","outputs":[],"stateMutability":"payable","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":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"beneficiary_","type":"address"}],"name":"setBeneficiary","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":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","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":"payable","type":"function"}]

60806040523480156200001157600080fd5b50733cc6cdda760b79bafa08df41ecfa224f810dceb660016daaeb6d7670e522a718067333cd4e3b156200016e578015620000bc57604051633e9f1edf60e11b81523060048201526001600160a01b03831660248201526daaeb6d7670e522a718067333cd4e90637d3e3dbe906044015b600060405180830381600087803b1580156200009d57600080fd5b505af1158015620000b2573d6000803e3d6000fd5b505050506200016e565b6001600160a01b038216156200010d5760405163a0af290360e01b81523060048201526001600160a01b03831660248201526daaeb6d7670e522a718067333cd4e9063a0af29039060440162000082565b604051632210724360e11b81523060048201526daaeb6d7670e522a718067333cd4e90634420e48690602401600060405180830381600087803b1580156200015457600080fd5b505af115801562000169573d6000803e3d6000fd5b505050505b5050611f2c80620001806000396000f3fe6080604052600436106101295760003560e01c806342842e0e116100a5578063a22cb46511610074578063b9c4d9fb11610059578063b9c4d9fb14610317578063e0e037f614610344578063e985e9c51461036457600080fd5b8063a22cb465146102e4578063b88d4fde1461030457600080fd5b806342842e0e146102715780636352211e1461028457806370a08231146102a4578063a20b3799146102c457600080fd5b806318160ddd116100fc57806323b872dd116100e157806323b872dd1461021c5780632c9a9a381461022f57806341f434341461024f57600080fd5b806318160ddd146101dd5780631c31f710146101fc57600080fd5b806301ffc9a71461012e578063081812fc14610163578063095ea7b31461019b5780630ebd4c7f146101b0575b600080fd5b34801561013a57600080fd5b5061014e61014936600461195f565b610384565b60405190151581526020015b60405180910390f35b34801561016f57600080fd5b5061018361017e36600461197c565b6103df565b6040516001600160a01b03909116815260200161015a565b6101ae6101a93660046119ac565b6103ea565b005b3480156101bc57600080fd5b506101d06101cb36600461197c565b610403565b60405161015a91906119d6565b3480156101e957600080fd5b506127105b60405190815260200161015a565b34801561020857600080fd5b506101ae610217366004611a1a565b610472565b6101ae61022a366004611a35565b610523565b34801561023b57600080fd5b506101ae61024a366004611a71565b61054e565b34801561025b57600080fd5b506101836daaeb6d7670e522a718067333cd4e81565b6101ae61027f366004611a35565b610616565b34801561029057600080fd5b5061018361029f36600461197c565b61063b565b3480156102b057600080fd5b506101ee6102bf366004611a1a565b610646565b3480156102d057600080fd5b506101ae6102df366004611a71565b610664565b3480156102f057600080fd5b506101ae6102ff366004611aba565b610711565b6101ae610312366004611b38565b610725565b34801561032357600080fd5b5061033761033236600461197c565b610752565b60405161015a9190611bf8565b34801561035057600080fd5b506101ee61035f366004611a1a565b6107d5565b34801561037057600080fd5b5061014e61037f366004611c39565b6107e0565b7fffffffff00000000000000000000000000000000000000000000000000000000811660009081527f326d0c59a7612f6a9919e2a8ee333c80ba689d8ba2634de89c85cbb04832e705602052604081205460ff165b92915050565b60006103d982610832565b816103f481610899565b6103fe8383610989565b505050565b60408051600180825281830190925260609160009190602080830190803683370190505090507fcc410442a83ee5ff86205e8b7585832614e9498a8404ee01ae13793dea594db8600101548160008151811061046157610461611c6c565b602090810291909101015292915050565b7f8a22373512790c48b83a1fe2efdd2888d4a917bcdc24d0adf63e60f671680460546001600160a01b031633146104d5576040517f2f7a8ee100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fcc410442a83ee5ff86205e8b7585832614e9498a8404ee01ae13793dea594db8805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b826001600160a01b038116331461053d5761053d33610899565b610548848484610a10565b50505050565b600261055a8280611c82565b905010806105735750600861056f8280611c82565b9050115b156105aa576040517f9f61f8d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b6105b78280611c82565b9050811015610600576105ee33306105cf8580611c82565b858181106105df576105df611c6c565b90506060020160000135610523565b806105f881611ce8565b9150506105ad565b506106133361060e83610a42565b610af5565b50565b826001600160a01b03811633146106305761063033610899565b610548848484610b13565b60006103d982610b2e565b600061065182610b9d565b61065a83610c01565b6103d99190611d01565b600061066f82610a42565b90503361067b82610b2e565b6001600160a01b0316146106a257604051630f2db4ad60e01b815260040160405180910390fd5b6106ab81610c65565b60005b6106b88380611c82565b90508110156103fe576106ff30336106d08680611c82565b858181106106e0576106e0611c6c565b9050606002016000013560405180602001604052806000815250610d4e565b8061070981611ce8565b9150506106ae565b8161071b81610899565b6103fe8383610d82565b836001600160a01b038116331461073f5761073f33610899565b61074b85858585610e36565b5050505050565b604080516001808252818301909252606091600091906020808301908036833750507fcc410442a83ee5ff86205e8b7585832614e9498a8404ee01ae13793dea594db85482519293506001600160a01b0316918391506000906107b7576107b7611c6c565b6001600160a01b039092166020928302919091019091015292915050565b60006103d982610b9d565b6001600160a01b0380831660009081527f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bffa6020908152604080832093851683529290529081205460ff165b9392505050565b60007f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff561085f8184610e69565b61087c5760405163c1b843ef60e01b815260040160405180910390fd5b60009283526004016020525060409020546001600160a01b031690565b6daaeb6d7670e522a718067333cd4e3b15610613576040517fc61711340000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03821660248201526daaeb6d7670e522a718067333cd4e9063c617113490604401602060405180830381865afa15801561091f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109439190611d14565b610613576040517fede71dcc0000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024015b60405180910390fd5b60006109948261063b565b9050806001600160a01b0316836001600160a01b0316036109c85760405163424a22cb60e01b815260040160405180910390fd5b336001600160a01b038216148015906109e857506109e681336107e0565b155b15610a0657604051632f5de44f60e01b815260040160405180910390fd5b6103fe8383610e75565b610a1a3382610f0f565b610a3757604051632f5de44f60e01b815260040160405180910390fd5b6103fe838383610ff5565b6000805b610a508380611c82565b9050811015610ad3576000610a658480611c82565b83818110610a7557610a75611c6c565b905060600201803603810190610a8b9190611d31565b805160208201519192509015610aa057614000175b816040015115610aaf57618000175b610aba836010611d9a565b1b92909217915080610acb81611ce8565b915050610a46565b5060011b610ae76040830160208401611db9565b15610af0576001175b919050565b610b0f828260405180602001604052806000815250611171565b5050565b6103fe83838360405180602001604052806000815250610725565b600080610b5b7f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff5846111a5565b90506001600160a01b0381166103d9576040517f7e1a7d8b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006001600160a01b038216610bc657604051630810ff9760e41b815260040160405180910390fd5b506001600160a01b031660009081527f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff8602052604090205490565b60006001600160a01b038216610c2a57604051630810ff9760e41b815260040160405180910390fd5b506001600160a01b031660009081527f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff7602052604090205490565b6000610c7082610b2e565b9050610c7e816000846111b1565b610c89600083610e75565b7f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff56201fbd0831115610cdc576001600160a01b038216600090815260038201602052604090208054600019019055610cff565b6001600160a01b0382166000908152600282016020526040902080546000190190555b610d0981846112c6565b5060405183906000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a46103fe826000856112d2565b610d59848484610ff5565b610d6584848484611383565b61054857604051639ff10cdd60e01b815260040160405180910390fd5b336001600160a01b03831603610dab5760405163424a22cb60e01b815260040160405180910390fd5b3360008181527f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bffa602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b610e403383610f0f565b610e5d57604051632f5de44f60e01b815260040160405180910390fd5b61054884848484610d4e565b600061082b83836114af565b60008181527f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff960205260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0384169081179091558190610ed682610b2e565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000610f3b7f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff583610e69565b610f585760405163c1b843ef60e01b815260040160405180910390fd5b6000610f6383610b2e565b9050806001600160a01b0316846001600160a01b03161480610f9e5750836001600160a01b0316610f9384610832565b6001600160a01b0316145b80610fed57506001600160a01b0380821660009081527f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bffa602090815260408083209388168352929052205460ff165b949350505050565b826001600160a01b031661100882610b2e565b6001600160a01b03161461102f57604051630f2db4ad60e01b815260040160405180910390fd5b6001600160a01b03821661106f576040517f40311a0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61107a8383836111b1565b611085600082610e75565b7f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff56201fbd08211156110e7576001600160a01b038085166000908152600383016020526040808220805460001901905591851681522080546001019055611119565b6001600160a01b0380851660009081526002830160205260408082208054600019019055918516815220805460010190555b6111248183856114c7565b5081836001600160a01b0316856001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a46105488484846112d2565b61117b83836114dd565b6111886000848484611383565b6103fe57604051639ff10cdd60e01b815260040160405180910390fd5b600061082b838361162e565b6001600160a01b038316158015906111d157506001600160a01b03821615155b8015611215575060008181527fcc410442a83ee5ff86205e8b7585832614e9498a8404ee01ae13793dea594dba602052604090205469ffffffffffffffffffff1615155b156103fe5760008181527fcc410442a83ee5ff86205e8b7585832614e9498a8404ee01ae13793dea594dba6020526040808220805469ffffffffffffffffffff19168155600101805473ffffffffffffffffffffffffffffffffffffffff19169055517fcc410442a83ee5ff86205e8b7585832614e9498a8404ee01ae13793dea594db89183917facbc44b7f46dc350c99fc0d9e5f61ed5c588cb4cdc6b69ea0deb0c5b28e5efc49190a250505050565b600061082b83836116ab565b7f4ca5b6536be965df5a6498d1883a57f2ca8946e5bd14e05eafa6e36d0ceffcd16004908101546040517f8ce516da0000000000000000000000000000000000000000000000000000000081526001600160a01b0386811693820193909352848316602482015260448101849052911690638ce516da90606401600060405180830381600087803b15801561136657600080fd5b505af115801561137a573d6000803e3d6000fd5b50505050505050565b60006001600160a01b0384163b61139c57506001610fed565b600061144763150b7a0260e01b338887876040516024016113c09493929190611e26565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051806060016040528060328152602001611ec5603291396001600160a01b0388169190611793565b905060008180602001905181019061145f9190611e62565b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a02000000000000000000000000000000000000000000000000000000001492505050949350505050565b6000818152600183016020526040812054151561082b565b6000610fed84846001600160a01b0385166117a2565b6001600160a01b03821661151d576040517f23e5311a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff56115488183610e69565b1561157f576040517f6c35dbd800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61158b600084846111b1565b6201fbd08211156115bc576001600160a01b03831660009081526003820160205260409020805460010190556115de565b6001600160a01b03831660009081526002820160205260409020805460010190555b6115e98183856114c7565b5060405182906001600160a01b038516906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a46103fe600084846112d2565b6000818152600183016020526040812054808203611678576040517ff551fb1400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83600001600182038154811061169057611690611c6c565b90600052602060002090600202016001015491505092915050565b60008181526001830160205260408120548015611789578354600090859060001981019081106116dd576116dd611c6c565b906000526020600020906002020190508085600001600184038154811061170657611706611c6c565b600091825260208083208454600290930201918255600193840154918401919091559254815290860190915260409020819055835484908061174a5761174a611e7f565b60008281526020808220600260001990940193840201828155600190810183905592909355858152868201909252604082209190915591506103d99050565b60009150506103d9565b6060610fed8484600085611841565b600082815260018401602052604081205480820361180957505060408051808201825283815260208082018481528654600181810189556000898152848120955160029093029095019182559151908201558654868452818801909252929091205561082b565b8285600001600183038154811061182257611822611c6c565b906000526020600020906002020160010181905550600091505061082b565b6060843b61187b576040517f89c35afc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080866001600160a01b031685876040516118979190611e95565b60006040518083038185875af1925050503d80600081146118d4576040519150601f19603f3d011682016040523d82523d6000602084013e6118d9565b606091505b509150915081156118ed579150610fed9050565b8051156118fd5780518082602001fd5b836040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109809190611eb1565b7fffffffff000000000000000000000000000000000000000000000000000000008116811461061357600080fd5b60006020828403121561197157600080fd5b813561082b81611931565b60006020828403121561198e57600080fd5b5035919050565b80356001600160a01b0381168114610af057600080fd5b600080604083850312156119bf57600080fd5b6119c883611995565b946020939093013593505050565b6020808252825182820181905260009190848201906040850190845b81811015611a0e578351835292840192918401916001016119f2565b50909695505050505050565b600060208284031215611a2c57600080fd5b61082b82611995565b600080600060608486031215611a4a57600080fd5b611a5384611995565b9250611a6160208501611995565b9150604084013590509250925092565b600060208284031215611a8357600080fd5b813567ffffffffffffffff811115611a9a57600080fd5b82016040818503121561082b57600080fd5b801515811461061357600080fd5b60008060408385031215611acd57600080fd5b611ad683611995565b91506020830135611ae681611aac565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715611b3057611b30611af1565b604052919050565b60008060008060808587031215611b4e57600080fd5b611b5785611995565b93506020611b66818701611995565b935060408601359250606086013567ffffffffffffffff80821115611b8a57600080fd5b818801915088601f830112611b9e57600080fd5b813581811115611bb057611bb0611af1565b611bc2601f8201601f19168501611b07565b91508082528984828501011115611bd857600080fd5b808484018584013760008482840101525080935050505092959194509250565b6020808252825182820181905260009190848201906040850190845b81811015611a0e5783516001600160a01b031683529284019291840191600101611c14565b60008060408385031215611c4c57600080fd5b611c5583611995565b9150611c6360208401611995565b90509250929050565b634e487b7160e01b600052603260045260246000fd5b6000808335601e19843603018112611c9957600080fd5b83018035915067ffffffffffffffff821115611cb457600080fd5b6020019150606081023603821315611ccb57600080fd5b9250929050565b634e487b7160e01b600052601160045260246000fd5b600060018201611cfa57611cfa611cd2565b5060010190565b808201808211156103d9576103d9611cd2565b600060208284031215611d2657600080fd5b815161082b81611aac565b600060608284031215611d4357600080fd5b6040516060810181811067ffffffffffffffff82111715611d6657611d66611af1565b604052823581526020830135611d7b81611aac565b60208201526040830135611d8e81611aac565b60408201529392505050565b6000816000190483118215151615611db457611db4611cd2565b500290565b600060208284031215611dcb57600080fd5b813561082b81611aac565b60005b83811015611df1578181015183820152602001611dd9565b50506000910152565b60008151808452611e12816020860160208601611dd6565b601f01601f19169290920160200192915050565b60006001600160a01b03808716835280861660208401525083604083015260806060830152611e586080830184611dfa565b9695505050505050565b600060208284031215611e7457600080fd5b815161082b81611931565b634e487b7160e01b600052603160045260246000fd5b60008251611ea7818460208701611dd6565b9190910192915050565b60208152600061082b6020830184611dfa56fe4552433732313a207472616e7366657220746f206e6f6e20455243373231526563656976657220696d706c656d656e746572a2646970667358221220d83dbbda1b7ccf18c2da0820817ba4336eb0260154d4530f408cfddada4c3f9564736f6c63430008100033

Deployed Bytecode

0x6080604052600436106101295760003560e01c806342842e0e116100a5578063a22cb46511610074578063b9c4d9fb11610059578063b9c4d9fb14610317578063e0e037f614610344578063e985e9c51461036457600080fd5b8063a22cb465146102e4578063b88d4fde1461030457600080fd5b806342842e0e146102715780636352211e1461028457806370a08231146102a4578063a20b3799146102c457600080fd5b806318160ddd116100fc57806323b872dd116100e157806323b872dd1461021c5780632c9a9a381461022f57806341f434341461024f57600080fd5b806318160ddd146101dd5780631c31f710146101fc57600080fd5b806301ffc9a71461012e578063081812fc14610163578063095ea7b31461019b5780630ebd4c7f146101b0575b600080fd5b34801561013a57600080fd5b5061014e61014936600461195f565b610384565b60405190151581526020015b60405180910390f35b34801561016f57600080fd5b5061018361017e36600461197c565b6103df565b6040516001600160a01b03909116815260200161015a565b6101ae6101a93660046119ac565b6103ea565b005b3480156101bc57600080fd5b506101d06101cb36600461197c565b610403565b60405161015a91906119d6565b3480156101e957600080fd5b506127105b60405190815260200161015a565b34801561020857600080fd5b506101ae610217366004611a1a565b610472565b6101ae61022a366004611a35565b610523565b34801561023b57600080fd5b506101ae61024a366004611a71565b61054e565b34801561025b57600080fd5b506101836daaeb6d7670e522a718067333cd4e81565b6101ae61027f366004611a35565b610616565b34801561029057600080fd5b5061018361029f36600461197c565b61063b565b3480156102b057600080fd5b506101ee6102bf366004611a1a565b610646565b3480156102d057600080fd5b506101ae6102df366004611a71565b610664565b3480156102f057600080fd5b506101ae6102ff366004611aba565b610711565b6101ae610312366004611b38565b610725565b34801561032357600080fd5b5061033761033236600461197c565b610752565b60405161015a9190611bf8565b34801561035057600080fd5b506101ee61035f366004611a1a565b6107d5565b34801561037057600080fd5b5061014e61037f366004611c39565b6107e0565b7fffffffff00000000000000000000000000000000000000000000000000000000811660009081527f326d0c59a7612f6a9919e2a8ee333c80ba689d8ba2634de89c85cbb04832e705602052604081205460ff165b92915050565b60006103d982610832565b816103f481610899565b6103fe8383610989565b505050565b60408051600180825281830190925260609160009190602080830190803683370190505090507fcc410442a83ee5ff86205e8b7585832614e9498a8404ee01ae13793dea594db8600101548160008151811061046157610461611c6c565b602090810291909101015292915050565b7f8a22373512790c48b83a1fe2efdd2888d4a917bcdc24d0adf63e60f671680460546001600160a01b031633146104d5576040517f2f7a8ee100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fcc410442a83ee5ff86205e8b7585832614e9498a8404ee01ae13793dea594db8805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b826001600160a01b038116331461053d5761053d33610899565b610548848484610a10565b50505050565b600261055a8280611c82565b905010806105735750600861056f8280611c82565b9050115b156105aa576040517f9f61f8d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b6105b78280611c82565b9050811015610600576105ee33306105cf8580611c82565b858181106105df576105df611c6c565b90506060020160000135610523565b806105f881611ce8565b9150506105ad565b506106133361060e83610a42565b610af5565b50565b826001600160a01b03811633146106305761063033610899565b610548848484610b13565b60006103d982610b2e565b600061065182610b9d565b61065a83610c01565b6103d99190611d01565b600061066f82610a42565b90503361067b82610b2e565b6001600160a01b0316146106a257604051630f2db4ad60e01b815260040160405180910390fd5b6106ab81610c65565b60005b6106b88380611c82565b90508110156103fe576106ff30336106d08680611c82565b858181106106e0576106e0611c6c565b9050606002016000013560405180602001604052806000815250610d4e565b8061070981611ce8565b9150506106ae565b8161071b81610899565b6103fe8383610d82565b836001600160a01b038116331461073f5761073f33610899565b61074b85858585610e36565b5050505050565b604080516001808252818301909252606091600091906020808301908036833750507fcc410442a83ee5ff86205e8b7585832614e9498a8404ee01ae13793dea594db85482519293506001600160a01b0316918391506000906107b7576107b7611c6c565b6001600160a01b039092166020928302919091019091015292915050565b60006103d982610b9d565b6001600160a01b0380831660009081527f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bffa6020908152604080832093851683529290529081205460ff165b9392505050565b60007f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff561085f8184610e69565b61087c5760405163c1b843ef60e01b815260040160405180910390fd5b60009283526004016020525060409020546001600160a01b031690565b6daaeb6d7670e522a718067333cd4e3b15610613576040517fc61711340000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03821660248201526daaeb6d7670e522a718067333cd4e9063c617113490604401602060405180830381865afa15801561091f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109439190611d14565b610613576040517fede71dcc0000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024015b60405180910390fd5b60006109948261063b565b9050806001600160a01b0316836001600160a01b0316036109c85760405163424a22cb60e01b815260040160405180910390fd5b336001600160a01b038216148015906109e857506109e681336107e0565b155b15610a0657604051632f5de44f60e01b815260040160405180910390fd5b6103fe8383610e75565b610a1a3382610f0f565b610a3757604051632f5de44f60e01b815260040160405180910390fd5b6103fe838383610ff5565b6000805b610a508380611c82565b9050811015610ad3576000610a658480611c82565b83818110610a7557610a75611c6c565b905060600201803603810190610a8b9190611d31565b805160208201519192509015610aa057614000175b816040015115610aaf57618000175b610aba836010611d9a565b1b92909217915080610acb81611ce8565b915050610a46565b5060011b610ae76040830160208401611db9565b15610af0576001175b919050565b610b0f828260405180602001604052806000815250611171565b5050565b6103fe83838360405180602001604052806000815250610725565b600080610b5b7f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff5846111a5565b90506001600160a01b0381166103d9576040517f7e1a7d8b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006001600160a01b038216610bc657604051630810ff9760e41b815260040160405180910390fd5b506001600160a01b031660009081527f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff8602052604090205490565b60006001600160a01b038216610c2a57604051630810ff9760e41b815260040160405180910390fd5b506001600160a01b031660009081527f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff7602052604090205490565b6000610c7082610b2e565b9050610c7e816000846111b1565b610c89600083610e75565b7f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff56201fbd0831115610cdc576001600160a01b038216600090815260038201602052604090208054600019019055610cff565b6001600160a01b0382166000908152600282016020526040902080546000190190555b610d0981846112c6565b5060405183906000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a46103fe826000856112d2565b610d59848484610ff5565b610d6584848484611383565b61054857604051639ff10cdd60e01b815260040160405180910390fd5b336001600160a01b03831603610dab5760405163424a22cb60e01b815260040160405180910390fd5b3360008181527f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bffa602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b610e403383610f0f565b610e5d57604051632f5de44f60e01b815260040160405180910390fd5b61054884848484610d4e565b600061082b83836114af565b60008181527f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff960205260409020805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0384169081179091558190610ed682610b2e565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000610f3b7f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff583610e69565b610f585760405163c1b843ef60e01b815260040160405180910390fd5b6000610f6383610b2e565b9050806001600160a01b0316846001600160a01b03161480610f9e5750836001600160a01b0316610f9384610832565b6001600160a01b0316145b80610fed57506001600160a01b0380821660009081527f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bffa602090815260408083209388168352929052205460ff165b949350505050565b826001600160a01b031661100882610b2e565b6001600160a01b03161461102f57604051630f2db4ad60e01b815260040160405180910390fd5b6001600160a01b03821661106f576040517f40311a0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61107a8383836111b1565b611085600082610e75565b7f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff56201fbd08211156110e7576001600160a01b038085166000908152600383016020526040808220805460001901905591851681522080546001019055611119565b6001600160a01b0380851660009081526002830160205260408082208054600019019055918516815220805460010190555b6111248183856114c7565b5081836001600160a01b0316856001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60405160405180910390a46105488484846112d2565b61117b83836114dd565b6111886000848484611383565b6103fe57604051639ff10cdd60e01b815260040160405180910390fd5b600061082b838361162e565b6001600160a01b038316158015906111d157506001600160a01b03821615155b8015611215575060008181527fcc410442a83ee5ff86205e8b7585832614e9498a8404ee01ae13793dea594dba602052604090205469ffffffffffffffffffff1615155b156103fe5760008181527fcc410442a83ee5ff86205e8b7585832614e9498a8404ee01ae13793dea594dba6020526040808220805469ffffffffffffffffffff19168155600101805473ffffffffffffffffffffffffffffffffffffffff19169055517fcc410442a83ee5ff86205e8b7585832614e9498a8404ee01ae13793dea594db89183917facbc44b7f46dc350c99fc0d9e5f61ed5c588cb4cdc6b69ea0deb0c5b28e5efc49190a250505050565b600061082b83836116ab565b7f4ca5b6536be965df5a6498d1883a57f2ca8946e5bd14e05eafa6e36d0ceffcd16004908101546040517f8ce516da0000000000000000000000000000000000000000000000000000000081526001600160a01b0386811693820193909352848316602482015260448101849052911690638ce516da90606401600060405180830381600087803b15801561136657600080fd5b505af115801561137a573d6000803e3d6000fd5b50505050505050565b60006001600160a01b0384163b61139c57506001610fed565b600061144763150b7a0260e01b338887876040516024016113c09493929190611e26565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050604051806060016040528060328152602001611ec5603291396001600160a01b0388169190611793565b905060008180602001905181019061145f9190611e62565b7fffffffff00000000000000000000000000000000000000000000000000000000167f150b7a02000000000000000000000000000000000000000000000000000000001492505050949350505050565b6000818152600183016020526040812054151561082b565b6000610fed84846001600160a01b0385166117a2565b6001600160a01b03821661151d576040517f23e5311a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f24192aa1e930860a59219c92c01fd96c8fde79f41992dc1c72ae9cf26bb3bff56115488183610e69565b1561157f576040517f6c35dbd800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61158b600084846111b1565b6201fbd08211156115bc576001600160a01b03831660009081526003820160205260409020805460010190556115de565b6001600160a01b03831660009081526002820160205260409020805460010190555b6115e98183856114c7565b5060405182906001600160a01b038516906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a46103fe600084846112d2565b6000818152600183016020526040812054808203611678576040517ff551fb1400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83600001600182038154811061169057611690611c6c565b90600052602060002090600202016001015491505092915050565b60008181526001830160205260408120548015611789578354600090859060001981019081106116dd576116dd611c6c565b906000526020600020906002020190508085600001600184038154811061170657611706611c6c565b600091825260208083208454600290930201918255600193840154918401919091559254815290860190915260409020819055835484908061174a5761174a611e7f565b60008281526020808220600260001990940193840201828155600190810183905592909355858152868201909252604082209190915591506103d99050565b60009150506103d9565b6060610fed8484600085611841565b600082815260018401602052604081205480820361180957505060408051808201825283815260208082018481528654600181810189556000898152848120955160029093029095019182559151908201558654868452818801909252929091205561082b565b8285600001600183038154811061182257611822611c6c565b906000526020600020906002020160010181905550600091505061082b565b6060843b61187b576040517f89c35afc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080866001600160a01b031685876040516118979190611e95565b60006040518083038185875af1925050503d80600081146118d4576040519150601f19603f3d011682016040523d82523d6000602084013e6118d9565b606091505b509150915081156118ed579150610fed9050565b8051156118fd5780518082602001fd5b836040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109809190611eb1565b7fffffffff000000000000000000000000000000000000000000000000000000008116811461061357600080fd5b60006020828403121561197157600080fd5b813561082b81611931565b60006020828403121561198e57600080fd5b5035919050565b80356001600160a01b0381168114610af057600080fd5b600080604083850312156119bf57600080fd5b6119c883611995565b946020939093013593505050565b6020808252825182820181905260009190848201906040850190845b81811015611a0e578351835292840192918401916001016119f2565b50909695505050505050565b600060208284031215611a2c57600080fd5b61082b82611995565b600080600060608486031215611a4a57600080fd5b611a5384611995565b9250611a6160208501611995565b9150604084013590509250925092565b600060208284031215611a8357600080fd5b813567ffffffffffffffff811115611a9a57600080fd5b82016040818503121561082b57600080fd5b801515811461061357600080fd5b60008060408385031215611acd57600080fd5b611ad683611995565b91506020830135611ae681611aac565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715611b3057611b30611af1565b604052919050565b60008060008060808587031215611b4e57600080fd5b611b5785611995565b93506020611b66818701611995565b935060408601359250606086013567ffffffffffffffff80821115611b8a57600080fd5b818801915088601f830112611b9e57600080fd5b813581811115611bb057611bb0611af1565b611bc2601f8201601f19168501611b07565b91508082528984828501011115611bd857600080fd5b808484018584013760008482840101525080935050505092959194509250565b6020808252825182820181905260009190848201906040850190845b81811015611a0e5783516001600160a01b031683529284019291840191600101611c14565b60008060408385031215611c4c57600080fd5b611c5583611995565b9150611c6360208401611995565b90509250929050565b634e487b7160e01b600052603260045260246000fd5b6000808335601e19843603018112611c9957600080fd5b83018035915067ffffffffffffffff821115611cb457600080fd5b6020019150606081023603821315611ccb57600080fd5b9250929050565b634e487b7160e01b600052601160045260246000fd5b600060018201611cfa57611cfa611cd2565b5060010190565b808201808211156103d9576103d9611cd2565b600060208284031215611d2657600080fd5b815161082b81611aac565b600060608284031215611d4357600080fd5b6040516060810181811067ffffffffffffffff82111715611d6657611d66611af1565b604052823581526020830135611d7b81611aac565b60208201526040830135611d8e81611aac565b60408201529392505050565b6000816000190483118215151615611db457611db4611cd2565b500290565b600060208284031215611dcb57600080fd5b813561082b81611aac565b60005b83811015611df1578181015183820152602001611dd9565b50506000910152565b60008151808452611e12816020860160208601611dd6565b601f01601f19169290920160200192915050565b60006001600160a01b03808716835280861660208401525083604083015260806060830152611e586080830184611dfa565b9695505050505050565b600060208284031215611e7457600080fd5b815161082b81611931565b634e487b7160e01b600052603160045260246000fd5b60008251611ea7818460208701611dd6565b9190910192915050565b60208152600061082b6020830184611dfa56fe4552433732313a207472616e7366657220746f206e6f6e20455243373231526563656976657220696d706c656d656e746572a2646970667358221220d83dbbda1b7ccf18c2da0820817ba4336eb0260154d4530f408cfddada4c3f9564736f6c63430008100033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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.